UNPKG

webdaw-modules

Version:

a set of modules for building a web-based DAW

542 lines (451 loc) 17.6 kB
// not in use! (function(){ 'use strict'; var //import typeString, // → defined in util.js remap, // → defined in util.js timedTasks, // → defined in open_module.js createReverb, // → defined in effects.js objectForEach, // → defined in util.js isEmptyObject, // → defined in util.js transpose, // → defined in transpose.js getEqualPowerCurve, // → defined in util.js dispatchEvent, // → defined in song.js setKeyScalingPanning, setKeyScalingRelease, setRelease, transposeSamples, processEvent, stopSustain, playNote, stopNote, allNotesOff, allNotesOffPart, update, hasScheduledSamples, reschedule, unschedule; setKeyScalingPanning = function(start, end){ //console.log('keyScalingPanning', start, end); var i, data, numSamples = this.sampleData.length, panStep, currentPan; if(start === false){ for(i = 0; i < numSamples; i++){ data = this.sampleData[i]; data.panning = false; } } if(isNaN(start) === false && isNaN(end) === false){ panStep = (end - start)/this.numNotes; currentPan = start; for(i = 0; i < numSamples; i++){ data = this.sampleData[i]; data.panning = true; data.panPosition = currentPan; //console.log(currentPan, panStep, highestNote, lowestNote, data.noteNumber); currentPan += panStep; } } }; setRelease = function(millis, envelope){ if(millis === undefined){ return; } this.releaseEnvelope = envelope || this.releaseEnvelope; this.keyScalingRelease = undefined; var i, data, numSamples = this.sampleData.length; for(i = 0; i < numSamples; i++){ data = this.sampleData[i]; data.release = true; data.release_duration = millis; data.release_envelope = this.releaseEnvelope; } this.releaseDuration = millis; }; setKeyScalingRelease = function(start, end, envelope){ var i, data, numSamples = this.sampleData.length, releaseStep, currentRelease; this.releaseEnvelope = envelope || this.releaseEnvelope; if(isNaN(start) === false && isNaN(end) === false){ this.keyScalingRelease = [start, end]; this.releaseDuration = 0; releaseStep = (end - start)/this.numNotes; currentRelease = start; for(i = 0; i < numSamples; i++){ data = this.sampleData[i]; data.release_duration = currentRelease; data.release_envelope = currentRelease; //console.log(currentRelease, releaseStep, data.noteNumber); currentRelease += releaseStep; } } }; transposeSamples = function(semitones, cb1, cb2){ if(transpose === undefined){ console.log('transpose is still experimental'); return; } var numSamples = this.sampleData.length; function loop(num, samples){ var data; if(cb2){ cb2('transposing sample ' + (num + 1) + ' of ' + numSamples); } //console.log(num, numSamples); if(num < numSamples){ data = samples[num]; setTimeout(function(){ transpose(data.buffer, semitones, function(transposedBuffer){ data.buffer = transposedBuffer; loop(++num, samples); }); }, 10); }else{ if(cb1){ console.log('ready'); cb1(); } } } loop(0, this.sampleData); }; // called when midi events arrive from a midi input, from processEvent or from the scheduler processEvent = function(midiEvent){ //console.log(midiEvent.type, midiEvent.velocity); var type = midiEvent.type, data1, data2, track, output; //seconds = seconds === undefined ? 0 : seconds; if(midiEvent.time === undefined){ midiEvent.time = 0; } if(type === 128 || type === 144){ if(type === 128){ if(this.sustainPedalDown === true){ midiEvent.sustainPedalDown = true; } this.stopNote(midiEvent); }else{ this.playNote(midiEvent); } }else if(type === 176){ //return; data1 = midiEvent.data1; data2 = midiEvent.data2; if(data1 === 64){ // sustain pedal //console.log(this.sustainPedalDown, data1, data2) if(data2 === 127){ this.sustainPedalDown = true; //console.log('sustain pedal down',this.track.song.id); dispatchEvent(this.track.song, 'sustain_pedal', 'down'); }else if(data2 === 0){ this.sustainPedalDown = false; //console.log('sustain pedal up'); dispatchEvent(this.track.song, 'sustain_pedal', 'up'); this.stopSustain(midiEvent.time); } }else if(data1 === 10){ // panning // panning is *not* exactly timed -> not possible (yet) with WebAudio track = this.track; //console.log(data2, remap(data2, 0, 127, -1, 1)); track.setPanning(remap(data2, 0, 127, -1, 1)); }else if(data1 === 7){ // volume track = this.track; output = track.output; output.gain.setValueAtTime(data2/127, midiEvent.time); /* //@TODO: this should be done by a plugin if(track.volumeChangeMethod === 'linear'){ output.gain.linearRampToValueAtTime(data2/127, seconds); }else if(track.volumeChangeMethod === 'equal_power'){ volume1 = track.getVolume(); volume2 = data2/127; if(volume1 > volume2){ values = getEqualPowerCurve(100, 'fadeOut', volume2); }else{ values = getEqualPowerCurve(100, 'fadeIn', volume2); } now = sequencer.getTime(); output.gain.setValueCurveAtTime(values, seconds, seconds + 0.05); }else{ output.gain.setValueAtTime(data2/127, seconds); } */ } } }; stopSustain = function(seconds){ var midiNote, scheduledSamples = this.scheduledSamples, sustainPedalSamples = this.sustainPedalSamples; objectForEach(sustainPedalSamples, function(sample){ if(sample !== undefined){ midiNote = sample.midiNote; midiNote.noteOn.sustainPedalDown = undefined; midiNote.noteOff.sustainPedalDown = undefined; sample.stop(seconds, function(sample){ //console.log('stopped sustain pedal up:', sample.id, sample.sourceId); scheduledSamples[sample.sourceId] = null; delete scheduledSamples[sample.sourceId]; //delete sustainPedalSamples[sample.sourceId]; }); } }); this.sustainPedalSamples = {}; }; playNote = function(midiEvent){ var sample, sourceId; if(!midiEvent.midiNote){ if(sequencer.debug){ console.warn('playNote() no midi note'); } return; } sourceId = midiEvent.midiNote.id; sample = this.scheduledSamples[sourceId]; //console.log('start', sourceId); if(sample !== undefined){ //console.log('already scheduled', sourceId); sample.unschedule(0); } sample = this.createSample(midiEvent); // add some extra attributes to the sample sample.addData({ midiNote: midiEvent.midiNote, noteName: midiEvent.midiNote.note.fullName, sourceId: sourceId }); this.scheduledSamples[sourceId] = sample; sample.start(midiEvent); }; stopNote = function(midiEvent){ if(midiEvent.midiNote === undefined){ if(sequencer.debug){ console.warn('stopNote() no midi note'); } return; } var sourceId = midiEvent.midiNote.id, sample = this.scheduledSamples[sourceId], scheduledSamples = this.scheduledSamples, sustainPedalSamples = this.sustainPedalSamples; // if(this.song && this.song.bar >= 6 && this.track.name === 'Sonata # 3'){ // console.log('stopNote', midiEvent, seconds, sequencer.getTime()); // } //console.log(midiEvent.sustainPedalDown); if(midiEvent.sustainPedalDown === true){ // while sustain pedal is pressed, bypass note off events //console.log('sustain'); sustainPedalSamples[sourceId] = sample; return; } if(sample === undefined){ // if(sequencer.debug){ // console.log('no sample scheduled (anymore) for this midiEvent', sourceId, seconds); // } return; } sample.stop(midiEvent.time, function(){ scheduledSamples[sourceId] = null; delete scheduledSamples[sourceId]; }); }; hasScheduledSamples = function(){ return isEmptyObject(this.scheduledSamples); }; function unscheduleCallback(sample){ //console.log(sample.id, 'has been unscheduled'); sample = null; } reschedule = function(song){ var min = song.millis, max = min + (sequencer.bufferTime * 1000), //max2 = min + 20, scheduledSamples = this.scheduledSamples, id, note, sample; for(id in scheduledSamples){ if(scheduledSamples.hasOwnProperty(id)){ sample = scheduledSamples[id]; // the sample note = sample.midiNote; // the midi note if(note === undefined || note.state === 'removed'){ sample.unschedule(0, unscheduleCallback); delete scheduledSamples[id]; }else if( note.noteOn.millis >= min && note.noteOff.millis < max && sample.noteName === note.fullName ){ // nothing has changed, skip continue; }else{ //console.log('unscheduled', id); delete scheduledSamples[id]; sample.unschedule(null, unscheduleCallback); } } } /* objectForEach(this.scheduledEvents, function(event, eventId){ if(event === undefined || event.state === 'removed'){ delete sequencer.timedTasks['event_' + eventId]; delete this.scheduledEvents[eventId]; }else if((event.millis >= min && event.millis < max2) === false){ delete sequencer.timedTasks['event_' + eventId]; delete this.scheduledEvents[eventId]; } }); */ }; function loop(data, i, maxi, events){ var arg; for(i = 0; i < maxi; i++){ arg = data[i]; if(arg === undefined){ continue; }else if(arg.className === 'MidiNote'){ events.push(arg.noteOn); }else if(typeString(arg) === 'array'){ loop(arg, 0, arg.length); } } } // stop specified events or notes, used by stopProcessEvent() unschedule = function(){ var args = Array.prototype.slice.call(arguments), events = [], i, e, id, sample; loop(args, 0, args.length, events); for(i = events.length - 1; i >= 0; i--){ e = events[i]; if(e.midiNote !== undefined){ // note on and note off events id = e.midiNote.id; sample = this.scheduledSamples[id]; if(sample !== undefined){ sample.unschedule(0, unscheduleCallback); delete this.scheduledSamples[id]; } }else if(e.className === 'MidiEvent'){ // other channel events id = e.id; delete timedTasks['event_' + id]; delete this.scheduledEvents[id]; } //console.log(id); } }; // stop all events and notes allNotesOff = function(){ var sample, sampleId, scheduledSamples = this.scheduledSamples; this.stopSustain(0); this.sustainPedalDown = false; //console.log(scheduledSamples); if(scheduledSamples === undefined || isEmptyObject(scheduledSamples) === true){ return; } for(sampleId in scheduledSamples){ if(scheduledSamples.hasOwnProperty(sampleId)){ //console.log('allNotesOff', sampleId); sample = scheduledSamples[sampleId]; if(sample){ sample.unschedule(0, unscheduleCallback); } } } this.scheduledSamples = {}; objectForEach(this.scheduledEvents, function(event, eventId){ delete timedTasks['event_' + eventId]; }); this.scheduledEvents = {}; }; allNotesOffPart = function(partId){ var sample, sampleId, scheduledSamples = this.scheduledSamples; // make this more subtle this.stopSustain(0); this.sustainPedalDown = false; //console.log(scheduledSamples); if(scheduledSamples === undefined || isEmptyObject(scheduledSamples) === true){ return; } for(sampleId in scheduledSamples){ if(scheduledSamples.hasOwnProperty(sampleId)){ //console.log('allNotesOff', sampleId); sample = scheduledSamples[sampleId]; if(sample){ sample.unschedule(0, unscheduleCallback); } } } this.scheduledSamples = {}; objectForEach(this.scheduledEvents, function(event, eventId){ delete timedTasks['event_' + eventId]; }); this.scheduledEvents = {}; }; update = function(value){ var sampleId, sample; //console.log(this.scheduledSamples); for(sampleId in this.scheduledSamples){ if(this.scheduledSamples.hasOwnProperty(sampleId)){ sample = this.scheduledSamples[sampleId]; if(sample){ sample.update(value); } } } }; function createAutoPanner(time){ /* var osc = context.createOscillator(); osc.frequency.value = 50; osc.type = 0; var gain = context.createGain(); gain.gain.value = 1; osc.connect(gain); gain.connect(context.destination); osc.start(); console.log(osc); return { getValue: function(){ return osc.frequency.getValueAtTime(time); } }; */ return { getValue: function(time){ return Math.sin(time * 2*Math.PI); } }; } sequencer.protectedScope.createAutoPanner = createAutoPanner; sequencer.protectedScope.setKeyScalingPanning = setKeyScalingPanning; sequencer.protectedScope.setKeyScalingRelease = setKeyScalingRelease; sequencer.protectedScope.setRelease = setRelease; sequencer.protectedScope.transposeSamples = transposeSamples; sequencer.protectedScope.processEvent = processEvent; sequencer.protectedScope.stopSustain = stopSustain; sequencer.protectedScope.playNote = playNote; sequencer.protectedScope.stopNote = stopNote; sequencer.protectedScope.allNotesOff = allNotesOff; sequencer.protectedScope.allNotesOffPart = allNotesOffPart; sequencer.protectedScope.update = update; sequencer.protectedScope.hasScheduledSamples = hasScheduledSamples; sequencer.protectedScope.reschedule = reschedule; sequencer.protectedScope.unschedule = unschedule; sequencer.protectedScope.addInitMethod(function(){ typeString = sequencer.protectedScope.typeString; timedTasks = sequencer.protectedScope.timedTasks; createReverb = sequencer.createReverb; objectForEach = sequencer.protectedScope.objectForEach; isEmptyObject = sequencer.protectedScope.isEmptyObject; transpose = sequencer.protectedScope.transpose; getEqualPowerCurve = sequencer.util.getEqualPowerCurve; remap = sequencer.util.remap; dispatchEvent = sequencer.protectedScope.songDispatchEvent; }); }());