TX81Zを解析した(パフォーマンスモード編 追記)
TX81ZのPCEDの設定項目「Volume」について調査し忘れていた。パフォーマンスモードで設定できる8つの楽器ごとに音量を設定するためのパラメータである。
VCEDのVolumeと同様に0~99の値を指定する。YM2414のTLレジスタにセットされる値に影響することも同様。4つのオペレータのうち、キャリアとなるオペレータのみに影響することも同様。
次の値がTLレジスタに書き込まれる値に加算される。TLレジスタは値が0の時に音量最大となり、値が127の時に音量最小となる。
vol 加算値 0 127 1 127 2 96 3 86 4 77 5 72 6 67 7 62 8 60 9 56 10 54 11 51 12 50 13 48 14 45 15 44 16 42 17 41 18 40 19 39 20 37 21 36 22 35 23 34 24 33 25 32 26 31 27 30 28 29 29 28 30 28 31 27 32 26 33 25 34 25 35 24 36 23 37 23 38 22 39 21 40 21 41 20 42 20 43 19 44 19 45 18 46 18 47 17 48 17 49 16 50 16 51 15 52 15 53 14 54 14 55 13 56 13 57 13 58 12 59 12 60 11 61 11 62 11 63 10 64 10 65 9 66 9 67 9 68 8 69 8 70 8 71 7 72 7 73 7 74 7 75 6 76 6 77 6 78 5 79 5 80 5 81 4 82 4 83 4 84 3 85 3 86 3 87 3 88 2 89 2 90 2 91 2 92 1 93 1 94 1 95 1 96 0 97 0 98 0 99 0
TX81Zを解析した(パフォーマンスモード編)
TX81Zに搭載されているOPZは(OPMと同様に)異なる8音を同時に発声する能力を持つ。1つのNote on信号を受信するとシングルモードでは1音を発声するのに対し、パフォーマンスモードでは2つ以上の音を同時に発声することができる。どのように組み合わせるかはPCED(Performance Edit Parameters)で指定する。
以下、Instrument Detuneというパラメータの働きを述べる。音の高さを微妙にずらすためのもので、音の厚み?を増やすのに使うようだ。-7~+7の値をとる。OPZのKF(Key Fraction:半音の1/64)に素直に加算されるかと思っていたが、その加算量は音の高さによって異なることが解析の結果判明した。
まず音の高さから。発声される音の高さは次のように決まる:
- MIDIのNote on信号で0~127が指定される
- この値にVCEDのTransposeパラメータで-24~+24が加算される
- さらにPCEDのNoteShiftパラメータで-24~+24が加算される
- 12以下の場合、13以上になるまで12を足す
- 109以上の場合、108以下になるまで12を引く
- この結果、音の高さ は13(C#-1)~108(C7)となる。真ん中のドは60(C3)。
この音の高さとInstrument Detuneパラメータの値でKF=0からずらす量が決まる。
横軸: Instrument Detune (-7~7) 縦軸: (音の高さ - 13) / 4 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 +------------------------------------------------------- 0 | -17 -15 -12 -10 -8 -5 -3 0 2 4 7 9 12 14 17 1 | -17 -15 -12 -10 -7 -5 -3 0 2 4 7 9 11 14 16 2 | -16 -14 -12 -10 -7 -5 -3 0 2 4 6 9 11 13 16 3 | -16 -14 -12 -9 -7 -5 -3 0 2 4 6 9 11 13 15 4 | -16 -13 -11 -9 -7 -5 -2 0 2 4 6 8 10 13 15 5 | -15 -13 -11 -9 -7 -5 -2 0 2 4 6 8 10 12 14 6 | -15 -13 -11 -9 -6 -4 -2 0 2 4 6 8 10 12 14 7 | -14 -12 -10 -8 -6 -4 -2 0 2 4 6 8 10 12 14 8 | -14 -12 -10 -8 -6 -4 -2 0 1 3 5 7 9 11 13 9 | -13 -12 -10 -8 -6 -4 -2 0 1 3 5 7 9 11 13 10 | -13 -11 -9 -8 -6 -4 -2 0 1 3 5 7 9 10 12 11 | -13 -11 -9 -7 -6 -4 -2 0 1 3 5 7 8 10 12 12 | -12 -10 -9 -7 -5 -4 -2 0 1 3 5 6 8 10 11 13 | -12 -10 -8 -7 -5 -4 -2 0 1 3 4 6 8 9 11 14 | -11 -10 -8 -7 -5 -3 -2 0 1 3 4 6 7 9 10 15 | -11 -9 -8 -6 -5 -3 -2 0 1 3 4 6 7 9 10 16 | -10 -9 -7 -6 -5 -3 -2 0 1 2 4 5 7 8 10 17 | -10 -9 -7 -6 -4 -3 -2 0 1 2 4 5 6 8 9 18 | -9 -8 -7 -6 -4 -3 -2 0 1 2 3 5 6 7 9 19 | -9 -8 -7 -5 -4 -3 -2 0 1 2 3 5 6 7 8 20 | -9 -7 -6 -5 -4 -3 -1 0 1 2 3 4 5 7 8 21 | -8 -7 -6 -5 -4 -3 -1 0 1 2 3 4 5 6 7 22 | -8 -7 -6 -5 -3 -2 -1 0 1 2 3 4 5 6 7 23 | -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7
あとは実装。しばらくプログラムから離れていたので思い出すところから。エミュレーションしたいのはWT11なので、TX81Zの解析結果がそのまま適用できるか微妙…。
WT11固有のパラメータ
WT11のエディットバッファ内のボイスデータ・パフォーマンスデータをTX81Z Programmerで取得する、という記事を前回書いた。その後マニュアルを眺めていたらTX81Zにはない固有のパラメータが存在するのに気が付いた。
ボイスデータについては、TX81ZのVCED・ACEDに加えWT11にはACED2というデータがある。アフタータッチの感度などを設定できる。
パフォーマンスデータについては、TX81ZのPCEDに加えWT11にはPCED2というデータがある。DSPエフェクタの設定項目のように見える。
ACED2・PCED2はTX81Z Programmerでは取得できなそうなので、吸出しプログラムを作ってみた。WT11はsecret modeで起動した後に本体のキーでボイスやパフォーマンスを変更するとsysexをMIDIに出力するようなので、それを拾って表示することにした。
ボイスデータを変更すると、こんなsysexデータが出力される。各行の先頭の数字はsysexのサイズ。マニュアルのTOTAL BULK SIZEにあるように、VCED=101、ACED=41、ACED2=28bytesとなる。
101:f0 43 00 03 00 5d 14 12 00 07 0f 0a 01 04 00 00 46 0a 03 1f 1f 00 07 0f 0f 01 03 00 00 44 04 03 15 14 00 05 0f 09 01 02 00 00 2f 08 03 12 1f 00 0b 0f 1f 01 05 00 00 63 0a 03 03 00 1f 0a 00 00 00 02 05 00 18 00 02 00 00 28 01 00 00 0f 00 0f 00 32 63 57 54 31 31 20 20 41 30 31 20 63 63 63 32 32 32 56 f7 41:f0 43 00 7e 00 21 4c 4d 20 20 38 39 37 36 41 45 00 00 00 02 00 00 00 00 01 00 00 00 00 03 00 00 00 00 03 00 00 00 00 3a f7 28:f0 43 00 7e 00 14 4c 4d 20 20 38 30 32 33 41 45 00 00 32 00 00 00 00 00 00 00 22 f7
一方パフォーマンスデータのsysexデータはこんな感じ。PCED=128、PCED2=51bytesとなる。
128:f0 43 00 7e 00 78 4c 4d 20 20 38 39 37 36 50 45 04 00 20 00 00 3b 07 18 47 03 01 00 04 00 21 00 00 7f 07 18 63 03 01 00 00 00 20 00 00 7f 07 18 63 03 00 00 00 00 20 00 00 7f 07 18 63 03 00 00 00 00 20 00 00 7f 07 18 63 03 00 00 00 00 20 00 00 7f 07 18 63 03 00 00 00 00 20 00 00 7f 07 18 63 03 00 00 00 00 20 00 00 7f 07 18 63 03 00 00 00 00 00 00 4f 62 6f 65 20 31 20 20 20 20 13 f7 51:f0 43 00 7e 00 2b 4c 4d 20 20 38 30 37 33 50 45 00 01 00 01 00 01 00 01 00 01 00 01 00 01 00 01 01 25 64 00 14 00 00 00 00 00 00 00 00 00 00 00 00 1a f7
プログラムはJavaで作った。Javaは意外とMIDI関連ライブラリが充実している。
import javax.sound.midi.MidiDevice; import javax.sound.midi.MidiMessage; import javax.sound.midi.MidiSystem; import javax.sound.midi.Receiver; import javax.sound.midi.SysexMessage; import javax.sound.midi.Transmitter; /** * WT11をsecret modeで起動すると, * 本体のキーでボイス/パフォーマンスを切り替えるときにMIDIにダンプデータが出てくる * これを拾って表示する */ public class WT11sysex { Receiver receiver; Transmitter transmitter; MidiDevice deviceIn, deviceOut; WT11sysex() { String AUDIOIF = "UR22mkII"; // オーディオインタフェース名 try { MidiDevice.Info info[] = MidiSystem.getMidiDeviceInfo(); for (MidiDevice.Info i: info) { MidiDevice device = MidiSystem.getMidiDevice(i); String name = i.getName(); String desc = i.getDescription(); System.out.printf("%s ## %s\n", name, desc); if (name.contains(AUDIOIF) && device.getMaxReceivers() != 0) deviceOut = device; if (name.contains(AUDIOIF) && device.getMaxTransmitters() != 0) deviceIn = device; } deviceOut.open(); receiver = deviceOut.getReceiver(); deviceIn.open(); transmitter = deviceIn.getTransmitter(); transmitter.setReceiver(new MyReceiver()); } catch (Exception e) { e.printStackTrace(); } } void shutdown() { receiver.close(); transmitter.close(); deviceOut.close(); deviceIn.close(); } /** 拾ったsysexデータを表示する */ class MyReceiver implements Receiver { @Override public void send(MidiMessage message, long timeStamp) { int len = message.getLength(); byte b[] = message.getMessage(); System.out.printf("%5d:", len); for (int i=0; i<len; i++) System.out.printf("%02x ", b[i]); System.out.println(); } @Override public void close() { } } /** SYSEXを送る予定だったが不要だった */ void sendSysex() { try { int seq[] = { 0xf0,0x43,0x20,0x7e,'L','M',' ',' ','8','0','2','3','A','E',0xf7 }; // (3)ACED2 + ACED + VCED byte b[] = new byte[seq.length]; for (int i=0; i<seq.length; i++) b[i] = (byte)(seq[i] & 0xff); SysexMessage message = new SysexMessage(b, b.length); receiver.send(message, -1); } catch (Exception ex) { ex.printStackTrace(); } } /** 60秒のうちにキーで切り替える */ public static void main(String args[]) throws Exception { WT11sysex wt11 = new WT11sysex(); //wt11.sendSysex(); Thread.sleep(60000); wt11.shutdown(); } }
WT11をゲットした
TX81Zを検索していると、ちょいちょいWT11のページも引っかかる。面白そうなので中古品を買ってみた。
WT11はYAMAHAから発売された音源モジュール。プリセットのほとんどが木管/金管楽器でWX11などのブレス入力MIDIコントローラーとつないで使う。Wikipediaによると30年以上前の設計のようだ。
- 1986年:TX81Z発売(FMシンセサイザ)
- 1987年:WX7発売(MIDIコントローラー)
- 1988年:WX11発売( 〃 )
- 1988年:WT11発売(FMシンセサイザ)
- 1998年:WX5発売(MIDIコントローラー 10年経ってからの発売)
WT11の中身はTX81Zとほぼ同じでエフェクタ付き、という仕様なのが分かった。TX81Zとも互換性?があるようだ。シークレットモードへの移行方法も説明されている。
ameblo.jp
というわけで、次の目標は「WT11のプリセットを自作ウィンドシンセで鳴らす」ということにする。まずはプリセットデータの取り出しから行った。
WT11はTX81Zで言うパフォーマンスモードで動いている。取り出すべきデータは次の2つ:
- PCED (Performance Edit Parameters):96個
- VCED+ACED (Voice Edit Parameters + Additional Parameters):112個
取説の40ページによると、これらをパフォーマンスエディットバッファ・ボイスエディットバッファに入れればMIDI経由で読み取れるように見える。TX81Z Programmerで取り出しを行った。
PCEDデータの取り出しは通常モードでできる。「A01 Oboe 1」~「C32 FreeJazz」という名前のデータ96個を得ることができた。
VCED+ACEDデータの取り出しはシークレットモードへの移行が必要(通常モードだとバッファにセットする方法がなさそうなので)。ちょっと変則的な名前がついていて、
- 「WT11 A01 」~「WT11 A32 」
- 「WT11 B01 」~「WT11 B32 」
- 「WT11 C01 」~「WT11 C32 」
- 「WT11-D01」~「WT11-D16」
という名前のデータ112個を得ることができた。パフォーマンスモードの実装はこれから。
話は変わるがちょっと前にティンホイッスルを買った。強く息を吹くとオクターブが高くなるという不思議な楽器だ。楽譜も買った。アイリッシュ音楽は祝祭感があってよいと思う(無印ぽい気もするが)。
celtnofue.com
リバーブつけた
今までのなんちゃってリバーブをFreeverbに変更した。ソースコードは以下のページからリンクされているページで入手できる。
prettoguitar.blogspot.com
リンク先はFreeverb3というVSTの作者のページ。Freeverbより音質がよいらしい。ESP32で動くかどうかは分からない。
freeverb3vst.osdn.jp
freeverbsource.zipをダウンロードし、
- allpass.cpp, allpass.hpp
- comb.cpp, comb.hpp
- denormals.h
- revmodel.cpp, revmodel.hpp
- tuning.h
をESP32ソースファイルの置き場所にコピーし、
revmodel *freeverb;
:
freeverb = new revmodel();
でリバーブモデル?を生成しておく。入出力バッファを
float revInput1[64], revInput2[64]; float revOutput1[64], revOutput2[64];
で64サンプル×2ch分確保しておく。ym2151.cのym2151_update_one()で得られる64サンプル2ch分をrevInput1/2[]に格納して
freeverb->processreplace(revInput1, revInput2, revOutput1, revOutput2, 64, 1);
を呼び出すとrevOutput1/2[]に出力される。これをi2s_write()に渡すとリバーブされた音が出る。思っていた以上にきれいな音だった。耳の肥えていない自分にはこれで十分かな。パラメータはroomsize, damp, wet, dry, widthの5つ。tuning.hに初期値が設定してある。dryが0だったので0.67に変更した。
ESP32で処理するときの負荷だが、44100サンプル作るのに
だった。結構ぎりぎり! 今はcore0で実行している。BLE MIDIと一緒に使うのは無理かもしれない。
音色エディタを作った
ウィンドシンセの音色をPCから変更するための音色エディタを作った。
よくありそうなFM音源音色エディタだけど、16進数を編集していたのに比べれば楽になった。ウィンドシンセとの通信はシリアル経由。そのうちBLE MIDIでやりとりしたい。PCでも同じエンジンが動いているので、ウィンドシンセを繋がなくても音は確認できる。
上半分はTX81Zのパラメータの設定と波形の表示。小さくまとめたかったのでcontrolP5で。Swingの中にProcessingコードを埋め込む方法はここを参考にした。音色作成には波形よりも周波数スペクトルを見たほうがよいのかも。今後の課題。
下半分はプリセットの選択。こちらはSwingで。赤文字はこのサイトで聴くことのできる音。実機が無かった頃は、この音と比較しながらエミュレータを作っていた。
音が出た
TX81Zのプリセットを発声させることができた。ESP32の2つのコアを使って、センサをcore1で、TX81Zエミュレータをcore0で動かしている。8音だしているがまだ余裕がありそう。
音色のパラメータVCED(Voice Edit Parameters)・ACED(Voice Edit Additional Parameters)のうち、以下は未実装。
- VCED67: Foot Control Volume
- VCED68~70: TX81Zでは未使用
- VCED71,72: Modulation Wheel Pitch/Amplitude (LFO)
- VCED73,74: Breath Control Pitch/Amplitude (LFO)
- VCED75: Breath Control Pitch Bias
- ACED20: Reverb Rate
- ACED21,22: Foot Controller Pitch/Amplitude (LFO)
今後の予定
- 音色エディタ:PCで編集したFMパラメータをウィンドシンセに設定する機能がほしい
- エミュレーション精度向上:実機と出音が違うのがいくつかある
- リバーブ実装:TX81Zの疑似リバーブではないものを実装したい
- LFO制御:加速度/角速度センサにつなげると面白いかも
- リップセンサ:指ではなく唇や歯でピッチベンドさせてみたい
動画も作ってみた。
栗コーダーカルテット「帝国のマーチ」を吹いてみた。ESP32でTX81Zをエミュレートさせてリコーダーぽい音を出してみた。オリジナルはアンデス25という楽器で演奏しているらしい。 #ウィンドシンセ #電子工作 pic.twitter.com/WAehgku2YH
— tama_nikki (@tama_ni_nikki) 2021年3月27日
指が動いてなくてピロっている。ミスなく演奏するのはデバッグよりも難しい。