sfml3.rb

Class: SFML::SoundBuffer

Inherits:
Object
  • Object
show all
Defined in:
ext/audio/sound_buffer.c,
ext/audio/sound_buffer.c

Overview

Audio samples held fully decoded in memory, ready to be played through one or more Sound instances (a single buffer may back several simultaneous Sounds). For long audio, prefer Music, which streams instead of loading everything up front.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.from_file(path) ⇒ SoundBuffer

Loads and decodes an audio file into a buffer.

Returns:

Returns:

Raises:

  • (RuntimeError) —

    if the file cannot be opened or decoded



72
73
74
# File 'ext/audio/sound_buffer.c', line 72

static VALUE SoundBuffer_from_file(VALUE klass, VALUE rb_path) {
    return SoundBuffer_wrap(klass, sfSoundBuffer_createFromFile(StringValueCStr(rb_path)));
}

.from_memory(data) ⇒ SoundBuffer

Decodes audio held in a String into a buffer.

Returns:

Returns:

Raises:

  • (RuntimeError) —

    if data cannot be decoded



84
85
86
87
88
89
# File 'ext/audio/sound_buffer.c', line 84

static VALUE SoundBuffer_from_memory(VALUE klass, VALUE rb_data) {
    StringValue(rb_data);

    return SoundBuffer_wrap(
        klass, sfSoundBuffer_createFromMemory(RSTRING_PTR(rb_data), (size_t)RSTRING_LEN(rb_data)));
}

.from_samples(samples, channel_count, sample_rate) ⇒ SoundBuffer .from_samples(samples, channel_count, sample_rate, channel_map) ⇒ SoundBuffer

samples is an Array of Integer samples or a packed String of int16 samples, interleaved by channel. channel_map, if given, is an Array of channel Symbols (see SoundChannel), one per channel; if omitted, a conventional layout is assumed for 1 and 2 channels.

Overloads:

Returns:

Raises:

  • (ArgumentError) —

    if channel_count is 0, samples is neither an Array nor a String, or channel_map doesn't have exactly one entry per channel



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'ext/audio/sound_buffer.c', line 172

static VALUE SoundBuffer_from_samples(int argc, VALUE* argv, VALUE klass) {
    VALUE rb_samples, rb_channel_count, rb_sample_rate, rb_channel_map;
    int16_t* samples;
    sfSoundChannel* channel_map;
    unsigned int channel_count;
    uint64_t sample_count;
    sfSoundBuffer* buffer;

    rb_scan_args(argc, argv, "31", &rb_samples, &rb_channel_count, &rb_sample_rate,
                 &rb_channel_map);

    channel_count = (unsigned int)NUM2INT(rb_channel_count);

    if (channel_count == 0) {
        rb_raise(rb_eArgError, "channel count must be positive");
    }

    /* channel_map's malloc below (sizeof(sfSoundChannel) * channel_count) can
       wrap on the 32-bit targets this gem ships if channel_count is large
       enough, allocating far less than the subsequent write loop assumes.
       Check before any allocation happens, so there's nothing to clean up on
       the raise path. (Written with channel_count as the divisor, not
       compared directly against a constant bound, so this isn't optimized
       away as tautological on 64-bit hosts where it can't actually trigger
       -- it still does real work on the 32-bit targets.) */
    if (SIZE_MAX / channel_count < sizeof(sfSoundChannel)) {
        rb_raise(rb_eArgError, "channel count too large");
    }

    if (RB_TYPE_P(rb_samples, T_STRING)) {
        size_t bytes = (size_t)RSTRING_LEN(rb_samples);

        if (bytes % sizeof(int16_t) != 0) {
            rb_raise(rb_eArgError, "sample string length must be a multiple of 2 bytes");
        }

        sample_count = bytes / sizeof(int16_t);
        samples = malloc(bytes > 0 ? bytes : 1);

        if (bytes > 0) {
            const int16_t* src = (const int16_t*)RSTRING_PTR(rb_samples);
            size_t i;

            for (i = 0; i < sample_count; i++) {
                samples[i] = src[i];
            }
        }
    } else if (RB_TYPE_P(rb_samples, T_ARRAY)) {
        long i;

        sample_count = (uint64_t)RARRAY_LEN(rb_samples);
        samples = malloc(sizeof(int16_t) * ((size_t)sample_count + 1));

        for (i = 0; i < (long)sample_count; i++) {
            samples[i] = (int16_t)NUM2INT(rb_ary_entry(rb_samples, i));
        }
    } else {
        rb_raise(rb_eArgError, "samples must be an Array of integers or a String");
        return Qnil;
    }

    channel_map = malloc(sizeof(sfSoundChannel) * channel_count);

    if (NIL_P(rb_channel_map)) {
        SoundBuffer_default_channel_map(channel_count, channel_map);
    } else {
        ChannelMapFillContext ctx = {.channel_map = channel_map,
                                     .rb_channel_map = rb_channel_map,
                                     .channel_count = channel_count};
        int state = 0;

        if (!RB_TYPE_P(rb_channel_map, T_ARRAY) ||
            (unsigned long)RARRAY_LEN(rb_channel_map) != channel_count) {
            free(samples);
            free(channel_map);
            rb_raise(rb_eArgError, "channel map must have exactly one entry per channel");
        }

        rb_protect(SoundBuffer_fill_channel_map, (VALUE)&ctx, &state);

        if (state) {
            free(samples);
            free(channel_map);
            rb_jump_tag(state);
        }
    }

    buffer = sfSoundBuffer_createFromSamples(samples, sample_count, channel_count,
                                             (unsigned int)NUM2INT(rb_sample_rate), channel_map,
                                             channel_count);

    free(samples);
    free(channel_map);

    return SoundBuffer_wrap(klass, buffer);
}

.from_stream(stream) ⇒ SoundBuffer

Decodes audio read from an SFML InputStream into a buffer.

Returns:

Returns:

Raises:

  • (RuntimeError) —

    if the stream cannot be decoded



99
100
101
102
103
104
105
106
# File 'ext/audio/sound_buffer.c', line 99

static VALUE SoundBuffer_from_stream(VALUE klass, VALUE rb_stream) {
    VALUE holder = Qnil;
    sfInputStream* stream = input_stream_from_rb(rb_stream, &holder);

    (void)holder;

    return SoundBuffer_wrap(klass, sfSoundBuffer_createFromStream(stream));
}

Instance Method Details

#channel_count ⇒ Integer

Returns the number of audio channels in the buffer.

Returns:

  • (Integer)

Returns:

  • (Integer)


344
345
346
# File 'ext/audio/sound_buffer.c', line 344

static VALUE SoundBuffer_channel_count(VALUE self) {
    return UINT2NUM(sfSoundBuffer_getChannelCount(Get_SoundBuffer_Struct(self)));
}

#channel_map ⇒ Array<Symbol>

Returns the channel layout of the buffer.

Returns:

  • (Array<Symbol>)

Returns:

  • (Array<Symbol>) —

    one entry per channel, e.g. [:front_left, :front_right]



355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'ext/audio/sound_buffer.c', line 355

static VALUE SoundBuffer_channel_map(VALUE self) {
    size_t count = 0;
    sfSoundChannel* map = sfSoundBuffer_getChannelMap(Get_SoundBuffer_Struct(self), &count);
    VALUE rb_array;
    size_t i;

    if (map == NULL) {
        return rb_ary_new();
    }

    rb_array = rb_ary_new_capa((long)count);

    for (i = 0; i < count; i++) {
        rb_ary_push(rb_array, ID2SYM(rb_intern(sound_channel_name(map[i]))));
    }

    return rb_array;
}

#copy ⇒ SoundBuffer

Creates an independent copy of the buffer.

Returns:

Returns:



275
276
277
278
# File 'ext/audio/sound_buffer.c', line 275

static VALUE SoundBuffer_copy(VALUE self) {
    return SoundBuffer_wrap(Get_Klass_SoundBuffer(),
                            sfSoundBuffer_copy(Get_SoundBuffer_Struct(self)));
}

#duration ⇒ Time

Returns the buffer's total playing duration.

Returns:

Returns:



380
381
382
# File 'ext/audio/sound_buffer.c', line 380

static VALUE SoundBuffer_duration(VALUE self) {
    return time_to_rb(sfSoundBuffer_getDuration(Get_SoundBuffer_Struct(self)));
}

#sample_count ⇒ Integer

Returns the total number of samples stored in the buffer.

Returns:

  • (Integer)

Returns:

  • (Integer) —

    total number of int16 samples, across all channels



324
325
326
# File 'ext/audio/sound_buffer.c', line 324

static VALUE SoundBuffer_sample_count(VALUE self) {
    return ULL2NUM(sfSoundBuffer_getSampleCount(Get_SoundBuffer_Struct(self)));
}

#sample_rate ⇒ Integer

Returns the buffer's sample rate in samples per second.

Returns:

  • (Integer)

Returns:

  • (Integer)


334
335
336
# File 'ext/audio/sound_buffer.c', line 334

static VALUE SoundBuffer_sample_rate(VALUE self) {
    return UINT2NUM(sfSoundBuffer_getSampleRate(Get_SoundBuffer_Struct(self)));
}

#samples ⇒ Array<Integer>

Returns the buffer's raw audio samples as Integers.

Returns:

  • (Array<Integer>)

Returns:

  • (Array<Integer>) —

    the raw int16 audio samples, interleaved by channel



299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
# File 'ext/audio/sound_buffer.c', line 299

static VALUE SoundBuffer_samples(VALUE self) {
    const int16_t* samples = sfSoundBuffer_getSamples(Get_SoundBuffer_Struct(self));
    uint64_t count = sfSoundBuffer_getSampleCount(Get_SoundBuffer_Struct(self));
    VALUE rb_array;
    uint64_t i;

    if (samples == NULL) {
        return rb_ary_new();
    }

    rb_array = rb_ary_new_capa((long)count);

    for (i = 0; i < count; i++) {
        rb_ary_push(rb_array, INT2NUM(samples[i]));
    }

    return rb_array;
}

#save_to_file(path) ⇒ Boolean

Writes the buffer to an audio file.

Returns:

  • (Boolean)

Returns:

  • (Boolean) —

    whether the file was written successfully



287
288
289
290
# File 'ext/audio/sound_buffer.c', line 287

static VALUE SoundBuffer_save_to_file(VALUE self, VALUE rb_path) {
    return BOOL2RB(
        sfSoundBuffer_saveToFile(Get_SoundBuffer_Struct(self), StringValueCStr(rb_path)));
}