Commit4a0d581bRecorded11 Dec 2025Repositorysigil-studio

Add audio module and more graphics primitives

Message

Audio module (via sokolaudio + stbvorbis): - Sound effects loaded into memory for low-latency playback - Music streaming from OGG files - Volume control, mute/unmute, pause/resume - Test app with keyboard controls

Graphics additions: - Drawing: draw-line, draw-point, draw-triangle, fill-triangle - Transform stack: push/pop, translate, rotate, scale

Changed
 src/c/audio.c                 |  561 ++++++++++++++++-
 src/c/graphics.c              |  253 ++++++++
 src/sigil/studio/audio.sgl    |   21 +-
 src/sigil/studio/graphics.sgl |    7 +
 test/music.ogg                |  Bin 0 -> 133623 bytes
 test/test-audio.sgl           |   86 +++
 vendor/stb/stb_vorbis.c       | 5584 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 6507 insertions(+), 5 deletions(-)
Diff
src/c/audio.cmodified
@@ -2,16 +2,231 @@
2
* audio.c - Sigil Studio Audio Module
3
*
4
* Wraps sokol_audio.h to provide audio playback capabilities.
5
* This is a stub for now - full implementation later.
+5
* Uses stb_vorbis for OGG decoding.
+6
*
+7
* Sound effects are loaded entirely into memory.
+8
* Music is streamed from disk via stb_vorbis.
9
*/
10
11
#include "studio-internal.h"
+12
#include "sigil-internal.h"
+13
+14
#include <stdio.h>
+15
#include <stdlib.h>
+16
#include <string.h>
+17
#include <math.h>
18
19
/* Sokol headers (implementation is in sokol.c) */
20
#include "sokol_audio.h"
21
+22
/* stb_vorbis for OGG decoding (implementation in stb_impl.c) */
+23
#include "stb_vorbis.c"
+24
+25
/* ============================================================
+26
* CONSTANTS
+27
* ============================================================ */
+28
+29
#define MAX_SOUNDS 64
+30
#define MAX_PLAYING_SOUNDS 16
+31
#define STREAM_BUFFER_SAMPLES 4096
+32
+33
/* ============================================================
+34
* DATA STRUCTURES
+35
* ============================================================ */
+36
+37
/* Sound effect - fully loaded into memory */
+38
typedef struct {
+39
float *samples; /* Interleaved stereo samples */
+40
int num_samples; /* Total samples (frames * channels) */
+41
int sample_rate;
+42
int channels;
+43
} StudioSound;
+44
+45
/* Playing sound instance */
+46
typedef struct {
+47
StudioSound *sound;
+48
int position; /* Current playback position */
+49
float volume;
+50
float pan; /* -1.0 left, 0.0 center, 1.0 right */
+51
bool playing;
+52
bool loop;
+53
} PlayingSound;
+54
+55
/* Music stream - decoded on the fly */
+56
typedef struct {
+57
stb_vorbis *vorbis;
+58
char *filepath; /* For reopening if looping */
+59
float volume;
+60
bool playing;
+61
bool loop;
+62
bool paused;
+63
} MusicStream;
+64
+65
/* ============================================================
+66
* GLOBAL STATE
+67
* ============================================================ */
+68
+69
static PlayingSound g_playing_sounds[MAX_PLAYING_SOUNDS];
+70
static MusicStream g_music = {0};
+71
static float g_master_volume = 1.0f;
+72
static bool g_muted = false;
+73
+74
/* Type tags for foreign objects */
+75
static Value sound_type_tag = SIGIL_UNDEFINED;
+76
+77
/* ============================================================
+78
* AUDIO CALLBACK
+79
* ============================================================ */
+80
+81
static void audio_callback(float *buffer, int num_frames, int num_channels)
+82
{
+83
/* Clear buffer */
+84
memset(buffer, 0, num_frames * num_channels * sizeof(float));
+85
+86
if (g_muted) return;
+87
+88
float master = g_master_volume;
+89
+90
/* Mix playing sounds */
+91
for (int i = 0; i < MAX_PLAYING_SOUNDS; i++) {
+92
PlayingSound *ps = &g_playing_sounds[i];
+93
if (!ps->playing || !ps->sound) continue;
+94
+95
StudioSound *snd = ps->sound;
+96
float vol = ps->volume * master;
+97
+98
/* Calculate pan gains */
+99
float pan = ps->pan;
+100
float left_gain = vol * (pan <= 0 ? 1.0f : 1.0f - pan);
+101
float right_gain = vol * (pan >= 0 ? 1.0f : 1.0f + pan);
+102
+103
for (int f = 0; f < num_frames; f++) {
+104
if (ps->position >= snd->num_samples / snd->channels) {
+105
if (ps->loop) {
+106
ps->position = 0;
+107
} else {
+108
ps->playing = false;
+109
break;
+110
}
+111
}
+112
+113
float left, right;
+114
if (snd->channels == 1) {
+115
/* Mono */
+116
left = right = snd->samples[ps->position];
+117
} else {
+118
/* Stereo */
+119
left = snd->samples[ps->position * 2];
+120
right = snd->samples[ps->position * 2 + 1];
+121
}
+122
+123
if (num_channels >= 2) {
+124
buffer[f * num_channels] += left * left_gain;
+125
buffer[f * num_channels + 1] += right * right_gain;
+126
} else {
+127
buffer[f] += (left + right) * 0.5f * vol;
+128
}
+129
+130
ps->position++;
+131
}
+132
}
+133
+134
/* Mix music stream */
+135
if (g_music.playing && !g_music.paused && g_music.vorbis) {
+136
float vol = g_music.volume * master;
+137
float temp[STREAM_BUFFER_SAMPLES * 2];
+138
int samples_needed = num_frames;
+139
int offset = 0;
+140
+141
while (samples_needed > 0) {
+142
int to_decode = samples_needed < STREAM_BUFFER_SAMPLES ?
+143
samples_needed : STREAM_BUFFER_SAMPLES;
+144
+145
int decoded = stb_vorbis_get_samples_float_interleaved(
+146
g_music.vorbis, 2, temp, to_decode * 2);
+147
+148
if (decoded == 0) {
+149
/* End of file */
+150
if (g_music.loop && g_music.filepath) {
+151
/* Reopen and continue */
+152
stb_vorbis_close(g_music.vorbis);
+153
int error;
+154
g_music.vorbis = stb_vorbis_open_filename(
+155
g_music.filepath, &error, NULL);
+156
if (!g_music.vorbis) {
+157
g_music.playing = false;
+158
break;
+159
}
+160
continue;
+161
} else {
+162
g_music.playing = false;
+163
break;
+164
}
+165
}
+166
+167
/* Mix decoded samples */
+168
for (int f = 0; f < decoded; f++) {
+169
int buf_idx = (offset + f) * num_channels;
+170
if (num_channels >= 2) {
+171
buffer[buf_idx] += temp[f * 2] * vol;
+172
buffer[buf_idx + 1] += temp[f * 2 + 1] * vol;
+173
} else {
+174
buffer[buf_idx] += (temp[f * 2] + temp[f * 2 + 1]) * 0.5f * vol;
+175
}
+176
}
+177
+178
samples_needed -= decoded;
+179
offset += decoded;
+180
}
+181
}
+182
+183
/* Clamp output */
+184
for (int i = 0; i < num_frames * num_channels; i++) {
+185
if (buffer[i] > 1.0f) buffer[i] = 1.0f;
+186
if (buffer[i] < -1.0f) buffer[i] = -1.0f;
+187
}
+188
}
+189
+190
/* ============================================================
+191
* HELPER FUNCTIONS
+192
* ============================================================ */
+193
+194
static void ensure_sound_type(SigilVM *vm)
+195
{
+196
if (sigil_is_undefined(sound_type_tag)) {
+197
sound_type_tag = sigil_intern_symbol(vm, "sigil-studio-sound", 18);
+198
}
+199
}
+200
+201
static StudioSound *get_sound(SigilVM *vm, Value v)
+202
{
+203
if (!sigil_is_foreign(v)) return NULL;
+204
ensure_sound_type(vm);
+205
if (sigil_foreign_type(v) != sound_type_tag) return NULL;
+206
return (StudioSound *)sigil_foreign_data(v);
+207
}
+208
+209
static void sound_destructor(void *data)
+210
{
+211
StudioSound *snd = (StudioSound *)data;
+212
if (snd) {
+213
free(snd->samples);
+214
free(snd);
+215
}
+216
}
+217
+218
static PlayingSound *find_free_slot(void)
+219
{
+220
for (int i = 0; i < MAX_PLAYING_SOUNDS; i++) {
+221
if (!g_playing_sounds[i].playing) {
+222
return &g_playing_sounds[i];
+223
}
+224
}
+225
return NULL;
+226
}
+227
228
/* ============================================================
14
* NATIVE FUNCTIONS
+229
* NATIVE FUNCTIONS - SETUP
230
* ============================================================ */
231
232
/*
@@ -25,9 +240,24 @@ static Value native_audio_setup(SigilVM *vm, int argc, Value *args)
240
return SIGIL_NIL;
241
}
242
28
saudio_desc desc = {0};
+243
saudio_desc desc = {
+244
.stream_cb = audio_callback,
+245
.num_channels = 2,
+246
.sample_rate = 44100,
+247
.buffer_frames = 2048
+248
};
249
saudio_setup(&desc);
250
+251
/* Clear playing sounds */
+252
memset(g_playing_sounds, 0, sizeof(g_playing_sounds));
+253
+254
/* Clear music */
+255
memset(&g_music, 0, sizeof(g_music));
+256
g_music.volume = 1.0f;
+257
+258
g_master_volume = 1.0f;
+259
g_muted = false;
+260
261
if (g_studio) {
262
g_studio->audio_initialized = true;
263
}
@@ -43,6 +273,14 @@ static Value native_audio_shutdown(SigilVM *vm, int argc, Value *args)
273
(void)vm; (void)argc; (void)args;
274
275
if (g_studio && g_studio->audio_initialized) {
+276
/* Stop music */
+277
if (g_music.vorbis) {
+278
stb_vorbis_close(g_music.vorbis);
+279
g_music.vorbis = NULL;
+280
}
+281
free(g_music.filepath);
+282
g_music.filepath = NULL;
+283
284
saudio_shutdown();
285
g_studio->audio_initialized = false;
286
}
@@ -60,6 +298,275 @@ static Value native_audio_initialized(SigilVM *vm, int argc, Value *args)
298
return g_studio->audio_initialized ? SIGIL_TRUE : SIGIL_FALSE;
299
}
300
+301
/* ============================================================
+302
* NATIVE FUNCTIONS - SOUNDS
+303
* ============================================================ */
+304
+305
/*
+306
* (load-sound path) -> <sound> or #f
+307
*
+308
* Load an OGG file entirely into memory.
+309
*/
+310
static Value native_load_sound(SigilVM *vm, int argc, Value *args)
+311
{
+312
if (argc < 1 || !sigil_is_string(args[0])) {
+313
sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "load-sound: expected string path");
+314
return SIGIL_FALSE;
+315
}
+316
+317
SigilString *path_str = (SigilString *)sigil_as_ptr(args[0]);
+318
const char *path = path_str->data;
+319
+320
int channels, sample_rate;
+321
short *raw_samples;
+322
int num_samples = stb_vorbis_decode_filename(path, &channels, &sample_rate,
+323
&raw_samples);
+324
if (num_samples < 0) {
+325
return SIGIL_FALSE;
+326
}
+327
+328
/* Convert to float */
+329
int total_samples = num_samples * channels;
+330
float *samples = malloc(total_samples * sizeof(float));
+331
if (!samples) {
+332
free(raw_samples);
+333
return SIGIL_FALSE;
+334
}
+335
+336
for (int i = 0; i < total_samples; i++) {
+337
samples[i] = raw_samples[i] / 32768.0f;
+338
}
+339
free(raw_samples);
+340
+341
StudioSound *snd = malloc(sizeof(StudioSound));
+342
if (!snd) {
+343
free(samples);
+344
return SIGIL_FALSE;
+345
}
+346
+347
snd->samples = samples;
+348
snd->num_samples = total_samples;
+349
snd->sample_rate = sample_rate;
+350
snd->channels = channels;
+351
+352
ensure_sound_type(vm);
+353
return sigil_make_foreign(vm, sound_type_tag, snd, sound_destructor,
+354
sizeof(StudioSound) + total_samples * sizeof(float));
+355
}
+356
+357
/*
+358
* (sound? obj) -> boolean
+359
*/
+360
static Value native_sound_p(SigilVM *vm, int argc, Value *args)
+361
{
+362
(void)argc;
+363
return get_sound(vm, args[0]) ? SIGIL_TRUE : SIGIL_FALSE;
+364
}
+365
+366
/*
+367
* (play-sound sound [volume] [pan] [loop?]) -> boolean
+368
*
+369
* Play a sound effect. Returns #t if started, #f if no slots available.
+370
*/
+371
static Value native_play_sound(SigilVM *vm, int argc, Value *args)
+372
{
+373
if (argc < 1) {
+374
sigil__vm_set_error(vm, SIGIL_ERR_ARITY, "play-sound: requires sound argument");
+375
return SIGIL_FALSE;
+376
}
+377
+378
StudioSound *snd = get_sound(vm, args[0]);
+379
if (!snd) {
+380
sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "play-sound: expected sound");
+381
return SIGIL_FALSE;
+382
}
+383
+384
PlayingSound *ps = find_free_slot();
+385
if (!ps) {
+386
return SIGIL_FALSE; /* No slots available */
+387
}
+388
+389
ps->sound = snd;
+390
ps->position = 0;
+391
ps->volume = argc > 1 ? (float)sigil_as_flonum(args[1]) : 1.0f;
+392
ps->pan = argc > 2 ? (float)sigil_as_flonum(args[2]) : 0.0f;
+393
ps->loop = argc > 3 ? sigil_is_true(args[3]) : false;
+394
ps->playing = true;
+395
+396
return SIGIL_TRUE;
+397
}
+398
+399
/*
+400
* (stop-all-sounds) - Stop all playing sound effects
+401
*/
+402
static Value native_stop_all_sounds(SigilVM *vm, int argc, Value *args)
+403
{
+404
(void)vm; (void)argc; (void)args;
+405
+406
for (int i = 0; i < MAX_PLAYING_SOUNDS; i++) {
+407
g_playing_sounds[i].playing = false;
+408
}
+409
+410
return SIGIL_NIL;
+411
}
+412
+413
/* ============================================================
+414
* NATIVE FUNCTIONS - MUSIC
+415
* ============================================================ */
+416
+417
/*
+418
* (play-music path [loop?]) -> boolean
+419
*
+420
* Start streaming music from an OGG file.
+421
*/
+422
static Value native_play_music(SigilVM *vm, int argc, Value *args)
+423
{
+424
if (argc < 1 || !sigil_is_string(args[0])) {
+425
sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "play-music: expected string path");
+426
return SIGIL_FALSE;
+427
}
+428
+429
/* Stop any existing music */
+430
if (g_music.vorbis) {
+431
stb_vorbis_close(g_music.vorbis);
+432
g_music.vorbis = NULL;
+433
}
+434
free(g_music.filepath);
+435
g_music.filepath = NULL;
+436
+437
SigilString *path_str = (SigilString *)sigil_as_ptr(args[0]);
+438
const char *path = path_str->data;
+439
+440
int error;
+441
g_music.vorbis = stb_vorbis_open_filename(path, &error, NULL);
+442
if (!g_music.vorbis) {
+443
return SIGIL_FALSE;
+444
}
+445
+446
g_music.filepath = strdup(path);
+447
g_music.loop = argc > 1 ? sigil_is_true(args[1]) : true;
+448
g_music.playing = true;
+449
g_music.paused = false;
+450
+451
return SIGIL_TRUE;
+452
}
+453
+454
/*
+455
* (stop-music) - Stop music playback
+456
*/
+457
static Value native_stop_music(SigilVM *vm, int argc, Value *args)
+458
{
+459
(void)vm; (void)argc; (void)args;
+460
+461
if (g_music.vorbis) {
+462
stb_vorbis_close(g_music.vorbis);
+463
g_music.vorbis = NULL;
+464
}
+465
free(g_music.filepath);
+466
g_music.filepath = NULL;
+467
g_music.playing = false;
+468
+469
return SIGIL_NIL;
+470
}
+471
+472
/*
+473
* (pause-music) - Pause music playback
+474
*/
+475
static Value native_pause_music(SigilVM *vm, int argc, Value *args)
+476
{
+477
(void)vm; (void)argc; (void)args;
+478
g_music.paused = true;
+479
return SIGIL_NIL;
+480
}
+481
+482
/*
+483
* (resume-music) - Resume music playback
+484
*/
+485
static Value native_resume_music(SigilVM *vm, int argc, Value *args)
+486
{
+487
(void)vm; (void)argc; (void)args;
+488
g_music.paused = false;
+489
return SIGIL_NIL;
+490
}
+491
+492
/*
+493
* (music-playing?) -> boolean
+494
*/
+495
static Value native_music_playing(SigilVM *vm, int argc, Value *args)
+496
{
+497
(void)vm; (void)argc; (void)args;
+498
return (g_music.playing && !g_music.paused) ? SIGIL_TRUE : SIGIL_FALSE;
+499
}
+500
+501
/*
+502
* (set-music-volume volume) - Set music volume (0.0 to 1.0)
+503
*/
+504
static Value native_set_music_volume(SigilVM *vm, int argc, Value *args)
+505
{
+506
if (argc < 1) {
+507
sigil__vm_set_error(vm, SIGIL_ERR_ARITY, "set-music-volume: requires volume");
+508
return SIGIL_NIL;
+509
}
+510
+511
float vol = (float)sigil_as_flonum(args[0]);
+512
if (vol < 0.0f) vol = 0.0f;
+513
if (vol > 1.0f) vol = 1.0f;
+514
g_music.volume = vol;
+515
+516
return SIGIL_NIL;
+517
}
+518
+519
/* ============================================================
+520
* NATIVE FUNCTIONS - GLOBAL CONTROL
+521
* ============================================================ */

Showing the first 500 of 610 diff lines for this file. This diff is INCOMPLETE; read the file or clone the repository for the rest.

src/c/graphics.cmodified
@@ -335,6 +335,221 @@ static Value native_draw_rect(SigilVM *vm, int argc, Value *args)
335
return SIGIL_NIL;
336
}
337
+338
/*
+339
* (draw-line x1 y1 x2 y2) - Draw a line
+340
*/
+341
static Value native_draw_line(SigilVM *vm, int argc, Value *args)
+342
{
+343
if (argc < 4) {
+344
sigil__vm_error(vm, SIGIL_ERR_ARITY, "draw-line: requires x1, y1, x2, y2 arguments");
+345
return SIGIL_UNDEFINED;
+346
}
+347
+348
float x1 = value_to_float(args[0]);
+349
float y1 = value_to_float(args[1]);
+350
float x2 = value_to_float(args[2]);
+351
float y2 = value_to_float(args[3]);
+352
+353
sgp_line line = {{x1, y1}, {x2, y2}};
+354
sgp_draw_lines(&line, 1);
+355
+356
return SIGIL_NIL;
+357
}
+358
+359
/*
+360
* (draw-point x y) - Draw a single point
+361
*/
+362
static Value native_draw_point(SigilVM *vm, int argc, Value *args)
+363
{
+364
if (argc < 2) {
+365
sigil__vm_error(vm, SIGIL_ERR_ARITY, "draw-point: requires x, y arguments");
+366
return SIGIL_UNDEFINED;
+367
}
+368
+369
float x = value_to_float(args[0]);
+370
float y = value_to_float(args[1]);
+371
+372
sgp_point pt = {x, y};
+373
sgp_draw_points(&pt, 1);
+374
+375
return SIGIL_NIL;
+376
}
+377
+378
/*
+379
* (draw-triangle x1 y1 x2 y2 x3 y3) - Draw a triangle outline
+380
*/
+381
static Value native_draw_triangle(SigilVM *vm, int argc, Value *args)
+382
{
+383
if (argc < 6) {
+384
sigil__vm_error(vm, SIGIL_ERR_ARITY, "draw-triangle: requires x1, y1, x2, y2, x3, y3");
+385
return SIGIL_UNDEFINED;
+386
}
+387
+388
float x1 = value_to_float(args[0]);
+389
float y1 = value_to_float(args[1]);
+390
float x2 = value_to_float(args[2]);
+391
float y2 = value_to_float(args[3]);
+392
float x3 = value_to_float(args[4]);
+393
float y3 = value_to_float(args[5]);
+394
+395
sgp_line lines[3] = {
+396
{{x1, y1}, {x2, y2}},
+397
{{x2, y2}, {x3, y3}},
+398
{{x3, y3}, {x1, y1}}
+399
};
+400
sgp_draw_lines(lines, 3);
+401
+402
return SIGIL_NIL;
+403
}
+404
+405
/*
+406
* (fill-triangle x1 y1 x2 y2 x3 y3) - Draw a filled triangle
+407
*/
+408
static Value native_fill_triangle(SigilVM *vm, int argc, Value *args)
+409
{
+410
if (argc < 6) {
+411
sigil__vm_error(vm, SIGIL_ERR_ARITY, "fill-triangle: requires x1, y1, x2, y2, x3, y3");
+412
return SIGIL_UNDEFINED;
+413
}
+414
+415
float x1 = value_to_float(args[0]);
+416
float y1 = value_to_float(args[1]);
+417
float x2 = value_to_float(args[2]);
+418
float y2 = value_to_float(args[3]);
+419
float x3 = value_to_float(args[4]);
+420
float y3 = value_to_float(args[5]);
+421
+422
sgp_triangle tri = {{x1, y1}, {x2, y2}, {x3, y3}};
+423
sgp_draw_filled_triangles(&tri, 1);
+424
+425
return SIGIL_NIL;
+426
}
+427
+428
/* ============================================================
+429
* TRANSFORM STACK
+430
* ============================================================ */
+431
+432
/*
+433
* (push-transform) - Save current transform state
+434
*/
+435
static Value native_push_transform(SigilVM *vm, int argc, Value *args)
+436
{
+437
(void)vm; (void)argc; (void)args;
+438
sgp_push_transform();
+439
return SIGIL_NIL;
+440
}
+441
+442
/*
+443
* (pop-transform) - Restore previous transform state
+444
*/
+445
static Value native_pop_transform(SigilVM *vm, int argc, Value *args)
+446
{
+447
(void)vm; (void)argc; (void)args;
+448
sgp_pop_transform();
+449
return SIGIL_NIL;
+450
}
+451
+452
/*
+453
* (reset-transform) - Reset to identity transform
+454
*/
+455
static Value native_reset_transform(SigilVM *vm, int argc, Value *args)
+456
{
+457
(void)vm; (void)argc; (void)args;
+458
sgp_reset_transform();
+459
return SIGIL_NIL;
+460
}
+461
+462
/*
+463
* (translate x y) - Translate by (x, y)
+464
*/
+465
static Value native_translate(SigilVM *vm, int argc, Value *args)
+466
{
+467
if (argc < 2) {
+468
sigil__vm_error(vm, SIGIL_ERR_ARITY, "translate: requires x, y arguments");
+469
return SIGIL_UNDEFINED;
+470
}
+471
+472
float x = value_to_float(args[0]);
+473
float y = value_to_float(args[1]);
+474
+475
sgp_translate(x, y);
+476
+477
return SIGIL_NIL;
+478
}
+479
+480
/*
+481
* (rotate angle) - Rotate by angle (in radians)
+482
*/
+483
static Value native_rotate(SigilVM *vm, int argc, Value *args)
+484
{
+485
if (argc < 1) {
+486
sigil__vm_error(vm, SIGIL_ERR_ARITY, "rotate: requires angle argument");
+487
return SIGIL_UNDEFINED;
+488
}
+489
+490
float angle = value_to_float(args[0]);
+491
sgp_rotate(angle);
+492
+493
return SIGIL_NIL;
+494
}
+495
+496
/*
+497
* (rotate-at angle x y) - Rotate around point (x, y)
+498
*/
+499
static Value native_rotate_at(SigilVM *vm, int argc, Value *args)
+500
{
+501
if (argc < 3) {
+502
sigil__vm_error(vm, SIGIL_ERR_ARITY, "rotate-at: requires angle, x, y arguments");
+503
return SIGIL_UNDEFINED;
+504
}
+505
+506
float angle = value_to_float(args[0]);
+507
float x = value_to_float(args[1]);
+508
float y = value_to_float(args[2]);
+509
+510
sgp_rotate_at(angle, x, y);
+511
+512
return SIGIL_NIL;
+513
}
+514
+515
/*
+516
* (scale sx sy) - Scale by (sx, sy)
+517
*/
+518
static Value native_scale(SigilVM *vm, int argc, Value *args)
+519
{
+520
if (argc < 2) {
+521
sigil__vm_error(vm, SIGIL_ERR_ARITY, "scale: requires sx, sy arguments");
+522
return SIGIL_UNDEFINED;
+523
}
+524
+525
float sx = value_to_float(args[0]);
+526
float sy = value_to_float(args[1]);
+527
+528
sgp_scale(sx, sy);
+529
+530
return SIGIL_NIL;
+531
}
+532
+533
/*
+534
* (scale-at sx sy x y) - Scale around point (x, y)
+535
*/
+536
static Value native_scale_at(SigilVM *vm, int argc, Value *args)
+537
{
+538
if (argc < 4) {
+539
sigil__vm_error(vm, SIGIL_ERR_ARITY, "scale-at: requires sx, sy, x, y arguments");
+540
return SIGIL_UNDEFINED;
+541
}
+542
+543
float sx = value_to_float(args[0]);
+544
float sy = value_to_float(args[1]);
+545
float x = value_to_float(args[2]);
+546
float y = value_to_float(args[3]);
+547
+548
sgp_scale_at(sx, sy, x, y);
+549
+550
return SIGIL_NIL;
+551
}
+552
553
/*
554
* (gfx-initialized?) -> boolean
555
*/
@@ -618,6 +833,32 @@ void sigil__init_sigil_studio_graphics_module(SigilVM *vm)
833
SIGIL_ARITY_EXACT(4), "Draw filled rectangle");
834
sigil_module_register_native(vm, "draw-rect", native_draw_rect,
835
SIGIL_ARITY_EXACT(4), "Draw rectangle outline");
+836
sigil_module_register_native(vm, "draw-line", native_draw_line,
+837
SIGIL_ARITY_EXACT(4), "Draw a line");
+838
sigil_module_register_native(vm, "draw-point", native_draw_point,
+839
SIGIL_ARITY_EXACT(2), "Draw a point");
+840
sigil_module_register_native(vm, "draw-triangle", native_draw_triangle,
+841
SIGIL_ARITY_EXACT(6), "Draw triangle outline");
+842
sigil_module_register_native(vm, "fill-triangle", native_fill_triangle,
+843
SIGIL_ARITY_EXACT(6), "Draw filled triangle");
+844
+845
/* Transform stack */
+846
sigil_module_register_native(vm, "push-transform", native_push_transform,
+847
SIGIL_ARITY_EXACT(0), "Save transform state");
+848
sigil_module_register_native(vm, "pop-transform", native_pop_transform,
+849
SIGIL_ARITY_EXACT(0), "Restore transform state");
+850
sigil_module_register_native(vm, "reset-transform", native_reset_transform,
+851
SIGIL_ARITY_EXACT(0), "Reset to identity transform");
+852
sigil_module_register_native(vm, "translate", native_translate,
+853
SIGIL_ARITY_EXACT(2), "Translate by (x, y)");
+854
sigil_module_register_native(vm, "rotate", native_rotate,
+855
SIGIL_ARITY_EXACT(1), "Rotate by angle (radians)");
+856
sigil_module_register_native(vm, "rotate-at", native_rotate_at,
+857
SIGIL_ARITY_EXACT(3), "Rotate around point");
+858
sigil_module_register_native(vm, "scale", native_scale,
+859
SIGIL_ARITY_EXACT(2), "Scale by (sx, sy)");
+860
sigil_module_register_native(vm, "scale-at", native_scale_at,
+861
SIGIL_ARITY_EXACT(4), "Scale around point");
862
863
/* Texture functions */
864
sigil_module_register_native(vm, "load-texture", native_load_texture,
@@ -645,6 +886,18 @@ void sigil__init_sigil_studio_graphics_module(SigilVM *vm)
886
sigil_module_export(vm, "set-color");
887
sigil_module_export(vm, "draw-filled-rect");
888
sigil_module_export(vm, "draw-rect");
+889
sigil_module_export(vm, "draw-line");
+890
sigil_module_export(vm, "draw-point");
+891
sigil_module_export(vm, "draw-triangle");
+892
sigil_module_export(vm, "fill-triangle");
+893
sigil_module_export(vm, "push-transform");
+894
sigil_module_export(vm, "pop-transform");
+895
sigil_module_export(vm, "reset-transform");
+896
sigil_module_export(vm, "translate");
+897
sigil_module_export(vm, "rotate");
+898
sigil_module_export(vm, "rotate-at");
+899
sigil_module_export(vm, "scale");
+900
sigil_module_export(vm, "scale-at");
901
sigil_module_export(vm, "load-texture");
902
sigil_module_export(vm, "texture?");
903
sigil_module_export(vm, "texture-width");
src/sigil/studio/audio.sglmodified
@@ -1,6 +1,11 @@
1
;;; (sigil studio audio) - Audio Module
2
;;;
3
;;; Provides audio playback capabilities via sokol_audio.
+4
;;; Uses stb_vorbis for OGG audio decoding.
+5
;;;
+6
;;; Sound effects are loaded entirely into memory for low-latency playback.
+7
;;; Music is streamed from disk for memory efficiency.
+8
;;;
9
;;; Native functions are registered by audio.c.
10
11
(define-library (sigil studio audio)
@@ -8,9 +13,21 @@
13
14
(export
15
;; Setup/shutdown
11
audio-setup audio-shutdown audio-initialized?)
+16
audio-setup audio-shutdown audio-initialized?
+17
+18
;; Sound effects (loaded into memory)
+19
load-sound sound?
+20
play-sound stop-all-sounds
+21
+22
;; Music streaming
+23
play-music stop-music
+24
pause-music resume-music
+25
music-playing? set-music-volume
+26
+27
;; Global control
+28
set-master-volume
+29
mute-audio unmute-audio audio-muted?)
30
31
(begin
32
;; Native functions are automatically available after native module init.
15
;; Full audio API (load-sound, play-sound, etc.) to be added later.
33
))
src/sigil/studio/graphics.sglmodified
@@ -25,6 +25,13 @@
25
;; Drawing
26
clear-screen set-color
27
draw-filled-rect draw-rect
+28
draw-line draw-point
+29
draw-triangle fill-triangle
+30
+31
;; Transform stack
+32
push-transform pop-transform reset-transform
+33
translate rotate rotate-at
+34
scale scale-at
35
36
;; Textures
37
load-texture
test/music.oggbinary

Binary. Not rendered here.

test/test-audio.sgladded
@@ -0,0 +1,86 @@
+1
;;; test-audio.sgl - Test audio playback
+2
+3
(import (sigil core)
+4
(sigil studio app)
+5
(sigil studio graphics)
+6
(sigil studio audio)
+7
(sigil studio font))
+8
+9
(define *font* #f)
+10
(define *music-playing* #f)
+11
(define *volume* 1.0)
+12
+13
(define (on-init)
+14
(display "Initializing audio...\n")
+15
(audio-setup)
+16
(set! *font* (load-font "test/Saucer.ttf" 24))
+17
+18
;; Start music
+19
(display "Playing music...\n")
+20
(if (play-music "test/music.ogg" #t)
+21
(begin
+22
(display "Music started!\n")
+23
(set! *music-playing* #t))
+24
(display "ERROR: Failed to load music!\n")))
+25
+26
(define (on-frame)
+27
(begin-frame)
+28
(clear-screen 0.1 0.1 0.15)
+29
+30
(when *font*
+31
(set-color 0.0 1.0 1.0)
+32
(draw-text *font* "Audio Test" 300 50)
+33
+34
(set-color 0.8 0.8 0.8)
+35
(draw-text *font* "P - Play/Pause music" 100 150)
+36
(draw-text *font* "S - Stop music" 100 190)
+37
(draw-text *font* "M - Mute/Unmute" 100 230)
+38
(draw-text *font* "UP/DOWN - Volume" 100 270)
+39
(draw-text *font* "ESC - Quit" 100 310)
+40
+41
(set-color 1.0 1.0 0.0)
+42
(if (music-playing?)
+43
(draw-text *font* "Status: Playing" 100 350)
+44
(draw-text *font* "Status: Stopped/Paused" 100 350))
+45
+46
(set-color 0.5 0.8 1.0)
+47
(draw-text *font* (string-append "Volume: " (number->string (truncate (* *volume* 100))) "%") 100 390)
+48
+49
(if (audio-muted?)
+50
(begin
+51
(set-color 1.0 0.3 0.3)
+52
(draw-text *font* "MUTED" 300 390))))
+53
+54
;; Input handling
+55
(when (key-pressed? 'p)
+56
(if (music-playing?)
+57
(pause-music)
+58
(resume-music)))
+59
+60
(when (key-pressed? 's)
+61
(stop-music))
+62
+63
(when (key-pressed? 'm)
+64
(if (audio-muted?)
+65
(unmute-audio)
+66
(mute-audio)))
+67
+68
(when (key-pressed? 'up)
+69
(set! *volume* (min 1.0 (+ *volume* 0.1)))
+70
(set-master-volume *volume*))
+71
+72
(when (key-pressed? 'down)
+73
(set! *volume* (max 0.0 (- *volume* 0.1)))
+74
(set-master-volume *volume*))
+75
+76
(when (key-pressed? 'escape)
+77
(request-quit))
+78
+79
(end-frame))
+80
+81
(define (on-cleanup)
+82
(display "Cleaning up audio...\n")
+83
(stop-music)
+84
(audio-shutdown))
+85
+86
(app-run on-init on-frame on-cleanup "Audio Test" 800 500)
vendor/stb/stb_vorbis.cadded
@@ -0,0 +1,5584 @@
+1
// Ogg Vorbis audio decoder - v1.22 - public domain
+2
// http://nothings.org/stb_vorbis/
+3
//
+4
// Original version written by Sean Barrett in 2007.
+5
//
+6
// Originally sponsored by RAD Game Tools. Seeking implementation
+7
// sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker,
+8
// Elias Software, Aras Pranckevicius, and Sean Barrett.
+9
//
+10
// LICENSE
+11
//
+12
// See end of file for license information.
+13
//
+14
// Limitations:
+15
//
+16
// - floor 0 not supported (used in old ogg vorbis files pre-2004)
+17
// - lossless sample-truncation at beginning ignored
+18
// - cannot concatenate multiple vorbis streams
+19
// - sample positions are 32-bit, limiting seekable 192Khz
+20
// files to around 6 hours (Ogg supports 64-bit)
+21
//
+22
// Feature contributors:
+23
// Dougall Johnson (sample-exact seeking)
+24
//
+25
// Bugfix/warning contributors:
+26
// Terje Mathisen Niklas Frykholm Andy Hill
+27
// Casey Muratori John Bolton Gargaj
+28
// Laurent Gomila Marc LeBlanc Ronny Chevalier
+29
// Bernhard Wodo Evan Balster github:alxprd
+30
// Tom Beaumont Ingo Leitgeb Nicolas Guillemot
+31
// Phillip Bennefall Rohit Thiago Goulart
+32
// github:manxorist Saga Musix github:infatum
+33
// Timur Gagiev Maxwell Koo Peter Waller
+34
// github:audinowho Dougall Johnson David Reid
+35
// github:Clownacy Pedro J. Estebanez Remi Verschelde
+36
// AnthoFoxo github:morlat Gabriel Ravier
+37
//
+38
// Partial history:
+39
// 1.22 - 2021-07-11 - various small fixes
+40
// 1.21 - 2021-07-02 - fix bug for files with no comments
+41
// 1.20 - 2020-07-11 - several small fixes
+42
// 1.19 - 2020-02-05 - warnings
+43
// 1.18 - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc.
+44
// 1.17 - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure)
+45
// 1.16 - 2019-03-04 - fix warnings
+46
// 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found
+47
// 1.14 - 2018-02-11 - delete bogus dealloca usage
+48
// 1.13 - 2018-01-29 - fix truncation of last frame (hopefully)
+49
// 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files
+50
// 1.11 - 2017-07-23 - fix MinGW compilation
+51
// 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory
+52
// 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version
+53
// 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame
+54
// 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const
+55
// 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson)
+56
// some crash fixes when out of memory or with corrupt files
+57
// fix some inappropriately signed shifts
+58
// 1.05 - 2015-04-19 - don't define __forceinline if it's redundant
+59
// 1.04 - 2014-08-27 - fix missing const-correct case in API
+60
// 1.03 - 2014-08-07 - warning fixes
+61
// 1.02 - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows
+62
// 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct)
+63
// 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel;
+64
// (API change) report sample rate for decode-full-file funcs
+65
//
+66
// See end of file for full version history.
+67
+68
+69
//////////////////////////////////////////////////////////////////////////////
+70
//
+71
// HEADER BEGINS HERE
+72
//
+73
+74
#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H
+75
#define STB_VORBIS_INCLUDE_STB_VORBIS_H
+76
+77
#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO)
+78
#define STB_VORBIS_NO_STDIO 1
+79
#endif
+80
+81
#ifndef STB_VORBIS_NO_STDIO
+82
#include <stdio.h>
+83
#endif
+84
+85
#ifdef __cplusplus
+86
extern "C" {
+87
#endif
+88
+89
/////////// THREAD SAFETY
+90
+91
// Individual stb_vorbis* handles are not thread-safe; you cannot decode from
+92
// them from multiple threads at the same time. However, you can have multiple
+93
// stb_vorbis* handles and decode from them independently in multiple thrads.
+94
+95
+96
/////////// MEMORY ALLOCATION
+97
+98
// normally stb_vorbis uses malloc() to allocate memory at startup,
+99
// and alloca() to allocate temporary memory during a frame on the
+100
// stack. (Memory consumption will depend on the amount of setup
+101
// data in the file and how you set the compile flags for speed
+102
// vs. size. In my test files the maximal-size usage is ~150KB.)
+103
//
+104
// You can modify the wrapper functions in the source (setup_malloc,
+105
// setup_temp_malloc, temp_malloc) to change this behavior, or you
+106
// can use a simpler allocation model: you pass in a buffer from
+107
// which stb_vorbis will allocate _all_ its memory (including the
+108
// temp memory). "open" may fail with a VORBIS_outofmem if you
+109
// do not pass in enough data; there is no way to determine how
+110
// much you do need except to succeed (at which point you can
+111
// query get_info to find the exact amount required. yes I know
+112
// this is lame).
+113
//
+114
// If you pass in a non-NULL buffer of the type below, allocation
+115
// will occur from it as described above. Otherwise just pass NULL
+116
// to use malloc()/alloca()
+117
+118
typedef struct
+119
{
+120
char *alloc_buffer;
+121
int alloc_buffer_length_in_bytes;
+122
} stb_vorbis_alloc;
+123
+124
+125
/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES
+126
+127
typedef struct stb_vorbis stb_vorbis;
+128
+129
typedef struct
+130
{
+131
unsigned int sample_rate;
+132
int channels;
+133
+134
unsigned int setup_memory_required;
+135
unsigned int setup_temp_memory_required;
+136
unsigned int temp_memory_required;
+137
+138
int max_frame_size;
+139
} stb_vorbis_info;
+140
+141
typedef struct
+142
{
+143
char *vendor;
+144
+145
int comment_list_length;
+146
char **comment_list;
+147
} stb_vorbis_comment;
+148
+149
// get general information about the file
+150
extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f);
+151
+152
// get ogg comments
+153
extern stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f);
+154
+155
// get the last error detected (clears it, too)
+156
extern int stb_vorbis_get_error(stb_vorbis *f);
+157
+158
// close an ogg vorbis file and free all memory in use
+159
extern void stb_vorbis_close(stb_vorbis *f);
+160
+161
// this function returns the offset (in samples) from the beginning of the
+162
// file that will be returned by the next decode, if it is known, or -1
+163
// otherwise. after a flush_pushdata() call, this may take a while before
+164
// it becomes valid again.
+165
// NOT WORKING YET after a seek with PULLDATA API
+166
extern int stb_vorbis_get_sample_offset(stb_vorbis *f);
+167
+168
// returns the current seek point within the file, or offset from the beginning
+169
// of the memory buffer. In pushdata mode it returns 0.
+170
extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f);
+171
+172
/////////// PUSHDATA API
+173
+174
#ifndef STB_VORBIS_NO_PUSHDATA_API
+175
+176
// this API allows you to get blocks of data from any source and hand
+177
// them to stb_vorbis. you have to buffer them; stb_vorbis will tell
+178
// you how much it used, and you have to give it the rest next time;
+179
// and stb_vorbis may not have enough data to work with and you will
+180
// need to give it the same data again PLUS more. Note that the Vorbis
+181
// specification does not bound the size of an individual frame.
+182
+183
extern stb_vorbis *stb_vorbis_open_pushdata(
+184
const unsigned char * datablock, int datablock_length_in_bytes,
+185
int *datablock_memory_consumed_in_bytes,
+186
int *error,
+187
const stb_vorbis_alloc *alloc_buffer);
+188
// create a vorbis decoder by passing in the initial data block containing
+189
// the ogg&vorbis headers (you don't need to do parse them, just provide
+190
// the first N bytes of the file--you're told if it's not enough, see below)
+191
// on success, returns an stb_vorbis *, does not set error, returns the amount of
+192
// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes;
+193
// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed
+194
// if returns NULL and *error is VORBIS_need_more_data, then the input block was
+195
// incomplete and you need to pass in a larger block from the start of the file
+196
+197
extern int stb_vorbis_decode_frame_pushdata(
+198
stb_vorbis *f,
+199
const unsigned char *datablock, int datablock_length_in_bytes,
+200
int *channels, // place to write number of float * buffers
+201
float ***output, // place to write float ** array of float * buffers
+202
int *samples // place to write number of output samples
+203
);
+204
// decode a frame of audio sample data if possible from the passed-in data block
+205
//
+206
// return value: number of bytes we used from datablock
+207
//
+208
// possible cases:
+209
// 0 bytes used, 0 samples output (need more data)
+210
// N bytes used, 0 samples output (resynching the stream, keep going)
+211
// N bytes used, M samples output (one frame of data)
+212
// note that after opening a file, you will ALWAYS get one N-bytes,0-sample
+213
// frame, because Vorbis always "discards" the first frame.
+214
//
+215
// Note that on resynch, stb_vorbis will rarely consume all of the buffer,
+216
// instead only datablock_length_in_bytes-3 or less. This is because it wants
+217
// to avoid missing parts of a page header if they cross a datablock boundary,
+218
// without writing state-machiney code to record a partial detection.
+219
//
+220
// The number of channels returned are stored in *channels (which can be
+221
// NULL--it is always the same as the number of channels reported by
+222
// get_info). *output will contain an array of float* buffers, one per
+223
// channel. In other words, (*output)[0][0] contains the first sample from
+224
// the first channel, and (*output)[1][0] contains the first sample from
+225
// the second channel.
+226
//
+227
// *output points into stb_vorbis's internal output buffer storage; these
+228
// buffers are owned by stb_vorbis and application code should not free
+229
// them or modify their contents. They are transient and will be overwritten
+230
// once you ask for more data to get decoded, so be sure to grab any data
+231
// you need before then.
+232
+233
extern void stb_vorbis_flush_pushdata(stb_vorbis *f);
+234
// inform stb_vorbis that your next datablock will not be contiguous with
+235
// previous ones (e.g. you've seeked in the data); future attempts to decode
+236
// frames will cause stb_vorbis to resynchronize (as noted above), and
+237
// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it
+238
// will begin decoding the _next_ frame.
+239
//
+240
// if you want to seek using pushdata, you need to seek in your file, then
+241
// call stb_vorbis_flush_pushdata(), then start calling decoding, then once
+242
// decoding is returning you data, call stb_vorbis_get_sample_offset, and
+243
// if you don't like the result, seek your file again and repeat.
+244
#endif
+245
+246
+247
////////// PULLING INPUT API
+248
+249
#ifndef STB_VORBIS_NO_PULLDATA_API
+250
// This API assumes stb_vorbis is allowed to pull data from a source--
+251
// either a block of memory containing the _entire_ vorbis stream, or a
+252
// FILE * that you or it create, or possibly some other reading mechanism
+253
// if you go modify the source to replace the FILE * case with some kind
+254
// of callback to your code. (But if you don't support seeking, you may
+255
// just want to go ahead and use pushdata.)
+256
+257
#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION)
+258
extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output);
+259
#endif
+260
#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION)
+261
extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output);
+262
#endif
+263
// decode an entire file and output the data interleaved into a malloc()ed
+264
// buffer stored in *output. The return value is the number of samples
+265
// decoded, or -1 if the file could not be opened or was not an ogg vorbis file.
+266
// When you're done with it, just free() the pointer returned in *output.
+267
+268
extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len,
+269
int *error, const stb_vorbis_alloc *alloc_buffer);
+270
// create an ogg vorbis decoder from an ogg vorbis stream in memory (note
+271
// this must be the entire stream!). on failure, returns NULL and sets *error
+272
+273
#ifndef STB_VORBIS_NO_STDIO
+274
extern stb_vorbis * stb_vorbis_open_filename(const char *filename,
+275
int *error, const stb_vorbis_alloc *alloc_buffer);
+276
// create an ogg vorbis decoder from a filename via fopen(). on failure,
+277
// returns NULL and sets *error (possibly to VORBIS_file_open_failure).
+278
+279
extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close,
+280
int *error, const stb_vorbis_alloc *alloc_buffer);
+281
// create an ogg vorbis decoder from an open FILE *, looking for a stream at
+282
// the _current_ seek point (ftell). on failure, returns NULL and sets *error.
+283
// note that stb_vorbis must "own" this stream; if you seek it in between
+284
// calls to stb_vorbis, it will become confused. Moreover, if you attempt to
+285
// perform stb_vorbis_seek_*() operations on this file, it will assume it
+286
// owns the _entire_ rest of the file after the start point. Use the next
+287
// function, stb_vorbis_open_file_section(), to limit it.
+288
+289
extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close,
+290
int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len);
+291
// create an ogg vorbis decoder from an open FILE *, looking for a stream at
+292
// the _current_ seek point (ftell); the stream will be of length 'len' bytes.
+293
// on failure, returns NULL and sets *error. note that stb_vorbis must "own"
+294
// this stream; if you seek it in between calls to stb_vorbis, it will become
+295
// confused.
+296
#endif
+297
+298
extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number);
+299
extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number);
+300
// these functions seek in the Vorbis file to (approximately) 'sample_number'.
+301
// after calling seek_frame(), the next call to get_frame_*() will include
+302
// the specified sample. after calling stb_vorbis_seek(), the next call to
+303
// stb_vorbis_get_samples_* will start with the specified sample. If you
+304
// do not need to seek to EXACTLY the target sample when using get_samples_*,
+305
// you can also use seek_frame().
+306
+307
extern int stb_vorbis_seek_start(stb_vorbis *f);
+308
// this function is equivalent to stb_vorbis_seek(f,0)
+309
+310
extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f);
+311
extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f);
+312
// these functions return the total length of the vorbis stream
+313
+314
extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output);
+315
// decode the next frame and return the number of samples. the number of
+316
// channels returned are stored in *channels (which can be NULL--it is always
+317
// the same as the number of channels reported by get_info). *output will
+318
// contain an array of float* buffers, one per channel. These outputs will
+319
// be overwritten on the next call to stb_vorbis_get_frame_*.
+320
//
+321
// You generally should not intermix calls to stb_vorbis_get_frame_*()
+322
// and stb_vorbis_get_samples_*(), since the latter calls the former.
+323
+324
#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
+325
extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts);
+326
extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples);
+327
#endif
+328
// decode the next frame and return the number of *samples* per channel.
+329
// Note that for interleaved data, you pass in the number of shorts (the
+330
// size of your array), but the return value is the number of samples per
+331
// channel, not the total number of samples.
+332
//
+333
// The data is coerced to the number of channels you request according to the
+334
// channel coercion rules (see below). You must pass in the size of your
+335
// buffer(s) so that stb_vorbis will not overwrite the end of the buffer.
+336
// The maximum buffer size needed can be gotten from get_info(); however,
+337
// the Vorbis I specification implies an absolute maximum of 4096 samples
+338
// per channel.
+339
+340
// Channel coercion rules:
+341
// Let M be the number of channels requested, and N the number of channels present,
+342
// and Cn be the nth channel; let stereo L be the sum of all L and center channels,
+343
// and stereo R be the sum of all R and center channels (channel assignment from the
+344
// vorbis spec).
+345
// M N output
+346
// 1 k sum(Ck) for all k
+347
// 2 * stereo L, stereo R
+348
// k l k > l, the first l channels, then 0s
+349
// k l k <= l, the first k channels
+350
// Note that this is not _good_ surround etc. mixing at all! It's just so
+351
// you get something useful.
+352
+353
extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats);
+354
extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples);
+355
// gets num_samples samples, not necessarily on a frame boundary--this requires
+356
// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES.
+357
// Returns the number of samples stored per channel; it may be less than requested
+358
// at the end of the file. If there are no more samples in the file, returns 0.
+359
+360
#ifndef STB_VORBIS_NO_INTEGER_CONVERSION
+361
extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts);
+362
extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples);
+363
#endif
+364
// gets num_samples samples, not necessarily on a frame boundary--this requires
+365
// buffering so you have to supply the buffers. Applies the coercion rules above
+366
// to produce 'channels' channels. Returns the number of samples stored per channel;
+367
// it may be less than requested at the end of the file. If there are no more
+368
// samples in the file, returns 0.
+369
+370
#endif
+371
+372
//////// ERROR CODES
+373
+374
enum STBVorbisError
+375
{
+376
VORBIS__no_error,
+377
+378
VORBIS_need_more_data=1, // not a real error
+379
+380
VORBIS_invalid_api_mixing, // can't mix API modes
+381
VORBIS_outofmem, // not enough memory
+382
VORBIS_feature_not_supported, // uses floor 0
+383
VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small
+384
VORBIS_file_open_failure, // fopen() failed
+385
VORBIS_seek_without_length, // can't seek in unknown-length file
+386
+387
VORBIS_unexpected_eof=10, // file is truncated?
+388
VORBIS_seek_invalid, // seek past EOF
+389
+390
// decoding errors (corrupt/invalid stream) -- you probably
+391
// don't care about the exact details of these
+392
+393
// vorbis errors:
+394
VORBIS_invalid_setup=20,
+395
VORBIS_invalid_stream,
+396
+397
// ogg errors:
+398
VORBIS_missing_capture_pattern=30,
+399
VORBIS_invalid_stream_structure_version,
+400
VORBIS_continued_packet_flag_invalid,
+401
VORBIS_incorrect_stream_serial_number,
+402
VORBIS_invalid_first_page,
+403
VORBIS_bad_packet_type,
+404
VORBIS_cant_find_last_page,
+405
VORBIS_seek_failed,
+406
VORBIS_ogg_skeleton_not_supported
+407
};
+408
+409
+410
#ifdef __cplusplus
+411
}
+412
#endif
+413
+414
#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H
+415
//
+416
// HEADER ENDS HERE
+417
//
+418
//////////////////////////////////////////////////////////////////////////////
+419
+420
#ifndef STB_VORBIS_HEADER_ONLY
+421
+422
// global configuration settings (e.g. set these in the project/makefile),
+423
// or just set them in this file at the top (although ideally the first few
+424
// should be visible when the header file is compiled too, although it's not
+425
// crucial)
+426
+427
// STB_VORBIS_NO_PUSHDATA_API
+428
// does not compile the code for the various stb_vorbis_*_pushdata()
+429
// functions
+430
// #define STB_VORBIS_NO_PUSHDATA_API
+431
+432
// STB_VORBIS_NO_PULLDATA_API
+433
// does not compile the code for the non-pushdata APIs
+434
// #define STB_VORBIS_NO_PULLDATA_API
+435
+436
// STB_VORBIS_NO_STDIO
+437
// does not compile the code for the APIs that use FILE *s internally
+438
// or externally (implied by STB_VORBIS_NO_PULLDATA_API)
+439
// #define STB_VORBIS_NO_STDIO
+440
+441
// STB_VORBIS_NO_INTEGER_CONVERSION
+442
// does not compile the code for converting audio sample data from
+443
// float to integer (implied by STB_VORBIS_NO_PULLDATA_API)
+444
// #define STB_VORBIS_NO_INTEGER_CONVERSION
+445
+446
// STB_VORBIS_NO_FAST_SCALED_FLOAT
+447
// does not use a fast float-to-int trick to accelerate float-to-int on
+448
// most platforms which requires endianness be defined correctly.
+449
//#define STB_VORBIS_NO_FAST_SCALED_FLOAT
+450
+451
+452
// STB_VORBIS_MAX_CHANNELS [number]
+453
// globally define this to the maximum number of channels you need.
+454
// The spec does not put a restriction on channels except that
+455
// the count is stored in a byte, so 255 is the hard limit.
+456
// Reducing this saves about 16 bytes per value, so using 16 saves
+457
// (255-16)*16 or around 4KB. Plus anything other memory usage
+458
// I forgot to account for. Can probably go as low as 8 (7.1 audio),
+459
// 6 (5.1 audio), or 2 (stereo only).
+460
#ifndef STB_VORBIS_MAX_CHANNELS
+461
#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone?
+462
#endif
+463
+464
// STB_VORBIS_PUSHDATA_CRC_COUNT [number]
+465
// after a flush_pushdata(), stb_vorbis begins scanning for the
+466
// next valid page, without backtracking. when it finds something
+467
// that looks like a page, it streams through it and verifies its
+468
// CRC32. Should that validation fail, it keeps scanning. But it's
+469
// possible that _while_ streaming through to check the CRC32 of
+470
// one candidate page, it sees another candidate page. This #define
+471
// determines how many "overlapping" candidate pages it can search
+472
// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas
+473
// garbage pages could be as big as 64KB, but probably average ~16KB.
+474
// So don't hose ourselves by scanning an apparent 64KB page and
+475
// missing a ton of real ones in the interim; so minimum of 2
+476
#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT
+477
#define STB_VORBIS_PUSHDATA_CRC_COUNT 4
+478
#endif
+479
+480
// STB_VORBIS_FAST_HUFFMAN_LENGTH [number]
+481
// sets the log size of the huffman-acceleration table. Maximum
+482
// supported value is 24. with larger numbers, more decodings are O(1),
+483
// but the table size is larger so worse cache missing, so you'll have
+484
// to probe (and try multiple ogg vorbis files) to find the sweet spot.
+485
#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH
+486
#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10
+487
#endif
+488
+489
// STB_VORBIS_FAST_BINARY_LENGTH [number]
+490
// sets the log size of the binary-search acceleration table. this
+491
// is used in similar fashion to the fast-huffman size to set initial
+492
// parameters for the binary search
+493
+494
// STB_VORBIS_FAST_HUFFMAN_INT
+495
// The fast huffman tables are much more efficient if they can be
+496
// stored as 16-bit results instead of 32-bit results. This restricts
+497
// the codebooks to having only 65535 possible outcomes, though.
+498
// (At least, accelerated by the huffman table.)
+499
#ifndef STB_VORBIS_FAST_HUFFMAN_INT

Showing the first 500 of 5585 diff lines for this file. This diff is INCOMPLETE; read the file or clone the repository for the rest.