midilib 0.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ChangeLog +87 -0
- data/Credits +8 -0
- data/README +361 -0
- data/Rakefile +68 -0
- data/TODO +25 -0
- data/examples/NoFences.mid +0 -0
- data/examples/from_scratch.mid +0 -0
- data/examples/from_scratch.rb +56 -0
- data/examples/reader2text.rb +220 -0
- data/examples/seq2text.rb +41 -0
- data/examples/strings.rb +34 -0
- data/examples/transpose.rb +75 -0
- data/html/classes/MIDI.html +738 -0
- data/html/classes/MIDI/ActiveSense.html +156 -0
- data/html/classes/MIDI/ActiveSense.src/M000136.html +18 -0
- data/html/classes/MIDI/ActiveSense.src/M000137.html +18 -0
- data/html/classes/MIDI/ChannelEvent.html +179 -0
- data/html/classes/MIDI/ChannelEvent.src/M000124.html +20 -0
- data/html/classes/MIDI/ChannelEvent.src/M000125.html +18 -0
- data/html/classes/MIDI/ChannelPressure.html +184 -0
- data/html/classes/MIDI/ChannelPressure.src/M000088.html +19 -0
- data/html/classes/MIDI/ChannelPressure.src/M000089.html +21 -0
- data/html/classes/MIDI/ChannelPressure.src/M000090.html +18 -0
- data/html/classes/MIDI/Clock.html +156 -0
- data/html/classes/MIDI/Clock.src/M000134.html +18 -0
- data/html/classes/MIDI/Clock.src/M000135.html +18 -0
- data/html/classes/MIDI/Continue.html +156 -0
- data/html/classes/MIDI/Continue.src/M000132.html +18 -0
- data/html/classes/MIDI/Continue.src/M000133.html +18 -0
- data/html/classes/MIDI/Controller.html +189 -0
- data/html/classes/MIDI/Controller.src/M000129.html +21 -0
- data/html/classes/MIDI/Controller.src/M000130.html +22 -0
- data/html/classes/MIDI/Controller.src/M000131.html +18 -0
- data/html/classes/MIDI/Event.html +424 -0
- data/html/classes/MIDI/Event.src/M000145.html +27 -0
- data/html/classes/MIDI/Event.src/M000146.html +18 -0
- data/html/classes/MIDI/Event.src/M000147.html +18 -0
- data/html/classes/MIDI/Event.src/M000148.html +18 -0
- data/html/classes/MIDI/Event.src/M000149.html +18 -0
- data/html/classes/MIDI/Event.src/M000150.html +18 -0
- data/html/classes/MIDI/Event.src/M000151.html +18 -0
- data/html/classes/MIDI/Event.src/M000152.html +18 -0
- data/html/classes/MIDI/Event.src/M000153.html +18 -0
- data/html/classes/MIDI/Event.src/M000154.html +22 -0
- data/html/classes/MIDI/Event.src/M000155.html +18 -0
- data/html/classes/MIDI/Event.src/M000156.html +18 -0
- data/html/classes/MIDI/Event.src/M000157.html +18 -0
- data/html/classes/MIDI/IO.html +121 -0
- data/html/classes/MIDI/IO/MIDIFile.html +925 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000028.html +22 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000029.html +24 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000030.html +19 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000031.html +19 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000032.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000033.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000034.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000035.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000036.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000037.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000038.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000039.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000040.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000041.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000042.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000043.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000044.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000045.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000046.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000047.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000048.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000049.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000050.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000051.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000052.html +17 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000053.html +43 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000054.html +34 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000055.html +96 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000056.html +18 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000057.html +48 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000058.html +42 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000059.html +19 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000060.html +19 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000061.html +20 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000062.html +21 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000063.html +31 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000064.html +20 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000065.html +22 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000066.html +30 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000067.html +18 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000068.html +20 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000069.html +18 -0
- data/html/classes/MIDI/IO/MIDIFile.src/M000070.html +18 -0
- data/html/classes/MIDI/IO/SeqReader.html +460 -0
- data/html/classes/MIDI/IO/SeqReader.src/M000001.html +22 -0
- data/html/classes/MIDI/IO/SeqReader.src/M000002.html +22 -0
- data/html/classes/MIDI/IO/SeqReader.src/M000003.html +21 -0
- data/html/classes/MIDI/IO/SeqReader.src/M000004.html +34 -0
- data/html/classes/MIDI/IO/SeqReader.src/M000005.html +26 -0
- data/html/classes/MIDI/IO/SeqReader.src/M000006.html +28 -0
- data/html/classes/MIDI/IO/SeqReader.src/M000007.html +21 -0
- data/html/classes/MIDI/IO/SeqReader.src/M000008.html +19 -0
- data/html/classes/MIDI/IO/SeqReader.src/M000009.html +19 -0
- data/html/classes/MIDI/IO/SeqReader.src/M000010.html +19 -0
- data/html/classes/MIDI/IO/SeqReader.src/M000011.html +19 -0
- data/html/classes/MIDI/IO/SeqReader.src/M000012.html +19 -0
- data/html/classes/MIDI/IO/SeqReader.src/M000013.html +18 -0
- data/html/classes/MIDI/IO/SeqReader.src/M000014.html +18 -0
- data/html/classes/MIDI/IO/SeqReader.src/M000015.html +27 -0
- data/html/classes/MIDI/IO/SeqReader.src/M000016.html +18 -0
- data/html/classes/MIDI/IO/SeqReader.src/M000017.html +18 -0
- data/html/classes/MIDI/IO/SeqReader.src/M000018.html +18 -0
- data/html/classes/MIDI/IO/SeqWriter.html +267 -0
- data/html/classes/MIDI/IO/SeqWriter.src/M000019.html +19 -0
- data/html/classes/MIDI/IO/SeqWriter.src/M000020.html +25 -0
- data/html/classes/MIDI/IO/SeqWriter.src/M000021.html +22 -0
- data/html/classes/MIDI/IO/SeqWriter.src/M000022.html +54 -0
- data/html/classes/MIDI/IO/SeqWriter.src/M000023.html +49 -0
- data/html/classes/MIDI/IO/SeqWriter.src/M000024.html +21 -0
- data/html/classes/MIDI/IO/SeqWriter.src/M000025.html +19 -0
- data/html/classes/MIDI/IO/SeqWriter.src/M000026.html +24 -0
- data/html/classes/MIDI/IO/SeqWriter.src/M000027.html +26 -0
- data/html/classes/MIDI/Marker.html +139 -0
- data/html/classes/MIDI/Marker.src/M000107.html +18 -0
- data/html/classes/MIDI/MetaEvent.html +189 -0
- data/html/classes/MIDI/MetaEvent.src/M000167.html +21 -0
- data/html/classes/MIDI/MetaEvent.src/M000168.html +23 -0
- data/html/classes/MIDI/MetaEvent.src/M000169.html +53 -0
- data/html/classes/MIDI/NoteEvent.html +233 -0
- data/html/classes/MIDI/NoteEvent.src/M000091.html +21 -0
- data/html/classes/MIDI/NoteEvent.src/M000092.html +20 -0
- data/html/classes/MIDI/NoteEvent.src/M000093.html +18 -0
- data/html/classes/MIDI/NoteEvent.src/M000094.html +22 -0
- data/html/classes/MIDI/NoteOffEvent.html +169 -0
- data/html/classes/MIDI/NoteOffEvent.src/M000086.html +19 -0
- data/html/classes/MIDI/NoteOffEvent.src/M000087.html +19 -0
- data/html/classes/MIDI/NoteOnEvent.html +169 -0
- data/html/classes/MIDI/NoteOnEvent.src/M000102.html +19 -0
- data/html/classes/MIDI/NoteOnEvent.src/M000103.html +19 -0
- data/html/classes/MIDI/PitchBend.html +184 -0
- data/html/classes/MIDI/PitchBend.src/M000104.html +19 -0
- data/html/classes/MIDI/PitchBend.src/M000105.html +22 -0
- data/html/classes/MIDI/PitchBend.src/M000106.html +18 -0
- data/html/classes/MIDI/PolyPressure.html +186 -0
- data/html/classes/MIDI/PolyPressure.src/M000170.html +18 -0
- data/html/classes/MIDI/PolyPressure.src/M000171.html +18 -0
- data/html/classes/MIDI/PolyPressure.src/M000172.html +18 -0
- data/html/classes/MIDI/PolyPressure.src/M000173.html +19 -0
- data/html/classes/MIDI/ProgramChange.html +184 -0
- data/html/classes/MIDI/ProgramChange.src/M000099.html +19 -0
- data/html/classes/MIDI/ProgramChange.src/M000100.html +21 -0
- data/html/classes/MIDI/ProgramChange.src/M000101.html +18 -0
- data/html/classes/MIDI/Realtime.html +171 -0
- data/html/classes/MIDI/Realtime.src/M000083.html +19 -0
- data/html/classes/MIDI/Realtime.src/M000084.html +20 -0
- data/html/classes/MIDI/Realtime.src/M000085.html +18 -0
- data/html/classes/MIDI/Sequence.html +469 -0
- data/html/classes/MIDI/Sequence.src/M000108.html +28 -0
- data/html/classes/MIDI/Sequence.src/M000109.html +21 -0
- data/html/classes/MIDI/Sequence.src/M000110.html +22 -0
- data/html/classes/MIDI/Sequence.src/M000113.html +18 -0
- data/html/classes/MIDI/Sequence.src/M000114.html +27 -0
- data/html/classes/MIDI/Sequence.src/M000115.html +18 -0
- data/html/classes/MIDI/Sequence.src/M000116.html +19 -0
- data/html/classes/MIDI/Sequence.src/M000117.html +19 -0
- data/html/classes/MIDI/Sequence.src/M000118.html +20 -0
- data/html/classes/MIDI/Sequence.src/M000119.html +19 -0
- data/html/classes/MIDI/Sequence.src/M000120.html +18 -0
- data/html/classes/MIDI/SongPointer.html +184 -0
- data/html/classes/MIDI/SongPointer.src/M000138.html +19 -0
- data/html/classes/MIDI/SongPointer.src/M000139.html +22 -0
- data/html/classes/MIDI/SongPointer.src/M000140.html +18 -0
- data/html/classes/MIDI/SongSelect.html +184 -0
- data/html/classes/MIDI/SongSelect.src/M000126.html +19 -0
- data/html/classes/MIDI/SongSelect.src/M000127.html +21 -0
- data/html/classes/MIDI/SongSelect.src/M000128.html +18 -0
- data/html/classes/MIDI/Start.html +156 -0
- data/html/classes/MIDI/Start.src/M000097.html +18 -0
- data/html/classes/MIDI/Start.src/M000098.html +18 -0
- data/html/classes/MIDI/Stop.html +156 -0
- data/html/classes/MIDI/Stop.src/M000095.html +18 -0
- data/html/classes/MIDI/Stop.src/M000096.html +18 -0
- data/html/classes/MIDI/SystemCommon.html +139 -0
- data/html/classes/MIDI/SystemCommon.src/M000144.html +19 -0
- data/html/classes/MIDI/SystemExclusive.html +184 -0
- data/html/classes/MIDI/SystemExclusive.src/M000141.html +19 -0
- data/html/classes/MIDI/SystemExclusive.src/M000142.html +23 -0
- data/html/classes/MIDI/SystemExclusive.src/M000143.html +18 -0
- data/html/classes/MIDI/SystemReset.html +156 -0
- data/html/classes/MIDI/SystemReset.src/M000081.html +18 -0
- data/html/classes/MIDI/SystemReset.src/M000082.html +18 -0
- data/html/classes/MIDI/Tempo.html +250 -0
- data/html/classes/MIDI/Tempo.src/M000158.html +18 -0
- data/html/classes/MIDI/Tempo.src/M000159.html +18 -0
- data/html/classes/MIDI/Tempo.src/M000160.html +18 -0
- data/html/classes/MIDI/Tempo.src/M000161.html +18 -0
- data/html/classes/MIDI/Tempo.src/M000162.html +18 -0
- data/html/classes/MIDI/Tempo.src/M000163.html +25 -0
- data/html/classes/MIDI/Tempo.src/M000164.html +18 -0
- data/html/classes/MIDI/Track.html +380 -0
- data/html/classes/MIDI/Track.src/M000071.html +23 -0
- data/html/classes/MIDI/Track.src/M000072.html +21 -0
- data/html/classes/MIDI/Track.src/M000073.html +26 -0
- data/html/classes/MIDI/Track.src/M000074.html +18 -0
- data/html/classes/MIDI/Track.src/M000075.html +22 -0
- data/html/classes/MIDI/Track.src/M000076.html +20 -0
- data/html/classes/MIDI/Track.src/M000077.html +22 -0
- data/html/classes/MIDI/Track.src/M000078.html +22 -0
- data/html/classes/MIDI/Track.src/M000079.html +18 -0
- data/html/classes/MIDI/Track.src/M000080.html +19 -0
- data/html/classes/MIDI/TuneRequest.html +171 -0
- data/html/classes/MIDI/TuneRequest.src/M000121.html +18 -0
- data/html/classes/MIDI/TuneRequest.src/M000122.html +20 -0
- data/html/classes/MIDI/TuneRequest.src/M000123.html +18 -0
- data/html/classes/MIDI/Utils.html +190 -0
- data/html/classes/MIDI/Utils.src/M000165.html +20 -0
- data/html/classes/MIDI/Utils.src/M000166.html +25 -0
- data/html/created.rid +1 -0
- data/html/files/README.html +599 -0
- data/html/files/TODO.html +142 -0
- data/html/files/lib/midilib/consts_rb.html +107 -0
- data/html/files/lib/midilib/event_rb.html +109 -0
- data/html/files/lib/midilib/info_rb.html +101 -0
- data/html/files/lib/midilib/io/midifile_rb.html +108 -0
- data/html/files/lib/midilib/io/seqreader_rb.html +110 -0
- data/html/files/lib/midilib/io/seqwriter_rb.html +115 -0
- data/html/files/lib/midilib/sequence_rb.html +109 -0
- data/html/files/lib/midilib/track_rb.html +108 -0
- data/html/files/lib/midilib/utils_rb.html +101 -0
- data/html/files/lib/midilib_rb.html +124 -0
- data/html/fr_class_index.html +59 -0
- data/html/fr_file_index.html +38 -0
- data/html/fr_method_index.html +199 -0
- data/html/index.html +24 -0
- data/html/rdoc-style.css +208 -0
- data/install.rb +57 -0
- data/lib/midilib.rb +16 -0
- data/lib/midilib/consts.rb +422 -0
- data/lib/midilib/event.rb +559 -0
- data/lib/midilib/info.rb +9 -0
- data/lib/midilib/io/midifile.rb +446 -0
- data/lib/midilib/io/seqreader.rb +198 -0
- data/lib/midilib/io/seqwriter.rb +151 -0
- data/lib/midilib/sequence.rb +144 -0
- data/lib/midilib/track.rb +115 -0
- data/lib/midilib/utils.rb +36 -0
- data/test/event_equality.rb +81 -0
- data/test/test_event.rb +116 -0
- data/test/test_io.rb +55 -0
- data/test/test_sequence.rb +68 -0
- data/test/test_track.rb +111 -0
- data/test/test_varlen.rb +40 -0
- metadata +330 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
require 'midilib/event'
|
|
2
|
+
|
|
3
|
+
module MIDI
|
|
4
|
+
|
|
5
|
+
# A Track is a list of events.
|
|
6
|
+
#
|
|
7
|
+
# When you modify the +events+ array, make sure to call recalc_times so
|
|
8
|
+
# each Event gets its +time_from_start+ recalculated.
|
|
9
|
+
#
|
|
10
|
+
# A Track also holds a bitmask that specifies the channels used by the track.
|
|
11
|
+
# This bitmask is set when the track is read from the MIDI file by an
|
|
12
|
+
# IO::SeqReader but is _not_ kept up to date by any other methods.
|
|
13
|
+
|
|
14
|
+
class Track
|
|
15
|
+
|
|
16
|
+
include Enumerable
|
|
17
|
+
|
|
18
|
+
UNNAMED = 'Unnamed'
|
|
19
|
+
|
|
20
|
+
attr_accessor :instrument, :events, :channels_used
|
|
21
|
+
attr_reader :sequence
|
|
22
|
+
|
|
23
|
+
def initialize(sequence)
|
|
24
|
+
@sequence = sequence
|
|
25
|
+
@events = Array.new()
|
|
26
|
+
|
|
27
|
+
# Bitmask of all channels used. Set when track is read in from
|
|
28
|
+
# a MIDI file.
|
|
29
|
+
@channels_used = 0
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Return track name. If there is no name, return UNNAMED.
|
|
33
|
+
def name
|
|
34
|
+
event = @events.detect { | e |
|
|
35
|
+
e.meta? && e.meta_type == META_SEQ_NAME
|
|
36
|
+
}
|
|
37
|
+
return event ? event.data : UNNAMED
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Set track name. Replaces or creates a name meta-event.
|
|
41
|
+
def name=(name)
|
|
42
|
+
event = @events.detect { | e |
|
|
43
|
+
e.meta? && e.meta_type == META_SEQ_NAME
|
|
44
|
+
}
|
|
45
|
+
if event
|
|
46
|
+
event.data = name
|
|
47
|
+
else
|
|
48
|
+
event = MetaEvent.new(META_SEQ_NAME, name, 0)
|
|
49
|
+
@events[0, 0] = event
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Merges an array of events into our event list. After merging, the
|
|
54
|
+
# events' time_from_start values are correct so you don't need to worry
|
|
55
|
+
# about calling recalc_times.
|
|
56
|
+
def merge(event_list)
|
|
57
|
+
@events = merge_event_lists(@events, event_list)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Merges two event arrays together. Does not modify this track.
|
|
61
|
+
def merge_event_lists(list1, list2)
|
|
62
|
+
recalc_times(0, list1)
|
|
63
|
+
recalc_times(0, list2)
|
|
64
|
+
list = (list1 + list2).sort_by { | e | e.time_from_start }
|
|
65
|
+
recalc_delta_from_times(0, list)
|
|
66
|
+
return list
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Quantize every event. +denom+ is the beat denominator. To quantize to
|
|
70
|
+
# sixteenths pass in a +denom+ of 16.
|
|
71
|
+
#
|
|
72
|
+
# Since each event's time_from_start is modified, we call
|
|
73
|
+
# recalc_delta_from_times after each event quantizes itself.
|
|
74
|
+
def quantize(denom)
|
|
75
|
+
boundary = @sequence.ppqn / denom
|
|
76
|
+
@events.each { | event | event.quantize_to(boundary) }
|
|
77
|
+
recalc_delta_from_times
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Recalculate start times for all events in +list+ from starting_at to
|
|
81
|
+
# end.
|
|
82
|
+
def recalc_times(starting_at=0, list=@events)
|
|
83
|
+
t = (starting_at == 0) ? 0 : list[starting_at - 1].time_from_start
|
|
84
|
+
list[starting_at .. -1].each { | e |
|
|
85
|
+
t += e.delta_time
|
|
86
|
+
e.time_from_start = t
|
|
87
|
+
}
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# The opposite of recalc_times: recalculates delta_time for each event
|
|
91
|
+
# from each event's time_from_start. This is useful, for example, when
|
|
92
|
+
# merging two event lists.
|
|
93
|
+
def recalc_delta_from_times(starting_at=0, list=@events)
|
|
94
|
+
prev_time_from_start = 0
|
|
95
|
+
list[starting_at .. -1].each { | e |
|
|
96
|
+
e.delta_time = e.time_from_start - prev_time_from_start
|
|
97
|
+
prev_time_from_start = e.time_from_start
|
|
98
|
+
}
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Iterate over events.
|
|
102
|
+
def each # :yields: event
|
|
103
|
+
@events.each { | event | yield event }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Sort events by their time_from_start. After sorting,
|
|
107
|
+
# recalc_delta_from_times is called to make sure that the delta times
|
|
108
|
+
# reflect the possibly new event order.
|
|
109
|
+
def sort
|
|
110
|
+
@events = @events.sort_by { | e | e.time_from_start }
|
|
111
|
+
recalc_delta_from_times()
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module MIDI
|
|
2
|
+
|
|
3
|
+
# Utility methods.
|
|
4
|
+
class Utils
|
|
5
|
+
|
|
6
|
+
# MIDI note names. NOTE_NAMES[0] is 'C', NOTE_NAMES[1] is 'C#', etc.
|
|
7
|
+
NOTE_NAMES = [
|
|
8
|
+
'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
# Given a MIDI note number, return the name and octave as a string.
|
|
12
|
+
def Utils.note_to_s(num)
|
|
13
|
+
note = num % 12
|
|
14
|
+
octave = num / 12
|
|
15
|
+
return "#{NOTE_NAMES[note]}#{octave - 1}"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Given an integer, returns it as a variable length array of bytes (the
|
|
19
|
+
# format used by MIDI files).
|
|
20
|
+
#
|
|
21
|
+
# The converse operation--converting a var len into a number--requires
|
|
22
|
+
# input from a stream of bytes. Therefore we don't supply it here. That is
|
|
23
|
+
# a part of the MIDIFile class.
|
|
24
|
+
def Utils.as_var_len(val)
|
|
25
|
+
buffer = ''
|
|
26
|
+
buffer << (val & 0x7f)
|
|
27
|
+
val = (val >> 7)
|
|
28
|
+
while val > 0
|
|
29
|
+
buffer << (0x80 + (val & 0x7f))
|
|
30
|
+
val = (val >> 7)
|
|
31
|
+
end
|
|
32
|
+
return buffer.reverse!
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# This code defines equality operators for all of the event classes. It's
|
|
2
|
+
# used by SeqTester.
|
|
3
|
+
#
|
|
4
|
+
# I don't think it is necessary to include these methods in the base Event
|
|
5
|
+
# classes. If someone disagrees, it would be trivial to move them there.
|
|
6
|
+
|
|
7
|
+
module MIDI
|
|
8
|
+
|
|
9
|
+
class Event
|
|
10
|
+
def ==(an_obj)
|
|
11
|
+
return an_obj.instance_of?(self.class) &&
|
|
12
|
+
@status == an_obj.status &&
|
|
13
|
+
@delta_time == an_obj.delta_time &&
|
|
14
|
+
@time_from_start == an_obj.time_from_start
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class ChannelEvent
|
|
19
|
+
def ==(an_obj)
|
|
20
|
+
return super(an_obj) && @channel == an_obj.channel
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class NoteEvent < ChannelEvent
|
|
25
|
+
def ==(an_obj)
|
|
26
|
+
return super(an_obj) &&
|
|
27
|
+
@note == an_obj.note && @velocity == an_obj.velocity
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class Controller < ChannelEvent
|
|
32
|
+
def ==(an_obj)
|
|
33
|
+
return super(an_obj) &&
|
|
34
|
+
@controller == an_obj.controller && @value == an_obj.value
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class ProgramChange < ChannelEvent
|
|
39
|
+
def ==(an_obj)
|
|
40
|
+
return super(an_obj) && @program == an_obj.program
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class ChannelPressure < ChannelEvent
|
|
45
|
+
def ==(an_obj)
|
|
46
|
+
return super(an_obj) && @pressure == an_obj.pressure
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
class PitchBend < ChannelEvent
|
|
51
|
+
def ==(an_obj)
|
|
52
|
+
return super(an_obj) && @value == an_obj.value
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class SystemExclusive < SystemCommon
|
|
57
|
+
def ==(an_obj)
|
|
58
|
+
return super(an_obj) && @data == an_obj.data
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
class SongPointer < SystemCommon
|
|
63
|
+
def ==(an_obj)
|
|
64
|
+
return super(an_obj) && @pointer == an_obj.pointer
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
class SongSelect < SystemCommon
|
|
69
|
+
def ==(an_obj)
|
|
70
|
+
return super(an_obj) && @song == an_obj.song
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
class MetaEvent < Event
|
|
75
|
+
def ==(an_obj)
|
|
76
|
+
return super(an_obj) && @meta_type == an_obj.meta_type &&
|
|
77
|
+
@data == an_obj.data
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
end
|
data/test/test_event.rb
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Start looking for MIDI classes in the directory above this one.
|
|
2
|
+
# This forces us to use the local copy of MIDI, even if there is
|
|
3
|
+
# a previously installed version out there somewhere.
|
|
4
|
+
$LOAD_PATH[0, 0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
|
5
|
+
|
|
6
|
+
require 'test/unit'
|
|
7
|
+
require 'midilib'
|
|
8
|
+
|
|
9
|
+
class EventTester < Test::Unit::TestCase
|
|
10
|
+
|
|
11
|
+
def test_note_on
|
|
12
|
+
e = MIDI::NoteOnEvent.new
|
|
13
|
+
assert_equal(MIDI::NOTE_ON, e.status)
|
|
14
|
+
assert_equal(0, e.channel)
|
|
15
|
+
assert_equal(64, e.note)
|
|
16
|
+
assert_equal(64, e.velocity)
|
|
17
|
+
assert_equal(0, e.delta_time)
|
|
18
|
+
assert_equal(0, e.time_from_start)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_quantize
|
|
22
|
+
e = MIDI::NoteOnEvent.new
|
|
23
|
+
e.quantize_to(4)
|
|
24
|
+
assert_equal(0, e.time_from_start)
|
|
25
|
+
|
|
26
|
+
# Each value in this array is the expected quantized value of
|
|
27
|
+
# its index in the array.
|
|
28
|
+
|
|
29
|
+
# Test with quantize_to(4)
|
|
30
|
+
[0, 0, 4, 4, 4, 4, 8, 8, 8, 8, 12, 12, 12, 12, 16].each_with_index {
|
|
31
|
+
| after, before |
|
|
32
|
+
e.time_from_start = before
|
|
33
|
+
e.quantize_to(4)
|
|
34
|
+
assert_equal(after, e.time_from_start)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Test with quantize_to(6)
|
|
38
|
+
[0, 0, 0, 6, 6, 6, 6, 6, 6, 12, 12, 12, 12, 12, 12,
|
|
39
|
+
18, 18, 18, 18, 18, 18, 24].each_with_index {
|
|
40
|
+
| after, before |
|
|
41
|
+
e.time_from_start = before
|
|
42
|
+
e.quantize_to(6)
|
|
43
|
+
assert_equal(after, e.time_from_start)
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def test_to_s
|
|
48
|
+
e = MIDI::NoteOnEvent.new
|
|
49
|
+
assert_equal("0: ch 00 on 40 40", e.to_s)
|
|
50
|
+
e.print_decimal_numbers = true
|
|
51
|
+
assert_equal("0: ch 0 on 64 64", e.to_s)
|
|
52
|
+
e.print_note_names = true
|
|
53
|
+
assert_equal("0: ch 0 on E4 64", e.to_s)
|
|
54
|
+
e.print_decimal_numbers = false
|
|
55
|
+
assert_equal("0: ch 00 on E4 40", e.to_s)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_bools_note_on
|
|
59
|
+
e = MIDI::NoteOnEvent.new
|
|
60
|
+
assert(e.channel?)
|
|
61
|
+
assert(e.note?)
|
|
62
|
+
assert(e.note_on?)
|
|
63
|
+
assert(!e.note_off?)
|
|
64
|
+
assert(!e.meta?)
|
|
65
|
+
assert(!e.system?)
|
|
66
|
+
assert(!e.realtime?)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def test_bools_note_off
|
|
70
|
+
e = MIDI::NoteOffEvent.new
|
|
71
|
+
assert(e.channel?)
|
|
72
|
+
assert(e.note?)
|
|
73
|
+
assert(!e.note_on?)
|
|
74
|
+
assert(e.note_off?)
|
|
75
|
+
assert(!e.meta?)
|
|
76
|
+
assert(!e.system?)
|
|
77
|
+
assert(!e.realtime?)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_bools_realtime
|
|
81
|
+
e = MIDI::Clock.new
|
|
82
|
+
assert(!e.channel?)
|
|
83
|
+
assert(!e.note?)
|
|
84
|
+
assert(!e.meta?)
|
|
85
|
+
assert(!e.system?)
|
|
86
|
+
assert(e.realtime?)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def test_bools_controller
|
|
90
|
+
e = MIDI::Controller.new
|
|
91
|
+
assert(e.channel?)
|
|
92
|
+
assert(!e.note?)
|
|
93
|
+
assert(!e.meta?)
|
|
94
|
+
assert(!e.system?)
|
|
95
|
+
assert(!e.realtime?)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def test_bools_meta
|
|
99
|
+
e = MIDI::MetaEvent.new(MIDI::META_SEQ_NUM)
|
|
100
|
+
assert(!e.channel?)
|
|
101
|
+
assert(!e.note?)
|
|
102
|
+
assert(e.meta?)
|
|
103
|
+
assert(!e.system?)
|
|
104
|
+
assert(!e.realtime?)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def test_bools_system
|
|
108
|
+
e = MIDI::TuneRequest.new
|
|
109
|
+
assert(!e.channel?)
|
|
110
|
+
assert(!e.note?)
|
|
111
|
+
assert(!e.meta?)
|
|
112
|
+
assert(e.system?)
|
|
113
|
+
assert(!e.realtime?)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
end
|
data/test/test_io.rb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Start looking for MIDI classes in the directory above this one.
|
|
2
|
+
# This forces us to use the local copy of MIDI, even if there is
|
|
3
|
+
# a previously installed version out there somewhere.
|
|
4
|
+
$LOAD_PATH[0, 0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
|
5
|
+
# Add current directory so we can find event_equality
|
|
6
|
+
$LOAD_PATH[0, 0] = File.dirname(__FILE__)
|
|
7
|
+
|
|
8
|
+
require 'test/unit'
|
|
9
|
+
require 'midilib'
|
|
10
|
+
require 'event_equality'
|
|
11
|
+
|
|
12
|
+
class IOTester < Test::Unit::TestCase
|
|
13
|
+
|
|
14
|
+
SEQ_TEST_FILE = File.join(File.dirname(__FILE__), 'test.mid')
|
|
15
|
+
OUTPUT_FILE = 'testout.mid'
|
|
16
|
+
|
|
17
|
+
def compare_tracks(t0, t1)
|
|
18
|
+
assert_equal(t0.name, t1.name, 'track names differ')
|
|
19
|
+
assert_equal(t0.events.length, t1.events.length,
|
|
20
|
+
'number of track events differ')
|
|
21
|
+
t0.each_with_index { | ev0, i |
|
|
22
|
+
assert_equal(ev0, t1.events[i], 'events differ')
|
|
23
|
+
}
|
|
24
|
+
assert_equal(t0.instrument, t1.instrument)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def compare_sequences(s0, s1)
|
|
28
|
+
assert_equal(s0.name, s1.name, 'sequence names differ')
|
|
29
|
+
assert_equal(s0.tracks.length, s1.tracks.length,
|
|
30
|
+
'number of tracks differ')
|
|
31
|
+
s0.each_with_index { | track0, i |
|
|
32
|
+
compare_tracks(track0, s1.tracks[i])
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def test_read_and_write
|
|
37
|
+
seq0 = MIDI::Sequence.new()
|
|
38
|
+
|
|
39
|
+
puts if $DEBUG
|
|
40
|
+
puts ' reading file' if $DEBUG
|
|
41
|
+
File.open(SEQ_TEST_FILE, 'rb') { | f | seq0.read(f) }
|
|
42
|
+
|
|
43
|
+
puts ' writing file' if $DEBUG
|
|
44
|
+
File.open(OUTPUT_FILE, 'wb') { | f | seq0.write(f) }
|
|
45
|
+
|
|
46
|
+
puts ' reading file' if $DEBUG
|
|
47
|
+
seq1 = MIDI::Sequence.new()
|
|
48
|
+
File.open(OUTPUT_FILE, 'rb') { | f | seq1.read(f) }
|
|
49
|
+
|
|
50
|
+
puts ' comparing sequences' if $DEBUG
|
|
51
|
+
compare_sequences(seq0, seq1)
|
|
52
|
+
File.delete(OUTPUT_FILE)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Start looking for MIDI classes in the directory above this one.
|
|
2
|
+
# This forces us to use the local copy of MIDI, even if there is
|
|
3
|
+
# a previously installed version out there somewhere.
|
|
4
|
+
$LOAD_PATH[0, 0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
|
5
|
+
|
|
6
|
+
require 'test/unit'
|
|
7
|
+
require 'midilib'
|
|
8
|
+
|
|
9
|
+
class SequenceTester < Test::Unit::TestCase
|
|
10
|
+
|
|
11
|
+
def setup
|
|
12
|
+
@seq = MIDI::Sequence.new
|
|
13
|
+
@track = MIDI::Track.new(@seq)
|
|
14
|
+
@seq.tracks << @track
|
|
15
|
+
3.times { @track.events << MIDI::NoteOnEvent.new(0, 64, 64, 100) }
|
|
16
|
+
@track.recalc_times
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def test_basics
|
|
20
|
+
assert_equal(120, @seq.beats_per_minute)
|
|
21
|
+
assert_equal(1, @seq.tracks.length)
|
|
22
|
+
assert_equal(MIDI::Track::UNNAMED, @seq.name)
|
|
23
|
+
assert_equal(MIDI::Sequence::DEFAULT_TEMPO, @seq.bpm)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_length_to_delta
|
|
27
|
+
assert_equal(480, @seq.ppqn)
|
|
28
|
+
assert_equal(480, @seq.length_to_delta(1))
|
|
29
|
+
assert_equal(240, @seq.length_to_delta(0.5))
|
|
30
|
+
|
|
31
|
+
@seq.ppqn = 12
|
|
32
|
+
assert_equal(12, @seq.ppqn)
|
|
33
|
+
assert_equal(12, @seq.length_to_delta(1))
|
|
34
|
+
assert_equal(6, @seq.length_to_delta(0.5))
|
|
35
|
+
assert_equal(5, @seq.length_to_delta(0.49))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def test_note_to_length
|
|
39
|
+
assert_equal(1, @seq.note_to_length('quarter'))
|
|
40
|
+
assert_equal(4, @seq.note_to_length('whole'))
|
|
41
|
+
assert_equal(1.5, @seq.note_to_length('dotted quarter'))
|
|
42
|
+
assert_equal(1.0 / 3.0, @seq.note_to_length('quarter triplet'))
|
|
43
|
+
assert_equal(0.5, @seq.note_to_length('dotted quarter triplet'))
|
|
44
|
+
assert_equal(1.0 / 4, @seq.note_to_length('sixteenth'))
|
|
45
|
+
assert_equal(1.0 / 4, @seq.note_to_length('16th'))
|
|
46
|
+
assert_equal(1.0 / 8, @seq.note_to_length('thirty second'))
|
|
47
|
+
assert_equal(1.0 / 8, @seq.note_to_length('32nd'))
|
|
48
|
+
assert_equal(1.0 / 16, @seq.note_to_length('sixty fourth'))
|
|
49
|
+
assert_equal(1.0 / 16, @seq.note_to_length('sixtyfourth'))
|
|
50
|
+
assert_equal(1.0 / 16, @seq.note_to_length('64th'))
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def test_note_to_delta
|
|
54
|
+
assert_equal(480, @seq.note_to_delta('quarter'))
|
|
55
|
+
assert_equal(480 * 4, @seq.note_to_delta('whole'))
|
|
56
|
+
assert_equal(720, @seq.note_to_delta('dotted quarter'))
|
|
57
|
+
assert_equal(480 / 3.0, @seq.note_to_delta('quarter triplet'))
|
|
58
|
+
assert_equal((480 / 3.0) * 1.5,
|
|
59
|
+
@seq.note_to_delta('dotted quarter triplet'))
|
|
60
|
+
assert_equal(480 / 4, @seq.note_to_delta('sixteenth'))
|
|
61
|
+
assert_equal(480 / 4, @seq.note_to_delta('16th'))
|
|
62
|
+
assert_equal(480 / 8, @seq.note_to_delta('thirty second'))
|
|
63
|
+
assert_equal(480 / 8, @seq.note_to_delta('32nd'))
|
|
64
|
+
assert_equal(480 / 16, @seq.note_to_delta('sixty fourth'))
|
|
65
|
+
assert_equal(480 / 16, @seq.note_to_delta('sixtyfourth'))
|
|
66
|
+
assert_equal(480 / 16, @seq.note_to_delta('64th'))
|
|
67
|
+
end
|
|
68
|
+
end
|