使用QAudioOutput(qt)实时流式传输

tru*_*ru7 5 c++ audio streaming qt low-latency

我想播放实时响应,没有明显的用户交互延迟.

为了获得低延迟,我必须发送一小块pcm数据.我在做什么:

    QAudioFormat format;
    format.setSampleRate(22050);
    format.setChannelCount(1);
    format.setSampleSize(16);
    format.setCodec("audio/pcm");
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::SignedInt);

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

    qAudioOutput = new QAudioOutput(format, NULL);

    qAudioDevice=qAudioOutput->start();
Run Code Online (Sandbox Code Playgroud)

然后

void Enqueue(TYPESAMPLEPCM *data,int countBytes){
    while(qAudioOutput->bytesFree()<countBytes){
          Sleep(1);
    }
    qAudioDevice->write((char *)data,countBytes);
}
Run Code Online (Sandbox Code Playgroud)

数据块是256字节(128个样本,它们的"粒度"约为6毫秒.

从具有高优先级的线程中的循环调用Enqueue,该线程提供数据块.它没有延迟,因为它所谓的Enqueue速度远远快于渲染音频数据.

但它看起来有一个缓冲不足的情况,因为声音播放,但有一种"噼里啪啦"的常规噪音.

如果我将块大小提高到256个样本,问题几乎消失了.开头只有一些噼啪声(?)

该平台是Windows和Qt 5.3.

这是正确的程序还是我遗漏了什么?

UmN*_*obe 7

问题是关于

void Enqueue(TYPESAMPLEPCM *data,int countBytes){
    while(qAudioOutput->bytesFree()<countBytes){
          Sleep(1);
    }
    qAudioDevice->write((char *)data,countBytes);
}
Run Code Online (Sandbox Code Playgroud)

有点天真.

首先,Sleep(1);.你在窗户上.问题是windows不是实时操作系统,并且预计会有大约10 - 15ms 的时间分辨率.这意味着当没有传入音频的地方时,你会比你想象的要睡得多.

第二.当音频输出不能消耗所提供的数据量时,你真的需要睡觉吗?你真正想要的是在音频输出消耗一些后提供一些音频.具体而言,它意味着:

  1. 设置QAudioOutput通知间隔,即系统将消耗音频数据并告诉您的时间段.
  2. 获取有关QAudioOutput使用某些数据的通知.哪个是连接到的插槽QAudioOutput::notify()
  3. 当音频输出已满时,缓冲来自高优先级线程的数据块.

这给:

QByteArray samplebuffer;

//init code
{
     qAudioOutput = new QAudioOutput(format, NULL);
     ...
     qAudioOutput->setNotifyInterval(128); //play with this number
     connect(qAudioOutput, SIGNAL(notify), someobject, SLOT(OnAudioNotify));
     ...
     qAudioDevice=qAudioOutput->start();
}

void EnqueueLock(TYPESAMPLEPCM *data,int countBytes)
{
    //lock mutex
    samplebuffer.append((char *)data,countBytes);
    tryWritingSomeSampleData();
    //unlock mutex
}

//slot
void SomeClass::OnAudioNotify()
{
   //lock mutex
   tryWritingSomeSampleData()
   //unlock mutex
}

void SomeClass::tryWritingSomeSampleData()
{
    int towritedevice = min(qAudioOutput->bytesFree(), samplebuffer.size());
    if(towritedevice > 0)
    {
        qAudioDevice->write(samplebuffer.data(),towritedevice);
        samplebuffer.remove(0,towritedevice); //pop front what is written
    }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,您需要防止samplebuffer并发访问.提供足够的互斥量.