musicality 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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