我在Qt论坛上发布了这个问题,但没有得到答案。这就是我在这里发布它的原因。
我想知道有没有办法在Qt同时录制和播放声音。我想录制麦克风的声音,同时我想在扬声器/耳机中播放声音。
有没有办法在Qt中这样做?或者我是否需要使用任何其他库?
如果解决方案是跨平台的(我需要覆盖windows,linux和mac),那将是很棒的。如果不可能,那么Linux解决方案就可以了。
我顺便使用Qt 4.7。
修改
我的最新实施是here。我创建了QIODevice的子类并重新实现了它的writeData和readData方法,以便可以使用循环缓冲区完成读写操作。我按照this suggestion完成了这项工作。此代码也不起作用,因为QAudioOutput实例面临Underrun Error
,根据this documentation表示 -
音频数据没有以足够快的速度馈送到音频设备
我已经应用了一个黑客来暂时解决这个问题。在outputStateChanged
方法中,我正在检查输出的状态是否已更改为IDLE
,如果有,我再次调用start()
方法,指定公共缓冲区。我不想将此作为一种永久的解决方案,因为它感觉真的很hacky,因为我在没有正确调查其原因的情况下吞下了一个错误。
我该怎么做才能解决这个问题?
我也尝试使用Phonon来解决这个问题,但是因为我对这个模块没有足够的了解而失败了。
答案 0 :(得分:9)
我对Qt不是很有经验,但我处理的是媒体,所以请原谅我,如果我的答案不是很具体,而是从更一般的角度来解决你的问题。
我查看了你的代码,我认为一般来说你的想法应该有效。我看到了一些问题:
writeData
方法似乎没有准备好处理缓冲区满状态。当循环缓冲区填充它时,它只会覆盖旧数据,并错误地继续递增currentBufferLength
变量。我认为正确的做法是更新readPosition
以跳过丢失的数据,并防止currentBufferLength
超过缓冲区大小。
你几乎同时开始作家和读者。相反,您应该启动编写器并填充循环缓冲区,然后启动阅读器。请记住,您将永远无法以零延迟进行录制和播放。至少你的延迟将是单个缓冲区写入的大小,但实际上你可能需要编写器提前几个缓冲区来避免打嗝。
您应该分别调试阅读器和编写器。仅设置编写器并验证是否定期写入循环缓冲区(首先按照我的建议修复溢出条件)。要进行调试,可以将缓冲区转储到文件中,然后在音频播放器(例如Audacity)中检查文件,或者可以使用printf调试来确保始终获取数据。然后只用读者做类似的事情。
最后的想法。调用readData
和writeData
方法的代码可能正在其他线程上运行,可能是两个不同的线程,一个用于读取器,另一个用于写入器。如果我的猜测是正确的,那么你的圆形结构就会出现很大的问题。您必须保护对确定读写位置和大小的变量的访问,否则您将有竞争条件。
答案 1 :(得分:2)
我不明白为什么使用您在评论中提到的课程会出现问题。两者都不仅限于使用文件。
从QIODevice
start()
方法返回QAudioInput
,并将其转换为start()
QAudioOutput
方法:
QIODevice *myDevice = myQAudioInput->start();
myQAudioOutput->start( myDevice );
答案 2 :(得分:2)
像这样启动输入和输出设备
m_output= m_audioOutput->start();
m_input = m_audioInput->start();
connect(m_input, SIGNAL(readyRead()), SLOT(readMore()));
并将输入样本写入readMore()
中的输出m_output->write(outdata, len);
请查看此文章了解更多信息 此示例应用程序在Qt中创建,将从麦克风录制并同时播放音频 http://www.codeproject.com/Articles/421287/Cross-Platform-Microphone-Audio-Processing-Utility
答案 3 :(得分:2)
下面是用QT5编写的代码,用于读取音频输入,麦克风,并将其放入64K循环缓冲区。一旦缓冲区有数据,它就会将其写入音频输出,PC上的扬声器。这是简单的代码,应该是熟悉声音设备的良好起点。请注意,此处声音输入和输出位于一个对象中,这可能会导致缓冲区问题。为此,它为输入和输出创建了一个单独的对象。 该程序有两个文件,第一个是qt配置文件(.pro),第二个是main.cpp文件。
#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;}
答案 4 :(得分:1)
您从启动QAudioInput获取QIOStream并使用它来创建Phonon :: MediaSource。然后在Phonon :: MediaSource和Phonon :: AudioOutput对象之间创建一条路径。有关Phonon::AudioOutput和Phonon::MediaSource的详细信息,请参阅结帐文档。