In the fourth week of the CS50 (Harvard) course, we learned how to work with memory, which will help you perform a laboratory work on changing the volume of the sound in a .wav file using a program that receives the original music file and receives the value of the factor via the command line (how many times you need to change volume). So if the user enters a factor of 2, this means that the resulting file should become twice as loud (volume.c).
To complete this task, you need to take into account that the music file has a 44-byte header that contains metadata, and only after that there are 2-byte (16-bit) values that describe the sounds. To overwrite a new file with a changed volume, you need to read and change the values of each of the two bytes that describe the sound. To separate the header values, you can enter a new uint8_t header[44]
data array, and write the rest of the values into an int16_t buffer
(these are two special types of variables that can then be used as an argument in the fread
and fwrite
functions).
Programme code by Shtukensia
// 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]); // 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; int buffer_size = 2; while (fread(&buffer, buffer_size, 1, input)) { buffer *= factor; fwrite(&buffer, buffer_size, 1, output); } // Close files fclose(input); fclose(output); }
Code from Igroglaz with detailed comments:
// 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; // array to store header data uint8_t header[HEADER_SIZE]; // variable which will be used as buffer - to store minimum audio sample int16_t buffer; int main(int argc, char *argv[]) { // Check command-line arguments if (argc != 4) { printf("Usage: ./volume input.wav output.wav factor\n"); return 1; } // STEP 1: open files and determine scaling factor: // open input file with 'read' mode FILE *input = fopen(argv[1], "r"); // check was it opened correctly if (input == NULL) { printf("Could not open file.\n"); return 1; } // open output file with 'write' mode FILE *output = fopen(argv[2], "w"); // check was it opened correctly if (output == NULL) { printf("Could not open file.\n"); return 1; } // convert volume factor from char to float float factor = atof(argv[3]); // STEP 2: copy header from input file to output file //- 1) read the header (44 bytes) from input file // fread() - read bytes from file to memory // size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); // reads `nmemb` items of data, // each `size` bytes long, // from the stream pointed to by `stream`, // storing them at the location given by `ptr`. fread(header, 1, HEADER_SIZE, input); //- 2) write the header (44 bytes) to output file // fwrite() - write bytes from memory to file // size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream); // writes `nmemb` items of data, // each `size` bytes long, // to the stream pointed to by `stream`, // obtaining them from the location given by `ptr`. fwrite(header, 1, HEADER_SIZE, output); // !!! fopen() "remembers" the number of bytes that were successfully read. // So until we fclose() - we can continue to work with the rest of data !!! // STEP 3: read samples from input file and write updated data to output file // 1) make a loop until we got to the end of file (EOF) // 2) read whole file // 3) multiply by factor while (fread(&buffer, 2, 1, input)) { buffer = buffer * (float)factor; fwrite(&buffer, 2, 1, output); } // Close files fclose(input); fclose(output); }