Improvements to Audio Recording
This commit is contained in:
parent
6f8de8cbf3
commit
34075514d2
2 changed files with 387 additions and 126 deletions
|
@ -66,29 +66,47 @@ AudioRecordingPopup::AudioRecordingPopup()
|
|||
m_saveButton = new QPushButton(tr("Save and Insert"));
|
||||
m_pauseRecordingButton = new QPushButton(this);
|
||||
m_pausePlaybackButton = new QPushButton(this);
|
||||
// m_refreshDevicesButton = new QPushButton(tr("Refresh"));
|
||||
m_duration = new QLabel("00:00");
|
||||
m_playDuration = new QLabel("00:00");
|
||||
m_refreshDevicesButton = new QPushButton(this);
|
||||
m_duration = new QLabel("00:00.000");
|
||||
m_playDuration = new QLabel("00:00.000");
|
||||
m_deviceListCB = new QComboBox();
|
||||
m_audioLevelsDisplay = new AudioLevelsDisplay(this);
|
||||
m_playXSheetCB = new QCheckBox(tr("Sync with Scene"), this);
|
||||
m_playXSheetCB = new QCheckBox(tr("Sync with XSheet/Timeline"), this);
|
||||
m_timer = new QElapsedTimer();
|
||||
m_recordedLevels = QMap<qint64, double>();
|
||||
m_oldElapsed = 0;
|
||||
m_probe = new QAudioProbe;
|
||||
m_player = new QMediaPlayer(this);
|
||||
m_console = FlipConsole::getCurrent();
|
||||
m_audioRecorder = new QAudioRecorder;
|
||||
|
||||
m_recordButton->setMaximumWidth(25);
|
||||
m_playButton->setMaximumWidth(25);
|
||||
m_pauseRecordingButton->setMaximumWidth(25);
|
||||
m_pausePlaybackButton->setMaximumWidth(25);
|
||||
m_labelDevice = new QLabel(tr("Device: "));
|
||||
m_labelSamplerate = new QLabel(tr("Sample rate: "));
|
||||
m_labelSamplefmt = new QLabel(tr("Sample format: "));
|
||||
m_comboSamplerate = new QComboBox();
|
||||
m_comboSamplefmt = new QComboBox();
|
||||
m_comboSamplerate->addItem(tr("8000 Hz"), QVariant::fromValue(8000));
|
||||
m_comboSamplerate->addItem(tr("11025 Hz"), QVariant::fromValue(11025));
|
||||
m_comboSamplerate->addItem(tr("22050 Hz"), QVariant::fromValue(22050));
|
||||
m_comboSamplerate->addItem(tr("44100 Hz"), QVariant::fromValue(44100));
|
||||
m_comboSamplerate->addItem(tr("48000 Hz"), QVariant::fromValue(48000));
|
||||
m_comboSamplerate->addItem(tr("96000 Hz"), QVariant::fromValue(96000));
|
||||
m_comboSamplerate->setCurrentIndex(3); // 44.1KHz
|
||||
m_comboSamplefmt->addItem(tr("Mono 8-Bits"), QVariant::fromValue(9));
|
||||
m_comboSamplefmt->addItem(tr("Stereo 8-Bits"), QVariant::fromValue(10));
|
||||
m_comboSamplefmt->addItem(tr("Mono 16-Bits"), QVariant::fromValue(17));
|
||||
m_comboSamplefmt->addItem(tr("Stereo 16-Bits"), QVariant::fromValue(18));
|
||||
m_comboSamplefmt->setCurrentIndex(2); // Mono 16-Bits
|
||||
|
||||
m_recordButton->setMaximumWidth(32);
|
||||
m_playButton->setMaximumWidth(32);
|
||||
m_pauseRecordingButton->setMaximumWidth(32);
|
||||
m_pausePlaybackButton->setMaximumWidth(32);
|
||||
m_refreshDevicesButton->setMaximumWidth(25);
|
||||
|
||||
QString playDisabled = QString(":Resources/play_disabled.svg");
|
||||
QString pauseDisabled = QString(":Resources/pause_disabled.svg");
|
||||
QString stopDisabled = QString(":Resources/stop_disabled.svg");
|
||||
QString recordDisabled = QString(":Resources/record_disabled.svg");
|
||||
QString refreshDisabled = QString(":Resources/repeat_icon.svg");
|
||||
|
||||
m_pauseIcon = createQIcon("pause");
|
||||
m_pauseIcon.addFile(pauseDisabled, QSize(), QIcon::Disabled);
|
||||
|
@ -98,6 +116,9 @@ AudioRecordingPopup::AudioRecordingPopup()
|
|||
m_recordIcon.addFile(recordDisabled, QSize(), QIcon::Disabled);
|
||||
m_stopIcon = createQIcon("stop");
|
||||
m_stopIcon.addFile(stopDisabled, QSize(), QIcon::Disabled);
|
||||
m_refreshIcon = createQIcon("repeat");
|
||||
m_refreshIcon.addFile(refreshDisabled, QSize(), QIcon::Disabled);
|
||||
|
||||
m_pauseRecordingButton->setIcon(m_pauseIcon);
|
||||
m_pauseRecordingButton->setIconSize(QSize(17, 17));
|
||||
m_playButton->setIcon(m_playIcon);
|
||||
|
@ -106,12 +127,32 @@ AudioRecordingPopup::AudioRecordingPopup()
|
|||
m_recordButton->setIconSize(QSize(17, 17));
|
||||
m_pausePlaybackButton->setIcon(m_pauseIcon);
|
||||
m_pausePlaybackButton->setIconSize(QSize(17, 17));
|
||||
m_refreshDevicesButton->setIcon(m_refreshIcon);
|
||||
m_refreshDevicesButton->setIconSize(QSize(17, 17));
|
||||
|
||||
QStringList inputs = m_audioRecorder->audioInputs();
|
||||
m_deviceListCB->addItems(inputs);
|
||||
QString selectedInput = m_audioRecorder->defaultAudioInput();
|
||||
m_deviceListCB->setCurrentText(selectedInput);
|
||||
m_audioRecorder->setAudioInput(selectedInput);
|
||||
// Enumerate devices and initialize default device
|
||||
enumerateAudioDevices("");
|
||||
QAudioDeviceInfo m_audioDeviceInfo = QAudioDeviceInfo::defaultInputDevice();
|
||||
QAudioFormat format;
|
||||
format.setSampleRate(44100);
|
||||
format.setChannelCount(1);
|
||||
format.setSampleSize(16);
|
||||
format.setSampleType(QAudioFormat::SignedInt);
|
||||
format.setByteOrder(QAudioFormat::LittleEndian);
|
||||
format.setCodec("audio/pcm");
|
||||
if (!m_audioDeviceInfo.isFormatSupported(format)) {
|
||||
format = m_audioDeviceInfo.nearestFormat(format);
|
||||
}
|
||||
m_audioInput = new QAudioInput(m_audioDeviceInfo, format);
|
||||
m_audioWriterWAV = new AudioWriterWAV(format);
|
||||
|
||||
// Tool tips to provide additional info to the user
|
||||
m_deviceListCB->setToolTip(tr("Audio input device to record"));
|
||||
m_comboSamplerate->setToolTip(tr("Number of samples per second, 44.1KHz = CD Quality"));
|
||||
m_comboSamplefmt->setToolTip(tr("Number of channels and bits per sample, 16-bits recommended"));
|
||||
m_playXSheetCB->setToolTip(tr("Play animation from current frame while recording/playback"));
|
||||
m_saveButton->setToolTip(tr("Save recording and insert into new column"));
|
||||
m_refreshDevicesButton->setToolTip(tr("Refresh list of connected audio input devices"));
|
||||
|
||||
m_topLayout->setMargin(5);
|
||||
m_topLayout->setSpacing(8);
|
||||
|
@ -124,11 +165,19 @@ AudioRecordingPopup::AudioRecordingPopup()
|
|||
recordGridLay->setHorizontalSpacing(2);
|
||||
recordGridLay->setVerticalSpacing(3);
|
||||
{
|
||||
recordGridLay->addWidget(m_deviceListCB, 0, 0, 1, 4, Qt::AlignCenter);
|
||||
// recordGridLay->addWidget(m_refreshDevicesButton, 0, 3, Qt::AlignLeft);
|
||||
recordGridLay->addWidget(new QLabel(tr(" ")), 1, 0, Qt::AlignCenter);
|
||||
recordGridLay->addWidget(m_audioLevelsDisplay, 2, 0, 1, 4,
|
||||
recordGridLay->addWidget(m_labelDevice, 0, 0, 1, 2, Qt::AlignRight);
|
||||
recordGridLay->addWidget(m_deviceListCB, 0, 2, 1, 2, Qt::AlignLeft);
|
||||
|
||||
recordGridLay->addWidget(m_labelSamplerate, 1, 0, 1, 2, Qt::AlignRight);
|
||||
recordGridLay->addWidget(m_comboSamplerate, 1, 2, 1, 1, Qt::AlignLeft);
|
||||
recordGridLay->addWidget(m_refreshDevicesButton, 1, 3, Qt::AlignRight);
|
||||
|
||||
recordGridLay->addWidget(m_labelSamplefmt, 2, 0, 1, 2, Qt::AlignRight);
|
||||
recordGridLay->addWidget(m_comboSamplefmt, 2, 2, 1, 2, Qt::AlignLeft);
|
||||
|
||||
recordGridLay->addWidget(m_audioLevelsDisplay, 3, 0, 1, 4,
|
||||
Qt::AlignCenter);
|
||||
recordGridLay->addWidget(m_playXSheetCB, 4, 0, 1, 5, Qt::AlignCenter);
|
||||
QHBoxLayout *recordLay = new QHBoxLayout();
|
||||
recordLay->setSpacing(4);
|
||||
recordLay->setContentsMargins(0, 0, 0, 0);
|
||||
|
@ -139,7 +188,7 @@ AudioRecordingPopup::AudioRecordingPopup()
|
|||
recordLay->addWidget(m_duration);
|
||||
recordLay->addStretch();
|
||||
}
|
||||
recordGridLay->addLayout(recordLay, 3, 0, 1, 4, Qt::AlignCenter);
|
||||
recordGridLay->addLayout(recordLay, 5, 0, 1, 4, Qt::AlignCenter);
|
||||
QHBoxLayout *playLay = new QHBoxLayout();
|
||||
playLay->setSpacing(4);
|
||||
playLay->setContentsMargins(0, 0, 0, 0);
|
||||
|
@ -150,11 +199,9 @@ AudioRecordingPopup::AudioRecordingPopup()
|
|||
playLay->addWidget(m_playDuration);
|
||||
playLay->addStretch();
|
||||
}
|
||||
recordGridLay->addLayout(playLay, 4, 0, 1, 4, Qt::AlignCenter);
|
||||
recordGridLay->addWidget(new QLabel(tr(" ")), 5, 0, Qt::AlignCenter);
|
||||
recordGridLay->addWidget(m_saveButton, 6, 0, 1, 4,
|
||||
Qt::AlignCenter | Qt::AlignVCenter);
|
||||
recordGridLay->addWidget(m_playXSheetCB, 7, 0, 1, 4,
|
||||
recordGridLay->addLayout(playLay, 6, 0, 1, 4, Qt::AlignCenter);
|
||||
recordGridLay->addWidget(new QLabel(tr(" ")), 7, 0, Qt::AlignCenter);
|
||||
recordGridLay->addWidget(m_saveButton, 8, 0, 1, 4,
|
||||
Qt::AlignCenter | Qt::AlignVCenter);
|
||||
}
|
||||
recordGridLay->setColumnStretch(0, 0);
|
||||
|
@ -172,23 +219,6 @@ AudioRecordingPopup::AudioRecordingPopup()
|
|||
|
||||
m_playXSheetCB->setChecked(true);
|
||||
|
||||
m_probe->setSource(m_audioRecorder);
|
||||
QAudioEncoderSettings audioSettings;
|
||||
audioSettings.setCodec("audio/PCM");
|
||||
// setting the sample rate to some value (like 44100)
|
||||
// may cause divide-by-zero crash in QAudioDeviceInfo::nearestFormat()
|
||||
// so here we set the value to -1, as the documentation says;
|
||||
// "A value of -1 indicates the encoder should make an optimal choice"
|
||||
audioSettings.setSampleRate(-1);
|
||||
audioSettings.setChannelCount(1);
|
||||
audioSettings.setBitRate(16);
|
||||
audioSettings.setEncodingMode(QMultimedia::ConstantBitRateEncoding);
|
||||
audioSettings.setQuality(QMultimedia::HighQuality);
|
||||
m_audioRecorder->setContainerFormat("wav");
|
||||
m_audioRecorder->setEncodingSettings(audioSettings);
|
||||
|
||||
connect(m_probe, SIGNAL(audioBufferProbed(QAudioBuffer)), this,
|
||||
SLOT(processBuffer(QAudioBuffer)));
|
||||
connect(m_playXSheetCB, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(onPlayXSheetCBChanged(int)));
|
||||
connect(m_saveButton, SIGNAL(clicked()), this, SLOT(onSaveButtonPressed()));
|
||||
|
@ -199,16 +229,18 @@ AudioRecordingPopup::AudioRecordingPopup()
|
|||
SLOT(onPauseRecordingButtonPressed()));
|
||||
connect(m_pausePlaybackButton, SIGNAL(clicked()), this,
|
||||
SLOT(onPausePlaybackButtonPressed()));
|
||||
connect(m_audioRecorder, SIGNAL(durationChanged(qint64)), this,
|
||||
connect(m_audioWriterWAV, SIGNAL(update(qint64)), this,
|
||||
SLOT(updateRecordDuration(qint64)));
|
||||
connect(m_console, SIGNAL(playStateChanged(bool)), this,
|
||||
if (m_console) connect(m_console, SIGNAL(playStateChanged(bool)), this,
|
||||
SLOT(onPlayStateChanged(bool)));
|
||||
connect(m_deviceListCB, SIGNAL(currentTextChanged(const QString)), this,
|
||||
SLOT(onInputDeviceChanged()));
|
||||
// connect(m_refreshDevicesButton, SIGNAL(clicked()), this,
|
||||
// SLOT(onRefreshButtonPressed()));
|
||||
// connect(m_audioRecorder, SIGNAL(availableAudioInputsChanged()), this,
|
||||
// SLOT(onRefreshButtonPressed()));
|
||||
connect(m_refreshDevicesButton, SIGNAL(clicked()), this,
|
||||
SLOT(onRefreshButtonPressed()));
|
||||
connect(m_comboSamplerate, SIGNAL(currentTextChanged(const QString)), this,
|
||||
SLOT(onAudioSettingChanged()));
|
||||
connect(m_comboSamplefmt, SIGNAL(currentTextChanged(const QString)), this,
|
||||
SLOT(onAudioSettingChanged()));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -218,12 +250,25 @@ AudioRecordingPopup::~AudioRecordingPopup() {}
|
|||
//-----------------------------------------------------------------------------
|
||||
|
||||
void AudioRecordingPopup::onRecordButtonPressed() {
|
||||
if (m_audioRecorder->state() == QAudioRecorder::StoppedState) {
|
||||
if (m_audioRecorder->status() == QMediaRecorder::UnavailableStatus) {
|
||||
#if QT_VERSION >= 0x051000
|
||||
if (m_audioInput->state() == QAudio::InterruptedState) {
|
||||
DVGui::warning(
|
||||
tr("The microphone is not available: "
|
||||
"\nPlease select a different device or check the microphone."));
|
||||
return;
|
||||
} else if (m_audioInput->state() == QAudio::StoppedState) {
|
||||
if (!m_console) {
|
||||
DVGui::warning(
|
||||
tr("Record failed: "
|
||||
"\nMake sure there's XSheet or Timeline in the room."));
|
||||
#else
|
||||
if (m_audioInput->state() == QAudio::StoppedState) {
|
||||
if (!m_console) {
|
||||
DVGui::warning(
|
||||
tr("The microphone is not available: "
|
||||
"\nPlease select a different device or check the microphone."));
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
// clear the player in case the file is open there
|
||||
// can't record to an opened file
|
||||
|
@ -236,8 +281,6 @@ void AudioRecordingPopup::onRecordButtonPressed() {
|
|||
// (rarely)
|
||||
// could cause a crash. I think OT tried to import the level before the
|
||||
// final file was fully copied to the new location
|
||||
m_audioRecorder->setOutputLocation(
|
||||
QUrl::fromLocalFile(m_filePath.getQString()));
|
||||
if (TSystem::doesExistFileOrLevel(m_filePath)) {
|
||||
TSystem::removeFileOrLevel(m_filePath);
|
||||
}
|
||||
|
@ -246,15 +289,21 @@ void AudioRecordingPopup::onRecordButtonPressed() {
|
|||
m_playButton->setDisabled(true);
|
||||
m_pausePlaybackButton->setDisabled(true);
|
||||
m_pauseRecordingButton->setEnabled(true);
|
||||
m_deviceListCB->setDisabled(true);
|
||||
m_refreshDevicesButton->setDisabled(true);
|
||||
m_comboSamplerate->setDisabled(true);
|
||||
m_comboSamplefmt->setDisabled(true);
|
||||
m_recordedLevels.clear();
|
||||
m_oldElapsed = 0;
|
||||
m_pausedTime = 0;
|
||||
m_startPause = 0;
|
||||
m_endPause = 0;
|
||||
m_stoppedAtEnd = false;
|
||||
m_playDuration->setText("00:00");
|
||||
m_playDuration->setText("00:00.000");
|
||||
m_timer->restart();
|
||||
m_audioRecorder->record();
|
||||
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;
|
||||
if (m_syncPlayback && !m_isPlaying) {
|
||||
|
@ -264,13 +313,20 @@ void AudioRecordingPopup::onRecordButtonPressed() {
|
|||
}
|
||||
|
||||
} else {
|
||||
m_audioRecorder->stop();
|
||||
m_audioLevelsDisplay->setLevel(0);
|
||||
m_recordButton->setIcon(m_recordIcon);
|
||||
m_audioInput->stop();
|
||||
m_audioWriterWAV->stop();
|
||||
if (m_audioWriterWAV->save(m_filePath.getQString())) {
|
||||
m_saveButton->setEnabled(true);
|
||||
m_playButton->setEnabled(true);
|
||||
}
|
||||
m_audioLevelsDisplay->setLevel(-1);
|
||||
m_recordButton->setIcon(m_recordIcon);
|
||||
m_pauseRecordingButton->setDisabled(true);
|
||||
m_pauseRecordingButton->setIcon(m_pauseIcon);
|
||||
m_deviceListCB->setEnabled(true);
|
||||
m_refreshDevicesButton->setEnabled(true);
|
||||
m_comboSamplerate->setEnabled(true);
|
||||
m_comboSamplefmt->setEnabled(true);
|
||||
if (m_syncPlayback) {
|
||||
if (m_isPlaying) {
|
||||
m_console->pressButton(FlipConsole::ePause);
|
||||
|
@ -285,15 +341,18 @@ void AudioRecordingPopup::onRecordButtonPressed() {
|
|||
//-----------------------------------------------------------------------------
|
||||
|
||||
void AudioRecordingPopup::updateRecordDuration(qint64 duration) {
|
||||
// this is only called every second or so - sometimes duration ~= 950
|
||||
// this gives some padding so it doesn't take two seconds to show one second
|
||||
// has passed
|
||||
if (duration % 1000 > 850) duration += 150;
|
||||
int minutes = duration / 60000;
|
||||
int seconds = (duration / 1000) % 60;
|
||||
int milis = duration % 1000;
|
||||
QString strMinutes = QString::number(minutes).rightJustified(2, '0');
|
||||
QString strSeconds = QString::number(seconds).rightJustified(2, '0');
|
||||
m_duration->setText(strMinutes + ":" + strSeconds);
|
||||
QString strMilis = QString::number(milis).rightJustified(3, '0');
|
||||
m_duration->setText(strMinutes + ":" + strSeconds + "." + strMilis);
|
||||
|
||||
// Show and record amplitude
|
||||
qreal level = m_audioWriterWAV->level();
|
||||
m_audioLevelsDisplay->setLevel(level);
|
||||
m_recordedLevels[duration / 20] = level;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -301,9 +360,11 @@ void AudioRecordingPopup::updateRecordDuration(qint64 duration) {
|
|||
void AudioRecordingPopup::updatePlaybackDuration(qint64 duration) {
|
||||
int minutes = duration / 60000;
|
||||
int seconds = (duration / 1000) % 60;
|
||||
int milis = duration % 1000;
|
||||
QString strMinutes = QString::number(minutes).rightJustified(2, '0');
|
||||
QString strSeconds = QString::number(seconds).rightJustified(2, '0');
|
||||
m_playDuration->setText(strMinutes + ":" + strSeconds);
|
||||
QString strMilis = QString::number(milis).rightJustified(3, '0');
|
||||
m_playDuration->setText(strMinutes + ":" + strSeconds + "." + strMilis);
|
||||
|
||||
// the qmediaplayer probe doesn't work on all platforms, so we fake it by
|
||||
// using
|
||||
|
@ -328,6 +389,10 @@ void AudioRecordingPopup::onPlayButtonPressed() {
|
|||
m_recordButton->setDisabled(true);
|
||||
m_saveButton->setDisabled(true);
|
||||
m_pausePlaybackButton->setEnabled(true);
|
||||
m_deviceListCB->setDisabled(true);
|
||||
m_refreshDevicesButton->setDisabled(true);
|
||||
m_comboSamplerate->setDisabled(true);
|
||||
m_comboSamplefmt->setDisabled(true);
|
||||
m_stoppedAtEnd = false;
|
||||
m_player->play();
|
||||
// this sometimes sets to one frame off, so + 1.
|
||||
|
@ -343,25 +408,29 @@ void AudioRecordingPopup::onPlayButtonPressed() {
|
|||
m_playButton->setIcon(m_playIcon);
|
||||
m_pausePlaybackButton->setDisabled(true);
|
||||
m_pausePlaybackButton->setIcon(m_pauseIcon);
|
||||
m_deviceListCB->setEnabled(true);
|
||||
m_refreshDevicesButton->setEnabled(true);
|
||||
m_comboSamplerate->setEnabled(true);
|
||||
m_comboSamplefmt->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void AudioRecordingPopup::onPauseRecordingButtonPressed() {
|
||||
if (m_audioRecorder->state() == QAudioRecorder::StoppedState) {
|
||||
if (m_audioInput->state() == QAudio::StoppedState) {
|
||||
return;
|
||||
} else if (m_audioRecorder->state() == QAudioRecorder::PausedState) {
|
||||
} else if (m_audioInput->state() == QAudio::SuspendedState) {
|
||||
m_endPause = m_timer->elapsed();
|
||||
m_pausedTime += m_endPause - m_startPause;
|
||||
m_audioRecorder->record();
|
||||
m_audioInput->resume();
|
||||
m_pauseRecordingButton->setIcon(m_pauseIcon);
|
||||
if (m_syncPlayback && !m_isPlaying && !m_stoppedAtEnd) {
|
||||
m_console->pressButton(FlipConsole::ePlay);
|
||||
m_isPlaying = true;
|
||||
}
|
||||
} else {
|
||||
m_audioRecorder->pause();
|
||||
m_audioInput->suspend();
|
||||
m_pauseRecordingButton->setIcon(m_recordIcon);
|
||||
m_startPause = m_timer->elapsed();
|
||||
if (m_syncPlayback && m_isPlaying) {
|
||||
|
@ -398,7 +467,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(0);
|
||||
m_audioLevelsDisplay->setLevel(-1);
|
||||
if (m_syncPlayback) {
|
||||
if (m_isPlaying) {
|
||||
m_console->pressButton(FlipConsole::ePause);
|
||||
|
@ -410,6 +479,10 @@ void AudioRecordingPopup::onMediaStateChanged(QMediaPlayer::State state) {
|
|||
m_pausePlaybackButton->setIcon(m_pauseIcon);
|
||||
m_pausePlaybackButton->setDisabled(true);
|
||||
m_recordButton->setEnabled(true);
|
||||
m_deviceListCB->setEnabled(true);
|
||||
m_refreshDevicesButton->setEnabled(true);
|
||||
m_comboSamplerate->setEnabled(true);
|
||||
m_comboSamplefmt->setEnabled(true);
|
||||
m_saveButton->setEnabled(true);
|
||||
m_isPlaying = false;
|
||||
}
|
||||
|
@ -426,36 +499,36 @@ void AudioRecordingPopup::onPlayXSheetCBChanged(int status) {
|
|||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Refresh isn't working right now, but I'm leaving the code in case a future
|
||||
// change
|
||||
// makes it work
|
||||
void AudioRecordingPopup::onRefreshButtonPressed() {
|
||||
QAudioDeviceInfo m_audioDeviceInfo =
|
||||
m_deviceListCB->itemData(m_deviceListCB->currentIndex())
|
||||
.value<QAudioDeviceInfo>();
|
||||
|
||||
// void AudioRecordingPopup::onRefreshButtonPressed() {
|
||||
// m_deviceListCB->clear();
|
||||
// QStringList inputs = m_audioRecorder->audioInputs();
|
||||
// int count = inputs.count();
|
||||
// m_deviceListCB->addItems(inputs);
|
||||
// QString selectedInput = m_audioRecorder->defaultAudioInput();
|
||||
// m_deviceListCB->setCurrentText(selectedInput);
|
||||
//
|
||||
//}
|
||||
enumerateAudioDevices(m_audioDeviceInfo.deviceName());
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void AudioRecordingPopup::onInputDeviceChanged() {
|
||||
m_audioRecorder->setAudioInput(m_deviceListCB->currentText());
|
||||
reinitAudioInput();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void AudioRecordingPopup::onAudioSettingChanged() {
|
||||
reinitAudioInput();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void AudioRecordingPopup::onSaveButtonPressed() {
|
||||
if (m_audioRecorder->state() != QAudioRecorder::StoppedState) {
|
||||
m_audioRecorder->stop();
|
||||
m_audioLevelsDisplay->setLevel(0);
|
||||
if (m_audioInput->state() != QAudio::StoppedState) {
|
||||
m_audioInput->stop();
|
||||
m_audioLevelsDisplay->setLevel(-1);
|
||||
}
|
||||
if (m_player->state() != QMediaPlayer::StoppedState) {
|
||||
m_player->stop();
|
||||
m_audioLevelsDisplay->setLevel(0);
|
||||
m_audioLevelsDisplay->setLevel(-1);
|
||||
}
|
||||
if (!TSystem::doesExistFileOrLevel(m_filePath)) return;
|
||||
|
||||
|
@ -499,31 +572,6 @@ void AudioRecordingPopup::makePaths() {
|
|||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void AudioRecordingPopup::processBuffer(const QAudioBuffer &buffer) {
|
||||
// keep from processing too many times
|
||||
// get 50 signals per second
|
||||
if (m_timer->elapsed() < m_oldElapsed + 20) return;
|
||||
m_oldElapsed = m_timer->elapsed() - m_pausedTime;
|
||||
qint16 value = 0;
|
||||
|
||||
if (!buffer.format().isValid() ||
|
||||
buffer.format().byteOrder() != QAudioFormat::LittleEndian)
|
||||
return;
|
||||
|
||||
if (buffer.format().codec() != "audio/pcm") return;
|
||||
|
||||
const qint16 *data = buffer.constData<qint16>();
|
||||
qreal maxValue = 0;
|
||||
qreal tempValue = 0;
|
||||
for (int i = 0; i < buffer.frameCount(); ++i) {
|
||||
tempValue = qAbs(qreal(data[i]));
|
||||
if (tempValue > maxValue) maxValue = tempValue;
|
||||
}
|
||||
maxValue /= SHRT_MAX;
|
||||
m_audioLevelsDisplay->setLevel(maxValue);
|
||||
m_recordedLevels[m_oldElapsed / 20] = maxValue;
|
||||
}
|
||||
|
||||
void AudioRecordingPopup::onPlayStateChanged(bool playing) {
|
||||
// m_isPlaying = playing;
|
||||
if (!playing && m_isPlaying) m_stoppedAtEnd = true;
|
||||
|
@ -545,22 +593,33 @@ void AudioRecordingPopup::resetEverything() {
|
|||
m_pauseRecordingButton->setIcon(m_pauseIcon);
|
||||
m_pauseRecordingButton->setDisabled(true);
|
||||
m_pausePlaybackButton->setDisabled(true);
|
||||
m_deviceListCB->setEnabled(true);
|
||||
m_refreshDevicesButton->setEnabled(true);
|
||||
m_comboSamplerate->setEnabled(true);
|
||||
m_comboSamplefmt->setEnabled(true);
|
||||
m_recordedLevels.clear();
|
||||
m_duration->setText("00:00");
|
||||
m_playDuration->setText("00:00");
|
||||
m_audioLevelsDisplay->setLevel(0);
|
||||
m_duration->setText("00:00.000");
|
||||
m_playDuration->setText("00:00.000");
|
||||
m_audioLevelsDisplay->setLevel(-1);
|
||||
if (!m_console) {
|
||||
m_console = FlipConsole::getCurrent();
|
||||
if (m_console)
|
||||
connect(m_console, SIGNAL(playStateChanged(bool)), this,
|
||||
SLOT(onPlayStateChanged(bool)));
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void AudioRecordingPopup::hideEvent(QHideEvent *event) {
|
||||
if (m_audioRecorder->state() != QAudioRecorder::StoppedState) {
|
||||
m_audioRecorder->stop();
|
||||
if (m_audioInput->state() != QAudio::StoppedState) {
|
||||
m_audioInput->stop();
|
||||
}
|
||||
if (m_player->state() != QMediaPlayer::StoppedState) {
|
||||
m_player->stop();
|
||||
}
|
||||
// make sure the file is freed before deleting
|
||||
delete m_player;
|
||||
m_player = new QMediaPlayer(this);
|
||||
// this should only remove files that haven't been used in the scene
|
||||
// make paths checks to only create path names that don't exist yet.
|
||||
|
@ -569,6 +628,172 @@ void AudioRecordingPopup::hideEvent(QHideEvent *event) {
|
|||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void AudioRecordingPopup::enumerateAudioDevices(const QString &selectedDeviceName) {
|
||||
const QAudioDeviceInfo &defaultDeviceInfo =
|
||||
QAudioDeviceInfo::defaultInputDevice();
|
||||
|
||||
m_blockAudioSettings = true;
|
||||
m_deviceListCB->clear();
|
||||
m_deviceListCB->addItem(defaultDeviceInfo.deviceName(),
|
||||
QVariant::fromValue(defaultDeviceInfo));
|
||||
|
||||
for (auto &deviceInfo :
|
||||
QAudioDeviceInfo::availableDevices(QAudio::AudioInput)) {
|
||||
if (deviceInfo != defaultDeviceInfo &&
|
||||
m_deviceListCB->findText(deviceInfo.deviceName()) == -1) {
|
||||
m_deviceListCB->addItem(deviceInfo.deviceName(),
|
||||
QVariant::fromValue(deviceInfo));
|
||||
}
|
||||
}
|
||||
|
||||
int deviceIndex = m_deviceListCB->findText(selectedDeviceName);
|
||||
if (deviceIndex != -1) m_deviceListCB->setCurrentIndex(deviceIndex);
|
||||
m_blockAudioSettings = false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void AudioRecordingPopup::reinitAudioInput() {
|
||||
if (m_blockAudioSettings) return;
|
||||
|
||||
QAudioDeviceInfo m_audioDeviceInfo =
|
||||
m_deviceListCB->itemData(m_deviceListCB->currentIndex())
|
||||
.value<QAudioDeviceInfo>();
|
||||
int samplerate =
|
||||
m_comboSamplerate->itemData(m_comboSamplerate->currentIndex())
|
||||
.value<int>();
|
||||
int sampletype =
|
||||
m_comboSamplefmt->itemData(m_comboSamplefmt->currentIndex()).value<int>();
|
||||
int bitdepth = sampletype & 56;
|
||||
int channels = sampletype & 7;
|
||||
|
||||
QAudioFormat format;
|
||||
format.setSampleRate(samplerate);
|
||||
format.setChannelCount(channels);
|
||||
format.setSampleSize(bitdepth);
|
||||
format.setSampleType(bitdepth == 8 ? QAudioFormat::UnSignedInt
|
||||
: QAudioFormat::SignedInt);
|
||||
format.setByteOrder(QAudioFormat::LittleEndian);
|
||||
format.setCodec("audio/pcm");
|
||||
if (!m_audioDeviceInfo.isFormatSupported(format)) {
|
||||
DVGui::warning(tr(
|
||||
"Audio format unsupported:\nNearest format will be internally used."));
|
||||
format = m_audioDeviceInfo.nearestFormat(format);
|
||||
}
|
||||
|
||||
// Recreate input
|
||||
delete m_audioInput;
|
||||
m_audioInput = new QAudioInput(m_audioDeviceInfo, format);
|
||||
m_audioWriterWAV->restart(format);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// AudioWriterWAV Class
|
||||
//-----------------------------------------------------------------------------
|
||||
// IODevice to write standard WAV files, performs peak level calc
|
||||
//
|
||||
// 8-bits audio must be unsigned
|
||||
// 16-bits audio must be signed
|
||||
// 32-bits isn't supported
|
||||
|
||||
AudioWriterWAV::AudioWriterWAV(const QAudioFormat &format)
|
||||
: m_level(0.0), m_maxAmp(0.0) {
|
||||
restart(format);
|
||||
}
|
||||
|
||||
bool AudioWriterWAV::restart(const QAudioFormat &format) {
|
||||
m_format = format;
|
||||
if (m_format.sampleSize() == 8) {
|
||||
m_rbytesms = 1000.0 / (m_format.sampleRate() * m_format.channelCount());
|
||||
m_maxAmp = 127.0;
|
||||
} else if (m_format.sampleSize() == 16) {
|
||||
m_rbytesms = 500.0 / (m_format.sampleRate() * m_format.channelCount());
|
||||
m_maxAmp = 32767.0;
|
||||
} else {
|
||||
// 32-bits isn't supported
|
||||
m_rbytesms = 250.0 / (m_format.sampleRate() * m_format.channelCount());
|
||||
m_maxAmp = 1.0;
|
||||
}
|
||||
m_barray.clear();
|
||||
return this->reset();
|
||||
}
|
||||
|
||||
void AudioWriterWAV::start() {
|
||||
open(QIODevice::WriteOnly);
|
||||
}
|
||||
|
||||
void AudioWriterWAV::stop() {
|
||||
close();
|
||||
}
|
||||
|
||||
qint64 AudioWriterWAV::readData(char *data, qint64 maxlen) {
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(maxlen)
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint64 AudioWriterWAV::writeData(const char *data, qint64 len) {
|
||||
qreal 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);
|
||||
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]));
|
||||
if (tmp > peak) peak = tmp;
|
||||
}
|
||||
} else {
|
||||
// 32-bits isn't supported
|
||||
peak = -1.0;
|
||||
}
|
||||
m_level = peak / m_maxAmp;
|
||||
|
||||
// Write to buffer
|
||||
m_barray.append(data, len);
|
||||
|
||||
// Emit an update
|
||||
emit update(m_barray.size() * 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;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// AudioLevelsDisplay Class
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -591,11 +816,11 @@ void AudioLevelsDisplay::paintEvent(QPaintEvent *event) {
|
|||
|
||||
QPainter painter(this);
|
||||
QColor color;
|
||||
if (m_level < 0.5) {
|
||||
if (m_level < 0.0) {
|
||||
return; // draw nothing...
|
||||
} else if (m_level < 0.5) {
|
||||
color = Qt::green;
|
||||
}
|
||||
|
||||
else if (m_level < 0.75) {
|
||||
} else if (m_level < 0.75) {
|
||||
color = QColor(204, 205, 0); // yellow
|
||||
} else if (m_level < 0.95) {
|
||||
color = QColor(255, 115, 0); // orange
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
class QComboBox;
|
||||
class QCheckBox;
|
||||
class QPushButton;
|
||||
class QAudioRecorder;
|
||||
class QLabel;
|
||||
class AudioLevelsDisplay;
|
||||
class FlipConsole;
|
||||
|
@ -23,6 +22,7 @@ class QAudioProbe;
|
|||
class QAudioBuffer;
|
||||
class QMediaPlayer;
|
||||
class QElapsedTimer;
|
||||
class AudioWriterWAV;
|
||||
|
||||
//=============================================================================
|
||||
// AudioRecordingPopup
|
||||
|
@ -31,13 +31,13 @@ class QElapsedTimer;
|
|||
class AudioRecordingPopup : public DVGui::Dialog {
|
||||
Q_OBJECT
|
||||
|
||||
QString m_deviceName;
|
||||
QPushButton
|
||||
*m_recordButton, // *m_refreshDevicesButton, -refresh not working for now
|
||||
*m_recordButton, *m_refreshDevicesButton,
|
||||
*m_playButton,
|
||||
*m_pauseRecordingButton, *m_pausePlaybackButton, *m_saveButton;
|
||||
QComboBox *m_deviceListCB;
|
||||
QAudioRecorder *m_audioRecorder;
|
||||
QAudioInput *m_audioInput;
|
||||
AudioWriterWAV *m_audioWriterWAV;
|
||||
QLabel *m_duration, *m_playDuration;
|
||||
QCheckBox *m_playXSheetCB;
|
||||
int m_currentFrame;
|
||||
|
@ -56,7 +56,11 @@ class AudioRecordingPopup : public DVGui::Dialog {
|
|||
QIcon m_pauseIcon;
|
||||
QIcon m_recordIcon;
|
||||
QIcon m_stopIcon;
|
||||
QIcon m_refreshIcon;
|
||||
bool m_isPlaying, m_syncPlayback, m_stoppedAtEnd;
|
||||
QLabel *m_labelDevice, *m_labelSamplerate, *m_labelSamplefmt;
|
||||
QComboBox *m_comboSamplerate, *m_comboSamplefmt;
|
||||
bool m_blockAudioSettings;
|
||||
|
||||
public:
|
||||
AudioRecordingPopup();
|
||||
|
@ -67,6 +71,8 @@ protected:
|
|||
void hideEvent(QHideEvent *event);
|
||||
void makePaths();
|
||||
void resetEverything();
|
||||
void enumerateAudioDevices(const QString &deviceName);
|
||||
void reinitAudioInput();
|
||||
|
||||
private slots:
|
||||
void onRecordButtonPressed();
|
||||
|
@ -76,12 +82,42 @@ private slots:
|
|||
void onSaveButtonPressed();
|
||||
void onPauseRecordingButtonPressed();
|
||||
void onPausePlaybackButtonPressed();
|
||||
void processBuffer(const QAudioBuffer &buffer);
|
||||
void onPlayStateChanged(bool playing);
|
||||
void onPlayXSheetCBChanged(int status);
|
||||
void onMediaStateChanged(QMediaPlayer::State state);
|
||||
void onInputDeviceChanged();
|
||||
// void onRefreshButtonPressed();
|
||||
void onRefreshButtonPressed();
|
||||
void onAudioSettingChanged();
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
// AudioWriterWAV
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class AudioWriterWAV : public QIODevice {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioWriterWAV(const QAudioFormat &format);
|
||||
bool restart(const QAudioFormat &format);
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
bool save(const QString &filename);
|
||||
|
||||
qint64 readData(char *data, qint64 maxlen) override;
|
||||
qint64 writeData(const char *data, qint64 len) override;
|
||||
|
||||
qreal level() const { return m_level; }
|
||||
|
||||
private:
|
||||
QByteArray m_barray;
|
||||
QAudioFormat m_format;
|
||||
qreal m_rbytesms;
|
||||
qreal m_maxAmp;
|
||||
qreal m_level;
|
||||
|
||||
signals:
|
||||
void update(qint64 duration);
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
|
|
Loading…
Reference in a new issue