[英]Qt - how to record and play sound simultaneously
我在Qt論壇上發布了這個問題,但沒有得到答案。 這就是我在這里發布它的原因。
我想知道有沒有辦法在Qt同時錄制和播放聲音。 我想錄制麥克風的聲音,同時我想在揚聲器/耳機中播放聲音。
在Qt有什么辦法嗎? 或者我需要使用任何其他庫嗎?
如果解決方案是跨平台的(我需要覆蓋windows,linux和mac),那將是很棒的。 如果不可能,那么Linux解決方案就可以了。
我順便使用Qt 4.7。
編輯
我的最新實施在這里給出。 我創建了QIODevice的子類並重新實現了writeData和readData方法,以便可以使用循環緩沖區進行讀寫。 我按照這個建議做了這個。 此代碼也不起作用,因為QAudioOutput實例面臨Underrun Underrun Error
,根據此文檔意味着 -
音頻數據沒有以足夠快的速率饋送到音頻設備
我已經應用了一個黑客來暫時解決這個問題。 在outputStateChanged
方法中,我正在檢查輸出的狀態是否已更改為IDLE
,如果有,我再次調用start()
方法,指定公共緩沖區。 我不想將此作為永久解決方案,因為它感覺非常hacky,因為我在沒有正確調查其原因的情況下吞下錯誤。
我該怎么做才能解決這個問題?
我也試圖用Phonon來解決這個問題,但是因為我對這個模塊沒有足夠的了解而失敗了。
我對Qt不是很有經驗,但我處理媒體,所以請原諒我,如果我的答案不是很具體,而是從更一般的角度來解決你的問題。
我查看了你的代碼,我認為一般來說你的想法應該有效。 我看到了一些問題:
writeData
方法似乎沒有准備好處理緩沖區滿狀態。 當循環緩沖區填充它時,它只會覆蓋舊數據,並錯誤地繼續增加currentBufferLength
變量。 我認為正確的做法是更新readPosition
以跳過丟失的數據,並防止currentBufferLength
超過緩沖區大小。
你幾乎在同一時間開始作家和讀者。 相反,您應該啟動編寫器並填充循環緩沖區,然后啟動閱讀器。 請記住,您將永遠無法以零延遲進行錄制和播放。 至少你的延遲將是單個緩沖區寫入的大小,但實際上你可能需要編寫器提前幾個緩沖區來避免打嗝。
您應該分別調試閱讀器和編寫器。 僅設置編寫器並驗證是否定期寫入循環緩沖區(首先按照我的建議修復溢出條件)。 要進行調試,可以將緩沖區轉儲到文件中,然后在音頻播放器(例如Audacity)中檢查文件,或者可以使用printf調試來確保始終獲取數據。 然后只用一個讀者做類似的事情。
最后的想法。 調用readData
和writeData
方法的代碼可能在其他線程上運行,可能是兩個不同的線程,一個用於讀取器,另一個用於writer。 如果我的猜測是正確的,那么你的圓形結構就會出現很大的問題。 您必須保護對確定讀寫位置和大小的變量的訪問,否則您將有競爭條件。
祝好運。
像這樣啟動輸入和輸出設備
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配置文件(.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;}
我不明白為什么使用您在評論中提到的課程會出現問題。 兩者都不僅限於使用文件。
采取QIODevice
從返回start()
方法QAudioInput
,並給它的start()
的方法QAudioOutput
:
QIODevice *myDevice = myQAudioInput->start();
myQAudioOutput->start( myDevice );
您可以從啟動QAudioInput獲取QIOStream並使用它來創建Phonon :: MediaSource。 然后在Phonon :: MediaSource和Phonon :: AudioOutput對象之間創建一條路徑。 有關Phonon :: AudioOutput和Phonon :: MediaSource的詳細信息,請參閱結帳文檔。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.