Moisés Gabriel Cachay Tello
Creator, destructor.
@xpktro - LimaJS
Puede verse esta presentación online visitando:
El código fuente mostrado aquí puede encontrarse en la siguiente dirección:
Previously on LimaJS...
var context = getAudioContext()
, frequencies = [220, 440, 880]
, currentTime = audioContext.currentTime
, duration = 1;
frequencies.forEach(function(frequency, index) {
var sineosc = context.createOscillator()
, startTime = currentTime + (index * duration);
sineosc.type = 'sine';
sineosc.frequency.value = frequency;
sineosc.connect(context.destination);
sineosc.start(startTime);
sineosc.stop(startTime + duration);
sineosc.onended = function(){ log('Finished ' + frequency + 'Hz tone.'); };
});
log('Playing octaves: ' + frequencies);
Oscilador (CCO)
Envolvente (AR CCA)
Filtro de paso
de banda (CCF)
Delay
class Synth {
start() {
this.context = getAudioContext();
this.createOscillator();
this.connectNodes();
}
createOscillator() {
this.oscillator = this.context.createOscillator();
this.oscillator.frequency.value = 440;
let oscillatorElement = document.getElementById('oscillator');
this.oscillator.type = oscillatorElement.value;
oscillatorElement.onchange = () => {
this.oscillator.type = oscillatorElement.value;
}
this.oscillator.start();
}
connectNodes() {
this.oscillator.connect(this.context.destination);
}
stop() {
this.context.close();
}
}
class Synth {
start() {
// ...
this.createAR();
this.connectNodes();
}
createAR() {
this.cca = this.context.createGain();
this.cca.gain.value = 0;
this.attack = 0.001;
this.release = 0.001;
}
connectNodes() {
this.oscillator.connect(this.cca);
this.cca.connect(this.context.destination);
}
setAttack(value) {
this.attack = (value / 127) * 2;
}
setRelease(value) {
this.release = (value / 127) * 2;
}
// ...
}
class Synth {
// ...
noteOn(midiNote) {
let now = this.context.currentTime;
this.cca.gain.cancelScheduledValues(0);
this.cca.gain.linearRampToValueAtTime(1, now + this.attack);
// https://en.wikipedia.org/wiki/MIDI_tuning_standard#Frequency_values
let frequency = Math.pow(2, (midiNote - 69) / 12) * 440;
this.oscillator.frequency.setValueAtTime(frequency, now);
}
noteOff() {
let now = this.context.currentTime;
this.cca.gain.cancelScheduledValues(0);
this.cca.gain.setValueAtTime(this.cca.gain.value, now);
this.cca.gain.linearRampToValueAtTime(0, now + this.release);
}
// ...
}
Es un estándar que define un protocolo de comunicaciones, interfaz digital y conectores para la comunicación entre dispositivos musicales.
En este taller, recibiremos mensajes MIDI emitidos por un dispositivo externo (controlador)
class MIDIHandler {
constructor() {
navigator
.requestMIDIAccess()
.then((access) => this.accessGranted(access))
.catch(log);
}
accessGranted(midiAccess) {
this.access = midiAccess;
let deviceSelector = document.getElementById('devices');
this.access.inputs.forEach((entry) => {
let option = document.createElement('option');
option.value = entry.id;
option.innerHTML = entry.name;
deviceSelector.appendChild(option);
});
}
}
class MIDIHandler {
// ...
useSynth(synth) {
this.currentNote = 0;
let selectedDevice = document.getElementById('devices').value;
let midiDevice = this.access.inputs.get(selectedDevice);
midiDevice.onmidimessage = (midiEvent) => {
let message = midiEvent.data[0]
, data1 = midiEvent.data[1]
, data2 = midiEvent.data[2];
switch (message) {
case 144:
// data1 = MIDI note | data2 = velocity
synth.noteOn(data1);
this.currentNote = data1;
log('received note ' + data1);
break;
case 128:
// data1 = MIDI note | data2 = aftertouch
if(this.currentNote === data1) {
synth.noteOff();
log('note off');
}
break;
case 176:
// Next slide...
}
}
}
}
case 176:
// data1 = CC number | data2 = value
switch (data1) {
case 74:
synth.setAttack(data2);
break;
case 71:
synth.setRelease(data2);
break;
}
log('control change ' + data1);
break;
synth = new Synth();
midi = new MIDIHandler();
// Para iniciar el sintetizador:
synth.start();
midi.useSynth(synth);
// Para detener el sintetizador:
synth.stop();
class Synth {
start() {
// ...
this.createFilter();
this.connectNodes();
}
createFilter() {
this.filter = this.context.createBiquadFilter();
this.filter.type = 'bandpass';
this.filter.frequency.value = 20000;
this.filter.Q.value = 0;
}
connectNodes() {
this.oscillator.connect(this.cca);
this.cca.connect(this.filter);
this.filter.connect(this.context.destination);
}
setFilterFrequency(value) {
this.filter.frequency.value = (value / 127) * 20000;
}
setFilterQ(value) {
this.filter.Q.value = (value / 127) * 10;
}
}
class MIDIHandler {
useSynth(synth) {
// ...
case 73:
synth.setFilterFrequency(data2);
break;
case 75:
synth.setFilterQ(data2);
break;
// ...
}
}
Un módulo de delay recibe una señal y retrasa su salida luego de un tiempo
Para obtener el efecto de eco, conectaremos la salida del delay a su propia entrada
DelayNode
Gain
Gain
In
Out
class Synth {
start() {
// ...
this.createFilter();
this.createDelay();
this.connectNodes();
}
createDelay() {
this.delay = this.context.createDelay(5);
this.delay.delayTime.value = 0;
this.feedbackGain = this.context.createGain();
this.feedbackGain.gain.value = 0;
this.delayGain = this.context.createGain();
this.delayGain.gain.value = 0;
}
connectNodes() {
// ...
this.filter.connect(this.delay);
this.delay.connect(this.feedbackGain);
this.feedbackGain.connect(this.delay);
this.delay.connect(this.delayGain);
this.delayGain.connect(this.context.destination);
}
}
class Synth {
// ...
setDelayAmount(value) {
this.delayGain.gain.value = value / 127;
}
setDelayRate(value) {
this.delay.delayTime.value = (value / 127) * 1.5;
}
setDelayFeedback(value) {
this.feedbackGain.gain.value = (value / 127) * 0.8;
}
// ...
}
class MIDIHandler {
useSynth(synth) {
// ...
case 72:
synth.setDelayAmount(data2);
break;
case 91:
synth.setDelayRate(data2);
break;
case 93:
synth.setDelayFeedback(data2);
break;
// ...
}
}
By Moisés Gabriel Cachay Tello