Cocoa Mac音频模块关键步骤总结

1. .driver 插件

#include <CoreAudio/AudioServerPlugIn.h> 头文件
static AudioServerPlugInDriverInterface gAudioServerPlugInDriverInterface = 静态函数struct, 返回一系列回调的函数指针
//开始io,代表有对象链接进来了,如果是第一个启动引擎, 创建circle buffer
static OSStatus xxx_StartIO(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID)
//如果是最后一个的话,关闭引擎,销毁circle buffer
static OSStatus xxx_StopIO(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt32 inClientID)

//真正的方法
static OSStatus CamStudioAudio_DoIOOperation(....)

里面真正执行任务,接收和发送都在这里完成。// virutal device -> Other Capturedif(inOperationID == kAudioServerPlugInIOOperationReadInput){return sendDataToOtherApp(inIOBufferFrameSize, inIOCycleInfo, ioMainBuffer);}// other app -> virutal deviceif(inOperationID == kAudioServerPlugInIOOperationWriteMix){return getDataFromOtherApp(inIOBufferFrameSize, inIOCycleInfo, ioMainBuffer);}

//这个函数可以不管
static OSStatus xxx_EndIOOperation(...)

2. CoreAudio 驱动

//准备获取ID

    AudioObjectPropertyAddress address = makeOutputPropertyAddress(kAudioHardwarePropertyDevices);UInt32 devicesDataSize;//获取具体列表的内存大小OSStatus status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,&address,0,NULL,&devicesDataSize);RETBOOL(status, "findMyAudioDevice-AudioObjectGetPropertyDataSize")//判断长度,并请求填充内存int count = devicesDataSize / sizeof(AudioDeviceID);AudioDeviceID deviceIDs[count];status = AudioObjectGetPropertyData(kAudioObjectSystemObject,&address,0,NULL,&devicesDataSize,deviceIDs);//轮询列表获取合适的idAudioObjectGetPropertyData(deviceID, &address, 0, NULL,  &size, &prop);//创建和析构监听AudioObjectAddPropertyListener(...kAudioDevicePropertyDeviceIsAlive)AudioObjectRemovePropertyListener(...kAudioDevicePropertyNominalSampleRate)

#import <CoreAudio/CoreAudio.h>
#import <CoreAudio/AudioHardware.h>
//绑定
OSStatus status = AudioDeviceCreateIOProcID(设备ID, deviceIOProcFunc/*回调函数*/, this, &mDeviceIOProcID/*创建的io句柄*/);
//开始
OSStatus status = AudioDeviceStart(mDevice.getNeedID(), mDeviceIOProcID);
//回调
。。。
//销毁
AudioDeviceDestroyIOProcID(mDevice.getNeedID(), mDeviceIOProcID);


3. AudioConvertRef 转码

头文件 #include <AudioToolbox/AudioToolbox.h>

 //创建转换对象AudioConverterRef audioConverter;AudioConverterNew(&_inASBD, &_outASBD, &audioConverter);

//重新从 Audio Convert 获取被校正过的 ASBD数据
AudioConverterGetProperty(audioConverter, kAudioConverterCurrentInputStreamDescription, &size, &_inASBD);

//将获取的MagicCookie 设置到 converter 中
AudioConverterSetProperty(converter,kAudioConverterDecompressionMagicCookie,cookieDataSize,cookieData),

//计算输入缓冲区的大小,及缓冲区能容纳的packet 数量
_inBuffer = malloc(4096*8)
//vbr 需要从文件中读取。kAudioFilePropertyPacketSizeUpperBound是预估不是打开计算
AudioFileGetProperty(_inFile, kAudioFilePropertyPacketSizeUpperBound, &size, &inSizePerPacket)

//计算和开辟输出缓冲区
//vbr得到最大输入的每包最大输出大小
AudioConverterGetProperty(audioConverter, kAudioConverterPropertyMaximumOutputPacketSize, &size, outData);

//写入Magic cookie

status = AudioConverterGetProperty(converter, kAudioConverterDecompressionMagicCookie, &cookieDataSize, cookies);
status = AudioFileSetProperty(_outFile, kAudioFilePropertyMagicCookieData, cookieDataSize, cookies);

//死循环进行数据转换
AudioConverterFillComplexBuffer(....) //会在这里的回调里面填充input数据,内部进行转换,返回值之后,获取到的就是转换后的数据
AudioFileWritePackets 写入文件
outFilePacketOffset += ioOutDataPacketsPerOut;//下次输出文件的时候,需要增加这次输出的数量
AudioConverterDispose(audioConverter)//关闭和释放AudioConverter的资源

4. AudioQueue output 输出

//创建Audioqueue对象,并配置callbackAudioQueueNewOutput(&inASBD,callback,UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()),nil,nil,0,&self.audioQueue)
//自定义输出设备,如果不想用默认的话var cuStr = "FC-E8-06-DB-74-1D:output" as CFStringAudioQueueSetProperty(self.audioQueue!, kAudioQueueProperty_CurrentDevice, &cuStr, size)
//获取输出设备的 asbd, 方便后面转码
AudioQueueGetProperty(self.audioQueue!, kAudioQueueProperty_StreamDescription, &outADSB, &size)
//创建三个默认的Audioqueue队列。并塞入静音数据,手动调用一次callbackfor _ in 0..<self.audioQueueNum {var buffer: AudioQueueBufferRef?AudioQueueAllocateBuffer(self.audioQueue!,self.byteSizeInBuffer,&buffer)//往buffer 中填充默认的静音数据let buf = UnsafeMutableRawPointer.allocate(byteCount: Int(self.byteSizeInBuffer), alignment: 1)memset(buf, 0, Int(self.byteSizeInBuffer))TPCircularBufferProduceBytes(&self.tpBuffer, buf, self.byteSizeInBuffer)buf.deallocate()callback(UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()), self.audioQueue!, buffer!)}
//正式开始
AudioQueueStart(self.audioQueue!, nil)
//开始后会自动回调callbackprivate let callback: AudioQueueOutputCallback = {inUserData, queue,  bufferRef in.... //判断数据是否足够,从circle bufer拿出数据,进行转码//将合适的格式大小的数据,塞入播放队列AudioQueueEnqueueBuffer(queue,bufferRef,0,nil)}
            //stopAudioQueueStop(self.audioQueue!, true)AudioQueueDispose(self.audioQueue!, true)

5. AudioQueue 采集

  //创建 queueJBAssertNoError(AudioQueueNewInput(&_mDataFormat,captureAudioDataCallback,(__bridge void *)(self),NULL,kCFRunLoopCommonModes,0,&_mQueue),
//获取asbdAudioQueueGetProperty(_mQueue,kAudioQueueProperty_StreamDescription,&_mDataFormat,&size),//内存分配,入队for (int i = 0; i != KNumberBuffers; i++ ){JBAssertNoError(AudioQueueAllocateBuffer(_mQueue, bufferByteSize, &_mBuffers[i]), @"AudioQueueAllocateBuffer");JBAssertNoError(AudioQueueEnqueueBuffer(_mQueue, _mBuffers[i], 0, NULL), @"AudioQueueEnqueueBuffer");}//启动audio queue , 第二个参数设置为NULL表示立即开始采集数据.JBAssertNoError(AudioQueueStart(_mQueue, NULL), @"AudioQueueStart");static void captureAudioDataCallback(void *__nullable  inUserData,...) {
//写入和拷贝数据
...
//释放队列AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL),
}
//关闭JBAssertNoError(AudioQueueStop(_mQueue, true),@"AudioQueueStop");JBAssertNoError(AudioQueueDispose(_mQueue, true), @"AudioQueueDispose");

6. AudioUnit 采集

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/219157.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java实现局域网内视频投屏播放(四)投屏实现

代码链接​​​​​​​​​​​​​​​​​​​​​ 设备发现 上一篇文章说过&#xff0c;设备的发现有两种情况&#xff0c;主动和被动&#xff0c;下面我们来用java实现这两种模式 主动发现 构建一个UDP请求发送到239.255.255.250:1900获取设备信息&#xff0c;UDP包的…

【价值几十万的仿抖音直播电商系统源码共享】

当下&#xff0c;传统的图文电商模式已经走向没落&#xff0c;以抖音为首的直播电商模式备受用户追捧&#xff0c;它具有实时直播和强互动的特点&#xff0c;是传统电商所不具备的优势。而且&#xff0c;当前正是直播电商的红利期&#xff0c;很多主播和品牌商都通过直播电商业…

C语言经典错误总结(二)

一.运算符优先级问题 该问题对于大多数C语言程序员都是存在的&#xff0c;原因当然就是无法准确找到表达式计算顺序。 看例题&#xff1a; 假如我们有两个整数a &#xff0c;b&#xff0c;现在我们要求整数c是一个八位整数&#xff0c;使其前4位与a相同&#xff0c;后4位与b…

【java】保留前N月数据文件,定期删除数据

数据越积越多&#xff0c;过于冗余&#xff1b;数据库定期删除指定时间前的数据&#xff1b;文件生成的删除指定时间前端文件 SFTP文件定期删除 java sftp 定时清理指定文件中固定时间 依赖 <!-- ftp文件上传/下载Jar包 --> <dependency><groupId>com.jc…

K8S(四)—pod详解

目录 pod介绍Pod的概念&#xff1a;Pod的特性&#xff1a;Pod的配置&#xff1a;Pod的控制&#xff1a;示例 YAML 文件&#xff1a; pod启动流程问题 两种方式启动镜像的升级和回滚更新 Deployment&#xff1a;回滚检查 Deployment 历史版本回滚到之前的修订版本缩放 Deploymen…

青少年CTF-Crypto(Morse code/ASCII和凯撒)

FLAG&#xff1a;你这一生到底想干嘛 专研方向: Web安全 &#xff0c;Md5碰撞 每日emo&#xff1a;不要因为别人都交卷了&#xff0c;就乱选答案 文章目录 1.Morse code2、ASCII和凯撒的约定 1.Morse code 题目提示摩尔斯电码&#xff0c;这个是给的附件 直接用摩尔斯解密&am…

Linux中的磁盘挂载与取消

Linux中的磁盘挂载与取消 前言磁盘挂载查看分区信息分区设置格式化磁盘挂载持久化挂载点配置 取消磁盘挂载查看已挂载的磁盘取消挂载 前言 今天准备给一台PVE设备添加一块磁盘&#xff0c;踩了点小坑&#xff1a;磁盘挂载后重启后发现磁盘没有被挂载&#xff0c;于是整理记录一…

antv X6

文章目录 graph配置项力导向图路由router graph配置项 https://x6.antv.antgroup.com/api/graph/graph 力导向图 https://x6.antv.antgroup.com/examples/layout/general#force 路由router https://x6.antv.antgroup.com/api/registry/router initGraph () {// https://x…

常用的测试用例大全

登录、添加、删除、查询模块是我们经常遇到的&#xff0c;这些模块的测试点该如何考虑 1)登录 ① 用户名和密码都符合要求(格式上的要求) ② 用户名和密码都不符合要求(格式上的要求) ③ 用户名符合要求&#xff0c;密码不符合要求(格式上的要求) ④ 密码符合要求&#xf…

[c]输出字符金字塔

我们可以把字符金字塔类比数字金字塔 输入3 输出 类似下图 下面附上我的代码&#xff0c;可能有些繁琐 #include<stdio.h> int main() {char s;scanf("%c",&s);int lens-64;//将字符的ascii码值减去64得到循环次数&#xff0c;比如你输入A&#xff0c;l…

Java入门基础:浅显易懂 死循环

文章目录 一、什么是死循环二、以fo循环示例三、如何避免死循环 一、什么是死循环 死循环就是循环语句的 循环布尔表达式 一直为true&#xff0c;没有终止循环的条件或者终止循环的条件根本不可能达成 二、以fo循环示例 /** 终止循环的条件根本不可能达成* 循环布尔表达式&a…

一篇文章,带你详细了解华为认证体系证书(二)

一篇文章&#xff0c;带你详细了解华为认证体系证书 &#xff08;一&#xff09;_华为高斯数据库证书_PICACHU的博客-CSDN博客一、总体概括一、总体概括华为认证是华为技术有限公司基于“平台生态”战略&#xff0c;围绕“云-管-端”协同的新ICT技术架构&#xff0c;打造的业界…

Spring Boot--Freemarker渲染技术+实际案例

目录 Freemarker 1.1.什么是Freemarker 1.2.Freemarker模板组成部分 1.3.优点 FreeMarker常见的方法&#xff1a; 2.2.2.数值 2.2.3.布尔值 2.2.4.日期 2.3.常见指令 2.3.1.处理不存在的值 assign 2.3.4.list 2.3.5.include SpringBoot整合Freemarker Freemarker…

STM32-TIM定时器输出比较

目录 一、输出比较简介 二、PWM简介 三、输出比较通道&#xff08;通用&#xff09; 四、输出比较通道&#xff08;高级&#xff09; 五、输出比较模式 六、PWM基本结构 七、PWM参数计算 八、外设介绍 8.1 舵机 8.2 直流电机及驱动 九、开发步骤 十、输出比较库函数…

每日一题,杨辉三角

给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]] 示例 2: 输入: numRows 1 输出: [[1]]

机器学习笔记:linear scaling learning rate (学习率 和batch size的关系)

在训练神经网络的过程中&#xff0c;随着batch size的增大&#xff0c;处理相同数据量的速度会越来越快&#xff0c;但是达到相同精度所需要的epoch数量越来越多 换句话说&#xff0c;使用相同的epoch数量时&#xff0c;大batch size训练的模型与小batch size训练的模型相比&am…

【C++】POCO学习总结(十五):字符串操作

【C】郭老二博文之&#xff1a;C目录 1、简介 POCO提供一些函数来处理 std::string 和 std::wstring&#xff1b; 比如&#xff1a; 去除空格大小写转换不区分大小写比较字符转换和子字符串替换连接 许多函数有两种变体: 返回一个新字符串而不修改原字符串的函数;直接修改…

限流常用算法以及基于Sentinel的微服务限流及熔断

一、服务限流的作用及实现 在没有任何保护机制的情况下&#xff0c;如果所有的流量都进入服务器&#xff0c;很可能造成服务器宕机导致整个系统不可用&#xff0c;从而造成巨大的损失。为了保证系统在这些场景中仍然能够稳定运行&#xff0c;就需要采取一定的系统保护策略&…

Unity_ET框架项目-斗地主_启动运行流程

unity_ET框架项目-斗地主_启动运行流程 项目源码地址&#xff1a; Viagi/LandlordsCore: ET斗地主Demohttps://github.com/Viagi/LandlordsCore下载项目到本地。 启动运行步骤&#xff1a; 下载目录如下&#xff1a; 1. VS&#xff08;我用是2022版VisualStudio&#xff09…

【机器学习】多模态机器学习

多模态机器学习是指利用多种不同的数据类型&#xff08;如图像、文本、音频等&#xff09;来训练和优化机器学习模型。相比于单一模态的机器学习&#xff0c;多模态机器学习可以更好地模拟现实世界中的复杂信息交互&#xff0c;从而提高模型的性能和泛化能力&#xff0c;同时也…