Core Audio Overview

来源:互联网 发布:ubuntu安装中文输入法 编辑:程序博客网 时间:2024/06/05 15:25

OS X上Core Audio架构

  • Audio Queue Services用于保存,播放,同步音频
  • Audio File,Converter,and Codec Services用于从文件中读取音频,以及音频的格式转换。
  • Audio Unit 可以用于音频信号处理
  • Music Sequencing Services用于播放MIDI音频。
  • Core Audio Clock用于时间同步
  • System Sounds 用于播放系统声音。

iOS上的Core Audio架构

Audio Session是处理音频播放上下文的。

Linear PCM(pulse-code-modulated)

线性脉冲编码调制,是最常见的非压缩格式的音频数据。CD品质的音频采用的是44.1kHz的采样率,16位量化。

  • Sample,一个通道的一个采样点,是一个16位的数字。
  • Frame,在同一时刻,所有通道的采样点集合。比如立体声就是两个16位数字,一个代表左声道,一个代表右声道。
  • Packet,一系列连续的Frame,在Linear PCM中,一个Packet通常只包含一个Frame。在压缩的数据格式中,通常一个Packet中有多个Frame,一个Packet表示最小的有意义的单元。

线性脉冲编码调制中线性的意思是Sample中的值和原始信号的振幅成线性关系。

Audio Unit

Audio Unit是用于处理Audio Data的 plug-ins。

在iOS上Audio Unit没有界面,主要用于降低延迟。

在Mac上可以使用系统提供的,第三方的或者自己写的Audio Unit,并且可以将这些Audio Unit提供给如GarageBand, Logic Studio等用。有些Audio Unit可以有用户界面。

以下这些都是Audio Unit的例子:
- 信号处理(高通,低通,混响,压缩),这些叫做effect unit
- 音乐合成,比如电子吉他。这些叫做instrument unit,通常是根据MIDI的输入合成音乐。
- 信号源,非MIDI的音频源。
- 代表硬件,这些叫做I/O unit。
- 格式转换器,这些叫做convert unit,可以合并或者拆分audio stream
- mixer or panner,mixer可以合并audio track,panner处理立体声或者3D效果。
- effect unit,用于非实时的处理音频。

在OS X上,可以用AU Lab这个软件测试各种Audio Unit的效果。这个软件在可以在https://developer.apple.com/download/more/下载,Audio Tools for Xcode

HAL

硬件抽象层,提供硬件操作的接口,使得我们不必和驱动直接打交道。一般情况下,在OS X上使用AUHAL unit,在iOS上使用AURemoteIO unit来从硬件获取声音和将声音发送到硬件。

MIDI

在OS X上Core MIDI能让应用程序和MIDI设备交互,MIDI设备比如电吉他,键盘等。

OS X上有个MIDI setup应用程序可以设置MIDI设备。

Core Audio 的编程接口

从编程接口的角度讲,Core Audio API分为3层:

最底层的I/O Kit是直接和驱动打交道的了;Audio HAL是硬件的抽象,隔离了具体的驱动接口;Core MIDI用于处理MIDI流和MIDI设备;Host Time Services是系统时钟。

中间层的Audio Convert Services处理音频格式转换;Audio File Service处理音频文件的读写;Audio Unit Services 和 Audio Processing Graph Service用于音频信号处理和均衡混音等;Audio File Stream Service用于处理音频流;Core Audio Clock Service 用于时间同步

最高层的Audio Queue Service 用于播放,录音等一般需求;AVAuidoPlayer提供了OC的接口用于播放;
Extended Audio File Service 合并了Audio File Serivice和Audio Converter Service,为音频文件的读写提供了统一的接口;OpenAL是基于系统的3D Mixer Audio Unit,主要给游戏用。

Core Audio的Frameworks

  • AudioToolbox.framework 提供了中层和高层的接口,在iOS上,还包括Audio Session。
  • AudioUnit.framework 提供了audio plug-ins如Audio Uint或者Codecs。
  • CoreAudio.framework 底层的编程接口
  • CoreAudioKit.framework 提供了Audio Uinit的用户界面接口,iOS上不可用
  • CoreMIDI.framework 处理MIDI data和配置MIDI network, iOS不可用
  • CoreMIDIServer.framework 允许MIDK驱动和OS X的MIDI Server交互,iOS不可用
  • OpenAL.framework 提供和OpenAL相关的接口

Properties

Core Audio使用属性的方式管理对象的行为,一个属性就是一个键值对。一般键一个枚举,如:kAudioFilePropertyFileFormat; 值是数值,结构体等。Core Audio 中有大量的属性需要慢慢熟悉。

获取属性值的函数一般长这样:AudioUnitGetPropertyInfo,AudioQueueGetPropertySize

Core Audio 的接口一般使用Callback来通知属性的改变。

Callback

一个Callback函数长这样:

typedef void  (*AudioQueuePropertyListenerProc)(    void *inUserData,    AudioQueueRef inAQ,    AudioQueuePropertyID inID);

关于回调的使用,就是编程通用的一种思想和模式,并无特殊之处。

音频数据格式

要区分音频文件格式和音频数据格式,音频文件格式规定了音频数据,音频meta,系统文件meta的存放和解析格式,音频数据格式包含音频的采样率,量化位数,分包信息等。

Core Audio中通用的音频数据格式:

struct AudioStreamBasicDescription{    Float64 mSampleRate;    UInt32  mFormatID;    UInt32  mFormatFlags;    UInt32  mBytesPerPacket;    UInit32 mFramePerPacket;    UInt32  mBytesPerFrame;    UInt32  mChennelsPerFrame;    UInt32  mBitsPerChannel;    UInt32  mReserved;};

这个结构是Core Audio中统一的音频数据格式(不管是不是Stream的音频),简称ASBD。

struct AudioStreamPacketDescription{    SInt64  mStartOffset;    UInt32  mVariableFramesInPackets;    UInt32  mDataByteSize;};

如果获知这些字段的值呢?如果已知,就直接赋值,如果不确定,就填0,然后利用Core Audio的API来获取。

对于音频文件,可以使用Audio File Service获取:

-(void) openPlaybackFile:(CFURLRef) soundFile{    AudioFileOpenURL(        soundFile,        0x01,        kAudioFileCAFType,        &audioFileID    );    UInt32 sizeOfPlaybackFormatASBDStruct = sizeof([self audioFormat]);    AudioFileGetProperty(        [self audioFileID],        kAudioFilePropertyDataFormat,        &sizeofPlaybackFormatASBDStruct,        &audioFormat    );}

对于某些特定的音频数据格式,Core Audio做了优化,也是在对应平台上的Canonical Format

iOS的输入输出是Linear PCM, 16位整数; iOS的Audio Unit,Linear PCM, 8.24-bit的固定精度; Mac 输入输出Linear PCM,32位整数; Mac的Audio Unit,Linear PCM, 32位浮点

Magic Cookies

Magic Cookie是一个metadata的句柄,表示了一个压缩了的音频文件或者流的格式。Core Audio使用它来解压。

Magic Cookie的获取方式:

-(void) copyMagicCookieToQueue:(AudioQueueRef) queue fromFile:(AudioFileID)file{    UInt32 propertySize = sizeof(UInt32);    OSStatus result = AudioFileGetPropertyInfo(                            file,                            kAudioFilePropertyMagicCookieData,                            &propertySize,                            NULL                        );    if(result && propertySize){        char *cookie = (char *) malloc (propertySize);        AudioFileGetProperty (            file,            kAudioFilePropertyMagicCookieData,            &propertySize,            cookie        );        AudioQueueSetProperty(            file,            kAudioQueueProperty_MagicCookie,            cookie,            propertySize        );        free(cookie);    }}

Audio Data Packets

一个Packet是一个或者多个Frame的集合,是最小的有意义的Audio Data单元,音频同步的单元。
有3种类型的Packet:

  • CBR(constant bit rate),linear PCM, IMA/ADPCM都是这种类型,特征是每个packet一样大。
  • VBR(variable bit rate),AAC,Apple LossLess,MP3都是这种类型,所有的packet都拥有同样多的frame,但是每个Sample的位数可能会变化。
  • VFR(Variable frame rate),每个包中包含的frame可能不同。但不是常用的格式。

对于VBR或者VFR类型的数据,每一个packet都会用一个AudioStreamPacketDescription来描述,而CBR只用一个AudioStreamPacketDescription就可以描述。

对于CBR和VBR类型的数据,每秒钟的packet数目是相同的。所以packet的number就可以作为时间量度。

下边是应用:

-(void) calculateSizeFor:(Float64) seconds{    UInt32 maxPacketSize;    UInt32 propetySize = sizeof(maxPacketSize);    AudioFileGetProperty(        audioFileID,        kAudioFilePropertyPacketSizeUpperBound,        &propetySize,        &maxPacketSize,    );    static const int maxBufferSize = 0x10000;    static const int minBufferSize = 0x4000;    if(audioFormat.mFramesPerpacket){        Float64 numPacketsForTime =             audioFormat.mSampleRate / audioFormat.mFramesPerPacket * seconds;        [self setBufferByteSize: numPacketForTime * maxPacketSize];    }else{        [self setBufferByteSize:            maxBufferSize > maxPacketSize ? maxBufferSize : maxPacketSize];    }    if(bufferByteSize > maxBufferSize && bufferSize > maxPacketSize){        [self setBufferByteSize: maxBufferSize];    }else{        if(bufferByteSize < minBufferSize) {            [self setBufferByteSize:minBufferSize];        }    }    [self setNumPacketsToRead: self.bufferByteSize / maxPacketSize];}

Data Format Conversion

有三种类型的转换:
解压音频数据转换为linear PCM,将Linear PCM转化为其他格式,不同linear PCM直接转换。

Audio Queue Service提供了自动的转换。Audio Codec Service(Mac only)可以让开发者字节写编解码器。

Sound Files

创建一个文件

AudioFileCreateWithURL(    audioFileURL,    kAudioFileCAFType,    &audioFormat,    kAudioFileFlags_EraseFile,    &audioFileID);

需要提供文件路径,文件格式,音频数据格式,文件覆盖flag,函数返回audioFileID。

打开一个文件
打开一个文件使用AudioFileOpenURL,返回一个audioFileID,然后利用这个audioFileID再去获取其他属性,常用的属性有:

  • kAudioFilePropertyFileFormat,
  • kAudioFilePropertyDataFormat,
  • kAudioFilePropertyMagicCookieData,
  • kAudioFilePropetyChannelLayout,

读写文件

在iOS上使用Audio File Service来读写音频文件,可以直接将packets写入文件。
Audio File Stream Service也可以做音频文件的读写。

还有一个叫Extend Audio File Service的,将Audio文件的读写和格式转合并成一个接口,方便使用。

iPhone上的音频文件格式

CAF(Core Audio Format)是iOS和OS X上默认的音频文件格式。文件格式定义:Apple Core Audio Format Specification 1.0.

Sound Stream

音频流和音频文件的不同之处在于,应用程序可能不知道音频流的开始和结束信息,传过来的数据可能是不完整的,或者有延迟的。

AudioFileStreamService用于处理音频流的复杂性。

AudioFileStreamService的使用:

创建一个audio file stream 对象,这个对象是一个proxy,AudioFileStreamID类型。
当AudioFileStreamService解析了音频流的数据后,会将相应的属性设置给这个proxy对象。可以通过前边所说的属性获取方式获取到。

应用程序需要给AudioFileStreamService提供两个回调:

第一个回调是proxy对象的属性变价回调,比如用户点击了Start按钮,流开始播放,有足够多的packets到达等。另外一个是音频数据的处理,当Audio File Stream Service攒够了足够多的packets,会调用这个回调,你可以有机会处理音频数据,也可以直接将数据返回给Audio File Stream Service。

Audio Session

在iOS上Audio Session是应用程序和系统的接口。通过Audio Session可以解决以下几个问题:

  • 应用程序被打断时,如何处理?比如来电话了。
  • 和其他应用程序的关系,是共享设备还是抢夺设备。
  • 如何处理audio route 的改变,比如插入了耳机。

Audio Session的API有3中类型,一种解决上述问题1和3的,被打断或者Audio Router变化;一种是应用程序本身的行为,比如锁屏后是否继续播放;还有一种是查询硬件信息的,比如采样率,通道数,是否有输入设备等。

Audio Session的几个默认行为:锁屏停止,静音停止,抢夺设备。

Audio Session有两种模式,activation和deactivation,只有在activation模式下才会播放声音。Audio Sesion在被打断后的默认模式是activation的,所以需要自己reactive,以便于继续播放。如果采用的API是AVAudioPlayer,那么自动reactive;如果用的是Audio Queue Service 或者I/O Unit,就需要自己处理。

AVAudioPlayer

AVAudioPlayer 提供了一组OC接口,如果应用程序不使用立体声,对同步要求不高,也不处理音频流,那么使用AVAudioPlayer可以更简单的完成任务。

AVAudioPlayer可以做的是:

  • 播放声音,从文件或者内存。
  • 循环播放
  • 同时播放多个声音,并且单独控制他们的音量。
  • 前进或者后退
  • 获取音量大小

例子:

NSString *soundFilePath = [NSBundle mainBundle] pathForResouce:@"sound" ofType:@"wav"];NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: soundFilePath];AVAudioPalyer *newPlayer = [[AVAudioPlayer alloc] initWithContentOfURL: fileURL error:nil];self.player = newPlayer;[self.palyer prepearToPlay];[self.play setDelegate: self];

Audio Queue Service

Audio Queue Service 就可以录音和解码和同步了,还不需要和硬件接口打交道。

一般来说Audio Queue Service和Auido File Service或者Audio File Stream Service一起使用。

Audio Queue Service 的原理图:

使用Audio Queue Service也要创建一个Audio Queue Object,同样是一个Proxy,类型为AudioQueueRef。
创建一个Record Audio Queue Object 使用AudioQueueNewInput
创建一个Playback Audio Queue Object 使用AudioQueueNewOutput

例子:

static const int kNumberBuffers = 3;//定义一个结构,用于管理和Audio相关的信息struct myAQStruct {    AudioFileID                 mAudioFile;    CAStreamBasicDescription    mDataFormat;    AudioQueuRef                mQueue;    AudioQueueBufferRef         mBuffers[kNumberBuffers];    SInt64                      mCurrentPacket;    UInt32                      mNumPacketToRead;    AudioStreamPacketDescription    *mPackectDescs;    bool                        mDone;};//定义一个callback函数static void AQTestBufferCallback(    void *inUserData,    AudioQueueRef inAQ;    AudioQueueBufferRef inCompleteAQBuffer) {    myAQStruct *myInfo = (myAQStruct *)inUserData;    if(myInfo->mDone) return;    UInt32 numBytes;    UInt32 nPackets = myInfo->mNumPacketsToRead;    AudioFileReadPackets(        myInfo->mAudioFile,        false,        &numBytes;        myInfo->mPacketDescs,        myInfo->mCurrentPacket,        &nPackets,        inCompleteAQBuffer->mAudioData    );    if(nPackets > 0) {        inCompleteAQBuffer->myAudioDataByteSize = numBytes;        AduioQueueEnqueueBuffer (            inAQ,            inCompleteAQBuffer,            (myInfo->mPacketDescs ? nPackets : 0),             myInfo->mPacketDescs        );    } else {        AudioQueueStop(            myInfo->mQueue,            false        );        myInfo->mDone = true;    }}//创建一个audio queue objectAudioQueueNewOutput (    &myInfo.mDataFormat,    AQTestBufferCallback,    &myInfo,    CFRunLoopGetCurrent(),    kCFRunLoopCommonModes,    0,    &myInfo.mQueue);

解释:创建一个audio output object之后,它会调用回调来获取数据,在回调中,使用Audio File Service来读取数据并通过函数AudioQueueEnququBuffer传给AudioQueue。而整个过程中的变量由myAQStruct来管理。

调整音量大小

调整音量大小有两种方法:

一种是直接使用AudioQueueSetParameter设置:

Float32 volume = 1;AudioQueueSetParameter(    myAQStruct.audioQueuObject,    kAudioQueueParam_Volume,    volume);

也可以在Enqueu buffer 的时候设置,使用AudioQueueEnqueueBufferWithParameters这个函数,instead of AduioQueueEnqueueBuffer。

获取音量大小

在这里要提一下,音量大小是对一个packet来说的,因为一个packet可能有多个frame,所以音量大小是一个范围。表示一个音量大小的结构体是:

typedef struct AudioQueueLevelMeterState {    Float32 mAveragePower;    Float32 mPeakPower;};

使用函数AudioQueueGetParameter获取kAudioQueueProperty_CurrentLevelMeterDB属性,可以得到一个AudioQueueLevelMeterState的数组,每个代表一个channel在这个packet上的音量范围。

同时播放

为每一个声音创建一个audio queue Object,然后在enqueue的时候使用AudioQueueEnqueueBufferWithParameters来同步播放时间。
在iOS上AAC,ALAC,MP3格式的声音在同一时间只能播放一个。(Audio format is critical when you play sounds simultaneously on iPhone or iPod touch. This is because playback of certain compressed formats in iOS employs an efficient hardware codec. Only a single instance of one of the following formats can play on the device at a time)

Linear PCM,IMA/ADPCM可以同时播放。

System Sound

system sound适合用在那些短的,不需要音量控制,positioning,audio session的场合。在iOS上,播放系统声音最长30s,这是最简单的播放声音的方式。

播放系统声音仅需要2步:将声音文件注册得到一个sound ID,然后使用AudioServicePlaySystemSound就可以了

SystemSoundID mySSID;CFURLRef myURLRef;//打开文件...OSStatus error = AudioServiceCreateSystemSoundID(myURLRef, &mySSID);AudioServicesAddSyetemSoundCompletion (    mySSID,    NULL,    NULL,    MyCompletionCallback,    (void *)myURLRef);AudioServicesPlaySystemSound(mySSID);

Core Audio Plug-ins

Core Audio使用Plugh-ins方案来处理音频数据,iOS 上系统提供了这些plug-ins,OS X上可以自己创建。

iOS上的Audio Unit:

  • 3D mixer unit。允许任意数目的单通道输入,8bit 或者16bit 的linear PCM。合成立体声的output,8.24 bit fixed point PCM。3D mixer Unit能处理采样率的转换,各个通道的音量,距离衰减等。3D mixer unit的类型是:kAudioUnitSubType_AU3DMixerEmbedded
  • Multichannel mixer unit。允许任意数目的单通道输入,8bit 或者16bit 的linear PCM。合成立体声的output,8.24 bit fixed point PCM。只能控制各通道的音量。Multichannel mixer unit的类型是:kAudioUnitSubType_MultiChannelMixer
  • Converter Unit。输入采样率,bit depth,bit-format,转换成8.24-bit fixed point PCM,或者反过来。Converter Unit的类型是kAudioUnitSubType_AUConverter
  • I/O Unit。提供实时的输入输出,自动转换采样率。I/O Unit的类型是kAudioUnitSubType_RemoteIO
  • iPod EQ Unit。一个简单的均衡器。类型是kAudioUnitSubType_AUiPodEQ

OS X上提供了40个Audio Unit。可以在这里找到说明: System-Supplied Audio Units in OS X
OS X上的Audio Unit使用了noninterleaved 32-bit floating point linear PCM格式的数据。

Audio Unit 的使用

host application使用Audio Unit Service来发现和加载Audio Unit。

Audio Unit的标识符由3部分组成:type,subtype,manufacturer code。Audio Unit的功能和配置是通过属性来获取的,有些属性是必须的,有些属性是可以自定义的。
Audio Unit使用parameter机制来实时调节unit 的行为。
Audio Unit使用Callback来通知host application状态改变。
更多的Audio Unit细节在 Audio Unit Programming Guide.

iOS 上的编解码器

iOS: unrestricted playback audio formats iLBC (internet Low Bitrate Codec, also a speech codec) IMA/ADPCM (also known as IMA-4) Linear PCM uLaw and aLaw

这些解码器是可以共用硬件的。

iOS: restricted playback audio formats AAC Apple Lossless MP3

这些解码器是独占硬件的。

录音的编码器

iOS: recording audio formats iLBC (internet Low Bitrate Codec, also a speech codec) IMA/ADPCM (also known as IMA-4) Linear PCM uLaw and aLaw

由于性能原因,录音不支持MP3,AAC等编码格式。

一组完成一个特性功能的Audio Unit形成了一个Audio Processing Graph。一个Audio Processing Graph一般以输出的I/O Unit结束,可以不输出到硬件,直接输出数据,以便于下一个Audio Processing Graph使用。I/O Unit是Audio Processing Graph中唯一可以启动或者停止数据流的Audio Unit 类型。

OS X上的Auido Unit
- AUHAL是向特殊的硬件输入输出的。
- Generic I/O Unit可以将Audio Processing Graph的输出数据传给应用程序或者下一个Audio Processing Graph
- System I/O Unit 用于播放系统声音
- Defult I/O Unit is for other audio input and output… other。

一个Audio Processing Graph:

MIDI Service

MIDI endpoint 是MIDIEndPointRef类型,代表了一个16channel的MIDI数据流的源或者终点。可以是物理设备,也可以是虚拟的。

MIDI 的驱动经常把多个MIDI endpoint合并成逻辑组,这个组叫做MIDI entities(MIDIEntityRef类型)。比如一个输入的MIDI endpoint和一个输入的MIDI endpoint成为一组,用于和一个MIDI设备双向交互。

一个MIDI设备由一个Core MIDI device object表示(MIDIDeviceRef),每个MIDI device Object中有多个MIDI entities。

MIDI Server是Application和设备中间层,在自己的进程中运行,如下图所示:

MIDI的设备驱动在MIDIServer进程中运行,但是PCI接口的设备不能完全在用户空间运行。所以如果你是非PCI接口的MIDI设备生产商,提供一个CFPlugin给MIDI Servcer就是驱动了。

Music Player Service

track,是一个MIDI的流。包含了一系列的事件,这些事件可能是MIDI data,也可能是Core Audio事件,用户自定义事件。可以认为一个track是某一个乐器的乐谱。
Sequence,是多个track。一般一个Sequence包含有一个tempo track,用于同步其他track。可以认为Sequence是整个乐谱。

使用Music Player Service播放一个Sequence。如果想制作声音,需要将track发送到instrument unit,或者MIDI设备中。

有关MIDI更多的知识,见Handling MIDI Data.

Core Audio Clock Service
Core Audio Clock Service提供了参考时钟。可以手动的开始和停止时钟也可以让时钟根据事件开始或者停止。

时钟提供的时间有多种格式:秒,beats,SMTPTE time, audio sample time和bar-beat time。根据不同的场景方便使用。这些格式是可以相互转换的:

Reading And Writing Audio Data

从一个声音文件中读取数据然后转成linear PCM格式可以使用Extented Audio File Service。Extented Audio File Service使用了Audio File Service和Audio Converter Service来完成这件事。

也可以直接使用Audio File Service读取数据,然后使用自己的解码器。总之都是先将Audio 数据转化成linear PCM格式的数据,再做其他处理。

Converting Audio Data Formats

任意两种格式的转换都需要linear PCM格式作为中间格式。比如将MP3格式转成AAC格式,需要有两个converter,一个是MP3到linear PCM,另外一个是linear PCM到AAC。

Interfacing with Hardware

一般情况下,使用系统提供的3个Audio Unit就可以了,default,system和AUHAL。也可以使用HAL,HAL的接口在AudioHardware.h中。

一个AUHAL一次只能和一个设备连接。有些硬件在系统中被表示为多个设备,这时候使用Aggregate Device方式。也可以将两个原来不同的硬件合并成一个设备。

有两种方式使用Aggregate Device,一种是使用系统的MIDI配置,这个作用是全局的。另外一种方式是使用HAL接口,编程实现,这种可以实现local的。

Aggregate Device的局限性:
- 所有的sub Device必须使用同样的采样率,数据必须mixable
- 不能控制音量
- 不能设为默认设备,除非sub device都是默认设备
- 只有IOAudio family的驱动类型可以使用Aggregate device

Host Audio Units

host application 需要用Component Manager来加载Audio Unit。一般情况下,使用NewAUGraph来连接Audio Unit。

  1. 调用NewAUGraph创建一个新的grpah object。
  2. 调用AUGraphNewNode添加Audio Unit。
  3. 调用AUGraphOpen,之后可以设置Audio Unit的属性。
  4. 调用AUGraphConnectNodeInput连接Audio Unit,必须以一个Output Unit 结尾。
  5. 调用AUGraphInitialize初始化各个Audio Unit
  6. 调用AUGraphStart开始
  7. 调用AUGraphStop结束
  8. 调用AUGraphUninitialize回到初始状态
  9. 调用AUGraphClose释放Audio Unit.
  10. 调用DisposeAUGraph销毁

Handle MIDI Data

使用MusicSequenceLoadSMFWithFlags或者MusicSequenceLoadSMFDataWithFlags来读取SMF(Standard MIDI Format)文件。

根据指定的flag可以将MIDI数据放到一个track中,或者每个channel放到一个track中,形成一个Sequence。默认是第二种方式。

一旦将MIDI数据读到了Sequence中就可以使用music player service来播放了。一个Sequence还必须和一个audio processing graph关联,一个track对应一个instrument unit。和Sequence关联的music player自己和audio processing graph通信。

Handle Audio and MIDI Data Together

原创粉丝点击