diff options
| -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();  | 
