范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

AudioUnit(一)

  最近一直在做iOS音频相关技术的项目,期间在官方及网上的资料文档也学习了很多,当然,iOS平台中音频相关技术还是有很多方面的,这里我先总体概述下,然后以I/O Audio Unit为例对其概念,基本用法和思路进行讲解,可能不够全面,一些细节需要自行查找相关文档。后面我会对github上一个开源的音频引擎框架进行源码分析,来展现在更复杂的音频技术应用场景下可能的设计及实现方式。
  本文图片及大部分技术概念阐述均来自apple官网 1、Core Audio
  Core Audio 是iOS和MAC系统中的关于数字音频处理的基础设施,它是应用程序用来处理音频的一组软件框架,所有关于iOS音频开发的接口都是由Core Audio来提供或者经过它提供的接口来进行封装的。Apple官方对Core Audio的框架分层图示如下:
  core_audio_layers.png2、Low-Level
  该主要在MAC上的音频APP实现中并且需要最大限度的实时性能的情况下使用,大部分音频APP不需要使用该层的服务。而且,在iOS上也提供了具备较高实时性能的高层API达到你的需求。例如OpenAL,在游戏中具备与I/O直接调用的实时音频处理能力  I/O Kit , 与硬件驱动交互  Audio HAL , 音频硬件抽象层,使API调用与实际硬件相分离,保持独立  Core MIDI , 为MIDI流和设备提供软件抽象工作层  Host Time Services , 访问电脑硬件时钟 3、Mid-Level
  该层功能比较齐全,包括音频数据格式转换,音频文件读写,音频流解析,插件工作支持等  Audio Convert Services  负责音频数据格式的转换  Audio File Services  负责音频数据的读写  Audio Unit Services  和  Audio Processing Graph Services  支持均衡器和混音器等数字信号处理的插件  Audio File Scream Services  负责流解析  Core Audio Clock Services  负责音频音频时钟同步 4、High-Level
  是一组从低层接口组合起来的高层应用,基本上我们很多关于音频开发的工作在这一层就可以完成  Audio Queue Services  提供录制、播放、暂停、循环、和同步音频它自动采用必要的编解码器处理压缩的音频格式  AVAudioPlayer  是专为IOS平台提供的基于Objective-C接口的音频播放类,可以支持iOS所支持的所有音频的播放  Extended Audio File Services  由Audio File与Audio Converter组合而成,提供压缩及无压缩音频文件的读写能力  OpenAL  是CoreAudio对OpenAL标准的实现,可以播放3D混音效果 5、不同场景所需要的API Service只实现音频的播放,没有其他需求,AVAudioPlayer就可以满足需求。它的接口使用简单,不用关心其中的细节,通常只提供给它一个播放源的URL地址,并且调用其play、pause、stop等方法进行控制,observer其播放状态更新UI即可
  APP需要对音频进行流播放,就需要AudioFileStreamer加Audio Queue,将网络或者本地的流读取到内存,提交给AudioFileStreamer解析分离音频帧,分离出来的音频帧可以送给AudioQueue进行解码和播放 可参考 AudioStreamer FreeStreamer AFSoundManager
  APP需要需要对音频施加音效(均衡器、混响器),就是除了数据的读取和解析以外还需要用到AudioConverter或者Codec来把音频数据转换成PCM数据,再由AudioUnit+AUGraph来进行音效处理和播放 可参考 DouAudioStreamer TheAmazingAudioEngine AudioKit 6、Audio Unit
  iOS提供了混音、均衡、格式转换、实时IO录制、回放、离线渲染、语音对讲(VoIP)等音频处理插件,它们都属于不同的AudioUnit,支持动态载入和使用。AudioUnit可以单独创建使用,但更多的是被组合使用在Audio Processing Graph容器中以达到多样的处理需要,例如下面的一种场景:
  APP持有的Audio Processing Graph容器中包含两个EQ Unit、一个Mixer Unit、一个I/O Unit,APP将磁盘或者网络中的两路流数据分别通过EQ Unit进行均衡处理,然后在Mixer Unit经过混音处理为一路,进入I/O Unit将此路数据送往硬件去播放。在这整个流程中,APP随时可以调整设置AU Graph及其中每个Unit的工作状态及参数,动态性的接入或者移出指定的Unit,并且保证线程安全。
  C++音视频学习资料免费获取方法:关注音视频开发T哥  ,点击「链接」即可免费获取2023年最新 C++音视频开发进阶独家免费学习大礼包! 6.1 Audio Unit类型:
  I/O : Remote I/O、Voice-Processing I/O、Generic Output  Mixing : 3D Mixer、Mutichannel Mixer  Effect : iPod Equalizer  Format Conversion : Format Converter 6.2 AudioUnit构建方式
  创建Audio Unit有两种途径,以I/O Unit为例,一种是直接调用unit接口创建,一种是通过Audio Unit Graph创建,下面是两种创建方式的基本流程和相关代码: 6.3 Unit API方式(Remote IO Unit)    // create IO Unit     BOOL result = NO;     AudioComponentDescription outputDescription = {0};     outputDescription.componentType = kAudioUnitType_Output;     outputDescription.componentSubType = kAudioUnitSubType_RemoteIO;     outputDescription.componentManufacturer = kAudioUnitManufacturer_Apple;     outputDescription.componentFlags = 0;     outputDescription.componentFlagsMask = 0;     AudioComponent comp = AudioComponentFindNext(NULL, &outputDescription);     result = CheckOSStatus(AudioComponentInstanceNew(comp, &mVoipUnit), @"couldn"t create a new instance of RemoteIO");     if (!result) return result;      // config IO Enable status     UInt32 flag = 1;     result = CheckOSStatus(AudioUnitSetProperty(mVoipUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag)), @"could not enable output on RemoteIO");     if (!result) return result;      result = CheckOSStatus(AudioUnitSetProperty(mVoipUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag)),                  @"AudioUnitSetProperty EnableIO");     if (!result) return result;      // Config default format     result = CheckOSStatus(AudioUnitSetProperty(mVoipUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &inputAudioDescription, sizeof(inputAudioDescription)), @"couldn"t set the input client format on RemoteIO");     if (!result) return result;     result = CheckOSStatus(AudioUnitSetProperty(mVoipUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &outputAudioDescription, sizeof(outputAudioDescription)), @"couldn"t set the output client format on RemoteIO");     if (!result) return result;      // Set the MaximumFramesPerSlice property. This property is used to describe to an audio unit the maximum number     // of samples it will be asked to produce on any single given call to AudioUnitRender     UInt32 maxFramesPerSlice = 4096;     result = CheckOSStatus(AudioUnitSetProperty(mVoipUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFramesPerSlice, sizeof(UInt32)), @"couldn"t set max frames per slice on RemoteIO");     if (!result) return result;      // Set the record callback     AURenderCallbackStruct recordCallback;     recordCallback.inputProc = recordCallbackFunc;     recordCallback.inputProcRefCon = (__bridge void * _Nullable)(self);     result = CheckOSStatus(AudioUnitSetProperty(mVoipUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, kInputBus, &recordCallback, sizeof(recordCallback)), @"couldn"t set record callback on RemoteIO");     if (!result) return result;      // Set the playback callback     AURenderCallbackStruct playbackCallback;     playbackCallback.inputProc = playbackCallbackFunc;     playbackCallback.inputProcRefCon = (__bridge void * _Nullable)(self);     result = CheckOSStatus(AudioUnitSetProperty(mVoipUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, kOutputBus, &playbackCallback, sizeof(playbackCallback)), @"couldn"t set playback callback on RemoteIO");     if (!result) return result;      // set buffer allocate     flag = 0;     result = CheckOSStatus(AudioUnitSetProperty(mVoipUnit,                                                 kAudioUnitProperty_ShouldAllocateBuffer,                                                 kAudioUnitScope_Output,                                                 kInputBus,                                                 &flag,                                                 sizeof(flag)), @"couldn"t set property for ShouldAllocateBuffer");     if (!result) return result;      // Initialize the output IO instance     result = CheckOSStatus(AudioUnitInitialize(mVoipUnit), @"couldn"t initialize VoiceProcessingIO instance");     if (!result) return result;      return YES;6.4 AU Graph方式(MultiChannelMixer Unit + Remote IO Unit)    // create AUGraph     BOOL result = NO;     result = CheckOSStatus(NewAUGraph (&processingGraph), @"couldn"t create a new instance of AUGraph");     if (!result) return result;      // I/O unit     AudioComponentDescription iOUnitDescription;     iOUnitDescription.componentType          = kAudioUnitType_Output;     iOUnitDescription.componentSubType       = kAudioUnitSubType_RemoteIO;     iOUnitDescription.componentManufacturer  = kAudioUnitManufacturer_Apple;     iOUnitDescription.componentFlags         = 0;     iOUnitDescription.componentFlagsMask     = 0;      // Multichannel mixer unit     AudioComponentDescription MixerUnitDescription;     MixerUnitDescription.componentType          = kAudioUnitType_Mixer;     MixerUnitDescription.componentSubType       = kAudioUnitSubType_MultiChannelMixer;     MixerUnitDescription.componentManufacturer  = kAudioUnitManufacturer_Apple;     MixerUnitDescription.componentFlags         = 0;     MixerUnitDescription.componentFlagsMask     = 0;      AUNode   iONode;         // node for I/O unit     AUNode   mixerNode;      // node for Multichannel Mixer unit      result = CheckOSStatus(AUGraphAddNode (                                            processingGraph,                                            &iOUnitDescription,                                            &iONode), @"couldn"t add a node instance of kAudioUnitSubType_RemoteIO");     if (!result) return result;      result = CheckOSStatus(AUGraphAddNode (                                            processingGraph,                                            &MixerUnitDescription,                                            &mixerNode), @"couldn"t add a node instance of mixer unit");     if (!result) return result;      // open the AUGraph     result = CheckOSStatus(AUGraphOpen (processingGraph), @"couldn"t get instance of mixer unit");     if (!result) return result;      // Obtain unit instance     result = CheckOSStatus(AUGraphNodeInfo (                                             processingGraph,                                             mixerNode,                                             NULL,                                             &mMixerUnit                                             ), @"couldn"t get instance of mixer unit");     if (!result) return result;      result = CheckOSStatus(AUGraphNodeInfo (                                             processingGraph,                                             iONode,                                             NULL,                                             &mVoipUnit                                             ), @"couldn"t get a new instance of remoteio unit");     if (!result) return result;      /////////////////////////////////////////////////////////////////////////////////////////      UInt32 busCount   = 2;    // bus count for mixer unit input     UInt32 guitarBus  = 0;    // mixer unit bus 0 will be stereo and will take the guitar sound     UInt32 beatsBus   = 1;    // mixer unit bus 1 will be mono and will take the beats sound     result = CheckOSStatus(AudioUnitSetProperty (                                                  mMixerUnit,                                                  kAudioUnitProperty_ElementCount,                                                  kAudioUnitScope_Input,                                                  0,                                                  &busCount,                                                  sizeof (busCount)                                                  ), @"could not set mixer unit input bus count");     if (!result) return result;      UInt32 maximumFramesPerSlice = 4096;     result = CheckOSStatus(AudioUnitSetProperty (                                                  mMixerUnit,                                                  kAudioUnitProperty_MaximumFramesPerSlice,                                                  kAudioUnitScope_Global,                                                  0,                                                  &maximumFramesPerSlice,                                                  sizeof (maximumFramesPerSlice)                                                  ), @"could not set mixer unit maximum frame per slice");     if (!result) return result;       // Attach the input render callback and context to each input bus     for (UInt16 busNumber = 0; busNumber < busCount; ++busNumber) {          // Setup the struture that contains the input render callback         AURenderCallbackStruct playbackCallback;         playbackCallback.inputProc = playbackCallbackFunc;         playbackCallback.inputProcRefCon = (__bridge void * _Nullable)(self);          NSLog (@"Registering the render callback with mixer unit input bus %u", busNumber);         // Set a callback for the specified node"s specified input         result = CheckOSStatus(AUGraphSetNodeInputCallback (                                                             processingGraph,                                                             mixerNode,                                                             busNumber,                                                             &playbackCallback                                                             ), @"couldn"t set playback callback on mixer unit");         if (!result) return result;     }       // Config mixer unit input default format     result = CheckOSStatus(AudioUnitSetProperty (                                                  mMixerUnit,                                                  kAudioUnitProperty_StreamFormat,                                                  kAudioUnitScope_Input,                                                  guitarBus,                                                  &outputAudioDescription,                                                  sizeof (outputAudioDescription)                                                  ), @"couldn"t set the input 0 client format on mixer unit");     if (!result) return result;      result = CheckOSStatus(AudioUnitSetProperty (                                                  mMixerUnit,                                                  kAudioUnitProperty_StreamFormat,                                                  kAudioUnitScope_Input,                                                  beatsBus,                                                  &outputAudioDescription,                                                  sizeof (outputAudioDescription)                                                  ), @"couldn"t set the input 1 client format on mixer unit");     if (!result) return result;      Float64 graphSampleRate = 44100.0;    // Hertz;     result = CheckOSStatus(AudioUnitSetProperty (                                                  mMixerUnit,                                                  kAudioUnitProperty_SampleRate,                                                  kAudioUnitScope_Output,                                                  0,                                                  &graphSampleRate,                                                  sizeof (graphSampleRate)                                                  ), @"couldn"t set the output client format on mixer unit");     if (!result) return result;      ////////////////////////////////////////////////////////////////////////////////////////////      // config void unit IO Enable status     UInt32 flag = 1;     result = CheckOSStatus(AudioUnitSetProperty(mVoipUnit,                                                 kAudioOutputUnitProperty_EnableIO,                                                 kAudioUnitScope_Output,                                                 kOutputBus,                                                 &flag,                                                 sizeof(flag)                                                 ), @"could not enable output on kAudioUnitSubType_RemoteIO");     if (!result) return result;      result = CheckOSStatus(AudioUnitSetProperty(mVoipUnit,                                                 kAudioOutputUnitProperty_EnableIO,                                                 kAudioUnitScope_Input,                                                 kInputBus,                                                 &flag,                                                 sizeof(flag)                                                 ), @"could not enable input on kAudioUnitSubType_RemoteIO");     if (!result) return result;      // config voip unit default format     result = CheckOSStatus(AudioUnitSetProperty(mVoipUnit,                                                 kAudioUnitProperty_StreamFormat,                                                 kAudioUnitScope_Output,                                                 kInputBus,                                                 &inputAudioDescription,                                                 sizeof(inputAudioDescription)                                                 ), @"couldn"t set the input client format on kAudioUnitSubType_RemoteIO");     if (!result) return result;       UInt32 maxFramesPerSlice = 4096;     result = CheckOSStatus(AudioUnitSetProperty(mVoipUnit,                                                 kAudioUnitProperty_MaximumFramesPerSlice,                                                 kAudioUnitScope_Global,                                                 0,                                                 &maxFramesPerSlice,                                                 sizeof(UInt32)                                                 ), @"couldn"t set max frames per slice on kAudioUnitSubType_RemoteIO");     if (!result) return result;      // Set the record callback     AURenderCallbackStruct recordCallback;     recordCallback.inputProc = recordCallbackFunc;     recordCallback.inputProcRefCon = (__bridge void * _Nullable)(self);     result = CheckOSStatus(AudioUnitSetProperty(mVoipUnit,                                                 kAudioOutputUnitProperty_SetInputCallback,                                                 kAudioUnitScope_Global,                                                 kInputBus,                                                 &recordCallback,                                                 sizeof(recordCallback)                                                 ), @"couldn"t set record callback on kAudioUnitSubType_RemoteIO");     if (!result) return result;      // set buffer allocate     flag = 0;     result = CheckOSStatus(AudioUnitSetProperty(mVoipUnit,                                                 kAudioUnitProperty_ShouldAllocateBuffer,                                                 kAudioUnitScope_Output,                                                 kInputBus,                                                 &flag,                                                 sizeof(flag)), @"couldn"t set property for ShouldAllocateBuffer");     if (!result) return result;      /////////////////////////////////////////////////////////////////////////////////////////////     // Initialize the output IO instance     result = CheckOSStatus(AUGraphConnectNodeInput (                                                     processingGraph,                                                     mixerNode,         // source node                                                     0,                 // source node output bus number                                                     iONode,            // destination node                                                     0                  // desintation node input bus number                                                     ), @"couldn"t connect ionode to mixernode");     if (!result) return result;      result = CheckOSStatus(AUGraphInitialize (processingGraph), @"AUGraphInitialize failed");     if (!result) return result;      return YES;6.5 AudioUnit数据的输入输出方式
  Unit处理音频数据,都要经过一个输入和输出过程,设置输入输出的音频格式(可以相同或者不同),两个Unit对接即是将一个Unit的输入接到另一个Unit的输出,或者将一个Unit的输出接到另一个Unit的输入,需要注意的是在对接点要保证Audio Format的一致性。以Remote I/O Unit为例,结构如下图所示:
  一个I/O Unit包含两个实体对象,两个实体对象(Element 0、Element 1)相互独立,根据需求可通过kAudioOutputUnitProperty_EnableIO属性去开关它们。Element 1与硬件输入连接,并且Element 1的输入域(input scope)对你不可见,你只能读取它的输出域的数据及设置其输出域的音频格式;Element 0与硬件输出连接,并且Element 0的输出域(ouput scope)对你不可见,你只能写入它的输入域的数据及设置其输入域的音频格式。
  如何将输入设备采集的数据抓出来,又如何将处理后的数据送到输出设备呢? 通过AURenderCallbackStruct结构,将定义的两个回调静态方法地址设置到需要的Element 0/1上,当Unit配置完毕并且运行后,Unit调度线程会按照当前设备状态及音频格式安排调度周期,循环往复的调用你提供的录制与播放回调方法,样例代码如下: // for record callback, read audio data from bufferlist static OSStatus recordCallbackFunc(void *inRefCon,                                   AudioUnitRenderActionFlags *ioActionFlags,                                   const AudioTimeStamp *inTimeStamp,                                   UInt32 inBusNumber,                                   UInt32 inNumberFrames,                                   AudioBufferList *ioData){  ASAudioEngineSingleU *engine = (__bridge ASAudioEngineSingleU* )inRefCon;  OSStatus err = noErr; if (engine.audioChainIsBeingReconstructed == NO){      @autoreleasepool {         AudioBufferList bufList = [engine getBufferList:inNumberFrames];         err = AudioUnitRender([engine recorderUnit], ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufList);         if (err) {             HMLogDebug(LogModuleAudio, @"AudioUnitRender error code = %d", err);         } else {             AudioBuffer buffer = bufList.mBuffers[0];             NSData *pcmBlock = [NSData dataWithBytes:buffer.mData length:buffer.mDataByteSize];             [engine didRecordData:pcmBlock];         }     } }  return err; }  // for play callback, fill audio data to bufferlist static OSStatus playbackCallbackFunc(void *inRefCon,                              AudioUnitRenderActionFlags *ioActionFlags,                              const AudioTimeStamp *inTimeStamp,                              UInt32 inBusNumber,                              UInt32 inNumberFrames,                              AudioBufferList *ioData){  ASAudioEngineSingleU *engine = (__bridge ASAudioEngineSingleU* )inRefCon;  OSStatus err = noErr; if (engine.audioChainIsBeingReconstructed == NO) {     for (int i = 0; i < ioData -> mNumberBuffers; i++) {         @autoreleasepool {             AudioBuffer buffer = ioData -> mBuffers[i];             NSData *pcmBlock = [engine getPlayFrame:buffer.mDataByteSize];             if (pcmBlock && pcmBlock.length) {                 UInt32 size = (UInt32)MIN(buffer.mDataByteSize, [pcmBlock length]);                 memcpy(buffer.mData, [pcmBlock bytes], size);                 buffer.mDataByteSize = size;                 //HMLogDebug(LogModuleAudio, @"AudioUnitRender pcm data has filled");             } else {                 buffer.mDataByteSize = 0;                 *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;             }         } // end pool     } // end for } // end if  return err;7、不同场景下AudioUnit构建样例7.1 I/O 无渲染
  从输入设备采集过来的数据,先经过MutilChannelMixer Unit,再送到输出设备播放,该构建方式在于中间的Unit可对mic采集采集过来的数据进行声相调节以及音量的调节 7.2 I/O 有渲染
  该构建方式在输入与输出之间增加了rendercallback,可以在硬件采集过来的数据上做一些处理(例如,增益、调制、音效等)后再送到输出播放
  IOWithRenderCallback_2x.png7.3 仅输出并且带渲染
  适合音乐游戏及合成器类的APP,仅使用IO Unit的output端,在rendercallback中负责播放源的提取整理并准备送播,比较简单的构建方式
  输入端有两路音频流,都是通过rendercallback方式抓取数据,其中一路音频流直接给入到Mixer Unit中,另一路先经过EQ Unit处理后给入到Mixer Unit中, 8、Tips8.1 多线程及内存管理
  尽可能的避免render callback方法内做加锁及处理耗时较高的操作,这样可以最大限度的提升实时性能,如果播放数据或者采集数据存在不同线程读写的情况,必需要加锁保护,推荐pthread相关lock方法性能比其它锁要高 音频的输入输出一般都是一个持续的过程,在采集与播放的callback中,应尽量复用buffer及避免多次buffer拷贝,而不是每次回调都重新申请和释放,在适当的位置加上@autoreleasepool避免长时间运行内存不断上涨 8.2 格式
  Core Audio Type中定义了AudioStreamBasicDescription结构,Audio Unit及其它很多音频API对格式的配置都需要用到它,根据需要将该结构的信息填充正确,下面是44.1K,stereo,16bit的填充例子 audioDescription.mSampleRate = 44100; audioDescription.mChannelsPerFrame = 2; audioDescription.mBitsPerChannel = 16;  audioDescription.mFramesPerPacket = 1; audioDescription.mFormatID = kAudioFormatLinearPCM; audioDescription.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;  audioDescription.mBytesPerFrame = (audioDescription.mBitsPerChannel/8) * audioDescription.mChannelsPerFrame; audioDescription.mBytesPerPacket = audioDescription.mBytesPerFrame ;
  苹果官方建议在整个Audio Processing Graph或者Unit之间尽量以相同的音频格式流通,尽管Audio Unit的输入输出可以不同。另外在Unit之间输入输出连接点要保持一致。 8.3 音质
  在使用过程中,Audio Unit的format是可以动态改变的,但存在一种情况,Unit在销毁前最好恢复到默认创建时的format,否则在销毁后再重建Unit后,可能出现播放音质变差(音量变小,声音粗糙)的情况。 在使用VoiceProcessing I/O Unit过程,遇到在有些iphone上开启扬声器后,Unit从Mic采集过来的数据为空或者噪音的情况,从APP STORE中下载了其它的VOIP类型的APP也同样存在该问题,后来将AudioUnitSubType改成RemoteIO类型后,问题消失,怀疑苹果在VoiceProcessing Unit上对回声消除功能的处理上有bug 8.4 AudioSession
  既然使用了音频特性,就会用到AudioSession,随着功能需求跟进,与它相关的问题也瞒多的,比如路由管理(听筒扬声器、线控耳机、蓝牙耳机),打断处理(interruption、iphone call)等,这里以Audio Unit为主,就不对它进行详细描述了,需要注意的是 音频的路由变更(用户挺拔耳机,或者代码调用强制切换)涉及到iOS硬件上输入和输出设备的改变,I/O类型Unit的采集和播放线程在切换过程中会阻塞一定时间(200ms左右),如果是语音对讲类对实时性要求较高的应用场景要考虑丢包策略。 在APP前台工作时,iPhone来电或者用户主动切换到其它音频类APP后,要及时处理音频的打断机制,在恰当的时机停止及恢复Unit的工作,由于iOS平台对资源的独占方式,iPhone在通话等操作时,APP中的Unit是无法初始化或者继续工作的。
  原文链接:iOS Audio Unit (涓 ) - 鎺橀噾

又立雕像又退役球衣,诺维斯基值得这一切,独行侠要珍惜东契奇又立雕像又退役球衣,诺维斯基值得这一切,独行侠要珍惜东契奇!每当球迷们谈论起一人一城的典范球员的时候,很多人首先想到的球员一定是科比,毕竟他20年如一日的在湖人队付出,并且最终为他杜兰特有欧文在太棒了,他的回归让每个人都轻松了许多虎扑01月06日讯今天,篮网以129121战胜步行者。赛后,凯文杜兰特接受了记者的采访。谈到凯里欧文的复出,杜兰特说欧文的表现太棒了,有他在太棒了。我想念欧文,想念他在更衣室的存在方舟生存进化好评超90!维京DLC苍凉遒劲的北欧生存体验新地图维京(Fjordur)进入2022年,方舟生存进化将继续与各位幸存者们相伴,共享生存冒险之旅。继失落之岛后,Wildcard工作室预计于2022年6月推出下一款免费DLC维京方舟生存进化好评超90!维京DLC苍凉遒劲的北欧生存体验新地图维京(Fjordur)进入2022年,方舟生存进化将继续与各位幸存者们相伴,共享生存冒险之旅。继失落之岛后,Wildcard工作室预计于2022年6月推出下一款免费DLC维京怀孕哪个阶段,胎宝长得快?孕期多喝3款汤,促进胎宝大脑发育若问什么事情带给人最大的希望,两道杠一定能上榜。像我第一次怀孕就特别地激动和期待,想着自己终于有孩子了,特别的兴奋。但怀胎十月也并不容易,孕期各种反应折磨着我,我又特别害怕孩子不健坚信三岁分床4岁分房,每晚听儿子可怜兮兮的哭声,宝妈心疼不已父母都是希望自己的孩子能够独立勇敢坚强自信的,因此,为了孩子不输在起跑线上,父母都做着各方面的努力。国外有育儿理念说,早一点与孩子分床睡,孩子将变得更加独立自主。因此,一些宝妈为了这个重阳节,带上家人,开着捷途汽车去登高赏秋吧九九重阳日,佳节倍思亲。中国传统节日大多都与家文化有着千丝万缕的关联,重阳节也不例外,九在易经中为阳数,九九两阳数相重,故曰重阳。古人认为九九重阳是吉祥的日子,有登高祈福秋游赏菊饮怀孕哪个阶段,胎宝长得快?孕期多喝3款汤,促进胎宝大脑发育若问什么事情带给人最大的希望,两道杠一定能上榜。像我第一次怀孕就特别地激动和期待,想着自己终于有孩子了,特别的兴奋。但怀胎十月也并不容易,孕期各种反应折磨着我,我又特别害怕孩子不健坚信三岁分床4岁分房,每晚听儿子可怜兮兮的哭声,宝妈心疼不已父母都是希望自己的孩子能够独立勇敢坚强自信的,因此,为了孩子不输在起跑线上,父母都做着各方面的努力。国外有育儿理念说,早一点与孩子分床睡,孩子将变得更加独立自主。因此,一些宝妈为了这个重阳节,带上家人,开着捷途汽车去登高赏秋吧九九重阳日,佳节倍思亲。中国传统节日大多都与家文化有着千丝万缕的关联,重阳节也不例外,九在易经中为阳数,九九两阳数相重,故曰重阳。古人认为九九重阳是吉祥的日子,有登高祈福秋游赏菊饮撕起来了!伊藤美诚看不起前辈水谷隼,水谷隼我早就该骂她一顿近日伊藤美诚和水谷隼这对混双搭档吸引了很多球迷的关注,他们在东京奥运会结束4个月之后依旧进行巡回演出,展示他们所获得的乒乓球金牌,这样的举动,引起了很多中国球迷的不满,而在最近伊藤
曝中超昔日土豪或退出中超,争冠组不踢了!中国足球联赛再遭打击北京时间11月11日消息,西北望看台报道,原河北队总经理李君已被集团调走,河北足球俱乐部已经很难坚持,球队至今没有收到集中的通知,参加12月中超联赛的希望越发渺茫。这意味着,这支曾每个人的泪痕,最后都会变成故事的花纹每个人的泪痕,最后都会变成故事的花纹。你在花瓣里笑着,让所有的忧伤随风而去。你在花瓣里哭着,让所有的痛,化作坚强而坚强的泪水,化作蝴蝶一缕飞回温暖的阳光。你的泪水,会融化作甘甜的甘走进五十岁,走进后半生,致自己文飞鱼导语走进五十岁,走进后半生,致自己。70后的我如今年过五十了,走进五十岁的人生里,后半生的人生又该如何继续呢?回想起前半生,我们都是摸着石头过河,没有背景,没有靠山,就靠着自最后刽子手邓海山狠辣异常,曾砍300人首级,晚年凄惨度过生命纯属偶然,所以每个生命都要依恋另一个生命,相依为命,结伴而行。生命也纯属偶然,所以每一个生命都不属于另一个生命,像一阵风,无牵无挂。周国平从古至今,敬畏生命热爱生命敬畏生命一直国际测试赛新闻吹风会在北京冬奥组委举行来源央视新闻今天(12日),相约北京系列冬季体育赛事2021下半年测试赛和测试活动新闻吹风会在北京冬奥组委举行。北京冬奥组委场馆管理部部长姚辉从测试工作进展顺利测试效果初步呈现有待什么?保温杯不要了?11月11日,勇士以123110击败森林狼,这并不新鲜。出人意料的是,一直养生的维金斯,爆砍35分,并且一改往日摸鱼的状态,怒摔保温杯,别的不说,先来看俩暴扣。补扣隔扣唐斯,炸裂我生命的影子常常望着天空发呆,那是因为想你了,想起你,就会把思念挂在云端,让它悠闲地飘到你的头顶,风里写满了我的忧愁常常坐在灯下发呆,那是因为想你,想起你,我会把思念寄予月儿,蒙蒙月儿洒下缕缕夜读有一种智慧,叫冷处理来源人民网01hr人生在世,被误解被非议都在所难免。有人想不通,选择生闷气有人必争辩,选择论是非有人气不过,选择斗到底。殊不知,发脾气是本能,但选择不发脾气才是本事。当心有怒气时,人生何其短,笑要格外甜人生何其短,笑要格外甜。在这样的一个秋天里,家人开始不厌其烦地生活,直到萧瑟起舞,再也没有人愿意放弃,这样的秋天让我们选择在这个季节才觉得安全。我是一个念旧的人,一直想用一种文字来湖人热火之战5次错漏判,浓眉最后0。3秒补篮本该绝杀NBA官方出具了昨天湖人与热火加时大战的裁判报告,证实在常规赛和加时赛的最后两分钟,场上总共出现5次错漏判,其中2次对热火不利,3次对湖人不利,包括浓眉第四节结束前0。3秒本可以绝江苏女排联赛16人名单!副攻换新,张常宁许若亚无缘,4二传出征新赛季排超联赛即将在11月25日开打,作为上届联赛的亚军江苏女排正式在其官方社交平台官宣了参加本次比赛的16人名单。新周期,江苏女排在人员方面做出了不小的调整,张常宁许若亚周馨忆等