Generating a sound wave according to a signal
-
Dear Qt users,
I have a sensor measuring vibrations at a rate of 1000Hz. I would like to be able to reproduce those vibrations on the audio output of my computer with Qt, in real time (also at 1000Hz). Over the last few weeks, I have been struggling with this and found no solution so far.
For example, what I would like to achieve is to be able to specify, at each millisecond, the amplitude of the audio signal that has to be sent to the audio output. The audio output (e.g. speakers) would then react also at each millisecond.
I have tried to do that with QtMultimedia and QAudioOutput, using the pulse-code modulation codec (pcm), but so far I have not been able to succeed.
Anyone has an idea of what I could do to solve this?
-
Hi, and welcome to the Qt Dev Net!
QAudioOutput with PCM is the correct class for your use case. Please post your code so that we can pinpoint the issue.
-
Hello JKSH!
Thank you for your quick reply, it is much appreciated.
Ok, so you said QAudioOutput with the PCM codec is the right way to attack this problem. Just to be sure I was clear in my previous message, let me explain another time... I have a sensor generating data at each millisecond. As soon as one data comes in, I want to generate an amplitude at my audio output. I want to repeat this cycle each and every time a new data comes in (at each millisecond).
Ok, so here's what I have at the moment:
In my header:
@
...
public:
uint16_t *audio_outputBuffer;
uint16_t *tempAudioBuffer;
QAudioOutput *audio_outputStream;
QIODevice *audio_outputDevice;
QAudioFormat format;
public slots:
void GeneratePulseCodeModulationSignal(double DynData);
@In the class' Constructor:
@
CommunicationHandler::CommunicationHandler(VisualizationForm *visualiationWindow, MainWindow *mainwnd)
{
...
// Set up the format
format.setSampleRate(1000);
format.setChannelCount(1);
format.setSampleSize(16);
format.setCodec("audio/pcm");
format.setSampleType(QAudioFormat::SignedInt);
format.setByteOrder(QAudioFormat::LittleEndian);
audio_outputBuffer = (uint16_t *) malloc((1000) * 4);
// Create audio output stream, set up signals
audio_outputStream = new QAudioOutput(format);
...
}
@Finally, the method:
@
void CommunicationHandler::GeneratePulseCodeModulationSignal(double DynData)
{
qDebug() << "This is DynData we just have received: " << DynData;
tempAudioBuffer = audio_outputBuffer;*tempAudioBuffer = ((uint16_t) (DynData) & 0xFFFF); audio_outputDevice = audio_outputStream->start(); qDebug() << audio_outputStream->periodSize(); if(audio_outputDevice->isOpen() && audio_outputStream->state() != QAudio::StoppedState) { bytesOfAudioWrittenToDevice = audio_outputDevice->write((char *) audio_outputBuffer,1); }
}
@So, this code doesn't work first of all because QAudioFormat won't allow a sample rate of 1000 (1 millisecond period). And even if it was accepting that rate, I'm not even sure the concept is correct...
Do you see any way to achieve this?
-
Hi jproberge,
[quote author="jproberge" date="1407959424"]I have a sensor generating data at each millisecond. As soon as one data comes in, I want to generate an amplitude at my audio output. I want to repeat this cycle each and every time a new data comes in (at each millisecond).[/quote]Your audio driver won't read one sample at a time. Instead, it reads a chunk of a few thousand samples at a time, and then it will interpolate your data to create electric signals.
As a result, you will also have a bit of latency.
[quote author="jproberge" date="1407959424"]So, this code doesn't work first of all because QAudioFormat won't allow a sample rate of 1000 (1 millisecond period).[/quote]Sound cards don't support a sample rate of 1000 Hz. The minimum standard sample rate (for audio) is 8000 Hz.
You can do is set your QAudioFormat to accept 8000 Hz, and then interpolate your data. (Every time you receive 1 sample, write 8 samples to audio_outputDevice)
Anyway, what is the maximum frequency of your vibrations? If you sample at 1000 Hz, the maximum frequency that you can measure is 500 Hz.
[quote author="jproberge" date="1407959424"]
Finally, the method:@
void CommunicationHandler::GeneratePulseCodeModulationSignal(double DynData)
{
tempAudioBuffer = audio_outputBuffer;*tempAudioBuffer = ((uint16_t) (DynData) & 0xFFFF); ... bytesOfAudioWrittenToDevice = audio_outputDevice->write((char *) audio_outputBuffer,1);
}
@
[/quote]Why do you write to audio_outputBuffer and then copy the data into audio_outputDevice? Just get rid of audio_outputBuffer and write directly into audio_outputDevice.[quote author="jproberge" date="1407959424"]
@
void CommunicationHandler::GeneratePulseCodeModulationSignal(double DynData)
{
...
audio_outputDevice = audio_outputStream->start();
...
}
@
[/quote]Don't start() the output every time you receive data!Call start() once. Then, acquire and write your data many times. Then, call stop() when you have finished.