musical_score 0.1.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.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +25 -0
  3. data/.gitignore +11 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +1156 -0
  6. data/.travis.yml +7 -0
  7. data/CODE_OF_CONDUCT.md +74 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +45 -0
  11. data/Rakefile +6 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +8 -0
  14. data/codeclimate-config.patch +1191 -0
  15. data/lib/musical_score/attribute/attribute.rb +69 -0
  16. data/lib/musical_score/attribute/clef.rb +44 -0
  17. data/lib/musical_score/attribute/key.rb +74 -0
  18. data/lib/musical_score/attribute/time.rb +43 -0
  19. data/lib/musical_score/const.rb +16 -0
  20. data/lib/musical_score/element_base.rb +17 -0
  21. data/lib/musical_score/errors.rb +3 -0
  22. data/lib/musical_score/io/importer.rb +22 -0
  23. data/lib/musical_score/location.rb +13 -0
  24. data/lib/musical_score/measures.rb +42 -0
  25. data/lib/musical_score/note/lyric.rb +43 -0
  26. data/lib/musical_score/note/notation/notation.rb +105 -0
  27. data/lib/musical_score/note/notation/tie.rb +22 -0
  28. data/lib/musical_score/note/notation/tuplet.rb +22 -0
  29. data/lib/musical_score/note/note.rb +141 -0
  30. data/lib/musical_score/note/pitch.rb +140 -0
  31. data/lib/musical_score/note/time_modification.rb +33 -0
  32. data/lib/musical_score/note/type.rb +29 -0
  33. data/lib/musical_score/notes.rb +37 -0
  34. data/lib/musical_score/part/measure.rb +49 -0
  35. data/lib/musical_score/part/part.rb +45 -0
  36. data/lib/musical_score/score/identification/creator.rb +29 -0
  37. data/lib/musical_score/score/identification/encoding.rb +66 -0
  38. data/lib/musical_score/score/identification/identification.rb +47 -0
  39. data/lib/musical_score/score/part/part.rb +37 -0
  40. data/lib/musical_score/score/score.rb +126 -0
  41. data/lib/musical_score/version.rb +4 -0
  42. data/lib/musical_score.rb +12 -0
  43. data/musical_score.gemspec +42 -0
  44. metadata +184 -0
@@ -0,0 +1,69 @@
1
+ require 'contracts'
2
+ Dir[File.expand_path('../', __FILE__) << '/**/*.rb'].each do |file|
3
+ # require file except myself
4
+ if(file != __FILE__)
5
+ require file
6
+ end
7
+ end
8
+ module MusicalScore
9
+ module Attribute
10
+ class Attribute < MusicalScore::ElementBase
11
+ include Contracts
12
+ attr_accessor :divisions, :key, :time, :instruments, :clef
13
+
14
+ # initialize the attibute
15
+ #
16
+ Contract KeywordArgs[
17
+ :divisions => Num,
18
+ :clef => Optional[MusicalScore::Attribute::Clef],
19
+ :key => Optional[MusicalScore::Attribute::Key],
20
+ :time => Optional[MusicalScore::Attribute::Time],
21
+ :instruments => Optional[String],
22
+ ] => Any
23
+ def initialize(
24
+ divisions:,
25
+ clef: MusicalScore::Attribute::Clef.new(:G),
26
+ key: MusicalScore::Attribute::Key.new(0, :major),
27
+ time: MusicalScore::Attribute::Time.new(4, 4),
28
+ instruments: 'Piano',
29
+ **rest_args
30
+ )
31
+ @divisions = divisions
32
+ @clef = clef
33
+ @key = key
34
+ @time = time
35
+ @instruments = instruments
36
+ end
37
+
38
+ Contract REXML::Element => MusicalScore::Attribute::Attribute
39
+ def self.create_by_xml(xml_doc)
40
+ divisions = xml_doc.elements["//divisions"].text.to_i
41
+ clef_doc = xml_doc.elements["//clef"]
42
+ time_doc = xml_doc.elements["//time"]
43
+ key_doc = xml_doc.elements["//key"]
44
+
45
+ clef = clef_doc ? MusicalScore::Attribute::Clef.create_by_xml(clef_doc) : nil
46
+ time = time_doc ? MusicalScore::Attribute::Time.create_by_xml(time_doc) : nil
47
+ key = key_doc ? MusicalScore::Attribute::Key.create_by_xml(key_doc) : nil
48
+
49
+ attributes = MusicalScore::Attribute::Attribute.new(divisions: divisions, clef: clef, time: time)
50
+ return attributes
51
+ end
52
+
53
+ def export_xml
54
+ attribute_element = REXML::Element.new('attributes')
55
+ divisions_element = REXML::Element.new('divisions').add_text(@divisions.to_s)
56
+ key_element = @key.export_xml
57
+ time_element = @time.export_xml
58
+ clef_element = @clef.export_xml
59
+
60
+ attribute_element.add_element(divisions_element)
61
+ attribute_element.add_element(key_element)
62
+ attribute_element.add_element(time_element)
63
+ attribute_element.add_element(clef_element)
64
+
65
+ return attribute_element
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,44 @@
1
+ require "contracts"
2
+ module MusicalScore
3
+ module Attribute
4
+ class Clef < MusicalScore::ElementBase
5
+ include Contracts
6
+ @@sign = %i(G F C percussion TAB jianpu none)
7
+ attr_reader :sign, :line, :clef_octave_change
8
+
9
+ # constructor
10
+ #
11
+ # @param sign The sign of clef, such as treble, bass. It is described by the @@sign symbols
12
+ # @param line The number of line from the bottom of the staff, which the sign note is defined at the line.
13
+ # @param clef_octave_change The number of clef changes, which is written either an octave higher or lower than sounding pitch
14
+ Contract Enum[*@@sign], Pos, Num => Any
15
+ def initialize(sign, line = 0, clef_octave_change = 0)
16
+ @sign = sign.to_sym
17
+ @line = line
18
+ @clef_octave_change = clef_octave_change
19
+ end
20
+
21
+ Contract REXML::Element => MusicalScore::Attribute::Clef
22
+ def self.create_by_xml(xml_doc)
23
+ sign = xml_doc.elements["sign"].text.to_sym
24
+ line = xml_doc.elements["line"] ? xml_doc.elements["line"].text.to_i : 0
25
+ clef_octave_change = xml_doc.elements["clef-octave-change"] ? xml_doc.elements["clef-octave-change"].text.to_i : 0
26
+ clef = MusicalScore::Attribute::Clef.new(sign, line, clef_octave_change)
27
+ return clef
28
+ end
29
+
30
+ def export_xml
31
+ clef_element = REXML::Element.new('clef')
32
+ sign_element = REXML::Element.new('sign').add_text(@sign.to_s)
33
+ line_element = @line != 0 ? REXML::Element.new('line').add_text(@line.to_s) : nil
34
+ clef_octave_change_element = @clef_octave_change != 0 ? REXML::Element.new('clef-octave-change').add_text(@clef_octave_change.to_s) : nil
35
+
36
+ clef_element.add_element(sign_element)
37
+ clef_element.add_element(line_element) if line_element
38
+ clef_element.add_element(clef_octave_change_element) if clef_octave_change_element
39
+
40
+ return clef_element
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,74 @@
1
+ require "contracts"
2
+ module MusicalScore
3
+ module Attribute
4
+ class Key < MusicalScore::ElementBase
5
+ include Contracts
6
+ @@mode = %i(major :minor)
7
+ @@circle_of_fifths = [0, 7, 2, 9, 4, 11, 6, 1, 8, 3, 10, 5]
8
+
9
+ attr_reader :fifths, :mode
10
+ # constructor
11
+ #
12
+ # @param fifths The number of sharps (positive) or flats (negative)
13
+ # @param mode major or minor
14
+ Contract Enum[*-NUMBER_OF_FIFTHS..NUMBER_OF_FIFTHS], Enum[*@@mode] => Any
15
+ def initialize(fifths, mode)
16
+ @fifths = fifths
17
+ @mode = mode.to_sym
18
+ end
19
+
20
+ # detect tonic in major scale and minor scale, and pithes that has sharp or flat
21
+ #
22
+ Contract None => HashOf[Any, Any]
23
+ def tonic_key_and_altered_pitches
24
+ if @fifths >= 0
25
+ pitch_number = @@circle_of_fifths[@fifths]
26
+ major_pitch = MusicalScore::Note::Pitch.new_note_sharp(pitch_number)
27
+
28
+ minor_number = (pitch_number + RELATED_KEY_SLIDE_NUMBER) % NUMBER_OF_NOTES
29
+ minor_pitch = MusicalScore::Note::Pitch.new_note_sharp(minor_number)
30
+
31
+ altered_pitches = Array.new
32
+ @fifths.times do |i|
33
+ count = (i + SHARP_START_INDEX) % NUMBER_OF_NOTES
34
+ altered_pitches.push(MusicalScore::Note::Pitch.new_note_sharp(@@circle_of_fifths[count]))
35
+ end
36
+ return { :major_pitch => major_pitch, :minor_pitch => minor_pitch, :altered_pitches => altered_pitches }
37
+ else
38
+ reversed = @@circle_of_fifths.reverse
39
+ fif = @fifths.abs
40
+ pitch_number = reversed[fif-1]
41
+ major_pitch = MusicalScore::Note::Pitch.new_note_flat(pitch_number)
42
+
43
+ minor_number = (pitch_number + RELATED_KEY_SLIDE_NUMBER) % NUMBER_OF_NOTES
44
+ minor_pitch = MusicalScore::Note::Pitch.new_note_flat(minor_number)
45
+
46
+ altered_pitches = Array.new
47
+ fif.times do |i|
48
+ count = (i + FLAT_START_INDEX) % NUMBER_OF_NOTES
49
+ altered_pitches.push(MusicalScore::Note::Pitch.new_note_flat(reversed[count]))
50
+ end
51
+ return { :major_pitch => major_pitch, :minor_pitch => minor_pitch, :altered_pitches => altered_pitches }
52
+ end
53
+ end
54
+
55
+ Contract REXML::Element => MusicalScore::Attribute::Key
56
+ def self.create_by_xml(xml_doc)
57
+ fifths = xml_doc.elements["fifths"].text.to_i
58
+ mode = xml_doc.elements["mode"].text.to_sym
59
+ return MusicalScore::Attribute::Key.new(fifths, mode)
60
+ end
61
+
62
+ def export_xml
63
+ key_element = REXML::Element.new('key')
64
+ fifths_element = REXML::Element.new('fifths').add_text(@fifths.to_s)
65
+ mode_element = REXML::Element.new('mode').add_text(@mode.to_s)
66
+
67
+ key_element.add_element(fifths_element)
68
+ key_element.add_element(mode_element)
69
+
70
+ return key_element
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,43 @@
1
+ require 'contracts'
2
+ module MusicalScore
3
+ module Attribute
4
+ class Time < MusicalScore::ElementBase
5
+ include Contracts
6
+ attr_reader :beats, :beat_type
7
+
8
+ # constructor
9
+ #
10
+ Contract Num, Num => Any
11
+ def initialize(beats, beat_type)
12
+ @beats = beats
13
+ @beat_type = beat_type
14
+ end
15
+
16
+ # @return [String] describe the time object in a fraction style
17
+ def to_s
18
+ return "%d/%d" % [@beats, @beat_type]
19
+ end
20
+
21
+ Contract REXML::Element => MusicalScore::Attribute::Time
22
+ def self.create_by_xml(xml_doc)
23
+ beats = xml_doc.elements["beats"].text.to_i
24
+ beat_type = xml_doc.elements["beat-type"].text.to_i
25
+ return MusicalScore::Attribute::Time.new(beats, beat_type)
26
+ end
27
+
28
+ def export_xml
29
+ time = REXML::Element.new('time')
30
+ beats = REXML::Element.new('beats')
31
+ beat_type = REXML::Element.new('beat-type')
32
+
33
+ beats.add_text(@beats.to_s)
34
+ beat_type.add_text(@beat_type.to_s)
35
+
36
+ time.add_element(beats)
37
+ time.add_element(beat_type)
38
+
39
+ return time
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,16 @@
1
+ module MusicalScore
2
+ SHARP = :sharp
3
+ FLAT = :flat
4
+
5
+ NUMBER_OF_NOTES = 12
6
+ NUMBER_OF_FIFTHS = 7
7
+ SHARP_START_INDEX = 11
8
+ FLAT_START_INDEX = 6
9
+
10
+ RELATED_KEY_SLIDE_NUMBER = 9
11
+
12
+ AVAILABLE_NUMBERS_OF_ALTER = [-2, -1, 0, 1, 2]
13
+ TYPE_START_STOP = %i(start stop)
14
+ TYPE_START_STOP_CONTINUE = %i(start stop continue)
15
+ TYPE_CREATOR = %i(composer lyricist arranger)
16
+ end
@@ -0,0 +1,17 @@
1
+ require 'contracts'
2
+ require 'rexml/document'
3
+
4
+ module MusicalScore
5
+ class ElementBase
6
+ include Contracts
7
+
8
+ Contract Or[REXML::Document, REXML::Element] => Any
9
+ def self.create_by_xml(element)
10
+ raise "Called abstract method: create_by_xml"
11
+ end
12
+
13
+ def export_xml
14
+ raise "Called abstract method: export_xml"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module MusicalScore
2
+ class InvalidFileType < StandardError; end
3
+ end
@@ -0,0 +1,22 @@
1
+ require 'musical_score'
2
+ require 'rexml/document'
3
+ module MusicalScore
4
+ module IO
5
+ def import(file)
6
+ extname = File.extname(file)
7
+ case extname
8
+ when ".xml"
9
+ return import_xml(file)
10
+ else
11
+ raise MusicalScore::InvalidFileType
12
+ end
13
+ end
14
+ def import_xml(file_path)
15
+ doc = REXML::Document.new(File.new(file_path))
16
+ score = MusicalScore::Score::Score.create_by_xml(doc, file_path)
17
+ return score
18
+ end
19
+
20
+ module_function :import, :import_xml
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ require 'contracts'
2
+
3
+ module MusicalScore
4
+ class Location
5
+ attr_reader :measure_number, :location
6
+ include Contracts
7
+ Contract Nat, Rational => Any
8
+ def initialize(measure_number, location)
9
+ @measure_number = measure_number
10
+ @location = location
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,42 @@
1
+ require 'contracts'
2
+ require 'musical_score/part/measure'
3
+ module MusicalScore
4
+ class Measures < MusicalScore::ElementBase
5
+ include Contracts
6
+ include Enumerable
7
+ attr_reader :measures
8
+
9
+ Contract ArrayOf[MusicalScore::Part::Measure] => Any
10
+ def initialize(measures)
11
+ @measures = measures
12
+ end
13
+
14
+ def [](index)
15
+ return @measures[index]
16
+ end
17
+
18
+ def each
19
+ @measures.each do |measure|
20
+ yield measure
21
+ end
22
+ end
23
+
24
+ def all_notes
25
+ result = Array.new
26
+ @measures.each do |measure|
27
+ result.concat(measure)
28
+ end
29
+ return result
30
+ end
31
+
32
+ def set_location
33
+ current_location = Rational(0)
34
+ @measures.each do |measure|
35
+ number = measure.number
36
+ measure.notes.set_location(current_location, number)
37
+ current_location += measure.notes.duration
38
+ measure.length = measure.notes.duration
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,43 @@
1
+ require 'contracts'
2
+ module MusicalScore
3
+ module Note
4
+ class Lyric < MusicalScore::ElementBase
5
+ include Contracts
6
+ @@syllabic = %i(single begin end middle)
7
+ attr_accessor :text, :syllabic, :is_extend
8
+
9
+ # constructor
10
+ #
11
+ # @param text
12
+ # @param syllabic
13
+ # @param is_extend
14
+ Contract String, Maybe[Enum[*@@syllabic]], Bool => Any
15
+ def initialize(text, syllabic, is_extend = false)
16
+ @text = text
17
+ @syllabic = syllabic
18
+ @is_extend = is_extend
19
+ end
20
+ Contract REXML::Element => MusicalScore::Note::Lyric
21
+ def self.create_by_xml(xml_doc)
22
+ syllabic = xml_doc.elements["syllabic"] ? xml_doc.elements["syllabic"].text.to_sym : nil
23
+ text = xml_doc.elements["text"].text
24
+ is_extend = xml_doc.elements["extend"] ? true : false
25
+ return MusicalScore::Note::Lyric.new(text, syllabic, is_extend)
26
+ end
27
+
28
+ def export_xml(number)
29
+ lyric_element = REXML::Element.new('lyric')
30
+ lyric_element.add_attribute('number', number.to_s)
31
+ text_element = REXML::Element.new('text').add_text(@text.to_s)
32
+
33
+ if (@syllabic)
34
+ syllabic_element = REXML::Element.new('syllabic').add_text(@syllabic.to_s)
35
+ lyric_element.add_element(syllabic_element)
36
+ end
37
+ lyric_element.add_element(text_element)
38
+
39
+ return lyric_element
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,105 @@
1
+ require 'contracts'
2
+ Dir[File.expand_path('../', __FILE__) << '/**/*.rb'].each do |file|
3
+ # require file except myself
4
+ if(file != __FILE__)
5
+ require file
6
+ end
7
+ end
8
+ module MusicalScore
9
+ module Note
10
+ module Notation
11
+ class Notation < MusicalScore::ElementBase
12
+ include Contracts
13
+ @@articulation = %i(
14
+ accent
15
+ breath_mark
16
+ caesura
17
+ detached_legato
18
+ doit
19
+ falloff
20
+ plop
21
+ scoop
22
+ spiccato
23
+ staccatissimo
24
+ staccato
25
+ stress
26
+ strong_accent
27
+ tenuto
28
+ unstress
29
+ )
30
+ @@dynamics = %i(
31
+ f
32
+ ff
33
+ fff
34
+ ffff
35
+ fffff
36
+ fp
37
+ fz
38
+ mf
39
+ mp
40
+ p
41
+ pp
42
+ ppp
43
+ pppp
44
+ ppppp
45
+ rf
46
+ rfz
47
+ sf
48
+ sffz
49
+ sfpp
50
+ sfz
51
+ )
52
+ attr_accessor :articulation, :dynamics, :tie, :tuplet
53
+ Contract KeywordArgs[
54
+ :articulation => Maybe[Enum[*@@articulation]],
55
+ :dynamics => Maybe[Enum[*@@dynamics]],
56
+ :tie => Maybe[MusicalScore::Note::Notation::Tie],
57
+ :tuplet => Maybe[MusicalScore::Note::Notation::Tuplet],
58
+ ] => Any
59
+ def initialize(
60
+ articulation: nil,
61
+ dynamics: nil,
62
+ tie: nil,
63
+ tuplet: nil,
64
+ **rest_args
65
+ )
66
+ @articulation = articulation
67
+ @dynamics = dynamics
68
+ @tie = tie
69
+ @tuplet = tuplet
70
+ end
71
+
72
+ Contract REXML::Element => MusicalScore::Note::Notation::Notation
73
+ def self.create_by_xml(xml_doc)
74
+ articulation = xml_doc.elements["articulations"] ? xml_doc.elements["articulations"].elements[1].name.to_sym : nil
75
+ dynamics = xml_doc.elements["dynamics"] ? xml_doc.elements["dynamics"].elements[1].name.to_sym : nil
76
+ tie_arg = xml_doc.elements["tied"] ? xml_doc.elements["tied"].attributes["type"].to_sym : nil
77
+ tie = tie_arg ? MusicalScore::Note::Notation::Tie.new(tie_arg) : nil
78
+ tuplet_arg = xml_doc.elements["tuplet"] ? xml_doc.elements["tuplet"].attributes["type"].to_sym : nil
79
+ tuplet = tuplet_arg ? MusicalScore::Note::Notation::Tuplet.new(tuplet_arg) : nil
80
+ return MusicalScore::Note::Notation::Notation.new(articulation: articulation, dynamics: dynamics, tie: tie, tuplet: tuplet)
81
+ end
82
+
83
+ def export_xml
84
+ notations_element = REXML::Element.new('notations')
85
+ articulation_element = REXML::Element.new('articulations')
86
+ dynamics_element = REXML::Element.new('dynamics')
87
+
88
+ if (@articulation)
89
+ articulation_element.add_element(REXML::Element.new(@articulation.to_s))
90
+ notations_element.add_element(articulation_element)
91
+ end
92
+ if (@dynamics)
93
+ dynamics_element.add_element(REXML::Element.new(@dynamics.to_s))
94
+ notations_element.add_element(dynamics_element)
95
+ end
96
+
97
+ notations_element.add_element(@tie.export_xml) if @tie
98
+ notations_element.add_element(@tuplet.export_xml) if @tuplet
99
+
100
+ return notations_element
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,22 @@
1
+ require 'contracts'
2
+ module MusicalScore
3
+ module Note
4
+ module Notation
5
+ class Tie < MusicalScore::ElementBase
6
+ include Contracts
7
+ attr_accessor :type
8
+ Contract Enum[*TYPE_START_STOP_CONTINUE] => Any
9
+ def initialize(type)
10
+ @type = type.to_sym
11
+ end
12
+
13
+ def export_xml
14
+ tie_element = REXML::Element.new('tied')
15
+ tie_element.add_attribute('type',@type.to_s)
16
+
17
+ return tie_element
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ require 'contracts'
2
+ module MusicalScore
3
+ module Note
4
+ module Notation
5
+ class Tuplet < MusicalScore::ElementBase
6
+ include Contracts
7
+ attr_accessor :type
8
+ Contract Enum[*TYPE_START_STOP] => Any
9
+ def initialize(type)
10
+ @type = type
11
+ end
12
+
13
+ def export_xml
14
+ tuplet_element = REXML::Element.new('tuplet')
15
+ tuplet_element.add_attribute('type', @type.to_s)
16
+
17
+ return tuplet_element
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end