tonal-tools 7.2.0 → 7.4.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 +4 -4
- data/lib/tonal/attributions.rb +1 -1
- data/lib/tonal/extended_ratio.rb +131 -0
- data/lib/tonal/extensions.rb +50 -1
- data/lib/tonal/interval.rb +6 -1
- data/lib/tonal/irb_helpers.rb +4 -0
- data/lib/tonal/ratio.rb +7 -7
- data/lib/tonal/tools.rb +1 -0
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 769baf7d488e059f0f8072fdcf39dcf33567d14a631e6a55eece8c7c9ff864d1
|
|
4
|
+
data.tar.gz: 1e7ef0b95e95df63a2667ac04f2062ce0a62502ea577fd7c6d529e407ecc04da
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 88ec036a5b4f17503d905c25bacf74a22d6d0c00b4a792d6196c5faecfcdebae3836711deb363a65863df4334a61f35d0b974de0fa42816390c0548e538a0a1f
|
|
7
|
+
data.tar.gz: 8f004f602f9e8780a7c0a64dbb1132ba323f7032411b8bd5617b89973c14047f5f607ea3c67d30287c29922719c3a3c580759463242fba73a3feac8280cee104
|
data/lib/tonal/attributions.rb
CHANGED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
module Tonal::ExtendableRatio
|
|
2
|
+
extend Forwardable
|
|
3
|
+
include Comparable
|
|
4
|
+
def_delegators :@partials, :size, :length, :each, :map, :select, :first, :last, :[], :count
|
|
5
|
+
|
|
6
|
+
attr_reader :partials
|
|
7
|
+
|
|
8
|
+
# @return [Tonal::ExtendedRatio, Tonal::SubharmonicExtendedRatio]
|
|
9
|
+
# @example
|
|
10
|
+
# Tonal::ExtendedRatio.new(partials: [1, 2, 3])
|
|
11
|
+
# Tonal::SubharmonicExtendedRatio.new(ratios: [Rational(1,1), Rational(1,2), Rational(1,3)])
|
|
12
|
+
# @param partials [Array<Numeric>] the partials to initialize the extended ratio
|
|
13
|
+
# @param ratios [Array<Rational>] the ratios to initialize the extended ratio
|
|
14
|
+
#
|
|
15
|
+
def initialize(partials: nil, ratios: nil)
|
|
16
|
+
raise(ArgumentError, "Provide either partials or ratios, not both", caller[0]) if !partials.nil? && !ratios.nil?
|
|
17
|
+
raise(ArgumentError, "Provide either partials or ratios", caller[0]) if partials.nil? && ratios.nil?
|
|
18
|
+
|
|
19
|
+
if partials
|
|
20
|
+
during_initialize(*partials)
|
|
21
|
+
elsif ratios
|
|
22
|
+
first = ratios.first
|
|
23
|
+
partials = ratios.map{|r| r * first}
|
|
24
|
+
during_initialize(*partials)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @return [Tonal::Interval] the interval between two partials
|
|
29
|
+
# @example
|
|
30
|
+
# er = Tonal::ExtendedRatio.new(partials: [1, 2, 3, 4])
|
|
31
|
+
# er.interval_between(0, 2) => Tonal::Interval representing 3/2
|
|
32
|
+
#
|
|
33
|
+
def interval_between(index1, index2, reduced: true)
|
|
34
|
+
return nil if self[index1].nil? || self[index2].nil?
|
|
35
|
+
first = partials.first
|
|
36
|
+
r1 = reduced ? Tonal::ReducedRatio.new(self[index1], first) : Tonal::Ratio.new(self[index1], first)
|
|
37
|
+
r2 = reduced ? Tonal::ReducedRatio.new(self[index2], first) : Tonal::Ratio.new(self[index2], first)
|
|
38
|
+
Tonal::Interval.new(r1, r2, reduced:)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def inspect
|
|
42
|
+
display.join(":")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
def switch_domain(domain)
|
|
47
|
+
case domain
|
|
48
|
+
when :harmonic
|
|
49
|
+
Tonal::ExtendedRatio
|
|
50
|
+
when :subharmonic
|
|
51
|
+
Tonal::SubharmonicExtendedRatio
|
|
52
|
+
else
|
|
53
|
+
raise(ArgumentError, "Unknown domain: #{domain}", caller[0])
|
|
54
|
+
end.new(ratios: ratios.map(&:to_r))
|
|
55
|
+
end
|
|
56
|
+
alias :switch_to :switch_domain
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
class Tonal::ExtendedRatio
|
|
60
|
+
include Tonal::ExtendableRatio
|
|
61
|
+
|
|
62
|
+
# @return [Array<Tonal::Ratio, Tonal::ReducedRatio>] the ratios of the extended ratio
|
|
63
|
+
# @example
|
|
64
|
+
# er = Tonal::ExtendedRatio.new(partials: [4, 5, 6])
|
|
65
|
+
# er.ratios => [1/1, 5/4, 3/2]
|
|
66
|
+
# @param reduced [Boolean] whether to return reduced ratios or not
|
|
67
|
+
#
|
|
68
|
+
def ratios(reduced: true)
|
|
69
|
+
first = partials.first
|
|
70
|
+
partials.map do |n|
|
|
71
|
+
reduced ? Tonal::ReducedRatio.new(n, first) : Tonal::Ratio.new(n, first)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @return [Tonal::SubharmonicExtendedRatio] the subharmonic extended ratio
|
|
76
|
+
# @example
|
|
77
|
+
# er = Tonal::ExtendedRatio.new(partials: [4, 5, 6])
|
|
78
|
+
# er.to_subharmonic_extended_ratio => Tonal::SubharmonicExtendedRatio with partials [(1/15), (1/12), (1/10)]
|
|
79
|
+
#
|
|
80
|
+
def to_subharmonic_extended_ratio
|
|
81
|
+
switch_to(:subharmonic)
|
|
82
|
+
end
|
|
83
|
+
alias :to_sefr :to_subharmonic_extended_ratio
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
def during_initialize(*args)
|
|
87
|
+
lcm = args.denominators.lcm
|
|
88
|
+
@partials = Array.new(args.map{|n| n * lcm}.numerators).sort
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def display
|
|
92
|
+
@display ||= partials.map{|r| r.round(2)}
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
class Tonal::SubharmonicExtendedRatio
|
|
97
|
+
include Tonal::ExtendableRatio
|
|
98
|
+
|
|
99
|
+
# @return [Array<Tonal::Ratio, Tonal::ReducedRatio>] the ratios of the subharmonic extended ratio
|
|
100
|
+
# @example
|
|
101
|
+
# ser = Tonal::SubharmonicExtendedRatio.new(partials: [6,5,4])
|
|
102
|
+
# ser.ratios => [1/1, 6/5, 3/2]
|
|
103
|
+
# @param reduced [Boolean] whether to return reduced ratios or not
|
|
104
|
+
#
|
|
105
|
+
def ratios(reduced: true)
|
|
106
|
+
first = partials.first
|
|
107
|
+
partials.map do |n|
|
|
108
|
+
reduced ? Tonal::ReducedRatio.new(n, first) : Tonal::Ratio.new(n, first)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# @return [Tonal::ExtendedRatio] the harmonic extended ratio
|
|
113
|
+
# @example
|
|
114
|
+
# ser = Tonal::SubharmonicExtendedRatio.new(partials: [6,5,4])
|
|
115
|
+
# ser.to_extended_ratio => Tonal::ExtendedRatio with partials [10, 12, 15]
|
|
116
|
+
#
|
|
117
|
+
def to_extended_ratio
|
|
118
|
+
switch_to(:harmonic)
|
|
119
|
+
end
|
|
120
|
+
alias :to_efr :to_extended_ratio
|
|
121
|
+
|
|
122
|
+
private
|
|
123
|
+
def during_initialize(*args)
|
|
124
|
+
lcm = args.numerators.lcm
|
|
125
|
+
@partials = args.map{|n| n.kind_of?(Rational) ? n / lcm : n.reciprocal}.sort
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def display
|
|
129
|
+
@display ||= partials.map(&:reciprocal).map{|r| (r % 1).zero? ? r.to_i : r}.map{|r| r.round(2)}
|
|
130
|
+
end
|
|
131
|
+
end
|
data/lib/tonal/extensions.rb
CHANGED
|
@@ -154,6 +154,12 @@ class Numeric
|
|
|
154
154
|
#
|
|
155
155
|
def interval_with(other_ratio) = Tonal::Interval.new(ratio, other_ratio)
|
|
156
156
|
|
|
157
|
+
# @return [Tonal::Interval] between 1/1 (lower) and self (upper)
|
|
158
|
+
# @example
|
|
159
|
+
# (3/2r).to_interval => 3/2 (3/2 / 1/1)
|
|
160
|
+
#
|
|
161
|
+
def to_interval = Tonal::Interval.new(self)
|
|
162
|
+
|
|
157
163
|
# @return [Tonal::Cents] difference between ratio (upper) and self (lower)
|
|
158
164
|
# @example
|
|
159
165
|
# (133).cents_difference_with(3/2r)
|
|
@@ -215,6 +221,12 @@ class Numeric
|
|
|
215
221
|
# @param base of the log
|
|
216
222
|
#
|
|
217
223
|
def log_floor(base=10) = Math.log(self, base).floor
|
|
224
|
+
|
|
225
|
+
# @return [Rational] the reciprocal of self
|
|
226
|
+
# @example
|
|
227
|
+
# (3/2r).reciprocal => (2/3)
|
|
228
|
+
#
|
|
229
|
+
def reciprocal = Rational(1,self)
|
|
218
230
|
end
|
|
219
231
|
|
|
220
232
|
class Integer
|
|
@@ -392,6 +404,16 @@ class Array
|
|
|
392
404
|
def to_cents = self.map{|r| r.to_cents}
|
|
393
405
|
alias :cents :to_cents
|
|
394
406
|
|
|
407
|
+
# @return [Tonal::Interval]
|
|
408
|
+
# @example
|
|
409
|
+
# [3/2r, 4/3r].to_interval => 16/9 (4/3 / 3/2)
|
|
410
|
+
# @example
|
|
411
|
+
# [5].to_interval => 5/4 (5/4 / 1/1)
|
|
412
|
+
# @example
|
|
413
|
+
# [2,3,3,4].to_interval => 9/8 (3/2 / 4/3)
|
|
414
|
+
#
|
|
415
|
+
def to_interval = Tonal::Interval.new(*self)
|
|
416
|
+
|
|
395
417
|
# @return [Float] the mean of the elements of self
|
|
396
418
|
# @example
|
|
397
419
|
# [1, 2].mean => 1.5
|
|
@@ -468,6 +490,34 @@ class Array
|
|
|
468
490
|
# [4,3].to_r => (4/3)
|
|
469
491
|
#
|
|
470
492
|
def to_r = Rational(numerator, denominator)
|
|
493
|
+
|
|
494
|
+
# @return [Tonal::ExtendedRatio]
|
|
495
|
+
# @example
|
|
496
|
+
# [4/1r, 5/1r, 6/1r].to_efr => ExtendedRatio "4:5:6"
|
|
497
|
+
# @param as :ratios or :partials
|
|
498
|
+
#
|
|
499
|
+
def to_efr(as: :ratios) = as == :ratios ? Tonal::ExtendedRatio.new(ratios: self) : Tonal::ExtendedRatio.new(partials: self)
|
|
500
|
+
|
|
501
|
+
# @return [Tonal::SubharmonicExtendedRatio]
|
|
502
|
+
# @example
|
|
503
|
+
# [4,5,6].to_sefr => SubharmonicExtendedRatio "4:5:6"
|
|
504
|
+
# @param as :ratios or :partials
|
|
505
|
+
#
|
|
506
|
+
def to_sefr(as: :ratios) = as == :ratios ? Tonal::SubharmonicExtendedRatio.new(ratios: self) : Tonal::SubharmonicExtendedRatio.new(partials: self)
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
class Range
|
|
510
|
+
# @return [Tonal::ExtendedRatio]
|
|
511
|
+
# @example
|
|
512
|
+
# (4..7).to_efr => 4:5:6:7
|
|
513
|
+
#
|
|
514
|
+
def to_efr = Tonal::ExtendedRatio.new(partials: self)
|
|
515
|
+
|
|
516
|
+
# @return [Tonal::SubharmonicExtendedRatio]
|
|
517
|
+
# @example
|
|
518
|
+
# (4..7).to_sefr => 7:6:5:4
|
|
519
|
+
#
|
|
520
|
+
def to_sefr = Tonal::SubharmonicExtendedRatio.new(partials: self)
|
|
471
521
|
end
|
|
472
522
|
|
|
473
523
|
class Vector
|
|
@@ -491,4 +541,3 @@ module Math
|
|
|
491
541
|
|
|
492
542
|
PHI = (1 + 5**(1.0/2))/2
|
|
493
543
|
end
|
|
494
|
-
|
data/lib/tonal/interval.rb
CHANGED
|
@@ -11,10 +11,15 @@ class Tonal::Interval
|
|
|
11
11
|
# @return [Tonal::Interval] the interval of the given ratios
|
|
12
12
|
# @example
|
|
13
13
|
# Tonal::Interval.new(2,3) => (3/2) ((3/2) / (1/1))
|
|
14
|
+
# @example
|
|
15
|
+
# Tonal::Interval.new(2,3,3,4) => (9/8) ((3/2) / (4/3))
|
|
16
|
+
# @example
|
|
17
|
+
# Tonal::Interval.new(3) => (3/1) ((3/1) / (1/1))
|
|
14
18
|
# @param args two arguments representing ratios or four arguments representing two pairs of numerator/denominator
|
|
15
19
|
# @param reduced boolean determining whether to use Tonal::ReducedRatio or Tonal::Ratio
|
|
16
20
|
#
|
|
17
21
|
def initialize(*args, reduced: true)
|
|
22
|
+
args = [1/1r, args[0]] if args.length == 1
|
|
18
23
|
klass = reduced ? Tonal::ReducedRatio : Tonal::Ratio
|
|
19
24
|
raise(ArgumentError, "Two or four arguments required. Either two ratios, or two pairs of numerator, denominator", caller[0]) unless [2, 4].include?(args.size)
|
|
20
25
|
@lower_ratio, @upper_ratio = case args.size
|
|
@@ -42,7 +47,7 @@ class Tonal::Interval
|
|
|
42
47
|
end
|
|
43
48
|
|
|
44
49
|
def inspect
|
|
45
|
-
"#{interval} (#{upper} / #{lower})"
|
|
50
|
+
"#{interval.label} (#{upper.label} / #{lower.label})"
|
|
46
51
|
end
|
|
47
52
|
|
|
48
53
|
def <=>(rhs)
|
data/lib/tonal/irb_helpers.rb
CHANGED
|
@@ -23,6 +23,10 @@ module Tonal
|
|
|
23
23
|
# @return [Tonal::Interval] the interval between the given args
|
|
24
24
|
# @example
|
|
25
25
|
# i(2,3) => (3/2) ((3/2) / (1/1))
|
|
26
|
+
# @example
|
|
27
|
+
# i(2,3,3,4) => (9/8) ((3/2) / (4/3))
|
|
28
|
+
# @example
|
|
29
|
+
# i(3) => (3/1) ((3/1) / (1/1))
|
|
26
30
|
# @param args two arguments representing ratios or four arguments representing two pairs of numerator/denominator
|
|
27
31
|
# @param reduced boolean determining whether to use Tonal::ReducedRatio or Tonal::Ratio
|
|
28
32
|
#
|
data/lib/tonal/ratio.rb
CHANGED
|
@@ -233,7 +233,7 @@ class Tonal::Ratio
|
|
|
233
233
|
# @return [Tonal::ReducedRatio] of self
|
|
234
234
|
# @example
|
|
235
235
|
# Tonal::Ratio.new(1,9).to_reduced_ratio => (16/9)
|
|
236
|
-
#
|
|
236
|
+
#
|
|
237
237
|
def to_reduced_ratio
|
|
238
238
|
Tonal::ReducedRatio.new(reduced_antecedent, reduced_consequent, equave: equave)
|
|
239
239
|
end
|
|
@@ -247,6 +247,7 @@ class Tonal::Ratio
|
|
|
247
247
|
self.class.new(consequent, antecedent)
|
|
248
248
|
end
|
|
249
249
|
alias :reflect :invert
|
|
250
|
+
alias :reciprocal :invert
|
|
250
251
|
|
|
251
252
|
# @return [Tonal::Ratio] with antecedent and precedent switched
|
|
252
253
|
# @example
|
|
@@ -472,10 +473,8 @@ class Tonal::Ratio
|
|
|
472
473
|
# @return [String] symbolic representation of Tonal::Ratio
|
|
473
474
|
#
|
|
474
475
|
def label
|
|
475
|
-
# Return label, if defined; or
|
|
476
|
-
|
|
477
|
-
# Return the floating point representation rounded to 2 digits of precision
|
|
478
|
-
(@label || ((Math.log10(antecedent).to_i + 1) <= 6 ? "#{antecedent}/#{consequent}" : to_f.round(PRECISION))).to_s
|
|
476
|
+
# Return label, if defined; or see inspect
|
|
477
|
+
@label || inspect
|
|
479
478
|
end
|
|
480
479
|
|
|
481
480
|
# @return [String] the string representation of Tonal::Ratio
|
|
@@ -483,7 +482,9 @@ class Tonal::Ratio
|
|
|
483
482
|
# Tonal::Ratio.new(3, 2).inspect => "(3/2)"
|
|
484
483
|
#
|
|
485
484
|
def inspect
|
|
486
|
-
"
|
|
485
|
+
# Return the "antecedent/consequent", if antecedent is less than 7 digits long; or
|
|
486
|
+
# Return the floating point representation rounded to PRECISION digits
|
|
487
|
+
((Math.log10(antecedent).to_i + 1) <= 6 ? "#{antecedent}/#{consequent}" : "#{to_f.round(PRECISION)}")
|
|
487
488
|
end
|
|
488
489
|
alias :to_s :inspect
|
|
489
490
|
|
|
@@ -642,4 +643,3 @@ module Ratio
|
|
|
642
643
|
Tonal::Ratio.new(u, l)
|
|
643
644
|
end
|
|
644
645
|
end
|
|
645
|
-
|
data/lib/tonal/tools.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tonal-tools
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 7.
|
|
4
|
+
version: 7.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jose Hales-Garcia
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2025-
|
|
10
|
+
date: 2025-12-13 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: yaml
|
|
@@ -161,6 +161,7 @@ files:
|
|
|
161
161
|
- lib/tonal/attributions.rb
|
|
162
162
|
- lib/tonal/cents.rb
|
|
163
163
|
- lib/tonal/comma.rb
|
|
164
|
+
- lib/tonal/extended_ratio.rb
|
|
164
165
|
- lib/tonal/extensions.rb
|
|
165
166
|
- lib/tonal/hertz.rb
|
|
166
167
|
- lib/tonal/interval.rb
|
|
@@ -191,7 +192,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
191
192
|
- !ruby/object:Gem::Version
|
|
192
193
|
version: '3.1'
|
|
193
194
|
requirements: []
|
|
194
|
-
rubygems_version: 3.
|
|
195
|
+
rubygems_version: 3.7.2
|
|
195
196
|
specification_version: 4
|
|
196
197
|
summary: Tonal tools
|
|
197
198
|
test_files: []
|