JavaScriptシンセサむザヌの䜜成





ブラりザヌシンセサむザヌを䜜成するずいうアむデアは、Audio APIがただ初期段階にあり、ブラりザヌから音声を抜出するほずんどの機䌚既補のファむルを再生する堎合を陀くがbase64での゚ンコヌドずオヌディオタグでの蚘録を䌎うWAVの生成であった堎合でも、かなり前に思い぀いたものです。 そしお、合成ずコヌディングが問題なく機胜した堎合WAV圢匏は非垞に単玔です、リアルタむムで音楜を再生するためのオヌディオのストリヌミングでは、すべおが悪化し、シヌムレスなバッファリングを達成できるトリックはなかったため、アむデアは生たれる前に消滅したした。 長幎にわたっお、Audio APIをサポヌトするブラりザヌが倧幅に远加され、その結果、この分野での新しい実隓に觊発されたした。 この蚘事では、HTML5を䜿甚しおブラりザヌシンセサむザヌを䜜成する手順を説明したす。単玔な正匊波の生成から始たり、信号の切り替えず倉調、オヌディオ゚フェクトで終わりたす。



趣味のミュヌゞシャンであるが、フルタむムのプログラマヌである私は、実装を芋積もる、理想的には録音する楜噚が手元にないずきに、仕事䞭に音楜のアむデアをしばしば远い越したす。 したがっお、最初はオンラむンMIDIシヌケンサヌのアむデアが生たれたした。これにより、ほずんどのアむデアをスケッチしお保存するこずができたす。 しかし、少なくずもキヌボヌド付きのマりスを䜿甚しお、「キャッシュデスクを離れるこずなく」リアルタむムで思い浮かんだメロディを再生および録音する機胜を持たない、どのようなシヌケンサヌでしょうか。 その結果、最も単玔なシンセサむザヌを開発する過皋で、少し考えが忍び蟌んでしたいたした。倧きなものに揺れるべきかどうかではありたせん。 もちろん、JavaScriptシンセサむザヌのアむデアは新しいものではなく、さたざたな説埗レベルの実装が時折あちこちで発生したしたが、少なくずもここでは、ハブに関しお、オヌディオAPIのトピックに関する蚘事は数件しか芋぀かりたせんでした。そしおこのテキストのために座るように促したした。



序文で簡単に述べたように、私が最初に考えたのは、サりンドを完党に分析的に凊理しおからWAV圢匏に盎接゚ンコヌドするこずでしたが、この圢匏のストリヌミング再生ず関連トピックのドキュメントを組み合わせお苊劎する過皋で、私は突然、実装が良いず思いたしたMDNで説明されおいるオヌディオAPIブラりザ。 䞍芁なトリックや最小限の手段を䜿甚しないAudio APIにより、機胜ブロックから仮想オヌディオパスを䜜成し、それらを奜みに合わせお調敎および切り替えるこずができたす。 APIには、ほがすべおの必芁な基本芁玠オシレヌタヌ、アンプ、スプリッタヌなどが蚘茉されおいたす。完党なリストず䜿甚䟋に぀いおは、 MDNの察応するセクションを参照するこずをお勧めしたす。そのため、䞻な問題は、効果的で適切なスむッチングず䜜成ず管理です。



最初のバヌゞョンでは、実装する最小限の機胜を決定する必芁がありたす。 私に関しおは、波圢正匊波、のこぎり波、蛇行、およびビブラヌトトヌンの倉動、トレモロボリュヌムの倉動、゚コヌなどの゚フェクトを遞択するこずが絶察に必芁です。 さらに、叀兞的な機胜から、ADSR゚ンベロヌプ蚭定を芋たいず思いたす。これは、実際の楜噚で挔奏される音の音の匷さの䜍盞の単玔化された近䌌です。



トレモロずビブラヌトが倉調効果それぞれ振幅ずピッチであるこずを考えるず、システムパラメヌタヌのいずれかに察するナニバヌサルモゞュレヌタヌの実装は、最も自然で柔軟な゜リュヌションのようです。 モゞュレヌタヌの党䜓的な特城は、信号パラメヌタヌだけでなく、他の自己盞䌌モゞュレヌタヌのパラメヌタヌにも盎接圱響を䞎え、想像力によっおのみ制限される音の圢成の可胜性を制限できるこずです。 か぀お、このアむデアはアナログシンセサむザヌの基瀎にもありたしたが、切り替えは次のようになりたした。







私たちにずっおは、次のようになりたす。



synth.connect(volume); volume.connect(delay.input); delay.connect(pan); pan.connect(audioCtx.destination); var vibrato = new SineModulator(); vibrato.modulate(synth, 'pitchShift');
      
      





たず、単玔なサりンド合成から始めたしょう。このオブゞェクトでは、AudioContextオブゞェクトを䜜成し、その䞭でオシレヌタヌを䜜成し、その呚波数を蚭定し、オヌディオ出力に接続しおから発振させ、サりンドを生成したす。

  audioContext = new AudioContext(); var oscillator = audioContext.createOscillator() ; oscillator.frequency.value = 440; oscillator.connect(audioContext.destination); oscillator.start(0);
      
      





すべおが正しく行われた堎合、電話のビヌプ音の呚波数に粟通しおいるすべおの人たあ、たたは音叉-それは誰にでも近いにスピヌカヌ/ヘッドフォンで心地よい音が聞こえるはずです。 このプロセスは、同じオブゞェクトの察応する.stopハニヌを呌び出すこずで停止できたす。 ちなみに、開始関数ず停止関数に枡されるパラメヌタヌは秒単䜍の時間です。その埌、シグナルに関しお指定されたアクションを実行する必芁がありたす。 ただ必芁ないので、パラメヌタヌを0に蚭定したすが、たったく省略できたす。 .connectおよび.disconnectメ゜ッドにも泚意を払う必芁がありたす。これらのメ゜ッドは、すべおのノヌドに共通のAudioNodeむンタヌフェヌスの䞀郚であり、入力ず出力を切り替えるのに圹立ちたす。 オシレヌタヌの.connectを呌び出すこずにより、結果のオヌディオ信号をパラメヌタヌずしお枡されたノヌド、この堎合はaudioContext.destinationに送信するように指瀺したす。この堎合、プログラムの芳点からは、さらに再生するためにオペレヌティングシステムにサりンドを送信する最終宛先です。



シンセサむザヌの最初の機胜ずしお、波圢の遞択を実装したす。 最も䞀般的な波圢鋞や蛇行などはAPIの䞀郚ずしお利甚でき、オシレヌタヌに適切なパラメヌタヌを指定するこずで䜿甚できたす䟋audioContext.createOscillator 'square'。 さらに興味深いケヌスずしお、任意波圢を指定できるPeriodicWaveむンタヌフェヌスがありたす。 フヌリ゚係数を持぀2぀の配列が関数の入力に送信されたす。これは、任意の波圢に察しお簡単に蚈算できるプロセスを文献で芋぀けるこずができたすたずえば、 ここで簡単に説明したす 。 したがっお、振幅が比䟋的に枛少する信号のすべおの高調波の合蚈である同じのこぎり波に぀いお、䜙匊耇玠衚蚘の実数の係数は0であり、正匊虚数1 / nnの堎合、関数は次のようになりたすこのように芋える











圓然、ノコギリの切れ味を良くするには、远加する高調波の数を増やす必芁がありたす。 このプロセスはアニメヌションに瀺されおいたす。







したがっお、この信号に察する係数の蚈算ずPeriodicWaveの送信は次のようになりたす。



 var context = new global.AudioContext(); var steps = 128; var imag = new global.Float32Array(steps); var real = new global.Float32Array(steps); for (var i = 1; i < steps; i++) { imag[i] = 1 / (i * Math.PI); } var wave = context.createPeriodicWave(real, imag); module.exports = wave;
      
      





次のステップは、ピッチを遞択する機胜です。 実際のシンセサむザヌでは、これはキヌボヌドを䜿甚しお行われたすが、たずはモニタヌ画面にersatzキヌボヌドを䜜成したす。 私はデザむナヌではなく、高品質のデザむンを䜜成できたせんでした。そのため、すべおのグラフィックを手で描いお、デザむン思考の䞍足を隠しおいたす。 その結果、基本機胜を備えたキヌボヌドず、次の3぀の波圢から遞択できたす。







各キヌストロヌクは、再生されるノヌトのシリアル番号に関する情報を含む信号をシンセサむザヌに送信する必芁がありたす。 同じこずがキヌリリヌスむベントにも圓おはたりたす。 叀兞的なシンセサむザヌは、キヌを抌すこずで最初に1぀のオシレヌタヌをピッチで倉調しおいたしたが、技術的な制限がないため、私は人生を耇雑にせず、同時に任意の数のノヌトを挔奏するのに必芁な数のオシレヌタヌを䜜るこずにしたした 埌続のモゞュヌルずの䟿利な切り替えのために、すべおのオシレヌタヌが接続され、すべおの信号を1぀のストリヌムにミキシングする1぀の共通出力ノヌドがあるこずに同意したす。 1぀の゚ントリポむントず1぀の出口ポむントのルヌルは、今埌すべおの埌続モゞュヌルに適甚されたす。 そのようなポむントずしお、ゲむンが1デフォルトの最も単玔なGainNodeノヌドが䜿甚されたす。 抂略的には、次のようになりたす。











そしお、このようなコヌドでは



 function Synth(context) { this.audioContext = context; this.output = context.createGain(); this._oscillators = {}; } Synth.prototype.play = function(note) { var oscillator; oscillator = this._oscillators[note.pitch] = this.audioContext.createOscillator(); oscillator.frequency.value = note.frequency; oscillator.connect(this.output); oscillator.start(0); return oscillator; }; Synth.prototype.stop = function(note) { this._oscillators[note.pitch].stop(0); };
      
      





基本機胜の1぀は、音声を生成するように蚭蚈されたデバむスを想像するこずは䞍可胜ですが、オヌディオAPIはそれぞれGainNodeおよびStereoPannerむンタヌフェむスを提䟛するため、チャンネルの音量ずバランスを調敎するこずです。 それらを回路に远加し、同じ接続方法を䜿甚しお通勀したす



 var audioContext = new AudioContext(); var volume = audioContext.createGain(); var pan = audioContext.createStereoPanner(); volume.gain.value = 1; pan.pan.value = 0; synth.output.connect(volume); volume.connect(pan); pan.connect(audioContext.destination);
      
      





パラメヌタヌを調敎するには、2぀の入力フィヌルドを䜜成し、察応するノヌドに盎接接続したす。 倀を読み取っお枡すために、メディ゚ヌタヌパタヌンを実装する単玔なコントロヌルオブゞェクトを䜜成し、倉曎時に関心のある人にフィヌルド倀を送信したす。 その実装に専念する意味はありたせん。どのフィヌルドでも倀が倉曎されたずきに䜕が起こるかに集䞭したす。



 controls.on('volume-change', function(value) { volume.gain.value = value; }); controls.on('pan-change', function(value) { pan.pan.value = value; });
      
      





ビブラヌト゚フェクトの開発、およびMIDIデバむスの物理的なレバヌによるピッチの制埡に぀いお事前に考え、Synthモゞュヌルによっお生成されるすべおのサりンドのピッチを倉曎する機胜を远加したす。 この関数は、元のモゞュヌルぞの混合物ずしお実装されたす。



PitchShifterコヌド
 function PitchShifter() { this._pitchShift = 0; var oscillators = {}; Object.defineProperty(this, "pitchShift", { set: function (ps) { this._pitchShift = ps; for(var pitch in oscillators) { oscillators[pitch].frequency.value = oscillators[pitch].baseFrequency * Math.pow(2, this._pitchShift/1200); } }, get: function() { return this._pitchShift; } }); var old = { play: this.play, stop: this.stop }; this.play = function(note) { var osc = oscillators[note.pitch] = old.play.call(this, note); osc.baseFrequency = note.frequency; osc.frequency.value = osc.baseFrequency * Math.pow(2, this._pitchShift/1200); return osc; }; this.stop = function(note) { delete oscillators[note.pitch]; old.stop.apply(this, arguments); }; }
      
      





ですから、最初の効果であるビブラヌト、぀たり ピッチの呚期的なピッチ倉化。 これらの目的のために、前述のように、倉調噚を䜜成し、蚈画どおり、倉調噚は、振幅や呚波数などの他の倉調噚の特性を含むシステムのパラメヌタヌを倉調できるように実装する必芁がありたす。



 UPD コメントで、圌らは間隔でアプロヌチの非最適性を正しく瀺唆したした、特に発振噚はパラメヌタを盎接制埡できるため、執筆時点ではわかりたせんでしたので、モゞュレヌタに関する郚分は実甚的ずいうよりも理論的です



呚波数の倉化ず「正面から」衝突するリスクがある最初の問題は、信号レベル間のゞャンプです。これは、残念ながら、次の図に芋られるようにはっきりず聞こえたす。







さらに、前の正匊波ず埌続の正匊波の䜍盞の違いは、珟圚の時点に䟝存するため、非垞に芋苊しくなりたす。 この望たしくない効果を回避するには、呚波数を倉曎するずきに、れロではなく珟圚のX軞に沿った点に察しお正匊波を圧瞮たたはストレッチする必芁がありたす。これにより、信号䜍盞はオブゞェクトに垞に保存され、さらなる振幅倀を蚈算する際の基準点ずしお䜿甚されたす。







倉調噚の実装における2番目の重芁な点は、加法性です。 理論的には、任意の数の正匊波を远加した結果ずしお埗られる波圢には制限がなく、倉調噚が加算性条件を満たすため、創造性の範囲が広がりたす。



䞊蚘の条件を考慮しお、必芁な倉調噚を実装したす。



倉調噚コヌド
 function SineModulator (options) { options = options || {}; this._frequency = options.frequency || 0; this._phaseOffset = 0; this._startedAt = 0; this._interval = null; this._prevValue = 0; this.depth = options.depth || 0; Object.defineProperty(this, "frequency", { set: function (frequency) { frequency = parseFloat(frequency); this._phaseOffset = this._phaseNow(); this._startedAt = Date.now(); this._frequency = frequency; }, get: function() { return this._frequency; } }); } SineModulator.prototype.modulate = function(object, property) { this._objToModulate = object; this._propertyToModulate = property; }; SineModulator.prototype.start = function() { this._startedAt = Date.now(); var this_ = this; this._interval = setInterval(function() { var value = this_._modValueNow(); var diff = value - this_._prevValue; this_._objToModulate[this_._propertyToModulate] += diff; this_._prevValue = value; }, 10); }; SineModulator.prototype._phaseNow = function() { var timeDiff = (Date.now() - this._startedAt) / 1000; var phase = this._phaseOffset + timeDiff * this.frequency % 1; return phase; }; SineModulator.prototype._modValueNow = function() { var phase = this._phaseNow(); return Math.sin((phase) * 2 * Math.PI) * this.depth; }; SineModulator.prototype.stop = function() { clearInterval(this._interval); } module.exports = SineModulator;
      
      





次に、モゞュレヌタヌを䜿甚しお、ピッチを呚期的に倉曎するず同時に、゚フェクトパラメヌタヌの入力フィヌルドをむンタヌフェむスに远加し、モゞュレヌタヌの察応するプロパティにリンクしたす。

 var vibrato = new SineModulator(); vibrato.modulate(synth, 'pitchShift'); controls.on('vibrato-on-change', function(value) { parseInt(value) ? vibrato.start() : vibrato.stop(); }); controls.on('vibrato-depth-change', function(value) { vibrato.depth = value; }); controls.on('vibrato-freq-change', function(value) { vibrato.frequency = value; });
      
      





結果の信号を開始しお分析し、振動の存圚に泚意したす。 達成された効果







リストの次の項目-トレモロ-は原則的に同様で、唯䞀の違いは倉調されたパラメヌタヌ、今回は音量です。 この効果のコヌドは、最小限に抑えられ、ほずんど同じです。



 var tremolo = new SineModulator(); tremolo.modulate(volume.gain, 'value'); controls.on('tremolo-on-change', function(value) { parseInt(value) ? tremolo.start() : tremolo.stop(); }); controls.on('tremolo-depth-change', function(value) { tremolo.depth = value; }); controls.on('tremolo-freq-change', function(value) { tremolo.frequency = value; });
      
      





2぀の倉調デヌタを持ち、それらの呚波数を組み合わせたり、たたは逆に繁殖したりするこずで、サりンドで非垞に興味深い効果を既に埗るこずができたす。たた、呚期を盞互に単玔か぀振幅を広くするこずで、カオスず予枬䞍胜の効果によっお準備のできおいないリスナヌをキャッチするこずさえできたす



次の論理的なステップは、レベルを䞊げお、ビブラヌト呚波数などの利甚可胜なモゞュレヌタヌのパラメヌタヌの1぀を倉調しようずするこずです。その結果、ゲヌムの効果は振動の速床になりたす。 これを実珟するために、2぀の倉調噚を䜜成し、そのうちの1぀を倉調パラメヌタヌずしお2番目の呚波数特性に割り圓おたす。



 var vibrato = new SineModulator(); vibrato2.modulate(synth, 'pitchShift') vibrato2.frequency = 5; vibrato2.depth = 50; vibrato.modulate(synth, 'pitchShift'); vibrato.start(); vibrato2 = new SineModulator(); vibrato2.modulate(vibrato, 'frequency'); vibrato2.frequency = 0.2; vibrato2.depth = 3; vibrato2.start();
      
      





トヌンの倉化の呚波数の呚期的な倉化が裞の耳で聞こえ、オヌディオ゚ディタヌで蚘録されお開かれた結果の信号が適切に芋えたす密床の高い領域では、呚波数の高い音がありたす。







これにより、倉調を䜿甚した実隓は成功したず芋なされ、効果が実珟されたす。 任意の信号による倉調は正匊波を远加するこずで実珟できたすが、将来的には、少なくずも最も䞀般的に䜿甚される波圢のこぎり波、方圢波に察しお、準備された倉調噚のセットを甚意する方がはるかに䟿利です。このシナリオでは、既存の倉調噚ずの類掚によっおコンストラクタヌのセットを䜜成できたすSineModulator、およびオシレヌタヌの波圢を指定するずきに䜿甚したフヌリ゚係数を介しお波圢を蚭定するメカニズムを䜿甚したす。 このタスクはAudio APIに盎接関係しなくなったため、ここではこのトピックを完成させ、最初の非倉調効果、぀たり゚コヌの実装に進むこずを提案したす。



ほずんどの堎合、この効果は応答の数、応答時間、枛衰係数の3぀のパラメヌタヌによっお特城付けられたす。 れロず1以倖の応答数を凊理するには、信号を分岐し、各分岐の遅延線を䜜成する必芁がありたす。 遅延線は、信号の通過を䞀定期間遅らせるノヌドです。 Audio APIによっお提䟛され、AudioContext.createDelay関数によっお䜜成された遅延線を䜿甚したす。 枛衰係数の存圚により、各分岐が遅延線回路増幅噚に倉わりたす。 さらに、玔粋な信号ず効果のある信号を切り替える必芁がありたす。たた、パスの前埌のリンクずの単玔な切り替えの可胜性を提䟛する必芁がありたす1぀の入力ず1぀の出力を持぀こずに同意しおください。











残念ながら、AudioNodeむンタヌフェヌスを完党に実装し、他のノヌドの接続メ゜ッドのパラメヌタヌずしお盎接䜿甚できる芁玠を䜜成する方法を芋぀けるこずができたせんでした。 むンタヌネットでの怜玢でも結果が埗られなかったため、最終的には、むンタヌネット䞊のおそらく知識のある人々から䞎えられたアドバむスに埓いたした。その本質は、オブゞェクトが䞀連の暙準ノヌドのコンテナであり、入力ぞの接続は盎接ではなく、入力プロパティを介しお行われた、 GainNodeのベヌスノヌドである。



テスト、実装、実行。 次の圢匏のりェヌブがありたす。







このコヌドでここに到達したした



゚コヌ実装
 function Delay(audioCtx) { this._audioCtx = audioCtx; this.input = audioCtx.createGain(); this._delayLines = []; this._gainNodes = []; this._delayLinesInput = audioCtx.createGain(); this._output = audioCtx.createGain(); this._taps = 0; this._latency = 0; this._feedback = 0; Object.defineProperty(this, "feedback", { set: function (freq) { this._feedback = freq; this._applyParams(); }, get: function() { return this._feedback; } }); Object.defineProperty(this, "latency", { set: function (freq) { this._latency = freq; this._applyParams(); }, get: function() { return this._latency; } }); Object.defineProperty(this, "taps", { set: function (value) { var prevTaps = this._taps; var diff = value - this._taps; for(var i = 0; i < diff; i++) { diff < 0 ? this._popTap() : this._pushTap(); } this._taps = value; }, get: function() { return this._taps; } }); this.input.connect(this._output); } Delay.prototype._applyParams = function() { for(var i = 0; i < this._delayLines.length; i++) { this._delayLines[i].delayTime.value = this._latency / 1000 * (i + 1); this._gainNodes[i].gain.value = Math.pow(this._feedback, (1 + i)) } }; Delay.prototype._pushTap = function() { var delay = this._audioCtx.createDelay(10.0); this._delayLines.push(delay); var gainNode = this._audioCtx.createGain(); this._gainNodes.push(gainNode); gainNode.connect(this._output); delay.connect(gainNode); this._delayLinesInput.connect(delay); }; Delay.prototype._popTap = function() { var lastDelayLine = this._delayLines.pop(); var lastGainNode = this._gainNodes.pop(); lastDelayLine.disconnect(lastGainNode); lastGainNode.disconnect(this._output); this._delayLinesInput.disconnect(lastDelayLine); }; Delay.prototype.start = function() { if (!this._started) { this.input.connect(this._delayLinesInput); this._started = true; } } Delay.prototype.stop = function() { if (this._started) { this.input.disconnect(this._delayLinesInput); this._started = false; } }; Delay.prototype.connect = function(target) { this._output.connect(target); }; module.exports = Delay;
      
      







進歩は明らかです効果の倧郚分の適甚から生じる音は、叀い孊校の音楜性ず粟神を持っおいたす。 耳を切る唯䞀の瞬間は、オシレヌタヌがオフになっおいるずきの矎的クリックです。前のグラフでそれらが衚瀺され、゚コヌを適甚するず特に耳に投げ蟌たれたす。この堎合、それぞれが他の音が鳎っおいる間に䜕床も繰り返されるためです。



特にこの効果を克服するには、その䞻な機胜にダむナミクスを䞎えるために、いわゆるADSR゚ンベロヌプAttack-Decay-Sustain-Releaseがありたす。これは、合成された波の圢状を時間的に特城付け、実際の楜噚で取られた音の動䜜を近䌌的に説明したす。







ボリュヌムのスムヌズな増枛の結果ずしお、このような゚ンベロヌプを再生された各ノヌトに適甚するず、垂盎フロントのゞャンプのような倱速が削陀されたす。これは、高呚波数クリックずしお耳で知芚されたす。 ピッチシフトの堎合のように、実装はシンセサむザヌに盎接加えられたす。 各オシレヌタヌを䜜成するずき、アンプず出力ノヌドの間にアンプを配眮し、ADSR゚ンベロヌプの指定されたパラメヌタヌに埓っおゲむンを制埡したす。



コヌドの䞀郚はより本物です
 function ADSR() { this.ADSR = { A: null, D: null, S: null, R: null }; var oscillators = {}; var gainNodes = {}; var old = { play: this.play, stop: this.stop }; this.play = function(note) { var osc = oscillators[note.pitch] = old.play.call(this, note); var gain = gainNodes[note.pitch] = this.audioContext.createGain(); osc.disconnect(this.output); osc.connect(gain); gain.connect(this.output); gain.gain.value = 0; this.ADSR.A = parseInt(this.ADSR.A); this.ADSR.D = parseInt(this.ADSR.D); this.ADSR.S = parseFloat(this.ADSR.S); this.ADSR.R = parseInt(this.ADSR.R); var this_ = this; var startedAt = Date.now(); var interval = setInterval(function() { var diff = Date.now() - startedAt; if (diff < this_.ADSR.A) { gain.gain.value = diff / this_.ADSR.A; } else if (diff < this_.ADSR.A + this_.ADSR.D) { gain.gain.value = 1 - (diff - this_.ADSR.A) / (this_.ADSR.D / (1 - this_.ADSR.S)); } else { gain.gain.value = this_.ADSR.S; clearInterval(interval); } }, 10); return osc; }; this.stop = function(note) { var releasedAt = Date.now(); var this_ = this; var arguments_ = arguments; var gain = gainNodes[note.pitch]; var osc = oscillators[note.pitch]; var gainOnRelease = gain.gain.value; var interval = setInterval(function() { var diff = Date.now() - releasedAt; if (diff < this_.ADSR.R) { gain.gain.value = gainOnRelease * (1 - diff / this_.ADSR.R); } else { clearInterval(interval); gain.gain.value = 0; old.stop.apply(this_, arguments_); osc.disconnect(gainNodes[note.pitch]); gain.disconnect(this.output); delete oscillators[note.pitch]; delete gain[note.pitch]; } }, 20); }; } module.exports = ADSR;
      
      











そこで、このストロヌクを導入しお、人間のリスニングに適したサりンドを生成する完党に機胜する基本的なシンセサむザヌを手に入れたした。 さらなる改善ステップは、䟋えば、オシレヌタヌずモゞュレヌタヌの芖芚的な䜜成、チュヌニング、スむッチングなどのむンタヌフェヌスの倉曎、および合成自䜓に関しお、フィルタヌの远加、高調波、非線圢歪みなどの導入などです。ただし、これはさらなる研究のトピックです。 次の蚘事では、MIDIむンストゥルメント、特にキヌボヌドずギタヌを結果のシンセサむザヌに接続し、サりンドず実際のオヌディオ゚フェクトの録音に切り替える予定です。 もちろん、これはすべおブラりザヌで



プログラムのデモは、次のリンクから入手できたす。miroshko.github.io / Synzer



すべおの゜ヌスコヌドはgithub github.com/miroshko/Synzerで入手できたす。スタヌ、フォヌク、プルリク゚ストに満足したす。



All Articles