void AudioRecorder::processBuffer(const QAudioBuffer& buffer) { if (audioLevels.count() != buffer.format().channelCount()) { qDeleteAll(audioLevels); audioLevels.clear(); for (int i = 0; i < buffer.format().channelCount(); ++i) { QAudioLevel *level = new QAudioLevel(ui->centralwidget); audioLevels.append(level); ui->levelsLayout->addWidget(level); } } QVector<qreal> levels = getBufferLevels(buffer); for (int i = 0; i < levels.count(); ++i) audioLevels.at(i)->setLevel(levels.at(i)); }
bool WaveFileWriter::write(const QAudioBuffer &buffer) { if (buffer.format() != m_format) return false; // buffer format has changed qint64 written = file.write((const char *)buffer.constData(), buffer.byteCount()); m_dataLength += written; return written == buffer.byteCount(); }
void AudioDecoder::bufferReady() { // read a buffer from audio decoder QAudioBuffer buffer = m_decoder.read(); if (!buffer.isValid()) return; if (!m_fileWriter.isOpen() && !m_fileWriter.open(m_targetFilename, buffer.format())) { m_decoder.stop(); return; } m_fileWriter.write(buffer); }
void Analyzer::preProcess(const QAudioBuffer &input) { m_input.fill(0); switch (input.format().sampleSize()) { case 8: extractAndScale<qint8>(input); break; case 16: extractAndScale<qint16>(input); break; case 32: extractAndScale<qint32>(input); break; case 64: extractAndScale<qint64>(input); break; } // Find a simple least squares fit y = ax + b to the scaled input const auto xMean = 0.5 * (m_sampleSize + 1); const auto sum = std::accumulate(m_input.constBegin(), m_input.constBegin() + m_sampleSize, 0); const auto yMean = sum / m_sampleSize; qreal covXY = 0; // Cross-covariance qreal varX = 0; // Variance auto y = m_input.constBegin(); for (int x = 0; x < m_input.size(); ++x, ++y) { const auto dx = x - xMean; covXY += dx * (*y - yMean); varX += dx * dx; } const auto a = covXY / varX; const auto b = yMean - a * xMean; // Subtract this fit and apply the window function auto i = m_input.begin(); auto w = m_window.constBegin(); for (int x = 0; x < m_input.size(); ++i, ++w, ++x) *i = *w * (*i - (a * x + b)); }
void Analyzer::doAnalysis(const QAudioBuffer &input) { if (m_state != Ready) return; if (m_calibrateFilter) setState(CalibratingFilter); else setState(Processing); // Process the bytearray into m_input and store a copy for computation of // the SNAC function m_currentFormat = input.format(); preProcess(input); auto processedInput = m_input; processedInput.detach(); getSpectrum(); if (m_calibrateFilter) calibrateFilter(); processSpectrum(); // Finally, compute the normalised ACF and frequency estimate getAcf(); const auto snac = computeSnac(m_input, processedInput); const auto snacPeak = determineSnacFundamental(snac); Spectrum snacPeaks; if (snacPeak.frequency > 0) snacPeaks << snacPeak; // The accuracy of the obtained fundamental is fair, but can be improved // using the accurate power spectrum stored earlier, which also allows // identifying overtones const auto harmonics = findHarmonics(m_spectrum, m_currentFormat.sampleRate() / snacPeak.frequency); // Report analysis results setState(Ready); emit done(harmonics, m_spectrum, snac, snacPeaks); }
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)); }
// returns the audio level for each channel QVector<qreal> getBufferLevels(const QAudioBuffer& buffer) { QVector<qreal> values; if (!buffer.format().isValid() || buffer.format().byteOrder() != QAudioFormat::LittleEndian) return values; if (buffer.format().codec() != "audio/pcm") return values; int channelCount = buffer.format().channelCount(); values.fill(0, channelCount); qreal peak_value = getPeakValue(buffer.format()); if (qFuzzyCompare(peak_value, qreal(0))) return values; switch (buffer.format().sampleType()) { case QAudioFormat::Unknown: case QAudioFormat::UnSignedInt: if (buffer.format().sampleSize() == 32) values = getBufferLevels(buffer.constData<quint32>(), buffer.frameCount(), channelCount); if (buffer.format().sampleSize() == 16) values = getBufferLevels(buffer.constData<quint16>(), buffer.frameCount(), channelCount); if (buffer.format().sampleSize() == 8) values = getBufferLevels(buffer.constData<quint8>(), buffer.frameCount(), channelCount); for (int i = 0; i < values.size(); ++i) values[i] = qAbs(values.at(i) - peak_value / 2) / (peak_value / 2); break; case QAudioFormat::Float: if (buffer.format().sampleSize() == 32) { values = getBufferLevels(buffer.constData<float>(), buffer.frameCount(), channelCount); for (int i = 0; i < values.size(); ++i) values[i] /= peak_value; } break; case QAudioFormat::SignedInt: if (buffer.format().sampleSize() == 32) values = getBufferLevels(buffer.constData<qint32>(), buffer.frameCount(), channelCount); if (buffer.format().sampleSize() == 16) values = getBufferLevels(buffer.constData<qint16>(), buffer.frameCount(), channelCount); if (buffer.format().sampleSize() == 8) values = getBufferLevels(buffer.constData<qint8>(), buffer.frameCount(), channelCount); for (int i = 0; i < values.size(); ++i) values[i] /= peak_value; break; } return values; }
// process audio buffer for fft calculations void MainWindow::processBuffer(QAudioBuffer buffer){ qreal peakValue; int duration; if(buffer.frameCount() < 512) return; // return left and right audio mean levels levelLeft = levelRight = 0; // It only knows how to process stereo audio frames // mono frames = :P if(buffer.format().channelCount() != 2) return; sample.resize(buffer.frameCount()); // audio is signed int if(buffer.format().sampleType() == QAudioFormat::SignedInt){ QAudioBuffer::S16S *data = buffer.data<QAudioBuffer::S16S>(); // peak value changes according to sample size. if (buffer.format().sampleSize() == 32) peakValue=INT_MAX; else if (buffer.format().sampleSize() == 16) peakValue=SHRT_MAX; else peakValue=CHAR_MAX; // scale everything to [0,1] for(int i=0; i<buffer.frameCount(); i++){ // for visualization purposes, we only need one of the // left/right channels sample[i] = data[i].left/peakValue; levelLeft+= abs(data[i].left)/peakValue; levelRight+= abs(data[i].right)/peakValue; } } // audio is unsigned int else if(buffer.format().sampleType() == QAudioFormat::UnSignedInt){ QAudioBuffer::S16U *data = buffer.data<QAudioBuffer::S16U>(); if (buffer.format().sampleSize() == 32) peakValue=UINT_MAX; else if (buffer.format().sampleSize() == 16) peakValue=USHRT_MAX; else peakValue=UCHAR_MAX; for(int i=0; i<buffer.frameCount(); i++){ sample[i] = data[i].left/peakValue; levelLeft+= abs(data[i].left)/peakValue; levelRight+= abs(data[i].right)/peakValue; } } // audio is float type else if(buffer.format().sampleType() == QAudioFormat::Float){ QAudioBuffer::S32F *data = buffer.data<QAudioBuffer::S32F>(); peakValue = 1.00003; for(int i=0; i<buffer.frameCount(); i++){ sample[i] = data[i].left/peakValue; // test if sample[i] is infinity (it works) // some tests produced infinity values :p if(sample[i] != sample[i]){ sample[i] = 0; } else{ levelLeft+= abs(data[i].left)/peakValue; levelRight+= abs(data[i].right)/peakValue; } } } // if the probe is listening to the audio // do fft calculations // when it is done, calculator will tell us if(probe->isActive()){ duration = buffer.format().durationForBytes(buffer.frameCount())/1000; //qDebug() << "duracao =" << duration; calculator->calc(sample, duration); } // tells anyone interested about left and right mean levels emit levels(levelLeft/buffer.frameCount(),levelRight/buffer.frameCount()); }
void AudioBuffer::init(QAudioBuffer &qtbuffer){ qDebug() << "void AudioBuffer::init(...) called"; QAudioFormat audioFormat = qtbuffer.format(); this->hzFreq = audioFormat.sampleRate(); this->durationInMs = qtbuffer.duration()*1000; QAudioFormat::SampleType sampleType = audioFormat.sampleType(); int frameCount = qtbuffer.frameCount(); int nChannels = audioFormat.channelCount(); int bytesPerFrame = audioFormat.bytesPerFrame(); int bytesPerValue = bytesPerFrame/nChannels; void *firstData = qtbuffer.data(); this->bufferSize = frameCount; this->buffer = QSharedPointer<SharedBuffer>( new SharedBuffer); this->buffer->buffer = new int[this->bufferSize]; this->mean = 0; const int tempReducFactor = 500; qint64 meanOfSquare = 0; for(int i=0; i<frameCount; i++){ int currentValue = 0; for(int j=0; j<nChannels; j++){ int currentPos = i*bytesPerFrame + j*bytesPerValue; void *valPos = (void *)(((quint8 *)firstData) + currentPos); if(sampleType == QAudioFormat::SignedInt){ if(bytesPerValue == 1){ quint8 val = *((quint8*)valPos); currentValue += val; }else if(bytesPerValue == 2){ quint16 val = *((quint16*)valPos); currentValue += val; }else if(bytesPerValue == 4){ quint32 val = *((quint32*)valPos); currentValue += val; }else if(bytesPerValue == 8){ quint64 val = *((quint64*)valPos); currentValue += val; } }else if(sampleType == QAudioFormat::UnSignedInt){ if(bytesPerValue == 1){ qint8 val = *((qint8*)valPos); currentValue += val; }else if(bytesPerValue == 2){ qint16 val = *((qint16*)valPos); currentValue += val; }else if(bytesPerValue == 4){ qint32 val = *((qint32*)valPos); currentValue += val; }else if(bytesPerValue == 8){ qint64 val = *((qint64*)valPos); currentValue += val; } }else if(sampleType == QAudioFormat::Float){ qreal val = *((qreal*)valPos); currentValue += val; } } currentValue /= nChannels; currentValue /= tempReducFactor; this->buffer->buffer[i] = currentValue; this->mean += currentValue; meanOfSquare += currentValue*currentValue; if(meanOfSquare < 0){ Q_ASSERT(false); } } this->mean /= frameCount; meanOfSquare /= frameCount; qint64 squaredMean = this->mean * this->mean; this->var = meanOfSquare - squaredMean; this->var *= tempReducFactor * tempReducFactor; this->mean *= tempReducFactor; this->sd = qSqrt(this->var); qDebug() << "void AudioBuffer::init(...) end"; }