#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 *event = seq->first; while (event) { xas_seq_event *next = event->next; free(event); event = next; } free(seq); } static void add_event(xas_seq *seq, xas_seq_event *ev) { xas_seq_event *current = seq->first, *prev = NULL; if (seq->first == NULL) { seq->first = ev; seq->last = ev; return; } while (current) { xas_seq_event *next = current->next; if (timercmp(&ev->timestamp, &seq->last->timestamp, >=)) { seq->last->next = ev; seq->last = ev; return; } else if (timercmp(&ev->timestamp, ¤t->timestamp, >)) { ev->next = current->next; current->next = ev; return; } else if (prev && timercmp(&ev->timestamp, &prev->timestamp, >) && timercmp(&ev->timestamp, ¤t->timestamp, <=)) { prev->next = ev; ev->next = current; return; } else if (prev && timercmp(&ev->timestamp, &prev->timestamp, <=) && timercmp(&ev->timestamp, &seq->first->timestamp, >)) { ev->next = seq->first->next; seq->first->next = ev; return; } else if (timercmp(&ev->timestamp, &seq->first->timestamp, <=)) { ev->next = seq->first; seq->first = ev; return; } prev = current; current = 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; ev->next = NULL; add_event(seq, ev); return 0; 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; ev->next = NULL; add_event(seq, ev); return 0; 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; ev->next = NULL; add_event(seq, ev); return 0; 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; ev->next = NULL; add_event(seq, ev); return 0; 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; ev->next = NULL; add_event(seq, ev); return 0; 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; ev->next = NULL; add_event(seq, ev); return 0; 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->next = NULL; ev->index = index; add_event(seq, ev); return 0; 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->next = NULL; ev->flags = flags; add_event(seq, ev); return 0; 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->next = NULL; ev->synth_type = type; add_event(seq, ev); return 0; 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->next = NULL; ev->frequency = frequency; add_event(seq, ev); return 0; 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->next = NULL; ev->phrase = phrase; add_event(seq, ev); return 0; 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 *ev = 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; if ((source = xas_spatial_scene_stream_new(seq->scene, seq->buffer_size)) == NULL) { goto error_spatial_scene_stream_new; } while (ev) { struct timeval tv; ssize_t readlen; timerupdate(&tv, interval, frame); while (ev && !timercmp(&tv, &ev->timestamp, <)) { 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++; } 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; }