Лучшие практики синтеза звука

Я хочу запрограммировать музыкальную программу с нуля. Большая цель: да. У меня нет четкого намерения закончить что-либо. В основном это личный проект для обучения. :П

Первый шаг - это создание осцилляторов и инструментов. Инструмент, вероятно, будет представлять собой комбинацию осцилляторов и фильтров (и огибающих + эффектов). Теперь мой первый вопрос: Как мне построить генераторы волн?

Представьте, что у меня есть трек, который воспроизводит разные ноты с помощью инструмента X. Я полагаю, что лучше всего выполнить «предварительный рендеринг» этих нот. Поэтому я бы заплатил авансом за запуск моих волновых функций для генерации массива чисел, представляющих волну. Скажем, я хочу сделать это с частотой дискретизации 44,1 кГц, означает ли это, что у меня будет массив из 44,1 тыс. Элементов звука в секунду для каждого инструмента?

Я думаю, что этот вопрос не зависит от языка. Но я планирую использовать JavaScript, потому что буду запускать его в браузере.


person simme    schedule 31.10.2014    source источник


Ответы (4)


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

checkout Web Audio API - он на удивление мощный и очень хорошо поддерживается. Просто чтобы оценить его гибкость, ознакомьтесь с этой демонстрацией, написанной инсайдером Google.

Web Audio Playground
http://webaudioplayground.appspot.com/

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

Вот определение обратного вызова, используемого для синтеза звука (осциллятор)

function setup_onaudioprocess_callback(given_node) {

    given_node.onaudioprocess = (function() {

        return function(event) {

            if (allow_synth) {

                // console.log('inside main_glob callback   onaudioprocess   BUFF_SIZE ', BUFF_SIZE);

                var synthesized_output_buffer;

                // stens TODO - how to pass in own buffer instead of being given object: out so I can do a circular ring of such buffers

                synthesized_output_buffer = event.outputBuffer.getChannelData(0); // stens TODO - do both channels not just left

                var phi = 0,
                    dphi = 2.0 * Math.PI * given_node.sample_freq /
                    given_node.sample_rate;

                for (var curr_sample = 0; curr_sample < given_node.BUFF_SIZE; curr_sample++, phi += dphi) {

                    synthesized_output_buffer[curr_sample] = Math.sin(phi);
                }

                given_node.sample_freq *= given_node.freq_factor;

                if (given_node.sample_freq <
                    given_node.MIN_FREQ) {

                    given_node.freq_factor = given_node.increasing_freq_factor;

                } else if (given_node.sample_freq > given_node.MAX_FREQ) {

                    given_node.freq_factor = given_node.decreasing_freq_factor;
                }

                // ---

                audio_display_obj.pipeline_buffer_for_time_domain_cylinder(synthesized_output_buffer,
                    BUFF_SIZE, "providence_2");
            }
        };

    }());
}

он будет использоваться по отношению к узлу, сгенерированному с помощью createScriptProcessor

function init_synth_settings(given_node, g_MIN_FREQ, g_MAX_FREQ, g_BUFF_SIZE, g_decreasing_freq_factor, g_increasing_freq_factor) {

    given_node.MIN_FREQ = g_MIN_FREQ;
    given_node.MAX_FREQ = g_MAX_FREQ;

    given_node.sample_freq = given_node.MIN_FREQ; // Hertz
    given_node.BUFF_SIZE = g_BUFF_SIZE;

    given_node.decreasing_freq_factor = g_decreasing_freq_factor;
    given_node.increasing_freq_factor = g_increasing_freq_factor;
    given_node.freq_factor = g_increasing_freq_factor;
}

var this_glob_01 = audio_context.createScriptProcessor(BUFF_SIZE, 1, 1);

init_synth_settings(this_glob_01, 20, 300, BUFF_SIZE, 0.98, 1.01);

setup_onaudioprocess_callback(this_glob_01);

это должно помочь вам

person Scott Stensland    schedule 31.10.2014
comment
Спасибо за ссылку на детскую площадку. Остальная часть поста была в основном повторением моего вопроса. Может мне непонятно было? Я охотился за советами по фактическому подходу к реализации! :) - person simme; 31.10.2014
comment
Спасибо за код! Но где ты это находишь? На github.com/cwilso/WebAudio? Я слеп, потому что не нашел? :П - person simme; 31.10.2014
comment
приведенный выше код взят из моего первого проекта JavaScript github.com/ scottstensland / webgl-3d-animation / blob / master / src / - person Scott Stensland; 01.11.2014

Как указали предыдущие ответчики, вы можете написать простую программу на C (или любом другом языке в этом отношении) для вывода серии значений, которые представляют звуковые образцы или точки в звуковой волне. Если вы записываете эти значения в текстовый файл, вы можете использовать такую ​​программу, как sox (http://sox.sourceforge.net/), чтобы преобразовать этот файл в файл .wav. Затем вы можете воспроизвести файл WAV на своем компьютере и прослушать звуковую волну через динамики.

person mti2935    schedule 09.01.2015

Скажем, я хочу сделать это с частотой дискретизации 44,1 кГц, означает ли это, что у меня будет массив из 44,1 тыс. Элементов звука в секунду для каждого инструмента?

Именно так, у вас будет 44,1 тыс. Образцов в виде чисел с плавающей запятой или байтов (в зависимости от того, какой язык вы используете).

Вот псевдокод для генерации 1-секундной синусоидальной волны с выборками на основе чисел с плавающей запятой на частоте 44,1 кГц:

RATE = 44100
frequency = 440 
for(i = 0; i < RATE; i++){
    array[i] = sin(i*2*PI*frequency/RATE);
}
person Erika    schedule 01.11.2014

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

#define PI 3.141592

int main(void){

double RATE = 44100;
double frequency = 440;
double Amp=16384;//amplitude of signal
FILE *file;
double data;
   file=fopen("dummyf.pcm", "w");

for(double i = 0; i < RATE; i++){
   data = Amp*sin(i*2*PI*frequency/RATE);
   fputc(data, file);
}

fclose(file);

return 0;
}

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

ЛЭ: вместо этого вам следует использовать DirectX DirectMusic, потому что он использует f.m. синтез для многих инструментов.

LE2: моя программа теперь работает не так, как ожидалось

person paul    schedule 02.01.2015
comment
Действительно странный код. Должно быть int main, вы вообще не используете array[0], вы записываете array[2] на диск, пока он не существует, вы вызываете free что-то, не выделенное malloc, вы не проверяете, можно ли открыть файл , вы сравниваете int i с RATE, который является _8 _... - person MSalters; 09.01.2015