Source code

Revision control

Copy as Markdown

// Copyright (C) 2026 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "audiorecorder.h"
#include <QDebug>
namespace {
using namespace std::chrono_literals;
constexpr auto recordingDuration = 5s;
} // namespace
AudioRecorder::AudioRecorder(const QAudioFormat &format, QString fileName)
: m_bytesExpected(
format.bytesForDuration(std::chrono::microseconds(recordingDuration).count())),
m_fileName(fileName)
{
// Prepare WAV file
drwav_data_format wavFormat;
wavFormat.container = drwav_container_w64;
wavFormat.format = (format.sampleFormat() == QAudioFormat::Float) ? DR_WAVE_FORMAT_IEEE_FLOAT
: DR_WAVE_FORMAT_PCM;
wavFormat.channels = format.channelCount();
wavFormat.sampleRate = format.sampleRate();
wavFormat.bitsPerSample = format.bytesPerSample() * 8;
if (drwav_init_file_write(&m_wav, m_fileName.toLatin1().constData(), &wavFormat, nullptr) == 0) {
qWarning() << "Error opening WAV file for writing:" << m_fileName;
}
}
AudioRecorder::~AudioRecorder()
{
auto result = drwav_uninit(&m_wav);
if (result != DRWAV_SUCCESS)
qWarning() << "Failed to close WAV file:" << result;
else
qInfo() << "Wav file written:" << m_fileName;
}
int AudioRecorder::progress(int maximum) const
{
return maximum * qMin(bytesWritten(), m_bytesExpected) / m_bytesExpected;
}
bool AudioRecorder::isDone() const
{
return bytesWritten() >= m_bytesExpected;
}
qint64 AudioRecorder::writeBytesToFile(QSpan<const std::byte> buffer)
{
qint64 bytesPerFrame = m_wav.channels * m_wav.bitsPerSample / 8;
if (bytesPerFrame == 0)
return 0;
qint64 framesOnBuffer = buffer.size() / bytesPerFrame;
quint64 framesWritten = drwav_write_pcm_frames(&m_wav, framesOnBuffer, buffer.data());
return qint64(framesWritten) * bytesPerFrame;
}
void PullRecorder::writeSpan(QSpan<const std::byte> data)
{
m_bytesWritten += writeBytesToFile(data);
}
quint64 PullRecorder::bytesWritten() const
{
return m_bytesWritten;
}
qint64 PushRecorder::writeData(const char *data, qint64 len)
{
auto bytesWritten =
writeBytesToFile(QSpan{ reinterpret_cast<const std::byte *>(data), (qsizetype)len });
m_bytesWritten += bytesWritten;
return bytesWritten;
}
quint64 PushRecorder::bytesWritten() const
{
return m_bytesWritten;
}
CallbackRecorder::CallbackRecorder(const QAudioFormat &format, QString fileName)
: AudioRecorder(format, fileName)
{
// Pre-allocate a 5s buffer
m_recordingBuffer.resize(m_bytesExpected, std::byte(0));
m_recordingIterator = m_recordingBuffer.begin();
}
CallbackRecorder::~CallbackRecorder()
{
writeBytesToFile(m_recordingBuffer);
}
void CallbackRecorder::writeSpan(QSpan<const std::byte> data) noexcept
Q_DECL_NONBLOCKING_FUNCTION
{
auto bytesToWrite = m_bytesExpected - bytesWritten();
auto length = qMin(bytesToWrite, quint64(data.size()));
if (length == 0)
return;
m_recordingIterator = std::copy_n(data.begin(), length, m_recordingIterator);
m_bytesWritten.store(std::distance(m_recordingBuffer.begin(), m_recordingIterator),
std::memory_order_relaxed);
}
quint64 CallbackRecorder::bytesWritten() const
{
return m_bytesWritten.load(std::memory_order_relaxed);
}