音频合成最佳实践

时间:2014-10-31 15:23:08

标签: audio waveform synthesis

我想从头开始编写音乐程序。大目标:是的。我没有明确意图完成任何事情。这主要是个人学习项目。 :P

第一步是构建振荡器和仪器。仪器可能是振荡器和滤波器(以及包络+效果)的组合。现在,我的第一个问题是:我应该如何构建波发生器?

想象一下,我有一个用乐器X演奏不同音符的音轨。我想它最好是预演和"这些笔记。所以我会支付一笔前期费用来运行我的波函数来生成代表波的数字数组。假设我想以44.1KHz的采样率进行此操作,这是否意味着每台乐器每秒钟会产生44.1k项目的数组?

我认为这个问题本身与语言无关。但我计划使用JavaScript,因为我会在浏览器中运行它。

4 个答案:

答案 0 :(得分:1)

音频只是一条曲线 - 所以为了构建你的振荡器,你可以用这个算法输出一条曲线。数字而非模拟的软件要求将曲线定义为一系列时间点(样本),其值是音频曲线的瞬时高度。通常,这些样品每秒发生44100次,即。赫兹。

checkout Web Audio API - 它的功能非常强大,非常支持。只是为了欣赏它的灵活性检查这个由谷歌员工内部人员编写的演示

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);

这应该让你超越驼峰

答案 1 :(得分:1)

  

假设我想以44.1KHz的采样率进行此操作,这是否意味着我每个乐器的每秒声音数量为44.1k?

就是这样,你将有浮动或字节形式的44.1k样本(取决于你使用的语言)。

这是一些用于生成1秒正弦波的伪代码,基于浮点的样本为44.1kH:

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

答案 2 :(得分:1)

正如之前的回答者所指出的那样,您可以用C(或任何语言)编写一个简单的程序来输出一系列值,这些值表示声音样本或声波中的点。如果将这些值写入文本文件,则可以使用soxhttp://sox.sourceforge.net/)之类的程序将此文件转换为.wav文件。然后,您可以在计算机上播放wav文件,并通过扬声器收听声波。

答案 3 :(得分:0)

#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,因为它使用的是f.m.许多仪器的合成。

LE2:我的程序现在无法按预期工作