Class: Deftones::Source::Player

Inherits:
Core::Source show all
Defined in:
lib/deftones/source/player.rb

Direct Known Subclasses

GrainPlayer, ToneBufferSource

Instance Attribute Summary collapse

Attributes inherited from Core::Source

#mute, #volume

Instance Method Summary collapse

Methods inherited from Core::Source

#active_at?, #apply_volume!, #cancel_stop, #clear_transport_event, #mute?, #notify_stop_in_window, #number_of_inputs, #render, #render_block, #resolve_time, #resolve_transport_time, #schedule_transport_event, #source_type, #stop, #sync, #synced?, #unsync, #uses_legacy_render_for_block?

Constructor Details

#initialize(buffer:, playback_rate: 1.0, loop: false, loop_start: 0.0, loop_end: nil, reverse: false, fade_in: 0.0, fade_out: 0.0, curve: :linear, autostart: false, onstop: nil, context: Deftones.context) ⇒ Player

Returns a new instance of Player.



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/deftones/source/player.rb', line 10

def initialize(buffer:, playback_rate: 1.0, loop: false, loop_start: 0.0, loop_end: nil,
               reverse: false, fade_in: 0.0, fade_out: 0.0, curve: :linear,
               autostart: false, onstop: nil, context: Deftones.context)
  super(context: context)
  @buffer = buffer.is_a?(IO::Buffer) ? buffer : IO::Buffer.load(buffer)
  @playback_rate = Core::Signal.new(value: playback_rate, units: :number, context: context)
  @loop = loop
  @loop_start = loop_start.to_f
  @loop_end = loop_end
  @reverse = reverse
  @fade_in = fade_in.to_f
  @fade_out = fade_out.to_f
  @curve = curve.to_sym
  @autostart = !!autostart
  @onstop = onstop
  @seek_position = 0.0
  @start_time = Float::INFINITY
  @stop_notified = false
  start(0.0) if @autostart
end

Instance Attribute Details

#autostartObject

Returns the value of attribute autostart.



8
9
10
# File 'lib/deftones/source/player.rb', line 8

def autostart
  @autostart
end

#bufferObject (readonly)

Returns the value of attribute buffer.



6
7
8
# File 'lib/deftones/source/player.rb', line 6

def buffer
  @buffer
end

#curveObject

Returns the value of attribute curve.



7
8
9
# File 'lib/deftones/source/player.rb', line 7

def curve
  @curve
end

#fade_inObject Also known as: fadeIn

Returns the value of attribute fade_in.



7
8
9
# File 'lib/deftones/source/player.rb', line 7

def fade_in
  @fade_in
end

#fade_outObject Also known as: fadeOut

Returns the value of attribute fade_out.



7
8
9
# File 'lib/deftones/source/player.rb', line 7

def fade_out
  @fade_out
end

#loopObject

Returns the value of attribute loop.



7
8
9
# File 'lib/deftones/source/player.rb', line 7

def loop
  @loop
end

#loop_endObject Also known as: loopEnd

Returns the value of attribute loop_end.



7
8
9
# File 'lib/deftones/source/player.rb', line 7

def loop_end
  @loop_end
end

#loop_startObject Also known as: loopStart

Returns the value of attribute loop_start.



7
8
9
# File 'lib/deftones/source/player.rb', line 7

def loop_start
  @loop_start
end

#playback_rateObject Also known as: playbackRate

Returns the value of attribute playback_rate.



6
7
8
# File 'lib/deftones/source/player.rb', line 6

def playback_rate
  @playback_rate
end

#reverseObject

Returns the value of attribute reverse.



7
8
9
# File 'lib/deftones/source/player.rb', line 7

def reverse
  @reverse
end

Instance Method Details

#disposeObject



51
52
53
54
# File 'lib/deftones/source/player.rb', line 51

def dispose
  @buffer = nil
  super
end

#envelope_gain(current_time) ⇒ Object (private)



133
134
135
# File 'lib/deftones/source/player.rb', line 133

def envelope_gain(current_time)
  fade_in_gain(current_time) * fade_out_gain(current_time)
end

#fade_in_gain(current_time) ⇒ Object (private)



137
138
139
140
141
# File 'lib/deftones/source/player.rb', line 137

def fade_in_gain(current_time)
  return 1.0 if @fade_in <= 0.0

  shaped_gain((current_time - @start_time) / @fade_in)
end

#fade_out_gain(current_time) ⇒ Object (private)



143
144
145
146
147
# File 'lib/deftones/source/player.rb', line 143

def fade_out_gain(current_time)
  return 1.0 unless @stop_time && @fade_out > 0.0

  shaped_gain((@stop_time - current_time) / @fade_out)
end

#fadeIn=(value) ⇒ Object



119
120
121
# File 'lib/deftones/source/player.rb', line 119

def fadeIn=(value)
  self.fade_in = value
end

#fadeOut=(value) ⇒ Object



123
124
125
# File 'lib/deftones/source/player.rb', line 123

def fadeOut=(value)
  self.fade_out = value
end

#integrated_position_at_frame(frame_index) ⇒ Object (private)



201
202
203
204
205
206
207
208
# File 'lib/deftones/source/player.rb', line 201

def integrated_position_at_frame(frame_index)
  start_frame = (@start_time * context.sample_rate).floor
  frames_to_integrate = [frame_index - start_frame, 0].max
  return @seek_position if frames_to_integrate.zero?

  rates = @playback_rate.process(frames_to_integrate, start_frame)
  @seek_position + (rates.sum * (@buffer.sample_rate.to_f / context.sample_rate))
end

#loadedObject



47
48
49
# File 'lib/deftones/source/player.rb', line 47

def loaded
  loaded?
end

#loaded?Boolean

Returns:

  • (Boolean)


43
44
45
# File 'lib/deftones/source/player.rb', line 43

def loaded?
  !@buffer.nil?
end

#loop_end_frameObject (private)



239
240
241
242
243
# File 'lib/deftones/source/player.rb', line 239

def loop_end_frame
  return unless @loop_end

  Deftones::Music::Time.parse(@loop_end) * @buffer.sample_rate
end

#looped_position(position) ⇒ Object (private)



227
228
229
230
231
232
# File 'lib/deftones/source/player.rb', line 227

def looped_position(position)
  min_position = @loop_start * @buffer.sample_rate
  max_position = loop_end_frame || @buffer.frames
  span = [max_position - min_position, 1.0].max
  min_position + ((position - min_position) % span)
end

#looped_reverse_position(position, min_position, max_position) ⇒ Object (private)



234
235
236
237
# File 'lib/deftones/source/player.rb', line 234

def looped_reverse_position(position, min_position, max_position)
  span = [max_position - min_position, 1.0].max
  min_position + ((position - min_position) % span)
end

#loopEnd=(value) ⇒ Object



115
116
117
# File 'lib/deftones/source/player.rb', line 115

def loopEnd=(value)
  self.loop_end = value
end

#loopStart=(value) ⇒ Object



111
112
113
# File 'lib/deftones/source/player.rb', line 111

def loopStart=(value)
  self.loop_start = value
end

#multichannel_process?Boolean (private)

Returns:

  • (Boolean)


129
130
131
# File 'lib/deftones/source/player.rb', line 129

def multichannel_process?
  @buffer && @buffer.channels > 1
end

#notify_stop(current_time) ⇒ Object (private)



156
157
158
159
160
161
# File 'lib/deftones/source/player.rb', line 156

def notify_stop(current_time)
  return if @stop_notified

  @stop_notified = true
  @onstop&.call(current_time)
end

#onstopObject



35
36
37
# File 'lib/deftones/source/player.rb', line 35

def onstop
  @onstop
end

#onstop=(callback) ⇒ Object



39
40
41
# File 'lib/deftones/source/player.rb', line 39

def onstop=(callback)
  @onstop = callback
end

#playbackRate=(value) ⇒ Object



107
108
109
# File 'lib/deftones/source/player.rb', line 107

def playbackRate=(value)
  self.playback_rate = value
end

#process(_input_buffer, num_frames, start_frame, _cache) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/deftones/source/player.rb', line 80

def process(_input_buffer, num_frames, start_frame, _cache)
  rates = @playback_rate.process(num_frames, start_frame)
  sample_positions = sample_positions_for(num_frames, start_frame, rates)
  return process_multichannel_buffer(num_frames, start_frame, rates) if multichannel_process?

  Array.new(num_frames) do |index|
    current_time = (start_frame + index).to_f / context.sample_rate
    notify_stop(current_time) if @stop_time && current_time >= @stop_time
    next 0.0 unless active_at?(current_time)

    sample_position = sample_positions[index]
    if sample_position.negative?
      @stop_time ||= current_time
      notify_stop(current_time)
      next 0.0
    end

    @buffer.sample_at(sample_position) * envelope_gain(current_time)
  end
end

#process_multichannel_buffer(num_frames, start_frame, rates) ⇒ Object (private)



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/deftones/source/player.rb', line 163

def process_multichannel_buffer(num_frames, start_frame, rates)
  output = Array.new(@buffer.channels) { Array.new(num_frames, 0.0) }
  sample_positions = sample_positions_for(num_frames, start_frame, rates)

  num_frames.times do |index|
    current_time = (start_frame + index).to_f / context.sample_rate
    notify_stop(current_time) if @stop_time && current_time >= @stop_time
    next unless active_at?(current_time)

    sample_position = sample_positions[index]
    if sample_position.negative?
      @stop_time ||= current_time
      notify_stop(current_time)
      next
    end

    gain = envelope_gain(current_time)
    @buffer.channels.times do |channel_index|
      output[channel_index][index] = @buffer.sample_at(sample_position, channel_index) * gain
    end
  end

  Core::AudioBlock.from_channel_data(output)
end

#resolve_buffer_position(base_position) ⇒ Object (private)



210
211
212
213
214
215
216
# File 'lib/deftones/source/player.rb', line 210

def resolve_buffer_position(base_position)
  return -1 if base_position.negative?
  return reverse_position(base_position) if @reverse
  return looped_position(base_position) if @loop

  base_position < @buffer.frames ? base_position : -1
end

#restart(time = nil, offset = nil, duration = nil) ⇒ Object



64
65
66
67
# File 'lib/deftones/source/player.rb', line 64

def restart(time = nil, offset = nil, duration = nil)
  stop(time)
  start(time, offset, duration)
end

#reverse_position(base_position) ⇒ Object (private)



218
219
220
221
222
223
224
225
# File 'lib/deftones/source/player.rb', line 218

def reverse_position(base_position)
  max_position = loop_end_frame || (@buffer.frames - 1)
  min_position = @loop_start * @buffer.sample_rate
  position = max_position - base_position
  return looped_reverse_position(position, min_position, max_position) if @loop

  position
end

#sample_positions_for(num_frames, start_frame, rates) ⇒ Object (private)



188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/deftones/source/player.rb', line 188

def sample_positions_for(num_frames, start_frame, rates)
  return Array.new(num_frames, -1.0) if @start_time.infinite?

  ratio = @buffer.sample_rate.to_f / context.sample_rate
  base_position = integrated_position_at_frame(start_frame)

  Array.new(num_frames) do |index|
    position = resolve_buffer_position(base_position)
    base_position += rates[index].to_f * ratio
    position
  end
end

#seek(time = nil) ⇒ Object



73
74
75
76
77
78
# File 'lib/deftones/source/player.rb', line 73

def seek(time = nil)
  return @seek_position.to_f / @buffer.sample_rate if time.nil?

  @seek_position = Deftones::Music::Time.parse(time) * @buffer.sample_rate
  self
end

#shaped_gain(progress) ⇒ Object (private)



149
150
151
152
153
154
# File 'lib/deftones/source/player.rb', line 149

def shaped_gain(progress)
  bounded = progress.clamp(0.0, 1.0)
  return bounded if @curve == :linear

  Math.sin(bounded * Math::PI * 0.5)
end

#start(time = nil, offset = nil, duration = nil) ⇒ Object



56
57
58
59
60
61
62
# File 'lib/deftones/source/player.rb', line 56

def start(time = nil, offset = nil, duration = nil)
  seek(offset) if offset
  @stop_notified = false
  super(time)
  self.stop(@start_time + Deftones::Music::Time.parse(duration)) if duration
  self
end

#state(time = context.current_time) ⇒ Object



69
70
71
# File 'lib/deftones/source/player.rb', line 69

def state(time = context.current_time)
  active_at?(resolve_time(time)) ? :started : :stopped
end