テクニカル Q&A
SND19 - 音声を映像に同期させる方法 (1999 年 10 月 5
日)
Q: Mac OS 9.0 と Sound Manager 4.0
では、自作のサウンドコールバックが定期的に呼び出されなくなりました。そうなると、音声を映像に同期させるのがたいへん困難です。サウンドコールバックが定期的に呼び出されるようにするにはどうしたらいいでしょうか。
A: Sound Manager 4.0 の大きな変更点は、DMA
エンジン、ひいてはハードウェアに音声を渡すとすぐにコールバックが呼び出されること、そして
DMA
バッファのサイズが増えたことです。つまりコールバックが以前より少し早く呼び出されることになります。ハードウェアバッファ
(この場合は DMA バッファ)
より小さいバッファを使っていると問題は深刻です。
ハードウェアバッファより少ない音声データを持つバッファは、ハードウェアバッファに完全にコピーされてしまいます。この時点で音声出力は「完了」とみなされ、サウンドチャネルキューの次のコマンドが呼び出されます。通常、キューにある次のコマンドは、callBackCmd
です。コールバックが呼ばれたときに音声出力は完了したものと期待していると、おそらくまだ完了しておらず、問題にまきこまれることになります。
解決方法は簡単です。再生するバッファのサイズを増やしてください。最低でもハードウェアバッファのサイズまでバッファのサイズを増やします。ほとんどの場合、ハードウェアバッファの
2 倍にするのがいいでしょう。
現在、Sound Manager 4.0 では、VM
がオンでないとバッファのサイズは 512 サンプル、VM がオンだと
4096 サンプルです。
しかし、将来的に変更されう可能性がありますから、これらの値を前提にしてはいけません。これらの値は、次のコードを使って、ハードウェアに問い合わせてください。
/*
出力バッファのサイズをバイト数で返す
*/
static long GetSoundOutputBufferSize (Component outputDevice,
short sampleSize, short numChannels,
UnsignedFixed sampleRate) {
SoundComponentData outputFormat;
OSErr err;
SndChannelPtr chan = nil;
SndCommand cmd;
ExtSoundHeader sndHeader;
long bufSize = 0;
err = SndNewChannel (&chan, 0, 0, nil);
sndHeader.samplePtr = nil;
sndHeader.numChannels = numChannels;
sndHeader.sampleRate = sampleRate;
sndHeader.loopStart = 0;
sndHeader.loopEnd = 0;
sndHeader.encode = extSH;
sndHeader.baseFrequency = kMiddleC;
sndHeader.numFrames = 0;
sndHeader.markerChunk = nil;
sndHeader.instrumentChunks = nil;
sndHeader.AESRecording = nil;
sndHeader.sampleSize = sampleSize;
sndHeader.futureUse1 = 0;
sndHeader.futureUse2 = 0;
sndHeader.futureUse3 = 0;
sndHeader.futureUse4 = 0;
sndHeader.sampleArea[0] = 0;
// Sound Manager はこの値を無視するので実際には不要
UnsignedFixedTox80 (sampleRate, &sndHeader.AIFFSampleRate);
// 問合せのためサウンドチャネルをセットアップ
cmd.cmd = soundCmd;
cmd.param2 = (long)&sndHeader;
err = SndDoCommand (chan, &cmd, true);
if (err == noErr) {
err = GetSoundOutputInfo (outputDevice, siHardwareFormat,
&outputFormat);
}
#if DEBUG
if (err != noErr) {
printf ("Got error #%d trying to do GetSoundOutputInfo with
siHardwareFormat\n\n", err);
} else {
bufSize = outputFormat.sampleCount * (sampleSize / 8) * numChannels;
printf ("Sound output buffer is %d samples, ", outputFormat.sampleCount);
printf ("which is %d bytes.\n\n", bufSize);
}
#endif
return (bufSize);
}
GetSoundOutputInfo
呼び出しが失敗した場合の次善の策として、音声出力バッファのサイズは、音声入力バッファのサイズと同じと仮定することができます。入力バッファのサイズは次の呼び出しで取得できます。
/*
入力バッファのサイズをバイト数で返す
*/
static long GetSoundInputBufferSize (UInt32 siRefNum) {
OSErr err;
long inputBufferSize;
err = SPBGetDeviceInfo (siRefNum, siDeviceBufferInfo, &inputBufferSize);
#if DEBUG
if (err != noErr) {
printf ("\nGet device buffer info error, err: %d\n", err);
} else {
printf ("\nSound input buffer is %d bytes\n", inputBufferSize);
}
#endif
return (inputBufferSize);
}
-- Mark Cookson
Worldwide Developer Technical Support
テクニカル Q&A | 目次
To contact us, please use the Contact
Us page.
|