deftones 0.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.
Files changed (135) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +5 -0
  3. data/CHANGELOG.md +12 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +197 -0
  6. data/Rakefile +8 -0
  7. data/examples/poly_chord.rb +14 -0
  8. data/examples/render_sampler.rb +16 -0
  9. data/examples/render_sequence.rb +13 -0
  10. data/examples/render_synth.rb +18 -0
  11. data/lib/deftones/analysis/analyser.rb +162 -0
  12. data/lib/deftones/analysis/dc_meter.rb +56 -0
  13. data/lib/deftones/analysis/fft.rb +128 -0
  14. data/lib/deftones/analysis/meter.rb +83 -0
  15. data/lib/deftones/analysis/waveform.rb +93 -0
  16. data/lib/deftones/component/amplitude_envelope.rb +8 -0
  17. data/lib/deftones/component/biquad_filter.rb +7 -0
  18. data/lib/deftones/component/channel.rb +109 -0
  19. data/lib/deftones/component/compressor.rb +64 -0
  20. data/lib/deftones/component/convolver.rb +99 -0
  21. data/lib/deftones/component/cross_fade.rb +67 -0
  22. data/lib/deftones/component/envelope.rb +160 -0
  23. data/lib/deftones/component/eq3.rb +73 -0
  24. data/lib/deftones/component/feedback_comb_filter.rb +75 -0
  25. data/lib/deftones/component/filter.rb +75 -0
  26. data/lib/deftones/component/follower.rb +55 -0
  27. data/lib/deftones/component/frequency_envelope.rb +24 -0
  28. data/lib/deftones/component/gate.rb +46 -0
  29. data/lib/deftones/component/lfo.rb +88 -0
  30. data/lib/deftones/component/limiter.rb +11 -0
  31. data/lib/deftones/component/lowpass_comb_filter.rb +43 -0
  32. data/lib/deftones/component/merge.rb +45 -0
  33. data/lib/deftones/component/mid_side_compressor.rb +43 -0
  34. data/lib/deftones/component/mid_side_merge.rb +53 -0
  35. data/lib/deftones/component/mid_side_split.rb +54 -0
  36. data/lib/deftones/component/mono.rb +8 -0
  37. data/lib/deftones/component/multiband_compressor.rb +70 -0
  38. data/lib/deftones/component/multiband_split.rb +133 -0
  39. data/lib/deftones/component/one_pole_filter.rb +71 -0
  40. data/lib/deftones/component/pan_vol.rb +26 -0
  41. data/lib/deftones/component/panner.rb +75 -0
  42. data/lib/deftones/component/panner3d.rb +322 -0
  43. data/lib/deftones/component/solo.rb +56 -0
  44. data/lib/deftones/component/split.rb +47 -0
  45. data/lib/deftones/component/volume.rb +31 -0
  46. data/lib/deftones/context.rb +213 -0
  47. data/lib/deftones/core/audio_block.rb +82 -0
  48. data/lib/deftones/core/audio_node.rb +262 -0
  49. data/lib/deftones/core/clock.rb +91 -0
  50. data/lib/deftones/core/computed_signal.rb +69 -0
  51. data/lib/deftones/core/delay.rb +44 -0
  52. data/lib/deftones/core/effect.rb +66 -0
  53. data/lib/deftones/core/emitter.rb +51 -0
  54. data/lib/deftones/core/gain.rb +39 -0
  55. data/lib/deftones/core/instrument.rb +109 -0
  56. data/lib/deftones/core/param.rb +31 -0
  57. data/lib/deftones/core/signal.rb +452 -0
  58. data/lib/deftones/core/signal_operator_methods.rb +73 -0
  59. data/lib/deftones/core/signal_operators.rb +138 -0
  60. data/lib/deftones/core/signal_shapers.rb +83 -0
  61. data/lib/deftones/core/source.rb +213 -0
  62. data/lib/deftones/core/synced_signal.rb +88 -0
  63. data/lib/deftones/destination.rb +132 -0
  64. data/lib/deftones/draw.rb +100 -0
  65. data/lib/deftones/dsp/biquad.rb +129 -0
  66. data/lib/deftones/dsp/delay_line.rb +41 -0
  67. data/lib/deftones/dsp/helpers.rb +25 -0
  68. data/lib/deftones/effect/auto_filter.rb +92 -0
  69. data/lib/deftones/effect/auto_panner.rb +57 -0
  70. data/lib/deftones/effect/auto_wah.rb +98 -0
  71. data/lib/deftones/effect/bit_crusher.rb +38 -0
  72. data/lib/deftones/effect/chebyshev.rb +36 -0
  73. data/lib/deftones/effect/chorus.rb +73 -0
  74. data/lib/deftones/effect/distortion.rb +22 -0
  75. data/lib/deftones/effect/feedback_delay.rb +38 -0
  76. data/lib/deftones/effect/freeverb.rb +11 -0
  77. data/lib/deftones/effect/frequency_shifter.rb +89 -0
  78. data/lib/deftones/effect/jc_reverb.rb +11 -0
  79. data/lib/deftones/effect/modulation_control.rb +159 -0
  80. data/lib/deftones/effect/phaser.rb +72 -0
  81. data/lib/deftones/effect/ping_pong_delay.rb +40 -0
  82. data/lib/deftones/effect/pitch_shift.rb +156 -0
  83. data/lib/deftones/effect/reverb.rb +71 -0
  84. data/lib/deftones/effect/stereo_widener.rb +34 -0
  85. data/lib/deftones/effect/tremolo.rb +52 -0
  86. data/lib/deftones/effect/vibrato.rb +47 -0
  87. data/lib/deftones/event/callback_behavior.rb +61 -0
  88. data/lib/deftones/event/loop.rb +53 -0
  89. data/lib/deftones/event/part.rb +51 -0
  90. data/lib/deftones/event/pattern.rb +94 -0
  91. data/lib/deftones/event/sequence.rb +87 -0
  92. data/lib/deftones/event/tone_event.rb +77 -0
  93. data/lib/deftones/event/transport.rb +276 -0
  94. data/lib/deftones/instrument/am_synth.rb +56 -0
  95. data/lib/deftones/instrument/duo_synth.rb +68 -0
  96. data/lib/deftones/instrument/fm_synth.rb +60 -0
  97. data/lib/deftones/instrument/membrane_synth.rb +60 -0
  98. data/lib/deftones/instrument/metal_synth.rb +61 -0
  99. data/lib/deftones/instrument/mono_synth.rb +88 -0
  100. data/lib/deftones/instrument/noise_synth.rb +56 -0
  101. data/lib/deftones/instrument/pluck_synth.rb +41 -0
  102. data/lib/deftones/instrument/poly_synth.rb +96 -0
  103. data/lib/deftones/instrument/sampler.rb +97 -0
  104. data/lib/deftones/instrument/synth.rb +60 -0
  105. data/lib/deftones/io/buffer.rb +352 -0
  106. data/lib/deftones/io/buffers.rb +77 -0
  107. data/lib/deftones/io/recorder.rb +89 -0
  108. data/lib/deftones/listener.rb +120 -0
  109. data/lib/deftones/music/frequency.rb +128 -0
  110. data/lib/deftones/music/midi.rb +206 -0
  111. data/lib/deftones/music/note.rb +58 -0
  112. data/lib/deftones/music/ticks.rb +106 -0
  113. data/lib/deftones/music/time.rb +209 -0
  114. data/lib/deftones/music/transport_time.rb +94 -0
  115. data/lib/deftones/music/unit_helpers.rb +30 -0
  116. data/lib/deftones/offline_context.rb +46 -0
  117. data/lib/deftones/portaudio_support.rb +112 -0
  118. data/lib/deftones/source/am_oscillator.rb +42 -0
  119. data/lib/deftones/source/fat_oscillator.rb +49 -0
  120. data/lib/deftones/source/fm_oscillator.rb +47 -0
  121. data/lib/deftones/source/grain_player.rb +198 -0
  122. data/lib/deftones/source/karplus_strong.rb +51 -0
  123. data/lib/deftones/source/noise.rb +99 -0
  124. data/lib/deftones/source/omni_oscillator.rb +175 -0
  125. data/lib/deftones/source/oscillator.rb +74 -0
  126. data/lib/deftones/source/player.rb +228 -0
  127. data/lib/deftones/source/players.rb +133 -0
  128. data/lib/deftones/source/pulse_oscillator.rb +38 -0
  129. data/lib/deftones/source/pwm_oscillator.rb +49 -0
  130. data/lib/deftones/source/tone_buffer_source.rb +136 -0
  131. data/lib/deftones/source/tone_oscillator_node.rb +65 -0
  132. data/lib/deftones/source/user_media.rb +519 -0
  133. data/lib/deftones/version.rb +5 -0
  134. data/lib/deftones.rb +542 -0
  135. metadata +221 -0
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Effects
5
+ class StereoWidener < Core::Effect
6
+ attr_accessor :width
7
+
8
+ def initialize(width: 0.5, context: Deftones.context, **options)
9
+ super(context: context, **options)
10
+ @width = width.to_f
11
+ end
12
+
13
+ private
14
+
15
+ def process_effect_block(input_block, num_frames, _start_frame, _cache)
16
+ stereo_input = input_block.fit_channels(2)
17
+ width = @width.to_f.clamp(0.0, 1.0)
18
+ left = Array.new(num_frames)
19
+ right = Array.new(num_frames)
20
+
21
+ num_frames.times do |index|
22
+ left_sample = stereo_input.channel_data[0][index]
23
+ right_sample = stereo_input.channel_data[1][index]
24
+ mid = (left_sample + right_sample) * (1.0 - width)
25
+ side = (left_sample - right_sample) * width
26
+ left[index] = mid + side
27
+ right[index] = mid - side
28
+ end
29
+
30
+ Core::AudioBlock.from_channel_data([left, right])
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Effects
5
+ class Tremolo < Core::Effect
6
+ include ModulationControl
7
+
8
+ attr_accessor :frequency, :depth, :spread, :type
9
+
10
+ def initialize(frequency: 5.0, depth: 0.8, spread: 0.0, type: :sine, context: Deftones.context, **options)
11
+ super(context: context, wet: 1.0, **options)
12
+ @frequency = frequency.to_f
13
+ @depth = depth.to_f
14
+ @spread = spread.to_f
15
+ @type = normalize_modulation_type(type)
16
+ @phase = 0.0
17
+ initialize_modulation_control
18
+ end
19
+
20
+ private
21
+
22
+ def process_effect_block(input_block, num_frames, start_frame, _cache)
23
+ output_channels = [input_block.channels, 2].max
24
+ source = input_block.fit_channels(output_channels)
25
+ output = Array.new(output_channels) { Array.new(num_frames, 0.0) }
26
+
27
+ num_frames.times do |index|
28
+ current_time = (start_frame + index).to_f / context.sample_rate
29
+ base_phase = modulation_phase_for(current_time)
30
+
31
+ output_channels.times do |channel_index|
32
+ phase = base_phase.nil? ? nil : base_phase + channel_phase_offset(channel_index, output_channels)
33
+ output[channel_index][index] = source.channel_data[channel_index][index] * channel_gain_for_phase(phase)
34
+ end
35
+ end
36
+
37
+ Core::AudioBlock.from_channel_data(output)
38
+ end
39
+
40
+ def channel_phase_offset(channel_index, channels)
41
+ return 0.0 if channels <= 1
42
+
43
+ (@spread / 360.0) * (channel_index.to_f / [channels - 1, 1].max)
44
+ end
45
+
46
+ def channel_gain_for_phase(phase)
47
+ modulation = unipolar_modulation_value(phase, default: 1.0)
48
+ 1.0 - (@depth * (1.0 - modulation))
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Effects
5
+ class Vibrato < Core::Effect
6
+ include ModulationControl
7
+
8
+ attr_accessor :frequency, :depth, :type
9
+
10
+ def initialize(frequency: 5.0, depth: 0.002, type: :sine, context: Deftones.context, **options)
11
+ super(context: context, wet: 1.0, **options)
12
+ @frequency = frequency.to_f
13
+ @depth = depth.to_f
14
+ @type = normalize_modulation_type(type)
15
+ @phase = 0.0
16
+ @delay_lines = []
17
+ initialize_modulation_control
18
+ end
19
+
20
+ private
21
+
22
+ def process_effect_block(input_block, num_frames, start_frame, _cache)
23
+ ensure_delay_lines(input_block.channels)
24
+ output = Array.new(input_block.channels) { Array.new(num_frames, 0.0) }
25
+
26
+ num_frames.times do |index|
27
+ current_time = (start_frame + index).to_f / context.sample_rate
28
+ phase = modulation_phase_for(current_time)
29
+ modulation = unipolar_modulation_value(phase, default: 0.5)
30
+ delay_samples = (0.01 + (@depth * modulation)) * context.sample_rate
31
+ input_block.channel_data.each_with_index do |channel, channel_index|
32
+ output[channel_index][index] = @delay_lines[channel_index].tap(delay_samples, input_sample: channel[index])
33
+ end
34
+ end
35
+
36
+ Core::AudioBlock.from_channel_data(output)
37
+ end
38
+
39
+ def ensure_delay_lines(channels)
40
+ required = [channels.to_i, 1].max
41
+ while @delay_lines.length < required
42
+ @delay_lines << DSP::DelayLine.new((0.05 * context.sample_rate).ceil)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Event
5
+ module CallbackBehavior
6
+ attr_accessor :humanize, :mute, :playback_rate, :probability
7
+ attr_reader :state
8
+
9
+ def mute?
10
+ @mute
11
+ end
12
+
13
+ alias playbackRate playback_rate
14
+
15
+ def playbackRate=(value)
16
+ self.playback_rate = value
17
+ end
18
+
19
+ private
20
+
21
+ def initialize_callback_behavior(probability: 1.0, humanize: false, mute: false, playback_rate: 1.0)
22
+ @probability = probability.to_f
23
+ @humanize = humanize
24
+ @mute = !!mute
25
+ @playback_rate = playback_rate.to_f
26
+ @state = :stopped
27
+ end
28
+
29
+ def mark_started
30
+ @state = :started
31
+ end
32
+
33
+ def mark_stopped
34
+ @state = :stopped
35
+ end
36
+
37
+ def callback_interval(value)
38
+ base = Deftones::Music::Time.parse(value)
39
+ rate = @playback_rate.zero? ? 1.0 : @playback_rate.abs
40
+ base / rate
41
+ end
42
+
43
+ def callback_time(value)
44
+ Deftones::Music::Time.parse(value)
45
+ end
46
+
47
+ def callback_permitted?
48
+ return false if @mute
49
+
50
+ rand <= @probability
51
+ end
52
+
53
+ def humanized_time(time)
54
+ return time unless @humanize
55
+
56
+ amount = @humanize == true ? 0.01 : Deftones::Music::Time.parse(@humanize)
57
+ time + (((rand * 2.0) - 1.0) * amount)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Event
5
+ class Loop
6
+ include CallbackBehavior
7
+
8
+ attr_reader :interval, :iterations
9
+
10
+ def initialize(interval:, iterations: nil, transport: Deftones.transport,
11
+ probability: 1.0, humanize: false, mute: false, playback_rate: 1.0, &callback)
12
+ @interval = interval
13
+ @iterations = iterations
14
+ @transport = transport
15
+ @callback = callback
16
+ @event_id = nil
17
+ initialize_callback_behavior(
18
+ probability: probability,
19
+ humanize: humanize,
20
+ mute: mute,
21
+ playback_rate: playback_rate
22
+ )
23
+ end
24
+
25
+ def start(time = 0)
26
+ count = 0
27
+ scaled_interval = callback_interval(@interval)
28
+ duration = @iterations ? (scaled_interval * (@iterations - 1)) : nil
29
+ @event_id = @transport.schedule_repeat(scaled_interval, start_time: time, duration: duration) do |scheduled_time|
30
+ @callback.call(humanized_time(scheduled_time)) if callback_permitted?
31
+ count += 1
32
+ end
33
+ mark_started
34
+ self
35
+ end
36
+
37
+ def stop(_time = nil)
38
+ cancel
39
+ end
40
+
41
+ def cancel
42
+ @transport.cancel(event_id: @event_id) if @event_id
43
+ @event_id = nil
44
+ mark_stopped
45
+ self
46
+ end
47
+
48
+ def dispose
49
+ cancel
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Event
5
+ class Part
6
+ include CallbackBehavior
7
+
8
+ def initialize(events:, transport: Deftones.transport,
9
+ probability: 1.0, humanize: false, mute: false, playback_rate: 1.0, &callback)
10
+ @events = events
11
+ @transport = transport
12
+ @callback = callback
13
+ @event_ids = []
14
+ initialize_callback_behavior(
15
+ probability: probability,
16
+ humanize: humanize,
17
+ mute: mute,
18
+ playback_rate: playback_rate
19
+ )
20
+ end
21
+
22
+ def start(time = 0)
23
+ offset = callback_time(time)
24
+ @event_ids = @events.map do |event|
25
+ event_time = offset + callback_interval(event.fetch(:time, 0))
26
+ @transport.schedule(event_time) do |scheduled_time|
27
+ @callback.call(humanized_time(scheduled_time), event) if callback_permitted?
28
+ end
29
+ end
30
+ mark_started
31
+ self
32
+ end
33
+
34
+ def stop(_time = nil)
35
+ cancel
36
+ end
37
+
38
+ def cancel
39
+ @event_ids.each { |event_id| @transport.cancel(event_id: event_id) }
40
+ @event_ids.clear
41
+ mark_stopped
42
+ self
43
+ end
44
+
45
+ def dispose
46
+ cancel
47
+ self
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Event
5
+ class Pattern
6
+ include CallbackBehavior
7
+
8
+ PATTERNS = %i[up down up_down random].freeze
9
+
10
+ def initialize(values:, pattern: :up, interval: "4n", transport: Deftones.transport,
11
+ probability: 1.0, humanize: false, mute: false, playback_rate: 1.0, &callback)
12
+ @values = values
13
+ @pattern = normalize_pattern(pattern)
14
+ @interval = interval
15
+ @transport = transport
16
+ @callback = callback
17
+ @event_id = nil
18
+ @index = 0
19
+ @direction = 1
20
+ initialize_callback_behavior(
21
+ probability: probability,
22
+ humanize: humanize,
23
+ mute: mute,
24
+ playback_rate: playback_rate
25
+ )
26
+ end
27
+
28
+ def start(time = 0)
29
+ @event_id = @transport.schedule_repeat(callback_interval(@interval), start_time: time) do |scheduled_time|
30
+ @callback.call(humanized_time(scheduled_time), next_value) if callback_permitted?
31
+ end
32
+ mark_started
33
+ self
34
+ end
35
+
36
+ def stop(_time = nil)
37
+ cancel
38
+ end
39
+
40
+ def cancel
41
+ @transport.cancel(event_id: @event_id) if @event_id
42
+ @event_id = nil
43
+ mark_stopped
44
+ self
45
+ end
46
+
47
+ def dispose
48
+ cancel
49
+ end
50
+
51
+ private
52
+
53
+ def next_value
54
+ case @pattern
55
+ when :up
56
+ ordered_value
57
+ when :down
58
+ descending_value
59
+ when :up_down
60
+ bounce_value
61
+ when :random
62
+ @values.sample
63
+ end
64
+ end
65
+
66
+ def ordered_value
67
+ value = @values[@index % @values.length]
68
+ @index += 1
69
+ value
70
+ end
71
+
72
+ def descending_value
73
+ value = @values.reverse[@index % @values.length]
74
+ @index += 1
75
+ value
76
+ end
77
+
78
+ def bounce_value
79
+ value = @values[@index]
80
+ @direction = -1 if @index >= @values.length - 1
81
+ @direction = 1 if @index <= 0
82
+ @index += @direction
83
+ value
84
+ end
85
+
86
+ def normalize_pattern(pattern)
87
+ normalized = pattern.to_sym
88
+ return normalized if PATTERNS.include?(normalized)
89
+
90
+ raise ArgumentError, "Unsupported pattern: #{pattern}"
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Event
5
+ class Sequence
6
+ include Enumerable
7
+ include CallbackBehavior
8
+
9
+ def initialize(notes:, subdivision: "4n", loop: true, transport: Deftones.transport,
10
+ probability: 1.0, humanize: false, mute: false, playback_rate: 1.0, &callback)
11
+ @notes = notes
12
+ @subdivision = subdivision
13
+ @loop = loop
14
+ @transport = transport
15
+ @callback = callback
16
+ @event_id = nil
17
+ @current_step = 0
18
+ initialize_callback_behavior(
19
+ probability: probability,
20
+ humanize: humanize,
21
+ mute: mute,
22
+ playback_rate: playback_rate
23
+ )
24
+ end
25
+
26
+ def start(time = 0)
27
+ @current_step = 0
28
+ scaled_subdivision = callback_interval(@subdivision)
29
+ duration = @loop ? nil : (scaled_subdivision * (@notes.length - 1))
30
+ @event_id = @transport.schedule_repeat(scaled_subdivision, start_time: time, duration: duration) do |scheduled_time|
31
+ process_step(scheduled_time)
32
+ @current_step += 1
33
+ end
34
+ mark_started
35
+ self
36
+ end
37
+
38
+ def stop(_time = nil)
39
+ cancel
40
+ end
41
+
42
+ def cancel
43
+ @transport.cancel(event_id: @event_id) if @event_id
44
+ @event_id = nil
45
+ mark_stopped
46
+ self
47
+ end
48
+
49
+ def dispose
50
+ cancel
51
+ end
52
+
53
+ def [](index)
54
+ @notes[index]
55
+ end
56
+
57
+ def []=(index, value)
58
+ @notes[index] = value
59
+ end
60
+
61
+ def each(&block)
62
+ return enum_for(:each) unless block
63
+
64
+ @notes.each(&block)
65
+ end
66
+
67
+ private
68
+
69
+ def process_step(scheduled_time)
70
+ note = @notes[@current_step % @notes.length]
71
+ return if note.nil?
72
+ return unless callback_permitted?
73
+
74
+ if note.is_a?(Array)
75
+ sub_duration = callback_interval(@subdivision) / note.length.to_f
76
+ note.each_with_index do |nested_note, index|
77
+ next if nested_note.nil?
78
+
79
+ @callback.call(humanized_time(scheduled_time + (sub_duration * index)), nested_note)
80
+ end
81
+ else
82
+ @callback.call(humanized_time(scheduled_time), note)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Deftones
4
+ module Event
5
+ class ToneEvent
6
+ include CallbackBehavior
7
+
8
+ attr_accessor :loop, :loop_start, :loop_end, :probability
9
+
10
+ def initialize(transport: Deftones.transport, probability: 1.0, loop: false,
11
+ loop_start: 0.0, loop_end: nil, humanize: false, mute: false, playback_rate: 1.0, &callback)
12
+ @transport = transport
13
+ @callback = callback
14
+ @loop = loop
15
+ @loop_start = loop_start
16
+ @loop_end = loop_end
17
+ @event_id = nil
18
+ initialize_callback_behavior(
19
+ probability: probability,
20
+ humanize: humanize,
21
+ mute: mute,
22
+ playback_rate: playback_rate
23
+ )
24
+ end
25
+
26
+ def start(time = 0)
27
+ start_time = resolve_time(time)
28
+
29
+ @event_id =
30
+ if @loop && @loop_end
31
+ interval = callback_interval(resolve_time(@loop_end) - resolve_time(@loop_start))
32
+ @transport.schedule_repeat(interval, start_time: start_time + resolve_time(@loop_start)) do |scheduled_time|
33
+ @callback.call(humanized_time(scheduled_time)) if callback_permitted?
34
+ end
35
+ else
36
+ @transport.schedule(start_time) do |scheduled_time|
37
+ @callback.call(humanized_time(scheduled_time)) if callback_permitted?
38
+ end
39
+ end
40
+ mark_started
41
+ self
42
+ end
43
+
44
+ def stop(_time = nil)
45
+ cancel
46
+ end
47
+
48
+ def cancel
49
+ @transport.cancel(event_id: @event_id) if @event_id
50
+ @event_id = nil
51
+ mark_stopped
52
+ self
53
+ end
54
+
55
+ def dispose
56
+ cancel
57
+ end
58
+
59
+ alias loopStart loop_start
60
+ alias loopEnd loop_end
61
+
62
+ def loopStart=(value)
63
+ self.loop_start = value
64
+ end
65
+
66
+ def loopEnd=(value)
67
+ self.loop_end = value
68
+ end
69
+
70
+ private
71
+
72
+ def resolve_time(value)
73
+ Deftones::Music::Time.parse(value)
74
+ end
75
+ end
76
+ end
77
+ end