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 ?
QtWS25 Last Chance

[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 17 Jun 2011, 13:15 last edited by
    #1

    I have some QAudioDevice with it's properties and need to make a Wav header using the settings of my device. I know that format of data is wave in a-law, mu-law or PCM - the raw data without any compression, also I know that it is mono(but I actualy can get it from QAudioDevice format). So I need to fiil all the fields of Wav header:"Your text to link here...":http://www.sonicspot.com/guide/wavefiles.html . Also I dont know now the file size beacouse I just starting to it's writing. So can anyone show me how to get all of this fields from my QAudioDevice format or some other method in Qt.

    1 Reply Last reply
    0
    • L Offline
      L Offline
      loladiro
      wrote on 17 Jun 2011, 16:58 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 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