使用QAudioOutput进行实时流式传输

时间:2017-12-27 04:45:47

标签: c++ qt audio

我正在开发一个C ++项目,用麦克风阵列系统读取/处理/播放原始音频,并使用自己的C ++ API。我正在使用Qt对软件进行编程。

在这篇关于Real Time Streaming With QAudioOutput (Qt)的帖子中,我想跟进并询问有关如果原始音频数据来自一个需要大约1000毫秒(1秒)处理的函数调用该怎么办的建议?我怎样才能实现实时音频播放。

处理需要大约一秒钟,因为我在写入QIODevice :: QAudioFormat-> start()时已经读过了建议使用句点值的字节来防止缓冲区欠载/溢出。 http://cell0907.blogspot.sg/2012/10/qt-audio-output.html

我已经设置了QByteArray和QDataStream来传输从函数调用接收的数据。

  • API是CcmXXX()
  • 从麦克风阵列读取数据将返回32位整数数组
  • 在32位整数中,24位分辨率,8位LSB填充零。
  • 它有块大小(设置为1024个样本)x 40个麦克风
  • 每个块写入一个块,直到写入的字节数接近周期大小/空闲字节数。

经测试:将我的插槽连接到大约50ms的通知,写入一个周期的字节。 QByteArray采用循环缓冲区样式。在读/写部分添加了互斥锁定/解锁。

结果:播放的实际音频的分割时间非常短,很多抖动和未录制的声音。

请提供有关如何改进代码的反馈。

设置QAudioFormat

void MainWindow::init_audio_format(){
            m_format.setSampleRate(48000); //(8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, 96000, 192000
            m_format.setByteOrder(QAudioFormat::LittleEndian);
            m_format.setChannelCount(1);
            m_format.setCodec("audio/pcm");
            m_format.setSampleSize(32); //(8, 16, 24, 32, 48, 64)
            m_format.setSampleType(QAudioFormat::SignedInt); //(SignedInt, UnSignedInt, Float)

            m_device = QAudioDeviceInfo::defaultOutputDevice();
            QAudioDeviceInfo info(m_device);
            if (!info.isFormatSupported(m_format)) {
                qWarning() << "Raw audio format not supported by backend, cannot play audio.";
                return;
            }
}

初始化音频和QByteArray / Datastream

void MainWindow::init_audio_output(){   
    m_bytearray.resize(65536);
    mstream = new QDataStream(&m_bytearray,QIODevice::ReadWrite);
    mstream->setByteOrder(QDataStream::LittleEndian);
    audio = new QAudioOutput(m_device,m_format,this);
    audio->setBufferSize(131072);
    audio->setNotifyInterval(50);
    m_audiodevice = audio->start();
    connect(audio,SIGNAL(notify()),this,SLOT(slot_writedata()));
    read_frames();
}

void MainWindow::slot_writedata(){
    QMutex mutex;
    mutex.lock();
    read_frames();
    mutex.unlock();
}

阅读相框:

    void MainWindow::read_frames(){
        qint32* buffer;
        int frameSize, byteCount=0;
        DWORD tdFrames, fdFrames;
        float fvalue = 0;
        qint32 q32value;

        frameSize = 40 * mBlockSize; //40 mics 
        buffer = new int[frameSize];
        int periodBytes = audio->periodSize();
        int freeBytes = audio->bytesFree();
        int chunks = qMin(periodBytes/mBlockSize,freeBytes/mBlockSize);

        CcmStartInput();

        while(chunks){
            CcmReadFrames(buffer,NULL,frameSize,0,&tdFrames,&fdFrames,NULL,CCM_WAIT);
            if(tdFrames==0){
                break;
            }
            int diffBytes = periodBytes - byteCount;

            if(diffBytes>=(int)sizeof(q32value)*mBlockSize){
                for(int x=0;x<mBlockSize;x++){
                    q32value = (quint32)buffer[x]/256;
                   *mstream << (qint32)fvalue;
                    byteCount+=sizeof(q32value);
                }
            }
            else{
                for(int x=0;x<(diffBytes/(int)sizeof(q32value));x++){
                    q32value = (quint32)buffer[x]/256;
                    *mstream << (qint32) fvalue;
                    byteCount+=sizeof(q32value);
                }
            }
            --chunks;
        }
        CcmStopInput();
        mPosEnd = mPos + byteCount;
        write_frames();
        mPos += byteCount;
        if(mPos >= m_bytearray.length()){
              mPos = 0;
              mstream->device()->seek(0); //change mstream pointer back to bytearray start
        }
    } 

要编写框架:

void MainWindow::write_frames()
{
    int len = m_bytearray.length() - mPos;
    int bytesWritten = mPosEnd - mPos;

    if(len>=audio->periodSize()){
        m_audiodevice->write(m_bytearray.data()+mPos, bytesWritten);
    }
    else{

        w_data.replace(0,qAbs(len),m_bytearray.data()+mPos);
        w_data.replace(qAbs(len),audio->periodSize()-abs(len),m_bytearray.data());
       m_audiodevice->write(w_data.data(),audio->periodSize());
    }
}

1 个答案:

答案 0 :(得分:4)

Qt中的音频支持实际上非常简陋。目标是以尽可能低的实施和维护成本进行媒体播放。 Windows的情况尤其糟糕,我认为古老的MME API仍然用于音频播放。

因此,Qt音频API远非实时,因此特别不适合此类应用。我建议使用portaudio或rtaudio,如果愿意,你仍然可以将其包装在Qt风格的IO设备中。这将使您能够以非常低的延迟访问性能更佳的平台音频API和更好的播放性能。