5
votes

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:

uint16_t *audio_outputBuffer;
uint32_t audio_bytesRemainingToRead;
QAudioOutput *audio_outputStream;
QIODevice *audio_outputDevice;

Implementation:

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:

// 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:

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:

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.

2
What is the question in one sentence? I read the question but couldn't 'get it'.sashoalm
@sashoalm I've revised the post to reflect a simple one-sentence question. Hopefully that's easier to understand.Tristan

2 Answers

8
votes

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:

format.setSampleType(QAudioFormat::SignedInt);

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

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/

1
votes

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.