Writing each track into a separate channel or resample all tracks into a single channel really depends on what your expected result is.
If you want mono audio or if you are otherwise limited to a single channel, resample all tracks into a single channel.
Otherwise, write each track into its own channel.
If you go for multiple channels, please keep in mind that other software can infer a use for each channel based on a list of standard speaker channels.
Some good sources on the RIFF / WAVE file format i've used myself to read and write wav files:
http://web.archive.org/web/20160530121622/http://www.blitter.com/~russtopia/MIDI/~jglatt/tech/wave.htm
http://web.archive.org/web/20140821040519/http://www.sonicspot.com/guide/wavefiles.html
The part you are most likely interested in:
Multi-channel digital audio samples are stored as interlaced wave data
which simply means that the audio samples of a multi-channel (such as
stereo and surround) wave file are stored by cycling through the audio
samples for each channel before advancing to the next sample time.
This is done so that the audio files can be played or streamed before
the entire file can be read. This is handy when playing a large file
from disk (that may not completely fit into memory) or streaming a
file over the Internet. The values in the diagram below would be
stored in a Wave file in the order they are listed in the Value column
(top to bottom).
Time |
Channel |
Value |
0 |
1 (left) |
0x0053 |
|
2 (right) |
0x0024 |
1 |
1 (left) |
0x0057 |
|
2 (right) |
0x0029 |
2 |
1 (left) |
0x0063 |
|
2 (right) |
0x003C |
Please note that your write loop definitely will not write your samples as provided in your question:
for (int i = 0; i < fsize; ++i) {
// write in blocks
out.write(reinterpret_cast<char *>(&sample), sizeof(int16_t));
}
During this loop the address of sample
remains the same, so each iteration writes the same 2 bytes.
And the way you cast int16_t
to char
will produce data with your systems' endianness, but it has to be little endian (least significant byte first). This might not be an issue for your project it just is not portable.
To do this in a portable way, you get each byte your self instead of using the way data is stored in memory.
Writing a single sample to your filestream can be done like this:
void write_sample(std::ofstream& out, uint16_t sample)
{
// little endian (least significant byte first)
out.put( static_cast<char>( (sample)&0xFF) ); // first byte
out.put( static_cast<char>( (sample>>8)&0xFF) ); // second byte
}
And an example of how to use it with multiple (4) channels:
// Open file for writing
// Write Wave file header (12 bytes)
// Write Format chunk with (26 or more bytes)
// Write Data chunk header (8 bytes)
uint16_t* track_C1 = make_wave( 523, sample_rate, fsize);
uint16_t* track_E1 = make_wave( 660, sample_rate, fsize);
uint16_t* track_G1 = make_wave( 784, sample_rate, fsize);
uint16_t* track_C2 = make_wave(1047, sample_rate, fsize);
for (int i=0; i<fsize; i++) {
write_sample(out, track_C1[i]);
write_sample(out, track_E1[i]);
write_sample(out, track_G1[i]);
write_sample(out, track_C2[i]);
}
// Close file