簡體   English   中英

Qt - 如何同時錄制和播放聲音

[英]Qt - how to record and play sound simultaneously

我在Qt論壇上發布了這個問題,但沒有得到答案。 這就是我在這里發布它的原因。

我想知道有沒有辦法在Qt同時錄制和播放聲音。 我想錄制麥克風的聲音,同時我想在揚聲器/耳機中播放聲音。

在Qt有什么辦法嗎? 或者我需要使用任何其他庫嗎?

如果解決方案是跨平台的(我需要覆蓋windows,linux和mac),那將是很棒的。 如果不可能,那么Linux解決方案就可以了。

我順便使用Qt 4.7。

編輯

我的最新實施在這里給出。 我創建了QIODevice的子類並重新實現了writeDatareadData方法,以便可以使用循環緩沖區進行讀寫。 我按照這個建議做了這個。 此代碼也不起作用,因為QAudioOutput實例面臨Underrun Underrun Error ,根據此文檔意味着 -

音頻數據沒有以足夠快的速率饋送到音頻設備

我已經應用了一個黑客來暫時解決這個問題。 outputStateChanged方法中,我正在檢查輸出的狀態是否已更改為IDLE ,如果有,我再次調用start()方法,指定公共緩沖區。 我不想將此作為永久解決方案,因為它感覺非常hacky,因為我在沒有正確調查其原因的情況下吞下錯誤。

我該怎么做才能解決這個問題?

我也試圖用Phonon來解決這個問題,但是因為我對這個模塊沒有足夠的了解而失敗了。

我對Qt不是很有經驗,但我處理媒體,所以請原諒我,如果我的答案不是很具體,而是從更一般的角度來解決你的問題。

我查看了你的代碼,我認為一般來說你的想法應該有效。 我看到了一些問題:

  • writeData方法似乎沒有准備好處理緩沖區滿狀態。 當循環緩沖區填充它時,它只會覆蓋舊數據,並錯誤地繼續增加currentBufferLength變量。 我認為正確的做法是更新readPosition以跳過丟失的數據,並防止currentBufferLength超過緩沖區大小。

  • 你幾乎在同一時間開始作家和讀者。 相反,您應該啟動編寫器並填充循環緩沖區,然后啟動閱讀器。 請記住,您將永遠無法以零延遲進行錄制和播放。 至少你的延遲將是單個緩沖區寫入的大小,但實際上你可能需要編寫器提前幾個緩沖區來避免打嗝。

  • 您應該分別調試閱讀器和編寫器。 僅設置編寫器並驗證是否定期寫入循環緩沖區(首先按照我的建議修復溢出條件)。 要進行調試,可以將緩沖區轉儲到文件中,然后在音頻播放器(例如Audacity)中檢查文件,或者可以使用printf調試來確保始終獲取數據。 然后只用一個讀者做類似的事情。

  • 最后的想法。 調用readDatawriteData方法的代碼可能在其他線程上運行,可能是兩個不同的線程,一個用於讀取器,另一個用於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 :: AudioOutputPhonon :: MediaSource的詳細信息,請參閱結帳文檔。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM