#include #include #include #include #include #include #include xas_script *xas_script_new(xas_spatial_scene *scene, size_t buffer_size) { xas_script *script; if ((script = malloc(sizeof(*script))) == NULL) { goto error_malloc_script; } script->scene = scene; script->first = NULL; script->buffer_size = buffer_size; return script; error_malloc_script: return NULL; } void xas_script_destroy(xas_script *script) { xas_script_event *event = script->first; while (event) { xas_script_event *next = event->next; free(event); event = next; } free(script); } static void add_event(xas_script *script, xas_script_event *ev) { xas_script_event *current = script->first; if (script->first == NULL) { script->first = ev; return; } while (current) { xas_script_event *next = current->next; if (next == NULL) { current->next = ev; return; } if (!timercmp(¤t->timestamp, &ev->timestamp, <) && timercmp(¤t->timestamp, &next->timestamp, <)) { current->next = ev; ev->next = next; return; } current = next; } } int xas_script_add_event_off(xas_script *script, xas_spatial_object *object, struct timeval timestamp) { xas_script_event *ev; if ((ev = malloc(sizeof(*ev))) == NULL) { goto error_malloc_ev; } ev->type = XAS_SCRIPT_EVENT_OFF; ev->objtype = XAS_SCRIPT_OBJECT_ANY; ev->object = object; ev->timestamp = timestamp; ev->next = NULL; add_event(script, ev); return 0; error_malloc_ev: return -1; } int xas_script_add_event_on(xas_script *script, xas_spatial_object *object, struct timeval timestamp) { xas_script_event *ev; if ((ev = malloc(sizeof(*ev))) == NULL) { goto error_malloc_ev; } ev->type = XAS_SCRIPT_EVENT_ON; ev->objtype = XAS_SCRIPT_OBJECT_ANY; ev->object = object; ev->timestamp = timestamp; ev->next = NULL; add_event(script, ev); return 0; error_malloc_ev: return -1; } int xas_script_add_set_position(xas_script *script, xas_spatial_object *object, struct timeval timestamp, xas_spatial_coord point) { xas_script_event *ev; if ((ev = malloc(sizeof(*ev))) == NULL) { goto error_malloc_ev; } ev->type = XAS_SCRIPT_EVENT_SET_POSITION; ev->objtype = XAS_SCRIPT_OBJECT_ANY; ev->object = object; ev->timestamp = timestamp; ev->point = point; ev->next = NULL; add_event(script, ev); return 0; error_malloc_ev: return -1; } int xas_script_add_set_gain(xas_script *script, xas_spatial_object *object, struct timeval timestamp, float gain) { xas_script_event *ev; if ((ev = malloc(sizeof(*ev))) == NULL) { goto error_malloc_ev; } ev->type = XAS_SCRIPT_EVENT_ON; ev->objtype = XAS_SCRIPT_OBJECT_ANY; ev->object = object; ev->timestamp = timestamp; ev->gain = gain; ev->next = NULL; add_event(script, ev); return 0; error_malloc_ev: return -1; } int xas_script_add_set_bank(xas_script *script, xas_spatial_object *object, struct timeval timestamp, size_t index) { xas_script_event *ev; if ((ev = malloc(sizeof(*ev))) == NULL) { goto error_malloc_ev; } ev->type = XAS_SCRIPT_EVENT_SET_BANK_INDEX; ev->objtype = XAS_SCRIPT_OBJECT_BANK_PLAYER; ev->object = object; ev->timestamp = timestamp; ev->next = NULL; ev->index = index; add_event(script, ev); return 0; error_malloc_ev: return -1; } int xas_script_add_set_frequency(xas_script *script, xas_spatial_object *object, struct timeval timestamp, size_t frequency) { xas_script_event *ev; if ((ev = malloc(sizeof(*ev))) == NULL) { goto error_malloc_ev; } ev->type = XAS_SCRIPT_EVENT_SET_FREQUENCY; ev->objtype = XAS_SCRIPT_OBJECT_SYNTH; ev->object = object; ev->timestamp = timestamp; ev->next = NULL; ev->frequency = frequency; add_event(script, ev); return 0; error_malloc_ev: return -1; } int xas_script_add_phrase(xas_script *script, xas_spatial_object *object, struct timeval timestamp, const char *phrase) { xas_script_event *ev; if ((ev = malloc(sizeof(*ev))) == NULL) { goto error_malloc_ev; } ev->type = XAS_SCRIPT_EVENT_SPEECH; ev->objtype = XAS_SCRIPT_OBJECT_VOX; ev->object = object; ev->timestamp = timestamp; ev->next = NULL; ev->phrase = phrase; add_event(script, ev); return 0; error_malloc_ev: return -1; } static int event_trigger(xas_spatial_scene *scene, xas_script_event *ev) { switch (ev->type) { case XAS_SCRIPT_EVENT_OFF: return xas_spatial_object_stop(ev->object); case XAS_SCRIPT_EVENT_ON: return xas_spatial_object_start(ev->object); case XAS_SCRIPT_EVENT_SET_POSITION: xas_spatial_scene_position_object(scene, ev->object, ev->point); break; case XAS_SCRIPT_EVENT_SET_GAIN: xas_object_set_gain(ev->object->ctx, ev->gain); break; case XAS_SCRIPT_EVENT_SET_FREQUENCY: if (ev->objtype != XAS_SCRIPT_OBJECT_SYNTH) { goto error_invalid_event; } xas_synth_set_frequency(ev->object->ctx, ev->frequency); break; case XAS_SCRIPT_EVENT_SET_BANK_INDEX: if (ev->objtype != XAS_SCRIPT_OBJECT_BANK_PLAYER) { goto error_invalid_event; } xas_bank_player_set_entry(ev->object->ctx, ev->gain); break; case XAS_SCRIPT_EVENT_SPEECH: if (ev->objtype != XAS_SCRIPT_OBJECT_VOX) { goto error_invalid_event; } xas_vox_say(ev->object->ctx, ev->phrase); } return 0; error_invalid_event: return -1; } static inline void timerupdate(struct timeval *tv, suseconds_t interval, size_t frame) { suseconds_t usec = frame * interval; tv->tv_sec = usec / 1000000; tv->tv_usec = usec % 1000000; } int xas_script_play(xas_script *script, xas_audio_stream *sink) { xas_script_event *ev = script->first; xas_audio_stream *source; size_t frame = 0, sample_rate = script->scene->format.sample_rate, buffer_size = script->buffer_size; suseconds_t interval = 1000000 / (sample_rate / buffer_size); int16_t *samples; if ((source = xas_spatial_scene_stream_new(script->scene, script->buffer_size)) == NULL) { goto error_spatial_scene_stream_new; } while (ev) { xas_script_event *next = ev->next; struct timeval tv; ssize_t readlen; timerupdate(&tv, interval, frame); if (timercmp(&ev->timestamp, &tv, >)) { if (event_trigger(script->scene, ev) < 0) { goto error_event_trigger; } ev = next; } if ((readlen = xas_audio_stream_read(source, (void **)&samples, buffer_size)) < 0) { goto error_audio_stream_io; } else if (readlen == 0) { break; } if (xas_audio_stream_write(sink, samples, readlen) < 0) { goto error_audio_stream_io; } frame++; } return frame * buffer_size; error_audio_stream_io: error_event_trigger: xas_audio_stream_destroy(source); error_spatial_scene_stream_new: return -1; }