UNPKG

webdaw-modules

Version:

a set of modules for building a web-based DAW

616 lines (532 loc) 19.6 kB
function songUpdate() { "use strict"; var // import getPosition, // -> defined in position.js parseTimeEvents, // -> defined in parse_time_events.js parseEvents, // -> defined in parse_events.js getInstrument, // -> defined in instrument_manager.js scheduledTasks, // -> defined in open_module.js update, update2; update = function(song, updateTimeEvents) { if (sequencer.playing === true) { scheduledTasks.updateSong = function() { update2(song, updateTimeEvents); }; return; } //console.log('not here while playing'); // console.log('update song'); update2(song, updateTimeEvents); }; update2 = function(song, updateTimeEvents) { //console.log('update song'); //console.time('update song'); var i, id1, id2, id3, tmp, track, part, event, note, dirtyEvents, dirtyNotes, newEvents = [], changedEvents = [], removedEvents = [], recordedEvents = [], newNotes = [], changedNotes = [], removedNotes = [], newParts = [], changedParts = [], removedParts = [], newTracks = [], changedTracks = [], removedTracks = [], eventsMidiAudioMetronome = [], eventsMidiTime = [], events = [], midiEvents = [], audioEvents = [], notes = [], parts = [], tracks = [], eventsById = {}, notesById = {}, partsById = {}; if (updateTimeEvents === true) { //console.log('update time events'); parseTimeEvents(song); } for (id1 in song.tracksById) { if (song.tracksById.hasOwnProperty(id1)) { track = song.tracksById[id1]; // console.log('song update track', track.needsUpdate); if (track.needsUpdate === true) { track.update(); } for (id2 in track.partsById) { if (track.partsById.hasOwnProperty(id2)) { part = track.partsById[id2]; // console.log(part.id, part.needsUpdate, part.dirtyEvents); if (part.needsUpdate === true) { // console.log('song update calls part.update()'); part.update(); } //console.log(part); dirtyEvents = part.dirtyEvents; for (id3 in dirtyEvents) { if (dirtyEvents.hasOwnProperty(id3)) { event = dirtyEvents[id3]; switch (event.state) { case "new": newEvents.push(event); break; case "recorded": recordedEvents.push(event); break; case "changed": changedEvents.push(event); break; case "removed": removedEvents.push(event); delete part.eventsById[id3]; break; } } } dirtyNotes = part.dirtyNotes; for (id3 in dirtyNotes) { if (dirtyNotes.hasOwnProperty(id3)) { note = dirtyNotes[id3]; //console.log(note.state); switch (note.state) { case "new": newNotes.push(note); break; case "changed": changedNotes.push(note); break; case "removed": removedNotes.push(note); delete part.notesById[id3]; break; } } } part.dirtyEvents = {}; part.dirtyNotes = {}; /* if(part.state === 'new' && part.track !== track){ part.state = 'removed'; } console.log(part.state, part.track); */ if (part.state !== "removed") { notes = notes.concat(part.notes); events = events.concat(part.events); } else { removedNotes = removedNotes.concat(part.notes); removedEvents = removedEvents.concat(part.events); } switch (part.state) { case "new": newParts.push(part); partsById[part.id] = part; break; case "changed": //console.log(part.id); changedParts.push(part); partsById[part.id] = part; break; case "removed": removedParts.push(part); delete track.partsById[part.id]; break; } } } //events = events.concat(track.events); //notes = notes.concat(track.notes); parts = parts.concat(track.parts); switch (track.state) { case "clean": track.index = tracks.length; tracks.push(track); break; case "new": newTracks.push(track); track.state = "clean"; track.index = tracks.length; tracks.push(track); break; case "changed": changedTracks.push(track); track.state = "clean"; track.index = tracks.length; tracks.push(track); break; case "removed": removedTracks.push(track); delete song.tracksById[track.id]; break; } } } for (i = removedEvents.length - 1; i >= 0; i--) { event = removedEvents[i]; event.state = "clean"; } for (i = removedNotes.length - 1; i >= 0; i--) { note = removedNotes[i]; note.state = "clean"; } for (i = removedParts.length - 1; i >= 0; i--) { part = removedParts[i]; part.state = "clean"; } // calculate the ticks position of the recorded events if (recordedEvents.length > 0) { parseRecordedEvents(song, recordedEvents); } events.sort(function(a, b) { return a.sortIndex - b.sortIndex; }); notes.sort(function(a, b) { return a.ticks - b.ticks; }); parts.sort(function(a, b) { return a.ticks - b.ticks; }); for (i = events.length - 1; i >= 0; i--) { event = events[i]; eventsById[event.id] = event; if (event.type === "audio") { audioEvents.push(event); } else { midiEvents.push(event); } } for (i = notes.length - 1; i >= 0; i--) { note = notes[i]; notesById[note.id] = note; } if (updateTimeEvents === false) { //console.log(newEvents); //console.log(tmp.length, events.length, newEvents.length, changedEvents.length, song.timeEvents.length, song.metronome.events.length); tmp = song.timeEvents.concat(newEvents, changedEvents); parseEvents(song, tmp); tmp = [].concat(newNotes, changedNotes); parseMidiNotes(song, tmp); tmp = [].concat(newParts, changedParts); parseParts(song, tmp); } else { // if time events have changed we need to update everything! tmp = song.timeEvents.concat(events); parseEvents(song, tmp); parseMidiNotes(song, notes); parseParts(song, parts); } /* console.log(' Song.update()'); console.log('new tracks', newTracks.length); console.log('new parts', newParts.length); console.log('new events', newEvents.length); console.log('changed tracks', changedTracks.length); console.log('changed parts', changedParts.length); console.log('changed events', changedEvents.length); console.log('removed tracks', removedTracks.length); console.log('removed parts', removedParts.length); console.log('removed events', removedEvents.length); console.log('all events', events.length); console.log('all parts', parts.length); console.log('all tracks', tracks.length); console.log('time events', song.timeEvents.length); console.log('--------'); */ /* if(changedEvents.length > 0){ console.log('changed events', changedEvents.length); console.log('changed notes', changedNotes.length); } */ checkDuration(song); // check if we need to generate new metronome events, metronome.update() calls parseMetronomeEvents() if (song.metronome.bars !== song.bars) { //song.metronome.update(song.metronome.bars, song.bars); song.metronome.update(); } else if (updateTimeEvents === true) { song.metronome.update(); } eventsMidiAudioMetronome = [].concat(midiEvents, audioEvents, song.metronome.events); eventsMidiAudioMetronome.sort(function(a, b) { //return a.sortIndex - b.sortIndex; return a.ticks - b.ticks; }); eventsMidiTime = [].concat(events, song.timeEvents); eventsMidiTime.sort(function(a, b) { return a.ticks - b.ticks; //return a.sortIndex - b.sortIndex; }); song.eventsMidiAudioMetronome = eventsMidiAudioMetronome; // all midi, audio and metronome events song.eventsMidiTime = eventsMidiTime; // all midi events plus time events song.events = events; // all events excluding tempo and time signature events and metronome ticks song.midiEvents = midiEvents; // all midi events excluding metronome events song.audioEvents = audioEvents; song.notes = notes; song.parts = parts; song.tracks = tracks; song.numEvents = events.length; song.numNotes = notes.length; song.numParts = parts.length; song.numTracks = tracks.length; song.eventsById = eventsById; song.notesById = notesById; song.partsById = partsById; song.newEvents = newEvents; song.changedEvents = changedEvents; song.removedEvents = removedEvents; song.newNotes = newNotes; song.changedNotes = changedNotes; song.removedNotes = removedNotes; song.newParts = newParts; song.changedParts = changedParts; song.removedParts = removedParts; // update all dependent objects song.playhead.updateSong(); song.playheadRecording.updateSong(); song.scheduler.updateSong(); song.scheduler.reschedule(); song.followEvent.updateSong(); if (song.grid !== undefined) { song.grid.update(); } if (song.keyEditor !== undefined) { song.keyEditor.updateSong({ numBars: song.bars, newEvents: newEvents, changedEvents: changedEvents, removedEvents: removedEvents, newNotes: newNotes, changedNotes: changedNotes, removedNotes: removedNotes, newParts: newParts, changedParts: changedParts, removedParts: removedParts, }); } //console.timeEnd('update song') }; function checkDuration(song, trim) { var lastEvent = song.lastEventTmp, position, key; //console.log('checkDuration', lastEvent.barsAsString,lastEvent.bar,song.lastBar); //console.log(lastEvent); //console.log(song.autoSize); if (song.autoSize === false) { // don't allow the song to grow song.lastBar = song.bars; } else if (trim) { // remove bars that don't contain any events(called via song.trim()) song.lastBar = lastEvent.bar; } else { // grow if needed song.lastBar = Math.max(song.lastBar, lastEvent.bar); } song.bars = parseInt(song.lastBar); position = getPosition(song, ["barsandbeats", song.bars, lastEvent.nominator, lastEvent.numSixteenth, lastEvent.ticksPerSixteenth, true]); //console.log(song.bars, lastEvent.nominator, lastEvent.numSixteenth, lastEvent.ticksPerSixteenth); song.durationTicks = position.ticks; song.durationMillis = position.millis; //console.log(song.bars, '->', position.barsAsString, song.durationMillis, song.durationTicks); // update song.lastEvent for (key in position) { if (position.hasOwnProperty(key)) { //console.log(key, position[key]) song.lastEvent[key] = position[key]; } } //console.log(song.name, song.durationTicks, song.durationMillis, song.bars); } function parseMetronomeEvents(song, events) { //console.log('parseMetronomeEvents', events.length); var tmp = events.concat(song.timeEvents); parseEvents(song, tmp); events = events.concat(song.events); events.sort(function(a, b) { return a.sortIndex - b.sortIndex; }); //console.log(1,song.allEvents.length); song.eventsMidiAudioMetronome = [].concat(events); //console.log(2,song.allEvents.length); //console.log(song.allEvents); // song.playhead.updateSong(); // song.scheduler.updateSong(); // song.scheduler.reschedule(); // song.followEvent.updateSong(); } function parseParts(song, parts) { var i, part; //console.log(' → parse parts', parts.length); for (i = parts.length - 1; i >= 0; i--) { part = parts[i]; //part.update(); //part.track.update(); part.startPosition = song.getPosition("ticks", part.start.ticks); part.endPosition = song.getPosition("ticks", part.end.ticks); part.start.millis = part.startPosition.millis; part.end.millis = part.endPosition.millis; part.duration.millis = part.end.millis - part.start.millis; part.state = "clean"; //console.log('s', part.start.ticks, 'e', part.end.ticks); //console.log('s', part.startPosition.barsAsString, 'e', part.endPosition.barsAsString); } } function parseMidiNotes(song, notes) { var i, note; //console.log(' → parseMidiNotes', notes.length); for (i = notes.length - 1; i >= 0; i--) { note = notes[i]; //console.log(note); if (note.endless === true) { note.durationTicks = sequencer.ticks - note.noteOn.ticks; note.durationMillis = sequencer.millis - note.noteOn.millis; } else { note.durationTicks = note.noteOff.ticks - note.noteOn.ticks; note.durationMillis = note.noteOff.millis - note.noteOn.millis; } note.ticks = note.noteOn.ticks; note.millis = note.noteOn.millis; note.number = note.noteOn.noteNumber; note.state = "clean"; } } function parseRecordedEvents(song, events) { var i, timeData, position, event, time, timestamp = song.recordTimestamp, startMillis = song.recordStartMillis, totalTime = startMillis, maxi = events.length, playhead = song.playheadRecording; //if(startMillis < 0){ // playhead.set('millis', 0); //}else{ playhead.set("millis", startMillis); //} //console.log(song, events, timestamp); //console.log('parseRecordedEvents', timestamp, startMillis); for (i = 0; i < maxi; i++) { event = events[i]; time = event.recordMillis - timestamp + startMillis; position = playhead.update("millis", time - totalTime); // update by supplying the diff in millis totalTime = time; timeData = sequencer.getNiceTime(position.millis); //console.log(event.ticks, position.ticks); //console.log(event.recordMillis, event.recordMillis - timestamp); event.ticks = position.ticks; event.bpm = position.bpm; event.factor = position.factor; event.nominator = position.nominator; event.denominator = position.denominator; event.ticksPerBar = position.ticksPerBar; event.ticksPerBeat = position.ticksPerBeat; event.ticksPerSixteenth = position.ticksPerSixteenth; event.numSixteenth = position.numSixteenth; event.millisPerTick = position.millisPerTick; event.secondsPerTick = position.secondsPerTick; event.millis = position.millis; event.seconds = position.millis / 1000; event.hour = timeData.hour; event.minute = timeData.minute; event.second = timeData.second; event.millisecond = timeData.millisecond; event.timeAsString = timeData.timeAsString; event.timeAsArray = timeData.timeAsArray; event.bar = position.bar; event.beat = position.beat; event.sixteenth = position.sixteenth; event.tick = position.tick; event.barsAsString = position.bar + ":" + position.beat + ":" + position.sixteenth + ":" + position.tick; event.barsAsArray = [position.bar, position.beat, position.sixteenth, position.tick]; event.state = "clean"; } song.recordStartMillis = undefined; song.recordTimestamp = undefined; } // not in use! function sortEvents(events) { var maxi = events.length, i, event, lastTick = -100000, buffer, newOrder = []; for (i = 0; i < maxi; i++) { event = events[i]; if (buffer === undefined) { buffer = []; } buffer.push(event); if (event.ticks !== lastTick) { if (buffer.length > 1) { // console.log('unsorted', buffer.length); // buffer.forEach(function(e){ // console.log(e.ticks, e.type, e.data1, e.data2); // }); buffer.sort(function(a, b) { // question is: comes a after b if (b.type === 144 && a.type === 128) { // note off before note on return false; } else if (b.type === 144 && a.type === 176 && a.data1 === 64 && a.data2 === 127) { // sustain pedal down before note on return false; } else if (b.type === 176 && b.data1 === 64 && b.data2 === 127 && a.type === 128) { // note off before sustain pedal down return false; } else if (b.type === 128 && a.type === 176 && a.data1 === 64 && a.data2 === 0) { // sustain pedal up before note off -> for better performance, the note off event doesn't get added to the sustainPedalSamples array return false; } else if (b.type === 144 && a.type === 176 && a.data1 === 64 && a.data2 === 0) { // sustain pedal up before note on return false; } else if (a.type === 176 && a.data1 === 64 && a.data2 === 0 && b.type === 176 && b.data1 === 64 && b.data2 === 127) { // sustain pedal up should come before sustain pedal up return false; } else { return true; } }); // console.log('sorted'); // buffer.forEach(function(e){ // console.log(e.ticks, e.type, e.data1, e.data2); // }); // console.log('---'); newOrder = newOrder.concat(buffer); buffer = undefined; } else { newOrder.push(buffer[0]); } } lastTick = event.ticks; } } sequencer.protectedScope.update = update; sequencer.protectedScope.checkDuration = checkDuration; sequencer.protectedScope.parseMetronomeEvents = parseMetronomeEvents; sequencer.protectedScope.addInitMethod(function() { getPosition = sequencer.protectedScope.getPosition; parseEvents = sequencer.protectedScope.parseEvents; parseTimeEvents = sequencer.protectedScope.parseTimeEvents; getInstrument = sequencer.protectedScope.getInstrument; scheduledTasks = sequencer.protectedScope.scheduledTasks; }); }