ruby-mext 0.21.1 → 0.21.6
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 +4 -4
- data/README.md +5 -0
- data/lib/mext/math.rb +1 -0
- data/lib/mext/math/polynomial.rb +95 -0
- data/lib/mext/music.rb +3 -0
- data/lib/mext/{numeric → music}/meter.rb +38 -3
- data/lib/mext/music/note_name.rb +43 -0
- data/lib/mext/music/note_names.rb +8 -0
- data/lib/mext/numeric.rb +0 -1
- data/lib/octave.rb +12 -0
- data/lib/octave/io.rb +29 -0
- data/lib/octave/polyfit.rb +22 -0
- data/lib/ruby-mext.rb +1 -0
- data/lib/tasks/gruff/gruff.rake +1 -1
- data/lib/tasks/gruff/polynomial.rake +17 -0
- data/lib/version.rb +1 -1
- data/ruby-mext.gemspec +2 -2
- metadata +13 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7d543e0e00be11dea0f00c0ce6d9679d4a4c75763f4ff106eee0a262e1a0405c
|
4
|
+
data.tar.gz: 68ec005980d394bc22d3a17f3ab4c62706d02fc795a90b731b9393556ab338e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 69bd963b1dcfa608bf0735b2b7c568e949621c591146f495456866fec37688398ebc9a712905e5af8ac9c597e8408845f7ed96c50d1edc3e353522d286ccced9
|
7
|
+
data.tar.gz: 271c3f54fbcf26341fbf1ab681b0c04742eae5fd00fd2ec05b92118e990a85415ec7f1b90c3a13717f38132b9146d892671b2fa9400beb7064db7e9abc8d36f7
|
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
@@ -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
@@ -1,5 +1,5 @@
|
|
1
1
|
module Mext
|
2
|
-
module
|
2
|
+
module Music
|
3
3
|
|
4
4
|
#
|
5
5
|
# +Meter+:
|
@@ -20,6 +20,7 @@ module Mext
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def to_r
|
23
|
+
raise ZeroDivisionError, "#{self.numerator}/#{self.divisor} provokes a zero-division error" unless self.divisor != 0
|
23
24
|
Rational(self.numerator, self.divisor)
|
24
25
|
end
|
25
26
|
|
@@ -30,13 +31,22 @@ module Mext
|
|
30
31
|
# we use the logic of +Rational+ to perform logic on +Meter+
|
31
32
|
#
|
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
|
+
|
33
39
|
|
34
40
|
private
|
35
41
|
|
36
42
|
def common_logic(method, other)
|
37
|
-
raise ArgumentError unless other.kind_of?(Meter) || other.kind_of?(Rational)
|
43
|
+
raise ArgumentError, "#{other} is neither a #{self.class} nor a Rational (#{other.class})" unless other.kind_of?(Meter) || other.kind_of?(Rational)
|
38
44
|
self.to_r.send(method, other.to_r)
|
39
45
|
end
|
46
|
+
|
47
|
+
def common_arithmetic(method, other)
|
48
|
+
self.to_r.send(method, other.to_r).to_meter
|
49
|
+
end
|
40
50
|
|
41
51
|
end
|
42
52
|
|
@@ -47,7 +57,7 @@ end
|
|
47
57
|
# +Meter(n, d)+ is defined to actually mimick a +Rational()+
|
48
58
|
#
|
49
59
|
def Meter(n, d)
|
50
|
-
Mext::
|
60
|
+
Mext::Music::Meter.new(n,d)
|
51
61
|
end
|
52
62
|
|
53
63
|
#
|
@@ -65,3 +75,28 @@ class String
|
|
65
75
|
end
|
66
76
|
|
67
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
|
data/lib/mext/numeric.rb
CHANGED
data/lib/octave.rb
ADDED
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
data/lib/tasks/gruff/gruff.rake
CHANGED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require File.expand_path(File.join(['..'] * 4, 'spec', 'fixtures', 'polynomial_dataset'), __FILE__)
|
3
|
+
require File.expand_path(File.join('..', 'gruff_plot'), __FILE__)
|
4
|
+
|
5
|
+
namespace :plot do
|
6
|
+
|
7
|
+
desc 'plot the Polynomial dataset'
|
8
|
+
task :polynomial => :create_output_dir do
|
9
|
+
ranges = [ Range.new(0, 0) ]
|
10
|
+
p = Mext::Gruff::Plotter.new('polynomial_dataset', 800, Mext::Spec::Fixtures::Math::POLYNOMIAL_DATASET, ranges)
|
11
|
+
p.plot do
|
12
|
+
|args|
|
13
|
+
Math::Polynomial.new(args)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
data/lib/version.rb
CHANGED
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 = ['
|
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://
|
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.
|
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.
|
4
|
+
version: 0.21.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nicola Bernardini
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-05-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: wavefile
|
@@ -124,7 +124,7 @@ dependencies:
|
|
124
124
|
version: '0'
|
125
125
|
description: 'ruby-mext: musical extensions for the Ruby language.'
|
126
126
|
email:
|
127
|
-
-
|
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
|
@@ -194,10 +200,11 @@ files:
|
|
194
200
|
- lib/tasks/gruff/gruff.rake
|
195
201
|
- lib/tasks/gruff/gruff_plot.rb
|
196
202
|
- lib/tasks/gruff/line.rake
|
203
|
+
- lib/tasks/gruff/polynomial.rake
|
197
204
|
- lib/tasks/gruff/stepwise.rake
|
198
205
|
- lib/version.rb
|
199
206
|
- ruby-mext.gemspec
|
200
|
-
homepage: https://
|
207
|
+
homepage: https://git.smerm.org/nicb/ruby-mext
|
201
208
|
licenses: []
|
202
209
|
metadata:
|
203
210
|
allowed_push_host: https://rubygems.org
|
@@ -216,7 +223,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
216
223
|
- !ruby/object:Gem::Version
|
217
224
|
version: '0'
|
218
225
|
requirements: []
|
219
|
-
rubygems_version: 3.
|
226
|
+
rubygems_version: 3.2.2
|
220
227
|
signing_key:
|
221
228
|
specification_version: 4
|
222
229
|
summary: 'ruby-mext: musical extensions for the Ruby language.'
|