Qt -如何记录和播放声音同时
Qt - how to record and play sound simultaneously
我在Qt论坛上发布了这个问题,但是没有得到答案。这就是为什么我把它贴在这里。
我想知道是否有任何方法可以在Qt中同时录制和播放声音。我想从麦克风中录制声音,同时我想在扬声器/耳机中播放它。
有什么办法做到这一点在Qt?或者我需要使用其他库吗?
如果解决方案是跨平台的(我需要覆盖windows, linux和mac),那将是伟大的。如果不可能,那么linux的解决方案也可以。
我正在使用Qt 4.7。
编辑
我的最新实现在这里给出。我已经创建了QIODevice的子类,并重新实现了它的writeData和readData方法,以便读写可以用循环缓冲区完成。我是按照这个建议做的。这段代码也不能工作,因为QAudioOutput实例面对的是Underrun Error
,根据本文档的意思是-
音频数据没有以足够快的速度馈送到音频设备
我已经应用了一个hack来暂时解决这个问题。在outputStateChanged
方法中,我检查输出的状态是否已经更改为IDLE
,如果已经更改,我再次调用start()
方法,指定公共缓冲区。我不想把它作为一个永久的解决方案,因为它感觉真的很粗糙,因为我在没有正确调查其原因的情况下就接受了一个错误。
我该怎么做才能解决这个问题?
我也尝试过用Phonon来解决这个问题,但是因为我对这个模块没有足够的了解而失败了。
我对Qt不是很有经验,但我处理媒体,所以原谅我,如果我的回答不是很具体,而是从更一般的角度解决你的问题。
我看了你的代码,我认为大体上你的想法应该是可行的。我看到了一些问题:
-
writeData
方法似乎没有准备好处理缓冲区满的情况。当循环缓冲区填满时,它只会覆盖旧数据,并错误地继续增加currentBufferLength
变量。我认为这里要做的正确的事情是更新readPosition
以跳过丢失的数据,并防止currentBufferLength
增长超过缓冲区大小。 -
你几乎同时启动了作者和读者。相反,您应该启动写入器并启动循环缓冲区,然后启动读取器。请记住,您永远不可能在零延迟的情况下录制和播放。至少,您的延迟将是单个缓冲区写入的大小,但在实践中,您可能需要写入器提前几个缓冲区以避免打嗝。
-
应该分别调试读取器和写入器。只设置写入器并验证循环缓冲区是否定期写入(首先修复我上面建议的溢出条件)。要进行调试,您可以将缓冲区转储到一个文件中,然后在音频播放器(例如Audacity)中检查该文件,或者您可以使用printf调试来确保不断获取数据。然后用阅读器做类似的事情。
-
最后的想法。调用
readData
和writeData
方法的代码可能在其他线程上运行,可能是两个不同的线程,一个用于读取器,另一个用于写入器。如果我猜对了,那么你的圆形结构就有大问题了。您必须保护对决定读写位置和大小的变量的访问,否则您将出现竞争条件。
好运。
我不明白为什么使用你在评论中提到的类会有问题。两者都不局限于仅使用文件。
将QAudioInput
的start()
方法返回的QIODevice
赋给QAudioOutput
的start()
方法:
QIODevice *myDevice = myQAudioInput->start();
myQAudioOutput->start( myDevice );
像这样启动输入输出设备
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
下面是用QT5编写的代码,用于读取音频输入,麦克风,并将其放入64K循环缓冲区中。一旦缓冲区有了数据,它就把数据写入音频输出,即PC上的扬声器。这是基本代码,应该是熟悉声音设备的一个很好的起点。注意,这里的声音输入和输出在一个对象中,这可能会导致缓冲区问题。为了克服这个问题,为输入和输出创建一个单独的对象。该程序在两个文件中,第一个是qt profile (.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;}
您从启动QAudioInput并使用它来创建Phonon::MediaSource获得的QIOStream。然后在Phonon::MediaSource和Phonon::AudioOutput对象之间创建一个路径。有关详细信息,请查看Phonon::AudioOutput和Phonon::MediaSource的文档。
- 如何在C++中播放声音?
- 有没有办法毫不拖延地播放声音?
- 在不打开应用程序的情况下在MinGW c++中播放声音(.wav)文件
- 如何在QT中飞溅之前播放声音?
- 计时器达到零时如何播放声音?
- 使用MinGW播放声音
- 如何从McIsendstring()中解决此错误并使其播放声音
- 如何在 c++ 中播放声音
- 视觉 在C++中正确使用播放声音功能
- 切换音频设备时播放声音的SDL问题
- 有没有一种简单的方法可以使用SFML在后台线程中播放声音
- 如何使用7.1音频系统sfml播放声音
- 在做其他事情时,在C 中播放声音
- 当Windows XP(32位,SP3)播放声音时,随机应用程序暂停半秒钟
- 在 DirectX Windows 8 应用中播放声音
- C 确定是否播放声音
- 播放 C/C++ 声音的最简单方法
- 无法使用适用于Android的Qt使手机播放声音或振动
- 与CocosDenshion一起按顺序播放声音
- Qt -如何记录和播放声音同时