Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. [SOLVED] How to form Wav header ?

[SOLVED] How to form Wav header ?

Scheduled Pinned Locked Moved General and Desktop
13 Posts 6 Posters 16.5k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • A Offline
    A Offline
    Anticross
    wrote on 18 Jun 2011, 13:07 last edited by
    #3

    I want to open this file by right codec. So as I know the wav file has all needed information(like bitrate, num of chanels, freeq, sample rate, codec etc) in its header after this header goes data. So the task is to add header is start of file and then write data.

    1 Reply Last reply
    0
    • A Offline
      A Offline
      alexisdm
      wrote on 18 Jun 2011, 13:43 last edited by
      #4

      You could write the header, put 0 instead of the data size, write the audio data.
      When you are done recording, use QFile::seek to go back where the sizes should be and write them.

      Qt Mobility has some code in their test suite to write the wav header for PCM audio:
      http://qt.gitorious.org/qt-mobility/qt-mobility/blobs/master/tests/auto/qaudiooutput/
      But their function writeDataLength (to write the size after the audio data length is known) seems to be missing some code to write the RIFF header length (fileSize - 8) at position 4.

      1 Reply Last reply
      0
      • A Offline
        A Offline
        Anticross
        wrote on 20 Jun 2011, 06:18 last edited by
        #5

        So, here what I done:
        Creating file here...
        @void AudioOutput::createAudioOutput(AudioBuffer * audioBuffer)
        {
        m_audioBuffer = audioBuffer;//new AudioBuffer(m_settings,this);
        m_audioOutput = new QAudioOutput(m_settings,this);

        QString fileName = QString("C:/AudiofromStation(%1,%2,%3).wav")
        .arg(m_audioOutput->format().frequency())
        .arg(m_audioOutput->format().channelCount())
        .arg(m_audioOutput->format().sampleSize());

        m_File = new QFile(fileName);
        m_File->open(QIODevice::WriteOnly);

        }@
        Start playing...
        @void AudioOutput::start() {
        m_audioBuffer->start();
        m_File->open(QIODevice::Append);
        m_output = m_audioOutput->start();
        }@
        Writing data to file and playing it on audio device...
        @void AudioOutput::write( const char *data, qint64 len )
        {
        if (m_audioBuffer)
        {
        m_audioBuffer->writeData(data, len);
        m_File->write(data, len);
        }
        }
        @
        or
        @
        void AudioOutput::write(const QByteArray & array) {
        if (m_audioBuffer)
        {
        m_audioBuffer->writeData(array,sizeof(array));
        m_File->write(array,sizeof(array));
        }
        }@
        Stop playing and recording, and calling write header function...
        @void AudioOutput::stop()
        {
        m_audioOutput->stop();
        writeWavHeader(m_File);
        m_File->close();
        // m_audioBuffer->clear();
        // m_audioBuffer->stop();
        m_output = NULL;
        }@
        And write header function copied from here :http://qt.gitorious.org/qt-mobility/qt-mobility/blobs/master/tests/auto/qaudiooutput/
        @void AudioOutput::writeWavHeader( QFile * file )
        {
        QAudioFormat format = m_audioOutput->format();
        qint64 dataLength = file->size() - HeaderLength;
        CombinedHeader header;

        memset(&header, 0, HeaderLength);

        // RIFF header
        if (format.byteOrder() == QAudioFormat::LittleEndian)
        memcpy(header.riff.descriptor.id,"RIFF",4);
        else
        memcpy(header.riff.descriptor.id,"RIFX",4);
        qToLittleEndian<quint32>(quint32(dataLength + HeaderLength - 8),
        reinterpret_cast<unsigned char*>(&header.riff.descriptor.size));
        memcpy(header.riff.type, "WAVE",4);

        // WAVE header
        memcpy(header.wave.descriptor.id,"fmt ",4);
        qToLittleEndian<quint32>(quint32(16),
        reinterpret_cast<unsigned char*>(&header.wave.descriptor.size));
        qToLittleEndian<quint16>(quint16(1),
        reinterpret_cast<unsigned char*>(&header.wave.audioFormat));
        qToLittleEndian<quint16>(quint16(format.channels()),
        reinterpret_cast<unsigned char*>(&header.wave.numChannels));
        qToLittleEndian<quint32>(quint32(format.frequency()),
        reinterpret_cast<unsigned char*>(&header.wave.sampleRate));
        qToLittleEndian<quint32>(quint32(format.frequency() * format.channels() * format.sampleSize() / 8),
        reinterpret_cast<unsigned char*>(&header.wave.byteRate));
        qToLittleEndian<quint16>(quint16(format.channels() * format.sampleSize() / 8),
        reinterpret_cast<unsigned char*>(&header.wave.blockAlign));
        qToLittleEndian<quint16>(quint16(format.sampleSize()),
        reinterpret_cast<unsigned char*>(&header.wave.bitsPerSample));

        // DATA header
        memcpy(header.data.descriptor.id,"data",4);
        qToLittleEndian<quint32>(quint32(dataLength),
        reinterpret_cast<unsigned char*>(&header.data.descriptor.size));

        file->write(reinterpret_cast<const char *>(&header), HeaderLength);

        }@

        But I can't play it with the help of Winamp or WindowsMedia player. So thats mean that the header is in wrong format or I done somthing wrong. When I open my file in audio editor with right codec and settings it's looks like I have no header at start, but have some addition data at end. (so, maybe it's my header ?).

        1 Reply Last reply
        0
        • A Offline
          A Offline
          alexisdm
          wrote on 20 Jun 2011, 06:57 last edited by
          #6

          Why are you using QAudioOutput ?

          You should write the header in the file before starting the recording, and go back at the beginning of the file after ending the recording to edit the 2 length fields.

          Or you could derive from QFile, and reimplement open and close to do just that. And pass that object to QAudioInput::start(QIODevice*):
          @class WavPcmFile : public QFile {
          public:
          WavPcmFile(const QString & name, const QAudioFormat & format, QObject *parent = 0);
          bool open();
          void close();

          private:
          void writeHeader();
          bool hasSupportedFormat();
          QAudioFormat format;
          };@

          @
          WavPcmFile::WavPcmFile(const QString & name, const QAudioFormat & format_, QObject *parent_)
          : QFile(name, parent_), format(format_)
          {
          }

          bool WavPcmFile::hasSupportedFormat()
          {
          return (format.sampleSize() == 8
          && format.sampleType() == QAudioFormat::UnSignedInt)
          || (format.sampleSize() > 8
          && format.sampleType() == QAudioFormat::SignedInt
          && format.byteOrder() == QAudioFormat::LittleEndian);
          }

          bool WavPcmFile::open()
          {
          if (!hasSupportedFormat()) {
          setErrorString("Wav PCM supports only 8-bit unsigned samples "
          "or 16-bit (or more) signed samples (in little endian)");
          return false;
          } else {
          if (!QFile::open(ReadWrite | Truncate))
          return false;
          writeHeader();
          return true;
          }
          }

          void WavPcmFile::writeHeader()
          {
          QDataStream out(this);
          out.setByteOrder(QDataStream::LittleEndian);

          // RIFF chunk
          out.writeRawData("RIFF", 4);
          out << quint32(0); // Placeholder for the RIFF chunk size (filled by close())
          out.writeRawData("WAVE", 4);

          // Format description chunk
          out.writeRawData("fmt ", 4);
          out << quint32(16); // "fmt " chunk size (always 16 for PCM)
          out << quint16(1); // data format (1 => PCM)
          out << quint16(format.channelCount());
          out << quint32(format.sampleRate());
          out << quint32(format.sampleRate() * format.channelCount()
          * format.sampleSize() / 8 ); // bytes per second
          out << quint16(format.channelCount() * format.sampleSize() / 8); // Block align
          out << quint16(format.sampleSize()); // Significant Bits Per Sample

          // Data chunk
          out.writeRawData("data", 4);
          out << quint32(0); // Placeholder for the data chunk size (filled by close())

          Q_ASSERT(pos() == 44); // Must be 44 for WAV PCM
          }

          void WavPcmFile::close()
          {
          // Fill the header size placeholders
          quint32 fileSize = size();

          QDataStream out(this);
          // Set the same ByteOrder like in writeHeader()
          out.setByteOrder(QDataStream::LittleEndian);
          // RIFF chunk size
          seek(4);
          out << quint32(fileSize - 8);

          // data chunk size
          seek(40);
          out << quint32(fileSize - 44);

          QFile::close();
          }@

          And you use the class like this:
          @// Starts the recording
          WavPcmFile *m_file = new WavPcmFile("Filename.wav", m_audioInput->format(), this);
          if(m_file.open()) {
          m_audioInput->start(m_file);
          } else {
          // Error
          }

          // Stops the recording
          m_audioInput->stop();
          m_file->close();
          @

          Edit: Fixed the close() byte order error as indicated by tiho_d.

          kybernetesK 1 Reply Last reply 20 Sept 2015, 10:01
          0
          • A Offline
            A Offline
            Anticross
            wrote on 20 Jun 2011, 07:12 last edited by
            #7

            It's solved by adding m_File->write("0x55",44); in this code:

            @void AudioOutput::createAudioOutput(AudioBuffer * audioBuffer)
            {
            m_audioBuffer = audioBuffer;//new AudioBuffer(m_settings,this);
            m_audioOutput = new QAudioOutput(m_settings,this);

            QString fileName = QString("C:/AudiofromStation(%1,%2,%3).wav")
            .arg(m_audioOutput->format().frequency())
            .arg(m_audioOutput->format().channelCount())
            .arg(m_audioOutput->format().sampleSize());

            m_File = new QFile(fileName);
            m_File->open(QIODevice::WriteOnly);
            m_File->write("0x55",44); // Here we fill 0x55 our for reserving place for future header

            connect(m_audioOutput,SIGNAL(notify()),SLOT(status()));
            connect(m_audioOutput,SIGNAL(stateChanged(QAudio::State)),SLOT(state(QAudio::State)));
            }@

            And here I do seek to 0 and write header:
            @void AudioOutput::stop()
            {
            m_audioOutput->stop();
            m_File->seek(0);
            writeWavHeader(m_File);
            m_File->close();
            // m_audioBuffer->clear();
            // m_audioBuffer->stop();
            m_output = NULL;
            }@

            Thanks everyone.

            1 Reply Last reply
            0
            • A Offline
              A Offline
              alexisdm
              wrote on 20 Jun 2011, 10:19 last edited by
              #8

              @
              m_File->write("0x55",44); // Here we fill 0x55 our for reserving place for future header
              @
              The string "0x55" isn't 44 bytes long, so it will probably crash randomly at some point...

              You can instead create a buffer with QByteArray:
              @
              m_File->write(QByteArray(44, '\x55'));
              @

              1 Reply Last reply
              0
              • A Offline
                A Offline
                Anticross
                wrote on 11 Jul 2011, 08:47 last edited by
                #9

                Thanks, alexisdm now It's works without crashes.

                1 Reply Last reply
                0
                • T Offline
                  T Offline
                  tiho_d
                  wrote on 24 Mar 2013, 16:34 last edited by
                  #10

                  Hi guys. I used alexisdm's code and it worked beautifully. Thank You, alexisdm.
                  I found a bug inside the WavPcmFile::close() function though. You need to set the correct ByteOrder on the out stream inside WavPcmFile::close() function too, just as you set inside the WavPcmFile::writeHeader() function. If you don't set the right ByteOrder the written value is not the expected one. Here comes the fixed close() function for completeness:
                  @
                  void WavPcmFile::close()
                  {
                  // Fill the header size placeholders
                  quint32 fileSize = size();

                  QDataStream out(this);
                  // Set the same ByteOrder like in writeHeader()
                  out.setByteOrder(QDataStream::LittleEndian);
                  // RIFF chunk size
                  seek(4);
                  out << quint32(fileSize - 8);

                  // data chunk size
                  seek(40);
                  out << quint32(fileSize - 44);

                  QFile::close();
                  }
                  @
                  Thanks again for your code sample alexisdm!

                  1 Reply Last reply
                  0
                  • P Offline
                    P Offline
                    prabhatjha
                    wrote on 12 Mar 2014, 07:56 last edited by
                    #11

                    Hey, i did recording from microphone using qt in .wav file.
                    i am able to play this file in media playe.but when i added this file in matlab for wavread this is giving a error invalid chunk size .....
                    and throgh matlab code i fixed the chunk size of my .wav file .
                    the matlab code is given below....
                    function wavchunksizefix( test13)
                    d = dir('test13.wav');
                    fileSize = d.bytes
                    fid=fopen('test13.wav','r+','l');
                    fseek(fid,4,-1);
                    fwrite(fid,fileSize-8,'uint32');
                    fseek(fid,40,-1);
                    fwrite(fid,fileSize-44,'uint32');
                    fclose(fid);

                    but i want to fix this problem throgh qt code.
                    plzzz give me some hint.

                    1 Reply Last reply
                    0
                    • P Offline
                      P Offline
                      prabhatjha
                      wrote on 12 Mar 2014, 07:58 last edited by
                      #12

                      [[blank-post-content-placeholder]]

                      1 Reply Last reply
                      0
                      • A alexisdm
                        20 Jun 2011, 06:57

                        Why are you using QAudioOutput ?

                        You should write the header in the file before starting the recording, and go back at the beginning of the file after ending the recording to edit the 2 length fields.

                        Or you could derive from QFile, and reimplement open and close to do just that. And pass that object to QAudioInput::start(QIODevice*):
                        @class WavPcmFile : public QFile {
                        public:
                        WavPcmFile(const QString & name, const QAudioFormat & format, QObject *parent = 0);
                        bool open();
                        void close();

                        private:
                        void writeHeader();
                        bool hasSupportedFormat();
                        QAudioFormat format;
                        };@

                        @
                        WavPcmFile::WavPcmFile(const QString & name, const QAudioFormat & format_, QObject *parent_)
                        : QFile(name, parent_), format(format_)
                        {
                        }

                        bool WavPcmFile::hasSupportedFormat()
                        {
                        return (format.sampleSize() == 8
                        && format.sampleType() == QAudioFormat::UnSignedInt)
                        || (format.sampleSize() > 8
                        && format.sampleType() == QAudioFormat::SignedInt
                        && format.byteOrder() == QAudioFormat::LittleEndian);
                        }

                        bool WavPcmFile::open()
                        {
                        if (!hasSupportedFormat()) {
                        setErrorString("Wav PCM supports only 8-bit unsigned samples "
                        "or 16-bit (or more) signed samples (in little endian)");
                        return false;
                        } else {
                        if (!QFile::open(ReadWrite | Truncate))
                        return false;
                        writeHeader();
                        return true;
                        }
                        }

                        void WavPcmFile::writeHeader()
                        {
                        QDataStream out(this);
                        out.setByteOrder(QDataStream::LittleEndian);

                        // RIFF chunk
                        out.writeRawData("RIFF", 4);
                        out << quint32(0); // Placeholder for the RIFF chunk size (filled by close())
                        out.writeRawData("WAVE", 4);

                        // Format description chunk
                        out.writeRawData("fmt ", 4);
                        out << quint32(16); // "fmt " chunk size (always 16 for PCM)
                        out << quint16(1); // data format (1 => PCM)
                        out << quint16(format.channelCount());
                        out << quint32(format.sampleRate());
                        out << quint32(format.sampleRate() * format.channelCount()
                        * format.sampleSize() / 8 ); // bytes per second
                        out << quint16(format.channelCount() * format.sampleSize() / 8); // Block align
                        out << quint16(format.sampleSize()); // Significant Bits Per Sample

                        // Data chunk
                        out.writeRawData("data", 4);
                        out << quint32(0); // Placeholder for the data chunk size (filled by close())

                        Q_ASSERT(pos() == 44); // Must be 44 for WAV PCM
                        }

                        void WavPcmFile::close()
                        {
                        // Fill the header size placeholders
                        quint32 fileSize = size();

                        QDataStream out(this);
                        // Set the same ByteOrder like in writeHeader()
                        out.setByteOrder(QDataStream::LittleEndian);
                        // RIFF chunk size
                        seek(4);
                        out << quint32(fileSize - 8);

                        // data chunk size
                        seek(40);
                        out << quint32(fileSize - 44);

                        QFile::close();
                        }@

                        And you use the class like this:
                        @// Starts the recording
                        WavPcmFile *m_file = new WavPcmFile("Filename.wav", m_audioInput->format(), this);
                        if(m_file.open()) {
                        m_audioInput->start(m_file);
                        } else {
                        // Error
                        }

                        // Stops the recording
                        m_audioInput->stop();
                        m_file->close();
                        @

                        Edit: Fixed the close() byte order error as indicated by tiho_d.

                        kybernetesK Offline
                        kybernetesK Offline
                        kybernetes
                        wrote on 20 Sept 2015, 10:01 last edited by
                        #13

                        @alexisdm Hello friend, i tried to use your class for recording wav file but i didnt understand "QAudioFormat & format_" part of your function.

                        u use "m_audioInput->format()" in WavPcmFile *m_file = new WavPcmFile("Filename.wav", m_audioInput->format(), this);

                        but i didnt understand what is m_audioInput and where u use it?

                        is it QAudioInput? and how i can configure format type in program.

                        1 Reply Last reply
                        0

                        • Login

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • Users
                        • Groups
                        • Search
                        • Get Qt Extensions
                        • Unsolved