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 object
AudioQueueNewOutput (
&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。
- 调用NewAUGraph创建一个新的grpah object。
- 调用AUGraphNewNode添加Audio Unit。
- 调用AUGraphOpen,之后可以设置Audio Unit的属性。
- 调用AUGraphConnectNodeInput连接Audio Unit,必须以一个Output Unit 结尾。
- 调用AUGraphInitialize初始化各个Audio Unit
- 调用AUGraphStart开始
- 调用AUGraphStop结束
- 调用AUGraphUninitialize回到初始状态
- 调用AUGraphClose释放Audio Unit.
- 调用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通信。