部分XAudio2的方法中含有OperationSet参数,这个参数能决定是否延迟执行这些方法(仅添加一个挂起的操作)。然后在某个特定的时间通过调用XAudio2::CommitChanges,并将它的OperationSet参数指定为目标操作的ID,这样就能让XAudio2组件真正执行对应的操作。这个ID没有特殊要求,只要互不相同即可,因此可以用一个全局计数器来作为它们的ID,每次使用后增加计数即可。这样一来,不同代码就可以互不冲突地提交修改(全局计数器同时也可用于区分提交修改的时间先后)。
同时,以原子方式提交的多个操作被确保是以采样率匹配方式混音的(一个混合音轨方面的术语,防止混音时因采样率的不准确而导致播放时长的变化,详见这里)。比如,声音的播放将会是同步的。另一方面,如果用XAUDIO2_COMMIT_NOW(值为0)作为OperationSet参数的值,改动将立即生效。如果以XAUDIO2_COMMIT_ALL(值也为0)参数调用CommitChanges,则所有挂起的操作都将生效,这时OperationSet的ID将被忽略。
例如,分别调用
pSourceVoice1->Start(0, 1); pSourceVoice2->Start(0, 2); // 这时声音还未播放 pXaudio2->CommitChanges(XAUDIO2_COMMIT_ALL); // 声音1、2同时播放 //pXaudio2->CommitChanges(1); // 仅播放声音1 //pXaudio2->CommitChanges(2); // 仅播放声音2 pSourceVoice3->Start(); // 声音3将立即播放(Start方法的两个参数默认值均为0)
在XAudio2的回调函数中用XAUDIO2_COMMIT_NOW调用一些方法时,操作会立即生效,而其他用XAUDIO2_COMMIT_NOW调用的方法将在方法返回后的下一个处理时机才会生效,或者等到用相同的OperationSet值调用CommitCahnges时生效。因此调用的生效顺序并不总是与调用顺序相等。
所有挂起的操作在IXAudio2::StopEngine被调用时将以原子方式提交。任何在引擎停止时调用的方法都会无视OperationSet参数而立即生效(以同步模式)。当你重新启动引擎时,XAudio2会恢复异步模式。
// 但我在测试时发现在调用StopEngine后 // 以挂起方式调用的方法并没有立即生效,不知是不是我的理解有错,代码如下 hr = pSourceVoice->Start(0, 0); Sleep(4000); pXaudio2->StopEngine(); wprintf(L"Engine Stopped!\n"); Sleep(4000); hr = pSourceVoice->SetVolume(0.1f, 1); //hr = pSourceVoice->SetVolume(0.1f, 0); // 如果以XAUDIO2_COMMIT_NOW调用则重启引擎时已经生效 Sleep(4000); pXaudio2->StartEngine(); wprintf(L"Engine Restart\n"); Sleep(4000); pXaudio2->CommitChanges(1); // 修改音量的操作直到此时才生效,而不是在重启引擎时已经生效
以下为操作集的几个简单场景下的有效应用:
1、同时播放多个声音。
2、同时提交声音Buffer(SubmitSourceBuffer),设置声音参数,播放声音。
3、对音频进行大规模的修改参数,如把所有source voices连接到一个新的submix voice上。