Module: Wavify::DSP::Processor
- Defined in:
- lib/wavify/dsp/processor.rb,
sig/dsp.rbs
Overview
Shared invocation and result contract for stateful audio processors.
Constant Summary collapse
- TAIL_CHUNK_FRAMES =
Maximum number of frames requested from a processor tail at once.
4_096
Class Method Summary collapse
- .build_runtime(processor) ⇒ Object
- .coerce_buffer(result, context = "processor") ⇒ Object
- .duration(processor, method_name) ⇒ Float
- .each_buffer(result, context = "processor") ⇒ Object
- .flush(processor, format: nil) ⇒ Enumerator[Core::SampleBuffer, nil]
- .process(processor, buffer) ⇒ Core::SampleBuffer
- .render(processor, buffer) ⇒ Core::SampleBuffer
Class Method Details
.build_runtime(processor) ⇒ Object
49 50 51 52 53 54 55 56 57 |
# File 'lib/wavify/dsp/processor.rb', line 49 def build_runtime(processor) return processor.build_runtime if processor.respond_to?(:build_runtime) runtime = processor.dup runtime.reset if runtime.respond_to?(:reset) runtime rescue TypeError raise InvalidParameterError, "stateful processor must implement #build_runtime or support #dup" end |
.coerce_buffer(result, context = "processor") ⇒ Object
42 43 44 45 46 47 |
# File 'lib/wavify/dsp/processor.rb', line 42 def coerce_buffer(result, context = "processor") return result.buffer if defined?(Wavify::Audio) && result.is_a?(Wavify::Audio) return result if result.is_a?(Core::SampleBuffer) raise ProcessingError, "#{context} must return Core::SampleBuffer or Audio" end |
.duration(processor, method_name) ⇒ Float
59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/wavify/dsp/processor.rb', line 59 def duration(processor, method_name) return 0.0 unless processor.respond_to?(method_name) value = processor.public_send(method_name) return 0.0 if value.nil? unless value.is_a?(Numeric) && value.respond_to?(:finite?) && value.finite? && value >= 0.0 raise InvalidParameterError, "#{method_name} must return a non-negative finite Numeric" end value.to_f end |
.each_buffer(result, context = "processor") ⇒ Object
32 33 34 35 36 37 38 39 40 |
# File 'lib/wavify/dsp/processor.rb', line 32 def each_buffer(result, context = "processor") return [].each if result.nil? return [coerce_buffer(result, context)].each if buffer?(result) unless result.respond_to?(:each) raise ProcessingError, "#{context} must return Core::SampleBuffer, Audio, an Enumerable of them, or nil" end result.lazy.map { |item| coerce_buffer(item, context) } end |
.flush(processor, format: nil) ⇒ Enumerator[Core::SampleBuffer, nil]
25 26 27 28 29 30 |
# File 'lib/wavify/dsp/processor.rb', line 25 def flush(processor, format: nil) return [].each unless processor.respond_to?(:flush) result = processor.flush(format: format) each_buffer(result, "processor flush") end |
.process(processor, buffer) ⇒ Core::SampleBuffer
12 13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/wavify/dsp/processor.rb', line 12 def process(processor, buffer) result = if processor.respond_to?(:process) processor.process(buffer) elsif processor.respond_to?(:call) processor.call(buffer) elsif processor.respond_to?(:apply) processor.apply(buffer) else raise InvalidParameterError, "processor must respond to :process, :call, or :apply" end coerce_buffer(result, "processor") end |
.render(processor, buffer) ⇒ Core::SampleBuffer
71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/wavify/dsp/processor.rb', line 71 def render(processor, buffer) runtime = build_runtime(processor) processed = process(runtime, buffer) chunks = [processed] chunks.concat(flush(runtime, format: processed.format).to_a) combined = chunks.reduce { |left, right| left.concat(right) } latency_frames = (duration(runtime, :latency) * combined.format.sample_rate).round return combined if latency_frames.zero? combined.slice(latency_frames, [combined.sample_frame_count - latency_frames, 0].max) end |