diff options
| author | XANTRONIX Development | 2022-02-15 20:53:24 -0500 | 
|---|---|---|
| committer | XANTRONIX Development | 2022-02-15 20:53:24 -0500 | 
| commit | e0fda7988c2cca763d92075a5d95f813a37b69f4 (patch) | |
| tree | e547109adad66235581ec60ea5836ad6e75b6f95 | |
| parent | 1fe063a6d66048e6f95d1f60a7d7f837799cefe7 (diff) | |
| download | xas-e0fda7988c2cca763d92075a5d95f813a37b69f4.tar.gz xas-e0fda7988c2cca763d92075a5d95f813a37b69f4.tar.bz2 xas-e0fda7988c2cca763d92075a5d95f813a37b69f4.zip | |
It's surprising this stuff compiles, but fuck it
| -rw-r--r-- | examples/Makefile | 2 | ||||
| -rw-r--r-- | examples/spatial.c | 166 | ||||
| -rw-r--r-- | include/xas/spatial.h | 49 | ||||
| -rw-r--r-- | src/Makefile | 4 | ||||
| -rw-r--r-- | src/spatial.c | 223 | 
5 files changed, 411 insertions, 33 deletions
| diff --git a/examples/Makefile b/examples/Makefile index 537d103..5604297 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -7,7 +7,7 @@ INCLUDE_PATH	= ../include  CFLAGS		+= -I$(INCLUDE_PATH)  LDFLAGS		+= -L../src -lxas -lm -EXAMPLES	= test open say +EXAMPLES	= test open say spatial  all: $(EXAMPLES) diff --git a/examples/spatial.c b/examples/spatial.c new file mode 100644 index 0000000..9985ffe --- /dev/null +++ b/examples/spatial.c @@ -0,0 +1,166 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <sysexits.h> +#include <fcntl.h> +#include <math.h> + +#include <xas/spatial.h> +#include <xas/synth.h> +#include <xas/audio.h> +#include <xas/riff.h> + +#define SYNTH_STATUS_CLEAR  0 +#define SYNTH_STATUS_ON    (1 << 0) + +typedef struct _synth_sine { +    int flags; +    float phase; + +    size_t frequency; +} synth_sine; + +static void usage(int argc, char **argv, const char *message, ...) { +    va_list args; + +    va_start(args, message); + +    if (message) { +        vfprintf(stderr, message, args); +        fputc('\n', stderr); +    } + +    va_end(args); + +    fprintf(stderr, "usage: %s output.wav\n", argv[0]); + +    exit(EX_USAGE); +} + +static int16_t sine_sample(xas_synth *synth, synth_sine *sine) { +    int16_t ret; +    static float tau = 2.0f * M_PI; + +    if (sine->flags & SYNTH_STATUS_ON) { +        ret = (int16_t)roundf((INT16_MAX >> 2) * sinf(sine->phase)); + +        sine->phase += tau / (synth->format.sample_rate / sine->frequency); + +        if (sine->phase > tau) { +            sine->phase -= tau; +        } +    } else { +        ret = 0; +    } + +    return ret; +} + +static void sine_cleanup(xas_synth *synth, synth_sine *sine) { +    return; +} + +int main(int argc, char **argv) { +    xas_spatial_scene *scene; + +    xas_audio_stream *synth, +                       *output, +                       *wave; + +    synth_sine sine_channels[2] = { +        { SYNTH_STATUS_ON, 0.0f, 220 }, +        { SYNTH_STATUS_ON, 0.0f, 420 }, +    }; + +    xas_audio_format format = { +        .channels    = XAS_AUDIO_STEREO, +        .sample_size = XAS_AUDIO_PCM_16_BIT, +        .sample_rate = 44100 +    }; + +    size_t buffer_size =  4096, +           duration_s  =    60, +           i; + +    xas_spatial_coord speakers[2] = { +        { -0.09, 0.0, 0.0 }, +        {  0.09, 0.0, 0.0 } +    }; + +    if (argc != 2) { +        usage(argc, argv, "No output file provided"); +    } + +    if ((wave = xas_riff_new_file(argv[1], +                                    format, +                                    O_WRONLY | O_CREAT | O_TRUNC)) == NULL) { +        goto error_riff_new_file; +    } + +    if ((synth = xas_synth_new((xas_synth_callback_sample)sine_sample, +                                 (xas_synth_callback_cleanup)sine_cleanup, +                                 format, +                                 buffer_size, +                                 &sine_channels[0])) == NULL) { +        goto error_synth_new; +    } + +    if ((scene = xas_spatial_scene_new(format, +                                         speakers[0], +                                         speakers[1])) == NULL) { +        goto error_spatial_scene_new; +    } + +    if ((output = xas_spatial_scene_new_stream(scene, +                                                 buffer_size)) == NULL) { +        goto error_spatial_scene_new_stream; +    } + +    if (xas_spatial_scene_add_object(scene, +                                       (xas_spatial_coord){ 0.0, 0.0, 20.0 }, +                                       synth) == NULL) { +        goto error_spatial_scene_add_object; +    } + +    for (i=0; i<duration_s; i++) { +        void *buf; +        ssize_t readlen; + +        if ((readlen = xas_audio_stream_read(output, +                                               &buf, +                                               buffer_size)) < 0) { +            goto error_audio_stream_read; +        } + +        if (xas_audio_stream_write(wave, buf, readlen) < 0) { +            goto error_audio_stream_write; +        } +    } + +    xas_audio_stream_flush(wave); + +    xas_audio_stream_destroy(output); +    xas_spatial_scene_destroy(scene); +    xas_audio_stream_destroy(synth); +    xas_audio_stream_destroy(wave); + +    return EX_OK; + +error_audio_stream_read: +error_audio_stream_write: +error_spatial_scene_add_object: +    xas_audio_stream_destroy(output); + +error_spatial_scene_new_stream: +    xas_spatial_scene_destroy(scene); + +error_spatial_scene_new: +    xas_audio_stream_destroy(synth); + +error_synth_new: +    xas_audio_stream_destroy(wave); + +error_riff_new_file: +    return EX_OSERR; +} diff --git a/include/xas/spatial.h b/include/xas/spatial.h index 8b1ca05..a715b65 100644 --- a/include/xas/spatial.h +++ b/include/xas/spatial.h @@ -4,14 +4,28 @@  #include <xas/audio.h>  #define XAS_SPATIAL_DEFAULT_OBSERVER_WIDTH   0.18f -#define XAS_SPATIAL_DEFAULT_SPEED          343.0f /* m/s */ +#define XAS_SPATIAL_DEFAULT_RADIUS         4000.0f /* metres */ +#define XAS_SPATIAL_DEFAULT_SPEED           343.0f /* m/s */  typedef struct _xas_spatial_coord {      float x, y, z;  } xas_spatial_coord;  typedef struct _xas_spatial_rotation { -    float pitch, roll, yaw; +    union { +        float x; +        float pitch; +    }; + +    union { +        float y; +        float roll; +    }; + +    union { +        float z; +        float yaw; +    };  } xas_spatial_rotation;  typedef struct _xas_spatial_observer { @@ -23,11 +37,16 @@ typedef struct _xas_spatial_observer {  typedef struct _xas_spatial_object xas_spatial_object;  struct _xas_spatial_object { -    float x, y, z; +    xas_spatial_coord coord;      xas_audio_stream *source;      xas_spatial_object *next;  }; +typedef struct _xas_spatial_buffer { +    size_t index, +           size; +} xas_spatial_buffer; +  typedef struct _xas_spatial_scene {      xas_audio_format format; @@ -36,13 +55,13 @@ typedef struct _xas_spatial_scene {      xas_spatial_coord speaker_l,                          speaker_r; -    float speed; +    float speed, +          radius;      xas_spatial_object *first,                           *last; -    void *buf; -    size_t buflen; +    xas_spatial_buffer *buffer;  } xas_spatial_scene;  xas_spatial_scene *xas_spatial_scene_new(xas_audio_format format, @@ -51,18 +70,21 @@ xas_spatial_scene *xas_spatial_scene_new(xas_audio_format format,  void xas_spatial_scene_destroy(xas_spatial_scene *scene); -int xas_spatial_scene_set_observer(xas_spatial_scene *scene, -                                     xas_spatial_coord coord, -                                     xas_spatial_rotation rotation, -                                     float width); +void xas_spatial_scene_set_observer(xas_spatial_scene *scene, +                                      xas_spatial_coord coord, +                                      xas_spatial_rotation rotation, +                                      float width);  void xas_spatial_scene_set_speaker_coords(xas_spatial_scene *scene,                                              xas_spatial_coord speaker_l,                                              xas_spatial_coord speaker_r); -void xas_spatial_scene_set_speed(xas_spatial_scene *scene, float speed); +int xas_spatial_scene_set_speed(xas_spatial_scene *scene, float speed); + +int xas_spatial_scene_set_radius(xas_spatial_scene *scene, float radius); -xas_spatial_object *xas_spatial_scene_add_object(xas_spatial_coord coord, +xas_spatial_object *xas_spatial_scene_add_object(xas_spatial_scene *scene, +                                                     xas_spatial_coord coord,                                                       xas_audio_stream *source);  void xas_spatial_object_get_coord(xas_spatial_object *object, @@ -71,6 +93,7 @@ void xas_spatial_object_get_coord(xas_spatial_object *object,  void xas_spatial_object_set_coord(xas_spatial_object *object,                                      xas_spatial_coord coord); -xas_audio_stream *xas_spatial_scene_new_stream(xas_spatial_scene *scene); +xas_audio_stream *xas_spatial_scene_new_stream(xas_spatial_scene *scene, +                                                   size_t buffer_size);  #endif /* _XAS_SPATIAL_H */ diff --git a/src/Makefile b/src/Makefile index b9e9150..7c26ac9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -7,9 +7,9 @@ CC		 = $(CROSS)cc  CFLAGS		+= -I$(INCLUDE_PATH)  LDFLAGS		+= -HEADERS		= audio.h riff.h mixer.h synth.h vox.h bank.h +HEADERS		= audio.h riff.h mixer.h synth.h vox.h bank.h spatial.h -OBJS		= audio.o riff.o mixer.o synth.o vox.o bank.o +OBJS		= audio.o riff.o mixer.o synth.o vox.o bank.o spatial.o  VERSION_MAJOR	= 0  VERSION_MINOR	= 0.1 diff --git a/src/spatial.c b/src/spatial.c index 809fce1..18173aa 100644 --- a/src/spatial.c +++ b/src/spatial.c @@ -10,24 +10,146 @@ static inline float dist(xas_spatial_coord a, xas_spatial_coord b) {                + powf(b.z - a.z, 2.0f), 0.5f);  } -static int buf_realloc(xas_spatial_scene *scene, void *buf) { -    float width = scene->observer.width, -          speed = scene->speed; +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       = floorf(width / (speed / sample_rate)); +           count       = sample_rate * ceilf(seconds), +           total       = sizeof(xas_spatial_buffer) + stride * count; -    if ((buf = realloc(buf, stride * count)) == NULL) { -        goto error_realloc; +    if ((buffer = realloc(buffer, total)) == NULL) { +        goto error_realloc_buffer;      } -    scene->buf    = buf; -    scene->buflen = count; +    memset(buffer + 1, '\0', stride * count); + +    scene->buffer        = buffer; +    scene->buffer->index = 0; +    scene->buffer->size  = count;      return 0; -error_realloc: +error_realloc_buffer: +    return -1; +} + +static inline size_t sample_index(xas_spatial_scene *scene, float distance) { +    size_t index = floorf(distance / scene->speed / scene->format.sample_rate); + +    return (scene->buffer->index + index) % scene->buffer->size; +} + +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[i*XAS_AUDIO_STEREO]   = 0; +        dest[i*XAS_AUDIO_STEREO+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; + +    if (count > buffer->size) { +        count = buffer->size; +    } + +    while (count--) { +        if (i == buffer->size) { +            i = 0; +        } + +        dest[i*XAS_AUDIO_STEREO]   = src[i*XAS_AUDIO_STEREO]; +        dest[i*XAS_AUDIO_STEREO+1] = src[i*XAS_AUDIO_STEREO+1]; +    } +} + +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 index_l, +               index_r; + +        ssize_t readlen, +                i; + +        if (distance_l > scene->radius || distance_r > scene->radius) { +            continue; +        } + +        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; +        } + +        index_l = sample_index(scene, distance_l); +        index_r = sample_index(scene, distance_r); + +        for (i=0; i<readlen; i++) { +            int16_t value_l = sample_scale(src[i], distance_l), +                    value_r = sample_scale(src[i], distance_r); + +            if (buffer->index == buffer->size) { +                buffer->index = 0; +            } + +            dest[XAS_AUDIO_STEREO*index_l+i]   += value_l; +            dest[XAS_AUDIO_STEREO*index_r+i+1] += value_r; + +            buffer->index++; +        } + +        buffer->index = index_old; +        obj           = obj->next; +    } + +    buffer_copy(buffer, output, count); + +    return count; + +error_audio_stream_read:      return -1;  } @@ -47,13 +169,13 @@ xas_spatial_scene *xas_spatial_scene_new(xas_audio_format format,      scene->speaker_r = speaker_r;      scene->speed     = XAS_SPATIAL_DEFAULT_SPEED; -    if (buf_realloc(scene, NULL) < 0) { -        goto error_buf_realloc; +    if (buffer_realloc(scene, NULL) < 0) { +        goto error_buffer_realloc;      }      return scene; -error_buf_realloc: +error_buffer_realloc:      free(scene);  error_malloc_scene: @@ -61,19 +183,28 @@ error_malloc_scene:  }  void xas_spatial_scene_destroy(xas_spatial_scene *scene) { -    free(scene->buf); +    xas_spatial_object *object = scene->first; + +    while (object) { +        xas_spatial_object *next = object->next; + +        free(object); + +        object = next; +    } + +    free(scene->buffer);      free(scene);  } -int xas_spatial_scene_set_observer(xas_spatial_scene *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; - -    return buf_realloc(scene, scene->buf);  }  void xas_spatial_scene_set_speaker_coords(xas_spatial_scene *scene, @@ -83,6 +214,64 @@ void xas_spatial_scene_set_speaker_coords(xas_spatial_scene *scene,      scene->speaker_r = speaker_r;  } -void xas_spatial_scene_set_speed(xas_spatial_scene *scene, float speed) { +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 ((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: +    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);  } | 
 
    