今天是周末乘着这点空闲时间总结一下ios的音频播放
1、AVFoundation
该类既可以播放音频又可以播放视频。常用的音频输出类为AVAudioSession
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
实例化对象之后就可以设置其Category属性了
AVAudioSessionCategorySoloAmbient
这个类别非常像AVAudioSessionCategoryAmbient类别,除了会停止其他程序的音频回放,比如iPod程序。当设备被设置为静音模式,你的音频回放将会停止。
AVAudioSessionCategoryRecord
这会停止其他应用的声音(比如iPod)并让你的应用也不能初始化音频回放,即使你的设备屏幕被用户锁定了,应用的录音仍会继续。
AVAudioSessionCategoryPlayback
这个类别会静止其他应用的音频回放,即使屏幕被锁定或者设备为静音模式,音频回放都会继续
AVAudioSessionCategoryPlayAndRecord
这个类别允许你的应用中同时进行声音的播放和录制。即使屏幕被锁定或者设备为静音模式,音频回放和录制都会继续
AVAudioSessionCategoryAudioProcessing
个类别用于应用中进行音频处理的情形,而不是音频回放或录制,设置了这种模式,你在应用中就不能播放和录制任何声音
AVAudioSessionCategoryAmbient
这个类别不会停止其他应用的声音,相反,它允许你的音频播放于其他应用的声音之上,用户锁屏时,你的应用将停止所有正在回放的音频。仅当你的应用是唯一播放该音频文件的应用时,静音模式将停止你程序的音频回放
2、AudioToolBox
该类提供了读取本地音频数据包进行音频播放功能。该类还提供了检测设备的输入输出属性。(拔插耳机)
读取本地数据包进行音频播放功能是调用函数AudioFileOpenURL
具体做法请看代码
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AudioToolbox/AudioFile.h>
#define NUM_BUFFERS 3
@interface AudioPlayer : NSObject
{
//播放音频文件ID
AudioFileID audioFile;
//音频流描述对象
AudioStreamBasicDescription dataFormat;
//音频队列
AudioQueueRef queue;
SInt64 packetIndex;
UInt32 numPacketsToRead;
UInt32 bufferByteSize;
AudioStreamPacketDescription *packetDescs;
AudioQueueBufferRef buffers[NUM_BUFFERS];
}
//定义队列为实例属性
@property AudioQueueRef queue;
//播放方法定义
- (void) play:(CFURLRef) path;
//定义缓存数据读取方法
- (void) audioQueueOutputWithQueue:(AudioQueueRef)audioQueue
queueBuffer:(AudioQueueBufferRef)audioQueueBuffer;
//定义回调(Callback)函数
static void BufferCallback(void *inUserData, AudioQueueRef inAQ,
AudioQueueBufferRef buffer);
//定义包数据的读取方法
- (UInt32)readPacketsIntoBuffer:(AudioQueueBufferRef)buffer;
@end
#import "AudioPlayer.h"
static UInt32 gBufferSizeBytes = 0x10000;
@implementation AudioPlayer
@synthesize queue;
// 回调(Callback)函数的实现
static void BufferCallback(void *inUserData, AudioQueueRef inAQ,
AudioQueueBufferRef buffer)
{
AudioPlayer* player = (__bridge AudioPlayer*)inUserData;
[player audioQueueOutputWithQueue:inAQ queueBuffer:buffer];
}
//初始化方法(为NSObject中定义的初始化方法)
- (id) init {
for(int i=0; i<NUM_BUFFERS; i++) {
AudioQueueEnqueueBuffer(queue,buffers[i],0,nil);
}
return self;
}
//缓存数据读取方法的实现
- (void) audioQueueOutputWithQueue:(AudioQueueRef)audioQueue
queueBuffer:(AudioQueueBufferRef)audioQueueBuffer {
OSStatus status;
// 读取包数据
UInt32 numBytes;
UInt32 numPackets = numPacketsToRead;
status = AudioFileReadPackets(
audioFile, NO, &numBytes, packetDescs,
packetIndex, &numPackets, audioQueueBuffer->mAudioData);
// 成功读取时
if (numPackets > 0) {
//将缓冲的容量设置为与读取的音频数据一样大小(确保内存空间)
audioQueueBuffer->mAudioDataByteSize = numBytes;
// 完成给队列配置缓存的处理
status = AudioQueueEnqueueBuffer(
audioQueue, audioQueueBuffer, numPackets, packetDescs);
// 移动包的位置
packetIndex += numPackets;
}
}
//音频播放方法的实现
-(void) play:(CFURLRef)path
{
UInt32 size, maxPacketSize;
char *cookie;
int i;
OSStatus status;
// 打开音频文件
status = AudioFileOpenURL(path, kAudioFileReadPermission, 0, &audioFile);
if (status != noErr) {
// 错误处理
return;
}
// 取得音频的
// 取得音频数据格式
size = sizeof(dataFormat);
AudioFileGetProperty(audioFile, kAudioFilePropertyDataFormat,
&size, &dataFormat);
// 创建播放用的音频队列
AudioQueueNewOutput(&dataFormat, BufferCallback,
(__bridge void *)(self), nil, nil, 0, &queue);
//计算单位时间包含的包数
if (dataFormat.mBytesPerPacket == 0 || dataFormat.mFramesPerPacket == 0)
{
size = sizeof(maxPacketSize);
AudioFileGetProperty(audioFile,
kAudioFilePropertyPacketSizeUpperBound, &size, &maxPacketSize);
if (maxPacketSize > gBufferSizeBytes)
{
maxPacketSize = gBufferSizeBytes;
}
// 算出单位时间内含有的包数
//
numPacketsToRead = gBufferSizeBytes / maxPacketSize;
packetDescs = malloc(sizeof(AudioStreamPacketDescription) * numPacketsToRead);
}
else
{
numPacketsToRead = gBufferSizeBytes / dataFormat.mBytesPerPacket;
packetDescs = nil;
}
//设置Magic Cookie,参见第二十七章的相关介绍
AudioFileGetPropertyInfo(audioFile,
kAudioFilePropertyMagicCookieData, &size, nil);
if (size > 0)
{
cookie = malloc(sizeof(char) * size);
AudioFileGetProperty(audioFile,
kAudioFilePropertyMagicCookieData, &size, cookie);
AudioQueueSetProperty(queue,
kAudioQueueProperty_MagicCookie, cookie, size);
free(cookie);
}
// 创建并分配缓存空间
packetIndex = 0;
for (i = 0; i < NUM_BUFFERS; i++)
{
AudioQueueAllocateBuffer(queue, gBufferSizeBytes, &buffers[i]);
//读取包数据
if ([self readPacketsIntoBuffer:buffers[i]] == 0)
{
break;
}
}
Float32 gain = 1.0;
//设置音量
AudioQueueSetParameter (
queue,
kAudioQueueParam_Volume,
gain
);
//队列处理开始,此后系统会自动调用回调(Callback)函数
AudioQueueStart(queue, nil);
}
- (UInt32)readPacketsIntoBuffer:(AudioQueueBufferRef)buffer
{
UInt32 numBytes, numPackets;
// 从文件中接受包数据并保存到缓存(buffer)中
numPackets = numPacketsToRead;
AudioFileReadPackets(audioFile, NO, &numBytes, packetDescs,
packetIndex, &numPackets, buffer->mAudioData);
if (numPackets > 0) {
buffer->mAudioDataByteSize = numBytes;
AudioQueueEnqueueBuffer(queue, buffer,
(packetDescs ? numPackets : 0), packetDescs);
packetIndex += numPackets;
}
return numPackets;
}
@endl
检测声音的输出设备
设备自身的外放(iTouch/iPad/iPhone都有),一种是当前是否插入了带外放的耳机
通过查看文档,IOS的所有输出设备有
- /* Known values of route:
- * "Headset"
- * "Headphone"
- * "Speaker"
- * "SpeakerAndMicrophone"
- * "HeadphonesAndMicrophone"
- * "HeadsetInOut"
- * "ReceiverAndMicrophone"
- * "Lineout"
-
- CFStringRef route;
- UInt32 propertySize = sizeof(CFStringRef);
- AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &propertySize, &route);
检测rout中是否包含上述设备的字符名称。
3、AudioUnit
该类主要提供了remote IO声音的输入输入输出功能,不多说了,附上代码段
// comment组件,个人理解是ios的音频设备一样,初始化这个设备,
// (要想播放音频要得有东西啊,对不)
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
// Get component
inputComponent = AudioComponentFindNext(NULL, &desc);
// Get audio units
AudioComponentInstanceNew(inputComponent, &audioUnit);
// 音频流的一些设置,从远处过来的数据流需要配置一下。
AudioStreamBasicDescription audioFormat = {0};
audioFormat.mSampleRate = 8000.00;
audioFormat.mFormatID = kAudioFormatLinearPCM;
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked|kAudioFormatFlagIsNonInterleaved;
audioFormat.mFramesPerPacket = 1;
audioFormat.mChannelsPerFrame = 1;
audioFormat.mBitsPerChannel = 16;
audioFormat.mBytesPerPacket = 2;
audioFormat.mBytesPerFrame = 2;
// 设置输入输出的属性
AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
kOutputBus,
&audioFormat,
sizeof(audioFormat));AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
kInputBus,
&audioFormat,
sizeof(audioFormat));
// Set 回调函数名称:pushDataIntoUnit
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = pushDataIntoUnit;
callbackStruct.inputProcRefCon = self;
// 初始化unit并开始播放
AudioUnitInitialize(audioUnit);
AudioOutputUnitStart(audioUnit);
// 接下来就是回调函数啦
static OSStatus pushDataIntoUnit (
void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData
);个人总结到此结束,睡觉