Обход звуковых / аудиофайлов (WAV) с помощью C

#c #audio

#c #Аудио

Вопрос:

В рамках моей более продолжительной проектной идеи по созданию VST для звукового дизайна я сейчас начинаю узнавать о звуковых файлах и о том, как они хранятся на компьютере.

Проблема, с которой я сталкиваюсь, связана с изменением байтов звукового файла. Я использую C для загрузки базового звукового файла WAV, используя putchar функцию, поскольку она предоставляет прямой доступ к каждому байту в коде ascii. Я нашел очень хорошее место для изучения файла WAV здесь и попал в него. После того, как я разобрался с содержимым заголовочного файла, мне захотелось испачкать руки, поэтому, используя некоторую обработку сигналов, я хотел создать небольшой скрипт, который выводит входной WAV-файл с половинной скоростью.

Чтобы сделать это без изменения значений тела WAV-файла (данных), мне нужно уменьшить частоту дискретизации вдвое, но также вдвое меньше байтов в секунду, чтобы (частота дискретизации x выравнивание блоков = байты в секунду) было истинным

Некоторый демонстрационный код, который я написал для обработки байтов для частоты дискретизации (между позициями 25 и 28 я получаю байты для частоты дискретизации)

 #include <stdio.h>

int main() {
    int character_of_byte, position = 0;
    while ((character_of_byte = getchar() ) != EOF) {
         position  ;
         if (position >= 25 amp;amp; position <= 28) { 
             putchar(character_of_byte / 2);
         } else {
             putchar(character_of_byte);
         }
    }
}
 

Например. если частота дискретизации составляет 44100 кГц, это задается как ввод в десятичном виде: 68 , 172, 0, 0 байты хранятся в форме с малым конечным порядком, а при уменьшении вдвое вывод в десятичном виде должен быть: 22050, что выполняется, если мы разделим все вышеперечисленное на половину и сохраним по порядку: 34, 86, 0, 0 Но затем, если я сделаю то же самое для байтов в секунду, например, 176400khz, после того, как я разделю все десятичные дроби, число позже интерпретируется как 88072.

В этом случае Putchar (176400khz) получает: 16, 177 , 2 , 0 (по порядку) для ввода, но: если я запущу тот же скрипт, он выведет: 8, 88 , 1 и 0, что даст число 88072.

Это неправильно, потому что сдвиг бита пропускает 1 в случае 8, поскольку 177 является нечетным числом, поэтому его последняя цифра 1, но должна была быть перенесена на 8 (которая вместо этого была сохранена как 00001000), чтобы создать 10001000. Как я могу это исправить, если мне нужно изменять байты ПО мере их поступления в качестве входных данных? Имея в виду, что последний байт до числа 32-битного числа задается в первой, строчной форме

Комментарии:

1. Представьте себе деление числа путем деления всех цифр и округления каждой цифры, например, 12345678, деленное на 2, равно 01122334. Это то, что вы здесь делаете.

2. Обычно это делается путем считывания 4 байт, создания 32-битного числа, деления 32-битного числа, затем повторного разделения 32-битного числа на байты и их записи.

3. Вы правы, это то, что я делаю. Я пытаюсь имитировать смещение бита вправо. Это то, что я бы тоже сделал, но, к сожалению, у меня нет такой возможности.

Ответ №1:

Обычно это делается путем чтения 4 байтов, создания 32-разрядного числа, деления 32-разрядного числа, затем повторного разделения 32-разрядного числа на байты и их записи.

 // assumes no EOF
int byte_1 = (unsigned char)getchar(f);
int byte_2 = (unsigned char)getchar(f);
int byte_3 = (unsigned char)getchar(f);
int byte_4 = (unsigned char)getchar(f);
int32_t number = (byte_1 | (byte_2 << 8) | (byte_3 << 16) || (byte_4 << 24));
number /= 2;
byte_1 = number amp; 255;
byte_2 = (number >> 8) amp; 255;
byte_3 = (number >> 16) amp; 255;
byte_4 = (number >> 24) amp; 255;
putchar(byte_1);
putchar(byte_2);
putchar(byte_3);
putchar(byte_4);
 

(конечно, это можно написать гораздо короче; я написал это так, чтобы ясно показать идею).

Если вы не возражаете, что ваш код будет работать только в системах с малым порядком, вы также можете использовать fread или fwrite для чтения всего числа сразу. На самом деле, вероятно, именно так программа записи записала файл, и именно поэтому они используют этот формат.

 // still no EOF checks
int32_t sample_rate;
fread(amp;sample_rate, sizeof(sample_rate), 1, f);
sample_rate /= 2;
fwrite(amp;sample_rate, sizeof(sample_rate), 1, stdout);