обработка аудио wav файл с C

Я работаю над обработкой амплитуды 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 МБ), и я подозреваю, что это - то, вследствие того, что я пишу весь буфер в вывод, даже при том, что файл, возможно, завершился прежде, чем заполнить весь размер буфера. Кроме того, выходной файл испорчен - не будет играть или открываться - таким образом, я, вероятно, делаю все это неправильно. Любые подсказки относительно того, где я порчу, будут большими.Спасибо!

8
задан bta 16 March 2010 в 19:40
поделиться

3 ответа

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 И последнее: вы изобретаете колесо. Пока это для обучения, ничего страшного. В противном случае вам следует использовать существующие библиотеки.

9
ответ дан 5 December 2019 в 10:40
поделиться

Намного лучше использовать библиотеки для чтения и записи звуковых файлов. Например. libsndfile . На этой веб-странице есть список «других похожих проектов», на которые вы также можете посмотреть. sndfile-tools могут быть хорошими примерами кода, чтобы научиться использовать библиотеку.

5
ответ дан 5 December 2019 в 10:40
поделиться

Я бы рекомендовал посмотреть на исходный файл и выходной файл в шестнадцатеричном редакторе, чтобы убедиться, что вы правильно переписываете данные. Если полученный файл не воспроизводится или не открывается, есть вероятность, что заголовок выходного файла неверен.

Другой вариант - удалить логику обработки звука и просто считать исходный файл во внутренний буфер и записать его в файл. Если ваш код может генерировать корректный, рабочий выходной файл таким образом, то вы можете свести проблему к вашему коду обработки.

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

Edit: Вызовы fread() и fwrite() нуждаются в проверке возвращаемых значений. Эти функции возвращают количество прочитанных или записанных элементов, и если вызов любой из них возвращает значение меньше ожидаемого, то это может быть источником разницы в размере файла.

Кроме того, второй параметр функции fread указывается в байтах. Поэтому, если вы хотите прочитать весь буфер, вам нужно сказать что-то вроде fread(inbuff16, sizeof(inbuff16[0]), BUFSIZE, infile);. Текущий код будет читать только BUFSIZE байт (что по совпадению работает для 8-битного случая, но я бы рекомендовал изменить и его для наглядности).

1
ответ дан 5 December 2019 в 10:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: