June 2009 Archives

名前を決めないと...

| No Comments | No TrackBacks

MMLコンパイラの計画は月単位どころか年単位で以前からあったにもかかわらず、未だにまともな名前がついていない。google code上にはsequerというコードネームで登録してあるのだけど、音読するとSQLと変わらなくていまいちなので、githubに移行するにあたっては、何かもっと相応しい名前をつけたい。

肝心のコンパイラのコードは、ようやく変数参照の解決までこぎ着けて、後はイベントをタイムラインに合わせてソートしてSMFとして出力する部分さえ作れば、最低限のコンパイラとしての機能は実現できることになる。とはいえ、省略している部分とやっつけている部分がかなりあるので、そこは一つずつ埋めていかなければならない。

できれば今月中に最低限の打ち込みが出来るようなSMF出力部分まで終わらせておきたいのだけど、あまりこっちにかまけていると今度は電子工作が全く進まなくなって困りそうだ。

a runnable worth a thousand words

| No Comments | No TrackBacks

MIDIプレイヤが出来たら、今度は再生する曲を作るものがほしくなる。というわけで週末からMMLコンパイラの作業を再開した。

こっちは少なからず複雑なプログラムになるので(一般的なソースコードパーサとはだいぶ性質が異なることは以前にぱらぱらと書いた)、とりあえず処理モデルを明確にしてひとつずつ片付けていくことにした。処理モデルはこんな感じになる:

  • ソーステキストを解析して、内容を1行ずつトラック、マクロ、その他変数定義などに区別する。
  • トラック定義とマクロ定義、変数定義から、トークンのリストを生成する。トークンの意味は多重に解釈されうるので、ここでは複雑な解析を行わない。
  • トラック定義、マクロ定義、変数定義に含まれるトークンリストから、命令と式を解析してツリー(もどき)構造にする。命令はマクロ呼び出しまたはプリミティブ命令。マクロ定義自体が、引数を包含するかたちで定義されないので、引数の数はチェックされない。
  • 変数定義を展開する。
  • マクロを参照している命令ツリーをプリミティブ命令のツリーに展開する。
  • 計算を要する式と変数参照を全て展開し、イベントのリストにする。
  • イベントリストからSMFを生成する。

で、ツリーもどき構造の生成までとりあえずやっつけたのだけど、その間デフォルト取り込みするMMLを大幅に書き換える必要があった(基本的にほとんどの命令は)。多くは(MML中の)バグ潰しと、文法的な矛盾の解消だ。

一番分かりやすい例を挙げるなら、ツリー構築を実装するまで、MMLの中ではマクロ呼び出しのあとに ( ) がついているものが多々存在していた。CC(0, $val) といったものである。しかし次のような例を考えると、これは全く正しくないことが分かる:

  • #macro ( $val:number=4 { __LET("defaultvelocity", $defaultvelocity - $val) } // define macro '(' as "velocity down"
  • 1 cde(cde)cde(cde) // track 1

'(' は、一般的な用法に合わせて、音量を小さくするコマンドとして定義されていて、上記の例でもそのような意図で使用されている。マクロの引数を明示するために使われてはならないのだ。(ただ、代入のようなプリミティブな命令では、とりあえず()を要求するかたちにしておいた。)

また、現在の文法では、マクロはいわゆる関数ではなく、従って値を返すものではないのだけど、そのことを忘れてマクロで関数のようなものを定義していた箇所がいくつかあった(たとえば剰余算MODをマクロで定義していた)。これが使えなくなるとけっこう困るので、暫定的に「計算を行う」プリミティブ命令を追加し、それを「適用」する式を作ることで無理矢理対応したりもしている。

そんな感じで、実装する前に設計した文法は、かなり画餅だった部分があって、これからも多々変更されなければならなくなるだろうと思う。マクロをどう展開するかは、まだ闇の中だ。いずれにしろ、MML文法はかなり頭でっかちに考案していたこともあって、実装を書いてみると、いろいろ改善されて、面白いといえば面白い。

releases

| No Comments | No TrackBacks

とりあえず作業にひと段落つける意味で、mldspのリリースtarballを作った。実際には、mldspだけでなくportmidiのパッチ当てバージョンもtarballを作った。これはLinux専用なので、portmidi-sharpとは別の(既に動いている)パッチに基づいている。

本当はこれをさらにopensuse build service(名前はsuseだが実際には他のdistro向けのパッケージもビルドできる)を使ってパッケージにしたかったのだけど、作業の合間に外出することになって時間切れになったので、その後やっていない。tarballがあれば普通のLinuxユーザであれば何とかするはずだし。

portmidiのアーカイブが出来たところで、次はmldsp(-gtk)である。こっちはMonoDevelopのMakefile統合をある程度使って実現しているのだけど、いくつかハマり箇所があった。まず、そもそもMoonlight.Gtkのプロジェクトのサポートが無いので、Moonlightプロジェクトのように自動的にxapを作ってくれることがない。なので仕方なくzipを呼んでxapアーカイブを作成するスクリプトを作った。このスクリプトもアーカイブに含めなければならないので、csprojに追加した。

そしてビルドして実行したらxapが無いと言われる。仕方ないのでこれもcsprojに追加して、ビルド時に実行ディレクトリにコピーしてやるよう設定する。これが後でmake distでハマって、実はいまでも困っている。プロジェクトに追加したため、xap自身はビルドされるファイルであるにもかかわらず、make distして作成されたtarballから消えないのである。そして消す方法が無い。仕方ないのでこれはあきらめてそのまま入れることにした。

Makefile統合のもうひとつのハマり場所は、参照設定したtsukimiのdllのデバッグシンボル(*.mdb)がなぜかmake distでコピーされないことだ。仕方ないのでこれは生成されたMakefileをいじって手作業で追加した。どうせweb版のmldspで使われるtsukimi(これは依存ランタイム/ライブラリが2.1になるので、別アセンブリになっている)のアセンブリも手作業で追加しなければならないのだ。

そんな感じでmldspもめでたくtarballリリースが出来たので、以降こっちの作業はのんびりと進めようかと思う。

実のところ、Moonlight.Gtkにも問題があって、いまmldspを実行できるMoonlight.Gtkは存在しない。これはmoonのSystem.Windows.dllが誤ってplugin APIに依存するコードを追加してしまっているためだ(デスクトップ版でplugin依存のネイティブライブラリは呼び出せない)。そしてちょっと古いバージョンのmoonlightをビルドしようとすると、monoとmcsも同じくらい古くないと出来ない。でもそのためのmono/mcsのtarballは存在していない。これは困った問題なので修正してもらう予定。

MidiPlayerOverWeb?

| No Comments | No TrackBacks

我が家には通称餅楽ESと呼ばれるYamahaのラック音源があって、かなり高価な買い物だったにも関わらず、実は全然使っていなかったりする。製品の魅力に欠けるということは全く無くて、単にPCから接続するのが面倒なのだ。これではもったいないので、使おうと思っているのだけど、気軽に使えるように環境を整えられないかとちょっと考えている。

理想としては、手持ちのPCからMIDIメッセージを送信すると、それが何かしらの経路を伝って餅楽に伝達され、そこから発音された音声が何かしらの経路を伝って手持ちのPCからリモートでも訊くことが出来る、というのが望ましい。MIDI楽曲を再生する時はリアルタイムで楽曲のMIDIイベントを個別に処理していたらとても追いつかないので、楽曲をネットワーク経由で転送して、それを再生することが出来れば十分だろう(一時停止などが多少遅延するのは大きな問題ではない)。

自宅には稼働しているPCがあるので、これと繋ぎっぱなしのCometっぽいWebサービスをひとつ立てて(C/S間でduplexな接続が出来れば良い)、Webサービス経由でメッセージを受信してそれをMIDIデバイスに出力する(MIDI楽曲を受信したら演奏する)クライアントアプリケーションが一つあれば良い。こういうカスタムCometアプリケーションを実装するには、どうやらそのうち立ち上がるGoogle Waveなるサービスが便利そうだ。

一方、MIDI楽器から発せられた音声をWeb経由で受信する方は、MIDI楽器から出力された音声を、マイク入力を経てそのままネットワーク上にストリーム送信する必要がある。これはちょっと簡単には思いつかない。PCがMIDIデバイスからマイク入力を受け取って、そのままストリームをアップロードすることになるだろうか。Waveを使うことでやはり効率的に実現できるものだろうか。

とりあえず後者は「外でも聴けるようにする」ためのものでしかないので後でもいいかな。実のところ、オーディオ入力を受け取ってwifiに送りつけるという機器は販売されているのだけど、30000円くらいするような物ばかりなので、買おうという気にはなれない...もしかして作れちゃったりするのだろうか。それはそれでアリだけど。

リリースをどうするか考える

| No Comments | No TrackBacks

演奏終了時にキーボードをクリアするようにして、リングメーターを追加して、mldspにキーオンメーターとパンポットを追加したら、画面構成としてはかなりリッチになってきた。あとは

  • 疑似スペアナがダミー
  • LFOというかモジュレーションの表示がダミー
  • 音色/バンクの表示が必要。もともとパンポットの下に置くつもりだったのだけど、音色名の概念が存在しないFM音源などとは異なり、MIDI音色は番号と音色名の並列表記が望ましいので、空間的に足りない。
  • スキップ関連命令が未実装
  • パフォーマンス改善の余地が大いにある

といった問題がある。しかし、現時点でもうベータ版としてリリースするには申し分の無い出来だと思う。

しかしちょっとばかし困った問題がある。mldsp自体はreadyなのだけど、portmidi-sharpが前提とするportmidiのautotoolizationがまだちゃんと出来ていないこととと、dependencyであるtsukimiの完成度があまり高くないのであまりリリースしたくはないということだ。

tsukimi自体processingのコードをプリプロセスしてC#のコードに変換するだけのやっつけ設計なので、完成度もへったくれも無いという気はしなくもない。実はtsukimiを依存関係から切り捨てるのは難しくないのだけど、tsukimiを使って作ったんだよということ自体は宣伝したいので、切り捨てるとしても今すぐにではないと思っている。

というように、ややこしい問題があるのだけど、実のところ、Web版についてはブラウザから実行できるため、ソースをほしがる人がそんなにいるとも思えない。そうすると主な問題はMoonlight.Gtkバージョンの配布ということになるのだけど、それはつまり、portmidiのパッチもLinux版だけで事足りるだろうということになる。Debianには共有ライブラリ版が存在しているようだし、OpenSUSE Build Serviceの使い方をちょっと勉強して、共有ライブラリ版のパッケージを置いておけば足りるだろう。後は、tsukimi自体はもうmldspのリリースソースに含めてしまうという手があるかと思っている。

何となく方向性は見えてきたけど、やはりportmidiのパッケージングが一番面倒くさいな。全くやったことのないことだし。

演奏時間表示と早送りの実装

| No Comments | No TrackBacks

気分的に先送りにしていた現在位置の表示(プログレスバーと経過時間表示)を実装してみた。この前も書いたけど、一時停止した後の演奏時間の再計算が多少問題になる。プログレスバーはStoryboardを使って実装しているけど、経過時間の表示(とステップカウントの表示)にはDispatcherTimerを使っている。というのは、これらはTextBlockのTextを変更するものなので、DoubleAnimationのような便利なものが無いからだ。まあそれでも概ね問題なく実装できた。

実はステップカウントの表示の部分に多少問題があって、プレイヤで保持されている、楽曲中の「現在の」ステップカウントが、次の命令が来ない限り先に進まないのを無視して、これを表示している。そうするとCodaなどで全音符が並んでいるだけの部分では、ステップカウントが全く先に進まないということになる。時間で計算しても良いのだけど、この表示はそれはそれで便利なので、このままにしてみることにした。

ついでに早送りも実装してみた。本当は後回しにするつもりだったのだけど、先に出来てしまいそうだったので試しにやってみた。そしたら案の定ハマるハマる。テンポが変わったときは、Storyboardの初期値も変化時間も再設定しなければならない。演奏時間の表示のセマンティクスも自明ではない。少なくとも、(一時停止中を除いて)演奏開始からの時間を保持するか(時間としての正しい表示)、倍速再生中は時間の経過も倍速にするか(演奏位置としての正しい表示)の二択がある。前者はあまり意味が無いので、後者を選ぶことにした。この辺に選択の余地があったので、プレイヤ上に演奏開始からの時間を保持するのはやめた。(そもそもタイマを内部的に保持しなければならないことになるので、プレイヤ側で実装するのはあまり賢くない選択肢に思えた、ということもある。)

演奏時間を表示するようになって気づいたのだけど、このプレイヤ、それなりに遅延している。設計上軽くはならないので仕方ないが、そのうち最適化しなければならなくなるだろう。

less code, less bugs

| No Comments | No TrackBacks

最近は不本意ながらMac上のMonoDevelopでmldspのコードを書いていることが多い。この環境でLinux環境がまだまだ実用的でなく(ACPIまわりにバグがあってkernelのfix待ちという状態なので、どんなdistroの最新カーネルでも無理なはず)、Silverlightアプリのレベルであれば十分にビルドもテストも出来るので(テストはSilverlight上で行われることになるけど、Moonlightはかなり出来が良いので、Linux環境に持っていって動かなかったことは1度しか無い)、それに甘んじているという状態だ。

これまで、そのMac環境でmldspを動かした時は、ちょっと困った問題があって、最初にファイルを選択した時は概ね動かない(けどたまに動く)という現象があった。Windows上では逆に概ね動く(けどたまに動かない)という状態だった。

さっきLinux環境に持っていったら、いろいろプレイヤまわりのコードをいじったせいか、演奏してくれなくなったので、プレイヤの状態管理まわりを整理しようと思っていろいろ改善を施した。基本的には非同期プレイヤは内部の同期プレイヤの状態をそのまま見ることにした。そうしたらなぜか上記の問題が一気に無くなって、プレイヤとしての安定性はかなり向上した。相変わらずUI上は停止命令を受けてもクリアされないが、演奏→停止→演奏などの操作もきちんと扱えるようになった。

コードが少なければ少ないほど、バグも減るという好例だ。

mldspにステータスパネルを追加して、演奏コントロールもできるように改造してみた。ファイルを選択して再生する部分は今までもあったけど、ここに一時停止と停止が付く。ただ停止に対応するUIのリセットがまだ出来ていない。難しくはないのだけど面倒でやっていない。ひとつには、キーボードまわりだけまだprocessingのコードを引きずっていて、ちゃんと制御するように出来ていないという話がある。これはまあ、やる気の問題だ。

テンポとトータル演奏時間も表示するようにしてみた。テンポは実際にmetaイベントで送られてくる値は意味不明過ぎるので、シーケンサ等でBPM指定して、SMFファイル生成時にmetaイベントのテンポ指定の値に変換していた(であろう)ものを、BPMに逆変換してやらないといけない。

画面上に乗っかる主な部品としては、あとスペアナとキーオンメータ(ここに音色とパンが一緒に表示される)、それと(デスクトップ向けには)ファイルセレクタ、くらいではなかろうか。ファイルセレクタはWeb版ではあまり意味が無いのだけど。isolated storageに保存しておけば良いだろうか。

ステータスパネルでは、演奏時間の経過表示も実装しなければならないのだけど、この辺から、少し状態管理もややこしくなる。ユーザからの入力を画面に反映させる部分と、楽曲からのイベントを画面に反映させる部分と、時間経過によって自動的に更新されなければならない部分があって、後2者は実のところまだ出来ていない。演奏時間は、GUI上でアニメーションするのが一番簡単なのだけど、一時停止中は更新されないことになるし、テキストで演奏時間を表示するやつは、一時停止を考えると演奏開始時間を使った単純な引き算ではまずいので、これはプレイヤ実装の方で管理した方がいいだろうと思っている(これが一時停止なども把握している)。

話をさらにややこしくするであろうものが、ジャンプと早送り/巻き戻し機能だ。MIDIデータを作っている時は必須に近い機能なので、実装するつもりはある。あと今UI上には存在していないのだけど、スロー再生とスキップも実装できるだろう。

もう少し整理して書くなら、早送りとスロー再生はテンポ計算の補正値の問題で、スキップと巻き戻しはジャンプの応用だ。前2者は実のところプレイヤ上にテンポ補正を実現するコードがあるので、今すぐにでも出来る。後者は小節単位でやるのが望ましいだろうが、いずれにしてもスキップの応用なのでスキップの実装が出来てからだ。

スキップは、プレイヤ上は、スキップ先の絶対時間(マイクロ秒なりステップカウントなり)を入力として受けて、フォーマット0のかたちでマージされたシーケンスの先頭からそこまでのイベントの時間を計算しつつスキップする、というやり方でいけるだろうが、その間のイベントを全部スキップするわけにはいかない。音色やピッチベンドの変更を保持しておく必要がある。しかも全ての変更をバカ正直に転送するわけにはいかないので(ピッチベンドやエクスプレッションは、リアルタイムでも短いステップで大量に送信されることが多く、それがスキップで全部押し寄せたら、音源側が処理しきれない)、仮想マシンで最後に格納されたレジスタ値を送信する、というやり方になるだろう。

スキップを実際に処理する前に、一時停止してオールサウンドオフしておく必要もある。ただし、一時停止状態でスキップを実行したら、その後勝手に再開しない方が望ましいし、演奏中にスキップを実行していたら、そのまま再開するべきだろう...という状態の遷移を考えると、スキップの準備として一時停止することで状態を壊さない必要もある。この辺の実装はプレイヤ上で行うのが妥当だろう。

GUI上は、スキップ命令を受信したら(これは、おそらく演奏状態バーの上をmouseupしたタイミングで行われる)、GUI上のキーオン状態を全てクリアしてから、プレイヤ上でスキップを実行する。その後、演奏時間関係のプロパティ(テンポ、演奏カウント、経過時間など)を再表示する。

...と、まあこういう実装にすれば、概ね解決するような気はしている。まあ、ジャンプ以外はニッチなので、後回しにする可能性が高いけど。

週の後半は進まない仕事に押されてこっちの作業がはかどらなかったので、今日ちょいと気合いを入れてパラメータのビジュアライザを追加した。とりあえず、volume, expression, リバーブ、コーラスは表示できている。まあテキスト値を更新するだけなので、大したことはやっていないけど。

その中でホールドのon/offが出てきたので、hold onの時はキーボードに別の色を付けて余韻を残すようにしてみた。TMIDIがやっているのと同じことだ。ちなみにこの上にさらに、ポリフォニックアフタータッチと、ポルタメントのキースライダが載る予定になっている(これはtmidiにはないmmdspの視覚効果だ)。PAfなんて自分では使わないので嬉しいのかどうか分からないけど。

Pause and Stop, w/o VM

| No Comments | No TrackBacks

今日は昨日のデスクトップ プレイヤーのために多少コードを切り分けたのを、さらに押し進めて、PortMidiSharpに依存するメッセージ出力機能を、単独で演奏を行うMidiPlayerから完全に切り離して、MidiPlayerにイベントフックを仕掛けるだけのものにした。コードはこれでかなりすっきりして、web版をデスクトップ版から完全に切り離して、かつ変更もスマートに共有できるようになった。

今日はこれだけで終わるかと思っていたけど、眠れなかったので、昨日の構想に基づいて、仮想レジスタマシンを作ろうと思って、とりあえずMIDI規格のメッセージ一覧を眺めて、ステータスを記録するべきものをリストアップする作業をやった。途中でCCの膨大なリストを全部拾い集めるのが面倒になって、単純に構造体にした時の配列のスロットだけ確認するかたちにした。

その途中で気がついたのだけど、コントロールチェンジにはモードメッセージというのがあって、オールサウンドオフすることができる。これを使えば一時停止はすぐ実装できるのではないかと思って、試しにPauseで 0xBn 0x78 0x00 を全チャネルに送ってみたら、あっさり停止できた。ということは...VMはこの目的では不要だったわけである。ついでに一時停止と停止まわりの処理がきちんと出来ていなかったので、一時停止や停止できちんと消音して終わるようにはした。

VMは今回は不要だったわけだけど、TMIDI Playerみたいに、画面上からコントロール パラメータを変更したい場合には、いずれにせよ記憶しておく必要があるので、これは実装することにする。

→実装してみた。RPN/NRPNだけtodoとして残っているけど。playerはイベントでVMのメッセージ設定を呼び出し、VMはportmidiにMIDIメッセージを送る、という仕組みになって、もしかしたらそろそろパフォーマンスの問題になるかもしれないな...

mldsp on Moonlight.Gtk

| No Comments | No TrackBacks

Silverlightアプリケーションとして作っていたmldspを、Moonlight.Gtkに乗っかるようにいろいろ手を加えて、何とかMIDI音源に出力しながら画面表示できるところまでもっていくことができた。

20090603131531.png

外観上はprocessing版とあまり変わらないような気もするけど、コレはC#で書いた演奏ロジックと連動して、きっちり演奏もやってくれる。processingでもsoundcipherとかいうライブラリを使うとjavax.sound.midiを使ってMIDI音源にアクセスできるみたいだけど、processingでプレイヤUIのややこしいロジックを書こうという気はなかなか起こらない。

Moonlight.Gtkまわりは、以前とは使い方がいろいろ変わっていてサンプルがビルドできないこともあるし、多少tipsがいるので、それはそのうち書こうかと思っている。

とりあえず、これで、Moonlightを使ってMIDIファイルを再生するという、mldspの最初の目的は達成した。と書くと長い時間をかけていたように思えてしまうが、よく考えたらprocessingのお絵描きを超えてmldspのコードを実質的に書き始めたのは昨日なので(それはむしろtsukimiのdogfoodingに近い)、2日で出来てしまったことになる。良いペースだ。

プレイヤのUIをいろいろ作り込むのも楽しそうだけど、とりあえず鍵盤が動いているだけでだいぶ面白いので、次は演奏状態の制御(先日書いた停止命令など)を整えたいと思う。というか、MIDIデバイスのモデルを作るのが楽しそうだ。

mldsp plays SMF

| No Comments | No TrackBacks

ようやく重い腰を上げて、mldspが演奏モニタになるよう、tsukimiで作ったプロトタイプを手書きのコードで崩して作ってみた。まだSilverlight上でしか動作確認できていないのだけど。

http://veritas-vos-liberabit.com/tmp/2009/mldsp/

最初にファイルを選択してもなぜか動かないことがほとんどだが(たまに動くのが謎)、懲りずにもう一度選択するとMIDIファイルの再生が始まる...といっても音は鳴らない(!)。音が鳴るのはあくまでMoonlight.Gtkでデスクトップ アプリケーションにした場合のみだ。

キーボードが動くだけでも、何となく動作しているような気分になって嬉しいものだ。

画面設計はMMDSPなどを意識して作っているけど、MIDIプレイヤはFM音源のプレイヤとはだいぶ性格が異なるので、おっさんホイホイにするために同じような色合いにすることはあっても、同じような表示項目が並ぶことは多分無いと思う。

いろいろ設計として考えていることはあるのだけど、その辺はおいおい実装しながら公開していこうと思う。(と書くともったいぶっているみたいだけど、そんなに大したことは考えていない。)

追記: moonlightでも動作するよう微修正した。アプリケーションのコンストラクタの時点で、まだHtmlPage.Windowが取得できない問題があったので、DispatcherをHtmlPage.Windowから取得する部分だけ書き換えた。

About this Archive

This page is an archive of entries from June 2009 listed from newest to oldest.

May 2009 is the previous archive.

July 2009 is the next archive.

Find recent content on the main index or look in the archives to find all content.

Categories

Pages

OpenID accepted here Learn more about OpenID
Powered by Movable Type 4.23-en