mtk 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +9 -0
- data/INTRO.md +73 -0
- data/LICENSE.txt +27 -0
- data/README.md +93 -18
- data/Rakefile +13 -1
- data/examples/crescendo.rb +20 -0
- data/examples/dynamic_pattern.rb +39 -0
- data/examples/play_midi.rb +19 -0
- data/examples/print_midi.rb +13 -0
- data/examples/random_tone_row.rb +18 -0
- data/examples/tone_row_melody.rb +21 -0
- data/lib/mtk/_constants/durations.rb +80 -0
- data/lib/mtk/_constants/intensities.rb +81 -0
- data/lib/mtk/{constants → _constants}/intervals.rb +10 -1
- data/lib/mtk/_constants/pitch_classes.rb +35 -0
- data/lib/mtk/_constants/pitches.rb +49 -0
- data/lib/mtk/{numeric_extensions.rb → _numeric_extensions.rb} +0 -0
- data/lib/mtk/event.rb +14 -5
- data/lib/mtk/helper/collection.rb +114 -0
- data/lib/mtk/helper/event_builder.rb +85 -0
- data/lib/mtk/{constants → helper}/pseudo_constants.rb +7 -6
- data/lib/mtk/lang/grammar.rb +17 -0
- data/lib/mtk/lang/mtk_grammar.citrus +60 -0
- data/lib/mtk/midi/file.rb +10 -15
- data/lib/mtk/midi/jsound_input.rb +68 -0
- data/lib/mtk/midi/jsound_output.rb +80 -0
- data/lib/mtk/note.rb +22 -3
- data/lib/mtk/pattern/abstract_pattern.rb +132 -0
- data/lib/mtk/pattern/choice.rb +25 -9
- data/lib/mtk/pattern/cycle.rb +51 -0
- data/lib/mtk/pattern/enumerator.rb +26 -0
- data/lib/mtk/pattern/function.rb +46 -0
- data/lib/mtk/pattern/lines.rb +60 -0
- data/lib/mtk/pattern/palindrome.rb +42 -0
- data/lib/mtk/pattern/sequence.rb +15 -50
- data/lib/mtk/pitch.rb +45 -6
- data/lib/mtk/pitch_class.rb +36 -35
- data/lib/mtk/pitch_class_set.rb +46 -14
- data/lib/mtk/pitch_set.rb +20 -31
- data/lib/mtk/sequencer/abstract_sequencer.rb +85 -0
- data/lib/mtk/sequencer/rhythmic_sequencer.rb +29 -0
- data/lib/mtk/sequencer/step_sequencer.rb +26 -0
- data/lib/mtk/timeline.rb +75 -22
- data/lib/mtk/transform/invertible.rb +15 -0
- data/lib/mtk/{util → transform}/mappable.rb +6 -2
- data/lib/mtk/transform/set_theory_operations.rb +34 -0
- data/lib/mtk/transform/transposable.rb +14 -0
- data/lib/mtk.rb +56 -22
- data/spec/mtk/_constants/durations_spec.rb +118 -0
- data/spec/mtk/{constants/dynamics_spec.rb → _constants/intensities_spec.rb} +48 -17
- data/spec/mtk/{constants → _constants}/intervals_spec.rb +21 -0
- data/spec/mtk/_constants/pitch_classes_spec.rb +58 -0
- data/spec/mtk/_constants/pitches_spec.rb +52 -0
- data/spec/mtk/{numeric_extensions_spec.rb → _numeric_extensions_spec.rb} +0 -0
- data/spec/mtk/event_spec.rb +19 -0
- data/spec/mtk/helper/collection_spec.rb +291 -0
- data/spec/mtk/helper/event_builder_spec.rb +92 -0
- data/spec/mtk/helper/pseudo_constants_spec.rb +20 -0
- data/spec/mtk/lang/grammar_spec.rb +100 -0
- data/spec/mtk/midi/file_spec.rb +41 -6
- data/spec/mtk/note_spec.rb +53 -3
- data/spec/mtk/pattern/abstract_pattern_spec.rb +45 -0
- data/spec/mtk/pattern/choice_spec.rb +89 -3
- data/spec/mtk/pattern/cycle_spec.rb +133 -0
- data/spec/mtk/pattern/function_spec.rb +133 -0
- data/spec/mtk/pattern/lines_spec.rb +93 -0
- data/spec/mtk/pattern/note_cycle_spec.rb.bak +116 -0
- data/spec/mtk/pattern/palindrome_spec.rb +124 -0
- data/spec/mtk/pattern/pitch_cycle_spec.rb.bak +47 -0
- data/spec/mtk/pattern/pitch_sequence_spec.rb.bak +37 -0
- data/spec/mtk/pattern/sequence_spec.rb +128 -31
- data/spec/mtk/pitch_class_set_spec.rb +240 -7
- data/spec/mtk/pitch_class_spec.rb +84 -18
- data/spec/mtk/pitch_set_spec.rb +45 -10
- data/spec/mtk/pitch_spec.rb +59 -0
- data/spec/mtk/sequencer/abstract_sequencer_spec.rb +159 -0
- data/spec/mtk/sequencer/rhythmic_sequencer_spec.rb +49 -0
- data/spec/mtk/sequencer/step_sequencer_spec.rb +71 -0
- data/spec/mtk/timeline_spec.rb +118 -15
- data/spec/spec_helper.rb +4 -3
- metadata +59 -22
- data/lib/mtk/chord.rb +0 -47
- data/lib/mtk/constants/dynamics.rb +0 -56
- data/lib/mtk/constants/pitch_classes.rb +0 -18
- data/lib/mtk/constants/pitches.rb +0 -24
- data/lib/mtk/pattern/note_sequence.rb +0 -60
- data/lib/mtk/pattern/pitch_sequence.rb +0 -22
- data/lib/mtk/patterns.rb +0 -4
- data/spec/mtk/chord_spec.rb +0 -74
- data/spec/mtk/constants/pitch_classes_spec.rb +0 -35
- data/spec/mtk/constants/pitches_spec.rb +0 -23
- data/spec/mtk/pattern/note_sequence_spec.rb +0 -121
- data/spec/mtk/pattern/pitch_sequence_spec.rb +0 -47
data/.yardopts
ADDED
data/INTRO.md
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
(Work in progress)
|
2
|
+
|
3
|
+
Core Concepts
|
4
|
+
-------------
|
5
|
+
|
6
|
+
### Core data types
|
7
|
+
|
8
|
+
These model the basic properties of musical events:
|
9
|
+
* pitch_class (MTK class)
|
10
|
+
* pitch (MTK class)
|
11
|
+
* intensity (Numeric)
|
12
|
+
* duration (Numeric)
|
13
|
+
|
14
|
+
Like Numeric types, PitchClass and Pitch are immutable. This helps avoid confusing bugs.
|
15
|
+
Mostly you don't need to worry about this. Just remember when you call methods that change the value, like #invert,
|
16
|
+
it does not change the value in-place. For example:
|
17
|
+
|
18
|
+
p = PitchClass[G]
|
19
|
+
p = p.invert(E) # because p.invert(E) does not change p
|
20
|
+
|
21
|
+
For efficiency and convenience, intensity and duration are modeled using Ruby's Numeric types (Fixnum, Float, etc).
|
22
|
+
|
23
|
+
Intensity is intended to range from 0.0 (minimum intensity) to 1.0 (maximum intensity).
|
24
|
+
|
25
|
+
A Duration of 1 is one beat (usually a quarter note, depending on the meter). By convention, negative durations
|
26
|
+
indicate a rest that lasts for the absolute value duration.
|
27
|
+
|
28
|
+
|
29
|
+
<br/>
|
30
|
+
### Events
|
31
|
+
|
32
|
+
Events group together the core data types together to represent a musical event, like a single Note.
|
33
|
+
Events can be converted to and from MIDI.
|
34
|
+
|
35
|
+
* event
|
36
|
+
* note
|
37
|
+
|
38
|
+
|
39
|
+
<br/>
|
40
|
+
### Core collections
|
41
|
+
|
42
|
+
A collection of timed events, such as a melody, a riff, or one track of an entire song.
|
43
|
+
|
44
|
+
* timeline
|
45
|
+
|
46
|
+
|
47
|
+
<br/>
|
48
|
+
### Patterns
|
49
|
+
|
50
|
+
Structured collections of core data types and other patterns. Used to generate musical material.
|
51
|
+
|
52
|
+
* cycle
|
53
|
+
* palindrome
|
54
|
+
* choice (randomly select from weghted distribution)
|
55
|
+
* line (linear interpolation between 2 points)
|
56
|
+
* function (dynamically generate elements with a lambda)
|
57
|
+
|
58
|
+
Future?
|
59
|
+
* curve (exponential/curved interpolation between points)
|
60
|
+
* permutation (cycle that permutes itself each cycle period)
|
61
|
+
* markov chain
|
62
|
+
* graph
|
63
|
+
* petri net (special type of graph??)
|
64
|
+
* custom (just implement enumerator pattern?)
|
65
|
+
|
66
|
+
|
67
|
+
<br/>
|
68
|
+
### Sequencers
|
69
|
+
|
70
|
+
Convert patterns into timelines
|
71
|
+
|
72
|
+
|
73
|
+
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright (c) 2011, Adam Murray (adam@compusition.com).
|
2
|
+
|
3
|
+
All rights reserved.
|
4
|
+
|
5
|
+
Redistribution and use of the "MTK" Ruby library, in source and binary forms,
|
6
|
+
with or without modification, are permitted provided that the following
|
7
|
+
conditions are met:
|
8
|
+
|
9
|
+
1. Redistributions of source code must retain the above copyright
|
10
|
+
notice, this list of conditions and the following disclaimer.
|
11
|
+
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright
|
13
|
+
notice, this list of conditions and the following disclaimer in
|
14
|
+
the documentation and/or other materials provided with the
|
15
|
+
distribution.
|
16
|
+
|
17
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
18
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
19
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
20
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
21
|
+
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
22
|
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
23
|
+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
24
|
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
25
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
26
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
27
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
CHANGED
@@ -1,52 +1,127 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
MTK
|
2
|
+
===
|
3
3
|
|
4
|
-
|
4
|
+
Music ToolKit for Ruby
|
5
|
+
----------------------
|
5
6
|
|
7
|
+
Classes for modeling music with a focus on simplicity. Support for reading/writing MIDI files and realtime MIDI.
|
6
8
|
|
7
9
|
|
8
|
-
|
10
|
+
|
11
|
+
Getting Started
|
12
|
+
---------------
|
13
|
+
|
14
|
+
gem install mtk
|
15
|
+
|
16
|
+
or download the source from here and add mtk/lib to your $LOAD_PATH. Then...
|
17
|
+
|
18
|
+
require 'mtk'
|
19
|
+
|
20
|
+
Some examples are available in the examples folder (more coming soon).
|
21
|
+
The specs provide a lot of details of usage...
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
Goals
|
26
|
+
-----
|
9
27
|
|
10
28
|
* Build musical generators to assist with composing music
|
11
29
|
* Re-implement Cosy (http://compusition.com/web/software/cosy) using these models as the "backend"
|
12
30
|
|
13
31
|
|
14
32
|
|
15
|
-
|
33
|
+
Status
|
34
|
+
------
|
16
35
|
|
17
|
-
|
36
|
+
Alpha phase, API subject to change. Feedback welcome!
|
18
37
|
|
19
38
|
|
20
39
|
|
21
|
-
|
40
|
+
Requirements
|
41
|
+
------------
|
42
|
+
|
22
43
|
### Ruby Version
|
23
44
|
|
24
|
-
Ruby 1.8 or 1.
|
45
|
+
Ruby 1.8+ or JRuby 1.5+
|
46
|
+
|
47
|
+
|
48
|
+
### Dependencies
|
49
|
+
|
50
|
+
MTK's core features should not depend on anything outside of the Ruby standard library.
|
25
51
|
|
26
|
-
|
52
|
+
MTK's optional features typically require gems. Currently the following gems are required:
|
27
53
|
|
28
|
-
*
|
54
|
+
* midilib: required by MTK::MIDI::File for file I/O
|
55
|
+
|
56
|
+
* jsound and gamelan: required by MTK::MIDI::JSoundInput/Output (also requires JRuby)
|
57
|
+
|
58
|
+
Development requires all the gems for optional features, plus the following development tools:
|
59
|
+
|
60
|
+
* rake
|
29
61
|
* rspec (tests)
|
30
62
|
* yard (docs)
|
31
63
|
* yard-rspec (docs)
|
32
64
|
* rdiscount (docs)
|
33
|
-
* midilib (MIDI file I/O -- not strictly required by core lib, but currently needed for tests)
|
34
65
|
|
66
|
+
[rvm](https://rvm.beginrescueend.com/) is recommended for cross version testing (see Development Notes below)
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
Documentation
|
71
|
+
-------------
|
72
|
+
|
73
|
+
Gem: http://rdoc.info/gems/mtk/0.0.2/frames
|
74
|
+
|
75
|
+
Latest for source: http://rubydoc.info/github/adamjmurray/mtk/master/frames
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
Development Notes
|
80
|
+
-----------------
|
81
|
+
|
82
|
+
### Run Tests ###
|
83
|
+
|
84
|
+
Test with current version of Ruby:
|
85
|
+
|
86
|
+
rake spec
|
87
|
+
|
88
|
+
Test with all supported versions of Ruby (requires [rvm](https://rvm.beginrescueend.com/), MRI 1.8.7, YARV 1.9.2, JRuby 1.5.6, and JRuby 1.6.2):
|
89
|
+
|
90
|
+
rake spec:xversion
|
91
|
+
|
92
|
+
The spec:xversion test must pass for a pull request to be accepted or for a release of the mtk gem.
|
93
|
+
|
94
|
+
|
95
|
+
### Generate Docs ###
|
35
96
|
|
97
|
+
yard
|
98
|
+
open doc/frames.html
|
36
99
|
|
37
|
-
|
100
|
+
or, to automatically refresh the documentation as you work:
|
38
101
|
|
39
|
-
|
102
|
+
yard server -r
|
103
|
+
open http://localhost:8808
|
40
104
|
|
41
|
-
then open doc/frames.html
|
42
105
|
|
106
|
+
### Project Roadmap ###
|
43
107
|
|
108
|
+
https://www.pivotaltracker.com/projects/295419
|
44
109
|
|
45
|
-
## Tests
|
46
110
|
|
47
|
-
rake spec
|
48
111
|
|
49
|
-
|
112
|
+
Changelog
|
113
|
+
---------
|
50
114
|
|
51
|
-
|
115
|
+
* July 8, 2011: version 0.0.2
|
116
|
+
- Added a Sequencer module to build Timelines out of Patterns
|
117
|
+
- Overhauled Pattern module: removed type-specific patterns, and added the Palindrome, Lines, and Function patterns
|
118
|
+
- Patterns can now be nested (they can contain other Patterns)
|
119
|
+
- Patterns can now be typed, to distinguish Numeric Patterns as :pitch (i.e. intervals), :intensity, :duration, or :rhythm patterns
|
120
|
+
- Removed auto-sorting behavior from PitchClassSet to support 12-tone rows and atonal composition techniques
|
121
|
+
- Added #quantize and #shift features to Timeline
|
122
|
+
- Got rid of Chord class, model Chords with PitchSets or Arrays of Notes instead
|
123
|
+
- Added support for realtime MIDI I/O with JSound (JRuby only)
|
124
|
+
- various cleanup and reorganization
|
52
125
|
|
126
|
+
* June 8, 2011: version 0.0.1
|
127
|
+
- First gem release.
|
data/Rakefile
CHANGED
@@ -7,17 +7,29 @@ task :default => :spec
|
|
7
7
|
CLEAN.include('html','doc') # clean and clobber do the same thing for now
|
8
8
|
|
9
9
|
desc "Run RSpec tests with full output"
|
10
|
-
RSpec::Core::RakeTask.new do |spec|
|
10
|
+
RSpec::Core::RakeTask.new do |spec|
|
11
11
|
spec.rspec_opts = ["--color", "--format", "nested"]
|
12
|
+
if ARGV[1]
|
13
|
+
# only run specs with filenames starting with the command line argument
|
14
|
+
spec.pattern = "spec/**/#{ARGV[1]}*"
|
15
|
+
end
|
12
16
|
end
|
13
17
|
|
14
18
|
namespace :spec do
|
19
|
+
|
15
20
|
desc "Run RSpecs tests with summary output and fast failure"
|
16
21
|
RSpec::Core::RakeTask.new(:fast) do |spec|
|
17
22
|
spec.rspec_opts = ["--color", "--fail-fast"]
|
18
23
|
end
|
24
|
+
|
25
|
+
desc "Run RSpecs tests on mutiple versions of Ruby: 1.8.7, 1.9.2, JRuby 1.5.6, and JRuby 1.6.2"
|
26
|
+
task :xversion do
|
27
|
+
fail unless system("rvm 1.8.7,1.9.2,jruby-1.5.6,jruby-1.6.2 rake -f #{__FILE__} spec:fast")
|
28
|
+
end
|
29
|
+
|
19
30
|
end
|
20
31
|
|
32
|
+
|
21
33
|
YARD::Rake::YardocTask.new do |yard|
|
22
34
|
yard.files = ['lib/**/*.rb', 'spec/**/*.rb']
|
23
35
|
yard.options = []
|
@@ -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/midi/file'
|
7
|
+
include MTK
|
8
|
+
include Pitches
|
9
|
+
include Intensities
|
10
|
+
|
11
|
+
file = ARGV[0] || 'MTK-crescendo.mid'
|
12
|
+
|
13
|
+
scale = Pattern::PitchSequence C4,D4,E4,F4,G4,A4,B4,C5
|
14
|
+
crescendo = Pattern::IntensityLines pp, [fff, scale.length-1] # step from pp to fff over the length of the scale
|
15
|
+
|
16
|
+
sequencer = Sequencer::StepSequencer.new [scale, crescendo]
|
17
|
+
timeline = sequencer.to_timeline
|
18
|
+
|
19
|
+
MIDI_File(file).write timeline
|
20
|
+
|
@@ -0,0 +1,39 @@
|
|
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/midi/file'
|
7
|
+
include MTK
|
8
|
+
include Pitches
|
9
|
+
include Intensities
|
10
|
+
include 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 = Pattern::Function.new interval_generator, :type => :pitch, :max_elements => 24
|
30
|
+
|
31
|
+
# we'll also use a weighted choice to generate the intensities
|
32
|
+
intensities = Pattern::Choice.new [mp, mf, f, ff, fff], :type => :intensity, :weights => [1,2,3,2,1]
|
33
|
+
|
34
|
+
|
35
|
+
sequencer = Sequencer::StepSequencer.new [pitches, intensities], :step_size => 0.5
|
36
|
+
timeline = sequencer.to_timeline
|
37
|
+
|
38
|
+
MIDI_File(file).write timeline
|
39
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Play a given MIDI file with the given MIDI output
|
2
|
+
#
|
3
|
+
# NOTE: this example only runs under JRuby with the jsound and gamelan gems installed
|
4
|
+
|
5
|
+
file, output = ARGV[0], ARGV[1]
|
6
|
+
unless file and output
|
7
|
+
puts "Usage: ruby #$0 midi_file output_device"
|
8
|
+
exit 1
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'mtk'
|
12
|
+
require 'mtk/midi/file'
|
13
|
+
require 'mtk/midi/jsound_output'
|
14
|
+
include MTK
|
15
|
+
|
16
|
+
timeline = MIDI_File(file).to_timelines.first # for now this example just plays the first track (JSoundOutput needs to be enhanced to support multiple timelines)
|
17
|
+
player = MTK::MIDI::JSoundOutput.new output
|
18
|
+
|
19
|
+
player.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/midi/file'
|
7
|
+
include MTK
|
8
|
+
|
9
|
+
file = ARGV[0] || 'MTK-random_tone_row.mid'
|
10
|
+
|
11
|
+
row = PitchClassSet.random_row
|
12
|
+
sequence = Pattern::PitchSequence *row.to_a
|
13
|
+
|
14
|
+
sequencer = Sequencer::StepSequencer.new [sequence]
|
15
|
+
timeline = sequencer.to_timeline
|
16
|
+
|
17
|
+
MIDI_File(file).write timeline
|
18
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Generate a MIDI file using a tone row, repeated 3 times using random rhythms
|
2
|
+
#
|
3
|
+
# NOTE: this blindly overwrites any existing MTK-tone_row_melody.mid file, unless an argument is provided
|
4
|
+
|
5
|
+
require 'mtk'
|
6
|
+
require 'mtk/midi/file'
|
7
|
+
include MTK
|
8
|
+
include PitchClasses
|
9
|
+
include Durations
|
10
|
+
|
11
|
+
file = ARGV[0] || 'MTK-tone_row_melody.mid'
|
12
|
+
|
13
|
+
row = PitchClassSet(Db, G, Ab, F, Eb, E, D, C, B, Gb, A, Bb)
|
14
|
+
pitch_pattern = Pattern.PitchCycle *row
|
15
|
+
rhythm_pattern = Pattern.RhythmCycle(Pattern.Choice s, e, e+s, q) # choose between sixteenth, eighth, dotted eighth, and quarter
|
16
|
+
|
17
|
+
sequencer = Sequencer::RhythmicSequencer.new [pitch_pattern, rhythm_pattern], :max_steps => 36
|
18
|
+
timeline = sequencer.to_timeline
|
19
|
+
|
20
|
+
MIDI_File(file).write timeline
|
21
|
+
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'rational'
|
2
|
+
|
3
|
+
module MTK
|
4
|
+
|
5
|
+
# Defines duration constants using abbreviations for standard rhythm values ('w' for whole note, 'h' for half note, etc).
|
6
|
+
#
|
7
|
+
# These can be thought of like constants, but in order to distinguish 'e' (eighth note) from the {PitchClass} 'E'
|
8
|
+
# it was necessary to use lower-case names and therefore define them as "pseudo constant" methods.
|
9
|
+
# The methods are available either through the module (MTK::Durations::e) or via mixin (include MTK::Durations; e)
|
10
|
+
#
|
11
|
+
# These values assume the quarter note is one beat (1.0), so they work best with 4/4 and other */4 time signatures.
|
12
|
+
#
|
13
|
+
# @note Including this module defines a bunch of single-character variables, which may shadow existing variable names.
|
14
|
+
# Just be mindful of what is defined in this module when including it.
|
15
|
+
#
|
16
|
+
# @see Note
|
17
|
+
module Durations
|
18
|
+
extend Helper::PseudoConstants
|
19
|
+
|
20
|
+
# NOTE: the yard doc macros here only fill in [$2] with the actual value when generating docs under Ruby 1.9+
|
21
|
+
|
22
|
+
# whole note
|
23
|
+
# @macro [attach] durations.define_constant
|
24
|
+
# @attribute [r]
|
25
|
+
# @return [$2] number of beats for $1
|
26
|
+
define_constant 'w', 4
|
27
|
+
|
28
|
+
# half note
|
29
|
+
define_constant 'h', 2
|
30
|
+
|
31
|
+
# quarter note
|
32
|
+
define_constant 'q', 1
|
33
|
+
|
34
|
+
# eight note
|
35
|
+
define_constant 'e', Rational(1,2)
|
36
|
+
|
37
|
+
# sixteenth note
|
38
|
+
define_constant 's', Rational(1,4)
|
39
|
+
|
40
|
+
# thirty-second note
|
41
|
+
define_constant 'r', Rational(1,8)
|
42
|
+
|
43
|
+
# sixty-fourth note
|
44
|
+
define_constant 'x', Rational(1,16)
|
45
|
+
|
46
|
+
# The values of all "psuedo constants" defined in this module
|
47
|
+
DURATIONS = [w, h, q, e, s, r, x].freeze
|
48
|
+
|
49
|
+
# The names of all "psuedo constants" defined in this module
|
50
|
+
DURATION_NAMES = %w[w h q e s r x].freeze
|
51
|
+
|
52
|
+
# Lookup the value of an duration constant by name.
|
53
|
+
# This method supports appending any combination of '.' and 't' for more fine-grained values.
|
54
|
+
# each '.' multiplies by 3/2, and each 't' multiplies by 2/3.
|
55
|
+
# @example lookup value of 'e.' (eight note), which is 0.75 (0.5 * 1.5)
|
56
|
+
# MTK::Durations['e.']
|
57
|
+
def self.[](name)
|
58
|
+
begin
|
59
|
+
modifier = nil
|
60
|
+
if name =~ /(\w)((.|t)*)/
|
61
|
+
name = $1
|
62
|
+
modifier = $2
|
63
|
+
end
|
64
|
+
|
65
|
+
value = send name
|
66
|
+
modifier.each_char do |mod|
|
67
|
+
case mod
|
68
|
+
when '.' then value *= Rational(3,2)
|
69
|
+
when 't' then value *= Rational(2,3)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
value
|
73
|
+
|
74
|
+
rescue
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module MTK
|
2
|
+
|
3
|
+
# Defines intensity constants using standard dynamic symbols.
|
4
|
+
#
|
5
|
+
# These can be thought of like constants, but in order to distinguish 'f' (forte) from the {PitchClass} 'F'
|
6
|
+
# it was necessary to use lower-case names and therefore define them as "pseudo constant" methods.
|
7
|
+
# The methods are available either through the module (MTK::Intensities::f) or via mixin (include MTK::Intensities; f)
|
8
|
+
#
|
9
|
+
# These values are intensities in the range 0.125 - 1.0 (in increments of 1/8), so they can be easily scaled (unlike MIDI velocities).
|
10
|
+
#
|
11
|
+
# It is also possible to retrieve values in increments of 1/24 by using the '+' and '-' suffix when looking
|
12
|
+
# up values via the {.[]} method.
|
13
|
+
#
|
14
|
+
# @note Including this module shadows Ruby's built-in p() method.
|
15
|
+
# If you include this module, you can access the built-in p() method via Kernel.p()
|
16
|
+
#
|
17
|
+
# @see Note
|
18
|
+
module Intensities
|
19
|
+
extend Helper::PseudoConstants
|
20
|
+
|
21
|
+
# NOTE: the yard doc macros here only fill in [$2] with the actual value when generating docs under Ruby 1.9+
|
22
|
+
|
23
|
+
# pianississimo
|
24
|
+
# @macro [attach] intensities.define_constant
|
25
|
+
# @attribute [r]
|
26
|
+
# @return [$2] intensity value for $1
|
27
|
+
define_constant 'ppp', 0.125
|
28
|
+
|
29
|
+
# pianissimo
|
30
|
+
define_constant 'pp', 0.25
|
31
|
+
|
32
|
+
# piano
|
33
|
+
# @note Including this module shadows Ruby's built-in p() method.
|
34
|
+
# If you include this module, you can access the built-in p() method via Kernel.p()
|
35
|
+
define_constant 'p', 0.375
|
36
|
+
|
37
|
+
# mezzo-piano
|
38
|
+
define_constant 'mp', 0.5
|
39
|
+
|
40
|
+
# mezzo-forte
|
41
|
+
define_constant 'mf', 0.625
|
42
|
+
|
43
|
+
# forte
|
44
|
+
define_constant 'f', 0.75
|
45
|
+
|
46
|
+
# fortissimo
|
47
|
+
define_constant 'ff', 0.875
|
48
|
+
|
49
|
+
# fortississimo
|
50
|
+
define_constant 'fff', 1.0
|
51
|
+
|
52
|
+
# The values of all "psuedo constants" defined in this module
|
53
|
+
INTENSITIES = [ppp, pp, p, mp, mf, f, ff, fff].freeze
|
54
|
+
|
55
|
+
# The names of all "psuedo constants" defined in this module
|
56
|
+
INTENSITY_NAMES = %w[ppp pp p mp mf f ff fff].freeze
|
57
|
+
|
58
|
+
# Lookup the value of an intensity constant by name.
|
59
|
+
# This method supports appending '+' or '-' for more fine-grained values.
|
60
|
+
# '+' and '-' add and subtract 1/24, respectively (enforcing the upper bound of 1.0 for 'fff+').
|
61
|
+
# @example lookup value of 'mp+', which is 0.5416666666666666 (0.5 + 1/24.0)
|
62
|
+
# MTK::Intensities['mp+']
|
63
|
+
def self.[](name)
|
64
|
+
return 1.0 if name == "fff+" # special case because "fff" is already the maximum
|
65
|
+
|
66
|
+
modifier = nil
|
67
|
+
if name =~ /(\w+)([+-])/
|
68
|
+
name = $1
|
69
|
+
modifier = $2
|
70
|
+
end
|
71
|
+
|
72
|
+
value = send name
|
73
|
+
value += 1.0/24 if modifier == '+'
|
74
|
+
value -= 1.0/24 if modifier == '-'
|
75
|
+
value
|
76
|
+
rescue
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -12,7 +12,7 @@ module MTK
|
|
12
12
|
# it was necessary to use lower-case names for some of the values and therefore define them as "pseudo constant" methods.
|
13
13
|
# The methods are available either through the module (MTK::Intervals::m2) or via mixin (include MTK::Intervals; m2)
|
14
14
|
module Intervals
|
15
|
-
extend PseudoConstants
|
15
|
+
extend Helper::PseudoConstants
|
16
16
|
|
17
17
|
# NOTE: the yard doc macros here only fill in [$2] with the actual value when generating docs under Ruby 1.9+
|
18
18
|
|
@@ -61,6 +61,15 @@ module MTK
|
|
61
61
|
# pefect octave
|
62
62
|
define_constant 'P8', 12
|
63
63
|
|
64
|
+
# The values of all "psuedo constants" defined in this module
|
65
|
+
INTERVALS = [P1, m2, M2, m3, M3, P4, TT, P5, m6, M6, m7, M7, P8].freeze
|
66
|
+
|
67
|
+
# The names of all "psuedo constants" defined in this module
|
68
|
+
INTERVAL_NAMES = %w[P1 m2 M2 m3 M3 P4 TT P5 m6 M6 m7 M7 P8].freeze
|
69
|
+
|
70
|
+
# Lookup the value of an interval constant by name.
|
71
|
+
# @example lookup value of 'M3', which is 4
|
72
|
+
# MTK::Intervals['M3']
|
64
73
|
def self.[](name)
|
65
74
|
send name
|
66
75
|
rescue
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module MTK
|
2
|
+
|
3
|
+
# Defines a constant for each {PitchClass} in the Western chromatic scale.
|
4
|
+
|
5
|
+
module PitchClasses
|
6
|
+
|
7
|
+
# The values of all "psuedo constants" defined in this module
|
8
|
+
PITCH_CLASSES = []
|
9
|
+
|
10
|
+
# The names of all "psuedo constants" defined in this module
|
11
|
+
PITCH_CLASS_NAMES = PitchClass::NAMES
|
12
|
+
|
13
|
+
for name in PITCH_CLASS_NAMES
|
14
|
+
pc = PitchClass[name]
|
15
|
+
PITCH_CLASSES << pc
|
16
|
+
const_set name, pc
|
17
|
+
end
|
18
|
+
|
19
|
+
PITCH_CLASSES.freeze
|
20
|
+
|
21
|
+
# Lookup the value of an pitch class constant by name.
|
22
|
+
# @example lookup value of 'C'
|
23
|
+
# MTK::PitchClasses['C']
|
24
|
+
# @see PitchClass.[]
|
25
|
+
def self.[](name)
|
26
|
+
begin
|
27
|
+
const_get name
|
28
|
+
rescue
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|