diff options
author | Yoann Lopes <yoann.lopes@digia.com> | 2013-10-04 17:02:18 +0200 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-10-09 18:01:52 +0200 |
commit | a1cd3c9274789a8735bf5c02094c5c4034992aa6 (patch) | |
tree | 962a8de6c4d0ebc2294ed6f4a458484983f2eb14 /examples/multimedia | |
parent | 7c9ff56f2f828410efee049ae01e712dc24aaa77 (diff) | |
download | qtmultimedia-a1cd3c9274789a8735bf5c02094c5c4034992aa6.tar.gz |
Improve audiorecorder example.
- Possibility to select channel count
- Show audio level for unsigned integer samples
- Show audio level for each channel
- Correctly set output file location
- Update controls depending on the recorder status (rather than the state)
Change-Id: Ieb08c379bb01a11ce1aa52a9d92ea1f320c87d49
Reviewed-by: Christian Stromme <christian.stromme@digia.com>
Diffstat (limited to 'examples/multimedia')
-rw-r--r-- | examples/multimedia/audiorecorder/audiorecorder.cpp | 182 | ||||
-rw-r--r-- | examples/multimedia/audiorecorder/audiorecorder.h | 7 | ||||
-rw-r--r-- | examples/multimedia/audiorecorder/audiorecorder.ui | 35 | ||||
-rw-r--r-- | examples/multimedia/audiorecorder/audiorecorder_small.ui | 134 | ||||
-rw-r--r-- | examples/multimedia/audiorecorder/qaudiolevel.cpp | 2 |
5 files changed, 227 insertions, 133 deletions
diff --git a/examples/multimedia/audiorecorder/audiorecorder.cpp b/examples/multimedia/audiorecorder/audiorecorder.cpp index 5f3b0a10a..18ced4d5f 100644 --- a/examples/multimedia/audiorecorder/audiorecorder.cpp +++ b/examples/multimedia/audiorecorder/audiorecorder.cpp @@ -45,6 +45,7 @@ #include <QMediaRecorder> #include "audiorecorder.h" +#include "qaudiolevel.h" #if defined(Q_WS_MAEMO_6) #include "ui_audiorecorder_small.h" @@ -53,10 +54,10 @@ #endif static qreal getPeakValue(const QAudioFormat &format); -static qreal getBufferLevel(const QAudioBuffer &buffer); +static QVector<qreal> getBufferLevels(const QAudioBuffer &buffer); template <class T> -static qreal getBufferLevel(const T *buffer, int samples); +static QVector<qreal> getBufferLevels(const T *buffer, int frames, int channels); AudioRecorder::AudioRecorder(QWidget *parent) : QMainWindow(parent), @@ -67,7 +68,8 @@ AudioRecorder::AudioRecorder(QWidget *parent) : audioRecorder = new QAudioRecorder(this); probe = new QAudioProbe; - connect(probe, SIGNAL(audioBufferProbed(QAudioBuffer)), this, SLOT(processBuffer(QAudioBuffer))); + connect(probe, SIGNAL(audioBufferProbed(QAudioBuffer)), + this, SLOT(processBuffer(QAudioBuffer))); probe->setSource(audioRecorder); //audio devices @@ -88,27 +90,34 @@ AudioRecorder::AudioRecorder(QWidget *parent) : ui->containerBox->addItem(containerName, QVariant(containerName)); } - //sample rate: + //sample rate ui->sampleRateBox->addItem(tr("Default"), QVariant(0)); foreach (int sampleRate, audioRecorder->supportedAudioSampleRates()) { ui->sampleRateBox->addItem(QString::number(sampleRate), QVariant( sampleRate)); } + //channels + ui->channelsBox->addItem(tr("Default"), QVariant(-1)); + ui->channelsBox->addItem(QStringLiteral("1"), QVariant(1)); + ui->channelsBox->addItem(QStringLiteral("2"), QVariant(2)); + ui->channelsBox->addItem(QStringLiteral("4"), QVariant(4)); + + //quality ui->qualitySlider->setRange(0, int(QMultimedia::VeryHighQuality)); ui->qualitySlider->setValue(int(QMultimedia::NormalQuality)); //bitrates: - ui->bitrateBox->addItem(QString("Default"), QVariant(0)); - ui->bitrateBox->addItem(QString("32000"), QVariant(32000)); - ui->bitrateBox->addItem(QString("64000"), QVariant(64000)); - ui->bitrateBox->addItem(QString("96000"), QVariant(96000)); - ui->bitrateBox->addItem(QString("128000"), QVariant(128000)); + ui->bitrateBox->addItem(tr("Default"), QVariant(0)); + ui->bitrateBox->addItem(QStringLiteral("32000"), QVariant(32000)); + ui->bitrateBox->addItem(QStringLiteral("64000"), QVariant(64000)); + ui->bitrateBox->addItem(QStringLiteral("96000"), QVariant(96000)); + ui->bitrateBox->addItem(QStringLiteral("128000"), QVariant(128000)); connect(audioRecorder, SIGNAL(durationChanged(qint64)), this, SLOT(updateProgress(qint64))); - connect(audioRecorder, SIGNAL(stateChanged(QMediaRecorder::State)), this, - SLOT(updateState(QMediaRecorder::State))); + connect(audioRecorder, SIGNAL(statusChanged(QMediaRecorder::Status)), this, + SLOT(updateStatus(QMediaRecorder::Status))); connect(audioRecorder, SIGNAL(error(QMediaRecorder::Error)), this, SLOT(displayErrorMessage())); } @@ -127,32 +136,47 @@ void AudioRecorder::updateProgress(qint64 duration) ui->statusbar->showMessage(tr("Recorded %1 sec").arg(duration / 1000)); } -void AudioRecorder::updateState(QMediaRecorder::State state) +void AudioRecorder::updateStatus(QMediaRecorder::Status status) { QString statusMessage; - switch (state) { - case QMediaRecorder::RecordingState: - ui->recordButton->setText(tr("Stop")); - ui->pauseButton->setText(tr("Pause")); - if (audioRecorder->outputLocation().isEmpty()) - statusMessage = tr("Recording"); - else - statusMessage = tr("Recording to %1").arg( + switch (status) { + case QMediaRecorder::RecordingStatus: + if (audioLevels.count() != audioRecorder->audioSettings().channelCount()) { + qDeleteAll(audioLevels); + audioLevels.clear(); + for (int i = 0; i < audioRecorder->audioSettings().channelCount(); ++i) { + QAudioLevel *level = new QAudioLevel(ui->centralwidget); + audioLevels.append(level); + ui->levelsLayout->addWidget(level); + } + } + + ui->recordButton->setText(tr("Stop")); + ui->pauseButton->setText(tr("Pause")); + if (audioRecorder->outputLocation().isEmpty()) + statusMessage = tr("Recording"); + else + statusMessage = tr("Recording to %1").arg( audioRecorder->outputLocation().toString()); - break; - case QMediaRecorder::PausedState: - ui->recordButton->setText(tr("Stop")); - ui->pauseButton->setText(tr("Resume")); - statusMessage = tr("Paused"); - break; - case QMediaRecorder::StoppedState: - ui->recordButton->setText(tr("Record")); - ui->pauseButton->setText(tr("Pause")); - statusMessage = tr("Stopped"); + break; + case QMediaRecorder::PausedStatus: + clearAudioLevels(); + ui->recordButton->setText(tr("Stop")); + ui->pauseButton->setText(tr("Resume")); + statusMessage = tr("Paused"); + break; + case QMediaRecorder::UnloadedStatus: + clearAudioLevels(); + ui->recordButton->setText(tr("Record")); + ui->pauseButton->setText(tr("Pause")); + statusMessage = tr("Stopped"); + default: + break; } - ui->pauseButton->setEnabled(state != QMediaRecorder::StoppedState); + ui->pauseButton->setEnabled(audioRecorder->state() + != QMediaRecorder::StoppedState); if (audioRecorder->error() == QMediaRecorder::NoError) ui->statusbar->showMessage(statusMessage); @@ -176,6 +200,7 @@ void AudioRecorder::toggleRecord() settings.setCodec(boxValue(ui->audioCodecBox).toString()); settings.setSampleRate(boxValue(ui->sampleRateBox).toInt()); settings.setBitRate(boxValue(ui->bitrateBox).toInt()); + settings.setChannelCount(boxValue(ui->channelsBox).toInt()); settings.setQuality(QMultimedia::EncodingQuality(ui->qualitySlider->value())); settings.setEncodingMode(ui->constantQualityRadioButton->isChecked() ? QMultimedia::ConstantQualityEncoding : @@ -202,7 +227,7 @@ void AudioRecorder::togglePause() void AudioRecorder::setOutputLocation() { QString fileName = QFileDialog::getSaveFileName(); - audioRecorder->setOutputLocation(QUrl(fileName)); + audioRecorder->setOutputLocation(QUrl::fromLocalFile(fileName)); outputLocationSet = true; } @@ -211,88 +236,121 @@ void AudioRecorder::displayErrorMessage() ui->statusbar->showMessage(audioRecorder->errorString()); } +void AudioRecorder::clearAudioLevels() +{ + for (int i = 0; i < audioLevels.size(); ++i) + audioLevels.at(i)->setLevel(0); +} + // This function returns the maximum possible sample value for a given audio format qreal getPeakValue(const QAudioFormat& format) { // Note: Only the most common sample formats are supported if (!format.isValid()) - return 0.0; + return qreal(0); if (format.codec() != "audio/pcm") - return 0.0; + return qreal(0); switch (format.sampleType()) { case QAudioFormat::Unknown: break; case QAudioFormat::Float: if (format.sampleSize() != 32) // other sample formats are not supported - return 0.0; - return 1.00003; + return qreal(0); + return qreal(1.00003); case QAudioFormat::SignedInt: if (format.sampleSize() == 32) - return 2147483648.0; + return qreal(INT_MAX); if (format.sampleSize() == 16) - return 32768.0; + return qreal(SHRT_MAX); if (format.sampleSize() == 8) - return 128.0; + return qreal(CHAR_MAX); break; case QAudioFormat::UnSignedInt: - // Unsigned formats are not supported in this example + if (format.sampleSize() == 32) + return qreal(UINT_MAX); + if (format.sampleSize() == 16) + return qreal(USHRT_MAX); + if (format.sampleSize() == 8) + return qreal(UCHAR_MAX); break; } - return 0.0; + return qreal(0); } -qreal getBufferLevel(const QAudioBuffer& buffer) +// returns the audio level for each channel +QVector<qreal> getBufferLevels(const QAudioBuffer& buffer) { + QVector<qreal> values; + if (!buffer.format().isValid() || buffer.format().byteOrder() != QAudioFormat::LittleEndian) - return 0.0; + return values; if (buffer.format().codec() != "audio/pcm") - return 0.0; + return values; + int channelCount = buffer.format().channelCount(); + values.fill(0, channelCount); qreal peak_value = getPeakValue(buffer.format()); - if (qFuzzyCompare(peak_value, 0.0)) - return 0.0; + if (qFuzzyCompare(peak_value, qreal(0))) + return values; switch (buffer.format().sampleType()) { case QAudioFormat::Unknown: case QAudioFormat::UnSignedInt: + if (buffer.format().sampleSize() == 32) + values = getBufferLevels(buffer.constData<quint32>(), buffer.frameCount(), channelCount); + if (buffer.format().sampleSize() == 16) + values = getBufferLevels(buffer.constData<quint16>(), buffer.frameCount(), channelCount); + if (buffer.format().sampleSize() == 8) + values = getBufferLevels(buffer.constData<quint8>(), buffer.frameCount(), channelCount); + for (int i = 0; i < values.size(); ++i) + values[i] = qAbs(values.at(i) - peak_value / 2) / (peak_value / 2); break; case QAudioFormat::Float: - if (buffer.format().sampleSize() == 32) - return getBufferLevel(buffer.constData<float>(), buffer.sampleCount()) / peak_value; + if (buffer.format().sampleSize() == 32) { + values = getBufferLevels(buffer.constData<float>(), buffer.frameCount(), channelCount); + for (int i = 0; i < values.size(); ++i) + values[i] /= peak_value; + } break; case QAudioFormat::SignedInt: if (buffer.format().sampleSize() == 32) - return getBufferLevel(buffer.constData<long int>(), buffer.sampleCount()) / peak_value; + values = getBufferLevels(buffer.constData<qint32>(), buffer.frameCount(), channelCount); if (buffer.format().sampleSize() == 16) - return getBufferLevel(buffer.constData<short int>(), buffer.sampleCount()) / peak_value; + values = getBufferLevels(buffer.constData<qint16>(), buffer.frameCount(), channelCount); if (buffer.format().sampleSize() == 8) - return getBufferLevel(buffer.constData<signed char>(), buffer.sampleCount()) / peak_value; + values = getBufferLevels(buffer.constData<qint8>(), buffer.frameCount(), channelCount); + for (int i = 0; i < values.size(); ++i) + values[i] /= peak_value; break; } - return 0.0; + return values; } template <class T> -qreal getBufferLevel(const T *buffer, int samples) +QVector<qreal> getBufferLevels(const T *buffer, int frames, int channels) { - qreal max_value = 0.0; - - for (int i = 0; i < samples; ++i) { - qreal value = qAbs(qreal(buffer[i])); - if (value > max_value) - max_value = value; + QVector<qreal> max_values; + max_values.fill(0, channels); + + for (int i = 0; i < frames; ++i) { + for (int j = 0; j < channels; ++j) { + qreal value = qAbs(qreal(buffer[i * channels + j])); + if (value > max_values.at(j)) + max_values.replace(j, value); + } } - return max_value; + return max_values; } void AudioRecorder::processBuffer(const QAudioBuffer& buffer) { - qreal level = getBufferLevel(buffer); - ui->audioLevel->setLevel(level); + QVector<qreal> levels = getBufferLevels(buffer); + for (int i = 0; i < levels.count(); ++i) + audioLevels.at(i)->setLevel(levels.at(i)); } diff --git a/examples/multimedia/audiorecorder/audiorecorder.h b/examples/multimedia/audiorecorder/audiorecorder.h index 1a12031ec..2c0a1e5ed 100644 --- a/examples/multimedia/audiorecorder/audiorecorder.h +++ b/examples/multimedia/audiorecorder/audiorecorder.h @@ -52,6 +52,8 @@ class QAudioProbe; class QAudioBuffer; QT_END_NAMESPACE +class QAudioLevel; + class AudioRecorder : public QMainWindow { Q_OBJECT @@ -68,15 +70,18 @@ private slots: void togglePause(); void toggleRecord(); - void updateState(QMediaRecorder::State); + void updateStatus(QMediaRecorder::Status); void updateProgress(qint64 pos); void displayErrorMessage(); private: + void clearAudioLevels(); + Ui::AudioRecorder *ui; QAudioRecorder *audioRecorder; QAudioProbe *probe; + QList<QAudioLevel*> audioLevels; bool outputLocationSet; }; diff --git a/examples/multimedia/audiorecorder/audiorecorder.ui b/examples/multimedia/audiorecorder/audiorecorder.ui index ff6c2d935..6ea510524 100644 --- a/examples/multimedia/audiorecorder/audiorecorder.ui +++ b/examples/multimedia/audiorecorder/audiorecorder.ui @@ -17,10 +17,10 @@ <layout class="QGridLayout" name="gridLayout_3"> <item row="0" column="0" colspan="3"> <layout class="QGridLayout" name="gridLayout_2"> - <item row="0" column="0"> - <widget class="QLabel" name="label"> + <item row="3" column="0"> + <widget class="QLabel" name="label_4"> <property name="text"> - <string>Input Device:</string> + <string>Sample rate:</string> </property> </widget> </item> @@ -34,8 +34,12 @@ </property> </widget> </item> - <item row="1" column="1"> - <widget class="QComboBox" name="audioCodecBox"/> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Input Device:</string> + </property> + </widget> </item> <item row="2" column="0"> <widget class="QLabel" name="label_3"> @@ -47,15 +51,21 @@ <item row="2" column="1"> <widget class="QComboBox" name="containerBox"/> </item> - <item row="3" column="0"> - <widget class="QLabel" name="label_4"> + <item row="3" column="1"> + <widget class="QComboBox" name="sampleRateBox"/> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="audioCodecBox"/> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_5"> <property name="text"> - <string>Sample rate:</string> + <string>Channels:</string> </property> </widget> </item> - <item row="3" column="1"> - <widget class="QComboBox" name="sampleRateBox"/> + <item row="4" column="1"> + <widget class="QComboBox" name="channelsBox"/> </item> </layout> </item> @@ -162,9 +172,8 @@ </property> </widget> </item> - <item row="3" column="1" colspan="-1"> - <widget class="QAudioLevel" name="audioLevel"> - </widget> + <item row="3" column="1" colspan="2"> + <layout class="QVBoxLayout" name="levelsLayout"/> </item> </layout> </widget> diff --git a/examples/multimedia/audiorecorder/audiorecorder_small.ui b/examples/multimedia/audiorecorder/audiorecorder_small.ui index 9d23c4267..2030ab963 100644 --- a/examples/multimedia/audiorecorder/audiorecorder_small.ui +++ b/examples/multimedia/audiorecorder/audiorecorder_small.ui @@ -28,57 +28,33 @@ <rect> <x>0</x> <y>0</y> - <width>398</width> - <height>275</height> + <width>400</width> + <height>277</height> </rect> </property> <layout class="QGridLayout" name="gridLayout_4"> <item row="0" column="0"> <widget class="QWidget" name="widget" native="true"> <layout class="QGridLayout" name="gridLayout_3"> - <item row="0" column="0"> - <layout class="QGridLayout" name="gridLayout_2"> - <item row="0" column="0"> - <widget class="QLabel" name="label"> - <property name="text"> - <string>Input Device:</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QComboBox" name="audioDeviceBox"/> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_2"> - <property name="text"> - <string>Audio Codec:</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QComboBox" name="audioCodecBox"/> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="label_3"> - <property name="text"> - <string>File Container:</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QComboBox" name="containerBox"/> - </item> - <item row="3" column="0"> - <widget class="QLabel" name="label_4"> - <property name="text"> - <string>Sample rate:</string> - </property> - </widget> - </item> - <item row="3" column="1"> - <widget class="QComboBox" name="sampleRateBox"/> - </item> - </layout> + <item row="3" column="0"> + <widget class="QLabel" name="label_6"> + <property name="text"> + <string>Audio Level:</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>29</height> + </size> + </property> + </spacer> </item> <item row="1" column="0"> <layout class="QGridLayout" name="gridLayout"> @@ -127,18 +103,62 @@ </item> </layout> </item> - <item row="2" column="0"> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>29</height> - </size> - </property> - </spacer> + <item row="0" column="0"> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="3" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Sample rate:</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Audio Codec:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QComboBox" name="containerBox"/> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="audioDeviceBox"/> + </item> + <item row="3" column="1"> + <widget class="QComboBox" name="sampleRateBox"/> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="audioCodecBox"/> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>File Container:</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Input Device:</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Channels:</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QComboBox" name="channelsBox"/> + </item> + </layout> + </item> + <item row="4" column="0"> + <layout class="QVBoxLayout" name="levelsLayout"/> </item> </layout> </widget> diff --git a/examples/multimedia/audiorecorder/qaudiolevel.cpp b/examples/multimedia/audiorecorder/qaudiolevel.cpp index 6f2ee1eff..92ec4174a 100644 --- a/examples/multimedia/audiorecorder/qaudiolevel.cpp +++ b/examples/multimedia/audiorecorder/qaudiolevel.cpp @@ -45,6 +45,8 @@ QAudioLevel::QAudioLevel(QWidget *parent) : QWidget(parent) , m_level(0.0) { + setMinimumHeight(15); + setMaximumHeight(50); } void QAudioLevel::setLevel(qreal level) |