#include #include #include #include #include #include static inline float dist(xas_spatial_coord a, xas_spatial_coord b) { return powf(powf(b.x - a.x, 2.0f) + powf(b.y - a.y, 2.0f) + powf(b.z - a.z, 2.0f), 0.5f); } static int buffer_realloc(xas_spatial_scene *scene, xas_spatial_buffer *buffer) { float seconds = scene->radius / scene->speed; size_t sample_rate = scene->format.sample_rate, stride = scene->format.channels * scene->format.sample_size, count = sample_rate * ceilf(seconds), total = sizeof(xas_spatial_buffer) + stride * count; if ((buffer = realloc(buffer, total)) == NULL) { goto error_realloc_buffer; } memset(buffer + 1, '\0', stride * count); scene->buffer = buffer; scene->buffer->index = 0; scene->buffer->size = count; return 0; error_realloc_buffer: return -1; } static inline size_t sample_delta(xas_spatial_scene *scene, float distance) { return floorf(distance / scene->speed / scene->format.sample_rate); } static inline int16_t sample_scale(int16_t value, float distance) { return (int16_t)roundf((float)value * (1.0f / powf(distance, 2.0))); } static void buffer_zero(xas_spatial_buffer *buffer, size_t count) { int16_t *dest = (int16_t *)(buffer + 1); size_t i = buffer->index; if (count > buffer->size) { count = buffer->size; } while (count--) { if (i == buffer->size) { i = 0; } dest[XAS_AUDIO_STEREO*i] = 0; dest[XAS_AUDIO_STEREO*i+1] = 0; i++; } } static void buffer_copy(xas_spatial_buffer *buffer, int16_t *dest, size_t count) { int16_t *src = (int16_t *)(buffer + 1); size_t i = buffer->index, o; if (count > buffer->size) { count = buffer->size; } for (o=0; osize) { i = 0; } dest[XAS_AUDIO_STEREO*o] = src[XAS_AUDIO_STEREO*i]; dest[XAS_AUDIO_STEREO*o+1] = src[XAS_AUDIO_STEREO*i+1]; i++; } } ssize_t scene_fill(xas_spatial_scene *scene, int16_t *output, size_t count, xas_audio_stream *source) { xas_spatial_buffer *buffer = scene->buffer; xas_spatial_object *obj = scene->first; size_t index_old = buffer->index; int16_t *dest = (int16_t *)(scene->buffer + 1); buffer_zero(buffer, count); while (obj) { int16_t *src; float distance_l = dist(scene->speaker_l, obj->coord), distance_r = dist(scene->speaker_r, obj->coord); size_t delta_l, delta_r; ssize_t readlen, i; if (distance_l > scene->radius || distance_r > scene->radius) { goto next; } if ((readlen = xas_audio_stream_read(obj->source, (void **)&src, count)) < 0) { goto error_audio_stream_read; } if (readlen > (ssize_t)buffer->size) { readlen = buffer->size; } delta_l = sample_delta(scene, distance_l); delta_r = sample_delta(scene, distance_r); for (i=0; iindex == buffer->size) { buffer->index = 0; } index_l = buffer->index + delta_l; index_r = buffer->index + delta_r; dest[XAS_AUDIO_STEREO*index_l] += value_l; dest[XAS_AUDIO_STEREO*index_r+1] += value_r; buffer->index++; } next: buffer->index = index_old; obj = obj->next; } buffer_copy(buffer, output, count); buffer->index = index_old + count; buffer->index %= buffer->size; return count; error_audio_stream_read: return -1; } xas_spatial_scene *xas_spatial_scene_new(xas_audio_format format, xas_spatial_coord speaker_l, xas_spatial_coord speaker_r) { xas_spatial_scene *scene; if ((scene = malloc(sizeof(*scene))) == NULL) { goto error_malloc_scene; } memset(scene, '\0', sizeof(*scene)); scene->format = format; scene->speaker_l = speaker_l; scene->speaker_r = speaker_r; scene->radius = XAS_SPATIAL_DEFAULT_RADIUS; scene->speed = XAS_SPATIAL_DEFAULT_SPEED; if (buffer_realloc(scene, NULL) < 0) { goto error_buffer_realloc; } return scene; error_buffer_realloc: free(scene); error_malloc_scene: return NULL; } void xas_spatial_scene_destroy(xas_spatial_scene *scene) { xas_spatial_object *object = scene->first; while (object) { xas_spatial_object *next = object->next; free(object); object = next; } free(scene->buffer); free(scene); } void xas_spatial_scene_set_observer(xas_spatial_scene *scene, xas_spatial_coord coord, xas_spatial_rotation rotation, float width) { scene->observer.coord = coord; scene->observer.rotation = rotation; scene->observer.width = width; } void xas_spatial_scene_set_speaker_coords(xas_spatial_scene *scene, xas_spatial_coord speaker_l, xas_spatial_coord speaker_r) { scene->speaker_l = speaker_l; scene->speaker_r = speaker_r; } int xas_spatial_scene_set_speed(xas_spatial_scene *scene, float speed) { scene->speed = speed; return buffer_realloc(scene, scene->buffer); } int xas_spatial_scene_set_radius(xas_spatial_scene *scene, float radius) { scene->radius = radius; return buffer_realloc(scene, scene->buffer); } xas_spatial_object *xas_spatial_scene_add_object(xas_spatial_scene *scene, xas_spatial_coord coord, xas_audio_stream *source) { xas_spatial_object *object; if (source->format.channels != XAS_AUDIO_MONO) { errno = EINVAL; goto error_invalid_source; } if ((object = malloc(sizeof(*object))) == NULL) { goto error_malloc_object; } object->coord = coord; object->source = source; object->next = NULL; if (scene->first == NULL) { scene->first = object; } if (scene->last) { scene->last->next = object; } scene->last = object; return object; error_malloc_object: error_invalid_source: return NULL; } void xas_spatial_object_get_coord(xas_spatial_object *object, xas_spatial_coord *coord) { coord->x = object->coord.x; coord->y = object->coord.y; coord->z = object->coord.z; } void xas_spatial_object_set_coord(xas_spatial_object *object, xas_spatial_coord coord) { object->coord = coord; } xas_audio_stream *xas_spatial_scene_new_stream(xas_spatial_scene *scene, size_t buffer_size) { return xas_audio_stream_new_source((xas_audio_fill)scene_fill, NULL, scene->format, buffer_size, scene); }