jmtk 0.0.3.3-java
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/.yardopts +10 -0
- data/DEVELOPMENT_NOTES.md +115 -0
- data/INTRO.md +129 -0
- data/LICENSE.txt +27 -0
- data/README.md +50 -0
- data/Rakefile +102 -0
- data/bin/jmtk +250 -0
- data/bin/mtk +250 -0
- data/examples/crescendo.rb +20 -0
- data/examples/drum_pattern.rb +23 -0
- data/examples/dynamic_pattern.rb +36 -0
- data/examples/gets_and_play.rb +27 -0
- data/examples/notation.rb +22 -0
- data/examples/play_midi.rb +17 -0
- data/examples/print_midi.rb +13 -0
- data/examples/random_tone_row.rb +18 -0
- data/examples/syntax_to_midi.rb +28 -0
- data/examples/test_output.rb +7 -0
- data/examples/tone_row_melody.rb +23 -0
- data/lib/mtk.rb +76 -0
- data/lib/mtk/core/duration.rb +213 -0
- data/lib/mtk/core/intensity.rb +158 -0
- data/lib/mtk/core/interval.rb +157 -0
- data/lib/mtk/core/pitch.rb +154 -0
- data/lib/mtk/core/pitch_class.rb +194 -0
- data/lib/mtk/events/event.rb +119 -0
- data/lib/mtk/events/note.rb +112 -0
- data/lib/mtk/events/parameter.rb +54 -0
- data/lib/mtk/events/timeline.rb +232 -0
- data/lib/mtk/groups/chord.rb +56 -0
- data/lib/mtk/groups/collection.rb +196 -0
- data/lib/mtk/groups/melody.rb +96 -0
- data/lib/mtk/groups/pitch_class_set.rb +163 -0
- data/lib/mtk/groups/pitch_collection.rb +23 -0
- data/lib/mtk/io/dls_synth_device.rb +146 -0
- data/lib/mtk/io/dls_synth_output.rb +62 -0
- data/lib/mtk/io/jsound_input.rb +87 -0
- data/lib/mtk/io/jsound_output.rb +82 -0
- data/lib/mtk/io/midi_file.rb +209 -0
- data/lib/mtk/io/midi_input.rb +97 -0
- data/lib/mtk/io/midi_output.rb +195 -0
- data/lib/mtk/io/notation.rb +162 -0
- data/lib/mtk/io/unimidi_input.rb +117 -0
- data/lib/mtk/io/unimidi_output.rb +140 -0
- data/lib/mtk/lang/durations.rb +57 -0
- data/lib/mtk/lang/intensities.rb +61 -0
- data/lib/mtk/lang/intervals.rb +73 -0
- data/lib/mtk/lang/mtk_grammar.citrus +237 -0
- data/lib/mtk/lang/parser.rb +29 -0
- data/lib/mtk/lang/pitch_classes.rb +29 -0
- data/lib/mtk/lang/pitches.rb +52 -0
- data/lib/mtk/lang/pseudo_constants.rb +26 -0
- data/lib/mtk/lang/variable.rb +32 -0
- data/lib/mtk/numeric_extensions.rb +66 -0
- data/lib/mtk/patterns/chain.rb +49 -0
- data/lib/mtk/patterns/choice.rb +43 -0
- data/lib/mtk/patterns/cycle.rb +18 -0
- data/lib/mtk/patterns/for_each.rb +71 -0
- data/lib/mtk/patterns/function.rb +39 -0
- data/lib/mtk/patterns/lines.rb +54 -0
- data/lib/mtk/patterns/palindrome.rb +45 -0
- data/lib/mtk/patterns/pattern.rb +171 -0
- data/lib/mtk/patterns/sequence.rb +20 -0
- data/lib/mtk/sequencers/event_builder.rb +132 -0
- data/lib/mtk/sequencers/legato_sequencer.rb +24 -0
- data/lib/mtk/sequencers/rhythmic_sequencer.rb +28 -0
- data/lib/mtk/sequencers/sequencer.rb +111 -0
- data/lib/mtk/sequencers/step_sequencer.rb +26 -0
- data/spec/mtk/core/duration_spec.rb +372 -0
- data/spec/mtk/core/intensity_spec.rb +289 -0
- data/spec/mtk/core/interval_spec.rb +265 -0
- data/spec/mtk/core/pitch_class_spec.rb +343 -0
- data/spec/mtk/core/pitch_spec.rb +297 -0
- data/spec/mtk/events/event_spec.rb +234 -0
- data/spec/mtk/events/note_spec.rb +174 -0
- data/spec/mtk/events/parameter_spec.rb +220 -0
- data/spec/mtk/events/timeline_spec.rb +430 -0
- data/spec/mtk/groups/chord_spec.rb +85 -0
- data/spec/mtk/groups/collection_spec.rb +374 -0
- data/spec/mtk/groups/melody_spec.rb +225 -0
- data/spec/mtk/groups/pitch_class_set_spec.rb +340 -0
- data/spec/mtk/io/midi_file_spec.rb +243 -0
- data/spec/mtk/io/midi_output_spec.rb +102 -0
- data/spec/mtk/lang/durations_spec.rb +89 -0
- data/spec/mtk/lang/intensities_spec.rb +101 -0
- data/spec/mtk/lang/intervals_spec.rb +143 -0
- data/spec/mtk/lang/parser_spec.rb +603 -0
- data/spec/mtk/lang/pitch_classes_spec.rb +62 -0
- data/spec/mtk/lang/pitches_spec.rb +56 -0
- data/spec/mtk/lang/pseudo_constants_spec.rb +20 -0
- data/spec/mtk/lang/variable_spec.rb +52 -0
- data/spec/mtk/numeric_extensions_spec.rb +83 -0
- data/spec/mtk/patterns/chain_spec.rb +110 -0
- data/spec/mtk/patterns/choice_spec.rb +97 -0
- data/spec/mtk/patterns/cycle_spec.rb +123 -0
- data/spec/mtk/patterns/for_each_spec.rb +136 -0
- data/spec/mtk/patterns/function_spec.rb +120 -0
- data/spec/mtk/patterns/lines_spec.rb +77 -0
- data/spec/mtk/patterns/palindrome_spec.rb +108 -0
- data/spec/mtk/patterns/pattern_spec.rb +132 -0
- data/spec/mtk/patterns/sequence_spec.rb +203 -0
- data/spec/mtk/sequencers/event_builder_spec.rb +245 -0
- data/spec/mtk/sequencers/legato_sequencer_spec.rb +45 -0
- data/spec/mtk/sequencers/rhythmic_sequencer_spec.rb +84 -0
- data/spec/mtk/sequencers/sequencer_spec.rb +215 -0
- data/spec/mtk/sequencers/step_sequencer_spec.rb +93 -0
- data/spec/spec_coverage.rb +2 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/test.mid +0 -0
- metadata +226 -0
data/bin/mtk
ADDED
@@ -0,0 +1,250 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'mtk'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
options = {}
|
7
|
+
|
8
|
+
|
9
|
+
#######################################################################
|
10
|
+
|
11
|
+
option_parser = OptionParser.new do |opts|
|
12
|
+
|
13
|
+
opts.banner = "Usage: #{$0} [options]"
|
14
|
+
opts.separator ''
|
15
|
+
opts.separator 'Options:'
|
16
|
+
|
17
|
+
opts.on('-c FILE', '--convert FILE', 'Convert a file containing MTK syntax to MIDI',
|
18
|
+
'if a --file is given, write the MIDI to a file',
|
19
|
+
'if an --output is given, play the MIDI',
|
20
|
+
'otherwise print the MIDI') {|file| options[:convert] = file }
|
21
|
+
|
22
|
+
opts.separator ''
|
23
|
+
|
24
|
+
opts.on('-e [syntax]', '--eval [syntax]', 'Convert the given MTK syntax String to MIDI',
|
25
|
+
'or start an interactive interpreter when [syntax] is omitted',
|
26
|
+
'Behaves like --convert when a --file or --output is given') {|syntax| options[:eval] = syntax }
|
27
|
+
|
28
|
+
opts.separator ''
|
29
|
+
|
30
|
+
opts.on('-f FILE', '--file FILE', 'Write the output of --convert, --eval, --input, or --watch to a file'
|
31
|
+
) {|file| options[:file] = file }
|
32
|
+
|
33
|
+
opts.separator ''
|
34
|
+
|
35
|
+
opts.on('-h', '--help', 'Show this message') { puts opts; exit }
|
36
|
+
|
37
|
+
opts.separator ''
|
38
|
+
|
39
|
+
opts.on('-i INPUT', '--input INPUT', 'Set MIDI input for recording',
|
40
|
+
'if no --file is specified, prints the recorded MIDI') {|input| options[:input] = input }
|
41
|
+
|
42
|
+
opts.separator ''
|
43
|
+
|
44
|
+
opts.on('-l', '--list', 'List available MIDI devices for --input and --output') { options[:list] = true }
|
45
|
+
|
46
|
+
opts.separator ''
|
47
|
+
|
48
|
+
opts.on('-m', '--monitor', 'Monitor MIDI input while recording') { options[:monitor] = true }
|
49
|
+
|
50
|
+
opts.separator ''
|
51
|
+
|
52
|
+
opts.on('-o OUTPUT', '--output OUTPUT', 'Set MIDI output for playing') {|output| options[:output] = output }
|
53
|
+
|
54
|
+
opts.separator ''
|
55
|
+
|
56
|
+
opts.on('-p FILE', '--play FILE', 'Play or print the contents of a MIDI file',
|
57
|
+
'if no --output is specified, print the MIDI file') {|file| options[:play] = file }
|
58
|
+
|
59
|
+
opts.separator ''
|
60
|
+
|
61
|
+
#opts.on('-t', '--tutorial', 'Start an interactive tutorial for the MTK syntax') { options[:tutorial] = true }
|
62
|
+
#
|
63
|
+
#opts.separator ''
|
64
|
+
|
65
|
+
opts.on('-w FILE', '--watch FILE', 'Watch an MTK syntax file for changes and automatically convert to MIDI',
|
66
|
+
'Behaves like --convert when a --file or --output is given') {|file| options[:watch] = file }
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
#######################################################################
|
72
|
+
|
73
|
+
puts option_parser and exit if ARGV.length == 0
|
74
|
+
#p ARGV
|
75
|
+
#p options
|
76
|
+
|
77
|
+
ERROR_INVALID_COMMAND = 1
|
78
|
+
ERROR_FILE_NOT_FOUND = 2
|
79
|
+
ERROR_OUTPUT_NOT_FOUND = 3
|
80
|
+
ERROR_INPUT_NOT_FOUND = 4
|
81
|
+
|
82
|
+
# Immediately trying to play output while Ruby is still "warming up" can cause timing issues with
|
83
|
+
# the first couple notes. So we play this "empty" Timeline containing a rest to address that issue.
|
84
|
+
WARMUP = MTK::Events::Timeline.from_h( {0 => MTK.Note(60,-1)} )
|
85
|
+
|
86
|
+
#######################################################################
|
87
|
+
|
88
|
+
begin
|
89
|
+
option_parser.parse!
|
90
|
+
rescue OptionParser::MissingArgument, OptionParser::InvalidOption
|
91
|
+
puts "Invalid command, #{$!}"
|
92
|
+
puts "For command line help: #{$0} --help"
|
93
|
+
puts "For command line help: #{$0} --help"
|
94
|
+
exit ERROR_INVALID_COMMAND
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def setup_io
|
99
|
+
require 'mtk/io/midi_input'
|
100
|
+
require 'mtk/io/midi_output'
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
def convert(mtk_syntax)
|
105
|
+
sequencer = MTK::Lang::Parser.parse(mtk_syntax)
|
106
|
+
if sequencer
|
107
|
+
timeline = sequencer.to_timeline
|
108
|
+
output(timeline)
|
109
|
+
end
|
110
|
+
rescue Citrus::ParseError
|
111
|
+
STDERR.puts $!
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
def output(timelines, print_header='Timeline')
|
116
|
+
timelines = [timelines] unless timelines.is_a? Array
|
117
|
+
if @output
|
118
|
+
@output.play WARMUP
|
119
|
+
@output.play timelines.first # TODO: support multiple timelines
|
120
|
+
elsif @file
|
121
|
+
require 'mtk/io/midi_file'
|
122
|
+
MTK.MIDIFile(@file).write timelines
|
123
|
+
else
|
124
|
+
puts print_header, timelines
|
125
|
+
puts
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
def record
|
131
|
+
if @input
|
132
|
+
print "Press Enter to begin recording MIDI input..."
|
133
|
+
gets
|
134
|
+
puts "Recording input. Press control-C to stop."
|
135
|
+
@input.record monitor:@monitor
|
136
|
+
Signal.trap("INT") do # SIGINT = control-C
|
137
|
+
@input.stop
|
138
|
+
output @input.to_timeline, "\nRecorded MIDI data"
|
139
|
+
exit
|
140
|
+
end
|
141
|
+
loop{ sleep 0.01 }
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
def watch_file_updated?
|
147
|
+
mtime = File.stat(@watch_file).mtime
|
148
|
+
updated = @watch_file_mtime.nil? || @watch_file_mtime < mtime
|
149
|
+
@watch_file_mtime = mtime
|
150
|
+
updated
|
151
|
+
end
|
152
|
+
|
153
|
+
|
154
|
+
#######################################################################
|
155
|
+
|
156
|
+
if options[:list]
|
157
|
+
setup_io
|
158
|
+
input_names = MTK::IO::MIDIInput.devices_by_name.keys
|
159
|
+
output_names = MTK::IO::MIDIOutput.devices_by_name.keys
|
160
|
+
puts
|
161
|
+
puts (['INPUTS:'] + input_names).join("\n * ")
|
162
|
+
puts
|
163
|
+
puts (['OUTPUTS:']+output_names).join("\n * ")
|
164
|
+
puts
|
165
|
+
puts 'When specifying --input INPUT or --output OUTPUT, the first substring match will be used.'
|
166
|
+
puts "For example: --output IAC will use 'Apple Inc. IAC Driver' if it's the first OUTPUT containing 'IAC'"
|
167
|
+
puts
|
168
|
+
exit
|
169
|
+
end
|
170
|
+
|
171
|
+
@monitor = true if options[:monitor]
|
172
|
+
|
173
|
+
if options[:input]
|
174
|
+
setup_io
|
175
|
+
input_name = options[:input]
|
176
|
+
@input = MTK::IO::MIDIInput.find_by_name /#{input_name}/
|
177
|
+
if @input
|
178
|
+
puts "Using input '#{@input.name}'"
|
179
|
+
else
|
180
|
+
STDERR.puts "Input '#{input_name}' not found."
|
181
|
+
exit ERROR_INPUT_NOT_FOUND
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
if options[:output]
|
186
|
+
setup_io
|
187
|
+
output_name = options[:output]
|
188
|
+
@output = MTK::IO::MIDIOutput.find_by_name /#{output_name}/
|
189
|
+
if @output
|
190
|
+
puts "Using output '#{@output.name}'"
|
191
|
+
else
|
192
|
+
STDERR.puts "Output '#{output_name}' not found."
|
193
|
+
exit ERROR_OUTPUT_NOT_FOUND
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
file = options[:file]
|
198
|
+
@file = file if file
|
199
|
+
|
200
|
+
if options[:play]
|
201
|
+
filename = options[:play]
|
202
|
+
require 'mtk/io/midi_file'
|
203
|
+
timelines = MTK.MIDIFile(filename).to_timelines
|
204
|
+
output(timelines, "Timeline for #{filename}")
|
205
|
+
end
|
206
|
+
|
207
|
+
if options.has_key? :eval
|
208
|
+
mtk_syntax = options[:eval]
|
209
|
+
if mtk_syntax.nil?
|
210
|
+
puts "Starting the interactive interpreter."
|
211
|
+
begin
|
212
|
+
loop do
|
213
|
+
puts "Enter MTK syntax. Press Ctrl+C to exit."
|
214
|
+
convert(gets)
|
215
|
+
end
|
216
|
+
rescue SystemExit,Interrupt
|
217
|
+
Kernel.exit
|
218
|
+
end
|
219
|
+
else
|
220
|
+
convert(mtk_syntax)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
if options[:convert]
|
225
|
+
mtk_syntax_file = options[:convert]
|
226
|
+
mtk_syntax = IO.read(mtk_syntax_file)
|
227
|
+
convert(mtk_syntax)
|
228
|
+
end
|
229
|
+
|
230
|
+
if options[:watch]
|
231
|
+
@watch_file = options[:watch]
|
232
|
+
puts "Watching #{@watch_file}. Press Ctrl+C to exit."
|
233
|
+
watch_file_updated? # prime the watcher
|
234
|
+
begin
|
235
|
+
loop do
|
236
|
+
mtk_syntax = IO.read(@watch_file)
|
237
|
+
convert(mtk_syntax)
|
238
|
+
Kernel.sleep(0.5) until watch_file_updated?
|
239
|
+
puts "#{Time.new}: #{@watch_file} updated"
|
240
|
+
end
|
241
|
+
rescue SystemExit,Interrupt
|
242
|
+
Kernel.exit
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
#if options.has_key? :tutorial
|
247
|
+
# puts "TODO: tutorial"
|
248
|
+
#end
|
249
|
+
|
250
|
+
record if @input
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Generate a MIDI file of the C-major scale with a crescendo (it increases in intensity)
|
2
|
+
#
|
3
|
+
# NOTE: this blindly overwrites any existing MTK-crescendo.mid file, unless an argument is provided
|
4
|
+
|
5
|
+
require 'mtk'
|
6
|
+
require 'mtk/io/midi_file'
|
7
|
+
include MTK
|
8
|
+
include Lang::Pitches
|
9
|
+
include Lang::Intensities
|
10
|
+
|
11
|
+
file = ARGV[0] || 'MTK-crescendo.mid'
|
12
|
+
|
13
|
+
scale = Patterns.Sequence C4,D4,E4,F4,G4,A4,B4,C5
|
14
|
+
crescendo = Patterns.Lines pp, [fff, scale.length-1] # step from pp to fff over the length of the scale
|
15
|
+
|
16
|
+
sequencer = Sequencers.StepSequencer scale, crescendo
|
17
|
+
timeline = sequencer.to_timeline
|
18
|
+
|
19
|
+
MIDIFile(file).write timeline
|
20
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'mtk'
|
2
|
+
require 'mtk/io/midi_file'
|
3
|
+
include MTK
|
4
|
+
include Lang::Pitches
|
5
|
+
include Lang::Intensities
|
6
|
+
|
7
|
+
file = ARGV[0] || "MTK-#{File.basename(__FILE__,'.rb')}.mid"
|
8
|
+
|
9
|
+
_ = nil # defines _ as a rest
|
10
|
+
|
11
|
+
pattern = {# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
12
|
+
C2 => [fff, _, _, _, mf, _, _, _, o, _, _, _, mp, _, _, _], # kick
|
13
|
+
Db2 => [ _, _, o, _, _, _, mp, _, _, _, o, _, _, _, mf, _], # rim shot
|
14
|
+
D2 => [ _, mp, _, mp, _, mp, _, mf, _, mp, _, mp, _, pp, _, mf] # snare
|
15
|
+
}
|
16
|
+
|
17
|
+
timeline = Events::Timeline.new
|
18
|
+
for pitch,intensities in pattern
|
19
|
+
track = Sequencers::StepSequencer( Patterns.Sequence(intensities), default_pitch:pitch, channel:10 )
|
20
|
+
timeline.merge track.to_timeline
|
21
|
+
end
|
22
|
+
|
23
|
+
MIDIFile(file).write timeline
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Generate a MIDI file using a lambda to dynamically generate the pitches
|
2
|
+
#
|
3
|
+
# NOTE: this blindly overwrites any existing MTK-dynamic_pattern.mid file, unless an argument is provided
|
4
|
+
|
5
|
+
require 'mtk'
|
6
|
+
require 'mtk/io/midi_file'
|
7
|
+
include MTK
|
8
|
+
include MTK::Lang::Pitches
|
9
|
+
include MTK::Lang::Intensities
|
10
|
+
include MTK::Lang::Intervals
|
11
|
+
|
12
|
+
file = ARGV[0] || "MTK-#{File.basename __FILE__,'.rb'}.mid"
|
13
|
+
|
14
|
+
interval_generator = lambda do
|
15
|
+
# Randomly return intervals
|
16
|
+
r = rand
|
17
|
+
case
|
18
|
+
when r < 0.1 then m2
|
19
|
+
when r < 0.4 then M2
|
20
|
+
when r < 0.5 then m3
|
21
|
+
when r < 0.6 then M3
|
22
|
+
when r < 0.7 then P4
|
23
|
+
when r < 0.8 then -M3
|
24
|
+
when r < 0.95 then -P5
|
25
|
+
else -P8
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
pitches = Patterns.Function( interval_generator, max_elements: 24 )
|
30
|
+
|
31
|
+
# we'll also use a weighted choice to generate the intensities
|
32
|
+
intensities = Patterns.Choice( mp,mf,o,ff,fff, weights: [1,2,3,2,1], max_cycles: 24 )
|
33
|
+
|
34
|
+
sequencer = Sequencers.StepSequencer( pitches,intensities, step_size: 0.5, max_interval: 17 )
|
35
|
+
|
36
|
+
MIDIFile(file).write( sequencer.to_timeline )
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Enter space-separated pitch classes (A,B,C,D,E,F,G) at the prompt and hear them play.
|
2
|
+
|
3
|
+
require 'mtk'
|
4
|
+
require_relative 'helpers/output_selector'
|
5
|
+
include MTK
|
6
|
+
include MTK::Core
|
7
|
+
|
8
|
+
output = OutputSelector.ensure_output ARGV[0]
|
9
|
+
|
10
|
+
def get_pitch_classes
|
11
|
+
puts "Enter pitch classes:"
|
12
|
+
input = STDIN.gets
|
13
|
+
input.strip.split(/\s+/).map{|name| PitchClass[name] } if input
|
14
|
+
rescue
|
15
|
+
puts "Invalid input."
|
16
|
+
end
|
17
|
+
|
18
|
+
while (pitch_classes = get_pitch_classes)
|
19
|
+
sequence = Patterns.Sequence pitch_classes
|
20
|
+
sequencer = Sequencers.StepSequencer sequence
|
21
|
+
timeline = sequencer.to_timeline
|
22
|
+
|
23
|
+
puts "Playing: #{pitch_classes}"
|
24
|
+
puts "Timeline:\n#{timeline}"
|
25
|
+
output.play timeline
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'mtk'
|
2
|
+
require 'mtk/io/notation'
|
3
|
+
include MTK
|
4
|
+
|
5
|
+
def arg_error(error)
|
6
|
+
puts "Usage: ruby #$0 syntax output_file"
|
7
|
+
puts error
|
8
|
+
exit 1
|
9
|
+
end
|
10
|
+
|
11
|
+
syntax = ARGV[0]
|
12
|
+
arg_error "MTK syntax string not provided" unless syntax
|
13
|
+
|
14
|
+
|
15
|
+
file = ARGV[1]
|
16
|
+
arg_error "The output_file must end in '.png', '.pdf', or '.ps'" unless file
|
17
|
+
|
18
|
+
|
19
|
+
sequencer = MTK::Lang::Parser.parse(syntax)
|
20
|
+
timeline = sequencer.to_timeline
|
21
|
+
# MTK::IO::Notation.open(file).write(timeline)
|
22
|
+
MTK::IO::Notation.open(file, dpi:300).write(timeline) # higher resolution PNG
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Play a given MIDI file with the given MIDI output
|
2
|
+
|
3
|
+
file, output_name = ARGV[0], ARGV[1]
|
4
|
+
unless file
|
5
|
+
puts "Usage: ruby #$0 midi_file [output_device]"
|
6
|
+
exit 1
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'mtk'
|
10
|
+
require 'mtk/io/midi_file'
|
11
|
+
require_relative 'helpers/output_selector'
|
12
|
+
|
13
|
+
output = OutputSelector.ensure_output(output_name)
|
14
|
+
|
15
|
+
timeline = MTK.MIDIFile(file).to_timelines
|
16
|
+
|
17
|
+
output.play(timeline)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Generate a MIDI file of a random 12-tone row
|
2
|
+
#
|
3
|
+
# NOTE: this blindly overwrites any existing MTK-random_tone_row.mid file, unless an argument is provided
|
4
|
+
|
5
|
+
require 'mtk'
|
6
|
+
require 'mtk/io/midi_file'
|
7
|
+
include MTK
|
8
|
+
|
9
|
+
file = ARGV[0] || 'MTK-random_tone_row.mid'
|
10
|
+
|
11
|
+
row = Groups::PitchClassSet.random_row
|
12
|
+
sequence = Patterns.Sequence *row
|
13
|
+
|
14
|
+
sequencer = Sequencers.StepSequencer sequence
|
15
|
+
timeline = sequencer.to_timeline
|
16
|
+
|
17
|
+
MIDIFile(file).write timeline
|
18
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Generate a MIDI file by reading in a file containing the MTK custom syntax (see mtk_grammar.citrus and grammar_spec.rb)
|
2
|
+
#
|
3
|
+
# NOTE: this blindly overwrites any existing MTK-syntax_to_midi.mid file, unless a second argument is provided
|
4
|
+
|
5
|
+
require 'mtk'
|
6
|
+
require 'mtk/io/midi_file'
|
7
|
+
|
8
|
+
input = ARGV[0]
|
9
|
+
if input.nil?
|
10
|
+
STDERR.puts "Input file is required."
|
11
|
+
STDERR.puts "Usage: #{$0} input [output]"
|
12
|
+
exit 1
|
13
|
+
end
|
14
|
+
|
15
|
+
unless File.exists? input
|
16
|
+
STDERR.puts "Cannot read file: #{input}"
|
17
|
+
exit 2
|
18
|
+
end
|
19
|
+
|
20
|
+
output = ARGV[1] || "MTK-#{File.basename(__FILE__,'.rb')}.mid"
|
21
|
+
|
22
|
+
|
23
|
+
syntax = IO.read(input)
|
24
|
+
sequencer = MTK::Lang::Parser.parse(syntax)
|
25
|
+
timeline = sequencer.to_timeline
|
26
|
+
|
27
|
+
MTK::MIDIFile(output).write timeline
|
28
|
+
|