File size: 4,016 Bytes
f23825d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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<NoteEvent>({
      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)],
      ])
    })
  })
})