-1

This is the code from what Brian explains from cs50 week4 lab4

// Modifies the volume of an audio file
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

// Number of bytes in .wav header
const int HEADER_SIZE = 44;

int main(int argc, char *argv[])
{
    // Check command-line arguments
    if (argc != 4)
    {
        printf("Usage: ./volume input.wav output.wav factor\n");
        return 1;
    }

    // Open files and determine scaling factor
    FILE *input = fopen(argv[1], "r");
    if (input == NULL)
    {
        printf("Could not open file.\n");
        return 1;
    }

    FILE *output = fopen(argv[2], "w");
    if (output == NULL)
    {
        printf("Could not open file.\n");
        return 1;
    }

    float factor = atof(argv[3]);

    // TODO: Copy header from input file to output file
    uint8_t header[HEADER_SIZE];
    fread(header, HEADER_SIZE, 1,input))
    
    fwrite(header,HEADER_SIZE, 1, output);
    

    // TODO: Read samples from input file and write updated data to output file
    int16_t buffer;
     while(fread(&buffer, sizeof(int16_t), 1, input))
    {
        buffer *= factor;       
        fwrite(&buffer, sizeof(int16_t ), 1 ,output);
    }
    // Close files
    fclose(input);
    fclose(output);
}

I am getting confused as to what fread() and fwrite() does. It says:

 while(fread(header, HEADER_SIZE, 1, input))

should not it be :

while(fread(header,sizeof(uint8_t), HEADER_SIZE, input)) 

since the syntax is :

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

ptr, which is the address (of the first byte) of memory into which to read the data,

size, which is the size (in bytes) of the type of data to read,

nmemb, which is the number of those types to read at once, and

stream, which is the pointer to a FILE returned by fopen.

And why are we using the address of buffer in fwrite() and fread() and not for header in fwrite() and fread()? Is the value of buffer gonna be overwritten after each loop?

Annie
  • 21
  • 2
  • 10
  • `sizeof(uint8_t)` is 1 by definition, and `fread(header, HEADER_SIZE, 1, input)` and `fread(header, 1, HEADER_SIZE, input)` are equivalent. but latter is more readable because it corresponds to the definition of `fread`. – Jabberwocky Sep 21 '21 at 08:19
  • You can look at it in 2 ways: 1) Read `HEADER_SIZE` number of single bytes or 2) Read one header of size `HEADER_SIZE`. – Gerhardh Sep 21 '21 at 08:21
  • You need the *whole* header, and if less than that was read, this `fread(header, HEADER_SIZE, 1, input)` will return 0, because `fread()` returns the number of *items* read. However in this `fread(header,sizeof(uint8_t), HEADER_SIZE, input)` any non-0 value (a partial read) will be considered to be `true`. – Weather Vane Sep 21 '21 at 08:22
  • What do you want to read? A number of bytes which happen to be the size of a header? Or one header (of its appropriate size)? Are you going to use the header for its purpose? Or are you going to work on bytes, without using the information that together they happen to make a header? – Yunnosch Sep 21 '21 at 08:24
  • I want to read the header from the input file and write it to the output file. Then read buffer from the input file(which is 16 bytes) in 16 bytes each and multiply it by factor and write it in the output file – Annie Sep 21 '21 at 08:33
  • `fread()` and `fwrite()` take a pointer to a buffer. Now, the header is an array, so passing the array *decays* into the required pointer. However when the data is read in the loop, this `int16_t buffer` isn't an array, it is an integer, and so you have to specifically pass its address with the `&` operator. – Weather Vane Sep 21 '21 at 08:43

1 Answers1

2

It depends.

If you want to receive one complete header and then process it as a header, with the meaning of the parts of a header, then you should ask for one copy of the kind of header you want to process:

fread(header, HEADER_SIZE, 1, input)

If you want to receive a number of bytes (which happens to be the size of a header) and then process them as separate bytes (i.e. ignore the fact that together they make a header), then you should ask for many bytes:

fread(header,sizeof(uint8_t), HEADER_SIZE, input)

(With explicit permission I add the contribution by WeatherVance. It adds details on the technical consequences to my approach of trying to explain the semantic meaning.)

fread(header, 1, 44, input) and fread(header, 44, 1, input) will both attempt to read up to 44 bytes.
If only 2 bytes could be read then fread will return 2 in the first case and 0 in the second case. Because the first is trying to read 44 items of size 1 and the second wants to read 1 item of size 44.

Yunnosch
  • 26,130
  • 9
  • 42
  • 54
  • It's not making any sense to me. Can anyone explain in simple terms as to what exactly is happening to the header and buffer when using fread() and fwrite() – Annie Sep 21 '21 at 08:38
  • Could you state which of the use cases is yours? – Yunnosch Sep 21 '21 at 08:41
  • Most probably the parameters `size` and `nmemb` are multiplied inside `fread()` to give the amount of bytes to read as a single value, as `read()` takes just one value. So the decision is more an abstract one. However, some implementation might have dependencies on the `size` due to alignment. – the busybee Sep 21 '21 at 08:47
  • Since header is an array we are not passing its address in fread() or fwrite() but when we use a variable,buffer why are we using its address in fread() and fwrite() – Annie Sep 21 '21 at 08:52
  • Annie please see my [comment](https://stackoverflow.com/questions/69265517/a-possible-error-in-solution-video-of-pset4-lab-volume-within-fread-function#comment122425590_69265517). – Weather Vane Sep 21 '21 at 08:56
  • I understood it now. The first argument takes a pointer and since header is an array (the name of an array is a pointer to its first element). So when I passed header I am passing a pointer. – Annie Sep 21 '21 at 09:00
  • No, you are passing an array, which is not a pointer. But it decays to a pointer to its first element when passed to a function. But the `int16_t buffer` is neither an array nor a pointer, so you must explicitly pass its address to those functions. – Weather Vane Sep 21 '21 at 09:01
  • Yes! I am passing an array but if you look deep down on the implementation of arrays, it's is a collection of characters in memory that are adjacent to each other where header is pointing to the array elements. Correct me if I am wrong – Annie Sep 21 '21 at 09:08
  • @Weather Vane Can you please rephrase your answer. Are telling if I pass: fread(header, HEADER_SIZE, 1, input) I am gonna get a whole copy of header(44bytes) in header? – Annie Sep 21 '21 at 09:14
  • I got it @Weather Vane Thanks! – Annie Sep 21 '21 at 09:24
  • 1
    @WeatherVane If OP got the appreciated help from one of your comments it would be appropriate to let you have the "accept". For that your contribution is needed as an answer post. (Or did somebody, maybe me, confuse something...?) – Yunnosch Sep 21 '21 at 09:42
  • 2
    @Yunnosch IMO that began before we got our hands on it. Annie, `fread(header, 1, 44, input)` and `fread(header, 44, 1, input)` will both attempt to read *up to* 44 bytes. If only 2 bytes could be read then `fread` will return `2` in the first case and `0` in the second case. Because the first is trying to read 44 items of size 1 and the second wants to read 1 item of size 44. – Weather Vane Sep 21 '21 at 15:24
  • @WeatherVane While I do not conisder my answer unhelpful (I try to get the idea of semantic meaning, i.e. beyond syntax, across), I think that your previous comment is a better answer than mine. If you make it an answer I will upvote and not pout when the "accept" changes. – Yunnosch Sep 21 '21 at 15:26
  • Thanks, but feel free to add whatever you like to your own answer if you think it needs more. – Weather Vane Sep 21 '21 at 15:37
  • 1
    Thanks. I did. @WeatherVane – Yunnosch Sep 21 '21 at 16:08