sound channel と sound Handle 廃棄の順番

/**************************** Pascal code version ****************************************/ void MyCheckSndChan( void ) { const Boolean kQuietNow = true; // need to quiet channel? OSErr myErr; if( gCallbackPerformed ) // check global flag { // channel is done gCallbackPerformed = false; // reset global flag if( gSndChan->userInfo != 0 ) { HUnlock( (Handle)(gSndChan->userInfo) ); HPurge( (Handle)(gSndChan->userInfo) ); } myErr = MyDisposeSndChannel( gSndChan, kQuietNow ); gSndChan = nil; // set pointer to nil } } Apple Document の Pascal code 翻訳版では、sound Handle ( userInfo に入っている)を Purge してから、SndChannel を Dispose しています。

/**************************** My recommendable version ****************************************/ void MyCheckSndChan( void ) { OSErr err; Handle mySndHandle; if( gCallbackPerformed ) // check global flag { // channel is done gCallbackPerformed = false; // reset global flag mySndHandle = (Handle)(gSndChan->userInfo); //save handle before DisposeChannel if( gSndChan != nil ) { err = SndDisposeChannel( gSndChan, true ); //quietNow if( err != noErr ) DoError( err, "\pSndDisposeChannel failed", false ); gSndChan = nil; // set pointer to nil } if( mySndHandle != nil ) { HUnlock( mySndHandle ); DisposeHandle( mySndHandle ); } } } これに対し私が推奨するコードでは、userInfo に入っている Handle をあらかじめ退避させた上で、SndChannel を Dispose してから、sound Handle を Dispose しています。

先に SndChannel を Dispose する理由

問題は短時間に MyStartPlaying() を連続して呼ぶときに起ります。Sound Manager 全体が機能不全に陥るというバグをしばしば経験しました。

最初の音が鳴り終らないうちに次の MyStartPlaying() が呼ばれると、すぐに MyStopPlaying() 経由で MyCheckSndChan() に制御がわたされます。
「 SndDisposeChannel() を実行すると、まず flushCmd つぎに quietCmd がチャンネルに送られる」と Apple Document には解説されています。
Pascal code ように sound Handle を先に廃棄してしまうと、quietCmd がいざ実行されたときに止めるべき sound がどこにも無く、 その結果 Sound Manager が機能不全に陥るのではないかと思います。

私が推奨するコードでは、これを避けるために順序を逆にしたというわけです。

mySndHandle を Purge でなく Dispose する理由

確証はないのですが CarbonLib を使わない MacOS 8 や Mac OS 9 環境では、Purge でもDispose でも問題は無いように思えます。 しかし Mac OS 9 であれ Mac OS X であれ Carbon 環境の下では、Purge だけではメモリリークが観測されます。

メモリリークをなくすには DisposeHandle() するしかないようです。


戻る