diff options
Diffstat (limited to 'examples/multimedia/audiorecorder')
-rw-r--r-- | examples/multimedia/audiorecorder/CMakeLists.txt | 51 | ||||
-rw-r--r-- | examples/multimedia/audiorecorder/Info.plist.in | 44 | ||||
-rw-r--r-- | examples/multimedia/audiorecorder/audiolevel.cpp | 56 | ||||
-rw-r--r-- | examples/multimedia/audiorecorder/audiolevel.h | 53 | ||||
-rw-r--r-- | examples/multimedia/audiorecorder/audiorecorder.cpp | 411 | ||||
-rw-r--r-- | examples/multimedia/audiorecorder/audiorecorder.h | 79 | ||||
-rw-r--r-- | examples/multimedia/audiorecorder/audiorecorder.pro | 1 | ||||
-rw-r--r-- | examples/multimedia/audiorecorder/audiorecorder.ui | 8 | ||||
-rw-r--r-- | examples/multimedia/audiorecorder/doc/src/audiorecorder.qdoc | 92 | ||||
-rw-r--r-- | examples/multimedia/audiorecorder/main.cpp | 53 |
10 files changed, 344 insertions, 504 deletions
diff --git a/examples/multimedia/audiorecorder/CMakeLists.txt b/examples/multimedia/audiorecorder/CMakeLists.txt new file mode 100644 index 000000000..6ff9306fe --- /dev/null +++ b/examples/multimedia/audiorecorder/CMakeLists.txt @@ -0,0 +1,51 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(audiorecorder LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/audiorecorder") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Multimedia Widgets) + +qt_add_executable(audiorecorder + audiolevel.cpp audiolevel.h + audiorecorder.cpp audiorecorder.h audiorecorder.ui + main.cpp +) + +set_target_properties(audiorecorder PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in +) + +target_include_directories(audiorecorder PUBLIC + ../shared +) + +target_link_libraries(audiorecorder PUBLIC + Qt::Core + Qt::Gui + Qt::Multimedia + Qt::Widgets +) + +if(WIN32) + target_include_directories(audiorecorder PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ) +endif() + +install(TARGETS audiorecorder + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/examples/multimedia/audiorecorder/Info.plist.in b/examples/multimedia/audiorecorder/Info.plist.in new file mode 100644 index 000000000..43b966509 --- /dev/null +++ b/examples/multimedia/audiorecorder/Info.plist.in @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + + <key>CFBundleName</key> + <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string> + <key>CFBundleIdentifier</key> + <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string> + <key>CFBundleExecutable</key> + <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string> + + <key>CFBundleVersion</key> + <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string> + <key>CFBundleShortVersionString</key> + <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string> + <key>CFBundleLongVersionString</key> + <string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string> + + <key>LSMinimumSystemVersion</key> + <string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string> + + <key>CFBundleGetInfoString</key> + <string>${MACOSX_BUNDLE_INFO_STRING}</string> + <key>NSHumanReadableCopyright</key> + <string>${MACOSX_BUNDLE_COPYRIGHT}</string> + + <key>CFBundleIconFile</key> + <string>${MACOSX_BUNDLE_ICON_FILE}</string> + + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + + <key>NSMicrophoneUsageDescription</key> + <string>Qt Multimedia Example</string> + + <key>NSSupportsAutomaticGraphicsSwitching</key> + <true/> +</dict> +</plist> diff --git a/examples/multimedia/audiorecorder/audiolevel.cpp b/examples/multimedia/audiorecorder/audiolevel.cpp index b13e7fbc8..444dca9a4 100644 --- a/examples/multimedia/audiorecorder/audiolevel.cpp +++ b/examples/multimedia/audiorecorder/audiolevel.cpp @@ -1,58 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "audiolevel.h" + #include <QPainter> -AudioLevel::AudioLevel(QWidget *parent) - : QWidget(parent) +AudioLevel::AudioLevel(QWidget *parent) : QWidget(parent) { setMinimumHeight(15); setMaximumHeight(50); @@ -77,3 +30,4 @@ void AudioLevel::paintEvent(QPaintEvent *event) // clear the rest of the control painter.fillRect(widthLevel, 0, width(), height(), Qt::black); } + diff --git a/examples/multimedia/audiorecorder/audiolevel.h b/examples/multimedia/audiorecorder/audiolevel.h index 3c71264c5..7a42a8d01 100644 --- a/examples/multimedia/audiorecorder/audiolevel.h +++ b/examples/multimedia/audiorecorder/audiolevel.h @@ -1,52 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef AUDIOLEVEL_H #define AUDIOLEVEL_H @@ -57,7 +10,7 @@ class AudioLevel : public QWidget { Q_OBJECT public: - explicit AudioLevel(QWidget *parent = 0); + explicit AudioLevel(QWidget *parent = nullptr); // Using [0; 1.0] range void setLevel(qreal level); diff --git a/examples/multimedia/audiorecorder/audiorecorder.cpp b/examples/multimedia/audiorecorder/audiorecorder.cpp index 4eecae835..4131d5d51 100644 --- a/examples/multimedia/audiorecorder/audiorecorder.cpp +++ b/examples/multimedia/audiorecorder/audiorecorder.cpp @@ -1,128 +1,102 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "audiorecorder.h" #include "audiolevel.h" - #include "ui_audiorecorder.h" -#include <QAudioProbe> -#include <QAudioRecorder> +#include <QAudioBuffer> +#include <QAudioDevice> +#include <QAudioInput> #include <QDir> #include <QFileDialog> +#include <QImageCapture> +#include <QMediaDevices> +#include <QMediaFormat> #include <QMediaRecorder> +#include <QMimeType> #include <QStandardPaths> -static qreal getPeakValue(const QAudioFormat &format); -static QList<qreal> getBufferLevels(const QAudioBuffer &buffer); +#if QT_CONFIG(permissions) + #include <QPermission> +#endif -template<class T> -static QList<qreal> getBufferLevels(const T *buffer, int frames, int channels); +static QList<qreal> getBufferLevels(const QAudioBuffer &buffer); -AudioRecorder::AudioRecorder() - : ui(new Ui::AudioRecorder) +AudioRecorder::AudioRecorder() : ui(new Ui::AudioRecorder) { ui->setupUi(this); - m_audioRecorder = new QAudioRecorder(this); - m_probe = new QAudioProbe(this); - connect(m_probe, &QAudioProbe::audioBufferProbed, - this, &AudioRecorder::processBuffer); - m_probe->setSource(m_audioRecorder); - - //audio devices - ui->audioDeviceBox->addItem(tr("Default"), QVariant(QString())); - for (auto &device: m_audioRecorder->audioInputs()) { - ui->audioDeviceBox->addItem(device, QVariant(device)); - } - - //audio codecs - ui->audioCodecBox->addItem(tr("Default"), QVariant(QString())); - for (auto &codecName: m_audioRecorder->supportedAudioCodecs()) { - ui->audioCodecBox->addItem(codecName, QVariant(codecName)); - } - - //containers - ui->containerBox->addItem(tr("Default"), QVariant(QString())); - for (auto &containerName: m_audioRecorder->supportedContainers()) { - ui->containerBox->addItem(containerName, QVariant(containerName)); - } - - //sample rate - ui->sampleRateBox->addItem(tr("Default"), QVariant(0)); - for (int sampleRate: m_audioRecorder->supportedAudioSampleRates()) { - ui->sampleRateBox->addItem(QString::number(sampleRate), QVariant( - sampleRate)); - } - - //channels + // 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)); + // quality + ui->qualitySlider->setRange(0, int(QImageCapture::VeryHighQuality)); + ui->qualitySlider->setValue(int(QImageCapture::NormalQuality)); - //bitrates: + // bit rates: 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(m_audioRecorder, &QAudioRecorder::durationChanged, this, &AudioRecorder::updateProgress); - connect(m_audioRecorder, &QAudioRecorder::statusChanged, this, &AudioRecorder::updateStatus); - connect(m_audioRecorder, &QAudioRecorder::stateChanged, this, &AudioRecorder::onStateChanged); - connect(m_audioRecorder, QOverload<QMediaRecorder::Error>::of(&QAudioRecorder::error), this, + // audio input initialization + init(); +} + +void AudioRecorder::init() +{ +#if QT_CONFIG(permissions) + QMicrophonePermission microphonePermission; + switch (qApp->checkPermission(microphonePermission)) { + case Qt::PermissionStatus::Undetermined: + qApp->requestPermission(microphonePermission, this, &AudioRecorder::init); + return; + case Qt::PermissionStatus::Denied: + qWarning("Microphone permission is not granted!"); + return; + case Qt::PermissionStatus::Granted: + break; + } +#endif + + m_audioRecorder = new QMediaRecorder(this); + m_captureSession.setRecorder(m_audioRecorder); + m_captureSession.setAudioInput(new QAudioInput(this)); + // ### replace with a monitoring output once we have it. + // m_probe = new QAudioProbe(this); + // connect(m_probe, &QAudioProbe::audioBufferProbed, + // this, &AudioRecorder::processBuffer); + // m_probe->setSource(m_audioRecorder); + + // audio devices + m_mediaDevices = new QMediaDevices(this); + connect(m_mediaDevices, &QMediaDevices::audioInputsChanged, this, + &AudioRecorder::updateDevices); + updateDevices(); + + // audio codecs and container formats + updateFormats(); + connect(ui->audioCodecBox, &QComboBox::currentIndexChanged, this, + &AudioRecorder::updateFormats); + connect(ui->containerBox, &QComboBox::currentIndexChanged, this, &AudioRecorder::updateFormats); + + // sample rate + ui->sampleRateBox->setRange(m_captureSession.audioInput()->device().minimumSampleRate(), + m_captureSession.audioInput()->device().maximumSampleRate()); + ui->sampleRateBox->setValue( + qBound(m_captureSession.audioInput()->device().minimumSampleRate(), 44100, + m_captureSession.audioInput()->device().maximumSampleRate())); + + connect(m_audioRecorder, &QMediaRecorder::durationChanged, this, + &AudioRecorder::updateProgress); + connect(m_audioRecorder, &QMediaRecorder::recorderStateChanged, this, + &AudioRecorder::onStateChanged); + connect(m_audioRecorder, &QMediaRecorder::errorChanged, this, &AudioRecorder::displayErrorMessage); } @@ -134,48 +108,33 @@ void AudioRecorder::updateProgress(qint64 duration) ui->statusbar->showMessage(tr("Recorded %1 sec").arg(duration / 1000)); } -void AudioRecorder::updateStatus(QMediaRecorder::Status status) +void AudioRecorder::onStateChanged(QMediaRecorder::RecorderState state) { QString statusMessage; - switch (status) { - case QMediaRecorder::RecordingStatus: - statusMessage = tr("Recording to %1").arg(m_audioRecorder->actualLocation().toString()); - break; - case QMediaRecorder::PausedStatus: - clearAudioLevels(); - statusMessage = tr("Paused"); - break; - case QMediaRecorder::UnloadedStatus: - case QMediaRecorder::LoadedStatus: - clearAudioLevels(); - statusMessage = tr("Stopped"); - default: - break; - } - - if (m_audioRecorder->error() == QMediaRecorder::NoError) - ui->statusbar->showMessage(statusMessage); -} - -void AudioRecorder::onStateChanged(QMediaRecorder::State state) -{ switch (state) { case QMediaRecorder::RecordingState: + statusMessage = tr("Recording to %1").arg(m_audioRecorder->actualLocation().toString()); ui->recordButton->setText(tr("Stop")); ui->pauseButton->setText(tr("Pause")); break; case QMediaRecorder::PausedState: + clearAudioLevels(); + statusMessage = tr("Paused"); ui->recordButton->setText(tr("Stop")); ui->pauseButton->setText(tr("Resume")); break; case QMediaRecorder::StoppedState: + clearAudioLevels(); + statusMessage = tr("Stopped"); ui->recordButton->setText(tr("Record")); ui->pauseButton->setText(tr("Pause")); break; } - ui->pauseButton->setEnabled(m_audioRecorder->state() != QMediaRecorder::StoppedState); + ui->pauseButton->setEnabled(m_audioRecorder->recorderState() != QMediaRecorder::StoppedState); + if (m_audioRecorder->error() == QMediaRecorder::NoError) + ui->statusbar->showMessage(statusMessage); } static QVariant boxValue(const QComboBox *box) @@ -189,32 +148,28 @@ static QVariant boxValue(const QComboBox *box) void AudioRecorder::toggleRecord() { - if (m_audioRecorder->state() == QMediaRecorder::StoppedState) { - m_audioRecorder->setAudioInput(boxValue(ui->audioDeviceBox).toString()); - - QAudioEncoderSettings settings; - 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 : - QMultimedia::ConstantBitRateEncoding); - - QString container = boxValue(ui->containerBox).toString(); - - m_audioRecorder->setEncodingSettings(settings, QVideoEncoderSettings(), container); + if (m_audioRecorder->recorderState() == QMediaRecorder::StoppedState) { + m_captureSession.audioInput()->setDevice( + boxValue(ui->audioDeviceBox).value<QAudioDevice>()); + + m_audioRecorder->setMediaFormat(selectedMediaFormat()); + m_audioRecorder->setAudioSampleRate(ui->sampleRateBox->value()); + m_audioRecorder->setAudioBitRate(boxValue(ui->bitrateBox).toInt()); + m_audioRecorder->setAudioChannelCount(boxValue(ui->channelsBox).toInt()); + m_audioRecorder->setQuality(QMediaRecorder::Quality(ui->qualitySlider->value())); + m_audioRecorder->setEncodingMode(ui->constantQualityRadioButton->isChecked() + ? QMediaRecorder::ConstantQualityEncoding + : QMediaRecorder::ConstantBitRateEncoding); + m_audioRecorder->record(); - } - else { + } else { m_audioRecorder->stop(); } } void AudioRecorder::togglePause() { - if (m_audioRecorder->state() != QMediaRecorder::PausedState) + if (m_audioRecorder->recorderState() != QMediaRecorder::PausedState) m_audioRecorder->pause(); else m_audioRecorder->record(); @@ -222,14 +177,10 @@ void AudioRecorder::togglePause() void AudioRecorder::setOutputLocation() { -#ifdef Q_OS_WINRT - // UWP does not allow to store outside the sandbox - const QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); - if (!QDir().mkpath(cacheDir)) { - qWarning() << "Failed to create cache directory"; - return; - } - QString fileName = cacheDir + QLatin1String("/output.wav"); +#ifdef Q_OS_ANDROID + QString fileName = QFileDialog::getSaveFileName( + this, tr("Save Recording"), + "output." + selectedMediaFormat().mimeType().preferredSuffix()); #else QString fileName = QFileDialog::getSaveFileName(); #endif @@ -242,48 +193,78 @@ void AudioRecorder::displayErrorMessage() ui->statusbar->showMessage(m_audioRecorder->errorString()); } -void AudioRecorder::clearAudioLevels() +void AudioRecorder::updateDevices() { - for (int i = 0; i < m_audioLevels.size(); ++i) - m_audioLevels.at(i)->setLevel(0); + const auto currentDevice = boxValue(ui->audioDeviceBox).value<QAudioDevice>(); + int currentDeviceIndex = 0; + + ui->audioDeviceBox->clear(); + + ui->audioDeviceBox->addItem(tr("Default"), {}); + for (const auto &device : m_mediaDevices->audioInputs()) { + const auto name = device.description(); + ui->audioDeviceBox->addItem(name, QVariant::fromValue(device)); + + if (device.id() == currentDevice.id()) + currentDeviceIndex = ui->audioDeviceBox->count() - 1; + } + + ui->audioDeviceBox->setCurrentIndex(currentDeviceIndex); } -// This function returns the maximum possible sample value for a given audio format -qreal getPeakValue(const QAudioFormat& format) +void AudioRecorder::updateFormats() { - // Note: Only the most common sample formats are supported - if (!format.isValid()) - return qreal(0); + if (m_updatingFormats) + return; + m_updatingFormats = true; + + QMediaFormat format; + if (ui->containerBox->count()) + format.setFileFormat(boxValue(ui->containerBox).value<QMediaFormat::FileFormat>()); + if (ui->audioCodecBox->count()) + format.setAudioCodec(boxValue(ui->audioCodecBox).value<QMediaFormat::AudioCodec>()); + + int currentIndex = 0; + ui->audioCodecBox->clear(); + ui->audioCodecBox->addItem(tr("Default audio codec"), + QVariant::fromValue(QMediaFormat::AudioCodec::Unspecified)); + for (auto codec : format.supportedAudioCodecs(QMediaFormat::Encode)) { + if (codec == format.audioCodec()) + currentIndex = ui->audioCodecBox->count(); + ui->audioCodecBox->addItem(QMediaFormat::audioCodecDescription(codec), + QVariant::fromValue(codec)); + } + ui->audioCodecBox->setCurrentIndex(currentIndex); + + currentIndex = 0; + ui->containerBox->clear(); + ui->containerBox->addItem(tr("Default file format"), + QVariant::fromValue(QMediaFormat::UnspecifiedFormat)); + for (auto container : format.supportedFileFormats(QMediaFormat::Encode)) { + if (container < QMediaFormat::Mpeg4Audio) // Skip video formats + continue; + if (container == format.fileFormat()) + currentIndex = ui->containerBox->count(); + ui->containerBox->addItem(QMediaFormat::fileFormatDescription(container), + QVariant::fromValue(container)); + } + ui->containerBox->setCurrentIndex(currentIndex); - if (format.codec() != "audio/pcm") - return qreal(0); + m_updatingFormats = false; +} - switch (format.sampleType()) { - case QAudioFormat::Unknown: - break; - case QAudioFormat::Float: - if (format.sampleSize() != 32) // other sample formats are not supported - return qreal(0); - return qreal(1.00003); - case QAudioFormat::SignedInt: - if (format.sampleSize() == 32) - return qreal(INT_MAX); - if (format.sampleSize() == 16) - return qreal(SHRT_MAX); - if (format.sampleSize() == 8) - return qreal(CHAR_MAX); - break; - case QAudioFormat::UnSignedInt: - 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; - } +void AudioRecorder::clearAudioLevels() +{ + for (auto m_audioLevel : std::as_const(m_audioLevels)) + m_audioLevel->setLevel(0); +} - return qreal(0); +QMediaFormat AudioRecorder::selectedMediaFormat() const +{ + QMediaFormat format; + format.setFileFormat(boxValue(ui->containerBox).value<QMediaFormat::FileFormat>()); + format.setAudioCodec(boxValue(ui->audioCodecBox).value<QMediaFormat::AudioCodec>()); + return format; } // returns the audio level for each channel @@ -291,70 +272,31 @@ QList<qreal> getBufferLevels(const QAudioBuffer &buffer) { QList<qreal> values; - if (!buffer.format().isValid() || buffer.format().byteOrder() != QAudioFormat::LittleEndian) - return values; - - if (buffer.format().codec() != "audio/pcm") - return values; - - int channelCount = buffer.format().channelCount(); - values.fill(0, channelCount); - qreal peak_value = getPeakValue(buffer.format()); - if (qFuzzyCompare(peak_value, qreal(0))) + auto format = buffer.format(); + if (!format.isValid()) 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) { - 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) - values = getBufferLevels(buffer.constData<qint32>(), buffer.frameCount(), channelCount); - if (buffer.format().sampleSize() == 16) - values = getBufferLevels(buffer.constData<qint16>(), buffer.frameCount(), channelCount); - if (buffer.format().sampleSize() == 8) - values = getBufferLevels(buffer.constData<qint8>(), buffer.frameCount(), channelCount); - for (int i = 0; i < values.size(); ++i) - values[i] /= peak_value; - break; - } - - return values; -} + int channels = buffer.format().channelCount(); + values.fill(0, channels); -template<class T> -QList<qreal> getBufferLevels(const T *buffer, int frames, int channels) -{ + int bytesPerSample = format.bytesPerSample(); QList<qreal> max_values; max_values.fill(0, channels); - for (int i = 0; i < frames; ++i) { + const char *data = buffer.constData<char>(); + for (int i = 0; i < buffer.frameCount(); ++i) { for (int j = 0; j < channels; ++j) { - qreal value = qAbs(qreal(buffer[i * channels + j])); + qreal value = qAbs(format.normalizedSampleValue(data)); if (value > max_values.at(j)) - max_values.replace(j, value); + max_values[j] = value; + data += bytesPerSample; } } return max_values; } -void AudioRecorder::processBuffer(const QAudioBuffer& buffer) +void AudioRecorder::processBuffer(const QAudioBuffer &buffer) { if (m_audioLevels.count() != buffer.format().channelCount()) { qDeleteAll(m_audioLevels); @@ -370,3 +312,4 @@ void AudioRecorder::processBuffer(const QAudioBuffer& buffer) for (int i = 0; i < levels.count(); ++i) m_audioLevels.at(i)->setLevel(levels.at(i)); } + diff --git a/examples/multimedia/audiorecorder/audiorecorder.h b/examples/multimedia/audiorecorder/audiorecorder.h index 6818dba90..2ce71c162 100644 --- a/examples/multimedia/audiorecorder/audiorecorder.h +++ b/examples/multimedia/audiorecorder/audiorecorder.h @@ -1,65 +1,20 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef AUDIORECORDER_H #define AUDIORECORDER_H #include <QMainWindow> +#include <QMediaCaptureSession> #include <QMediaRecorder> #include <QUrl> QT_BEGIN_NAMESPACE -namespace Ui { class AudioRecorder; } -class QAudioRecorder; -class QAudioProbe; +namespace Ui { +class AudioRecorder; +} class QAudioBuffer; +class QMediaDevices; QT_END_NAMESPACE class AudioLevel; @@ -72,28 +27,34 @@ public: AudioRecorder(); public slots: - void processBuffer(const QAudioBuffer&); + void processBuffer(const QAudioBuffer &); private slots: + void init(); void setOutputLocation(); void togglePause(); void toggleRecord(); - void updateStatus(QMediaRecorder::Status); - void onStateChanged(QMediaRecorder::State); + void onStateChanged(QMediaRecorder::RecorderState); void updateProgress(qint64 pos); void displayErrorMessage(); + void updateDevices(); + void updateFormats(); + private: void clearAudioLevels(); + QMediaFormat selectedMediaFormat() const; Ui::AudioRecorder *ui = nullptr; - QAudioRecorder *m_audioRecorder = nullptr; - QAudioProbe *m_probe = nullptr; - QList<AudioLevel*> m_audioLevels; - bool m_outputLocationSet = false; + QMediaCaptureSession m_captureSession; + QMediaRecorder *m_audioRecorder = nullptr; + QMediaDevices *m_mediaDevices = nullptr; + QList<AudioLevel *> m_audioLevels; + bool m_outputLocationSet = false; + bool m_updatingFormats = false; }; #endif // AUDIORECORDER_H diff --git a/examples/multimedia/audiorecorder/audiorecorder.pro b/examples/multimedia/audiorecorder/audiorecorder.pro index dfd0364a4..a0e05cc03 100644 --- a/examples/multimedia/audiorecorder/audiorecorder.pro +++ b/examples/multimedia/audiorecorder/audiorecorder.pro @@ -21,3 +21,4 @@ INSTALLS += target QT+=widgets include(../shared/shared.pri) + diff --git a/examples/multimedia/audiorecorder/audiorecorder.ui b/examples/multimedia/audiorecorder/audiorecorder.ui index 6ea510524..bd2560622 100644 --- a/examples/multimedia/audiorecorder/audiorecorder.ui +++ b/examples/multimedia/audiorecorder/audiorecorder.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>297</width> - <height>374</height> + <height>476</height> </rect> </property> <property name="windowTitle"> @@ -51,9 +51,6 @@ <item row="2" column="1"> <widget class="QComboBox" name="containerBox"/> </item> - <item row="3" column="1"> - <widget class="QComboBox" name="sampleRateBox"/> - </item> <item row="1" column="1"> <widget class="QComboBox" name="audioCodecBox"/> </item> @@ -67,6 +64,9 @@ <item row="4" column="1"> <widget class="QComboBox" name="channelsBox"/> </item> + <item row="3" column="1"> + <widget class="QSpinBox" name="sampleRateBox"/> + </item> </layout> </item> <item row="1" column="0" colspan="3"> diff --git a/examples/multimedia/audiorecorder/doc/src/audiorecorder.qdoc b/examples/multimedia/audiorecorder/doc/src/audiorecorder.qdoc index 4e4032bff..71481f24d 100644 --- a/examples/multimedia/audiorecorder/doc/src/audiorecorder.qdoc +++ b/examples/multimedia/audiorecorder/doc/src/audiorecorder.qdoc @@ -1,35 +1,14 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: https://www.gnu.org/licenses/fdl-1.3.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2015 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! - \example multimedia/audiorecorder + \example audiorecorder \title Audio Recorder Example \ingroup multimedia_examples + \ingroup audio_examples + \examplecategory {Multimedia} \brief Discovering the available devices and supported codecs. + \meta {tag} {widgets} \e{Audio Recorder} demonstrates how to identify the available devices and supported codecs, and the use of QAudioRecorder class. @@ -41,30 +20,33 @@ \section1 Displaying the Window and Audio Settings We display a window for the user to select the appropriate audio input, - codec, container, and sample rate. Allow a setting of either quality or - bitrate. Finally, the output file can be selected and recording can be - started. - - The lists are setup using the \l{QAudioRecorder::audioInputs()}{audioInputs()}, - \l{QAudioRecorder::supportedAudioCodecs()}{supportedAudioCodecs()}, - \l{QAudioRecorder::supportedContainers()}{supportedContainers()}, - \l{QAudioRecorder::supportedContainers()}{supportedContainers()}, and - \l{QAudioRecorder::supportedAudioSampleRates()}{supportedAudioSampleRates()} - methods. The quality slider is setup from 0 (zero) to - \l{QMultimedia::VeryHighQuality} with a default value of - \l{QMultimedia::NormalQuality}, while the bitrates are hardcoded + codec, container, sample rate, and channels. It allows setting of either + quality or bit rate. Finally, the output file can be selected and recording + can be started. + + The lists are populated using the following methods: + \list + \li \l{QMediaDevices::audioInputs()} + \li \l{QMediaFormat::supportedAudioCodecs} + \li \l{QMediaFormat::supportedFileFormats} + \li \l{QAudioDevice::maximumSampleRate()} + \li \l{QAudioDevice::minimumSampleRate()} + \endlist + The quality slider is setup from 0 (zero) to + \l{QMediaRecorder::VeryHighQuality} with a default value of + \l{QMediaRecorder::NormalQuality}, while the bit rate box are hard-coded into the list. \section1 Recording Audio - To record audio we simply create a QAudioRecorder object. + To record audio we simply create a QAudioRecorder object, \code audioRecorder = new QAudioRecorder(this); \endcode - And setup the lists as described above. The text on the record and pause - buttons are toggled depending on the \l{QMediaRecorder::State}{state} of + and setup the lists as described above. The text on the record and pause + buttons are toggled depending on the \l{QMediaRecorder::RecorderState}{state} of the \c audioRecorder object. This means that if the state is \l{QMediaRecorder::StoppedState} then the button text will be "Record" and "Pause". In \l{QMediaRecorder::RecordingState} the record button will have @@ -72,25 +54,23 @@ will have the text "Resume". Pressing the buttons will also result in a toggle based on the state. If - recording is stopped, then pressing the record button will setup the - \l{QAudioEncoderSettings} based on the values of the selection lists, - will set the encoding settings and container on the \c audioRecorder - object, and start recording using the - \l{QMediaRecorder::record()}{record()} method. + recording is stopped, then pressing the record button will set the encoding + settings and container on the \c audioRecorder object, and start recording + using the \l{QMediaRecorder::record()}{record()} method. \code - QAudioEncoderSettings settings; - settings.setCodec(boxValue(ui->audioCodecBox).toString()); - settings.setSampleRate(boxValue(ui->sampleRateBox).toInt()); - settings.setBitRate(boxValue(ui->bitrateBox).toInt()); - settings.setQuality(QMultimedia::EncodingQuality(ui->qualitySlider->value())); - settings.setEncodingMode(ui->constantQualityRadioButton->isChecked() ? - QMultimedia::ConstantQualityEncoding : - QMultimedia::ConstantBitRateEncoding); + QMediaFormat format; + format.setCodec(boxValue(ui->audioCodecBox).toString()); + audioRecorder->setMediaFormat(format); + audioRecorder->setSampleRate(boxValue(ui->sampleRateBox).toInt()); + audioRecorder->setBitRate(boxValue(ui->bitrateBox).toInt()); + audioRecorder->setQuality(QMediaRecorder::EncodingQuality(ui->qualitySlider->value())); + audioRecorder->setEncodingMode(ui->constantQualityRadioButton->isChecked() ? + QMediaRecorder::ConstantQualityEncoding : + QMediaRecorder::ConstantBitRateEncoding); QString container = boxValue(ui->containerBox).toString(); - audioRecorder->setEncodingSettings(settings, QVideoEncoderSettings(), container); audioRecorder->record(); \endcode diff --git a/examples/multimedia/audiorecorder/main.cpp b/examples/multimedia/audiorecorder/main.cpp index f13940b23..407729616 100644 --- a/examples/multimedia/audiorecorder/main.cpp +++ b/examples/multimedia/audiorecorder/main.cpp @@ -1,56 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "audiorecorder.h" -#include <QtWidgets> +#include <QApplication> int main(int argc, char *argv[]) { |