AudioDecoder::AudioDecoder(bool isPlayback, bool isDelete) : m_cout(stdout, QIODevice::WriteOnly) { m_isPlayback = isPlayback; m_isDelete = isDelete; // Make sure the data we receive is in correct PCM format. // Our wav file writer only supports SignedInt sample type. QAudioFormat format; format.setChannelCount(2); format.setSampleSize(16); format.setSampleRate(48000); format.setCodec("audio/pcm"); format.setSampleType(QAudioFormat::SignedInt); m_decoder.setAudioFormat(format); connect(&m_decoder, SIGNAL(bufferReady()), this, SLOT(bufferReady())); connect(&m_decoder, SIGNAL(error(QAudioDecoder::Error)), this, SLOT(error(QAudioDecoder::Error))); connect(&m_decoder, SIGNAL(stateChanged(QAudioDecoder::State)), this, SLOT(stateChanged(QAudioDecoder::State))); connect(&m_decoder, SIGNAL(finished()), this, SLOT(finished())); connect(&m_decoder, SIGNAL(positionChanged(qint64)), this, SLOT(updateProgress())); connect(&m_decoder, SIGNAL(durationChanged(qint64)), this, SLOT(updateProgress())); connect(&m_soundEffect, SIGNAL(statusChanged()), this, SLOT(playbackStatusChanged())); connect(&m_soundEffect, SIGNAL(playingChanged()), this, SLOT(playingChanged())); m_progress = -1.0; }
/*! Construct an QAudioDecoder instance parented to \a parent. */ QAudioDecoder::QAudioDecoder(QObject *parent) : QMediaObject(*new QAudioDecoderPrivate, parent, QMediaServiceProvider::defaultServiceProvider()->requestService(Q_MEDIASERVICE_AUDIODECODER)) { Q_D(QAudioDecoder); d->provider = QMediaServiceProvider::defaultServiceProvider(); if (d->service) { d->control = qobject_cast<QAudioDecoderControl*>(d->service->requestControl(QAudioDecoderControl_iid)); if (d->control != 0) { connect(d->control, SIGNAL(stateChanged(QAudioDecoder::State)), SLOT(_q_stateChanged(QAudioDecoder::State))); connect(d->control, SIGNAL(error(int,QString)), SLOT(_q_error(int,QString))); connect(d->control, SIGNAL(formatChanged(QAudioFormat)), SIGNAL(formatChanged(QAudioFormat))); connect(d->control, SIGNAL(sourceChanged()), SIGNAL(sourceChanged())); connect(d->control, SIGNAL(bufferReady()), this, SIGNAL(bufferReady())); connect(d->control ,SIGNAL(bufferAvailableChanged(bool)), this, SIGNAL(bufferAvailableChanged(bool))); connect(d->control ,SIGNAL(finished()), this, SIGNAL(finished())); connect(d->control ,SIGNAL(positionChanged(qint64)), this, SIGNAL(positionChanged(qint64))); connect(d->control ,SIGNAL(durationChanged(qint64)), this, SIGNAL(durationChanged(qint64))); }
void tst_QAudioDecoderBackend::fileTest() { QAudioDecoder d; QAudioBuffer buffer; quint64 duration = 0; int byteCount = 0; int sampleCount = 0; QVERIFY(d.state() == QAudioDecoder::StoppedState); QVERIFY(d.bufferAvailable() == false); QCOMPARE(d.sourceFilename(), QString("")); QVERIFY(d.audioFormat() == QAudioFormat()); // Test local file QFileInfo fileInfo(QFINDTESTDATA(TEST_FILE_NAME)); d.setSourceFilename(fileInfo.absoluteFilePath()); QVERIFY(d.state() == QAudioDecoder::StoppedState); QVERIFY(!d.bufferAvailable()); QCOMPARE(d.sourceFilename(), fileInfo.absoluteFilePath()); QSignalSpy readySpy(&d, SIGNAL(bufferReady())); QSignalSpy bufferChangedSpy(&d, SIGNAL(bufferAvailableChanged(bool))); QSignalSpy errorSpy(&d, SIGNAL(error(QAudioDecoder::Error))); QSignalSpy stateSpy(&d, SIGNAL(stateChanged(QAudioDecoder::State))); QSignalSpy durationSpy(&d, SIGNAL(durationChanged(qint64))); QSignalSpy finishedSpy(&d, SIGNAL(finished())); QSignalSpy positionSpy(&d, SIGNAL(positionChanged(qint64))); d.start(); QTRY_VERIFY(d.state() == QAudioDecoder::DecodingState); QTRY_VERIFY(!stateSpy.isEmpty()); QTRY_VERIFY(!readySpy.isEmpty()); QTRY_VERIFY(!bufferChangedSpy.isEmpty()); QVERIFY(d.bufferAvailable()); QTRY_VERIFY(!durationSpy.isEmpty()); QVERIFY(qAbs(d.duration() - 1000) < 20); buffer = d.read(); QVERIFY(buffer.isValid()); // Test file is 44.1K 16bit mono, 44094 samples QCOMPARE(buffer.format().channelCount(), 1); QCOMPARE(buffer.format().sampleRate(), 44100); QCOMPARE(buffer.format().sampleSize(), 16); QCOMPARE(buffer.format().sampleType(), QAudioFormat::SignedInt); QCOMPARE(buffer.format().codec(), QString("audio/pcm")); QCOMPARE(buffer.byteCount(), buffer.sampleCount() * 2); // 16bit mono // The decoder should still have no format set QVERIFY(d.audioFormat() == QAudioFormat()); QVERIFY(errorSpy.isEmpty()); duration += buffer.duration(); sampleCount += buffer.sampleCount(); byteCount += buffer.byteCount(); // Now drain the decoder if (sampleCount < 44094) { QTRY_COMPARE(d.bufferAvailable(), true); } while (d.bufferAvailable()) { buffer = d.read(); QVERIFY(buffer.isValid()); QTRY_VERIFY(!positionSpy.isEmpty()); QVERIFY(positionSpy.takeLast().at(0).toLongLong() == qint64(duration / 1000)); duration += buffer.duration(); sampleCount += buffer.sampleCount(); byteCount += buffer.byteCount(); if (sampleCount < 44094) { QTRY_COMPARE(d.bufferAvailable(), true); } } // Make sure the duration is roughly correct (+/- 20ms) QCOMPARE(sampleCount, 44094); QCOMPARE(byteCount, 44094 * 2); QVERIFY(qAbs(qint64(duration) - 1000000) < 20000); QVERIFY(qAbs((d.position() + (buffer.duration() / 1000)) - 1000) < 20); QTRY_COMPARE(finishedSpy.count(), 1); QVERIFY(!d.bufferAvailable()); QTRY_COMPARE(d.state(), QAudioDecoder::StoppedState); d.stop(); QTRY_COMPARE(d.state(), QAudioDecoder::StoppedState); QTRY_COMPARE(durationSpy.count(), 2); QCOMPARE(d.duration(), qint64(-1)); QVERIFY(!d.bufferAvailable()); readySpy.clear(); bufferChangedSpy.clear(); stateSpy.clear(); durationSpy.clear(); finishedSpy.clear(); positionSpy.clear(); // change output audio format QAudioFormat format; format.setChannelCount(2); format.setSampleSize(8); format.setSampleRate(11050); format.setCodec("audio/pcm"); format.setSampleType(QAudioFormat::SignedInt); d.setAudioFormat(format); // We expect 1 second still, at 11050 * 2 samples == 22k samples. // (at 1 byte/sample -> 22kb) // Make sure it stuck QVERIFY(d.audioFormat() == format); duration = 0; sampleCount = 0; byteCount = 0; d.start(); QTRY_VERIFY(d.state() == QAudioDecoder::DecodingState); QTRY_VERIFY(!stateSpy.isEmpty()); QTRY_VERIFY(!readySpy.isEmpty()); QTRY_VERIFY(!bufferChangedSpy.isEmpty()); QVERIFY(d.bufferAvailable()); QTRY_VERIFY(!durationSpy.isEmpty()); QVERIFY(qAbs(d.duration() - 1000) < 20); buffer = d.read(); QVERIFY(buffer.isValid()); // See if we got the right format QVERIFY(buffer.format() == format); // The decoder should still have the same format QVERIFY(d.audioFormat() == format); QVERIFY(errorSpy.isEmpty()); duration += buffer.duration(); sampleCount += buffer.sampleCount(); byteCount += buffer.byteCount(); // Now drain the decoder if (duration < 998000) { QTRY_COMPARE(d.bufferAvailable(), true); } while (d.bufferAvailable()) { buffer = d.read(); QVERIFY(buffer.isValid()); QTRY_VERIFY(!positionSpy.isEmpty()); QVERIFY(positionSpy.takeLast().at(0).toLongLong() == qint64(duration / 1000)); QVERIFY(d.position() - (duration / 1000) < 20); duration += buffer.duration(); sampleCount += buffer.sampleCount(); byteCount += buffer.byteCount(); if (duration < 998000) { QTRY_COMPARE(d.bufferAvailable(), true); } } // Resampling might end up with fewer or more samples // so be a bit sloppy QVERIFY(qAbs(sampleCount - 22047) < 100); QVERIFY(qAbs(byteCount - 22047) < 100); QVERIFY(qAbs(qint64(duration) - 1000000) < 20000); QVERIFY(qAbs((d.position() + (buffer.duration() / 1000)) - 1000) < 20); QTRY_COMPARE(finishedSpy.count(), 1); QVERIFY(!d.bufferAvailable()); QTRY_COMPARE(d.state(), QAudioDecoder::StoppedState); d.stop(); QTRY_COMPARE(d.state(), QAudioDecoder::StoppedState); QTRY_COMPARE(durationSpy.count(), 2); QCOMPARE(d.duration(), qint64(-1)); QVERIFY(!d.bufferAvailable()); }
void tst_QAudioDecoderBackend::deviceTest() { QAudioDecoder d; QAudioBuffer buffer; quint64 duration = 0; int sampleCount = 0; QSignalSpy readySpy(&d, SIGNAL(bufferReady())); QSignalSpy bufferChangedSpy(&d, SIGNAL(bufferAvailableChanged(bool))); QSignalSpy errorSpy(&d, SIGNAL(error(QAudioDecoder::Error))); QSignalSpy stateSpy(&d, SIGNAL(stateChanged(QAudioDecoder::State))); QSignalSpy durationSpy(&d, SIGNAL(durationChanged(qint64))); QSignalSpy finishedSpy(&d, SIGNAL(finished())); QSignalSpy positionSpy(&d, SIGNAL(positionChanged(qint64))); QVERIFY(d.state() == QAudioDecoder::StoppedState); QVERIFY(d.bufferAvailable() == false); QCOMPARE(d.sourceFilename(), QString("")); QVERIFY(d.audioFormat() == QAudioFormat()); QFileInfo fileInfo(QFINDTESTDATA(TEST_FILE_NAME)); QFile file(fileInfo.absoluteFilePath()); QVERIFY(file.open(QIODevice::ReadOnly)); d.setSourceDevice(&file); QVERIFY(d.sourceDevice() == &file); QVERIFY(d.sourceFilename().isEmpty()); // We haven't set the format yet QVERIFY(d.audioFormat() == QAudioFormat()); d.start(); QTRY_VERIFY(d.state() == QAudioDecoder::DecodingState); QTRY_VERIFY(!stateSpy.isEmpty()); QTRY_VERIFY(!readySpy.isEmpty()); QTRY_VERIFY(!bufferChangedSpy.isEmpty()); QVERIFY(d.bufferAvailable()); QTRY_VERIFY(!durationSpy.isEmpty()); QVERIFY(qAbs(d.duration() - 1000) < 20); buffer = d.read(); QVERIFY(buffer.isValid()); // Test file is 44.1K 16bit mono QCOMPARE(buffer.format().channelCount(), 1); QCOMPARE(buffer.format().sampleRate(), 44100); QCOMPARE(buffer.format().sampleSize(), 16); QCOMPARE(buffer.format().sampleType(), QAudioFormat::SignedInt); QCOMPARE(buffer.format().codec(), QString("audio/pcm")); QVERIFY(errorSpy.isEmpty()); duration += buffer.duration(); sampleCount += buffer.sampleCount(); // Now drain the decoder if (sampleCount < 44094) { QTRY_COMPARE(d.bufferAvailable(), true); } while (d.bufferAvailable()) { buffer = d.read(); QVERIFY(buffer.isValid()); QTRY_VERIFY(!positionSpy.isEmpty()); QVERIFY(positionSpy.takeLast().at(0).toLongLong() == qint64(duration / 1000)); QVERIFY(d.position() - (duration / 1000) < 20); duration += buffer.duration(); sampleCount += buffer.sampleCount(); if (sampleCount < 44094) { QTRY_COMPARE(d.bufferAvailable(), true); } } // Make sure the duration is roughly correct (+/- 20ms) QCOMPARE(sampleCount, 44094); QVERIFY(qAbs(qint64(duration) - 1000000) < 20000); QVERIFY(qAbs((d.position() + (buffer.duration() / 1000)) - 1000) < 20); QTRY_COMPARE(finishedSpy.count(), 1); QVERIFY(!d.bufferAvailable()); QTRY_COMPARE(d.state(), QAudioDecoder::StoppedState); d.stop(); QTRY_COMPARE(d.state(), QAudioDecoder::StoppedState); QVERIFY(!d.bufferAvailable()); QTRY_COMPARE(durationSpy.count(), 2); QCOMPARE(d.duration(), qint64(-1)); readySpy.clear(); bufferChangedSpy.clear(); stateSpy.clear(); durationSpy.clear(); finishedSpy.clear(); positionSpy.clear(); // Now try changing formats QAudioFormat format; format.setChannelCount(2); format.setSampleSize(8); format.setSampleRate(8000); format.setCodec("audio/pcm"); format.setSampleType(QAudioFormat::SignedInt); d.setAudioFormat(format); // Make sure it stuck QVERIFY(d.audioFormat() == format); d.start(); QTRY_VERIFY(d.state() == QAudioDecoder::DecodingState); QTRY_VERIFY(!stateSpy.isEmpty()); QTRY_VERIFY(!readySpy.isEmpty()); QTRY_VERIFY(!bufferChangedSpy.isEmpty()); QVERIFY(d.bufferAvailable()); QTRY_VERIFY(!durationSpy.isEmpty()); QVERIFY(qAbs(d.duration() - 1000) < 20); buffer = d.read(); QVERIFY(buffer.isValid()); // See if we got the right format QVERIFY(buffer.format() == format); // The decoder should still have the same format QVERIFY(d.audioFormat() == format); QVERIFY(errorSpy.isEmpty()); d.stop(); QTRY_COMPARE(d.state(), QAudioDecoder::StoppedState); QVERIFY(!d.bufferAvailable()); QTRY_COMPARE(durationSpy.count(), 2); QCOMPARE(d.duration(), qint64(-1)); }
/* The corrupted file is generated by copying a few random numbers from /dev/random on a linux machine. */ void tst_QAudioDecoderBackend::corruptedFileTest() { QAudioDecoder d; QAudioBuffer buffer; QVERIFY(d.state() == QAudioDecoder::StoppedState); QVERIFY(d.bufferAvailable() == false); QCOMPARE(d.sourceFilename(), QString("")); QVERIFY(d.audioFormat() == QAudioFormat()); // Test local file QFileInfo fileInfo(QFINDTESTDATA(TEST_CORRUPTED_FILE_NAME)); d.setSourceFilename(fileInfo.absoluteFilePath()); QVERIFY(d.state() == QAudioDecoder::StoppedState); QVERIFY(!d.bufferAvailable()); QCOMPARE(d.sourceFilename(), fileInfo.absoluteFilePath()); QSignalSpy readySpy(&d, SIGNAL(bufferReady())); QSignalSpy bufferChangedSpy(&d, SIGNAL(bufferAvailableChanged(bool))); QSignalSpy errorSpy(&d, SIGNAL(error(QAudioDecoder::Error))); QSignalSpy stateSpy(&d, SIGNAL(stateChanged(QAudioDecoder::State))); QSignalSpy durationSpy(&d, SIGNAL(durationChanged(qint64))); QSignalSpy finishedSpy(&d, SIGNAL(finished())); QSignalSpy positionSpy(&d, SIGNAL(positionChanged(qint64))); d.start(); QTRY_VERIFY(d.state() == QAudioDecoder::StoppedState); QVERIFY(!d.bufferAvailable()); QCOMPARE(d.audioFormat(), QAudioFormat()); QCOMPARE(d.duration(), qint64(-1)); QCOMPARE(d.position(), qint64(-1)); // Check the error code. QTRY_VERIFY(!errorSpy.isEmpty()); // Have to use qvariant_cast, toInt will return 0 because unrecognized type; QAudioDecoder::Error errorCode = qvariant_cast<QAudioDecoder::Error>(errorSpy.takeLast().at(0)); QCOMPARE(errorCode, QAudioDecoder::FormatError); QCOMPARE(d.error(), QAudioDecoder::FormatError); // Check all other spies. QVERIFY(readySpy.isEmpty()); QVERIFY(bufferChangedSpy.isEmpty()); QVERIFY(stateSpy.isEmpty()); QVERIFY(finishedSpy.isEmpty()); QVERIFY(positionSpy.isEmpty()); QVERIFY(durationSpy.isEmpty()); errorSpy.clear(); // Try read even if the file is corrupted to test the robustness. buffer = d.read(); QTRY_VERIFY(d.state() == QAudioDecoder::StoppedState); QVERIFY(!buffer.isValid()); QVERIFY(!d.bufferAvailable()); QCOMPARE(d.position(), qint64(-1)); QVERIFY(errorSpy.isEmpty()); QVERIFY(readySpy.isEmpty()); QVERIFY(bufferChangedSpy.isEmpty()); QVERIFY(stateSpy.isEmpty()); QVERIFY(finishedSpy.isEmpty()); QVERIFY(positionSpy.isEmpty()); QVERIFY(durationSpy.isEmpty()); d.stop(); QTRY_COMPARE(d.state(), QAudioDecoder::StoppedState); QCOMPARE(d.duration(), qint64(-1)); QVERIFY(!d.bufferAvailable()); }
void DMediaDriverNVMemory::DoSessionEndDfc() { __DEBUG_PRINT(">DMediaDriverNVMemory::DoSessionEndDfc()"); TInt r = KErrNone; // Check that we have a request in process if( iCurrentRequest ) { // Transaction variables TUint32 transactionSectorOffset(0); TUint32 transactionLength(0); TUint32 transactionDirection( NVMEM_TRANSACTION_UNDEFINED ); // How much did we actually transfer? TUint32 latestTransferSize = (iLatestTransferSectorCount * KDiskSectorSize); __DEBUG_PRINT("DMediaDriverNVMemory::DoSessionEndDfc() latestTransferSize: %d", latestTransferSize ); // Subtract alignment overhead latestTransferSize = latestTransferSize - iAlignmentOverhead; // For decision whether the buffer is ready for operation already TBool bufferReady(EFalse); // For decision whether we have finished the latest request TBool sessionComplete(EFalse); // Was there a read-modify-write (RWM) for which we need to do some buffer manipulation before proceeding? // Note that in case of format we triggered to alignment violation in earlier method already and can not enter to following // condition when there is a format operation going on if( iReadModifyWrite ) { bufferReady = ETrue; iReadModifyWrite = EFalse; // Was it a splitted operation for which we only need to take care of the broken head sector. if( iSplitted > 0 ) { // We have a sector here here filled with data from mass memory. Modify with client data. __DEBUG_PRINT("DMediaDriverNVMemory::DoSessionEndDfc() readremote splitted: %d head: %d", latestTransferSize, iHead ); TPtr8 targetDescriptor(&iTransferBufferLin[iHead], KNVMemTransferBufferSize - iHead); // SF BUG 3759 - NVM drives do not support operations larger than 262144 bytes correctly - start r = iCurrentRequest->ReadRemote(&targetDescriptor,iSplitLength); // SF BUG 3759 - NVM drives do not support operations larger than 262144 bytes correctly - end } // Else we need to take care of both head and tail else { // We have a piece of data read from mass memory. Modify with client data. __DEBUG_PRINT("DMediaDriverNVMemory::DoSessionEndDfc() readremote: %d head: %d", I64LOW(iTotalLength - iProsessedLength), iHead ); TPtr8 targetDescriptor(&iTransferBufferLin[iHead], I64LOW(iTotalLength - iProsessedLength)); // SF BUG 3759 - NVM drives do not support operations larger than 262144 bytes correctly - start r = iCurrentRequest->ReadRemote(&targetDescriptor,iSplitLength); // SF BUG 3759 - NVM drives do not support operations larger than 262144 bytes correctly - end // latestTransferSize -= (KDiskSectorSize - iTail); iTail = 0; } // SF BUG 3759 - NVM drives do not support operations larger than 262144 bytes correctly - start iSplitLength+= latestTransferSize; // Update split transfer position count // SF BUG 3759 - NVM drives do not support operations larger than 262144 bytes correctly - end } else { // Overhead is processed when we enter here iAlignmentOverhead = 0; // Update position iPos += latestTransferSize; // Save the information on how many bytes we transferred already iProsessedLength += latestTransferSize; // Update the splitted information. We don't take head into account here anymore since it is already taken care of iSplitted = (iTotalLength - iProsessedLength + iTail) / KNVMemTransferBufferSize; // Check if we have done already if( iProsessedLength >= iTotalLength ) { // If this was the final transfer for this request let's take tail into account as well (if not taken already) // iProsessedLength -= iTail; // latestTransferSize -= iTail; if( iProsessedLength > iTotalLength ) { Kern::Fault("DMediaDriverNVMemory: Illegal transfer operation!", 0); } sessionComplete = ETrue; } } TInt request = iCurrentRequest->Id(); // Set our sector offset transactionSectorOffset = (TUint32)(iPos / KDiskSectorSize); if( bufferReady ) { // Write as much as we read in RMW operation transactionLength = (iLatestTransferSectorCount * KDiskSectorSize); } else { // Do we have an operation longer than our shared memory? if( iSplitted > 0 ) { transactionLength = KNVMemTransferBufferSize; } else { // Do the whole operation in one go since we have enough room in our memory transactionLength = I64LOW(iTotalLength - iProsessedLength); // Read the "broken" tail sector if( iTail > 0 && request == DLocalDrive::EWrite ) { iReadModifyWrite = ETrue; // Read the "broken" tail sector transactionLength += iTail; iAlignmentOverhead = iTail; } } } // Was there a need to read-modify before writing if( iReadModifyWrite ) { transactionDirection = NVMEM_TRANSACTION_READ; } else { if( request == DLocalDrive::ERead ) { transactionDirection = NVMEM_TRANSACTION_READ; // Write to client __DEBUG_PRINT("DMediaDriverNVMemory::DoSessionEndDfc() WriteRemote: %d head: %d", latestTransferSize, iHead ); TPtrC8 sourceDescriptor(&iTransferBufferLin[iHead], latestTransferSize); // SF BUG 3759 - NVM drives do not support operations larger than 262144 bytes correctly - start r = iCurrentRequest->WriteRemote( &sourceDescriptor, iSplitLength ); // Allow for "splitted" operations // SF BUG 3759 - NVM drives do not support operations larger than 262144 bytes correctly - end } // Head is processed iHead = 0; if( request == DLocalDrive::EWrite && !sessionComplete ) { transactionDirection = NVMEM_TRANSACTION_WRITE; if( bufferReady ) { // Actually no need for any actions here } else { // Prepare a buffer for transfer __DEBUG_PRINT("DMediaDriverNVMemory::DoSessionEndDfc() ReadRemote: %d head: %d", latestTransferSize, iHead ); TPtr8 targetDescriptor(iTransferBufferLin, transactionLength); // SF BUG 3759 - NVM drives do not support operations larger than 262144 bytes correctly - start r = iCurrentRequest->ReadRemote(&targetDescriptor,iSplitLength); // allow for "splitted" operations // SF BUG 3759 - NVM drives do not support operations larger than 262144 bytes correctly - end } } // SF BUG 3759 - NVM drives do not support operations larger than 262144 bytes correctly - start iSplitLength+= latestTransferSize; // Update split transfer position count // SF BUG 3759 - NVM drives do not support operations larger than 262144 bytes correctly - end if( request == DLocalDrive::EFormat ) { transactionDirection = NVMEM_TRANSACTION_WRITE; } } if( sessionComplete ) { CompleteRequest( r ); } else { ContinueTransaction( transactionSectorOffset, transactionLength/KDiskSectorSize, transactionDirection ); } } else { // Let's just flow through for now } __DEBUG_PRINT("<DMediaDriverNVMemory::DoSessionEndDfc()" ); }