使用 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, , 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(, , , );
[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(, , , );
[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=;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 = ;//采样率
audioDescription.mFormatID = kAudioFormatLinearPCM;
audioDescription.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioDescription.mChannelsPerFrame = ;///单声道
audioDescription.mFramesPerPacket = ;//每一个packet一侦数据
audioDescription.mBitsPerChannel = ;//每个采样点16bit量化
audioDescription.mBytesPerFrame = (audioDescription.mBitsPerChannel/) * audioDescription.mChannelsPerFrame;
audioDescription.mBytesPerPacket = audioDescription.mBytesPerFrame ;
///创建一个新的从audioqueue到硬件层的通道
// AudioQueueNewOutput(&audioDescription, AudioPlayerAQInputCallback, self, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &audioQueue);///使用当前线程播
AudioQueueNewOutput(&audioDescription, AudioPlayerAQInputCallback, self, nil, nil, , &audioQueue);//使用player的内部线程播
////添加buffer区
for(int i=;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, , EVERY_READ_LENGTH, file);//读取文件
NSLog(@"read raw data size = %d",readLength);
outQB->mAudioDataByteSize = readLength;
Byte *audiodata = (Byte *)outQB->mAudioData;
for(int i=;i<readLength;i++)
{
audiodata[i] = pcmDataBuffer[i];
}
/*
将创建的buffer区添加到audioqueue里播放
AudioQueueBufferRef用来缓存待播放的数据区,AudioQueueBufferRef有两个比较重要的参数,AudioQueueBufferRef->mAudioDataByteSize用来指示数据区大小,AudioQueueBufferRef->mAudioData用来保存数据区
*/
AudioQueueEnqueueBuffer(outQ, outQB, , NULL);
[synlock unlock];
} -(void)checkUsedQueueBuffer:(AudioQueueBufferRef) qbuf
{
if(qbuf == audioQueueBuffers[])
{
NSLog(@"AudioPlayerAQInputCallback,bufferindex = 0");
}
if(qbuf == audioQueueBuffers[])
{
NSLog(@"AudioPlayerAQInputCallback,bufferindex = 1");
}
if(qbuf == audioQueueBuffers[])
{
NSLog(@"AudioPlayerAQInputCallback,bufferindex = 2");
}
if(qbuf == audioQueueBuffers[])
{
NSLog(@"AudioPlayerAQInputCallback,bufferindex = 3");
}
} @end
源代码下载地址 http://download.csdn.net/detail/samguoyi/4509544
使用 audioqueue 播放PCM数据的更多相关文章
- ffplay代码播放pcm数据
摘抄雷兄 http://blog.csdn.net/leixiaohua1020/article/details/46890259 /** * 最简单的SDL2播放音频的例子(SDL2播放PCM) * ...
- FFMPEG学习----使用SDL播放PCM数据
参考雷神的代码: /** * 最简单的SDL2播放音频的例子(SDL2播放PCM) * Simplest Audio Play SDL2 (SDL2 play PCM) * * 本程序使用SDL2播放 ...
- Android OpenSL ES 开发:使用 OpenSL 播放 PCM 数据
OpenSL ES 是基于NDK也就是c语言的底层开发音频的公开API,通过使用它能够做到标准化, 高性能,低响应时间的音频功能实现方法. 这次是使用OpenSL ES来做一个音乐播放器,它能够播放m ...
- DirectSound播放PCM(可播放实时采集的音频数据)
前言 该篇整理的原始来源为http://blog.csdn.net/leixiaohua1020/article/details/40540147.非常感谢该博主的无私奉献,写了不少关于不同多媒体库的 ...
- 使用AudioTrack播放PCM音频数据(android)
众所周知,Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上,Music和Video两个应用程序都是调用MediaPlayer实现的.MediaPl ...
- 音频 PCM 数据的采集和播放
PCM(Pulse Code Modulation)脉冲编码调制 —— 音频的采集与量化过程. PCM数据是最原始的音频数据完全无损,所以PCM数据虽然音质优秀但体积庞大. 为了解决这个问题先后诞生了 ...
- 最简单的视音频播放示例9:SDL2播放PCM
本文记录SDL播放音频的技术.在这里使用的版本是SDL2.实际上SDL本身并不提供视音频播放的功能,它只是封装了视音频播放的底层API.在Windows平台下,SDL封装了Direct3D这类的API ...
- 最简单的视音频播放示例8:DirectSound播放PCM
本文记录DirectSound播放音频的技术.DirectSound是Windows下最常见的音频播放技术.目前大部分的音频播放应用都是通过DirectSound来播放的.本文记录一个使用Direct ...
- 最简单的视音频播放演示样例8:DirectSound播放PCM
===================================================== 最简单的视音频播放演示样例系列文章列表: 最简单的视音频播放演示样例1:总述 最简单的视音频 ...
随机推荐
- 650. Find Leaves of Binary Tree
class Solution { public: vector<vector<int>> findLeaves(TreeNode* root) { vector<vect ...
- JMeter压测上对于并发的认识误区
1.误区 在JMeter压测过程中,我们通常认为1s内100的并发量(即:QPS为100)的设置如下: 此时,没有再添加额外的控制器.上述中的参数设置解释:Number of Threads(user ...
- windows中设置php环境变量
1.我的电脑->右键(选择我的属性) 2.点击高级设置 3.点击环境变量 4.在系统变量中找到Path 点击 5.找到php.exe的文件目录,添加到path中 6.php -v 显示版本,表示 ...
- go 单元测试框架介绍
最近项目在补充单元测试,这里介绍以下几个go里流行的单元测试框架. gomock gostub monkey Convey 下面介绍下各个框架的主要用途 convey 主要用途是用来组织测试用例的 g ...
- Maven手动导本地jar到项目
第一步:先把目标jar安装在本地,下面是安装到本地的步骤:在cmd命令中,输入:mvn install:install-file -Dfile=C:\Users\Ter\Desktop\jqd_doc ...
- [bug]——vue 组件状态外置引发的一个 bug
背景 在编写 .vue 组件时,可以将状态外置来获取一些额外的好处,譬如有这么一个组件(global-components.vue): <template> <div> < ...
- ElasticSearch中"distinct","count"和"group by"的实现
最近在业务中需要使用ES来进行数据查询,在某些场景下需要对数据进行去重,以及去重后的统计.为了方便大家理解,特意从SQL角度,方便大家能够理解ES查询语句. 1 - distinct ; { &quo ...
- 防范sql注入值得注意地方
sql注入是大家基本都清楚,一般来说用参数化就能解决注入的问题,也是最好的解决方式. 有次技术群里问到一个问题,如下图 很显然tableName是外部传递过来的,暂时不考虑具体的业务环境,但如果以se ...
- Hyperledger Fabric 入门 first-network 搭建
1.准备环境: 安装git.docker.curl.go [root@test_vonedao_83 fabric]# git --version git version 1.8.3.1 [root@ ...
- Spark学习(1) Spark入门
什么事spark Spark是一种快速.通用.可扩展的大数据计算引擎.项目是用Scala进行编写,基于内存计算的 包括交互式查询和流处理 spark内置项目 Spark SQL:是 Spark 用来操 ...