Class: Deftones::Source::ToneBufferSource

Inherits:
Player show all
Defined in:
lib/deftones/source/tone_buffer_source.rb

Instance Attribute Summary collapse

Attributes inherited from Player

#autostart, #buffer, #loop, #loop_end, #loop_start, #playback_rate, #reverse

Attributes inherited from Core::Source

#mute, #onstop, #volume

Instance Method Summary collapse

Methods inherited from Player

#dispose, #fadeIn=, #fadeOut=, #integrated_position_at_frame, #loaded, #loaded?, #loopEnd=, #loopStart=, #loop_end_frame, #looped_position, #looped_reverse_position, #multichannel_process?, #notify_stop, #onstop, #onstop=, #playbackRate=, #resolve_buffer_position, #restart, #reverse_position, #sample_positions_for, #seek

Methods inherited from Core::Source

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

Constructor Details

#initialize(buffer:, playback_rate: 1.0, detune: 0.0, fade_in: 0.0, fade_out: 0.0, curve: :linear, context: Deftones.context, **options) ⇒ ToneBufferSource

Returns a new instance of ToneBufferSource.



9
10
11
12
13
14
15
16
17
18
19
# File 'lib/deftones/source/tone_buffer_source.rb', line 9

def initialize(buffer:, playback_rate: 1.0, detune: 0.0, fade_in: 0.0, fade_out: 0.0, curve: :linear,
               context: Deftones.context, **options)
  super(buffer: buffer, playback_rate: playback_rate, context: context, **options)
  @detune = Core::Signal.new(value: detune, units: :number, context: context)
  @fade_in = fade_in.to_f
  @fade_out = fade_out.to_f
  @curve = curve.to_sym
  @start_gain = 1.0
  @onended = nil
  @ended_notified = false
end

Instance Attribute Details

#curveObject

Returns the value of attribute curve.



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

def curve
  @curve
end

#detuneObject

Returns the value of attribute detune.



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

def detune
  @detune
end

#fade_inObject

Returns the value of attribute fade_in.



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

def fade_in
  @fade_in
end

#fade_outObject

Returns the value of attribute fade_out.



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

def fade_out
  @fade_out
end

#onendedObject

Returns the value of attribute onended.



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

def onended
  @onended
end

#start_gainObject (readonly)

Returns the value of attribute start_gain.



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

def start_gain
  @start_gain
end

Instance Method Details

#cancel_stopObject



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

def cancel_stop
  @stop_time = nil
  self
end

#detune_ratio(cents) ⇒ Object (private)



102
103
104
# File 'lib/deftones/source/tone_buffer_source.rb', line 102

def detune_ratio(cents)
  2.0**(cents.to_f / 1200.0)
end

#effective_playback_rates(rates, detunes) ⇒ Object (private)



98
99
100
# File 'lib/deftones/source/tone_buffer_source.rb', line 98

def effective_playback_rates(rates, detunes)
  rates.each_with_index.map { |rate, index| rate * detune_ratio(detunes[index]) }
end

#envelope_gain(current_time) ⇒ Object (private)



106
107
108
109
110
111
# File 'lib/deftones/source/tone_buffer_source.rb', line 106

def envelope_gain(current_time)
  gain = @start_gain
  gain *= fade_in_gain(current_time)
  gain *= fade_out_gain(current_time)
  gain
end

#fade_in_gain(current_time) ⇒ Object (private)



113
114
115
116
117
118
# File 'lib/deftones/source/tone_buffer_source.rb', line 113

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

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

#fade_out_gain(current_time) ⇒ Object (private)



120
121
122
123
124
125
# File 'lib/deftones/source/tone_buffer_source.rb', line 120

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

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

#notify_ended(current_time) ⇒ Object (private)



134
135
136
137
138
139
# File 'lib/deftones/source/tone_buffer_source.rb', line 134

def notify_ended(current_time)
  return if @ended_notified

  @ended_notified = true
  @onended&.call(current_time)
end

#playback_rate=(value) ⇒ Object



21
22
23
# File 'lib/deftones/source/tone_buffer_source.rb', line 21

def playback_rate=(value)
  @playback_rate.value = value
end

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



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/deftones/source/tone_buffer_source.rb', line 47

def process(_input_buffer, num_frames, start_frame, _cache)
  rates = @playback_rate.process(num_frames, start_frame)
  detunes = @detune.process(num_frames, start_frame)
  effective_rates = effective_playback_rates(rates, detunes)
  return process_multichannel_buffer(num_frames, start_frame, effective_rates) if multichannel_process?

  sample_positions = sample_positions_for(num_frames, start_frame, effective_rates)

  Array.new(num_frames) do |index|
    current_time = (start_frame + index).to_f / context.sample_rate
    notify_ended(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_ended(current_time)
      next 0.0
    end

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

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



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/deftones/source/tone_buffer_source.rb', line 73

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

  num_frames.times do |index|
    current_time = (start_frame + index).to_f / context.sample_rate
    notify_ended(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_ended(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

#shaped_gain(progress) ⇒ Object (private)



127
128
129
130
131
132
# File 'lib/deftones/source/tone_buffer_source.rb', line 127

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, gain: 1.0) ⇒ Object



29
30
31
32
33
34
35
36
# File 'lib/deftones/source/tone_buffer_source.rb', line 29

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

#state(time = context.current_time) ⇒ Object



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

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