diff options
author | sanine <sanine.not@pm.me> | 2022-09-04 00:39:24 -0500 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2022-09-04 00:39:24 -0500 |
commit | 63db9380f84cb3eb35d2de430b0783afa5773e85 (patch) | |
tree | ffc8360c7bc7e670768cbadcf79597c03b9434a2 | |
parent | ac48f807cb85423a8063795e3320fedde1ddf5c1 (diff) |
implement callbacks
-rw-r--r-- | examples/CMakeLists.txt | 5 | ||||
-rw-r--r-- | examples/callback.c | 55 | ||||
-rw-r--r-- | include/mossrose.h | 4 | ||||
-rw-r--r-- | src/channel.c | 27 | ||||
-rw-r--r-- | src/channel.h | 10 | ||||
-rw-r--r-- | src/channel.test.c | 75 | ||||
-rw-r--r-- | src/mossrose.c | 22 |
7 files changed, 198 insertions, 0 deletions
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 7fe2030..aea5060 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -13,3 +13,8 @@ add_dependencies(examples panning) add_executable(loop ${CMAKE_CURRENT_LIST_DIR}/loop.c) target_link_libraries(loop mossrose) add_dependencies(examples loop) + + +add_executable(callback ${CMAKE_CURRENT_LIST_DIR}/callback.c) +target_link_libraries(callback mossrose) +add_dependencies(examples callback) diff --git a/examples/callback.c b/examples/callback.c new file mode 100644 index 0000000..bf7e187 --- /dev/null +++ b/examples/callback.c @@ -0,0 +1,55 @@ +#include <stdio.h> +#include <math.h> +#include <mossrose.h> +#include <portaudio.h> + +#define PI 3.14159 + + +#define SAMPLE_RATE 44100 +#define N_CHANNELS 8 + +float f(float t) +{ + const int f0 = 440; + const int f1 = 880; + return ( t*f1 ) + ( (1-t)*f0 ); +} + + +void callback(int chan, void *d) +{ + bool *loop = d; + *loop = false; + printf("channel %d waves goodnight!\n", chan); +} + + +int main() +{ + float data[SAMPLE_RATE]; + for (long i=0; i<SAMPLE_RATE; i++) { + float time = ((float)i)/SAMPLE_RATE; + data[i] = sin(2*PI*f(time)*time); + } + + struct mossrose_sound_t sound = { + .left = data, .right = NULL, .mono = true, .len = SAMPLE_RATE + }; + + int err = mossrose_init(SAMPLE_RATE, N_CHANNELS, true); + if (err != 0) + fprintf(stderr, "FAILED TO INITIALIZE MOSSROSE\n"); + + int chan = mossrose_play(&sound, -1, 1); + + bool loop = true; + mossrose_channel_set_callback(chan, callback, &loop); + + while(loop) { + mossrose_poll_callbacks(); + } + + mossrose_terminate(); + return 0; +} diff --git a/include/mossrose.h b/include/mossrose.h index 72f7b99..d8573a8 100644 --- a/include/mossrose.h +++ b/include/mossrose.h @@ -4,6 +4,8 @@ #include <stddef.h> #include <stdbool.h> +typedef void (*mossrose_channel_callback_t)(int, void*); + struct mossrose_sound_t { float *left; float *right; @@ -20,5 +22,7 @@ void mossrose_channel_set_volume(int channel, float volume); void mossrose_channel_set_pan(int channel, float left, float right); void mossrose_channel_pause(int channel); void mossrose_channel_resume(int channel); +void mossrose_channel_set_callback(int channel, mossrose_channel_callback_t callback, void *userdata); +void mossrose_poll_callbacks(); #endif diff --git a/src/channel.c b/src/channel.c index 8615265..ac955a1 100644 --- a/src/channel.c +++ b/src/channel.c @@ -15,16 +15,24 @@ void channel_init(struct channel_t *chan) chan->shared.volume = 255; chan->shared.pan_left = -128; chan->shared.pan_right = 128; + chan->shared.trigger_callback = 0; chan->sound_mutex = p_mutex_new(); chan->sound.left = NULL; chan->sound.right = NULL; chan->pos = 0; + + chan->callback = NULL; + chan->userdata = NULL; } void channel_reset(struct channel_t *chan) { + p_atomic_int_inc(&(chan->shared.trigger_callback)); + if (p_atomic_int_get(&(chan->shared.trigger_callback)) > 32000) + /* prevent integer overflow */ + p_atomic_int_set(&(chan->shared.trigger_callback), 1); chan->pos = 0; int loops = channel_atomic_get(chan->shared.loops); if (loops) { @@ -68,6 +76,25 @@ void channel_set_pan(struct channel_t *chan, float pan_left, float pan_right) } +void channel_set_callback(struct channel_t *chan, channel_callback_t callback, void *userdata) +{ + channel_atomic_set(chan->shared.trigger_callback, 0); + chan->callback = callback; + chan->userdata = userdata; +} + + +void channel_poll_callbacks(struct channel_t *chan, int index) +{ + int trigger_callback = channel_atomic_get(chan->shared.trigger_callback); + while (trigger_callback) { + if (chan->callback != NULL) chan->callback(index, chan->userdata); + trigger_callback -= 1; + } + channel_atomic_set(chan->shared.trigger_callback, 0); +} + + int channel_sound_load(struct channel_t *chan, struct mossrose_sound_t *sound, bool force, int loops) { if (!force && channel_atomic_get(chan->shared.active)) diff --git a/src/channel.h b/src/channel.h index 5be42a2..61a1c7d 100644 --- a/src/channel.h +++ b/src/channel.h @@ -7,6 +7,9 @@ #include <mossrose.h> +typedef void (*channel_callback_t)(int channel, void *userdata); + + struct channel_shared_t { volatile pint active; /* boolean */ volatile pint paused; /* boolean */ @@ -14,6 +17,7 @@ struct channel_shared_t { volatile pint pan_left; /* -255-255 */ volatile pint pan_right; /* -255-255 */ volatile pint loops; + volatile pint trigger_callback; }; @@ -26,6 +30,9 @@ struct channel_t { PMutex *sound_mutex; struct mossrose_sound_t sound; size_t pos; + + channel_callback_t callback; + void *userdata; }; @@ -35,7 +42,10 @@ void channel_pause(struct channel_t *chan); void channel_resume(struct channel_t *chan); void channel_set_volume(struct channel_t *chan, float volume); void channel_set_pan(struct channel_t *chan, float pan_left, float pan_right); +void channel_set_callback(struct channel_t *chan, channel_callback_t callback, void *userdata); +void channel_poll_callbacks(struct channel_t *chan, int index); int channel_sound_load(struct channel_t *chan, struct mossrose_sound_t *sound, bool force, int loops); +void channel_update_shared(struct channel_t *chan); void channel_get_next_sample(float *left, float *right, struct channel_t *chan); diff --git a/src/channel.test.c b/src/channel.test.c index 067879f..1b07350 100644 --- a/src/channel.test.c +++ b/src/channel.test.c @@ -68,6 +68,9 @@ void test_channel_init() lily_assert_null(chan.sound.left); lily_assert_null(chan.sound.right); lily_assert_int_equal(chan.pos, 0); + + lily_assert_null(chan.callback); + lily_assert_null(chan.userdata); } @@ -76,10 +79,12 @@ void test_channel_reset_last_loop() struct channel_t chan; chan.shared.active = true; chan.shared.loops = 1; + chan.shared.trigger_callback = 0; channel_reset(&chan); lily_assert_int_equal(chan.shared.active, false); + lily_assert_int_equal(chan.shared.trigger_callback, 1); } @@ -113,6 +118,19 @@ void test_channel_reset_infinite_loop() } +void test_channel_reset_trigger_callback() +{ + struct channel_t chan; + chan.shared.active = true; + chan.shared.loops = 1; + chan.shared.trigger_callback = 0; + + channel_reset(&chan); + + lily_assert_int_equal(chan.shared.trigger_callback, 1); +} + + void test_channel_pause() { struct channel_t chan; @@ -187,6 +205,59 @@ void test_channel_set_pan() } +void test_cb(int c, void *d) {} + +void test_channel_set_callback() +{ + struct channel_t chan; + chan.callback = NULL; + chan.userdata = NULL; + chan.shared.trigger_callback = 15; + + channel_set_callback(&chan, test_cb, &chan); + + lily_assert_int_equal(chan.shared.trigger_callback, 0); + lily_assert_ptr_equal(chan.callback, test_cb); + lily_assert_ptr_equal(chan.userdata, &chan); +} + + +void test_cb2(int c, void *d) +{ + int *x = d; + *x += 1; +} + +void test_channel_poll_callbacks() +{ + struct channel_t chan; + chan.callback = test_cb2; + int x = 0; + chan.userdata = &x; + chan.shared.trigger_callback = 5; + + channel_poll_callbacks(&chan, 0); + + lily_assert_int_equal(x, 5); + lily_assert_int_equal(chan.shared.trigger_callback, 0); +} + + +void test_channel_poll_callbacks_none() +{ + struct channel_t chan; + chan.callback = test_cb2; + int x = 0; + chan.userdata = &x; + chan.shared.trigger_callback = 0; + + channel_poll_callbacks(&chan, 0); + + lily_assert_int_equal(x, 0); + lily_assert_int_equal(chan.shared.trigger_callback, 0); +} + + void test_channel_sound_load() { lily_mock_use(&mock_p_mutex_lock); @@ -677,6 +748,7 @@ void suite_channel() lily_run_test(test_channel_reset_last_loop); lily_run_test(test_channel_reset_penultimate_loop); lily_run_test(test_channel_reset_infinite_loop); + lily_run_test(test_channel_reset_trigger_callback); lily_run_test(test_channel_sound_load); lily_run_test(test_channel_sound_load_mono); @@ -692,6 +764,9 @@ void suite_channel() lily_run_test(test_channel_get_next_sample_mono_panned); lily_run_test(test_channel_get_next_sample_panned); + lily_run_test(test_channel_poll_callbacks); + lily_run_test(test_channel_poll_callbacks_none); + lily_mock_destroy(mock_p_mutex_new); lily_mock_destroy(mock_p_mutex_trylock); lily_mock_destroy(mock_p_mutex_unlock); diff --git a/src/mossrose.c b/src/mossrose.c index cd1b98d..2e27c45 100644 --- a/src/mossrose.c +++ b/src/mossrose.c @@ -4,6 +4,7 @@ #include <portaudio.h> #include <mossrose.h> #include "channel.h" +#include "sound.h" struct mossrose_global_t { @@ -131,8 +132,29 @@ void mossrose_channel_resume(int channel) { } +void mossrose_channel_set_callback(int channel, channel_callback_t callback, void *userdata) +{ + struct channel_t *chan = mossrose_global.channels + channel; + channel_set_callback(chan, callback, userdata); +} + + +void mossrose_poll_callbacks() +{ + for (int i=0; i<mossrose_global.n_channels; i++) { + struct channel_t *chan = mossrose_global.channels + i; + channel_poll_callbacks(chan, i); + } +} + + int mossrose_terminate() { + for (int i=0; i<mossrose_global.n_channels; i++) { + sound_free_audio(&(mossrose_global.channels[i].sound)); + } + free(mossrose_global.channels); + Pa_AbortStream(mossrose_global.stream); Pa_CloseStream(mossrose_global.stream); Pa_Terminate(); |