Class: SFML::SoundRecorder
- Inherits:
-
Object
- Object
- SFML::SoundRecorder
- Defined in:
- lib/sfml/audio/sound_recorder.rb
Overview
Callback-based audio-capture base class. Subclass and override ‘#on_start` (initialise capture state, return `true` to begin), `#on_process_samples(samples, channels)` (called every audio chunk with an Array<Integer> of interleaved int16 PCM, return `true` to keep recording or `false` to stop), and `#on_stop` (release any resources).
The simpler “record to a SoundBuffer” path lives in SFML::SoundBufferRecorder — reach for SoundRecorder only when you need to stream samples somewhere else (file, socket, DSP pipeline).
class LevelMeter < SFML::SoundRecorder
def on_start
@peak = 0
true
end
def on_process_samples(samples, _channels)
@peak = [@peak, *samples.map(&:abs)].max
true
end
def on_stop
puts "Peak: #{@peak}"
end
end
meter = LevelMeter.new
meter.start(sample_rate: 44_100)
sleep 2
meter.stop
CAVEATS
-
All three callbacks run on CSFML’s audio thread; heavy Ruby work on the audio thread will glitch the capture.
-
Always keep a reference to the SoundRecorder object — if the Ruby object is GC’d while CSFML is mid-capture, the process crashes.
Instance Attribute Summary collapse
-
#handle ⇒ Object
readonly
:nodoc:.
Class Method Summary collapse
-
.available? ⇒ Boolean
—- Static helpers (work without a recorder instance) ————.
- .default_device ⇒ Object
-
.devices ⇒ Object
All input devices the OS exposes to SFML, as an Array of String names.
Instance Method Summary collapse
- #channel_count ⇒ Object
- #channel_count=(n) ⇒ Object
-
#channel_map ⇒ Object
The channel layout the recorder is producing, as an Array of ‘sfSoundChannel` enum values (1 = Mono, 2 = FrontLeft, 3 = FrontRight, etc — see SoundBuffer::DEFAULT_CHANNEL_MAPS).
- #device ⇒ Object
- #device=(name) ⇒ Object
-
#initialize ⇒ SoundRecorder
constructor
—- Instance API ————————————————-.
-
#on_process_samples(_samples, _channels) ⇒ Object
Called with each chunk of captured audio.
-
#on_start ⇒ Object
Called once before the capture starts.
-
#on_stop ⇒ Object
Called once after the capture is done.
- #sample_rate ⇒ Object
-
#start(sample_rate: 44_100) ⇒ Object
—- Public recorder API —-.
- #stop ⇒ Object
Constructor Details
#initialize ⇒ SoundRecorder
—- Instance API ————————————————-
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/sfml/audio/sound_recorder.rb', line 63 def initialize # Strong refs so the GC doesn't disappear callbacks under CSFML. @start_cb = FFI::Function.new(:bool, [:pointer]) do |_user| on_start ? true : false end @process_cb = FFI::Function.new(:bool, [:pointer, :size_t, :pointer]) do |samples_ptr, count, _user| samples = count.zero? ? [] : samples_ptr.read_array_of_int16(count) on_process_samples(samples, channel_count) ? true : false end @stop_cb = FFI::Function.new(:void, [:pointer]) do |_user| on_stop nil end ptr = C::Audio.sfSoundRecorder_create(@start_cb, @process_cb, @stop_cb, nil) raise Error, "sfSoundRecorder_create returned NULL" if ptr.null? @handle = FFI::AutoPointer.new(ptr, C::Audio.method(:sfSoundRecorder_destroy)) end |
Instance Attribute Details
#handle ⇒ Object (readonly)
:nodoc:
141 142 143 |
# File 'lib/sfml/audio/sound_recorder.rb', line 141 def handle @handle end |
Class Method Details
.available? ⇒ Boolean
—- Static helpers (work without a recorder instance) ————
42 43 44 |
# File 'lib/sfml/audio/sound_recorder.rb', line 42 def self.available? C::Audio.sfSoundRecorder_isAvailable end |
.default_device ⇒ Object
46 47 48 |
# File 'lib/sfml/audio/sound_recorder.rb', line 46 def self.default_device C::Audio.sfSoundRecorder_getDefaultDevice end |
.devices ⇒ Object
All input devices the OS exposes to SFML, as an Array of String names. Pass any of them to SoundRecorder#device= or SoundBufferRecorder#device= to switch.
53 54 55 56 57 58 59 |
# File 'lib/sfml/audio/sound_recorder.rb', line 53 def self.devices count_buf = FFI::MemoryPointer.new(:size_t) array_ptr = C::Audio.sfSoundRecorder_getAvailableDevices(count_buf) n = count_buf.read(:size_t) return [] if array_ptr.null? || n.zero? array_ptr.read_array_of_pointer(n).map { |p| p.read_string } end |
Instance Method Details
#channel_count ⇒ Object
117 |
# File 'lib/sfml/audio/sound_recorder.rb', line 117 def channel_count = C::Audio.sfSoundRecorder_getChannelCount(@handle) |
#channel_count=(n) ⇒ Object
119 120 121 |
# File 'lib/sfml/audio/sound_recorder.rb', line 119 def channel_count=(n) C::Audio.sfSoundRecorder_setChannelCount(@handle, Integer(n)) end |
#channel_map ⇒ Object
The channel layout the recorder is producing, as an Array of ‘sfSoundChannel` enum values (1 = Mono, 2 = FrontLeft, 3 = FrontRight, etc — see SoundBuffer::DEFAULT_CHANNEL_MAPS).
133 134 135 136 137 138 139 |
# File 'lib/sfml/audio/sound_recorder.rb', line 133 def channel_map count_buf = FFI::MemoryPointer.new(:size_t) ptr = C::Audio.sfSoundRecorder_getChannelMap(@handle, count_buf) n = count_buf.read(:size_t) return [] if ptr.null? || n.zero? ptr.read_array_of_int32(n) end |
#device ⇒ Object
123 |
# File 'lib/sfml/audio/sound_recorder.rb', line 123 def device = C::Audio.sfSoundRecorder_getDevice(@handle) |
#device=(name) ⇒ Object
125 126 127 128 |
# File 'lib/sfml/audio/sound_recorder.rb', line 125 def device=(name) C::Audio.sfSoundRecorder_setDevice(@handle, name.to_s) || raise(Error, "sfSoundRecorder_setDevice failed for #{name.inspect}") end |
#on_process_samples(_samples, _channels) ⇒ Object
Called with each chunk of captured audio. ‘samples` is an Array<Integer> of interleaved int16 PCM (length = frames * channels). Return `true` to keep capturing or `false` to stop. Default raises so subclasses must implement it.
96 97 98 |
# File 'lib/sfml/audio/sound_recorder.rb', line 96 def on_process_samples(_samples, _channels) raise NoMethodError, "#{self.class} must override #on_process_samples" end |
#on_start ⇒ Object
Called once before the capture starts. Return ‘true` to begin the capture or `false` to abort. Default does nothing and accepts the start.
88 89 90 |
# File 'lib/sfml/audio/sound_recorder.rb', line 88 def on_start true end |
#on_stop ⇒ Object
Called once after the capture is done. Default is a no-op.
101 |
# File 'lib/sfml/audio/sound_recorder.rb', line 101 def on_stop; end |
#sample_rate ⇒ Object
116 |
# File 'lib/sfml/audio/sound_recorder.rb', line 116 def sample_rate = C::Audio.sfSoundRecorder_getSampleRate(@handle) |
#start(sample_rate: 44_100) ⇒ Object
—- Public recorder API —-
105 106 107 108 109 |
# File 'lib/sfml/audio/sound_recorder.rb', line 105 def start(sample_rate: 44_100) C::Audio.sfSoundRecorder_start(@handle, Integer(sample_rate)) || raise(Error, "sfSoundRecorder_start failed (no input device or driver error)") self end |