Я работаю над обработкой амплитуды wav файла и масштабирования его некоторым десятичным фактором. Я пытаюсь перенести голову, как считать и переписать файл эффективным памятью способом, также пытаясь заняться нюансами языка (я плохо знаком с C). Файл может быть или в 8-или в 16-разрядном формате. Путем я думал о выполнении, это - первым чтением данные заголовка в некоторую предопределенную структуру и затем обработка фактических данных в цикле, где я считаю блок данных в буфер, сделаю то, что необходимо к нему, и затем запишите это в вывод.
#include
#include
typedef struct header
{
char chunk_id[4];
int chunk_size;
char format[4];
char subchunk1_id[4];
int subchunk1_size;
short int audio_format;
short int num_channels;
int sample_rate;
int byte_rate;
short int block_align;
short int bits_per_sample;
short int extra_param_size;
char subchunk2_id[4];
int subchunk2_size;
} header;
typedef struct header* header_p;
void scale_wav_file(char * input, float factor, int is_8bit)
{
FILE * infile = fopen(input, "rb");
FILE * outfile = fopen("outfile.wav", "wb");
int BUFSIZE = 4000, i, MAX_8BIT_AMP = 255, MAX_16BIT_AMP = 32678;
// used for processing 8-bit file
unsigned char inbuff8[BUFSIZE], outbuff8[BUFSIZE];
// used for processing 16-bit file
short int inbuff16[BUFSIZE], outbuff16[BUFSIZE];
// header_p points to a header struct that contains the file's metadata fields
header_p meta = (header_p)malloc(sizeof(header));
if (infile)
{
// read and write header data
fread(meta, 1, sizeof(header), infile);
fwrite(meta, 1, sizeof(meta), outfile);
while (!feof(infile))
{
if (is_8bit)
{
fread(inbuff8, 1, BUFSIZE, infile);
} else {
fread(inbuff16, 1, BUFSIZE, infile);
}
// scale amplitude for 8/16 bits
for (i=0; i < BUFSIZE; ++i)
{
if (is_8bit)
{
outbuff8[i] = factor * inbuff8[i];
if ((int)outbuff8[i] > MAX_8BIT_AMP)
{
outbuff8[i] = MAX_8BIT_AMP;
}
} else {
outbuff16[i] = factor * inbuff16[i];
if ((int)outbuff16[i] > MAX_16BIT_AMP)
{
outbuff16[i] = MAX_16BIT_AMP;
} else if ((int)outbuff16[i] < -MAX_16BIT_AMP) {
outbuff16[i] = -MAX_16BIT_AMP;
}
}
}
// write to output file for 8/16 bit
if (is_8bit)
{
fwrite(outbuff8, 1, BUFSIZE, outfile);
} else {
fwrite(outbuff16, 1, BUFSIZE, outfile);
}
}
}
// cleanup
if (infile) { fclose(infile); }
if (outfile) { fclose(outfile); }
if (meta) { free(meta); }
}
int main (int argc, char const *argv[])
{
char infile[] = "file.wav";
float factor = 0.5;
scale_wav_file(infile, factor, 0);
return 0;
}
Я получаю отличающиеся размеры файла в конце (1k или так для файла 40 МБ), и я подозреваю, что это - то, вследствие того, что я пишу весь буфер в вывод, даже при том, что файл, возможно, завершился прежде, чем заполнить весь размер буфера. Кроме того, выходной файл испорчен - не будет играть или открываться - таким образом, я, вероятно, делаю все это неправильно. Любые подсказки относительно того, где я порчу, будут большими.Спасибо!
1 Вы читаете байты вместо 16-битных выборок в этой ветке else:
while (!feof(infile))
{
if (is_8bit)
{
fread(inbuff8, 1, BUFSIZE, infile);
} else {
fread(inbuff16, 1, BUFSIZE, infile); // <-- should be BUFSIZE*2
}
2 Вы не насыщаете значения при масштабировании, например исходная 16-битная выборка = 32000 и коэффициент = 1.5 будет обертываться вокруг целочисленного значения вместо того, чтобы ограничивать его максимумом 32767.
3 Вы вообще не смотрите на RIFF и другие заголовки. В файлах WAV возможно, что за аудиоданными следуют некоторые информационные колонтитулы или им предшествуют дополнительные заголовки. Или другими словами: ваша структура header
слишком статична. Вы также должны читать формат WAV из файла вместо того, чтобы указывать параметр, указывающий, что это 8-битные образцы.
4 Этого просто не произойдет:
outbuff16[i] = factor * inbuff16[i];
if ((int)outbuff16[i] > MAX_16BIT_AMP)
8-битные / 16-битные значения никогда не будут больше 255/32768, за исключением случаев, когда ваш компьютер вставляет некоторые волшебные биты в память при переполнении целых чисел: P
И аудио образцы подписаны, поэтому диапазоны: -128; 127 и -32768; 32767. В выражении умножения должна выполняться проверка переполнения. Вы также делаете предположения о режиме округления чисел с плавающей запятой до целых чисел, который можно настраивать и который следует учитывать. Что-то вроде if (roundf (factor * inbuff16 [i])> 32767 || roundf (factor * inbuff16 [i]) <-32768)
, возможно.
5 Вы не сохраняете результат fread
, поэтому вы запишете слишком много семплов в выходной файл.
6 И последнее: вы изобретаете колесо. Пока это для обучения, ничего страшного. В противном случае вам следует использовать существующие библиотеки.
Намного лучше использовать библиотеки для чтения и записи звуковых файлов. Например. libsndfile
. На этой веб-странице есть список «других похожих проектов», на которые вы также можете посмотреть. sndfile-tools
могут быть хорошими примерами кода, чтобы научиться использовать библиотеку.
Я бы рекомендовал посмотреть на исходный файл и выходной файл в шестнадцатеричном редакторе, чтобы убедиться, что вы правильно переписываете данные. Если полученный файл не воспроизводится или не открывается, есть вероятность, что заголовок выходного файла неверен.
Другой вариант - удалить логику обработки звука и просто считать исходный файл во внутренний буфер и записать его в файл. Если ваш код может генерировать корректный, рабочий выходной файл таким образом, то вы можете свести проблему к вашему коду обработки.
Возможно, вы также захотите начать с файла меньшего размера, чем 40 Мб. Если нет ничего другого, сделайте копию этого входного файла и обрежьте его до пары секунд звука. Файл меньшего размера будет легче проверять вручную.
Edit: Вызовы fread()
и fwrite()
нуждаются в проверке возвращаемых значений. Эти функции возвращают количество прочитанных или записанных элементов, и если вызов любой из них возвращает значение меньше ожидаемого, то это может быть источником разницы в размере файла.
Кроме того, второй параметр функции fread
указывается в байтах. Поэтому, если вы хотите прочитать весь буфер, вам нужно сказать что-то вроде fread(inbuff16, sizeof(inbuff16[0]), BUFSIZE, infile);
. Текущий код будет читать только BUFSIZE
байт (что по совпадению работает для 8-битного случая, но я бы рекомендовал изменить и его для наглядности).