(Note: Apologies to anyone who read my previous post from half an hour ago- I've realised that I posted the wrong half of the code, and it was actually the second half that was giving me problems. So, if this post seems familiar it probably is!)
I'm trying to make a basic program which copies a .wav
file, this was my attempt:
#include <stdio.h>
#include <stdint.h>
int main (void)
{
FILE* input = fopen("input.wav", "r");
FILE* output = fopen("output.wav", "w");
uint8_t header [44];
fread(header, sizeof(uint8_t), 44, input);
fwrite(header, sizeof(uint8_t), 44, output);
int16_t body;
fread(&body, sizeof(int16_t), 1, input);
fwrite(&body, sizeof(int16_t), 1, output);
fclose(input);
fclose(output);
}
However, after failing to make it work, I looked up how to do it and apparently
fread(&body, sizeof(int16_t), 1, input);
fwrite(&body, sizeof(int16_t), 1, output);
should be
while (fread(&body, sizeof(int16_t), 1, input))
{
fwrite(&body, sizeof(int16_t), 1, output);
}
Why is it that the first part can copy the header without a problem, but the second part has to use a loop in order to work? To my eyes, they appear to be doing the same thing.
I think the fundamental issue here is that your code for reading and writing the header is reading the 44-byte header all at once, whereas the code for reading and writing the body is reading and writing just two bytes. But the body of the wav file is probably much bigger than two bytes!
Specifically, this code:
fread(header, sizeof(uint8_t), 44, input);
fwrite(header, sizeof(uint8_t), 44, output);
reads and writes a 44-byte header, all at once. But this code:
fread(&body, sizeof(int16_t), 1, input);
fwrite(&body, sizeof(int16_t), 1, output);
reads and writes two bytes. If the body is just two bytes long, that's fine. But if not, it's not going to copy the entire body; it's just going to copy the first two bytes.
This code, on the other hand, reads and writes multiple two-byte objects, as many of them as there are:
while (fread(&body, sizeof(int16_t), 1, input) == 1)
fwrite(&body, sizeof(int16_t), 1, output);
If you knew how big the body was, you could read and write it all at once, also (that is, just like you did with the header):
uint8_t body[body_size];
fread(body, 1, body_size, input);
fwrite(body, 1, body_size, output);
Or, if you know the body size as 16-bit words, and you want to think about it that way, you could do
uint16_t body [body_size_in_words];
fread(body, 2, body_size_in_words, input);
fwrite(body, 2, body_size_in_words, output);
fread
and fwrite
can be confusing because they're designed to let you pretend you're reading and writing something other than individual bytes. There are sort of three different ways to use them:
fread(buf, 1, n, fp)
read n
bytesfread(buf, size, 1, fp)
read one record of size
bytesfread(buf, size, n, fp)
read n
records of size
bytesDeep down, fread
basically just multiplies n * size
, and tries to read that many bytes. Then, however many bytes it reads, it divides that number by size
before returning it to you -- that is, it returns the number of records read, not necessarily the number of bytes read. And the same examples, explanations, and arguments hold for fwrite
, of course.