mainwidget.cpp Example File
multimedia/spectrum/app/mainwidget.cpp
/****************************************************************************
**
** 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 "levelmeter.h"
#include "mainwidget.h"
#include "waveform.h"
#include "progressbar.h"
#include "settingsdialog.h"
#include "spectrograph.h"
#include "tonegeneratordialog.h"
#include "utils.h"
#include <QLabel>
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QStyle>
#include <QMenu>
#include <QFileDialog>
#include <QTimerEvent>
#include <QMessageBox>
const int NullTimerId = - 1 ;
MainWidget:: MainWidget(QWidget * parent)
: QWidget (parent)
, m_mode(NoMode)
, m_engine(new Engine(this ))
#ifndef DISABLE_WAVEFORM
, 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_spectrograph- > setParams(SpectrumNumBands, SpectrumLowFreq, SpectrumHighFreq);
createUi();
connectUi();
}
MainWidget:: ~ MainWidget()
{
}
//-----------------------------------------------------------------------------
// Public slots
//-----------------------------------------------------------------------------
void MainWidget:: stateChanged(QAudio :: Mode mode, QAudio :: State state)
{
Q_UNUSED(mode);
updateButtonStates();
if (QAudio :: ActiveState ! = state & &
QAudio :: SuspendedState ! = state & &
QAudio :: InterruptedState ! = state) {
m_levelMeter- > reset();
m_spectrograph- > reset();
}
}
void MainWidget:: formatChanged(const QAudioFormat & format)
{
infoMessage(formatToString(format), NullMessageTimeout);
#ifndef DISABLE_WAVEFORM
if (QAudioFormat () ! = format) {
m_waveform- > initialize(format, WaveformTileLength,
WaveformWindowDuration);
}
#endif
}
void MainWidget:: spectrumChanged(qint64 position, qint64 length,
const FrequencySpectrum & spectrum)
{
m_progressBar- > windowChanged(position, length);
m_spectrograph- > spectrumChanged(spectrum);
}
void MainWidget:: infoMessage(const QString & message, int timeoutMs)
{
m_infoMessage- > setText(message);
if (NullTimerId ! = m_infoMessageTimerId) {
killTimer(m_infoMessageTimerId);
m_infoMessageTimerId = NullTimerId;
}
if (NullMessageTimeout ! = timeoutMs)
m_infoMessageTimerId = startTimer(timeoutMs);
}
void MainWidget:: errorMessage(const QString & heading, const QString & detail)
{
QMessageBox :: warning(this , heading, detail, QMessageBox :: Close);
}
void MainWidget:: timerEvent(QTimerEvent * event)
{
Q_ASSERT(event- > timerId() = = m_infoMessageTimerId);
Q_UNUSED(event) // suppress warnings in release builds
killTimer(m_infoMessageTimerId);
m_infoMessageTimerId = NullTimerId;
m_infoMessage- > setText("" );
}
void MainWidget:: audioPositionChanged(qint64 position)
{
#ifndef DISABLE_WAVEFORM
m_waveform- > audioPositionChanged(position);
#else
Q_UNUSED(position)
#endif
}
void MainWidget:: bufferLengthChanged(qint64 length)
{
m_progressBar- > bufferLengthChanged(length);
}
//-----------------------------------------------------------------------------
// Private slots
//-----------------------------------------------------------------------------
void MainWidget:: showFileDialog()
{
const QString dir;
const QStringList fileNames = QFileDialog :: getOpenFileNames(this , tr("Open WAV file" ), dir, "*.wav" );
if (fileNames. count()) {
reset();
setMode(LoadFileMode);
m_engine- > loadFile(fileNames. front());
updateButtonStates();
} else {
updateModeMenu();
}
}
void MainWidget:: showSettingsDialog()
{
m_settingsDialog- > exec();
if (m_settingsDialog- > result() = = QDialog :: Accepted) {
m_engine- > setAudioInputDevice(m_settingsDialog- > inputDevice());
m_engine- > setAudioOutputDevice(m_settingsDialog- > outputDevice());
m_engine- > setWindowFunction(m_settingsDialog- > windowFunction());
}
}
void MainWidget:: showToneGeneratorDialog()
{
m_toneGeneratorDialog- > exec();
if (m_toneGeneratorDialog- > result() = = QDialog :: Accepted) {
reset();
setMode(GenerateToneMode);
const qreal amplitude = m_toneGeneratorDialog- > amplitude();
if (m_toneGeneratorDialog- > isFrequencySweepEnabled()) {
m_engine- > generateSweptTone(amplitude);
} else {
const qreal frequency = m_toneGeneratorDialog- > frequency();
const Tone tone(frequency, amplitude);
m_engine- > generateTone(tone);
updateButtonStates();
}
} else {
updateModeMenu();
}
}
void MainWidget:: initializeRecord()
{
reset();
setMode(RecordMode);
if (m_engine- > initializeRecord())
updateButtonStates();
}
//-----------------------------------------------------------------------------
// Private functions
//-----------------------------------------------------------------------------
void MainWidget:: createUi()
{
createMenus();
setWindowTitle(tr("Spectrum Analyser" ));
QVBoxLayout * windowLayout = new QVBoxLayout (this );
m_infoMessage- > setSizePolicy(QSizePolicy :: Preferred, QSizePolicy :: Fixed);
m_infoMessage- > setAlignment(Qt :: AlignHCenter);
windowLayout- > addWidget(m_infoMessage);
#ifdef SUPERIMPOSE_PROGRESS_ON_WAVEFORM
QScopedPointer < QHBoxLayout > waveformLayout(new QHBoxLayout );
waveformLayout- > addWidget(m_progressBar);
m_progressBar- > setMinimumHeight(m_waveform- > minimumHeight());
waveformLayout- > setMargin(0 );
m_waveform- > setLayout(waveformLayout. data());
waveformLayout. take();
windowLayout- > addWidget(m_waveform);
#else
#ifndef DISABLE_WAVEFORM
windowLayout- > addWidget(m_waveform);
#endif // DISABLE_WAVEFORM
windowLayout- > addWidget(m_progressBar);
#endif // SUPERIMPOSE_PROGRESS_ON_WAVEFORM
// Spectrograph and level meter
QScopedPointer < QHBoxLayout > analysisLayout(new QHBoxLayout );
analysisLayout- > addWidget(m_spectrograph);
analysisLayout- > addWidget(m_levelMeter);
windowLayout- > addLayout(analysisLayout. data());
analysisLayout. take();
// Button panel
const QSize buttonSize(30 , 30 );
m_modeButton- > setText(tr("Mode" ));
m_recordIcon = QIcon (":/images/record.png" );
m_recordButton- > setIcon(m_recordIcon);
m_recordButton- > setEnabled(false );
m_recordButton- > setSizePolicy(QSizePolicy :: Fixed, QSizePolicy :: Fixed);
m_recordButton- > setMinimumSize(buttonSize);
m_pauseIcon = style()- > standardIcon(QStyle :: SP_MediaPause);
m_pauseButton- > setIcon(m_pauseIcon);
m_pauseButton- > setEnabled(false );
m_pauseButton- > setSizePolicy(QSizePolicy :: Fixed, QSizePolicy :: Fixed);
m_pauseButton- > setMinimumSize(buttonSize);
m_playIcon = style()- > standardIcon(QStyle :: SP_MediaPlay);
m_playButton- > setIcon(m_playIcon);
m_playButton- > setEnabled(false );
m_playButton- > setSizePolicy(QSizePolicy :: Fixed, QSizePolicy :: Fixed);
m_playButton- > setMinimumSize(buttonSize);
m_settingsIcon = QIcon (":/images/settings.png" );
m_settingsButton- > setIcon(m_settingsIcon);
m_settingsButton- > setEnabled(true );
m_settingsButton- > setSizePolicy(QSizePolicy :: Fixed, QSizePolicy :: Fixed);
m_settingsButton- > setMinimumSize(buttonSize);
QScopedPointer < QHBoxLayout > buttonPanelLayout(new QHBoxLayout );
buttonPanelLayout- > addStretch();
buttonPanelLayout- > addWidget(m_modeButton);
buttonPanelLayout- > addWidget(m_recordButton);
buttonPanelLayout- > addWidget(m_pauseButton);
buttonPanelLayout- > addWidget(m_playButton);
buttonPanelLayout- > addWidget(m_settingsButton);
QWidget * buttonPanel = new QWidget (this );
buttonPanel- > setSizePolicy(QSizePolicy :: Fixed, QSizePolicy :: Fixed);
buttonPanel- > setLayout(buttonPanelLayout. data());
buttonPanelLayout. take(); // ownership transferred to buttonPanel
QScopedPointer < QHBoxLayout > bottomPaneLayout(new QHBoxLayout );
bottomPaneLayout- > addWidget(buttonPanel);
windowLayout- > addLayout(bottomPaneLayout. data());
bottomPaneLayout. take(); // ownership transferred to windowLayout
// Apply layout
setLayout(windowLayout);
}
void MainWidget:: connectUi()
{
connect(m_recordButton, & QPushButton :: clicked,
m_engine, & Engine:: startRecording);
connect(m_pauseButton, & QPushButton :: clicked,
m_engine, & Engine:: suspend);
connect(m_playButton, & QPushButton :: clicked,
m_engine, & Engine:: startPlayback);
connect(m_settingsButton, & QPushButton :: clicked,
this , & MainWidget:: showSettingsDialog);
connect(m_engine, & Engine:: stateChanged,
this , & MainWidget:: stateChanged);
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:: dataLengthChanged,
this , & MainWidget:: updateButtonStates);
connect(m_engine, & Engine:: recordPositionChanged,
m_progressBar, & ProgressBar:: recordPositionChanged);
connect(m_engine, & Engine:: playPositionChanged,
m_progressBar, & ProgressBar:: playPositionChanged);
connect(m_engine, & Engine:: recordPositionChanged,
this , & MainWidget:: audioPositionChanged);
connect(m_engine, & Engine:: playPositionChanged,
this , & MainWidget:: audioPositionChanged);
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, & Engine:: infoMessage,
this , & MainWidget:: infoMessage);
connect(m_engine, & Engine:: errorMessage,
this , & MainWidget:: errorMessage);
connect(m_spectrograph, & Spectrograph:: infoMessage,
this , & MainWidget:: infoMessage);
#ifndef DISABLE_WAVEFORM
connect(m_engine, & Engine:: bufferChanged,
m_waveform, & Waveform:: bufferChanged);
#endif
}
void MainWidget:: createMenus()
{
m_modeButton- > setMenu(m_modeMenu);
m_generateToneAction = m_modeMenu- > addAction(tr("Play generated tone" ));
m_recordAction = m_modeMenu- > addAction(tr("Record and play back" ));
m_loadFileAction = m_modeMenu- > addAction(tr("Play file" ));
m_loadFileAction- > setCheckable(true );
m_generateToneAction- > setCheckable(true );
m_recordAction- > setCheckable(true );
connect(m_loadFileAction, & QAction :: triggered, this , & MainWidget:: showFileDialog);
connect(m_generateToneAction, & QAction :: triggered, this , & MainWidget:: showToneGeneratorDialog);
connect(m_recordAction, & QAction :: triggered, this , & MainWidget:: initializeRecord);
}
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 pauseEnabled = (QAudio :: ActiveState = = m_engine- > state() | |
QAudio :: IdleState = = m_engine- > state());
m_pauseButton- > setEnabled(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);
}
void MainWidget:: reset()
{
#ifndef DISABLE_WAVEFORM
m_waveform- > reset();
#endif
m_engine- > reset();
m_levelMeter- > reset();
m_spectrograph- > reset();
m_progressBar- > reset();
}
void MainWidget:: setMode(Mode mode)
{
m_mode = mode;
updateModeMenu();
}
void MainWidget:: updateModeMenu()
{
m_loadFileAction- > setChecked(LoadFileMode = = m_mode);
m_generateToneAction- > setChecked(GenerateToneMode = = m_mode);
m_recordAction- > setChecked(RecordMode = = m_mode);
}