关于QT QAudioOutput push模式问题

在MAC上基于QT和ffmpeg实现最简单播放器,在完成声音播放模块后导致无法正常播放,QAudioOutput start后state仍是idel,将QAudioOutput buffer写满后就不能继续写数据了,导致播放被卡死.

QAudioOutput创建代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
QAudioFormat fmt;
fmt.setSampleRate(this->sampleRate);
fmt.setSampleSize(this->sampleSize);
fmt.setChannelCount(this->channel);
fmt.setCodec("audio/pcm");
fmt.setByteOrder(QAudioFormat::LittleEndian);
fmt.setSampleType(QAudioFormat::UnSignedInt);
QAudioDeviceInfo info = QAudioDeviceInfo::defaultOutputDevice();
printf("deviceName:%s\n",qPrintable(info.deviceName()));
if (!info.isFormatSupported(fmt)) {
printf("default format not supported try to use nearest\n");
fmt = info.nearestFormat(fmt);
}
output = new QAudioOutput(fmt);
//connect(output, SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged(QAudio::State)));
io = output->start();
printf("io:%p\n", io);
//printf("state:%s",output->state());
QAudio::State stat = output->state();

在stackoverflow Qt QAudioOutput push mode看到别人的问题和解释,发现是setSampleType引起的,代码在window上可以正常跑,只是在mac上失败.

问题

I’ve got a question about using QAudioOutput to directly write samples at a specific sample rate to the sound output device. I’m writing an emulator that emualates sound chips on a per-frame basis, and then gets a buffer containing a frame’s worth of audio samples, which I would like to write to the audio output. Currently, to test my audio output routine, I allocate a huge (5 minute) buffer to put random numbers into, like so:

Header:

1
2
3
4
uint16_t *audio_outputBuffer;
uint32_t audio_bytesRemainingToRead;
QAudioOutput *audio_outputStream;
QIODevice *audio_outputDevice;

Implementation:

1
2
3
4
5
6
7
8
9
10
audio_outputBuffer = (uint16_t *) malloc((96000 * 4) * 300);
int i = 0;

uint16_t *tempAudioBuffer = audio_outputBuffer;
for(i = 0; i < ((96000 * 4) * 150); i++) {
*tempAudioBuffer = (uint16_t) rand() & 0xFFFF;
tempAudioBuffer++;
}

audio_bytesRemainingToRead = (96000 * 4) * 300;

Next, I set up my audio device with some basic parameters:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Set up the format
QAudioFormat format;
format.setFrequency(96000); // Usually this is specified through an UI option
format.setChannels(2);
format.setSampleSize(16);
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::UnSignedInt);

// There's code here to notify the user of inability to match the format and choose an action, which is omitted for clarity

// Create audio output stream, set up signals
audio_outputStream = new QAudioOutput(format, this);

connect(audio_outputStream, SIGNAL(stateChanged(QAudio::State)), this, SLOT(audio_stateChanged(QAudio::State)));

audio_outputDevice = audio_outputStream->start();

Then, in my timer tick routine, which is called by a QTimer at 60 FPS, I do the following code to write a ‘chunk’ of audio data to the QAudioOutput’s buffer:

1
2
3
4
5
6
if(audio_outputDevice->isOpen() && audio_outputStream->state() != QAudio::StoppedState) {
qint64 bytesOfAudioWrittenToDevice = audio_outputDevice->write((char *) audio_outputBuffer, audio_outputStream->periodSize());
audio_bytesRemainingToRead -= bytesOfAudioWrittenToDevice;
qDebug() << "Wrote" << bytesOfAudioWrittenToDevice << "bytes of audio to output device. Remaining bytes to read:" << audio_bytesRemainingToRead;
qDebug() << "Free bytes in audio output:" << audio_outputStream->bytesFree();
}

Once I start the audio output process, I get the following output on the console:

1
2
3
4
5
6
7
8
9
10
Current audio state: 3 Error: 0
Wrote 2048 bytes of audio to output device. Remaining bytes to read: 115197952
Free bytes in audio output: 6144
Current audio state: 0 Error: 0
Wrote 2048 bytes of audio to output device. Remaining bytes to read: 115195904
Free bytes in audio output: 4096
Wrote 2048 bytes of audio to output device. Remaining bytes to read: 115193856
Free bytes in audio output: 2048
Wrote 2048 bytes of audio to output device. Remaining bytes to read: 115191808
Free bytes in audio output: 0 (This and the above line is repeated forever)

To me, it looks like QAudioOutput isn’t flushing it’s internal buffer to the sound card, which goes along with the entire “no sound coming out of my computer” thing.

What would cause this issue, and how could I fix it?

(By the way, I’m compiling my code against Qt 4.8.1, on Mac OS X 10.7.4.)

Thanks for any answers.

解释

Upfront just wanna point out: This is not a Qt bug. Why? The answer is that in the WAV spec’, 8-bit samples are always unsigned, whereas 16-bit samples are always signed. Any other combination does not work. This is device related, the framework can not do anything about it.

So this will not work because you have set 16 bit sample size and unsigned integer format. And yes, the solution is: you have to set the sample type to signed for 16-bit resolution:

1
format.setSampleType(QAudioFormat::SignedInt);

Inversely for 8-bit samples you would have to put:

1
format.setSampleType(QAudioFormat:UnsignedInt);

Also this very similar question (same problem but with 8-bit) shows you that it is not a particular problem of using signed or unsigned samples in Qt, but that it is the combination of samples size and type that matters (for the audio device, not for Qt ;) QAudioOutput in Qt5 is not producing any sound

IMHO the fact that Qt does not take care of handling these cases by forcing the correct format is a flaw but not a lack in functionnality.

You can learn more about this in the notes section of this page: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/

I’ve figured this out — apparently Qt has issues with UNSIGNED samples. If you set the sample type to signed, everything works fine, regardless of platform.

坚持原创技术分享,您的支持将鼓励我继续创作!