ios播放PCM數據 – iPhone手機開發技術文章 iPhone軟體開發教學課程

[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 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *