musicality 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c2138ce48e8476b381ac46a6bdc6c9b5578de656
4
- data.tar.gz: f95722c9c8a4818701880bbe733ccc7684ee41ef
3
+ metadata.gz: 1b76956290ba2ff351587624b43db12c84fc99a5
4
+ data.tar.gz: 02460a5a7b390c335ffac5c8050b76bb280fd149
5
5
  SHA512:
6
- metadata.gz: 9787df99b773046c025138212a9b67494e6ef080211ef429ae04519900e7f3e51dd68c5f0bccb68e762c918e77362fee23a9effb6df8dd06876674914e6d727c
7
- data.tar.gz: 965abe4f0fd8e897f99909a4d8a6b8c1967f2e45cb8291357d11e60d7c7666660ac9b4f11c5c21f2653de16bb1f74e0e307156183d42ece31d871802d1d07f64
6
+ metadata.gz: ed2d94a4bd155e24a072481ed45027b362475c68628d41be6cf1e565e08636564fd71375f01936fbb74a7fe13dfcd5bcd22649117b76e5d179ffa83d19031d53
7
+ data.tar.gz: 05d65518d4f9d8abb5bda61490cdfa18d4458148648b46f6edc4ba518d10caaf6f12c9dea74522cc4615fd650441a0892e43057928d97b358dcdc2eacc6d540a
data/README.md CHANGED
@@ -58,13 +58,56 @@ twinkle = Score::Measured.new(TWO_FOUR, 120) do |s|
58
58
  end
59
59
  ```
60
60
 
61
- To continue the above example, a score can be prepare for MIDI playback using the `Score::Measured#to_midi_seq` method.
61
+ ## MIDI Sequencing
62
+
63
+ A score can be prepared for MIDI playback using the `ScoreSequencer` class or `#to_midi_seq` method. To continue the previous example,
62
64
  ```ruby
63
65
  TEMPO_SAMPLE_RATE = 500
64
66
  seq = twinkle.to_midi_seq TEMPO_SAMPLE_RATE
65
67
  File.open('twinkle.mid', 'wb'){ |fout| seq.write(fout) }
66
68
  ```
67
- ##
69
+ ## Score DSL
70
+
71
+ The score DSL is an internal DSL (built on Ruby) that consists of a *score* block with additional blocks inside this to add sections, notes, and tempo/meter/dynamic changes.
72
+
73
+ Here is an example of a score file.
74
+ ```ruby
75
+ measured_score FOUR_FOUR, 120 do
76
+ title "Twinkle, Twinkle, Little Star"
77
+
78
+ Cmaj = [C3,E3,G3]
79
+ Fmaj = [F2,A2,C3]
80
+ Gmaj = [G2,B2,D3]
81
+ section "A" do
82
+ notes(
83
+ "rhand" => q(C4,C4,G4,G4,A4,A4) + h(G4) +
84
+ q(F4,F4,E4,E4,D4,D4) + h(C4),
85
+ "lhand" => h(Cmaj,Cmaj,Fmaj,Cmaj) +
86
+ h(Fmaj,Cmaj,Gmaj,Cmaj)
87
+ )
88
+ end
89
+
90
+ section "B" do
91
+ notes(
92
+ "rhand" => q(G4,G4,F4,F4,E4,E4) + h(D4),
93
+ "lhand" => h(Cmaj,Fmaj,Cmaj,Gmaj)
94
+ )
95
+ end
96
+ repeat "B"
97
+ repeat "A"
98
+ end
99
+ ```
100
+
101
+ The above score file is processed by the `ScoreDSL.load` method, as in:
102
+ ```ruby
103
+ require 'musicality'
104
+ include Musicality
105
+ include Meters
106
+ include Pitches
107
+
108
+ dsl = ScoreDSL.load 'twinkle.score'
109
+ score = dsl.score
110
+ ```
68
111
 
69
112
  ## Contributing
70
113
 
data/bin/lilify ADDED
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ exe_name = File.basename(__FILE__)
4
+
5
+ doc = <<DOCOPT
6
+ Loads a musicality score from YAML file, and converts it to a Lilypond file for engraving.
7
+
8
+ Usage:
9
+ #{exe_name} <input> [PART TITLE] ... [options]
10
+ #{exe_name} -h | --help
11
+ #{exe_name} --version
12
+
13
+ Arguments:
14
+ input A musicality score file (may be packed as a hash) in YAML format
15
+ PART name of a part in the score (e.g. "lhand")
16
+ TITLE The title to give the part (e.g. "Left Hand")
17
+
18
+ Options:
19
+ --all Select all parts for engraving, and use part name as the title if not given
20
+ -h --help Show this screen.
21
+ --version Show version.
22
+ --outfile=OUTF Name of output Lilypond file
23
+
24
+ DOCOPT
25
+
26
+ require 'docopt'
27
+ begin
28
+ args = Docopt::docopt(doc)
29
+ puts args
30
+ rescue Docopt::Exit => e
31
+ puts e.message
32
+ exit
33
+ end
34
+
35
+ require 'musicality'
36
+
37
+ if args["--version"]
38
+ puts "#{exe_name} from musicality v#{Musicality::VERSION}"
39
+ exit
40
+ end
41
+
42
+ require 'yaml'
43
+ include Musicality
44
+
45
+ fin_name = args["<input>"]
46
+ File.open(fin_name) do |fin|
47
+ print "Reading file '#{fin_name}'..."
48
+ score = YAML.load(fin.read)
49
+
50
+ if score.is_a? Hash
51
+ score = Score.unpack(score)
52
+ end
53
+ puts "done"
54
+
55
+ if score.valid?
56
+ part_names = args["PART"]
57
+ part_titles = args["TITLE"]
58
+ name_title_map =
59
+
60
+ make_lilypond_args = {
61
+ :part_titles => Hash[[part_names,part_titles].transpose]
62
+ }
63
+ unless args["--all"]
64
+ make_lilypond_args[:selected_parts] = part_names
65
+ end
66
+
67
+ print "Making Lilypond instructions..."
68
+ engraver = ScoreEngraver.new(score)
69
+ lilypond_text = engraver.make_lilypond(make_lilypond_args)
70
+ puts "done"
71
+
72
+ fout_name = args["--outfile"]
73
+ if fout_name.nil?
74
+ fout_name = "#{File.dirname(fin_name)}/#{File.basename(fin_name,File.extname(fin_name))}.ly"
75
+ end
76
+ print "Writing Lilypond file '#{fout_name}'..."
77
+ File.write(fout_name, lilypond_text)
78
+ puts "done"
79
+ else
80
+ puts "Score is not valid. See errors:"
81
+ puts score.errors.join("\n")
82
+ end
83
+ end
data/bin/midify CHANGED
@@ -33,8 +33,14 @@ rescue Docopt::Exit => e
33
33
  exit
34
34
  end
35
35
 
36
- require 'yaml'
37
36
  require 'musicality'
37
+
38
+ if args["--version"]
39
+ puts "#{exe_name} in musicality v#{Musicality::VERSION}"
40
+ exit
41
+ end
42
+
43
+ require 'yaml'
38
44
  include Musicality
39
45
 
40
46
  fin_name = args["<input>"]
data/lib/musicality.rb CHANGED
@@ -69,6 +69,7 @@ require 'musicality/composition/util/adding_sequence'
69
69
  require 'musicality/composition/util/compound_sequence'
70
70
  require 'musicality/composition/util/random_sampler'
71
71
  require 'musicality/composition/util/probabilities'
72
+ require 'musicality/composition/util/note_generation'
72
73
 
73
74
  require 'musicality/composition/model/pitch_class'
74
75
  require 'musicality/composition/model/pitch_classes'
@@ -76,15 +77,14 @@ require 'musicality/composition/model/scale'
76
77
  require 'musicality/composition/model/scale_class'
77
78
  require 'musicality/composition/model/scale_classes'
78
79
 
79
- require 'musicality/composition/note_generation'
80
- require 'musicality/composition/transposition'
81
-
82
80
  require 'musicality/composition/generation/counterpoint_generator'
83
81
  require 'musicality/composition/generation/random_rhythm_generator'
84
82
 
85
83
  require 'musicality/composition/dsl/score_methods'
86
84
  require 'musicality/composition/dsl/score_dsl'
87
85
 
86
+ require 'musicality/composition/convenience_methods'
87
+
88
88
  #
89
89
  # Performance
90
90
  #
@@ -115,4 +115,5 @@ require 'musicality/printing/lilypond/errors'
115
115
  require 'musicality/printing/lilypond/pitch_engraving'
116
116
  require 'musicality/printing/lilypond/note_engraving'
117
117
  require 'musicality/printing/lilypond/meter_engraving'
118
- require 'musicality/printing/lilypond/score_engraver'
118
+ require 'musicality/printing/lilypond/score_engraver'
119
+ require 'musicality/printing/lilypond/score_engraving'
@@ -0,0 +1,53 @@
1
+ module Musicality
2
+
3
+ def transpose notes, diff
4
+ notes.map {|n| n.transpose(diff) }
5
+ end
6
+ module_function :transpose
7
+
8
+ def s(*pitch_groups)
9
+ pitch_groups.map {|pg| Note.sixteenth(pg) }
10
+ end
11
+ module_function :s
12
+
13
+ def ds(*pitch_groups)
14
+ pitch_groups.map {|pg| Note.dotted_sixteenth(pg) }
15
+ end
16
+ module_function :ds
17
+
18
+ def e(*pitch_groups)
19
+ pitch_groups.map {|pg| Note.eighth(pg) }
20
+ end
21
+ module_function :e
22
+
23
+ def de(*pitch_groups)
24
+ pitch_groups.map {|pg| Note.dotted_eighth(pg) }
25
+ end
26
+ module_function :e
27
+
28
+ def q(*pitch_groups)
29
+ pitch_groups.map {|pg| Note.quarter(pg) }
30
+ end
31
+ module_function :q
32
+
33
+ def dq(*pitch_groups)
34
+ pitch_groups.map {|pg| Note.dotted_quarter(pg) }
35
+ end
36
+ module_function :dq
37
+
38
+ def h(*pitch_groups)
39
+ pitch_groups.map {|pg| Note.half(pg) }
40
+ end
41
+ module_function :h
42
+
43
+ def dh(*pitch_groups)
44
+ pitch_groups.map {|pg| Note.dotted_half(pg) }
45
+ end
46
+ module_function :dh
47
+
48
+ def w(*pitch_groups)
49
+ pitch_groups.map {|pg| Note.whole(pg) }
50
+ end
51
+ module_function :w
52
+
53
+ end
@@ -1,24 +1,21 @@
1
1
  module Musicality
2
2
 
3
- class ScoreDSL
3
+ class ScoreDSL
4
4
  def self.load fname
5
5
  dsl = ScoreDSL.new
6
6
  dsl.instance_eval(File.read(fname), fname)
7
7
  dsl
8
8
  end
9
9
 
10
+ attr_reader :score
10
11
  def initialize
11
12
  @score = nil
12
13
  end
13
14
 
14
- def score start_meter, start_tempo, &block
15
+ def measured_score start_meter, start_tempo, &block
15
16
  @score = Score::Measured.new(start_meter,start_tempo)
16
17
  @score.instance_eval(&block)
17
18
  end
18
-
19
- def score_yaml
20
- @score.pack.to_yaml
21
- end
22
19
  end
23
20
 
24
21
  end
@@ -28,19 +28,4 @@ def make_notes rhythm, pitch_groups
28
28
  end
29
29
  module_function :make_notes
30
30
 
31
- def e(pitch_groups)
32
- pitch_groups.map {|pg| Note.eighth(pg) }
33
- end
34
- module_function :e
35
-
36
- def q(pitch_groups)
37
- pitch_groups.map {|pg| Note.quarter(pg) }
38
- end
39
- module_function :q
40
-
41
- def dq(pitch_groups)
42
- pitch_groups.map {|pg| Note.dotted_quarter(pg) }
43
- end
44
- module_function :dq
45
-
46
31
  end
@@ -103,7 +103,7 @@ class Note
103
103
 
104
104
  {
105
105
  :sixteenth => Rational(1,16),
106
- :dotted_SIXTEENTH => Rational(3,32),
106
+ :dotted_sixteenth => Rational(3,32),
107
107
  :eighth => Rational(1,8),
108
108
  :dotted_eighth => Rational(3,16),
109
109
  :quarter => Rational(1,4),
@@ -3,7 +3,8 @@ module Musicality
3
3
  class Score
4
4
  include Validatable
5
5
  attr_accessor :parts, :program
6
-
6
+ attr_writer :title, :composer
7
+
7
8
  def title value = nil
8
9
  if value.nil?
9
10
  return @title
@@ -44,7 +44,7 @@ class ScoreEngraver
44
44
  output += " \\time #{@start_meter.to_lilypond}\n"
45
45
  end
46
46
 
47
- line = ""
47
+ line = " "
48
48
  part.notes.each_index do |i|
49
49
  note = part.notes[i]
50
50
  begin
@@ -54,12 +54,12 @@ class ScoreEngraver
54
54
  end
55
55
 
56
56
  if (line.size + str.size) > MAX_LINE_LEN
57
- output += " " + line
58
- line = ""
57
+ output += line + "\n"
58
+ line = " "
59
59
  end
60
60
  line += str
61
61
  end
62
- output += " " + line
62
+ output += line + "\n"
63
63
 
64
64
  output += " }\n"
65
65
  end
@@ -0,0 +1,12 @@
1
+ module Musicality
2
+
3
+ class Score
4
+ class TempoBased < Score
5
+ # See ScoreEngraver#make_lilypond for details on supported keyword args
6
+ def to_lilypond **kwargs
7
+ ScoreEngraver.new(self).make_lilypond(**kwargs)
8
+ end
9
+ end
10
+ end
11
+
12
+ end
@@ -1,3 +1,3 @@
1
1
  module Musicality
2
- VERSION = "0.7.0"
2
+ VERSION = "0.8.0"
3
3
  end
@@ -0,0 +1,60 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe 'transpose' do
4
+ it 'should map given notes to new notes, transposing by given diff' do
5
+ notes = "/4A2,C2 /4D2,F2,Gb2 /8 /8E4".to_notes
6
+ semitones = 3
7
+ notes2 = transpose(notes,semitones)
8
+
9
+ notes2.size.should eq(notes.size)
10
+ notes2.each_index do |i|
11
+ notes2[i].pitches.each_with_index do |pitch2,j|
12
+ pitch = notes[i].pitches[j]
13
+ pitch2.diff(pitch).should eq(semitones)
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ {
20
+ :s => "1/16".to_r,
21
+ :ds => "3/32".to_r,
22
+ :e => "1/8".to_r,
23
+ :de => "3/16".to_r,
24
+ :q => "1/4".to_r,
25
+ :dq => "3/8".to_r,
26
+ :h => "1/2".to_r,
27
+ :dh => "3/4".to_r,
28
+ :w => '1'.to_r
29
+ }.each do |method,dur|
30
+ describe method do
31
+ context 'given no args' do
32
+ it 'should produce an empty array' do
33
+ send(method).should eq([])
34
+ end
35
+ end
36
+
37
+ context 'given one pitch' do
38
+ it 'should produce an array with one note, with proper duration' do
39
+ send(method,*[C2]).should eq([Note.new(dur,C2)])
40
+ end
41
+ end
42
+
43
+ context 'given one pitch group' do
44
+ it 'should produce an array with one note, given pitch group, and with proper duration' do
45
+ send(method,*[[C2,E2,G2]]).should eq([Note.new(dur,[C2,E2,G2])])
46
+ end
47
+ end
48
+
49
+ context 'given many pitch groups' do
50
+ it 'should produce an array of notes with same pitch groups, and with proper duration' do
51
+ pg1 = A3
52
+ pg2 = [B3,G3]
53
+ pg3 = F4
54
+ send(method,*[pg1,pg2,pg3]).should eq([
55
+ Note.new(dur,pg1), Note.new(dur,pg2), Note.new(dur,pg3)
56
+ ])
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
2
 
3
3
  describe 'make_note' do
4
4
  it 'should return a Note' do
@@ -48,7 +48,7 @@ describe Note do
48
48
 
49
49
  {
50
50
  :sixteenth => Rational(1,16),
51
- :dotted_SIXTEENTH => Rational(3,32),
51
+ :dotted_sixteenth => Rational(3,32),
52
52
  :eighth => Rational(1,8),
53
53
  :dotted_eighth => Rational(3,16),
54
54
  :quarter => Rational(1,4),
@@ -1,6 +1,38 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
2
 
3
3
  describe Score do
4
+ describe '#title' do
5
+ context 'given no arg' do
6
+ it 'should return the title' do
7
+ Score.new(:title => "MyTitle").title.should eq("MyTitle")
8
+ end
9
+ end
10
+
11
+ context 'given an arg' do
12
+ it 'should assign the given value to title' do
13
+ score = Score.new(:title => "MyTitle")
14
+ score.title("A Better Title")
15
+ score.title.should eq("A Better Title")
16
+ end
17
+ end
18
+ end
19
+
20
+ describe '#composer' do
21
+ context 'given no arg' do
22
+ it 'should return the composer' do
23
+ Score.new(:composer => "Beethoven").composer.should eq("Beethoven")
24
+ end
25
+ end
26
+
27
+ context 'given an arg' do
28
+ it 'should assign the given value to composer' do
29
+ score = Score.new(:composer => "Beethoven")
30
+ score.composer("Mozart")
31
+ score.composer.should eq("Mozart")
32
+ end
33
+ end
34
+ end
35
+
4
36
  describe '#collated?' do
5
37
  context 'has program with more than one segment' do
6
38
  it 'should return false' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: musicality
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Tunnell
@@ -116,6 +116,7 @@ description: "The library is based around an abstract representation for music n
116
116
  email:
117
117
  - jamestunnell@gmail.com
118
118
  executables:
119
+ - lilify
119
120
  - midify
120
121
  extensions: []
121
122
  extra_rdoc_files: []
@@ -128,6 +129,7 @@ files:
128
129
  - LICENSE.txt
129
130
  - README.md
130
131
  - Rakefile
132
+ - bin/lilify
131
133
  - bin/midify
132
134
  - examples/composition/auto_counterpoint.rb
133
135
  - examples/composition/part_generator.rb
@@ -137,6 +139,7 @@ files:
137
139
  - examples/notation/song1.rb
138
140
  - examples/notation/song2.rb
139
141
  - lib/musicality.rb
142
+ - lib/musicality/composition/convenience_methods.rb
140
143
  - lib/musicality/composition/dsl/score_dsl.rb
141
144
  - lib/musicality/composition/dsl/score_methods.rb
142
145
  - lib/musicality/composition/generation/counterpoint_generator.rb
@@ -146,11 +149,10 @@ files:
146
149
  - lib/musicality/composition/model/scale.rb
147
150
  - lib/musicality/composition/model/scale_class.rb
148
151
  - lib/musicality/composition/model/scale_classes.rb
149
- - lib/musicality/composition/note_generation.rb
150
- - lib/musicality/composition/transposition.rb
151
152
  - lib/musicality/composition/util/adding_sequence.rb
152
153
  - lib/musicality/composition/util/biinfinite_sequence.rb
153
154
  - lib/musicality/composition/util/compound_sequence.rb
155
+ - lib/musicality/composition/util/note_generation.rb
154
156
  - lib/musicality/composition/util/probabilities.rb
155
157
  - lib/musicality/composition/util/random_sampler.rb
156
158
  - lib/musicality/composition/util/repeating_sequence.rb
@@ -233,18 +235,19 @@ files:
233
235
  - lib/musicality/printing/lilypond/part_engraver.rb
234
236
  - lib/musicality/printing/lilypond/pitch_engraving.rb
235
237
  - lib/musicality/printing/lilypond/score_engraver.rb
238
+ - lib/musicality/printing/lilypond/score_engraving.rb
236
239
  - lib/musicality/validatable.rb
237
240
  - lib/musicality/version.rb
238
241
  - musicality.gemspec
242
+ - spec/composition/convenience_methods_spec.rb
239
243
  - spec/composition/generation/random_rhythm_generator_spec.rb
240
244
  - spec/composition/model/pitch_class_spec.rb
241
245
  - spec/composition/model/pitch_classes_spec.rb
242
246
  - spec/composition/model/scale_class_spec.rb
243
247
  - spec/composition/model/scale_spec.rb
244
- - spec/composition/note_generation_spec.rb
245
- - spec/composition/transposition_spec.rb
246
248
  - spec/composition/util/adding_sequence_spec.rb
247
249
  - spec/composition/util/compound_sequence_spec.rb
250
+ - spec/composition/util/note_generation_spec.rb
248
251
  - spec/composition/util/probabilities_spec.rb
249
252
  - spec/composition/util/random_sampler_spec.rb
250
253
  - spec/composition/util/repeating_sequence_spec.rb
@@ -323,15 +326,15 @@ signing_key:
323
326
  specification_version: 4
324
327
  summary: Music notation, composition, and performance
325
328
  test_files:
329
+ - spec/composition/convenience_methods_spec.rb
326
330
  - spec/composition/generation/random_rhythm_generator_spec.rb
327
331
  - spec/composition/model/pitch_class_spec.rb
328
332
  - spec/composition/model/pitch_classes_spec.rb
329
333
  - spec/composition/model/scale_class_spec.rb
330
334
  - spec/composition/model/scale_spec.rb
331
- - spec/composition/note_generation_spec.rb
332
- - spec/composition/transposition_spec.rb
333
335
  - spec/composition/util/adding_sequence_spec.rb
334
336
  - spec/composition/util/compound_sequence_spec.rb
337
+ - spec/composition/util/note_generation_spec.rb
335
338
  - spec/composition/util/probabilities_spec.rb
336
339
  - spec/composition/util/random_sampler_spec.rb
337
340
  - spec/composition/util/repeating_sequence_spec.rb
@@ -1,8 +0,0 @@
1
- module Musicality
2
-
3
- def transpose notes, diff
4
- notes.map {|n| n.transpose(diff) }
5
- end
6
- module_function :transpose
7
-
8
- end
@@ -1,17 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
-
3
- describe 'transpose' do
4
- it 'should map given notes to new notes, transposing by given diff' do
5
- notes = "/4A2,C2 /4D2,F2,Gb2 /8 /8E4".to_notes
6
- semitones = 3
7
- notes2 = transpose(notes,semitones)
8
-
9
- notes2.size.should eq(notes.size)
10
- notes2.each_index do |i|
11
- notes2[i].pitches.each_with_index do |pitch2,j|
12
- pitch = notes[i].pitches[j]
13
- pitch2.diff(pitch).should eq(semitones)
14
- end
15
- end
16
- end
17
- end