一个简单的录音软件(利用QT录音,ffmpeg进行音频重采样,fdk-aac编码)

         录音软件是一种非常有用的工具,可以帮助我们记录和存储语音信息。在本文中,我们将介绍一个简单的录音软件,该软件利用QT进行录音,使用ffmpeg进行音频重采样,并使用fdk-aac编码。

一、 环境介绍    

1、QT版本: QT5.12.6

2、编译器:  MSVC2017 64

3、ffmpeg版本: 6.1.1

4、完整工程下载地址(下载即可编译运行): https://download.csdn.net/download/u012959478/89624722

二、软件介绍

         本文是一个简单的录音软件的示例,使用QT的QAudioInput来录制原始音频数据,并使用QIODevice作为输入和输出来读取和写入数据,ffmpeg进行音频重采样,fdk-aac进行编码。最终的编码结果保存为AAC格式的文件。

        首先,让我们来介绍一下QT。QT是一个跨平台的应用程序开发框架,它提供了丰富的功能和界面设计工具,可以帮助我们快速开发各种应用程序。在我们的录音软件中,我们将使用QT的多媒体模块的QAudioInput来进行录音。

        接下来,让我们来了解一下ffmpeg。ffmpeg是一个开源的跨平台多媒体处理工具,它可以处理各种音频和视频格式。在我们的录音软件中,我们将使用ffmpeg的音频重采样功能来将录制的音频转换为我们需要的格式。

        最后,让我们来介绍一下fdk-aac。fdk-aac是一个高质量的音频编码器,它可以将音频转换为AAC格式。在我们的录音软件中,我们将使用fdk-aac来对录制的音频进行编码。

        现在,让我们来看看录音软件的主要功能。首先,我们需要实现一个界面,用户可以点击开始录音按钮来开始录音。当用户点击停止录音按钮时,录音将停止并保存为一个音频文件。

        在录音过程中,我们将使用QT的录音类来实现录音功能。当录音停止后,我们将使用ffmpeg进行音频重采样,以将音频转换为我们所需的采样率和格式。最后,我们将使用fdk-aac对音频进行编码,并将其保存为AAC格式的文件。

        在我们的录音软件中,用户还可以选择保存音频文件的路径和文件名。当用户点击保存按钮时,我们将使用QT的文件对话框来选择保存路径和文件名。

三、示例代码  

 audiothread.h

#ifndef AUDIOTHREAD_H
#define AUDIOTHREAD_H#include <QThread>
#include <QFile>extern "C"
{
#include <libswresample/swresample.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/audio_fifo.h>
}class QAudioInput;
class AudioThread : public QThread
{Q_OBJECT
public:explicit AudioThread(QObject *parent = nullptr);~AudioThread();signals:void timeChanged(unsigned long long ms);private:void run();bool init();bool initResample();//初始化重采样bool InitAudioCodec();//初始化音频编码器void encode(AVCodecContext *ctx,AVFrame *frame,AVPacket *pkt, QFile &outFile);void increaseVolume(AVFrame *frame, double volume);//提高音量private:SwrContext *_swr_ctx = nullptr;QAudioInput *_input = nullptr;AVCodecContext *_ac = nullptr;
};#endif // AUDIOTHREAD_H

audiothread.cpp 

#include "audiothread.h"
#include <QAudioInput>
#include <QMutex>
#include <QDebug>
#include <iostream>extern QString g_srcDirPath;AudioThread::AudioThread(QObject *parent):QThread(parent)
{connect(this, &AudioThread::finished,this, &AudioThread::deleteLater);initResample();InitAudioCodec();
}AudioThread::~AudioThread()
{requestInterruption();swr_free(&_swr_ctx);avcodec_free_context(&_ac);// 安全退出quit();wait();
}void AudioThread::run()
{if(!init())return;QFile outFile(g_srcDirPath);if (!outFile.open(QFile::WriteOnly)) {return;}//开始录制音频QIODevice *io = _input->start();//音频重采样输出空间分配AVFrame *pcm = av_frame_alloc();pcm->format = AV_SAMPLE_FMT_S16;pcm->channels = 2;pcm->channel_layout = av_get_default_channel_layout(pcm->channels);pcm->nb_samples = 1024; //一帧音频一通道的采用数量int ret = av_frame_get_buffer(pcm, 0); // 给pcm分配存储空间if (ret != 0){return;}//一次读取一帧音频的字节数int in_nb_samples = av_rescale_rnd(1024, 48000, 44100, AV_ROUND_UP);int readSize = in_nb_samples*_input->format().bytesPerFrame();char *buf = new char[readSize];AVPacket pkt = { 0 };while (!isInterruptionRequested()){//一次读取一帧音频if (_input->bytesReady() < readSize){QThread::msleep(1);continue;}int size = 0;while (size != readSize){int len = io->read(buf + size, readSize - size);if (len < 0)break;size += len;}if (size != readSize)continue;//已经读一帧源数据//重采样源数据const uint8_t *indata[AV_NUM_DATA_POINTERS] = { 0 };indata[0] = (uint8_t *)buf;swr_convert(_swr_ctx, pcm->data, pcm->nb_samples, indata, in_nb_samples);increaseVolume(pcm,10);//麦克风录音声音轻,提高点音量encode(_ac,pcm,&pkt,outFile);}_input->stop();delete []buf;outFile.close();av_frame_free(&pcm);
}bool AudioThread::init()
{if(QAudioDeviceInfo::availableDevices(QAudio::AudioInput).size()<1){qDebug()<<"没有录音设备";return false;}int sampleRate = 48000;int channels = 2;int sampleByte = 2;//qt音频参数设置QAudioFormat fmt;fmt.setSampleRate(sampleRate);fmt.setChannelCount(channels);fmt.setSampleSize(sampleByte * 8);fmt.setCodec("audio/pcm");fmt.setByteOrder(QAudioFormat::LittleEndian);fmt.setSampleType(QAudioFormat::SignedInt);QAudioDeviceInfo info=QAudioDeviceInfo::defaultInputDevice();if (!info.isFormatSupported(fmt)){qDebug() << "Audio format not support!";fmt = info.nearestFormat(fmt);}_input = new QAudioInput(fmt);if( !initResample() || !InitAudioCodec())return false;return true;
}bool AudioThread::InitAudioCodec()
{const AVCodec *codec = avcodec_find_encoder_by_name("libfdk_aac");if(!codec){return false;}_ac = avcodec_alloc_context3(codec);if (!_ac) {return false;}_ac->sample_fmt = AV_SAMPLE_FMT_S16;       // 输入音频的采样大小。fdk_aac需要16位的音频输													                入数据_ac->channel_layout = AV_CH_LAYOUT_STEREO; // 输入音频的CHANNEL LAYOUT_ac->channels = 2;                         // 输入音频的声道数_ac->sample_rate = 44100;                  // 输入音频的采样率_ac->bit_rate = 0;                         // AAC : 128K   AAV_HE: 64K  AAC_HE_V2: 32K. bit_rate为0时会查找profile属性值// 打开编码器int ret = avcodec_open2(_ac,codec,nullptr);if (ret < 0) {return false;}return true;
}bool AudioThread::initResample()
{_swr_ctx = swr_alloc_set_opts(nullptr,AV_CH_LAYOUT_STEREO,AV_SAMPLE_FMT_S16,44100, //输出参数AV_CH_LAYOUT_STEREO,AV_SAMPLE_FMT_S16,48000, //输入参数0,nullptr);if (swr_init(_swr_ctx) < 0){return false;}return true;
}void AudioThread::encode(AVCodecContext *ctx,AVFrame *frame,AVPacket *pkt, QFile &outFile)
{// 发送数据到编码器int ret = avcodec_send_frame(ctx, frame);if (ret < 0){qDebug() << "avcodec_send_frame error" ;return;}// 不断从编码器中取出编码后的数据while (true){// 获取编码后的音频数据ret = avcodec_receive_packet(ctx, pkt);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){return;}else if (ret < 0){return;}// 将编码后的数据写入文件outFile.write((char *) pkt->data, pkt->size);// 释放pkt内部的资源av_packet_unref(pkt);}
}void AudioThread::increaseVolume(AVFrame *frame, double volume)
{int16_t *samples = (int16_t *)frame->data[0];int nb_samples = frame->nb_samples;int channels = av_get_channel_layout_nb_channels(frame->channel_layout);// 提高音量for (int i = 0; i < nb_samples; i++){for (int ch = 0; ch < channels; ch++){// 使用线性插值来提高音量int pcmval = samples[ch] * volume;if (pcmval < 32767 && pcmval > -32768){samples[ch] = pcmval;}else if (pcmval > 32767){samples[ch] = 32767;}else if (pcmval < -32768){samples[ch] = -32768;}}samples += channels;}
}

 界面设计mainwindow.ui

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QTimer>
#include "audiothread.h"QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:void on_audioButton_clicked();void onTimeChanged();void onAudioThreadFinished();void on_saveButton_clicked();private:Ui::MainWindow *ui;AudioThread *_audioThread = nullptr;int m_num;QTimer *_timer;
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTime>
#include <QFileDialog>
#include <QMessageBox>QString g_srcDirPath;
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);ui->lcdNumber->setDigitCount(8);m_num =  -1;onTimeChanged();_timer = new QTimer(this);connect(_timer,&QTimer::timeout,this,&MainWindow::onTimeChanged);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_audioButton_clicked()
{if(g_srcDirPath.isEmpty()){QMessageBox::warning(NULL, "warning", "请设置文件保存路径!\n", QMessageBox::Yes, QMessageBox::Yes);return;}if(!_audioThread){_audioThread = new AudioThread(this);_audioThread->start();connect(_audioThread,&AudioThread::finished,this,&MainWindow::onAudioThreadFinished);ui->audioButton->setText("结束录音");m_num =  -1;onTimeChanged();_timer->start(1000);}else{_audioThread->requestInterruption();}
}void MainWindow::onTimeChanged()
{m_num++;QTime time(0, 0, 0);QString text = time.addSecs(m_num).toString("HH:mm:ss");ui->lcdNumber->display(text);
}void MainWindow::onAudioThreadFinished()
{_audioThread = nullptr;ui->audioButton->setText("开始录音");_timer->stop();
}void MainWindow::on_saveButton_clicked()
{QString runPath = g_srcDirPath;if(runPath.isEmpty()){runPath = QCoreApplication::applicationDirPath() + "/save.aac";}g_srcDirPath = QFileDialog::getSaveFileName(this, "保存文件",runPath,"AAC文件(*.aac)",nullptr,QFileDialog::DontConfirmOverwrite);
}

        通过以上的实现,我们就可以得到一个简单的录音软件,它可以利用QT实现录音,使用ffmpeg进行音频重采样,并使用fdk-aac进行编码。这个录音软件不仅简单易用,可以帮助我们记录和存储语音信息,是一个非常实用的工具。

四、运行效果

        谢谢您的阅读。希望本文能对您有所帮助,并且给您带来了一些新的观点和思考。如果您有任何问题或意见,请随时与我联系。再次感谢您的支持!

 五、相关文章

Windosw下Visual Studio2022编译FFmpeg(支持x264、x265、fdk-acc)-CSDN博客

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

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

相关文章

SuccBI+低代码文档中心 — 可视化分析(仪表板)(上)

有关仪表板的设计器&#xff1a; 查询设置 由于仪表板的设计器是所见即所得的&#xff0c;可以将当前制作的内容和数据的查询结果实时展示在界面中&#xff0c;当引入到仪表板的模型数据量较大时&#xff0c;为了提高设计器界面的查询性能&#xff0c;提供了以下两种方法&…

Azure openai connection with javascript

题意&#xff1a;使用JavaScript与Azure OpenAI进行连接 问题背景&#xff1a; I have created my chatbot with javascript and used open ai. I need to change it to azure open ai but can not find the connection details for javascript. This is how i connect with p…

基于C#调用文心一言大模型制作桌面软件(可改装接口)

目录 开发前的准备账号注册应用创建应用接入开始开发创建项目设计界面使用 AK,SK 生成鉴权签名窗体代码百度智能云千帆大模型平台什么是百度智能云千帆大模型平台模型更新记录开发前的准备 账号注册 访问百度智能云平台,通过百度账号登录或手机号验证。 点此跳转百度智能云平…

数值分析【4】

目录 ​编辑第六章 数值积分微分 龙贝格 高斯求积 查表&#xff1f; 插值求导 两点 ​编辑 三点​编辑 第七章 ode 龙哥库塔 线性多步法 第八章 eig 幂法&#xff1a;v-》Av-》AAv-》……​编辑 反幂法 每次成得是A逆&#xff0c;这样得到摸最小的特征值​编辑 Q…

ubuntu大模型GPU版本安装及部署

版本查看&#xff1a; nvidia-smi 离线下载地址&#xff1a; 下载 NVIDIA 官方驱动 | NVIDIA (选型) Linux x64 (AMD64/EM64T) Display Driver | 535.146.02 | Linux 64-bit | NVIDIA(选型结果) 下载 NVIDIA 官方驱动 | NVIDIA apt-get update 禁用nouveau(nouveau是通用的…

【深度学习|目标跟踪】快速入门卡尔曼滤波!

卡尔曼滤波详解 申明一、什么是卡尔曼滤波1.1 卡尔曼滤波的使用场景1.2 卡尔曼滤波的定义 二、卡尔曼滤波公式详解&#xff08;无推导&#xff09;三、卡尔曼滤波的简单应用 申明 本博客参考了b站up主“华南小虎队”的卡尔曼滤波教学视频以及Lauszus Kristian Sloth Lauszus的卡…

企业微信无法正常启动 报错0xc0000142

解决办法&#xff1a; 1、根据处理器不同位数打开如下目录 32位&#xff1a;C:\Windows\System32 64位&#xff1a;C:\Windows\SysWOW64 我电脑是64位的&#xff0c;就打开&#xff1a;C:\Windows\SysWOW64&#xff0c;然后搜索&#xff1a;kernel32.dll 2、复制一份这个文件至…

Advanced IP Scanner - 网络扫描工具介绍

Advanced IP Scanner 是一款免费、快速且用户友好的网络扫描工具。它能够帮助用户扫描局域网&#xff08;LAN&#xff09;中的所有设备&#xff0c;提供详细的设备信息&#xff0c;包括IP地址、MAC地址、设备名称和厂商信息。该工具对IT管理员和普通用户都非常有用&#xff0c;…

2024剪辑神器盘点:四大热门剪辑软件推荐!

亲爱的朋友们&#xff0c;想要制作出精彩短视频&#xff0c;却苦于找不到合适的剪辑工具&#xff1f;别担心&#xff0c;今天要向大家推荐几款剪辑软件&#xff0c;它们能帮助大家更好地完成视频创作&#xff01; 福昕视频剪辑 链接&#xff1a;www.pdf365.cn/foxit-clip/ 对…

【爬虫实战】利用代理爬取电商数据

文章目录 前言工具介绍实战获取网站数据编写代码数据展示 推荐总结 前言 当今电商平台正经历着快速的转型与升级。随着技术的进步和用户需求的多样化&#xff0c;电商不仅从简单的在线购物演变为综合性的购物生态系统&#xff0c;还融合了人工智能、大数据和云计算等先进技术。…

zdppy+vue3+onllyoffice开发文档管理系统项目实战 20240808 上课笔记

遗留的问题 1、实现删除的功能 2、分享的功能暂时往后放&#xff0c;因为目前没有用户&#xff0c;等有了用户之后再考虑做 3、增加新建和导入按钮 zdppy的学习计划 机器学习平台&#xff0c;QQ音乐的开源项目&#xff0c;https://github.com/tencentmusic/cube-studio&#…

手表运动报告生成以及手机展示

一.运动报告组成部分 一般一份运动健康的报告包括以下信息&#xff1a; 1.运动轨迹区。2.报告数据区。(运动总体概览&#xff0c;如距离&#xff0c;时长&#xff0c;训练表现等)3.曲线图表区。(心率曲线&#xff0c;海拔曲线&#xff0c;速度&#xff0c;配速曲线) 二.组成部…

3.OpenFeign与负载均衡

文章目录 什么是 OpenFegin0penFeign 与 Ribbon.对 consumer 的改造超时配置请求响应的压缩设置选择远程调用的底层实现技术OpenFegin 整合 LoadBalancer 负载均衡负载均衡策略的更换小结 前面消费者对于微服务的消费是通过 RestTemplate 完成的,这种方式的弊端是很明显的:消费…

Qt实现圆形窗口

重新实现paintEvent()函数。 效果如下&#xff1a; 效果为蓝色区域&#xff0c;背景是vs接面&#xff0c;代码直接复制可用&#xff0c;留给有需要的人。 #ifndef CircleWidget_h__ #define CircleWidget_h__#include <QWidget>class CCircleWidget : public QWidget {Q…

学习vue3 三,组件基础,父子组件传值

组件基础 每一个.vue 文件都可以充当组件来使用 每一个组件都可以复用 父组件引入之后可以直接当标签使用 案例&#xff1a; App.vue <script setup lang"ts"> import BaseRefAndReactive from "./components/BaseRefAndReactive.vue";</sc…

MySQL——数据表的基本操作(二)查看数据表

使用 SQL 语句创建好数据表后,可以通过查看数据表结构的定义,以确认数据表的定义是否正确。在 MySQL中,查看数据表的方式有两种,具体如下。 1、使用 SHOW CREATE TABLE 查看数据表 在 MySQL 中,SHOW CREATE TABLE语句不仅可以查看创建表时的定义语句还可以查看表的字符编码。S…

PLL基本原理、设计及应用

PLL基本原理 锁相环&#xff08;Phase-Locked Loop, PLL&#xff09;是一种基本的反馈控制系统&#xff0c;广泛应用于电子通信、信号处理、时钟同步等多个领域。PLL通过反馈机制锁定输入信号的频率和相位&#xff0c;从而实现输出信号与输入信号的同步。其基本工作原理可以概…

使用AI绘图工具生成风景图像的教程

随着人工智能技术的飞速发展&#xff0c;AI绘图工具在图像生成和艺术创作方面变得越来越强大&#xff0c;无论你是一个设计师、艺术家&#xff0c;还是仅仅对生成艺术感兴趣的爱好者&#xff0c;AI绘图工具都可以帮助你轻松地创作出惊艳的风景图像。 在这篇教程中&#xff0c;…

顶顶通手机助手拦截方案

现在很多品牌的手机&#xff0c;都自带语音助手&#xff0c;比如我用的是小米手机&#xff0c;就自带小爱助手&#xff0c;可以自动接听各种广告营销电话。如果来电号码被标记为广告营销&#xff0c;小爱助手就会自动应答&#xff0c;然后模拟真人进行对话。 现在自动外呼系统拨…

RPC通信的简单流程

远程调用者假设需要调用Login方法&#xff0c;将调用的信息通过muduo库&#xff0c;同时进行了序列化和反序列化&#xff0c;发送到Rpcprovider上&#xff0c;RpcProvider通过对象和方法表来确定需要调用哪个服务对象的哪个方法。 UserRpcServiceRpc和UseRpcServiceRpcStub是继…