[SOLVED] How to form Wav header ?
-
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. -
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 ?).
-
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.
-
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 headerconnect(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.
-
@
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'));
@ -
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! -
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. -
[[blank-post-content-placeholder]]
-
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.
@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.