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

2023-05-16

标贝科技 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 Queue Enqueuing的过程

音频录制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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • mips uclibc 交叉编译ffmpeg,支持 G711A 编解码

    1 说明 使用 ffmpeg 源码 xff0c 进行交叉编译 xff0c 支持 H264 和 G711A 编码支持 2 环境说明 硬件环境 xff1a mips 架构芯片 软件环境 xff1a Linux 任意版本 3 原理 xff08 1
  • Linux下USB CDC ACM 驱动简析

    一 硬件平台 xff1a TI AM335X 芯片 二 软件平台 xff1a Ubuntu 10 04 三 USB CDC ACM 驱动简介 USB的CDC类是USB通信设备类 xff08 Communication Device Clas
  • Openwrt增加对 sd card 支持

    一 硬件平台 1 1 控制器 xff1a MT7620 xff08 A9内核 xff09 二 软件平台 2 1 开发环境 xff1a Ubuntu12 04 2 2 软件版本 xff1a openwrt 官方15 05版本SDK开发包 xf
  • linux sed命令删除特殊字符(含斜线、冒号等转义字符)

    简介 sed 是一种在线编辑器 xff0c 它一次处理一行内容 处理时 xff0c 把当前处理的行存储在临时缓冲区中 xff0c 称为 模式空间 xff08 pattern space xff09 接着用sed命令处理缓冲区中的内容 xff
  • makefile 中指定程序运行时加载的库文件路径

    1 问题描述 程序运行时 xff0c 提示找不到库 原因 xff1a 默认运行加载的库路径为 usr lib 2 解决方法 2 1 方法一 xff0c 设置PATH环境变量 如何在加入这些路径呢 xff1f 以PATH变量为例 一种方法是
  • Openwrt 新增平台编译

    1 说明 本文主要介绍 xff0c 如何在openwrt系统中 xff0c 新增一个平台进行编译 如原本的openwrt 包含了adm5120 arc770 ath25 imx6等平台 xff0c 现在需要新增一个sc9820平台的编译 本
  • Docker概述(一)(标贝科技)

    Docker概述 xff08 一 xff09 顺便介绍下 xff1a 我们是一家致力于智能语音交互的AI公司 xff0c 我们提供了语音识别 语音合成 声纹识别 声音复刻 声音转换等技术产品供小伙伴们测试调用 xff0c 感兴趣的 xff0
  • 树莓派网络配置

    1 说明 1 树莓派有线网络配置 1 树莓派系统WIFI 静态IP设置 2 树莓派系统DNS地址 etc resolv conf 开机后被还原为空或者192 168 1 1处理方法 2 环境 软件环境 xff1a 树莓派3 0 系统 硬件环
  • Linux 读写memory操作,devmem直接访问物理内存地址

    1 说明 由于开发需要 xff0c 需要通过memory传输数据 xff0c 所以使用devmem 方式读写数据 xff0c 操作linux 内存数据 devmem的方式是提供给驱动开发人员 xff0c 在应用层能够侦测内存地址中的数据变化
  • Open3D+vs配置以及使用教程

    Open3d 0 8 0 43 Cmake 43 vs2015 1 下载 简要看一下官网 xff1a Open3D Home Page Github主页 xff1a Open3D Github 注意下载版本 xff0c 一定要与vs相匹配
  • 彻底明白ip地址,区分localhost、127.0.0.1和0.0.0.0

    通俗的了解IP地址是什么 对于IP地址 xff0c 大家并不陌生 xff0c 特别是在网络访问中我们会经常使用到 xff08 平时对域名如百度的www baidu com的访问 xff0c 本质就是对域名所绑定的IP地址的访问 xff09
  • C3927 “->“: 非函数声明符后不允许尾随返回类型等错误

    C3927 34 gt 34 非函数声明符后不允许尾随返回类型等错误 xff0c 如下所示 xff1a 解决方法 xff1a 在VS2015版本 Visual Studio 2015 Update 2 xff0c 增加一个编译选项 utf
  • c++ nan或inf

    nan xff1a not a number 非数字 注意事项 xff1a 对负数开方sqrt 1 0 对负数求对数 log 1 0 0 0 0 0 0 0 inf inf inf inf inf这些操作都会得到nan 0 0会产生操作异常
  • 膨胀、腐蚀、开、闭运算——数字图像处理中的形态学

    膨胀 腐蚀 开 闭运算是数学形态学最基本的变换 形态学通常用于二值图像 一 膨胀与腐蚀能够实现以下作用 xff1a 1 消除噪声 2 分割出独立的图像元素 xff0c 在图像中连接相邻的元素 3 寻找图像中的明显的极大值区域或者极小值区域
  • c语言中&与&&区别

    c语言中 amp 与 amp amp 的区别 amp xff1a 按照位与操作 xff0c 例如 xff1a 0010 amp 1101 xff0c 结果为0000 amp 是java中的位逻辑运算 xff1a eg xff1a 2 amp
  • 网格搜索法

    网格搜索法是指定参数值的一种穷举搜索方法 xff0c 通过将估计函数的参数通过交叉验证的方法进行优化来得到最优的学习算法 即 xff0c 将各个参数可能的取值进行排列组合 xff0c 列出所有可能的组合结果生成 网格 然后将各组合用于SVM
  • pytorch模型从训练到LibTorch部署(标贝科技)

    标贝科技 https ai data baker com source 61 qwer12 填写邀请码fwwqgs xff0c 每日免费调用量还可以翻倍 1 pytorch和libtorch安装 标贝科技 PyTorch 是Torch7 团
  • C++Debug Assertion Failed!到底出错在哪里?

    总结来说这种错误存在两种情况 xff0c 其一就是野指针 另一种情况就是内存泄露 在调试的时候一定是在自己编写的函数上找错 xff0c 不要一只跟着调试顺序在库函数里找错 注意事项 单步调试找到错误 xff0c 祝好运 以上为百度出来的结果
  • 截止频率概念

    截止频率 fc xff08 1HZ 100HZ xff09 xff0c 代表什么意思 截止频率fc xff0c 用来描述一个滤波器或一个放大器频率特性的指标 一个滤波器或一个放大器 xff0c 当保持输入信号的幅度不变 xff0c 改变信号
  • C语言中EOF什么意思

    在C语言中 xff0c 或更精确地说成C标准函数库中表示文件结束符 xff08 end of file xff09 在while循环中以EOF作为文件结束标志 xff0c 这种以EOF作为文件结束标志的文件 xff0c 必须是文本文件 在文

随机推荐

  • C++里面的LPBYTE是什么意思

    BYTE 为 unsigned char LPBYTE 为 unsigned char
  • ORACLE 字符串聚合函数 strCat

    create or replace type strcat type as object currentstr varchar2 4000 currentseprator varchar2 8 static function ODCIAgg
  • 1.unity3d Astar pathfinding 第一个例子

    1 场景准备 先建立一个scene 添加一个plane xff0c 让其坐标处于 0 0 0 xff0c 并三方向scale都为10 添加一个新的layer xff0c 命名为Ground xff0c 并将上面建立的plane设置为Grou
  • [STM32]开源多功能DAP 脱机烧录器 DAPLink仿真下载器

    网上已经有很多开源脱机烧录器的了 xff0c XVIN1987的很小巧 xff0c 很有参考价值 xff0c 志明的很庞大 xff0c 源码较多较复杂 xff0c 于是自己在无线DAP的基础上 xff0c 添加了脱机烧录功能 xff0c u
  • 全志V3S 入门 RTL8723BS 连接wifi

    开发板 xff1a 荔枝派zero linux xff1a 5 10 rootfs xff1a buildroot 2017 8 1 使用最新的主线linux内核5 10 xff0c 主线5 10内核linux make ARCH 61 a
  • ESP32蓝牙 华为手机容易断连解决

    在长达两年的售后中 xff0c 收到华为手机连接ESP32蓝牙是最容易断开连接的 xff0c 一开始小米手机 苹果手机都没问题 xff0c 就只有华为 xff0c 一连上就断开 xff0c 所以我们认为是APP问题 xff0c 于是我们去找
  • ESP32-S3 LVGL http下载B站头像 JPG显示

    最近在用ESP32S3获取B站JPG头像进行显示 记录一下 xff0c 开发板链接 xff1a ESP32 S3 LVGL 开发板 人工智能语音识别 人脸识别 触摸 音频 淘宝网 taobao com 直接上代码 xff1a 第一种下载方式
  • Docker概述(二)(标贝科技)

    标贝科技 https ai data baker com source 61 qwer12 填写邀请码fwwqgs xff0c 每日免费调用量还可以翻倍 Docker概述 xff08 二 xff09 文章目录 Docker概述 xff08
  • 【串口通信】K210与STM32串口通信、K210与OpenMV串口通信

    串口通信 K210与STM32串口通信 K210与OpenMV串口通信 串口通信前言为何需要串口通信 K210如何进行串口通信K210串口配置K210串口发送相关定义K210串口发送测试K210串口接收相关定义K210串口接收测试 STM3
  • 【ESP32S3系列】1、使用ESP32S3开发板点亮WS2812

    开发板链接 xff1a ESP32 S3 LVGL 开发板BLE人工智能语音人脸识别触摸 音频芯片wifi 淘宝网 taobao com 板子上的LED是WS2812 xff0c 它的引脚分别是 xff1a VDD xff1a 电源 xff
  • win10 使用sonarqube扫描ESP32 C语言工程代码

    社区版的扫描不了C代码 xff0c 所以要找个破解的 项目场景 xff1a 最近项目上霍尼韦尔要求使用sonarqube来扫描代码得出漏洞报告 xff0c 网上找了一圈没有比较容易成功的 xff0c 总结下经验 电脑里要有visual st
  • ESP32 如何引入静态库lib.a

    只需要在Cmakelist txt里这样写 xff1a 如果这个lib正常运行的话需要用到json和lwip相关的函数 xff0c 那就添加一个PRIV REQUIRES 这个component的名字即可
  • OPENMV结合PIX飞控实现四轴定点 循迹 2017电赛

    本文章代码已上传Github xff1a https github com Kevincoooool 2017 Follow 有兴趣的可以加个STAR 自从17年国赛之后 xff0c 自己做了openmv xff0c 加了很多群 xff0c
  • 简易旋转倒立摆设计报告

    完整文档和源码 xff1a https github com Kevincoooool inverted pendulum 43 2017年成都信息工程大学 第六届 电协杯 电子设计竞赛 简易旋转倒立摆及控制装置 xff08 I题 xff0
  • OpenMV-IDE 免KEY提示 编译教程

    很多人在购买了OpenMV之后都没有购买key xff0c 虽然key作为一种捐赠开发者的形式存在 xff0c 但大多数人都是不愿意每次使用都看到那三个提示框的 xff0c 而且不愿意去购买key xff0c 附上我的编译教程 xff1a
  • OpenMV H7也就是OpenMV4来啦 最新

    17年开始做过了四个版本的openmv 可谓是电赛好帮手 开发了两年的openmv xff0c 我的目的就是让openmv变得便宜 目前官方的第四代openmv还在众筹中 xff1a https www kickstarter com pr
  • 开源一个安卓四轴上位机+配套ESP8266 UDP串口通信源码

    手机APP是由匿名四轴遥控器修改而来 xff0c 在原有基础上添加了双摇杆控制和单摇杆控制 数据通过UDP和ESP8266通信 协议可修改 安卓app源码 xff1a https github com Kevincoooool KSTC V
  • c++中函数屏蔽和函数参数屏蔽

    情况一 函数的屏蔽 例如 nbsp nbsp void Function int a int b nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp int c a b 若是不使用函
  • STM32串口之奇偶校验设置

    今天 xff0c 在调试rs485的时候出现了32芯片串口端可以正常发送数据 xff0c 485芯片数据输出端不能发送数据 xff0c 自己查了下485芯片手册 xff0c 发现485芯片有个引脚通过32芯片给出高低电平来控制它是接受还是发
  • 基于AudioQueue实现音频的录制和播放(标贝科技)

    标贝科技 https ai data baker com source 61 qwer12 填写邀请码fwwqgs xff0c 每日免费调用量还可以翻倍 基于AudioQueue实现音频的录制和播放 文章目录 基于AudioQueue实现音