stringyfi 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,85 @@
1
+ require 'stringyfi'
2
+
3
+ # Requires supporting files with custom matchers and macros, etc,
4
+ # in ./support/ and its subdirectories.
5
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
6
+
7
+
8
+ # This file was generated by the `rspec --init` command. Conventionally, all
9
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
10
+ # The generated `.rspec` file contains `--require spec_helper` which will cause this
11
+ # file to always be loaded, without a need to explicitly require it in any files.
12
+ #
13
+ # Given that it is always loaded, you are encouraged to keep this file as
14
+ # light-weight as possible. Requiring heavyweight dependencies from this file
15
+ # will add to the boot time of your test suite on EVERY test run, even for an
16
+ # individual file that may not need all of that loaded. Instead, make a
17
+ # separate helper file that requires this one and then use it only in the specs
18
+ # that actually need it.
19
+ #
20
+ # The `.rspec` file also contains a few flags that are not defaults but that
21
+ # users commonly want.
22
+ #
23
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
24
+ RSpec.configure do |config|
25
+ # The settings below are suggested to provide a good initial experience
26
+ # with RSpec, but feel free to customize to your heart's content.
27
+ =begin
28
+ # These two settings work together to allow you to limit a spec run
29
+ # to individual examples or groups you care about by tagging them with
30
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
31
+ # get run.
32
+ config.filter_run :focus
33
+ config.run_all_when_everything_filtered = true
34
+
35
+ # Many RSpec users commonly either run the entire suite or an individual
36
+ # file, and it's useful to allow more verbose output when running an
37
+ # individual spec file.
38
+ if config.files_to_run.one?
39
+ # Use the documentation formatter for detailed output,
40
+ # unless a formatter has already been configured
41
+ # (e.g. via a command-line flag).
42
+ config.default_formatter = 'doc'
43
+ end
44
+
45
+ # Print the 10 slowest examples and example groups at the
46
+ # end of the spec run, to help surface which specs are running
47
+ # particularly slow.
48
+ config.profile_examples = 10
49
+
50
+ # Run specs in random order to surface order dependencies. If you find an
51
+ # order dependency and want to debug it, you can fix the order by providing
52
+ # the seed, which is printed after each run.
53
+ # --seed 1234
54
+ config.order = :random
55
+
56
+ # Seed global randomization in this process using the `--seed` CLI option.
57
+ # Setting this allows you to use `--seed` to deterministically reproduce
58
+ # test failures related to randomization by passing the same `--seed` value
59
+ # as the one that triggered the failure.
60
+ Kernel.srand config.seed
61
+
62
+ # rspec-expectations config goes here. You can use an alternate
63
+ # assertion/expectation library such as wrong or the stdlib/minitest
64
+ # assertions if you prefer.
65
+ config.expect_with :rspec do |expectations|
66
+ # Enable only the newer, non-monkey-patching expect syntax.
67
+ # For more details, see:
68
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
69
+ expectations.syntax = :expect
70
+ end
71
+
72
+ # rspec-mocks config goes here. You can use an alternate test double
73
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
74
+ config.mock_with :rspec do |mocks|
75
+ # Enable only the newer, non-monkey-patching expect syntax.
76
+ # For more details, see:
77
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
78
+ mocks.syntax = :expect
79
+
80
+ # Prevents you from mocking or stubbing a method that does not exist on
81
+ # a real object. This is generally recommended.
82
+ mocks.verify_partial_doubles = true
83
+ end
84
+ =end
85
+ end
@@ -0,0 +1,14 @@
1
+ module FixturesHelper
2
+
3
+ def xml_sample_path(name='chromatic.xml')
4
+ File.join(xml_samples_path, name)
5
+ end
6
+
7
+ def xml_samples_path
8
+ "#{File.dirname(__FILE__)}/../fixtures/music_xml"
9
+ end
10
+ end
11
+
12
+ RSpec.configure do |conf|
13
+ conf.include FixturesHelper
14
+ end
@@ -0,0 +1,166 @@
1
+ require 'spec_helper'
2
+
3
+ describe StringyFi::Converter do
4
+ subject(:converter) { described_class.new(filename) }
5
+
6
+ context "with chromatic.xml sample" do
7
+ let(:filename) { xml_sample_path }
8
+
9
+ describe "#xml_doc" do
10
+ subject { converter.xml_doc }
11
+ it "returns the XML doc" do
12
+ expect(subject).to be_a(Nokogiri::XML::Document)
13
+ end
14
+ end
15
+
16
+ describe "#identification" do
17
+ subject(:identification) { converter.identification }
18
+ it "returns expected metadata" do
19
+ expect(subject).to eql({
20
+ title: "chromatic scale",
21
+ encoding: {
22
+ date: "2017-10-15",
23
+ software: "Guitar Pro 7.0.6"
24
+ }
25
+ })
26
+ end
27
+ end
28
+
29
+ describe "#part_list" do
30
+ subject(:part_list) { converter.part_list }
31
+ it "returns an array of parts" do
32
+ expect(part_list).to eql([
33
+ {id: "P1"}
34
+ ])
35
+ end
36
+ end
37
+
38
+ describe "#parts" do
39
+ subject(:parts) { converter.parts }
40
+ it "returns parts corresponding to parts list" do
41
+ expect(parts.count).to eql(converter.part_list.count)
42
+ end
43
+ end
44
+
45
+ describe "#measures" do
46
+ subject(:measures) { converter.measures }
47
+ it "has the correct number of measures" do
48
+ expect(measures).to be_a(StringyFi::Measures)
49
+ expect(measures.count).to eql(7)
50
+ end
51
+ end
52
+
53
+ describe "#measure" do
54
+ subject(:measure) { converter.measure(measure_id) }
55
+ context "with first measure" do
56
+ let(:measure_id) { 0 }
57
+ it "has the correct note info" do
58
+ expect(measure).to be_an(Array)
59
+ expect(measure.size).to eql(1)
60
+ expect(measure[0]).to be_a(StringyFi::Note)
61
+ expect(measure[0].to_str).to eql("3:E:0")
62
+ end
63
+ end
64
+ end
65
+
66
+ describe "#score_preamble" do
67
+ subject { converter.score_preamble }
68
+ it "includes the expected identification" do
69
+ expect(subject).to include(';** Title: chromatic scale')
70
+ end
71
+ it "includes the tstart" do
72
+ expect(subject).to include("\ttstart DemoTune")
73
+ end
74
+ end
75
+
76
+ describe "#score_coda" do
77
+ subject { converter.score_coda }
78
+ it "includes a rest and tstop" do
79
+ expect(subject).to include("\ttrest 8")
80
+ expect(subject).to include("\ttstop")
81
+ end
82
+ end
83
+
84
+ describe "#score_body" do
85
+ subject { converter.score_body(1/32.0) }
86
+ it "returns the expected conversion" do
87
+ expect(subject).to eql([
88
+ "\t; measure 1",
89
+ "\ttoctave -1",
90
+ "\ttnote E1,4,0 ; 3:E:0",
91
+ "\t; measure 2",
92
+ "\ttnote F1,4,0 ; 3:F:0",
93
+ "\ttnote F1S,4,0 ; 3:F:1",
94
+ "\t; measure 3",
95
+ "\ttnote G1,4,0 ; 3:G:0",
96
+ "\ttnote A1,4,0 ; 3:A:0",
97
+ "\ttnote A1S,4,0 ; 3:A:1",
98
+ "\ttnote B1,4,0 ; 3:B:0",
99
+ "\t; measure 4",
100
+ "\ttoctave +1",
101
+ "\ttnote C1,4,0 ; 4:C:0",
102
+ "\ttnote C1S,4,0 ; 4:C:1",
103
+ "\ttnote D1,4,0 ; 4:D:0",
104
+ "\ttnote D1S,4,0 ; 4:D:1",
105
+ "\t; measure 5",
106
+ "\ttnote E1,3,0 ; 4:E:0",
107
+ "\ttnote F1,3,0 ; 4:F:0",
108
+ "\ttnote F1S,3,0 ; 4:F:1",
109
+ "\ttnote G1,3,0 ; 4:G:0",
110
+ "\ttnote G1S,3,0 ; 4:G:1",
111
+ "\ttnote A1,3,0 ; 4:A:0",
112
+ "\ttnote A1S,3,0 ; 4:A:1",
113
+ "\ttnote B1,3,0 ; 4:B:0",
114
+ "\t; measure 6",
115
+ "\ttoctave +1",
116
+ "\ttnote C1,2,0 ; 5:C:0",
117
+ "\ttnote C1S,2,0 ; 5:C:1",
118
+ "\ttnote D1,2,0 ; 5:D:0",
119
+ "\ttnote D1S,2,0 ; 5:D:1",
120
+ "\ttnote E1,2,0 ; 5:E:0",
121
+ "\ttnote F1,2,0 ; 5:F:0",
122
+ "\ttnote F1S,2,0 ; 5:F:1",
123
+ "\ttnote G1,2,0 ; 5:G:0",
124
+ "\ttnote G1S,2,0 ; 5:G:1",
125
+ "\ttnote A1,2,0 ; 5:A:0",
126
+ "\ttnote A1S,2,0 ; 5:A:1",
127
+ "\ttnote B1,2,0 ; 5:B:0",
128
+ "\ttoctave +1",
129
+ "\ttnote C1,2,0 ; 6:C:0",
130
+ "\ttnote C1S,2,0 ; 6:C:1",
131
+ "\ttnote D1,2,0 ; 6:D:0",
132
+ "\ttnote D1S,2,0 ; 6:D:1",
133
+ "\t; measure 7",
134
+ "\ttnote E1,1,0 ; 6:E:0",
135
+ "\ttnote F1,1,0 ; 6:F:0",
136
+ "\ttnote F1S,1,0 ; 6:F:1",
137
+ "\ttnote G1,1,0 ; 6:G:0",
138
+ "\ttnote G1S,1,0 ; 6:G:1",
139
+ "\ttnote A1,1,0 ; 6:A:0",
140
+ "\ttnote A1S,1,0 ; 6:A:1",
141
+ "\ttnote B1,1,0 ; 6:B:0",
142
+ "\ttoctave +1",
143
+ "\ttnote C1,1,0 ; 7:C:0",
144
+ "\ttnote C1S,1,0 ; 7:C:1",
145
+ "\ttnote D1,1,0 ; 7:D:0",
146
+ "\ttnote D1S,1,0 ; 7:D:1",
147
+ "\ttnote E1,4,0 ; 7:E:0",
148
+ "\ttnote E1,4,0 ; 7:E:0"
149
+ ])
150
+ end
151
+ end
152
+
153
+ describe "#convert!" do
154
+ subject { converter.convert! }
155
+ it "calls all the necessary bits" do
156
+ expect(converter).to receive(:score_preamble)
157
+ expect(converter).to receive(:score_body)
158
+ expect(converter).to receive(:score_coda)
159
+ expect($stderr).to receive(:puts).exactly(4).times
160
+ expect($stdout).to receive(:puts).exactly(3).times
161
+ subject
162
+ end
163
+ end
164
+
165
+ end
166
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe StringyFi::Measures do
4
+
5
+ let(:note_class) { StringyFi::Note }
6
+ let(:measures) { described_class.new }
7
+
8
+ before do
9
+ measures << [
10
+ note_class.new('G', 4, 1, 1, "half"),
11
+ note_class.new('G', 2, 0, 1, "quarter")
12
+ ]
13
+ measures << [
14
+ note_class.new('G', 3, 0, 1, "whole"),
15
+ note_class.new('A', 4, 0, 1, "32nd"),
16
+ note_class.new('G', 4, 1, 1, "eighth")
17
+ ]
18
+ end
19
+
20
+ describe "#shortest_fractional_duration" do
21
+ subject { measures.shortest_fractional_duration }
22
+ it "returns the shortest of any note" do
23
+ expect(subject).to eql(1/32.0)
24
+ end
25
+ end
26
+
27
+ describe "#octave_range" do
28
+ subject { measures.octave_range }
29
+ it "returns the lowest and highest as an array" do
30
+ expect(subject).to eql([2, 4])
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,106 @@
1
+ require 'spec_helper'
2
+
3
+ describe StringyFi::Note do
4
+
5
+ let(:name) { 'G' }
6
+ let(:octave) { '4' }
7
+ let(:alter) { nil }
8
+ let(:duration) { 1 }
9
+ let(:duration_type) { "quarter" }
10
+ subject(:note) { described_class.new(name, octave, alter, duration, duration_type) }
11
+
12
+
13
+ describe "#stringy_durations" do
14
+ subject { note.stringy_durations(shortest_fractional_duration)}
15
+ context "when quarter is shortest" do
16
+ let(:shortest_fractional_duration) { 1/4.0 }
17
+ [
18
+ {duration: 1, duration_type: "quarter", expect: [1, 0, 0, 0]},
19
+ {duration: 2, duration_type: "quarter", expect: [0, 1, 0, 0]},
20
+ {duration: 3, duration_type: "quarter", expect: [0, 1, 0, 0]},
21
+ {duration: 4, duration_type: "quarter", expect: [0, 0, 1, 0]},
22
+ {duration: 5, duration_type: "quarter", expect: [0, 0, 1, 0]},
23
+ {duration: 6, duration_type: "quarter", expect: [0, 0, 1, 0]},
24
+ {duration: 7, duration_type: "quarter", expect: [0, 0, 1, 0]},
25
+ {duration: 1, duration_type: "half", expect: [0, 1, 0, 0]},
26
+ {duration: 4, duration_type: "half", expect: [0, 0, 0, 1]},
27
+ {duration: 1, duration_type: "whole", expect: [0, 0, 1, 0]},
28
+ {duration: 2, duration_type: "whole", expect: [0, 0, 0, 1]},
29
+ ].each do |options|
30
+ context "and duration=#{options[:duration]}-#{options[:duration_type]}" do
31
+ let(:duration) { options[:duration] }
32
+ let(:duration_type) { options[:duration_type] }
33
+ it "returns expected repeats/remainder" do
34
+ expect(subject).to eql(options[:expect])
35
+ end
36
+ end
37
+ end
38
+ end
39
+ context "when 32nd is shortest" do
40
+ let(:shortest_fractional_duration) { 1/32.0 }
41
+ [
42
+ {duration: 1, duration_type: "32nd", expect: [1, 0, 0, 0]},
43
+ {duration: 1, duration_type: "quarter", expect: [0, 0, 0, 1]},
44
+ ].each do |options|
45
+ context "and duration=#{options[:duration]}-#{options[:duration_type]}" do
46
+ let(:duration) { options[:duration] }
47
+ let(:duration_type) { options[:duration_type] }
48
+ it "returns expected repeats/remainder" do
49
+ expect(subject).to eql(options[:expect])
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ describe "#to_s" do
57
+ subject { note.to_s }
58
+ context "when not altered" do
59
+ it { should eql('G')}
60
+ end
61
+ context "when altered up" do
62
+ let(:alter) { 1 }
63
+ it { should eql('G#')}
64
+ end
65
+ context "when altered down" do
66
+ let(:alter) { -1 }
67
+ it { should eql('Gb')}
68
+ end
69
+ end
70
+
71
+ describe "#to_str" do
72
+ subject { note.to_str }
73
+ context "when not altered" do
74
+ it { should eql('4:G:0')}
75
+ end
76
+ context "when altered up" do
77
+ let(:alter) { 1 }
78
+ it { should eql('4:G:1')}
79
+ end
80
+ context "when altered down" do
81
+ let(:alter) { -1 }
82
+ it { should eql('4:G:-1')}
83
+ end
84
+ end
85
+
86
+ describe "#<=>" do
87
+ let(:other_name) { name }
88
+ let(:other_octave) { octave }
89
+ let(:other_alter) { alter }
90
+ let(:other_note) { described_class.new(other_name, other_octave, other_alter) }
91
+
92
+ subject { note <=> other_note }
93
+ context "when same" do
94
+ it { should eql(0) }
95
+ end
96
+ context "when vary by alter up" do
97
+ let(:other_alter) { 1 }
98
+ it { should eql(-1) }
99
+ end
100
+ context "when vary by octave down" do
101
+ let(:other_octave) { 3 }
102
+ it { should eql(1) }
103
+ end
104
+ end
105
+
106
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe StringyFi::Shell do
4
+ subject(:shell) { described_class.new }
5
+
6
+ context "when no args passed" do
7
+ it "initialises with sample fixture" do
8
+ expect(subject).to be_a(described_class)
9
+ expect(subject.converter.filename).to include("chromatic.xml")
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe StringyFi do
4
+
5
+ it "has a version" do
6
+ expect(described_class::VERSION).to match(/\d+\.\d+\.\d+/)
7
+ end
8
+
9
+ end
data/stringyfi.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'stringyfi/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "stringyfi"
8
+ spec.version = StringyFi::VERSION
9
+ spec.authors = ["Paul Gallagher"]
10
+ spec.email = ["gallagher.paul@gmail.com"]
11
+ spec.summary = "Convert MusicXML to PIC assembler for the Boldport Stringy"
12
+ spec.homepage = "https://github.com/tardate/stringyfi"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_runtime_dependency "nokogiri", "~> 1.8"
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.6"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency "rspec"
25
+
26
+ # guard versions are pegged to avoid issue with ruby_dep requires Ruby version >= 2.2.5, ~> 2.2.
27
+ spec.add_development_dependency "guard-rspec", "4.6.4"
28
+ spec.add_development_dependency "rb-fsevent", "0.9.6"
29
+ spec.add_development_dependency "rb-inotify", "0.9.5"
30
+ spec.add_development_dependency "pry-coolline", "0.2.5" # avoid readline dependency
31
+ end