2017年09月16日

Alchemusica のリビルド詳細

 Alchemusica のビルド環境を Xcode 3.2.6 → 8.2.1 に変えるため、あちこち手を入れた。SDK は 10.5 → 10.12, deployment target は 10.6(10.5 用のビルドはできなかった)。あまり必要ない気もするけど、作業内容を一応メモしておく。

 Cocoa 関連の API 変更への対応。

  • NSImagecompositePoint 系を drawInRect に置き換え。respectFlipped というパラメータがある。これを使えば、NSImagesetFlipped (deprecated) を使わなくてもよい。
    [[MyPopUpButton triangleImage] compositeToPoint: NSMakePoint(theRect.origin.x + theRect.size.width - 7, theRect.origin.y + theRect.size.height - 2) operation: NSCompositeSourceAtop fraction: fraction];
    →
    NSRect r;
    r.origin.x = theRect.origin.x + theRect.size.width - 7;
    r.origin.y = theRect.origin.y + theRect.size.height - 7;
    r.size.width = 5;
    r.size.height = 5;
    [[MyPopUpButton triangleImage] drawInRect:r fromRect:NSZeroRect operation:NSCompositeSourceAtop fraction:fraction respectFlipped:YES hints:nil];
    
  • NSWindowcacheImageInRect などが deprecated。NSViewbitmapImageRepForCachingDisplayInRect:, cacheDisplayInRect:toBitmapImageRep: を使えば代用できるが、結局キャッシュする際も各サブビューの drawRect: を呼び出しているので、それなら普通に描画しても一緒じゃん、ということで、キャッシュイメージを使わない方法で書き直してしまった。
  • NSFontdefaultLineHeightForFontdeprecated. これには定石があって、NSLayoutManager のインスタンスを一つ作っておいてそれに問い合わせる。NSWindowController の拡張クラスメソッドとして sharedLayoutManager を実装した。
    [font defaultLineHeightForFont]];
    →
    [[NSWindowController sharedLayoutManager] defaultLineHeightForFont:font];
    

 それから、CoreAudio 関連の API 変更に対応。一番大変なのは、AUMIDIController 関連の API の変更だった。10.11 では警告は出るものの一応動いているのだが、だいぶ前から deprecated なので、この機会に大修正。AUMIDIController は、MusicDevice を元に作成して、CoreMIDI から見えるようにするもの。CoreMIDI のイベントスケジュール機能が使えるので楽だった。これを使わないとなると、MusicDeviceMIDIEvent(), MusicDeviceSysEx() を使って、自分でイベントのスケジューリングをしないといけない。

 まず、MusicDeviceRenderNotify コールバック関数を作って、それを登録する。

static OSStatus
sMDAudioSendMIDIProc(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
{ ... }
/*  登録方法  */
CHECK_ERR(result, AudioUnitAddRenderNotify(ip->unit, sMDAudioSendMIDIProc, ip));
/*  登録解除方法  */
CHECK_ERR(result, AudioUnitRemoveRenderNotify(ip->unit, sMDAudioSendMIDIProc, ip));

 このコールバック関数の中で、MusicDeviceMIDIEvent() を使ってイベントを送る。

OSStatus MusicDeviceMIDIEvent(MusicDeviceComponent inUnit, UInt32 inStatus, UInt32 inData1, UInt32 inData2, UInt32 inOffsetSampleFrame);

 inOffsetSampleFrame は、コールバック関数に渡される inTimeStamp からのオフセット。これで、発音を開始するタイミングを指定することができる。サンプリング間隔の整数倍しか指定出来ないのが残念。ここは double で設計して欲しかった。(どうせ内部的には補間処理をしているはず)

 また、MusicDeviceSysEx() にはこの引数がない。SysEx を使って発音を制御する場合もあるので、ここもオフセットが指定できるように設計すべきだったと思う。

OSStatus MusicDeviceSysEx(MusicDeviceComponent inUnit, const UInt8 *inData, UInt32 inLength);

 コールバック関数は CoreAudio から呼ばれる。一方、MIDI イベントを送る関数は独立した pthread から呼ばれる。間にリングバッファを置いて、送り側からタイムスタンプ+MIDI データをバッファに書き出し、コールバック関数ではそのデータを取り出して処理するようにする。送り側の pthread はだいたい 0.1 sec 間隔ぐらいでまとめて処理しているので、リングバッファが一杯になることがある。そのとき、送ろうとしたMIDIイベントを取り下げて、次の処理のときにもう一度送れるようにしないといけない。また、コールバック関数の方は、基本的にはオーディオバッファ1回分の処理しかしないので、リングバッファ中のイベントがずっと先のタイムスタンプを持つ場合には、そのイベントを待たせないといけない。このあたりの処理がちょっとあいまいだったので、CoreMIDI を使うところも含めてかなり書き直した。

 一応 32bit では動くようになった。喜ばしいことに、前におかしかった SoundCanvas VA もちゃんと動作するようになった。Alchemusica 側で何か変な処理をしていたに違いない。

 64bit に移行しようとしていろいろ苦労したが、結局断念した。断念したのは、CoreAudio プラグインの CarbonView が表示できないんじゃないか、という懸念から。やってみたらできたのかもしれないけど、そのために必要な作業量を考えると、別に 32bit のままでいいんじゃない、と思った。10.11 でも 32bit で普通に動いてるしな。32bit アプリが一切動かなくなってから考えてもいいだろう。

タグ:Mac Alchemusica
posted by toshinagata at 20:04| 日記

2017年09月11日

Alchemusica リビルド:Sound Canvas VA ちゃんと鳴った!

 週末頑張って、Alchemusica を改訂しました。嬉しい効果は、Sound Canvas VA がちゃんと鳴ったこと!

 だいぶ前に「Sound Canvas VA の不具合」と称して書き込みをしたのだが、結局 Alchemusica の問題でした(同じデータで他のプラグインだと変な症状は出ないので、Alchemusica だけの問題とも言い切れないんだけど…)。ローランドさんごめんなさい。当該記事にも修正を入れておきます。

 なお、上の動画は、Alchemusica の出力を SoundFlower に設定して、QuickTime Player の「新規画面収録」でサウンド入力を SoundFlower にし、Alchemusica の画面を切り出して録画した。SoundFlower はいろいろなバージョンがあるので、自分の OS バージョンにあったものを探しましょう。"Yosemite/Mavericks/El Capitan/Sierra" 用バージョンが上のリンクからダウンロードできます。

posted by toshinagata at 00:08| 日記

2017年09月09日

Alchemusica をリビルド中

 Mac OS 10.11 にしてから Alchemusica の動作がおかしかったので、Xcode 8.2 でビルドし直している。一番変だったのは、ピアノロール画面で演奏中に演奏位置を示すマーカーが、通過した後に画面を乱してしまうこと。これは一部ロジックを書き換えて復旧した。それから、なるべく警告を減らすように修正。

 難敵なのは、AUMIDIController 関連の API。これは、10.5 から deprecated になっている。現在、32 bit だとまだ実装されているのだが、64 bit だと実装すらされていないため、64 bit でビルドするとコンパイルが通らない。仕方なく、今は 32 bit 限定でビルドしている。MusicDeviceMIDIEvent(), MusicDeviceSysEx() を使って、MIDI 演奏部分をかなり大幅に書き換える必要がある。これは後回しだな。

タグ:Mac DTM
posted by toshinagata at 00:58| 日記