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
Sign in to Reply
  1. AaronT's Avatar
    • Full Member
    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
  2. Psyk's Avatar
    • TSR Royalty
    • Location: Leamington Spa
    • Posts: 19,070
    Re: PCM to WAV header in C++
    What problem are you having with it? If you know the format of the header, what bit are you finding difficult?
  3. AaronT's Avatar
    • Full Member
    Re: PCM to WAV header in C++
    (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?
    Writing the code that places the header. I don't know how to even start it!
  4. Psyk's Avatar
    • TSR Royalty
    • Location: Leamington Spa
    • Posts: 19,070
    Re: PCM to WAV header in C++
    (Original post by AaronT)
    Writing the code that places the header. I don't know how to even start it!
    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.
  5. AaronT's Avatar
    • Full Member
    Re: PCM to WAV header in C++
    (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.
    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
  6. Psyk's Avatar
    • TSR Royalty
    • Location: Leamington Spa
    • Posts: 19,070
    Re: PCM to WAV header in C++
    (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
    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.

    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.
  7. AaronT's Avatar
    • Full Member
    Re: PCM to WAV header in C++
    (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.
    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? :

    (Original post by Psyk)
    outputArray[0] = 'R'
    outputArray[1] = 'I'
    outputArray[2] = 'F'
    outputArray[3] = 'F'
    ...Obviously with the rest of the header as well...

    I imagined i'd have to do something more complicated than that!
  8. Psyk's Avatar
    • TSR Royalty
    • Location: Leamington Spa
    • Posts: 19,070
    Re: PCM to WAV header in C++
    (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!
    Nope, that's basically it. One way to think about C and C++ is that it's all basically about pushing bytes around.

    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.

    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
    That's how you can write something other than a byte/char to your output array.

    * 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.
  9. AaronT's Avatar
    • Full Member
    Re: PCM to WAV header in C++
    (Original post by Psyk)
    Nope, that's basically it
    In that case, heres the code i've got:

    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;
    I'm just guessing at using the ':' but it doesn't work. How do i do it?
  10. TheUnbeliever's Avatar
    • TSR Demigod
    • Location: Scotland
    • Posts: 5,838
    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.)
    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
    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...'

    Honestly, this seems a pretty hairy task for an introductory course!
    Last edited by TheUnbeliever; 25-04-2012 at 02:00.
  11. Psyk's Avatar
    • TSR Royalty
    • Location: Leamington Spa
    • Posts: 19,070
    Re: PCM to WAV header in C++
    I agree with TheUnbeliever. In your case it might be more simple just to write the data out to the output stream directly, since then you don't have to worry about all the casting.
  12. JGR's Avatar
    • Exalted and Worshipped Member
    • Posts: 1,239
    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.
  13. TheUnbeliever's Avatar
    • TSR Demigod
    • Location: Scotland
    • Posts: 5,838
    Re: PCM to WAV header in C++
    You will also need to ensure that struct is packed, however.
  14. Psyk's Avatar
    • TSR Royalty
    • Location: Leamington Spa
    • Posts: 19,070
    Re: PCM to WAV header in C++
    (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.
    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.

    Since this is C++, a stream would also be an easier way of doing it. There's lots of ways it could be done.
  15. AaronT's Avatar
    • Full Member
    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:
    (Original post by TheUnbeliever)
    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
    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?

    As for the googling...
    (Original post by JGR)
    I suspect that a bit a googling or searching of header files will reveal one anyway.
    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.

    I really do appreciate all the fuss that everyones making for this
  16. TheUnbeliever's Avatar
    • TSR Demigod
    • Location: Scotland
    • Posts: 5,838
    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.
  17. AaronT's Avatar
    • Full Member
    Re: PCM to WAV header in C++
    Thanks a bunch. Makes a lot more sense now.
Sign in to Reply
Share this discussion:  
Useful resources
Article updates
Moderators

We have a brilliant team of more than 60 volunteers looking after discussions on The Student Room, helping to make it a fun, safe and useful place to hang out.

Reputation gems:
The Reputation gems seen here indicate how well reputed the user is, red gem indicate negative reputation and green indicates a good rep.
Post rating score:
These scores show if a post has been positively or negatively rated by our members.