#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 *ev = seq->first; while (ev) { xas_seq_event *next = ev->next; free(ev); ev = next; } free(seq); } static int event_add(xas_seq *seq, xas_seq_event *ev) { if (seq->first == NULL) { seq->first = ev; seq->last = ev; } else { seq->last->next = ev; seq->last = ev; } ev->next = NULL; return 0; } static xas_seq_event *event_tail(xas_seq_event *head) { if (head == NULL) { return NULL; } while (head->next) { head = head->next; } return head; } static xas_seq_event *event_list_partition(xas_seq_event *head, xas_seq_event *end, xas_seq_event **head_new, xas_seq_event **end_new) { xas_seq_event *pivot = end, *prev = NULL, *cur = head, *tail = pivot; while (cur != pivot) { if (timercmp(&cur->timestamp, &pivot->timestamp, <)) { if (*head_new == NULL) { *head_new = cur; } prev = cur; cur = cur->next; } else { xas_seq_event *tmp; if (prev) { prev->next = cur->next; } tmp = cur->next; cur->next = NULL; tail->next = cur; tail = cur; cur = tmp; } } if (*head_new == NULL) { *head_new = pivot; } *end_new = tail; return pivot; } static xas_seq_event *event_list_sort_recur(xas_seq_event *head, xas_seq_event *end) { xas_seq_event *head_new = NULL, *end_new = NULL, *pivot; if (head == NULL || head == end) { return head; } pivot = event_list_partition(head, end, &head_new, &end_new); if (head_new != pivot) { xas_seq_event *tmp = head_new; while (tmp->next != pivot) { tmp = tmp->next; } tmp->next = NULL; head_new = event_list_sort_recur(head_new, tmp); tmp = event_tail(head_new); tmp->next = pivot; } pivot->next = event_list_sort_recur(pivot->next, end_new); return head_new; } static void event_sort(xas_seq *seq) { seq->first = event_list_sort_recur(seq->first, seq->last); seq->last = event_tail(seq->first); } 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; } int xas_seq_add_stop(xas_seq *seq, struct timeval timestamp) { xas_seq_event *ev; if ((ev = malloc(sizeof(*ev))) == NULL) { goto error_malloc_ev; } ev->type = XAS_SEQ_EVENT_STOP; ev->objtype = XAS_SEQ_OBJECT_ANY; ev->object = NULL; ev->timestamp = timestamp; ev->flags = 0; 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; case XAS_SEQ_EVENT_STOP: 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 *ev; 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; if ((source = xas_spatial_scene_stream_new(seq->scene, seq->buffer_size)) == NULL) { goto error_spatial_scene_stream_new; } event_sort(seq); ev = seq->first; while (ev) { struct timeval tv; ssize_t readlen; timerupdate(&tv, interval, frame); while (ev && !timercmp(&tv, &ev->timestamp, <)) { if (ev->type == XAS_SEQ_EVENT_STOP) { goto done; } if (event_trigger(seq->scene, ev) < 0) { goto error_event_trigger; } ev = 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++; } done: 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; }