jmtk 0.0.3.3-java → 0.4-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.
- checksums.yaml +15 -0
- data/DEVELOPMENT_NOTES.md +20 -0
- data/INTRO.md +63 -31
- data/README.md +9 -3
- data/Rakefile +42 -42
- data/bin/jmtk +75 -32
- data/bin/mtk +75 -32
- data/examples/drum_pattern.rb +2 -2
- data/examples/dynamic_pattern.rb +1 -1
- data/examples/helpers/output_selector.rb +71 -0
- data/examples/notation.rb +5 -1
- data/examples/tone_row_melody.rb +1 -1
- data/lib/mtk.rb +1 -0
- data/lib/mtk/core/duration.rb +18 -3
- data/lib/mtk/core/intensity.rb +5 -3
- data/lib/mtk/core/interval.rb +21 -14
- data/lib/mtk/core/pitch.rb +2 -0
- data/lib/mtk/core/pitch_class.rb +6 -3
- data/lib/mtk/events/event.rb +2 -1
- data/lib/mtk/events/note.rb +1 -1
- data/lib/mtk/events/parameter.rb +1 -0
- data/lib/mtk/events/rest.rb +85 -0
- data/lib/mtk/events/timeline.rb +6 -2
- data/lib/mtk/io/jsound_input.rb +9 -3
- data/lib/mtk/io/midi_file.rb +38 -2
- data/lib/mtk/io/midi_input.rb +1 -1
- data/lib/mtk/io/midi_output.rb +95 -4
- data/lib/mtk/io/unimidi_input.rb +7 -3
- data/lib/mtk/lang/durations.rb +31 -26
- data/lib/mtk/lang/intensities.rb +29 -30
- data/lib/mtk/lang/intervals.rb +108 -41
- data/lib/mtk/lang/mtk_grammar.citrus +14 -4
- data/lib/mtk/lang/parser.rb +10 -5
- data/lib/mtk/lang/pitch_classes.rb +45 -17
- data/lib/mtk/lang/pitches.rb +169 -32
- data/lib/mtk/lang/tutorial.rb +279 -0
- data/lib/mtk/lang/tutorial_lesson.rb +87 -0
- data/lib/mtk/sequencers/event_builder.rb +29 -8
- data/spec/mtk/core/duration_spec.rb +14 -1
- data/spec/mtk/core/intensity_spec.rb +1 -1
- data/spec/mtk/events/event_spec.rb +10 -16
- data/spec/mtk/events/note_spec.rb +3 -3
- data/spec/mtk/events/rest_spec.rb +184 -0
- data/spec/mtk/events/timeline_spec.rb +5 -1
- data/spec/mtk/io/midi_file_spec.rb +13 -2
- data/spec/mtk/io/midi_output_spec.rb +42 -9
- data/spec/mtk/lang/durations_spec.rb +5 -5
- data/spec/mtk/lang/intensities_spec.rb +5 -5
- data/spec/mtk/lang/intervals_spec.rb +139 -13
- data/spec/mtk/lang/parser_spec.rb +65 -25
- data/spec/mtk/lang/pitch_classes_spec.rb +0 -11
- data/spec/mtk/lang/pitches_spec.rb +0 -15
- data/spec/mtk/patterns/chain_spec.rb +7 -7
- data/spec/mtk/patterns/for_each_spec.rb +2 -2
- data/spec/mtk/sequencers/event_builder_spec.rb +49 -17
- metadata +12 -22
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MjlmMWRmOTljZjQ0MWViNmQ0YzJmNGQ3N2U2NTljOGY3OGE3MTRmMQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NDQwMTQ4ZjA4ZWI0YjI2MDY0YzE2ZTQzNDM5ZjA3ZGU5YzQ2ZWMzMQ==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
OThkZmNiZDcxNTM0OWEzZDU1YzRlZTlhODFlYTc1ZGE5OTMwY2EyMjc4ZGFj
|
10
|
+
M2U2ZTEwYzBiNDk0OWI1ZmY1YmM2ZjdjYzViYTZkMDQ5MDU0MTZjYTEzMTdl
|
11
|
+
ZGM5NTZjNmZkMDU4MjdmY2E3NGNhN2YzMTJhM2RmMjEwYWMyMjM=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YmQxODMzMjUwZThjOGQ3OGQ4OGFkMmYzNTE5NGRmNmZiZDhmZjkxNGVmOGZj
|
14
|
+
ZGUyOGM3MTFiNzA5NTYyMWI5ZjM2MzM1MjA1ZmJlMTAyZDM0OTJmZmI2ZTg5
|
15
|
+
NzkzNmM3MWYzNTAzODI0MTU2MmRiM2I1Zjg0YzU1M2RmZTYwYjQ=
|
data/DEVELOPMENT_NOTES.md
CHANGED
@@ -113,3 +113,23 @@ or, to automatically refresh the documentation as you work:
|
|
113
113
|
|
114
114
|
bundle exec yard server -r
|
115
115
|
open http://localhost:8808
|
116
|
+
|
117
|
+
|
118
|
+
### Release the gems ###
|
119
|
+
|
120
|
+
To better handle the differing depdencies between CRuby and JRuby, there are two gems, mtk and jmtk.
|
121
|
+
You can build both gems with:
|
122
|
+
|
123
|
+
bundle exec gem:build
|
124
|
+
|
125
|
+
Do a local sanity check by installing
|
126
|
+
|
127
|
+
# Using CRuby 1.9 or 2.0:
|
128
|
+
gem install mtk-0.x.x.gem
|
129
|
+
... test mtk command ...
|
130
|
+
|
131
|
+
rvm use jruby
|
132
|
+
jgem install jmtk-0.x.x-java.gem
|
133
|
+
... test jmtk command ...
|
134
|
+
|
135
|
+
And then authorized users can push gems to rubygems.org.
|
data/INTRO.md
CHANGED
@@ -20,9 +20,12 @@ it does not change the value in-place. For example:
|
|
20
20
|
|
21
21
|
Intensity values are intended to range from 0.0 (minimum intensity) to 1.0 (maximum intensity).
|
22
22
|
|
23
|
-
A Duration of 1 is one beat (usually a quarter note, depending on the meter
|
23
|
+
A Duration of 1 is one beat (usually a quarter note, depending on the meter, but note the quarter note value in this
|
24
|
+
library is 1 beat, so adjust accordingly for non-standard meters). By convention, negative durations
|
24
25
|
indicate a rest that lasts for the absolute value duration.
|
25
26
|
|
27
|
+
Additionally there is a core type Interval to model intervals between pitches and pitch classes.
|
28
|
+
|
26
29
|
|
27
30
|
<br/>
|
28
31
|
### Events
|
@@ -30,16 +33,23 @@ indicate a rest that lasts for the absolute value duration.
|
|
30
33
|
Events group together the core data types together to represent a musical event, like a single Note.
|
31
34
|
Events can be converted to and from MIDI.
|
32
35
|
|
33
|
-
*
|
34
|
-
*
|
36
|
+
* Event
|
37
|
+
* Note
|
38
|
+
* Parameter
|
35
39
|
|
40
|
+
Paramter represents any non-note event like a MIDI CC, pitch bend, aftertouch (pressure), etc.
|
36
41
|
|
37
|
-
|
38
|
-
|
42
|
+
Events with a negative duration are considered a rest. There is also a dedicated rest class so you
|
43
|
+
don't have to specifiy unnecessary properties like pitch for a rest.
|
44
|
+
|
45
|
+
Events are organized in time via the Timeline
|
39
46
|
|
40
|
-
A collection of timed events, such as a melody, a riff, or one track of an entire song.
|
41
47
|
|
42
|
-
|
48
|
+
<br/>
|
49
|
+
### Collections
|
50
|
+
|
51
|
+
The collection classes need some work... The interface is likely to change so it's best to not rely on them too much
|
52
|
+
right now.
|
43
53
|
|
44
54
|
|
45
55
|
<br/>
|
@@ -53,7 +63,7 @@ Structured collections of core data types and other patterns. Used to generate m
|
|
53
63
|
* line (linear interpolation between 2 points)
|
54
64
|
* function (dynamically generate elements with a lambda)
|
55
65
|
|
56
|
-
Future?
|
66
|
+
To be added in the Future?
|
57
67
|
* curve (exponential/curved interpolation between points)
|
58
68
|
* permutation (cycle that permutes itself each cycle period)
|
59
69
|
* markov chain
|
@@ -65,7 +75,23 @@ Future?
|
|
65
75
|
<br/>
|
66
76
|
### Sequencers
|
67
77
|
|
68
|
-
Convert patterns into
|
78
|
+
Convert patterns into event Timelines
|
79
|
+
|
80
|
+
The sequencer classes differ in terms of how they determine how much time should occur between one event and the next.
|
81
|
+
So far the options are:
|
82
|
+
* LegatoSequencer - the end of each event is the start of the next event
|
83
|
+
* RhythmSequencer - requires a special rhythm-type patter to determine the inter-event time intervals
|
84
|
+
* StepSequencer - uses a constant amount of time between all events. In other words, works like a drum sequencer/
|
85
|
+
|
86
|
+
|
87
|
+
<br/>
|
88
|
+
### Creating object conveniently
|
89
|
+
|
90
|
+
The Core types as well as Patterns and Note events have "convenience constructors" under the top-level MTK module.
|
91
|
+
|
92
|
+
You can construct objects from almost any arguments by using the methods such as MTK.Pitch() and MTK.Note().
|
93
|
+
These methods do their best to guess what you want from the arguments and even handle out-of-order arguments
|
94
|
+
in most cases. See the unit tests for ideas...
|
69
95
|
|
70
96
|
|
71
97
|
<br/>
|
@@ -74,56 +100,62 @@ Convert patterns into timelines
|
|
74
100
|
Basic Tenants
|
75
101
|
|
76
102
|
* Minimal syntax: less typing means more music!
|
77
|
-
*
|
78
|
-
*
|
79
|
-
* pitch is more important than rhythm
|
103
|
+
* Case-sensitive to allow for more to be expressed in fewer characters.
|
104
|
+
* Pitch and duration are the most important properties
|
80
105
|
|
81
106
|
Because pitch class and duration are such important properties, and we want to minimize typing, we represent these with 1 letter.
|
82
107
|
|
83
|
-
**Diatonic Pitch Class:
|
108
|
+
**Diatonic Pitch Class: C D E F G A B**
|
84
109
|
|
85
|
-
**Chromatic Pitch Class (Sharp/Flat):
|
110
|
+
**Chromatic Pitch Class (Sharp/Flat): C/B# C#/Db D D#/Eb E/Fb E#/F F#/Gb G G#/Ab A A#/Bb B/Cb**
|
86
111
|
|
87
|
-
Double-sharps (
|
112
|
+
Double-sharps (C##) and double-flats (Dbb) are also allowed.
|
88
113
|
|
89
|
-
**Pitch (Pitch Class + Octave Number):
|
114
|
+
**Pitch (Pitch Class + Octave Number): C4 Db5 C-1 G9**
|
90
115
|
|
91
|
-
|
116
|
+
C-1 (that's octave number: negative 1) is the lowest note. G9 is the highest.
|
117
|
+
Note: The MTK syntax expects C-1 as you'd expect, but the corresponding constant in Ruby is C_1 to ensure valid Ruby syntax.
|
92
118
|
|
93
|
-
**Duration: w h q
|
119
|
+
**Duration: w h q e s r x (that's: whole note, half, quarter, eighth, sixteenth, thirty-second, sixty-fourth)**
|
94
120
|
|
95
|
-
|
96
|
-
|
121
|
+
In the MTK syntax, durations can be suffixed with "t" or "." for triplets and dotted-notes
|
122
|
+
(mathematically that multiplies the dureation by 2/3 or 1.5).
|
97
123
|
|
98
|
-
|
124
|
+
Why "r", and "x"? We're trying to keep these one letter.
|
125
|
+
"t" is used for triplets, so the thirty-second note became "r".
|
126
|
+
"s" is used by sixteenth notes, so the sixty-fourth note became "x".
|
127
|
+
Hopefully this abnormal naming won't cause problems since those duration values are fairly uncommon.
|
99
128
|
|
100
|
-
**Intensity: ppp pp p mp mf o ff fff**
|
101
129
|
|
102
|
-
|
130
|
+
**Intensity: ppp pp p mp mf f ff fff**
|
103
131
|
|
104
132
|
|
105
133
|
TODO: keep documenting this...
|
134
|
+
TODO: explanation of intervals
|
106
135
|
|
107
136
|
|
108
137
|
Summary of single-letter assignments:
|
109
138
|
```
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
139
|
+
A -> pitch class A
|
140
|
+
B -> pitch class B
|
141
|
+
b -> flat modifier on pitch class
|
142
|
+
D -> pitch class c
|
143
|
+
D -> pitch class d
|
144
|
+
E -> pitch class e
|
145
|
+
e -> eighth note duration
|
146
|
+
F -> pitch class f
|
147
|
+
f -> forte intensity
|
116
148
|
|
117
149
|
h -> half note duration
|
118
|
-
i ->
|
150
|
+
i -> minor tonic chord (PLANNED, not implemented yet)
|
119
151
|
|
120
|
-
o -> forte intensity
|
121
152
|
p -> piano intensity
|
122
153
|
q -> quarter note duration
|
123
154
|
r -> thirty-second note duration
|
124
155
|
s -> sixteenth note duration
|
125
156
|
t -> triplet modifier on durations
|
126
157
|
|
158
|
+
v -> minor dominant chord (PLANNED, not implemented yet)
|
127
159
|
w -> whole note duration
|
128
160
|
x -> sixty-fourth note duration
|
129
161
|
```
|
data/README.md
CHANGED
@@ -19,20 +19,26 @@ Features
|
|
19
19
|
Getting Started
|
20
20
|
---------------
|
21
21
|
|
22
|
-
MTK works with Ruby 1.9, Ruby 2.0, and JRuby
|
22
|
+
MTK works with Ruby 1.9, Ruby 2.0, and JRuby 1.7+.
|
23
|
+
|
24
|
+
JRuby is recommended for Windows users.
|
23
25
|
|
24
26
|
0. Install
|
25
27
|
|
26
28
|
gem install mtk
|
27
29
|
|
28
|
-
|
30
|
+
Or for JRuby:
|
29
31
|
|
30
|
-
jgem install
|
32
|
+
jgem install jmtk
|
31
33
|
|
32
34
|
0. Learn the command-line interface:
|
33
35
|
|
34
36
|
mtk --help
|
35
37
|
|
38
|
+
Or for JRuby:
|
39
|
+
|
40
|
+
jmtk --help
|
41
|
+
|
36
42
|
0. Learn the MTK syntax: TODO... documentation forthcoming. In the meantime, see the unit tests @ https://github.com/adamjmurray/mtk/blob/master/spec/mtk/lang/parser_spec.rb
|
37
43
|
|
38
44
|
0. Check out examples: https://github.com/adamjmurray/mtk/tree/master/examples
|
data/Rakefile
CHANGED
@@ -1,14 +1,16 @@
|
|
1
1
|
require 'rspec/core/rake_task'
|
2
2
|
require 'rake/clean'
|
3
|
+
require 'erb'
|
3
4
|
|
4
|
-
|
5
|
+
|
6
|
+
GEM_VERSION = '0.4'
|
5
7
|
|
6
8
|
SUPPORTED_RUBIES = %w[ 1.9.3 2.0 jruby-1.7.4 ]
|
7
|
-
|
9
|
+
|
10
|
+
CLEAN.include('html','doc','coverage.data','coverage', '*.gemspec', '*.gem') # clean and clobber do the same thing for now
|
8
11
|
|
9
12
|
task :default => :test
|
10
13
|
|
11
|
-
CLEAN.include('html','doc','coverage.data','coverage', '*.gem') # clean and clobber do the same thing for now
|
12
14
|
|
13
15
|
desc "Run RSpec tests with full output"
|
14
16
|
RSpec::Core::RakeTask.new('test') do |spec|
|
@@ -18,8 +20,44 @@ RSpec::Core::RakeTask.new('test') do |spec|
|
|
18
20
|
spec.pattern = "spec/**/#{ARGV[1]}*"
|
19
21
|
end
|
20
22
|
end
|
23
|
+
task :spec => :test # alias test task as spec task
|
24
|
+
|
25
|
+
namespace :test do
|
26
|
+
desc "Run RSpec tests with summary output and fast failure"
|
27
|
+
RSpec::Core::RakeTask.new(:fast) do |spec|
|
28
|
+
spec.rspec_opts = ["--color", "--fail-fast"]
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "Run RSpec tests and generate a coverage report"
|
32
|
+
if RUBY_PLATFORM == "java"
|
33
|
+
task :cov do |t|
|
34
|
+
fail "#{t} task is not compatible with JRuby. Use Ruby 1.9 instead."
|
35
|
+
end
|
36
|
+
else
|
37
|
+
RSpec::Core::RakeTask.new(:cov) do |spec|
|
38
|
+
spec.rspec_opts = ["--color", "-r", "#{File.dirname __FILE__}/spec/spec_coverage.rb"]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
desc "Profile RSpec tests and report 10 slowest"
|
43
|
+
RSpec::Core::RakeTask.new(:prof) do |spec|
|
44
|
+
spec.rspec_opts = ["--color", "-p"]
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "Run RSpec tests on all supported versions of Ruby: #{SUPPORTED_RUBIES.join ', '}"
|
48
|
+
task :all do
|
49
|
+
fail unless system("rvm #{SUPPORTED_RUBIES.join ','} do bundle exec rake -f #{__FILE__} test:fast")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
21
53
|
|
22
|
-
|
54
|
+
begin
|
55
|
+
require 'yard'
|
56
|
+
YARD::Rake::YardocTask.new(:doc) do |yard|
|
57
|
+
yard.files = ['lib/**/*.rb']
|
58
|
+
end
|
59
|
+
rescue Exception # yard is optional, so don't cause rake to fail if it's missing
|
60
|
+
end
|
23
61
|
|
24
62
|
|
25
63
|
namespace :gem do
|
@@ -62,41 +100,3 @@ namespace :gem do
|
|
62
100
|
`rm bin/jmtk`
|
63
101
|
end
|
64
102
|
end
|
65
|
-
|
66
|
-
|
67
|
-
namespace :test do
|
68
|
-
desc "Run RSpec tests with summary output and fast failure"
|
69
|
-
RSpec::Core::RakeTask.new(:fast) do |spec|
|
70
|
-
spec.rspec_opts = ["--color", "--fail-fast"]
|
71
|
-
end
|
72
|
-
|
73
|
-
desc "Run RSpec tests and generate a coverage report"
|
74
|
-
if RUBY_PLATFORM == "java"
|
75
|
-
task :cov do |t|
|
76
|
-
fail "#{t} task is not compatible with JRuby. Use Ruby 1.9 instead."
|
77
|
-
end
|
78
|
-
else
|
79
|
-
RSpec::Core::RakeTask.new(:cov) do |spec|
|
80
|
-
spec.rspec_opts = ["--color", "-r", "#{File.dirname __FILE__}/spec/spec_coverage.rb"]
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
desc "Profile RSpec tests and report 10 slowest"
|
85
|
-
RSpec::Core::RakeTask.new(:prof) do |spec|
|
86
|
-
spec.rspec_opts = ["--color", "-p"]
|
87
|
-
end
|
88
|
-
|
89
|
-
desc "Run RSpec tests on all supported versions of Ruby: #{SUPPORTED_RUBIES.join ', '}"
|
90
|
-
task :all do
|
91
|
-
fail unless system("rvm #{SUPPORTED_RUBIES.join ','} do bundle exec rake -f #{__FILE__} test:fast")
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
|
96
|
-
begin
|
97
|
-
require 'yard'
|
98
|
-
YARD::Rake::YardocTask.new(:doc) do |yard|
|
99
|
-
yard.files = ['lib/**/*.rb']
|
100
|
-
end
|
101
|
-
rescue Exception # yard is optional, so don't cause rake to fail if it's missing
|
102
|
-
end
|
data/bin/jmtk
CHANGED
@@ -10,38 +10,38 @@ options = {}
|
|
10
10
|
|
11
11
|
option_parser = OptionParser.new do |opts|
|
12
12
|
|
13
|
-
opts.banner = "
|
13
|
+
opts.banner = "\nMTK: Music Tool Kit for Ruby\n\nUsage: #{$0} [options]"
|
14
14
|
opts.separator ''
|
15
15
|
opts.separator 'Options:'
|
16
16
|
|
17
|
-
|
18
|
-
'if
|
19
|
-
'if
|
17
|
+
opts.on('-c FILE', '--convert FILE', 'Convert file containing MTK syntax to MIDI',
|
18
|
+
'if --file is given, write the MIDI to file',
|
19
|
+
'if --output is given, play the MIDI',
|
20
20
|
'otherwise print the MIDI') {|file| options[:convert] = file }
|
21
21
|
|
22
22
|
opts.separator ''
|
23
23
|
|
24
|
-
opts.on('-e [syntax]', '--eval [syntax]', 'Convert the given MTK syntax
|
25
|
-
'
|
26
|
-
'Behaves like --convert
|
24
|
+
opts.on('-e [syntax]', '--eval [syntax]', 'Convert the given MTK syntax to MIDI or',
|
25
|
+
'interactive interpreter when no [syntax]',
|
26
|
+
'Behaves like --convert for --file/--output') {|syntax| options[:eval] = syntax }
|
27
27
|
|
28
28
|
opts.separator ''
|
29
29
|
|
30
|
-
opts.on('-f FILE', '--file FILE', 'Write
|
31
|
-
|
30
|
+
opts.on('-f FILE', '--file FILE', 'Write output of --convert, --eval, --input',
|
31
|
+
'or --watch to a file') {|file| options[:file] = file }
|
32
32
|
|
33
33
|
opts.separator ''
|
34
34
|
|
35
|
-
opts.on('-h', '--help', 'Show
|
35
|
+
opts.on('-h', '--help', 'Show these usage instructions') { puts opts; exit }
|
36
36
|
|
37
37
|
opts.separator ''
|
38
38
|
|
39
39
|
opts.on('-i INPUT', '--input INPUT', 'Set MIDI input for recording',
|
40
|
-
'if no --file
|
40
|
+
'if no --file given, prints recorded MIDI') {|input| options[:input] = input }
|
41
41
|
|
42
42
|
opts.separator ''
|
43
43
|
|
44
|
-
opts.on('-l', '--list', 'List
|
44
|
+
opts.on('-l', '--list', 'List MIDI devices for --input and --output') { options[:list] = true }
|
45
45
|
|
46
46
|
opts.separator ''
|
47
47
|
|
@@ -53,17 +53,19 @@ option_parser = OptionParser.new do |opts|
|
|
53
53
|
|
54
54
|
opts.separator ''
|
55
55
|
|
56
|
-
opts.on('-p FILE', '--play FILE', 'Play or print
|
57
|
-
'if no --output
|
56
|
+
opts.on('-p FILE', '--play FILE', 'Play or print a MIDI file',
|
57
|
+
'if no --output given, print the MIDI') {|file| options[:play] = file }
|
58
58
|
|
59
59
|
opts.separator ''
|
60
60
|
|
61
|
-
|
62
|
-
|
63
|
-
#opts.separator ''
|
61
|
+
opts.on('-t [color]', '--tutorial [color]', 'Interactive tutorial for MTK syntax',
|
62
|
+
'Text color can be set on/off') {|color| options[:tutorial] = true; options[:color] = color }
|
64
63
|
|
65
|
-
opts.
|
66
|
-
|
64
|
+
opts.separator ''
|
65
|
+
|
66
|
+
opts.on('-w FILE', '--watch FILE', 'Watch an MTK syntax file for changes and',
|
67
|
+
'automatically convert to MIDI',
|
68
|
+
'Behaves like --convert for --file/--output') {|file| options[:watch] = file }
|
67
69
|
|
68
70
|
end
|
69
71
|
|
@@ -71,18 +73,16 @@ end
|
|
71
73
|
#######################################################################
|
72
74
|
|
73
75
|
puts option_parser and exit if ARGV.length == 0
|
74
|
-
#p ARGV
|
75
|
-
#p options
|
76
76
|
|
77
77
|
ERROR_INVALID_COMMAND = 1
|
78
78
|
ERROR_FILE_NOT_FOUND = 2
|
79
79
|
ERROR_OUTPUT_NOT_FOUND = 3
|
80
80
|
ERROR_INPUT_NOT_FOUND = 4
|
81
81
|
|
82
|
-
#
|
83
|
-
# the first couple notes. So we play this "empty" Timeline containing a rest to address that issue.
|
82
|
+
# Empty timeline used to prime the realtime output
|
84
83
|
WARMUP = MTK::Events::Timeline.from_h( {0 => MTK.Note(60,-1)} )
|
85
84
|
|
85
|
+
|
86
86
|
#######################################################################
|
87
87
|
|
88
88
|
begin
|
@@ -115,6 +115,9 @@ end
|
|
115
115
|
def output(timelines, print_header='Timeline')
|
116
116
|
timelines = [timelines] unless timelines.is_a? Array
|
117
117
|
if @output
|
118
|
+
# Immediately trying to play output while Ruby is still "warming up" can cause timing issues with
|
119
|
+
# the first couple notes. So we play this "empty" Timeline containing a rest to address that issue.
|
120
|
+
# TODO? move this into the output class and do it automatically when playing for the first time? (warmup code is also in output_selector))
|
118
121
|
@output.play WARMUP
|
119
122
|
@output.play timelines.first # TODO: support multiple timelines
|
120
123
|
elsif @file
|
@@ -151,6 +154,33 @@ def watch_file_updated?
|
|
151
154
|
end
|
152
155
|
|
153
156
|
|
157
|
+
def set_tutorial_color(color_option)
|
158
|
+
if color_option
|
159
|
+
case color_option.strip.downcase
|
160
|
+
when /^(on|yes|true|y|t|color)$/ then $tutorial_color = true
|
161
|
+
when /^(off|no|false|n|f)$/ then $tutorial_color = false
|
162
|
+
else
|
163
|
+
STDERR.puts "Invalid tutorial color setting '#{color}'. Try 'on' or 'off'."
|
164
|
+
exit ERROR_INVALID_COMMAND
|
165
|
+
end
|
166
|
+
else
|
167
|
+
require 'rbconfig'
|
168
|
+
os = RbConfig::CONFIG['host_os'].downcase
|
169
|
+
if os =~ /win/ and os !~ /darwin/
|
170
|
+
puts
|
171
|
+
puts "Windows command line text color is off by default."
|
172
|
+
puts "If you want color, use ANSI terminal software like Cygwin or Ansicon and "
|
173
|
+
puts "run #{$0} with the color option \"--tutorial on\""
|
174
|
+
$tutorial_color = false
|
175
|
+
else
|
176
|
+
$tutorial_color = true
|
177
|
+
end
|
178
|
+
end
|
179
|
+
puts "Tutorial color is #{if $tutorial_color then 'enabled' else 'disabled' end}."
|
180
|
+
puts
|
181
|
+
end
|
182
|
+
|
183
|
+
|
154
184
|
#######################################################################
|
155
185
|
|
156
186
|
if options[:list]
|
@@ -162,18 +192,21 @@ if options[:list]
|
|
162
192
|
puts
|
163
193
|
puts (['OUTPUTS:']+output_names).join("\n * ")
|
164
194
|
puts
|
165
|
-
puts 'When specifying --input INPUT or --output OUTPUT, the first substring match
|
166
|
-
puts
|
195
|
+
puts 'When specifying --input INPUT or --output OUTPUT, the first substring match'
|
196
|
+
puts '(case-insensitive) will be used. For example: "--output iac" will use'
|
197
|
+
puts '"Apple Inc. IAC Driver" if it\'s the first OUTPUT containing "IAC".'
|
167
198
|
puts
|
168
199
|
exit
|
169
200
|
end
|
170
201
|
|
202
|
+
|
171
203
|
@monitor = true if options[:monitor]
|
172
204
|
|
205
|
+
|
173
206
|
if options[:input]
|
174
207
|
setup_io
|
175
208
|
input_name = options[:input]
|
176
|
-
@input = MTK::IO::MIDIInput.find_by_name /#{input_name}/
|
209
|
+
@input = MTK::IO::MIDIInput.find_by_name /#{input_name}/i
|
177
210
|
if @input
|
178
211
|
puts "Using input '#{@input.name}'"
|
179
212
|
else
|
@@ -182,10 +215,11 @@ if options[:input]
|
|
182
215
|
end
|
183
216
|
end
|
184
217
|
|
218
|
+
|
185
219
|
if options[:output]
|
186
220
|
setup_io
|
187
221
|
output_name = options[:output]
|
188
|
-
@output = MTK::IO::MIDIOutput.find_by_name /#{output_name}/
|
222
|
+
@output = MTK::IO::MIDIOutput.find_by_name /#{output_name}/i
|
189
223
|
if @output
|
190
224
|
puts "Using output '#{@output.name}'"
|
191
225
|
else
|
@@ -194,8 +228,9 @@ if options[:output]
|
|
194
228
|
end
|
195
229
|
end
|
196
230
|
|
197
|
-
|
198
|
-
@file = file
|
231
|
+
|
232
|
+
@file = options[:file]
|
233
|
+
|
199
234
|
|
200
235
|
if options[:play]
|
201
236
|
filename = options[:play]
|
@@ -204,6 +239,7 @@ if options[:play]
|
|
204
239
|
output(timelines, "Timeline for #{filename}")
|
205
240
|
end
|
206
241
|
|
242
|
+
|
207
243
|
if options.has_key? :eval
|
208
244
|
mtk_syntax = options[:eval]
|
209
245
|
if mtk_syntax.nil?
|
@@ -221,12 +257,14 @@ if options.has_key? :eval
|
|
221
257
|
end
|
222
258
|
end
|
223
259
|
|
260
|
+
|
224
261
|
if options[:convert]
|
225
262
|
mtk_syntax_file = options[:convert]
|
226
263
|
mtk_syntax = IO.read(mtk_syntax_file)
|
227
264
|
convert(mtk_syntax)
|
228
265
|
end
|
229
266
|
|
267
|
+
|
230
268
|
if options[:watch]
|
231
269
|
@watch_file = options[:watch]
|
232
270
|
puts "Watching #{@watch_file}. Press Ctrl+C to exit."
|
@@ -243,8 +281,13 @@ if options[:watch]
|
|
243
281
|
end
|
244
282
|
end
|
245
283
|
|
246
|
-
|
247
|
-
|
248
|
-
|
284
|
+
|
285
|
+
if options.has_key? :tutorial
|
286
|
+
set_tutorial_color(options[:color])
|
287
|
+
require 'mtk/lang/tutorial'
|
288
|
+
tutorial = MTK::Lang::Tutorial.new
|
289
|
+
tutorial.run(@output)
|
290
|
+
end
|
291
|
+
|
249
292
|
|
250
293
|
record if @input
|