teejayvanslyke-clef 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 teejayvanslyke
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,24 @@
1
+ = clef
2
+ === An Interactive Music Experience Beyond Your Wildest Imagination!
3
+ === By Mister Teejay VanSlyke!
4
+
5
+ == Getting Started!
6
+
7
+ Clef is an interactive music programming language. To install, type
8
+
9
+ sudo gem install teejayvanslyke-clef # that's me!
10
+
11
+ Clef is a client-server application. Ooooh. It's so fancy. That means
12
+ you'll want to type
13
+
14
+ clef_server
15
+
16
+ in one terminal window, and
17
+
18
+ clef
19
+
20
+ in another!
21
+
22
+ == Copyright
23
+
24
+ Copyright (c) 2009 teejayvanslyke. See LICENSE for details.
@@ -0,0 +1,52 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "clef"
8
+ gem.summary = %Q{TODO}
9
+ gem.email = "tj@elctech.com"
10
+ gem.homepage = "http://github.com/teejayvanslyke/clef"
11
+ gem.authors = ["teejayvanslyke"]
12
+ gem.add_dependency('gamelan')
13
+ gem.add_dependency('midiator')
14
+ gem.add_dependency('treetop')
15
+ gem.add_dependency('andand')
16
+
17
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
18
+ end
19
+ rescue LoadError
20
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
21
+ end
22
+
23
+ require 'spec/rake/spectask'
24
+ Spec::Rake::SpecTask.new(:spec) do |spec|
25
+ spec.libs << 'lib' << 'spec'
26
+ spec.spec_files = FileList['spec/**/*_spec.rb']
27
+ end
28
+
29
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
30
+ spec.libs << 'lib' << 'spec'
31
+ spec.pattern = 'spec/**/*_spec.rb'
32
+ spec.rcov = true
33
+ end
34
+
35
+
36
+ task :default => :spec
37
+
38
+ require 'rake/rdoctask'
39
+ Rake::RDocTask.new do |rdoc|
40
+ if File.exist?('VERSION.yml')
41
+ config = YAML.load(File.read('VERSION.yml'))
42
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
43
+ else
44
+ version = ""
45
+ end
46
+
47
+ rdoc.rdoc_dir = 'rdoc'
48
+ rdoc.title = "clef #{version}"
49
+ rdoc.rdoc_files.include('README*')
50
+ rdoc.rdoc_files.include('lib/**/*.rb')
51
+ end
52
+
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 0
4
+ :patch: 0
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.dirname(__FILE__) + '/../lib/clef'
4
+
5
+ DRb.start_service
6
+ Clef::Console.new.run
7
+ DRb.stop_service
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.dirname(__FILE__) + '/../lib/clef'
4
+
5
+ environment = Clef::Environment.new
6
+
7
+ puts "Starting Clef server..."
8
+ DRb.start_service('druby://localhost:12345', environment)
9
+ puts "Listening at #{DRb.uri}."
10
+
11
+ environment.run
12
+
13
+ DRb.thread.join
14
+
@@ -0,0 +1,47 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'rubygems'
5
+ gem 'treetop'
6
+ require 'treetop'
7
+
8
+ gem 'andand'
9
+ require 'andand'
10
+
11
+ require 'midiator'
12
+ require 'gamelan'
13
+ require 'drb'
14
+
15
+ Treetop.load File.dirname(__FILE__) + '/clef'
16
+
17
+ require 'core_ext/fixnum'
18
+
19
+ require 'clef/utils'
20
+ require 'clef/note'
21
+ require 'clef/rest'
22
+ require 'clef/harmony'
23
+ require 'clef/sequence'
24
+ require 'clef/environment'
25
+ require 'clef/channel'
26
+ require 'clef/console'
27
+
28
+ module Clef
29
+
30
+ def self.parse(expr)
31
+ @parser ||= ClefParser.new
32
+ @parser.parse(expr)
33
+ end
34
+
35
+ def self.evaluate(expr)
36
+ self.parse(expr).andand.evaluate(self.environment)
37
+ end
38
+
39
+ def self.last_failure
40
+ @parser.failure_reason
41
+ end
42
+
43
+ def self.environment
44
+ DRbObject.new(nil, 'druby://localhost:12345')
45
+ end
46
+
47
+ end
@@ -0,0 +1,202 @@
1
+ grammar Clef
2
+
3
+ rule statement
4
+ assignment / expression
5
+ end
6
+
7
+ rule expression
8
+ additive
9
+ end
10
+
11
+ rule assignment
12
+ channel
13
+ space '=' space
14
+ expression {
15
+ def evaluate(env)
16
+ env.play(channel.evaluate(env).to_i, expression.evaluate(env))
17
+ end
18
+ }
19
+ end
20
+
21
+ rule additive
22
+ operand1:multitive
23
+ space operator:additive_operator space
24
+ operand2:additive {
25
+ def evaluate(env)
26
+ operator.apply(operand1.evaluate(env), operand2.evaluate(env))
27
+ end
28
+ }
29
+ /
30
+ multitive
31
+ end
32
+
33
+ rule additive_operator
34
+ '+' {
35
+ def apply(a, b)
36
+ a + b
37
+ end
38
+ }
39
+ /
40
+ '-' {
41
+ def apply(a, b)
42
+ a - b
43
+ end
44
+ }
45
+ end
46
+
47
+ rule multitive
48
+ operand1:primary
49
+ space operator:multitive_operator space
50
+ operand2:multitive {
51
+ def evaluate(env)
52
+ operator.apply(operand1.evaluate(env), operand2.evaluate(env))
53
+ end
54
+ }
55
+ /
56
+ primary
57
+ end
58
+
59
+ rule multitive_operator
60
+ '*' {
61
+ def apply(a, b)
62
+ a * b
63
+ end
64
+ }
65
+ /
66
+ '/' {
67
+ def apply(a, b)
68
+ a / b
69
+ end
70
+ }
71
+ /
72
+ '&' {
73
+ def apply(a, b)
74
+ a & b
75
+ end
76
+ }
77
+ end
78
+
79
+ rule primary
80
+ sequence
81
+ /
82
+ integer
83
+ /
84
+ channel
85
+ /
86
+ '(' space expression space ')' {
87
+ def evaluate(env)
88
+ expression.evaluate(env)
89
+ end
90
+ }
91
+ end
92
+
93
+ rule sequence
94
+ '[' playables ']' {
95
+ def evaluate(env)
96
+ Clef::Sequence.new(playables.evaluate(env))
97
+ end
98
+ }
99
+ end
100
+
101
+ rule key
102
+ [A-Ga-g] {
103
+ def evaluate(env)
104
+ text_value
105
+ end
106
+ }
107
+ end
108
+
109
+ rule accent
110
+ ('-' / '#') {
111
+ def evaluate(env)
112
+ text_value
113
+ end
114
+ }
115
+ end
116
+
117
+ rule integer
118
+ '-'? [0-9]+ {
119
+ def evaluate(env)
120
+ text_value.to_i
121
+ end
122
+ }
123
+ end
124
+
125
+ rule octave
126
+ [0-9] {
127
+ def evaluate(env)
128
+ text_value.to_i
129
+ end
130
+ }
131
+ end
132
+
133
+ rule note
134
+ key accent octave {
135
+ def evaluate(env)
136
+ Clef::Note.new(
137
+ key.evaluate(env),
138
+ accent.evaluate(env),
139
+ octave.evaluate(env))
140
+ end
141
+ }
142
+ /
143
+ rest
144
+ end
145
+
146
+ rule notes
147
+ note space notes {
148
+ def evaluate(env)
149
+ [ note.evaluate(env), notes.evaluate(env) ].flatten
150
+ end
151
+ }
152
+ /
153
+ note
154
+ end
155
+
156
+ rule rest
157
+ '_'+ {
158
+ def evaluate(env)
159
+ Clef::Rest.new
160
+ end
161
+ }
162
+ end
163
+
164
+ rule harmony
165
+ '(' space notes space ')' {
166
+ def evaluate(env)
167
+ Harmony.new(notes.evaluate(env))
168
+ end
169
+ }
170
+ end
171
+
172
+ rule playable
173
+ note / harmony
174
+ end
175
+
176
+ rule playables
177
+ playable space playables {
178
+ def evaluate(env)
179
+ [ playable.evaluate(env), playables.evaluate(env) ].flatten
180
+ end
181
+ }
182
+ /
183
+ playable
184
+ end
185
+
186
+ rule channel_number
187
+ '0' / '1' / '2' / '3' / '4' / '5' / '6' / '7' / '8' / '9' / '10' / '11' / '12' / '13' / '14' / '15'
188
+ end
189
+
190
+ rule channel
191
+ '@' channel_number {
192
+ def evaluate(env)
193
+ env.channels[channel_number.text_value.to_i]
194
+ end
195
+ }
196
+ end
197
+
198
+ rule space
199
+ ' '*
200
+ end
201
+
202
+ end
@@ -0,0 +1,39 @@
1
+ module Clef
2
+
3
+ class Channel
4
+
5
+ def initialize(number)
6
+ @number = number
7
+ @expression = Sequence.new([])
8
+ end
9
+
10
+ def play(expression)
11
+ @expression = expression
12
+ end
13
+
14
+ def to_s
15
+ @expression.to_s
16
+ end
17
+
18
+ def to_i
19
+ @number
20
+ end
21
+
22
+ def schedule(environment)
23
+ puts "Scheduling channel #{self.to_i} on #{environment}"
24
+ @expression.each_with_index do |playable, time|
25
+ if Harmony === playable
26
+ playable.each do |note|
27
+ environment.schedule_note_on(time, note.to_i, self.to_i, 127)
28
+ end
29
+ else
30
+ environment.schedule_note_on(time, playable.to_i, self.to_i, 127)
31
+ end
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+
39
+
@@ -0,0 +1,42 @@
1
+ require 'readline'
2
+
3
+ module Clef
4
+
5
+ class Console
6
+
7
+ def run
8
+
9
+ puts "Clef."
10
+ puts "An algorithmic music language."
11
+ puts "By T.J. VanSlyke."
12
+ puts "\n"
13
+
14
+ loop do
15
+ begin
16
+ line = Readline::readline('$>> ')
17
+
18
+ if line.nil?
19
+ puts "\nExiting..."
20
+ exit
21
+ end
22
+
23
+ Readline::HISTORY.push(line)
24
+
25
+ result = Clef.evaluate(line)
26
+
27
+ if result.nil?
28
+ puts "Clef didn't understand '#{line}': #{Clef.last_failure}"
29
+ else
30
+ puts "=> #{result.to_s}"
31
+ end
32
+ rescue => e
33
+ puts "There was a system error: #{e}"
34
+ end
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+
@@ -0,0 +1,145 @@
1
+ module Clef
2
+
3
+ class Environment
4
+
5
+ attr_reader :channels
6
+
7
+ def initialize
8
+ initialize_channels
9
+ initialize_midi
10
+ end
11
+
12
+ def run
13
+ schedule_events
14
+ @scheduler.run
15
+ end
16
+
17
+ def tempo=(tempo)
18
+ @scheduler.tempo = tempo
19
+ end
20
+
21
+ def play(channel, expression)
22
+ channels[channel].play(expression)
23
+ end
24
+
25
+ def schedule_note_on(time, pitch, channel, velocity)
26
+ @scheduler.at(@time + time) { @midi.note_on(pitch,channel,velocity) }
27
+ end
28
+
29
+ private
30
+
31
+ def initialize_channels
32
+ @channels = []
33
+ 16.times {|i| @channels << Channel.new(i) }
34
+ end
35
+
36
+ def initialize_midi
37
+ @events = {}
38
+
39
+ @time = 0
40
+ @beats = 16
41
+
42
+ @midi = MIDIator::Interface.new
43
+ @midi.use(:dls_synth)
44
+
45
+ @midi.control_change(32, 10, 1)
46
+ @midi.program_change(10, 26)
47
+
48
+ @scheduler = Gamelan::Scheduler.new({:tempo => 180})
49
+ end
50
+
51
+ def schedule_events
52
+ @channels.each do |channel|
53
+ channel.schedule(self)
54
+ end
55
+
56
+ @time = @time + @beats
57
+
58
+ @scheduler.at(@time) do
59
+ schedule_events
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+
67
+ =begin
68
+ module Spieluhr
69
+
70
+ module Engine
71
+
72
+ class Sequencer
73
+
74
+ attr_reader :events
75
+
76
+ def initialize
77
+ @events = {}
78
+
79
+ @time = 0
80
+ @beats = 16
81
+
82
+ @midi = MIDIator::Interface.new
83
+ @midi.use(:dls_synth)
84
+
85
+ @midi.control_change(32, 10, 1)
86
+ @midi.program_change(10, 26)
87
+
88
+ @scheduler = Gamelan::Scheduler.new({:tempo => 180})
89
+ end
90
+
91
+ def beats
92
+ @beats
93
+ end
94
+
95
+ def self.current
96
+ DRbObject.new(nil, 'druby://localhost:12345')
97
+ end
98
+
99
+ def push(patch, line)
100
+ @events[patch] ||= []
101
+ @current_patch = patch
102
+ patch.block.call(patch.parameters(line))
103
+ end
104
+
105
+ def schedule(time)
106
+ @scheduler.at(@time + time) { yield self }
107
+ end
108
+
109
+ def tempo=(tempo)
110
+ @scheduler.tempo = tempo
111
+ end
112
+
113
+ def play(options)
114
+ event = Event.new(self, options)
115
+ @events[@current_patch] << event
116
+ event
117
+ end
118
+
119
+ def note_on(pitch, channel, velocity)
120
+ @midi.note_on(pitch,channel,velocity)
121
+ end
122
+
123
+ def schedule_events
124
+ @events.values.flatten.each do |event|
125
+ event.schedule(self)
126
+ end
127
+
128
+ @time = @time + @beats
129
+
130
+ @scheduler.at(@time) do
131
+ schedule_events
132
+ end
133
+ end
134
+
135
+ def run
136
+ schedule_events
137
+ @scheduler.run
138
+ end
139
+
140
+ end
141
+
142
+ end
143
+
144
+ end
145
+ =end
@@ -0,0 +1,38 @@
1
+ require 'set'
2
+
3
+ module Clef
4
+
5
+ class Harmony < Set
6
+
7
+ def initialize(elements)
8
+ notes = []
9
+ elements.each do |e|
10
+ if Harmony === e
11
+ notes += e.to_a
12
+ else
13
+ notes << e
14
+ end
15
+ end
16
+
17
+ super(notes.compact.uniq)
18
+ end
19
+
20
+ def to_s
21
+ if size == 1
22
+ map {|n| n.to_s}.join(' ')
23
+ else
24
+ "(#{sort.map {|n| n.to_s}.join(' ')})"
25
+ end
26
+ end
27
+
28
+ def +(rhs)
29
+ if rhs.is_a?(Fixnum)
30
+ Harmony.new(map {|n| n + rhs})
31
+ else
32
+ super(rhs)
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,21 @@
1
+ require 'set'
2
+
3
+ module Clef
4
+
5
+ class Matrix < Array
6
+
7
+ include Clef::Utils
8
+
9
+ def initialize(sequences)
10
+ return unless sequences.is_a?(Array) && sequences.size > 0
11
+ rotate_matrix(sequences, :right)
12
+ map! {|row| row.compact.uniq }
13
+ end
14
+
15
+ def to_s
16
+ "[#{map {|row| "(#{row.sort.map {|n| n.to_s}.join(' ')})"}.join(' ')}]"
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,53 @@
1
+ module Clef
2
+
3
+ class Note
4
+
5
+ NOTES = %w(C- C# D- D# E- F- F# G- G# A- A# B-)
6
+
7
+ attr_reader :octave
8
+
9
+ def initialize(key, accent, octave)
10
+ @key, @accent, @octave = key, accent, octave
11
+ end
12
+
13
+ def to_s
14
+ "#{@key}#{@accent}#{@octave}"
15
+ end
16
+
17
+ def +(rhs)
18
+ Clef::Note.from_i(to_i + rhs)
19
+ end
20
+
21
+ def -(rhs)
22
+ Clef::Note.from_i(to_i - rhs)
23
+ end
24
+
25
+ def self.from_i(integer)
26
+ key = NOTES[integer % 12]
27
+ octave = (integer / 12).to_i
28
+ new(key[0..0], key[1..1], octave)
29
+ end
30
+
31
+ def to_i
32
+ NOTES.index("#{@key.upcase}#{@accent}") + (octave * 12)
33
+ end
34
+
35
+ def sharp?
36
+ @accent == '#'
37
+ end
38
+
39
+ def <=>(rhs)
40
+ self.to_i <=> rhs.to_i
41
+ end
42
+
43
+ def ==(rhs)
44
+ if rhs === Note
45
+ to_s == rhs.to_s
46
+ elsif rhs.is_a?(String)
47
+ to_s == rhs
48
+ end
49
+ end
50
+
51
+ end
52
+
53
+ end
@@ -0,0 +1,27 @@
1
+ module Clef
2
+
3
+ class Rest < Note
4
+ def initialize
5
+ super(nil, nil, nil)
6
+ end
7
+
8
+ def to_s
9
+ '___'
10
+ end
11
+
12
+ def to_i
13
+ -1
14
+ end
15
+
16
+ def +(rhs)
17
+ self
18
+ end
19
+
20
+ def -(rhs)
21
+ self
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+
@@ -0,0 +1,41 @@
1
+ module Clef
2
+
3
+ class Sequence < Array
4
+
5
+ include Clef::Utils
6
+
7
+ def initialize(elements)
8
+ elements = [ elements ] unless elements.is_a?(Array)
9
+ super(elements)
10
+ end
11
+
12
+ def +(rhs)
13
+ Sequence.new(map {|note| note + rhs})
14
+ end
15
+
16
+ def &(rhs)
17
+ sequences = [ self, rhs ]
18
+ sequences = rotate_matrix(sequences, :right)
19
+ Sequence.new(sequences.map {|row| Harmony.new(row)})
20
+ end
21
+
22
+ def -(rhs)
23
+ Sequence.new(map {|note| note - rhs})
24
+ end
25
+
26
+ def /(rhs)
27
+ result = []
28
+ each do |note|
29
+ result << note
30
+ (rhs-1).times { result << Rest.new }
31
+ end
32
+ Sequence.new(result)
33
+ end
34
+
35
+ def to_s
36
+ "[#{map {|n| n.to_s}.join(' ')}]"
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,39 @@
1
+ module Clef
2
+
3
+ module Utils
4
+ # Adapted from http://snippets.dzone.com/posts/show/3404
5
+ def rotate_matrix(matrix, direction)
6
+ # - You must Rotate the matrix neo!
7
+ oldMap = matrix
8
+
9
+ map = []
10
+
11
+ # Get the number of lines in the old map (they're the new columns)
12
+ lineCount = oldMap.size
13
+ # Get the number of columns in the old map (We have that many rows now)
14
+ columnCount = oldMap[0].size
15
+ columnCount.times { map.push [] }
16
+
17
+ # Loop through every line in the old map, retrieve the appropriate column
18
+ # and make a horizontal column with it's contents
19
+ # we'll take one (old)line at a time and rotate it.
20
+ onLine = 0
21
+ oldMap.each do |oldLine|
22
+ onColumn = 0
23
+ oldLine.each do
24
+ case direction
25
+ when :right
26
+ map[(columnCount - 1) - onColumn][(lineCount - 1) - onLine] = oldLine[(columnCount - 1) - onColumn]
27
+ when :left
28
+ map[onColumn][onLine] = oldLine[(columnCount - 1) - onColumn]
29
+ end
30
+ onColumn += 1
31
+ end
32
+ onLine += 1
33
+ end
34
+ map
35
+ end
36
+ end
37
+
38
+ end
39
+
@@ -0,0 +1,22 @@
1
+ class Fixnum
2
+
3
+ alias :__clef__plus__ :+
4
+ alias :__clef__asterisk__ :*
5
+
6
+ def +(rhs)
7
+ if rhs.is_a?(Clef::Sequence)
8
+ rhs + self
9
+ else
10
+ __clef__plus__(rhs)
11
+ end
12
+ end
13
+
14
+ def *(rhs)
15
+ if rhs.is_a?(Clef::Sequence)
16
+ rhs * self
17
+ else
18
+ __clef__asterisk__(rhs)
19
+ end
20
+ end
21
+
22
+ end
@@ -0,0 +1,9 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Clef::Note do
4
+
5
+ describe '- When transposing' do
6
+ (Clef::Note.from_i(60) + 12).to_i.should == 72
7
+ end
8
+
9
+ end
@@ -0,0 +1,227 @@
1
+ require 'spec_helper'
2
+
3
+ describe Clef do
4
+
5
+ describe '- When evaluating a sequence of notes' do
6
+
7
+ def do_evaluate
8
+ Clef.evaluate('[C-4 C#4 D-4 E-4]')
9
+ end
10
+
11
+ it 'should return a Sequence instance' do
12
+ do_evaluate.should be_instance_of(Clef::Sequence)
13
+ end
14
+
15
+ it 'should have four elements' do
16
+ do_evaluate.size.should == 4
17
+ end
18
+
19
+ it 'should have all elements in order' do
20
+ %w(C-4 C#4 D-4 E-4).each_with_index do |note, index|
21
+ do_evaluate[index].to_s.should == note
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ describe '- When evaluating a sequence of notes with rests' do
28
+
29
+ def do_evaluate
30
+ Clef.evaluate('[C-4 C#4 ___ D-4 E-4]')
31
+ end
32
+
33
+ it 'should have five elements' do
34
+ do_evaluate.size.should == 5
35
+ end
36
+
37
+ it 'should have all elements in order' do
38
+ %w(C-4 C#4 ___ D-4 E-4).each_with_index do |note, index|
39
+ do_evaluate[index].to_s.should == note
40
+ end
41
+ end
42
+
43
+ it 'should have a rest object in the appropriate position' do
44
+ do_evaluate[2].should be_instance_of(Clef::Rest)
45
+ end
46
+
47
+ end
48
+
49
+ describe '- When transposing (+) a sequence of notes' do
50
+
51
+ describe 'from the left-hand side' do
52
+
53
+ def do_evaluate
54
+ Clef.evaluate('[C-4 C#4] + 12')
55
+ end
56
+
57
+ it 'should have the same number of elements as the original' do
58
+ do_evaluate.size.should == 2
59
+ end
60
+
61
+ it 'should transpose the notes by the modifier' do
62
+ do_evaluate[0].should == 'C-5'
63
+ do_evaluate[1].should == 'C#5'
64
+ end
65
+
66
+ end
67
+
68
+ describe 'from the right-hand side' do
69
+
70
+ def do_evaluate
71
+ Clef.evaluate('12 + [C-4 C#4]')
72
+ end
73
+
74
+ it 'should have the same number of elements as the original' do
75
+ do_evaluate.size.should == 2
76
+ end
77
+
78
+ it 'should transpose the notes by the modifier' do
79
+ do_evaluate[0].should == 'C-5'
80
+ do_evaluate[1].should == 'C#5'
81
+ end
82
+
83
+ end
84
+
85
+ end
86
+
87
+ describe '- When transposing (-) a sequence of notes' do
88
+
89
+ describe 'from the left-hand side' do
90
+
91
+ def do_evaluate
92
+ Clef.evaluate('[C-4 C#4] - 2')
93
+ end
94
+
95
+ it 'should have the same number of elements as the original' do
96
+ do_evaluate.size.should == 2
97
+ end
98
+
99
+ it 'should transpose the notes by the modifier' do
100
+ do_evaluate[0].should == 'A#3'
101
+ do_evaluate[1].should == 'B-3'
102
+ end
103
+
104
+ end
105
+
106
+ describe 'from the right-hand side' do
107
+
108
+ def do_evaluate
109
+ Clef.evaluate('-2 + [C-4 C#4]')
110
+ end
111
+
112
+ it 'should have the same number of elements as the original' do
113
+ do_evaluate.size.should == 2
114
+ end
115
+
116
+ it 'should transpose the notes by the modifier' do
117
+ do_evaluate[0].should == 'A#3'
118
+ do_evaluate[1].should == 'B-3'
119
+ end
120
+
121
+ end
122
+
123
+ end
124
+
125
+ describe '- When transposing a sequence of notes with rests' do
126
+ it 'should leave the rests alone' do
127
+ Clef.evaluate('[C-3 ___] + 12').to_s.should == '[C-4 ___]'
128
+ end
129
+ end
130
+
131
+ describe '- When repeating (*) a sequence of notes' do
132
+
133
+ describe 'from the left-hand side' do
134
+
135
+ def do_evaluate
136
+ Clef.evaluate('[C-4 __] * 4')
137
+ end
138
+
139
+ it 'should be a repeated sequence of notes' do
140
+ do_evaluate.to_s.should == '[C-4 ___ C-4 ___ C-4 ___ C-4 ___]'
141
+ end
142
+
143
+ end
144
+
145
+ describe 'from the right-hand side' do
146
+
147
+ def do_evaluate
148
+ Clef.evaluate('2 * [C-4 __]')
149
+ end
150
+
151
+ it 'should be a repeated sequence of notes' do
152
+ do_evaluate.to_s.should == '[C-4 ___ C-4 ___]'
153
+ end
154
+
155
+ end
156
+
157
+ end
158
+
159
+ describe '- When rest-padding (/) a sequence of notes' do
160
+
161
+ describe 'from the left-hand side' do
162
+
163
+ it 'should insert the appropriate number of rests' do
164
+ Clef.evaluate('[C-4] / 4').to_s.should == '[C-4 ___ ___ ___]'
165
+ Clef.evaluate('[C-4 ___] / 4').to_s.should == '[C-4 ___ ___ ___ ___ ___ ___ ___]'
166
+ Clef.evaluate('[C-4 ___ C#4 ___ C-4 ___] / 4').to_s.should ==
167
+ '[C-4 ___ ___ ___ ___ ___ ___ ___ C#4 ___ ___ ___ ___ ___ ___ ___ C-4 ___ ___ ___ ___ ___ ___ ___]'
168
+ end
169
+
170
+ end
171
+
172
+ end
173
+
174
+ describe '- When combining two sequences into a matrix' do
175
+
176
+ def do_evaluate
177
+ Clef.evaluate('[C-4] & [C#4]')
178
+ end
179
+
180
+ it 'should have a length of 1' do
181
+ do_evaluate.length.should == 1
182
+ end
183
+
184
+ it 'should render a string representation' do
185
+ do_evaluate.to_s.should == '[(C-4 C#4)]'
186
+ end
187
+
188
+ end
189
+
190
+ describe '- When combining two complex sequences into a matrix' do
191
+
192
+ def do_evaluate
193
+ Clef.evaluate('[C-4 D-4 E-4] & [C-5 D-5 ___]')
194
+ end
195
+
196
+ it 'should combine appropriately' do
197
+ do_evaluate.to_s.should == '[(C-4 C-5) (D-4 D-5) (___ E-4)]'
198
+ end
199
+
200
+ end
201
+
202
+ describe '- When parsing complex expressions' do
203
+
204
+ it 'should parse transposed sequences' do
205
+ Clef.evaluate('([C-3 D-3] + 12) & [C-4]').to_s.should == '[C-4 D-4]'
206
+ end
207
+
208
+ it 'should parse chained unions' do
209
+ Clef.evaluate('[C-3] & [E-3] & [G-3]').to_s.should == '[(C-3 E-3 G-3)]'
210
+ end
211
+
212
+ it 'should allow transposing unions' do
213
+ Clef.evaluate('[C-3] & [E-3] & [G-3] + 12').to_s.should == '[(C-4 E-4 G-4)]'
214
+ end
215
+
216
+ end
217
+
218
+ describe '- When assigning sequences to channels' do
219
+
220
+ it 'should behave just like a sequence' do
221
+ Clef.evaluate('@1 = [C-3 D-3 E-3]')
222
+ Clef.evaluate('@1').to_s.should == '[C-3 D-3 E-3]'
223
+ end
224
+
225
+ end
226
+
227
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec'
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ require 'clef'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: teejayvanslyke-clef
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - teejayvanslyke
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-12 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: gamelan
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: midiator
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: treetop
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: andand
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ description:
56
+ email: tj@elctech.com
57
+ executables:
58
+ - clef
59
+ - clef_server
60
+ extensions: []
61
+
62
+ extra_rdoc_files:
63
+ - LICENSE
64
+ - README.rdoc
65
+ files:
66
+ - LICENSE
67
+ - README.rdoc
68
+ - Rakefile
69
+ - VERSION.yml
70
+ - bin/clef
71
+ - bin/clef_server
72
+ - lib/clef.rb
73
+ - lib/clef.treetop
74
+ - lib/clef/channel.rb
75
+ - lib/clef/console.rb
76
+ - lib/clef/environment.rb
77
+ - lib/clef/harmony.rb
78
+ - lib/clef/matrix.rb
79
+ - lib/clef/note.rb
80
+ - lib/clef/rest.rb
81
+ - lib/clef/sequence.rb
82
+ - lib/clef/utils.rb
83
+ - lib/core_ext/fixnum.rb
84
+ - spec/clef/note_spec.rb
85
+ - spec/clef_spec.rb
86
+ - spec/spec_helper.rb
87
+ has_rdoc: true
88
+ homepage: http://github.com/teejayvanslyke/clef
89
+ post_install_message:
90
+ rdoc_options:
91
+ - --charset=UTF-8
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: "0"
99
+ version:
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: "0"
105
+ version:
106
+ requirements: []
107
+
108
+ rubyforge_project:
109
+ rubygems_version: 1.2.0
110
+ signing_key:
111
+ specification_version: 2
112
+ summary: TODO
113
+ test_files:
114
+ - spec/clef/note_spec.rb
115
+ - spec/clef_spec.rb
116
+ - spec/spec_helper.rb