summaryrefslogtreecommitdiffstats
path: root/demos/spectrum
diff options
context:
space:
mode:
Diffstat (limited to 'demos/spectrum')
-rw-r--r--demos/spectrum/app/engine.cpp333
-rw-r--r--demos/spectrum/app/engine.h57
-rw-r--r--demos/spectrum/app/mainwidget.cpp67
-rw-r--r--demos/spectrum/app/mainwidget.h10
-rw-r--r--demos/spectrum/app/progressbar.cpp26
-rw-r--r--demos/spectrum/app/progressbar.h4
-rw-r--r--demos/spectrum/app/spectrumanalyser.cpp2
-rw-r--r--demos/spectrum/app/utils.cpp4
-rw-r--r--demos/spectrum/app/waveform.cpp120
-rw-r--r--demos/spectrum/app/waveform.h14
-rw-r--r--demos/spectrum/app/wavfile.cpp205
-rw-r--r--demos/spectrum/app/wavfile.h36
12 files changed, 399 insertions, 479 deletions
diff --git a/demos/spectrum/app/engine.cpp b/demos/spectrum/app/engine.cpp
index 119a0e3..cd847fe 100644
--- a/demos/spectrum/app/engine.cpp
+++ b/demos/spectrum/app/engine.cpp
@@ -85,6 +85,7 @@ Engine::Engine(QObject *parent)
, m_state(QAudio::StoppedState)
, m_generateTone(false)
, m_file(0)
+ , m_analysisFile(0)
, m_availableAudioInputDevices
(QAudioDeviceInfo::availableDevices(QAudio::AudioInput))
, m_audioInputDevice(QAudioDeviceInfo::defaultInputDevice())
@@ -96,15 +97,19 @@ Engine::Engine(QObject *parent)
, 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_spectrumLengthBytes(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,
@@ -132,34 +137,33 @@ Engine::~Engine()
bool Engine::loadFile(const QString &fileName)
{
+ reset();
bool result = false;
- m_generateTone = false;
-
- Q_ASSERT(!fileName.isEmpty());
+ Q_ASSERT(!m_generateTone);
Q_ASSERT(!m_file);
- m_file = new QFile(fileName, this);
- m_file->setFileName(fileName);
- Q_ASSERT(m_file->exists());
- if (m_file->open(QFile::ReadOnly)) {
- m_wavFile.readHeader(*m_file);
- if (isPCMS16LE(m_wavFile.format())) {
+ 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_wavFile.format()));
+ formatToString(m_file->fileFormat()));
}
} else {
emit errorMessage(tr("Could not open file"), fileName);
}
-
- delete m_file;
- m_file = 0;
-
+ 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;
@@ -172,6 +176,7 @@ bool Engine::generateTone(const Tone &tone)
bool Engine::generateSweptTone(qreal amplitude)
{
+ Q_ASSERT(!m_generateTone);
Q_ASSERT(!m_file);
m_generateTone = true;
m_tone.startFreq = 1;
@@ -185,41 +190,18 @@ bool Engine::generateSweptTone(qreal amplitude)
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::bufferDuration() const
-{
- return BufferDurationUs;
-}
-
-qint64 Engine::dataDuration() const
+qint64 Engine::bufferLength() const
{
- qint64 result = 0;
- if (QAudioFormat() != m_format)
- result = audioDuration(m_format, m_dataLength);
- return result;
-}
-
-qint64 Engine::audioBufferLength() const
-{
- qint64 length = 0;
- if (QAudio::ActiveState == m_state || QAudio::IdleState == m_state) {
- Q_ASSERT(QAudioFormat() != m_format);
- switch (m_mode) {
- case QAudio::AudioInput:
- length = m_audioInput->bufferSize();
- break;
- case QAudio::AudioOutput:
- length = m_audioOutput->bufferSize();
- break;
- }
- }
- return length;
+ return m_file ? m_file->size() : m_bufferLength;
}
void Engine::setWindowFunction(WindowFunction type)
@@ -252,7 +234,7 @@ void Engine::startRecording()
this, SLOT(audioNotify()));
m_count = 0;
m_dataLength = 0;
- emit dataDurationChanged(0);
+ emit dataLengthChanged(0);
m_audioInputIODevice = m_audioInput->start();
CHECKED_CONNECT(m_audioInputIODevice, SIGNAL(readyRead()),
this, SLOT(audioDataReady()));
@@ -275,7 +257,6 @@ void Engine::startPlayback()
} else {
m_spectrumAnalyser.cancelCalculation();
spectrumChanged(0, 0, FrequencySpectrum());
-
setPlayPosition(0, true);
stopRecording();
m_mode = QAudio::AudioOutput;
@@ -284,10 +265,17 @@ void Engine::startPlayback()
CHECKED_CONNECT(m_audioOutput, SIGNAL(notify()),
this, SLOT(audioNotify()));
m_count = 0;
- m_audioOutputIODevice.close();
- m_audioOutputIODevice.setBuffer(&m_buffer);
- m_audioOutputIODevice.open(QIODevice::ReadOnly);
- m_audioOutput->start(&m_audioOutputIODevice);
+ 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);
+ }
}
}
}
@@ -332,40 +320,55 @@ void Engine::audioNotify()
{
switch (m_mode) {
case QAudio::AudioInput: {
- const qint64 recordPosition =
- qMin(BufferDurationUs, m_audioInput->processedUSecs());
+ const qint64 recordPosition = qMin(m_bufferLength, audioLength(m_format, m_audioInput->processedUSecs()));
setRecordPosition(recordPosition);
-
- // Calculate level of most recently captured data
- qint64 levelLength = audioLength(m_format, LevelWindowUs);
- levelLength = qMin(m_dataLength, levelLength);
- const qint64 levelPosition = m_dataLength - levelLength;
- calculateLevel(levelPosition, levelLength);
-
- // Calculate spectrum of most recently captured data
- if (m_dataLength >= m_spectrumLengthBytes) {
- const qint64 spectrumPosition = m_dataLength - m_spectrumLengthBytes;
+ 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 =
- qMin(dataDuration(), m_audioOutput->processedUSecs());
- setPlayPosition(playPosition);
-
- qint64 analysisPosition = audioLength(m_format, playPosition);
-
- // Calculate level of data starting at current playback position
- const qint64 levelLength = audioLength(m_format, LevelWindowUs);
- if (analysisPosition + levelLength < m_dataLength)
- calculateLevel(analysisPosition, levelLength);
-
- if (analysisPosition + m_spectrumLengthBytes < m_dataLength)
- calculateSpectrum(analysisPosition);
-
- if (dataDuration() == playPosition)
- stopPlayback();
+ 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;
}
@@ -376,27 +379,32 @@ void Engine::audioStateChanged(QAudio::State state)
ENGINE_DEBUG << "Engine::audioStateChanged from" << m_state
<< "to" << state;
- 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;
+ 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);
}
- 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);
@@ -407,9 +415,7 @@ void Engine::audioDataReady()
if (bytesRead) {
m_dataLength += bytesRead;
-
- const qint64 duration = audioDuration(m_format, m_dataLength);
- emit dataDurationChanged(duration);
+ emit dataLengthChanged(dataLength());
}
if (m_buffer.size() == m_dataLength)
@@ -419,9 +425,7 @@ void Engine::audioDataReady()
void Engine::spectrumChanged(const FrequencySpectrum &spectrum)
{
ENGINE_DEBUG << "Engine::spectrumChanged" << "pos" << m_spectrumPosition;
- const qint64 positionUs = audioDuration(m_format, m_spectrumPosition);
- const qint64 lengthUs = audioDuration(m_format, m_spectrumLengthBytes);
- emit spectrumChanged(positionUs, lengthUs, spectrum);
+ emit spectrumChanged(m_spectrumPosition, m_spectrumBufferLength, spectrum);
}
@@ -429,12 +433,8 @@ void Engine::spectrumChanged(const FrequencySpectrum &spectrum)
// Private functions
//-----------------------------------------------------------------------------
-void Engine::reset()
+void Engine::resetAudioDevices()
{
- stopRecording();
- stopPlayback();
- setState(QAudio::AudioInput, QAudio::StoppedState);
- setFormat(QAudioFormat());
delete m_audioInput;
m_audioInput = 0;
m_audioInputIODevice = 0;
@@ -442,55 +442,71 @@ void Engine::reset()
delete m_audioOutput;
m_audioOutput = 0;
setPlayPosition(0);
- m_buffer.clear();
- m_dataLength = 0;
m_spectrumPosition = 0;
- emit dataDurationChanged(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;
- reset();
+ QAudioFormat format = m_format;
if (selectFormat()) {
- const qint64 bufferLength = audioLength(m_format, BufferDurationUs);
- m_buffer.resize(bufferLength);
- m_buffer.fill(0);
- emit bufferDurationChanged(BufferDurationUs);
-
- 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_buffer.size();
- emit dataDurationChanged(bufferDuration());
- setRecordPosition(bufferDuration());
- result = true;
- } else if (m_file) {
- const qint64 length = m_wavFile.readData(*m_file, m_buffer, m_format);
- if (length) {
- m_dataLength = length;
- emit dataDurationChanged(dataDuration());
- setRecordPosition(dataDuration());
+ 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;
+ }
}
- } else {
- 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 = new QAudioOutput(m_audioOutputDevice, m_format, this);
- m_audioOutput->setNotifyInterval(NotifyIntervalMs);
- m_spectrumLengthBytes = SpectrumLengthSamples *
- (m_format.sampleSize() / 8) * m_format.channels();
} else {
if (m_file)
emit errorMessage(tr("Audio format not supported"),
@@ -501,6 +517,8 @@ bool Engine::initialize()
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;
@@ -510,21 +528,15 @@ bool Engine::selectFormat()
{
bool foundSupportedFormat = false;
- if (m_file) {
- // Header is read from the WAV file; just need to check whether
- // it is supported by the audio output device
- QAudioFormat format = m_wavFile.format();
- if (m_audioOutputDevice.isFormatSupported(m_wavFile.format())) {
- setFormat(m_wavFile.format());
+ 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 {
- // Try flipping mono <-> stereo
- const int channels = (format.channels() == 1) ? 2 : 1;
- format.setChannels(channels);
- if (m_audioOutputDevice.isFormatSupported(format)) {
- setFormat(format);
- foundSupportedFormat = true;
- }
}
} else {
@@ -648,12 +660,12 @@ void Engine::calculateLevel(qint64 position, qint64 length)
Q_UNUSED(position)
Q_UNUSED(length)
#else
- Q_ASSERT(position + length <= m_dataLength);
+ Q_ASSERT(position + length <= m_bufferPosition + m_dataLength);
qreal peakLevel = 0.0;
qreal sum = 0.0;
- const char *ptr = m_buffer.constData() + position;
+ 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);
@@ -679,18 +691,18 @@ void Engine::calculateSpectrum(qint64 position)
#ifdef DISABLE_SPECTRUM
Q_UNUSED(position)
#else
- Q_ASSERT(position + m_spectrumLengthBytes <= m_dataLength);
- Q_ASSERT(0 == m_spectrumLengthBytes % 2); // constraint of FFT algorithm
+ 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_spectrumLengthBytes
+ << "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_spectrumLengthBytes);
+ m_spectrumBuffer = QByteArray::fromRawData(m_buffer.constData() + position - m_bufferPosition,
+ m_spectrumBufferLength);
m_spectrumPosition = position;
m_spectrumAnalyser.calculate(m_spectrumBuffer, m_format);
}
@@ -701,6 +713,9 @@ 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.channels();
if (changed)
emit formatChanged(m_format);
}
diff --git a/demos/spectrum/app/engine.h b/demos/spectrum/app/engine.h
index e14ac83..c97083e 100644
--- a/demos/spectrum/app/engine.h
+++ b/demos/spectrum/app/engine.h
@@ -91,12 +91,6 @@ public:
QAudio::State state() const { return m_state; }
/**
- * \return Reference to internal audio buffer
- * \note This reference is valid for the lifetime of the Engine
- */
- const QByteArray& buffer() const { return m_buffer; }
-
- /**
* \return Current audio format
* \note May be QAudioFormat() if engine is not initialized
*/
@@ -129,7 +123,7 @@ public:
/**
* Position of the audio input device.
- * \return Position in microseconds.
+ * \return Position in bytes.
*/
qint64 recordPosition() const { return m_recordPosition; }
@@ -147,27 +141,21 @@ public:
/**
* Position of the audio output device.
- * \return Position in microseconds.
+ * \return Position in bytes.
*/
qint64 playPosition() const { return m_playPosition; }
/**
* Length of the internal engine buffer.
- * \return Buffer length in microseconds.
+ * \return Buffer length in bytes.
*/
- qint64 bufferDuration() const;
+ qint64 bufferLength() const;
/**
* Amount of data held in the buffer.
- * \return Data duration in microseconds.
+ * \return Data length in bytes.
*/
- qint64 dataDuration() const;
-
- /**
- * Returns the size of the underlying audio buffer in bytes.
- * This should be an approximation of the capture latency.
- */
- qint64 audioBufferLength() const;
+ qint64 dataLength() const { return m_dataLength; }
/**
* Set window function applied to audio data before spectral analysis.
@@ -203,23 +191,23 @@ signals:
* Length of buffer has changed.
* \param duration Duration in microseconds
*/
- void bufferDurationChanged(qint64 duration);
+ void bufferLengthChanged(qint64 duration);
/**
* Amount of data in buffer has changed.
- * \param duration Duration of data in microseconds
+ * \param Length of data in bytes
*/
- void dataDurationChanged(qint64 duration);
+ void dataLengthChanged(qint64 duration);
/**
* Position of the audio input device has changed.
- * \param position Position in microseconds
+ * \param position Position in bytes
*/
void recordPositionChanged(qint64 position);
/**
* Position of the audio output device has changed.
- * \param position Position in microseconds
+ * \param position Position in bytes
*/
void playPositionChanged(qint64 position);
@@ -233,12 +221,19 @@ signals:
/**
* Spectrum has changed.
- * \param position Position of start of window in microseconds
- * \param length Length of window in microseconds
+ * \param position Position of start of window in bytes
+ * \param length Length of window in bytes
* \param spectrum Resulting frequency spectrum
*/
void spectrumChanged(qint64 position, qint64 length, const FrequencySpectrum &spectrum);
+ /**
+ * Buffer containing audio data has changed.
+ * \param position Position of start of buffer in bytes
+ * \param buffer Buffer
+ */
+ void bufferChanged(qint64 position, qint64 length, const QByteArray &buffer);
+
private slots:
void audioNotify();
void audioStateChanged(QAudio::State state);
@@ -246,6 +241,7 @@ private slots:
void spectrumChanged(const FrequencySpectrum &spectrum);
private:
+ void resetAudioDevices();
bool initialize();
bool selectFormat();
void stopRecording();
@@ -275,8 +271,10 @@ private:
bool m_generateTone;
SweptTone m_tone;
- QFile* m_file;
- WavFile m_wavFile;
+ WavFile* m_file;
+ // We need a second file handle via which to read data into m_buffer
+ // for analysis
+ WavFile* m_analysisFile;
QAudioFormat m_format;
@@ -293,12 +291,15 @@ private:
QBuffer m_audioOutputIODevice;
QByteArray m_buffer;
+ qint64 m_bufferPosition;
+ qint64 m_bufferLength;
qint64 m_dataLength;
+ int m_levelBufferLength;
qreal m_rmsLevel;
qreal m_peakLevel;
- int m_spectrumLengthBytes;
+ int m_spectrumBufferLength;
QByteArray m_spectrumBuffer;
SpectrumAnalyser m_spectrumAnalyser;
qint64 m_spectrumPosition;
diff --git a/demos/spectrum/app/mainwidget.cpp b/demos/spectrum/app/mainwidget.cpp
index dd51a91..4b53bbe 100644
--- a/demos/spectrum/app/mainwidget.cpp
+++ b/demos/spectrum/app/mainwidget.cpp
@@ -65,7 +65,7 @@ MainWidget::MainWidget(QWidget *parent)
, m_mode(NoMode)
, m_engine(new Engine(this))
#ifndef DISABLE_WAVEFORM
- , m_waveform(new Waveform(m_engine->buffer(), this))
+ , m_waveform(new Waveform(this))
#endif
, m_progressBar(new ProgressBar(this))
, m_spectrograph(new Spectrograph(this))
@@ -166,19 +166,18 @@ void MainWidget::timerEvent(QTimerEvent *event)
m_infoMessage->setText("");
}
-void MainWidget::positionChanged(qint64 positionUs)
+void MainWidget::audioPositionChanged(qint64 position)
{
#ifndef DISABLE_WAVEFORM
- qint64 positionBytes = audioLength(m_engine->format(), positionUs);
- m_waveform->positionChanged(positionBytes);
+ m_waveform->audioPositionChanged(position);
#else
- Q_UNUSED(positionUs)
+ Q_UNUSED(position)
#endif
}
-void MainWidget::bufferDurationChanged(qint64 durationUs)
+void MainWidget::bufferLengthChanged(qint64 length)
{
- m_progressBar->bufferDurationChanged(durationUs);
+ m_progressBar->bufferLengthChanged(length);
}
@@ -186,33 +185,22 @@ void MainWidget::bufferDurationChanged(qint64 durationUs)
// Private slots
//-----------------------------------------------------------------------------
-void MainWidget::dataDurationChanged(qint64 duration)
-{
-#ifndef DISABLE_WAVEFORM
- const qint64 dataLength = audioLength(m_engine->format(), duration);
- m_waveform->dataLengthChanged(dataLength);
-#else
- Q_UNUSED(duration)
-#endif
-
- updateButtonStates();
-}
-
void MainWidget::showFileDialog()
{
- reset();
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()
{
- reset();
m_settingsDialog->exec();
if (m_settingsDialog->result() == QDialog::Accepted) {
m_engine->setAudioInputDevice(m_settingsDialog->inputDevice());
@@ -223,9 +211,9 @@ void MainWidget::showSettingsDialog()
void MainWidget::showToneGeneratorDialog()
{
- reset();
m_toneGeneratorDialog->exec();
if (m_toneGeneratorDialog->result() == QDialog::Accepted) {
+ reset();
setMode(GenerateToneMode);
const qreal amplitude = m_toneGeneratorDialog->amplitude();
if (m_toneGeneratorDialog->isFrequencySweepEnabled()) {
@@ -236,6 +224,8 @@ void MainWidget::showToneGeneratorDialog()
m_engine->generateTone(tone);
updateButtonStates();
}
+ } else {
+ updateModeMenu();
}
}
@@ -360,13 +350,13 @@ void MainWidget::connectUi()
CHECKED_CONNECT(m_engine, SIGNAL(formatChanged(const QAudioFormat &)),
this, SLOT(formatChanged(const QAudioFormat &)));
- m_progressBar->bufferDurationChanged(m_engine->bufferDuration());
+ m_progressBar->bufferLengthChanged(m_engine->bufferLength());
- CHECKED_CONNECT(m_engine, SIGNAL(bufferDurationChanged(qint64)),
- this, SLOT(bufferDurationChanged(qint64)));
+ CHECKED_CONNECT(m_engine, SIGNAL(bufferLengthChanged(qint64)),
+ this, SLOT(bufferLengthChanged(qint64)));
- CHECKED_CONNECT(m_engine, SIGNAL(dataDurationChanged(qint64)),
- this, SLOT(dataDurationChanged(qint64)));
+ CHECKED_CONNECT(m_engine, SIGNAL(dataLengthChanged(qint64)),
+ this, SLOT(updateButtonStates()));
CHECKED_CONNECT(m_engine, SIGNAL(recordPositionChanged(qint64)),
m_progressBar, SLOT(recordPositionChanged(qint64)));
@@ -375,10 +365,10 @@ void MainWidget::connectUi()
m_progressBar, SLOT(playPositionChanged(qint64)));
CHECKED_CONNECT(m_engine, SIGNAL(recordPositionChanged(qint64)),
- this, SLOT(positionChanged(qint64)));
+ this, SLOT(audioPositionChanged(qint64)));
CHECKED_CONNECT(m_engine, SIGNAL(playPositionChanged(qint64)),
- this, SLOT(positionChanged(qint64)));
+ this, SLOT(audioPositionChanged(qint64)));
CHECKED_CONNECT(m_engine, SIGNAL(levelChanged(qreal, qreal, int)),
m_levelMeter, SLOT(levelChanged(qreal, qreal, int)));
@@ -394,6 +384,11 @@ void MainWidget::connectUi()
CHECKED_CONNECT(m_spectrograph, SIGNAL(infoMessage(QString, int)),
this, SLOT(infoMessage(QString, int)));
+
+#ifndef DISABLE_WAVEFORM
+ CHECKED_CONNECT(m_engine, SIGNAL(bufferChanged(qint64, qint64, const QByteArray &)),
+ m_waveform, SLOT(bufferChanged(qint64, qint64, const QByteArray &)));
+#endif
}
void MainWidget::createMenus()
@@ -425,7 +420,7 @@ void MainWidget::updateButtonStates()
QAudio::IdleState == m_engine->state());
m_pauseButton->setEnabled(pauseEnabled);
- const bool playEnabled = (m_engine->dataDuration() &&
+ const bool playEnabled = (/*m_engine->dataLength() &&*/
(QAudio::AudioOutput != m_engine->mode() ||
(QAudio::ActiveState != m_engine->state() &&
QAudio::IdleState != m_engine->state())));
@@ -445,10 +440,14 @@ void MainWidget::reset()
void MainWidget::setMode(Mode mode)
{
-
m_mode = mode;
- m_loadFileAction->setChecked(LoadFileMode == mode);
- m_generateToneAction->setChecked(GenerateToneMode == mode);
- m_recordAction->setChecked(RecordMode == mode);
+ updateModeMenu();
+}
+
+void MainWidget::updateModeMenu()
+{
+ m_loadFileAction->setChecked(LoadFileMode == m_mode);
+ m_generateToneAction->setChecked(GenerateToneMode == m_mode);
+ m_recordAction->setChecked(RecordMode == m_mode);
}
diff --git a/demos/spectrum/app/mainwidget.h b/demos/spectrum/app/mainwidget.h
index ddab8b7..13131c0 100644
--- a/demos/spectrum/app/mainwidget.h
+++ b/demos/spectrum/app/mainwidget.h
@@ -80,21 +80,21 @@ public slots:
const FrequencySpectrum &spectrum);
void infoMessage(const QString &message, int timeoutMs);
void errorMessage(const QString &heading, const QString &detail);
- void positionChanged(qint64 position);
- void bufferDurationChanged(qint64 duration);
+ void audioPositionChanged(qint64 position);
+ void bufferLengthChanged(qint64 length);
private slots:
void showFileDialog();
void showSettingsDialog();
void showToneGeneratorDialog();
void initializeRecord();
- void dataDurationChanged(qint64 duration);
+ void updateModeMenu();
+ void updateButtonStates();
private:
void createUi();
void createMenus();
void connectUi();
- void updateButtonStates();
void reset();
enum Mode {
@@ -111,7 +111,9 @@ private:
Engine* m_engine;
+#ifndef DISABLE_WAVEFORM
Waveform* m_waveform;
+#endif
ProgressBar* m_progressBar;
Spectrograph* m_spectrograph;
LevelMeter* m_levelMeter;
diff --git a/demos/spectrum/app/progressbar.cpp b/demos/spectrum/app/progressbar.cpp
index 6bfc690..0ac76f1 100644
--- a/demos/spectrum/app/progressbar.cpp
+++ b/demos/spectrum/app/progressbar.cpp
@@ -44,7 +44,7 @@
ProgressBar::ProgressBar(QWidget *parent)
: QWidget(parent)
- , m_bufferDuration(0)
+ , m_bufferLength(0)
, m_recordPosition(0)
, m_playPosition(0)
, m_windowPosition(0)
@@ -64,7 +64,7 @@ ProgressBar::~ProgressBar()
void ProgressBar::reset()
{
- m_bufferDuration = 0;
+ m_bufferLength = 0;
m_recordPosition = 0;
m_playPosition = 0;
m_windowPosition = 0;
@@ -86,26 +86,26 @@ void ProgressBar::paintEvent(QPaintEvent * /*event*/)
painter.fillRect(rect(), Qt::black);
#endif
- if (m_bufferDuration) {
+ if (m_bufferLength) {
QRect bar = rect();
- const qreal play = qreal(m_playPosition) / m_bufferDuration;
+ const qreal play = qreal(m_playPosition) / m_bufferLength;
bar.setLeft(rect().left() + play * rect().width());
- const qreal record = qreal(m_recordPosition) / m_bufferDuration;
+ 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_bufferDuration;
+ const qreal windowLeft = qreal(m_windowPosition) / m_bufferLength;
window.setLeft(rect().left() + windowLeft * rect().width());
- const qreal windowWidth = qreal(m_windowLength) / m_bufferDuration;
+ const qreal windowWidth = qreal(m_windowLength) / m_bufferLength;
window.setWidth(windowWidth * rect().width());
painter.fillRect(window, windowColor);
}
}
-void ProgressBar::bufferDurationChanged(qint64 bufferSize)
+void ProgressBar::bufferLengthChanged(qint64 bufferSize)
{
- m_bufferDuration = bufferSize;
+ m_bufferLength = bufferSize;
m_recordPosition = 0;
m_playPosition = 0;
m_windowPosition = 0;
@@ -116,7 +116,7 @@ void ProgressBar::bufferDurationChanged(qint64 bufferSize)
void ProgressBar::recordPositionChanged(qint64 recordPosition)
{
Q_ASSERT(recordPosition >= 0);
- Q_ASSERT(recordPosition <= m_bufferDuration);
+ Q_ASSERT(recordPosition <= m_bufferLength);
m_recordPosition = recordPosition;
repaint();
}
@@ -124,7 +124,7 @@ void ProgressBar::recordPositionChanged(qint64 recordPosition)
void ProgressBar::playPositionChanged(qint64 playPosition)
{
Q_ASSERT(playPosition >= 0);
- Q_ASSERT(playPosition <= m_bufferDuration);
+ Q_ASSERT(playPosition <= m_bufferLength);
m_playPosition = playPosition;
repaint();
}
@@ -132,8 +132,8 @@ void ProgressBar::playPositionChanged(qint64 playPosition)
void ProgressBar::windowChanged(qint64 position, qint64 length)
{
Q_ASSERT(position >= 0);
- Q_ASSERT(position <= m_bufferDuration);
- Q_ASSERT(position + length <= m_bufferDuration);
+ Q_ASSERT(position <= m_bufferLength);
+ Q_ASSERT(position + length <= m_bufferLength);
m_windowPosition = position;
m_windowLength = length;
repaint();
diff --git a/demos/spectrum/app/progressbar.h b/demos/spectrum/app/progressbar.h
index 8514adb..e715cf5 100644
--- a/demos/spectrum/app/progressbar.h
+++ b/demos/spectrum/app/progressbar.h
@@ -57,13 +57,13 @@ public:
void paintEvent(QPaintEvent *event);
public slots:
- void bufferDurationChanged(qint64 bufferSize);
+ void bufferLengthChanged(qint64 length);
void recordPositionChanged(qint64 recordPosition);
void playPositionChanged(qint64 playPosition);
void windowChanged(qint64 position, qint64 length);
private:
- qint64 m_bufferDuration;
+ qint64 m_bufferLength;
qint64 m_recordPosition;
qint64 m_playPosition;
qint64 m_windowPosition;
diff --git a/demos/spectrum/app/spectrumanalyser.cpp b/demos/spectrum/app/spectrumanalyser.cpp
index 1cc47a6..2fa17b1 100644
--- a/demos/spectrum/app/spectrumanalyser.cpp
+++ b/demos/spectrum/app/spectrumanalyser.cpp
@@ -64,6 +64,8 @@ SpectrumAnalyserThread::SpectrumAnalyserThread(QObject *parent)
#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
diff --git a/demos/spectrum/app/utils.cpp b/demos/spectrum/app/utils.cpp
index 4ead6c2..49a7626 100644
--- a/demos/spectrum/app/utils.cpp
+++ b/demos/spectrum/app/utils.cpp
@@ -49,8 +49,10 @@ qint64 audioDuration(const QAudioFormat &format, qint64 bytes)
qint64 audioLength(const QAudioFormat &format, qint64 microSeconds)
{
- return (format.frequency() * format.channels() * (format.sampleSize() / 8))
+ qint64 result = (format.frequency() * format.channels() * (format.sampleSize() / 8))
* microSeconds / 1000000;
+ result -= result % (format.channelCount() * format.sampleSize());
+ return result;
}
qreal nyquistFrequency(const QAudioFormat &format)
diff --git a/demos/spectrum/app/waveform.cpp b/demos/spectrum/app/waveform.cpp
index 1f7d315..bd854c0 100644
--- a/demos/spectrum/app/waveform.cpp
+++ b/demos/spectrum/app/waveform.cpp
@@ -44,12 +44,18 @@
#include <QResizeEvent>
#include <QDebug>
-
-Waveform::Waveform(const QByteArray &buffer, QWidget *parent)
+//#define PAINT_EVENT_TRACE
+#ifdef PAINT_EVENT_TRACE
+# define WAVEFORM_PAINT_DEBUG qDebug()
+#else
+# define WAVEFORM_PAINT_DEBUG nullDebug()
+#endif
+
+Waveform::Waveform(QWidget *parent)
: QWidget(parent)
- , m_buffer(buffer)
- , m_dataLength(0)
- , m_position(0)
+ , m_bufferPosition(0)
+ , m_bufferLength(0)
+ , m_audioPosition(0)
, m_active(false)
, m_tileLength(0)
, m_tileArrayStart(0)
@@ -72,19 +78,19 @@ void Waveform::paintEvent(QPaintEvent * /*event*/)
painter.fillRect(rect(), Qt::black);
if (m_active) {
- WAVEFORM_DEBUG << "Waveform::paintEvent"
- << "windowPosition" << m_windowPosition
- << "windowLength" << m_windowLength;
+ WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent"
+ << "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_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];
@@ -104,9 +110,9 @@ void Waveform::paintEvent(QPaintEvent * /*event*/)
sourceRect.setLeft(point.pixelOffset);
sourceRect.setRight(sourceRight);
- WAVEFORM_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);
@@ -114,25 +120,25 @@ void Waveform::paintEvent(QPaintEvent * /*event*/)
if (point.index < m_tiles.count()) {
pos = tilePosition(point.index + 1);
- WAVEFORM_DEBUG << "Waveform::paintEvent" << "pos ->" << pos;
+ WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent" << "pos ->" << pos;
} else {
// Reached end of tile array
- WAVEFORM_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_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_DEBUG << "Waveform::paintEvent" << "pos" << pos << "past end of tile array";
+ WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent" << "pos" << pos << "past end of tile array";
break;
}
}
- WAVEFORM_DEBUG << "Waveform::paintEvent" << "final pos" << pos << "final x" << destRight;
+ WAVEFORM_PAINT_DEBUG << "Waveform::paintEvent" << "final pos" << pos << "final x" << destRight;
}
}
@@ -146,7 +152,6 @@ void Waveform::initialize(const QAudioFormat &format, qint64 audioBufferSize, qi
{
WAVEFORM_DEBUG << "Waveform::initialize"
<< "audioBufferSize" << audioBufferSize
- << "m_buffer.size()" << m_buffer.size()
<< "windowDurationUs" << windowDurationUs;
reset();
@@ -186,8 +191,9 @@ void Waveform::reset()
{
WAVEFORM_DEBUG << "Waveform::reset";
- m_dataLength = 0;
- m_position = 0;
+ m_bufferPosition = 0;
+ m_buffer = QByteArray();
+ m_audioPosition = 0;
m_format = QAudioFormat();
m_active = false;
deletePixmaps();
@@ -198,30 +204,31 @@ void Waveform::reset()
m_windowLength = 0;
}
-void Waveform::dataLengthChanged(qint64 length)
+void Waveform::bufferChanged(qint64 position, qint64 length, const QByteArray &buffer)
{
- WAVEFORM_DEBUG << "Waveform::dataLengthChanged" << length;
- const qint64 oldLength = m_dataLength;
- m_dataLength = length;
-
- if (m_active) {
- if (m_dataLength < oldLength)
- positionChanged(m_dataLength);
- else
- paintTiles();
- }
+ WAVEFORM_DEBUG << "Waveform::bufferChanged"
+ << "audioPosition" << m_audioPosition
+ << "bufferPosition" << position
+ << "bufferLength" << length;
+ m_bufferPosition = position;
+ m_bufferLength = length;
+ m_buffer = buffer;
+ paintTiles();
}
-void Waveform::positionChanged(qint64 position)
+void Waveform::audioPositionChanged(qint64 position)
{
- WAVEFORM_DEBUG << "Waveform::positionChanged" << position;
-
- if (position + m_windowLength > m_dataLength)
- position = m_dataLength - m_windowLength;
-
- m_position = position;
-
- setWindowPosition(position);
+ WAVEFORM_DEBUG << "Waveform::audioPositionChanged"
+ << "audioPosition" << position
+ << "bufferPosition" << m_bufferPosition
+ << "bufferLength" << m_bufferLength;
+
+ if (position >= m_bufferPosition) {
+ if (position + m_windowLength > m_bufferPosition + m_bufferLength)
+ position = qMax(qint64(0), m_bufferPosition + m_bufferLength - m_windowLength);
+ m_audioPosition = position;
+ setWindowPosition(position);
+ }
}
void Waveform::deletePixmaps()
@@ -255,8 +262,6 @@ void Waveform::createPixmaps(const QSize &widgetSize)
m_tiles[i].pixmap = m_pixmaps[i];
m_tiles[i].painted = false;
}
-
- paintTiles();
}
void Waveform::setWindowPosition(qint64 position)
@@ -327,8 +332,9 @@ bool Waveform::paintTiles()
for (int i=0; i<m_tiles.count(); ++i) {
const Tile &tile = m_tiles[i];
if (!tile.painted) {
- const qint64 tileEnd = m_tileArrayStart + (i + 1) * m_tileLength;
- if (m_dataLength >= tileEnd) {
+ const qint64 tileStart = m_tileArrayStart + i * m_tileLength;
+ const qint64 tileEnd = tileStart + m_tileLength;
+ if (m_bufferPosition <= tileStart && m_bufferPosition + m_bufferLength >= tileEnd) {
paintTile(i);
updateRequired = true;
}
@@ -343,16 +349,23 @@ bool Waveform::paintTiles()
void Waveform::paintTile(int index)
{
- WAVEFORM_DEBUG << "Waveform::paintTile" << "index" << index;
-
const qint64 tileStart = m_tileArrayStart + index * m_tileLength;
- Q_ASSERT(m_dataLength >= tileStart + m_tileLength);
+
+ WAVEFORM_DEBUG << "Waveform::paintTile"
+ << "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);
Tile &tile = m_tiles[index];
Q_ASSERT(!tile.painted);
const qint16* base = reinterpret_cast<const qint16*>(m_buffer.constData());
- const qint16* buffer = base + (tileStart / 2);
+ const qint16* buffer = base + ((tileStart - m_bufferPosition) / 2);
const int numSamples = m_tileLength / (2 * m_format.channels());
QPainter painter(tile.pixmap);
@@ -376,6 +389,11 @@ void Waveform::paintTile(int index)
for (int i=0; i<numSamples; ++i) {
const qint16* ptr = buffer + i * m_format.channels();
+
+ const int offset = reinterpret_cast<const char*>(ptr) - m_buffer.constData();
+ Q_ASSERT(offset >= 0);
+ Q_ASSERT(offset < m_bufferLength);
+
const qint16 pcmValue = *ptr;
const qreal realValue = pcmToReal(pcmValue);
diff --git a/demos/spectrum/app/waveform.h b/demos/spectrum/app/waveform.h
index 57c9eec..1c54c86 100644
--- a/demos/spectrum/app/waveform.h
+++ b/demos/spectrum/app/waveform.h
@@ -60,7 +60,7 @@ QT_FORWARD_DECLARE_CLASS(QByteArray)
class Waveform : public QWidget {
Q_OBJECT
public:
- Waveform(const QByteArray &buffer, QWidget *parent = 0);
+ Waveform(QWidget *parent = 0);
~Waveform();
// QWidget
@@ -73,8 +73,8 @@ public:
void setAutoUpdatePosition(bool enabled);
public slots:
- void dataLengthChanged(qint64 length);
- void positionChanged(qint64 position);
+ void bufferChanged(qint64 position, qint64 length, const QByteArray &buffer);
+ void audioPositionChanged(qint64 position);
private:
static const int NullIndex = -1;
@@ -167,9 +167,11 @@ private:
void resetTiles(qint64 newStartPos);
private:
- const QByteArray& m_buffer;
- qint64 m_dataLength;
- qint64 m_position;
+ qint64 m_bufferPosition;
+ qint64 m_bufferLength;
+ QByteArray m_buffer;
+
+ qint64 m_audioPosition;
QAudioFormat m_format;
bool m_active;
diff --git a/demos/spectrum/app/wavfile.cpp b/demos/spectrum/app/wavfile.cpp
index 74d5918..44c3ac5 100644
--- a/demos/spectrum/app/wavfile.cpp
+++ b/demos/spectrum/app/wavfile.cpp
@@ -78,179 +78,74 @@ struct CombinedHeader
WAVEHeader wave;
};
-
-
-WavFile::WavFile(const QAudioFormat &format, qint64 dataLength)
- : m_format(format)
- , m_dataLength(dataLength)
- , m_dataPosition(0)
-{
-}
-
-bool WavFile::readHeader(QIODevice &device)
-{
- if (!device.isSequential()) {
- if (!device.seek(0))
- return false;
- // XXX: else, assume that current position is the start of the header
- }
-
- CombinedHeader header;
- if (device.read(reinterpret_cast<char *>(&header), sizeof(CombinedHeader)) != sizeof(CombinedHeader))
- return false;
-
- 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 (device.peek((char*)&extraFormatBytes, sizeof(quint16)) != sizeof(quint16))
- return false;
- const qint64 throwAwayBytes = sizeof(quint16) + qFromLittleEndian<quint16>(extraFormatBytes);
- if (device.read(throwAwayBytes).size() != throwAwayBytes)
- return false;
- }
-
- if (device.read((char*)&dataHeader, sizeof(DATAHeader)) != sizeof(DATAHeader))
- return false;
-
- // Establish format
- if (memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0)
- m_format.setByteOrder(QAudioFormat::LittleEndian);
- else
- m_format.setByteOrder(QAudioFormat::BigEndian);
-
- int bps = qFromLittleEndian<quint16>(header.wave.bitsPerSample);
- m_format.setChannels(qFromLittleEndian<quint16>(header.wave.numChannels));
- m_format.setCodec("audio/pcm");
- m_format.setFrequency(qFromLittleEndian<quint32>(header.wave.sampleRate));
- m_format.setSampleSize(qFromLittleEndian<quint16>(header.wave.bitsPerSample));
- m_format.setSampleType(bps == 8 ? QAudioFormat::UnSignedInt : QAudioFormat::SignedInt);
-
- m_dataLength = qFromLittleEndian<quint32>(dataHeader.descriptor.size);
- m_dataPosition = 0;
- }
-
- return true;
-}
-
-bool WavFile::writeHeader(QIODevice &device)
+WavFile::WavFile(QObject *parent)
+ : QFile(parent)
+ , m_headerLength(0)
{
- CombinedHeader header;
- DATAHeader dataHeader;
-
- memset(&header, 0, sizeof(CombinedHeader));
-
- // RIFF header
- if (m_format.byteOrder() == QAudioFormat::LittleEndian)
- strncpy(&header.riff.descriptor.id[0], "RIFF", 4);
- else
- strncpy(&header.riff.descriptor.id[0], "RIFX", 4);
- qToLittleEndian<quint32>(quint32(m_dataLength + sizeof(CombinedHeader) + sizeof(DATAHeader) - sizeof(chunk)),
- reinterpret_cast<unsigned char*>(&header.riff.descriptor.size));
- strncpy(&header.riff.type[0], "WAVE", 4);
-
- // WAVE header
- strncpy(&header.wave.descriptor.id[0], "fmt ", 4);
- qToLittleEndian<quint32>(quint32(16),
- reinterpret_cast<unsigned char*>(&header.wave.descriptor.size));
- qToLittleEndian<quint16>(quint16(1),
- reinterpret_cast<unsigned char*>(&header.wave.audioFormat));
- qToLittleEndian<quint16>(quint16(m_format.channels()),
- reinterpret_cast<unsigned char*>(&header.wave.numChannels));
- qToLittleEndian<quint32>(quint32(m_format.frequency()),
- reinterpret_cast<unsigned char*>(&header.wave.sampleRate));
- qToLittleEndian<quint32>(quint32(m_format.frequency() * m_format.channels() * m_format.sampleSize() / 8),
- reinterpret_cast<unsigned char*>(&header.wave.byteRate));
- qToLittleEndian<quint16>(quint16(m_format.channels() * m_format.sampleSize() / 8),
- reinterpret_cast<unsigned char*>(&header.wave.blockAlign));
- qToLittleEndian<quint16>(quint16(m_format.sampleSize()),
- reinterpret_cast<unsigned char*>(&header.wave.bitsPerSample));
-
- // DATA header
- strncpy(dataHeader.descriptor.id, "data", 4);
- qToLittleEndian<quint32>(quint32(m_dataLength),
- reinterpret_cast<unsigned char*>(&dataHeader.descriptor.size));
- return device.write(reinterpret_cast<const char *>(&header), sizeof(CombinedHeader)) == sizeof(CombinedHeader)
- && device.write(reinterpret_cast<const char*>(&dataHeader), sizeof(DATAHeader)) == sizeof(DATAHeader);
}
-const QAudioFormat& WavFile::format() const
+bool WavFile::open(const QString &fileName)
{
- return m_format;
+ close();
+ setFileName(fileName);
+ return QFile::open(QIODevice::ReadOnly) && readHeader();
}
-qint64 WavFile::dataLength() const
+const QAudioFormat &WavFile::fileFormat() const
{
- return m_dataLength;
+ return m_fileFormat;
}
-qint64 WavFile::headerLength()
+qint64 WavFile::headerLength() const
{
- return sizeof(CombinedHeader);
-}
-
-bool WavFile::writeDataLength(QIODevice &device, qint64 dataLength)
-{
- bool result = false;
- if (!device.isSequential()) {
- device.seek(40);
- unsigned char dataLengthLE[4];
- qToLittleEndian<quint32>(quint32(dataLength), dataLengthLE);
- result = (device.write(reinterpret_cast<const char *>(dataLengthLE), 4) == 4);
- }
- return result;
+return m_headerLength;
}
-qint64 WavFile::readData(QIODevice &device, QByteArray &buffer,
- QAudioFormat outputFormat)
+bool WavFile::readHeader()
{
- // Sanity checks
- if (!outputFormat.isValid())
- outputFormat = m_format;
-
- if (!isPCMS16LE(outputFormat) || !isPCMS16LE(m_format))
- return 0;
-
- if (m_dataPosition == m_dataLength)
- return 0;
-
- // Process
- qint64 result = 0;
-
- const int frameSize = 2 * m_format.channels(); // 16 bit samples
- QVector<char> inputSample(frameSize);
-
- qint16 *output = reinterpret_cast<qint16*>(buffer.data());
+ 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;
+ }
- while (result < buffer.size()) {
- if (m_dataPosition == m_dataLength)
- break;
+ if (read((char*)&dataHeader, sizeof(DATAHeader)) != sizeof(DATAHeader))
+ return false;
- // XXX only working with particular alignments
- if (device.read(inputSample.data(), inputSample.count())) {
- int inputIdx = 0;
- for (int outputIdx = 0; outputIdx < outputFormat.channels(); ++outputIdx) {
- const qint16* input = reinterpret_cast<const qint16*>(inputSample.data() + 2 * inputIdx);
- *output++ = qFromLittleEndian<qint16>(*input);
- result += 2;
- if (inputIdx < m_format.channels())
- ++inputIdx;
- }
- m_dataPosition += frameSize;
+ // 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.setChannels(qFromLittleEndian<quint16>(header.wave.numChannels));
+ m_fileFormat.setCodec("audio/pcm");
+ m_fileFormat.setFrequency(qFromLittleEndian<quint32>(header.wave.sampleRate));
+ m_fileFormat.setSampleSize(qFromLittleEndian<quint16>(header.wave.bitsPerSample));
+ m_fileFormat.setSampleType(bps == 8 ? QAudioFormat::UnSignedInt : QAudioFormat::SignedInt);
} else {
- break;
+ result = false;
}
}
-
+ m_headerLength = pos();
return result;
}
-
diff --git a/demos/spectrum/app/wavfile.h b/demos/spectrum/app/wavfile.h
index fc14b08..935e935 100644
--- a/demos/spectrum/app/wavfile.h
+++ b/demos/spectrum/app/wavfile.h
@@ -46,38 +46,22 @@
#include <QtCore/qfile.h>
#include <QtMultimedia/qaudioformat.h>
-/**
- * Helper class for reading WAV files
- *
- * See https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
- */
-class WavFile
+class WavFile : public QFile
{
public:
- WavFile(const QAudioFormat &format = QAudioFormat(),
- qint64 dataLength = 0);
+ WavFile(QObject *parent = 0);
- // Reads WAV header and seeks to start of data
- bool readHeader(QIODevice &device);
+ bool open(const QString &fileName);
+ const QAudioFormat &fileFormat() const;
+ qint64 headerLength() const;
- // Writes WAV header
- bool writeHeader(QIODevice &device);
-
- // Read PCM data
- qint64 readData(QIODevice &device, QByteArray &buffer,
- QAudioFormat outputFormat = QAudioFormat());
-
- const QAudioFormat& format() const;
- qint64 dataLength() const;
-
- static qint64 headerLength();
-
- static bool writeDataLength(QIODevice &device, qint64 dataLength);
+private:
+ bool readHeader();
private:
- QAudioFormat m_format;
- qint64 m_dataLength;
- qint64 m_dataPosition;
+ QAudioFormat m_fileFormat;
+ qint64 m_headerLength;
+
};
#endif