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() するしかないようです。
戻る