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 ?
Forum Updated to NodeBB v4.3 + New Features

[SOLVED] How to form Wav header ?

Scheduled Pinned Locked Moved General and Desktop
13 Posts 6 Posters 16.7k Views 1 Watching
  • 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
    alexisdm
    wrote on 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 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 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
        0
        • A Offline
          A Offline
          Anticross
          wrote on 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 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 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 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 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 last edited by
                    #12

                    [[blank-post-content-placeholder]]

                    1 Reply Last reply
                    0
                    • A alexisdm

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