Audio decoder example.
authorLev Zelenskiy <lev.zelenskiy@nokia.com>
Fri, 24 Feb 2012 03:00:35 +0000 (13:00 +1000)
committerQt by Nokia <qt-info@nokia.com>
Mon, 27 Feb 2012 04:26:51 +0000 (05:26 +0100)
Change-Id: I0f9fb755ef8bc6f66696a06996ac7a9f13de69cc
Reviewed-by: Michael Goddard <michael.goddard@nokia.com>
examples/audiodecoder/audiodecoder.cpp [new file with mode: 0644]
examples/audiodecoder/audiodecoder.h [new file with mode: 0644]
examples/audiodecoder/audiodecoder.pro [new file with mode: 0644]
examples/audiodecoder/main.cpp [new file with mode: 0644]
examples/audiodecoder/wavefilewriter.cpp [new file with mode: 0644]
examples/audiodecoder/wavefilewriter.h [new file with mode: 0644]

diff --git a/examples/audiodecoder/audiodecoder.cpp b/examples/audiodecoder/audiodecoder.cpp
new file mode 100644 (file)
index 0000000..7263a10
--- /dev/null
@@ -0,0 +1,188 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+**     the names of its contributors may be used to endorse or promote
+**     products derived from this software without specific prior written
+**     permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "audiodecoder.h"
+#include <iostream>
+
+AudioDecoder::AudioDecoder(bool isPlayback, bool isDelete)
+    : m_cout(stdout, QIODevice::WriteOnly)
+{
+    m_isPlayback = isPlayback;
+    m_isDelete = isDelete;
+
+    // Make sure the data we receive is in correct PCM format.
+    // Our wav file writer only supports SignedInt sample type.
+    QAudioFormat format;
+    format.setChannels(2);
+    format.setSampleSize(16);
+    format.setFrequency(48000);
+    format.setCodec("audio/pcm");
+    format.setSampleType(QAudioFormat::SignedInt);
+    m_decoder.setAudioFormat(format);
+
+    connect(&m_decoder, SIGNAL(bufferReady()), this, SLOT(bufferReady()));
+    connect(&m_decoder, SIGNAL(error(QAudioDecoder::Error)), this, SLOT(error(QAudioDecoder::Error)));
+    connect(&m_decoder, SIGNAL(stateChanged(QAudioDecoder::State)), this, SLOT(stateChanged(QAudioDecoder::State)));
+    connect(&m_decoder, SIGNAL(finished()), this, SLOT(finished()));
+    connect(&m_decoder, SIGNAL(positionChanged(qint64)), this, SLOT(updateProgress()));
+    connect(&m_decoder, SIGNAL(durationChanged(qint64)), this, SLOT(updateProgress()));
+
+    connect(&m_soundEffect, SIGNAL(statusChanged()), this, SLOT(playbackStatusChanged()));
+    connect(&m_soundEffect, SIGNAL(playingChanged()), this, SLOT(playingChanged()));
+
+    m_progress = -1.0;
+}
+
+void AudioDecoder::setSourceFilename(const QString &fileName)
+{
+    m_decoder.setSourceFilename(fileName);
+}
+
+void AudioDecoder::start()
+{
+    m_decoder.start();
+}
+
+void AudioDecoder::stop()
+{
+    m_decoder.stop();
+}
+
+void AudioDecoder::setTargetFilename(const QString &fileName)
+{
+    m_targetFilename = fileName;
+}
+
+void AudioDecoder::bufferReady()
+{
+    // read a buffer from audio decoder
+    QAudioBuffer buffer = m_decoder.read();
+    if (!buffer.isValid())
+        return;
+
+    if (!m_fileWriter.isOpen() && !m_fileWriter.open(m_targetFilename, buffer.format())) {
+        m_decoder.stop();
+        return;
+    }
+
+    m_fileWriter.write(buffer);
+}
+
+void AudioDecoder::error(QAudioDecoder::Error error)
+{
+    switch (error) {
+    case QAudioDecoder::NoError:
+        return;
+    case QAudioDecoder::ResourceError:
+        m_cout << "Resource error" << endl;
+        break;
+    case QAudioDecoder::FormatError:
+        m_cout << "Format error" << endl;
+        break;
+    case QAudioDecoder::AccessDeniedError:
+        m_cout << "Access denied error" << endl;
+        break;
+    case QAudioDecoder::ServiceMissingError:
+        m_cout << "Service missing error" << endl;
+        break;
+    }
+
+    emit done();
+}
+
+void AudioDecoder::stateChanged(QAudioDecoder::State newState)
+{
+    switch (newState) {
+    case QAudioDecoder::DecodingState:
+        m_cout << "Decoding..." << endl;
+        break;
+    case QAudioDecoder::StoppedState:
+        m_cout << "Decoding stopped" << endl;
+        break;
+    }
+}
+
+void AudioDecoder::finished()
+{
+    if (!m_fileWriter.close())
+        m_cout << "Failed to finilize output file" << endl;
+
+    m_cout << "Decoding finished" << endl;
+
+    if (m_isPlayback) {
+        m_cout << "Starting playback" << endl;
+        m_soundEffect.setSource(QUrl::fromLocalFile(m_targetFilename));
+        m_soundEffect.play();
+    } else {
+        emit done();
+    }
+}
+
+void AudioDecoder::playbackStatusChanged()
+{
+    if (m_soundEffect.status() == QSoundEffect::Error) {
+        m_cout << "Playback error" << endl;
+        emit done();
+    }
+}
+
+void AudioDecoder::playingChanged()
+{
+    if (!m_soundEffect.isPlaying()) {
+        m_cout << "Playback finished" << endl;
+        if (m_isDelete)
+            QFile::remove(m_targetFilename);
+        emit done();
+    }
+}
+
+void AudioDecoder::updateProgress()
+{
+    qint64 position = m_decoder.position();
+    qint64 duration = m_decoder.duration();
+    qreal progress = m_progress;
+    if (position >= 0 && duration > 0)
+        progress = position / (qreal)duration;
+
+    if (progress > m_progress + 0.1) {
+        m_cout << "Decoding progress: " << (int)(progress * 100.0) << "%" << endl;
+        m_progress = progress;
+    }
+}
diff --git a/examples/audiodecoder/audiodecoder.h b/examples/audiodecoder/audiodecoder.h
new file mode 100644 (file)
index 0000000..63dd823
--- /dev/null
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+**     the names of its contributors may be used to endorse or promote
+**     products derived from this software without specific prior written
+**     permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef AUDIODECODER_H
+#define AUDIODECODER_H
+
+#include <private/qaudiodecoder_p.h>
+#include "wavefilewriter.h"
+#include <QSoundEffect>
+#include <QTextStream>
+
+QT_USE_NAMESPACE
+
+class AudioDecoder : public QObject
+{
+    Q_OBJECT
+public:
+    AudioDecoder(bool isPlayback, bool isDelete);
+    ~AudioDecoder() { }
+
+    void setSourceFilename(const QString &fileName);
+    void start();
+    void stop();
+
+    void setTargetFilename(const QString &fileName);
+
+Q_SIGNALS:
+    void done();
+
+public slots:
+    void bufferReady();
+    void error(QAudioDecoder::Error error);
+    void stateChanged(QAudioDecoder::State newState);
+    void finished();
+
+    void playbackStatusChanged();
+    void playingChanged();
+
+private slots:
+    void updateProgress();
+
+private:
+    bool m_isPlayback;
+    bool m_isDelete;
+    QAudioDecoder m_decoder;
+    QTextStream m_cout;
+
+    QString m_targetFilename;
+    WaveFileWriter m_fileWriter;
+    QSoundEffect m_soundEffect;
+
+    qreal m_progress;
+};
+
+#endif // AUDIODECODER_H
diff --git a/examples/audiodecoder/audiodecoder.pro b/examples/audiodecoder/audiodecoder.pro
new file mode 100644 (file)
index 0000000..20e660b
--- /dev/null
@@ -0,0 +1,13 @@
+TEMPLATE = app
+TARGET = audiodecoder
+
+CONFIG += qt warn_on
+
+HEADERS = \
+    audiodecoder.h \
+    wavefilewriter.h
+SOURCES = main.cpp \
+    audiodecoder.cpp \
+    wavefilewriter.cpp
+
+QT+= multimedia multimedia-private console
diff --git a/examples/audiodecoder/main.cpp b/examples/audiodecoder/main.cpp
new file mode 100644 (file)
index 0000000..b4b1700
--- /dev/null
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+**     the names of its contributors may be used to endorse or promote
+**     products derived from this software without specific prior written
+**     permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QCoreApplication>
+#include <QTextStream>
+#include <iostream>
+
+#include <qfileinfo.h>
+#include <qdir.h>
+#include "audiodecoder.h"
+
+int main(int argc, char *argv[])
+{
+    QCoreApplication app(argc, argv);
+
+    QTextStream cout(stdout, QIODevice::WriteOnly);
+    if (app.arguments().size() < 2) {
+        cout << "Usage: audiodecoder [-p] [-pd] SOURCEFILE [TARGETFILE]" << endl;
+        cout << "Set -p option if you want to play output file." << endl;
+        cout << "Set -pd option if you want to play output file and delete it after successful playback." << endl;
+        cout << "Default TARGETFILE name is \"out.wav\" in the same directory as the source file." << endl;
+        return 0;
+    }
+
+    bool isPlayback = false;
+    bool isDelete = false;
+
+    if (app.arguments().at(1) == "-p")
+        isPlayback = true;
+    else if (app.arguments().at(1) == "-pd") {
+        isPlayback = true;
+        isDelete = true;
+    }
+
+    QFileInfo sourceFile;
+    QFileInfo targetFile;
+
+    int sourceFileIndex = (isPlayback || isDelete) ? 2 : 1;
+    if (app.arguments().size() <= sourceFileIndex) {
+        cout << "Error: source filename is not specified." << endl;
+        return 0;
+    }
+    sourceFile.setFile(app.arguments().at(sourceFileIndex));
+    if (app.arguments().size() > sourceFileIndex + 1)
+        targetFile.setFile(app.arguments().at(sourceFileIndex + 1));
+    else
+        targetFile.setFile(sourceFile.dir().absoluteFilePath("out.wav"));
+
+    AudioDecoder decoder(isPlayback, isDelete);
+    QObject::connect(&decoder, SIGNAL(done()), &app, SLOT(quit()));
+    decoder.setSourceFilename(sourceFile.absoluteFilePath());
+    decoder.setTargetFilename(targetFile.absoluteFilePath());
+    decoder.start();
+
+    return app.exec();
+};
diff --git a/examples/audiodecoder/wavefilewriter.cpp b/examples/audiodecoder/wavefilewriter.cpp
new file mode 100644 (file)
index 0000000..218e68d
--- /dev/null
@@ -0,0 +1,192 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+**     the names of its contributors may be used to endorse or promote
+**     products derived from this software without specific prior written
+**     permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "wavefilewriter.h"
+
+struct Q_PACKED chunk
+{
+    char        id[4];
+    quint32     size;
+};
+
+struct Q_PACKED RIFFHeader
+{
+    chunk       descriptor;     // "RIFF"
+    char        type[4];        // "WAVE"
+};
+
+struct Q_PACKED WAVEHeader
+{
+    chunk       descriptor;
+    quint16     audioFormat;
+    quint16     numChannels;
+    quint32     sampleRate;
+    quint32     byteRate;
+    quint16     blockAlign;
+    quint16     bitsPerSample;
+};
+
+struct Q_PACKED DATAHeader
+{
+    chunk       descriptor;
+};
+
+struct Q_PACKED CombinedHeader
+{
+    RIFFHeader  riff;
+    WAVEHeader  wave;
+    DATAHeader  data;
+};
+static const int HeaderLength = sizeof(CombinedHeader);
+
+
+WaveFileWriter::WaveFileWriter(QObject *parent)
+    : QObject(parent)
+    , m_dataLength(0)
+{
+}
+
+WaveFileWriter::~WaveFileWriter()
+{
+    close();
+}
+
+bool WaveFileWriter::open(const QString& fileName, const QAudioFormat& format)
+{
+    if (file.isOpen())
+        return false; // file already open
+
+    if (format.codec() != "audio/pcm" || format.sampleType() != QAudioFormat::SignedInt)
+        return false; // data format is not supported
+
+    file.setFileName(fileName);
+    if (!file.open(QIODevice::WriteOnly))
+        return false; // unable to open file for writing
+
+    if (!writeHeader(format))
+        return false;
+
+    m_format = format;
+    return true;
+}
+
+bool WaveFileWriter::write(const QAudioBuffer& buffer)
+{
+    if (buffer.format() != m_format)
+        return false; // buffer format has changed
+
+    qint64 written = file.write((const char*)buffer.constData(), buffer.byteCount());
+    m_dataLength += written;
+    return written == buffer.byteCount();
+}
+
+bool WaveFileWriter::close()
+{
+    bool result = false;
+    if (file.isOpen()) {
+        Q_ASSERT(m_dataLength < INT_MAX);
+        result = writeDataLength();
+
+        m_dataLength = 0;
+        file.close();
+    }
+    return result;
+}
+
+bool WaveFileWriter::writeHeader(const QAudioFormat& format)
+{
+    // check if format is supported
+    if (format.byteOrder() == QAudioFormat::BigEndian || format.sampleType() != QAudioFormat::SignedInt)
+        return false;
+
+    CombinedHeader header;
+    memset(&header, 0, HeaderLength);
+
+#ifndef Q_LITTLE_ENDIAN
+    // only implemented for LITTLE ENDIAN
+    return false;
+#else
+    // RIFF header
+    memcpy(header.riff.descriptor.id, "RIFF", 4);
+    header.riff.descriptor.size = 0; // this will be updated with correct duration:
+                                     // m_dataLength + HeaderLength - 8
+    // WAVE header
+    memcpy(header.riff.type, "WAVE", 4);
+    memcpy(header.wave.descriptor.id, "fmt ", 4);
+    header.wave.descriptor.size = quint32(16);
+    header.wave.audioFormat = quint16(1);
+    header.wave.numChannels = quint16(format.channels());
+    header.wave.sampleRate = quint32(format.sampleRate());
+    header.wave.byteRate = quint32(format.sampleRate() * format.channels() * format.sampleSize() / 8);
+    header.wave.blockAlign = quint16(format.channels() * format.sampleSize() / 8);
+    header.wave.bitsPerSample = quint16(format.sampleSize());
+
+    // DATA header
+    memcpy(header.data.descriptor.id,"data", 4);
+    header.data.descriptor.size = 0; // this will be updated with correct data length: m_dataLength
+
+    return (file.write(reinterpret_cast<const char *>(&header), HeaderLength) == HeaderLength);
+#endif
+}
+
+bool WaveFileWriter::writeDataLength()
+{
+#ifndef Q_LITTLE_ENDIAN
+    // only implemented for LITTLE ENDIAN
+    return false;
+#endif
+
+    if (file.isSequential())
+        return false;
+
+    // seek to RIFF header size, see header.riff.descriptor.size above
+    if (!file.seek(4))
+        return false;
+
+    quint32 length = m_dataLength + HeaderLength - 8;
+    if (file.write(reinterpret_cast<const char *>(&length), 4) != 4)
+        return false;
+
+    // seek to DATA header size, see header.data.descriptor.size above
+    if (!file.seek(40))
+        return false;
+
+    return file.write(reinterpret_cast<const char *>(&m_dataLength), 4) == 4;
+}
diff --git a/examples/audiodecoder/wavefilewriter.h b/examples/audiodecoder/wavefilewriter.h
new file mode 100644 (file)
index 0000000..a3c2df4
--- /dev/null
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+**     the names of its contributors may be used to endorse or promote
+**     products derived from this software without specific prior written
+**     permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef WAVEFILEWRITER_H
+#define WAVEFILEWRITER_H
+
+#include <QObject>
+#include <qfile.h>
+#include <qaudiobuffer.h>
+
+class WaveFileWriter : public QObject
+{
+    Q_OBJECT
+public:
+    explicit WaveFileWriter(QObject *parent = 0);
+    ~WaveFileWriter();
+
+    bool open(const QString& fileName, const QAudioFormat& format);
+    bool write(const QAudioBuffer& buffer);
+    bool close();
+    bool isOpen() const { return file.isOpen(); }
+
+private:
+    bool writeHeader(const QAudioFormat& format);
+    bool writeDataLength();
+
+    QFile file;
+    QAudioFormat m_format;
+    qint64 m_dataLength;
+};
+
+#endif // WAVEFILEWRITER_H