tonal-tools 5.3.0 → 6.1.1

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: cf75078baac2ea863202aca76b6c00ab636971096fc676982dcb0114376bca8d
4
- data.tar.gz: 85b5550ccf29103f87416eea33f8a01c865730eecb4396e482e659ead1e5bf9e
3
+ metadata.gz: 50cbd332f2e7aeb3da776868cd3bd27cd3fd4f25146de194781fa1f8032a3e93
4
+ data.tar.gz: 3cddb6f9f59e814afface6e739597653442b13f402bf5839784decff633b4662
5
5
  SHA512:
6
- metadata.gz: aac11d2e96bda93047dea33f9e79be30f6db228b2fe4c01813544498740e21d82959a3678d0502ec9ee54e055fd96093d9135b244e37e9847de001ccb5a6a7ed
7
- data.tar.gz: 1d71ebff05b9d1e83b0dc90f4968c4ffbc9ebc51f0d931e0627588a2ad4262fcf1c63413a519450367b46eafa9f48b65cb2d38373a43fc23be49b4bbfbb5447d
6
+ metadata.gz: 425f07aa55d053031df5bb092495ab88a82741964b3f56295868873b4a96e773ca0af9005352bdb289331d090331c8cb75c05aada7573db858b453002153a1fc
7
+ data.tar.gz: bb7871f1f9dcdcef36de328f9f839d50c9e9ceba6918b0d6bbd335a85803760461fb4a37a203149a3f7e629d53fcb3f80c56b1bd97d6249a781a0c433fd1ef23
@@ -35,7 +35,7 @@ class Tonal::Ratio
35
35
  Set.new(ratio: ratio) do |ratios|
36
36
  ContinuedFraction.new(antecedent.to_f/consequent, conv_limit).convergents.each do |convergent|
37
37
  ratio2 = ratio.class.new(convergent.numerator,convergent.denominator)
38
- ratios << ratio2 if ratio.class.within_cents?(self_in_cents, ratio2.to_cents, within) && ratio2.within_prime?(max_prime)
38
+ ratios << ratio2 if ratio.class.within_cents?(self_in_cents, ratio2.to_cents, within) && ratio2.max_prime_within?(max_prime)
39
39
  break if ratios.length >= depth
40
40
  end
41
41
  end
@@ -55,7 +55,7 @@ class Tonal::Ratio
55
55
  Set.new(ratio: ratio) do |ratios|
56
56
  FractionTree.node(to_f).path.each do |node|
57
57
  ratio2 = ratio.class.new(node.number)
58
- ratios << ratio2 if ratio.class.within_cents?(self_in_cents, ratio2.to_cents, within) && ratio2.within_prime?(max_prime)
58
+ ratios << ratio2 if ratio.class.within_cents?(self_in_cents, ratio2.to_cents, within) && ratio2.max_prime_within?(max_prime)
59
59
  break if ratios.length >= depth
60
60
  end
61
61
  end
@@ -77,7 +77,7 @@ class Tonal::Ratio
77
77
  n = 1
78
78
  while true do
79
79
  ratio2 = ratio.class.superparticular(n, factor: ratio.to_r, superpart:)
80
- ratios << ratio2 if ratio.class.within_cents?(self_in_cents, ratio2.to_cents, within) && ratio2.within_prime?(max_prime) && ratio2 != ratio
80
+ ratios << ratio2 if ratio.class.within_cents?(self_in_cents, ratio2.to_cents, within) && ratio2.max_prime_within?(max_prime) && ratio2 != ratio
81
81
  break if ratios.length >= depth
82
82
  n += 1
83
83
  end
@@ -105,7 +105,7 @@ class Tonal::Ratio
105
105
  while boundary <= max_boundary
106
106
  vacinity = ratio.respond_to?(:to_basic_ratio) ? to_basic_ratio.scale(scale) : ratio.scale(scale)
107
107
  self.class.neighbors(away: boundary, vacinity: vacinity).each do |neighbor|
108
- ratios << neighbor if ratio.class.within_cents?(self_in_cents, neighbor.to_cents, within) && neighbor.within_prime?(max_prime) && neighbor != ratio
108
+ ratios << neighbor if ratio.class.within_cents?(self_in_cents, neighbor.to_cents, within) && neighbor.max_prime_within?(max_prime) && neighbor != ratio
109
109
  end
110
110
  boundary += 1
111
111
  end
@@ -1,4 +1,4 @@
1
1
  module Tonal
2
2
  TOOLS_PRODUCER = "mTonal"
3
- TOOLS_VERSION = "5.3.0"
3
+ TOOLS_VERSION = "6.1.1"
4
4
  end
data/lib/tonal/cents.rb CHANGED
@@ -70,7 +70,7 @@ class Tonal::Cents
70
70
  # @return
71
71
  # [Tonal::Cents] nearest hundredth cent difference
72
72
  # @example
73
- # Tonal::Cents.new(701.9550008653874).nearest_hundredth_difference => 1.955000865387433
73
+ # Tonal::Cents.new(cents: 701.9550008653874).nearest_hundredth_difference => 1.96
74
74
  #
75
75
  def nearest_hundredth_difference
76
76
  self.class.new(cents: (value - nearest_hundredth))
@@ -108,7 +108,7 @@ class Tonal::Cents
108
108
  def derive_log(cents: nil, ratio: nil, log: nil)
109
109
  return Tonal::Log2.new(logarithm: cents / CENT_SCALE) if cents
110
110
  return Tonal::Log2.new(logarithmand: ratio) if ratio
111
- log.kind_of?(Tonal::Log2) ? log : Tonal::Log2.new(logarithm: log)
111
+ log.kind_of?(Tonal::Log) ? log : Tonal::Log2.new(logarithm: log)
112
112
  end
113
113
 
114
114
  def derive_ratio(log: nil, ratio: nil)
@@ -9,6 +9,9 @@ class Prime
9
9
  end
10
10
 
11
11
  class Numeric
12
+ alias :antecedent :numerator
13
+ alias :consequent :denominator
14
+
12
15
  # @return [Numeric] translated modularly
13
16
  # @example
14
17
  # Math::PI.modulo_translate(-3, 3) => -2.858407346410207
@@ -102,7 +105,7 @@ class Numeric
102
105
 
103
106
  # @return [Integer] the product complexity of self
104
107
  # @example
105
- # (3/2r).tenney_height => 2.584962500721156
108
+ # (3/2r).tenney_height => 2.58
106
109
  #
107
110
  def tenney_height = self.ratio.tenney_height
108
111
  alias :log_product_complexity :tenney_height
@@ -115,7 +118,7 @@ class Numeric
115
118
 
116
119
  # @return [Tonal::Log2] the log of Weil height
117
120
  # @example
118
- # (3/2r).log_weil_height => 1.5849625007211563
121
+ # (3/2r).log_weil_height => 1.58
119
122
  #
120
123
  def log_weil_height = self.ratio.log_weil_height
121
124
 
@@ -126,10 +129,11 @@ class Numeric
126
129
 
127
130
  # @return [Float] the cents difference between self and its step in the given modulo
128
131
  # @example
129
- # (3/2r).efficiency(12) => -1.955000865387433
132
+ # (3/2r).efficiency(12) => -1.96
130
133
  # @param modulo
131
134
  #
132
- def efficiency(modulo) = (Tonal::Cents::CENT_SCALE * step(modulo).step / modulo.to_f) - to_cents
135
+ # We want the efficiency from the ratio (self)
136
+ def efficiency(modulo) = to_ratio.efficiency(modulo)
133
137
 
134
138
  # @return [Tonal::Interval] beween self (upper) and ratio (lower)
135
139
  # @example
@@ -240,11 +244,11 @@ class Integer
240
244
  alias :totient :phi
241
245
 
242
246
  # @return [Array] of integers that are n-smooth with self
243
- # Adapted from https://rosettacode.org/wiki/N-smooth_numbers#Ruby
244
247
  # @example
245
248
  # 5.nsmooth(25)
246
249
  # => [1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 27, 30, 32, 36, 40, 45, 48, 50, 54]
247
250
  # @param limit
251
+ # @note Adapted from https://rosettacode.org/wiki/N-smooth_numbers#Ruby
248
252
  #
249
253
  def nsmooth(limit=2)
250
254
  ([0] * limit).tap do |ns|
@@ -267,6 +271,11 @@ class Integer
267
271
  end
268
272
 
269
273
  class Array
274
+ alias :numerator :first
275
+ alias :denominator :last
276
+ alias :antecedent :first
277
+ alias :consequent :last
278
+
270
279
  # @return [Array] self replaced by array padded to the right up to n, with value. value default is nil
271
280
  # @example
272
281
  # [3,2].rpad!(3, 12) => [3, 2, 12]
@@ -379,6 +388,12 @@ class Array
379
388
  (value - lower) % range + lower
380
389
  end
381
390
  end
391
+
392
+ # @return [Rational] from first and last element of array. Ideally to be used with tuples.
393
+ # @example
394
+ # [4,3].to_r => (4/3)
395
+ #
396
+ def to_r = Rational(numerator, denominator)
382
397
  end
383
398
 
384
399
  class Vector
@@ -8,9 +8,21 @@ class Tonal::Interval
8
8
 
9
9
  INTERVAL_OF_EQUIVALENCE = 2/1r
10
10
 
11
- def initialize(upper_ratio, lower_ratio)
12
- @lower_ratio = lower_ratio.ratio
13
- @upper_ratio = upper_ratio.ratio
11
+ # @return [Tonal::Interval] the interval of the given ratios
12
+ # @example
13
+ # Tonal::Interval.new(2,3) => (3/2) ((3/2) / (1/1))
14
+ # @param args two arguments representing ratios or four arguments representing two pairs of numerator/denominator
15
+ # @param reduced boolean determining whether to use Tonal::ReducedRatio or Tonal::Ratio
16
+ #
17
+ def initialize(*args, reduced: true)
18
+ klass = reduced ? Tonal::ReducedRatio : Tonal::Ratio
19
+ 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
+ @lower_ratio, @upper_ratio = case args.size
21
+ when 2
22
+ [klass.new(args[0].antecedent, args[0].consequent), klass.new(args[1].antecedent, args[1].consequent)]
23
+ when 4
24
+ [klass.new(args[0],args[1]), klass.new(args[2], args[3])]
25
+ end
14
26
  @interval = @upper_ratio / @lower_ratio
15
27
  end
16
28
  alias :ratio :interval
@@ -30,10 +42,16 @@ class Tonal::Interval
30
42
  end
31
43
 
32
44
  def inspect
33
- "#{self.to_r} (#{upper.to_r} / #{lower.to_r})"
45
+ "#{interval} (#{upper} / #{lower})"
34
46
  end
35
47
 
36
48
  def <=>(rhs)
37
49
  interval.to_r <=> rhs.interval.to_r
38
50
  end
39
51
  end
52
+
53
+ module Interval
54
+ def self.[](l, u, reduced=true)
55
+ Tonal::Interval.new(l, u, reduced:)
56
+ end
57
+ end
@@ -0,0 +1,45 @@
1
+ module Tonal
2
+ module IRBHelpers
3
+ # @return [Tonal::Ratio] an unreduced ratio
4
+ # @example
5
+ # r(3,3) => (3/3)
6
+ # @param arg1 the ratio if only argument provided, or the numerator if two argments are provided
7
+ # @param arg2 the denominator when two arguments are provided
8
+ #
9
+ def r(arg1, arg2=nil)
10
+ Tonal::Ratio.new(arg1, arg2)
11
+ end
12
+
13
+ # @return [Tonal::ReducedRatio] a reduced ratio
14
+ # @example
15
+ # rr(3,3) => (1/1)
16
+ # @param arg1 the ratio if only argument provided, or the numerator if two argments are provided
17
+ # @param arg2 the denominator when two arguments are provided
18
+ #
19
+ def rr(arg1, arg2=nil)
20
+ Tonal::ReducedRatio.new(arg1, arg2)
21
+ end
22
+
23
+ # @return [Tonal::Interval] the interval between the given args
24
+ # @example
25
+ # i(2,3) => (3/2) ((3/2) / (1/1))
26
+ # @param args two arguments representing ratios or four arguments representing two pairs of numerator/denominator
27
+ # @param reduced boolean determining whether to use Tonal::ReducedRatio or Tonal::Ratio
28
+ #
29
+ def i(*args, reduced: true)
30
+ Tonal::Interval.new(*args, reduced:)
31
+ end
32
+ end
33
+
34
+ # @note
35
+ # Intended for activation from +~/.irbrc+, by placing: +ENV["MTONAL_IRB_HELPERS" ] = "1"+, in the file
36
+ #
37
+ # Invoking this command from the IRB will add the helper methods: +r+, +rr+, +i+ in +main+.
38
+ # These methods represent {Tonal::Ratio}, {Tonal::ReducedRatio} and {Tonal::Interval} respectively.
39
+ #
40
+ # @see Tonal::IRBHelpers
41
+ #
42
+ def self.include_irb_helpers
43
+ Object.include(IRBHelpers)
44
+ end
45
+ end
data/lib/tonal/log.rb CHANGED
@@ -4,6 +4,8 @@ class Tonal::Log
4
4
 
5
5
  def_delegators :@logarithmand, :ratio, :to_ratio
6
6
 
7
+ PRECISION = 2
8
+
7
9
  attr_reader :logarithmand, :logarithm, :base
8
10
 
9
11
  # @return [Tonal::Log]
@@ -52,7 +54,7 @@ class Tonal::Log
52
54
 
53
55
  # @return [Tonal::Cents] the cents scale logarithm
54
56
  # @example
55
- # Tonal::Log.new(logarithmand: 3/2r, base: 2).to_cents => 701.9550008653874
57
+ # Tonal::Log.new(logarithmand: 3/2r, base: 2).to_cents => 701.96
56
58
  # @see Tonal::Cents
57
59
  #
58
60
  def to_cents(precision: Tonal::Cents::PRECISION)
@@ -61,7 +63,7 @@ class Tonal::Log
61
63
 
62
64
  # @return [Tonal::Step] the nearest step in the given modulo
63
65
  # @example
64
- # Tonal::Log.new(3/2r, base: 2).step(12) => 7\12
66
+ # Tonal::Log.new(logarithmand: 3/2r, base: 2).step(12) => 7\12
65
67
  #
66
68
  def step(modulo)
67
69
  Tonal::Step.new(modulo: modulo, log: self)
@@ -69,10 +71,10 @@ class Tonal::Log
69
71
 
70
72
  # @return [String] the string representation of Tonal::Log
71
73
  # @example
72
- # Tonal::Log.new(3/2r, base: 2).inspect => "0.5849625007211562"
74
+ # Tonal::Log.new(logarithmand: 3/2r, base: 2).inspect => "0.58"
73
75
  #
74
76
  def inspect
75
- "#{logarithm}"
77
+ "#{logarithm.round(PRECISION)}"
76
78
  end
77
79
 
78
80
  def <=>(rhs)
data/lib/tonal/ratio.rb CHANGED
@@ -4,6 +4,8 @@ class Tonal::Ratio
4
4
 
5
5
  def_delegators :@approximation, :neighborhood
6
6
 
7
+ PRECISION = 2
8
+
7
9
  attr_reader :antecedent, :consequent, :equave, :reduced_antecedent, :reduced_consequent
8
10
 
9
11
  # @return [Tonal::Ratio]
@@ -81,7 +83,7 @@ class Tonal::Ratio
81
83
 
82
84
  # @return [Boolean] if pair of ratios are within the given cents limit
83
85
  # @example
84
- # Tonal::Ratio.within_cents?(100, 105, 2) => true
86
+ # Tonal::Ratio.within_cents?(100, 105, 2) => false
85
87
  # @param cents1
86
88
  # @param cents2
87
89
  # @param within
@@ -141,7 +143,7 @@ class Tonal::Ratio
141
143
 
142
144
  # @return [Tonal::Log] Math.log of self in given base
143
145
  # @example
144
- # Tonal::Ratio.new(3,2).log(3) => 0.3690702464285425
146
+ # Tonal::Ratio.new(3,2).log(3) => 0.37
145
147
  # @param base
146
148
  #
147
149
  def to_log(base=2)
@@ -151,7 +153,7 @@ class Tonal::Ratio
151
153
 
152
154
  # @return [Tonal::Log2] Math.log2 of self
153
155
  # @example
154
- # Tonal::ReducedRatio.new(3,2).to_log2 => 0.5849625007211562
156
+ # Tonal::ReducedRatio.new(3,2).to_log2 => 0.58
155
157
  #
156
158
  def to_log2
157
159
  Tonal::Log2.new(logarithmand: to_r)
@@ -169,7 +171,7 @@ class Tonal::Ratio
169
171
 
170
172
  # @return [Integer] the step of self in the given modulo
171
173
  # @example
172
- # Tonal::ReducedRatio.new(3,2).step(12) => 7
174
+ # Tonal::ReducedRatio.new(3,2).step(12) => 7\12
173
175
  #
174
176
  def step(modulo=12)
175
177
  Tonal::Step.new(ratio: to_r, modulo: modulo)
@@ -180,7 +182,7 @@ class Tonal::Ratio
180
182
  # Tonal::Ratio.new(3,2).period_degrees => 210.59
181
183
  # @param round
182
184
  #
183
- def period_degrees(round: 2)
185
+ def period_degrees(round: PRECISION)
184
186
  (360.0 * Math.log(to_f, equave)).round(round)
185
187
  end
186
188
 
@@ -189,7 +191,7 @@ class Tonal::Ratio
189
191
  # Tonal::Ratio.new(3,2).period_radians => 3.68
190
192
  # @param round
191
193
  #
192
- def period_radians(round: 2)
194
+ def period_radians(round: PRECISION)
193
195
  (2 * Math::PI * Math.log(to_f, equave)).round(round)
194
196
  end
195
197
 
@@ -254,7 +256,7 @@ class Tonal::Ratio
254
256
 
255
257
  # @return [Tonal::Ratio] the mirror of self along the axis (default 1/1)
256
258
  # @example
257
- # Tonal::Ratio.new(4,3).mirror => (3/2)
259
+ # Tonal::ReducedRatio.new(4,3).mirror => (3/2)
258
260
  # @param axis
259
261
  #
260
262
  def mirror(axis=1/1r)
@@ -299,7 +301,7 @@ class Tonal::Ratio
299
301
 
300
302
  # @return [Tonal::Ratio] self sheared by given arguments
301
303
  # @example
302
- # Tonal::Ratio.new(3,2).shear(1, 3) => (9/11)
304
+ # Tonal::Ratio.new(3,2).shear(1, 3) => (14/11)
303
305
  # @param a [Numeric]
304
306
  # @param b [Numeric]
305
307
  #
@@ -313,7 +315,7 @@ class Tonal::Ratio
313
315
  # Tonal::Ratio.new(3,2).planar_degrees => 33.69
314
316
  # @param round
315
317
  #
316
- def planar_degrees(round: 2)
318
+ def planar_degrees(round: PRECISION)
317
319
  (Math.atan2(consequent, antecedent) * 180/Math::PI).round(round)
318
320
  end
319
321
 
@@ -322,7 +324,7 @@ class Tonal::Ratio
322
324
  # Tonal::Ratio.new(3,2).planar_radians => 0.59
323
325
  # @param round
324
326
  #
325
- def planar_radians(round: 2)
327
+ def planar_radians(round: PRECISION)
326
328
  Math.atan2(consequent, antecedent).round(round)
327
329
  end
328
330
 
@@ -368,8 +370,13 @@ class Tonal::Ratio
368
370
  prime_divisions.flatten(1).map(&:first).min
369
371
  end
370
372
 
371
- def within_prime?(prime)
372
- max_prime <= prime
373
+ # @return [Boolean] whether self's max prime is within the given number
374
+ # @example
375
+ # Tonal::Ratio.new(31/30r).max_prime_within?(7) => false
376
+ # @param number to compare max prime against
377
+ #
378
+ def max_prime_within?(number)
379
+ max_prime <= number
373
380
  end
374
381
 
375
382
  # @return [Integer] the product complexity of self
@@ -383,7 +390,7 @@ class Tonal::Ratio
383
390
 
384
391
  # @return [Tonal::Log2] the log product complexity of self
385
392
  # @example
386
- # Tonal::ReducedRatio.new(3/2r).tenney_height => 2.584962500721156
393
+ # Tonal::ReducedRatio.new(3/2r).tenney_height => 2.58
387
394
  #
388
395
  def tenney_height
389
396
  Tonal::Log2.new(logarithmand: benedetti_height)
@@ -402,7 +409,7 @@ class Tonal::Ratio
402
409
 
403
410
  # @return [Tonal::Log2] the log of Weil height
404
411
  # @example
405
- # Tonal::ReducedRatio.new(3/2r).log_weil_height => 1.5849625007211563
412
+ # Tonal::ReducedRatio.new(3/2r).log_weil_height => 1.58
406
413
  #
407
414
  def log_weil_height
408
415
  Tonal::Log2.new(logarithmand: weil_height)
@@ -418,33 +425,33 @@ class Tonal::Ratio
418
425
 
419
426
  # @return [Tonal::Cents] the cents difference between self and its step in the given modulo
420
427
  # @example
421
- # Tonal::ReducedRatio.new(3,2).efficiency(12) => -1.955000865387433
422
- # @param modulo
428
+ # Tonal::ReducedRatio.new(3,2).efficiency(12) => -1.96
429
+ # @param modulo against which the difference of self is compared
423
430
  #
424
431
  def efficiency(modulo)
425
- # We want the efficiency from the ratio, instead of from the step.
426
- # If step efficiency is X cents, then ratio efficiency is -X cents.
432
+ # We want the efficiency from the ratio (self).
433
+ # If the step efficiency is X cents, then the ratio efficiency is -X cents.
427
434
  step(modulo).efficiency * -1.0
428
435
  end
429
436
 
430
437
  # @return [Array] the results of ratio dividing and multiplying self
431
438
  # @example
432
439
  # Tonal::ReducedRatio.new(3/2r).div_times(5/4r) => [(6/5), (15/8)]
433
- # @param ratio
440
+ # @param other_ratio to divide and multiple on self
434
441
  #
435
- def div_times(ratio)
436
- ratio = ratio.ratio
437
- [self / ratio, self * ratio]
442
+ def div_times(other_ratio)
443
+ other_ratio = self.class.new(other_ratio)
444
+ [self / other_ratio, self * other_ratio]
438
445
  end
439
446
 
440
447
  # @return [Array] the results of ratio subtracted and added to self
441
448
  # @example
442
449
  # Tonal::ReducedRatio.new(3/2r).plus_minus(5/4r) => [(1/1), (11/8)]
443
- # @param ratio
450
+ # @param other_ratio to add and subtract from self
444
451
  #
445
- def plus_minus(ratio)
446
- ratio = ratio.ratio
447
- [self - ratio, self + ratio]
452
+ def plus_minus(other_ratio)
453
+ other_ratio = self.class.new(other_ratio)
454
+ [self - other_ratio, self + other_ratio]
448
455
  end
449
456
  alias :min_plus :plus_minus
450
457
 
@@ -463,7 +470,7 @@ class Tonal::Ratio
463
470
  # Return label, if defined; or,
464
471
  # Return the "antecedent/consequent", if antecedent is less than 7 digits long; or
465
472
  # Return the floating point representation rounded to 2 digits of precision
466
- (@label || ((Math.log10(antecedent).to_i + 1) <= 6 ? "#{antecedent}/#{consequent}" : to_f.round(Tonal::Cents::PRECISION))).to_s
473
+ (@label || ((Math.log10(antecedent).to_i + 1) <= 6 ? "#{antecedent}/#{consequent}" : to_f.round(PRECISION))).to_s
467
474
  end
468
475
 
469
476
  # @return [String] the string representation of Tonal::Ratio
@@ -484,11 +491,11 @@ class Tonal::Ratio
484
491
  end
485
492
 
486
493
  def *(rhs)
487
- self.class.new(antecedent * rhs.antecedent, consequent * rhs.consequent)
494
+ operate(rhs, :*)
488
495
  end
489
496
 
490
497
  def /(rhs)
491
- self.class.new(antecedent * rhs.consequent, consequent * rhs.antecedent)
498
+ operate(rhs, :/)
492
499
  end
493
500
 
494
501
  def **(rhs)
@@ -519,6 +526,18 @@ class Tonal::Ratio
519
526
  [self.denominator, lhs.denominator].lcm
520
527
  end
521
528
 
529
+ # @return [Tonal::Interval] between ratio (upper) and self (lower)
530
+ # @example
531
+ # Tonal::ReducedRatio.new(133).interval_with(3/2r)
532
+ # => (192/133) ((3/2) / (133/128))
533
+ # @param antecedent
534
+ # @param consequent
535
+ #
536
+ def interval_with(antecedent, consequent=nil)
537
+ r = self.class.new(antecedent, consequent)
538
+ Tonal::Interval.new(self, r)
539
+ end
540
+
522
541
  # @return [Integer] the difference between antecedent and consequent
523
542
  # @example
524
543
  # Tonal::ReducedRatio.new(3,2).difference => 1
@@ -579,32 +598,32 @@ class Tonal::Ratio
579
598
  end
580
599
  end
581
600
 
601
+ def coerce(other)
602
+ [self.class.new(other), self]
603
+ end
604
+
582
605
  def operate(rhs, op)
606
+ klass = (rhs.class == Tonal::ReducedRatio || self.class == Tonal::ReducedRatio) ? Tonal::ReducedRatio : Tonal::Ratio
607
+
583
608
  case op
584
- when :+, :-
585
- case rhs
586
- when Tonal::Ratio
587
- self.class.new((antecedent * rhs.consequent).send(op, rhs.antecedent * consequent).abs, consequent * rhs.consequent)
588
- when Rational
589
- self.class.new((antecedent * rhs.denominator).send(op, rhs.numerator * consequent).abs, consequent * rhs.denominator)
590
- when Array
591
- self.class.new((antecedent * rhs[1]).send(op, rhs[0] * consequent), consequent * rhs[1])
592
- else
593
- r = Rational(rhs)
594
- self.class.new((antecedent * r.denominator).send(op, r.numerator * consequent), consequent * r.denominator)
595
- end
596
609
  when :*
597
- case rhs
598
- when Rational
599
- self.class.new(antecedent.send(op, rhs.numerator), consequent.send(op, rhs.denominator))
600
- when Array
601
- self.class.new(antecedent.send(op, rhs[0]), consequent.send(op, rhs[1]))
602
- else
603
- r = Rational(rhs)
604
- self.class.new(antecedent.send(op, r.numerator), consequent.send(op, r.denominator))
605
- end
610
+ klass.new(antecedent * rhs.antecedent, consequent * rhs.consequent)
611
+ when :/
612
+ klass.new(antecedent * rhs.consequent, consequent * rhs.antecedent)
613
+ when :+, :-
614
+ lcm = self.denominator.lcm(rhs.denominator)
615
+ left = (self.to_r * lcm).numerator
616
+ right = (rhs.to_r * lcm).numerator
617
+ klass.new((left).send(op, right).abs, lcm)
606
618
  when :**
607
- self.class.new(antecedent.send(op, rhs), consequent.send(op, rhs))
619
+ klass.new(Rational(antecedent, consequent) ** rhs.to_r)
608
620
  end
609
621
  end
610
622
  end
623
+
624
+ module Ratio
625
+ def self.[](u, l=nil)
626
+ Tonal::Ratio.new(u, l)
627
+ end
628
+ end
629
+
@@ -25,17 +25,6 @@ class Tonal::ReducedRatio < Tonal::Ratio
25
25
  Tonal::Ratio.new(antecedent, consequent)
26
26
  end
27
27
 
28
- # @return [Tonal::Interval] between ratio (upper) and self (lower)
29
- # @example
30
- # Tonal::ReducedRatio.new(133).interval_with(3/2r)
31
- # => 192/133 (3/2 / 133/128)
32
- # @param ratio
33
- #
34
- def interval_with(ratio)
35
- r = ratio.is_a?(self.class) ? ratio : self.class.new(ratio)
36
- Tonal::Interval.new(r, self)
37
- end
38
-
39
28
  # @return [Tonal::ReducedRatio] with antecedent and precedent switched
40
29
  # @example
41
30
  # Tonal::ReducedRatio.new(3,2).invert! => (4/3)
@@ -46,3 +35,9 @@ class Tonal::ReducedRatio < Tonal::Ratio
46
35
  self
47
36
  end
48
37
  end
38
+
39
+ module ReducedRatio
40
+ def self.[](u, l=nil)
41
+ Tonal::ReducedRatio.new(u, l)
42
+ end
43
+ end
data/lib/tonal/step.rb CHANGED
@@ -40,7 +40,7 @@ class Tonal::Step
40
40
  # @return [Rational] of the step
41
41
  # @example
42
42
  # Tonal::Step.new(ratio: 3/2r, modulo: 31).step_to_r
43
- # => 6735213777669305/4503599627370496
43
+ # => (6735213777669305/4503599627370496)
44
44
  #
45
45
  def step_to_r
46
46
  tempered.to_r
@@ -50,7 +50,7 @@ class Tonal::Step
50
50
  # @return [Rational] of the ratio
51
51
  # @example
52
52
  # Tonal::Step.new(ratio: 3/2r, modulo: 31).ratio_to_r
53
- # => 3/2
53
+ # => (3/2)
54
54
  #
55
55
  def ratio_to_r
56
56
  ratio.to_r
@@ -81,6 +81,7 @@ class Tonal::Step
81
81
  # => 5.19
82
82
  #
83
83
  def efficiency
84
+ # We want the efficiency from the step (self).
84
85
  ratio_to_cents - step_to_cents
85
86
  end
86
87
 
@@ -98,7 +99,7 @@ class Tonal::Step
98
99
  if ratio
99
100
  [Tonal::ReducedRatio.new(ratio), Tonal::Log2.new(logarithmand: ratio)]
100
101
  elsif log
101
- if log.kind_of?(Tonal::Log2)
102
+ if log.kind_of?(Tonal::Log)
102
103
  [log.logarithmand, log]
103
104
  else
104
105
  lg = Tonal::Log2.new(logarithm: log)
data/lib/tonal/tools.rb CHANGED
@@ -17,4 +17,10 @@ module Tonal
17
17
  require "tonal/interval"
18
18
  require "tonal/step"
19
19
  require "tonal/extensions"
20
+ require "tonal/irb_helpers"
21
+ end
22
+
23
+ if ENV["MTONAL_IRB_HELPERS"]
24
+ Tonal.include_irb_helpers
25
+ puts 'mTonal IRB helpers have been enabled.'
20
26
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tonal-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.3.0
4
+ version: 6.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jose Hales-Garcia
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-11-09 00:00:00.000000000 Z
11
+ date: 2025-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: yaml
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.2'
19
+ version: '0.4'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0.2'
26
+ version: '0.4'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: json
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '2.6'
33
+ version: '2.9'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '2.6'
40
+ version: '2.9'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: prime
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -114,14 +114,14 @@ dependencies:
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: '3.2'
117
+ version: '3'
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: '3.2'
124
+ version: '3'
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: byebug
127
127
  requirement: !ruby/object:Gem::Requirement
@@ -165,6 +165,7 @@ files:
165
165
  - lib/tonal/extensions.rb
166
166
  - lib/tonal/hertz.rb
167
167
  - lib/tonal/interval.rb
168
+ - lib/tonal/irb_helpers.rb
168
169
  - lib/tonal/log.rb
169
170
  - lib/tonal/log2.rb
170
171
  - lib/tonal/ratio.rb