使用 audioqueue 播放PCM数据

    //
    //  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  
    //
    //  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  

源代码下载地址      http://download.csdn.net/detail/samguoyi/4509544

时间: 2024-07-31 10:39:38

使用 audioqueue 播放PCM数据的相关文章

安卓播放pcm数据

Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上,Music和Video两个应用程序都是调用MediaPlayer实现的.MediaPlayer在底层是基于OpenCore(PacketVideo)的库实现的,为了构建一个MediaPlayer程序,上层还包含了进程间通讯等内容,这种进程间通讯的基础是Android基本库中的Binder机制. 但是该类只能对完整的音频文件进行操作,而不能直接对纯PCM音频数据操作.假如我们通过解码得到PCM数

DirectSound播放PCM(可播放实时采集的音频数据)

前言 该篇整理的原始来源为http://blog.csdn.net/leixiaohua1020/article/details/40540147.非常感谢该博主的无私奉献,写了不少关于不同多媒体库的博文.让我这个小白学习到不少.现在将其整理是为了收录,以备自己查看. 一.DirectSound简介 DirectSound是微软所开发DirectX的组件之一,可以在Windows 操作系统上录音,并且记录波形音效(waveform sound).目前DirectSound 是一个成熟的API ,

使用AudioTrack播放PCM音频数据(android)

众所周知,Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上,Music和Video两个应用程序都是调用MediaPlayer实现的.MediaPlayer在底层是基于OpenCore(PacketVideo)的库实现的,为了构建一个MediaPlayer程序,上层还包含了进程间通讯等内容,这种进程间通讯的基础是Android基本库中的Binder机制.但是该类只能对完整的音频文件进行操作,而不能直接对纯PCM音频数据操作.假如我们通过解码得到

最简单的视音频播放示例9:SDL2播放PCM

本文记录SDL播放音频的技术.在这里使用的版本是SDL2.实际上SDL本身并不提供视音频播放的功能,它只是封装了视音频播放的底层API.在Windows平台下,SDL封装了Direct3D这类的API用于播放视频:封装了DirectSound这类的API用于播放音频.因为SDL的编写目的就是简化视音频播放的开发难度,所以使用SDL播放视频(YUV/RGB)和音频(PCM)数据非常的容易. SDL简介 SDL(Simple DirectMedia Layer)是一套开放源代码的跨平台多媒体开发库,

最简单的视频播放示例8:DirectSound播放PCM

本文记录DirectSound播放音频的技术.DirectSound是Windows下最常见的音频播放技术.目前大部分的音频播放应用都是通过DirectSound来播放的.本文记录一个使用DirectSound播放PCM的例子.注:一位仁兄已经提醒我DirectSound已经计划被XAudio2取代了.后来考证了一下发现确有此事.因此在下次更新中考虑加入XAudio2播放PCM的例子.本文仍然记录一下DirectSound这位"元老". DirectSound简介 DirectSoun

使用WindowsAPI播放PCM音频

这一篇文章同上一篇<使用WindowsAPI获取录音音频>原理具有相似之处,不再详细介绍函数与结构体的参数 1. waveOutGetNumDevs 2. waveOutGetDevCaps 3. waveOutOpen 回调函数 void CALLBACK PlayCallback(HWAVEOUT hwaveout, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2); 4. waveOutPrepareHeader 5

使用WindowsAPI实现播放PCM音频的方法

这篇文章主要介绍了使用WindowsAPI实现播放PCM音频的方法,很实用的一个功能,需要的朋友可以参考下 本文介绍了使用WindowsAPI实现播放PCM音频的方法,同前面一篇使用WindowsAPI获取录音音频的方法原理具有相似之处,这里就不再详细介绍具体的函数与结构体的参数,相同的部分加以省略,只介绍主要的功能部分代码.如下所示: 1. waveOutGetNumDevs 2. waveOutGetDevCaps 3. waveOutOpen 回调函数: ? 1 void CALLBACK

XAudio2播放PCM

XAudio2 是一个跨平台的API,在Xbox 360及Windows中得到支持.在Xbox 360上, XAudio2作为一个静态库编译到游戏可执行文件中.在Windows上,XAudio2提供一个动态链接库(DLL).以下例子只使用了其中的一部分功能,并不全面.详情请看微软技术页的XAudio2编程相关(英文). 使用XAudio2来播放未压缩的PCM音频数据的过程并不复杂,主要有以下几个步骤: 1. 建立XAudio2 引擎 使用XAudio2Create函数,该函数的功能是创建一个XA

最简单的视音频播放示例8:DirectSound播放PCM

本文记录DirectSound播放音频的技术.DirectSound是Windows下最常见的音频播放技术.目前大部分的音频播放应用都是通过DirectSound来播放的.本文记录一个使用DirectSound播放PCM的例子.注:一位仁兄已经提醒我DirectSound已经计划被XAudio2取代了.后来考证了一下发现确有此事.因此在下次更新中考虑加入XAudio2播放PCM的例子.本文仍然记录一下DirectSound这位“元老”. DirectSound简介 DirectSound是微软所