class SFML::SoundStream

Procedural audio source. Subclass it and override on_get_data to fill each chunk on demand β€” CSFML invokes the callback from its audio thread whenever the sound queue runs low.

on_get_data returns either: * an Array (or anything responding to to_a) of Int16 PCM samples β€” they’re packed into a buffer and handed to CSFML; * nil to stop the stream.

Override on_seek(time) to support playing_offset=. Default is a no-op (the next callback continues from wherever the subclass internal state happens to be).

class SineWave < SFML::SoundStream def initialize(freq:, sample_rate: 44_100) super(channel_count: 1, sample_rate: sample_rate) @freq, @sample_rate = freq, sample_rate @phase = 0.0 end

def on_get_data
  n = @sample_rate / 10   # ~100ms chunks
  step = 2 * Math::PI * @freq / @sample_rate
  Array.new(n) do
    s = (Math.sin(@phase) * 30_000).to_i
    @phase = (@phase + step) % (2 * Math::PI)
    s
  end
end

def on_seek(time)
  @phase = 0.0
end

end

CAVEATS * The callback runs on the SFML audio thread; doing heavy work there will glitch the audio. Generate samples and return. * Ruby threads still need the GVL β€” your callback acquires it each invocation. Long Ruby work on the main thread can starve the audio thread and produce dropouts. * Always keep a reference to the SoundStream object (assign to a variable, store in an instance var). If the Ruby object is GC’d while CSFML is still calling callbacks, the process crashes.