标贝科技 https://ai.data-baker.com/#/?source=qwer12

填写邀请码fwwqgs,每日免费调用量还可以翻倍

基于AudioQueue实现音频的录制和播放

文章目录

  • 基于AudioQueue实现音频的录制和播放
  • 背景
  • 总览
  • Audio Queue 架构
    • AudioQueueBuffer数据结构
    • 创建`AudioQueueBuffer`
    • 释放 `AudioQueueBuffer`
    • Buffer Queue 和Enqueuing
    • Audio Queue Callback
  • 音频录制
    • 创建一个录音 AudioQueue 的示例
  • 音频播放
    • 播放流程说明
    • 通过AudioQueue来控制音频的播放
  • Audio Queue 的控制和状态
  • Audio Queue 运行状态的监控
  • demo地址
  • 参考文献

背景

在iOS中常使用AVPlayerAVAudioPlayer来播放在线音乐或者本地音乐,但是支持的格式都是封装好的,比如Mp3,Wav 格式的音频,但是如果需要播放流式的PCM音频数据该怎么办呢? 答案是使用Audio Queue,它也是苹果官方封装的音频处理框架,可以用来播放或录制音频,并且支持平台级音频格式的编码和解码。

AudioQueue 有以下作用

  1. 连接设备的音频硬件
  2. 管理音频播放的内存数据
  3. 协作codec 进行音频的的编解码
  4. 实现音频的录制和播放

本篇文章主要以PCM 数据为例子进行讲解,讲解音频的录制和实现,文末会附带基于AudioQueue的录音器和播放起的源代码文件;

总览

本篇主要介绍来音频的录制和播放过程,共包含三个部分,Audio Queue 架构、音频的录制、和音频的播放,其中Audio Queue 架构是实现录制和播放的核心,理解了AudioQueue的实现原理,再来看录制和播放将会更加高效率;

Audio Queue 架构

Audio Queue 架包含三个部分: audio queue buffers, Buffer queueaudio queue callback; audio queue buffers 在结构上是一个数组结构,存储的AudioBuffer数据,下面会专门分析AudioQueueBuffer 的数据结构; Buffer queue 可以理解为管理类,用来管理和组织这些audio queue buffers 按照一定的顺序进行排列和运行, 并且协调audio queue callback 的调用;

AudioQueueBuffer数据结构

下面重点来说明AudioQueueBuffer的数据结构的数据结构如下,主要包含四个部分,其中最核心的是aAduioData 部分。

typedef struct AudioQueueBuffer {const UInt32   mAudioDataBytesCapacity;void *const    mAudioData;UInt32         mAudioDataByteSize;void           *mUserData;
} AudioQueueBuffer;
typedef AudioQueueBuffer *AudioQueueBufferRef;

mAudioData, 它代表要录制和播放的音频数据;
mAudioDataByteSize: 用来表示audioDatalength;
mAudioDataBytesCapacity: 表示一个mAudioData 需要分配的空间,单位是字节(Byte),它的值必须大于mAudioDataByteSize,否则音频的数据放不下会出现丢失;
mUserData: 一个万能指针,用来传递调用者的值,一般结合bridge(void *)传递self对象,实现C函数和OC 语言的交互

创建AudioQueueBuffer

调用函数AudioQueueAllocateBuffer 来分配,以下为示例代码;

int result =  AudioQueueAllocateBuffer(_audioQueue, kAudioBufferSize, &_audioQueueBuffers[i]);NSLog(@"Mic AudioQueueAllocateBuffer i = %d,result = %d", i, result);AudioQueueEnqueueBuffer(_audioQueue, _audioQueueBuffers[i], 0, NULL);

释放 AudioQueueBuffer

销毁则是通过AudioQueueDispose来实现;

- (void)freeAudioBuffers {for(int i=0; i<kAudioQueueBufferCount; i++) {int result = AudioQueueFreeBuffer(_audioQueue, _audioQueueBuffers[i]);NSLog(@"AudioQueueFreeBuffer i = %d,result = %d", i, result);}AudioQueueDispose(_audioQueue, YES);
}

Buffer Queue 和Enqueuing

Buffer queue 的数据结构是一个队列,队列里可以存放许多Auido Queue Buffer, 存放的Buffer数量不受限制,推荐使用三个,例如录制的过程,一个buffer 负责收集麦克风的数据,一个buffer负责将数据传递给磁盘,还有一个Buffe用来做备用,防止磁盘I/O 时间过长出现卡顿。Enqueuing的含义是加入队列, 可以通过下图来了解Enqueue的过程。

音频录制Buffer Queuing的Enqueuing说明:

  1. 读取麦克风的数据到内存中,然后将数据写入到buffer中;
  2. 当第一个buffer数据写满后,会将数据存放到磁盘中,并且触发回调函数,这个时候回调函数需要处理数据,将新的音频数据覆写到该buffer中;
  3. 在回调函数中将采集的音频数据写入到磁盘中;
  4. 当三个buffer 都填满后之后会继续复用第一个buffer;
  5. 重复第2部进行填充数据和进行回调;
  6. 重复第3步将数据写入磁盘中

Audio Queue Callback

Audio Queue 的Callback 是开发的重要内容,它是通过AudioQueueEnqueueBuffer函数来驱动数据,它是在音频录制或播放中进行重复调用的,调用的间隔取决于buffer的大小,设置的buffer数据容量越大,回调触发的间隔也越大,一般为0.5秒到几秒不等; AudioQueueCallback分为两个部分,录制AuidoQueueInputCallback 和播放 AudioQueuOutputCallBack

音频录制

音频录制的本质是调用手机上的录音设置(如麦克风,耳机)来采集声音,采集声音的声音进行数字编码调制,形成音频数据,然后读取到内存中,最后写入到手机设备的硬盘中进行保存。iOS 的录音实现是通过Audio Queue 进行实现,下面主要分析AudioQueu的的结构和使用原理。

###录制 AuidoQueueInputCallback 回调函数

AudioQueueInputCallback (void                               *inUserData,AudioQueueRef                      inAQ,AudioQueueBufferRef                inBuffer,const AudioTimeStamp               *inStartTime,UInt32                             inNumberPacketDescriptions,const AudioStreamPacketDescription *inPacketDescs
);

inUserData 用户数据指针,本身是一个无类型的指针,常被用来指向调用实例;
inAQ 调用该 CallBack的AudioQueue;
inBuffer 初始化AudioBuffer的时候我们封装好的音频数据;
inStartTime 每个buffer 对应的时间,这里我们用不上;
inNumberPacketDescriptions结合inPacketDescs的参数使用,一般涉及到编码的地方会用到;
inPacketDescs 对应buffer的音频包描述,一般涉及到编码的地方会用到;

音频录制的调用实例

// 音频录制的回调函数
void AudioAQInputCallback(void * __nullable               inUserData,AudioQueueRef                   inAQ,AudioQueueBufferRef             inBuffer,const AudioTimeStamp *          inStartTime,UInt32                          inNumberPacket,const AudioStreamPacketDescription * __nullable inPacketDescs) {DBAudioMicrophone * SELF = (__bridge DBAudioMicrophone *)inUserData;NSLog(@"Mic Audio Callback");if (inNumberPacket > 0){[SELF processAudioBuffer:inBuffer withQueue:inAQ];}
}// 读取录制到的音频数据 - (void)processAudioBuffer:(AudioQueueBufferRef)inBuffer withQueue:(AudioQueueRef)inAudioQueue {NSData *data = [NSData dataWithBytes:inBuffer->mAudioData length:inBuffer->mAudioDataByteSize];[self.sendData appendData:data];if (_isOn) {AudioQueueEnqueueBuffer(inAudioQueue, inBuffer, 0, NULL);}
}

创建一个录音 AudioQueue 的示例

- (void)audioNewInput {/// 创建一个新的从audioqueue到硬件层的通道AudioQueueNewInput(&_audioDescription, AudioAQInputCallback, (__bridge void * _Nullable)(self), NULL, kCFRunLoopCommonModes, 0, &_audioQueue);}

说明:
1._audioDescription 是 AudioStreamBasicDescription 类型的数据结构,主要用来描述数据的特点,包含采样率,位深,声道数量,音频格式,音频包,音频帧等数据;
2.AudioAQInputCallback 是音频的回调函数;
3.kCFRunLoopCommonModes 当前AudioQueue所运行的Runloop;
4._audioQueue 是AudioQueuueRef 类型的指针,此处用它的指针来进行实例化;

下面看一下AudioStreamBasicDescription的初始化赋值;

+ (AudioStreamBasicDescription)defaultAudioDescriptionWithSampleRate:(Float64)sampleRate numOfChannel:(NSInteger)numOfChannel {AudioStreamBasicDescription asbd;memset(&asbd, 0, sizeof(asbd));asbd.mSampleRate = sampleRate;asbd.mFormatID = kAudioFormatLinearPCM;asbd.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;asbd.mChannelsPerFrame = (UInt32)numOfChannel;asbd.mFramesPerPacket = 1;//每一个packet一侦数据asbd.mBitsPerChannel = 16;//每个采样点16bit量化asbd.mBytesPerFrame = (asbd.mBitsPerChannel/8) * asbd.mChannelsPerFrame;asbd.mBytesPerPacket = asbd.mBytesPerFrame * asbd.mFramesPerPacket;return asbd;
}

说明:传入的参数是采样率和声道的数据,这里位深一般用的是16位;

音频播放

播放流程说明

音频的播放也包含三个部分,1.磁盘的的音频输入流;2.音频队列; 3. 扬声器;

说明:
首先读取磁盘中的音频数据,第二,填充音频数据到Audio Queue 中; 第三, 驱动数据,使用扬声器播放数据;

通过AudioQueue来控制音频的播放

1.声明AudioStreamBasicDescription 来描述音频特征,如采样率,位深,声道数量等;

   audioDescription.mSampleRate =16000;//采样率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;

2.AudioQueueOutputCallback 设置回调函数

AudioQueueOutputCallback (void                  *inUserData,AudioQueueRef         inAQ,AudioQueueBufferRef   inBuffer
);

说明:
inUserData: 用户指针,用来处理用户数据;
inAQ: auidoQueue的引用对象
inBuffer: AudioQueueBufferRef 对象,用来描述音频数据;

3.处理音频回调函数收到的数据

static void AudioPlayerAQInputCallbackV2(void* inUserData,AudioQueueRef outQ, AudioQueueBufferRef outQB){DSAQPool* pool = (__bridge DSAQPool*)inUserData;[pool playCallBack:outQB];
}-(BOOL)enqueueBuffer:(AudioQueueBufferRefWrapper*)buf{if(AudioQueueEnqueueBuffer(audioQueue, buf.ref,0,NULL) == noErr){buf.inUse = YES;@synchronized (_buffers) {[_buffers addObject:buf];}return YES;}else{//DDLogError(@"AudioQueueEnqueueBuffer error.");return NO;}
}

说明:
1.AudioPlayerAQInputCallbackV2是回调函数,数据在播放的过程中会不停的触发;
2.-(BOOL)enqueueBuffer:(AudioQueueBufferRefWrapper*)buf; 在回调的过程中需要不停的对buffer进行赋值,驱动扬声器进行播放;

Audio Queue 的控制和状态

常用的控制有开始,暂停和停止;

开始-AudioQueueStart 控制开始录制或者开始播放;
暂停AudioQueuePause 控制暂停,可以调用开始的方法继续播放;
停止 AudioQueueStop结束,调用这个方法后不能再调用start方法进行播放了,表示音频已经播放完成了。

Audio Queue 运行状态的监控

1.检测声音的分贝值

-(float)getCurrentPower {UInt32 dataSize = sizeof(AudioQueueLevelMeterState) * _audioDescription.mChannelsPerFrame;AudioQueueLevelMeterState *levels = (AudioQueueLevelMeterState*)malloc(dataSize);OSStatus rc = AudioQueueGetProperty(_audioQueue, kAudioQueueProperty_CurrentLevelMeterDB, levels, &dataSize);if (rc) {NSLog(@"NoiseLeveMeter>>takeSample - AudioQueueGetProperty(CurrentLevelMeter) returned %d", rc);}float channelAvg = 0;for (int i = 0; i < _audioDescription.mChannelsPerFrame; i++) {channelAvg += levels[i].mAveragePower;}free(levels);return channelAvg ;
}

说明: 这个方法主要用来监控声音的分贝数,一般在录音的过程中需要对音量的大小进行反馈会用到;也可以利用AudioQueueGetProperty 来监控音频的运行状态,具体可以参照: AudioQueuePropertyID 中的说明;

demo地址

音频录制: https://github.com/data-baker/BakerIosSdks/tree/main/DBAudioSDK/Classes/DBToolKit/DBMicrophone
音频播放:https://github.com/data-baker/BakerIosSdks/tree/main/DBAudioSDK/Classes/DBToolKit/DBPlayer

参考文献

苹果开发者文档:https://developer.apple.com/library/archive/documentation/MusicAudio/Conceptual/AudioQueueProgrammingGuide/AQPlayback/PlayingAudio.html

基于AudioQueue实现音频的录制和播放(标贝科技)相关推荐

  1. iOS AudioQueue实现音频的录制和播放(附Demo)

    #基于AudioQueue实现音频的录制和播放 文章目录 背景 总览 Audio Queue 架构 AudioQueueBuffer数据结构 创建 AudioQueueBuffer 释放 AudioQ ...

  2. iOS 音频的录制、播放及音频文件管理

    文章目录 音频会话 音效播放 音乐播放 音频录制 音频管理 补充:音频队列服务 参考地址 音频会话 在使用Apple设备时,我们注意到有些应用打开音频播放时,其他音频就会终止,而有些应用却可以同时使用 ...

  3. 教你如何用ffmpeg处理音频格式转换(标贝科技)

    文章目录 前言 ffmpeg介绍? 一.下载与安装 1.下载安装包 2.设置环境变量 3.验证是否安装成功 二.命令讲解 1 参数解释 2 音频格式转换命令 三.标贝开放平台介绍 前言 ffmpeg介 ...

  4. 微信小程序音频(录制、播放、重录)功能

    1.图片展示及相关图片            2.wxml代码: <view class="" ><view class="recordingPlace ...

  5. android aac格式,android aac文件格式音频的录制和播放

    封装的工具类: package com.example.voaactest; import java.io.File; import java.io.FileNotFoundException; im ...

  6. Android native音频:录制播放的实现以及低延迟音频方案

    文章目录 1. 前言 2. 工程准备 3. 低延迟音频原理及功能实现方案 4. 使用OpenSL ES 4.1 播放器实现 4.2 录音器实现 4.3 Echo实现 5. 使用AAudio 5.1 播 ...

  7. 基于 STM32对音频数据的 Flash 读取与 DAC 播放

    目录 1 项目准备1.1 具体要求 1.2 项目环境 2 原理简介 2.1 STM32 的内部 FLASH 2.2 STM32 的 DAC 通道2.2.1 何为 DAC 2.2.2 STM32 的 D ...

  8. 基于 STM32F103C8T6 对音频数据的 Flash 读取与 DAC 播放

    文章内容: STM32F103C8T6 的 Flash 地址空间的数据读取. STM32F103C8T6 自带的 DAC 通道,转换为模拟音频进行播放,并用示波器观察波形,用耳机/喇叭收听,评判音乐还 ...

  9. iOS 音频录制、播放(本地、网络)

    文章目录一.录音机(AVAudioRecorder)1.简介2.如何使用3.具体实现(开始.暂停.停止.播放 四个功能)4.附件实现demo二.播放音频1.播放本地音频文件(AVAudioPlayer ...

最新文章

  1. 浏览器打开出现证书错误_SSL证书=安全?小心,别错漏了TA…
  2. Boostrap的按钮下拉菜单
  3. 2010年北京大学计算机研究生机试真题
  4. 下列关于java语言特性,下列关于Java 语言特点的叙述中,错误的是()。
  5. Bosh vs Comet vs Long Polling vs Polling
  6. 论文笔记:Deep neural networks for YouTube recommendations
  7. 访问 Confluence 6 的计划任务配置
  8. Mac版WebStorm破解方案
  9. vs2010 破解版,试用版变正式版
  10. 中信证券显示连接服务器,我的中信证券的交易软件真不好用
  11. 人生中重要抉择:读研还是工作?
  12. Ubuntu 20.04 源码编译Paddle2.2.2
  13. matlab中eval的用法
  14. 微信小程序实现图片预览的功能
  15. 医学图像处理涉及到的窗宽窗位 1
  16. 批处理系统脱机使用计算机,1.2.2 脱机输入/输出和批处理系统
  17. c语言指针关键字,C语言关键字const和指针的结合使用
  18. 沈剑老师的MySQL军规
  19. 华为鸿蒙Matepad 11,你值得拥有的学习、娱乐、办公利器
  20. 如何写出完美的复试简历?没有科研竞赛经历怎么办?老师喜欢什么样的学生?跨考生?//2021-2-5

热门文章

  1. 2022最新出炉的互联网大厂软件测试面试真题(阿里、京东、百度、美团、腾讯.......)
  2. 个性化快餐桌椅发展趋势
  3. 25岁到35岁,人生最关键的10年:这7件事你越早明白越好
  4. Hyperledger-indy 官方文档人工翻译
  5. java word转pdf,可以解决转pdf后word横版变窄问题(pdf显示不全)
  6. 网站服务器端指的是什么,web客户端和服务器分别指什么
  7. SPDK SRIOV VIRTIO
  8. python正则匹配统计_python正则表达式统计字符串的个数
  9. Odoo凭什么打败传统ERP?
  10. docker安装mysql并配置mysql主从集群