PCM to WAV header in C++
From C++ to PHP, debugging to webhosting; help and discussion about writing your latest program to running your website. NOT for help when your PC won't work.
| Announcements | Posted on | |
|---|---|---|
| Enter our travel-writing competition for the chance to win a Nikon 1 J3 camera | 21-05-2013 | |
-
PCM to WAV header in C++
I'm trying to just add a .WAV header to a PCM file so it becomes more widely usable. I'm struggling with the code. I know how the header needs to be formatted, i just need a head start with the code if anyone can help?
I've got a pcm file "input.pcm" which is opened up, edited by normalising or reversing etc, and then written to "output.pcm".
Please help
-
Re: PCM to WAV header in C++Writing the code that places the header. I don't know how to even start it!(Original post by Psyk)
What problem are you having with it? If you know the format of the header, what bit are you finding difficult? -
Re: PCM to WAV header in C++Well at the end of the day it's just a series of bytes. Surely if you know the header format, you know how to construct that data right? Once you've done that you just write it to a file just like you're already doing for the reversed PCM data.(Original post by AaronT)
Writing the code that places the header. I don't know how to even start it! -
Re: PCM to WAV header in C++Yeah, the problem is the coding. I don't know how to apply the header to the array. For example applying 'RIFF' to the first 4 bytes, i've never done it/had it explained to me. This is my first semester of programming, we're not taught how to do it. A push in the right direction with it would be nice.(Original post by Psyk)
Well at the end of the day it's just a series of bytes. Surely if you know the header format, you know how to construct that data right? Once you've done that you just write it to a file just like you're already doing for the reversed PCM data.
input.pcm:
signed 16 bit
22050Hz sample frequency
mono
2 second clip -
Re: PCM to WAV header in C++So do you have the data in a byte array already? You'll need to create a new byte array which is the size of the existing data + the size of the header. Then you can just assign the bytes as you need to.(Original post by AaronT)
Yeah, the problem is the coding. I don't know how to apply the header to the array. For example applying 'RIFF' to the first 4 bytes, i've never done it/had it explained to me. This is my first semester of programming, we're not taught how to do it. A push in the right direction with it would be nice.
input.pcm:
signed 16 bit
22050Hz sample frequency
mono
2 second clip
e.g.
byte* outputArray = new byte[headerSize + dataSize]
outputArray[0] = 'R'
outputArray[1] = 'I'
outputArray[2] = 'F'
outputArray[3] = 'F'
etc.
Basically you can treat your data as just an array of bytes. You can put whatever data you need to in there. -
Re: PCM to WAV header in C++Riiight. So i'll make a new array sound_data_wav[max_number_samples + header_size]. Wav header is 44 bytes, so i'll place the header first and then all the original data after that.(Original post by Psyk)
You'll need to create a new byte array which is the size of the existing data + the size of the header.
And can i literally do as you've written? :
...Obviously with the rest of the header as well...(Original post by Psyk)
outputArray[0] = 'R'
outputArray[1] = 'I'
outputArray[2] = 'F'
outputArray[3] = 'F'
I imagined i'd have to do something more complicated than that! -
Re: PCM to WAV header in C++Nope, that's basically it. One way to think about C and C++ is that it's all basically about pushing bytes around.(Original post by AaronT)
Riiight. So i'll make a new array sound_data_wav[max_number_samples + header_size]. Wav header is 44 bytes, so i'll place the header first and then all the original data after that.
And can i literally do as you've written? :
...Obviously with the rest of the header as well...
I imagined i'd have to do something more complicated than that!
Every type is really just a set of bytes, like an int is really just a set of 4 bytes*. Which means you can treat an array of bytes as something a different type.
e.g.
That's how you can write something other than a byte/char to your output array.Code:int valueToWrite = 5; byte* addressToWriteTo = outputArray + 4; // get the memory address to write int to int* pointerToInt = reinterpret_cast<int*>(addressToWriteTo); // cast it to an int pointer *point = valueToWrite; // write the value of the int to that address in memory
* the size of an int is actually platform specific, the language doesn't guarantee that it's 4 bytes. But for you it probably will be 4 bytes. -
Re: PCM to WAV header in C++In that case, heres the code i've got:(Original post by Psyk)
Nope, that's basically it
I'm just guessing at using the ':' but it doesn't work. How do i do it?Code://shift all the values in sound_data along by the header size for(sample_count=max_number_samples;sample_count>0;sample_count--) { sound_data[sample_count + header_size] = sound_data[sample_count]; } const int bits_per_sample = 16, bytes_per_sample = bits_per_sample / 8, bytes_per_second = bytes_per_sample * sample_rate_pcm, header_size = 16, channel_no = 1; sound_data[0:3] = 'RIFF'; sound_data[4:7] = max_number_samples + 36; sound_data[8:11] = 'WAVE'; sound_data[12:15] = 'FMT'; sound_data[16:19] = header_size; sound_data[20:21] = 1; sound_data[22:23] = channel_no; sound_data[24:27] = sample_rate_pcm; sound_data[28:31] = bytes_per_second; sound_data[32:33] = bytes_per_sample; sound_data[34:35] = bits_per_sample; sound_data[36:39] = 'data'; sound_data[40:43] = max_number_samples; -
Re: PCM to WAV header in C++
Some languages have support for 'slicing' arrays like that, but C++ is most certainly not one of them. You would need to construct things in place using e.g. std::strncpy and much reinterpret_casting of pointers as Psyk demonstrates above. Having said that, since you don't actually need to manipulate the header in memory (don't get me wrong - being able to do so is very useful, but it seems overkill for this task) you may be as well just writing to the output stream directly:
(Caution: uncompiled and it's late.)
Note that, whether you're doing this in memory or not, you want to be careful of two things: firstly, the size of the types involved as Psyk again mentions above, and secondly endianness - which is the order that the individual bytes of a larger data type are stored. You're almost certainly on a little-endian machine, meaning that e.g. 0x1234 is stored in memory as the byte sequence 0x34 0x12, but it's not unusual for file formats to require another endianness, usually big-endian (0x12 0x34). I don't know if that's the case here, but it's something to bear in mind if only as the theoretical 'if I run my code on another architecture, it might stop working because...'Code:std::ofstream out("output.pcm", std::ofstream::binary); out.write("RIFF", 4); unsigned remaining_size = max_number_samples + 36; out.write(reinterpret_cast<char*>(&remaining_size), 4); // and so on
Honestly, this seems a pretty hairy task for an introductory course!Last edited by TheUnbeliever; 25-04-2012 at 02:00. -
Re: PCM to WAV header in C++
You'd be better off defining/finding a struct with the format of the WAV header.
I suspect that a bit a googling or searching of header files will reveal one anyway.
You can then fill in the fields and write the whole struct out to the file at once, without needing a muck about with dozens of typecasts.
(You still need to bear in mind machine-endianness though).
I would advise against just trying to push out one byte at a time as it's clumsy and unnecessarily difficult. -
Re: PCM to WAV header in C++It does depend on the format of the header though. It doesn't necessarily translate into a C style struct, if it has a variable number of fields, or some kind of embedded array. But from what the OP has described so far, it looks like it would work fine as a struct, so that would be a better way to do it.(Original post by JGR)
You'd be better off defining/finding a struct with the format of the WAV header.
I suspect that a bit a googling or searching of header files will reveal one anyway.
You can then fill in the fields and write the whole struct out to the file at once, without needing a muck about with dozens of typecasts.
(You still need to bear in mind machine-endianness though).
I would advise against just trying to push out one byte at a time as it's clumsy and unnecessarily difficult.
Since this is C++, a stream would also be an easier way of doing it. There's lots of ways it could be done. -
Re: PCM to WAV header in C++
I'm sure there are many ways of doing it, I'd like to be able to understand it however. The code that you've written:
I don't understand most of the notation, we've been taught very little of the notation in fact. Would you be able to dumb down what its doing step by step for me?(Original post by TheUnbeliever)
Code:std::ofstream out("output.pcm", std:
:binary);
out.write("RIFF", 4);
unsigned remaining_size = max_number_samples + 36;
out.write(reinterpret_cast<char*>(&remaining_size), 4);
// and so on
As for the googling...
I've done far too much googling about this. Trying to find an openly available code for the header which i can understand and explain in my own words what is going on seems impossible! The way i understand it is for any 'char' values, for example 'RIFF', you need to tell the array that between this value in the array, say array[0] to array[3], theres going to be a char and it's going to be 'RIFF'. I don't know how to do that bit! It doesn't have to be versatile, i just need to be able to add the header file to any two second long, 22050Hz sample rate, 16 bit, little endian, raw PCM file.(Original post by JGR)
I suspect that a bit a googling or searching of header files will reveal one anyway.
I really do appreciate all the fuss that everyones making for this
-
Re: PCM to WAV header in C++
As has been said, there are a handful of different ways to do this, and they all come with their own gotchas. I don't know exactly which bits you don't follow, but here's a reasonably-detailed breakdown of the code you quoted:
std::ofstream is an output file stream. The std:: bit just says that the class ofstream lives in the namespace std - the standard library. Specifically, :: is the namespace resolution operator which you can read right-to-left as 'in' (ofstream in std). We open the file "output.pcm" in binary mode.
The member function std::ofstream::write takes two parameters, a const char* to a buffer, and a parameter telling it how many chars from that buffer to write to the stream.
The string "RIFF" can be represented directly by a char buffer, so we can use this without much fuss. We explicitly tell 'write' to copy 4 chars - we don't want the null terminator.
I have to store the calculation in 'remaining_size' because I need somewhere to copy it from. reinterpret_cast allows you to convert any integral type to a pointer, and throws pretty much any guarantee of safety out the window. In this case, I say to the compiler something like "take the address of 'remaining_size' to produce an unsigned*, which we'll then pretend is a char* and write 4 chars - a number which we hope is no more than sizeof(unsigned) - from here to the output stream".
If all goes to plan, the likely result of this is that you'll get the number stored in 'remaining_size', in little-endian (less-significant bytes first) byte order, written to the output stream.
You'd then need to repeat this process for each other part of the header, then finally write the actual data.