本例子实现了将mp3格式转换为pcm播放
Git地址
一、源文件处理
1、文件的基本操作
1.1、打开文件
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"周杰伦 - 晴天" ofType:@"mp3"];
NSURL *url = [NSURL fileURLWithPath:filePath];
OSStatus status = AudioFileOpenURL((__bridge CFURLRef)url, kAudioFileReadPermission, 0, &audioFileID);//
if (status != noErr)
{
NSLog(@"打开文件失败 %@", url);
error = [NSError errorWithDomain:@"打开文件失败" code:1008601 userInfo:nil];
if (errorBlock)
{
errorBlock(error);
}
return;
}
代码解读:
AudioFileOpenURL ( CFURLRef inFileRef,//文件路径
AudioFilePermissions inPermissions,//文件的允许使用方式,
AudioFileTypeID inFileTypeHint,
AudioFileID __nullable * __nonnull outAudioFile//AudioFile实例对应的AudioFileID,这个ID需要保存起来作为后续一些方法的参数使用
)
以下为文件权限,可读、可写、可读写,在这里为可读权限,如果文件打开后进行了使用方式以外的操作,就得到kAudioFilePermissionsError错误码
typedef CF_ENUM(SInt8, AudioFilePermissions) {
kAudioFileReadPermission = 0x01,
kAudioFileWritePermission = 0x02,
kAudioFileReadWritePermission = 0x03
};
1.2、读取文件格式
uint32_t size = sizeof(AudioStreamBasicDescription);
status = AudioFileGetProperty(audioFileID,
kAudioFilePropertyDataFormat,
&size,
&audioFileFormat);
if (status != noErr)
{
error = [NSError errorWithDomain:[NSString stringWithFormat:@"读取文件格式出错,error status %d", (int)status] code:1008602 userInfo:nil];
if (errorBlock)
{
errorBlock(error);
}
return;
}
1.3、读取文件数据包数量
size = sizeof(packetNums);
status = AudioFileGetProperty(audioFileID,
kAudioFilePropertyAudioDataPacketCount,
&size,
&packetNums);
if (error != noErr)
{
error = [NSError errorWithDomain:[NSString stringWithFormat:@"读取文件packets总数出错,error status %d", (int)status] code:1008603 userInfo:nil];
if (errorBlock)
{
errorBlock(error);
}
return;
}
1.4、读取单个数据包的最大帧数
maxFramesPerPacket = audioFileFormat.mFramesPerPacket;
if (maxFramesPerPacket == 0) {
size = sizeof(maxFramesPerPacket);
status = AudioFileGetProperty(audioFileID, kAudioFilePropertyMaximumPacketSize, &size, &maxFramesPerPacket);
if (status != noErr)
{
error = [NSError errorWithDomain:[NSString stringWithFormat:@"读取单个packet的最大数量出错,error status %d", (int)status] code:1008604 userInfo:nil];
if (errorBlock)
{
errorBlock(error);
}
return;
}
if(status ==noErr && maxFramesPerPacket == 0)
{
error = [NSError errorWithDomain:@"AudioFileGetProperty error or sizePerPacket = 0" code:1008605 userInfo:nil];
if (errorBlock)
{
errorBlock(error);
}
return;
}
}
上述都使用到
AudioFileGetProperty( AudioFileID inAudioFile,
AudioFilePropertyID inPropertyID,
UInt32 *ioDataSize,
void *outPropertyData)
- 第一个参数 顾名思义是文件的ID,打开文件的时候就已经拿到
- 第二个参数 是文件的属性ID,用于获取文件的那个属性
- 第三个参数 文件的大小
- 第四个参数 文件属性数据
2、音频文件的属性
ExtAudioFilePropertyID | 意义 | 结构类型 |
---|---|---|
kExtAudioFileProperty_FileDataFormat | 音频数据的格式 | AudioStreamBasicDescription |
kExtAudioFileProperty_FileChannelLayout | 音频数据的通道格式 | AudioChannelLayout |
kExtAudioFileProperty_ClientMaxPacketSize | 读出后音频数据最大的Packet大小 | UInt32 |
具体属性表
二、转换文件格式
1、创建AudioConverte
原来音频格式描述到目标音频格式的转化
AudioConverterNew( const AudioStreamBasicDescription * inSourceFormat,
const AudioStreamBasicDescription * inDestinationFormat,
AudioConverterRef __nullable * __nonnull outAudioConverter)
2、转码方法
1、把音频数据从线性PCM转换成其他格式
AudioConverterConvertBuffer( AudioConverterRef inAudioConverter,
UInt32 inInputDataSize,
const void * inInputData,
UInt32 * ioOutputDataSize,
void * outOutputData
2、使用此方法实现所有音频格式之间的转换
OSStatus AudioConverterFillComplexBuffer(
AudioConverterRef inAudioConverter,
AudioConverterComplexInputDataProc inInputDataProc,
void *inInputDataProcUserData,
UInt32 *ioOutputDataPacketSize,
AudioBufferList *outOutputData,
AudioStreamPacketDescription *outPacketDescription
);
3、把音频数据从线性PCM转换成其他格式,而转换的格式必须具有相同的采样率
OSStatus AudioConverterConvertComplexBuffer (
AudioConverterRef inAudioConverter,
UInt32 inNumberPCMFrames,
const AudioBufferList *inInputData,
AudioBufferList *outOutputData
);
三、播放目标文件
- 设置缓冲区
- 设置AVAudioSession
- 设置音频格式描述以及音频单元描述
- 设置应用描述
- 设置回调函数。