UNPKG

audio-source-composer

Version:

Audio Source Composer

265 lines (223 loc) 9.98 kB
import TrackIterator from "./TrackIterator"; import ProgramLoader from "../program/ProgramLoader"; export default class TrackPlayback extends TrackIterator { constructor(destination, song, startingTrackName = null, filterProgramCommand=null) { super(song, startingTrackName || song.getStartTrackName(), { destination, startTime: null, }, filterProgramCommand ); if (!destination || !destination.context) throw new Error("Invalid destination"); this.audioContext = destination.context; this.destination = destination; this.lastCurrentTime = null; this.startTime = null; this.seekLength = 1; this.active = false; this.activePrograms = []; this.endPromise = new Promise((resolve, reject) => { this.endResolve = resolve; }); // console.log('TrackPlayback', startingTrackName, song); } isActive() { return this.active; } /** Command Processing Interface **/ processCommandInstruction(instructionData, stats) { super.processCommandInstruction(instructionData, stats); } onExecuteProgram(trackStats, commandString, params) { const program = trackStats.program; if(!program) { console.warn("Failed to execute command on empty program: ", commandString, params, trackStats); // TODO: error state } else { // console.log(program.constructor.name, `(${commandString})`, params); program[commandString].apply(program, params); } } // onPlayTrack(trackStats, trackName, trackDuration=null, trackStartTime=0, trackFrequency=null) { // const subTrackStats = super.onPlayTrack(trackStats, trackName, trackDuration, trackStartTime, trackFrequency); // // subTrackStats.program = trackStats.program; // subTrackStats.destination = trackStats.destination; // } onLoadProgram(trackStats, program) { const oldProgram = trackStats.program; // const oldDestination = trackStats.destination; let programInstance; if(Array.isArray(program)) { programInstance = ProgramLoader.loadInstance(program[0], program[1]); delete trackStats.programID; } else { programInstance = this.song.programLoadInstanceFromID(program); trackStats.programID = program; } trackStats.program = programInstance; // useDestination allows for audio processing (i.e. effects) // if(typeof programInstance.useDestination === 'function') // trackStats.destination = programInstance.useDestination(oldDestination); // useProgram allows for both note processing and audio processing effects if(typeof programInstance.useProgram === 'function') programInstance.useProgram(oldProgram); this.activePrograms.push(programInstance); } /** Actions **/ startTrackIteration(stats) { super.startTrackIteration(stats) if(!stats.playingIndices) stats.playingIndices = []; const playingIndices = stats.playingIndices; stats.onInstructionStart = (startTime) => { if(!this.active) return; const waitTime = (startTime - this.audioContext.currentTime) * 1000; const iEvent = { type: 'instruction:play', trackName: stats.trackName, index: stats.currentIndex, playingIndices: playingIndices, } setTimeout(() => { if(!this.active) return; playingIndices.push(iEvent.index); this.song.dispatchEvent(iEvent) // console.log('onInstructionStart', startTime, waitTime); }, waitTime); // console.log('playingIndices.push', playingIndices); }; stats.onInstructionEnd = (index) => { if(!this.active) return; const iEvent = { type: 'instruction:stop', trackName: stats.trackName, index, playingIndices, } // console.log('onInstructionEnd', playingIndices, event); const i = playingIndices.indexOf(iEvent.index); if(i !== -1) { playingIndices.splice(i, 1); // console.log("Removing playing instruction: ", event.index, playingIndices) }else console.warn("Playing instruction not found: ", iEvent.index, playingIndices) this.song.dispatchEvent(iEvent) }; // this.onEvent({ // type: 'track:start', // playback: this, // trackStats // }); } play(startPosition=null) { // console.log('TrackPlayback.play', startPosition); const stats = this.activeIterators[0].stats; this.active = true; this.startTime = this.audioContext.currentTime; // this.audioContext.currentTime if(startPosition !== null) this.startTime -= startPosition; stats.startTime = this.startTime; // TODO: redundant? this.seekInterval = setInterval(() => this.renderPlayback(), (this.seekLength * 1000) / 2); this.renderPlayback(); } playAtStartingTrackIndex(index, callback=null) { const stats = this.activeIterators[0].stats; const iterator = this.instructionGetIterator({ trackName: stats.trackName, }); iterator.seekToIndex(index, callback); const startPosition = iterator.getPositionInSeconds(); this.play(startPosition); // console.log('playAtStartingTrackIndex', index, startPosition); // this.seekToPosition(startPosition, callback); } getPlaybackPosition() { return this.audioContext.currentTime - this.startTime; } // getPositionInSeconds() { // return this.startTime; // } async awaitPlaybackReachedEnd() { return await this.endPromise; } renderPlayback() { const currentPositionSeconds = this.audioContext.currentTime - this.startTime; if(this.audioContext.currentTime === this.lastCurrentTime) console.warn("audioContext.currentTime is stalling: ", this.audioContext.currentTime, this.lastCurrentTime); this.lastCurrentTime = this.audioContext.currentTime; // console.log('renderPlayback()', {currentPositionSeconds}, this.active, this.hasReachedEnd(), this); if(!this.active || this.hasReachedEnd()) { clearInterval(this.seekInterval); const endPositionSeconds = this.getEndPositionInSeconds(); const timeTillFinished = endPositionSeconds - currentPositionSeconds; // console.log(`Song is ending in ${timeTillFinished} seconds`); if(timeTillFinished > 0) setTimeout(() => this.stopPlayback(false), timeTillFinished * 1000); else this.stopPlayback(false); } else { this.seekToPosition(currentPositionSeconds + this.seekLength); } } stopPlayback(stopAllNotes=true) { // console.log('TrackPlayback.stopPlayback', stopAllNotes); if(this.active) { this.active = false; this.endResolve(); } // TODO: unnecessary? if(stopAllNotes) { ProgramLoader.stopAllPlayback() // this.activePrograms.forEach(program => { // try { program.stopPlayback() } // catch (e) { console.log(program.constructor.name, e); } // }); } this.activePrograms = []; // this.endPromise = true; } // playInstruction(instruction, trackStats) { // if(instruction instanceof NoteInstruction) { // if(typeof trackStats.program === "undefined") // return console.error(`Track '${trackStats.trackName}' has no program set`); // const destination = trackStats.destination || this.destination; // const noteStartTime = this.startTime + trackStats.startPosition + trackStats.iterator.getPositionInSeconds(); // ASCTrack start time equals current track's start + playback times // if(noteStartTime > 0) { // const noteIndex = trackStats.currentIndex; // const playingIndices = trackStats.playingIndices; // const onEvent = this.onEvent; // this.song.playInstruction(destination, instruction, trackStats.program, noteStartTime, // () => { // if(playingIndices.indexOf(noteIndex) === -1) { // playingIndices.push(noteIndex); // // console.log('playingIndices.push', playingIndices); // // onEvent({ // type: 'instruction:play', // playback: this, // playingIndices, // trackStats // }); // } // }, // () => { // playingIndices.splice(playingIndices.indexOf(noteIndex), 1); // // console.log('playingIndices.splice', playingIndices); // onEvent({ // type: 'instruction:stop', // playback: this, // playingIndices, // trackStats // }); // // // }); // } // } // } // onPlayTrack(trackStats, params) { // super.onPlayTrack(trackStats, params); // } }