问题
I posted this question on the Qt forum, but got no answers. That's why I am posting it here.
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?
It would be great if the solution is cross-platform (I need to cover windows, linux and mac). If it isn't possible, then a linux solution will do.
I am using Qt 4.7 by the way.
Edit
My latest implementation is given here. I have created a sub-class of the QIODevice and re-implemented its writeData and readData method so that reading and writing can be done with a circular buffer. I have done this as per this suggestion. This code also doesn't work because the QAudioOutput instance faces Underrun Error
, which according to this documentation means -
Audio data is not being fed to the audio device at a fast enough rate
I have applied a hack to solve this problem temporarily. In the outputStateChanged
method, I am checking to see if the state of the output has changed to IDLE
and if it has, I am again calling start()
method, specifying the common buffer. I don't want to use this as a permanent solution because it feels really hacky and because I am swallowing an error without properly investigating its reasons.
What should I do to solve this problem?
I also tried to solve this using Phonon but failed because I do not have sufficient knowledge of this module.
回答1:
I'm not very experienced with Qt, but I am with handling media, so forgive me if my answer isn't very specific but instead addresses your problem from a more general point of view.
I looked at your code, and I think in general your idea should work. I see some problems though:
the
writeData
method doesn't seem to be prepared to handle a buffer full condition. When the circular buffer fills it'll just overwrite old data, and incorrectly continue to increment thecurrentBufferLength
variable. I think the correct thing to do here is to update thereadPosition
to skip over the data that was lost, and to preventcurrentBufferLength
from ever growing past the buffer size.You are starting both the writer and the reader at pretty much the same time. Instead, you should start the writer and prime the circular buffer, then start the reader. Keep in mind that you will never be able to record and play with zero latency. At the very least your latency will be the size of an individual buffer write, but in practice you'll probably need the writer to be ahead by a few buffers to avoid hiccups.
You should debug the reader and the writer separately. Set up only the writer and verify that the circular buffer is getting written to at regular intervals (first fix the overflow condition as I suggested above). To debug, you can dump the buffers to a file and then check the file in an audio player (Audacity, for example), or you can use printf debugging to ensure you are constantly getting data. Then do something similar with only a reader.
Final thought. The code that calls your
readData
andwriteData
methods is probably running on other threads, likely two different threads, one for the reader and another one for the writer. If my guess is correct, then you have a big problem with your circular structure. You have to protect access to the variables that determine the read and write positions and sizes, if not you will have race conditions.
Good luck.
回答2:
I don't see why there would be a problem using the classes you mention in your comment. Neither are restricted to just using files.
Take the QIODevice
returned from the start()
method of QAudioInput
and give it to the start()
method of QAudioOutput
:
QIODevice *myDevice = myQAudioInput->start();
myQAudioOutput->start( myDevice );
回答3:
Start the input and output device like this
m_output= m_audioOutput->start();
m_input = m_audioInput->start();
connect(m_input, SIGNAL(readyRead()), SLOT(readMore()));
and write the input sample to output in readMore()
m_output->write(outdata, len);
Please look at this article for more.
This sample application is created in Qt will record from microphone and play audio simultaneously
http://www.codeproject.com/Articles/421287/Cross-Platform-Microphone-Audio-Processing-Utility
回答4:
Below is the code written in QT5 to read the audio input, the microphone, and places it into a 64K circular buffer. Once the buffer has data it writes it to audio output, the speaker on the PC. This is the bare bones code that should be a good starting point for getting familiar with the sound device. Note, that here the sound input and output are in one object this may cause buffer problems. To over come this create a separate objects for the input and output. The program is in two file, the first being qt profile (.pro) and the second being the main.cpp file.
#AudioEcho.pro file for QT5.2.1
QT += core
QT -= gui
QT += multimedia widgets
TARGET = AudioEcho
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp
//main.cpp file
#include <QDebug>
#include <QIODevice>
#include <QAudioInput>
#include <QAudioOutput>
#include <QCoreApplication>
class myAudio :public QIODevice
{
// Q_OBJECT
public:
QAudioOutput *audioOut;
QAudioInput *audioIn;
myAudio();
~myAudio(){}
void fillBuffer();
QAudioFormat formatIn,formatOut;
QByteArray buff;
char *pbuff;
quint64 RXbuff;
quint64 buffPtr;
protected:
qint64 readData(char *data, qint64 maxlen);
qint64 writeData(const char *data, qint64 len);
qint64 bytesAvailable() const;
};
#define SAMPLE_RATE 22050
#define CHANNELS 1
#define SAMPLE_SIZE 16
#define SAMPLE_TYPE SignedInt
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
myAudio *m= new myAudio();
return a.exec();
}
myAudio::myAudio()
{
formatIn.setSampleRate(SAMPLE_RATE);
formatIn.setChannelCount(CHANNELS);
formatIn.setSampleSize(SAMPLE_SIZE);
formatIn.setCodec("audio/pcm");
formatIn.setByteOrder(QAudioFormat::LittleEndian);
formatIn.setSampleType(QAudioFormat::SAMPLE_TYPE);
formatOut.setSampleRate(SAMPLE_RATE);
formatOut.setChannelCount(CHANNELS);
formatOut.setSampleSize(SAMPLE_SIZE);
formatOut.setCodec("audio/pcm");
formatOut.setByteOrder(QAudioFormat::LittleEndian);
formatOut.setSampleType(QAudioFormat::SAMPLE_TYPE);
//print out the output device setup parameters
QAudioDeviceInfo deviceOut(QAudioDeviceInfo::availableDevices(QAudio::AudioOutput).at(0)); //select output device 0
qDebug()<<"Selected Output device ="<<deviceOut.deviceName();
//print out the input device setup parameters
QAudioDeviceInfo deviceIn(QAudioDeviceInfo::availableDevices(QAudio::AudioInput).at(0)); //select output device 0
qDebug()<<"Selected input device ="<<deviceIn.deviceName();
//configure device
audioOut = new QAudioOutput(deviceOut,formatOut,0);
audioIn = new QAudioInput (deviceIn, formatIn,0);
//print out the device specifications
foreach(const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioInput))
{
qDebug() << "\nSuported Input devices";
qDebug() << "\nDevice name: " << deviceInfo.deviceName();
qDebug() << "Supported channel count: " << deviceInfo.supportedChannelCounts();
qDebug() << "Supported Codec: " << deviceInfo.supportedCodecs();
qDebug() << "Supported byte order: " << deviceInfo.supportedByteOrders();
qDebug() << "Supported Sample Rate: " << deviceInfo.supportedSampleRates();
qDebug() << "Supported Sample Size: " << deviceInfo.supportedSampleSizes();
qDebug() << "Supported Sample Type: " << deviceInfo.supportedSampleTypes();
qDebug() << "Preferred Device settings:" << deviceInfo.preferredFormat();
}
foreach(const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))
{
qDebug() << "\nSuported output devices";
qDebug() << "Device name: " << deviceInfo.deviceName();
qDebug() << "Supported channel count: " << deviceInfo.supportedChannelCounts();
qDebug() << "Supported Codec: " << deviceInfo.supportedCodecs();
qDebug() << "Supported byte order: " << deviceInfo.supportedByteOrders();
qDebug() << "Supported Sample Rate: " << deviceInfo.supportedSampleRates();
qDebug() << "Supported Sample Size: " << deviceInfo.supportedSampleSizes();
qDebug() << "Supported Sample Type: " << deviceInfo.supportedSampleTypes();
qDebug() << "Preferred Device settings:" << deviceInfo.preferredFormat();
}
buff.resize(0x10000); //create a rx buffer
pbuff=buff.data(); //get the buff address;
RXbuff=0; //set RX buffer pointer
qDebug()<<"File open"<<open(QIODevice::ReadWrite);
qDebug()<<"is device Sequential="<<isSequential();
audioIn->start(this); //start reading device
audioOut->setVolume(0.5); //volume 0 to 1.0
audioOut->start(this); //start writing to device
}
//QIODevice Class (Protected Functions)This function is called by QIODevice.
//send to output(Speaker)
qint64 myAudio::readData(char *data, qint64 len)
{
static quint64 TXbuff=0;
qint64 total = 0;
while (len > total && RXbuff>TXbuff)//write and synchonise buffers
{
//write data to speaker
memcpy(&data[total],&pbuff[TXbuff%0x10000],2); //copy 2 Bytes
TXbuff+=2; //point to next buffer 16 bit location
total+=2;
}
return total; //the reset interval
}
//audio input (from Microphone)
qint64 myAudio::writeData(const char *data, qint64 len)
{
int total=0;
while (len > total)
{
memcpy(&pbuff[RXbuff%0x10000],&data[total], 2); //write 2Bytes into circular buffer(64K)
RXbuff+=2; //next 16bit buffer location
total+=2; //next data location
}
return (total); //return total number of bytes received
}
qint64 myAudio::bytesAvailable() const{return 0;}
回答5:
You take the QIOStream that you get from starting the QAudioInput and use it to create a Phonon::MediaSource. Then you create a path between that Phonon::MediaSource and a Phonon::AudioOutput object. For more details checkout documentation for Phonon::AudioOutput and Phonon::MediaSource.
来源:https://stackoverflow.com/questions/7776022/qt-how-to-record-and-play-sound-simultaneously