summaryrefslogtreecommitdiffstats
path: root/examples/multimedia/spectrum
diff options
context:
space:
mode:
Diffstat (limited to 'examples/multimedia/spectrum')
-rw-r--r--examples/multimedia/spectrum/3rdparty/fftreal/CMakeLists.txt27
-rw-r--r--examples/multimedia/spectrum/3rdparty/fftreal/fftreal.pro11
-rw-r--r--examples/multimedia/spectrum/3rdparty/fftreal/fftreal_wrapper.h8
-rw-r--r--examples/multimedia/spectrum/CMakeLists.txt67
-rw-r--r--examples/multimedia/spectrum/Info.plist.in43
-rw-r--r--examples/multimedia/spectrum/app.pro55
-rw-r--r--examples/multimedia/spectrum/app/.gitignore2
-rw-r--r--examples/multimedia/spectrum/app/app.pro86
-rw-r--r--examples/multimedia/spectrum/app/engine.cpp783
-rw-r--r--examples/multimedia/spectrum/app/frequencyspectrum.cpp99
-rw-r--r--examples/multimedia/spectrum/app/frequencyspectrum.h107
-rw-r--r--examples/multimedia/spectrum/app/levelmeter.cpp153
-rw-r--r--examples/multimedia/spectrum/app/levelmeter.h128
-rw-r--r--examples/multimedia/spectrum/app/main.cpp63
-rw-r--r--examples/multimedia/spectrum/app/mainwidget.h157
-rw-r--r--examples/multimedia/spectrum/app/progressbar.cpp150
-rw-r--r--examples/multimedia/spectrum/app/progressbar.h85
-rw-r--r--examples/multimedia/spectrum/app/settingsdialog.cpp157
-rw-r--r--examples/multimedia/spectrum/app/settingsdialog.h99
-rw-r--r--examples/multimedia/spectrum/app/spectrograph.h108
-rw-r--r--examples/multimedia/spectrum/app/spectrum.h140
-rw-r--r--examples/multimedia/spectrum/app/spectrumanalyser.cpp286
-rw-r--r--examples/multimedia/spectrum/app/spectrumanalyser.h206
-rw-r--r--examples/multimedia/spectrum/app/tonegenerator.cpp100
-rw-r--r--examples/multimedia/spectrum/app/tonegenerator.h68
-rw-r--r--examples/multimedia/spectrum/app/tonegeneratordialog.cpp154
-rw-r--r--examples/multimedia/spectrum/app/tonegeneratordialog.h94
-rw-r--r--examples/multimedia/spectrum/app/utils.cpp149
-rw-r--r--examples/multimedia/spectrum/app/utils.h122
-rw-r--r--examples/multimedia/spectrum/app/wavfile.cpp158
-rw-r--r--examples/multimedia/spectrum/app/wavfile.h76
-rw-r--r--examples/multimedia/spectrum/doc/src/spectrum.qdoc36
-rw-r--r--examples/multimedia/spectrum/engine.cpp750
-rw-r--r--examples/multimedia/spectrum/engine.h (renamed from examples/multimedia/spectrum/app/engine.h)163
-rw-r--r--examples/multimedia/spectrum/frequencyspectrum.cpp48
-rw-r--r--examples/multimedia/spectrum/frequencyspectrum.h60
-rw-r--r--examples/multimedia/spectrum/images/record.png (renamed from examples/multimedia/spectrum/app/images/record.png)bin466 -> 466 bytes
-rw-r--r--examples/multimedia/spectrum/images/settings.png (renamed from examples/multimedia/spectrum/app/images/settings.png)bin3649 -> 3649 bytes
-rw-r--r--examples/multimedia/spectrum/levelmeter.cpp103
-rw-r--r--examples/multimedia/spectrum/levelmeter.h80
-rw-r--r--examples/multimedia/spectrum/main.cpp17
-rw-r--r--examples/multimedia/spectrum/mainwidget.cpp (renamed from examples/multimedia/spectrum/app/mainwidget.cpp)258
-rw-r--r--examples/multimedia/spectrum/mainwidget.h105
-rw-r--r--examples/multimedia/spectrum/progressbar.cpp101
-rw-r--r--examples/multimedia/spectrum/progressbar.h38
-rw-r--r--examples/multimedia/spectrum/settingsdialog.cpp102
-rw-r--r--examples/multimedia/spectrum/settingsdialog.h52
-rw-r--r--examples/multimedia/spectrum/spectrograph.cpp (renamed from examples/multimedia/spectrum/app/spectrograph.cpp)93
-rw-r--r--examples/multimedia/spectrum/spectrograph.h62
-rw-r--r--examples/multimedia/spectrum/spectrum.h91
-rw-r--r--examples/multimedia/spectrum/spectrum.pri3
-rw-r--r--examples/multimedia/spectrum/spectrum.pro6
-rw-r--r--examples/multimedia/spectrum/spectrum.qrc (renamed from examples/multimedia/spectrum/app/spectrum.qrc)0
-rw-r--r--examples/multimedia/spectrum/spectrumanalyser.cpp233
-rw-r--r--examples/multimedia/spectrum/spectrumanalyser.h151
-rw-r--r--examples/multimedia/spectrum/tonegenerator.cpp54
-rw-r--r--examples/multimedia/spectrum/tonegenerator.h21
-rw-r--r--examples/multimedia/spectrum/tonegeneratordialog.cpp105
-rw-r--r--examples/multimedia/spectrum/tonegeneratordialog.h47
-rw-r--r--examples/multimedia/spectrum/utils.cpp68
-rw-r--r--examples/multimedia/spectrum/utils.h79
-rw-r--r--examples/multimedia/spectrum/waveform.cpp (renamed from examples/multimedia/spectrum/app/waveform.cpp)188
-rw-r--r--examples/multimedia/spectrum/waveform.h (renamed from examples/multimedia/spectrum/app/waveform.h)97
63 files changed, 2855 insertions, 4297 deletions
diff --git a/examples/multimedia/spectrum/3rdparty/fftreal/CMakeLists.txt b/examples/multimedia/spectrum/3rdparty/fftreal/CMakeLists.txt
new file mode 100644
index 000000000..6c2af7a83
--- /dev/null
+++ b/examples/multimedia/spectrum/3rdparty/fftreal/CMakeLists.txt
@@ -0,0 +1,27 @@
+find_package(Qt6 REQUIRED COMPONENTS Core)
+
+add_library(fftreal STATIC
+ Array.h Array.hpp
+ DynArray.h DynArray.hpp
+ FFTRealFixLen.h FFTRealFixLen.hpp
+ FFTRealFixLenParam.h
+ FFTRealPassDirect.h FFTRealPassDirect.hpp
+ FFTRealPassInverse.h FFTRealPassInverse.hpp
+ FFTRealSelect.h FFTRealSelect.hpp
+ FFTRealUseTrigo.h FFTRealUseTrigo.hpp
+ OscSinCos.h OscSinCos.hpp
+ def.h
+ fftreal_wrapper.cpp fftreal_wrapper.h
+)
+
+target_compile_definitions(fftreal PRIVATE
+ FFTREAL_LIBRARY
+ LOG_ENGINE
+ LOG_SPECTRUMANALYSER
+ SPECTRUM_ANALYSER_SEPARATE_THREAD
+ SUPERIMPOSE_PROGRESS_ON_WAVEFORM
+)
+
+target_link_libraries(fftreal PRIVATE Qt6::Core)
+
+target_include_directories(fftreal INTERFACE .)
diff --git a/examples/multimedia/spectrum/3rdparty/fftreal/fftreal.pro b/examples/multimedia/spectrum/3rdparty/fftreal/fftreal.pro
index b2c96f96c..0b36ee7d1 100644
--- a/examples/multimedia/spectrum/3rdparty/fftreal/fftreal.pro
+++ b/examples/multimedia/spectrum/3rdparty/fftreal/fftreal.pro
@@ -1,8 +1,7 @@
include(../../spectrum.pri)
-static: error(This library cannot be built for static linkage)
-
TEMPLATE = lib
+CONFIG += static
TARGET = fftreal
# FFTReal
@@ -24,18 +23,14 @@ HEADERS += Array.h \
OscSinCos.h \
OscSinCos.hpp \
def.h
-
+
# Wrapper used to export the required instantiation of the FFTRealFixLen template
HEADERS += fftreal_wrapper.h
SOURCES += fftreal_wrapper.cpp
DEFINES += FFTREAL_LIBRARY
-macx {
- CONFIG += lib_bundle
-} else {
- DESTDIR = ../..$${spectrum_build_dir}
-}
+DESTDIR = ../..$${spectrum_build_dir}
EXAMPLE_FILES = bwins/fftreal.def eabi/fftreal.def readme.txt license.txt
diff --git a/examples/multimedia/spectrum/3rdparty/fftreal/fftreal_wrapper.h b/examples/multimedia/spectrum/3rdparty/fftreal/fftreal_wrapper.h
index 2da3147ac..1b92d8e65 100644
--- a/examples/multimedia/spectrum/3rdparty/fftreal/fftreal_wrapper.h
+++ b/examples/multimedia/spectrum/3rdparty/fftreal/fftreal_wrapper.h
@@ -22,12 +22,6 @@
#include <QtCore/QtGlobal>
-#if defined(FFTREAL_LIBRARY)
-# define FFTREAL_EXPORT Q_DECL_EXPORT
-#else
-# define FFTREAL_EXPORT Q_DECL_IMPORT
-#endif
-
class FFTRealWrapperPrivate;
// Each pass of the FFT processes 2^X samples, where X is the
@@ -46,7 +40,7 @@ static const int FFTLengthPowerOfTwo = 12;
*
* See http://ldesoras.free.fr/prod.html
*/
-class FFTREAL_EXPORT FFTRealWrapper
+class FFTRealWrapper
{
public:
FFTRealWrapper();
diff --git a/examples/multimedia/spectrum/CMakeLists.txt b/examples/multimedia/spectrum/CMakeLists.txt
new file mode 100644
index 000000000..faabe9c27
--- /dev/null
+++ b/examples/multimedia/spectrum/CMakeLists.txt
@@ -0,0 +1,67 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(spectrum LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/multimedia/spectrum")
+
+find_package(Qt6 REQUIRED COMPONENTS Multimedia Widgets)
+
+add_subdirectory(3rdparty/fftreal)
+
+qt_add_executable(spectrum
+ engine.cpp engine.h
+ frequencyspectrum.cpp frequencyspectrum.h
+ levelmeter.cpp levelmeter.h
+ main.cpp
+ mainwidget.cpp mainwidget.h
+ progressbar.cpp progressbar.h
+ settingsdialog.cpp settingsdialog.h
+ spectrograph.cpp spectrograph.h
+ spectrum.h
+ spectrumanalyser.cpp spectrumanalyser.h
+ tonegenerator.cpp tonegenerator.h
+ tonegeneratordialog.cpp tonegeneratordialog.h
+ utils.cpp utils.h
+ waveform.cpp waveform.h
+)
+
+set_target_properties(spectrum PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+ MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in
+)
+
+target_compile_definitions(spectrum PRIVATE
+ LOG_ENGINE
+ LOG_SPECTRUMANALYSER
+ SPECTRUM_ANALYSER_SEPARATE_THREAD
+ SUPERIMPOSE_PROGRESS_ON_WAVEFORM
+)
+
+target_link_libraries(spectrum PRIVATE
+ Qt6::Multimedia
+ Qt6::Widgets
+ fftreal
+)
+
+qt_add_resources(spectrum "spectrum"
+ PREFIX
+ "/"
+ FILES
+ "images/record.png"
+ "images/settings.png"
+)
+
+install(TARGETS spectrum
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/multimedia/spectrum/Info.plist.in b/examples/multimedia/spectrum/Info.plist.in
new file mode 100644
index 000000000..d0b9ddd11
--- /dev/null
+++ b/examples/multimedia/spectrum/Info.plist.in
@@ -0,0 +1,43 @@
+<?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>Qt ${MACOSX_BUNDLE_BUNDLE_NAME} Example</string>
+ <key>CFBundleIdentifier</key>
+ <string>Qt ${MACOSX_BUNDLE_GUI_IDENTIFIER} Example</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>NSPrincipalClass</key>
+ <string>NSApplication</string>
+ <key>NSMicrophoneUsageDescription</key>
+ <string>Qt Multimedia Example</string>
+</dict>
+</plist>
diff --git a/examples/multimedia/spectrum/app.pro b/examples/multimedia/spectrum/app.pro
new file mode 100644
index 000000000..9688940bb
--- /dev/null
+++ b/examples/multimedia/spectrum/app.pro
@@ -0,0 +1,55 @@
+include(spectrum.pri)
+
+TEMPLATE = app
+
+TARGET = spectrum
+
+QT += multimedia widgets
+
+SOURCES += main.cpp \
+ engine.cpp \
+ frequencyspectrum.cpp \
+ levelmeter.cpp \
+ mainwidget.cpp \
+ progressbar.cpp \
+ settingsdialog.cpp \
+ spectrograph.cpp \
+ spectrumanalyser.cpp \
+ tonegenerator.cpp \
+ tonegeneratordialog.cpp \
+ utils.cpp \
+ waveform.cpp \
+
+HEADERS += engine.h \
+ frequencyspectrum.h \
+ levelmeter.h \
+ mainwidget.h \
+ progressbar.h \
+ settingsdialog.h \
+ spectrograph.h \
+ spectrum.h \
+ spectrumanalyser.h \
+ tonegenerator.h \
+ tonegeneratordialog.h \
+ utils.h \
+ waveform.h \
+
+fftreal_dir = 3rdparty/fftreal
+
+INCLUDEPATH += $${fftreal_dir}
+
+RESOURCES = spectrum.qrc
+
+LIBS += -L./$${spectrum_build_dir}
+android:LIBS += -lfftreal_$$QT_ARCH
+else:LIBS += -lfftreal
+
+target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/spectrum
+INSTALLS += target
+
+CONFIG += install_ok # Do not cargo-cult this!
+
+# Deployment
+
+DESTDIR = ./$${spectrum_build_dir}
+include(../shared/shared.pri)
diff --git a/examples/multimedia/spectrum/app/.gitignore b/examples/multimedia/spectrum/app/.gitignore
deleted file mode 100644
index 82cf2a28f..000000000
--- a/examples/multimedia/spectrum/app/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-spectrum
-spectrum.exe
diff --git a/examples/multimedia/spectrum/app/app.pro b/examples/multimedia/spectrum/app/app.pro
deleted file mode 100644
index 726ce2d3f..000000000
--- a/examples/multimedia/spectrum/app/app.pro
+++ /dev/null
@@ -1,86 +0,0 @@
-include(../spectrum.pri)
-
-TEMPLATE = app
-
-TARGET = spectrum
-
-QT += multimedia widgets
-
-SOURCES += main.cpp \
- engine.cpp \
- frequencyspectrum.cpp \
- levelmeter.cpp \
- mainwidget.cpp \
- progressbar.cpp \
- settingsdialog.cpp \
- spectrograph.cpp \
- spectrumanalyser.cpp \
- tonegenerator.cpp \
- tonegeneratordialog.cpp \
- utils.cpp \
- waveform.cpp \
- wavfile.cpp
-
-HEADERS += engine.h \
- frequencyspectrum.h \
- levelmeter.h \
- mainwidget.h \
- progressbar.h \
- settingsdialog.h \
- spectrograph.h \
- spectrum.h \
- spectrumanalyser.h \
- tonegenerator.h \
- tonegeneratordialog.h \
- utils.h \
- waveform.h \
- wavfile.h
-
-fftreal_dir = ../3rdparty/fftreal
-
-INCLUDEPATH += $${fftreal_dir}
-
-RESOURCES = spectrum.qrc
-
-# Dynamic linkage against FFTReal DLL
-!contains(DEFINES, DISABLE_FFT) {
- macx {
- # Link to fftreal framework
- LIBS += -F$${fftreal_dir}
- LIBS += -framework fftreal
- } else {
- LIBS += -L..$${spectrum_build_dir}
- LIBS += -lfftreal
- }
-}
-
-target.path = $$[QT_INSTALL_EXAMPLES]/multimedia/spectrum
-INSTALLS += target
-
-CONFIG += install_ok # Do not cargo-cult this!
-
-# Deployment
-
-DESTDIR = ..$${spectrum_build_dir}
-macx {
- !contains(DEFINES, DISABLE_FFT) {
- # Relocate fftreal.framework into spectrum.app bundle
- framework_dir = ../spectrum.app/Contents/Frameworks
- framework_name = fftreal.framework/Versions/1/fftreal
- QMAKE_POST_LINK = \
- mkdir -p $${framework_dir} &&\
- rm -rf $${framework_dir}/fftreal.framework &&\
- cp -R $${fftreal_dir}/fftreal.framework $${framework_dir} &&\
- install_name_tool -id @executable_path/../Frameworks/$${framework_name} \
- $${framework_dir}/$${framework_name} &&\
- install_name_tool -change $${framework_name} \
- @executable_path/../Frameworks/$${framework_name} \
- ../spectrum.app/Contents/MacOS/spectrum
- }
-} else {
- linux-g++*: {
- # Provide relative path from application to fftreal library
- QMAKE_LFLAGS += -Wl,--rpath=\\\$\$ORIGIN
- }
-}
-include(../../shared/shared.pri)
diff --git a/examples/multimedia/spectrum/app/engine.cpp b/examples/multimedia/spectrum/app/engine.cpp
deleted file mode 100644
index f934f8788..000000000
--- a/examples/multimedia/spectrum/app/engine.cpp
+++ /dev/null
@@ -1,783 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "engine.h"
-#include "tonegenerator.h"
-#include "utils.h"
-
-#include <math.h>
-
-#include <QAudioInput>
-#include <QAudioOutput>
-#include <QCoreApplication>
-#include <QDebug>
-#include <QFile>
-#include <QMetaObject>
-#include <QSet>
-#include <QThread>
-
-//-----------------------------------------------------------------------------
-// Constants
-//-----------------------------------------------------------------------------
-
-const qint64 BufferDurationUs = 10 * 1000000;
-const int NotifyIntervalMs = 100;
-
-// Size of the level calculation window in microseconds
-const int LevelWindowUs = 0.1 * 1000000;
-
-//-----------------------------------------------------------------------------
-// Constructor and destructor
-//-----------------------------------------------------------------------------
-
-Engine::Engine(QObject *parent)
- : QObject(parent)
- , m_mode(QAudio::AudioInput)
- , m_state(QAudio::StoppedState)
- , m_generateTone(false)
- , m_file(0)
- , m_analysisFile(0)
- , m_availableAudioInputDevices
- (QAudioDeviceInfo::availableDevices(QAudio::AudioInput))
- , m_audioInputDevice(QAudioDeviceInfo::defaultInputDevice())
- , m_audioInput(0)
- , m_audioInputIODevice(0)
- , m_recordPosition(0)
- , m_availableAudioOutputDevices
- (QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))
- , m_audioOutputDevice(QAudioDeviceInfo::defaultOutputDevice())
- , m_audioOutput(0)
- , m_playPosition(0)
- , m_bufferPosition(0)
- , m_bufferLength(0)
- , m_dataLength(0)
- , m_levelBufferLength(0)
- , m_rmsLevel(0.0)
- , m_peakLevel(0.0)
- , m_spectrumBufferLength(0)
- , m_spectrumAnalyser()
- , m_spectrumPosition(0)
- , m_count(0)
-{
- qRegisterMetaType<FrequencySpectrum>("FrequencySpectrum");
- qRegisterMetaType<WindowFunction>("WindowFunction");
- connect(&m_spectrumAnalyser, QOverload<const FrequencySpectrum&>::of(&SpectrumAnalyser::spectrumChanged),
- this, QOverload<const FrequencySpectrum&>::of(&Engine::spectrumChanged));
-
- // This code might misinterpret things like "-something -category". But
- // it's unlikely that that needs to be supported so we'll let it go.
- QStringList arguments = QCoreApplication::instance()->arguments();
- for (int i = 0; i < arguments.count(); ++i) {
- if (arguments.at(i) == QStringLiteral("--"))
- break;
-
- if (arguments.at(i) == QStringLiteral("-category")
- || arguments.at(i) == QStringLiteral("--category")) {
- ++i;
- if (i < arguments.count())
- m_audioOutputCategory = arguments.at(i);
- else
- --i;
- }
- }
-
- initialize();
-
-#ifdef DUMP_DATA
- createOutputDir();
-#endif
-
-#ifdef DUMP_SPECTRUM
- m_spectrumAnalyser.setOutputPath(outputPath());
-#endif
-}
-
-Engine::~Engine()
-{
-
-}
-
-//-----------------------------------------------------------------------------
-// Public functions
-//-----------------------------------------------------------------------------
-
-bool Engine::loadFile(const QString &fileName)
-{
- reset();
- bool result = false;
- Q_ASSERT(!m_generateTone);
- Q_ASSERT(!m_file);
- Q_ASSERT(!fileName.isEmpty());
- m_file = new WavFile(this);
- if (m_file->open(fileName)) {
- if (isPCMS16LE(m_file->fileFormat())) {
- result = initialize();
- } else {
- emit errorMessage(tr("Audio format not supported"),
- formatToString(m_file->fileFormat()));
- }
- } else {
- emit errorMessage(tr("Could not open file"), fileName);
- }
- if (result) {
- m_analysisFile = new WavFile(this);
- m_analysisFile->open(fileName);
- }
- return result;
-}
-
-bool Engine::generateTone(const Tone &tone)
-{
- reset();
- Q_ASSERT(!m_generateTone);
- Q_ASSERT(!m_file);
- m_generateTone = true;
- m_tone = tone;
- ENGINE_DEBUG << "Engine::generateTone"
- << "startFreq" << m_tone.startFreq
- << "endFreq" << m_tone.endFreq
- << "amp" << m_tone.amplitude;
- return initialize();
-}
-
-bool Engine::generateSweptTone(qreal amplitude)
-{
- Q_ASSERT(!m_generateTone);
- Q_ASSERT(!m_file);
- m_generateTone = true;
- m_tone.startFreq = 1;
- m_tone.endFreq = 0;
- m_tone.amplitude = amplitude;
- ENGINE_DEBUG << "Engine::generateSweptTone"
- << "startFreq" << m_tone.startFreq
- << "amp" << m_tone.amplitude;
- return initialize();
-}
-
-bool Engine::initializeRecord()
-{
- reset();
- ENGINE_DEBUG << "Engine::initializeRecord";
- Q_ASSERT(!m_generateTone);
- Q_ASSERT(!m_file);
- m_generateTone = false;
- m_tone = SweptTone();
- return initialize();
-}
-
-qint64 Engine::bufferLength() const
-{
- return m_file ? m_file->size() : m_bufferLength;
-}
-
-void Engine::setWindowFunction(WindowFunction type)
-{
- m_spectrumAnalyser.setWindowFunction(type);
-}
-
-
-//-----------------------------------------------------------------------------
-// Public slots
-//-----------------------------------------------------------------------------
-
-void Engine::startRecording()
-{
- if (m_audioInput) {
- if (QAudio::AudioInput == m_mode &&
- QAudio::SuspendedState == m_state) {
- m_audioInput->resume();
- } else {
- m_spectrumAnalyser.cancelCalculation();
- spectrumChanged(0, 0, FrequencySpectrum());
-
- m_buffer.fill(0);
- setRecordPosition(0, true);
- stopPlayback();
- m_mode = QAudio::AudioInput;
- connect(m_audioInput, &QAudioInput::stateChanged,
- this, &Engine::audioStateChanged);
- connect(m_audioInput, &QAudioInput::notify,
- this, &Engine::audioNotify);
-
- m_count = 0;
- m_dataLength = 0;
- emit dataLengthChanged(0);
- m_audioInputIODevice = m_audioInput->start();
- connect(m_audioInputIODevice, &QIODevice::readyRead,
- this, &Engine::audioDataReady);
- }
- }
-}
-
-void Engine::startPlayback()
-{
- if (m_audioOutput) {
- if (QAudio::AudioOutput == m_mode &&
- QAudio::SuspendedState == m_state) {
-#ifdef Q_OS_WIN
- // The Windows backend seems to internally go back into ActiveState
- // while still returning SuspendedState, so to ensure that it doesn't
- // ignore the resume() call, we first re-suspend
- m_audioOutput->suspend();
-#endif
- m_audioOutput->resume();
- } else {
- m_spectrumAnalyser.cancelCalculation();
- spectrumChanged(0, 0, FrequencySpectrum());
- setPlayPosition(0, true);
- stopRecording();
- m_mode = QAudio::AudioOutput;
- connect(m_audioOutput, &QAudioOutput::stateChanged,
- this, &Engine::audioStateChanged);
- connect(m_audioOutput, &QAudioOutput::notify,
- this, &Engine::audioNotify);
-
- m_count = 0;
- if (m_file) {
- m_file->seek(0);
- m_bufferPosition = 0;
- m_dataLength = 0;
- m_audioOutput->start(m_file);
- } else {
- m_audioOutputIODevice.close();
- m_audioOutputIODevice.setBuffer(&m_buffer);
- m_audioOutputIODevice.open(QIODevice::ReadOnly);
- m_audioOutput->start(&m_audioOutputIODevice);
- }
- }
- }
-}
-
-void Engine::suspend()
-{
- if (QAudio::ActiveState == m_state ||
- QAudio::IdleState == m_state) {
- switch (m_mode) {
- case QAudio::AudioInput:
- m_audioInput->suspend();
- break;
- case QAudio::AudioOutput:
- m_audioOutput->suspend();
- break;
- }
- }
-}
-
-void Engine::setAudioInputDevice(const QAudioDeviceInfo &device)
-{
- if (device.deviceName() != m_audioInputDevice.deviceName()) {
- m_audioInputDevice = device;
- initialize();
- }
-}
-
-void Engine::setAudioOutputDevice(const QAudioDeviceInfo &device)
-{
- if (device.deviceName() != m_audioOutputDevice.deviceName()) {
- m_audioOutputDevice = device;
- initialize();
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Private slots
-//-----------------------------------------------------------------------------
-
-void Engine::audioNotify()
-{
- switch (m_mode) {
- case QAudio::AudioInput: {
- const qint64 recordPosition = qMin(m_bufferLength, audioLength(m_format, m_audioInput->processedUSecs()));
- setRecordPosition(recordPosition);
- const qint64 levelPosition = m_dataLength - m_levelBufferLength;
- if (levelPosition >= 0)
- calculateLevel(levelPosition, m_levelBufferLength);
- if (m_dataLength >= m_spectrumBufferLength) {
- const qint64 spectrumPosition = m_dataLength - m_spectrumBufferLength;
- calculateSpectrum(spectrumPosition);
- }
- emit bufferChanged(0, m_dataLength, m_buffer);
- }
- break;
- case QAudio::AudioOutput: {
- const qint64 playPosition = audioLength(m_format, m_audioOutput->processedUSecs());
- setPlayPosition(qMin(bufferLength(), playPosition));
- const qint64 levelPosition = playPosition - m_levelBufferLength;
- const qint64 spectrumPosition = playPosition - m_spectrumBufferLength;
- if (m_file) {
- if (levelPosition > m_bufferPosition ||
- spectrumPosition > m_bufferPosition ||
- qMax(m_levelBufferLength, m_spectrumBufferLength) > m_dataLength) {
- m_bufferPosition = 0;
- m_dataLength = 0;
- // Data needs to be read into m_buffer in order to be analysed
- const qint64 readPos = qMax(qint64(0), qMin(levelPosition, spectrumPosition));
- const qint64 readEnd = qMin(m_analysisFile->size(), qMax(levelPosition + m_levelBufferLength, spectrumPosition + m_spectrumBufferLength));
- const qint64 readLen = readEnd - readPos + audioLength(m_format, WaveformWindowDuration);
- qDebug() << "Engine::audioNotify [1]"
- << "analysisFileSize" << m_analysisFile->size()
- << "readPos" << readPos
- << "readLen" << readLen;
- if (m_analysisFile->seek(readPos + m_analysisFile->headerLength())) {
- m_buffer.resize(readLen);
- m_bufferPosition = readPos;
- m_dataLength = m_analysisFile->read(m_buffer.data(), readLen);
- qDebug() << "Engine::audioNotify [2]" << "bufferPosition" << m_bufferPosition << "dataLength" << m_dataLength;
- } else {
- qDebug() << "Engine::audioNotify [2]" << "file seek error";
- }
- emit bufferChanged(m_bufferPosition, m_dataLength, m_buffer);
- }
- } else {
- if (playPosition >= m_dataLength)
- stopPlayback();
- }
- if (levelPosition >= 0 && levelPosition + m_levelBufferLength < m_bufferPosition + m_dataLength)
- calculateLevel(levelPosition, m_levelBufferLength);
- if (spectrumPosition >= 0 && spectrumPosition + m_spectrumBufferLength < m_bufferPosition + m_dataLength)
- calculateSpectrum(spectrumPosition);
- }
- break;
- }
-}
-
-void Engine::audioStateChanged(QAudio::State state)
-{
- ENGINE_DEBUG << "Engine::audioStateChanged from" << m_state
- << "to" << state;
-
- if (QAudio::IdleState == state && m_file && m_file->pos() == m_file->size()) {
- stopPlayback();
- } else {
- if (QAudio::StoppedState == state) {
- // Check error
- QAudio::Error error = QAudio::NoError;
- switch (m_mode) {
- case QAudio::AudioInput:
- error = m_audioInput->error();
- break;
- case QAudio::AudioOutput:
- error = m_audioOutput->error();
- break;
- }
- if (QAudio::NoError != error) {
- reset();
- return;
- }
- }
- setState(state);
- }
-}
-
-void Engine::audioDataReady()
-{
- Q_ASSERT(0 == m_bufferPosition);
- const qint64 bytesReady = m_audioInput->bytesReady();
- const qint64 bytesSpace = m_buffer.size() - m_dataLength;
- const qint64 bytesToRead = qMin(bytesReady, bytesSpace);
-
- const qint64 bytesRead = m_audioInputIODevice->read(
- m_buffer.data() + m_dataLength,
- bytesToRead);
-
- if (bytesRead) {
- m_dataLength += bytesRead;
- emit dataLengthChanged(dataLength());
- }
-
- if (m_buffer.size() == m_dataLength)
- stopRecording();
-}
-
-void Engine::spectrumChanged(const FrequencySpectrum &spectrum)
-{
- ENGINE_DEBUG << "Engine::spectrumChanged" << "pos" << m_spectrumPosition;
- emit spectrumChanged(m_spectrumPosition, m_spectrumBufferLength, spectrum);
-}
-
-
-//-----------------------------------------------------------------------------
-// Private functions
-//-----------------------------------------------------------------------------
-
-void Engine::resetAudioDevices()
-{
- delete m_audioInput;
- m_audioInput = 0;
- m_audioInputIODevice = 0;
- setRecordPosition(0);
- delete m_audioOutput;
- m_audioOutput = 0;
- setPlayPosition(0);
- m_spectrumPosition = 0;
- setLevel(0.0, 0.0, 0);
-}
-
-void Engine::reset()
-{
- stopRecording();
- stopPlayback();
- setState(QAudio::AudioInput, QAudio::StoppedState);
- setFormat(QAudioFormat());
- m_generateTone = false;
- delete m_file;
- m_file = 0;
- delete m_analysisFile;
- m_analysisFile = 0;
- m_buffer.clear();
- m_bufferPosition = 0;
- m_bufferLength = 0;
- m_dataLength = 0;
- emit dataLengthChanged(0);
- resetAudioDevices();
-}
-
-bool Engine::initialize()
-{
- bool result = false;
-
- QAudioFormat format = m_format;
-
- if (selectFormat()) {
- if (m_format != format) {
- resetAudioDevices();
- if (m_file) {
- emit bufferLengthChanged(bufferLength());
- emit dataLengthChanged(dataLength());
- emit bufferChanged(0, 0, m_buffer);
- setRecordPosition(bufferLength());
- result = true;
- } else {
- m_bufferLength = audioLength(m_format, BufferDurationUs);
- m_buffer.resize(m_bufferLength);
- m_buffer.fill(0);
- emit bufferLengthChanged(bufferLength());
- if (m_generateTone) {
- if (0 == m_tone.endFreq) {
- const qreal nyquist = nyquistFrequency(m_format);
- m_tone.endFreq = qMin(qreal(SpectrumHighFreq), nyquist);
- }
- // Call function defined in utils.h, at global scope
- ::generateTone(m_tone, m_format, m_buffer);
- m_dataLength = m_bufferLength;
- emit dataLengthChanged(dataLength());
- emit bufferChanged(0, m_dataLength, m_buffer);
- setRecordPosition(m_bufferLength);
- result = true;
- } else {
- emit bufferChanged(0, 0, m_buffer);
- m_audioInput = new QAudioInput(m_audioInputDevice, m_format, this);
- m_audioInput->setNotifyInterval(NotifyIntervalMs);
- result = true;
- }
- }
- m_audioOutput = new QAudioOutput(m_audioOutputDevice, m_format, this);
- m_audioOutput->setNotifyInterval(NotifyIntervalMs);
- m_audioOutput->setCategory(m_audioOutputCategory);
- }
- } else {
- if (m_file)
- emit errorMessage(tr("Audio format not supported"),
- formatToString(m_format));
- else if (m_generateTone)
- emit errorMessage(tr("No suitable format found"), "");
- else
- emit errorMessage(tr("No common input / output format found"), "");
- }
-
- ENGINE_DEBUG << "Engine::initialize" << "m_bufferLength" << m_bufferLength;
- ENGINE_DEBUG << "Engine::initialize" << "m_dataLength" << m_dataLength;
- ENGINE_DEBUG << "Engine::initialize" << "format" << m_format;
- ENGINE_DEBUG << "Engine::initialize" << "m_audioOutputCategory" << m_audioOutputCategory;
-
- return result;
-}
-
-bool Engine::selectFormat()
-{
- bool foundSupportedFormat = false;
-
- if (m_file || QAudioFormat() != m_format) {
- QAudioFormat format = m_format;
- if (m_file)
- // Header is read from the WAV file; just need to check whether
- // it is supported by the audio output device
- format = m_file->fileFormat();
- if (m_audioOutputDevice.isFormatSupported(format)) {
- setFormat(format);
- foundSupportedFormat = true;
- }
- } else {
-
- QList<int> sampleRatesList;
- #ifdef Q_OS_WIN
- // The Windows audio backend does not correctly report format support
- // (see QTBUG-9100). Furthermore, although the audio subsystem captures
- // at 11025Hz, the resulting audio is corrupted.
- sampleRatesList += 8000;
- #endif
-
- if (!m_generateTone)
- sampleRatesList += m_audioInputDevice.supportedSampleRates();
-
- sampleRatesList += m_audioOutputDevice.supportedSampleRates();
- std::sort(sampleRatesList.begin(), sampleRatesList.end());
- const auto uniqueRatesEnd = std::unique(sampleRatesList.begin(), sampleRatesList.end());
- sampleRatesList.erase(uniqueRatesEnd, sampleRatesList.end());
- ENGINE_DEBUG << "Engine::initialize frequenciesList" << sampleRatesList;
-
- QList<int> channelsList;
- channelsList += m_audioInputDevice.supportedChannelCounts();
- channelsList += m_audioOutputDevice.supportedChannelCounts();
- std::sort(channelsList.begin(), channelsList.end());
- const auto uniqueChannelsEnd = std::unique(channelsList.begin(), channelsList.end());
- channelsList.erase(uniqueChannelsEnd, channelsList.end());
- ENGINE_DEBUG << "Engine::initialize channelsList" << channelsList;
-
- QAudioFormat format;
- format.setByteOrder(QAudioFormat::LittleEndian);
- format.setCodec("audio/pcm");
- format.setSampleSize(16);
- format.setSampleType(QAudioFormat::SignedInt);
- for (int sampleRate : qAsConst(sampleRatesList)) {
- if (foundSupportedFormat)
- break;
- format.setSampleRate(sampleRate);
- for (int channels : qAsConst(channelsList)) {
- format.setChannelCount(channels);
- const bool inputSupport = m_generateTone ||
- m_audioInputDevice.isFormatSupported(format);
- const bool outputSupport = m_audioOutputDevice.isFormatSupported(format);
- ENGINE_DEBUG << "Engine::initialize checking " << format
- << "input" << inputSupport
- << "output" << outputSupport;
- if (inputSupport && outputSupport) {
- foundSupportedFormat = true;
- break;
- }
- }
- }
-
- if (!foundSupportedFormat)
- format = QAudioFormat();
-
- setFormat(format);
- }
-
- return foundSupportedFormat;
-}
-
-void Engine::stopRecording()
-{
- if (m_audioInput) {
- m_audioInput->stop();
- QCoreApplication::instance()->processEvents();
- m_audioInput->disconnect();
- }
- m_audioInputIODevice = 0;
-
-#ifdef DUMP_AUDIO
- dumpData();
-#endif
-}
-
-void Engine::stopPlayback()
-{
- if (m_audioOutput) {
- m_audioOutput->stop();
- QCoreApplication::instance()->processEvents();
- m_audioOutput->disconnect();
- setPlayPosition(0);
- }
-}
-
-void Engine::setState(QAudio::State state)
-{
- const bool changed = (m_state != state);
- m_state = state;
- if (changed)
- emit stateChanged(m_mode, m_state);
-}
-
-void Engine::setState(QAudio::Mode mode, QAudio::State state)
-{
- const bool changed = (m_mode != mode || m_state != state);
- m_mode = mode;
- m_state = state;
- if (changed)
- emit stateChanged(m_mode, m_state);
-}
-
-void Engine::setRecordPosition(qint64 position, bool forceEmit)
-{
- const bool changed = (m_recordPosition != position);
- m_recordPosition = position;
- if (changed || forceEmit)
- emit recordPositionChanged(m_recordPosition);
-}
-
-void Engine::setPlayPosition(qint64 position, bool forceEmit)
-{
- const bool changed = (m_playPosition != position);
- m_playPosition = position;
- if (changed || forceEmit)
- emit playPositionChanged(m_playPosition);
-}
-
-void Engine::calculateLevel(qint64 position, qint64 length)
-{
-#ifdef DISABLE_LEVEL
- Q_UNUSED(position);
- Q_UNUSED(length);
-#else
- Q_ASSERT(position + length <= m_bufferPosition + m_dataLength);
-
- qreal peakLevel = 0.0;
-
- qreal sum = 0.0;
- const char *ptr = m_buffer.constData() + position - m_bufferPosition;
- const char *const end = ptr + length;
- while (ptr < end) {
- const qint16 value = *reinterpret_cast<const qint16*>(ptr);
- const qreal fracValue = pcmToReal(value);
- peakLevel = qMax(peakLevel, fracValue);
- sum += fracValue * fracValue;
- ptr += 2;
- }
- const int numSamples = length / 2;
- qreal rmsLevel = sqrt(sum / numSamples);
-
- rmsLevel = qMax(qreal(0.0), rmsLevel);
- rmsLevel = qMin(qreal(1.0), rmsLevel);
- setLevel(rmsLevel, peakLevel, numSamples);
-
- ENGINE_DEBUG << "Engine::calculateLevel" << "pos" << position << "len" << length
- << "rms" << rmsLevel << "peak" << peakLevel;
-#endif
-}
-
-void Engine::calculateSpectrum(qint64 position)
-{
-#ifdef DISABLE_SPECTRUM
- Q_UNUSED(position);
-#else
- Q_ASSERT(position + m_spectrumBufferLength <= m_bufferPosition + m_dataLength);
- Q_ASSERT(0 == m_spectrumBufferLength % 2); // constraint of FFT algorithm
-
- // QThread::currentThread is marked 'for internal use only', but
- // we're only using it for debug output here, so it's probably OK :)
- ENGINE_DEBUG << "Engine::calculateSpectrum" << QThread::currentThread()
- << "count" << m_count << "pos" << position << "len" << m_spectrumBufferLength
- << "spectrumAnalyser.isReady" << m_spectrumAnalyser.isReady();
-
- if (m_spectrumAnalyser.isReady()) {
- m_spectrumBuffer = QByteArray::fromRawData(m_buffer.constData() + position - m_bufferPosition,
- m_spectrumBufferLength);
- m_spectrumPosition = position;
- m_spectrumAnalyser.calculate(m_spectrumBuffer, m_format);
- }
-#endif
-}
-
-void Engine::setFormat(const QAudioFormat &format)
-{
- const bool changed = (format != m_format);
- m_format = format;
- m_levelBufferLength = audioLength(m_format, LevelWindowUs);
- m_spectrumBufferLength = SpectrumLengthSamples *
- (m_format.sampleSize() / 8) * m_format.channelCount();
- if (changed)
- emit formatChanged(m_format);
-}
-
-void Engine::setLevel(qreal rmsLevel, qreal peakLevel, int numSamples)
-{
- m_rmsLevel = rmsLevel;
- m_peakLevel = peakLevel;
- emit levelChanged(m_rmsLevel, m_peakLevel, numSamples);
-}
-
-#ifdef DUMP_DATA
-void Engine::createOutputDir()
-{
- m_outputDir.setPath("output");
-
- // Ensure output directory exists and is empty
- if (m_outputDir.exists()) {
- const QStringList files = m_outputDir.entryList(QDir::Files);
- for (const QString &file : files)
- m_outputDir.remove(file);
- } else {
- QDir::current().mkdir("output");
- }
-}
-#endif // DUMP_DATA
-
-#ifdef DUMP_AUDIO
-void Engine::dumpData()
-{
- const QString txtFileName = m_outputDir.filePath("data.txt");
- QFile txtFile(txtFileName);
- txtFile.open(QFile::WriteOnly | QFile::Text);
- QTextStream stream(&txtFile);
- const qint16 *ptr = reinterpret_cast<const qint16*>(m_buffer.constData());
- const int numSamples = m_dataLength / (2 * m_format.channels());
- for (int i=0; i<numSamples; ++i) {
- stream << i << "\t" << *ptr << "\n";
- ptr += m_format.channels();
- }
-
- const QString pcmFileName = m_outputDir.filePath("data.pcm");
- QFile pcmFile(pcmFileName);
- pcmFile.open(QFile::WriteOnly);
- pcmFile.write(m_buffer.constData(), m_dataLength);
-}
-#endif // DUMP_AUDIO
diff --git a/examples/multimedia/spectrum/app/frequencyspectrum.cpp b/examples/multimedia/spectrum/app/frequencyspectrum.cpp
deleted file mode 100644
index abec65d23..000000000
--- a/examples/multimedia/spectrum/app/frequencyspectrum.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "frequencyspectrum.h"
-
-FrequencySpectrum::FrequencySpectrum(int numPoints)
- : m_elements(numPoints)
-{
-
-}
-
-void FrequencySpectrum::reset()
-{
- iterator i = begin();
- for ( ; i != end(); ++i)
- *i = Element();
-}
-
-int FrequencySpectrum::count() const
-{
- return m_elements.count();
-}
-
-FrequencySpectrum::Element &FrequencySpectrum::operator[](int index)
-{
- return m_elements[index];
-}
-
-const FrequencySpectrum::Element &FrequencySpectrum::operator[](int index) const
-{
- return m_elements[index];
-}
-
-FrequencySpectrum::iterator FrequencySpectrum::begin()
-{
- return m_elements.begin();
-}
-
-FrequencySpectrum::iterator FrequencySpectrum::end()
-{
- return m_elements.end();
-}
-
-FrequencySpectrum::const_iterator FrequencySpectrum::begin() const
-{
- return m_elements.begin();
-}
-
-FrequencySpectrum::const_iterator FrequencySpectrum::end() const
-{
- return m_elements.end();
-}
diff --git a/examples/multimedia/spectrum/app/frequencyspectrum.h b/examples/multimedia/spectrum/app/frequencyspectrum.h
deleted file mode 100644
index 4b13cbb99..000000000
--- a/examples/multimedia/spectrum/app/frequencyspectrum.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef FREQUENCYSPECTRUM_H
-#define FREQUENCYSPECTRUM_H
-
-#include <QtCore/QList>
-
-/**
- * Represents a frequency spectrum as a series of elements, each of which
- * consists of a frequency, an amplitude and a phase.
- */
-class FrequencySpectrum {
-public:
- FrequencySpectrum(int numPoints = 0);
-
- struct Element {
- Element()
- : frequency(0.0), amplitude(0.0), phase(0.0), clipped(false)
- { }
-
- /**
- * Frequency in Hertz
- */
- qreal frequency;
-
- /**
- * Amplitude in range [0.0, 1.0]
- */
- qreal amplitude;
-
- /**
- * Phase in range [0.0, 2*PI]
- */
- qreal phase;
-
- /**
- * Indicates whether value has been clipped during spectrum analysis
- */
- bool clipped;
- };
-
- typedef QList<Element>::iterator iterator;
- typedef QList<Element>::const_iterator const_iterator;
-
- void reset();
-
- int count() const;
- Element& operator[](int index);
- const Element& operator[](int index) const;
- iterator begin();
- iterator end();
- const_iterator begin() const;
- const_iterator end() const;
-
-private:
- QList<Element> m_elements;
-};
-
-#endif // FREQUENCYSPECTRUM_H
diff --git a/examples/multimedia/spectrum/app/levelmeter.cpp b/examples/multimedia/spectrum/app/levelmeter.cpp
deleted file mode 100644
index f0c66eeb3..000000000
--- a/examples/multimedia/spectrum/app/levelmeter.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "levelmeter.h"
-
-#include <math.h>
-
-#include <QPainter>
-#include <QTimer>
-#include <QDebug>
-
-
-// Constants
-const int RedrawInterval = 100; // ms
-const qreal PeakDecayRate = 0.001;
-const int PeakHoldLevelDuration = 2000; // ms
-
-
-LevelMeter::LevelMeter(QWidget *parent)
- : QWidget(parent)
- , m_rmsLevel(0.0)
- , m_peakLevel(0.0)
- , m_decayedPeakLevel(0.0)
- , m_peakDecayRate(PeakDecayRate)
- , m_peakHoldLevel(0.0)
- , m_redrawTimer(new QTimer(this))
- , m_rmsColor(Qt::red)
- , m_peakColor(255, 200, 200, 255)
-{
- setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
- setMinimumWidth(30);
-
- connect(m_redrawTimer, &QTimer::timeout,
- this, &LevelMeter::redrawTimerExpired);
- m_redrawTimer->start(RedrawInterval);
-}
-
-LevelMeter::~LevelMeter()
-{
-
-}
-
-void LevelMeter::reset()
-{
- m_rmsLevel = 0.0;
- m_peakLevel = 0.0;
- update();
-}
-
-void LevelMeter::levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples)
-{
- // Smooth the RMS signal
- const qreal smooth = pow(qreal(0.9), static_cast<qreal>(numSamples) / 256); // TODO: remove this magic number
- m_rmsLevel = (m_rmsLevel * smooth) + (rmsLevel * (1.0 - smooth));
-
- if (peakLevel > m_decayedPeakLevel) {
- m_peakLevel = peakLevel;
- m_decayedPeakLevel = peakLevel;
- m_peakLevelChanged.start();
- }
-
- if (peakLevel > m_peakHoldLevel) {
- m_peakHoldLevel = peakLevel;
- m_peakHoldLevelChanged.start();
- }
-
- update();
-}
-
-void LevelMeter::redrawTimerExpired()
-{
- // Decay the peak signal
- const int elapsedMs = m_peakLevelChanged.elapsed();
- const qreal decayAmount = m_peakDecayRate * elapsedMs;
- if (decayAmount < m_peakLevel)
- m_decayedPeakLevel = m_peakLevel - decayAmount;
- else
- m_decayedPeakLevel = 0.0;
-
- // Check whether to clear the peak hold level
- if (m_peakHoldLevelChanged.elapsed() > PeakHoldLevelDuration)
- m_peakHoldLevel = 0.0;
-
- update();
-}
-
-void LevelMeter::paintEvent(QPaintEvent *event)
-{
- Q_UNUSED(event);
-
- QPainter painter(this);
- painter.fillRect(rect(), Qt::black);
-
- QRect bar = rect();
-
- bar.setTop(rect().top() + (1.0 - m_peakHoldLevel) * rect().height());
- bar.setBottom(bar.top() + 5);
- painter.fillRect(bar, m_rmsColor);
- bar.setBottom(rect().bottom());
-
- bar.setTop(rect().top() + (1.0 - m_decayedPeakLevel) * rect().height());
- painter.fillRect(bar, m_peakColor);
-
- bar.setTop(rect().top() + (1.0 - m_rmsLevel) * rect().height());
- painter.fillRect(bar, m_rmsColor);
-}
diff --git a/examples/multimedia/spectrum/app/levelmeter.h b/examples/multimedia/spectrum/app/levelmeter.h
deleted file mode 100644
index 987f90e8f..000000000
--- a/examples/multimedia/spectrum/app/levelmeter.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef LEVELMETER_H
-#define LEVELMETER_H
-
-#include <QElapsedTimer>
-#include <QWidget>
-
-/**
- * Widget which displays a vertical audio level meter, indicating the
- * RMS and peak levels of the window of audio samples most recently analyzed
- * by the Engine.
- */
-class LevelMeter : public QWidget
-{
- Q_OBJECT
-
-public:
- explicit LevelMeter(QWidget *parent = 0);
- ~LevelMeter();
-
- void paintEvent(QPaintEvent *event) override;
-
-public slots:
- void reset();
- void levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples);
-
-private slots:
- void redrawTimerExpired();
-
-private:
- /**
- * Height of RMS level bar.
- * Range 0.0 - 1.0.
- */
- qreal m_rmsLevel;
-
- /**
- * Most recent peak level.
- * Range 0.0 - 1.0.
- */
- qreal m_peakLevel;
-
- /**
- * Height of peak level bar.
- * This is calculated by decaying m_peakLevel depending on the
- * elapsed time since m_peakLevelChanged, and the value of m_decayRate.
- */
- qreal m_decayedPeakLevel;
-
- /**
- * Time at which m_peakLevel was last changed.
- */
- QElapsedTimer m_peakLevelChanged;
-
- /**
- * Rate at which peak level bar decays.
- * Expressed in level units / millisecond.
- */
- qreal m_peakDecayRate;
-
- /**
- * High watermark of peak level.
- * Range 0.0 - 1.0.
- */
- qreal m_peakHoldLevel;
-
- /**
- * Time at which m_peakHoldLevel was last changed.
- */
- QElapsedTimer m_peakHoldLevelChanged;
-
- QTimer *m_redrawTimer;
-
- QColor m_rmsColor;
- QColor m_peakColor;
-
-};
-
-#endif // LEVELMETER_H
diff --git a/examples/multimedia/spectrum/app/main.cpp b/examples/multimedia/spectrum/app/main.cpp
deleted file mode 100644
index 7eeb94949..000000000
--- a/examples/multimedia/spectrum/app/main.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "mainwidget.h"
-#include <QApplication>
-
-int main(int argc, char *argv[])
-{
- QApplication app(argc, argv);
- app.setApplicationName("Qt Multimedia spectrum analyzer");
-
- MainWidget w;
- w.show();
-
- return app.exec();
-}
diff --git a/examples/multimedia/spectrum/app/mainwidget.h b/examples/multimedia/spectrum/app/mainwidget.h
deleted file mode 100644
index 2ff863a13..000000000
--- a/examples/multimedia/spectrum/app/mainwidget.h
+++ /dev/null
@@ -1,157 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef MAINWIDGET_H
-#define MAINWIDGET_H
-
-#include <QAudio>
-#include <QIcon>
-#include <QWidget>
-
-class Engine;
-class FrequencySpectrum;
-class LevelMeter;
-class ProgressBar;
-class SettingsDialog;
-class Spectrograph;
-class ToneGeneratorDialog;
-class Waveform;
-
-QT_BEGIN_NAMESPACE
-class QAction;
-class QAudioFormat;
-class QLabel;
-class QMenu;
-class QPushButton;
-QT_END_NAMESPACE
-
-/**
- * Main application widget, responsible for connecting the various UI
- * elements to the Engine.
- */
-class MainWidget : public QWidget
-{
- Q_OBJECT
-
-public:
- explicit MainWidget(QWidget *parent = 0);
- ~MainWidget();
-
- // QObject
- void timerEvent(QTimerEvent *event) override;
-
-public slots:
- void stateChanged(QAudio::Mode mode, QAudio::State state);
- void formatChanged(const QAudioFormat &format);
- void spectrumChanged(qint64 position, qint64 length,
- const FrequencySpectrum &spectrum);
- void infoMessage(const QString &message, int timeoutMs);
- void errorMessage(const QString &heading, const QString &detail);
- void audioPositionChanged(qint64 position);
- void bufferLengthChanged(qint64 length);
-
-private slots:
- void showFileDialog();
- void showSettingsDialog();
- void showToneGeneratorDialog();
- void initializeRecord();
- void updateModeMenu();
- void updateButtonStates();
-
-private:
- void createUi();
- void createMenus();
- void connectUi();
- void reset();
-
- enum Mode {
- NoMode,
- RecordMode,
- GenerateToneMode,
- LoadFileMode
- };
-
- void setMode(Mode mode);
-
-private:
- Mode m_mode;
-
- Engine* m_engine;
-
-#ifndef DISABLE_WAVEFORM
- Waveform* m_waveform;
-#endif
- ProgressBar* m_progressBar;
- Spectrograph* m_spectrograph;
- LevelMeter* m_levelMeter;
-
- QPushButton* m_modeButton;
- QPushButton* m_recordButton;
- QIcon m_recordIcon;
- QPushButton* m_pauseButton;
- QIcon m_pauseIcon;
- QPushButton* m_playButton;
- QIcon m_playIcon;
- QPushButton* m_settingsButton;
- QIcon m_settingsIcon;
-
- QLabel* m_infoMessage;
- int m_infoMessageTimerId;
-
- SettingsDialog* m_settingsDialog;
- ToneGeneratorDialog* m_toneGeneratorDialog;
-
- QMenu* m_modeMenu;
- QAction* m_loadFileAction;
- QAction* m_generateToneAction;
- QAction* m_recordAction;
-};
-
-#endif // MAINWIDGET_H
diff --git a/examples/multimedia/spectrum/app/progressbar.cpp b/examples/multimedia/spectrum/app/progressbar.cpp
deleted file mode 100644
index b42816e1c..000000000
--- a/examples/multimedia/spectrum/app/progressbar.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "progressbar.h"
-#include "spectrum.h"
-#include <QPainter>
-
-ProgressBar::ProgressBar(QWidget *parent)
- : QWidget(parent)
- , m_bufferLength(0)
- , m_recordPosition(0)
- , m_playPosition(0)
- , m_windowPosition(0)
- , m_windowLength(0)
-{
- setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
- setMinimumHeight(30);
-#ifdef SUPERIMPOSE_PROGRESS_ON_WAVEFORM
- setAutoFillBackground(false);
-#endif
-}
-
-ProgressBar::~ProgressBar()
-{
-
-}
-
-void ProgressBar::reset()
-{
- m_bufferLength = 0;
- m_recordPosition = 0;
- m_playPosition = 0;
- m_windowPosition = 0;
- m_windowLength = 0;
- update();
-}
-
-void ProgressBar::paintEvent(QPaintEvent * /*event*/)
-{
- QPainter painter(this);
-
- QColor bufferColor(0, 0, 255);
- QColor windowColor(0, 255, 0);
-
-#ifdef SUPERIMPOSE_PROGRESS_ON_WAVEFORM
- bufferColor.setAlphaF(0.5);
- windowColor.setAlphaF(0.5);
-#else
- painter.fillRect(rect(), Qt::black);
-#endif
-
- if (m_bufferLength) {
- QRect bar = rect();
- const qreal play = qreal(m_playPosition) / m_bufferLength;
- bar.setLeft(rect().left() + play * rect().width());
- const qreal record = qreal(m_recordPosition) / m_bufferLength;
- bar.setRight(rect().left() + record * rect().width());
- painter.fillRect(bar, bufferColor);
-
- QRect window = rect();
- const qreal windowLeft = qreal(m_windowPosition) / m_bufferLength;
- window.setLeft(rect().left() + windowLeft * rect().width());
- const qreal windowWidth = qreal(m_windowLength) / m_bufferLength;
- window.setWidth(windowWidth * rect().width());
- painter.fillRect(window, windowColor);
- }
-}
-
-void ProgressBar::bufferLengthChanged(qint64 bufferSize)
-{
- m_bufferLength = bufferSize;
- m_recordPosition = 0;
- m_playPosition = 0;
- m_windowPosition = 0;
- m_windowLength = 0;
- repaint();
-}
-
-void ProgressBar::recordPositionChanged(qint64 recordPosition)
-{
- Q_ASSERT(recordPosition >= 0);
- Q_ASSERT(recordPosition <= m_bufferLength);
- m_recordPosition = recordPosition;
- repaint();
-}
-
-void ProgressBar::playPositionChanged(qint64 playPosition)
-{
- Q_ASSERT(playPosition >= 0);
- Q_ASSERT(playPosition <= m_bufferLength);
- m_playPosition = playPosition;
- repaint();
-}
-
-void ProgressBar::windowChanged(qint64 position, qint64 length)
-{
- Q_ASSERT(position >= 0);
- Q_ASSERT(position <= m_bufferLength);
- Q_ASSERT(position + length <= m_bufferLength);
- m_windowPosition = position;
- m_windowLength = length;
- repaint();
-}
diff --git a/examples/multimedia/spectrum/app/progressbar.h b/examples/multimedia/spectrum/app/progressbar.h
deleted file mode 100644
index 601900efb..000000000
--- a/examples/multimedia/spectrum/app/progressbar.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef PROGRESSBAR_H
-#define PROGRESSBAR_H
-
-#include <QWidget>
-
-/**
- * Widget which displays a the current fill state of the Engine's internal
- * buffer, and the current play/record position within that buffer.
- */
-class ProgressBar : public QWidget
-{
- Q_OBJECT
-
-public:
- explicit ProgressBar(QWidget *parent = 0);
- ~ProgressBar();
-
- void reset();
- void paintEvent(QPaintEvent *event) override;
-
-public slots:
- void bufferLengthChanged(qint64 length);
- void recordPositionChanged(qint64 recordPosition);
- void playPositionChanged(qint64 playPosition);
- void windowChanged(qint64 position, qint64 length);
-
-private:
- qint64 m_bufferLength;
- qint64 m_recordPosition;
- qint64 m_playPosition;
- qint64 m_windowPosition;
- qint64 m_windowLength;
-};
-
-#endif // PROGRESSBAR_H
diff --git a/examples/multimedia/spectrum/app/settingsdialog.cpp b/examples/multimedia/spectrum/app/settingsdialog.cpp
deleted file mode 100644
index 889fcf639..000000000
--- a/examples/multimedia/spectrum/app/settingsdialog.cpp
+++ /dev/null
@@ -1,157 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "settingsdialog.h"
-#include <QCheckBox>
-#include <QComboBox>
-#include <QDialogButtonBox>
-#include <QLabel>
-#include <QPushButton>
-#include <QSlider>
-#include <QSpinBox>
-#include <QVBoxLayout>
-
-SettingsDialog::SettingsDialog(
- const QList<QAudioDeviceInfo> &availableInputDevices,
- const QList<QAudioDeviceInfo> &availableOutputDevices,
- QWidget *parent)
- : QDialog(parent)
- , m_windowFunction(DefaultWindowFunction)
- , m_inputDeviceComboBox(new QComboBox(this))
- , m_outputDeviceComboBox(new QComboBox(this))
- , m_windowFunctionComboBox(new QComboBox(this))
-{
- QVBoxLayout *dialogLayout = new QVBoxLayout(this);
-
- // Populate combo boxes
-
- for (const QAudioDeviceInfo &device : availableInputDevices)
- m_inputDeviceComboBox->addItem(device.deviceName(),
- QVariant::fromValue(device));
- for (const QAudioDeviceInfo &device : availableOutputDevices)
- m_outputDeviceComboBox->addItem(device.deviceName(),
- QVariant::fromValue(device));
-
- m_windowFunctionComboBox->addItem(tr("None"), QVariant::fromValue(int(NoWindow)));
- m_windowFunctionComboBox->addItem("Hann", QVariant::fromValue(int(HannWindow)));
- m_windowFunctionComboBox->setCurrentIndex(m_windowFunction);
-
- // Initialize default devices
- if (!availableInputDevices.empty())
- m_inputDevice = availableInputDevices.front();
- if (!availableOutputDevices.empty())
- m_outputDevice = availableOutputDevices.front();
-
- // Add widgets to layout
-
- QScopedPointer<QHBoxLayout> inputDeviceLayout(new QHBoxLayout);
- QLabel *inputDeviceLabel = new QLabel(tr("Input device"), this);
- inputDeviceLayout->addWidget(inputDeviceLabel);
- inputDeviceLayout->addWidget(m_inputDeviceComboBox);
- dialogLayout->addLayout(inputDeviceLayout.data());
- inputDeviceLayout.take(); // ownership transferred to dialogLayout
-
- QScopedPointer<QHBoxLayout> outputDeviceLayout(new QHBoxLayout);
- QLabel *outputDeviceLabel = new QLabel(tr("Output device"), this);
- outputDeviceLayout->addWidget(outputDeviceLabel);
- outputDeviceLayout->addWidget(m_outputDeviceComboBox);
- dialogLayout->addLayout(outputDeviceLayout.data());
- outputDeviceLayout.take(); // ownership transferred to dialogLayout
-
- QScopedPointer<QHBoxLayout> windowFunctionLayout(new QHBoxLayout);
- QLabel *windowFunctionLabel = new QLabel(tr("Window function"), this);
- windowFunctionLayout->addWidget(windowFunctionLabel);
- windowFunctionLayout->addWidget(m_windowFunctionComboBox);
- dialogLayout->addLayout(windowFunctionLayout.data());
- windowFunctionLayout.take(); // ownership transferred to dialogLayout
-
- // Connect
- connect(m_inputDeviceComboBox, QOverload<int>::of(&QComboBox::activated),
- this, &SettingsDialog::inputDeviceChanged);
- connect(m_outputDeviceComboBox, QOverload<int>::of(&QComboBox::activated),
- this, &SettingsDialog::outputDeviceChanged);
- connect(m_windowFunctionComboBox, QOverload<int>::of(&QComboBox::activated),
- this, &SettingsDialog::windowFunctionChanged);
-
- // Add standard buttons to layout
- QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
- buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
- dialogLayout->addWidget(buttonBox);
-
- // Connect standard buttons
- connect(buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked,
- this, &SettingsDialog::accept);
- connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked,
- this, &SettingsDialog::reject);
-
- setLayout(dialogLayout);
-}
-
-SettingsDialog::~SettingsDialog()
-{
-
-}
-
-void SettingsDialog::windowFunctionChanged(int index)
-{
- m_windowFunction = static_cast<WindowFunction>(
- m_windowFunctionComboBox->itemData(index).value<int>());
-}
-
-void SettingsDialog::inputDeviceChanged(int index)
-{
- m_inputDevice = m_inputDeviceComboBox->itemData(index).value<QAudioDeviceInfo>();
-}
-
-void SettingsDialog::outputDeviceChanged(int index)
-{
- m_outputDevice = m_outputDeviceComboBox->itemData(index).value<QAudioDeviceInfo>();
-}
-
diff --git a/examples/multimedia/spectrum/app/settingsdialog.h b/examples/multimedia/spectrum/app/settingsdialog.h
deleted file mode 100644
index 3200ccf54..000000000
--- a/examples/multimedia/spectrum/app/settingsdialog.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef SETTINGSDIALOG_H
-#define SETTINGSDIALOG_H
-
-#include "spectrum.h"
-#include <QDialog>
-#include <QAudioDeviceInfo>
-
-QT_BEGIN_NAMESPACE
-class QComboBox;
-class QCheckBox;
-class QSlider;
-class QSpinBox;
-class QGridLayout;
-QT_END_NAMESPACE
-
-/**
- * Dialog used to control settings such as the audio input / output device
- * and the windowing function.
- */
-class SettingsDialog : public QDialog
-{
- Q_OBJECT
-
-public:
- SettingsDialog(const QList<QAudioDeviceInfo> &availableInputDevices,
- const QList<QAudioDeviceInfo> &availableOutputDevices,
- QWidget *parent = 0);
- ~SettingsDialog();
-
- WindowFunction windowFunction() const { return m_windowFunction; }
- const QAudioDeviceInfo &inputDevice() const { return m_inputDevice; }
- const QAudioDeviceInfo &outputDevice() const { return m_outputDevice; }
-
-private slots:
- void windowFunctionChanged(int index);
- void inputDeviceChanged(int index);
- void outputDeviceChanged(int index);
-
-private:
- WindowFunction m_windowFunction;
- QAudioDeviceInfo m_inputDevice;
- QAudioDeviceInfo m_outputDevice;
-
- QComboBox *m_inputDeviceComboBox;
- QComboBox *m_outputDeviceComboBox;
- QComboBox *m_windowFunctionComboBox;
-};
-
-#endif // SETTINGSDIALOG_H
diff --git a/examples/multimedia/spectrum/app/spectrograph.h b/examples/multimedia/spectrum/app/spectrograph.h
deleted file mode 100644
index 0e7b11717..000000000
--- a/examples/multimedia/spectrum/app/spectrograph.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef SPECTROGRAPH_H
-#define SPECTROGRAPH_H
-
-#include "frequencyspectrum.h"
-
-#include <QWidget>
-
-/**
- * Widget which displays a spectrograph showing the frequency spectrum
- * of the window of audio samples most recently analyzed by the Engine.
- */
-class Spectrograph : public QWidget
-{
- Q_OBJECT
-
-public:
- explicit Spectrograph(QWidget *parent = 0);
- ~Spectrograph();
-
- void setParams(int numBars, qreal lowFreq, qreal highFreq);
-
- // QObject
- void timerEvent(QTimerEvent *event) override;
-
- // QWidget
- void paintEvent(QPaintEvent *event) override;
- void mousePressEvent(QMouseEvent *event) override;
-
-signals:
- void infoMessage(const QString &message, int intervalMs);
-
-public slots:
- void reset();
- void spectrumChanged(const FrequencySpectrum &spectrum);
-
-private:
- int barIndex(qreal frequency) const;
- QPair<qreal, qreal> barRange(int barIndex) const;
- void updateBars();
-
- void selectBar(int index);
-
-private:
- struct Bar {
- Bar() : value(0.0), clipped(false) { }
- qreal value;
- bool clipped;
- };
-
- QList<Bar> m_bars;
- int m_barSelected;
- int m_timerId;
- qreal m_lowFreq;
- qreal m_highFreq;
- FrequencySpectrum m_spectrum;
-};
-
-#endif // SPECTROGRAPH_H
diff --git a/examples/multimedia/spectrum/app/spectrum.h b/examples/multimedia/spectrum/app/spectrum.h
deleted file mode 100644
index aea9ce7c5..000000000
--- a/examples/multimedia/spectrum/app/spectrum.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef SPECTRUM_H
-#define SPECTRUM_H
-
-#include <qglobal.h>
-#include "utils.h"
-#include "fftreal_wrapper.h" // For FFTLengthPowerOfTwo
-
-//-----------------------------------------------------------------------------
-// Constants
-//-----------------------------------------------------------------------------
-
-// Number of audio samples used to calculate the frequency spectrum
-const int SpectrumLengthSamples = PowerOfTwo<FFTLengthPowerOfTwo>::Result;
-
-// Number of bands in the frequency spectrum
-const int SpectrumNumBands = 10;
-
-// Lower bound of first band in the spectrum
-const qreal SpectrumLowFreq = 0.0; // Hz
-
-// Upper band of last band in the spectrum
-const qreal SpectrumHighFreq = 1000.0; // Hz
-
-// Waveform window size in microseconds
-const qint64 WaveformWindowDuration = 500 * 1000;
-
-// Length of waveform tiles in bytes
-// Ideally, these would match the QAudio*::bufferSize(), but that isn't
-// available until some time after QAudio*::start() has been called, and we
-// need this value in order to initialize the waveform display.
-// We therefore just choose a sensible value.
-const int WaveformTileLength = 4096;
-
-// Fudge factor used to calculate the spectrum bar heights
-const qreal SpectrumAnalyserMultiplier = 0.15;
-
-// Disable message timeout
-const int NullMessageTimeout = -1;
-
-
-//-----------------------------------------------------------------------------
-// Types and data structures
-//-----------------------------------------------------------------------------
-
-enum WindowFunction {
- NoWindow,
- HannWindow
-};
-
-const WindowFunction DefaultWindowFunction = HannWindow;
-
-struct Tone
-{
- Tone(qreal freq = 0.0, qreal amp = 0.0)
- : frequency(freq), amplitude(amp)
- { }
-
- // Start and end frequencies for swept tone generation
- qreal frequency;
-
- // Amplitude in range [0.0, 1.0]
- qreal amplitude;
-};
-
-struct SweptTone
-{
- SweptTone(qreal start = 0.0, qreal end = 0.0, qreal amp = 0.0)
- : startFreq(start), endFreq(end), amplitude(amp)
- { Q_ASSERT(end >= start); }
-
- SweptTone(const Tone &tone)
- : startFreq(tone.frequency), endFreq(tone.frequency), amplitude(tone.amplitude)
- { }
-
- // Start and end frequencies for swept tone generation
- qreal startFreq;
- qreal endFreq;
-
- // Amplitude in range [0.0, 1.0]
- qreal amplitude;
-};
-
-// Handle some dependencies between macros defined in the .pro file
-
-#ifdef DISABLE_WAVEFORM
-#undef SUPERIMPOSE_PROGRESS_ON_WAVEFORM
-#endif
-
-#endif // SPECTRUM_H
-
diff --git a/examples/multimedia/spectrum/app/spectrumanalyser.cpp b/examples/multimedia/spectrum/app/spectrumanalyser.cpp
deleted file mode 100644
index 6daa1c6aa..000000000
--- a/examples/multimedia/spectrum/app/spectrumanalyser.cpp
+++ /dev/null
@@ -1,286 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "spectrumanalyser.h"
-#include "utils.h"
-#include "fftreal_wrapper.h"
-
-#include <qmath.h>
-#include <qmetatype.h>
-#include <QAudioFormat>
-#include <QThread>
-
-SpectrumAnalyserThread::SpectrumAnalyserThread(QObject *parent)
- : QObject(parent)
-#ifndef DISABLE_FFT
- , m_fft(new FFTRealWrapper)
-#endif
- , m_numSamples(SpectrumLengthSamples)
- , m_windowFunction(DefaultWindowFunction)
- , m_window(SpectrumLengthSamples, 0.0)
- , m_input(SpectrumLengthSamples, 0.0)
- , m_output(SpectrumLengthSamples, 0.0)
- , m_spectrum(SpectrumLengthSamples)
-#ifdef SPECTRUM_ANALYSER_SEPARATE_THREAD
- , m_thread(new QThread(this))
-#endif
-{
-#ifdef SPECTRUM_ANALYSER_SEPARATE_THREAD
- // moveToThread() cannot be called on a QObject with a parent
- setParent(0);
- moveToThread(m_thread);
- m_thread->start();
-#endif
- calculateWindow();
-}
-
-SpectrumAnalyserThread::~SpectrumAnalyserThread()
-{
-#ifndef DISABLE_FFT
- delete m_fft;
-#endif
-}
-
-void SpectrumAnalyserThread::setWindowFunction(WindowFunction type)
-{
- m_windowFunction = type;
- calculateWindow();
-}
-
-void SpectrumAnalyserThread::calculateWindow()
-{
- for (int i=0; i<m_numSamples; ++i) {
- DataType x = 0.0;
-
- switch (m_windowFunction) {
- case NoWindow:
- x = 1.0;
- break;
- case HannWindow:
- x = 0.5 * (1 - qCos((2 * M_PI * i) / (m_numSamples - 1)));
- break;
- default:
- Q_ASSERT(false);
- }
-
- m_window[i] = x;
- }
-}
-
-void SpectrumAnalyserThread::calculateSpectrum(const QByteArray &buffer,
- int inputFrequency,
- int bytesPerSample)
-{
-#ifndef DISABLE_FFT
- Q_ASSERT(buffer.size() == m_numSamples * bytesPerSample);
-
- // Initialize data array
- const char *ptr = buffer.constData();
- for (int i=0; i<m_numSamples; ++i) {
- const qint16 pcmSample = *reinterpret_cast<const qint16*>(ptr);
- // Scale down to range [-1.0, 1.0]
- const DataType realSample = pcmToReal(pcmSample);
- const DataType windowedSample = realSample * m_window[i];
- m_input[i] = windowedSample;
- ptr += bytesPerSample;
- }
-
- // Calculate the FFT
- m_fft->calculateFFT(m_output.data(), m_input.data());
-
- // Analyze output to obtain amplitude and phase for each frequency
- for (int i=2; i<=m_numSamples/2; ++i) {
- // Calculate frequency of this complex sample
- m_spectrum[i].frequency = qreal(i * inputFrequency) / (m_numSamples);
-
- const qreal real = m_output[i];
- qreal imag = 0.0;
- if (i>0 && i<m_numSamples/2)
- imag = m_output[m_numSamples/2 + i];
-
- const qreal magnitude = qSqrt(real*real + imag*imag);
- qreal amplitude = SpectrumAnalyserMultiplier * qLn(magnitude);
-
- // Bound amplitude to [0.0, 1.0]
- m_spectrum[i].clipped = (amplitude > 1.0);
- amplitude = qMax(qreal(0.0), amplitude);
- amplitude = qMin(qreal(1.0), amplitude);
- m_spectrum[i].amplitude = amplitude;
- }
-#endif
-
- emit calculationComplete(m_spectrum);
-}
-
-
-//=============================================================================
-// SpectrumAnalyser
-//=============================================================================
-
-SpectrumAnalyser::SpectrumAnalyser(QObject *parent)
- : QObject(parent)
- , m_thread(new SpectrumAnalyserThread(this))
- , m_state(Idle)
-#ifdef DUMP_SPECTRUMANALYSER
- , m_count(0)
-#endif
-{
- connect(m_thread, &SpectrumAnalyserThread::calculationComplete,
- this, &SpectrumAnalyser::calculationComplete);
-}
-
-SpectrumAnalyser::~SpectrumAnalyser()
-{
-
-}
-
-#ifdef DUMP_SPECTRUMANALYSER
-void SpectrumAnalyser::setOutputPath(const QString &outputDir)
-{
- m_outputDir.setPath(outputDir);
- m_textFile.setFileName(m_outputDir.filePath("spectrum.txt"));
- m_textFile.open(QIODevice::WriteOnly | QIODevice::Text);
- m_textStream.setDevice(&m_textFile);
-}
-#endif
-
-//-----------------------------------------------------------------------------
-// Public functions
-//-----------------------------------------------------------------------------
-
-void SpectrumAnalyser::setWindowFunction(WindowFunction type)
-{
- const bool b = QMetaObject::invokeMethod(m_thread, "setWindowFunction",
- Qt::AutoConnection,
- Q_ARG(WindowFunction, type));
- Q_ASSERT(b);
- Q_UNUSED(b); // suppress warnings in release builds
-}
-
-void SpectrumAnalyser::calculate(const QByteArray &buffer,
- const QAudioFormat &format)
-{
- // QThread::currentThread is marked 'for internal use only', but
- // we're only using it for debug output here, so it's probably OK :)
- SPECTRUMANALYSER_DEBUG << "SpectrumAnalyser::calculate"
- << QThread::currentThread()
- << "state" << m_state;
-
- if (isReady()) {
- Q_ASSERT(isPCMS16LE(format));
-
- const int bytesPerSample = format.sampleSize() * format.channelCount() / 8;
-
-#ifdef DUMP_SPECTRUMANALYSER
- m_count++;
- const QString pcmFileName = m_outputDir.filePath(QString("spectrum_%1.pcm").arg(m_count, 4, 10, QChar('0')));
- QFile pcmFile(pcmFileName);
- pcmFile.open(QIODevice::WriteOnly);
- const int bufferLength = m_numSamples * bytesPerSample;
- pcmFile.write(buffer, bufferLength);
-
- m_textStream << "TimeDomain " << m_count << "\n";
- const qint16* input = reinterpret_cast<const qint16*>(buffer);
- for (int i=0; i<m_numSamples; ++i) {
- m_textStream << i << "\t" << *input << "\n";
- input += format.channels();
- }
-#endif
-
- m_state = Busy;
-
- // Invoke SpectrumAnalyserThread::calculateSpectrum using QMetaObject. If
- // m_thread is in a different thread from the current thread, the
- // calculation will be done in the child thread.
- // Once the calculation is finished, a calculationChanged signal will be
- // emitted by m_thread.
- const bool b = QMetaObject::invokeMethod(m_thread, "calculateSpectrum",
- Qt::AutoConnection,
- Q_ARG(QByteArray, buffer),
- Q_ARG(int, format.sampleRate()),
- Q_ARG(int, bytesPerSample));
- Q_ASSERT(b);
- Q_UNUSED(b); // suppress warnings in release builds
-
-#ifdef DUMP_SPECTRUMANALYSER
- m_textStream << "FrequencySpectrum " << m_count << "\n";
- FrequencySpectrum::const_iterator x = m_spectrum.begin();
- for (int i=0; i<m_numSamples; ++i, ++x)
- m_textStream << i << "\t"
- << x->frequency << "\t"
- << x->amplitude<< "\t"
- << x->phase << "\n";
-#endif
- }
-}
-
-bool SpectrumAnalyser::isReady() const
-{
- return (Idle == m_state);
-}
-
-void SpectrumAnalyser::cancelCalculation()
-{
- if (Busy == m_state)
- m_state = Cancelled;
-}
-
-
-//-----------------------------------------------------------------------------
-// Private slots
-//-----------------------------------------------------------------------------
-
-void SpectrumAnalyser::calculationComplete(const FrequencySpectrum &spectrum)
-{
- Q_ASSERT(Idle != m_state);
- if (Busy == m_state)
- emit spectrumChanged(spectrum);
- m_state = Idle;
-}
diff --git a/examples/multimedia/spectrum/app/spectrumanalyser.h b/examples/multimedia/spectrum/app/spectrumanalyser.h
deleted file mode 100644
index 799143489..000000000
--- a/examples/multimedia/spectrum/app/spectrumanalyser.h
+++ /dev/null
@@ -1,206 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef SPECTRUMANALYSER_H
-#define SPECTRUMANALYSER_H
-
-#include <QByteArray>
-#include <QObject>
-#include <QList>
-
-#ifdef DUMP_SPECTRUMANALYSER
-#include <QDir>
-#include <QFile>
-#include <QTextStream>
-#endif
-
-#include "frequencyspectrum.h"
-#include "spectrum.h"
-
-#ifndef DISABLE_FFT
-#include "FFTRealFixLenParam.h"
-#endif
-
-QT_FORWARD_DECLARE_CLASS(QAudioFormat)
-QT_FORWARD_DECLARE_CLASS(QThread)
-
-class FFTRealWrapper;
-
-class SpectrumAnalyserThreadPrivate;
-
-/**
- * Implementation of the spectrum analysis which can be run in a
- * separate thread.
- */
-class SpectrumAnalyserThread : public QObject
-{
- Q_OBJECT
-
-public:
- SpectrumAnalyserThread(QObject *parent);
- ~SpectrumAnalyserThread();
-
-public slots:
- void setWindowFunction(WindowFunction type);
- void calculateSpectrum(const QByteArray &buffer,
- int inputFrequency,
- int bytesPerSample);
-
-signals:
- void calculationComplete(const FrequencySpectrum &spectrum);
-
-private:
- void calculateWindow();
-
-private:
-#ifndef DISABLE_FFT
- FFTRealWrapper* m_fft;
-#endif
-
- const int m_numSamples;
-
- WindowFunction m_windowFunction;
-
-#ifdef DISABLE_FFT
- typedef qreal DataType;
-#else
- typedef FFTRealFixLenParam::DataType DataType;
-#endif
- QList<DataType> m_window;
-
- QList<DataType> m_input;
- QList<DataType> m_output;
-
- FrequencySpectrum m_spectrum;
-
-#ifdef SPECTRUM_ANALYSER_SEPARATE_THREAD
- QThread* m_thread;
-#endif
-};
-
-/**
- * Class which performs frequency spectrum analysis on a window of
- * audio samples, provided to it by the Engine.
- */
-class SpectrumAnalyser : public QObject
-{
- Q_OBJECT
-
-public:
- SpectrumAnalyser(QObject *parent = 0);
- ~SpectrumAnalyser();
-
-#ifdef DUMP_SPECTRUMANALYSER
- void setOutputPath(const QString &outputPath);
-#endif
-
-public:
- /*
- * Set the windowing function which is applied before calculating the FFT
- */
- void setWindowFunction(WindowFunction type);
-
- /*
- * Calculate a frequency spectrum
- *
- * \param buffer Audio data
- * \param format Format of audio data
- *
- * Frequency spectrum is calculated asynchronously. The result is returned
- * via the spectrumChanged signal.
- *
- * An ongoing calculation can be cancelled by calling cancelCalculation().
- *
- */
- void calculate(const QByteArray &buffer, const QAudioFormat &format);
-
- /*
- * Check whether the object is ready to perform another calculation
- */
- bool isReady() const;
-
- /*
- * Cancel an ongoing calculation
- *
- * Note that cancelling is asynchronous.
- */
- void cancelCalculation();
-
-signals:
- void spectrumChanged(const FrequencySpectrum &spectrum);
-
-private slots:
- void calculationComplete(const FrequencySpectrum &spectrum);
-
-private:
- void calculateWindow();
-
-private:
-
- SpectrumAnalyserThread* m_thread;
-
- enum State {
- Idle,
- Busy,
- Cancelled
- };
-
- State m_state;
-
-#ifdef DUMP_SPECTRUMANALYSER
- QDir m_outputDir;
- int m_count;
- QFile m_textFile;
- QTextStream m_textStream;
-#endif
-};
-
-#endif // SPECTRUMANALYSER_H
-
diff --git a/examples/multimedia/spectrum/app/tonegenerator.cpp b/examples/multimedia/spectrum/app/tonegenerator.cpp
deleted file mode 100644
index 2e549ab4f..000000000
--- a/examples/multimedia/spectrum/app/tonegenerator.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "spectrum.h"
-#include "utils.h"
-#include <QByteArray>
-#include <QAudioFormat>
-#include <qmath.h>
-#include <qendian.h>
-
-void generateTone(const SweptTone &tone, const QAudioFormat &format, QByteArray &buffer)
-{
- Q_ASSERT(isPCMS16LE(format));
-
- const int channelBytes = format.sampleSize() / 8;
- const int sampleBytes = format.channelCount() * channelBytes;
- int length = buffer.size();
- const int numSamples = buffer.size() / sampleBytes;
-
- Q_ASSERT(length % sampleBytes == 0);
- Q_UNUSED(sampleBytes); // suppress warning in release builds
-
- unsigned char *ptr = reinterpret_cast<unsigned char *>(buffer.data());
-
- qreal phase = 0.0;
-
- const qreal d = 2 * M_PI / format.sampleRate();
-
- // We can't generate a zero-frequency sine wave
- const qreal startFreq = tone.startFreq ? tone.startFreq : 1.0;
-
- // Amount by which phase increases on each sample
- qreal phaseStep = d * startFreq;
-
- // Amount by which phaseStep increases on each sample
- // If this is non-zero, the output is a frequency-swept tone
- const qreal phaseStepStep = d * (tone.endFreq - startFreq) / numSamples;
-
- while (length) {
- const qreal x = tone.amplitude * qSin(phase);
- const qint16 value = realToPcm(x);
- for (int i=0; i<format.channelCount(); ++i) {
- qToLittleEndian<qint16>(value, ptr);
- ptr += channelBytes;
- length -= channelBytes;
- }
-
- phase += phaseStep;
- while (phase > 2 * M_PI)
- phase -= 2 * M_PI;
- phaseStep += phaseStepStep;
- }
-}
diff --git a/examples/multimedia/spectrum/app/tonegenerator.h b/examples/multimedia/spectrum/app/tonegenerator.h
deleted file mode 100644
index af6efade2..000000000
--- a/examples/multimedia/spectrum/app/tonegenerator.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef TONEGENERATOR_H
-#define TONEGENERATOR_H
-
-#include <qglobal.h>
-#include "spectrum.h"
-
-QT_BEGIN_NAMESPACE
-class QAudioFormat;
-class QByteArray;
-QT_END_NAMESPACE
-
-/**
- * Generate a sine wave
- */
-void generateTone(const SweptTone &tone, const QAudioFormat &format, QByteArray &buffer);
-
-#endif // TONEGENERATOR_H
-
diff --git a/examples/multimedia/spectrum/app/tonegeneratordialog.cpp b/examples/multimedia/spectrum/app/tonegeneratordialog.cpp
deleted file mode 100644
index 76fe5d2e3..000000000
--- a/examples/multimedia/spectrum/app/tonegeneratordialog.cpp
+++ /dev/null
@@ -1,154 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include "tonegeneratordialog.h"
-#include <QComboBox>
-#include <QDialogButtonBox>
-#include <QLabel>
-#include <QPushButton>
-#include <QVBoxLayout>
-#include <QCheckBox>
-#include <QSlider>
-#include <QSpinBox>
-
-const int ToneGeneratorFreqMin = 1;
-const int ToneGeneratorFreqMax = 1000;
-const int ToneGeneratorFreqDefault = 440;
-const int ToneGeneratorAmplitudeDefault = 75;
-
-ToneGeneratorDialog::ToneGeneratorDialog(QWidget *parent)
- : QDialog(parent)
- , m_toneGeneratorSweepCheckBox(new QCheckBox(tr("Frequency sweep"), this))
- , m_frequencySweepEnabled(true)
- , m_toneGeneratorControl(new QWidget(this))
- , m_toneGeneratorFrequencyControl(new QWidget(this))
- , m_frequencySlider(new QSlider(Qt::Horizontal, this))
- , m_frequencySpinBox(new QSpinBox(this))
- , m_frequency(ToneGeneratorFreqDefault)
- , m_amplitudeSlider(new QSlider(Qt::Horizontal, this))
-{
- QVBoxLayout *dialogLayout = new QVBoxLayout(this);
-
- m_toneGeneratorSweepCheckBox->setChecked(true);
-
- // Configure tone generator controls
- m_frequencySlider->setRange(ToneGeneratorFreqMin, ToneGeneratorFreqMax);
- m_frequencySlider->setValue(ToneGeneratorFreqDefault);
- m_frequencySpinBox->setRange(ToneGeneratorFreqMin, ToneGeneratorFreqMax);
- m_frequencySpinBox->setValue(ToneGeneratorFreqDefault);
- m_amplitudeSlider->setRange(0, 100);
- m_amplitudeSlider->setValue(ToneGeneratorAmplitudeDefault);
-
- // Add widgets to layout
- QGridLayout *frequencyControlLayout = new QGridLayout;
- QLabel *frequencyLabel = new QLabel(tr("Frequency (Hz)"), this);
- frequencyControlLayout->addWidget(frequencyLabel, 0, 0, 2, 1);
- frequencyControlLayout->addWidget(m_frequencySlider, 0, 1);
- frequencyControlLayout->addWidget(m_frequencySpinBox, 1, 1);
- m_toneGeneratorFrequencyControl->setLayout(frequencyControlLayout);
- m_toneGeneratorFrequencyControl->setEnabled(false);
-
- QGridLayout *toneGeneratorLayout = new QGridLayout;
- QLabel *amplitudeLabel = new QLabel(tr("Amplitude"), this);
- toneGeneratorLayout->addWidget(m_toneGeneratorSweepCheckBox, 0, 1);
- toneGeneratorLayout->addWidget(m_toneGeneratorFrequencyControl, 1, 0, 1, 2);
- toneGeneratorLayout->addWidget(amplitudeLabel, 2, 0);
- toneGeneratorLayout->addWidget(m_amplitudeSlider, 2, 1);
- m_toneGeneratorControl->setLayout(toneGeneratorLayout);
- m_toneGeneratorControl->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
- dialogLayout->addWidget(m_toneGeneratorControl);
-
- // Connect
- connect(m_toneGeneratorSweepCheckBox, &QCheckBox::toggled,
- this, &ToneGeneratorDialog::frequencySweepEnabled);
- connect(m_frequencySlider, &QSlider::valueChanged,
- m_frequencySpinBox, &QSpinBox::setValue);
- connect(m_frequencySpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
- m_frequencySlider, &QSlider::setValue);
-
- // Add standard buttons to layout
- QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
- buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
- dialogLayout->addWidget(buttonBox);
-
- // Connect standard buttons
- connect(buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked,
- this, &ToneGeneratorDialog::accept);
- connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked,
- this, &ToneGeneratorDialog::reject);
-
- setLayout(dialogLayout);
-}
-
-ToneGeneratorDialog::~ToneGeneratorDialog()
-{
-
-}
-
-bool ToneGeneratorDialog::isFrequencySweepEnabled() const
-{
- return m_toneGeneratorSweepCheckBox->isChecked();
-}
-
-qreal ToneGeneratorDialog::frequency() const
-{
- return qreal(m_frequencySlider->value());
-}
-
-qreal ToneGeneratorDialog::amplitude() const
-{
- return qreal(m_amplitudeSlider->value()) / 100.0;
-}
-
-void ToneGeneratorDialog::frequencySweepEnabled(bool enabled)
-{
- m_frequencySweepEnabled = enabled;
- m_toneGeneratorFrequencyControl->setEnabled(!enabled);
-}
diff --git a/examples/multimedia/spectrum/app/tonegeneratordialog.h b/examples/multimedia/spectrum/app/tonegeneratordialog.h
deleted file mode 100644
index 48a44a152..000000000
--- a/examples/multimedia/spectrum/app/tonegeneratordialog.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef TONEGENERATORDIALOG_H
-#define TONEGENERATORDIALOG_H
-
-#include "spectrum.h"
-#include <QAudioDeviceInfo>
-#include <QDialog>
-
-QT_BEGIN_NAMESPACE
-class QCheckBox;
-class QSlider;
-class QSpinBox;
-class QGridLayout;
-QT_END_NAMESPACE
-
-/**
- * Dialog which controls the parameters of the tone generator.
- */
-class ToneGeneratorDialog : public QDialog
-{
- Q_OBJECT
-
-public:
- explicit ToneGeneratorDialog(QWidget *parent = 0);
- ~ToneGeneratorDialog();
-
- bool isFrequencySweepEnabled() const;
- qreal frequency() const;
- qreal amplitude() const;
-
-private slots:
- void frequencySweepEnabled(bool enabled);
-
-private:
- QCheckBox *m_toneGeneratorSweepCheckBox;
- bool m_frequencySweepEnabled;
- QWidget *m_toneGeneratorControl;
- QWidget *m_toneGeneratorFrequencyControl;
- QSlider *m_frequencySlider;
- QSpinBox *m_frequencySpinBox;
- qreal m_frequency;
- QSlider *m_amplitudeSlider;
-};
-
-#endif // TONEGENERATORDIALOG_H
diff --git a/examples/multimedia/spectrum/app/utils.cpp b/examples/multimedia/spectrum/app/utils.cpp
deleted file mode 100644
index fc8b9a448..000000000
--- a/examples/multimedia/spectrum/app/utils.cpp
+++ /dev/null
@@ -1,149 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include <QAudioFormat>
-#include "utils.h"
-
-qint64 audioDuration(const QAudioFormat &format, qint64 bytes)
-{
- return (bytes * 1000000) /
- (format.sampleRate() * format.channelCount() * (format.sampleSize() / 8));
-}
-
-qint64 audioLength(const QAudioFormat &format, qint64 microSeconds)
-{
- qint64 result = (format.sampleRate() * format.channelCount() * (format.sampleSize() / 8))
- * microSeconds / 1000000;
- result -= result % (format.channelCount() * format.sampleSize());
- return result;
-}
-
-qreal nyquistFrequency(const QAudioFormat &format)
-{
- return format.sampleRate() / 2;
-}
-
-QString formatToString(const QAudioFormat &format)
-{
- QString result;
-
- if (QAudioFormat() != format) {
- if (format.codec() == "audio/pcm") {
- Q_ASSERT(format.sampleType() == QAudioFormat::SignedInt);
-
- const QString formatEndian = (format.byteOrder() == QAudioFormat::LittleEndian)
- ? QString("LE") : QString("BE");
-
- QString formatType;
- switch (format.sampleType()) {
- case QAudioFormat::SignedInt:
- formatType = "signed";
- break;
- case QAudioFormat::UnSignedInt:
- formatType = "unsigned";
- break;
- case QAudioFormat::Float:
- formatType = "float";
- break;
- case QAudioFormat::Unknown:
- formatType = "unknown";
- break;
- }
-
- QString formatChannels = QString("%1 channels").arg(format.channelCount());
- switch (format.channelCount()) {
- case 1:
- formatChannels = "mono";
- break;
- case 2:
- formatChannels = "stereo";
- break;
- }
-
- result = QString("%1 Hz %2 bit %3 %4 %5")
- .arg(format.sampleRate())
- .arg(format.sampleSize())
- .arg(formatType)
- .arg(formatEndian)
- .arg(formatChannels);
- } else {
- result = format.codec();
- }
- }
-
- return result;
-}
-
-bool isPCM(const QAudioFormat &format)
-{
- return (format.codec() == "audio/pcm");
-}
-
-
-bool isPCMS16LE(const QAudioFormat &format)
-{
- return isPCM(format) &&
- format.sampleType() == QAudioFormat::SignedInt &&
- format.sampleSize() == 16 &&
- format.byteOrder() == QAudioFormat::LittleEndian;
-}
-
-const qint16 PCMS16MaxValue = 32767;
-const quint16 PCMS16MaxAmplitude = 32768; // because minimum is -32768
-
-qreal pcmToReal(qint16 pcm)
-{
- return qreal(pcm) / PCMS16MaxAmplitude;
-}
-
-qint16 realToPcm(qreal real)
-{
- return real * PCMS16MaxValue;
-}
diff --git a/examples/multimedia/spectrum/app/utils.h b/examples/multimedia/spectrum/app/utils.h
deleted file mode 100644
index cd6357182..000000000
--- a/examples/multimedia/spectrum/app/utils.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef UTILS_H
-#define UTILS_H
-
-#include <QtCore/qglobal.h>
-#include <QDebug>
-
-QT_FORWARD_DECLARE_CLASS(QAudioFormat)
-
-//-----------------------------------------------------------------------------
-// Miscellaneous utility functions
-//-----------------------------------------------------------------------------
-
-qint64 audioDuration(const QAudioFormat &format, qint64 bytes);
-qint64 audioLength(const QAudioFormat &format, qint64 microSeconds);
-
-QString formatToString(const QAudioFormat &format);
-
-qreal nyquistFrequency(const QAudioFormat &format);
-
-// Scale PCM value to [-1.0, 1.0]
-qreal pcmToReal(qint16 pcm);
-
-// Scale real value in [-1.0, 1.0] to PCM
-qint16 realToPcm(qreal real);
-
-// Check whether the audio format is PCM
-bool isPCM(const QAudioFormat &format);
-
-// Check whether the audio format is signed, little-endian, 16-bit PCM
-bool isPCMS16LE(const QAudioFormat &format);
-
-// Compile-time calculation of powers of two
-
-template<int N> class PowerOfTwo
-{ public: static const int Result = PowerOfTwo<N-1>::Result * 2; };
-
-template<> class PowerOfTwo<0>
-{ public: static const int Result = 1; };
-
-
-//-----------------------------------------------------------------------------
-// Debug output
-//-----------------------------------------------------------------------------
-
-class NullDebug
-{
-public:
- template <typename T>
- NullDebug& operator<<(const T&) { return *this; }
-};
-
-inline NullDebug nullDebug() { return NullDebug(); }
-
-#ifdef LOG_ENGINE
-# define ENGINE_DEBUG qDebug()
-#else
-# define ENGINE_DEBUG nullDebug()
-#endif
-
-#ifdef LOG_SPECTRUMANALYSER
-# define SPECTRUMANALYSER_DEBUG qDebug()
-#else
-# define SPECTRUMANALYSER_DEBUG nullDebug()
-#endif
-
-#ifdef LOG_WAVEFORM
-# define WAVEFORM_DEBUG qDebug()
-#else
-# define WAVEFORM_DEBUG nullDebug()
-#endif
-
-#endif // UTILS_H
diff --git a/examples/multimedia/spectrum/app/wavfile.cpp b/examples/multimedia/spectrum/app/wavfile.cpp
deleted file mode 100644
index 475200db5..000000000
--- a/examples/multimedia/spectrum/app/wavfile.cpp
+++ /dev/null
@@ -1,158 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#include <qendian.h>
-#include "wavfile.h"
-
-struct chunk
-{
- char id[4];
- quint32 size;
-};
-
-struct RIFFHeader
-{
- chunk descriptor; // "RIFF"
- char type[4]; // "WAVE"
-};
-
-struct WAVEHeader
-{
- chunk descriptor;
- quint16 audioFormat;
- quint16 numChannels;
- quint32 sampleRate;
- quint32 byteRate;
- quint16 blockAlign;
- quint16 bitsPerSample;
-};
-
-struct DATAHeader
-{
- chunk descriptor;
-};
-
-struct CombinedHeader
-{
- RIFFHeader riff;
- WAVEHeader wave;
-};
-
-WavFile::WavFile(QObject *parent)
- : QFile(parent)
- , m_headerLength(0)
-{
-
-}
-
-bool WavFile::open(const QString &fileName)
-{
- close();
- setFileName(fileName);
- return QFile::open(QIODevice::ReadOnly) && readHeader();
-}
-
-const QAudioFormat &WavFile::fileFormat() const
-{
- return m_fileFormat;
-}
-
-qint64 WavFile::headerLength() const
-{
- return m_headerLength;
-}
-
-bool WavFile::readHeader()
-{
- seek(0);
- CombinedHeader header;
- bool result = read(reinterpret_cast<char *>(&header), sizeof(CombinedHeader)) == sizeof(CombinedHeader);
- if (result) {
- if ((memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0
- || memcmp(&header.riff.descriptor.id, "RIFX", 4) == 0)
- && memcmp(&header.riff.type, "WAVE", 4) == 0
- && memcmp(&header.wave.descriptor.id, "fmt ", 4) == 0
- && (header.wave.audioFormat == 1 || header.wave.audioFormat == 0)) {
-
- // Read off remaining header information
- DATAHeader dataHeader;
-
- if (qFromLittleEndian<quint32>(header.wave.descriptor.size) > sizeof(WAVEHeader)) {
- // Extended data available
- quint16 extraFormatBytes;
- if (peek((char*)&extraFormatBytes, sizeof(quint16)) != sizeof(quint16))
- return false;
- const qint64 throwAwayBytes = sizeof(quint16) + qFromLittleEndian<quint16>(extraFormatBytes);
- if (read(throwAwayBytes).size() != throwAwayBytes)
- return false;
- }
-
- if (read((char*)&dataHeader, sizeof(DATAHeader)) != sizeof(DATAHeader))
- return false;
-
- // Establish format
- if (memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0)
- m_fileFormat.setByteOrder(QAudioFormat::LittleEndian);
- else
- m_fileFormat.setByteOrder(QAudioFormat::BigEndian);
-
- int bps = qFromLittleEndian<quint16>(header.wave.bitsPerSample);
- m_fileFormat.setChannelCount(qFromLittleEndian<quint16>(header.wave.numChannels));
- m_fileFormat.setCodec("audio/pcm");
- m_fileFormat.setSampleRate(qFromLittleEndian<quint32>(header.wave.sampleRate));
- m_fileFormat.setSampleSize(qFromLittleEndian<quint16>(header.wave.bitsPerSample));
- m_fileFormat.setSampleType(bps == 8 ? QAudioFormat::UnSignedInt : QAudioFormat::SignedInt);
- } else {
- result = false;
- }
- }
- m_headerLength = pos();
- return result;
-}
diff --git a/examples/multimedia/spectrum/app/wavfile.h b/examples/multimedia/spectrum/app/wavfile.h
deleted file mode 100644
index a63bc5c93..000000000
--- a/examples/multimedia/spectrum/app/wavfile.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef WAVFILE_H
-#define WAVFILE_H
-
-#include <QObject>
-#include <QFile>
-#include <QAudioFormat>
-
-class WavFile : public QFile
-{
-public:
- WavFile(QObject *parent = 0);
-
- using QFile::open;
- bool open(const QString &fileName);
- const QAudioFormat &fileFormat() const;
- qint64 headerLength() const;
-
-private:
- bool readHeader();
-
-private:
- QAudioFormat m_fileFormat;
- qint64 m_headerLength;
-};
-
-#endif // WAVFILE_H
diff --git a/examples/multimedia/spectrum/doc/src/spectrum.qdoc b/examples/multimedia/spectrum/doc/src/spectrum.qdoc
index ad0ec984f..25e0760a4 100644
--- a/examples/multimedia/spectrum/doc/src/spectrum.qdoc
+++ b/examples/multimedia/spectrum/doc/src/spectrum.qdoc
@@ -1,34 +1,11 @@
-/****************************************************************************
-**
-** 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/spectrum
+ \example spectrum
\title Spectrum Example
\ingroup multimedia_examples
+ \examplecategory {Multimedia}
\brief Analyzing a raw audio stream using the FFTReal library.
\e Spectrum demonstrates how the \l{Qt Multimedia} module can be used to
@@ -58,8 +35,9 @@
Spectrum analysis is performed by calculating the Fast Fourier Transform
(FFT) of a segment of audio data. An open-source library,
- \l{http://ldesoras.free.fr/prod.html}{FFTReal}, against which the
- application is dynamically linked, is used to compute the transform.
+ \l{http://ldesoras.free.fr/prod.html}{FFTReal} is used to compute the
+ transform. FFTReal is available under the GNU Library General Public License
+ 2.0 or later.
\include examples-run.qdocinc
*/
diff --git a/examples/multimedia/spectrum/engine.cpp b/examples/multimedia/spectrum/engine.cpp
new file mode 100644
index 000000000..cb7aeadcb
--- /dev/null
+++ b/examples/multimedia/spectrum/engine.cpp
@@ -0,0 +1,750 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "engine.h"
+#include "tonegenerator.h"
+#include "utils.h"
+
+#include <QAudioSink>
+#include <QAudioSource>
+#include <QCoreApplication>
+#include <QDebug>
+#include <QFile>
+#include <QMetaObject>
+#include <QSet>
+#include <QThread>
+
+#if QT_CONFIG(permissions)
+ #include <QPermission>
+#endif
+
+#include <math.h>
+
+//-----------------------------------------------------------------------------
+// Constants
+//-----------------------------------------------------------------------------
+
+const qint64 BufferDurationUs = 10 * 1000000;
+
+// Size of the level calculation window in microseconds
+const int LevelWindowUs = 0.1 * 1000000;
+
+//-----------------------------------------------------------------------------
+// Constructor and destructor
+//-----------------------------------------------------------------------------
+
+Engine::Engine(QObject *parent)
+ : QObject(parent),
+ m_mode(QAudioDevice::Input),
+ m_state(QAudio::StoppedState),
+ m_devices(new QMediaDevices(this)),
+ m_generateTone(false),
+ m_file(nullptr),
+ m_analysisFile(nullptr),
+ m_audioInput(nullptr),
+ m_audioInputIODevice(nullptr),
+ m_recordPosition(0),
+ m_audioOutput(nullptr),
+ m_playPosition(0),
+ m_bufferPosition(0),
+ m_bufferLength(0),
+ m_dataLength(0),
+ m_levelBufferLength(0),
+ m_rmsLevel(0.0),
+ m_peakLevel(0.0),
+ m_spectrumBufferLength(0),
+ m_spectrumPosition(0),
+ m_count(0)
+{
+ connect(&m_spectrumAnalyser,
+ QOverload<const FrequencySpectrum &>::of(&SpectrumAnalyser::spectrumChanged), this,
+ QOverload<const FrequencySpectrum &>::of(&Engine::spectrumChanged));
+
+ // This code might misinterpret things like "-something -category". But
+ // it's unlikely that that needs to be supported so we'll let it go.
+ QStringList arguments = QCoreApplication::instance()->arguments();
+ for (int i = 0; i < arguments.count(); ++i) {
+ if (arguments.at(i) == QStringLiteral("--"))
+ break;
+ }
+
+ initAudioDevices();
+
+ initialize();
+
+#ifdef DUMP_DATA
+ createOutputDir();
+#endif
+
+#ifdef DUMP_SPECTRUM
+ m_spectrumAnalyser.setOutputPath(outputPath());
+#endif
+
+ m_notifyTimer = new QTimer(this);
+ m_notifyTimer->setInterval(1000);
+ connect(m_notifyTimer, &QTimer::timeout, this, &Engine::audioNotify);
+}
+
+Engine::~Engine() = default;
+
+//-----------------------------------------------------------------------------
+// Public functions
+//-----------------------------------------------------------------------------
+
+bool Engine::loadFile(const QString &fileName)
+{
+ reset();
+ bool result = false;
+ Q_ASSERT(!m_generateTone);
+ Q_ASSERT(!m_file);
+ Q_ASSERT(!fileName.isEmpty());
+ QIODevice *file = new QFile(fileName);
+ if (file->open(QIODevice::ReadOnly)) {
+ m_file = new QWaveDecoder(file, this);
+ if (m_file->open(QIODevice::ReadOnly)) {
+ if (m_file->audioFormat().sampleFormat() == QAudioFormat::Int16) {
+ result = initialize();
+ } else {
+ emit errorMessage(tr("Audio format not supported"),
+ formatToString(m_file->audioFormat()));
+ }
+ } else
+ emit errorMessage(tr("Could not open WAV decoder for file"), fileName);
+ } else {
+ emit errorMessage(tr("Could not open file"), fileName);
+ }
+ if (result) {
+ file->close();
+ file->open(QIODevice::ReadOnly);
+ m_analysisFile = new QWaveDecoder(file, this);
+ m_analysisFile->open(QIODevice::ReadOnly);
+ }
+ return result;
+}
+
+bool Engine::generateTone(const Tone &tone)
+{
+ reset();
+ Q_ASSERT(!m_generateTone);
+ Q_ASSERT(!m_file);
+ m_generateTone = true;
+ m_tone = tone;
+ ENGINE_DEBUG << "Engine::generateTone"
+ << "startFreq" << m_tone.startFreq << "endFreq" << m_tone.endFreq << "amp"
+ << m_tone.amplitude;
+ return initialize();
+}
+
+bool Engine::generateSweptTone(qreal amplitude)
+{
+ Q_ASSERT(!m_generateTone);
+ Q_ASSERT(!m_file);
+ m_generateTone = true;
+ m_tone.startFreq = 1;
+ m_tone.endFreq = 0;
+ m_tone.amplitude = amplitude;
+ ENGINE_DEBUG << "Engine::generateSweptTone"
+ << "startFreq" << m_tone.startFreq << "amp" << m_tone.amplitude;
+ return initialize();
+}
+
+bool Engine::initializeRecord()
+{
+ reset();
+ ENGINE_DEBUG << "Engine::initializeRecord";
+ Q_ASSERT(!m_generateTone);
+ Q_ASSERT(!m_file);
+ m_generateTone = false;
+ m_tone = SweptTone();
+ return initialize();
+}
+
+qint64 Engine::bufferLength() const
+{
+ return m_file ? m_file->getDevice()->size() : m_bufferLength;
+}
+
+void Engine::setWindowFunction(WindowFunction type)
+{
+ m_spectrumAnalyser.setWindowFunction(type);
+}
+
+//-----------------------------------------------------------------------------
+// Public slots
+//-----------------------------------------------------------------------------
+
+void Engine::startRecording()
+{
+ if (m_audioInput) {
+ if (QAudioDevice::Input == m_mode && QAudio::SuspendedState == m_state) {
+ m_audioInput->resume();
+ } else {
+ m_spectrumAnalyser.cancelCalculation();
+ emit spectrumChanged(0, 0, FrequencySpectrum());
+
+ m_buffer.fill(0);
+ setRecordPosition(0, true);
+ stopPlayback();
+ m_mode = QAudioDevice::Input;
+ connect(m_audioInput, &QAudioSource::stateChanged, this, &Engine::audioStateChanged);
+
+ m_count = 0;
+ m_dataLength = 0;
+ emit dataLengthChanged(0);
+ m_audioInputIODevice = m_audioInput->start();
+ connect(m_audioInputIODevice, &QIODevice::readyRead, this, &Engine::audioDataReady);
+ }
+ m_notifyTimer->start();
+ }
+}
+
+void Engine::startPlayback()
+{
+ if (!m_audioOutput)
+ initialize();
+
+ if (m_audioOutput) {
+ if (QAudioDevice::Output == m_mode && QAudio::SuspendedState == m_state) {
+#ifdef Q_OS_WIN
+ // The Windows backend seems to internally go back into ActiveState
+ // while still returning SuspendedState, so to ensure that it doesn't
+ // ignore the resume() call, we first re-suspend
+ m_audioOutput->suspend();
+#endif
+ m_audioOutput->resume();
+ } else {
+ m_spectrumAnalyser.cancelCalculation();
+ emit spectrumChanged(0, 0, FrequencySpectrum());
+ setPlayPosition(0, true);
+ stopRecording();
+ m_mode = QAudioDevice::Output;
+ connect(m_audioOutput, &QAudioSink::stateChanged, this, &Engine::audioStateChanged);
+
+ m_count = 0;
+ if (m_file) {
+ m_file->seek(0);
+ m_bufferPosition = 0;
+ m_dataLength = 0;
+ m_audioOutput->start(m_file->getDevice());
+ } else {
+ m_audioOutputIODevice.close();
+ m_audioOutputIODevice.setBuffer(&m_buffer);
+ m_audioOutputIODevice.open(QIODevice::ReadOnly);
+ m_audioOutput->start(&m_audioOutputIODevice);
+ }
+ }
+ m_notifyTimer->start();
+ }
+}
+
+void Engine::suspend()
+{
+ if (QAudio::ActiveState == m_state || QAudio::IdleState == m_state) {
+ switch (m_mode) {
+ case QAudioDevice::Input:
+ m_audioInput->suspend();
+ break;
+ case QAudioDevice::Output:
+ m_audioOutput->suspend();
+ break;
+ default:
+ break;
+ }
+ m_notifyTimer->stop();
+ }
+}
+
+void Engine::setAudioInputDevice(const QAudioDevice &device)
+{
+ if (device.id() != m_audioInputDevice.id()) {
+ m_audioInputDevice = device;
+ initialize();
+ }
+}
+
+void Engine::setAudioOutputDevice(const QAudioDevice &device)
+{
+ if (device.id() != m_audioOutputDevice.id()) {
+ m_audioOutputDevice = device;
+ initialize();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Private slots
+//-----------------------------------------------------------------------------
+
+void Engine::initAudioDevices()
+{
+#if QT_CONFIG(permissions)
+ QMicrophonePermission microphonePermission;
+ switch (qApp->checkPermission(microphonePermission)) {
+ case Qt::PermissionStatus::Undetermined:
+ qApp->requestPermission(microphonePermission, this, &Engine::initAudioDevices);
+ return;
+ case Qt::PermissionStatus::Denied:
+ qWarning("Microphone permission is not granted!");
+ return;
+ case Qt::PermissionStatus::Granted:
+ break;
+ }
+#endif
+ m_availableAudioInputDevices = m_devices->audioInputs();
+ m_audioInputDevice = m_devices->defaultAudioInput();
+ m_availableAudioOutputDevices = m_devices->audioOutputs();
+ m_audioOutputDevice = m_devices->defaultAudioOutput();
+}
+
+void Engine::audioNotify()
+{
+ switch (m_mode) {
+ case QAudioDevice::Input: {
+ const qint64 recordPosition =
+ qMin(m_bufferLength, m_format.bytesForDuration(m_audioInput->processedUSecs()));
+ setRecordPosition(recordPosition);
+ const qint64 levelPosition = m_dataLength - m_levelBufferLength;
+ if (levelPosition >= 0)
+ calculateLevel(levelPosition, m_levelBufferLength);
+ if (m_dataLength >= m_spectrumBufferLength) {
+ const qint64 spectrumPosition = m_dataLength - m_spectrumBufferLength;
+ calculateSpectrum(spectrumPosition);
+ }
+ emit bufferChanged(0, m_dataLength, m_buffer);
+ } break;
+ case QAudioDevice::Output: {
+ const qint64 playPosition = m_format.bytesForDuration(m_audioOutput->processedUSecs());
+ setPlayPosition(qMin(bufferLength(), playPosition));
+ const qint64 levelPosition = playPosition - m_levelBufferLength;
+ const qint64 spectrumPosition = playPosition - m_spectrumBufferLength;
+ if (m_file) {
+ if (levelPosition > m_bufferPosition || spectrumPosition > m_bufferPosition
+ || qMax(m_levelBufferLength, m_spectrumBufferLength) > m_dataLength) {
+ m_bufferPosition = 0;
+ m_dataLength = 0;
+ // Data needs to be read into m_buffer in order to be analysed
+ const qint64 readPos = qMax(qint64(0), qMin(levelPosition, spectrumPosition));
+ const qint64 readEnd = qMin(m_analysisFile->getDevice()->size(),
+ qMax(levelPosition + m_levelBufferLength,
+ spectrumPosition + m_spectrumBufferLength));
+ const qint64 readLen =
+ readEnd - readPos + m_format.bytesForDuration(WaveformWindowDuration);
+ qDebug() << "Engine::audioNotify [1]"
+ << "analysisFileSize" << m_analysisFile->getDevice()->size() << "readPos"
+ << readPos << "readLen" << readLen;
+ if (m_analysisFile->seek(readPos + m_analysisFile->headerLength())) {
+ m_buffer.resize(readLen);
+ m_bufferPosition = readPos;
+ m_dataLength = m_analysisFile->read(m_buffer.data(), readLen);
+ qDebug() << "Engine::audioNotify [2]"
+ << "bufferPosition" << m_bufferPosition << "dataLength"
+ << m_dataLength;
+ } else {
+ qDebug() << "Engine::audioNotify [2]"
+ << "file seek error";
+ }
+ emit bufferChanged(m_bufferPosition, m_dataLength, m_buffer);
+ }
+ } else {
+ if (playPosition >= m_dataLength)
+ stopPlayback();
+ }
+ if (levelPosition >= 0
+ && levelPosition + m_levelBufferLength < m_bufferPosition + m_dataLength)
+ calculateLevel(levelPosition, m_levelBufferLength);
+ if (spectrumPosition >= 0
+ && spectrumPosition + m_spectrumBufferLength < m_bufferPosition + m_dataLength)
+ calculateSpectrum(spectrumPosition);
+ } break;
+ default:
+ break;
+ }
+}
+
+void Engine::audioStateChanged(QAudio::State state)
+{
+ ENGINE_DEBUG << "Engine::audioStateChanged from" << m_state << "to" << state;
+
+ if (QAudio::IdleState == state && m_file && m_file->pos() == m_file->getDevice()->size()) {
+ stopPlayback();
+ } else {
+ if (QAudio::StoppedState == state) {
+ // Check error
+ QAudio::Error error = QAudio::NoError;
+ switch (m_mode) {
+ case QAudioDevice::Input:
+ error = m_audioInput->error();
+ break;
+ case QAudioDevice::Output:
+ error = m_audioOutput->error();
+ break;
+ default:
+ break;
+ }
+ if (QAudio::NoError != error) {
+ emitError(error);
+ reset();
+ return;
+ }
+ }
+ setState(state);
+ }
+}
+
+void Engine::audioDataReady()
+{
+ Q_ASSERT(0 == m_bufferPosition);
+ const qint64 bytesReady = m_audioInput->bytesAvailable();
+ const qint64 bytesSpace = m_buffer.size() - m_dataLength;
+ const qint64 bytesToRead = qMin(bytesReady, bytesSpace);
+
+ const qint64 bytesRead =
+ m_audioInputIODevice->read(m_buffer.data() + m_dataLength, bytesToRead);
+
+ if (bytesRead) {
+ m_dataLength += bytesRead;
+ emit dataLengthChanged(dataLength());
+ }
+
+ if (m_buffer.size() == m_dataLength)
+ stopRecording();
+}
+
+void Engine::spectrumChanged(const FrequencySpectrum &spectrum)
+{
+ ENGINE_DEBUG << "Engine::spectrumChanged"
+ << "pos" << m_spectrumPosition;
+ emit spectrumChanged(m_spectrumPosition, m_spectrumBufferLength, spectrum);
+}
+
+//-----------------------------------------------------------------------------
+// Private functions
+//-----------------------------------------------------------------------------
+
+void Engine::resetAudioDevices()
+{
+ delete m_audioInput;
+ m_audioInput = nullptr;
+ m_audioInputIODevice = nullptr;
+ setRecordPosition(0);
+ delete m_audioOutput;
+ m_audioOutput = nullptr;
+ setPlayPosition(0);
+ m_spectrumPosition = 0;
+ setLevel(0.0, 0.0, 0);
+}
+
+void Engine::reset()
+{
+ stopRecording();
+ stopPlayback();
+ setState(QAudioDevice::Input, QAudio::StoppedState);
+ m_generateTone = false;
+ setFormat(QAudioFormat());
+ delete m_file;
+ m_file = nullptr;
+ delete m_analysisFile;
+ m_analysisFile = nullptr;
+ m_buffer.clear();
+ m_bufferPosition = 0;
+ m_bufferLength = 0;
+ m_dataLength = 0;
+ emit dataLengthChanged(0);
+ resetAudioDevices();
+}
+
+bool Engine::initialize()
+{
+ bool result = false;
+
+ QAudioFormat format = m_format;
+
+ if (selectFormat()) {
+ if (m_format != format) {
+ resetAudioDevices();
+ if (m_file) {
+ emit bufferLengthChanged(bufferLength());
+ emit dataLengthChanged(dataLength());
+ emit bufferChanged(0, 0, m_buffer);
+ setRecordPosition(bufferLength());
+ result = true;
+ } else {
+ m_bufferLength = m_format.bytesForDuration(BufferDurationUs);
+ m_buffer.resize(m_bufferLength);
+ m_buffer.fill(0);
+ emit bufferLengthChanged(bufferLength());
+ if (m_generateTone) {
+ if (0 == m_tone.endFreq) {
+ const qreal nyquist = nyquistFrequency(m_format);
+ m_tone.endFreq = qMin(qreal(SpectrumHighFreq), nyquist);
+ }
+ // Call function defined in utils.h, at global scope
+ ::generateTone(m_tone, m_format, m_buffer);
+ m_dataLength = m_bufferLength;
+ emit dataLengthChanged(dataLength());
+ emit bufferChanged(0, m_dataLength, m_buffer);
+ setRecordPosition(m_bufferLength);
+ result = true;
+ } else {
+ emit bufferChanged(0, 0, m_buffer);
+ m_audioInput = new QAudioSource(m_audioInputDevice, m_format, this);
+ result = true;
+ }
+ }
+ m_audioOutput = new QAudioSink(m_audioOutputDevice, m_format, this);
+ }
+ } else {
+ if (m_file)
+ emit errorMessage(tr("Audio format not supported"), formatToString(m_format));
+ else if (m_generateTone)
+ emit errorMessage(tr("No suitable format found"), "");
+ else
+ emit errorMessage(tr("No common input / output format found"), "");
+ }
+
+ ENGINE_DEBUG << "Engine::initialize"
+ << "m_bufferLength" << m_bufferLength;
+ ENGINE_DEBUG << "Engine::initialize"
+ << "m_dataLength" << m_dataLength;
+ ENGINE_DEBUG << "Engine::initialize"
+ << "format" << m_format;
+
+ return result;
+}
+
+bool Engine::selectFormat()
+{
+ bool foundSupportedFormat = false;
+
+ if (m_file || QAudioFormat() != m_format) {
+ QAudioFormat format = m_format;
+ if (m_file)
+ // Header is read from the WAV file; just need to check whether
+ // it is supported by the audio output device
+ format = m_file->audioFormat();
+ if (m_audioOutputDevice.isFormatSupported(format)) {
+ setFormat(format);
+ foundSupportedFormat = true;
+ }
+ } else {
+
+ int minSampleRate = qMin(m_audioInputDevice.minimumSampleRate(),
+ m_audioOutputDevice.minimumSampleRate());
+ int maxSampleRate = qMin(m_audioInputDevice.maximumSampleRate(),
+ m_audioOutputDevice.maximumSampleRate());
+ int minChannelCount = qMin(m_audioInputDevice.minimumChannelCount(),
+ m_audioOutputDevice.minimumChannelCount());
+ int maxChannelCount = qMin(m_audioInputDevice.maximumChannelCount(),
+ m_audioOutputDevice.maximumChannelCount());
+
+ QAudioFormat format;
+ format.setSampleFormat(QAudioFormat::Int16);
+ format.setSampleRate(qBound(minSampleRate, 48000, maxSampleRate));
+ format.setChannelCount(qBound(minChannelCount, 2, maxChannelCount));
+
+ const bool inputSupport = m_audioInputDevice.isFormatSupported(format);
+ const bool outputSupport = m_audioOutputDevice.isFormatSupported(format);
+ if (inputSupport && outputSupport)
+ foundSupportedFormat = true;
+
+ setFormat(format);
+ }
+
+ return foundSupportedFormat;
+}
+
+void Engine::stopRecording()
+{
+ if (m_audioInput) {
+ m_audioInput->stop();
+ QCoreApplication::instance()->processEvents();
+ m_audioInput->disconnect();
+ }
+ m_audioInputIODevice = nullptr;
+ m_notifyTimer->stop();
+
+#ifdef DUMP_AUDIO
+ dumpData();
+#endif
+}
+
+void Engine::stopPlayback()
+{
+ if (m_audioOutput) {
+ m_audioOutput->stop();
+ QCoreApplication::instance()->processEvents();
+ m_audioOutput->disconnect();
+ setPlayPosition(0);
+ }
+ m_notifyTimer->stop();
+}
+
+void Engine::setState(QAudio::State state)
+{
+ const bool changed = (m_state != state);
+ m_state = state;
+ if (changed)
+ emit stateChanged(m_mode, m_state);
+}
+
+void Engine::setState(QAudioDevice::Mode mode, QAudio::State state)
+{
+ const bool changed = (m_mode != mode || m_state != state);
+ m_mode = mode;
+ m_state = state;
+ if (changed)
+ emit stateChanged(m_mode, m_state);
+}
+
+void Engine::setRecordPosition(qint64 position, bool forceEmit)
+{
+ const bool changed = (m_recordPosition != position);
+ m_recordPosition = position;
+ if (changed || forceEmit)
+ emit recordPositionChanged(m_recordPosition);
+}
+
+void Engine::setPlayPosition(qint64 position, bool forceEmit)
+{
+ const bool changed = (m_playPosition != position);
+ m_playPosition = position;
+ if (changed || forceEmit)
+ emit playPositionChanged(m_playPosition);
+}
+
+void Engine::calculateLevel(qint64 position, qint64 length)
+{
+#ifdef DISABLE_LEVEL
+ Q_UNUSED(position);
+ Q_UNUSED(length);
+#else
+ Q_ASSERT(position + length <= m_bufferPosition + m_dataLength);
+
+ qreal peakLevel = 0.0;
+
+ qreal sum = 0.0;
+ const char *ptr = m_buffer.constData() + position - m_bufferPosition;
+ const char *const end = ptr + length;
+ while (ptr < end) {
+ const qint16 value = *reinterpret_cast<const qint16 *>(ptr);
+ const qreal fracValue = pcmToReal(value);
+ peakLevel = qMax(peakLevel, fracValue);
+ sum += fracValue * fracValue;
+ ptr += 2;
+ }
+ const int numSamples = length / 2;
+ qreal rmsLevel = sqrt(sum / numSamples);
+
+ rmsLevel = qMax(qreal(0.0), rmsLevel);
+ rmsLevel = qMin(qreal(1.0), rmsLevel);
+ setLevel(rmsLevel, peakLevel, numSamples);
+
+ ENGINE_DEBUG << "Engine::calculateLevel"
+ << "pos" << position << "len" << length << "rms" << rmsLevel << "peak"
+ << peakLevel;
+#endif
+}
+
+void Engine::calculateSpectrum(qint64 position)
+{
+#ifdef DISABLE_SPECTRUM
+ Q_UNUSED(position);
+#else
+ Q_ASSERT(position + m_spectrumBufferLength <= m_bufferPosition + m_dataLength);
+ Q_ASSERT(0 == m_spectrumBufferLength % 2); // constraint of FFT algorithm
+
+ // QThread::currentThread is marked 'for internal use only', but
+ // we're only using it for debug output here, so it's probably OK :)
+ ENGINE_DEBUG << "Engine::calculateSpectrum" << QThread::currentThread() << "count" << m_count
+ << "pos" << position << "len" << m_spectrumBufferLength
+ << "spectrumAnalyser.isReady" << m_spectrumAnalyser.isReady();
+
+ if (m_spectrumAnalyser.isReady()) {
+ m_spectrumBuffer = QByteArray::fromRawData(
+ m_buffer.constData() + position - m_bufferPosition, m_spectrumBufferLength);
+ m_spectrumPosition = position;
+ m_spectrumAnalyser.calculate(m_spectrumBuffer, m_format);
+ }
+#endif
+}
+
+void Engine::setFormat(const QAudioFormat &format)
+{
+ const bool changed = (format != m_format);
+ m_format = format;
+ m_levelBufferLength = m_format.bytesForDuration(LevelWindowUs);
+ m_spectrumBufferLength = SpectrumLengthSamples * format.bytesPerFrame();
+ if (changed)
+ emit formatChanged(m_format);
+}
+
+void Engine::setLevel(qreal rmsLevel, qreal peakLevel, int numSamples)
+{
+ m_rmsLevel = rmsLevel;
+ m_peakLevel = peakLevel;
+ emit levelChanged(m_rmsLevel, m_peakLevel, numSamples);
+}
+
+void Engine::emitError(QAudio::Error error)
+{
+ QString errorString;
+ switch (error) {
+ case QAudio::NoError:
+ errorString = tr("NoError");
+ break;
+ case QAudio::OpenError:
+ errorString = tr("OpenError: An error occurred opening the audio device.");
+ break;
+ case QAudio::IOError:
+ errorString = tr("IOError: An error occurred during read/write of audio device.");
+ break;
+ case QAudio::UnderrunError:
+ errorString = tr("UnderrunError: Audio data is not being fed"
+ "to the audio device at a fast enough rate.");
+ break;
+ case QAudio::FatalError:
+ errorString = tr("FatalError: A non-recoverable error has occurred,"
+ "the audio device is not usable at this time.");
+ break;
+ }
+
+ emit errorMessage(tr("Audio Device"), errorString);
+}
+
+#ifdef DUMP_DATA
+void Engine::createOutputDir()
+{
+ m_outputDir.setPath("output");
+
+ // Ensure output directory exists and is empty
+ if (m_outputDir.exists()) {
+ const QStringList files = m_outputDir.entryList(QDir::Files);
+ for (const QString &file : files)
+ m_outputDir.remove(file);
+ } else {
+ QDir::current().mkdir("output");
+ }
+}
+#endif // DUMP_DATA
+
+#ifdef DUMP_AUDIO
+void Engine::dumpData()
+{
+ const QString txtFileName = m_outputDir.filePath("data.txt");
+ QFile txtFile(txtFileName);
+ txtFile.open(QFile::WriteOnly | QFile::Text);
+ QTextStream stream(&txtFile);
+ const qint16 *ptr = reinterpret_cast<const qint16 *>(m_buffer.constData());
+ const int numSamples = m_dataLength / (2 * m_format.channels());
+ for (int i = 0; i < numSamples; ++i) {
+ stream << i << "\t" << *ptr << "\n";
+ ptr += m_format.channels();
+ }
+
+ const QString pcmFileName = m_outputDir.filePath("data.pcm");
+ QFile pcmFile(pcmFileName);
+ pcmFile.open(QFile::WriteOnly);
+ pcmFile.write(m_buffer.constData(), m_dataLength);
+}
+#endif // DUMP_AUDIO
+
+#include "moc_engine.cpp"
diff --git a/examples/multimedia/spectrum/app/engine.h b/examples/multimedia/spectrum/engine.h
index 714af4d9d..9e8ba51d0 100644
--- a/examples/multimedia/spectrum/app/engine.h
+++ b/examples/multimedia/spectrum/engine.h
@@ -1,80 +1,35 @@
-/****************************************************************************
-**
-** 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 ENGINE_H
#define ENGINE_H
#include "spectrum.h"
#include "spectrumanalyser.h"
-#include "wavfile.h"
-#include <QAudioDeviceInfo>
+#include <QAudioDevice>
#include <QAudioFormat>
#include <QBuffer>
#include <QByteArray>
#include <QDir>
#include <QList>
+#include <QMediaDevices>
#include <QObject>
+#include <QTimer>
+#include <QWaveDecoder>
#ifdef DUMP_CAPTURED_AUDIO
-#define DUMP_DATA
+# define DUMP_DATA
#endif
#ifdef DUMP_SPECTRUM
-#define DUMP_DATA
+# define DUMP_DATA
#endif
class FrequencySpectrum;
QT_BEGIN_NAMESPACE
-class QAudioInput;
-class QAudioOutput;
+class QAudioSource;
+class QAudioSink;
QT_END_NAMESPACE
/**
@@ -88,29 +43,32 @@ class Engine : public QObject
Q_OBJECT
public:
- explicit Engine(QObject *parent = 0);
+ explicit Engine(QObject *parent = nullptr);
~Engine();
- const QList<QAudioDeviceInfo> &availableAudioInputDevices() const
- { return m_availableAudioInputDevices; }
+ const QList<QAudioDevice> &availableAudioInputDevices() const
+ {
+ return m_availableAudioInputDevices;
+ }
- const QList<QAudioDeviceInfo> &availableAudioOutputDevices() const
- { return m_availableAudioOutputDevices; }
+ const QList<QAudioDevice> &availableAudioOutputDevices() const
+ {
+ return m_availableAudioOutputDevices;
+ }
- QAudio::Mode mode() const { return m_mode; }
+ QAudioDevice::Mode mode() const { return m_mode; }
QAudio::State state() const { return m_state; }
/**
* \return Current audio format
* \note May be QAudioFormat() if engine is not initialized
*/
- const QAudioFormat& format() const { return m_format; }
+ const QAudioFormat &format() const { return m_format; }
/**
* Stop any ongoing recording or playback, and reset to ground state.
*/
void reset();
-
/**
* Load data from WAV file
*/
@@ -176,11 +134,11 @@ public slots:
void startRecording();
void startPlayback();
void suspend();
- void setAudioInputDevice(const QAudioDeviceInfo &device);
- void setAudioOutputDevice(const QAudioDeviceInfo &device);
+ void setAudioInputDevice(const QAudioDevice &device);
+ void setAudioOutputDevice(const QAudioDevice &device);
signals:
- void stateChanged(QAudio::Mode mode, QAudio::State state);
+ void stateChanged(QAudioDevice::Mode mode, QAudio::State state);
/**
* Informational message for non-modal display
@@ -245,6 +203,7 @@ signals:
void bufferChanged(qint64 position, qint64 length, const QByteArray &buffer);
private slots:
+ void initAudioDevices();
void audioNotify();
void audioStateChanged(QAudio::State state);
void audioDataReady();
@@ -257,17 +216,21 @@ private:
void stopRecording();
void stopPlayback();
void setState(QAudio::State state);
- void setState(QAudio::Mode mode, QAudio::State state);
+ void setState(QAudioDevice::Mode mode, QAudio::State state);
void setFormat(const QAudioFormat &format);
void setRecordPosition(qint64 position, bool forceEmit = false);
void setPlayPosition(qint64 position, bool forceEmit = false);
void calculateLevel(qint64 position, qint64 length);
void calculateSpectrum(qint64 position);
void setLevel(qreal rmsLevel, qreal peakLevel, int numSamples);
+ void emitError(QAudio::Error error);
#ifdef DUMP_DATA
void createOutputDir();
- QString outputPath() const { return m_outputDir.path(); }
+ QString outputPath() const
+ {
+ return m_outputDir.path();
+ }
#endif
#ifdef DUMP_CAPTURED_AUDIO
@@ -275,52 +238,52 @@ private:
#endif
private:
- QAudio::Mode m_mode;
- QAudio::State m_state;
+ QAudioDevice::Mode m_mode;
+ QAudio::State m_state;
+ QMediaDevices *m_devices;
- bool m_generateTone;
- SweptTone m_tone;
+ bool m_generateTone;
+ SweptTone m_tone;
- WavFile* m_file;
+ QWaveDecoder *m_file;
// We need a second file handle via which to read data into m_buffer
// for analysis
- WavFile* m_analysisFile;
+ QWaveDecoder *m_analysisFile;
- QAudioFormat m_format;
+ QAudioFormat m_format;
- const QList<QAudioDeviceInfo> m_availableAudioInputDevices;
- QAudioDeviceInfo m_audioInputDevice;
- QAudioInput* m_audioInput;
- QIODevice* m_audioInputIODevice;
- qint64 m_recordPosition;
+ QList<QAudioDevice> m_availableAudioInputDevices;
+ QAudioDevice m_audioInputDevice;
+ QAudioSource *m_audioInput;
+ QIODevice *m_audioInputIODevice;
+ qint64 m_recordPosition;
- const QList<QAudioDeviceInfo> m_availableAudioOutputDevices;
- QAudioDeviceInfo m_audioOutputDevice;
- QAudioOutput* m_audioOutput;
- QString m_audioOutputCategory;
- qint64 m_playPosition;
- QBuffer m_audioOutputIODevice;
+ QList<QAudioDevice> m_availableAudioOutputDevices;
+ QAudioDevice m_audioOutputDevice;
+ QAudioSink *m_audioOutput;
+ qint64 m_playPosition;
+ QBuffer m_audioOutputIODevice;
- QByteArray m_buffer;
- qint64 m_bufferPosition;
- qint64 m_bufferLength;
- qint64 m_dataLength;
+ QByteArray m_buffer;
+ qint64 m_bufferPosition;
+ qint64 m_bufferLength;
+ qint64 m_dataLength;
- int m_levelBufferLength;
- qreal m_rmsLevel;
- qreal m_peakLevel;
+ int m_levelBufferLength;
+ qreal m_rmsLevel;
+ qreal m_peakLevel;
- int m_spectrumBufferLength;
- QByteArray m_spectrumBuffer;
- SpectrumAnalyser m_spectrumAnalyser;
- qint64 m_spectrumPosition;
+ int m_spectrumBufferLength;
+ QByteArray m_spectrumBuffer;
+ SpectrumAnalyser m_spectrumAnalyser;
+ qint64 m_spectrumPosition;
- int m_count;
+ int m_count;
+ QTimer *m_notifyTimer = nullptr;
#ifdef DUMP_DATA
- QDir m_outputDir;
+ QDir m_outputDir;
#endif
-
};
#endif // ENGINE_H
diff --git a/examples/multimedia/spectrum/frequencyspectrum.cpp b/examples/multimedia/spectrum/frequencyspectrum.cpp
new file mode 100644
index 000000000..f271a04aa
--- /dev/null
+++ b/examples/multimedia/spectrum/frequencyspectrum.cpp
@@ -0,0 +1,48 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "frequencyspectrum.h"
+
+FrequencySpectrum::FrequencySpectrum(int numPoints) : m_elements(numPoints) { }
+
+void FrequencySpectrum::reset()
+{
+ iterator i = begin();
+ for (; i != end(); ++i)
+ *i = Element();
+}
+
+int FrequencySpectrum::count() const
+{
+ return m_elements.count();
+}
+
+FrequencySpectrum::Element &FrequencySpectrum::operator[](int index)
+{
+ return m_elements[index];
+}
+
+const FrequencySpectrum::Element &FrequencySpectrum::operator[](int index) const
+{
+ return m_elements[index];
+}
+
+FrequencySpectrum::iterator FrequencySpectrum::begin()
+{
+ return m_elements.begin();
+}
+
+FrequencySpectrum::iterator FrequencySpectrum::end()
+{
+ return m_elements.end();
+}
+
+FrequencySpectrum::const_iterator FrequencySpectrum::begin() const
+{
+ return m_elements.begin();
+}
+
+FrequencySpectrum::const_iterator FrequencySpectrum::end() const
+{
+ return m_elements.end();
+}
diff --git a/examples/multimedia/spectrum/frequencyspectrum.h b/examples/multimedia/spectrum/frequencyspectrum.h
new file mode 100644
index 000000000..9f3771571
--- /dev/null
+++ b/examples/multimedia/spectrum/frequencyspectrum.h
@@ -0,0 +1,60 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef FREQUENCYSPECTRUM_H
+#define FREQUENCYSPECTRUM_H
+
+#include <QList>
+
+/**
+ * Represents a frequency spectrum as a series of elements, each of which
+ * consists of a frequency, an amplitude and a phase.
+ */
+class FrequencySpectrum
+{
+public:
+ FrequencySpectrum(int numPoints = 0);
+
+ struct Element
+ {
+ Element() : frequency(0.0), amplitude(0.0), phase(0.0), clipped(false) { }
+
+ /**
+ * Frequency in Hertz
+ */
+ qreal frequency;
+
+ /**
+ * Amplitude in range [0.0, 1.0]
+ */
+ qreal amplitude;
+
+ /**
+ * Phase in range [0.0, 2*PI]
+ */
+ qreal phase;
+
+ /**
+ * Indicates whether value has been clipped during spectrum analysis
+ */
+ bool clipped;
+ };
+
+ typedef QList<Element>::iterator iterator;
+ typedef QList<Element>::const_iterator const_iterator;
+
+ void reset();
+
+ int count() const;
+ Element &operator[](int index);
+ const Element &operator[](int index) const;
+ iterator begin();
+ iterator end();
+ const_iterator begin() const;
+ const_iterator end() const;
+
+private:
+ QList<Element> m_elements;
+};
+
+#endif // FREQUENCYSPECTRUM_H
diff --git a/examples/multimedia/spectrum/app/images/record.png b/examples/multimedia/spectrum/images/record.png
index 184fce809..184fce809 100644
--- a/examples/multimedia/spectrum/app/images/record.png
+++ b/examples/multimedia/spectrum/images/record.png
Binary files differ
diff --git a/examples/multimedia/spectrum/app/images/settings.png b/examples/multimedia/spectrum/images/settings.png
index 12179dc9a..12179dc9a 100644
--- a/examples/multimedia/spectrum/app/images/settings.png
+++ b/examples/multimedia/spectrum/images/settings.png
Binary files differ
diff --git a/examples/multimedia/spectrum/levelmeter.cpp b/examples/multimedia/spectrum/levelmeter.cpp
new file mode 100644
index 000000000..2c889c34d
--- /dev/null
+++ b/examples/multimedia/spectrum/levelmeter.cpp
@@ -0,0 +1,103 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "levelmeter.h"
+
+#include <QDebug>
+#include <QPainter>
+#include <QTimer>
+
+#include <math.h>
+
+// Constants
+const int RedrawInterval = 100; // ms
+const qreal PeakDecayRate = 0.001;
+const int PeakHoldLevelDuration = 2000; // ms
+
+LevelMeter::LevelMeter(QWidget *parent)
+ : QWidget(parent),
+ m_rmsLevel(0.0),
+ m_peakLevel(0.0),
+ m_decayedPeakLevel(0.0),
+ m_peakDecayRate(PeakDecayRate),
+ m_peakHoldLevel(0.0),
+ m_redrawTimer(new QTimer(this)),
+ m_rmsColor(Qt::red),
+ m_peakColor(255, 200, 200, 255)
+{
+ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
+ setMinimumWidth(30);
+
+ connect(m_redrawTimer, &QTimer::timeout, this, &LevelMeter::redrawTimerExpired);
+ m_redrawTimer->start(RedrawInterval);
+}
+
+LevelMeter::~LevelMeter() = default;
+
+void LevelMeter::reset()
+{
+ m_rmsLevel = 0.0;
+ m_peakLevel = 0.0;
+ update();
+}
+
+void LevelMeter::levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples)
+{
+ // Smooth the RMS signal
+ const qreal smooth =
+ pow(qreal(0.9), static_cast<qreal>(numSamples) / 256); // TODO: remove this magic number
+ m_rmsLevel = (m_rmsLevel * smooth) + (rmsLevel * (1.0 - smooth));
+
+ if (peakLevel > m_decayedPeakLevel) {
+ m_peakLevel = peakLevel;
+ m_decayedPeakLevel = peakLevel;
+ m_peakLevelChanged.start();
+ }
+
+ if (peakLevel > m_peakHoldLevel) {
+ m_peakHoldLevel = peakLevel;
+ m_peakHoldLevelChanged.start();
+ }
+
+ update();
+}
+
+void LevelMeter::redrawTimerExpired()
+{
+ // Decay the peak signal
+ const int elapsedMs = m_peakLevelChanged.elapsed();
+ const qreal decayAmount = m_peakDecayRate * elapsedMs;
+ if (decayAmount < m_peakLevel)
+ m_decayedPeakLevel = m_peakLevel - decayAmount;
+ else
+ m_decayedPeakLevel = 0.0;
+
+ // Check whether to clear the peak hold level
+ if (m_peakHoldLevelChanged.elapsed() > PeakHoldLevelDuration)
+ m_peakHoldLevel = 0.0;
+
+ update();
+}
+
+void LevelMeter::paintEvent(QPaintEvent *event)
+{
+ Q_UNUSED(event);
+
+ QPainter painter(this);
+ painter.fillRect(rect(), Qt::black);
+
+ QRect bar = rect();
+
+ bar.setTop(rect().top() + (1.0 - m_peakHoldLevel) * rect().height());
+ bar.setBottom(bar.top() + 5);
+ painter.fillRect(bar, m_rmsColor);
+ bar.setBottom(rect().bottom());
+
+ bar.setTop(rect().top() + (1.0 - m_decayedPeakLevel) * rect().height());
+ painter.fillRect(bar, m_peakColor);
+
+ bar.setTop(rect().top() + (1.0 - m_rmsLevel) * rect().height());
+ painter.fillRect(bar, m_rmsColor);
+}
+
+#include "moc_levelmeter.cpp"
diff --git a/examples/multimedia/spectrum/levelmeter.h b/examples/multimedia/spectrum/levelmeter.h
new file mode 100644
index 000000000..e131bc904
--- /dev/null
+++ b/examples/multimedia/spectrum/levelmeter.h
@@ -0,0 +1,80 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef LEVELMETER_H
+#define LEVELMETER_H
+
+#include <QElapsedTimer>
+#include <QWidget>
+
+/**
+ * Widget which displays a vertical audio level meter, indicating the
+ * RMS and peak levels of the window of audio samples most recently analyzed
+ * by the Engine.
+ */
+class LevelMeter : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit LevelMeter(QWidget *parent = nullptr);
+ ~LevelMeter();
+
+ void paintEvent(QPaintEvent *event) override;
+
+public slots:
+ void reset();
+ void levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples);
+
+private slots:
+ void redrawTimerExpired();
+
+private:
+ /**
+ * Height of RMS level bar.
+ * Range 0.0 - 1.0.
+ */
+ qreal m_rmsLevel;
+
+ /**
+ * Most recent peak level.
+ * Range 0.0 - 1.0.
+ */
+ qreal m_peakLevel;
+
+ /**
+ * Height of peak level bar.
+ * This is calculated by decaying m_peakLevel depending on the
+ * elapsed time since m_peakLevelChanged, and the value of m_decayRate.
+ */
+ qreal m_decayedPeakLevel;
+
+ /**
+ * Time at which m_peakLevel was last changed.
+ */
+ QElapsedTimer m_peakLevelChanged;
+
+ /**
+ * Rate at which peak level bar decays.
+ * Expressed in level units / millisecond.
+ */
+ qreal m_peakDecayRate;
+
+ /**
+ * High watermark of peak level.
+ * Range 0.0 - 1.0.
+ */
+ qreal m_peakHoldLevel;
+
+ /**
+ * Time at which m_peakHoldLevel was last changed.
+ */
+ QElapsedTimer m_peakHoldLevelChanged;
+
+ QTimer *m_redrawTimer;
+
+ QColor m_rmsColor;
+ QColor m_peakColor;
+};
+
+#endif // LEVELMETER_H
diff --git a/examples/multimedia/spectrum/main.cpp b/examples/multimedia/spectrum/main.cpp
new file mode 100644
index 000000000..31913b2f2
--- /dev/null
+++ b/examples/multimedia/spectrum/main.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "mainwidget.h"
+
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ app.setApplicationName("Qt Multimedia spectrum analyzer");
+
+ MainWidget w;
+ w.show();
+
+ return app.exec();
+}
diff --git a/examples/multimedia/spectrum/app/mainwidget.cpp b/examples/multimedia/spectrum/mainwidget.cpp
index d6163f7f9..214d317d9 100644
--- a/examples/multimedia/spectrum/app/mainwidget.cpp
+++ b/examples/multimedia/spectrum/mainwidget.cpp
@@ -1,101 +1,55 @@
-/****************************************************************************
-**
-** 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 "mainwidget.h"
#include "engine.h"
#include "levelmeter.h"
-#include "mainwidget.h"
-#include "waveform.h"
#include "progressbar.h"
#include "settingsdialog.h"
#include "spectrograph.h"
#include "tonegeneratordialog.h"
#include "utils.h"
+#include "waveform.h"
+#include <QFileDialog>
+#include <QHBoxLayout>
#include <QLabel>
+#include <QMenu>
+#include <QMessageBox>
#include <QPushButton>
-#include <QHBoxLayout>
-#include <QVBoxLayout>
#include <QStyle>
-#include <QMenu>
-#include <QFileDialog>
#include <QTimerEvent>
-#include <QMessageBox>
+#include <QVBoxLayout>
const int NullTimerId = -1;
MainWidget::MainWidget(QWidget *parent)
- : QWidget(parent)
- , m_mode(NoMode)
- , m_engine(new Engine(this))
+ : QWidget(parent),
+ m_mode(NoMode),
+ m_engine(new Engine(this))
#ifndef DISABLE_WAVEFORM
- , m_waveform(new Waveform(this))
+ ,
+ m_waveform(new Waveform(this))
#endif
- , m_progressBar(new ProgressBar(this))
- , m_spectrograph(new Spectrograph(this))
- , m_levelMeter(new LevelMeter(this))
- , m_modeButton(new QPushButton(this))
- , m_recordButton(new QPushButton(this))
- , m_pauseButton(new QPushButton(this))
- , m_playButton(new QPushButton(this))
- , m_settingsButton(new QPushButton(this))
- , m_infoMessage(new QLabel(tr("Select a mode to begin"), this))
- , m_infoMessageTimerId(NullTimerId)
- , m_settingsDialog(new SettingsDialog(
- m_engine->availableAudioInputDevices(),
- m_engine->availableAudioOutputDevices(),
- this))
- , m_toneGeneratorDialog(new ToneGeneratorDialog(this))
- , m_modeMenu(new QMenu(this))
- , m_loadFileAction(0)
- , m_generateToneAction(0)
- , m_recordAction(0)
+ ,
+ m_progressBar(new ProgressBar(this)),
+ m_spectrograph(new Spectrograph(this)),
+ m_levelMeter(new LevelMeter(this)),
+ m_modeButton(new QPushButton(this)),
+ m_recordButton(new QPushButton(this)),
+ m_pauseButton(new QPushButton(this)),
+ m_playButton(new QPushButton(this)),
+ m_settingsButton(new QPushButton(this)),
+ m_infoMessage(new QLabel(tr("Select a mode to begin"), this)),
+ m_infoMessageTimerId(NullTimerId),
+ m_settingsDialog(new SettingsDialog(m_engine->availableAudioInputDevices(),
+ m_engine->availableAudioOutputDevices(), this)),
+ m_toneGeneratorDialog(new ToneGeneratorDialog(this)),
+ m_modeMenu(new QMenu(this)),
+ m_loadFileAction(nullptr),
+ m_generateToneAction(nullptr),
+ m_recordAction(nullptr),
+ m_errorOccurred(false)
{
m_spectrograph->setParams(SpectrumNumBands, SpectrumLowFreq, SpectrumHighFreq);
@@ -103,25 +57,19 @@ MainWidget::MainWidget(QWidget *parent)
connectUi();
}
-MainWidget::~MainWidget()
-{
-
-}
-
+MainWidget::~MainWidget() = default;
//-----------------------------------------------------------------------------
// Public slots
//-----------------------------------------------------------------------------
-void MainWidget::stateChanged(QAudio::Mode mode, QAudio::State state)
+void MainWidget::stateChanged(QAudioDevice::Mode mode, QAudio::State state)
{
Q_UNUSED(mode);
updateButtonStates();
- if (QAudio::ActiveState != state &&
- QAudio::SuspendedState != state &&
- QAudio::InterruptedState != state) {
+ if (QAudio::ActiveState != state && QAudio::SuspendedState != state) {
m_levelMeter->reset();
m_spectrograph->reset();
}
@@ -129,18 +77,16 @@ void MainWidget::stateChanged(QAudio::Mode mode, QAudio::State state)
void MainWidget::formatChanged(const QAudioFormat &format)
{
- infoMessage(formatToString(format), NullMessageTimeout);
+ infoMessage(formatToString(format), NullMessageTimeout);
#ifndef DISABLE_WAVEFORM
if (QAudioFormat() != format) {
- m_waveform->initialize(format, WaveformTileLength,
- WaveformWindowDuration);
+ m_waveform->initialize(format, WaveformTileLength, WaveformWindowDuration);
}
#endif
}
-void MainWidget::spectrumChanged(qint64 position, qint64 length,
- const FrequencySpectrum &spectrum)
+void MainWidget::spectrumChanged(qint64 position, qint64 length, const FrequencySpectrum &spectrum)
{
m_progressBar->windowChanged(position, length);
m_spectrograph->spectrumChanged(spectrum);
@@ -162,6 +108,8 @@ void MainWidget::infoMessage(const QString &message, int timeoutMs)
void MainWidget::errorMessage(const QString &heading, const QString &detail)
{
QMessageBox::warning(this, heading, detail, QMessageBox::Close);
+ m_errorOccurred = true;
+ reset();
}
void MainWidget::timerEvent(QTimerEvent *event)
@@ -187,15 +135,16 @@ void MainWidget::bufferLengthChanged(qint64 length)
m_progressBar->bufferLengthChanged(length);
}
-
//-----------------------------------------------------------------------------
// Private slots
//-----------------------------------------------------------------------------
void MainWidget::showFileDialog()
{
+ m_errorOccurred = false;
const QString dir;
- const QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Open WAV file"), dir, "*.wav");
+ const QStringList fileNames =
+ QFileDialog::getOpenFileNames(this, tr("Open WAV file"), dir, "*.wav");
if (fileNames.count()) {
reset();
setMode(LoadFileMode);
@@ -218,6 +167,7 @@ void MainWidget::showSettingsDialog()
void MainWidget::showToneGeneratorDialog()
{
+ m_errorOccurred = false;
m_toneGeneratorDialog->exec();
if (m_toneGeneratorDialog->result() == QDialog::Accepted) {
reset();
@@ -232,19 +182,20 @@ void MainWidget::showToneGeneratorDialog()
updateButtonStates();
}
} else {
+ setMode(NoMode);
updateModeMenu();
}
}
void MainWidget::initializeRecord()
{
+ m_errorOccurred = false;
reset();
setMode(RecordMode);
if (m_engine->initializeRecord())
updateButtonStates();
}
-
//-----------------------------------------------------------------------------
// Private functions
//-----------------------------------------------------------------------------
@@ -262,27 +213,25 @@ void MainWidget::createUi()
windowLayout->addWidget(m_infoMessage);
#ifdef SUPERIMPOSE_PROGRESS_ON_WAVEFORM
- QScopedPointer<QHBoxLayout> waveformLayout(new QHBoxLayout);
+ std::unique_ptr<QHBoxLayout> waveformLayout(new QHBoxLayout);
waveformLayout->addWidget(m_progressBar);
m_progressBar->setMinimumHeight(m_waveform->minimumHeight());
waveformLayout->setContentsMargins(0, 0, 0, 0);
- m_waveform->setLayout(waveformLayout.data());
- waveformLayout.take();
+ m_waveform->setLayout(waveformLayout.release());
windowLayout->addWidget(m_waveform);
#else
-#ifndef DISABLE_WAVEFORM
+# ifndef DISABLE_WAVEFORM
windowLayout->addWidget(m_waveform);
-#endif // DISABLE_WAVEFORM
+# endif // DISABLE_WAVEFORM
windowLayout->addWidget(m_progressBar);
#endif // SUPERIMPOSE_PROGRESS_ON_WAVEFORM
// Spectrograph and level meter
- QScopedPointer<QHBoxLayout> analysisLayout(new QHBoxLayout);
+ std::unique_ptr<QHBoxLayout> analysisLayout(new QHBoxLayout);
analysisLayout->addWidget(m_spectrograph);
analysisLayout->addWidget(m_levelMeter);
- windowLayout->addLayout(analysisLayout.data());
- analysisLayout.take();
+ windowLayout->addLayout(analysisLayout.release());
// Button panel
@@ -314,7 +263,7 @@ void MainWidget::createUi()
m_settingsButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
m_settingsButton->setMinimumSize(buttonSize);
- QScopedPointer<QHBoxLayout> buttonPanelLayout(new QHBoxLayout);
+ std::unique_ptr<QHBoxLayout> buttonPanelLayout(new QHBoxLayout);
buttonPanelLayout->addStretch();
buttonPanelLayout->addWidget(m_modeButton);
buttonPanelLayout->addWidget(m_recordButton);
@@ -324,13 +273,11 @@ void MainWidget::createUi()
QWidget *buttonPanel = new QWidget(this);
buttonPanel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
- buttonPanel->setLayout(buttonPanelLayout.data());
- buttonPanelLayout.take(); // ownership transferred to buttonPanel
+ buttonPanel->setLayout(buttonPanelLayout.release());
- QScopedPointer<QHBoxLayout> bottomPaneLayout(new QHBoxLayout);
+ std::unique_ptr<QHBoxLayout> bottomPaneLayout(new QHBoxLayout);
bottomPaneLayout->addWidget(buttonPanel);
- windowLayout->addLayout(bottomPaneLayout.data());
- bottomPaneLayout.take(); // ownership transferred to windowLayout
+ windowLayout->addLayout(bottomPaneLayout.release());
// Apply layout
@@ -339,62 +286,49 @@ void MainWidget::createUi()
void MainWidget::connectUi()
{
- connect(m_recordButton, &QPushButton::clicked,
- m_engine, &Engine::startRecording);
+ connect(m_recordButton, &QPushButton::clicked, m_engine, &Engine::startRecording);
- connect(m_pauseButton, &QPushButton::clicked,
- m_engine, &Engine::suspend);
+ connect(m_pauseButton, &QPushButton::clicked, m_engine, &Engine::suspend);
- connect(m_playButton, &QPushButton::clicked,
- m_engine, &Engine::startPlayback);
+ connect(m_playButton, &QPushButton::clicked, m_engine, &Engine::startPlayback);
- connect(m_settingsButton, &QPushButton::clicked,
- this, &MainWidget::showSettingsDialog);
+ connect(m_settingsButton, &QPushButton::clicked, this, &MainWidget::showSettingsDialog);
- connect(m_engine, &Engine::stateChanged,
- this, &MainWidget::stateChanged);
+ connect(m_engine, &Engine::stateChanged, this, &MainWidget::stateChanged);
- connect(m_engine, &Engine::formatChanged,
- this, &MainWidget::formatChanged);
+ connect(m_engine, &Engine::formatChanged, this, &MainWidget::formatChanged);
m_progressBar->bufferLengthChanged(m_engine->bufferLength());
- connect(m_engine, &Engine::bufferLengthChanged,
- this, &MainWidget::bufferLengthChanged);
+ connect(m_engine, &Engine::bufferLengthChanged, this, &MainWidget::bufferLengthChanged);
- connect(m_engine, &Engine::dataLengthChanged,
- this, &MainWidget::updateButtonStates);
+ connect(m_engine, &Engine::dataLengthChanged, this, &MainWidget::updateButtonStates);
- connect(m_engine, &Engine::recordPositionChanged,
- m_progressBar, &ProgressBar::recordPositionChanged);
+ connect(m_engine, &Engine::recordPositionChanged, m_progressBar,
+ &ProgressBar::recordPositionChanged);
- connect(m_engine, &Engine::playPositionChanged,
- m_progressBar, &ProgressBar::playPositionChanged);
+ connect(m_engine, &Engine::playPositionChanged, m_progressBar,
+ &ProgressBar::playPositionChanged);
- connect(m_engine, &Engine::recordPositionChanged,
- this, &MainWidget::audioPositionChanged);
+ connect(m_engine, &Engine::recordPositionChanged, this, &MainWidget::audioPositionChanged);
- connect(m_engine, &Engine::playPositionChanged,
- this, &MainWidget::audioPositionChanged);
+ connect(m_engine, &Engine::playPositionChanged, this, &MainWidget::audioPositionChanged);
- connect(m_engine, &Engine::levelChanged,
- m_levelMeter, &LevelMeter::levelChanged);
+ connect(m_engine, &Engine::levelChanged, m_levelMeter, &LevelMeter::levelChanged);
- connect(m_engine, QOverload<qint64, qint64, const FrequencySpectrum&>::of(&Engine::spectrumChanged),
- this, QOverload<qint64, qint64, const FrequencySpectrum&>::of(&MainWidget::spectrumChanged));
+ connect(m_engine,
+ QOverload<qint64, qint64, const FrequencySpectrum &>::of(&Engine::spectrumChanged),
+ this,
+ QOverload<qint64, qint64, const FrequencySpectrum &>::of(&MainWidget::spectrumChanged));
- connect(m_engine, &Engine::infoMessage,
- this, &MainWidget::infoMessage);
+ connect(m_engine, &Engine::infoMessage, this, &MainWidget::infoMessage);
- connect(m_engine, &Engine::errorMessage,
- this, &MainWidget::errorMessage);
+ connect(m_engine, &Engine::errorMessage, this, &MainWidget::errorMessage);
- connect(m_spectrograph, &Spectrograph::infoMessage,
- this, &MainWidget::infoMessage);
+ connect(m_spectrograph, &Spectrograph::infoMessage, this, &MainWidget::infoMessage);
#ifndef DISABLE_WAVEFORM
- connect(m_engine, &Engine::bufferChanged,
- m_waveform, &Waveform::bufferChanged);
+ connect(m_engine, &Engine::bufferChanged, m_waveform, &Waveform::bufferChanged);
#endif
}
@@ -417,22 +351,22 @@ void MainWidget::createMenus()
void MainWidget::updateButtonStates()
{
- const bool recordEnabled = ((QAudio::AudioOutput == m_engine->mode() ||
- (QAudio::ActiveState != m_engine->state() &&
- QAudio::IdleState != m_engine->state())) &&
- RecordMode == m_mode);
- m_recordButton->setEnabled(recordEnabled);
+ const bool recordEnabled = ((QAudioDevice::Output == m_engine->mode()
+ || (QAudio::ActiveState != m_engine->state()
+ && QAudio::IdleState != m_engine->state()))
+ && RecordMode == m_mode);
+ m_recordButton->setEnabled(m_errorOccurred ? false : recordEnabled);
- const bool pauseEnabled = (QAudio::ActiveState == m_engine->state() ||
- QAudio::IdleState == m_engine->state());
- m_pauseButton->setEnabled(pauseEnabled);
+ const bool pauseEnabled =
+ (QAudio::ActiveState == m_engine->state() || QAudio::IdleState == m_engine->state());
+ m_pauseButton->setEnabled(m_errorOccurred ? false : pauseEnabled);
const bool playEnabled = (/*m_engine->dataLength() &&*/
- (QAudio::AudioOutput != m_engine->mode() ||
- (QAudio::ActiveState != m_engine->state() &&
- QAudio::IdleState != m_engine->state() &&
- QAudio::InterruptedState != m_engine->state())));
- m_playButton->setEnabled(playEnabled);
+ (QAudioDevice::Output != m_engine->mode()
+ || (QAudio::ActiveState != m_engine->state()
+ && QAudio::IdleState != m_engine->state())));
+
+ m_playButton->setEnabled(m_errorOccurred ? false : playEnabled);
}
void MainWidget::reset()
@@ -444,6 +378,10 @@ void MainWidget::reset()
m_levelMeter->reset();
m_spectrograph->reset();
m_progressBar->reset();
+ if (m_errorOccurred) {
+ setMode(Mode::NoMode);
+ updateButtonStates();
+ }
}
void MainWidget::setMode(Mode mode)
@@ -458,3 +396,5 @@ void MainWidget::updateModeMenu()
m_generateToneAction->setChecked(GenerateToneMode == m_mode);
m_recordAction->setChecked(RecordMode == m_mode);
}
+
+#include "moc_mainwidget.cpp"
diff --git a/examples/multimedia/spectrum/mainwidget.h b/examples/multimedia/spectrum/mainwidget.h
new file mode 100644
index 000000000..789e312f5
--- /dev/null
+++ b/examples/multimedia/spectrum/mainwidget.h
@@ -0,0 +1,105 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MAINWIDGET_H
+#define MAINWIDGET_H
+
+#include <QAudioDevice>
+#include <QIcon>
+#include <QWidget>
+
+class Engine;
+class FrequencySpectrum;
+class LevelMeter;
+class ProgressBar;
+class SettingsDialog;
+class Spectrograph;
+class ToneGeneratorDialog;
+class Waveform;
+
+QT_BEGIN_NAMESPACE
+class QAction;
+class QAudioFormat;
+class QLabel;
+class QMenu;
+class QPushButton;
+QT_END_NAMESPACE
+
+/**
+ * Main application widget, responsible for connecting the various UI
+ * elements to the Engine.
+ */
+class MainWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit MainWidget(QWidget *parent = nullptr);
+ ~MainWidget();
+
+ // QObject
+ void timerEvent(QTimerEvent *event) override;
+
+public slots:
+ void stateChanged(QAudioDevice::Mode mode, QAudio::State state);
+ void formatChanged(const QAudioFormat &format);
+ void spectrumChanged(qint64 position, qint64 length, const FrequencySpectrum &spectrum);
+ void infoMessage(const QString &message, int timeoutMs);
+ void errorMessage(const QString &heading, const QString &detail);
+ void audioPositionChanged(qint64 position);
+ void bufferLengthChanged(qint64 length);
+
+private slots:
+ void showFileDialog();
+ void showSettingsDialog();
+ void showToneGeneratorDialog();
+ void initializeRecord();
+ void updateModeMenu();
+ void updateButtonStates();
+
+private:
+ void createUi();
+ void createMenus();
+ void connectUi();
+ void reset();
+
+ enum Mode { NoMode, RecordMode, GenerateToneMode, LoadFileMode };
+
+ void setMode(Mode mode);
+
+private:
+ Mode m_mode;
+
+ Engine *m_engine;
+
+#ifndef DISABLE_WAVEFORM
+ Waveform *m_waveform;
+#endif
+ ProgressBar *m_progressBar;
+ Spectrograph *m_spectrograph;
+ LevelMeter *m_levelMeter;
+
+ QPushButton *m_modeButton;
+ QPushButton *m_recordButton;
+ QIcon m_recordIcon;
+ QPushButton *m_pauseButton;
+ QIcon m_pauseIcon;
+ QPushButton *m_playButton;
+ QIcon m_playIcon;
+ QPushButton *m_settingsButton;
+ QIcon m_settingsIcon;
+
+ QLabel *m_infoMessage;
+ int m_infoMessageTimerId;
+
+ SettingsDialog *m_settingsDialog;
+ ToneGeneratorDialog *m_toneGeneratorDialog;
+
+ QMenu *m_modeMenu;
+ QAction *m_loadFileAction;
+ QAction *m_generateToneAction;
+ QAction *m_recordAction;
+ bool m_errorOccurred;
+};
+
+#endif // MAINWIDGET_H
diff --git a/examples/multimedia/spectrum/progressbar.cpp b/examples/multimedia/spectrum/progressbar.cpp
new file mode 100644
index 000000000..1806097e2
--- /dev/null
+++ b/examples/multimedia/spectrum/progressbar.cpp
@@ -0,0 +1,101 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "progressbar.h"
+#include <QPainter>
+
+ProgressBar::ProgressBar(QWidget *parent)
+ : QWidget(parent),
+ m_bufferLength(0),
+ m_recordPosition(0),
+ m_playPosition(0),
+ m_windowPosition(0),
+ m_windowLength(0)
+{
+ setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+ setMinimumHeight(30);
+#ifdef SUPERIMPOSE_PROGRESS_ON_WAVEFORM
+ setAutoFillBackground(false);
+#endif
+}
+
+ProgressBar::~ProgressBar() = default;
+
+void ProgressBar::reset()
+{
+ m_bufferLength = 0;
+ m_recordPosition = 0;
+ m_playPosition = 0;
+ m_windowPosition = 0;
+ m_windowLength = 0;
+ update();
+}
+
+void ProgressBar::paintEvent(QPaintEvent * /*event*/)
+{
+ QPainter painter(this);
+
+ QColor bufferColor(0, 0, 255);
+ QColor windowColor(0, 255, 0);
+
+#ifdef SUPERIMPOSE_PROGRESS_ON_WAVEFORM
+ bufferColor.setAlphaF(0.5);
+ windowColor.setAlphaF(0.5);
+#else
+ painter.fillRect(rect(), Qt::black);
+#endif
+
+ if (m_bufferLength) {
+ QRect bar = rect();
+ const qreal play = qreal(m_playPosition) / m_bufferLength;
+ bar.setLeft(rect().left() + play * rect().width());
+ const qreal record = qreal(m_recordPosition) / m_bufferLength;
+ bar.setRight(rect().left() + record * rect().width());
+ painter.fillRect(bar, bufferColor);
+
+ QRect window = rect();
+ const qreal windowLeft = qreal(m_windowPosition) / m_bufferLength;
+ window.setLeft(rect().left() + windowLeft * rect().width());
+ const qreal windowWidth = qreal(m_windowLength) / m_bufferLength;
+ window.setWidth(windowWidth * rect().width());
+ painter.fillRect(window, windowColor);
+ }
+}
+
+void ProgressBar::bufferLengthChanged(qint64 bufferSize)
+{
+ m_bufferLength = bufferSize;
+ m_recordPosition = 0;
+ m_playPosition = 0;
+ m_windowPosition = 0;
+ m_windowLength = 0;
+ repaint();
+}
+
+void ProgressBar::recordPositionChanged(qint64 recordPosition)
+{
+ Q_ASSERT(recordPosition >= 0);
+ Q_ASSERT(recordPosition <= m_bufferLength);
+ m_recordPosition = recordPosition;
+ repaint();
+}
+
+void ProgressBar::playPositionChanged(qint64 playPosition)
+{
+ Q_ASSERT(playPosition >= 0);
+ Q_ASSERT(playPosition <= m_bufferLength);
+ m_playPosition = playPosition;
+ repaint();
+}
+
+void ProgressBar::windowChanged(qint64 position, qint64 length)
+{
+ Q_ASSERT(position >= 0);
+ Q_ASSERT(position <= m_bufferLength);
+ Q_ASSERT(position + length <= m_bufferLength);
+ m_windowPosition = position;
+ m_windowLength = length;
+ repaint();
+}
+
+#include "moc_progressbar.cpp"
diff --git a/examples/multimedia/spectrum/progressbar.h b/examples/multimedia/spectrum/progressbar.h
new file mode 100644
index 000000000..8b117c7e6
--- /dev/null
+++ b/examples/multimedia/spectrum/progressbar.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef PROGRESSBAR_H
+#define PROGRESSBAR_H
+
+#include <QWidget>
+
+/**
+ * Widget which displays a the current fill state of the Engine's internal
+ * buffer, and the current play/record position within that buffer.
+ */
+class ProgressBar : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit ProgressBar(QWidget *parent = nullptr);
+ ~ProgressBar();
+
+ void reset();
+ void paintEvent(QPaintEvent *event) override;
+
+public slots:
+ void bufferLengthChanged(qint64 length);
+ void recordPositionChanged(qint64 recordPosition);
+ void playPositionChanged(qint64 playPosition);
+ void windowChanged(qint64 position, qint64 length);
+
+private:
+ qint64 m_bufferLength;
+ qint64 m_recordPosition;
+ qint64 m_playPosition;
+ qint64 m_windowPosition;
+ qint64 m_windowLength;
+};
+
+#endif // PROGRESSBAR_H
diff --git a/examples/multimedia/spectrum/settingsdialog.cpp b/examples/multimedia/spectrum/settingsdialog.cpp
new file mode 100644
index 000000000..bdd52e123
--- /dev/null
+++ b/examples/multimedia/spectrum/settingsdialog.cpp
@@ -0,0 +1,102 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "settingsdialog.h"
+
+#include <QCheckBox>
+#include <QComboBox>
+#include <QDialogButtonBox>
+#include <QLabel>
+#include <QPushButton>
+#include <QSlider>
+#include <QSpinBox>
+#include <QVBoxLayout>
+
+SettingsDialog::SettingsDialog(const QList<QAudioDevice> &availableInputDevices,
+ const QList<QAudioDevice> &availableOutputDevices, QWidget *parent)
+ : QDialog(parent),
+ m_windowFunction(DefaultWindowFunction),
+ m_inputDeviceComboBox(new QComboBox(this)),
+ m_outputDeviceComboBox(new QComboBox(this)),
+ m_windowFunctionComboBox(new QComboBox(this))
+{
+ QVBoxLayout *dialogLayout = new QVBoxLayout(this);
+
+ // Populate combo boxes
+
+ for (const QAudioDevice &device : availableInputDevices)
+ m_inputDeviceComboBox->addItem(device.description(), QVariant::fromValue(device));
+ for (const QAudioDevice &device : availableOutputDevices)
+ m_outputDeviceComboBox->addItem(device.description(), QVariant::fromValue(device));
+
+ m_windowFunctionComboBox->addItem(tr("None"), QVariant::fromValue(int(NoWindow)));
+ m_windowFunctionComboBox->addItem("Hann", QVariant::fromValue(int(HannWindow)));
+ m_windowFunctionComboBox->setCurrentIndex(m_windowFunction);
+
+ // Initialize default devices
+ if (!availableInputDevices.empty())
+ m_inputDevice = availableInputDevices.front();
+ if (!availableOutputDevices.empty())
+ m_outputDevice = availableOutputDevices.front();
+
+ // Add widgets to layout
+
+ std::unique_ptr<QHBoxLayout> inputDeviceLayout(new QHBoxLayout);
+ QLabel *inputDeviceLabel = new QLabel(tr("Input device"), this);
+ inputDeviceLayout->addWidget(inputDeviceLabel);
+ inputDeviceLayout->addWidget(m_inputDeviceComboBox);
+ dialogLayout->addLayout(inputDeviceLayout.release());
+
+ std::unique_ptr<QHBoxLayout> outputDeviceLayout(new QHBoxLayout);
+ QLabel *outputDeviceLabel = new QLabel(tr("Output device"), this);
+ outputDeviceLayout->addWidget(outputDeviceLabel);
+ outputDeviceLayout->addWidget(m_outputDeviceComboBox);
+ dialogLayout->addLayout(outputDeviceLayout.release());
+
+ std::unique_ptr<QHBoxLayout> windowFunctionLayout(new QHBoxLayout);
+ QLabel *windowFunctionLabel = new QLabel(tr("Window function"), this);
+ windowFunctionLayout->addWidget(windowFunctionLabel);
+ windowFunctionLayout->addWidget(m_windowFunctionComboBox);
+ dialogLayout->addLayout(windowFunctionLayout.release());
+
+ // Connect
+ connect(m_inputDeviceComboBox, QOverload<int>::of(&QComboBox::activated), this,
+ &SettingsDialog::inputDeviceChanged);
+ connect(m_outputDeviceComboBox, QOverload<int>::of(&QComboBox::activated), this,
+ &SettingsDialog::outputDeviceChanged);
+ connect(m_windowFunctionComboBox, QOverload<int>::of(&QComboBox::activated), this,
+ &SettingsDialog::windowFunctionChanged);
+
+ // Add standard buttons to layout
+ QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
+ buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+ dialogLayout->addWidget(buttonBox);
+
+ // Connect standard buttons
+ connect(buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this,
+ &SettingsDialog::accept);
+ connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this,
+ &SettingsDialog::reject);
+
+ setLayout(dialogLayout);
+}
+
+SettingsDialog::~SettingsDialog() = default;
+
+void SettingsDialog::windowFunctionChanged(int index)
+{
+ m_windowFunction =
+ static_cast<WindowFunction>(m_windowFunctionComboBox->itemData(index).value<int>());
+}
+
+void SettingsDialog::inputDeviceChanged(int index)
+{
+ m_inputDevice = m_inputDeviceComboBox->itemData(index).value<QAudioDevice>();
+}
+
+void SettingsDialog::outputDeviceChanged(int index)
+{
+ m_outputDevice = m_outputDeviceComboBox->itemData(index).value<QAudioDevice>();
+}
+
+#include "moc_settingsdialog.cpp"
diff --git a/examples/multimedia/spectrum/settingsdialog.h b/examples/multimedia/spectrum/settingsdialog.h
new file mode 100644
index 000000000..ac877d975
--- /dev/null
+++ b/examples/multimedia/spectrum/settingsdialog.h
@@ -0,0 +1,52 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef SETTINGSDIALOG_H
+#define SETTINGSDIALOG_H
+
+#include "spectrum.h"
+
+#include <QAudioDevice>
+#include <QDialog>
+
+QT_BEGIN_NAMESPACE
+class QComboBox;
+class QCheckBox;
+class QSlider;
+class QSpinBox;
+class QGridLayout;
+QT_END_NAMESPACE
+
+/**
+ * Dialog used to control settings such as the audio input / output device
+ * and the windowing function.
+ */
+class SettingsDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ SettingsDialog(const QList<QAudioDevice> &availableInputDevices,
+ const QList<QAudioDevice> &availableOutputDevices, QWidget *parent = nullptr);
+ ~SettingsDialog();
+
+ WindowFunction windowFunction() const { return m_windowFunction; }
+ const QAudioDevice &inputDevice() const { return m_inputDevice; }
+ const QAudioDevice &outputDevice() const { return m_outputDevice; }
+
+private slots:
+ void windowFunctionChanged(int index);
+ void inputDeviceChanged(int index);
+ void outputDeviceChanged(int index);
+
+private:
+ WindowFunction m_windowFunction;
+ QAudioDevice m_inputDevice;
+ QAudioDevice m_outputDevice;
+
+ QComboBox *m_inputDeviceComboBox;
+ QComboBox *m_outputDeviceComboBox;
+ QComboBox *m_windowFunctionComboBox;
+};
+
+#endif // SETTINGSDIALOG_H
diff --git a/examples/multimedia/spectrum/app/spectrograph.cpp b/examples/multimedia/spectrum/spectrograph.cpp
index 6eaa097fc..96274b50b 100644
--- a/examples/multimedia/spectrum/app/spectrograph.cpp
+++ b/examples/multimedia/spectrum/spectrograph.cpp
@@ -1,54 +1,8 @@
-/****************************************************************************
-**
-** 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 "spectrograph.h"
+
#include <QDebug>
#include <QMouseEvent>
#include <QPainter>
@@ -59,19 +13,16 @@ const int NullIndex = -1;
const int BarSelectionInterval = 2000;
Spectrograph::Spectrograph(QWidget *parent)
- : QWidget(parent)
- , m_barSelected(NullIndex)
- , m_timerId(NullTimerId)
- , m_lowFreq(0.0)
- , m_highFreq(0.0)
+ : QWidget(parent),
+ m_barSelected(NullIndex),
+ m_timerId(NullTimerId),
+ m_lowFreq(0.0),
+ m_highFreq(0.0)
{
setMinimumHeight(100);
}
-Spectrograph::~Spectrograph()
-{
-
-}
+Spectrograph::~Spectrograph() = default;
void Spectrograph::setParams(int numBars, qreal lowFreq, qreal highFreq)
{
@@ -134,8 +85,8 @@ void Spectrograph::paintEvent(QPaintEvent *event)
if (numBars) {
const int numHorizontalSections = numBars;
QLine line(rect().topLeft(), rect().bottomLeft());
- for (int i=1; i<numHorizontalSections; ++i) {
- line.translate(rect().width()/numHorizontalSections, 0);
+ for (int i = 1; i < numHorizontalSections; ++i) {
+ line.translate(rect().width() / numHorizontalSections, 0);
painter.drawLine(line);
}
}
@@ -143,8 +94,8 @@ void Spectrograph::paintEvent(QPaintEvent *event)
// Draw horizontal lines
const int numVerticalSections = 10;
QLine line(rect().topLeft(), rect().topRight());
- for (int i=1; i<numVerticalSections; ++i) {
- line.translate(0, rect().height()/numVerticalSections);
+ for (int i = 1; i < numVerticalSections; ++i) {
+ line.translate(0, rect().height() / numVerticalSections);
painter.drawLine(line);
}
@@ -163,7 +114,7 @@ void Spectrograph::paintEvent(QPaintEvent *event)
const int leftPaddingWidth = (paddingWidth + gapWidth) / 2;
const int barHeight = rect().height() - 2 * gapWidth;
- for (int i=0; i<numBars; ++i) {
+ for (int i = 0; i < numBars; ++i) {
const qreal value = m_bars[i].value;
Q_ASSERT(value >= 0.0 && value <= 1.0);
QRect bar = rect();
@@ -205,7 +156,7 @@ int Spectrograph::barIndex(qreal frequency) const
Q_ASSERT(frequency >= m_lowFreq && frequency < m_highFreq);
const qreal bandWidth = (m_highFreq - m_lowFreq) / m_bars.count();
const int index = (frequency - m_lowFreq) / bandWidth;
- if (index <0 || index >= m_bars.count())
+ if (index < 0 || index >= m_bars.count())
Q_ASSERT(false);
return index;
}
@@ -214,7 +165,7 @@ QPair<qreal, qreal> Spectrograph::barRange(int index) const
{
Q_ASSERT(index >= 0 && index < m_bars.count());
const qreal bandWidth = (m_highFreq - m_lowFreq) / m_bars.count();
- return QPair<qreal, qreal>(index * bandWidth, (index+1) * bandWidth);
+ return QPair<qreal, qreal>(index * bandWidth, (index + 1) * bandWidth);
}
void Spectrograph::updateBars()
@@ -222,7 +173,7 @@ void Spectrograph::updateBars()
m_bars.fill(Bar());
FrequencySpectrum::const_iterator i = m_spectrum.begin();
const FrequencySpectrum::const_iterator end = m_spectrum.end();
- for ( ; i != end; ++i) {
+ for (; i != end; ++i) {
const FrequencySpectrum::Element e = *i;
if (e.frequency >= m_lowFreq && e.frequency < m_highFreq) {
Bar &bar = m_bars[barIndex(e.frequency)];
@@ -233,11 +184,11 @@ void Spectrograph::updateBars()
update();
}
-void Spectrograph::selectBar(int index) {
+void Spectrograph::selectBar(int index)
+{
const QPair<qreal, qreal> frequencyRange = barRange(index);
- const QString message = QString("%1 - %2 Hz")
- .arg(frequencyRange.first)
- .arg(frequencyRange.second);
+ const QString message =
+ QStringLiteral("%1 - %2 Hz").arg(frequencyRange.first).arg(frequencyRange.second);
emit infoMessage(message, BarSelectionInterval);
if (NullTimerId != m_timerId)
@@ -248,4 +199,4 @@ void Spectrograph::selectBar(int index) {
update();
}
-
+#include "moc_spectrograph.cpp"
diff --git a/examples/multimedia/spectrum/spectrograph.h b/examples/multimedia/spectrum/spectrograph.h
new file mode 100644
index 000000000..1344d3257
--- /dev/null
+++ b/examples/multimedia/spectrum/spectrograph.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef SPECTROGRAPH_H
+#define SPECTROGRAPH_H
+
+#include "frequencyspectrum.h"
+
+#include <QWidget>
+
+/**
+ * Widget which displays a spectrograph showing the frequency spectrum
+ * of the window of audio samples most recently analyzed by the Engine.
+ */
+class Spectrograph : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit Spectrograph(QWidget *parent = nullptr);
+ ~Spectrograph();
+
+ void setParams(int numBars, qreal lowFreq, qreal highFreq);
+
+ // QObject
+ void timerEvent(QTimerEvent *event) override;
+
+ // QWidget
+ void paintEvent(QPaintEvent *event) override;
+ void mousePressEvent(QMouseEvent *event) override;
+
+signals:
+ void infoMessage(const QString &message, int intervalMs);
+
+public slots:
+ void reset();
+ void spectrumChanged(const FrequencySpectrum &spectrum);
+
+private:
+ int barIndex(qreal frequency) const;
+ QPair<qreal, qreal> barRange(int barIndex) const;
+ void updateBars();
+
+ void selectBar(int index);
+
+private:
+ struct Bar
+ {
+ Bar() : value(0.0), clipped(false) { }
+ qreal value;
+ bool clipped;
+ };
+
+ QList<Bar> m_bars;
+ int m_barSelected;
+ int m_timerId;
+ qreal m_lowFreq;
+ qreal m_highFreq;
+ FrequencySpectrum m_spectrum;
+};
+
+#endif // SPECTROGRAPH_H
diff --git a/examples/multimedia/spectrum/spectrum.h b/examples/multimedia/spectrum/spectrum.h
new file mode 100644
index 000000000..e416e82bd
--- /dev/null
+++ b/examples/multimedia/spectrum/spectrum.h
@@ -0,0 +1,91 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef SPECTRUM_H
+#define SPECTRUM_H
+
+#include "fftreal_wrapper.h" // For FFTLengthPowerOfTwo
+#include "utils.h"
+
+#include <QtGlobal>
+
+//-----------------------------------------------------------------------------
+// Constants
+//-----------------------------------------------------------------------------
+
+// Number of audio samples used to calculate the frequency spectrum
+const int SpectrumLengthSamples = PowerOfTwo<FFTLengthPowerOfTwo>::Result;
+
+// Number of bands in the frequency spectrum
+const int SpectrumNumBands = 10;
+
+// Lower bound of first band in the spectrum
+const qreal SpectrumLowFreq = 0.0; // Hz
+
+// Upper band of last band in the spectrum
+const qreal SpectrumHighFreq = 1000.0; // Hz
+
+// Waveform window size in microseconds
+const qint64 WaveformWindowDuration = 500 * 1000;
+
+// Length of waveform tiles in bytes
+// Ideally, these would match the QAudio*::bufferSize(), but that isn't
+// available until some time after QAudio*::start() has been called, and we
+// need this value in order to initialize the waveform display.
+// We therefore just choose a sensible value.
+const int WaveformTileLength = 4096;
+
+// Fudge factor used to calculate the spectrum bar heights
+const qreal SpectrumAnalyserMultiplier = 0.15;
+
+// Disable message timeout
+const int NullMessageTimeout = -1;
+
+//-----------------------------------------------------------------------------
+// Types and data structures
+//-----------------------------------------------------------------------------
+
+enum WindowFunction { NoWindow, HannWindow };
+Q_DECLARE_METATYPE(WindowFunction)
+
+const WindowFunction DefaultWindowFunction = HannWindow;
+
+struct Tone
+{
+ Tone(qreal freq = 0.0, qreal amp = 0.0) : frequency(freq), amplitude(amp) { }
+
+ // Start and end frequencies for swept tone generation
+ qreal frequency;
+
+ // Amplitude in range [0.0, 1.0]
+ qreal amplitude;
+};
+
+struct SweptTone
+{
+ SweptTone(qreal start = 0.0, qreal end = 0.0, qreal amp = 0.0)
+ : startFreq(start), endFreq(end), amplitude(amp)
+ {
+ Q_ASSERT(end >= start);
+ }
+
+ SweptTone(const Tone &tone)
+ : startFreq(tone.frequency), endFreq(tone.frequency), amplitude(tone.amplitude)
+ {
+ }
+
+ // Start and end frequencies for swept tone generation
+ qreal startFreq;
+ qreal endFreq;
+
+ // Amplitude in range [0.0, 1.0]
+ qreal amplitude;
+};
+
+// Handle some dependencies between macros defined in the .pro file
+
+#ifdef DISABLE_WAVEFORM
+# undef SUPERIMPOSE_PROGRESS_ON_WAVEFORM
+#endif
+
+#endif // SPECTRUM_H
diff --git a/examples/multimedia/spectrum/spectrum.pri b/examples/multimedia/spectrum/spectrum.pri
index 38b3f1501..431d728ec 100644
--- a/examples/multimedia/spectrum/spectrum.pri
+++ b/examples/multimedia/spectrum/spectrum.pri
@@ -23,8 +23,6 @@ DEFINES += LOG_ENGINE
# If this macro is defined, the FFTReal DLL will not be built
#DEFINES += DISABLE_FFT
-static: DEFINES += DISABLE_FFT
-
# Disables rendering of the waveform
#DEFINES += DISABLE_WAVEFORM
@@ -48,4 +46,3 @@ win32 {
CONFIG(debug, release|debug): spectrum_build_dir = /debug
}
}
-
diff --git a/examples/multimedia/spectrum/spectrum.pro b/examples/multimedia/spectrum/spectrum.pro
index 0ca2ee554..782c00698 100644
--- a/examples/multimedia/spectrum/spectrum.pro
+++ b/examples/multimedia/spectrum/spectrum.pro
@@ -2,10 +2,10 @@ include(spectrum.pri)
TEMPLATE = subdirs
-# Ensure that library is built before application
-CONFIG += ordered
+SUBDIRS += 3rdparty/fftreal
-!contains(DEFINES, DISABLE_FFT): SUBDIRS += 3rdparty/fftreal
+app.file = app.pro
+app.depends = 3rdparty/fftreal
SUBDIRS += app
TARGET = spectrum
diff --git a/examples/multimedia/spectrum/app/spectrum.qrc b/examples/multimedia/spectrum/spectrum.qrc
index 61004791b..61004791b 100644
--- a/examples/multimedia/spectrum/app/spectrum.qrc
+++ b/examples/multimedia/spectrum/spectrum.qrc
diff --git a/examples/multimedia/spectrum/spectrumanalyser.cpp b/examples/multimedia/spectrum/spectrumanalyser.cpp
new file mode 100644
index 000000000..c309c6a73
--- /dev/null
+++ b/examples/multimedia/spectrum/spectrumanalyser.cpp
@@ -0,0 +1,233 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "spectrumanalyser.h"
+#include "fftreal_wrapper.h"
+#include "utils.h"
+
+#include <QAudioFormat>
+#include <QMetaType>
+#include <QThread>
+#include <QtMath>
+
+SpectrumAnalyserThread::SpectrumAnalyserThread(QObject *parent)
+ : QObject(parent)
+#ifndef DISABLE_FFT
+ ,
+ m_fft(new FFTRealWrapper)
+#endif
+ ,
+ m_numSamples(SpectrumLengthSamples),
+ m_windowFunction(DefaultWindowFunction),
+ m_window(SpectrumLengthSamples, 0.0),
+ m_input(SpectrumLengthSamples, 0.0),
+ m_output(SpectrumLengthSamples, 0.0),
+ m_spectrum(SpectrumLengthSamples)
+#ifdef SPECTRUM_ANALYSER_SEPARATE_THREAD
+ ,
+ m_thread(new QThread(this))
+#endif
+{
+#ifdef SPECTRUM_ANALYSER_SEPARATE_THREAD
+ // moveToThread() cannot be called on a QObject with a parent
+ setParent(nullptr);
+ moveToThread(m_thread);
+ m_thread->start();
+#endif
+ calculateWindow();
+}
+
+SpectrumAnalyserThread::~SpectrumAnalyserThread()
+{
+#ifndef DISABLE_FFT
+ delete m_fft;
+#endif
+}
+
+void SpectrumAnalyserThread::setWindowFunction(WindowFunction type)
+{
+ m_windowFunction = type;
+ calculateWindow();
+}
+
+void SpectrumAnalyserThread::calculateWindow()
+{
+ for (int i = 0; i < m_numSamples; ++i) {
+ DataType x = 0.0;
+
+ switch (m_windowFunction) {
+ case NoWindow:
+ x = 1.0;
+ break;
+ case HannWindow:
+ x = 0.5 * (1 - qCos((2 * M_PI * i) / (m_numSamples - 1)));
+ break;
+ default:
+ Q_ASSERT(false);
+ }
+
+ m_window[i] = x;
+ }
+}
+
+void SpectrumAnalyserThread::calculateSpectrum(const QByteArray &buffer, int inputFrequency,
+ int bytesPerFrame)
+{
+#ifndef DISABLE_FFT
+ Q_ASSERT(buffer.size() == m_numSamples * bytesPerFrame);
+
+ // Initialize data array
+ const char *ptr = buffer.constData();
+ for (int i = 0; i < m_numSamples; ++i) {
+ const qint16 pcmSample = *reinterpret_cast<const qint16 *>(ptr);
+ // Scale down to range [-1.0, 1.0]
+ const DataType realSample = pcmToReal(pcmSample);
+ const DataType windowedSample = realSample * m_window[i];
+ m_input[i] = windowedSample;
+ ptr += bytesPerFrame;
+ }
+
+ // Calculate the FFT
+ m_fft->calculateFFT(m_output.data(), m_input.data());
+
+ // Analyze output to obtain amplitude and phase for each frequency
+ for (int i = 2; i <= m_numSamples / 2; ++i) {
+ // Calculate frequency of this complex sample
+ m_spectrum[i].frequency = qreal(i * inputFrequency) / (m_numSamples);
+
+ const qreal real = m_output[i];
+ qreal imag = 0.0;
+ if (i > 0 && i < m_numSamples / 2)
+ imag = m_output[m_numSamples / 2 + i];
+
+ const qreal magnitude = qSqrt(real * real + imag * imag);
+ qreal amplitude = SpectrumAnalyserMultiplier * qLn(magnitude);
+
+ // Bound amplitude to [0.0, 1.0]
+ m_spectrum[i].clipped = (amplitude > 1.0);
+ amplitude = qMax(qreal(0.0), amplitude);
+ amplitude = qMin(qreal(1.0), amplitude);
+ m_spectrum[i].amplitude = amplitude;
+ }
+#endif
+
+ emit calculationComplete(m_spectrum);
+}
+
+//=============================================================================
+// SpectrumAnalyser
+//=============================================================================
+
+SpectrumAnalyser::SpectrumAnalyser(QObject *parent)
+ : QObject(parent),
+ m_thread(new SpectrumAnalyserThread(this)),
+ m_state(Idle)
+#ifdef DUMP_SPECTRUMANALYSER
+ ,
+ m_count(0)
+#endif
+{
+ connect(m_thread, &SpectrumAnalyserThread::calculationComplete, this,
+ &SpectrumAnalyser::calculationComplete);
+}
+
+SpectrumAnalyser::~SpectrumAnalyser() = default;
+
+#ifdef DUMP_SPECTRUMANALYSER
+void SpectrumAnalyser::setOutputPath(const QString &outputDir)
+{
+ m_outputDir.setPath(outputDir);
+ m_textFile.setFileName(m_outputDir.filePath("spectrum.txt"));
+ m_textFile.open(QIODevice::WriteOnly | QIODevice::Text);
+ m_textStream.setDevice(&m_textFile);
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Public functions
+//-----------------------------------------------------------------------------
+
+void SpectrumAnalyser::setWindowFunction(WindowFunction type)
+{
+ const bool b = QMetaObject::invokeMethod(m_thread, "setWindowFunction", Qt::AutoConnection,
+ Q_ARG(WindowFunction, type));
+ Q_ASSERT(b);
+ Q_UNUSED(b); // suppress warnings in release builds
+}
+
+void SpectrumAnalyser::calculate(const QByteArray &buffer, const QAudioFormat &format)
+{
+ // QThread::currentThread is marked 'for internal use only', but
+ // we're only using it for debug output here, so it's probably OK :)
+ SPECTRUMANALYSER_DEBUG << "SpectrumAnalyser::calculate" << QThread::currentThread() << "state"
+ << m_state;
+
+ if (isReady()) {
+ Q_ASSERT(format.sampleFormat() == QAudioFormat::Int16);
+
+ const int bytesPerFrame = format.bytesPerFrame();
+
+#ifdef DUMP_SPECTRUMANALYSER
+ m_count++;
+ const QString pcmFileName =
+ m_outputDir.filePath(QStringLiteral("spectrum_%1.pcm").arg(m_count, 4, 10, QChar('0')));
+ QFile pcmFile(pcmFileName);
+ pcmFile.open(QIODevice::WriteOnly);
+ const int bufferLength = m_numSamples * bytesPerFrame;
+ pcmFile.write(buffer, bufferLength);
+
+ m_textStream << "TimeDomain " << m_count << "\n";
+ const qint16 *input = reinterpret_cast<const qint16 *>(buffer);
+ for (int i = 0; i < m_numSamples; ++i) {
+ m_textStream << i << "\t" << *input << "\n";
+ input += format.channels();
+ }
+#endif
+
+ m_state = Busy;
+
+ // Invoke SpectrumAnalyserThread::calculateSpectrum using QMetaObject. If
+ // m_thread is in a different thread from the current thread, the
+ // calculation will be done in the child thread.
+ // Once the calculation is finished, a calculationChanged signal will be
+ // emitted by m_thread.
+ const bool b = QMetaObject::invokeMethod(
+ m_thread, "calculateSpectrum", Qt::AutoConnection, Q_ARG(QByteArray, buffer),
+ Q_ARG(int, format.sampleRate()), Q_ARG(int, bytesPerFrame));
+ Q_ASSERT(b);
+ Q_UNUSED(b); // suppress warnings in release builds
+
+#ifdef DUMP_SPECTRUMANALYSER
+ m_textStream << "FrequencySpectrum " << m_count << "\n";
+ FrequencySpectrum::const_iterator x = m_spectrum.begin();
+ for (int i = 0; i < m_numSamples; ++i, ++x)
+ m_textStream << i << "\t" << x->frequency << "\t" << x->amplitude << "\t" << x->phase
+ << "\n";
+#endif
+ }
+}
+
+bool SpectrumAnalyser::isReady() const
+{
+ return (Idle == m_state);
+}
+
+void SpectrumAnalyser::cancelCalculation()
+{
+ if (Busy == m_state)
+ m_state = Cancelled;
+}
+
+//-----------------------------------------------------------------------------
+// Private slots
+//-----------------------------------------------------------------------------
+
+void SpectrumAnalyser::calculationComplete(const FrequencySpectrum &spectrum)
+{
+ Q_ASSERT(Idle != m_state);
+ if (Busy == m_state)
+ emit spectrumChanged(spectrum);
+ m_state = Idle;
+}
+
+#include "moc_spectrumanalyser.cpp"
diff --git a/examples/multimedia/spectrum/spectrumanalyser.h b/examples/multimedia/spectrum/spectrumanalyser.h
new file mode 100644
index 000000000..202e602fe
--- /dev/null
+++ b/examples/multimedia/spectrum/spectrumanalyser.h
@@ -0,0 +1,151 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef SPECTRUMANALYSER_H
+#define SPECTRUMANALYSER_H
+
+#include "frequencyspectrum.h"
+#include "spectrum.h"
+
+#include <QByteArray>
+#include <QList>
+#include <QObject>
+
+#ifdef DUMP_SPECTRUMANALYSER
+# include <QDir>
+# include <QFile>
+# include <QTextStream>
+#endif
+
+#ifndef DISABLE_FFT
+# include "FFTRealFixLenParam.h"
+#endif
+
+QT_FORWARD_DECLARE_CLASS(QAudioFormat)
+QT_FORWARD_DECLARE_CLASS(QThread)
+
+class FFTRealWrapper;
+
+class SpectrumAnalyserThreadPrivate;
+
+/**
+ * Implementation of the spectrum analysis which can be run in a
+ * separate thread.
+ */
+class SpectrumAnalyserThread : public QObject
+{
+ Q_OBJECT
+
+public:
+ SpectrumAnalyserThread(QObject *parent);
+ ~SpectrumAnalyserThread();
+
+public slots:
+ void setWindowFunction(WindowFunction type);
+ void calculateSpectrum(const QByteArray &buffer, int inputFrequency, int bytesPerSample);
+
+signals:
+ void calculationComplete(const FrequencySpectrum &spectrum);
+
+private:
+ void calculateWindow();
+
+private:
+#ifndef DISABLE_FFT
+ FFTRealWrapper *m_fft;
+#endif
+
+ const int m_numSamples;
+
+ WindowFunction m_windowFunction;
+
+#ifdef DISABLE_FFT
+ typedef qreal DataType;
+#else
+ typedef FFTRealFixLenParam::DataType DataType;
+#endif
+ QList<DataType> m_window;
+
+ QList<DataType> m_input;
+ QList<DataType> m_output;
+
+ FrequencySpectrum m_spectrum;
+
+#ifdef SPECTRUM_ANALYSER_SEPARATE_THREAD
+ QThread *m_thread;
+#endif
+};
+
+/**
+ * Class which performs frequency spectrum analysis on a window of
+ * audio samples, provided to it by the Engine.
+ */
+class SpectrumAnalyser : public QObject
+{
+ Q_OBJECT
+
+public:
+ SpectrumAnalyser(QObject *parent = nullptr);
+ ~SpectrumAnalyser();
+
+#ifdef DUMP_SPECTRUMANALYSER
+ void setOutputPath(const QString &outputPath);
+#endif
+
+public:
+ /*
+ * Set the windowing function which is applied before calculating the FFT
+ */
+ void setWindowFunction(WindowFunction type);
+
+ /*
+ * Calculate a frequency spectrum
+ *
+ * \param buffer Audio data
+ * \param format Format of audio data
+ *
+ * Frequency spectrum is calculated asynchronously. The result is returned
+ * via the spectrumChanged signal.
+ *
+ * An ongoing calculation can be cancelled by calling cancelCalculation().
+ *
+ */
+ void calculate(const QByteArray &buffer, const QAudioFormat &format);
+
+ /*
+ * Check whether the object is ready to perform another calculation
+ */
+ bool isReady() const;
+
+ /*
+ * Cancel an ongoing calculation
+ *
+ * Note that cancelling is asynchronous.
+ */
+ void cancelCalculation();
+
+signals:
+ void spectrumChanged(const FrequencySpectrum &spectrum);
+
+private slots:
+ void calculationComplete(const FrequencySpectrum &spectrum);
+
+private:
+ void calculateWindow();
+
+private:
+ SpectrumAnalyserThread *m_thread;
+
+ enum State { Idle, Busy, Cancelled };
+
+ State m_state;
+
+#ifdef DUMP_SPECTRUMANALYSER
+ QDir m_outputDir;
+ int m_count;
+ QFile m_textFile;
+ QTextStream m_textStream;
+#endif
+};
+
+#endif // SPECTRUMANALYSER_H
diff --git a/examples/multimedia/spectrum/tonegenerator.cpp b/examples/multimedia/spectrum/tonegenerator.cpp
new file mode 100644
index 000000000..5df1df44d
--- /dev/null
+++ b/examples/multimedia/spectrum/tonegenerator.cpp
@@ -0,0 +1,54 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "spectrum.h"
+#include "utils.h"
+
+#include <QAudioFormat>
+#include <QByteArray>
+#include <QtEndian>
+#include <QtMath>
+
+void generateTone(const SweptTone &tone, const QAudioFormat &format, QByteArray &buffer)
+{
+ Q_ASSERT(format.sampleFormat() == QAudioFormat::Int16);
+
+ const int channelBytes = format.bytesPerSample();
+ const int sampleBytes = format.channelCount() * channelBytes;
+ int length = buffer.size();
+ const int numSamples = buffer.size() / sampleBytes;
+
+ Q_ASSERT(length % sampleBytes == 0);
+ Q_UNUSED(sampleBytes); // suppress warning in release builds
+
+ unsigned char *ptr = reinterpret_cast<unsigned char *>(buffer.data());
+
+ qreal phase = 0.0;
+
+ const qreal d = 2 * M_PI / format.sampleRate();
+
+ // We can't generate a zero-frequency sine wave
+ const qreal startFreq = tone.startFreq ? tone.startFreq : 1.0;
+
+ // Amount by which phase increases on each sample
+ qreal phaseStep = d * startFreq;
+
+ // Amount by which phaseStep increases on each sample
+ // If this is non-zero, the output is a frequency-swept tone
+ const qreal phaseStepStep = d * (tone.endFreq - startFreq) / numSamples;
+
+ while (length) {
+ const qreal x = tone.amplitude * qSin(phase);
+ const qint16 value = realToPcm(x);
+ for (int i = 0; i < format.channelCount(); ++i) {
+ qToLittleEndian<qint16>(value, ptr);
+ ptr += channelBytes;
+ length -= channelBytes;
+ }
+
+ phase += phaseStep;
+ while (phase > 2 * M_PI)
+ phase -= 2 * M_PI;
+ phaseStep += phaseStepStep;
+ }
+}
diff --git a/examples/multimedia/spectrum/tonegenerator.h b/examples/multimedia/spectrum/tonegenerator.h
new file mode 100644
index 000000000..2c331fb3c
--- /dev/null
+++ b/examples/multimedia/spectrum/tonegenerator.h
@@ -0,0 +1,21 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef TONEGENERATOR_H
+#define TONEGENERATOR_H
+
+#include "spectrum.h"
+
+#include <QtGlobal>
+
+QT_BEGIN_NAMESPACE
+class QAudioFormat;
+class QByteArray;
+QT_END_NAMESPACE
+
+/**
+ * Generate a sine wave
+ */
+void generateTone(const SweptTone &tone, const QAudioFormat &format, QByteArray &buffer);
+
+#endif // TONEGENERATOR_H
diff --git a/examples/multimedia/spectrum/tonegeneratordialog.cpp b/examples/multimedia/spectrum/tonegeneratordialog.cpp
new file mode 100644
index 000000000..1c98c2b86
--- /dev/null
+++ b/examples/multimedia/spectrum/tonegeneratordialog.cpp
@@ -0,0 +1,105 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "tonegeneratordialog.h"
+
+#include <QCheckBox>
+#include <QComboBox>
+#include <QDialogButtonBox>
+#include <QLabel>
+#include <QPushButton>
+#include <QSlider>
+#include <QSpinBox>
+#include <QVBoxLayout>
+
+const int ToneGeneratorFreqMin = 1;
+const int ToneGeneratorFreqMax = 1000;
+const int ToneGeneratorFreqDefault = 440;
+const int ToneGeneratorAmplitudeDefault = 75;
+
+ToneGeneratorDialog::ToneGeneratorDialog(QWidget *parent)
+ : QDialog(parent),
+ m_toneGeneratorSweepCheckBox(new QCheckBox(tr("Frequency sweep"), this)),
+ m_frequencySweepEnabled(true),
+ m_toneGeneratorControl(new QWidget(this)),
+ m_toneGeneratorFrequencyControl(new QWidget(this)),
+ m_frequencySlider(new QSlider(Qt::Horizontal, this)),
+ m_frequencySpinBox(new QSpinBox(this)),
+ m_amplitudeSlider(new QSlider(Qt::Horizontal, this))
+{
+ QVBoxLayout *dialogLayout = new QVBoxLayout(this);
+
+ m_toneGeneratorSweepCheckBox->setChecked(true);
+
+ // Configure tone generator controls
+ m_frequencySlider->setRange(ToneGeneratorFreqMin, ToneGeneratorFreqMax);
+ m_frequencySlider->setValue(ToneGeneratorFreqDefault);
+ m_frequencySpinBox->setRange(ToneGeneratorFreqMin, ToneGeneratorFreqMax);
+ m_frequencySpinBox->setValue(ToneGeneratorFreqDefault);
+ m_amplitudeSlider->setRange(0, 100);
+ m_amplitudeSlider->setValue(ToneGeneratorAmplitudeDefault);
+
+ // Add widgets to layout
+ QGridLayout *frequencyControlLayout = new QGridLayout;
+ QLabel *frequencyLabel = new QLabel(tr("Frequency (Hz)"), this);
+ frequencyControlLayout->addWidget(frequencyLabel, 0, 0, 2, 1);
+ frequencyControlLayout->addWidget(m_frequencySlider, 0, 1);
+ frequencyControlLayout->addWidget(m_frequencySpinBox, 1, 1);
+ m_toneGeneratorFrequencyControl->setLayout(frequencyControlLayout);
+ m_toneGeneratorFrequencyControl->setEnabled(false);
+
+ QGridLayout *toneGeneratorLayout = new QGridLayout;
+ QLabel *amplitudeLabel = new QLabel(tr("Amplitude"), this);
+ toneGeneratorLayout->addWidget(m_toneGeneratorSweepCheckBox, 0, 1);
+ toneGeneratorLayout->addWidget(m_toneGeneratorFrequencyControl, 1, 0, 1, 2);
+ toneGeneratorLayout->addWidget(amplitudeLabel, 2, 0);
+ toneGeneratorLayout->addWidget(m_amplitudeSlider, 2, 1);
+ m_toneGeneratorControl->setLayout(toneGeneratorLayout);
+ m_toneGeneratorControl->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
+ dialogLayout->addWidget(m_toneGeneratorControl);
+
+ // Connect
+ connect(m_toneGeneratorSweepCheckBox, &QCheckBox::toggled, this,
+ &ToneGeneratorDialog::frequencySweepEnabled);
+ connect(m_frequencySlider, &QSlider::valueChanged, m_frequencySpinBox, &QSpinBox::setValue);
+ connect(m_frequencySpinBox, QOverload<int>::of(&QSpinBox::valueChanged), m_frequencySlider,
+ &QSlider::setValue);
+
+ // Add standard buttons to layout
+ QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
+ buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+ dialogLayout->addWidget(buttonBox);
+
+ // Connect standard buttons
+ connect(buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this,
+ &ToneGeneratorDialog::accept);
+ connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this,
+ &ToneGeneratorDialog::reject);
+
+ setLayout(dialogLayout);
+}
+
+ToneGeneratorDialog::~ToneGeneratorDialog() = default;
+
+bool ToneGeneratorDialog::isFrequencySweepEnabled() const
+{
+ return m_toneGeneratorSweepCheckBox->isChecked();
+}
+
+qreal ToneGeneratorDialog::frequency() const
+{
+ return qreal(m_frequencySlider->value());
+}
+
+qreal ToneGeneratorDialog::amplitude() const
+{
+ return qreal(m_amplitudeSlider->value()) / 100.0;
+}
+
+void ToneGeneratorDialog::frequencySweepEnabled(bool enabled)
+{
+ m_frequencySweepEnabled = enabled;
+ m_toneGeneratorFrequencyControl->setEnabled(!enabled);
+}
+
+#include "moc_tonegeneratordialog.cpp"
diff --git a/examples/multimedia/spectrum/tonegeneratordialog.h b/examples/multimedia/spectrum/tonegeneratordialog.h
new file mode 100644
index 000000000..2b0aed501
--- /dev/null
+++ b/examples/multimedia/spectrum/tonegeneratordialog.h
@@ -0,0 +1,47 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef TONEGENERATORDIALOG_H
+#define TONEGENERATORDIALOG_H
+
+#include "spectrum.h"
+
+#include <QAudioDevice>
+#include <QDialog>
+
+QT_BEGIN_NAMESPACE
+class QCheckBox;
+class QSlider;
+class QSpinBox;
+class QGridLayout;
+QT_END_NAMESPACE
+
+/**
+ * Dialog which controls the parameters of the tone generator.
+ */
+class ToneGeneratorDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit ToneGeneratorDialog(QWidget *parent = nullptr);
+ ~ToneGeneratorDialog();
+
+ bool isFrequencySweepEnabled() const;
+ qreal frequency() const;
+ qreal amplitude() const;
+
+private slots:
+ void frequencySweepEnabled(bool enabled);
+
+private:
+ QCheckBox *m_toneGeneratorSweepCheckBox;
+ bool m_frequencySweepEnabled;
+ QWidget *m_toneGeneratorControl;
+ QWidget *m_toneGeneratorFrequencyControl;
+ QSlider *m_frequencySlider;
+ QSpinBox *m_frequencySpinBox;
+ QSlider *m_amplitudeSlider;
+};
+
+#endif // TONEGENERATORDIALOG_H
diff --git a/examples/multimedia/spectrum/utils.cpp b/examples/multimedia/spectrum/utils.cpp
new file mode 100644
index 000000000..3b4146a51
--- /dev/null
+++ b/examples/multimedia/spectrum/utils.cpp
@@ -0,0 +1,68 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "utils.h"
+#include <QAudioFormat>
+
+qreal nyquistFrequency(const QAudioFormat &format)
+{
+ return format.sampleRate() / 2;
+}
+
+QString formatToString(const QAudioFormat &format)
+{
+ QString result;
+
+ if (QAudioFormat() != format) {
+
+ QString formatType;
+ switch (format.sampleFormat()) {
+ case QAudioFormat::UInt8:
+ formatType = "Unsigned8";
+ break;
+ case QAudioFormat::Int16:
+ formatType = "Signed16";
+ break;
+ case QAudioFormat::Int32:
+ formatType = "Signed32";
+ break;
+ case QAudioFormat::Float:
+ formatType = "Float";
+ break;
+ default:
+ formatType = "Unknown";
+ break;
+ }
+
+ QString formatChannels = QStringLiteral("%1 channels").arg(format.channelCount());
+ switch (format.channelCount()) {
+ case 1:
+ formatChannels = "mono";
+ break;
+ case 2:
+ formatChannels = "stereo";
+ break;
+ }
+
+ result = QStringLiteral("%1 Hz %2 bit %3 %4")
+ .arg(format.sampleRate())
+ .arg(format.bytesPerSample() * 8)
+ .arg(formatType)
+ .arg(formatChannels);
+ }
+
+ return result;
+}
+
+const qint16 PCMS16MaxValue = 32767;
+const quint16 PCMS16MaxAmplitude = 32768; // because minimum is -32768
+
+qreal pcmToReal(qint16 pcm)
+{
+ return qreal(pcm) / PCMS16MaxAmplitude;
+}
+
+qint16 realToPcm(qreal real)
+{
+ return real * PCMS16MaxValue;
+}
diff --git a/examples/multimedia/spectrum/utils.h b/examples/multimedia/spectrum/utils.h
new file mode 100644
index 000000000..da0711c66
--- /dev/null
+++ b/examples/multimedia/spectrum/utils.h
@@ -0,0 +1,79 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <QDebug>
+#include <QtGlobal>
+
+QT_FORWARD_DECLARE_CLASS(QAudioFormat)
+
+//-----------------------------------------------------------------------------
+// Miscellaneous utility functions
+//-----------------------------------------------------------------------------
+
+QString formatToString(const QAudioFormat &format);
+
+qreal nyquistFrequency(const QAudioFormat &format);
+
+// Scale PCM value to [-1.0, 1.0]
+qreal pcmToReal(qint16 pcm);
+
+// Scale real value in [-1.0, 1.0] to PCM
+qint16 realToPcm(qreal real);
+
+// Compile-time calculation of powers of two
+
+template<int N>
+class PowerOfTwo
+{
+public:
+ static const int Result = PowerOfTwo<N - 1>::Result * 2;
+};
+
+template<>
+class PowerOfTwo<0>
+{
+public:
+ static const int Result = 1;
+};
+
+//-----------------------------------------------------------------------------
+// Debug output
+//-----------------------------------------------------------------------------
+
+class NullDebug
+{
+public:
+ template<typename T>
+ NullDebug &operator<<(const T &)
+ {
+ return *this;
+ }
+};
+
+inline NullDebug nullDebug()
+{
+ return NullDebug();
+}
+
+#ifdef LOG_ENGINE
+# define ENGINE_DEBUG qDebug()
+#else
+# define ENGINE_DEBUG nullDebug()
+#endif
+
+#ifdef LOG_SPECTRUMANALYSER
+# define SPECTRUMANALYSER_DEBUG qDebug()
+#else
+# define SPECTRUMANALYSER_DEBUG nullDebug()
+#endif
+
+#ifdef LOG_WAVEFORM
+# define WAVEFORM_DEBUG qDebug()
+#else
+# define WAVEFORM_DEBUG nullDebug()
+#endif
+
+#endif // UTILS_H
diff --git a/examples/multimedia/spectrum/app/waveform.cpp b/examples/multimedia/spectrum/waveform.cpp
index 3e7462157..a3727a2a6 100644
--- a/examples/multimedia/spectrum/app/waveform.cpp
+++ b/examples/multimedia/spectrum/waveform.cpp
@@ -1,76 +1,30 @@
-/****************************************************************************
-**
-** 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 "waveform.h"
#include "utils.h"
+
+#include <QDebug>
#include <QPainter>
#include <QResizeEvent>
-#include <QDebug>
//#define PAINT_EVENT_TRACE
#ifdef PAINT_EVENT_TRACE
-# define WAVEFORM_PAINT_DEBUG qDebug()
+# define WAVEFORM_PAINT_DEBUG qDebug()
#else
-# define WAVEFORM_PAINT_DEBUG nullDebug()
+# define WAVEFORM_PAINT_DEBUG nullDebug()
#endif
Waveform::Waveform(QWidget *parent)
- : QWidget(parent)
- , m_bufferPosition(0)
- , m_bufferLength(0)
- , m_audioPosition(0)
- , m_active(false)
- , m_tileLength(0)
- , m_tileArrayStart(0)
- , m_windowPosition(0)
- , m_windowLength(0)
+ : QWidget(parent),
+ m_bufferPosition(0),
+ m_bufferLength(0),
+ m_audioPosition(0),
+ m_active(false),
+ m_tileLength(0),
+ m_tileArrayStart(0),
+ m_windowPosition(0),
+ m_windowLength(0)
{
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
setMinimumHeight(50);
@@ -89,24 +43,23 @@ void Waveform::paintEvent(QPaintEvent * /*event*/)
if (m_active) {
WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent"
- << "windowPosition" << m_windowPosition
- << "windowLength" << m_windowLength;
+ << "windowPosition" << m_windowPosition << "windowLength"
+ << m_windowLength;
qint64 pos = m_windowPosition;
const qint64 windowEnd = m_windowPosition + m_windowLength;
int destLeft = 0;
int destRight = 0;
while (pos < windowEnd) {
const TilePoint point = tilePoint(pos);
- WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent" << "pos" << pos
- << "tileIndex" << point.index
- << "positionOffset" << point.positionOffset
- << "pixelOffset" << point.pixelOffset;
+ WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent"
+ << "pos" << pos << "tileIndex" << point.index << "positionOffset"
+ << point.positionOffset << "pixelOffset" << point.pixelOffset;
if (point.index != NullIndex) {
const Tile &tile = m_tiles[point.index];
if (tile.painted) {
- const qint64 sectionLength = qMin((m_tileLength - point.positionOffset),
- (windowEnd - pos));
+ const qint64 sectionLength =
+ qMin((m_tileLength - point.positionOffset), (windowEnd - pos));
Q_ASSERT(sectionLength > 0);
const int sourceRight = tilePixelOffset(point.positionOffset + sectionLength);
@@ -120,9 +73,10 @@ void Waveform::paintEvent(QPaintEvent * /*event*/)
sourceRect.setLeft(point.pixelOffset);
sourceRect.setRight(sourceRight);
- WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent" << "tileIndex" << point.index
- << "source" << point.pixelOffset << sourceRight
- << "dest" << destLeft << destRight;
+ WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent"
+ << "tileIndex" << point.index << "source"
+ << point.pixelOffset << sourceRight << "dest" << destLeft
+ << destRight;
painter.drawPixmap(destRect, *tile.pixmap, sourceRect);
@@ -130,25 +84,30 @@ void Waveform::paintEvent(QPaintEvent * /*event*/)
if (point.index < m_tiles.count()) {
pos = tilePosition(point.index + 1);
- WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent" << "pos ->" << pos;
+ WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent"
+ << "pos ->" << pos;
} else {
// Reached end of tile array
- WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent" << "reached end of tile array";
+ WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent"
+ << "reached end of tile array";
break;
}
} else {
// Passed last tile which is painted
- WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent" << "tile" << point.index << "not painted";
+ WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent"
+ << "tile" << point.index << "not painted";
break;
}
} else {
// pos is past end of tile array
- WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent" << "pos" << pos << "past end of tile array";
+ WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent"
+ << "pos" << pos << "past end of tile array";
break;
}
}
- WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent" << "final pos" << pos << "final x" << destRight;
+ WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent"
+ << "final pos" << pos << "final x" << destRight;
}
}
@@ -158,11 +117,12 @@ void Waveform::resizeEvent(QResizeEvent *event)
createPixmaps(event->size());
}
-void Waveform::initialize(const QAudioFormat &format, qint64 audioBufferSize, qint64 windowDurationUs)
+void Waveform::initialize(const QAudioFormat &format, qint64 audioBufferSize,
+ qint64 windowDurationUs)
{
WAVEFORM_DEBUG << "Waveform::initialize"
- << "audioBufferSize" << audioBufferSize
- << "windowDurationUs" << windowDurationUs;
+ << "audioBufferSize" << audioBufferSize << "windowDurationUs"
+ << windowDurationUs;
reset();
@@ -172,7 +132,7 @@ void Waveform::initialize(const QAudioFormat &format, qint64 audioBufferSize, qi
m_tileLength = audioBufferSize;
// Calculate window size
- m_windowLength = audioLength(m_format, windowDurationUs);
+ m_windowLength = m_format.bytesForDuration(windowDurationUs);
// Calculate number of tiles required
int nTiles;
@@ -185,11 +145,10 @@ void Waveform::initialize(const QAudioFormat &format, qint64 audioBufferSize, qi
}
WAVEFORM_DEBUG << "Waveform::initialize"
- << "tileLength" << m_tileLength
- << "windowLength" << m_windowLength
- << "nTiles" << nTiles;
+ << "tileLength" << m_tileLength << "windowLength" << m_windowLength << "nTiles"
+ << nTiles;
- m_pixmaps.fill(0, nTiles);
+ m_pixmaps.fill(nullptr, nTiles);
m_tiles.resize(nTiles);
createPixmaps(rect().size());
@@ -217,8 +176,7 @@ void Waveform::reset()
void Waveform::bufferChanged(qint64 position, qint64 length, const QByteArray &buffer)
{
WAVEFORM_DEBUG << "Waveform::bufferChanged"
- << "audioPosition" << m_audioPosition
- << "bufferPosition" << position
+ << "audioPosition" << m_audioPosition << "bufferPosition" << position
<< "bufferLength" << length;
m_bufferPosition = position;
m_bufferLength = length;
@@ -229,8 +187,7 @@ void Waveform::bufferChanged(qint64 position, qint64 length, const QByteArray &b
void Waveform::audioPositionChanged(qint64 position)
{
WAVEFORM_DEBUG << "Waveform::audioPositionChanged"
- << "audioPosition" << position
- << "bufferPosition" << m_bufferPosition
+ << "audioPosition" << position << "bufferPosition" << m_bufferPosition
<< "bufferLength" << m_bufferLength;
if (position >= m_bufferPosition) {
@@ -243,7 +200,7 @@ void Waveform::audioPositionChanged(qint64 position)
void Waveform::deletePixmaps()
{
- qDeleteAll(qExchange(m_pixmaps, {}));
+ qDeleteAll(std::exchange(m_pixmaps, {}));
}
void Waveform::createPixmaps(const QSize &widgetSize)
@@ -252,20 +209,18 @@ void Waveform::createPixmaps(const QSize &widgetSize)
m_pixmapSize.setWidth(qreal(widgetSize.width()) * m_tileLength / m_windowLength);
WAVEFORM_DEBUG << "Waveform::createPixmaps"
- << "widgetSize" << widgetSize
- << "pixmapSize" << m_pixmapSize;
+ << "widgetSize" << widgetSize << "pixmapSize" << m_pixmapSize;
Q_ASSERT(m_tiles.count() == m_pixmaps.count());
// (Re)create pixmaps
- for (int i=0; i<m_pixmaps.size(); ++i) {
- delete m_pixmaps[i];
- m_pixmaps[i] = 0;
- m_pixmaps[i] = new QPixmap(m_pixmapSize);
+ for (auto &pixmap : m_pixmaps) {
+ delete pixmap;
+ pixmap = new QPixmap(m_pixmapSize);
}
// Update tile pixmap pointers, and mark for repainting
- for (int i=0; i<m_tiles.count(); ++i) {
+ for (int i = 0; i < m_tiles.count(); ++i) {
m_tiles[i].pixmap = m_pixmaps[i];
m_tiles[i].painted = false;
}
@@ -274,14 +229,14 @@ void Waveform::createPixmaps(const QSize &widgetSize)
void Waveform::setWindowPosition(qint64 position)
{
WAVEFORM_DEBUG << "Waveform::setWindowPosition"
- << "old" << m_windowPosition << "new" << position
- << "tileArrayStart" << m_tileArrayStart;
+ << "old" << m_windowPosition << "new" << position << "tileArrayStart"
+ << m_tileArrayStart;
const qint64 oldPosition = m_windowPosition;
m_windowPosition = position;
- if ((m_windowPosition >= oldPosition) &&
- (m_windowPosition - m_tileArrayStart < (m_tiles.count() * m_tileLength))) {
+ if ((m_windowPosition >= oldPosition)
+ && (m_windowPosition - m_tileArrayStart < (m_tiles.count() * m_tileLength))) {
// Work out how many tiles need to be shuffled
const qint64 offset = m_windowPosition - m_tileArrayStart;
const int nTiles = offset / m_tileLength;
@@ -336,7 +291,7 @@ bool Waveform::paintTiles()
WAVEFORM_DEBUG << "Waveform::paintTiles";
bool updateRequired = false;
- for (int i=0; i<m_tiles.count(); ++i) {
+ for (int i = 0; i < m_tiles.count(); ++i) {
const Tile &tile = m_tiles[i];
if (!tile.painted) {
const qint64 tileStart = m_tileArrayStart + i * m_tileLength;
@@ -359,11 +314,8 @@ void Waveform::paintTile(int index)
const qint64 tileStart = m_tileArrayStart + index * m_tileLength;
WAVEFORM_DEBUG << "Waveform::paintTile"
- << "index" << index
- << "bufferPosition" << m_bufferPosition
- << "bufferLength" << m_bufferLength
- << "start" << tileStart
- << "end" << tileStart + m_tileLength;
+ << "index" << index << "bufferPosition" << m_bufferPosition << "bufferLength"
+ << m_bufferLength << "start" << tileStart << "end" << tileStart + m_tileLength;
Q_ASSERT(m_bufferPosition <= tileStart);
Q_ASSERT(m_bufferPosition + m_bufferLength >= tileStart + m_tileLength);
@@ -371,8 +323,8 @@ void Waveform::paintTile(int index)
Tile &tile = m_tiles[index];
Q_ASSERT(!tile.painted);
- const qint16* base = reinterpret_cast<const qint16*>(m_buffer.constData());
- const qint16* buffer = base + ((tileStart - m_bufferPosition) / 2);
+ const qint16 *base = reinterpret_cast<const qint16 *>(m_buffer.constData());
+ const qint16 *buffer = base + ((tileStart - m_bufferPosition) / 2);
const int numSamples = m_tileLength / (2 * m_format.channelCount());
QPainter painter(tile.pixmap);
@@ -394,10 +346,10 @@ void Waveform::paintTile(int index)
QLine line(origin, origin);
- for (int i=0; i<numSamples; ++i) {
- const qint16* ptr = buffer + i * m_format.channelCount();
+ for (int i = 0; i < numSamples; ++i) {
+ const qint16 *ptr = buffer + i * m_format.channelCount();
- const int offset = reinterpret_cast<const char*>(ptr) - m_buffer.constData();
+ const int offset = reinterpret_cast<const char *>(ptr) - m_buffer.constData();
Q_ASSERT(offset >= 0);
Q_ASSERT(offset < m_bufferLength);
Q_UNUSED(offset);
@@ -418,7 +370,8 @@ void Waveform::paintTile(int index)
void Waveform::shuffleTiles(int n)
{
- WAVEFORM_DEBUG << "Waveform::shuffleTiles" << "n" << n;
+ WAVEFORM_DEBUG << "Waveform::shuffleTiles"
+ << "n" << n;
while (n--) {
Tile tile = m_tiles.first();
@@ -428,17 +381,20 @@ void Waveform::shuffleTiles(int n)
m_tileArrayStart += m_tileLength;
}
- WAVEFORM_DEBUG << "Waveform::shuffleTiles" << "tileArrayStart" << m_tileArrayStart;
+ WAVEFORM_DEBUG << "Waveform::shuffleTiles"
+ << "tileArrayStart" << m_tileArrayStart;
}
void Waveform::resetTiles(qint64 newStartPos)
{
- WAVEFORM_DEBUG << "Waveform::resetTiles" << "newStartPos" << newStartPos;
+ WAVEFORM_DEBUG << "Waveform::resetTiles"
+ << "newStartPos" << newStartPos;
QList<Tile>::iterator i = m_tiles.begin();
- for ( ; i != m_tiles.end(); ++i)
+ for (; i != m_tiles.end(); ++i)
i->painted = false;
m_tileArrayStart = newStartPos;
}
+#include "moc_waveform.cpp"
diff --git a/examples/multimedia/spectrum/app/waveform.h b/examples/multimedia/spectrum/waveform.h
index 8c26e99b2..86fda9962 100644
--- a/examples/multimedia/spectrum/app/waveform.h
+++ b/examples/multimedia/spectrum/waveform.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 WAVEFORM_H
#define WAVEFORM_H
@@ -70,7 +23,7 @@ class Waveform : public QWidget
Q_OBJECT
public:
- explicit Waveform(QWidget *parent = 0);
+ explicit Waveform(QWidget *parent = nullptr);
~Waveform();
// QWidget
@@ -115,17 +68,18 @@ private:
struct TilePoint
{
TilePoint(int idx = 0, qint64 pos = 0, qint64 pix = 0)
- : index(idx), positionOffset(pos), pixelOffset(pix)
- { }
+ : index(idx), positionOffset(pos), pixelOffset(pix)
+ {
+ }
// Index of tile
- int index;
+ int index;
// Number of bytes from start of tile
- qint64 positionOffset;
+ qint64 positionOffset;
// Number of pixels from left of corresponding pixmap
- int pixelOffset;
+ int pixelOffset;
};
/*
@@ -177,36 +131,37 @@ private:
void resetTiles(qint64 newStartPos);
private:
- qint64 m_bufferPosition;
- qint64 m_bufferLength;
- QByteArray m_buffer;
+ qint64 m_bufferPosition;
+ qint64 m_bufferLength;
+ QByteArray m_buffer;
- qint64 m_audioPosition;
- QAudioFormat m_format;
+ qint64 m_audioPosition;
+ QAudioFormat m_format;
- bool m_active;
+ bool m_active;
- QSize m_pixmapSize;
- QList<QPixmap*> m_pixmaps;
+ QSize m_pixmapSize;
+ QList<QPixmap *> m_pixmaps;
- struct Tile {
+ struct Tile
+ {
// Pointer into parent m_pixmaps array
- QPixmap* pixmap;
+ QPixmap *pixmap;
// Flag indicating whether this tile has been painted
- bool painted;
+ bool painted;
};
- QList<Tile> m_tiles;
+ QList<Tile> m_tiles;
// Length of audio data in bytes depicted by each tile
- qint64 m_tileLength;
+ qint64 m_tileLength;
// Position in bytes of the first tile, relative to m_buffer
- qint64 m_tileArrayStart;
+ qint64 m_tileArrayStart;
- qint64 m_windowPosition;
- qint64 m_windowLength;
+ qint64 m_windowPosition;
+ qint64 m_windowLength;
};
#endif // WAVEFORM_H