Лучшие практики синтеза аудио
Я хочу программировать музыкальную программу с нуля. Большая цель: да. У меня нет четкого намерения что-либо закончить. Это в основном личный проект для обучения.:П
Первым шагом является построение генераторов и инструментов. Инструмент, вероятно, будет комбинацией осцилляторов и фильтров (и огибающих + эффектов). Теперь мой первый вопрос: как мне построить волновые генераторы?
Представьте, что у меня есть трек, который играет разные ноты с инструментом X. Я думаю, что лучше всего "предварительно отрендерить" эти ноты. Так что я бы заплатил аванс за запуск моих волновых функций для генерации массива чисел, представляющих волну. Скажем, я хочу сделать это с частотой дискретизации 44,1 кГц, означает ли это, что у меня будет массив из 44,1 тыс. Элементов в секунду звука на инструмент?
Я думаю, что этот вопрос сам по себе не зависит от языка. Но я планирую использовать JavaScript, потому что я буду запускать это в браузере.
4 ответа
Аудио - это просто кривая - поэтому, чтобы построить свой генератор, вы придумаете этот алгоритм для вывода кривой. Программное обеспечение, являющееся цифровым, а не аналоговым, требует, чтобы кривая была определена как последовательность моментов времени (выборок), где ее значение является мгновенной высотой звуковой кривой. Как правило, эти образцы происходят со скоростью 44100 раз в секунду, т.е. Герц.
Оформить заказ 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);
это должно перебить тебя
Скажем, я хочу сделать это с частотой дискретизации 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);
}
Как указывали предыдущие авторы, вы можете написать простую программу на C (или на любом другом языке) для вывода ряда значений, которые представляют звуковые образцы или точки звуковой волны. Если вы записываете эти значения в текстовый файл, то вы можете использовать такую программу, как sox
( http://sox.sourceforge.net/), чтобы преобразовать этот файл в файл.wav. Затем вы можете воспроизвести файл wav на своем компьютере и прослушать звуковую волну через динамики.
#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, чем в памяти, не так ли?
LE: вы должны использовать DirectX DirectMusic, потому что он использует синтез FM для многих инструментов.
LE2: моя программа сейчас работает не так, как ожидалось