артефакты при смешивании файлов wav с libsndfile

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

Я обрезаю сэмплы на одну секунду, поэтому у меня есть следующее: [одна секунда звука 1][одна секунда звука 2][одна секунда звука 1 + звук 2]

Проблема в том, что при каждом переходе возникает странный звуковой артефакт. Кто-нибудь знает, откуда оно?

Заранее спасибо.

Вот код, который я использую:

#include "stdafx.h"
#include <cstdlib>
#include <sndfile.h>


int _tmain(int argc, _TCHAR* argv[])
{
    SF_INFO sInfo1;
    SF_INFO sInfo2;
    SF_INFO sInfo3;

    SNDFILE *sFile1 = NULL;
    SNDFILE *sFile2 = NULL;
    SNDFILE *sFile3 = NULL;

    double *buff1;
    double *buff2;
    double *buff3;

    sf_count_t count1 = 0;
    sf_count_t count2 = 0;
    sf_count_t count3 = 0;

    buff1 = (double*)malloc(88200*sizeof(double));
    buff2 = (double*)malloc(88200*sizeof(double));
    buff3 = (double*)malloc(88200*sizeof(double));

    sInfo1.format = 0;
    sInfo2.format = 0;
    sFile1 = sf_open("C:/samples/mezzo forte/mcg_mf_022.wav", SFM_READ, &sInfo1);
    sFile2 = sf_open("C:/samples/mezzo forte/mcg_mf_046.wav", SFM_READ, &sInfo2);

    sInfo3 = sInfo2;
    sFile3 = sf_open("C:/samples/test1.wav", SFM_WRITE, &sInfo3);

    count1 = sf_read_double(sFile1, buff1, 88200);
    count2 = sf_read_double(sFile2, buff2, 88200);

    for(int i=0; i<88200; i++)
    {
        buff3[i] = buff1[i] + buff2[i] - ( buff1[i] * buff2[i] );
    }

    count1 = sf_write_double(sFile3, buff1, 88200);
    count2 = sf_write_double(sFile3, buff2, 88200);
    count3 = sf_write_double(sFile3, buff3, 88200);

    sf_close(sFile1);
    sf_close(sFile2);
    sf_close(sFile3);

    free(buff1);
    free(buff2);
    free(buff3);

    //getchar();
    return 0;
}

person user1832968    schedule 18.11.2012    source источник


Ответы (2)


Это не проблема libsndfile. Это общий вопрос синтеза звука.

Всякий раз, когда вы усекаете сэмпл до произвольного значения (например, 1 секунды), вы можете ожидать услышать (или увидеть, если вы загрузите полученный файл, скажем, в Audacity и проверите спектрограмму и форму волны на границах перехода). ) артефакт. Это происходит из-за резкого изменения формы сигнала выборки. Я пропущу попытку обсудить проблемы, связанные с ограничением полосы пропускания, и просто попрошу вас сделать быстрое затухание семплов, а не просто их обрезать. Это заставляет вашу звуковую волну [быстро] приближаться к нулю непосредственно перед переходом — плавно.

Вы можете обнаружить, что вам также необходимо плавно (или кроссфейдно, если вы перекрываете плавные переходы) следующего сэмпла, взвешивая его первые несколько сэмплов на значение, близкое к нулю, и увеличивая его [быстро, или вы' пропущу атаку] в полную силу. Во-первых, начните с простого быстрого затухания перед каждым переходом, и только если вам нужно, беспокойтесь о затухании. Реализация такая же (значение масштабирования выборки, которое увеличивается или уменьшается), но это произвольное усечение (окончание) выборок на 1 секунду, что, вероятно, вызывает больше всего проблем.

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

РЕДАКТИРОВАТЬ:

Я изначально предположил, что ваше микширование было в порядке, потому что вы спрашивали только об артефактах перехода. Мой ответ касается этого. Однако стоит отметить, что я понятия не имею, почему вы смешиваетесь с buff3 именно так, как вы есть, учитывая заявленную вами цель. Если я правильно понимаю, что вы хотите просто объединить два звука в buff3, просто добавьте два других соответствующих семпла вместе и убедитесь, что они не пересекаются (т. е. выходят за пределы диапазона [-1.0, +1.0]). libsndfile автоматически «защищает» от клиппинга, но он может только установить, например, значение выборки от 1,0 до 1,0, но не гарантировать одинаковое микширование обеих звуковых волн.

Если бы уровень любого из двух ваших входных звуков был достаточно высоким, простой аддитивный микс обрезался бы, что было бы еще одним типом «артефакта» (за исключением того, что он мог испортить весь звук, так что в этом конкретном случае вы, вероятно, заметили бы столько). Однако для общего микширования ваш цикл будет таким:

for(int i=0; i<88200; i++)
{
    /* multiply sum of signals by factor slightly
       less than reciprocal of their count to guard
       also against floating-point error. */

    buff3[i] = (buff1[i] + buff2[i]) * 0.499;
}
person Matthew Hall    schedule 18.11.2012
comment
Большое спасибо за ваш ответ. Я попробую вариант постепенного исчезновения и буду держать вас в курсе. Что касается алгоритма микширования, я взял его с vttoth.com/CMS/index. .php/technical-notes/68, и я думаю, что это имеет смысл. - person user1832968; 18.11.2012
comment
Ваша идея с исчезновением работает отлично! Я использовал линейное затухание для 10 последних выборочных значений. Спасибо еще раз. - person user1832968; 18.11.2012
comment
Алгоритм, размещенный в вашей ссылке, неверен. Это произведет искажение. Для получения дополнительной информации вы можете найти кольцевую модуляцию: en.wikipedia.org/wiki/Ring_modulation - person Bjorn Roche; 18.11.2012

Мне вообще непонятно, что вы пытаетесь сделать. Ваше описание гласит: «Я хочу создать wav-файл, содержащий один звук, другой и их сочетание». Если вы хотите соединить один звук с другим, зачем смешивать их вместе? Это все равно что взять смесь молока и сливок и добавить немного пополам.

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

Давайте сделаем немного основы.

Чтобы скопировать первую секунду первого файла, ваш цикл будет выглядеть так:

buff3[i] = buff1[i] ;

Чтобы скопировать первую секунду второго файла, ваш цикл будет выглядеть так:

buff3[i] = buff2[i] ;

Чтобы смешать их, вы просто добавляете их. Смешивание — это то же самое, что объединение. Иногда мы говорим, что берем «суперпозицию» двух сигналов:

buff3[i] = buff1[i] + buff2[i] ;

Обычно вы хотите разделить на два, чтобы сигнал не выходил за пределы:

buff3[i] = ( buff1[i] + buff2[i] ) / 2  ;

Обратите внимание, что мы нигде не умножали выборочные значения сигналов друг на друга. Умножение семпл за семплом, как вы делали, зарезервировано для очень необычных обстоятельств, таких как AM-синтез.

person Bjorn Roche    schedule 18.11.2012
comment
Хотя я не понимаю, почему ОП также вычитает произведение двух образцов из своего аддитивного микса, остальная часть кода на самом деле функциональна, за исключением отсутствия того, что технически является необязательным защитным ограждением на этапе смешивания. Его описание соответствует тому, что он закодировал с помощью libsndfile, и то, что он закодировал, вполне соответствует его заявленной цели. Он просто упустил из виду непрерывность формы волны на переходах. - person Matthew Hall; 18.11.2012