C++ Qt-如何同时录制和播放声音

C++ Qt-如何同时录制和播放声音,c++,qt4,C++,Qt4,我在Qt论坛上发布了这个问题,但没有得到答案。这就是为什么我把它贴在这里 我想知道有没有办法在Qt中同时录制和播放声音。我想用麦克风录制声音,同时用扬声器/耳机播放 在Qt中有什么方法可以做到这一点吗?或者我需要使用其他图书馆吗 如果解决方案是跨平台的(我需要介绍windows、linux和mac),那就太好了。如果不可能,那么linux解决方案就可以了 顺便说一下,我正在使用Qt4.7 编辑 给出了我的最新实现。我已经创建了的一个子类,并重新实现了它的和方法,以便可以使用循环缓冲区进行读写。我

我在Qt论坛上发布了这个问题,但没有得到答案。这就是为什么我把它贴在这里

我想知道有没有办法在Qt中同时录制和播放声音。我想用麦克风录制声音,同时用扬声器/耳机播放

在Qt中有什么方法可以做到这一点吗?或者我需要使用其他图书馆吗

如果解决方案是跨平台的(我需要介绍windows、linux和mac),那就太好了。如果不可能,那么linux解决方案就可以了

顺便说一下,我正在使用Qt4.7

编辑

给出了我的最新实现。我已经创建了的一个子类,并重新实现了它的和方法,以便可以使用循环缓冲区进行读写。我是按照你的要求做的。此代码也不起作用,因为实例面临
欠运行错误
,根据平均值-

音频数据没有以足够快的速率馈送到音频设备

我已经应用了一种黑客技术暂时解决了这个问题。在
outputStateChanged
方法中,我检查输出的状态是否已更改为
IDLE
,如果已更改,我将再次调用
start()
方法,指定公共缓冲区。我不想把它作为一个永久性的解决方案,因为它让我感觉很不舒服,因为我没有正确地调查它的原因,就吞下了一个错误

我该怎么解决这个问题


我也尝试用QAudioInput解决这个问题,但失败了,因为我对这个模块没有足够的了解。

您使用启动QAudioInput得到的QIOStream,并使用它创建一个Phonon::MediaSource。然后在该Phonon::MediaSource和Phonon::AudioOutput对象之间创建一条路径。有关更多详细信息,请查看和的文档。

我不明白为什么使用您在评论中提到的类会出现问题。两者都不限于仅使用文件

获取从
qaudioput
start()
方法返回的
QIODevice
,并将其交给
qaudioput
start()
方法:

QIODevice *myDevice = myQAudioInput->start();
myQAudioOutput->start( myDevice ); 

我对Qt不是很有经验,但我是处理媒体的,所以如果我的回答不是很具体,而是从更一般的角度解决您的问题,请原谅我

我看了你的代码,我认为总体上你的想法应该行得通。不过,我看到了一些问题:

  • writeData
    方法似乎没有准备好处理缓冲区已满的情况。当循环缓冲区填满时,它只会覆盖旧数据,并错误地继续增加
    currentBufferLength
    变量。我认为正确的做法是更新
    readPosition
    跳过丢失的数据,并防止
    currentBufferLength
    超过缓冲区大小

  • 你几乎同时启动了作者和读者。相反,您应该启动writer并初始化循环缓冲区,然后启动reader。请记住,您将永远无法以零延迟录制和播放。至少您的延迟将是单个缓冲区写入的大小,但在实践中,您可能需要写入程序领先几个缓冲区以避免打嗝

  • 您应该分别调试读写器。只设置writer,并验证循环缓冲区是否以固定的间隔写入(首先按照我上面的建议修复溢出情况)。要进行调试,可以将缓冲区转储到文件中,然后在音频播放器中检查该文件(例如Audacity),也可以使用printf调试来确保不断获取数据。然后只对一个读者做类似的事情

  • 最后的想法。调用
    readData
    writeData
    方法的代码可能正在其他线程上运行,可能是两个不同的线程,一个用于读卡器,另一个用于写卡器。如果我的猜测是正确的,那么你的圆形结构有一个大问题。您必须保护对决定读写位置和大小的变量的访问,否则您将有竞争条件


祝你好运。

像这样启动输入和输出设备

m_output= m_audioOutput->start();
    m_input = m_audioInput->start();
    connect(m_input, SIGNAL(readyRead()), SLOT(readMore()));
并将输入样本写入readMore()中的输出

有关更多信息,请参阅本文。
此示例应用程序是在Qt中创建的,它将从麦克风录制并同时播放音频
下面是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;}
QT5.2.1的AudioEcho.pro文件 QT+=核心 QT-=gui QT+=多媒体小部件 目标=音频回波 配置+=控制台 配置-=应用程序包 模板=应用程序 SOURCES+=main.cpp //main.cpp文件 #包括 #包括 #包括 #包括 #包括 myAudio类:公共设备 { //Q_对象 公众: 音频输出*音频输出; QAudioInput*音频输入; myAudio(); ~myAudio(){} void fillBuffer(); QAudioFormat formatIn,formatOut; QByteArray buff; char*pbuff; quint64 RXbuff; 五级甲状旁腺激素受体; 受保护的: qint64读取数据(字符*数据,qint64最大值); qint64写入数据(常量字符*数据,qint64 len); qint64字节可用()常量; }; #定义采样率22050 #定义通道1 #定义样本大小16 #定义签名的样本类型
#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;}