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.8k 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.
  • L Offline
    L Offline
    loladiro
    wrote on last edited by
    #2

    What do you need the wav headers for? If you just want to record from you device to a wav file, let Qt handle it.

    1 Reply Last reply
    0
    • A Offline
      A Offline
      Anticross
      wrote on 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 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