#include #include #include #include #include #include #include struct params { float gain, bias_l, bias_r; }; static void mixer_apply_mono_to_mono(int16_t *dest, int16_t *src, size_t count, struct params params) { size_t i; for (i=0; ioutput; xas_mixer_input *input = mixer->inputs; struct params params = { .gain = input->gain, .bias_l = input->bias_l, .bias_r = input->bias_r }; xas_audio_zero(output->format, mixer->buf, 0, mixer->buffer_size); while (input) { void (*mixer_apply)(int16_t *, int16_t *, size_t, struct params); xas_mixer_input *next = input->next; int16_t *buf; ssize_t readlen; size_t channels_in = input->stream->format.channels, channels_out = output->format.channels; if (channels_in == 2 && channels_out == 2) { mixer_apply = mixer_apply_stereo_to_stereo; } else if (channels_in == 1 && channels_out == 2) { mixer_apply = mixer_apply_mono_to_stereo; } else if (channels_in == 1 && channels_out == 1) { mixer_apply = mixer_apply_mono_to_mono; } else if (channels_in == 2 && channels_out == 2) { mixer_apply = mixer_apply_stereo_to_mono; } else { goto error_invalid_input; } if ((readlen = xas_audio_stream_read(input->stream, (void **)&buf, count)) < 0) { goto error_audio_read_stream; } mixer_apply((int16_t *)(mixer->buf), buf, readlen, params); if (total < readlen) { total = readlen; } input = next; } xas_audio_copy(output->format, samples, mixer->buf, 0, 0, total); return total; error_invalid_input: error_audio_read_stream: return -1; } /* TODO: Implement a single output gain, don't adjust gain on all inputs */ static int set_gain(xas_mixer *mixer, float gain) { xas_mixer_input *input = mixer->inputs; while (input) { xas_mixer_input *next = input->next; xas_mixer_input_set_gain(input, gain); input = next; } return 0; } static int noop() { return 0; } static xas_object_call_table call_table = { .start = (xas_object_start_callback)noop, .stop = (xas_object_stop_callback)noop, .set_gain = (xas_object_set_gain_callback)set_gain, .stream_new = (xas_object_stream_new_callback)xas_mixer_output, .destroy = (xas_object_destroy_callback)xas_mixer_destroy }; xas_mixer *xas_mixer_new(xas_audio_format format, size_t buffer_size) { xas_mixer *mixer; size_t total = buffer_size * format.channels * format.sample_size; if ((mixer = malloc(sizeof(*mixer))) == NULL) { goto error_malloc_mixer; } if ((mixer->buf = malloc(total)) == NULL) { goto error_malloc_buf; } if ((mixer->output = xas_audio_stream_new_source((xas_audio_fill)mixer_fill, NULL, format, buffer_size, mixer)) == NULL) { goto error_audio_stream_new_source; } mixer->obj.table = &call_table; mixer->inputs = NULL; mixer->last = NULL; mixer->buffer_size = buffer_size; return mixer; error_audio_stream_new_source: free(mixer->buf); error_malloc_buf: free(mixer); error_malloc_mixer: return NULL; } void xas_mixer_destroy(xas_mixer *mixer) { xas_mixer_input *input = mixer->inputs; while (input) { xas_mixer_input *next = input->next; if (input->flags & XAS_MIXER_INPUT_STREAM_MANAGED) { xas_audio_stream_destroy(input->stream); } free(input); input = next; } xas_audio_stream_destroy(mixer->output); free(mixer->buf); free(mixer); } xas_audio_stream *xas_mixer_output(xas_mixer *mixer) { return mixer->output; } static inline void input_set_pan(xas_mixer_input *input, float pan) { static float range = 45.0f * (M_PI / 180.0); float angle, term, cosf_angle, sinf_angle; if (pan < -1.0 || pan > 1.0) { return; } angle = pan * range; term = sqrtf(2.0f) / 2.0f; cosf_angle = cosf(angle); sinf_angle = sinf(angle); input->bias_l = term * (cosf_angle - sinf_angle); input->bias_r = term * (cosf_angle + sinf_angle); } xas_mixer_input *xas_mixer_input_add(xas_mixer *mixer, xas_audio_stream *stream, float gain, float pan) { xas_mixer_input *input; if (stream->format.sample_size != mixer->output->format.sample_size || stream->format.sample_rate != mixer->output->format.sample_rate) { errno = EINVAL; goto error_invalid_stream; } if ((input = malloc(sizeof(*input))) == NULL) { goto error_malloc_input; } input->stream = stream; input->gain = gain; input->flags = 0; input->next = NULL; input_set_pan(input, pan); if (mixer->inputs == NULL) { mixer->inputs = input; mixer->last = input; } else { mixer->last->next = input; mixer->last = input; } return input; error_malloc_input: error_invalid_stream: return NULL; } xas_mixer_input *xas_mixer_object_add(xas_mixer *mixer, xas_object *object, float gain, float pan) { xas_mixer_input *input; xas_audio_stream *stream; if ((stream = xas_object_stream_new(object)) == NULL) { goto error_object_stream_new; } if ((input = xas_mixer_input_add(mixer, stream, gain, pan)) == NULL) { goto error_input_add; } input->flags |= XAS_MIXER_INPUT_STREAM_MANAGED; return input; error_input_add: xas_audio_stream_destroy(stream); error_object_stream_new: return NULL; } void xas_mixer_input_set_gain(xas_mixer_input *input, float gain) { if (gain < 0.0 || gain > 1.0) { return; } input->gain = gain; } void xas_mixer_input_set_pan(xas_mixer_input *input, float pan) { return input_set_pan(input, pan); }