当前位置: 首页 > article >正文

C语言基础巩固:通过实现简易音频处理函数理解Qwen3-ASR-0.6B输入

C语言基础巩固通过实现简易音频处理函数理解Qwen3-ASR-0.6B输入最近在折腾一些语音相关的项目发现很多朋友对语音模型背后的数据输入感到困惑。大家可能知道怎么调用现成的语音识别接口但一说到模型到底“吃”进去什么样的数据就有点摸不着头脑了。特别是像Qwen3-ASR-0.6B这样的模型它对输入音频的格式、采样率、位深都有特定要求。其实理解这些要求最好的方式就是自己动手写点代码。今天咱们就用C语言从最基础的音频文件读写开始一步步实现几个简单的处理函数。这个过程不仅能帮你巩固C语言的文件操作、内存管理和数据结构更能让你直观地看到一段原始音频数据是如何被“收拾”得整整齐齐然后喂给模型的。咱们的目标不是造轮子而是通过亲手“拧螺丝”搞清楚整个音频预处理流水线是怎么工作的。准备好了吗我们开始吧。1. 从WAV文件开始理解音频的“身份证”在处理任何音频之前我们得先知道它长什么样。WAV文件就像音频世界的“标准居民”它的结构非常清晰。一个WAV文件主要分为两部分文件头和数据块。文件头就是这段音频的“身份证”记录了所有关键信息。1.1 WAV文件头里藏着什么秘密你可以把WAV文件头想象成一个包裹的运单。运单上会写明包裹的大小、内容物、重量等信息。WAV文件头也是类似的它用44个字节这是最常见的情况告诉程序“嘿我是一段PCM编码的音频采样率是16000Hz单声道每个采样点用16位表示。”为了在C语言里方便地操作这些信息我们定义一个结构体来“翻译”这个文件头#include stdint.h // 使用标准整数类型确保跨平台一致性 typedef struct { // RIFF块 char riff[4]; // 固定为RIFF uint32_t fileSize; // 文件总大小减8字节 char wave[4]; // 固定为WAVE // fmt子块 char fmt[4]; // 固定为fmt uint32_t fmtSize; // fmt子块的大小通常为16 uint16_t audioFormat; // 音频格式1表示PCM脉冲编码调制 uint16_t numChannels; // 声道数1为单声道2为立体声 uint32_t sampleRate; // 采样率如16000每秒采样次数 uint32_t byteRate; // 每秒数据字节数 sampleRate * numChannels * bitsPerSample/8 uint16_t blockAlign; // 每个采样帧的字节数 numChannels * bitsPerSample/8 uint16_t bitsPerSample; // 每个采样点的位数如16 // data子块 char data[4]; // 固定为data uint32_t dataSize; // 音频数据部分的大小字节 } WavHeader;这个结构体里的每个字段都对应着WAV文件头中的一个特定信息。比如sampleRate是模型非常关心的一个参数。Qwen3-ASR-0.6B通常期望输入音频的采样率是16000Hz。如果原始音频是8000Hz或44100Hz我们就需要通过后续的重采样步骤来调整。1.2 动手读取WAV文件头定义好了结构体接下来我们写一个函数来读取它。这个过程就像按照固定格式去解析一份电报。#include stdio.h #include stdlib.h #include string.h int readWavHeader(const char* filename, WavHeader* header) { FILE* file fopen(filename, rb); // 以二进制只读方式打开 if (!file) { printf(无法打开文件: %s\n, filename); return -1; } // 一次性读取整个文件头结构 size_t readCount fread(header, sizeof(WavHeader), 1, file); if (readCount ! 1) { printf(读取文件头失败\n); fclose(file); return -1; } // 简单验证检查关键标识 if (memcmp(header-riff, RIFF, 4) ! 0 || memcmp(header-wave, WAVE, 4) ! 0) { printf(这不是一个有效的WAV文件\n); fclose(file); return -1; } fclose(file); printf(文件头读取成功\n); printf(采样率: %u Hz\n, header-sampleRate); printf(声道数: %u\n, header-numChannels); printf(位深度: %u 位\n, header-bitsPerSample); printf(数据大小: %u 字节\n, header-dataSize); return 0; }这个函数做了几件事打开文件、读取44个字节到我们定义的结构体里、然后检查几个关键标识“RIFF”和“WAVE”以确保这确实是一个合法的WAV文件。最后它打印出关键信息让我们一眼就能看到这段音频的基本属性。你可以写个简单的main函数测试一下int main() { WavHeader header; if (readWavHeader(test.wav, header) 0) { // 读取成功header里已经包含了所有信息 } return 0; }2. 把音频数据“搬”进内存读懂了文件头接下来就要处理真正的音频数据了。对于模型来说它需要的不是磁盘上的文件而是内存中整齐排列的一串数字。这些数字代表了每个采样时刻的声波振幅。2.1 读取PCM音频数据对于最常见的16位PCM格式每个采样点是一个有符号的16位整数int16_t。我们需要根据文件头里的信息计算出有多少个采样点然后申请一块合适大小的内存来存放它们。int16_t* readPcmData(const char* filename, const WavHeader* header, uint32_t* outNumSamples) { FILE* file fopen(filename, rb); if (!file) { return NULL; } // 跳过文件头直接定位到音频数据开始处 fseek(file, sizeof(WavHeader), SEEK_SET); // 计算采样点总数 // 每个采样点占 bitsPerSample/8 字节总字节数为 dataSize uint32_t numSamples header-dataSize / (header-bitsPerSample / 8); *outNumSamples numSamples; // 申请内存来存放所有采样点 int16_t* audioData (int16_t*)malloc(header-dataSize); if (!audioData) { printf(内存分配失败\n); fclose(file); return NULL; } // 读取音频数据 size_t readCount fread(audioData, 1, header-dataSize, file); if (readCount ! header-dataSize) { printf(音频数据读取不完整\n); free(audioData); fclose(file); return NULL; } fclose(file); printf(成功读取 %u 个采样点\n, numSamples); return audioData; }这个函数返回了一个指针指向存放音频数据的内存块。每个采样点的值范围通常在-32768到32767之间对于16位有符号整数。这个数值范围直接对应着录音时声波的振幅大小。2.2 处理立体声音频转为单声道很多音频文件是立体声的有两个声道左和右。但像Qwen3-ASR-0.6B这样的语音识别模型通常只需要单声道音频。所以我们需要把两个声道合并成一个。最简单的合并方式就是取平均值。虽然这不是最高质量的下混方法但对于理解原理来说足够了。int16_t* convertToMono(const int16_t* stereoData, uint32_t numSamples, int numChannels) { if (numChannels ! 2) { printf(不是立体声音频无需转换\n); return NULL; } // 立体声的采样点数是单声道的两倍每个采样时刻有两个值 uint32_t monoNumSamples numSamples / 2; int16_t* monoData (int16_t*)malloc(monoNumSamples * sizeof(int16_t)); if (!monoData) { return NULL; } // 每两个采样点左、右合并为一个 for (uint32_t i 0; i monoNumSamples; i) { int32_t sum (int32_t)stereoData[i * 2] (int32_t)stereoData[i * 2 1]; monoData[i] (int16_t)(sum / 2); // 取平均 } printf(已将立体声转换为单声道采样点从 %u 减少到 %u\n, numSamples, monoNumSamples); return monoData; }这里有个细节需要注意两个16位数相加可能会超出16位的范围-32768到32767所以我们先用32位的int32_t来保存中间结果然后再转换回16位。这样可以避免溢出导致的音频失真。3. 让音频“变速不变调”重采样音频重采样可能是预处理中最关键的步骤之一。不同的音频源可能有不同的采样率但模型通常只接受特定采样率的输入。比如Qwen3-ASR-0.6B期望的是16000Hz的音频。3.1 实现简单的线性插值重采样重采样的核心思想是我们需要一套新的采样点但音频的“内容”即声音的频率成分不能变。线性插值是一种直观的方法——如果新采样点落在两个原始采样点之间我们就用这两个点的值按比例混合出一个新值。int16_t* resampleAudio(const int16_t* input, uint32_t inputLen, uint32_t inputRate, uint32_t outputRate) { if (inputRate outputRate) { printf(采样率相同无需重采样\n); return NULL; } // 计算输出音频的采样点数量 // 比如从8000Hz重采样到16000Hz时长不变采样点数量翻倍 double ratio (double)outputRate / inputRate; uint32_t outputLen (uint32_t)(inputLen * ratio); int16_t* output (int16_t*)malloc(outputLen * sizeof(int16_t)); if (!output) { return NULL; } // 线性插值重采样 for (uint32_t i 0; i outputLen; i) { // 找到在输入音频中对应的位置可能不是整数 double inputPos i / ratio; uint32_t leftIndex (uint32_t)inputPos; uint32_t rightIndex leftIndex 1; // 处理边界情况 if (rightIndex inputLen) { rightIndex inputLen - 1; } // 计算插值权重 double weight inputPos - leftIndex; // 线性插值output left*(1-weight) right*weight double interpolated (1.0 - weight) * input[leftIndex] weight * input[rightIndex]; output[i] (int16_t)interpolated; } printf(重采样完成%u Hz - %u Hz采样点从 %u 变为 %u\n, inputRate, outputRate, inputLen, outputLen); return output; }这个实现虽然简单但已经能说明重采样的基本原理。在实际工程中可能会使用更复杂的插值算法如样条插值或专门的库如libsamplerate但线性插值是个很好的起点。3.2 为什么重采样对语音识别很重要你可以把采样率理解为对声音的“拍照频率”。采样率越高记录的声音细节越多。但模型在训练时使用的是固定采样率的音频比如16000Hz。如果我们输入一个8000Hz的音频模型就会“听不懂”——因为时间轴对不上。重采样就是调整这个“拍照频率”让所有音频都在同一个时间尺度上。这样模型才能正确识别语音内容。4. 给音频“降噪”简单的数字滤波原始音频中可能包含一些我们不想要的高频噪声。虽然现代语音模型通常有一定的抗噪能力但一个简单的低通滤波有时能让结果更好。4.1 实现移动平均滤波器移动平均是最简单的滤波方式之一。它的思想是每个输出采样点是附近几个输入采样点的平均值。这样可以平滑掉快速变化的高频成分。int16_t* applyLowPassFilter(const int16_t* input, uint32_t length, int windowSize) { if (windowSize 2) { printf(窗口大小至少为2\n); return NULL; } int16_t* output (int16_t*)malloc(length * sizeof(int16_t)); if (!output) { return NULL; } // 处理边界开头和结尾的采样点无法用完整窗口这里简单复制 for (int i 0; i windowSize / 2 i length; i) { output[i] input[i]; output[length - 1 - i] input[length - 1 - i]; } // 对中间部分应用移动平均 for (uint32_t i windowSize / 2; i length - windowSize / 2; i) { int32_t sum 0; // 计算窗口内采样点的和 for (int j -windowSize / 2; j windowSize / 2; j) { sum input[i j]; } output[i] (int16_t)(sum / windowSize); } printf(已应用移动平均滤波窗口大小%d\n, windowSize); return output; }这个滤波器会稍微让声音变得“闷”一点因为它削弱了高频部分。但对于语音来说大部分重要信息都在低频所以适度的滤波有时反而有助于识别。5. 完整的预处理流程现在我们把上面这些步骤串起来形成一个完整的音频预处理流程。这个流程的输出就是可以直接喂给Qwen3-ASR-0.6B等语音模型的“干净”数据。void processAudioForASR(const char* inputFile, const char* outputFile, uint32_t targetSampleRate) { printf( 开始处理音频%s \n, inputFile); // 1. 读取WAV文件头 WavHeader header; if (readWavHeader(inputFile, header) ! 0) { printf(读取文件头失败\n); return; } // 2. 读取音频数据 uint32_t numSamples; int16_t* audioData readPcmData(inputFile, header, numSamples); if (!audioData) { printf(读取音频数据失败\n); return; } // 3. 如果是立体声转为单声道 int16_t* monoData NULL; if (header.numChannels 2) { monoData convertToMono(audioData, numSamples, 2); free(audioData); // 释放原始数据 audioData monoData; numSamples numSamples / 2; // 采样点数减半 header.numChannels 1; // 更新声道数 } // 4. 如果需要进行重采样 int16_t* resampledData NULL; if (header.sampleRate ! targetSampleRate) { resampledData resampleAudio(audioData, numSamples, header.sampleRate, targetSampleRate); if (resampledData) { free(audioData); audioData resampledData; numSamples (uint32_t)(numSamples * (double)targetSampleRate / header.sampleRate); header.sampleRate targetSampleRate; // 更新采样率 } } // 5. 可选应用低通滤波 int16_t* filteredData applyLowPassFilter(audioData, numSamples, 5); if (filteredData) { free(audioData); audioData filteredData; } // 6. 这里可以添加归一化等更多处理步骤... printf( 处理完成 \n); printf(最终参数%u Hz, %u 声道, %u 个采样点\n, header.sampleRate, header.numChannels, numSamples); // 清理内存 free(audioData); }这个流程展示了典型的音频预处理步骤读取→声道转换→重采样→滤波。在实际应用中可能还会包括音量归一化、静音检测与切除、特征提取如MFCC等步骤但上面的核心流程已经涵盖了最基础的部分。6. 总结走完这一趟你应该对音频预处理有了更直观的理解。我们通过C语言实现了一系列基础但关键的函数从解析WAV文件头到读取PCM数据再到声道转换、重采样和简单滤波。每一步都是在为后续的语音识别模型准备“食材”。对于Qwen3-ASR-0.6B这样的模型它期望的输入通常是这样处理后的音频单声道、16000Hz采样率、16位位深的PCM数据。当然在实际调用模型API时可能还需要将PCM数据转换为模型特定的输入格式如对数梅尔频谱图但那是更上游的处理了。自己动手实现这些函数的最大好处是当遇到问题时你知道该从哪里排查。是采样率不对是声道数不对还是数据格式有问题有了这些底层知识你就能快速定位。如果你还想继续深入可以尝试扩展这些函数支持更多音频格式如MP3、FLAC、实现更高质量的重采样算法、添加语音活动检测VAD来自动切除静音段。每深入一步你对音频处理的理解就会更扎实一分。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。

相关文章:

C语言基础巩固:通过实现简易音频处理函数理解Qwen3-ASR-0.6B输入

C语言基础巩固:通过实现简易音频处理函数理解Qwen3-ASR-0.6B输入 最近在折腾一些语音相关的项目,发现很多朋友对语音模型背后的数据输入感到困惑。大家可能知道怎么调用现成的语音识别接口,但一说到模型到底“吃”进去什么样的数据&#xff…...

别再只会重启了!手把手教你用BlueScreenView和WhoCrashed精准定位Windows蓝屏元凶

从蓝屏恐慌到精准诊断:Windows崩溃分析实战指南 1. 蓝屏现象的本质与诊断价值 每当那抹刺眼的蓝色突然占据屏幕,大多数用户的第一反应往往是慌乱地按下电源键。然而,这种条件反射式的重启操作,恰恰让我们错过了系统留下的宝贵诊断…...

AnimateDiff在教育领域的应用:交互式课件自动生成

AnimateDiff在教育领域的应用:交互式课件自动生成 1. 教育场景里的真实痛点 上周听一位中学物理老师聊起备课的事,她说现在每准备一节关于电磁感应的课,光是找合适的动画演示就要花两小时——网上资源要么太专业学生看不懂,要么…...

LightOnOCR-2-1B实现.NET平台文档自动化处理方案

LightOnOCR-2-1B实现.NET平台文档自动化处理方案 1. 企业文档处理的痛点与机遇 每天,企业都要处理大量的文档——合同、发票、报告、扫描档案...这些文档往往以PDF、图片等非结构化格式存在,人工处理既耗时又容易出错。传统OCR方案要么识别精度不够&am…...

别再只会调库了!手把手带你用C语言和GPIO操作28BYJ-48步进电机(基于I.MX6ULL)

从寄存器操作到精准控制:I.MX6ULL裸机驱动28BYJ-48步进电机全解析 在嵌入式开发领域,能够脱离现成驱动库直接操作硬件是工程师的核心竞争力。本文将带你用最原始的方式——直接操作I.MX6ULL的GPIO寄存器,实现28BYJ-48步进电机的精准控制。不同…...

如何利用A股上市公司新闻舆情数据优化投资决策?3个实战案例分析

如何利用A股上市公司新闻舆情数据优化投资决策?3个实战案例分析 在信息爆炸的时代,投资者每天面对海量的上市公司新闻、公告和社交媒体讨论,如何从中提取真正有价值的信号?传统的基本面分析和技术分析固然重要,但往往滞…...

Win11Debloat:Windows系统深度优化与隐私保护终极指南

Win11Debloat:Windows系统深度优化与隐私保护终极指南 【免费下载链接】Win11Debloat 一个简单的PowerShell脚本,用于从Windows中移除预装的无用软件,禁用遥测,从Windows搜索中移除Bing,以及执行各种其他更改以简化和改…...

别再手动轮询了!用STM32的UART DMA+环形缓冲区处理不定长数据(附状态机解析代码)

STM32高效串口通信:DMA环形缓冲区与状态机实战指南 在嵌入式开发中,串口通信是最基础却又最常出问题的环节之一。特别是当面对GPS模块、无线模块等设备发送的不定长数据包时,传统的轮询或简单中断方式往往会导致数据丢失、系统卡顿甚至崩溃。…...

ENVI5.3实战:如何用landsat_gapfill工具一键去除Landsat影像的讨厌条纹(附工具下载)

ENVI5.3实战指南:Landsat影像条纹修复全流程解析与landsat_gapfill工具深度应用 遥感影像处理中,数据质量直接影响分析结果的可靠性。Landsat系列卫星作为地球观测的中坚力量,其影像偶尔出现的条纹噪声让许多研究者头疼不已。这些条纹不仅影…...

清华大学《信号与系统》电力系统同步相量计算【FFT谐波小波变换】

✅作者简介:热爱科研的Matlab仿真开发者,擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室🍊个人信条:格物致知,完整Matlab代码及仿真咨询…...

基于matlab的包络谱分析,目标信号→希尔伯特变换→得到解析信号→求解析信号的模→得到包络信...

基于matlab的包络谱分析,目标信号→希尔伯特变换→得到解析信号→求解析信号的模→得到包络信号→傅里叶变换→得到Hilbert包络谱,包络谱分析能够有效地将这种低频冲击信号进行解调提取。 程序已调通,可直接运行。 最近在搞设备故障诊断的时…...

Qt5中文乱码终极解决方案:从编码原理到实战避坑(Windows/Linux双平台)

Qt5中文乱码终极解决方案:从编码原理到实战避坑(Windows/Linux双平台) 在跨平台GUI开发中,中文乱码问题堪称Qt开发者的"必修课"。每当看到界面上出现的一串问号或火星文,开发者们往往陷入编码转换的迷宫。本…...

DebouncedEdgeIn:嵌入式抗抖动边沿触发输入实现

1. DebouncedEdgeIn:嵌入式系统中抗抖动边沿触发输入的工程实现1.1 问题起源:机械开关与数字输入的固有矛盾在嵌入式硬件开发中,按键、拨码开关、继电器触点等机械式输入器件普遍存在**接触抖动(Contact Bounce)**现象…...

Packet Tracer实战:校园网三层架构搭建全流程(附VLAN划分与DHCP配置)

Packet Tracer实战:校园网三层架构搭建全流程(附VLAN划分与DHCP配置) 校园网络作为数字化教育的基础设施,其稳定性和扩展性直接影响教学活动的开展。传统校园网设计常面临广播风暴、IP管理混乱、安全隔离不足等问题。本文将基于Ci…...

3步快速完成音频转文字:AsrTools语音识别工具完全指南

3步快速完成音频转文字:AsrTools语音识别工具完全指南 【免费下载链接】AsrTools ✨ AsrTools: Smart Voice-to-Text Tool | Efficient Batch Processing | User-Friendly Interface | No GPU Required | Supports SRT/TXT Output | Turn your audio into accurate …...

Stable-Diffusion-V1-5 结合传统图像处理:使用OpenCV进行生成后处理

Stable-Diffusion-V1-5 结合传统图像处理:使用OpenCV进行生成后处理 你有没有遇到过这样的情况?用Stable Diffusion生成了一张构图、创意都很棒的图片,但总觉得差了那么一点意思——颜色有点灰蒙蒙的,细节不够锐利,或…...

避坑指南:Halcon三通道图像操作中set_grayval的5个常见错误(附正确写法)

Halcon三通道图像操作中set_grayval的5个高频错误与工业级解决方案 在工业视觉项目里处理彩色图像时,set_grayval就像个带着陷阱的瑞士军刀——功能强大但稍有不慎就会踩坑。上周有个做半导体检测的客户发来紧急求助,他们的AOI系统在处理金线键合图像时&…...

探索基于Matlab的FFT滤波:谐波分析与频段处理

基于matlab的FFT滤波,可以实现对simulink模型中示波器的波形数据或者外部mat数据、csv数据进行谐波分析(FFT)和自定义频段清除,对已有数据特定频段的数据进行提取也可以。 优点是滤波前后波形无相位滞后,幅值衰减可补偿,不足之处在…...

2025年AI视频生成工具大比拼:5款免费神器与谷歌VEO 2的终极对决

1. 2025年AI视频生成工具现状与竞争格局 2025年的AI视频生成领域已经进入白热化阶段,各种工具层出不穷,功能也越来越强大。作为一个长期关注这个领域的技术爱好者,我亲眼见证了从最初的简单动画生成到如今近乎电影级质量的视频创作。现在的AI…...

QMCDecode:突破QQ音乐加密限制,让音乐文件重获自由

QMCDecode:突破QQ音乐加密限制,让音乐文件重获自由 【免费下载链接】QMCDecode QQ音乐QMC格式转换为普通格式(qmcflac转flac,qmc0,qmc3转mp3, mflac,mflac0等转flac),仅支持macOS,可自动识别到QQ音乐下载目录&#xff…...

取证专家不会告诉你的技巧:用UFS Explorer Technician生成法庭级数据完整性报告

数字取证实战:用UFS Explorer Technician构建法庭级证据链 在电子数据取证领域,一份经得起法庭质询的报告不仅需要呈现结果,更需要完整展示从数据获取到分析的全链条过程。专业取证工具的选择往往决定了证据的可采信度,而操作流程…...

从数据集到高精度模型:基于YOLOv5/v8的蘑菇种类智能检测实战

1. 蘑菇识别项目背景与价值 野生蘑菇种类繁多,有些可食用,有些却含有剧毒。每年因误食毒蘑菇导致的中毒事件屡见不鲜。传统识别方法依赖专家经验,普通人很难准确辨别。这正是计算机视觉技术可以大显身手的地方——通过目标检测算法自动识别蘑…...

用过才敢说 一键生成论文工具测评:2026年最新推荐与对比

2026年真正好用的一键生成论文工具,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。…...

【rust】Rust 默认引用 std::prelude

读音与词义 prelude 的读音:英式/美式均为 /ˈprel.juːd/(“prel-yood”) 含义:名词,意为"序曲、前奏、序幕"。在编程语言中,指自动导入的预定义模块,为代码提供开箱即用的常用功能。…...

QMCDecode:重构音乐格式自由的开源工具 | 音乐爱好者的用户主权解决方案

QMCDecode:重构音乐格式自由的开源工具 | 音乐爱好者的用户主权解决方案 【免费下载链接】QMCDecode QQ音乐QMC格式转换为普通格式(qmcflac转flac,qmc0,qmc3转mp3, mflac,mflac0等转flac),仅支持macOS,可自动识别到QQ音乐下载目录…...

SEO_中小企业必备的SEO优化入门方法指南

为什么中小企业需要SEO优化? 在当今互联网时代,拥有一个高效的网站是企业生存和发展的关键。特别是中小企业,它们往往面临激烈的市场竞争。如何让自己的网站在众多的网站中脱颖而出,成为用户搜索结果中的首选呢?这就是…...

Language Selector:重新定义Android应用语言管理的颠覆式工具

Language Selector:重新定义Android应用语言管理的颠覆式工具 【免费下载链接】Language-Selector Language Selector let users select individual app languages (Android 13) 项目地址: https://gitcode.com/gh_mirrors/la/Language-Selector Language Se…...

STM32项目文档自动化分析:CasRel抽取硬件模块关联

STM32项目文档自动化分析:CasRel抽取硬件模块关联 1. 引言 接手一个新的STM32项目,尤其是那种已经迭代了好几个版本、代码量不小的项目,对嵌入式工程师来说,第一感觉往往是“头大”。项目里用了哪些外设?UART1的TX引…...

DeOldify Web界面汉化教程:修改前端i18n配置支持中英文双语切换

DeOldify Web界面汉化教程:修改前端i18n配置支持中英文双语切换 1. 为什么需要汉化DeOldify界面? 如果你用过DeOldify这个黑白照片上色工具,可能会发现它的Web界面默认是英文的。对于很多国内用户来说,满屏的英文按钮和提示&…...

小米智能家居集成全攻略:Xiaomi Miot解决方案实现设备无感联动

小米智能家居集成全攻略:Xiaomi Miot解决方案实现设备无感联动 【免费下载链接】hass-xiaomi-miot Automatic integrate all Xiaomi devices to HomeAssistant via miot-spec, support Wi-Fi, BLE, ZigBee devices. 小米米家智能家居设备接入Hass集成 项目地址: h…...