aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndre Weissflog <floooh@gmail.com>2019-02-21 19:56:28 +0100
committerGitHub <noreply@github.com>2019-02-21 19:56:28 +0100
commit77c2b42e2ee26c86541dbf40acf6b99d0529d20d (patch)
tree2e8cd20a3d8320069e51578933377ee8df59d46b
parente125467e5e2b1ebb9bffedd64cf06f100431f87a (diff)
sokol_app.h/sokol_audio.h: add userdata support for callbacks (#120)
sokol_app.h and sokol_audio.h now have an alternative set of callbacks with user_data arguments. This is useful if you don't want or cannot store your own application state in global variables. See the header documentation in sokol_app.h and sokol_audio.h for details, and check out the samples *sapp/noentry-sapp.c* and *sapp/modplay-sapp.c* in https://github.com/floooh/sokol-samples
-rw-r--r--README.md7
-rw-r--r--sokol_app.h193
-rw-r--r--sokol_audio.h63
3 files changed, 186 insertions, 77 deletions
diff --git a/README.md b/README.md
index 60ef446a..5c658f95 100644
--- a/README.md
+++ b/README.md
@@ -407,6 +407,13 @@ Mainly some "missing features" for desktop apps:
# Updates
+- **21-Feb-2019**: sokol_app.h and sokol_audio.h now have an alternative
+set of callbacks with user_data arguments. This is useful if you don't
+want or cannot store your own application state in global variables.
+See the header documentation in sokol_app.h and sokol_audio.h for details,
+and check out the samples *sapp/noentry-sapp.c* and *sapp/modplay-sapp.c*
+in https://github.com/floooh/sokol-samples
+
- **19-Feb-2019**: sokol_app.h now has an alternative mode where it doesn't
"hijack" the platform's main() function. Search for SOKOL_NO_ENTRY in
sokol_app.h for details and documentation.
diff --git a/sokol_app.h b/sokol_app.h
index e8217f58..4bec50ea 100644
--- a/sokol_app.h
+++ b/sokol_app.h
@@ -127,22 +127,44 @@
All provided function callbacks will be called from the same thread,
but this may be different from the thread where sokol_main() was called.
- .init_cb (void (*init_cb)(void))
+ .init_cb (void (*)(void))
This function is called once after the application window,
3D rendering context and swap chain have been created. The
function takes no arguments and has no return value.
- .frame_cb (void (*frame_cb)(void))
+ .frame_cb (void (*)(void))
This is the per-frame callback, which is usually called 60
times per second. This is where your application would update
most of its state and perform all rendering.
- .cleanup_cb (void (*cleanup_cb)(void))
+ .cleanup_cb (void (*)(void))
The cleanup callback is called once right before the application
quits.
- .event_cb (void (*event_cb)(const sapp_event* event))
+ .event_cb (void (*)(const sapp_event* event))
The event callback is mainly for input handling, but in the
future may also be used to communicate other types of events
to the application. Keep the event_cb struct member zero-initialized
if your application doesn't require event handling.
+ .fail_cb (void (*)(const char* msg))
+ The fail callback is called when a fatal error is encountered
+ during start which doesn't allow the program to continue.
+ Providing a callback here gives you a chance to show an error message
+ to the user. The default behaviour is SOKOL_LOG(msg)
+
+ As you can see, those 'standard callbacks' don't have a user_data
+ argument, so any data that needs to be preserved between callbacks
+ must live in global variables. If you're allergic to global variables
+ or cannot use them for other reasons, an alternative set of callbacks
+ can be defined in sapp_desc, together with a user_data pointer:
+
+ .user_data (void*)
+ The user-data argument for the callbacks below
+ .init_userdata_cb (void (*)(void* user_data))
+ .frame_userdata_cb (void (*)(void* user_data))
+ .cleanup_userdata_cb (void (*)(void* user_data))
+ .event_cb (void(*)(const sapp_event* event, void* user_data))
+ .fail_cb (void(*)(const char* msg, void* user_data))
+ These are the user-data versions of the callback functions. You
+ can mix those with the standard callbacks that don't have the
+ user_data argument.
NOTE that there's also an alternative compile mode where sokol_app.h
doesn't "hijack" the main() function. Search below for SOKOL_NO_ENTRY.
@@ -568,28 +590,35 @@ typedef struct sapp_event {
} sapp_event;
typedef struct sapp_desc {
- void (*init_cb)(void);
+ void (*init_cb)(void); /* these are the user-provided callbacks without user data */
void (*frame_cb)(void);
void (*cleanup_cb)(void);
void (*event_cb)(const sapp_event*);
void (*fail_cb)(const char*);
- int width;
- int height;
- int sample_count;
- int swap_interval;
- bool high_dpi;
- bool fullscreen;
- bool alpha;
- bool premultiplied_alpha;
- bool preserve_drawing_buffer;
- const char* window_title;
- const char* html5_canvas_name;
- bool html5_canvas_resize;
- bool ios_keyboard_resizes_canvas;
- /* use GLES2 even if GLES3 is available */
- bool gl_force_gles2;
- /* if true, user is expected to manage cursor image and visibility on SAPP_EVENTTYPE_UPDATE_CURSOR */
- bool user_cursor;
+
+ void* user_data; /* these are the user-provided callbacks with user data */
+ void (*init_userdata_cb)(void*);
+ void (*frame_userdata_cb)(void*);
+ void (*cleanup_userdata_cb)(void*);
+ void (*event_userdata_cb)(const sapp_event*, void*);
+ void (*fail_userdata_cb)(const char*, void*);
+
+ int width; /* the preferred width of the window / canvas */
+ int height; /* the preferred height of the window / canvas */
+ int sample_count; /* MSAA sample count */
+ int swap_interval; /* the preferred swap interval (ignored on some platforms) */
+ bool high_dpi; /* whether the rendering canvas is full-resolution on HighDPI displays */
+ bool fullscreen; /* whether the window should be created in fullscreen mode */
+ bool alpha; /* whether the framebuffer should have an alpha channel (ignored on some platforms) */
+ const char* window_title; /* the window title as UTF-8 encoded string */
+ bool user_cursor; /* if true, user is expected to manage cursor image in SAPP_EVENTTYPE_UPDATE_CURSOR */
+
+ const char* html5_canvas_name; /* the name (id) of the HTML5 canvas element, default is "canvas" */
+ bool html5_canvas_resize; /* if true, the HTML5 canvas size is set to sapp_desc.width/height, otherwise canvas size is tracked */
+ bool html5_preserve_drawing_buffer; /* HTML5 only: whether to preserve default framebuffer content between frames */
+ bool html5_premultiplied_alpha; /* HTML5 only: whether the rendered pixels use premultiplied alpha convention */
+ bool ios_keyboard_resizes_canvas; /* if true, showing the iOS keyboard shrinks the canvas */
+ bool gl_force_gles2; /* if true, setup GLES2/WebGL even if GLES3/WebGL2 is available */
} sapp_desc;
/* user-provided functions */
@@ -775,12 +804,51 @@ _SOKOL_PRIVATE void _sapp_fail(const char* msg) {
if (_sapp.desc.fail_cb) {
_sapp.desc.fail_cb(msg);
}
+ else if (_sapp.desc.fail_userdata_cb) {
+ _sapp.desc.fail_userdata_cb(msg, _sapp.desc.user_data);
+ }
else {
SOKOL_LOG(msg);
}
SOKOL_ABORT();
}
+_SOKOL_PRIVATE void _sapp_call_init(void) {
+ if (_sapp.desc.init_cb) {
+ _sapp.desc.init_cb();
+ }
+ else if (_sapp.desc.init_userdata_cb) {
+ _sapp.desc.init_userdata_cb(_sapp.desc.user_data);
+ }
+}
+
+_SOKOL_PRIVATE void _sapp_call_frame(void) {
+ if (_sapp.desc.frame_cb) {
+ _sapp.desc.frame_cb();
+ }
+ else if (_sapp.desc.frame_userdata_cb) {
+ _sapp.desc.frame_userdata_cb(_sapp.desc.user_data);
+ }
+}
+
+_SOKOL_PRIVATE void _sapp_call_cleanup(void) {
+ if (_sapp.desc.cleanup_cb) {
+ _sapp.desc.cleanup_cb();
+ }
+ else if (_sapp.desc.cleanup_userdata_cb) {
+ _sapp.desc.cleanup_userdata_cb(_sapp.desc.user_data);
+ }
+}
+
+_SOKOL_PRIVATE void _sapp_call_event(const sapp_event* e) {
+ if (_sapp.desc.event_cb) {
+ _sapp.desc.event_cb(e);
+ }
+ else if (_sapp.desc.event_userdata_cb) {
+ _sapp.desc.event_userdata_cb(e, _sapp.desc.user_data);
+ }
+}
+
_SOKOL_PRIVATE void _sapp_strcpy(const char* src, char* dst, int max_len) {
SOKOL_ASSERT(src && dst && (max_len > 0));
char* const end = &(dst[max_len-1]);
@@ -799,9 +867,6 @@ _SOKOL_PRIVATE void _sapp_strcpy(const char* src, char* dst, int max_len) {
}
_SOKOL_PRIVATE void _sapp_init_state(const sapp_desc* desc) {
- SOKOL_ASSERT(desc->init_cb);
- SOKOL_ASSERT(desc->frame_cb);
- SOKOL_ASSERT(desc->cleanup_cb);
memset(&_sapp, 0, sizeof(_sapp));
_sapp.desc = *desc;
_sapp.first_frame = true;
@@ -835,7 +900,7 @@ _SOKOL_PRIVATE void _sapp_init_event(sapp_event_type type) {
_SOKOL_PRIVATE bool _sapp_events_enabled(void) {
/* only send events when an event callback is set, and the init function was called */
- return _sapp.desc.event_cb && _sapp.init_called;
+ return (_sapp.desc.event_cb || _sapp.desc.event_userdata_cb) && _sapp.init_called;
}
_SOKOL_PRIVATE sapp_keycode _sapp_translate_key(int scan_code) {
@@ -850,10 +915,10 @@ _SOKOL_PRIVATE sapp_keycode _sapp_translate_key(int scan_code) {
_SOKOL_PRIVATE void _sapp_frame(void) {
if (_sapp.first_frame) {
_sapp.first_frame = false;
- _sapp.desc.init_cb();
+ _sapp_call_init();
_sapp.init_called = true;
}
- _sapp.desc.frame_cb();
+ _sapp_call_frame();
_sapp.frame_count++;
}
@@ -1126,7 +1191,7 @@ _SOKOL_PRIVATE void _sapp_macos_mouse_event(sapp_event_type type, sapp_mousebutt
_sapp.event.modifiers = mod;
_sapp.event.mouse_x = _sapp.mouse_x;
_sapp.event.mouse_y = _sapp.mouse_y;
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
@@ -1135,20 +1200,20 @@ _SOKOL_PRIVATE void _sapp_macos_key_event(sapp_event_type type, sapp_keycode key
_sapp_init_event(type);
_sapp.event.key_code = key;
_sapp.event.modifiers = mod;
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
_SOKOL_PRIVATE void _sapp_macos_app_event(sapp_event_type type) {
if (_sapp_events_enabled()) {
_sapp_init_event(type);
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
@implementation _sapp_macos_window_delegate
- (BOOL)windowShouldClose:(id)sender {
- _sapp.desc.cleanup_cb();
+ _sapp_call_cleanup();
return YES;
}
@@ -1244,7 +1309,7 @@ _SOKOL_PRIVATE void _sapp_macos_app_event(sapp_event_type type) {
_sapp.event.mouse_y = _sapp.mouse_y;
_sapp.event.scroll_x = dx;
_sapp.event.scroll_y = dy;
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
}
@@ -1263,7 +1328,7 @@ _SOKOL_PRIVATE void _sapp_macos_app_event(sapp_event_type type) {
continue;
}
_sapp.event.char_code = codepoint;
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
}
@@ -1375,7 +1440,7 @@ int main(int argc, char* argv[]) {
_SOKOL_PRIVATE void _sapp_ios_app_event(sapp_event_type type) {
if (_sapp_events_enabled()) {
_sapp_init_event(type);
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
@@ -1575,7 +1640,7 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) {
if ((c < 0xD800) || (c > 0xDFFF)) {
_sapp_init_event(SAPP_EVENTTYPE_CHAR);
_sapp.event.char_code = c;
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
if (c <= 32) {
@@ -1588,10 +1653,10 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) {
if (k != SAPP_KEYCODE_INVALID) {
_sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN);
_sapp.event.key_code = k;
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
_sapp_init_event(SAPP_EVENTTYPE_KEY_UP);
_sapp.event.key_code = k;
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
}
@@ -1600,10 +1665,10 @@ _SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) {
/* this was a backspace */
_sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN);
_sapp.event.key_code = SAPP_KEYCODE_BACKSPACE;
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
_sapp_init_event(SAPP_EVENTTYPE_KEY_UP);
_sapp.event.key_code = SAPP_KEYCODE_BACKSPACE;
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
return NO;
@@ -1648,7 +1713,7 @@ _SOKOL_PRIVATE void _sapp_ios_touch_event(sapp_event_type type, NSSet<UITouch *>
}
}
if (_sapp.event.num_touches > 0) {
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
}
@@ -1811,7 +1876,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenU
emscripten_set_canvas_element_size(_sapp.html5_canvas_name, w, h);
if (_sapp_events_enabled()) {
_sapp_init_event(SAPP_EVENTTYPE_RESIZED);
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
return true;
}
@@ -1832,7 +1897,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_context_cb(int emsc_type, const void* reserved
}
if (_sapp_events_enabled() && (SAPP_EVENTTYPE_INVALID != type)) {
_sapp_init_event(type);
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
return true;
}
@@ -1892,7 +1957,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseE
}
_sapp.event.mouse_x = _sapp.mouse_x;
_sapp.event.mouse_y = _sapp.mouse_y;
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
_sapp_emsc_update_keyboard_state();
@@ -1916,7 +1981,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_wheel_cb(int emsc_type, const EmscriptenWheelE
}
_sapp.event.scroll_x = -0.1 * (float)emsc_event->deltaX;
_sapp.event.scroll_y = -0.1 * (float)emsc_event->deltaY;
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
_sapp_emsc_update_keyboard_state();
return true;
@@ -2024,7 +2089,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboard
break;
}
}
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
_sapp_emsc_update_keyboard_state();
@@ -2079,7 +2144,7 @@ _SOKOL_PRIVATE EM_BOOL _sapp_emsc_touch_cb(int emsc_type, const EmscriptenTouchE
dst->pos_y = src->targetY * _sapp.dpi_scale;
dst->changed = src->isChanged;
}
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
_sapp_emsc_update_keyboard_state();
@@ -2216,8 +2281,8 @@ _SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) {
attrs.depth = true;
attrs.stencil = true;
attrs.antialias = _sapp.sample_count > 1;
- attrs.premultipliedAlpha = _sapp.desc.premultiplied_alpha;
- attrs.preserveDrawingBuffer = _sapp.desc.preserve_drawing_buffer;
+ attrs.premultipliedAlpha = _sapp.desc.html5_premultiplied_alpha;
+ attrs.preserveDrawingBuffer = _sapp.desc.html5_preserve_drawing_buffer;
attrs.enableExtensionsByDefault = true;
#if defined(SOKOL_GLES3)
if (_sapp.desc.gl_force_gles2) {
@@ -3617,7 +3682,7 @@ _SOKOL_PRIVATE void _sapp_win32_mouse_event(sapp_event_type type, sapp_mousebutt
_sapp.event.mouse_button = btn;
_sapp.event.mouse_x = _sapp.mouse_x;
_sapp.event.mouse_y = _sapp.mouse_y;
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
@@ -3627,7 +3692,7 @@ _SOKOL_PRIVATE void _sapp_win32_scroll_event(float x, float y) {
_sapp.event.modifiers = _sapp_win32_mods();
_sapp.event.scroll_x = -x / 30.0f;
_sapp.event.scroll_y = y / 30.0f;
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
@@ -3636,7 +3701,7 @@ _SOKOL_PRIVATE void _sapp_win32_key_event(sapp_event_type type, int vk) {
_sapp_init_event(type);
_sapp.event.modifiers = _sapp_win32_mods();
_sapp.event.key_code = _sapp.keycodes[vk];
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
@@ -3645,14 +3710,14 @@ _SOKOL_PRIVATE void _sapp_win32_char_event(uint32_t c) {
_sapp_init_event(SAPP_EVENTTYPE_CHAR);
_sapp.event.modifiers = _sapp_win32_mods();
_sapp.event.char_code = c;
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
_SOKOL_PRIVATE void _sapp_win32_app_event(sapp_event_type type) {
if (_sapp_events_enabled()) {
_sapp_init_event(type);
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
@@ -3922,7 +3987,7 @@ _SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) {
_sapp_wgl_swap_buffers();
#endif
}
- _sapp.desc.cleanup_cb();
+ _sapp_call_cleanup();
#if defined(SOKOL_D3D11)
_sapp_d3d11_destroy_default_render_target();
@@ -4145,7 +4210,7 @@ _SOKOL_PRIVATE void _sapp_android_app_event(sapp_event_type type) {
if (_sapp_events_enabled()) {
_sapp_init_event(type);
SOKOL_LOG("event_cb()");
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
@@ -4204,7 +4269,7 @@ _SOKOL_PRIVATE void _sapp_android_cleanup(void) {
/* egl context is bound, cleanup gracefully */
if (_sapp.init_called && !_sapp.cleanup_called) {
SOKOL_LOG("cleanup_cb()");
- _sapp.desc.cleanup_cb();
+ _sapp_call_cleanup();
_sapp.cleanup_called = true;
}
}
@@ -4284,7 +4349,7 @@ _SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) {
dst->changed = true;
}
}
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
return true;
}
@@ -6115,7 +6180,7 @@ _SOKOL_PRIVATE uint32_t _sapp_x11_mod(int x11_mods) {
_SOKOL_PRIVATE void _sapp_x11_app_event(sapp_event_type type) {
if (_sapp_events_enabled()) {
_sapp_init_event(type);
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
@@ -6135,7 +6200,7 @@ _SOKOL_PRIVATE void _sapp_x11_mouse_event(sapp_event_type type, sapp_mousebutton
_sapp.event.modifiers = mods;
_sapp.event.mouse_x = _sapp.mouse_x;
_sapp.event.mouse_y = _sapp.mouse_y;
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
@@ -6145,7 +6210,7 @@ _SOKOL_PRIVATE void _sapp_x11_scroll_event(float x, float y, uint32_t mods) {
_sapp.event.modifiers = mods;
_sapp.event.scroll_x = x;
_sapp.event.scroll_y = y;
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
@@ -6154,7 +6219,7 @@ _SOKOL_PRIVATE void _sapp_x11_key_event(sapp_event_type type, sapp_keycode key,
_sapp_init_event(type);
_sapp.event.key_code = key;
_sapp.event.modifiers = mods;
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
@@ -6163,7 +6228,7 @@ _SOKOL_PRIVATE void _sapp_x11_char_event(uint32_t chr, uint32_t mods) {
_sapp_init_event(SAPP_EVENTTYPE_CHAR);
_sapp.event.char_code = chr;
_sapp.event.modifiers = mods;
- _sapp.desc.event_cb(&_sapp.event);
+ _sapp_call_event(&_sapp.event);
}
}
@@ -6477,7 +6542,7 @@ _SOKOL_PRIVATE void _sapp_run(const sapp_desc* desc) {
_sapp_glx_swap_buffers();
XFlush(_sapp_x11_display);
}
- _sapp.desc.cleanup_cb();
+ _sapp_call_cleanup();
_sapp_glx_destroy_context();
_sapp_x11_destroy_window();
XCloseDisplay(_sapp_x11_display);
diff --git a/sokol_audio.h b/sokol_audio.h
index 982f3de5..210e47fc 100644
--- a/sokol_audio.h
+++ b/sokol_audio.h
@@ -99,8 +99,8 @@
on all audio backends.
If you want to use the callback-model, you need to provide a stream
- callback function in stream_cb, otherwise keep the stream_cb member
- initialized to zero.
+ callback function either in saudio_desc.stream_cb or saudio_desc.stream_userdata_cb,
+ otherwised keep both function pointers zero-initialized.
Use push model and default playback parameters:
@@ -112,6 +112,14 @@
.stream_cb = my_stream_callback
});
+ The standard stream callback doesn't have a user data argument, if you want
+ that, use the alternative stream_userdata_cb and also set the user_data pointer:
+
+ saudio_setup(&(saudio_desc){
+ .stream_userdata_cb = my_stream_callback,
+ .user_data = &my_data
+ });
+
The following playback parameters can be provided through the
saudio_desc struct:
@@ -121,9 +129,10 @@
int num_channels -- number of channels, default: 1 (mono)
int buffer_frames -- number of frames in streaming buffer, default: 2048
- Stream callback parameters:
+ The stream callback prototype (either with or without userdata):
void (*stream_cb)(float* buffer, int num_frames, int num_channels)
+ void (*stream_userdata_cb)(float* buffer, int num_frames, int num_channels, void* user_data)
Function pointer to the user-provide stream callback.
Push-model parameters:
@@ -168,7 +177,16 @@
To use Sokol Audio in stream-callback-mode, provide a callback function
like this in the saudio_desc struct when calling saudio_setup():
- void stream_cb(float* buffer, int num_frames, int num_channels) { ... }
+ void stream_cb(float* buffer, int num_frames, int num_channels) {
+ ...
+ }
+
+ Or the alternative version with a user-data argument:
+
+ void stream_userdata_cb(float* buffer, int num_frames, int num_channels, void* user_data) {
+ my_data_t* my_data = (my_data_t*) user_data;
+ ...
+ }
The job of the callback function is to fill the *buffer* with 32-bit
float sample values.
@@ -351,7 +369,9 @@ typedef struct saudio_desc {
int buffer_frames; /* number of frames in streaming buffer */
int packet_frames; /* number of frames in a packet */
int num_packets; /* number of packets in packet queue */
- void (*stream_cb)(float* buffer, int num_frames, int num_channels); /* optional streaming callback */
+ void (*stream_cb)(float* buffer, int num_frames, int num_channels); /* optional streaming callback (no user data) */
+ void (*stream_userdata_cb)(float* buffer, int num_frames, int num_channels, void* user_data); /*... and with user data */
+ void* user_data; /* optional user data argument for stream_userdata_cb */
} saudio_desc;
/* setup sokol-audio */
@@ -573,6 +593,8 @@ typedef struct {
typedef struct {
bool valid;
void (*stream_cb)(float* buffer, int num_frames, int num_channels);
+ void (*stream_userdata_cb)(float* buffer, int num_frames, int num_channels, void* user_data);
+ void* user_data;
int sample_rate; /* sample rate */
int buffer_frames; /* number of frames in streaming buffer */
int bytes_per_frame; /* filled by backend */
@@ -586,6 +608,19 @@ typedef struct {
static _saudio_state_t _saudio;
+_SOKOL_PRIVATE bool _saudio_has_callback(void) {
+ return (_saudio.stream_cb || _saudio.stream_userdata_cb);
+}
+
+_SOKOL_PRIVATE void _saudio_stream_callback(float* buffer, int num_frames, int num_channels) {
+ if (_saudio.stream_cb) {
+ _saudio.stream_cb(buffer, num_frames, num_channels);
+ }
+ else if (_saudio.stream_userdata_cb) {
+ _saudio.stream_userdata_cb(buffer, num_frames, num_channels, _saudio.user_data);
+ }
+}
+
/*=== MUTEX IMPLEMENTATION ===================================================*/
#if (defined(__APPLE__) || defined(__linux__) || defined(__unix__)) && !defined(__EMSCRIPTEN__)
_SOKOL_PRIVATE void _saudio_mutex_init(_saudio_mutex_t* m) {
@@ -813,10 +848,10 @@ _SOKOL_PRIVATE void _saudio_backend_shutdown(void) { };
/* NOTE: the buffer data callback is called on a separate thread! */
_SOKOL_PRIVATE void _sapp_ca_callback(void* user_data, AudioQueueRef queue, AudioQueueBufferRef buffer) {
- if (_saudio.stream_cb) {
+ if (_saudio_has_callback()) {
const int num_frames = buffer->mAudioDataByteSize / _saudio.bytes_per_frame;
const int num_channels = _saudio.num_channels;
- _saudio.stream_cb((float*)buffer->mAudioData, num_frames, num_channels);
+ _saudio_stream_callback((float*)buffer->mAudioData, num_frames, num_channels);
}
else {
uint8_t* ptr = (uint8_t*)buffer->mAudioData;
@@ -887,8 +922,8 @@ _SOKOL_PRIVATE void* _saudio_alsa_cb(void* param) {
}
else {
/* fill the streaming buffer with new data */
- if (_saudio.stream_cb) {
- _saudio.stream_cb(_saudio.backend.buffer, _saudio.backend.buffer_frames, _saudio.num_channels);
+ if (_saudio_has_callback()) {
+ _saudio_stream_callback(_saudio.backend.buffer, _saudio.backend.buffer_frames, _saudio.num_channels);
}
else {
if (0 == _saudio_fifo_read(&_saudio.fifo, (uint8_t*)_saudio.backend.buffer, _saudio.backend.buffer_byte_size)) {
@@ -969,8 +1004,8 @@ _SOKOL_PRIVATE void _saudio_backend_shutdown(void) {
/* fill intermediate buffer with new data and reset buffer_pos */
_SOKOL_PRIVATE void _saudio_wasapi_fill_buffer(void) {
- if (_saudio.stream_cb) {
- _saudio.stream_cb(_saudio.backend.thread.src_buffer, _saudio.backend.thread.src_buffer_frames, _saudio.num_channels);
+ if (_saudio_has_callback()) {
+ _saudio_stream_callback(_saudio.backend.thread.src_buffer, _saudio.backend.thread.src_buffer_frames, _saudio.num_channels);
}
else {
if (0 == _saudio_fifo_read(&_saudio.fifo, (uint8_t*)_saudio.backend.thread.src_buffer, _saudio.backend.thread.src_buffer_byte_size)) {
@@ -1166,8 +1201,8 @@ extern "C" {
EMSCRIPTEN_KEEPALIVE int _saudio_emsc_pull(int num_frames) {
SOKOL_ASSERT(_saudio.backend.buffer);
if (num_frames == _saudio.buffer_frames) {
- if (_saudio.stream_cb) {
- _saudio.stream_cb((float*)_saudio.backend.buffer, num_frames, _saudio.num_channels);
+ if (_saudio_has_callback()) {
+ _saudio_stream_callback((float*)_saudio.backend.buffer, num_frames, _saudio.num_channels);
}
else {
const int num_bytes = num_frames * _saudio.bytes_per_frame;
@@ -1298,6 +1333,8 @@ SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) {
memset(&_saudio, 0, sizeof(_saudio));
_saudio.desc = *desc;
_saudio.stream_cb = desc->stream_cb;
+ _saudio.stream_userdata_cb = desc->stream_userdata_cb;
+ _saudio.user_data = desc->user_data;
_saudio.sample_rate = _saudio_def(_saudio.desc.sample_rate, _SAUDIO_DEFAULT_SAMPLE_RATE);
_saudio.buffer_frames = _saudio_def(_saudio.desc.buffer_frames, _SAUDIO_DEFAULT_BUFFER_FRAMES);
_saudio.packet_frames = _saudio_def(_saudio.desc.packet_frames, _SAUDIO_DEFAULT_PACKET_FRAMES);