midilib 0.8.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (252) hide show
  1. data/ChangeLog +87 -0
  2. data/Credits +8 -0
  3. data/README +361 -0
  4. data/Rakefile +68 -0
  5. data/TODO +25 -0
  6. data/examples/NoFences.mid +0 -0
  7. data/examples/from_scratch.mid +0 -0
  8. data/examples/from_scratch.rb +56 -0
  9. data/examples/reader2text.rb +220 -0
  10. data/examples/seq2text.rb +41 -0
  11. data/examples/strings.rb +34 -0
  12. data/examples/transpose.rb +75 -0
  13. data/html/classes/MIDI.html +738 -0
  14. data/html/classes/MIDI/ActiveSense.html +156 -0
  15. data/html/classes/MIDI/ActiveSense.src/M000136.html +18 -0
  16. data/html/classes/MIDI/ActiveSense.src/M000137.html +18 -0
  17. data/html/classes/MIDI/ChannelEvent.html +179 -0
  18. data/html/classes/MIDI/ChannelEvent.src/M000124.html +20 -0
  19. data/html/classes/MIDI/ChannelEvent.src/M000125.html +18 -0
  20. data/html/classes/MIDI/ChannelPressure.html +184 -0
  21. data/html/classes/MIDI/ChannelPressure.src/M000088.html +19 -0
  22. data/html/classes/MIDI/ChannelPressure.src/M000089.html +21 -0
  23. data/html/classes/MIDI/ChannelPressure.src/M000090.html +18 -0
  24. data/html/classes/MIDI/Clock.html +156 -0
  25. data/html/classes/MIDI/Clock.src/M000134.html +18 -0
  26. data/html/classes/MIDI/Clock.src/M000135.html +18 -0
  27. data/html/classes/MIDI/Continue.html +156 -0
  28. data/html/classes/MIDI/Continue.src/M000132.html +18 -0
  29. data/html/classes/MIDI/Continue.src/M000133.html +18 -0
  30. data/html/classes/MIDI/Controller.html +189 -0
  31. data/html/classes/MIDI/Controller.src/M000129.html +21 -0
  32. data/html/classes/MIDI/Controller.src/M000130.html +22 -0
  33. data/html/classes/MIDI/Controller.src/M000131.html +18 -0
  34. data/html/classes/MIDI/Event.html +424 -0
  35. data/html/classes/MIDI/Event.src/M000145.html +27 -0
  36. data/html/classes/MIDI/Event.src/M000146.html +18 -0
  37. data/html/classes/MIDI/Event.src/M000147.html +18 -0
  38. data/html/classes/MIDI/Event.src/M000148.html +18 -0
  39. data/html/classes/MIDI/Event.src/M000149.html +18 -0
  40. data/html/classes/MIDI/Event.src/M000150.html +18 -0
  41. data/html/classes/MIDI/Event.src/M000151.html +18 -0
  42. data/html/classes/MIDI/Event.src/M000152.html +18 -0
  43. data/html/classes/MIDI/Event.src/M000153.html +18 -0
  44. data/html/classes/MIDI/Event.src/M000154.html +22 -0
  45. data/html/classes/MIDI/Event.src/M000155.html +18 -0
  46. data/html/classes/MIDI/Event.src/M000156.html +18 -0
  47. data/html/classes/MIDI/Event.src/M000157.html +18 -0
  48. data/html/classes/MIDI/IO.html +121 -0
  49. data/html/classes/MIDI/IO/MIDIFile.html +925 -0
  50. data/html/classes/MIDI/IO/MIDIFile.src/M000028.html +22 -0
  51. data/html/classes/MIDI/IO/MIDIFile.src/M000029.html +24 -0
  52. data/html/classes/MIDI/IO/MIDIFile.src/M000030.html +19 -0
  53. data/html/classes/MIDI/IO/MIDIFile.src/M000031.html +19 -0
  54. data/html/classes/MIDI/IO/MIDIFile.src/M000032.html +17 -0
  55. data/html/classes/MIDI/IO/MIDIFile.src/M000033.html +17 -0
  56. data/html/classes/MIDI/IO/MIDIFile.src/M000034.html +17 -0
  57. data/html/classes/MIDI/IO/MIDIFile.src/M000035.html +17 -0
  58. data/html/classes/MIDI/IO/MIDIFile.src/M000036.html +17 -0
  59. data/html/classes/MIDI/IO/MIDIFile.src/M000037.html +17 -0
  60. data/html/classes/MIDI/IO/MIDIFile.src/M000038.html +17 -0
  61. data/html/classes/MIDI/IO/MIDIFile.src/M000039.html +17 -0
  62. data/html/classes/MIDI/IO/MIDIFile.src/M000040.html +17 -0
  63. data/html/classes/MIDI/IO/MIDIFile.src/M000041.html +17 -0
  64. data/html/classes/MIDI/IO/MIDIFile.src/M000042.html +17 -0
  65. data/html/classes/MIDI/IO/MIDIFile.src/M000043.html +17 -0
  66. data/html/classes/MIDI/IO/MIDIFile.src/M000044.html +17 -0
  67. data/html/classes/MIDI/IO/MIDIFile.src/M000045.html +17 -0
  68. data/html/classes/MIDI/IO/MIDIFile.src/M000046.html +17 -0
  69. data/html/classes/MIDI/IO/MIDIFile.src/M000047.html +17 -0
  70. data/html/classes/MIDI/IO/MIDIFile.src/M000048.html +17 -0
  71. data/html/classes/MIDI/IO/MIDIFile.src/M000049.html +17 -0
  72. data/html/classes/MIDI/IO/MIDIFile.src/M000050.html +17 -0
  73. data/html/classes/MIDI/IO/MIDIFile.src/M000051.html +17 -0
  74. data/html/classes/MIDI/IO/MIDIFile.src/M000052.html +17 -0
  75. data/html/classes/MIDI/IO/MIDIFile.src/M000053.html +43 -0
  76. data/html/classes/MIDI/IO/MIDIFile.src/M000054.html +34 -0
  77. data/html/classes/MIDI/IO/MIDIFile.src/M000055.html +96 -0
  78. data/html/classes/MIDI/IO/MIDIFile.src/M000056.html +18 -0
  79. data/html/classes/MIDI/IO/MIDIFile.src/M000057.html +48 -0
  80. data/html/classes/MIDI/IO/MIDIFile.src/M000058.html +42 -0
  81. data/html/classes/MIDI/IO/MIDIFile.src/M000059.html +19 -0
  82. data/html/classes/MIDI/IO/MIDIFile.src/M000060.html +19 -0
  83. data/html/classes/MIDI/IO/MIDIFile.src/M000061.html +20 -0
  84. data/html/classes/MIDI/IO/MIDIFile.src/M000062.html +21 -0
  85. data/html/classes/MIDI/IO/MIDIFile.src/M000063.html +31 -0
  86. data/html/classes/MIDI/IO/MIDIFile.src/M000064.html +20 -0
  87. data/html/classes/MIDI/IO/MIDIFile.src/M000065.html +22 -0
  88. data/html/classes/MIDI/IO/MIDIFile.src/M000066.html +30 -0
  89. data/html/classes/MIDI/IO/MIDIFile.src/M000067.html +18 -0
  90. data/html/classes/MIDI/IO/MIDIFile.src/M000068.html +20 -0
  91. data/html/classes/MIDI/IO/MIDIFile.src/M000069.html +18 -0
  92. data/html/classes/MIDI/IO/MIDIFile.src/M000070.html +18 -0
  93. data/html/classes/MIDI/IO/SeqReader.html +460 -0
  94. data/html/classes/MIDI/IO/SeqReader.src/M000001.html +22 -0
  95. data/html/classes/MIDI/IO/SeqReader.src/M000002.html +22 -0
  96. data/html/classes/MIDI/IO/SeqReader.src/M000003.html +21 -0
  97. data/html/classes/MIDI/IO/SeqReader.src/M000004.html +34 -0
  98. data/html/classes/MIDI/IO/SeqReader.src/M000005.html +26 -0
  99. data/html/classes/MIDI/IO/SeqReader.src/M000006.html +28 -0
  100. data/html/classes/MIDI/IO/SeqReader.src/M000007.html +21 -0
  101. data/html/classes/MIDI/IO/SeqReader.src/M000008.html +19 -0
  102. data/html/classes/MIDI/IO/SeqReader.src/M000009.html +19 -0
  103. data/html/classes/MIDI/IO/SeqReader.src/M000010.html +19 -0
  104. data/html/classes/MIDI/IO/SeqReader.src/M000011.html +19 -0
  105. data/html/classes/MIDI/IO/SeqReader.src/M000012.html +19 -0
  106. data/html/classes/MIDI/IO/SeqReader.src/M000013.html +18 -0
  107. data/html/classes/MIDI/IO/SeqReader.src/M000014.html +18 -0
  108. data/html/classes/MIDI/IO/SeqReader.src/M000015.html +27 -0
  109. data/html/classes/MIDI/IO/SeqReader.src/M000016.html +18 -0
  110. data/html/classes/MIDI/IO/SeqReader.src/M000017.html +18 -0
  111. data/html/classes/MIDI/IO/SeqReader.src/M000018.html +18 -0
  112. data/html/classes/MIDI/IO/SeqWriter.html +267 -0
  113. data/html/classes/MIDI/IO/SeqWriter.src/M000019.html +19 -0
  114. data/html/classes/MIDI/IO/SeqWriter.src/M000020.html +25 -0
  115. data/html/classes/MIDI/IO/SeqWriter.src/M000021.html +22 -0
  116. data/html/classes/MIDI/IO/SeqWriter.src/M000022.html +54 -0
  117. data/html/classes/MIDI/IO/SeqWriter.src/M000023.html +49 -0
  118. data/html/classes/MIDI/IO/SeqWriter.src/M000024.html +21 -0
  119. data/html/classes/MIDI/IO/SeqWriter.src/M000025.html +19 -0
  120. data/html/classes/MIDI/IO/SeqWriter.src/M000026.html +24 -0
  121. data/html/classes/MIDI/IO/SeqWriter.src/M000027.html +26 -0
  122. data/html/classes/MIDI/Marker.html +139 -0
  123. data/html/classes/MIDI/Marker.src/M000107.html +18 -0
  124. data/html/classes/MIDI/MetaEvent.html +189 -0
  125. data/html/classes/MIDI/MetaEvent.src/M000167.html +21 -0
  126. data/html/classes/MIDI/MetaEvent.src/M000168.html +23 -0
  127. data/html/classes/MIDI/MetaEvent.src/M000169.html +53 -0
  128. data/html/classes/MIDI/NoteEvent.html +233 -0
  129. data/html/classes/MIDI/NoteEvent.src/M000091.html +21 -0
  130. data/html/classes/MIDI/NoteEvent.src/M000092.html +20 -0
  131. data/html/classes/MIDI/NoteEvent.src/M000093.html +18 -0
  132. data/html/classes/MIDI/NoteEvent.src/M000094.html +22 -0
  133. data/html/classes/MIDI/NoteOffEvent.html +169 -0
  134. data/html/classes/MIDI/NoteOffEvent.src/M000086.html +19 -0
  135. data/html/classes/MIDI/NoteOffEvent.src/M000087.html +19 -0
  136. data/html/classes/MIDI/NoteOnEvent.html +169 -0
  137. data/html/classes/MIDI/NoteOnEvent.src/M000102.html +19 -0
  138. data/html/classes/MIDI/NoteOnEvent.src/M000103.html +19 -0
  139. data/html/classes/MIDI/PitchBend.html +184 -0
  140. data/html/classes/MIDI/PitchBend.src/M000104.html +19 -0
  141. data/html/classes/MIDI/PitchBend.src/M000105.html +22 -0
  142. data/html/classes/MIDI/PitchBend.src/M000106.html +18 -0
  143. data/html/classes/MIDI/PolyPressure.html +186 -0
  144. data/html/classes/MIDI/PolyPressure.src/M000170.html +18 -0
  145. data/html/classes/MIDI/PolyPressure.src/M000171.html +18 -0
  146. data/html/classes/MIDI/PolyPressure.src/M000172.html +18 -0
  147. data/html/classes/MIDI/PolyPressure.src/M000173.html +19 -0
  148. data/html/classes/MIDI/ProgramChange.html +184 -0
  149. data/html/classes/MIDI/ProgramChange.src/M000099.html +19 -0
  150. data/html/classes/MIDI/ProgramChange.src/M000100.html +21 -0
  151. data/html/classes/MIDI/ProgramChange.src/M000101.html +18 -0
  152. data/html/classes/MIDI/Realtime.html +171 -0
  153. data/html/classes/MIDI/Realtime.src/M000083.html +19 -0
  154. data/html/classes/MIDI/Realtime.src/M000084.html +20 -0
  155. data/html/classes/MIDI/Realtime.src/M000085.html +18 -0
  156. data/html/classes/MIDI/Sequence.html +469 -0
  157. data/html/classes/MIDI/Sequence.src/M000108.html +28 -0
  158. data/html/classes/MIDI/Sequence.src/M000109.html +21 -0
  159. data/html/classes/MIDI/Sequence.src/M000110.html +22 -0
  160. data/html/classes/MIDI/Sequence.src/M000113.html +18 -0
  161. data/html/classes/MIDI/Sequence.src/M000114.html +27 -0
  162. data/html/classes/MIDI/Sequence.src/M000115.html +18 -0
  163. data/html/classes/MIDI/Sequence.src/M000116.html +19 -0
  164. data/html/classes/MIDI/Sequence.src/M000117.html +19 -0
  165. data/html/classes/MIDI/Sequence.src/M000118.html +20 -0
  166. data/html/classes/MIDI/Sequence.src/M000119.html +19 -0
  167. data/html/classes/MIDI/Sequence.src/M000120.html +18 -0
  168. data/html/classes/MIDI/SongPointer.html +184 -0
  169. data/html/classes/MIDI/SongPointer.src/M000138.html +19 -0
  170. data/html/classes/MIDI/SongPointer.src/M000139.html +22 -0
  171. data/html/classes/MIDI/SongPointer.src/M000140.html +18 -0
  172. data/html/classes/MIDI/SongSelect.html +184 -0
  173. data/html/classes/MIDI/SongSelect.src/M000126.html +19 -0
  174. data/html/classes/MIDI/SongSelect.src/M000127.html +21 -0
  175. data/html/classes/MIDI/SongSelect.src/M000128.html +18 -0
  176. data/html/classes/MIDI/Start.html +156 -0
  177. data/html/classes/MIDI/Start.src/M000097.html +18 -0
  178. data/html/classes/MIDI/Start.src/M000098.html +18 -0
  179. data/html/classes/MIDI/Stop.html +156 -0
  180. data/html/classes/MIDI/Stop.src/M000095.html +18 -0
  181. data/html/classes/MIDI/Stop.src/M000096.html +18 -0
  182. data/html/classes/MIDI/SystemCommon.html +139 -0
  183. data/html/classes/MIDI/SystemCommon.src/M000144.html +19 -0
  184. data/html/classes/MIDI/SystemExclusive.html +184 -0
  185. data/html/classes/MIDI/SystemExclusive.src/M000141.html +19 -0
  186. data/html/classes/MIDI/SystemExclusive.src/M000142.html +23 -0
  187. data/html/classes/MIDI/SystemExclusive.src/M000143.html +18 -0
  188. data/html/classes/MIDI/SystemReset.html +156 -0
  189. data/html/classes/MIDI/SystemReset.src/M000081.html +18 -0
  190. data/html/classes/MIDI/SystemReset.src/M000082.html +18 -0
  191. data/html/classes/MIDI/Tempo.html +250 -0
  192. data/html/classes/MIDI/Tempo.src/M000158.html +18 -0
  193. data/html/classes/MIDI/Tempo.src/M000159.html +18 -0
  194. data/html/classes/MIDI/Tempo.src/M000160.html +18 -0
  195. data/html/classes/MIDI/Tempo.src/M000161.html +18 -0
  196. data/html/classes/MIDI/Tempo.src/M000162.html +18 -0
  197. data/html/classes/MIDI/Tempo.src/M000163.html +25 -0
  198. data/html/classes/MIDI/Tempo.src/M000164.html +18 -0
  199. data/html/classes/MIDI/Track.html +380 -0
  200. data/html/classes/MIDI/Track.src/M000071.html +23 -0
  201. data/html/classes/MIDI/Track.src/M000072.html +21 -0
  202. data/html/classes/MIDI/Track.src/M000073.html +26 -0
  203. data/html/classes/MIDI/Track.src/M000074.html +18 -0
  204. data/html/classes/MIDI/Track.src/M000075.html +22 -0
  205. data/html/classes/MIDI/Track.src/M000076.html +20 -0
  206. data/html/classes/MIDI/Track.src/M000077.html +22 -0
  207. data/html/classes/MIDI/Track.src/M000078.html +22 -0
  208. data/html/classes/MIDI/Track.src/M000079.html +18 -0
  209. data/html/classes/MIDI/Track.src/M000080.html +19 -0
  210. data/html/classes/MIDI/TuneRequest.html +171 -0
  211. data/html/classes/MIDI/TuneRequest.src/M000121.html +18 -0
  212. data/html/classes/MIDI/TuneRequest.src/M000122.html +20 -0
  213. data/html/classes/MIDI/TuneRequest.src/M000123.html +18 -0
  214. data/html/classes/MIDI/Utils.html +190 -0
  215. data/html/classes/MIDI/Utils.src/M000165.html +20 -0
  216. data/html/classes/MIDI/Utils.src/M000166.html +25 -0
  217. data/html/created.rid +1 -0
  218. data/html/files/README.html +599 -0
  219. data/html/files/TODO.html +142 -0
  220. data/html/files/lib/midilib/consts_rb.html +107 -0
  221. data/html/files/lib/midilib/event_rb.html +109 -0
  222. data/html/files/lib/midilib/info_rb.html +101 -0
  223. data/html/files/lib/midilib/io/midifile_rb.html +108 -0
  224. data/html/files/lib/midilib/io/seqreader_rb.html +110 -0
  225. data/html/files/lib/midilib/io/seqwriter_rb.html +115 -0
  226. data/html/files/lib/midilib/sequence_rb.html +109 -0
  227. data/html/files/lib/midilib/track_rb.html +108 -0
  228. data/html/files/lib/midilib/utils_rb.html +101 -0
  229. data/html/files/lib/midilib_rb.html +124 -0
  230. data/html/fr_class_index.html +59 -0
  231. data/html/fr_file_index.html +38 -0
  232. data/html/fr_method_index.html +199 -0
  233. data/html/index.html +24 -0
  234. data/html/rdoc-style.css +208 -0
  235. data/install.rb +57 -0
  236. data/lib/midilib.rb +16 -0
  237. data/lib/midilib/consts.rb +422 -0
  238. data/lib/midilib/event.rb +559 -0
  239. data/lib/midilib/info.rb +9 -0
  240. data/lib/midilib/io/midifile.rb +446 -0
  241. data/lib/midilib/io/seqreader.rb +198 -0
  242. data/lib/midilib/io/seqwriter.rb +151 -0
  243. data/lib/midilib/sequence.rb +144 -0
  244. data/lib/midilib/track.rb +115 -0
  245. data/lib/midilib/utils.rb +36 -0
  246. data/test/event_equality.rb +81 -0
  247. data/test/test_event.rb +116 -0
  248. data/test/test_io.rb +55 -0
  249. data/test/test_sequence.rb +68 -0
  250. data/test/test_track.rb +111 -0
  251. data/test/test_varlen.rb +40 -0
  252. metadata +330 -0
@@ -0,0 +1,198 @@
1
+ require 'midilib/io/midifile'
2
+ require 'midilib/track'
3
+ require 'midilib/event'
4
+
5
+ module MIDI
6
+
7
+ module IO
8
+
9
+ # Reads MIDI files. As a subclass of MIDIFile, this class implements
10
+ # the callback methods for each MIDI event and use them to build Track
11
+ # and Event objects and give the tracks to a Sequence.
12
+ #
13
+ # We append new events to the end of a track's event list, bypassing a call
14
+ # to Track.#add. This means that we must call Track.recalc_times at the end
15
+ # of the track so it can update each event with its time from the track's
16
+ # start (see end_track below).
17
+ #
18
+ # META_TRACK_END events are not added to tracks. This way, we don't have to
19
+ # worry about making sure the last event is always a track end event. We
20
+ # rely on the SeqWriter to append a META_TRACK_END event to each track when
21
+ # it is output.
22
+
23
+ class SeqReader < MIDIFile
24
+
25
+ # The optional proc block is called once at the start of the file
26
+ # and again at the end of each track. There are three arguments
27
+ # to the block: the track, the track number (1 through _n_), and
28
+ # the total number of tracks.
29
+ def initialize(seq, proc = nil) # :yields: track, num_tracks, index
30
+ super()
31
+ @seq = seq
32
+ @track = nil
33
+ @chan_mask = 0
34
+ @update_block = block_given?() ? Proc.new() : proc
35
+ end
36
+
37
+ def header(format, ntrks, division)
38
+ @seq.format = format
39
+ @seq.ppqn = division
40
+
41
+ @ntrks = ntrks
42
+ @update_block.call(nil, @ntrks, 0) if @update_block
43
+ end
44
+
45
+ def start_track()
46
+ @track = Track.new(@seq)
47
+ @seq.tracks << @track
48
+
49
+ @pending = []
50
+ end
51
+
52
+ def end_track()
53
+ # Turn off any pending note on messages
54
+ @pending.each { | on | make_note_off(on, 64) }
55
+ @pending = nil
56
+
57
+ # Don't bother adding the META_TRACK_END event to the track.
58
+ # This way, we don't have to worry about making sure the
59
+ # last event is always a track end event.
60
+
61
+ # Let the track calculate event times from start of track. This is
62
+ # in lieu of calling Track.add for each event.
63
+ @track.recalc_times()
64
+
65
+ # Store bitmask of all channels used into track
66
+ @track.channels_used = @chan_mask
67
+
68
+ # call update block
69
+ @update_block.call(@track, @ntrks, @seq.tracks.length) if @update_block
70
+ end
71
+
72
+ def note_on(chan, note, vel)
73
+ if vel == 0
74
+ note_off(chan, note, 64)
75
+ return
76
+ end
77
+
78
+ on = NoteOnEvent.new(chan, note, vel, @curr_ticks)
79
+ @track.events << on
80
+ @pending << on
81
+ track_uses_channel(chan)
82
+ end
83
+
84
+ def note_off(chan, note, vel)
85
+ # Find note on, create note off, connect the two, and remove
86
+ # note on from pending list.
87
+ @pending.each_with_index { | on, i |
88
+ if on.note == note && on.channel == chan
89
+ make_note_off(on, vel)
90
+ @pending.delete_at(i)
91
+ return
92
+ end
93
+ }
94
+ $stderr.puts "note off with no earlier note on (ch #{chan}, note" +
95
+ " #{note}, vel #{vel})" if $DEBUG
96
+ end
97
+
98
+ def make_note_off(on, vel)
99
+ off = NoteOffEvent.new(on.channel, on.note, vel, @curr_ticks)
100
+ @track.events << off
101
+ on.off = off
102
+ off.on = on
103
+ end
104
+
105
+ def pressure(chan, note, press)
106
+ @track.events << PolyPressure.new(chan, note, press, @curr_ticks)
107
+ track_uses_channel(chan)
108
+ end
109
+
110
+ def controller(chan, control, value)
111
+ @track.events << Controller.new(chan, control, value, @curr_ticks)
112
+ track_uses_channel(chan)
113
+ end
114
+
115
+ def pitch_bend(chan, msb, lsb)
116
+ @track.events << PitchBend.new(chan, (msb << 8) + lsb, @curr_ticks)
117
+ track_uses_channel(chan)
118
+ end
119
+
120
+ def program(chan, program)
121
+ @track.events << ProgramChange.new(chan, program, @curr_ticks)
122
+ track_uses_channel(chan)
123
+ end
124
+
125
+ def chan_pressure(chan, press)
126
+ @track.events << ChannelPressure.new(chan, press, @curr_ticks)
127
+ track_uses_channel(chan)
128
+ end
129
+
130
+ def sysex(msg)
131
+ @track.events << SystemExclusive.new(msg, @curr_ticks)
132
+ end
133
+
134
+ def meta_misc(type, msg)
135
+ @track.events << MetaEvent.new(type, msg, @curr_ticks)
136
+ end
137
+
138
+ # --
139
+ # def sequencer_specific(type, msg)
140
+ # end
141
+
142
+ # def sequence_number(num)
143
+ # end
144
+ # ++
145
+
146
+ def text(type, msg)
147
+ case type
148
+ when META_SEQ_NAME
149
+ @track.events << MetaEvent.new(META_SEQ_NAME, msg, 0)
150
+ when META_INSTRUMENT
151
+ @track.instrument = msg
152
+ when META_MARKER
153
+ @track.events << Marker.new(msg, @curr_ticks)
154
+ else
155
+ $stderr.puts "text = #{msg}, type = #{type}" if $DEBUG
156
+ end
157
+ end
158
+
159
+ # --
160
+ # Don't bother adding the META_TRACK_END event to the track. This way,
161
+ # we don't have to worry about always making sure the last event is
162
+ # always a track end event. We just have to make sure to write one when
163
+ # the track is output back to a file.
164
+ # def eot()
165
+ # @track.events << MetaEvent.new(META_TRACK_END, nil, @curr_ticks)
166
+ # end
167
+ # ++
168
+
169
+ def time_signature(numer, denom, clocks, qnotes)
170
+ @seq.time_signature(numer, denom, clocks, qnotes)
171
+ end
172
+
173
+ # --
174
+ # def smpte(hour, min, sec, frame, fract)
175
+ # end
176
+ # ++
177
+
178
+ def tempo(microsecs)
179
+ @track.events << Tempo.new(microsecs, @curr_ticks)
180
+ end
181
+
182
+ # --
183
+ # def key_signature(sharpflat, is_minor)
184
+ # end
185
+
186
+ # def arbitrary(msg)
187
+ # end
188
+ # ++
189
+
190
+ # Return true if the current track uses the specified channel.
191
+ def track_uses_channel(chan)
192
+ @chan_mask = @chan_mask | (1 << chan)
193
+ end
194
+
195
+ end
196
+
197
+ end
198
+ end
@@ -0,0 +1,151 @@
1
+ # Writes MIDI files.
2
+
3
+ require 'midilib/event'
4
+ require 'midilib/utils'
5
+
6
+ module MIDI
7
+
8
+ module IO
9
+
10
+ class SeqWriter
11
+
12
+ def initialize(seq, proc = nil) # :yields: num_tracks, index
13
+ @seq = seq
14
+ @update_block = block_given?() ? Proc.new() : proc
15
+ end
16
+
17
+ # Writes a MIDI format 1 file.
18
+ def write_to(io)
19
+ @io = io
20
+ @bytes_written = 0
21
+ write_header()
22
+ @update_block.call(nil, @seq.tracks.length, 0) if @update_block
23
+ @seq.tracks.each_with_index { | track, i |
24
+ write_track(track)
25
+ @update_block.call(track, @seq.tracks.length, i) if @update_block
26
+ }
27
+ end
28
+
29
+ def write_header
30
+ @io.print 'MThd'
31
+ write32(6)
32
+ write16(1) # Ignore sequence format; write as format 1
33
+ write16(@seq.tracks.length)
34
+ write16(@seq.ppqn)
35
+ end
36
+
37
+ def write_track(track)
38
+ @io.print 'MTrk'
39
+ track_size_file_pos = @io.tell()
40
+ write32(0) # Dummy byte count; overwritten later
41
+ @bytes_written = 0 # Reset after previous write
42
+
43
+ write_instrument(track.instrument)
44
+
45
+ prev_event = nil
46
+ prev_status = 0
47
+ track.events.each { | event |
48
+ if !event.kind_of?(Realtime)
49
+ write_var_len(event.delta_time)
50
+ end
51
+
52
+ data = event.data_as_bytes()
53
+ status = data[0] # status byte plus channel number, if any
54
+
55
+ # running status byte
56
+ status = possibly_munge_due_to_running_status_byte(data,
57
+ prev_status)
58
+
59
+ @bytes_written += @io.write(data)
60
+
61
+ prev_event = event
62
+ prev_status = status
63
+ }
64
+
65
+ # Write track end event.
66
+ event = MetaEvent.new(META_TRACK_END)
67
+ write_var_len(0)
68
+ @bytes_written += @io.write(event.data_as_bytes())
69
+
70
+ # Go back to beginning of track data and write number of bytes,
71
+ # then come back here to end of file.
72
+ @io.seek(track_size_file_pos)
73
+ write32(@bytes_written)
74
+ @io.seek(0, ::IO::SEEK_END)
75
+ end
76
+
77
+ # If we can use a running status byte, delete the status byte from
78
+ # the given data. Return the status to remember for next time as the
79
+ # running status byte for this event.
80
+ def possibly_munge_due_to_running_status_byte(data, prev_status)
81
+ status = data[0]
82
+ return status if status >= 0xf0 || prev_status >= 0xf0
83
+
84
+ chan = (status & 0x0f)
85
+ return status if chan != (prev_status & 0x0f)
86
+
87
+ status = (status & 0xf0)
88
+ prev_status = (prev_status & 0xf0)
89
+
90
+ # Both events are on the same channel. If the two status bytes are
91
+ # exactly the same, the rest is trivial. If it's note on/note off,
92
+ # we can combine those further.
93
+ if status == prev_status
94
+ data[0] = '' # delete status byte from data
95
+ return status + chan
96
+ elsif status == NOTE_OFF && data[2] == 64
97
+ # If we see a note off and the velocity is 64, we can store
98
+ # a note on with a velocity of 0. If the velocity isn't 64
99
+ # then storing a note on would be bad because the would be
100
+ # changed to 64 when reading the file back in.
101
+ data[2] = 0 # set vel to 0; do before possible shrinking
102
+ status = NOTE_ON + chan
103
+ if prev_status == NOTE_ON
104
+ data[0] = '' # delete status byte
105
+ else
106
+ data[0] = status
107
+ end
108
+ return status
109
+ else
110
+ # Can't compress data
111
+ return status + chan
112
+ end
113
+ end
114
+
115
+ def write_instrument(instrument)
116
+ event = MetaEvent.new(META_INSTRUMENT, instrument)
117
+ write_var_len(0)
118
+ data = event.data_as_bytes()
119
+ @bytes_written += @io.write(data)
120
+ end
121
+
122
+ def write_var_len(val)
123
+ buffer = Utils.as_var_len(val)
124
+ @bytes_written += @io.write(buffer)
125
+ end
126
+
127
+ def write16(val)
128
+ val = (-val | 0x8000) if val < 0
129
+
130
+ buffer = ''
131
+ buffer << ((val >> 8) & 0xff)
132
+ buffer << (val & 0xff)
133
+
134
+ @bytes_written += @io.write(buffer)
135
+ end
136
+
137
+ def write32(val)
138
+ val = (-val | 0x80000000) if val < 0
139
+
140
+ buffer = ''
141
+ buffer << ((val >> 24) & 0xff)
142
+ buffer << ((val >> 16) & 0xff)
143
+ buffer << ((val >> 8) & 0xff)
144
+ buffer << (val & 0xff)
145
+
146
+ @bytes_written += @io.write(buffer)
147
+ end
148
+ end
149
+
150
+ end
151
+ end
@@ -0,0 +1,144 @@
1
+ require 'midilib/io/seqreader'
2
+ require 'midilib/io/seqwriter'
3
+
4
+ module MIDI
5
+
6
+ # A MIDI::Sequence contains MIDI::Track objects.
7
+ class Sequence
8
+
9
+ include Enumerable
10
+
11
+ UNNAMED = 'Unnamed Sequence'
12
+ DEFAULT_TEMPO = 120
13
+
14
+ NOTE_TO_LENGTH = {
15
+ 'whole' => 4.0,
16
+ 'half' => 2.0,
17
+ 'quarter' => 1.0,
18
+ 'eighth' => 0.5,
19
+ '8th' => 0.5,
20
+ 'sixteenth' => 0.25,
21
+ '16th' => 0.25,
22
+ 'thirty second' => 0.125,
23
+ 'thirtysecond' => 0.125,
24
+ '32nd' => 0.125,
25
+ 'sixty fourth' => 0.0625,
26
+ 'sixtyfourth' => 0.0625,
27
+ '64th' => 0.0625
28
+ }
29
+
30
+ attr_accessor :tracks, :ppqn, :format
31
+ attr_accessor :numer, :denom, :clocks, :qnotes
32
+ # The class to use for reading MIDI from a stream. The default is
33
+ # MIDI::IO::SeqReader. You can change this at any time.
34
+ attr_accessor :reader_class
35
+ # The class to use for writeing MIDI from a stream. The default is
36
+ # MIDI::IO::SeqWriter. You can change this at any time.
37
+ attr_accessor :writer_class
38
+
39
+ def initialize
40
+ @tracks = Array.new()
41
+ @ppqn = 480
42
+
43
+ # Time signature
44
+ @numer = 4 # Numer + denom = 4/4 time default
45
+ @denom = 2
46
+ @clocks = @ppqn
47
+ @qnotes = 8
48
+
49
+ @reader_class = IO::SeqReader
50
+ @writer_class = IO::SeqWriter
51
+ end
52
+
53
+ # Sets the time signature.
54
+ def time_signature(numer, denom, clocks, qnotes)
55
+ @numer = numer
56
+ @denom = denom
57
+ @clocks = clocks
58
+ @qnotes = qnotes
59
+ end
60
+
61
+ # Returns the song tempo in beats per minute.
62
+ def beats_per_minute
63
+ return DEFAULT_TEMPO if @tracks.nil? || @tracks.empty?
64
+ event = @tracks.first.events.detect { | e |
65
+ e.kind_of?(MIDI::Tempo)
66
+ }
67
+ return event ? (Tempo.mpq_to_bpm(event.tempo)) : DEFAULT_TEMPO
68
+ end
69
+ alias_method :bpm, :beats_per_minute
70
+ alias_method :tempo, :beats_per_minute
71
+
72
+ # Given a note length name like "whole", "dotted quarter", or "8th
73
+ # triplet", return the length of that note in quarter notes as a delta
74
+ # time.
75
+ def note_to_delta(name)
76
+ return length_to_delta(note_to_length(name))
77
+ end
78
+
79
+ # Given a note length name like "whole", "dotted quarter", or "8th
80
+ # triplet", return the length of that note in quarter notes as a
81
+ # floating-point number, suitable for use as an argument to
82
+ # length_to_delta.
83
+ #
84
+ # Legal names are any value in NOTE_TO_LENGTH, optionally prefixed by
85
+ # "dotted_" and/or suffixed by "_triplet". So, for example,
86
+ # "dotted_quarter_triplet" returns the length of a dotted quarter-note
87
+ # triplet and "32nd" returns 1/32.
88
+ def note_to_length(name)
89
+ name.strip!
90
+ name =~ /^(dotted)?(.*?)(triplet)?$/
91
+ dotted, note_name, triplet = $1, $2, $3
92
+ note_name.strip!
93
+ mult = 1.0
94
+ mult = 1.5 if dotted
95
+ mult /= 3.0 if triplet
96
+ len = NOTE_TO_LENGTH[note_name]
97
+ raise "Sequence.note_to_length: \"#{note_name}\" not understood in \"#{name}\"" unless len
98
+ return len * mult
99
+ end
100
+
101
+ # Translates +length+ (a multiple of a quarter note) into a delta time.
102
+ # For example, 1 is a quarter note, 1.0/32.0 is a 32nd note, 1.5 is a
103
+ # dotted quarter, etc. Be aware when using division; 1/32 is zero due to
104
+ # integer mathematics and rounding. Use floating-point numbers like 1.0
105
+ # and 32.0. This method always returns an integer.
106
+ #
107
+ # See also note_to_delta and note_to_length.
108
+ def length_to_delta(length)
109
+ return (@ppqn * length).to_i
110
+ end
111
+
112
+ # Returns the name of the first track (track zero). If there are no
113
+ # tracks, returns UNNAMED.
114
+ def name
115
+ return UNNAMED if @tracks.empty?
116
+ return @tracks.first.name()
117
+ end
118
+
119
+ # Hands the name to the first track. Does nothing if there are no tracks.
120
+ def name=(name)
121
+ return if @tracks.empty?
122
+ @tracks.first.name = name
123
+ end
124
+
125
+ # Reads a MIDI stream.
126
+ def read(io, proc = nil) # :yields: track, num_tracks, index
127
+ @tracks = Array.new()
128
+ reader = @reader_class.new(self, block_given?() ? Proc.new() : proc)
129
+ reader.read_from(io)
130
+ end
131
+
132
+ # Writes to a MIDI stream.
133
+ def write(io, proc = nil) # :yields: track, num_tracks, index
134
+ writer = @writer_class.new(self, block_given?() ? Proc.new() : proc)
135
+ writer.write_to(io)
136
+ end
137
+
138
+ # Iterates over the tracks.
139
+ def each # :yields: track
140
+ @tracks.each { | track | yield track }
141
+ end
142
+
143
+ end
144
+ end