2005
6
7
8
9
10
11
12
2006
1
2
3
4
5
6
7
8
9
10
11
12
2007
1
2
3
4
5
6
7
8
9
10
11
12
2008
1
2
3
4
5
6
7
8
9
10
11
12
2009
1
2
3
4
5
6
7
8
9
10
11
12
2010
1
2
3
4
5
6
7
8
9
10
11
12
2011
1
2
3
4
5
6
7
8
9
10
11
12
2012
1
2
3
4
5
6
7
8
9
10
11
12
2013
1
2
3
4
5
6
7
8
9
10
11
12
2014
1
2
3
4
5
6
7
8
9
10
11
12
2015
1
2
3
4
5
6
7
8
9
10
11
12
2016
1
2
3
4
5
6
7
8
9
10
11
12
2017
1
2
3
4
5
6
7
8
9
10
11
12
2018
1
2
3
4
5
6
7
8
9
10
11
12
2019
1
2
3
4
5
6
7
8
9
10
11
12
2020
1
2
3
4
5
6
7
8
9
10
11
12
2021
1
music-synthesizer-for-androidの話の続き。 PianoKeyの生成をPianoKeyboardに移したところまで 書いたんでしたね。次に、PianoKeyの配置の処理を PianoKeyboardにまとめます。 各PianoKeyの配置は、各々が自分で決めています。 これはこれで一理あるんですが、鍵盤全体で配置を決め たいときには面倒なんですね。 各PianoKeyの配置は、PianoKey#layoutというメソッド で決めています。このメソッドでPianoKey#rect_という Rectを求めています。ですから、PianoKeyboardで各 PianoKeyの配置をRectで求め、それを各PianoKeyに配る という設計に変えます。 これで、PianoKeyの生成と配置の処理をPianoKeyboard に集めることができました。変更前に変更したい処理を 一ヶ所に集めるというリファクタリングは、わりとやり ます。 public class PianoKeyboard { // Generate the set of keys. There are 12 music keys per octave, plus the octave change button // on either end. public PianoKey[] createKeys(PianoView piano, int octaves) { ArrayListkeys = createWhiteKeys(piano, octaves); keys.addAll(createBlackKeys(piano, octaves)); keys.addAll(createOctaveChangingKeys(piano)); PianoKey[] typeArg = {}; return keys.toArray(typeArg); } private ArrayList createWhiteKeys(PianoView piano, int octaves) { ArrayList keys = new ArrayList (); for (int octave = 0; octave < octaves; ++octave) { for (int note = 0; note < 7; ++note) { keys.add(new WhitePianoKey(piano, octave, note)); } } return keys; } private ArrayList createBlackKeys(PianoView piano, int octaves) { ArrayList keys = new ArrayList (); for (int octave = 0; octave < octaves; ++octave) { for (int note = 0; note < 7; ++note) { if (BlackPianoKey.isValid(note)) { keys.add(new BlackPianoKey(piano, octave, note)); } } } return keys; } private ArrayList createOctaveChangingKeys(PianoView piano) { ArrayList keys = new ArrayList (); keys.add(new OctavePianoKey(piano, -1)); keys.add(new OctavePianoKey(piano, 1)); return keys; } public void draw(Canvas canvas, Rect drawingRect, PianoKey[] keys, int octaves) { layoutKeys(drawingRect, keys, octaves); drawKeys(canvas, keys); } private void layoutKeys(Rect drawingRect, PianoKey[] keys, int octaves) { for (int i = 0; i < keys.length; ++i) { if (keys[i] instanceof OctavePianoKey) { Rect rect = getOctaveKeyRect(drawingRect, octaves, keys[i]); keys[i].setRect(rect); } else if (keys[i] instanceof BlackPianoKey) { Rect rect = getBlackKeyRect(drawingRect, octaves, (NotePianoKey) keys[i]); keys[i].setRect(rect); } else { Rect rect = getWhiteKeyRect(drawingRect, octaves, (NotePianoKey) keys[i]); keys[i].setRect(rect); } } } private Rect getWhiteKeyRect(Rect drawingRect, int octaves, NotePianoKey key) { int whiteKeyWidth = getWhiteKeyWidth(drawingRect, octaves); Rect rect = new Rect(); rect.top = 0; rect.bottom = getWhiteKeyHeight(drawingRect); rect.left = ((key.getOctaveOffset() * PianoKey.WHITE_KEYS.length + key.getKey() + 1) * whiteKeyWidth); rect.right = rect.left + whiteKeyWidth; return rect; } private Rect getBlackKeyRect(Rect drawingRect, int octaves, NotePianoKey key) { int whiteKeyWidth = getWhiteKeyWidth(drawingRect, octaves); int blackKeyWidth = getBlackKeyWidth(drawingRect, octaves); Rect rect = new Rect(); rect.top = 0; rect.bottom = rect.top + getBlackKeyHeight(drawingRect); rect.left = ((key.getOctaveOffset() * PianoKey.WHITE_KEYS.length + key.getKey() + 2) * whiteKeyWidth) - (blackKeyWidth/2); rect.right = rect.left + blackKeyWidth; return rect; } private Rect getOctaveKeyRect(Rect drawingRect, int octaves, PianoKey key) { int whiteKeyWidth = getWhiteKeyWidth(drawingRect, octaves); Rect rect = new Rect(); rect.top = 0; rect.bottom = getWhiteKeyHeight(drawingRect); if (key.getDelta() <= 0) { rect.left = 0; rect.right = rect.left + whiteKeyWidth; } else { rect.right = drawingRect.right; rect.left = rect.right - whiteKeyWidth; } return rect; } private void drawKeys(Canvas canvas, PianoKey[] keys) { for (int i = 0; i < keys.length; ++i) { keys[i].draw(canvas); } } private int getWhiteKeyWidth(Rect drawingRect, int octaves) { // It's +2 to reserve space for the octave-up/down buttons. return drawingRect.width() / ((PianoKey.WHITE_KEYS.length * octaves) + 2); } private int getWhiteKeyHeight(Rect drawingRect) { return drawingRect.height(); } private int getBlackKeyWidth(Rect drawingRect, int octaves) { return (getWhiteKeyWidth(drawingRect, octaves) * 2) / 3; } private int getBlackKeyHeight(Rect drawingRect) { return getWhiteKeyHeight(drawingRect) / 2; } }
music-synthesizer-for-androidなんですけど、こないだ 「素人の自分が感じるようなレイテンシはなかった」って 書きましたけど、旧Nexus7でUSB電源ケーブルを抜くと CPUパワーが落ちるようで、そうなるとタッチを取り こぼすことがありました。あと、ごく稀にノイズが出ます。 で、music-synthesizerなんですけど、鍵盤のレイアウト がちょっと自分の好みと違うんですね。自分は歌メロを 指1本で弾く程度の楽しみ方なんですけど、music- synthesizerの鍵盤はC4...B3なんですよね。これだと 歌メロを弾くには向いてない。 ということで、レイアウトを変えてみようとソースを 見てみました。AndroidManifest.xmlを見ればわかる ように、起動時のアクティビティは com.levien.synthesizer.android.ui.PianoActivity2 なんですね。他にもアクティビティが並んでますけど、 今のところ、UIで切り替えることはできないのかな? PianoActivity2.javaを見ると、鍵盤は com.levien.synthesizer.android.widgets.piano.PianoView になるみたい。ちなみに、PianoActivity2にUSBがらみ のコードがあって、USB MIDIキーボードがつながるん じゃないかと。 PianoView.javaを見ると、白鍵、黒鍵、それと左右の オクターブ変えるキーがnewされています。キー関係の クラス構成を整理しておくと: PianoKey | +--OctavePianoKey | +--NotePianoKey | +--BlackPianoKey | +--WhitePianoKey という感じ。PianoKeyはViewじゃありません。PianoKey の役割は大体: * 音程 * 描画領域 * 描画 * ヒット判定 といったところ。 で、鍵盤のレイアウトを変えたいわけで、PianoKeyの 生成と描画領域の割り当てが別れているとやりにくい。 PianoKeyの生成はPianoViewでやって、描画領域の割り 当てはPianoKeyにやらせています。これを一ヶ所に まとめたい: public class PianoKeyboard { // Generate the set of keys. There are 12 music keys per octave, plus the octave change button // on either end. public PianoKey[] createKeys(PianoView piano, int octaves) { ArrayList<PianoKey> keys = createWhiteKeys(piano, octaves); keys.addAll(createBlackKeys(piano, octaves)); keys.addAll(createOctaveChangingKeys(piano)); PianoKey[] typeArg = {}; return keys.toArray(typeArg); } private ArrayList<PianoKey> createWhiteKeys(PianoView piano, int octaves) { ArrayList<PianoKey> keys = new ArrayList<PianoKey>(); for (int octave = 0; octave < octaves; ++octave) { for (int note = 0; note < 7; ++note) { keys.add(new WhitePianoKey(piano, octave, note)); } } return keys; } private ArrayList<PianoKey> createBlackKeys(PianoView piano, int octaves) { ArrayList<PianoKey> keys = new ArrayList<PianoKey>(); for (int octave = 0; octave < octaves; ++octave) { for (int note = 0; note < 7; ++note) { if (BlackPianoKey.isValid(note)) { keys.add(new BlackPianoKey(piano, octave, note)); } } } return keys; } private ArrayList<PianoKey> createOctaveChangingKeys(PianoView piano) { ArrayList<PianoKey> keys = new ArrayList<PianoKey>(); keys.add(new OctavePianoKey(piano, -1)); keys.add(new OctavePianoKey(piano, 1)); return keys; } public void draw(Canvas canvas, Rect drawingRect, PianoKey[] keys, int octaves) { layoutKeys(drawingRect, keys, octaves); drawKeys(canvas, keys); } private void layoutKeys(Rect drawingRect, PianoKey[] keys, int octaves) { for (int i = 0; i < keys.length; ++i) { keys[i].layout(drawingRect, octaves); } } private void drawKeys(Canvas canvas, PianoKey[] keys) { for (int i = 0; i < keys.length; ++i) { keys[i].draw(canvas); } } } public class PianoView extends View { public PianoView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PianoView); octaves_ = a.getInteger(R.styleable.PianoView_octaves, 1); firstOctave_ = a.getInteger(R.styleable.PianoView_first_octave, 4); drawingRect_ = new Rect(); keys_ = keyboard.createKeys(this, octaves_); pianoViewListener_ = null; } protected void onDraw(Canvas canvas) { super.onDraw(canvas); getDrawingRect(drawingRect_); keyboard.draw(canvas, drawingRect_, keys_, octaves_); } ... private PianoKeyboard keyboard = new PianoKeyboard(); } PianoKeyboardという名前がいいかどうかはともかく、 これで鍵盤のレイアウトを変えやすくはなりました。
music-synthesizer-for-androidなんですけど、こないだ 「素人の自分が感じるようなレイテンシはなかった」って 書きましたけど、旧Nexus7でUSB電源ケーブルを抜くと CPUパワーが落ちるようで、そうなるとタッチを取り こぼすことがありました。あと、ごく稀にノイズが出ます。 で、music-synthesizerなんですけど、鍵盤のレイアウト がちょっと自分の好みと違うんですね。自分は歌メロを 指1本で弾く程度の楽しみ方なんですけど、music- synthesizerの鍵盤はC4...B3なんですよね。これだと 歌メロを弾くには向いてない。 ということで、レイアウトを変えてみようとソースを 見てみました。AndroidManifest.xmlを見ればわかる ように、起動時のアクティビティは com.levien.synthesizer.android.ui.PianoActivity2 なんですね。他にもアクティビティが並んでますけど、 今のところ、UIで切り替えることはできないのかな? PianoActivity2.javaを見ると、鍵盤は com.levien.synthesizer.android.widgets.piano.PianoView になるみたい。ちなみに、PianoActivity2にUSBがらみ のコードがあって、USB MIDIキーボードがつながるん じゃないかと。 PianoView.javaを見ると、白鍵、黒鍵、それと左右の オクターブ変えるキーがnewされています。キー関係の クラス構成を整理しておくと: PianoKey | +--OctavePianoKey | +--NotePianoKey | +--BlackPianoKey | +--WhitePianoKey という感じ。PianoKeyはViewじゃありません。PianoKey の役割は大体: * 音程 * 描画領域 * 描画 * ヒット判定 といったところ。 で、鍵盤のレイアウトを変えたいわけで、PianoKeyの 生成と描画領域の割り当てが別れているとやりにくい。 PianoKeyの生成はPianoViewでやって、描画領域の割り 当てはPianoKeyにやらせています。これを一ヶ所に まとめたい。手始めにPianoKeyの生成を別クラスに 移します: public class PianoKeyboard { // Generate the set of keys. There are 12 music keys per octave, plus the octave change button // on either end. public PianoKey[] createKeys(PianoView piano, int octaves) { ArrayList<PianoKey> keys = createWhiteKeys(piano, octaves); keys.addAll(createBlackKeys(piano, octaves)); keys.addAll(createOctaveChangingKeys(piano)); PianoKey[] typeArg = {}; return keys.toArray(typeArg); } private ArrayList<PianoKey> createWhiteKeys(PianoView piano, int octaves) { ArrayList<PianoKey> keys = new ArrayList<PianoKey>(); for (int octave = 0; octave < octaves; ++octave) { for (int note = 0; note < 7; ++note) { keys.add(new WhitePianoKey(piano, octave, note)); } } return keys; } private ArrayList<PianoKey> createBlackKeys(PianoView piano, int octaves) { ArrayList<PianoKey> keys = new ArrayList<PianoKey>(); for (int octave = 0; octave < octaves; ++octave) { for (int note = 0; note < 7; ++note) { if (BlackPianoKey.isValid(note)) { keys.add(new BlackPianoKey(piano, octave, note)); } } } return keys; } private ArrayList<PianoKey> createOctaveChangingKeys(PianoView piano) { ArrayList<PianoKey> keys = new ArrayList<PianoKey>(); keys.add(new OctavePianoKey(piano, -1)); keys.add(new OctavePianoKey(piano, 1)); return keys; } public void draw(Canvas canvas, Rect drawingRect, PianoKey[] keys, int octaves) { layoutKeys(drawingRect, keys, octaves); drawKeys(canvas, keys); } private void layoutKeys(Rect drawingRect, PianoKey[] keys, int octaves) { for (int i = 0; i < keys.length; ++i) { keys[i].layout(drawingRect, octaves); } } private void drawKeys(Canvas canvas, PianoKey[] keys) { for (int i = 0; i < keys.length; ++i) { keys[i].draw(canvas); } } } public class PianoView extends View { public PianoView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PianoView); octaves_ = a.getInteger(R.styleable.PianoView_octaves, 1); firstOctave_ = a.getInteger(R.styleable.PianoView_first_octave, 4); drawingRect_ = new Rect(); keys_ = keyboard.createKeys(this, octaves_); pianoViewListener_ = null; } protected void onDraw(Canvas canvas) { super.onDraw(canvas); getDrawingRect(drawingRect_); keyboard.draw(canvas, drawingRect_, keys_, octaves_); } ... private PianoKeyboard keyboard = new PianoKeyboard(); }
で、ちょっとmusic-synthesizer-for-androidをいじり たくなって。とりあえずソースもらってきてビルドして みることに。 ビルドのやり方はちょっとややこしい。必要なものを 先にあげておくと: * Eclipse ADT いわゆるAndroidの開発環境 * Android NDK OpenSL ESを使うので必要 * Protocol Buffer 後述 * Apache Maven Protocol Bufferをビルドするのに使う * git music-synthesizerのソースをもらう * Apache Ant テストに使う? Eclipse ADKのほうは今さらなので省略。NDKは、都合 上、スペースのない場所、たとえばC:/直下に置いた ほうがいいみたい。Pathに加えておく。 さて、ビルドの手順は次のページに従う: http://code.google.com/p/music-synthesizer-for-android/wiki/GettingStarted ちょっと間違ってるとこもあるんだけど、それは後述。 このページによると、まずProtocol Bufferを入れろと ある。 Protocol Bufferについては: http://code.google.com/p/protobuf/ にホームページがある。簡単にいうと、構造化された データを言語中立のバイナリでやり取りするための ライブラリとDSLコンパイラのセット。Googleの多くの 製品で利用されていると書かれている。 music-synthesizerでProtocol Bufferがどんな感じで 使われているかはわからないけど、とりあえずインス トールする。 Procol BufferのJava用のライブラリが必要になるんだ けど、そのライブラリを作るためにProcol Bufferの DSLコンパイラが必要。なので、まずそれからはじめ なきゃいけないんだけど、幸いなことに、Windowsには DSLコンパイラのバイナリが用意されている: http://code.google.com/p/protobuf/downloads/list ここにあるprotoc-*-win32.zipというのがそれ。 で、Protocol Bufferのソースをもらってきたら、Getting Startedに書かれているとおりに従って.jarを作る。この ためにMavenが必要。 これでmusic-synthesizerのソースをgitでもらってくる。 で、protocをmusic-synthesizerの直下のbin/に置く。 ここがGetting Startedが間違っているところ。core/bin の下じゃなくて、music-synthesizerの直下でないとダメ。 で、Protocol Bufferの.jarをcore/lib/libprotobuf.jar としてコピーする。 ここまで来たら、ant testでこれまでの作業が正しく できているか確かめる。この手順が必須かどうかわから ないけどやっとく。 ant testが成功したら、android/の下でndk-buildを やってネイティブコードをコンパイルする。 最後にEclipseでmusic-synthesizerをインポートして 終わり。 かなり端折って書いたけど、上手くいけばPlayで配布 されているのと違って、Overdriveっていうダイアルが 新たに加わったアプリが動くはず。
Androidも、OpenSL ESのおかげで、結構サウ ンドプログラミングの環境が整ってきたのか な。 最近見つけたのがこれ: https://code.google.com/p/music-synthesizer-for-android/ Playにもパッケージがある。自分の旧Nexus7 で試したけど、素人の自分が気になるような レイテンシは感じられなかった。 まだ発展途上で、音数も少ない。シーケンサー はまだ使えないのかな? ソースをチラ見すると、USBキーボードがつな げるっぽいけど、まだ試してない。 -- ちなみに、OpenSL ESの仕様にはMIDIシンセ サイザが規定されているんだけど、Android NDKのライブラリには含まれていなかった。 AndroidのOpenSL ESを使ったものにはlibpd というのもある。こっちは試してない。最近 になってUSB MIDIがサポートされたらしい。
Copyright © 1905 tko at jitu.org