与AudioUnit一起播放声音文件

时间:2012-11-20 11:40:11

标签: ios core-audio avaudioplayer audiounit

我有一个使用AudioUnit子类型kAudioUnitSubType_VoiceProcessingIO的应用。输入来自网络流。这很好,但现在我想从文件播放一个简单的声音(收到推送通知后发出警报声)。当AudioUnit处于运行状态时,这不起作用,只有当它尚未启动或已经处置时。我甚至可以在声音文件静止播放时停止AudioUnit以听取最终样本,因此播放似乎有效,但它是静音的。

我在真实设备上尝试AudioServicesPlaySystemSoundAVAudioPlayer但未成功(可能在模拟器中有效)。

这个简单的任务是否有简单的解决方案,还是我需要手动混合基于文件的音频内容?

提前致谢!

4 个答案:

答案 0 :(得分:2)

您可以使用音频单元构建AUGraph并通过kAudioUnitSubType_AudioFilePlayer单元播放音频文件。

答案 1 :(得分:2)

尝试

OSStatus propertySetError = 0;
UInt32 allowMixing = true;

propertySetError = AudioSessionSetProperty (
                       kAudioSessionProperty_OverrideCategoryMixWithOthers,  // 1
                       sizeof (allowMixing),                                 // 2
                       &allowMixing                                          // 3
                   );

或者您可以使用kAudioUnitType_Mixer类型AudioUnit。

AudioComponentDescription MixerUnitDescription;
MixerUnitDescription.componentType          = kAudioUnitType_Mixer;
MixerUnitDescription.componentSubType       = kAudioUnitSubType_MultiChannelMixer;
MixerUnitDescription.componentManufacturer  = kAudioUnitManufacturer_Apple;
MixerUnitDescription.componentFlags         = 0;
MixerUnitDescription.componentFlagsMask     = 0;

将它们(mixer_unit,voice_processing_unit)添加到AUGraph中。为混音器单元设置输入总线计数2。

UInt32 busCount   = 2;    // bus count for mixer unit input
status = AudioUnitSetProperty (mixer_unit,
                               kAudioUnitProperty_ElementCount,
                               kAudioUnitScope_Input,
                               0,
                               &busCount,
                               sizeof(busCount)
                               );

为mixer_unit的每个总线添加渲染回调:

for (UInt16 busNumber = 0; busNumber < busCount; ++busNumber) {
        AURenderCallbackStruct inputCallbackStruct;
        inputCallbackStruct.inputProc        = &VoiceMixRenderCallback;
        inputCallbackStruct.inputProcRefCon  = self;

        status = AUGraphSetNodeInputCallback (_mixSaveGraph,
                                              mixerNode,
                                              busNumber,
                                              &inputCallbackStruct
                                              );
        if (status) { printf("AudioUnitSetProperty set callback"); return NO; }            

    }

将mixer_unit的输出总线连接到io_unit的输入总线:

status = AUGraphConnectNodeInput (_mixSaveGraph,
                                      mixerNode,         // source node
                                      0,                 // source node output bus number
                                      saveNode,          // destination node
                                      0                  // desintation node input bus number
                                      );

启动图表,您将使用不同的总线编号(0和1)回读读者,如下所示:

static OSStatus VoiceMixRenderCallback(void *                       inRefCon,
                                  AudioUnitRenderActionFlags    *ioActionFlags,
                                  const AudioTimeStamp *        inTimeStamp,
                                  UInt32                        inBusNumber,
                                  UInt32                        inNumberFrames,
                                  AudioBufferList *             ioData )
{
    OSStatus status;
    AURecorder *recorder = (AURecorder*)inRefCon;
    if (inBusNumber == BUS_ONE && (recorder.isMusicPlaying)) {
        //Get data from music file. try ExtAudioFileRead 
    } else if (inBusNumber == BUS_TWO) {
        status = AudioUnitRender(recorder.io_unit, ioActionFlags, inTimeStamp, INPUT_BUS, inNumberFrames, ioData);
       //Get data from io unit's input bus.
        if (status)
        {
            printf("AudioUnitRender for record error");
            *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
        }
    } else {
        *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
    }

    return noErr;
}

答案 2 :(得分:0)

这可能是系统限制。如果在使用PlayAndRecord类别时调用AudioServicesPlaySystemSound时iOS设备没有执行任何操作,请尝试先停止AudioUnit,然后再调用此函数。

答案 3 :(得分:0)

我遇到了类似的问题。在 VOIP 应用程序中,我必须在通话过程中播放音调。使用 AVPlayer 播放音调文件将通过接收器以 50% 的音量播放。

对我有用的是在音调文件的持续时间内将 AVAudioSession 类别从 AVAudioSessionCategoryPlayAndRecord 切换到 AVAudioSessionCategoryPlayback 并返回。

这里有几个问题:

  1. 您必须在要播放的音频文件的持续时间内切换。
  2. 您的 AudioUnit 或输入处理代码应该可以在切换期间保持静音。
  3. 类别切换不是免费的。请注意,旧设备可能需要更长时间才能打开和关闭麦克风。

我认为发生的事情是扬声器被专门分配给 AudioUnit,任何使用 AVAudioPlayer 或 AVPlayer 的并行播放都通过接收器播放。如果您的音频文件的电平较低,则几乎不会引起注意。此外,当 AudioUnit 处于活动状态且 AVAudioSession 处于 PlayAndRecord 类别时,其他声音文件将被“躲避”。

您可以在连接设备时使用 Mac 的控制台确认这一点,搜索 mediaserverd 和 grep 以获取“duck”。

我得到了这样的东西:

default 18:36:01.844883-0800 mediaserverd -CMSessionMgr- cmsDuckVolumeBy: ducking 'sid:0x36952, $$$$$$(2854), 'prim'' duckToLevelDB = -40.0000 duckVolume = 0.100 duckFadeDuration = 0.500
default 18:36:02.371953-0800 mediaserverd -CMSystSounds- cmsmSystemSoundShouldPlay_block_invoke: SSID = 4096 with systemSoundCategory = UserAlert returning OutVolume = 1.000000, Audio = 1, Vibration = 2, Synchronized = 16, Interrupt = 0, NeedsDidFinishCall = 0, NeedsUnduckCall = 128, BudgetNotAvailable = 0
default 18:36:02.372685-0800 mediaserverd SSSActionLists.cpp:130:LogFlags: mSSID 4105, mShouldPlayAudio 1, mShouldVibe 1, mAudioVolume 1.000000, mVibeIntensity 1.000000, mNeedsFinishCall 0, mNeedsUnduckCall 1, mSynchronizedSystemSound 1, mInterruptCurrentSystemSounds 0
default 18:36:02.729872-0800 mediaserverd ActiveSoundList.cpp:386:SendUnduckMessageToCM: Notifying CM that sound should unduck now for ssidForCMSession: 4096, token 3569
default 18:36:02.729928-0800 mediaserverd -CMSystSounds- cmsmSystemSoundUnduckMedia: called for ssid 4096
default 18:36:02.729973-0800 mediaserverd -CMSessionMgr- cmsUnduckVolume: 'sid:0x36952, $$$$$$(2854), 'prim'' unduckFadeDuration: 0.500000