#include #include #include #include #include #include #include static int drone_set_mood(xas_drone *drone, size_t index, int mood) { return xas_drone_mood_set(drone, mood); } static int drone_start(xas_drone *drone, size_t index) { switch (index) { case XAS_DRONE_INDEX_BANK: return xas_bank_player_start(drone->player); case XAS_DRONE_INDEX_BEEP: xas_synth_start(drone->beep_a); xas_synth_start(drone->beep_b); break; default: break; } return 0; } static int drone_stop(xas_drone *drone, size_t index) { switch (index) { case XAS_DRONE_INDEX_BANK: return xas_bank_player_stop(drone->player); case XAS_DRONE_INDEX_BEEP: xas_synth_stop(drone->beep_a); xas_synth_stop(drone->beep_b); break; } return 0; } static int drone_set_entry(xas_drone *drone, size_t index, size_t entry) { switch (index) { case XAS_DRONE_INDEX_BANK: return xas_bank_player_set_entry(drone->player, entry); default: break; } return 0; } static int drone_set_flags(xas_drone *drone, size_t index, int flags) { return xas_bank_player_set_flags(drone->player, flags); } static xas_object_call_table call_table = { .start = (xas_object_start_callback)drone_start, .stop = (xas_object_stop_callback)drone_stop, .set_type = (xas_object_set_type_callback)drone_set_mood, .set_entry = (xas_object_set_entry_callback)drone_set_entry, .set_flags = (xas_object_set_flags_callback)drone_set_flags }; xas_drone *xas_drone_new(xas_spatial_scene *scene, xas_spatial_coord position, size_t bank_entry_size, size_t bank_entry_count) { xas_drone *drone; xas_object *objects[3]; int i; if ((drone = malloc(sizeof(*drone))) == NULL) { goto error_malloc_drone; } if ((drone->bank = xas_bank_new(scene->format, bank_entry_size, bank_entry_count)) == NULL) { goto error_bank_new; } if ((drone->sfx = xas_bank_new(scene->format, XAS_DRONE_SFX_BANK_SIZE, XAS_DRONE_SFX_BANK_ENTRIES)) == NULL) { goto error_bank_new_sfx; } if ((drone->vox = xas_vox_new(scene->format, XAS_DRONE_VOX_BUFFER_SIZE, XAS_DRONE_VOX_TEXT2WAVE_PATH)) == NULL) { goto error_vox_new; } if ((drone->player = xas_bank_player_new(drone->bank)) == NULL) { goto error_bank_player_new; } if ((drone->beep_a = xas_synth_new(scene->format, XAS_DRONE_BEEP_BUFFER_SIZE, XAS_SYNTH_SINE)) == NULL) { goto error_synth_new_a; } if ((drone->beep_b = xas_synth_new(scene->format, XAS_DRONE_BEEP_BUFFER_SIZE, XAS_SYNTH_SINE)) == NULL) { goto error_synth_new_b; } if ((drone->mixer = xas_mixer_new(xas_audio_format_mono(scene->format), XAS_DRONE_MIXER_BUFFER_SIZE)) == NULL) { goto error_mixer_new; } objects[0] = &drone->player->obj; objects[1] = &drone->beep_a->obj; objects[2] = &drone->beep_b->obj; for (i=0; i<3; i++) { if ((xas_mixer_object_add(drone->mixer, objects[i], XAS_DRONE_MIXER_DEFAULT_GAIN, XAS_DRONE_MIXER_DEFAULT_PAN)) == NULL) { goto error_mixer_object_add; } } if ((drone->head = xas_spatial_scene_add_mixer(scene, position, drone->mixer)) == NULL) { goto error_spatial_scene_add_mixer; } drone->obj.table = &call_table; drone->mood = XAS_DRONE_MOOD_NEUTRAL; drone->scene = scene; return drone; error_spatial_scene_add_mixer: error_mixer_object_add: xas_mixer_destroy(drone->mixer); error_mixer_new: xas_synth_destroy(drone->beep_b); error_synth_new_b: xas_synth_destroy(drone->beep_a); error_synth_new_a: xas_vox_destroy(drone->vox); error_vox_new: xas_bank_player_destroy(drone->player); error_bank_player_new: xas_bank_destroy(drone->sfx); error_bank_new_sfx: xas_bank_destroy(drone->bank); error_bank_new: free(drone); error_malloc_drone: return NULL; } void xas_drone_destroy(xas_drone *drone) { xas_mixer_destroy(drone->mixer); xas_synth_destroy(drone->beep_b); xas_synth_destroy(drone->beep_a); xas_bank_player_destroy(drone->player); xas_vox_destroy(drone->vox); xas_bank_destroy(drone->sfx); xas_bank_destroy(drone->bank); free(drone); } enum xas_drone_mood xas_drone_mood_get(xas_drone *drone) { return drone->mood; } int xas_drone_mood_set(xas_drone *drone, enum xas_drone_mood mood) { static struct { size_t a, b; enum xas_synth_type b_type; } freqs[6] = { { 400, 0, XAS_SYNTH_SINE }, { 600, 0, XAS_SYNTH_SINE }, { 800, 0, XAS_SYNTH_SINE }, { 300, 0, XAS_SYNTH_SINE }, { 300, 200, XAS_SYNTH_SQUARE }, { 400, 600, XAS_SYNTH_SAWTOOTH } }; switch (mood) { case XAS_DRONE_MOOD_NEUTRAL: case XAS_DRONE_MOOD_HAPPY: case XAS_DRONE_MOOD_FLIRTY: case XAS_DRONE_MOOD_SAD: case XAS_DRONE_MOOD_ANGRY: case XAS_DRONE_MOOD_DISTRESSED: { xas_synth_set_frequency(drone->beep_a, freqs[mood].a); xas_synth_set_frequency(drone->beep_b, freqs[mood].b); xas_synth_set_type( drone->beep_b, freqs[mood].b_type); drone->mood = mood; break; } default: errno = EINVAL; goto error_invalid_mood; } return 0; error_invalid_mood: return -1; } ssize_t xas_drone_sample_record(xas_drone *drone, xas_audio_stream *source, size_t sample_index, size_t sample_len) { if (sample_index >= drone->bank->entry_count || sample_len > drone->bank->entry_size) { errno = EINVAL; goto error_invalid; } return xas_bank_record(drone->bank, source, sample_index, sample_len); return 0; error_invalid: return -1; } ssize_t xas_drone_sample_import(xas_drone *drone, const char *path, size_t sample_index) { ssize_t ret; xas_audio_stream *source; if ((source = xas_riff_open_file(path, O_RDONLY)) == NULL) { goto error_riff_open_file; } if ((ret = xas_drone_sample_record(drone, source, sample_index, drone->bank->entry_size)) < 0) { goto error_sample_record; } xas_audio_stream_destroy(source); return ret; error_sample_record: xas_audio_stream_destroy(source); error_riff_open_file: return -1; } int xas_drone_speech_import(xas_drone *drone, const char *voice, float speed, size_t sample_index, size_t sample_count, const char **speech_lines) { size_t i, o; if (sample_index + sample_count - 1 >= drone->bank->entry_count) { errno = EINVAL; goto error_invalid; } if (voice) { xas_vox_set_voice(drone->vox, voice); } xas_drone_vox_set_speed(drone, speed); for (i=0, o=sample_index; ivox, speech_lines[i]) < 0) { goto error_vox_say; } if (xas_drone_vox_save(drone, o) < 0) { goto error_vox_save; } } return 0; error_vox_save: error_vox_say: error_invalid: return -1; } int xas_drone_seq_sample(xas_drone *drone, xas_seq *seq, size_t speech_part, struct timeval *now) { struct timeval duration, tmp; if (xas_seq_add_set_entry(seq, &drone->obj, *now, XAS_DRONE_INDEX_BANK, speech_part) < 0) { goto error_xas_seq_add; } if (xas_seq_add_event_on(seq, &drone->obj, *now, XAS_DRONE_INDEX_BANK) < 0) { goto error_xas_seq_add; } xas_bank_entry_duration(drone->bank, speech_part, &duration); timeradd(now, &duration, &tmp); now->tv_sec = tmp.tv_sec; now->tv_usec = tmp.tv_usec; return 0; error_xas_seq_add: return -1; } int xas_drone_seq_beep(xas_drone *drone, xas_seq *seq, enum xas_drone_mood mood, struct timeval *now) { static struct timeval duration = { 1, 0 }, tmp; if (xas_seq_add_set_type(seq, &drone->obj, *now, XAS_DRONE_INDEX_BEEP, mood) < 0) { goto error_seq; } if (xas_seq_add_event_on(seq, &drone->obj, *now, XAS_DRONE_INDEX_BEEP) < 0) { goto error_seq; } timeradd(now, &duration, &tmp); now->tv_sec = tmp.tv_sec; now->tv_usec = tmp.tv_usec; if (xas_seq_add_event_off(seq, &drone->obj, *now, XAS_DRONE_INDEX_BEEP) < 0) { goto error_seq; } return 0; error_seq: return -1; } int xas_drone_vox_set_voice(xas_drone *drone, const char *voice) { return xas_vox_set_voice(drone->vox, voice); } int xas_drone_vox_set_speed(xas_drone *drone, float speed) { return xas_vox_set_parameter_float(drone->vox, "Duration_Stretch", 1.0f / speed); } int xas_drone_vox_say(xas_drone *drone, const char *text) { return xas_vox_say(drone->vox, text); } int xas_drone_vox_vsayf(xas_drone *drone, const char *format, va_list args) { return xas_vox_vsayf(drone->vox, format, args); } int xas_drone_vox_sayf(xas_drone *drone, const char *format, ...) { int ret; va_list args; va_start(args, format); ret = xas_vox_vsayf(drone->vox, format, args); va_end(args); return ret; } int xas_drone_vox_save(xas_drone *drone, size_t sample_index) { xas_audio_stream *source; if ((source = xas_vox_stream_new(drone->vox)) == NULL) { goto error_vox_stream_new; } if (xas_vox_generate(drone->vox) < 0) { goto error_vox_generate; } if (xas_bank_record(drone->bank, source, sample_index, drone->bank->entry_size) < 0) { goto error_bank_record; } xas_audio_stream_destroy(source); return 0; error_bank_record: error_vox_generate: xas_audio_stream_destroy(source); error_vox_stream_new: return -1; } xas_drone_chamber *xas_drone_chamber_new(xas_spatial_scene *scene, xas_spatial_coord location, size_t drone_count) { xas_drone_chamber *chamber; size_t total = drone_count * sizeof(xas_drone *); if ((chamber = malloc(sizeof(*chamber))) == NULL) { goto error_malloc_chamber; } if ((chamber->drones = malloc(total)) == NULL) { goto error_malloc_chamber_drones; } if ((chamber->synth_bass = xas_spatial_scene_add_synth(scene, (xas_spatial_coord){ location.x, location.y, location.z + 5.0 }, XAS_SYNTH_SQUARE)) == NULL) { goto error_spatial_scene_add_synth; } if ((chamber->synth_l = xas_spatial_scene_add_synth(scene, (xas_spatial_coord){ location.x - 5.0, location.y, location.z }, XAS_SYNTH_SINE)) == NULL) { goto error_spatial_scene_add_synth; } if ((chamber->synth_r = xas_spatial_scene_add_synth(scene, (xas_spatial_coord){ location.x + 5.0, location.y, location.z }, XAS_SYNTH_SINE)) == NULL) { goto error_spatial_scene_add_synth; } xas_synth_set_frequency(chamber->synth_bass->ctx, XAS_DRONE_CHAMBER_BASS_FREQUENCY); xas_synth_start(chamber->synth_l->ctx); xas_synth_start(chamber->synth_r->ctx); chamber->drone_count = drone_count; memset(chamber->drones, '\0', total); return chamber; error_spatial_scene_add_synth: error_malloc_chamber_drones: free(chamber); error_malloc_chamber: return NULL; } void xas_drone_chamber_destroy(xas_drone_chamber *chamber) { free(chamber->drones); free(chamber); } void xas_drone_chamber_insert_drone(xas_drone_chamber *chamber, xas_drone *drone, size_t index) { chamber->drones[index] = drone; } void xas_drone_chamber_set_drone_gain(xas_drone_chamber *chamber, float gain) { size_t i; for (i=0; idrone_count; i++) { xas_drone *drone = chamber->drones[i]; xas_object_set_gain(&drone->mixer->obj, 0, gain); } } void xas_drone_chamber_set_synth_gain(xas_drone_chamber *chamber, float gain) { xas_synth_set_gain(chamber->synth_l->ctx, gain); xas_synth_set_gain(chamber->synth_r->ctx, gain); } void xas_drone_chamber_set_bass_gain(xas_drone_chamber *chamber, float gain) { xas_synth_set_gain(chamber->synth_bass->ctx, gain); } void xas_drone_chamber_bass_start(xas_drone_chamber *chamber) { xas_synth_start(chamber->synth_bass->ctx); } void xas_drone_chamber_bass_stop(xas_drone_chamber *chamber) { xas_synth_stop(chamber->synth_bass->ctx); } void xas_drone_chamber_bass_set_type(xas_drone_chamber *chamber, enum xas_synth_type type) { xas_synth_set_type(chamber->synth_bass->ctx, type); } void xas_drone_chamber_bass_set_frequency(xas_drone_chamber *chamber, size_t freq) { xas_synth_set_frequency(chamber->synth_bass->ctx, freq); } int xas_drone_chamber_seq_intervals(xas_drone_chamber *chamber, xas_drone_chamber_interval *intervals, xas_seq *seq, size_t count, struct timeval *now) { size_t i; for (i=0; itv_sec, .tv_usec = now->tv_usec }; if (xas_seq_add_set_frequency(seq, chamber->synth_l->ctx, *now, 0, intervals[i].freq_l) < 0) { goto error_seq_add; } if (xas_seq_add_set_type(seq, chamber->synth_l->ctx, *now, 0, intervals[i].type_l) < 0) { goto error_seq_add; } if (xas_seq_add_set_frequency(seq, chamber->synth_r->ctx, *now, 0, intervals[i].freq_r) < 0) { goto error_seq_add; } if (xas_seq_add_set_type(seq, chamber->synth_r->ctx, *now, 0, intervals[i].type_r) < 0) { goto error_seq_add; } timeradd(&cur, &intervals[i].duration, now); } return 0; error_seq_add: return -1; } static void max_speech_duration(xas_drone_chamber *chamber, size_t speech_part, struct timeval *max) { size_t i; max->tv_sec = 0; max->tv_usec = 0; for (i=0; idrone_count; i++) { xas_drone *drone = chamber->drones[i]; struct timeval duration; xas_bank_entry_duration(drone->bank, speech_part, &duration); if (timercmp(max, &duration, <)) { max->tv_sec = duration.tv_sec; max->tv_usec = duration.tv_usec; } } } int xas_drone_chamber_seq_chorus(xas_drone_chamber *chamber, xas_seq *seq, size_t speech_part, struct timeval *now) { struct timeval duration, tmp, delay = { 0, 500000 }; size_t i; max_speech_duration(chamber, speech_part, &duration); for (i=0; idrone_count; i++) { xas_drone *drone = chamber->drones[i]; if (xas_seq_add_set_entry(seq, &drone->obj, *now, XAS_DRONE_INDEX_BANK, speech_part) < 0) { goto error_seq_add; } if (xas_seq_add_event_on(seq, &drone->obj, *now, XAS_DRONE_INDEX_BANK) < 0) { goto error_seq_add; } } timeradd(now, &duration, &tmp); timeradd(&tmp, &delay, now); return 0; error_seq_add: return -1; }