QML Audio with Surface3D FFT
-
I am trying to implement Audio FFT with Surface3D in qml to plot the FFT.
Audio is working in a different thread and Surface3D (QML) is the main thread.Once QAudioInput writes to a buffer and once there is a value written a signal is emited. In the slot i read the writting buffer and do DSP and play it back to QAudioOutput. In the DSP i copy the values and do FFT and send the result to the main thread via signal.
connect(&wrBuff, &QIODevice::bytesWritten, this, &AudioSoundcard::readBufferSlot); audioInput = new QAudioInput (input, m_format, this); audioOutput = new QAudioOutput(output, m_format, this); audioInput->start(&wrBuff); audioOutput->start(&rdBuff); .... void AudioSoundcard::readBufferSlot(void) { if((!wrBuff.buffer().size()>=2)) return; rdBuff.buffer().remove(0, rdBuff.pos()); // set pointer to the beginning of the unread data rdBuff.seek(0); QDataStream in(wrBuff.buffer()); //< Attach a read-only stream to it in.setByteOrder(QDataStream::LittleEndian); //< Set the proper byte order QByteArray filter; quint16 outdata; //< The result you want float* audioData[2]; audioData[0] = new float[wrBuff.buffer().size()/2]; audioData[1] = new float[wrBuff.buffer().size()/2]; int r=0,l=0; for(int i=0; i<wrBuff.buffer().size()/2; i++) { in >> outdata; //< Just read it from the stream //samplesSave.append(outdata); //audioData[0][i]=outdata; //audioData[1][i]=outdata; //r++; if(i % 2==0 && r< wrBuff.buffer().size()/2) { audioData[0][r]=outdata; r++; } else if(l< wrBuff.buffer().size()/2) { audioData[1][l]=outdata; l++; } //out << outdata; } if(l != 0 && r != 0) { if(HighfilterEnable) highpass->process (wrBuff.buffer().size()/4, audioData); if(LowfilterEnable) lowpass->process (wrBuff.buffer().size()/4, audioData); } for(int i=0; i<wrBuff.buffer().size()/4; i++) { qint16 valueR=audioData[0][i]; qint16 valueL=audioData[1][i]; samplesSaveR.append(valueR); samplesSaveL.append(valueL); filter.append(quint8(valueR)); filter.append(quint8(valueR>>8)); filter.append(quint8(valueL)); filter.append(quint8(valueL>>8)); } if (m_spectrumAnalyser.isReady() && samplesSaveR.size()>=m_spectrumBufferLength) { m_spectrumBuffer = QByteArray::fromRawData(samplesSaveR.constData(),m_spectrumBufferLength); m_spectrumPosition = 0; channel=0; m_spectrumAnalyser.calculate(m_spectrumBuffer, m_formatFFT); samplesSaveR.clear(); } // write new data rdBuff.buffer().append(filter); // remove all data that was already written wrBuff.buffer().clear(); wrBuff.seek(0); }
So in the main thread i go ahead and write to the Vector and send a notification to QML to update the graph.
void AudioMain::spectrumChanged(const FrequencySpectrum &spectrum) { FrequencySpectrum::const_iterator i = spectrum.begin(); const FrequencySpectrum::const_iterator end = spectrum.end(); QSurfaceDataArray &cache = m_data[0]; QSurfaceDataRow &row = *(cache[timeCount]); float x = timeCount; int h=0; for ( ; i != end; ++i) { const FrequencySpectrum::Element e = *i; float z =e.frequency; float y =e.amplitude; row[h] = QVector3D(x, y, z); h++; } if(timeCount>=timebase-1) { timeCount=0; emit samplesArrived(); } else timeCount++; } void AudioMain::update(QSurface3DSeries *series) { if (series && m_data.size()) { m_index = 0; QSurfaceDataArray array = m_data.at(m_index); int newRowCount = array.size(); int newColumnCount = array.at(0)->size(); // If the first time or the dimensions of the cache array have changed, // reconstruct the reset array if (m_resetArray || series->dataProxy()->rowCount() != newRowCount || series->dataProxy()->columnCount() != newColumnCount) { m_resetArray = new QSurfaceDataArray(); m_resetArray->reserve(newRowCount); for (int i(0); i < newRowCount; i++) m_resetArray->append(new QSurfaceDataRow(newColumnCount)); } // Copy items from our cache to the reset array for (int i(0); i < newRowCount; i++) { const QSurfaceDataRow &sourceRow = *(array.at(i)); QSurfaceDataRow &row = *(*m_resetArray)[i]; for (int j(0); j < newColumnCount; j++) row[j].setPosition(sourceRow.at(j).position()); } // Notify the proxy that data has changed series->dataProxy()->resetArray(m_resetArray); } }
If the i call "series->dataProxy()->resetArray(m_resetArray); " the audioouput stops and says
ALSA lib pcm.c:7843:(snd_pcm_recover) underrun occurredThis means that the audio thread isnt quick enought to write to the QAudioOutput buffer and play the sound.
Any idea?