|
|
import { WorkletSequencerReturnMessageType } from "./sequencer_message.js"; |
|
|
import { consoleColors, formatTime } from "../../utils/other.js"; |
|
|
import { |
|
|
SpessaSynthGroupCollapsed, |
|
|
SpessaSynthGroupEnd, |
|
|
SpessaSynthInfo, |
|
|
SpessaSynthWarn |
|
|
} from "../../utils/loggin.js"; |
|
|
import { MIDIData } from "../../midi_parser/midi_data.js"; |
|
|
import { MIDI } from "../../midi_parser/midi_loader.js"; |
|
|
import { BasicMIDI } from "../../midi_parser/basic_midi.js"; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function assignMIDIPort(trackNum, port) |
|
|
{ |
|
|
|
|
|
if (this.midiData.usedChannelsOnTrack[trackNum].size === 0) |
|
|
{ |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
if (this.midiPortChannelOffset === 0) |
|
|
{ |
|
|
this.midiPortChannelOffset += 16; |
|
|
this.midiPortChannelOffsets[port] = 0; |
|
|
} |
|
|
|
|
|
if (this.midiPortChannelOffsets[port] === undefined) |
|
|
{ |
|
|
if (this.synth.workletProcessorChannels.length < this.midiPortChannelOffset + 15) |
|
|
{ |
|
|
this._addNewMidiPort(); |
|
|
} |
|
|
this.midiPortChannelOffsets[port] = this.midiPortChannelOffset; |
|
|
this.midiPortChannelOffset += 16; |
|
|
} |
|
|
|
|
|
this.midiPorts[trackNum] = port; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function loadNewSequence(parsedMidi, autoPlay = true) |
|
|
{ |
|
|
this.stop(); |
|
|
if (!parsedMidi.tracks) |
|
|
{ |
|
|
throw new Error("This MIDI has no tracks!"); |
|
|
} |
|
|
|
|
|
this.oneTickToSeconds = 60 / (120 * parsedMidi.timeDivision); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.midiData = parsedMidi; |
|
|
|
|
|
|
|
|
if (this.midiData.embeddedSoundFont !== undefined) |
|
|
{ |
|
|
SpessaSynthInfo("%cEmbedded soundfont detected! Using it.", consoleColors.recognized); |
|
|
this.synth.setEmbeddedSoundFont(this.midiData.embeddedSoundFont, this.midiData.bankOffset); |
|
|
} |
|
|
else |
|
|
{ |
|
|
if (this.synth.overrideSoundfont) |
|
|
{ |
|
|
|
|
|
this.synth.clearSoundFont(true, true); |
|
|
} |
|
|
SpessaSynthGroupCollapsed("%cPreloading samples...", consoleColors.info); |
|
|
|
|
|
const used = this.midiData.getUsedProgramsAndKeys(this.synth.soundfontManager); |
|
|
for (const [programBank, combos] of Object.entries(used)) |
|
|
{ |
|
|
const bank = parseInt(programBank.split(":")[0]); |
|
|
const program = parseInt(programBank.split(":")[1]); |
|
|
const preset = this.synth.getPreset(bank, program); |
|
|
SpessaSynthInfo( |
|
|
`%cPreloading used samples on %c${preset.presetName}%c...`, |
|
|
consoleColors.info, |
|
|
consoleColors.recognized, |
|
|
consoleColors.info |
|
|
); |
|
|
for (const combo of combos) |
|
|
{ |
|
|
const split = combo.split("-"); |
|
|
preset.preloadSpecific(parseInt(split[0]), parseInt(split[1])); |
|
|
} |
|
|
} |
|
|
SpessaSynthGroupEnd(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.tracks = this.midiData.tracks; |
|
|
|
|
|
|
|
|
this.midiPorts = this.midiData.midiPorts.slice(); |
|
|
|
|
|
|
|
|
this.midiPortChannelOffset = 0; |
|
|
this.midiPortChannelOffsets = {}; |
|
|
|
|
|
this.midiData.midiPorts.forEach((port, trackIndex) => |
|
|
{ |
|
|
this.assignMIDIPort(trackIndex, port); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.duration = this.midiData.duration; |
|
|
this.firstNoteTime = this.midiData.MIDIticksToSeconds(this.midiData.firstNoteOn); |
|
|
SpessaSynthInfo(`%cTotal song time: ${formatTime(Math.ceil(this.duration)).time}`, consoleColors.recognized); |
|
|
|
|
|
this.post(WorkletSequencerReturnMessageType.songChange, [this.songIndex, autoPlay]); |
|
|
|
|
|
if (this.duration <= 1) |
|
|
{ |
|
|
SpessaSynthWarn( |
|
|
`%cVery short song: (${formatTime(Math.round(this.duration)).time}). Disabling loop!`, |
|
|
consoleColors.warn |
|
|
); |
|
|
this.loop = false; |
|
|
} |
|
|
if (autoPlay) |
|
|
{ |
|
|
this.play(true); |
|
|
} |
|
|
else |
|
|
{ |
|
|
|
|
|
const targetTime = this.skipToFirstNoteOn ? this.midiData.firstNoteOn - 1 : 0; |
|
|
this.setTimeTicks(targetTime); |
|
|
this.pause(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function loadNewSongList(midiBuffers, autoPlay = true) |
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.songs = midiBuffers.reduce((mids, b) => |
|
|
{ |
|
|
if (b.duration) |
|
|
{ |
|
|
mids.push(BasicMIDI.copyFrom(b)); |
|
|
return mids; |
|
|
} |
|
|
try |
|
|
{ |
|
|
mids.push(new MIDI(b.binary, b.altName || "")); |
|
|
} |
|
|
catch (e) |
|
|
{ |
|
|
console.error(e); |
|
|
this.post(WorkletSequencerReturnMessageType.midiError, e); |
|
|
return mids; |
|
|
} |
|
|
return mids; |
|
|
}, []); |
|
|
if (this.songs.length < 1) |
|
|
{ |
|
|
return; |
|
|
} |
|
|
this.songIndex = 0; |
|
|
if (this.songs.length > 1) |
|
|
{ |
|
|
this.loop = false; |
|
|
} |
|
|
this.shuffleSongIndexes(); |
|
|
const midiDatas = this.songs.map(s => new MIDIData(s)); |
|
|
this.post(WorkletSequencerReturnMessageType.songListChange, midiDatas); |
|
|
this.loadCurrentSong(autoPlay); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function nextSong() |
|
|
{ |
|
|
if (this.songs.length === 1) |
|
|
{ |
|
|
this.currentTime = 0; |
|
|
return; |
|
|
} |
|
|
this.songIndex++; |
|
|
this.songIndex %= this.songs.length; |
|
|
this.loadCurrentSong(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function previousSong() |
|
|
{ |
|
|
if (this.songs.length === 1) |
|
|
{ |
|
|
this.currentTime = 0; |
|
|
return; |
|
|
} |
|
|
this.songIndex--; |
|
|
if (this.songIndex < 0) |
|
|
{ |
|
|
this.songIndex = this.songs.length - 1; |
|
|
} |
|
|
this.loadCurrentSong(); |
|
|
} |