2

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

我在真实设备上尝试过AudioServicesPlaySystemSound但没有成功(可能在模拟器中工作)。AVAudioPlayer

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

提前致谢!

4

4 回答 4

2

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

于 2012-11-20T16:34:32.873 回答
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;
}
于 2013-01-16T04:16:48.897 回答
0

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

于 2013-01-10T14:12:39.617 回答
0

我有一个类似的问题。在 VOIP 应用程序中,我必须在通话进行时播放提示音。使用 AVPlayer 播放音调文件将通过接收器以 50% 的音量播放。

对我有用的是在音调文件的持续时间内AVAudioSessionCategoryPlayAndRecord来回切换 AVAudioSession 类别。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
于 2021-01-11T22:51:38.060 回答