diff options
Diffstat (limited to 'examples/multimedia/spectrum/app/engine.cpp')
-rw-r--r-- | examples/multimedia/spectrum/app/engine.cpp | 754 |
1 files changed, 754 insertions, 0 deletions
diff --git a/examples/multimedia/spectrum/app/engine.cpp b/examples/multimedia/spectrum/app/engine.cpp new file mode 100644 index 000000000..908cb9df6 --- /dev/null +++ b/examples/multimedia/spectrum/app/engine.cpp @@ -0,0 +1,754 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 Digia Plc and its Subsidiary(-ies) 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 |