tonal-tools 6.2.1 → 7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8b2ee2750807000bca57c6e3624a3ad589afe4ce01bde9b1a62fc66a565e7ac6
4
- data.tar.gz: '0823e862608da5a193aacd5a582d9094694b36c794e652af39987580f14cf9d7'
3
+ metadata.gz: 6eb41e5ffdfb2d4e1d6e76f5f69a2c1ad9a81d658af99e8a338211a5deea5d09
4
+ data.tar.gz: b63cb8bd6081fecdbbd97a2b003d84c2ea6533f9c656a63ba856c9732d021810
5
5
  SHA512:
6
- metadata.gz: bfb21cebb86682d76e3f7bd9ac5aafa26307382d8788fad7176ca893aa9edc34074f7b89ae9e32ed4913f84285a388ecf8b3fd32f788d1cee8377fb05237c410
7
- data.tar.gz: ad25d51f865409821b6ae06d2efa74364fa324c13bc3a1e215ed79c289df143e3f117b4396fffd912fbce03642507bd72d3ce2a23dc14cfbb7e6976270415b72
6
+ metadata.gz: f33625446c980e2a0e92d37efefb6abda13a084f214900af13c2cad9a981e087ff971d3fd62f86e83ec574a1bc9f153f80a5b61001c36cc9fe8eb4fda6fb9336
7
+ data.tar.gz: 99f81e26f709629654cd54402635d2e504c7075426f5f5807add1a3226736951f37cd459e929fc6c96b101a7290e88a23914b296e5b987212def629455fd9886
@@ -1,4 +1,4 @@
1
1
  module Tonal
2
2
  TOOLS_PRODUCER = "mTonal"
3
- TOOLS_VERSION = "6.2.1"
3
+ TOOLS_VERSION = "7.1.0"
4
4
  end
@@ -39,18 +39,27 @@ class Numeric
39
39
  #
40
40
  def div_times(factor) = [self / factor, self * factor]
41
41
 
42
- # @return [Tonal::ReducedRatio] the octave reduced ratio of self
42
+ # @return [Tonal::Ratio] the octave reduced ratio of self
43
43
  # @example
44
- # (2**(1.0/12)).ratio => (4771397596969315/4503599627370496)
44
+ # (4/5r).to_ratio => 4/5
45
+ # @param reduced
46
+ # @param equave
45
47
  #
46
- def to_ratio(reduced: true, equave: 2/1r) = reduced ? Tonal::ReducedRatio.new(self, equave: equave) : Tonal::Ratio.new(self, equave: equave)
48
+ def to_ratio(reduced: false, equave: 2/1r) = reduced ? Tonal::ReducedRatio.new(self, equave: equave) : Tonal::Ratio.new(self, equave: equave)
47
49
  alias :ratio :to_ratio
48
50
 
51
+ # @return [Tonal::ReducedRatio]
52
+ # @example
53
+ # {4/5r}.to_reduced_ratio => 8/5
54
+ # @param equave
55
+ #
56
+ def to_reduced_ratio(equave: 2/1r) = to_ratio(reduced: true, equave: equave)
57
+
49
58
  # @return [Float], the degrees on a circle of self
50
59
  # @example
51
60
  # (2**(6.0/12)).period_degrees => 180.0
52
61
  #
53
- def period_degrees = self.ratio.period_degrees
62
+ def period_degrees = ratio.period_degrees
54
63
 
55
64
  # @return [Tonal::Log] the log of self to the given base
56
65
  # @example
@@ -82,7 +91,7 @@ class Numeric
82
91
  # @example
83
92
  # (3/2r).to_cents => 701.96
84
93
  #
85
- def to_cents = self.log2.to_cents
94
+ def to_cents = Tonal::Cents.new(ratio: self)
86
95
 
87
96
  # @return [Tonal::Hertz] of self
88
97
  #
@@ -94,38 +103,41 @@ class Numeric
94
103
  # (5/4r).step(12) => 4\12
95
104
  # @param modulo
96
105
  #
97
- def step(modulo=12) = to_log2.step(modulo)
106
+ def step(modulo=12) = Tonal::Step.new(ratio: self, modulo: modulo)
98
107
 
99
108
  # @return [Float] the log product complexity of self
100
109
  # @example
101
110
  # (3/2r).benedetti_height => 6
102
111
  #
103
- def benedetti_height = self.ratio.benedetti_height
112
+ def benedetti_height = ratio.benedetti_height
104
113
  alias :product_complexity :benedetti_height
105
114
 
106
115
  # @return [Integer] the product complexity of self
107
116
  # @example
108
117
  # (3/2r).tenney_height => 2.58
109
118
  #
110
- def tenney_height = self.ratio.tenney_height
119
+ def tenney_height = ratio.tenney_height
111
120
  alias :log_product_complexity :tenney_height
112
121
 
113
122
  # @return [Integer] the Weil height
114
123
  # @example
115
124
  # (3/2r).weil_height => 3
116
125
  #
117
- def weil_height = self.ratio.weil_height
126
+ def weil_height = ratio.weil_height
118
127
 
119
128
  # @return [Tonal::Log2] the log of Weil height
120
129
  # @example
121
130
  # (3/2r).log_weil_height => 1.58
122
131
  #
123
- def log_weil_height = self.ratio.log_weil_height
132
+ def log_weil_height = ratio.log_weil_height
124
133
 
125
134
  # @return [Integer] the Wilson height
126
135
  # @example (14/9r).wilson_height => 13
136
+ # @param reduced
137
+ # @param equave
138
+ # @param prime_rejects
127
139
  #
128
- def wilson_height(reduced: true, equave: 2/1r, prime_rejects: [2]) = self.ratio(reduced: reduced, equave: equave).wilson_height(prime_rejects: prime_rejects)
140
+ def wilson_height(reduced: false, equave: 2/1r, prime_rejects: [2]) = ratio(reduced: reduced, equave: equave).wilson_height(prime_rejects: prime_rejects)
129
141
 
130
142
  # @return [Float] the cents difference between self and its step in the given modulo
131
143
  # @example
@@ -133,21 +145,31 @@ class Numeric
133
145
  # @param modulo
134
146
  #
135
147
  # We want the efficiency from the ratio (self)
136
- def efficiency(modulo) = to_ratio.efficiency(modulo)
148
+ def efficiency(modulo, reduced: false) = ratio(reduced: reduced).efficiency(modulo)
137
149
 
138
150
  # @return [Tonal::Interval] beween self (upper) and ratio (lower)
139
151
  # @example
140
152
  # (133).interval_with(3/2r) => 133/96 (133/128 / 3/2)
141
- # @param ratio
153
+ # @param other_ratio
154
+ #
155
+ def interval_with(other_ratio) = Tonal::Interval.new(ratio, other_ratio)
156
+
157
+ # @return [Tonal::Cents] difference between ratio (upper) and self (lower)
158
+ # @example
159
+ # (133).cents_difference_with(3/2r)
160
+ # => 635.62
161
+ # @param other_ratio
142
162
  #
143
- def interval_with(ratio) = Tonal::Interval.new(self.ratio, ratio)
163
+ def cents_difference_with(other_ratio) = interval_with(other_ratio).to_cents
144
164
 
145
165
  # @return [Vector], self represented as a prime vector
146
166
  # @example
147
167
  # (3/2r).prime_vector => Vector[-1, 1]
168
+ # @param reduced
148
169
  #
149
- def prime_vector = self.ratio.prime_vector
170
+ def prime_vector(reduced: false) = ratio(reduced: reduced).prime_vector
150
171
  alias :monzo :prime_vector
172
+ alias :prime_exponent_vector :prime_vector
151
173
 
152
174
  # @return [Array], self decomposed into its prime factors
153
175
  # @example
@@ -174,18 +196,18 @@ class Numeric
174
196
  def to_vector = Vector[self.numerator, self.denominator]
175
197
  alias :vector :to_vector
176
198
 
177
- # @return [Tonal::ReducedRatio], the Ernst Levy negative of self
199
+ # @return [Tonal::Ratio], the Ernst Levy negative of self
178
200
  # @example
179
201
  # (7/4r).negative => (12/7)
180
202
  #
181
- def negative = self.ratio.negative
203
+ def negative = ratio.negative
182
204
 
183
- # @return [Tonal::ReducedRatio], the ratio rotated on the given axis, default 1/1
205
+ # @return [Tonal::Ratio], the ratio rotated on the given axis, default 1/1
184
206
  # @example
185
207
  # (3/2r).mirror => (4/3)
186
208
  # @param axis around which self is mirrored
187
209
  #
188
- def mirror(axis=1/1r) = self.ratio.mirror(axis)
210
+ def mirror(axis=1/1r) = ratio.mirror(axis)
189
211
 
190
212
  # @return [Integer] the floor of the log (to the given base) of self
191
213
  # @example
@@ -268,6 +290,30 @@ class Integer
268
290
  end
269
291
  end
270
292
  end
293
+
294
+ # @return [Array] of signature of self
295
+ # @example
296
+ # 5.signature
297
+ # => [1]
298
+ #
299
+ def prime_signature
300
+ raise ArgumentError, "applicable only to positive integers" if self <= 0
301
+
302
+ n = self
303
+ exponents = []
304
+ i = 2
305
+ while i * i <= n
306
+ count = 0
307
+ while n % i == 0
308
+ n /= i
309
+ count += 1
310
+ end
311
+ exponents << count if count > 0
312
+ i += 1
313
+ end
314
+ exponents << 1 if n > 1 # n is prime at this point
315
+ exponents.sort
316
+ end
271
317
  end
272
318
 
273
319
  class Array
@@ -344,13 +390,33 @@ class Array
344
390
  #
345
391
  def mean = self.sum / self.count.to_f
346
392
 
347
- # @return [Tonal::ReducedRatio] ratio reconstructed from the result of a prime factor decomposition
393
+ # @return [Tonal::Ratio] ratio reconstructed from the result of a prime factor decomposition
348
394
  # @example
349
395
  # [[[3, 1]], [[2, 1]]].ratio_from_prime_divisions => (3/2)
350
396
  # @param reduced [Boolean] if a reduced or unreduced ratio is returned
351
397
  #
352
398
  def ratio_from_prime_divisions(reduced: false) = reduced ? Tonal::ReducedRatio.new(Prime.int_from_prime_division(self.first), Prime.int_from_prime_division(self.last)) : Tonal::Ratio.new(Prime.int_from_prime_division(self.first), Prime.int_from_prime_division(self.last))
353
399
 
400
+ # @return [Array] with the EDO and its error best fitting the given ratios contained in self
401
+ # @example
402
+ # [3/2r].best_fitting_edo) => [53, 0.07]
403
+ # [7/4r, 3/2r].best_fitting_edo => [41, 3.46]
404
+ # @param min_edo [Integer] the mininum edo to search
405
+ # @param max_edo [Integer] the maximum edo to search
406
+ #
407
+ def best_fitting_edo(min_edo: 5, max_edo: 72)
408
+ (min_edo..max_edo).map do |edo|
409
+ step_size = 1200.0 / edo
410
+
411
+ total_error_for_edo = to_cents.map do |r_cents|
412
+ quantized = (r_cents / step_size).round * step_size
413
+ (r_cents - quantized).abs
414
+ end.sum
415
+
416
+ [edo, total_error_for_edo.round(2)]
417
+ end.min_by{|_, error| error}
418
+ end
419
+
354
420
  # @return [Array] translated by value
355
421
  # @example
356
422
  # [0.24184760813024642, 0.49344034900361244, 0.07231824070126536].translate(-0.07231824070126536) = [0.16952936742898106, 0.4211221083023471, 0.0]
@@ -403,7 +469,7 @@ class Vector
403
469
  # @param reduced
404
470
  # @param equave
405
471
  #
406
- def to_ratio(reduced: true, equave: 2/1r) = reduced ? Tonal::ReducedRatio.new(*self, equave: equave) : Tonal::Ratio.new(*self, equave: equave)
472
+ def to_ratio(reduced: false, equave: 2/1r) = reduced ? Tonal::ReducedRatio.new(*self, equave: equave) : Tonal::Ratio.new(*self, equave: equave)
407
473
  alias :ratio :to_ratio
408
474
  end
409
475
 
data/lib/tonal/ratio.rb CHANGED
@@ -336,7 +336,6 @@ class Tonal::Ratio
336
336
  # Tonal::Ratio.new(31/30r).prime_divisions => [[[31, 1]], [[2, 1], [3, 1], [5, 1]]]
337
337
  #
338
338
  def prime_divisions
339
- return [[[2, 1]], [[2, 1]]] if antecedent == 1
340
339
  [antecedent.prime_division, consequent.prime_division]
341
340
  end
342
341
 
@@ -346,7 +345,9 @@ class Tonal::Ratio
346
345
  #
347
346
  def prime_vector
348
347
  pds = prime_divisions
349
- max = [pds.first.max{|p| p.first}, pds.last.max{|p| p.first}].max.first
348
+ return nil if pds.all?(&:empty?)
349
+
350
+ max = [pds.first.max{|p| p.first}, pds.last.max{|p| p.first}].compact.max.first
350
351
 
351
352
  pds.last.collect!{|i| [i.first, -i.last]}
352
353
 
@@ -356,6 +357,7 @@ class Tonal::Ratio
356
357
  end.to_vector
357
358
  end
358
359
  alias :monzo :prime_vector
360
+ alias :prime_exponent_vector :prime_vector
359
361
 
360
362
  # @return [Integer] the maximum prime factor of self
361
363
  # @example
@@ -533,14 +535,25 @@ class Tonal::Ratio
533
535
  # @example
534
536
  # Tonal::ReducedRatio.new(133).interval_with(3/2r)
535
537
  # => (192/133) ((3/2) / (133/128))
536
- # @param antecedent
537
- # @param consequent
538
+ # @param upper ratio
539
+ # @param lower ratio
538
540
  #
539
- def interval_with(antecedent, consequent=nil)
540
- r = self.class.new(antecedent, consequent)
541
+ def interval_with(upper, lower=nil)
542
+ r = self.class.new(upper, lower)
541
543
  Tonal::Interval.new(self, r)
542
544
  end
543
545
 
546
+ # @return [Tonal::Cents] difference between ratio (upper) and self (lower)
547
+ # @example
548
+ # Tonal::ReducedRatio.new(133).cents_difference_with(3/2r)
549
+ # => 635.62
550
+ # @param upper ratio
551
+ # @param lower ratio
552
+ #
553
+ def cents_difference_with(upper, lower=nil)
554
+ interval_with(upper, lower).to_cents
555
+ end
556
+
544
557
  # @return [Integer] the difference between antecedent and consequent
545
558
  # @example
546
559
  # Tonal::ReducedRatio.new(3,2).difference => 1
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: 6.2.1
4
+ version: 7.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jose Hales-Garcia
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-01-16 00:00:00.000000000 Z
10
+ date: 2025-08-09 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: yaml
@@ -191,7 +191,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
191
191
  - !ruby/object:Gem::Version
192
192
  version: '3.1'
193
193
  requirements: []
194
- rubygems_version: 3.6.2
194
+ rubygems_version: 3.7.1
195
195
  specification_version: 4
196
196
  summary: Tonal tools
197
197
  test_files: []