Improvements to Audio Recording III: Peak menace!

This commit is contained in:
justburner 2021-11-30 10:29:32 +00:00 committed by manongjohn
parent a1b7482a5b
commit 290ff0b0d3
2 changed files with 140 additions and 69 deletions

View file

@ -283,6 +283,16 @@ void AudioRecordingPopup::onRecordButtonPressed() {
if (TSystem::doesExistFileOrLevel(m_filePath)) {
TSystem::removeFileOrLevel(m_filePath);
}
// The audio writer support either writing to buffer or directly to disk
// each method have their own pros and cons
// For now using false to mimic previous QAudioRecorder behaviour
m_audioWriterWAV->restart(m_audioInput->format());
if (!m_audioWriterWAV->start(m_filePath.getQString(), false)) {
DVGui::warning(
tr("Failed to save WAV file:\nMake sure you have write permissions "
"in folder."));
return;
}
m_recordButton->setIcon(m_stopIcon);
m_saveButton->setDisabled(true);
m_playButton->setDisabled(true);
@ -299,8 +309,6 @@ void AudioRecordingPopup::onRecordButtonPressed() {
m_stoppedAtEnd = false;
m_playDuration->setText("00:00.000");
m_timer->restart();
m_audioWriterWAV->restart(m_audioInput->format());
m_audioWriterWAV->start();
m_audioInput->start(m_audioWriterWAV);
// this sometimes sets to one frame off, so + 1.
m_currentFrame = TApp::instance()->getCurrentFrame()->getFrame() + 1;
@ -312,13 +320,12 @@ void AudioRecordingPopup::onRecordButtonPressed() {
} else {
m_audioInput->stop();
m_audioWriterWAV->stop();
if (m_audioWriterWAV->save(m_filePath.getQString())) {
bool success = m_audioWriterWAV->stop();
if (success) {
m_saveButton->setEnabled(true);
m_playButton->setEnabled(true);
}
m_audioWriterWAV->freeup();
m_audioLevelsDisplay->setLevel(-1);
m_audioLevelsDisplay->setLevel(-1, -1);
m_recordButton->setIcon(m_recordIcon);
m_pauseRecordingButton->setDisabled(true);
m_pauseRecordingButton->setIcon(m_pauseIcon);
@ -334,6 +341,10 @@ void AudioRecordingPopup::onRecordButtonPressed() {
TApp::instance()->getCurrentFrame()->setCurrentFrame(m_currentFrame);
}
m_isPlaying = false;
if (!success) {
DVGui::warning(tr(
"Failed to save WAV file:\nMake sure you have write permissions in folder."));
}
}
}
@ -350,7 +361,8 @@ void AudioRecordingPopup::updateRecordDuration(qint64 duration) {
// Show and record amplitude
qreal level = m_audioWriterWAV->level();
m_audioLevelsDisplay->setLevel(level);
qreal peakL = m_audioWriterWAV->peakLevel();
m_audioLevelsDisplay->setLevel(level, peakL);
m_recordedLevels[duration / 20] = level;
}
@ -369,7 +381,7 @@ void AudioRecordingPopup::updatePlaybackDuration(qint64 duration) {
// using
// a map that is made during recording
if (m_recordedLevels.contains(duration / 20)) {
m_audioLevelsDisplay->setLevel(m_recordedLevels.value(duration / 20));
m_audioLevelsDisplay->setLevel(m_recordedLevels.value(duration / 20), -1);
}
}
@ -466,7 +478,7 @@ void AudioRecordingPopup::onPausePlaybackButtonPressed() {
void AudioRecordingPopup::onMediaStateChanged(QMediaPlayer::State state) {
// stopping can happen through the stop button or the file ending
if (state == QMediaPlayer::StoppedState) {
m_audioLevelsDisplay->setLevel(-1);
m_audioLevelsDisplay->setLevel(-1, -1);
if (m_syncPlayback) {
if (m_isPlaying) {
m_console->pressButton(FlipConsole::ePause);
@ -523,11 +535,11 @@ void AudioRecordingPopup::onAudioSettingChanged() {
void AudioRecordingPopup::onSaveButtonPressed() {
if (m_audioInput->state() != QAudio::StoppedState) {
m_audioInput->stop();
m_audioLevelsDisplay->setLevel(-1);
m_audioLevelsDisplay->setLevel(-1, -1);
}
if (m_player->state() != QMediaPlayer::StoppedState) {
m_player->stop();
m_audioLevelsDisplay->setLevel(-1);
m_audioLevelsDisplay->setLevel(-1, -1);
}
if (!TSystem::doesExistFileOrLevel(m_filePath)) return;
@ -599,7 +611,7 @@ void AudioRecordingPopup::resetEverything() {
m_recordedLevels.clear();
m_duration->setText("00:00.000");
m_playDuration->setText("00:00.000");
m_audioLevelsDisplay->setLevel(-1);
m_audioLevelsDisplay->setLevel(-1, -1);
if (!m_console) {
m_console = FlipConsole::getCurrent();
if (m_console)
@ -613,6 +625,7 @@ void AudioRecordingPopup::resetEverything() {
void AudioRecordingPopup::hideEvent(QHideEvent *event) {
if (m_audioInput->state() != QAudio::StoppedState) {
m_audioInput->stop();
m_audioWriterWAV->stop();
}
if (m_player->state() != QMediaPlayer::StoppedState) {
m_player->stop();
@ -627,7 +640,6 @@ void AudioRecordingPopup::hideEvent(QHideEvent *event) {
}
// Free up memory used in recording
m_recordedLevels.clear();
m_audioWriterWAV->freeup();
}
//-----------------------------------------------------------------------------
@ -701,7 +713,12 @@ void AudioRecordingPopup::reinitAudioInput() {
// 32-bits isn't supported
AudioWriterWAV::AudioWriterWAV(const QAudioFormat &format)
: m_level(0.0), m_maxAmp(0.0) {
: m_level(0.0)
, m_peakL(0.0)
, m_maxAmp(0.0)
, m_wrRawB(0)
, m_wavFile(NULL)
, m_wavBuff(NULL) {
restart(format);
}
@ -718,16 +735,79 @@ bool AudioWriterWAV::restart(const QAudioFormat &format) {
m_rbytesms = 250.0 / (m_format.sampleRate() * m_format.channelCount());
m_maxAmp = 1.0;
}
m_barray.clear();
m_wrRawB = 0;
m_peakL = 0.0;
if (m_wavBuff) m_wavBuff->clear();
return this->reset();
}
void AudioWriterWAV::start() {
open(QIODevice::WriteOnly);
// Just a tiny define to avoid a magic number
// this is the size of a WAV header with PCM format
#define AWWAV_HEADER_SIZE 44
bool AudioWriterWAV::start(const QString &filename, bool useMem) {
open(QIODevice::WriteOnly);
m_filename = filename;
if (useMem) {
m_wavBuff = new QByteArray();
} else {
m_wavFile = new QFile(m_filename);
if (!m_wavFile->open(QIODevice::WriteOnly | QIODevice::Truncate))
return false;
m_wavFile->seek(AWWAV_HEADER_SIZE); // skip header
}
m_wrRawB = 0;
m_peakL = 0.0;
return true;
}
void AudioWriterWAV::stop() {
close();
bool AudioWriterWAV::stop() {
close();
if (m_wavBuff) {
// Using memory
QFile file;
file.setFileName(m_filename);
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) return false;
writeWAVHeader(file);
file.write(m_wavBuff->constData(), m_wavBuff->size());
delete m_wavBuff;
m_wavBuff = NULL;
} else {
// Using disk directly
writeWAVHeader(*m_wavFile);
m_wavFile->close();
delete m_wavFile;
m_wavFile = NULL;
}
m_wrRawB = 0;
m_peakL = 0.0;
return true;
}
void AudioWriterWAV::writeWAVHeader(QFile &file) {
quint16 channels = m_format.channelCount();
quint32 samplerate = m_format.sampleRate();
quint16 bitrate = m_format.sampleSize();
qint64 pos = file.pos();
file.seek(0);
QDataStream out(&file);
out.setByteOrder(QDataStream::LittleEndian);
out.writeRawData("RIFF", 4);
out << (quint32)(m_wrRawB + AWWAV_HEADER_SIZE);
out.writeRawData("WAVEfmt ", 8);
out << (quint32)16 << (quint16)1; // magic numbers!
out << channels << samplerate;
out << quint32(samplerate * channels * bitrate / 8);
out << quint16(channels * bitrate / 8);
out << bitrate;
out.writeRawData("data", 4);
out << (quint32)m_wrRawB;
}
qint64 AudioWriterWAV::readData(char *data, qint64 maxlen) {
@ -737,80 +817,57 @@ qint64 AudioWriterWAV::readData(char *data, qint64 maxlen) {
}
qint64 AudioWriterWAV::writeData(const char *data, qint64 len) {
qreal tmp, peak = 0.0;
int tmp, peak = 0.0;
// Measure peak
if (m_format.sampleSize() == 8) {
const quint8 *sdata = (const quint8 *)data;
int slen = len;
for (int i = 0; i < slen; ++i) {
tmp = qAbs(qreal(sdata[i]) - 128.0);
tmp = qAbs<int>(sdata[i] - 128);
if (tmp > peak) peak = tmp;
}
} else if (m_format.sampleSize() == 16) {
const qint16 *sdata = (const qint16 *)data;
int slen = len / 2;
for (int i = 0; i < slen; ++i) {
tmp = qAbs(qreal(sdata[i]));
tmp = qAbs<int>(sdata[i]);
if (tmp > peak) peak = tmp;
}
} else {
// 32-bits isn't supported
peak = -1.0;
peak = -1;
}
m_level = peak / m_maxAmp;
m_level = qreal(peak) / m_maxAmp;
if (m_level > m_peakL) m_peakL = m_level;
// Write to buffer
m_barray.append(data, len);
// Write to memory or disk
if (m_wavBuff) {
m_wavBuff->append(data, len);
} else {
m_wavFile->write(data, len);
}
m_wrRawB += len;
// Emit an update
emit update(m_barray.size() * m_rbytesms);
emit update(qreal(m_wrRawB) * m_rbytesms);
return len;
}
bool AudioWriterWAV::save(const QString &filename)
{
QFile file;
quint16 channels = m_format.channelCount();
quint32 samplerate = m_format.sampleRate();
quint16 bitrate = m_format.sampleSize();
file.setFileName(filename);
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
DVGui::warning(tr(
"Failed to save WAV file:\nMake sure you have folder permissions."));
return false;
}
QDataStream out(&file);
out.setByteOrder(QDataStream::LittleEndian);
out.writeRawData("RIFF", 4);
out << (quint32)(m_barray.size() + 44);
out.writeRawData("WAVEfmt ", 8);
out << (quint32)16 << (quint16)1;
out << channels << samplerate;
out << quint32(samplerate * channels * bitrate / 8);
out << quint16(channels * bitrate / 8);
out << bitrate;
out.writeRawData("data", 4);
out << (quint32)m_barray.size();
out.writeRawData(m_barray.constData(), m_barray.size());
return true;
}
void AudioWriterWAV::freeup() { m_barray.clear(); }
//-----------------------------------------------------------------------------
// AudioLevelsDisplay Class
//-----------------------------------------------------------------------------
AudioLevelsDisplay::AudioLevelsDisplay(QWidget *parent)
: QWidget(parent), m_level(0.0) {
: QWidget(parent), m_level(0.0), m_peakL(0.0) {
setFixedHeight(20);
setFixedWidth(300);
}
void AudioLevelsDisplay::setLevel(qreal level) {
if (m_level != level) {
void AudioLevelsDisplay::setLevel(qreal level, qreal peakLevel) {
if (m_level != level || m_peakL != peakLevel) {
m_level = level;
m_peakL = peakLevel;
update();
}
}
@ -831,9 +888,18 @@ void AudioLevelsDisplay::paintEvent(QPaintEvent *event) {
} else
color = Qt::red;
qreal widthLevel = m_level * width();
painter.fillRect(0, 0, widthLevel, height(), color);
int widthLevel = m_level * width();
int widthPeakL = m_peakL * width();
painter.fillRect(widthLevel, 0, width(), height(), Qt::black);
if (widthPeakL >= 0) {
if (m_peakL >= 0.995) {
painter.fillRect(width() - 4, 0, 4, height(), Qt::red);
} else {
painter.setPen(QColor(64, 64, 64)); // very dark gray
painter.drawLine(widthPeakL, 0, widthPeakL, height());
}
}
painter.fillRect(0, 0, widthLevel, height(), color);
}
//-----------------------------------------------------------------------------

View file

@ -97,22 +97,26 @@ public:
AudioWriterWAV(const QAudioFormat &format);
bool restart(const QAudioFormat &format);
void start();
void stop();
bool save(const QString &filename);
void freeup();
bool start(const QString &filename, bool useMem);
bool stop();
qint64 readData(char *data, qint64 maxlen) override;
qint64 writeData(const char *data, qint64 len) override;
qreal level() const { return m_level; }
qreal peakLevel() const { return m_peakL; }
private:
QByteArray m_barray;
QString m_filename;
QFile *m_wavFile;
QByteArray *m_wavBuff; // if not null then use memory
QAudioFormat m_format;
quint64 m_wrRawB; // Written raw bytes
qreal m_rbytesms;
qreal m_maxAmp;
qreal m_level;
qreal m_level, m_peakL;
void writeWAVHeader(QFile &file);
signals:
void update(qint64 duration);
@ -128,12 +132,13 @@ public:
explicit AudioLevelsDisplay(QWidget *parent = 0);
// Using [0; 1.0] range
void setLevel(qreal level);
void setLevel(qreal level, qreal peak);
protected:
void paintEvent(QPaintEvent *event);
private:
qreal m_level;
qreal m_peakL;
};
#endif