На четвертой неделе курса CS50 (Harvard) мы изучили работу с памятью, что поможет выполнить лабу (лабораторную работу) по изменению громкости звука в файле .wav при помощи программы, которая получает исходный файл с музыкой и через командную строку получает значение фактора (во сколько раз нужно изменить громкость). Так если пользователь введет фактор 2 — то это значит, что полученный файл должен стать в два раза громче (volume.c).
Чтобы выполнить это задание, нужно учесть, что у музыкального файла есть заголовок из 44 байтов, в котором содержатся метаданные, и только после этого идут значения по 2 байта (16 бит), описывающие звуки. Чтобы перезаписать новый файл с измененной громкостью, нужно прочитать и изменить значения каждый двух байтов, описывающих звук. Чтобы отделить значения заголовка, можно ввести новый массив данных uint8_t header[44]
, а остальные значения записать в int16_t buffer
(это два специальных типа переменных, которые затем можно использовать как аргумент в функциях fread
и fwrite
).
Код программы (Штукенция)
// 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); }
Код от Игроглаза с подробными комментариями:
Важный момент: этот код для линуксового компилятора. Если на винде запускать — надо использоват не «r», а «rb» для fopen
// 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); }