ruby-mext 0.21.0 → 0.21.5

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
  SHA256:
3
- metadata.gz: 641330c8c2cd9031e4ef3be9947cff95bae6b687c3b1466843a3df066d53243f
4
- data.tar.gz: 04e7e322a7dcbfc7da30fd8a5858aa52dda0c8201a83142118b58f65ba8780b0
3
+ metadata.gz: f2f4f4f25273a53383d136a74e37bc86532da799ffcda4a158216d87e68e4eaf
4
+ data.tar.gz: 006b52ca22341b85fe7f3e6bfb2a53d8242880fb66e5e577ead3a1fe437e29f3
5
5
  SHA512:
6
- metadata.gz: 63b6f8e53ede313224d7ed45c65157b73bad7e5ced9436beac0efa9eb92f81331ac0e731dac6f3535917609014e670e1c3cd6a9aeabe66324566efc395d2a613
7
- data.tar.gz: 107500aec8b7c15107192e71b0e23c780432c88d8f2964b1124ce293dbc10438213174ea4fd716804be1de39ae197c0ff265aee225013995c9344af45ddf9ccc
6
+ metadata.gz: c0aede90dcd997f577eea7f96280c38e1bd815983f746a026f22b0ebe3e7a8a588b762c37a3e76345b7d8159181bf5c11af08e5f8bcf844d362f10a9b3172db3
7
+ data.tar.gz: 56c1b1346179f2daee885cfb0d1e68c929d79b42a5260a618892a226348b9cc3b36fbc1e83f2612e97cec6b87a6166df4db654980a32c6edc6089cc6b0c807c9
data/README.md CHANGED
@@ -10,6 +10,11 @@ These are extensions of usual idiomatic `ruby` objects (i.e. `Numeric`,
10
10
  It is the usual unfinished ever-expanding project. Feel free to contribute to
11
11
  it in the form of pull requests if you are so inclined.
12
12
 
13
+ ## Requirements
14
+
15
+ Some `Function`s (notably: `Mext::Math::Polynomial`) require [`octave`](https://octave.org) to be
16
+ installed and running on the same computer.
17
+
13
18
  ## Installation
14
19
 
15
20
  Add this line to your application's Gemfile:
data/lib/mext/math.rb CHANGED
@@ -10,4 +10,5 @@ end
10
10
  expon
11
11
  log
12
12
  stepwise
13
+ polynomial
13
14
  ).each { |f| require File.join(Mext::MATH_PATH, f) }
@@ -0,0 +1,95 @@
1
+ module Math
2
+
3
+ class Polynomial < Function
4
+
5
+ #
6
+ # +:values+
7
+ #
8
+ # is an array of pairs (+x+, +y+)
9
+ #
10
+ attr_reader :values, :factors
11
+
12
+ #
13
+ # +Math::Polynomial.new(values = [])+
14
+ #
15
+ # stepwise function
16
+ #
17
+ # Arguments are:
18
+ #
19
+ # +values+: an array of (+x+, +y+) pairs
20
+ #
21
+ #:nodoc:
22
+ def initialize(vs = [])
23
+ setup(vs)
24
+ end
25
+
26
+ #:doc:
27
+ #
28
+ # +y(x)+:
29
+ #
30
+ # Returns a value for any given x
31
+ #
32
+ #:nodoc:
33
+ def y(x)
34
+ calculate(x)
35
+ end
36
+
37
+ def label
38
+ 'polynomial function'
39
+ end
40
+
41
+ class << self
42
+
43
+ #
44
+ # +from_yaml(yaml_hash)+:
45
+ #
46
+ # creates a Math::Polynomial class from a yaml file which must have the
47
+ # relevant fields:
48
+ #
49
+ # +values+: an array of duples [x, y]
50
+ #
51
+ def from_yaml(yh)
52
+ new(yh['values'])
53
+ end
54
+
55
+ end
56
+
57
+ private
58
+
59
+ def calculate(x)
60
+ result = 0
61
+ idx = self.factors.size-1
62
+ fidx = 0
63
+ idx.downto(0) do
64
+ |n|
65
+ result += (self.factors[fidx]*(x**(n)))
66
+ fidx += 1
67
+ end
68
+ result
69
+ end
70
+
71
+ #
72
+ # +setup+
73
+ #
74
+ # calls +octave+'s +polyfit+ function to load the factors of the
75
+ # polynomial
76
+ #
77
+ #:nodoc:
78
+ def setup(vs)
79
+ @values = []
80
+ sorted = vs.sort { |a, b| a[0] <=> b[0] }
81
+ @values.concat(sorted)
82
+ xs = self.values.map { |v| v[0] }
83
+ ys = self.values.map { |v| v[1] }
84
+ porder = vs.size
85
+ @factors = ::Octave::Io::polyfit(xs, ys, porder)
86
+ #
87
+ # keep in mind that vs might be empty
88
+ #
89
+ @x_start = self.values.first ? self.values.first.first : self.values.first
90
+ @x_end = self.values.last ? self.values.last.first : self.values.last
91
+ end
92
+
93
+ end
94
+
95
+ end
data/lib/mext/music.rb CHANGED
@@ -6,4 +6,7 @@ end
6
6
 
7
7
  %w(
8
8
  pitch_class
9
+ meter
10
+ note_name
11
+ note_names
9
12
  ).each { |f| require File.join(Mext::MUSIC_PATH, f) }
@@ -0,0 +1,102 @@
1
+ module Mext
2
+ module Music
3
+
4
+ #
5
+ # +Meter+:
6
+ #
7
+ # in music we cannot use the +Rational+ class
8
+ # because the latter will make all due conversions
9
+ # simplifying meters (like: 4/4 => 1/1), which is not what we want
10
+ #
11
+ class Meter
12
+
13
+ attr_accessor :numerator, :divisor
14
+
15
+ alias_method :denominator, :divisor
16
+
17
+ def initialize(n, d)
18
+ self.numerator = n.to_f
19
+ self.divisor = d.to_f
20
+ end
21
+
22
+ def to_r
23
+ raise ZeroDivisionError, "#{self.numerator}/#{self.divisor} provokes a zero-division error" unless self.divisor != 0
24
+ Rational(self.numerator, self.divisor)
25
+ end
26
+
27
+ def to_s
28
+ "#{self.numerator}/#{self.divisor}"
29
+ end
30
+ #
31
+ # we use the logic of +Rational+ to perform logic on +Meter+
32
+ #
33
+ [:==, :<, :<=, :>, :>=, :<=>, :===].each { |m| define_method(m) { |other| common_logic(m, other) } }
34
+ #
35
+ # we use the arithmetic of +Rational+ to perform arithmetic on +Meter+
36
+ #
37
+ [:+, :-, :*, :/, :**, :^, ].each { |m| define_method(m) { |other| common_arithmetic(m, other) } }
38
+
39
+
40
+ private
41
+
42
+ def common_logic(method, other)
43
+ raise ArgumentError, "#{other} is neither a #{self.class} nor a Rational (#{other.class})" unless other.kind_of?(Meter) || other.kind_of?(Rational)
44
+ self.to_r.send(method, other.to_r)
45
+ end
46
+
47
+ def common_arithmetic(method, other)
48
+ self.to_r.send(method, other.to_r).to_meter
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+ end
55
+
56
+ #
57
+ # +Meter(n, d)+ is defined to actually mimick a +Rational()+
58
+ #
59
+ def Meter(n, d)
60
+ Mext::Music::Meter.new(n,d)
61
+ end
62
+
63
+ #
64
+ # we extend the +String+ class to carry a +to_meter+ which
65
+ # will convert a string into a meter
66
+ #
67
+ class String
68
+
69
+ def to_meter
70
+ div = num = 1.0
71
+ (nums, divs) = self.split(/\s*\/\s*/, 2)
72
+ num = nums.to_f
73
+ div = divs.to_f
74
+ Meter(num, div)
75
+ end
76
+
77
+ end
78
+
79
+ #
80
+ # we extend the +Rational+ class to carry a +to_meter+ which
81
+ # will convert a into a Meter class
82
+ #
83
+ class Rational
84
+
85
+ def to_meter
86
+ Meter(self.numerator, self.denominator)
87
+ end
88
+
89
+ end
90
+
91
+ #
92
+ # we extend the +Numeric+ class to carry a +to_meter+ which
93
+ # will convert a into a Meter class
94
+ #
95
+ class Numeric
96
+
97
+ def to_meter
98
+ r = self.to_r
99
+ Meter(r.numerator, r.denominator)
100
+ end
101
+
102
+ end
@@ -0,0 +1,43 @@
1
+ module Mext
2
+ module Music
3
+ #
4
+ # +Mext::Music::NoteName+
5
+ #
6
+ # produces all the conversions from a named note to +pitch frequency+,
7
+ # +pitch class+, +LilyPond notation+, etc.
8
+ #
9
+ # In these conversions we assume that 8 is the central octave.
10
+ #
11
+ class NotALilypondName < StandardError; end
12
+ class NoteName
13
+
14
+ DEFAULT_NOTE_NAME_OFFSET = 9 # central 'A' 440 is 8.09, isn't it?
15
+ LILY_NOTE_NAMES = %w( a b c d e f )
16
+
17
+ attr_reader :name, :index
18
+
19
+ def initialize(name, idx)
20
+ @name = name
21
+ @index = idx
22
+ end
23
+
24
+ def to_lily(octave = 8, other = '') # 8 is the central octave
25
+ nn = self.name.sub(/\A\s*(.).*$/, '\1').downcase
26
+ raise NotALilypondName, "\"#{self.name}\" does not contain a note" unless LILY_NOTE_NAMES.include?(nn)
27
+ sharps = (self.name.match?(/\A\s*\w#/) && self.name.sub(/\A\s*\w(#+).*$/, '\1').gsub(/#/, 'is')) || nil
28
+ flats = (self.name.match?(/\A\s*\w[Ff]/) && self.name.sub(/\A\s*\w([Ff]+).*$/, '\1').gsub(/[Ff]/, 'es')) || nil
29
+ accs = sharps ? sharps : (flats ? flats : '')
30
+ "#{nn}#{accs}#{octave}#{other}"
31
+ end
32
+
33
+ def to_pitch_class(octave = 8) # 8 is the central octave
34
+ octave + (self.index/100.0)
35
+ end
36
+
37
+ def to_frequency(octave = 8)
38
+ self.to_pitch_class(octave).pchcps
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,8 @@
1
+ module Mext
2
+ module Music
3
+
4
+ DEFAULT_ANGLO_NAMES = %w(A B C D E F G)
5
+ DEFAULT_ITA_NAMES = %w(Do Re Mi Fa Sol La Si)
6
+
7
+ end
8
+ end
data/lib/mext/numeric.rb CHANGED
@@ -24,7 +24,6 @@ module Mext
24
24
  NON_VECTORIZABLE_METHODS = %w(
25
25
  constants
26
26
  pitch_fork
27
- meter
28
27
  )
29
28
  ADDED_METHODS = NON_VECTORIZABLE_METHODS + VECTORIZABLE_METHODS
30
29
 
data/lib/octave.rb ADDED
@@ -0,0 +1,12 @@
1
+ module Mext
2
+ module Octave
3
+
4
+ PATH = File.expand_path(File.join('..', 'octave'), __FILE__)
5
+
6
+ end
7
+ end
8
+
9
+ %w(
10
+ io
11
+ polyfit
12
+ ).each { |f| require File.join(Mext::Octave::PATH, f) }
data/lib/octave/io.rb ADDED
@@ -0,0 +1,29 @@
1
+ module Octave
2
+
3
+ module Io
4
+
5
+ class << self
6
+
7
+ OCTAVE_EXE='/usr/bin/env octave -q'
8
+ #
9
+ # +Octave::Io.command(command_string)+
10
+ #
11
+ # send a command to Octave in a pipe and return the results.
12
+ # The results are returned as arrays of float values
13
+ #
14
+ def command(command_string)
15
+ res = []
16
+ decorated_command_string = "echo \"#{command_string}\" | #{OCTAVE_EXE}"
17
+ IO.popen(decorated_command_string) do
18
+ |octave_result|
19
+ out = octave_result.readlines.map { |l| l.chomp }
20
+ out.each { |line| res << line.to_f if line.match?(/\s*\d+/) }
21
+ end
22
+ res
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,22 @@
1
+ module Octave
2
+
3
+ module Io
4
+
5
+ class << self
6
+
7
+ #
8
+ # +Octave::Io.polyfit(x, y, n)+
9
+ #
10
+ # get the coefficients of an n-degree polynomial
11
+ # using +octave+'s +polyfit+ function
12
+ #
13
+ def polyfit(x, y, n)
14
+ command_string = "a = polyfit(#{x}, #{y}, #{n}); a'"
15
+ ::Octave::Io.command(command_string)
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+
22
+ end
data/lib/ruby-mext.rb CHANGED
@@ -8,5 +8,6 @@ end
8
8
 
9
9
  %w(
10
10
  ruby
11
+ octave
11
12
  mext
12
13
  ).each { |f| require File.join(Mext::ROOT, f) }
data/lib/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Mext
2
- VERSION = '0.21.0'
2
+ VERSION = '0.21.5'
3
3
  end
data/ruby-mext.gemspec CHANGED
@@ -7,11 +7,11 @@ Gem::Specification.new do |spec|
7
7
  spec.name = 'ruby-mext'
8
8
  spec.version = Mext::VERSION
9
9
  spec.authors = ['Nicola Bernardini']
10
- spec.email = ['nicb@sme-ccppd.org']
10
+ spec.email = ['nicola.bernardini.rome@gmail.com']
11
11
 
12
12
  spec.summary = %q{ruby-mext: musical extensions for the Ruby language.}
13
13
  spec.description = %q{ruby-mext: musical extensions for the Ruby language.}
14
- spec.homepage = 'https://github.com/nicb/ruby-mext'
14
+ spec.homepage = 'https://git.smerm.org/nicb/ruby-mext'
15
15
 
16
16
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
17
  # to allow pushing to a single host or delete this section to allow pushing to any host.
@@ -36,6 +36,6 @@ Gem::Specification.new do |spec|
36
36
  spec.add_development_dependency 'rspec'
37
37
  spec.add_development_dependency 'rdoc'
38
38
  spec.add_development_dependency 'byebug'
39
- spec.add_development_dependency 'gruff'
39
+ spec.add_development_dependency 'matplotlib'
40
40
 
41
41
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-mext
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.21.0
4
+ version: 0.21.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nicola Bernardini
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-01 00:00:00.000000000 Z
11
+ date: 2021-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: wavefile
@@ -109,7 +109,7 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
- name: gruff
112
+ name: matplotlib
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - ">="
@@ -124,7 +124,7 @@ dependencies:
124
124
  version: '0'
125
125
  description: 'ruby-mext: musical extensions for the Ruby language.'
126
126
  email:
127
- - nicb@sme-ccppd.org
127
+ - nicola.bernardini.rome@gmail.com
128
128
  executables: []
129
129
  extensions: []
130
130
  extra_rdoc_files: []
@@ -157,8 +157,12 @@ files:
157
157
  - lib/mext/math/function.rb
158
158
  - lib/mext/math/line.rb
159
159
  - lib/mext/math/log.rb
160
+ - lib/mext/math/polynomial.rb
160
161
  - lib/mext/math/stepwise.rb
161
162
  - lib/mext/music.rb
163
+ - lib/mext/music/meter.rb
164
+ - lib/mext/music/note_name.rb
165
+ - lib/mext/music/note_names.rb
162
166
  - lib/mext/music/pitch_class.rb
163
167
  - lib/mext/numeric.rb
164
168
  - lib/mext/numeric/addsemi.rb
@@ -169,7 +173,6 @@ files:
169
173
  - lib/mext/numeric/dbamp.rb
170
174
  - lib/mext/numeric/ftom.rb
171
175
  - lib/mext/numeric/gold.rb
172
- - lib/mext/numeric/meter.rb
173
176
  - lib/mext/numeric/mmtot.rb
174
177
  - lib/mext/numeric/mround.rb
175
178
  - lib/mext/numeric/mtof.rb
@@ -185,6 +188,9 @@ files:
185
188
  - lib/mext/sound.rb
186
189
  - lib/mext/sound/info.rb
187
190
  - lib/mext/utilities.rb
191
+ - lib/octave.rb
192
+ - lib/octave/io.rb
193
+ - lib/octave/polyfit.rb
188
194
  - lib/ruby-mext.rb
189
195
  - lib/ruby.rb
190
196
  - lib/ruby/extensions.rb
@@ -197,7 +203,7 @@ files:
197
203
  - lib/tasks/gruff/stepwise.rake
198
204
  - lib/version.rb
199
205
  - ruby-mext.gemspec
200
- homepage: https://github.com/nicb/ruby-mext
206
+ homepage: https://git.smerm.org/nicb/ruby-mext
201
207
  licenses: []
202
208
  metadata:
203
209
  allowed_push_host: https://rubygems.org
@@ -216,7 +222,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
216
222
  - !ruby/object:Gem::Version
217
223
  version: '0'
218
224
  requirements: []
219
- rubygems_version: 3.1.2
225
+ rubygems_version: 3.2.2
220
226
  signing_key:
221
227
  specification_version: 4
222
228
  summary: 'ruby-mext: musical extensions for the Ruby language.'
@@ -1,46 +0,0 @@
1
- module Mext
2
- module Numeric
3
-
4
- #
5
- # +Meter+:
6
- #
7
- # in music we cannot use the +Rational+ class
8
- # because the latter will make all due conversions
9
- # simplifying meters (like: 4/4 => 1/1), which is not what we want
10
- #
11
- class Meter
12
-
13
- attr_accessor :numerator, :divisor
14
-
15
- def initialize(n, d)
16
- self.numerator = n.to_f
17
- self.divisor = d.to_f
18
- end
19
-
20
- end
21
-
22
- end
23
- end
24
-
25
- #
26
- # +Meter(n, d)+ is defined to actually mimick a +Rational()+
27
- #
28
- def Meter(n, d)
29
- Mext::Numeric::Meter.new(n,d)
30
- end
31
-
32
- #
33
- # we extend the +String+ class to carry a +to_meter+ which
34
- # will convert a string into a meter
35
- #
36
- class String
37
-
38
- def to_meter
39
- div = num = 1.0
40
- (nums, divs) = self.split(/\s*\/\s*/, 2)
41
- num = nums.to_f
42
- div = divs.to_f
43
- Meter(num, div)
44
- end
45
-
46
- end