mtk 0.0.1 → 0.0.2
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 +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
|