How to record and play sound simultaneously
-
Hi, I wanted to know is there any way to record and play sound at the same time in Qt. I want to record sound from a microphone and at the same time I want to play it in the speaker/headphone.
Is there any way to do this in Qt? Or do I need to use any other library?
-
This is what I have tried so far -
@void AudioRecorder::on_btnStart_clicked()
{
// Get audio format and search for nearest matching format of the device
QAudioFormat format = this->getFormat();// Star recording using the specified format this->audioInput = new QAudioInput(format, this); connect(this->audioInput,SIGNAL(stateChanged(QAudio::State)), SLOT(inputStateChanged(QAudio::State))); QIODevice *myBuffer = this->audioInput->start(); this->audioOutput = new QAudioOutput(format, this); connect(this->audioOutput,SIGNAL(stateChanged(QAudio::State)), SLOT(outputStateChanged(QAudio::State))); this->audioOutput->start(myBuffer);
}
QAudioFormat AudioRecorder::getFormat()
{
QAudioFormat format;
format.setFrequency(8000);
format.setChannels(1);
format.setSampleSize(8);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::UnSignedInt);QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice(); if (!info.isFormatSupported(format)) { this->log("Default format not supported. Trying to use the nearest format....."); format = info.nearestFormat(format); } return format;
}@
This code works fine for some time after I start recording i.e., I talk in the microphone and I can hear my voice on the headphones, but after some time the audioOutput changes its state to IDLE because of UnderrunError, which according to "this":http://doc.qt.nokia.com/stable/qaudio.html#Error-enum documentation means -
bq. Audio data is not being fed to the audio device at a fast enough rate
What should I do to solve this problem?
-
Yes, it is possible to record and output audio simultaneously in Qt. You might take a look at http://developer.qt.nokia.com/forums/viewthread/7679/ . However, I guess that (for Windows) the latency you can achieve with Qt is not the best...
-
I wonder if it is possible to use a QIODevice directly, without subclassing it, as the documentation says:
bq. QIODevice is abstract and can not be instantiated, but it is common to use the interface it defines to provide device-independent I/O features
You might consider creating your own QIODevice subclass to implement fast read and write methods by using a circular buffer. I did that some time ago and never experienced underruns, even for high-quality settings (48000Hz etc.).
Which OS are you using? -
[quote author="vidar" date="1318837148"]However, I guess that (for Windows) the latency you can achieve with Qt is not the best...[/quote]
Then which library will give me the best latency and performance? I would really like to know. Is it cross-platform?
-
You generally need an ASIO sound card to get proper latency on windows. However, if you don't have an asio enabled sound card you can use "ASIO4ALL":http://www.asio4all.com, which has given decent results for me in the past on cheap hardware with applications like Cubase VST.
-
@Franzk: No, you don't need ASIO for lowest latency on Windows. WASAPI provides great latencies as well (in Exclusive Mode) and you don't need to cope with ASIO4ALL etc.
@sayem.bd: A library called "portaudio" will be the solution you want. It supports WASAPI and ASIO on Windows and also supports ALSA / Linux and MacOS stuff.
You can compile the portaudio to a library and use it in your Qt project.
"See":http://www.portaudio.com/(P.S. If you use WASAPI, be sure to use the synchronous API functions, due to a Windows bug...)
-
I have sub-classed QIODevice and re-implemented the writeData and readData method. The code is given "here":http://pastebin.com/n7e9KREA. The problem is, I am still running into the Underrun error. I have applied a hack in these scenario, you can check it in the outputStateChanged method. Whenever I am running into idle state, I am forcefully starting it again. As a result, now my audio plays nicely, but I am not sure if this is recommended approach since I am simply swallowing an error.
Why am I still getting Underrun Error? Is there anything wrong in my QIODevice subclass implementation or is there any other mechanism?
Please note that my input and output both devices are using the same audio format.
-
The code looks good and I assume that your MAXSIZE is large enough to buffer the latency between the start of the recording and the start of the output. Hmm, well ... Do the Qt audio demos work on your system? I guess your application does not perform any processing that might block the read/write methods?
( Otherwise you could try out portaudio, which works fine with Qt projects. It's not that different from the audio API that Qt uses and it already handles that buffering stuff internally (though I must admit that its API appears to be more complex).
After you tried out some console applications using portaudio, you can try migrating your code into your Qt project by adding the portaudio library to the LIBS setting in your Qt project file. The portaudio part should maybe be done in a second thread.... ) -
I have run the Spectrum Analyzer example in my machine, and it's running nicely. I am not doing any extra processing that might block the read/write methods (I guess). This is a pretty simple GUI application which has a window and a button and after pressing that button I am initializing the audio input/output and calling the start methods on them (you can see it from the code that I have posted there).
However, I was debugging the code today, with breakpoints set to the first statements of both the writeData and readData methods and found that sometimes after a write has occured, two consecutive read is occurring. Suppose that writeData method wrote X bytes of data into the buffer. then immediately after that a read is occurring, reading those X bytes of data from the buffer. After this read, the currentBufferLength obviously turns to zero. After this read, another read is again happening before a write could occur. Since in the second read the buffer length is zero so the read method is simply returning zero from the first statement. I think this is the reason why the output device is running into the underrun error.
This event is totally random but is occurring frequently. I don't know what's the reason behind this and how to stop these random consecutive reads.
@vidar: Thanks for bearing with me this far, by the way :-) .
-
@vidar: If you are on stackoverflow, then you can post your answer there. I have asked this question there with a bounty of 100 reputations, and your answer is the one which helped me most. I think you deserve this bounty :-) .
The question can be found "here":http://stackoverflow.com/questions/7776022/.