UNPKG

jzz-synth-osc

Version:

Fallback MIDI-Out implementation

215 lines (194 loc) 6.23 kB
(function(global, factory) { /* istanbul ignore next */ if (typeof exports === 'object' && typeof module !== 'undefined') { factory.OSC = factory; module.exports = factory; } else if (typeof define === 'function' && define.amd) { define('JZZ.synth.OSC', ['JZZ'], factory); } else { factory(JZZ); } })(this, function(JZZ) { /* istanbul ignore next */ if (!JZZ) return; /* istanbul ignore next */ if (!JZZ.synth) JZZ.synth = {}; /* istanbul ignore next */ if (JZZ.synth.OSC) return; var _version = '1.2.8'; var _ac; function initAC() { if (!_ac) _ac = JZZ.lib.getAudioContext(); return !!_ac; } function Synth() { this.ac = _ac; this.dest = this.ac.destination; this.channels = []; this.channel = function(c) { if (!this.channels[c]) { this.channels[c] = new Channel(this); if (c == 9) this.channels[c].note = function(n) { if (!this.notes[n]) this.notes[n] = new Perc(n, this); return this.notes[n]; }; } return this.channels[c]; }; this.play = function(arr) { var b = arr[0]; var n = arr[1]; var v = arr[2]; /* istanbul ignore next */ if (b < 0 || b > 255) return; var c = b & 15; var s = b >> 4; if (s == 9) this.channel(c).play(n, v); else if (s == 8) this.channel(c).play(n, 0); else if (s == 0xb) { if (n == 0x78 || n == 0x7b) this.channel(c).allSoundOff(); else if (n == 0x40) this.channel(c).damper(!!v); } }; this.plug = function(dest) { try { this.ac = undefined; if (dest.context instanceof AudioContext) { this.ac = dest.context; this.dest = dest; } } catch (e) { this.ac = undefined; } if (!this.ac) { this.ac = _ac; this.dest = this.ac.destination; } }; } function Channel(synth) { this.synth = synth; this.notes = []; this.sustain = false; this.note = function(n) { if (!this.notes[n]) this.notes[n] = new Note(n, this); return this.notes[n]; }; this.play = function(n, v) { this.note(n).play(v); }; this.allSoundOff = function() { for (var n = 0; n < this.notes.length; n++) if (this.notes[n]) this.notes[n].stop(); }; this.damper = function(x) { if (!x && this.sustain != x) { for (var n = 0; n < this.notes.length; n++) if (this.notes[n] && this.notes[n].sustain) this.notes[n].stop(); } this.sustain = x; }; } function Note(n, c) { this.note = n; this.channel = c; this.freq = 440 * Math.pow(2,(n-69)/12); this.stop = function() { try { if (this.oscillator) this.oscillator.stop(0); this.oscillator = undefined; this.sustain = false; } catch (e) {/**/} }; this.play = function(v) { if (v || !this.channel.sustain) this.stop(); if (!v) { this.sustain = this.channel.sustain; return; } var ampl = v/127; this.oscillator = this.channel.synth.ac.createOscillator(); this.oscillator.type = 'sawtooth'; this.oscillator.frequency.setTargetAtTime(this.freq, this.channel.synth.ac.currentTime, 0.005); /* istanbul ignore next */ if (!this.oscillator.start) this.oscillator.start = this.oscillator.noteOn; /* istanbul ignore next */ if (!this.oscillator.stop) this.oscillator.stop = this.oscillator.noteOff; this.gain = this.channel.synth.ac.createGain(); var releaseTime = 2; var now = this.channel.synth.ac.currentTime; this.gain.gain.setValueAtTime(ampl, now); this.gain.gain.exponentialRampToValueAtTime(0.01*ampl, now + releaseTime); this.oscillator.connect(this.gain); this.gain.connect(this.channel.synth.dest); this.oscillator.start(0); }; } function Perc(n, c) { this.note = n; this.channel = c; this.freq = 200; this.stop = function() {}; this.play = function(v) { if (!v) return; var ampl = v/127; this.oscillator = this.channel.synth.ac.createOscillator(); this.oscillator.type = 'sine'; this.oscillator.frequency.setTargetAtTime(this.freq, this.channel.synth.ac.currentTime, 0.005); /* istanbul ignore next */ if (!this.oscillator.start) this.oscillator.start = this.oscillator.noteOn; /* istanbul ignore next */ if (!this.oscillator.stop) this.oscillator.stop = this.oscillator.noteOff; this.gain = this.channel.synth.ac.createGain(); var now = this.channel.synth.ac.currentTime; this.gain.gain.setValueAtTime(ampl, now); this.oscillator.connect(this.gain); this.gain.connect(this.channel.synth.dest); this.oscillator.start(0); this.oscillator.stop(this.channel.synth.ac.currentTime + 0.04); }; } var _synth = {}; var _noname = []; var _engine = {}; _engine._info = function(name) { if (!name) name = 'JZZ.synth.OSC'; return { type: 'Web Audio', name: name, manufacturer: 'virtual', version: _version }; }; _engine._openOut = function(port, name) { initAC(); /* istanbul ignore next */ if (!_ac) { port._crash('AudioContext not supported'); return; } var synth; if (typeof name !== 'undefined') { name = '' + name; if (!_synth[name]) _synth[name] = new Synth(); synth = _synth[name]; } else { synth = new Synth(); _noname.push(synth); } port.plug = function(dest) { synth.plug(dest); }; port._info = _engine._info(name); port._receive = function(msg) { synth.play(msg); }; port._resume(); }; JZZ.synth.OSC = function(name) { return JZZ.lib.openMidiOut(name, _engine); }; JZZ.synth.OSC.register = function(name) { return initAC() ? JZZ.lib.registerMidiOut(name, _engine) : false; }; JZZ.synth.OSC.version = function() { return _version; }; });