Tony
Tony

Reputation: 2483

Play audio data using QIODevice (Qt4.6 with VC++)

I'm working on playing audio from an audio stream using VC++ with the QtMultimedia library. Since I'm not too experienced with Qt's libraries I started by reading in a .wav file and writing it to a buffer:

ifstream wavFile;
char* file = "error_ex.wav";
wavFile.open( file, ios::binary );

After that, I used ifstream's .read() function and write all the data into a buffer. After the buffer is written it's sent off to the audio writer that prepares it for Qt:

QByteArray fData;

for( int i = 0; i < (int)data.size(); ++i )
{
    fData.push_back(data.at(i));
}

m_pBuffer->open(QIODevice::ReadWrite);
m_pBuffer->write( fData );

m_pBuffer->close();

(m_pBuffer is of type QBuffer)

Once the QBuffer is ready I attempt to play the buffer:

QIODevice* ioDevice = m_pAudioOut->start();
ioDevice->write( m_pBuffer->buffer() );

(m_pAudioOut is of type QAudioOutput)

This results in a small pop from the speakers and then it stops playing. Any ideas why?

Running Visual Studios 2008 on Windows XP SP2 using Qt library 4.6.3.

Upvotes: 11

Views: 8223

Answers (2)

Gareth Stockwell
Gareth Stockwell

Reputation: 3122

As Frank pointed out, if your requirement is simply to play audio data from a file, a higher-level API would do the job, and would simplify your application code. Phonon would be one option; alternatively, the QtMobility project provides the QMediaPlayer API for high-level use cases.

Given that the question is specifically about using QIODevice however, and that you mentioned that reading from a WAV file was just your intitial approach, I'll assume that you actually need a streaming API, i.e. one which allows the client to control the buffering, rather than handing over this control to a higher-level abstraction such as Phonon.

QAudioOutput can be used in two different modes, depending on which overload of start() is called:

  • "Pull mode": void QAudioOutput::start(QIODevice *)

    In this mode, QAudioOutput will pull data from the supplied QIODevice without further intervention from the client. It is a good choice if the QIODevice being used is one which is provided by Qt (e.g. QFile, QAbstractSocket etc).

  • "Push mode": QIODevice* QAudioOutput::start()

    In this mode, the QAudioOutput client must push mode to the audio device by calling QIODevice::write(). This will need to be done in a loop, something like:

    qint64 dataRemaining = ... // assign correct value here
    while (dataRemaining) {
        qint64 bytesWritten = audioOutput->write(buffer, dataRemaining);
        dataRemaining -= bytesWritten;
        buffer += bytesWritten;
        // Then wait for a short time
    }
    

    How the wait is implemented will depend on the context of your application - if audio is being written from a dedicated thread, it could simply sleep(). Alternatively, if audio is being written from the main thread, you will probably want the write to be triggered by a QTimer.

    Since you don't mention anything about using a loop around the write() calls in your app, it looks like what is happening is that you write a short segment of data (which plays as a pop), then don't write any more.

You can see code using both modes in the examples/multimedia/audiooutput app which is delivered with Qt.

Upvotes: 12

Frank Osterfeld
Frank Osterfeld

Reputation: 25165

Are you sure you use the right (high-level) API? It would be weird if you had to handle data streams and buffering manually. Also, QIODevice::write() doesn't necessarily write the whole buffer but might stop after n bytes, just like POSIX write() (that's why one always should check the return value).

I didn't look into QtMultimedia yet, but using the more mature Phonon, video and audio output worked just fine for me in the past. It works like this:

  1. Create a Phonon::AudioOutput object
  2. Create a Phonon::MediaObject object
  3. Phonon::createPath( mediaObject, audioObject )
  4. mediaObject->setCurrentSource( Phonon::MediaSource( path ) );
  5. mediaObject->play();

There are also examples in Qt.

Upvotes: 2

Related Questions