Module: Vizcore::Audio::PortAudioFFI

Extended by:
PortAudioFFI
Included in:
PortAudioFFI
Defined in:
lib/vizcore/audio/portaudio_ffi.rb

Overview

PortAudio FFI bridge for microphone stream access and device discovery.

Defined Under Namespace

Classes: Stream

Constant Summary collapse

DEFAULT_CHANNELS =

Default mono channel count.

1
PA_NO_ERROR =

PortAudio no-error status code.

0
PA_FLOAT_32 =

PortAudio float sample format code.

0x0000_0001
PA_NO_FLAGS =

PortAudio stream flags.

0

Instance Method Summary collapse

Instance Method Details

#available?Boolean

Returns true when PortAudio native library can be loaded.

Returns:

  • (Boolean)

    true when PortAudio native library can be loaded



128
129
130
# File 'lib/vizcore/audio/portaudio_ffi.rb', line 128

def available?
  !ffi_module.nil?
end

#close_stream(stream) ⇒ nil

Parameters:

Returns:

  • (nil)


237
238
239
240
241
242
# File 'lib/vizcore/audio/portaudio_ffi.rb', line 237

def close_stream(stream)
  stream&.close
  ffi_module&.Pa_Terminate
rescue StandardError
  nil
end

#input_devicesArray<Hash>

Returns available input device descriptors.

Returns:

  • (Array<Hash>)

    available input device descriptors



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/vizcore/audio/portaudio_ffi.rb', line 133

def input_devices
  mod = ffi_module
  return [] unless mod
  return [] unless ok?(mod.Pa_Initialize)

  count = mod.Pa_GetDeviceCount
  return [] if count <= 0

  count.times.filter_map do |index|
    pointer = mod.Pa_GetDeviceInfo(index)
    next if pointer.null?

    info = mod::DeviceInfo.new(pointer)
    next unless info[:maxInputChannels].positive?

    {
      index: index,
      name: info[:name].read_string,
      max_input_channels: info[:maxInputChannels],
      default_sample_rate: info[:defaultSampleRate].to_f
    }
  end
ensure
  mod&.Pa_Terminate
end

#open_default_input_stream(sample_rate:, channels: DEFAULT_CHANNELS, frames_per_buffer: 1024) ⇒ Vizcore::Audio::PortAudioFFI::Stream?

Parameters:

  • sample_rate (Float)
  • channels (Integer) (defaults to: DEFAULT_CHANNELS)
  • frames_per_buffer (Integer) (defaults to: 1024)

Returns:



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/vizcore/audio/portaudio_ffi.rb', line 163

def open_default_input_stream(sample_rate:, channels: DEFAULT_CHANNELS, frames_per_buffer: 1024)
  mod = ffi_module
  return nil unless mod
  return nil unless ok?(mod.Pa_Initialize)

  stream_ptr_ptr = ffi::MemoryPointer.new(:pointer)

  result = mod.Pa_OpenDefaultStream(
    stream_ptr_ptr,
    Integer(channels),
    0,
    PA_FLOAT_32,
    Float(sample_rate),
    Integer(frames_per_buffer),
    nil,
    nil
  )
  return terminate_with_nil(mod) unless ok?(result)

  stream_pointer = stream_ptr_ptr.read_pointer
  return terminate_with_nil(mod) if stream_pointer.null?

  Stream.new(mod: mod, pointer: stream_pointer, channels: Integer(channels))
rescue StandardError
  mod&.Pa_Terminate
  nil
end

#open_input_stream(device:, sample_rate:, channels: DEFAULT_CHANNELS, frames_per_buffer: 1024) ⇒ Vizcore::Audio::PortAudioFFI::Stream?

Parameters:

  • device (String, Integer)

    PortAudio device index or case-insensitive name fragment

  • sample_rate (Float)
  • channels (Integer) (defaults to: DEFAULT_CHANNELS)
  • frames_per_buffer (Integer) (defaults to: 1024)

Returns:



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/vizcore/audio/portaudio_ffi.rb', line 196

def open_input_stream(device:, sample_rate:, channels: DEFAULT_CHANNELS, frames_per_buffer: 1024)
  mod = ffi_module
  return nil unless mod
  return nil unless ok?(mod.Pa_Initialize)

  device_info = resolve_input_device(mod, device)
  return terminate_with_nil(mod) unless device_info

  device_index, info = device_info
  actual_channels = [Integer(channels), info[:maxInputChannels]].min
  input_params = mod::StreamParameters.new
  input_params[:device] = device_index
  input_params[:channelCount] = actual_channels
  input_params[:sampleFormat] = PA_FLOAT_32
  input_params[:suggestedLatency] = info[:defaultLowInputLatency].to_f
  input_params[:hostApiSpecificStreamInfo] = nil

  stream_ptr_ptr = ffi::MemoryPointer.new(:pointer)
  result = mod.Pa_OpenStream(
    stream_ptr_ptr,
    input_params.to_ptr,
    nil,
    Float(sample_rate),
    Integer(frames_per_buffer),
    PA_NO_FLAGS,
    nil,
    nil
  )
  return terminate_with_nil(mod) unless ok?(result)

  stream_pointer = stream_ptr_ptr.read_pointer
  return terminate_with_nil(mod) if stream_pointer.null?

  Stream.new(mod: mod, pointer: stream_pointer, channels: actual_channels)
rescue StandardError
  mod&.Pa_Terminate
  nil
end