summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/CMakeLists.txt5
-rw-r--r--examples/callback.c55
-rw-r--r--include/mossrose.h4
-rw-r--r--src/channel.c27
-rw-r--r--src/channel.h10
-rw-r--r--src/channel.test.c75
-rw-r--r--src/mossrose.c22
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();