【问题标题】:Play sound file together with AudioUnit与 AudioUnit 一起播放声音文件
【发布时间】:2012-11-08 11:21:18
【问题描述】:

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

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

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

提前致谢!

【问题讨论】:

    标签: ios core-audio avaudioplayer audiounit


    【解决方案1】:

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

    【讨论】:

    • 感谢您的回复,奇怪的是没有更简单的解决方案。我最终在 Apples MixerHost example 的帮助下使用 ExtAudioFileOpenURL + ExtAudioFileRead 将文件读入 AudioBuffers。然后我使用这个准备好的缓冲区来播放警报声音而不是网络流。目前我不使用混音器单元。这将是一个完美的解决方案,但对于我短暂的警报声来说,这不是必需的。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      试试

      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;
      }
      

      【讨论】:

        【解决方案4】:

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

        对我有用的是将 AVAudioSession 类别从 AVAudioSessionCategoryPlayAndRecord 切换到 AVAudioSessionCategoryPlayback 并在音调文件期间切换回来。

        这里有几个问题:

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

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

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

        我得到了这样的东西:

        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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-08-07
          • 2015-03-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-12-21
          • 1970-01-01
          相关资源
          最近更新 更多