diff options
36 files changed, 792 insertions, 522 deletions
diff --git a/demos/declarative/samegame/SamegameCore/Button.qml b/demos/declarative/samegame/SamegameCore/Button.qml index 7fb7b65..140b196 100644 --- a/demos/declarative/samegame/SamegameCore/Button.qml +++ b/demos/declarative/samegame/SamegameCore/Button.qml @@ -48,7 +48,7 @@ Rectangle { signal clicked - width: buttonLabel.width + 20; height: buttonLabel.height + 6 + width: buttonLabel.width + 20; height: buttonLabel.height + 20 smooth: true border { width: 1; color: Qt.darker(activePalette.button) } radius: 8 @@ -70,6 +70,6 @@ Rectangle { MouseArea { id: mouseArea; anchors.fill: parent; onClicked: container.clicked() } Text { - id: buttonLabel; text: container.text; anchors.centerIn: container; color: activePalette.buttonText + id: buttonLabel; text: container.text; anchors.centerIn: container; color: activePalette.buttonText; font.pixelSize: 24 } } diff --git a/demos/declarative/samegame/samegame.qml b/demos/declarative/samegame/samegame.qml index f66c40e..b66c5a6 100644 --- a/demos/declarative/samegame/samegame.qml +++ b/demos/declarative/samegame/samegame.qml @@ -133,7 +133,7 @@ Rectangle { Rectangle { id: toolBar - width: parent.width; height: 32 + width: parent.width; height: 58 color: activePalette.window anchors.bottom: screen.bottom @@ -156,6 +156,7 @@ Rectangle { anchors { right: parent.right; rightMargin: 3; verticalCenter: parent.verticalCenter } text: "Score: " + gameCanvas.score font.bold: true + font.pixelSize: 24 color: activePalette.windowText } } 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 diff --git a/src/declarative/debugger/qdeclarativedebugservice.cpp b/src/declarative/debugger/qdeclarativedebugservice.cpp index b98cd5d..c39da3d 100644 --- a/src/declarative/debugger/qdeclarativedebugservice.cpp +++ b/src/declarative/debugger/qdeclarativedebugservice.cpp @@ -204,13 +204,13 @@ QDeclarativeDebugServer *QDeclarativeDebugServer::instance() bool ok = false; // format: qmljsdebugger=port:3768[,block] - if (!appD->qmljsDebugArguments.isEmpty()) { + if (!appD->qmljsDebugArgumentsString().isEmpty()) { - if (appD->qmljsDebugArguments.indexOf(QLatin1String("port:")) == 0) { - int separatorIndex = appD->qmljsDebugArguments.indexOf(QLatin1Char(',')); - port = appD->qmljsDebugArguments.mid(5, separatorIndex - 5).toInt(&ok); + if (appD->qmljsDebugArgumentsString().indexOf(QLatin1String("port:")) == 0) { + int separatorIndex = appD->qmljsDebugArgumentsString().indexOf(QLatin1Char(',')); + port = appD->qmljsDebugArgumentsString().mid(5, separatorIndex - 5).toInt(&ok); } - block = appD->qmljsDebugArguments.contains(QLatin1String("block")); + block = appD->qmljsDebugArgumentsString().contains(QLatin1String("block")); if (ok) { server = new QDeclarativeDebugServer(port); @@ -221,7 +221,7 @@ QDeclarativeDebugServer *QDeclarativeDebugServer::instance() } else { qWarning(QString::fromAscii("QDeclarativeDebugServer: Ignoring \"-qmljsdebugger=%1\". " "Format is -qmljsdebugger=port:<port>[,block]").arg( - appD->qmljsDebugArguments).toAscii().constData()); + appD->qmljsDebugArgumentsString()).toAscii().constData()); } } #endif diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 3323fbc..6c4004e 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -497,7 +497,7 @@ bool QApplicationPrivate::fade_tooltip = false; bool QApplicationPrivate::animate_toolbox = false; bool QApplicationPrivate::widgetCount = false; bool QApplicationPrivate::load_testability = false; -QString QApplicationPrivate::qmljsDebugArguments; +QString QApplicationPrivate::qmljs_debug_arguments; #ifdef QT_KEYPAD_NAVIGATION # ifdef Q_OS_SYMBIAN Qt::NavigationMode QApplicationPrivate::navigationMode = Qt::NavigationModeKeypadDirectional; @@ -570,7 +570,7 @@ void QApplicationPrivate::process_cmdline() if (arg == "-qdevel" || arg == "-qdebug") { // obsolete argument } else if (arg.indexOf("-qmljsdebugger=", 0) != -1) { - qmljsDebugArguments = QString::fromLocal8Bit(arg.right(arg.length() - 15)); + qmljs_debug_arguments = QString::fromLocal8Bit(arg.right(arg.length() - 15)); } else if (arg.indexOf("-style=", 0) != -1) { s = QString::fromLocal8Bit(arg.right(arg.length() - 7).toLower()); } else if (arg == "-style" && i < argc-1) { @@ -6086,6 +6086,11 @@ QPixmap QApplicationPrivate::getPixmapCursor(Qt::CursorShape cshape) return QPixmap(); } +QString QApplicationPrivate::qmljsDebugArgumentsString() +{ + return qmljs_debug_arguments; +} + QT_END_NAMESPACE #include "moc_qapplication.cpp" diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index 9c5095d..a0e1452 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -446,7 +446,8 @@ public: static bool animate_toolbox; static bool widgetCount; // Coupled with -widgetcount switch static bool load_testability; // Coupled with -testability switch - static QString qmljsDebugArguments; // a string containing arguments for js/qml debugging. + static QString qmljs_debug_arguments; // a string containing arguments for js/qml debugging. + static QString qmljsDebugArgumentsString(); // access string from other libraries #ifdef Q_WS_MAC static bool native_modal_dialog_active; diff --git a/src/gui/kernel/qapplication_s60.cpp b/src/gui/kernel/qapplication_s60.cpp index 5ff2fd4..ae7070e 100644 --- a/src/gui/kernel/qapplication_s60.cpp +++ b/src/gui/kernel/qapplication_s60.cpp @@ -2055,6 +2055,17 @@ int QApplicationPrivate::symbianProcessWsEvent(const QSymbianEvent *symbianEvent } #endif break; +#ifdef Q_SYMBIAN_SUPPORTS_SURFACES + case EEventUser: + { + // GOOM is looking for candidates to kill so indicate that we are + // capable of cleaning up by handling this event + TInt32 *data = reinterpret_cast<TInt32 *>(event->EventData()); + if (data[0] == EApaSystemEventShutdown && data[1] == KGoomMemoryLowEvent) + return 1; + } + break; +#endif default: break; } diff --git a/src/gui/kernel/qclipboard_s60.cpp b/src/gui/kernel/qclipboard_s60.cpp index c9b1d23..73280b2 100644 --- a/src/gui/kernel/qclipboard_s60.cpp +++ b/src/gui/kernel/qclipboard_s60.cpp @@ -257,18 +257,14 @@ const QMimeData* QClipboard::mimeData(Mode mode) const } } CleanupStack::PopAndDestroy(cb); - if (dataExists) { - return d->source(); - } - else { - return 0; - } - }); if (err != KErrNone){ qDebug()<< "clipboard is empty/err: " << err; } + if (dataExists) { + return d->source(); + } } return 0; } diff --git a/src/gui/styles/qs60style.cpp b/src/gui/styles/qs60style.cpp index ca3a9d0..d39a2ba 100644 --- a/src/gui/styles/qs60style.cpp +++ b/src/gui/styles/qs60style.cpp @@ -3424,8 +3424,11 @@ bool QS60Style::eventFilter(QObject *object, QEvent *event) qobject_cast<QCheckBox *>(w)) d->m_pressedWidget = w; - if ( d->m_pressedWidget) + if (d->m_pressedWidget) d->m_pressedWidget->update(); +#ifdef Q_WS_S60 + d->touchFeedback(event, w); +#endif } break; } diff --git a/src/gui/styles/qs60style_feedbackinterface_p.h b/src/gui/styles/qs60style_feedbackinterface_p.h new file mode 100644 index 0000000..81fcdc3 --- /dev/null +++ b/src/gui/styles/qs60style_feedbackinterface_p.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QObject> + +class TactileFeedbackInterface : public QObject +{ + public: + virtual void touchFeedback(QEvent *event, const QWidget *widget) = 0; +}; + +Q_DECLARE_INTERFACE(TactileFeedbackInterface, "com.trolltech.Qt.TactileFeedbackInterface/1.0") diff --git a/src/gui/styles/qs60style_p.h b/src/gui/styles/qs60style_p.h index b3f4160..b46f75e 100644 --- a/src/gui/styles/qs60style_p.h +++ b/src/gui/styles/qs60style_p.h @@ -387,6 +387,7 @@ private: //data members class QFocusFrame; class QProgressBar; class QS60StyleAnimation; +class TactileFeedbackInterface; // Private class #ifdef Q_OS_SYMBIAN @@ -572,6 +573,8 @@ public: void stopAnimation(QS60StyleEnums::SkinParts animation); static QS60StyleAnimation* animationDefinition(QS60StyleEnums::SkinParts part); static void removeAnimations(); + //No support for tactile feedback in emulated style + void touchFeedback(QEvent *event, const QWidget *widget); #endif @@ -626,6 +629,7 @@ private: #ifdef Q_WS_S60 //list of progress bars having animation running QList<QProgressBar *> m_bars; + TactileFeedbackInterface *m_feedbackPlugin; #endif }; diff --git a/src/gui/styles/qs60style_s60.cpp b/src/gui/styles/qs60style_s60.cpp index 5dda42e..a1ea308 100644 --- a/src/gui/styles/qs60style_s60.cpp +++ b/src/gui/styles/qs60style_s60.cpp @@ -49,6 +49,10 @@ #include "private/qcore_symbian_p.h" #include "qapplication.h" +#include "qpluginloader.h" +#include "qlibraryinfo.h" +#include "private/qs60style_feedbackinterface_p.h" + #include <w32std.h> #include <AknsConstants.h> #include <aknconsts.h> @@ -1143,13 +1147,25 @@ void QS60StylePrivate::setActiveLayout() Q_GLOBAL_STATIC(QList<QS60StyleAnimation *>, m_animations) -QS60StylePrivate::QS60StylePrivate() +QS60StylePrivate::QS60StylePrivate() : m_feedbackPlugin(0) { //Animation defaults need to be created when style is instantiated QS60StyleAnimation* progressBarAnimation = new QS60StyleAnimation(QS60StyleEnums::SP_QgnGrafBarWaitAnim, 7, 100); m_animations()->append(progressBarAnimation); // No need to set active layout, if dynamic metrics API is available setActiveLayout(); + + //Tactile feedback plugin is only available for touch devices. + if (isTouchSupported()) { + QString pluginsPath = QLibraryInfo::location(QLibraryInfo::PluginsPath); + pluginsPath += QLatin1String("/feedback/qtactilefeedback.dll"); + + // Create plugin loader + QPluginLoader pluginLoader(pluginsPath); + // Load plugin and store pointer to the plugin implementation + if (pluginLoader.load()) + m_feedbackPlugin = qobject_cast<TactileFeedbackInterface*>(pluginLoader.instance()); + } } void QS60StylePrivate::removeAnimations() @@ -1439,6 +1455,12 @@ void QS60StylePrivate::stopAnimation(QS60StyleEnums::SkinParts animationPart) } } +void QS60StylePrivate::touchFeedback(QEvent *event, const QWidget *widget) +{ + if (m_feedbackPlugin) + m_feedbackPlugin->touchFeedback(event, widget); +} + QVariant QS60StyleModeSpecifics::themeDefinition( QS60StyleEnums::ThemeDefinitions definition, QS60StyleEnums::SkinParts part) { diff --git a/src/openvg/qpaintengine_vg.cpp b/src/openvg/qpaintengine_vg.cpp index ce9d11a..9df32d9 100644 --- a/src/openvg/qpaintengine_vg.cpp +++ b/src/openvg/qpaintengine_vg.cpp @@ -55,6 +55,8 @@ #include <QtGui/private/qfontengine_p.h> #include <QtGui/private/qpainterpath_p.h> #include <QtGui/private/qstatictext_p.h> +#include <QtGui/QApplication> +#include <QtGui/QDesktopWidget> #include <QtCore/qmath.h> #include <QDebug> #include <QSet> @@ -3060,6 +3062,21 @@ void qt_vg_drawVGImageStencil vgDrawImage(vgImg); } +bool QVGPaintEngine::canVgWritePixels(const QImage &image) const +{ + Q_D(const QVGPaintEngine); + // vgWritePixels ignores masking, blending and xforms so we can only use it if + // ALL of the following conditions are true: + // - It is a simple translate, or a scale of -1 on the y-axis (inverted) + // - The opacity is totally opaque + // - The composition mode is "source" OR "source over" provided the image is opaque + return ( d->imageTransform.type() <= QTransform::TxScale + && d->imageTransform.m11() == 1.0 && qAbs(d->imageTransform.m22()) == 1.0) + && d->opacity == 1.0f + && (d->blendMode == VG_BLEND_SRC || (d->blendMode == VG_BLEND_SRC_OVER && + !image.hasAlphaChannel())); +} + void QVGPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr) { QPixmapData *pd = pm.pixmapData(); @@ -3074,9 +3091,18 @@ void QVGPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF drawVGImage(d, r, vgpd->toVGImage(), vgpd->size(), sr); else drawVGImage(d, r, vgpd->toVGImage(d->opacity), vgpd->size(), sr); - } else { - drawImage(r, *(pd->buffer()), sr, Qt::AutoColor); + + if(!vgpd->failedToAlloc) + return; + + // try to reallocate next time if reasonable small pixmap + QSize screenSize = QApplication::desktop()->screenGeometry().size(); + if (pm.size().width() <= screenSize.width() + && pm.size().height() <= screenSize.height()) + vgpd->failedToAlloc = false; } + + drawImage(r, *(pd->buffer()), sr, Qt::AutoColor); } void QVGPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pm) @@ -3093,9 +3119,18 @@ void QVGPaintEngine::drawPixmap(const QPointF &pos, const QPixmap &pm) drawVGImage(d, pos, vgpd->toVGImage()); else drawVGImage(d, pos, vgpd->toVGImage(d->opacity)); - } else { - drawImage(pos, *(pd->buffer())); + + if (!vgpd->failedToAlloc) + return; + + // try to reallocate next time if reasonable small pixmap + QSize screenSize = QApplication::desktop()->screenGeometry().size(); + if (pm.size().width() <= screenSize.width() + && pm.size().height() <= screenSize.height()) + vgpd->failedToAlloc = false; } + + drawImage(pos, *(pd->buffer())); } void QVGPaintEngine::drawImage @@ -3116,9 +3151,31 @@ void QVGPaintEngine::drawImage QRectF(QPointF(0, 0), sr.size())); } } else { - // Monochrome images need to use the vgChildImage() path. - vgImg = toVGImage(image, flags); - drawVGImage(d, r, vgImg, image.size(), sr); + if (canVgWritePixels(image) && (r.size() == sr.size()) && !flags) { + // Optimization for straight blits, no blending + int x = sr.x(); + int y = sr.y(); + int bpp = image.depth() >> 3; // bytes + int offset = 0; + int bpl = image.bytesPerLine(); + if (d->imageTransform.m22() < 0) { + // inverted + offset = ((y + sr.height()) * bpl) - ((image.width() - x) * bpp); + bpl = -bpl; + } else { + offset = (y * bpl) + (x * bpp); + } + const uchar *bits = image.constBits() + offset; + + QPointF mapped = d->imageTransform.map(r.topLeft()); + vgWritePixels(bits, bpl, qt_vg_image_to_vg_format(image.format()), + mapped.x(), mapped.y() - sr.height(), r.width(), r.height()); + return; + } else { + // Monochrome images need to use the vgChildImage() path. + vgImg = toVGImage(image, flags); + drawVGImage(d, r, vgImg, image.size(), sr); + } } vgDestroyImage(vgImg); } @@ -3127,10 +3184,21 @@ void QVGPaintEngine::drawImage(const QPointF &pos, const QImage &image) { Q_D(QVGPaintEngine); VGImage vgImg; - if (d->simpleTransform || d->opacity == 1.0f) + if (canVgWritePixels(image)) { + // Optimization for straight blits, no blending + bool inverted = (d->imageTransform.m22() < 0); + const uchar *bits = inverted ? image.constBits() + image.byteCount() : image.constBits(); + int bpl = inverted ? -image.bytesPerLine() : image.bytesPerLine(); + + QPointF mapped = d->imageTransform.map(pos); + vgWritePixels(bits, bpl, qt_vg_image_to_vg_format(image.format()), + mapped.x(), mapped.y() - image.height(), image.width(), image.height()); + return; + } else if (d->simpleTransform || d->opacity == 1.0f) { vgImg = toVGImage(image); - else + } else { vgImg = toVGImageWithOpacity(image, d->opacity); + } drawVGImage(d, pos, vgImg); vgDestroyImage(vgImg); } diff --git a/src/openvg/qpaintengine_vg_p.h b/src/openvg/qpaintengine_vg_p.h index 75cf053..dc98137 100644 --- a/src/openvg/qpaintengine_vg_p.h +++ b/src/openvg/qpaintengine_vg_p.h @@ -170,6 +170,7 @@ private: bool isDefaultClipRegion(const QRegion& region); bool isDefaultClipRect(const QRect& rect); bool clearRect(const QRectF &rect, const QColor &color); + bool canVgWritePixels(const QImage &image) const; }; QT_END_NAMESPACE diff --git a/src/openvg/qpixmapdata_vg.cpp b/src/openvg/qpixmapdata_vg.cpp index e8ec333..509882b 100644 --- a/src/openvg/qpixmapdata_vg.cpp +++ b/src/openvg/qpixmapdata_vg.cpp @@ -65,6 +65,7 @@ QVGPixmapData::QVGPixmapData(PixelType type) recreate = true; inImagePool = false; inLRU = false; + failedToAlloc = false; #if !defined(QT_NO_EGL) context = 0; qt_vg_register_pixmap(this); @@ -155,6 +156,9 @@ void QVGPixmapData::resize(int wid, int ht) void QVGPixmapData::fromImage (const QImage &image, Qt::ImageConversionFlags flags) { + if(image.isNull()) + return; + QImage img = image; createPixmapForImage(img, flags, false); } @@ -203,10 +207,19 @@ void QVGPixmapData::createPixmapForImage(QImage &image, Qt::ImageConversionFlags else resize(image.width(), image.height()); - if (inPlace && image.data_ptr()->convertInPlace(sourceFormat(), flags)) + QImage::Format format = sourceFormat(); + int d = image.depth(); + if (d == 1 || d == 16 || d == 24 || (d == 32 && !image.hasAlphaChannel())) + format = QImage::Format_RGB32; + else if (!(flags & Qt::NoOpaqueDetection) && const_cast<QImage &>(image).data_ptr()->checkForAlphaPixels()) + format = sourceFormat(); + else + format = QImage::Format_RGB32; + + if (inPlace && image.data_ptr()->convertInPlace(format, flags)) source = image; else - source = image.convertToFormat(sourceFormat()); + source = image.convertToFormat(format); recreate = true; } @@ -278,7 +291,7 @@ QPaintEngine* QVGPixmapData::paintEngine() const VGImage QVGPixmapData::toVGImage() { - if (!isValid()) + if (!isValid() || failedToAlloc) return VG_INVALID_HANDLE; #if !defined(QT_NO_EGL) @@ -294,11 +307,13 @@ VGImage QVGPixmapData::toVGImage() if (vgImage == VG_INVALID_HANDLE) { vgImage = QVGImagePool::instance()->createImageForPixmap - (VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER, this); + (qt_vg_image_to_vg_format(source.format()), w, h, VG_IMAGE_QUALITY_FASTER, this); // Bail out if we run out of GPU memory - try again next time. - if (vgImage == VG_INVALID_HANDLE) + if (vgImage == VG_INVALID_HANDLE) { + failedToAlloc = true; return VG_INVALID_HANDLE; + } inImagePool = true; } else if (inImagePool) { @@ -309,7 +324,7 @@ VGImage QVGPixmapData::toVGImage() vgImageSubData (vgImage, source.constBits(), source.bytesPerLine(), - VG_sARGB_8888_PRE, 0, 0, w, h); + qt_vg_image_to_vg_format(source.format()), 0, 0, w, h); } recreate = false; diff --git a/src/openvg/qpixmapdata_vg_p.h b/src/openvg/qpixmapdata_vg_p.h index 114d545..7ffdc85 100644 --- a/src/openvg/qpixmapdata_vg_p.h +++ b/src/openvg/qpixmapdata_vg_p.h @@ -143,7 +143,9 @@ private: QVGPixmapData *nextLRU; QVGPixmapData *prevLRU; bool inLRU; + bool failedToAlloc; friend class QVGImagePool; + friend class QVGPaintEngine; #if !defined(QT_NO_EGL) QVGPixmapData *next; diff --git a/src/openvg/qvg_symbian.cpp b/src/openvg/qvg_symbian.cpp index 41b35fc..c608c81 100644 --- a/src/openvg/qvg_symbian.cpp +++ b/src/openvg/qvg_symbian.cpp @@ -164,7 +164,6 @@ void QVGPixmapData::fromNativeType(void* pixmap, NativeType type) CFbsBitmap *bitmap = reinterpret_cast<CFbsBitmap*>(pixmap); bool deleteSourceBitmap = false; - #ifdef Q_SYMBIAN_HAS_EXTENDED_BITMAP_TYPE // Rasterize extended bitmaps diff --git a/src/plugins/s60/feedback/feedback.pro b/src/plugins/s60/feedback/feedback.pro new file mode 100644 index 0000000..32ddf6f --- /dev/null +++ b/src/plugins/s60/feedback/feedback.pro @@ -0,0 +1,16 @@ +include(../../qpluginbase.pri) + +TARGET = qtactilefeedback$${QT_LIBINFIX} + +INCLUDEPATH += $$APP_LAYER_SYSTEMINCLUDE + +contains(S60_VERSION, 5.0)|contains(S60_VERSION, symbian3) { + HEADERS += qtactileFeedback.h + SOURCES += qtactileFeedback_s60.cpp + + LIBS += -ltouchfeedback +} + +load(data_caging_paths) + +TARGET.UID3=0x200315B4 diff --git a/src/plugins/s60/feedback/qtactileFeedback.h b/src/plugins/s60/feedback/qtactileFeedback.h new file mode 100644 index 0000000..7c4cc29 --- /dev/null +++ b/src/plugins/s60/feedback/qtactileFeedback.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QEvent> +#include <QWidget> + +#include "private/qs60style_feedbackinterface_p.h" + +class QTactileFeedback : public TactileFeedbackInterface +{ + Q_OBJECT + Q_INTERFACES(TactileFeedbackInterface) + + public: + void touchFeedback(QEvent *event, const QWidget *widget); + }; diff --git a/src/plugins/s60/feedback/qtactileFeedback_s60.cpp b/src/plugins/s60/feedback/qtactileFeedback_s60.cpp new file mode 100644 index 0000000..c2f1d34 --- /dev/null +++ b/src/plugins/s60/feedback/qtactileFeedback_s60.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> +#include <QObject> +#include <QSlider> +#include <QScrollBar> + +#include <QtCore/qplugin.h> +#include "qtactileFeedback.h" + +#include <touchfeedback.h> + +void QTactileFeedback::touchFeedback(QEvent *event, const QWidget *widget) +{ + //Lets share the global instance for touch feedback (you are NOT allowed to try and delete it!). + MTouchFeedback* feedback = MTouchFeedback::Instance(); + + //If the widget itself is not handling focus, try to use focusProxy widget. + const QWidget *w = ((widget->focusPolicy() == Qt::NoFocus) && (widget->focusProxy())) ? widget->focusProxy() : widget; + + //Only give tactile feedback for enabled widgets that take focus. + if (feedback && w && w->isEnabled() && w->isWidgetType() && w->isVisible()) { + //Scrollbars are 'special' that they don't take focus (nor they have focusProxy), yet we'd like to have tactile feedback for them + if (w->focusPolicy() == Qt::NoFocus) + if (!qobject_cast<const QScrollBar *>(w)) + return; + + //Don't give tactile feedback for widgets that are outside topmost dialog. + QWidget *dialog = QApplication::activeModalWidget(); + if (dialog) { + QList<const QWidget *> allChildren = dialog->findChildren<const QWidget *>(); + if (!allChildren.contains(w)) + return; + } + + //Widget specific tactile feedback. + if (qobject_cast<const QSlider *>(w) || qobject_cast<const QScrollBar *>(w)) + feedback->InstantFeedback(ETouchFeedbackSensitive); + else + feedback->InstantFeedback(ETouchFeedbackBasic); + } +} + +Q_EXPORT_PLUGIN2("feedback", QTactileFeedback); diff --git a/src/plugins/s60/s60.pro b/src/plugins/s60/s60.pro index c999fff..ffcd170 100644 --- a/src/plugins/s60/s60.pro +++ b/src/plugins/s60/s60.pro @@ -6,6 +6,10 @@ symbian { SUBDIRS += 3_1 3_2 } + contains(S60_VERSION, 5.0)|contains(S60_VERSION, symbian3) { + SUBDIRS += feedback + } + # 5.0 is used also for Symbian3 and later SUBDIRS += 5_0 }
\ No newline at end of file diff --git a/src/s60installs/bwins/QtGuiu.def b/src/s60installs/bwins/QtGuiu.def index 7cc2752..c91b22c 100644 --- a/src/s60installs/bwins/QtGuiu.def +++ b/src/s60installs/bwins/QtGuiu.def @@ -12890,10 +12890,12 @@ EXPORTS ?lastRightBearing@QFontEngine@@IAE?AUQFixed@@ABUQGlyphLayout@@_N@Z @ 12889 NONAME ; struct QFixed QFontEngine::lastRightBearing(struct QGlyphLayout const &, bool) ?childrenBoundingRectHelper@QGraphicsItemPrivate@@QAEXPAVQTransform@@PAVQRectF@@PAVQGraphicsItem@@@Z @ 12890 NONAME ; void QGraphicsItemPrivate::childrenBoundingRectHelper(class QTransform *, class QRectF *, class QGraphicsItem *) ?setTimeout@QTapAndHoldGesture@@SAXH@Z @ 12891 NONAME ; void QTapAndHoldGesture::setTimeout(int) - ?qmljsDebugArguments@QApplicationPrivate@@2VQString@@A @ 12892 NONAME ; class QString QApplicationPrivate::qmljsDebugArguments + ?qmljsDebugArguments@QApplicationPrivate@@2VQString@@A @ 12892 NONAME ABSENT ; class QString QApplicationPrivate::qmljsDebugArguments ?effectiveBoundingRect@QGraphicsItemPrivate@@QBE?AVQRectF@@PAVQGraphicsItem@@@Z @ 12893 NONAME ; class QRectF QGraphicsItemPrivate::effectiveBoundingRect(class QGraphicsItem *) const ?maxTextureHeight@QTextureGlyphCache@@UBEHXZ @ 12894 NONAME ; int QTextureGlyphCache::maxTextureHeight(void) const ?maxTextureWidth@QTextureGlyphCache@@UBEHXZ @ 12895 NONAME ; int QTextureGlyphCache::maxTextureWidth(void) const - ?convertToPostscriptFontFamilyName@QFontEngine@@SA?AVQByteArray@@ABV2@@Z @ 12896 NONAME ; class QByteArray QFontEngine::convertToPostscriptFontFamilyName(class QByteArray const &) - ?lastResortFont@QFont@@QBE?AVQString@@XZ @ 12897 NONAME ; class QString QFont::lastResortFont(void) const + ?qmljs_debug_arguments@QApplicationPrivate@@2VQString@@A @ 12896 NONAME ; class QString QApplicationPrivate::qmljs_debug_arguments + ?qmljsDebugArgumentsString@QApplicationPrivate@@SA?AVQString@@XZ @ 12897 NONAME ; class QString QApplicationPrivate::qmljsDebugArgumentsString(void) + ?convertToPostscriptFontFamilyName@QFontEngine@@SA?AVQByteArray@@ABV2@@Z @ 12898 NONAME ; class QByteArray QFontEngine::convertToPostscriptFontFamilyName(class QByteArray const &) + ?lastResortFont@QFont@@QBE?AVQString@@XZ @ 12899 NONAME ; class QString QFont::lastResortFont(void) const diff --git a/src/s60installs/eabi/QtGuiu.def b/src/s60installs/eabi/QtGuiu.def index 4e867a3..f772bcc 100644 --- a/src/s60installs/eabi/QtGuiu.def +++ b/src/s60installs/eabi/QtGuiu.def @@ -12094,9 +12094,11 @@ EXPORTS _ZN18QTapAndHoldGesture7timeoutEv @ 12093 NONAME _ZN20QGraphicsItemPrivate26childrenBoundingRectHelperEP10QTransformP6QRectFb @ 12094 NONAME ABSENT _ZN20QGraphicsItemPrivate14children_clearEP24QDeclarativeListPropertyI15QGraphicsObjectE @ 12095 NONAME - _ZN19QApplicationPrivate19qmljsDebugArgumentsE @ 12096 NONAME DATA 4 + _ZN19QApplicationPrivate19qmljsDebugArgumentsE @ 12096 NONAME DATA 4 ABSENT _ZN20QGraphicsItemPrivate26childrenBoundingRectHelperEP10QTransformP6QRectFP13QGraphicsItem @ 12097 NONAME _ZNK20QGraphicsItemPrivate21effectiveBoundingRectEP13QGraphicsItem @ 12098 NONAME - _ZNK5QFont14lastResortFontEv @ 12099 NONAME - _ZN11QFontEngine33convertToPostscriptFontFamilyNameERK10QByteArray @ 12100 NONAME + _ZN19QApplicationPrivate21qmljs_debug_argumentsE @ 12099 NONAME DATA 4 + _ZN19QApplicationPrivate25qmljsDebugArgumentsStringEv @ 12100 NONAME + _ZNK5QFont14lastResortFontEv @ 12101 NONAME + _ZN11QFontEngine33convertToPostscriptFontFamilyNameERK10QByteArray @ 12102 NONAME diff --git a/src/s60installs/qt.iby b/src/s60installs/qt.iby index 4afbf05..2b3be0a 100644 --- a/src/s60installs/qt.iby +++ b/src/s60installs/qt.iby @@ -60,6 +60,8 @@ file=ABI_DIR\BUILD_DIR\qsymbianbearer.dll SHARED_LIB_DIR\qsymbianbearer.dll // so don't bother including those plugins file=ABI_DIR\BUILD_DIR\qts60plugin_5_0.dll SHARED_LIB_DIR\qts60plugin_5_0.dll +file=ABI_DIR\BUILD_DIR\qtactilefeedback.dll SHARED_LIB_DIR\qtactilefeedback.dll + S60_APP_RESOURCE(s60main) // imageformats stubs @@ -105,6 +107,9 @@ data=\epoc32\data\z\resource\qt\plugins\graphicssystems\qglgraphicssystem.qtplug // bearer stub data=\epoc32\data\z\resource\qt\plugins\bearer\qsymbianbearer.qtplugin resource\qt\plugins\bearer\qsymbianbearer.qtplugin +// feedback +data=\epoc32\data\z\resource\qt\plugins\feedback\qtactilefeedback.qtplugin resource\qt\plugins\feedback\qtactilefeedback.qtplugin + // Stub sis file data=ZSYSTEM\install\qt_stub.sis System\Install\qt_stub.sis data=ZSYSTEM\install\qtwebkit_stub.sis System\Install\qtwebkit_stub.sis diff --git a/src/s60installs/s60installs.pro b/src/s60installs/s60installs.pro index 9559da6..65b8781 100644 --- a/src/s60installs/s60installs.pro +++ b/src/s60installs/s60installs.pro @@ -87,6 +87,12 @@ symbian: { DEPLOYMENT += bearer_plugin } + contains(S60_VERSION, 5.0)|contains(S60_VERSION, symbian3) { + feedback_plugin.sources = $$QT_BUILD_TREE/plugins/s60/feedback/qtactilefeedback$${QT_LIBINFIX}.dll + feedback_plugin.path = c:$$QT_PLUGINS_BASE_DIR/feedback + DEPLOYMENT += feedback_plugin + } + qtlibraries.pkg_postrules += qts60plugindeployment qtlibraries.path = c:/sys/bin |