CMXチュートリアル

MusicRepresentationの使い方

演奏表現を表すクラスMusicRepresentationの使い方を解説する。

  1. Calculatorオブジェクトを用意する
  2. 更新時の挙動を実装する
  3. MusicRepresentationを用意する
  4. レイヤーを登録する
  5. 用意したCalculatorを登録する
  6. 値を決定する
  7. 変更を確認する

MusicRepresentationは複数の演奏レイヤー(メロディーやコード等)を保持し、それぞれに計算オブジェクトを登録することができる。登録した計算オブジェクトは、レイヤーが更新されるとその情報を元に別のレイヤーの値を更新する。

ここでは、メロディーを表すmelodyレイヤーとコード進行を表すchordレイヤーを持ち、メロディーの値が更新されるとコードの値を更新するMusicRepresentationを作製する。

Calculatorオブジェクトを用意する

Calculatorインターフェースを実装したクラスは計算オブジェクトとしてMusicRepresentationに登録することができる。

public class ChordCalculator implements Calculator {

  private int[] chordMapping = { 0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6 };

ここでは、ノートナンバーからコードの種類への変換を表すchordMappingを定義しておく。

更新時の挙動を実装する

Calculatorオブジェクトが登録されたレイヤーが更新されるとupdateメソッドが呼び出される。

  public void update(MusicRepresentation musRep, MusicElement me, int index) {
    int division = musRep.getDivision();
    int measureLen = musRep.getMeasureNum();

    // 最後の小節なら処理しない
    if (index > division * (measureLen - 1))
      return;

    // ノートナンバー
    int noteNum = me.getHighestProbIndex();

    // 次の小節の開始インデックス
    int nextMeasureIndex = index / division + division;

    // 次の小節のコードを決める
    MusicElement nextChord = musRep.getMusicElement("chord", nextMeasureIndex);
    nextChord.setEvidence(chordMapping[noteNum % 12]);

    // 必要な場合更新する
    // musRep.update("chord", nextMeasureIndex);
  }

引数には更新されたMusicElementとそのインデックスが入る。ここでは、渡されたメロディーのMusicElementからノートナンバーを所得し、対応するコードの種類に変換して次の小節のコードを指定している。

MusicRepresentationを用意する

import jp.crestmuse.cmx.inference.MusicRepresentation;
import jp.crestmuse.cmx.inference.MusicRepresentation.MusicElement;

public class CMXTutorial5 {

  public static void main(String[] args) {
    // MusicRepresentationの初期化
    MusicRepresentation musRep = new MusicRepresentation(8, 8);

コンストラクタの引数にはそれぞれ小節数と小節あたりの分解能を指定する。

レイヤーを登録する

MusicRepresentationを使うにはまず使用するレイヤーを登録する。

    // レイヤーを登録する
    musRep.addMusicLayer("melody", 12);
    musRep.addMusicLayer("chord", new String[]{ "C", "Dm", "Em", "F", "G", "Am", "Bm(b5)" }, 8);

引数はそれぞれレイヤー名、結合長、ノートかコードかを表し、第二第三引数は指定しなければデフォルトの値が適用される。結合長とは、レイヤーがMusicRepresentationを区切る量を表し、例えば分解能が8で結合長が8だと一小節を一つの区切りとして扱う。

用意したCalculatorを登録する

    // Calculatorを登録する
    musRep.addCalculator("melody", new ChordCalculator(musRep));

引数にレイヤーの名前とCalculatorオブジェクトを指定する。

値を決定する

    // melodyレイヤーを一つ決定する
    musRep.getMusicElement("melody", 0).setEvidence(62);
    musRep.update("melody", 0);

メロディーレイヤーの最初の値を決定する。

変更を確認する

MusicRepresentation.updateを呼び出すと指定したレイヤーに登録されたCalculatorオブジェクトが計算処理を行う。

    // chordが更新されたかどうか確認する
    MusicElement nextChord = musRep.getMusicElement("chord", 8);
    int highestIndex = nextChord.getHighestProbIndex();
    System.out.println(nextChord.getLabel(highestIndex));

ここでは、登録したChordCalculatorが新しいコードの値を設定している。

CMXTutorial5.java
import jp.crestmuse.cmx.inference.MusicRepresentation;
import jp.crestmuse.cmx.inference.MusicRepresentation.MusicElement;

public class CMXTutorial5 {

  public static void main(String[] args) {
    // MusicRepresentationの初期化
    MusicRepresentation musRep = new MusicRepresentation(8, 8);

    // レイヤーを登録する
    musRep.addMusicLayer("melody", 12);
    musRep.addMusicLayer("chord", new String[]{ "C", "Dm", "Em", "F", "G", "Am", "Bm(b5)" }, 8);

    // Calculatorを登録する
    musRep.addCalculator("melody", new ChordCalculator());

    // melodyレイヤーを一つ決定する
    musRep.getMusicElement("melody", 0).setEvidence(62);
    musRep.update("melody", 0);

    // chordが更新されたかどうか確認する
    MusicElement nextChord = musRep.getMusicElement("chord", 8);
    int highestIndex = nextChord.getHighestProbIndex();
    System.out.println(nextChord.getLabel(highestIndex));
  }

}
ChordCalculator.java
import jp.crestmuse.cmx.inference.Calculator;
import jp.crestmuse.cmx.inference.MusicRepresentation;
import jp.crestmuse.cmx.inference.MusicRepresentation.MusicElement;

public class ChordCalculator implements Calculator {

  private int[] chordMapping = { 0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6 };

  public void update(MusicRepresentation musRep, MusicElement me, int index) {
    int division = musRep.getDivision();
    int measureLen = musRep.getMeasureNum();

    // 最後の小節なら処理しない
    if (index > division * (measureLen - 1))
      return;

    // ノートナンバー
    int noteNum = me.getHighestProbIndex();

    // 次の小節の開始インデックス
    int nextMeasureIndex = index / division + division;

    // 次の小節のコードを決める
    MusicElement nextChord = musRep.getMusicElement("chord", nextMeasureIndex);
    nextChord.setEvidence(chordMapping[noteNum % 12]);

    // 必要な場合更新する
    // musRep.update("chord", nextMeasureIndex);
  }

}