[cpp]
//
// MainViewController.h
// RawAudioDataPlayer
//
// Created by SamYou on 12-8-18.
// Copyright (c) 2012年 SamYou. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <AudioToolbox/AudioToolbox.h>
#define QUEUE_BUFFER_SIZE 4 //隊列緩沖個數
#define EVERY_READ_LENGTH 1000 //每次從文件讀取的長度
#define MIN_SIZE_PER_FRAME 2000 //每偵最小數據長度
@interface MainViewController : UIViewController
{
AudioStreamBasicDescription audioDescription;///音頻參數
AudioQueueRef audioQueue;//音頻播放隊列
AudioQueueBufferRef audioQueueBuffers[QUEUE_BUFFER_SIZE];//音頻緩存
NSLock *synlock ;///同步控制
Byte *pcmDataBuffer;//pcm的讀文件數據區
FILE *file;//pcm源文件
}
static void AudioPlayerAQInputCallback(void *input, AudioQueueRef inQ, AudioQueueBufferRef outQB);
-(void)onbutton1clicked;
-(void)onbutton2clicked;
-(void)initAudio;
-(void)readPCMAndPlay:(AudioQueueRef)outQ buffer:(AudioQueueBufferRef)outQB;
-(void)checkUsedQueueBuffer:(AudioQueueBufferRef) qbuf;
@end
[cpp]
//
// MainViewController.m
// RawAudioDataPlayer
//
// Created by SamYou on 12-8-18.
// Copyright (c) 2012年 SamYou. All rights reserved.
//
#import "MainViewController.h"
@interface MainViewController ()
@end
@implementation MainViewController
#pragma mark -
#pragma mark life cycle
- (id)init
{
self = [super init];
if (self) {
NSString *filepath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"audio.raw"];
NSLog(@"filepath = %@",filepath);
NSFileManager *manager = [NSFileManager defaultManager];
NSLog(@"file exist = %d",[manager fileExistsAtPath:filepath]);
NSLog(@"file size = %lld",[[manager attributesOfItemAtPath:filepath error:nil] fileSize]) ;
file = fopen([filepath UTF8String], "r");
if(file)
{
fseek(file, 0, SEEK_SET);
pcmDataBuffer = malloc(EVERY_READ_LENGTH);
}
else{
NSLog(@"!!!!!!!!!!!!!!!!");
}
synlock = [[NSLock alloc] init];
}
return self;
}
-(void)loadView
{
[super loadView];
self.view.backgroundColor = [UIColor grayColor];
UIButton *button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button1.frame = CGRectMake(10, 10, 300, 50);
[button1 setTitle:@"button1" forState:UIControlStateNormal];
[button1 setTitle:@"button1" forState:UIControlStateHighlighted];
[button1 addTarget:self action:@selector(onbutton1clicked) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button1];
UIButton *button2 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button2.frame = CGRectMake(10, 70, 300, 50);
[button2 setTitle:@"button2" forState:UIControlStateNormal];
[button2 setTitle:@"button2" forState:UIControlStateHighlighted];
[button2 addTarget:self action:@selector(onbutton2clicked) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button2];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
-(void)onbutton1clicked
{
[self initAudio];
NSLog(@"onbutton1clicked");
AudioQueueStart(audioQueue, NULL);
for(int i=0;i<QUEUE_BUFFER_SIZE;i++)
{
[self readPCMAndPlay:audioQueue buffer:audioQueueBuffers[i]];
}
/*
audioQueue使用的是驅動回調方式,即通過AudioQueueEnqueueBuffer(outQ, outQB, 0, NULL);傳入一個buff去播放,播放完buffer區後通過回調通知用戶,
用戶得到通知後再重新初始化buff去播放,周而復始,當然,可以使用多個buff提高效率(測試發現使用單個buff會小卡)
*/
}
-(void)onbutton2clicked
{
NSLog(@"onbutton2clicked");
}
#pragma mark -
#pragma mark player call back
/*
試了下其實可以不用靜態函數,但是c寫法的函數內是無法調用[self ***]這種格式的寫法,所以還是用靜態函數通過void *input來獲取原類指針
這個回調存在的意義是為了重用緩沖buffer區,當通過AudioQueueEnqueueBuffer(outQ, outQB, 0, NULL);函數放入queue裡面的音頻文件播放完以後,通過這個函數通知
調用者,這樣可以重新再使用回調傳回的AudioQueueBufferRef
*/
static void AudioPlayerAQInputCallback(void *input, AudioQueueRef outQ, AudioQueueBufferRef outQB)
{
NSLog(@"AudioPlayerAQInputCallback");
MainViewController *mainviewcontroller = (MainViewController *)input;
[mainviewcontroller checkUsedQueueBuffer:outQB];
[mainviewcontroller readPCMAndPlay:outQ buffer:outQB];
}
-(void)initAudio
{
///設置音頻參數
audioDescription.mSampleRate = 8000;//采樣率
audioDescription.mFormatID = kAudioFormatLinearPCM;
audioDescription.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioDescription.mChannelsPerFrame = 1;///單聲道
audioDescription.mFramesPerPacket = 1;//每一個packet一偵數據
audioDescription.mBitsPerChannel = 16;//每個采樣點16bit量化
audioDescription.mBytesPerFrame = (audioDescription.mBitsPerChannel/8) * audioDescription.mChannelsPerFrame;
audioDescription.mBytesPerPacket = audioDescription.mBytesPerFrame ;
///創建一個新的從audioqueue到硬件層的通道
// AudioQueueNewOutput(&audioDescription, AudioPlayerAQInputCallback, self, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &audioQueue);///使用當前線程播
AudioQueueNewOutput(&audioDescription, AudioPlayerAQInputCallback, self, nil, nil, 0, &audioQueue);//使用player的內部線程播
////添加buffer區
for(int i=0;i<QUEUE_BUFFER_SIZE;i++)
{
int result = AudioQueueAllocateBuffer(audioQueue, MIN_SIZE_PER_FRAME, &audioQueueBuffers[i]);///創建buffer區,MIN_SIZE_PER_FRAME為每一偵所需要的最小的大小,該大小應該比每次往buffer裡寫的最大的一次還大
NSLog(@"AudioQueueAllocateBuffer i = %d,result = %d",i,result);
}
}
-(void)readPCMAndPlay:(AudioQueueRef)outQ buffer:(AudioQueueBufferRef)outQB
{
[synlock lock];
int readLength = fread(pcmDataBuffer, 1, EVERY_READ_LENGTH, file);//讀取文件
NSLog(@"read raw data size = %d",readLength);
outQB->mAudioDataByteSize = readLength;
Byte *audiodata = (Byte *)outQB->mAudioData;
for(int i=0;i<readLength;i++)
{
audiodata[i] = pcmDataBuffer[i];
}
/*
將創建的buffer區添加到audioqueue裡播放
AudioQueueBufferRef用來緩存待播放的數據區,AudioQueueBufferRef有兩個比較重要的參數,AudioQueueBufferRef->mAudioDataByteSize用來指示數據區大小,AudioQueueBufferRef->mAudioData用來保存數據區
*/
AudioQueueEnqueueBuffer(outQ, outQB, 0, NULL);
[synlock unlock];
}
-(void)checkUsedQueueBuffer:(AudioQueueBufferRef) qbuf
{
if(qbuf == audioQueueBuffers[0])
{
NSLog(@"AudioPlayerAQInputCallback,bufferindex = 0");
}
if(qbuf == audioQueueBuffers[1])
{
NSLog(@"AudioPlayerAQInputCallback,bufferindex = 1");
}
if(qbuf == audioQueueBuffers[2])
{
NSLog(@"AudioPlayerAQInputCallback,bufferindex = 2");
}
if(qbuf == audioQueueBuffers[3])
{
NSLog(@"AudioPlayerAQInputCallback,bufferindex = 3");
}
}
@end