import * as fs from "fs" import { AnyEvent } from "midifile-ts" import * as path from "path" import { serialize } from "serializr" import { emptySong } from "../song/SongFactory" import { NoteEvent } from "../track" import Track from "../track/Track" import { noteOffMidiEvent, noteOnMidiEvent, setTempoMidiEvent, timeSignatureMidiEvent, } from "./MidiEvent" import { createConductorTrackIfNeeded, songFromMidi, songToMidi, songToMidiEvents, } from "./midiConversion" // id for each event will not be serialized in midi file // we change ids sorted by order in events array const reassignIDs = (track: Track) => { track.events.forEach((e, i) => { track.events[i].id = i }) } describe("SongFile", () => { it("write and read", () => { const song = emptySong() const note = song.tracks[1].addEvent({ type: "channel", subtype: "note", noteNumber: 57, tick: 960, velocity: 127, duration: 240, }) song.tracks.forEach(reassignIDs) const bytes = songToMidi(song) const song2 = songFromMidi(bytes) song2.filepath = song.filepath // filepath will not be serialized expect(serialize(song2)).toStrictEqual(serialize(song)) }) describe("songToMidiEvents", () => { const expectEveryTrackHaveEndOfTrackEvent = (tracks: AnyEvent[][]) => { for (const track of tracks) { expect( track.findIndex( (e) => e.type === "meta" && e.subtype === "endOfTrack", ), ).toBe(track.length - 1) } } const openFile = (fileName: string): AnyEvent[][] => { const song = songFromMidi( fs.readFileSync(path.join(__dirname, "../../../testdata/", fileName)) .buffer, ) return songToMidiEvents(song) } describe("format 1", () => { const rawTracks = openFile("tracks.mid") it("every tracks have endOfTrack event", () => { expect(rawTracks.length).toBe(18) expectEveryTrackHaveEndOfTrackEvent(rawTracks) }) }) describe("format 0", () => { const rawTracks = openFile("format0.mid") it("every tracks have endOfTrack event", () => { expect(rawTracks.length).toBe(17) expectEveryTrackHaveEndOfTrackEvent(rawTracks) }) }) }) describe("signal events", () => { it("should save the track color", () => { const song = emptySong() song.tracks[1].setColor({ red: 12, green: 34, blue: 56, alpha: 78, }) const bytes = songToMidi(song) const song2 = songFromMidi(bytes) expect(song2.tracks[1].color).toMatchObject({ red: 12, green: 34, blue: 56, alpha: 78, }) }) }) describe("createConductorTrackIfNeeded", () => { it("should not create the conductor track", () => { const tracks: AnyEvent[][] = [ [timeSignatureMidiEvent(0, 4, 4), setTempoMidiEvent(120, 500000)], [noteOnMidiEvent(0, 1, 60, 100), noteOffMidiEvent(120, 1, 60, 0)], ] const result = createConductorTrackIfNeeded(tracks) expect(result).toStrictEqual([ [timeSignatureMidiEvent(0, 4, 4), setTempoMidiEvent(120, 500000)], [noteOnMidiEvent(0, 1, 60, 100), noteOffMidiEvent(120, 1, 60, 0)], ]) }) it("should create the conductor track", () => { const tracks: AnyEvent[][] = [ [ timeSignatureMidiEvent(0, 4, 4), setTempoMidiEvent(120, 500000), noteOnMidiEvent(120, 5, 60, 100), noteOffMidiEvent(120, 5, 60, 0), ], [noteOnMidiEvent(0, 2, 60, 100), noteOffMidiEvent(120, 2, 60, 0)], ] const result = createConductorTrackIfNeeded(tracks) expect(result).toStrictEqual([ [timeSignatureMidiEvent(0, 4, 4), setTempoMidiEvent(120, 500000)], [noteOnMidiEvent(240, 5, 60, 100), noteOffMidiEvent(120, 5, 60, 0)], [noteOnMidiEvent(0, 2, 60, 100), noteOffMidiEvent(120, 2, 60, 0)], ]) }) }) })