#include #include #include #include #include #include #include xas_seq *xas_seq_new(xas_spatial_scene *scene, size_t buffer_size) { xas_seq *seq; if ((seq = malloc(sizeof(*seq))) == NULL) { goto error_malloc_seq; } seq->scene = scene; seq->first = NULL; seq->last = NULL; seq->buffer_size = buffer_size; return seq; error_malloc_seq: return NULL; } void xas_seq_destroy(xas_seq *seq) { xas_seq_event_list_item *item = seq->first; while (item) { xas_seq_event_list_item *next = item->next; free(item->ev); free(item); item = next; } free(seq); } static int event_add(xas_seq *seq, xas_seq_event *ev) { xas_seq_event_list_item *item; if ((item = malloc(sizeof(*item))) == NULL) { goto error_malloc_item; } item->ev = ev; item->next = NULL; if (seq->first == NULL) { seq->first = item; seq->last = item; } else { seq->last->next = item; seq->last = item; } return 0; error_malloc_item: return -1; } static void sort_events(xas_seq *seq) { xas_seq_event_list_item *item = seq->first, *temp = NULL; while (item) { temp = item; while (temp->next) { if (timercmp(&temp->ev->timestamp, &temp->next->ev->timestamp, >)) { xas_seq_event *ev = temp->ev; temp->ev = temp->next->ev; temp->next->ev = ev; } temp = temp->next; } item = item->next; } } int xas_seq_add_event_off(xas_seq *seq, xas_spatial_object *object, struct timeval timestamp) { xas_seq_event *ev; if ((ev = malloc(sizeof(*ev))) == NULL) { goto error_malloc_ev; } ev->type = XAS_SEQ_EVENT_OFF; ev->objtype = XAS_SEQ_OBJECT_ANY; ev->object = object; ev->timestamp = timestamp; if (event_add(seq, ev) < 0) { goto error_event_add; } return 0; error_event_add: free(ev); error_malloc_ev: return -1; } int xas_seq_add_event_on(xas_seq *seq, xas_spatial_object *object, struct timeval timestamp) { xas_seq_event *ev; if ((ev = malloc(sizeof(*ev))) == NULL) { goto error_malloc_ev; } ev->type = XAS_SEQ_EVENT_ON; ev->objtype = XAS_SEQ_OBJECT_ANY; ev->object = object; ev->timestamp = timestamp; if (event_add(seq, ev) < 0) { goto error_event_add; } return 0; error_event_add: free(ev); error_malloc_ev: return -1; } int xas_seq_add_set_position(xas_seq *seq, xas_spatial_object *object, struct timeval timestamp, xas_spatial_coord point) { xas_seq_event *ev; if ((ev = malloc(sizeof(*ev))) == NULL) { goto error_malloc_ev; } ev->type = XAS_SEQ_EVENT_SET_POSITION; ev->objtype = XAS_SEQ_OBJECT_ANY; ev->object = object; ev->timestamp = timestamp; ev->point = point; if (event_add(seq, ev) < 0) { goto error_event_add; } return 0; error_event_add: free(ev); error_malloc_ev: return -1; } int xas_seq_add_set_heading(xas_seq *seq, xas_spatial_object *object, struct timeval timestamp, xas_spatial_coord heading) { xas_seq_event *ev; if ((ev = malloc(sizeof(*ev))) == NULL) { goto error_malloc_ev; } ev->type = XAS_SEQ_EVENT_SET_HEADING; ev->objtype = XAS_SEQ_OBJECT_ANY; ev->object = object; ev->timestamp = timestamp; ev->heading = heading; if (event_add(seq, ev) < 0) { goto error_event_add; } return 0; error_event_add: free(ev); error_malloc_ev: return -1; } int xas_seq_add_set_speed(xas_seq *seq, xas_spatial_object *object, struct timeval timestamp, float speed) { xas_seq_event *ev; if ((ev = malloc(sizeof(*ev))) == NULL) { goto error_malloc_ev; } ev->type = XAS_SEQ_EVENT_SET_SPEED; ev->objtype = XAS_SEQ_OBJECT_ANY; ev->object = object; ev->timestamp = timestamp; ev->speed = speed; if (event_add(seq, ev) < 0) { goto error_event_add; } return 0; error_event_add: free(ev); error_malloc_ev: return -1; } int xas_seq_add_set_gain(xas_seq *seq, xas_spatial_object *object, struct timeval timestamp, float gain) { xas_seq_event *ev; if ((ev = malloc(sizeof(*ev))) == NULL) { goto error_malloc_ev; } ev->type = XAS_SEQ_EVENT_ON; ev->objtype = XAS_SEQ_OBJECT_ANY; ev->object = object; ev->timestamp = timestamp; ev->gain = gain; if (event_add(seq, ev) < 0) { goto error_event_add; } return 0; error_event_add: free(ev); error_malloc_ev: return -1; } int xas_seq_add_set_bank(xas_seq *seq, xas_spatial_object *object, struct timeval timestamp, size_t index) { xas_seq_event *ev; if ((ev = malloc(sizeof(*ev))) == NULL) { goto error_malloc_ev; } ev->type = XAS_SEQ_EVENT_SET_BANK_INDEX; ev->objtype = XAS_SEQ_OBJECT_BANK_PLAYER; ev->object = object; ev->timestamp = timestamp; ev->index = index; if (event_add(seq, ev) < 0) { goto error_event_add; } return 0; error_event_add: free(ev); error_malloc_ev: return -1; } int xas_seq_add_set_player_flags(xas_seq *seq, xas_spatial_object *object, struct timeval timestamp, int flags) { xas_seq_event *ev; if ((ev = malloc(sizeof(*ev))) == NULL) { goto error_malloc_ev; } ev->type = XAS_SEQ_EVENT_SET_PLAYER_FLAGS; ev->objtype = XAS_SEQ_OBJECT_BANK_PLAYER; ev->object = object; ev->timestamp = timestamp; ev->flags = flags; if (event_add(seq, ev) < 0) { goto error_event_add; } return 0; error_event_add: free(ev); error_malloc_ev: return -1; } int xas_seq_add_set_synth_type(xas_seq *seq, xas_spatial_object *object, struct timeval timestamp, enum xas_synth_type type) { xas_seq_event *ev; if ((ev = malloc(sizeof(*ev))) == NULL) { goto error_malloc_ev; } ev->type = XAS_SEQ_EVENT_SET_SYNTH_TYPE; ev->objtype = XAS_SEQ_OBJECT_SYNTH; ev->object = object; ev->timestamp = timestamp; ev->synth_type = type; if (event_add(seq, ev) < 0) { goto error_event_add; } return 0; error_event_add: free(ev); error_malloc_ev: return -1; } int xas_seq_add_set_frequency(xas_seq *seq, xas_spatial_object *object, struct timeval timestamp, size_t frequency) { xas_seq_event *ev; if ((ev = malloc(sizeof(*ev))) == NULL) { goto error_malloc_ev; } ev->type = XAS_SEQ_EVENT_SET_FREQUENCY; ev->objtype = XAS_SEQ_OBJECT_SYNTH; ev->object = object; ev->timestamp = timestamp; ev->frequency = frequency; if (event_add(seq, ev) < 0) { goto error_event_add; } return 0; error_event_add: free(ev); error_malloc_ev: return -1; } int xas_seq_add_phrase(xas_seq *seq, xas_spatial_object *object, struct timeval timestamp, const char *phrase) { xas_seq_event *ev; if ((ev = malloc(sizeof(*ev))) == NULL) { goto error_malloc_ev; } ev->type = XAS_SEQ_EVENT_SPEECH; ev->objtype = XAS_SEQ_OBJECT_VOX; ev->object = object; ev->timestamp = timestamp; ev->phrase = phrase; if (event_add(seq, ev) < 0) { goto error_event_add; } return 0; error_event_add: free(ev); error_malloc_ev: return -1; } static int event_trigger(xas_spatial_scene *scene, xas_seq_event *ev) { switch (ev->type) { case XAS_SEQ_EVENT_OFF: return xas_spatial_object_stop(ev->object); case XAS_SEQ_EVENT_ON: return xas_spatial_object_start(ev->object); case XAS_SEQ_EVENT_SET_POSITION: xas_spatial_scene_position_object(scene, ev->object, ev->point); break; case XAS_SEQ_EVENT_SET_HEADING: ev->object->heading = ev->heading; break; case XAS_SEQ_EVENT_SET_SPEED: ev->object->speed = ev->speed; break; case XAS_SEQ_EVENT_SET_GAIN: xas_object_set_gain(ev->object->ctx, ev->gain); break; case XAS_SEQ_EVENT_SET_FREQUENCY: if (ev->objtype != XAS_SEQ_OBJECT_SYNTH) { goto error_invalid_event; } xas_synth_set_frequency(ev->object->ctx, ev->frequency); break; case XAS_SEQ_EVENT_SET_SYNTH_TYPE: if (ev->objtype != XAS_SEQ_OBJECT_SYNTH) { goto error_invalid_event; } xas_synth_set_type(ev->object->ctx, ev->synth_type); break; case XAS_SEQ_EVENT_SET_BANK_INDEX: if (ev->objtype != XAS_SEQ_OBJECT_BANK_PLAYER) { goto error_invalid_event; } xas_bank_player_set_entry(ev->object->ctx, ev->index); break; case XAS_SEQ_EVENT_SET_PLAYER_FLAGS: if (ev->objtype != XAS_SEQ_OBJECT_BANK_PLAYER) { goto error_invalid_event; } xas_bank_player_set_flags(ev->object->ctx, ev->flags); break; case XAS_SEQ_EVENT_SPEECH: if (ev->objtype != XAS_SEQ_OBJECT_VOX) { goto error_invalid_event; } xas_vox_say(ev->object->ctx, ev->phrase); xas_vox_generate(ev->object->ctx); break; } 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_seq_play(xas_seq *seq, xas_audio_stream *sink) { xas_seq_event_list_item *item = seq->first; xas_audio_stream *source; size_t frame = 0, sample_rate = seq->scene->format.sample_rate, buffer_size = seq->buffer_size; suseconds_t interval = 1000000 / (sample_rate / buffer_size); int16_t *samples; sort_events(seq); if ((source = xas_spatial_scene_stream_new(seq->scene, seq->buffer_size)) == NULL) { goto error_spatial_scene_stream_new; } while (item) { struct timeval tv; ssize_t readlen; timerupdate(&tv, interval, frame); while (item && !timercmp(&tv, &item->ev->timestamp, <)) { if (event_trigger(seq->scene, item->ev) < 0) { goto error_event_trigger; } item = item->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++; } xas_audio_stream_destroy(source); return frame * buffer_size; error_audio_stream_io: error_event_trigger: xas_audio_stream_destroy(source); error_spatial_scene_stream_new: return -1; }