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

ホーム

2013年11月30日

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) {
    ArrayList keys = 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;
  }
}

本家Permlink


2013年11月29日

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という名前がいいかどうかはともかく、
これで鍵盤のレイアウトを変えやすくはなりました。

本家Permlink


2013年11月28日

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();
}

本家Permlink


2013年11月27日

で、ちょっと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っていうダイアルが
新たに加わったアプリが動くはず。

本家Permlink


2013年11月25日

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がサポートされたらしい。


本家Permlink


Copyright © 1905 tko at jitu.org

バカが征く on Rails