engine.cpp Example File
multimedia/spectrum/app/engine.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 "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" );
CHECKED_CONNECT(& m_spectrumAnalyser,
SIGNAL(spectrumChanged(FrequencySpectrum)),
this ,
SLOT(spectrumChanged(FrequencySpectrum)));
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;
CHECKED_CONNECT(m_audioInput, SIGNAL(stateChanged(QAudio :: State)),
this , SLOT(audioStateChanged(QAudio :: State)));
CHECKED_CONNECT(m_audioInput, SIGNAL(notify()),
this , SLOT(audioNotify()));
m_count = 0 ;
m_dataLength = 0 ;
emit dataLengthChanged(0 );
m_audioInputIODevice = m_audioInput- > start();
CHECKED_CONNECT(m_audioInputIODevice, SIGNAL(readyRead()),
this , SLOT(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;
CHECKED_CONNECT(m_audioOutput, SIGNAL(stateChanged(QAudio :: State)),
this , SLOT(audioStateChanged(QAudio :: State)));
CHECKED_CONNECT(m_audioOutput, SIGNAL(notify()),
this , SLOT(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);
}
} 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- > 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();
sampleRatesList = sampleRatesList. toSet(). toList(); // remove duplicates
qSort (sampleRatesList);
ENGINE_DEBUG < < "Engine::initialize frequenciesList" < < sampleRatesList;
QList < int > channelsList;
channelsList + = m_audioInputDevice. supportedChannelCounts();
channelsList + = m_audioOutputDevice. supportedChannelCounts();
channelsList = channelsList. toSet(). toList();
qSort (channelsList);
ENGINE_DEBUG < < "Engine::initialize channelsList" < < channelsList;
QAudioFormat format;
format. setByteOrder(QAudioFormat :: LittleEndian);
format. setCodec("audio/pcm" );
format. setSampleSize(16 );
format. setSampleType(QAudioFormat :: SignedInt);
int sampleRate, channels;
foreach (sampleRate, sampleRatesList) {
if (foundSupportedFormat)
break ;
format. setSampleRate(sampleRate);
foreach (channels, 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);
QString file;
foreach (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