#include #include #include #include #include "channel.h" #include "sound.h" #define channel_atomic_get(var) p_atomic_int_get(&(var)) #define channel_atomic_set(var, value) p_atomic_int_set(&(var), (value)) void channel_init(struct channel_t *chan) { chan->shared.active = false; chan->shared.paused = false; 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) { if (p_atomic_int_dec_and_test(&(chan->shared.loops))) { channel_atomic_set(chan->shared.active, false); chan->skip = true; } } } void channel_pause(struct channel_t *chan) { p_atomic_int_set(&(chan->shared.paused), true); } void channel_resume(struct channel_t *chan) { p_atomic_int_set(&(chan->shared.paused), false); } void channel_set_volume(struct channel_t *chan, float volume) { if (volume > 1.0f) volume = 1.0f; if (volume < 0.0f) volume = 0.0f; p_atomic_int_set(&(chan->shared.volume), 255*volume); } void channel_set_pan(struct channel_t *chan, float pan_left, float pan_right) { if (pan_left > 1.0f) pan_left = 1.0f; if (pan_left < -1.0f) pan_left = -1.0f; if (pan_right > 1.0f) pan_right = 1.0f; if (pan_right < -1.0f) pan_right = -1.0f; p_atomic_int_set(&(chan->shared.pan_left), 128*pan_left); p_atomic_int_set(&(chan->shared.pan_right), 128*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)) /* active, fail! */ return 1; p_mutex_lock(chan->sound_mutex); sound_copy(&(chan->sound), sound); chan->pos = 0; p_atomic_int_set(&(chan->shared.paused), false); p_atomic_int_set(&(chan->shared.loops), loops); if (!force) { /* overwrite channel settings */ p_atomic_int_set(&(chan->shared.volume), 255); if (sound->mono) { p_atomic_int_set(&(chan->shared.pan_left), 0); p_atomic_int_set(&(chan->shared.pan_right), 0); } else { /* stereo */ p_atomic_int_set(&(chan->shared.pan_left), -128); p_atomic_int_set(&(chan->shared.pan_right), 128); } } p_mutex_unlock(chan->sound_mutex); p_atomic_int_set(&(chan->shared.active), true); return 0; } #define QUARTER_PI 0.785397 static void pan_gain(float *gain_l, float *gain_r, float pan) { float theta = (QUARTER_PI * pan) + QUARTER_PI; /* 0-PI/2 */ *gain_l = cos(theta); *gain_r = sin(theta); } void channel_update_shared(struct channel_t *chan) { if (channel_atomic_get(chan->shared.paused) || !channel_atomic_get(chan->shared.active)) chan->skip = true; else chan->skip = false; float vol = ((float)channel_atomic_get(chan->shared.volume))/255; float pan_l = ((float)channel_atomic_get(chan->shared.pan_left))/128; float pan_r = ((float)channel_atomic_get(chan->shared.pan_right))/128; pan_gain(&(chan->gain_ll), &(chan->gain_lr), pan_l); pan_gain(&(chan->gain_rl), &(chan->gain_rr), pan_r); chan->gain_ll *= vol; chan->gain_lr *= vol; chan->gain_rl *= vol; chan->gain_rr *= vol; } void channel_get_next_sample(float *left, float *right, struct channel_t *chan) { if (chan->skip) { /* skip this channel */ *left = 0; *right = 0; return; } if (!p_mutex_trylock(chan->sound_mutex)) { /* can't lock mutex, skip */ *left = 0; *right = 0; return; } if (chan->sound.mono) { float x = chan->sound.left[chan->pos]; *left = chan->gain_ll * x; *right = chan->gain_lr * x; } else { float l, r; l = chan->sound.left[chan->pos]; r = chan->sound.right[chan->pos]; *left = (chan->gain_ll * l) + (chan->gain_rl * r); *right = (chan->gain_lr * l) + (chan->gain_rr * r); } chan->pos += 1; if (chan->pos >= chan->sound.len) { channel_reset(chan); } p_mutex_unlock(chan->sound_mutex); }