diff options
| author | Andre Weissflog <floooh@gmail.com> | 2019-02-21 19:56:28 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-02-21 19:56:28 +0100 |
| commit | 77c2b42e2ee26c86541dbf40acf6b99d0529d20d (patch) | |
| tree | 2e8cd20a3d8320069e51578933377ee8df59d46b | |
| parent | e125467e5e2b1ebb9bffedd64cf06f100431f87a (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.md | 7 | ||||
| -rw-r--r-- | sokol_app.h | 193 | ||||
| -rw-r--r-- | sokol_audio.h | 63 |
3 files changed, 186 insertions, 77 deletions
@@ -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); |