> 文章列表 > QT学习笔记(录制 wav 音频 )

QT学习笔记(录制 wav 音频 )

QT学习笔记(录制 wav 音频 )

录制 wav 音频

在 12.5 小节,已经介绍过开发板如何录制音频文件了,详细请看 12.5 小节,就不详细介绍
了,注意需要修改的地方如下。因为百度语音识别支持采样率 16000、8000 的固定值,16bit

深的单声道,音频长度最长 60 秒。格式支持 wav,pcm 等格式。我们需要修改录制音频格式为

wav 格式,通道为单声道,采样率为 16000,源码如下,已用红色字体标出。录制的音频文件
保存为本地 16k.wav 文件。

源码路径为 4/02_asr_demo/audiorecorder/audiorecorder.cpp。

 /****************************************************************** Copyright © Deng Zhimao Co., Ltd. 1990-2021. All rights reserved. * @projectName audiorecorder * @brief audiorecorder.cpp * @author Deng Zhimao * @email 1252699831@qq.com * @net www.openedv.com * @date 2021-06-04 *******************************************************************/ 1 #include "audiorecorder.h" 2 #include <QDebug> 3 #include <QUrl> 4 #include <QDateTime> 5 #include <QDir> 6 #include <QCoreApplication> 7 8 static qreal getPeakValue(const QAudioFormat &format); 9 static QVector<qreal> getBufferLevels(const QAudioBuffer &buffer); 10 11 template <class T> 12 static QVector<qreal> getBufferLevels(const T *buffer, int frames, int 
channels); 13 14 AudioRecorder::AudioRecorder(QWidget *parent) 15 { 16 Q_UNUSED(parent); 17 18 /* 录制音频的类 */ 19 m_audioRecorder = new QAudioRecorder(this); 20 
21 /* 用于探测缓冲区的数据 */ 22 m_probe = new QAudioProbe(this); 23 24 /* 信号槽连接,更新录音 level 显示 */ 25 connect(m_probe, &QAudioProbe::audioBufferProbed, 26 this, &AudioRecorder::processBuffer); 27 28 /* 设置探测的对象 */ 29 m_probe->setSource(m_audioRecorder); 30 31 /* 扫描本地声卡设备 */ 32 devicesVar.append(QVariant(QString())); 33 for (auto &device: m_audioRecorder->audioInputs()) { 34 devicesVar.append(QVariant(device)); 35 //qDebug()<<"本地声卡设备:"<<device<<endl; 36 } 37 38 /* 音频编码 */ 39 codecsVar.append(QVariant(QString())); 40 for (auto &codecName: m_audioRecorder->supportedAudioCodecs()) { 41 codecsVar.append(QVariant(codecName)); 42 //qDebug()<<"音频编码:"<<codecName<<endl; 43 } 44 45 /* 容器/支持的格式 */ 46 containersVar.append(QVariant(QString())); 47 for (auto &containerName: m_audioRecorder->supportedContainers()) { 48 containersVar.append(QVariant(containerName)); 49 //qDebug()<<"支持的格式:"<<containerName<<endl; 50 } 51 52 /* 采样率 */ 53 sampleRateVar.append(QVariant(0)); 54 /* 百度语音识别只支持 8000、 16000 采样率 */ 55 sampleRateVar.append(QVariant(8000)); 56 sampleRateVar.append(QVariant(16000)); 57 for (int sampleRate: m_audioRecorder->supportedAudioSampleRates()) { 58 sampleRateVar.append(QVariant(sampleRate)); 59 //qDebug()<<"采样率:"<<sampleRate<<endl; 60 } 
61 62 63 /* 通道 */ 64 channelsVar.append(QVariant(-1)); 65 channelsVar.append(QVariant(1)); 66 channelsVar.append(QVariant(2)); 67 channelsVar.append(QVariant(4)); 68 69 /* 质量 */ 70 qualityVar.append(QVariant(int(QMultimedia::LowQuality))); 71 qualityVar.append(QVariant(int(QMultimedia::NormalQuality))); 72 qualityVar.append(QVariant(int(QMultimedia::HighQuality))); 73 74 /* 比特率 */ 75 bitratesVar.append(QVariant(0)); 76 bitratesVar.append(QVariant(32000)); 77 bitratesVar.append(QVariant(64000)); 78 bitratesVar.append(QVariant(96000)); 79 bitratesVar.append(QVariant(128000)); 80 81 /* 录音类信号槽连接 */ 82 connect(m_audioRecorder, &QAudioRecorder::durationChanged, 83 this, &AudioRecorder::updateProgress); 84 } 85 86 AudioRecorder::~AudioRecorder() 87 { 88 } 89 90 91 void AudioRecorder::startRecorder() 92 { 93 /* 备注:录音需要设置成 16000 采样率和通道数为 1, 
94 * 保存为 wav 文件需要设置成 audio/x-wav(container 文件格式) */ 95 96 /* 如果录音已经停止,则开始录音 */ 97 if (m_audioRecorder->state() == QMediaRecorder::StoppedState) { 98 /* 设置默认的录音设备 */ 99 m_audioRecorder->setAudioInput(devicesVar.at(0).toString()); 100 101 /* 下面的是录音设置 */ 102 QAudioEncoderSettings settings; 103 settings.setCodec(codecsVar.at(0).toString()); 104 settings.setSampleRate(sampleRateVar[2].toInt()); 105 settings.setBitRate(bitratesVar[0].toInt()); 106 settings.setChannelCount(channelsVar[1].toInt()); 107 settings.setQuality(QMultimedia::EncodingQuality( 108 qualityVar[0].toInt())); 109 110 /* 以恒定的质量录制,可选恒定的比特率 */ 111 
settings.setEncodingMode(QMultimedia::ConstantQualityEncoding); 112 113 /* I.MX6ULL 第 20 个支持的格式为 audio/x-wav */ 114 QString container = containersVar.at(20).toString(); 115 116 /* 使用配置 */ 117 m_audioRecorder->setEncodingSettings(settings, 118 QVideoEncoderSettings(), 119 container); 120 /* 录音保存为 16k.wav 文件 */ 121 m_audioRecorder->setOutputLocation(QUrl::fromLocalFile(tr("./16k.wav"))
); 122 123 /* 开始录音 */ 124 m_audioRecorder->record(); 125 } 126 } 127 128 void AudioRecorder::stopRecorder() 129 { 130 /* 停止录音 */ 131 m_audioRecorder->stop(); 132 } 133 134 135 void AudioRecorder::updateProgress(qint64 duration) 136 { 137 Q_UNUSED(duration); 138 139 if (m_audioRecorder->error() 140 != QMediaRecorder::NoError) 141 return; 142 143 /* 打印录制时长 */ 144 //qDebug()<<duration / 1000<<endl; 145 } 146 147 148 void AudioRecorder::clearAudioLevels() 149 { 150 //... 151 } 152 153 // This function returns the maximum possible sample value for a given 
audio format 154 qreal getPeakValue(const QAudioFormat& format) 155 { 156 // Note: Only the most common sample formats are supported 157 if (!format.isValid()) 158 return qreal(0); 159 160 if (format.codec() != "audio/pcm") 161 return qreal(0); 162 163 switch (format.sampleType()) { 164 case QAudioFormat::Unknown: 165 break; 166 case QAudioFormat::Float: 167 if (format.sampleSize() != 32) // other sample formats are not 
supported 168 return qreal(0); 169 return qreal(1.00003); 170 case QAudioFormat::SignedInt: 171 if (format.sampleSize() == 32) 172 return qreal(INT_MAX); 173 if (format.sampleSize() == 16) 174 return qreal(SHRT_MAX); 175 if (format.sampleSize() == 8) 176 return qreal(CHAR_MAX); 177 break; 178 case QAudioFormat::UnSignedInt: 179 if (format.sampleSize() == 32) 180 return qreal(UINT_MAX); 181 if (format.sampleSize() == 16) 182 return qreal(USHRT_MAX); 183 if (format.sampleSize() == 8) 184 return qreal(UCHAR_MAX); 185 break; 186 } 187 188 return qreal(0); 189 } 190 191 // returns the audio level for each channel 192 QVector<qreal> getBufferLevels(const QAudioBuffer& buffer) 193 { 194 QVector<qreal> values; 195 196 if (!buffer.format().isValid() || buffer.format().byteOrder() != 
QAudioFormat::LittleEndian) 197 return values; 198 199 if (buffer.format().codec() != "audio/pcm") 200 return values; 201 202 int channelCount = buffer.format().channelCount(); 203 values.fill(0, channelCount); 204 qreal peak_value = getPeakValue(buffer.format()); 205 if (qFuzzyCompare(peak_value, qreal(0))) 206 return values; 207 208 switch (buffer.format().sampleType()) { 209 case QAudioFormat::Unknown: 210 case QAudioFormat::UnSignedInt: 211 if (buffer.format().sampleSize() == 32) 212 values = getBufferLevels(buffer.constData<quint32>(), 
buffer.frameCount(), channelCount); 213 if (buffer.format().sampleSize() == 16) 214 values = getBufferLevels(buffer.constData<quint16>(), 
buffer.frameCount(), channelCount); 215 if (buffer.format().sampleSize() == 8) 216 values = getBufferLevels(buffer.constData<quint8>(), 
buffer.frameCount(), channelCount); 217 for (int i = 0; i < values.size(); ++i) 218 values[i] = qAbs(values.at(i) - peak_value / 2) / (peak_value / 2); 219 break; 220 case QAudioFormat::Float: 221 if (buffer.format().sampleSize() == 32) { 222 values = getBufferLevels(buffer.constData<float>(), 
buffer.frameCount(), channelCount); 223 for (int i = 0; i < values.size(); ++i) 224 values[i] /= peak_value; 225 } 226 break; 227 case QAudioFormat::SignedInt: 228 if (buffer.format().sampleSize() == 32) 229 values = getBufferLevels(buffer.constData<qint32>(), 
buffer.frameCount(), channelCount); 230 if (buffer.format().sampleSize() == 16) 231 values = getBufferLevels(buffer.constData<qint16>(), 
buffer.frameCount(), channelCount); 232 if (buffer.format().sampleSize() == 8) 233 values = getBufferLevels(buffer.constData<qint8>(), 
buffer.frameCount(), channelCount); 234 for (int i = 0; i < values.size(); ++i) 235 values[i] /= peak_value; 236 break; 237 } 238 239 return values; 240 } 241 242 template <class T> 243 QVector<qreal> getBufferLevels(const T *buffer, int frames, int 
channels) 244 { 245 QVector<qreal> max_values; 246 max_values.fill(0, channels); 247 248 for (int i = 0; i < frames; ++i) { 249 for (int j = 0; j < channels; ++j) { 250 qreal value = qAbs(qreal(buffer[i * channels + j])); 251 if (value > max_values.at(j)) 252 max_values.replace(j, value); 253 } 254 } 255 256 return max_values; 257 } 258 259 void AudioRecorder::processBuffer(const QAudioBuffer& buffer) 260 { 261 /* 根据通道数目需要显示 count 个 level */ 262 int count = buffer.format().channelCount(); 263 /* 打印通道数 */ 264 Q_UNUSED(count); 265 // qDebug()<<"通道数"<<count<<endl; 266 267 /* 设置 level 的值 */ 268 QVector<qreal> levels = getBufferLevels(buffer); 269 for (int i = 0; i < levels.count(); ++i) { 270 /* 打印音量等级 */ 271 // qDebug()<<"音量等级"<<levels.at(i)<<endl; 272 } 273 } 

4/02_asr_demo/audiorecorder/audiorecorder.cpp 主 要 提 供 了 一 个 startRecorder() 和

stopRecorder()的接口,录音保存的文件为可执行程序当前路径下的 16k.wav 文件。startRecorder()

和 stopRecorder()分别是开始录音和停止录音。
第 54~56 行,增加 8000 和 16000 的支持项。
第 104 行,设置为下标为 2 的项,也就是 16000 采样率。
第 106 行,设置通道数下标为 1 的项,也就是单通道。
第 114 行,设置文件容器/格式,为 audio/x-wav 格式(项的下标为 20)。设置此格式会保存

wav 后缀的文件。