Module: Deftones::PortAudioSupport
- Defined in:
- lib/deftones/portaudio_support.rb
Class Method Summary collapse
- .acquire! ⇒ Object
- .available? ⇒ Boolean
- .build_stream_parameters(direction:, channels:, device_id: nil, label: nil, sample_rate: nil) ⇒ Object private
- .check_error!(result, fallback: nil) ⇒ Object
- .default_device(direction) ⇒ Object private
- .detect_sample_rate_mismatch!(device, requested_sample_rate) ⇒ Object private
- .input_parameters(channels, device_id: nil, label: nil, sample_rate: nil) ⇒ Object
- .load_backend! ⇒ Object private
- .matches_device_id?(device, device_id) ⇒ Boolean private
- .matches_device_label?(device, label) ⇒ Boolean private
- .mutex ⇒ Object private
- .output_parameters(channels, device_id: nil, label: nil, sample_rate: nil) ⇒ Object
- .ref_count ⇒ Object private
- .release ⇒ Object
- .resolve_device(direction:, device_id:, label:) ⇒ Object private
- .suggested_latency(device, direction) ⇒ Object private
Class Method Details
.acquire! ⇒ Object
13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/deftones/portaudio_support.rb', line 13 def acquire! raise Deftones::MissingRealtimeBackendError, "PortAudio backend is unavailable" unless available? mutex.synchronize do PortAudio.init if ref_count.zero? @ref_count = ref_count + 1 end self rescue StandardError => error raise Deftones::MissingRealtimeBackendError, error. end |
.available? ⇒ Boolean
8 9 10 11 |
# File 'lib/deftones/portaudio_support.rb', line 8 def available? load_backend! !!defined?(PortAudio) end |
.build_stream_parameters(direction:, channels:, device_id: nil, label: nil, sample_rate: nil) ⇒ Object (private)
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/deftones/portaudio_support.rb', line 84 def build_stream_parameters(direction:, channels:, device_id: nil, label: nil, sample_rate: nil) device = resolve_device(direction: direction, device_id: device_id, label: label) raise Deftones::MissingRealtimeBackendError, "No default #{direction} device available" unless device detect_sample_rate_mismatch!(device, sample_rate) if sample_rate { device: device, channels: channels, format: :float32, latency: suggested_latency(device, direction) } rescue StandardError => error raise Deftones::MissingRealtimeBackendError, error. end |
.check_error!(result, fallback: nil) ⇒ Object
59 60 61 62 63 |
# File 'lib/deftones/portaudio_support.rb', line 59 def check_error!(result, fallback: nil) PortAudio.check_error!(result) rescue StandardError => error raise Deftones::MissingRealtimeBackendError, fallback || error. end |
.default_device(direction) ⇒ Object (private)
121 122 123 124 125 126 127 |
# File 'lib/deftones/portaudio_support.rb', line 121 def default_device(direction) case direction when :input then PortAudio::Device.default_input when :output then PortAudio::Device.default_output else raise ArgumentError, "Unsupported PortAudio direction: #{direction}" end end |
.detect_sample_rate_mismatch!(device, requested_sample_rate) ⇒ Object (private)
156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/deftones/portaudio_support.rb', line 156 def detect_sample_rate_mismatch!(device, requested_sample_rate) device_sample_rate = if device.respond_to?(:default_sample_rate) device.default_sample_rate elsif device.respond_to?(:sample_rate) device.sample_rate end return unless device_sample_rate return if device_sample_rate.to_f == requested_sample_rate.to_f raise Deftones::MissingRealtimeBackendError, "PortAudio device sample rate #{device_sample_rate} does not match requested #{requested_sample_rate}" end |
.input_parameters(channels, device_id: nil, label: nil, sample_rate: nil) ⇒ Object
49 50 51 52 53 54 55 56 57 |
# File 'lib/deftones/portaudio_support.rb', line 49 def input_parameters(channels, device_id: nil, label: nil, sample_rate: nil) build_stream_parameters( direction: :input, channels: channels, device_id: device_id, label: label, sample_rate: sample_rate ) end |
.load_backend! ⇒ Object (private)
67 68 69 70 71 72 73 74 |
# File 'lib/deftones/portaudio_support.rb', line 67 def load_backend! return true if defined?(PortAudio) require "portaudio" true rescue LoadError false end |
.matches_device_id?(device, device_id) ⇒ Boolean (private)
129 130 131 132 133 134 135 136 137 |
# File 'lib/deftones/portaudio_support.rb', line 129 def matches_device_id?(device, device_id) return false if device_id.nil? candidates = [] candidates << device.device_id if device.respond_to?(:device_id) candidates << device.index if device.respond_to?(:index) candidates << device.device_index if device.respond_to?(:device_index) candidates.compact.any? { |candidate| candidate.to_s == device_id.to_s } end |
.matches_device_label?(device, label) ⇒ Boolean (private)
139 140 141 142 143 144 145 146 147 |
# File 'lib/deftones/portaudio_support.rb', line 139 def matches_device_label?(device, label) return false if label.nil? candidates = [] candidates << device.label if device.respond_to?(:label) candidates << device.name if device.respond_to?(:name) matcher = label.is_a?(Regexp) ? label : Regexp.new(Regexp.escape(label.to_s), Regexp::IGNORECASE) candidates.compact.any? { |candidate| candidate.to_s.match?(matcher) } end |
.mutex ⇒ Object (private)
76 77 78 |
# File 'lib/deftones/portaudio_support.rb', line 76 def mutex @mutex ||= Mutex.new end |
.output_parameters(channels, device_id: nil, label: nil, sample_rate: nil) ⇒ Object
39 40 41 42 43 44 45 46 47 |
# File 'lib/deftones/portaudio_support.rb', line 39 def output_parameters(channels, device_id: nil, label: nil, sample_rate: nil) build_stream_parameters( direction: :output, channels: channels, device_id: device_id, label: label, sample_rate: sample_rate ) end |
.ref_count ⇒ Object (private)
80 81 82 |
# File 'lib/deftones/portaudio_support.rb', line 80 def ref_count @ref_count ||= 0 end |
.release ⇒ Object
25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/deftones/portaudio_support.rb', line 25 def release return unless available? mutex.synchronize do return self if ref_count.zero? @ref_count = ref_count - 1 PortAudio.terminate if ref_count.zero? end self rescue StandardError nil end |
.resolve_device(direction:, device_id:, label:) ⇒ Object (private)
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/deftones/portaudio_support.rb', line 101 def resolve_device(direction:, device_id:, label:) return default_device(direction) if device_id.nil? && label.nil? devices = if PortAudio::Device.respond_to?(:all) Array(PortAudio::Device.all) elsif PortAudio::Device.respond_to?(:devices) Array(PortAudio::Device.devices) else [] end matched = devices.find do |device| matches_device_id?(device, device_id) || matches_device_label?(device, label) end return matched if matched raise Deftones::MissingRealtimeBackendError, "No matching #{direction} device available" end |
.suggested_latency(device, direction) ⇒ Object (private)
149 150 151 152 153 154 |
# File 'lib/deftones/portaudio_support.rb', line 149 def suggested_latency(device, direction) method_name = direction == :input ? :default_low_input_latency : :default_low_output_latency return device.public_send(method_name) if device.respond_to?(method_name) 0.05 end |