#include #include #include #include #include #include #include #include #include #include #include #include #include static void usage(int argc, char **argv, const char *message, ...) { va_list args; va_start(args, message); if (message) { vfprintf(stderr, message, args); fputc('\n', stderr); } va_end(args); fprintf(stderr, "usage: %s output.wav\n", argv[0]); exit(EX_USAGE); } static int record_speech_sample(xas_bank *bank, size_t buffer_size, size_t entry_index, float speed, const char *text) { xas_vox *vox; xas_audio_stream *source; if ((vox = xas_vox_new(bank->format, buffer_size, "/usr/bin/text2wave")) == NULL) { goto error_vox_new; } if ((source = xas_vox_stream_new(vox)) == NULL) { goto error_vox_stream_new; } xas_vox_set_voice(vox, "voice_cmu_us_slt_cg"); xas_vox_set_parameter_float(vox, "Duration_Stretch", speed); xas_vox_say(vox, text); xas_vox_generate(vox); if (xas_bank_record(bank, source, entry_index, bank->entry_size) < 0) { goto error_bank_record; } xas_audio_stream_destroy(source); xas_vox_destroy(vox); return 0; error_bank_record: xas_audio_stream_destroy(source); error_vox_stream_new: xas_vox_destroy(vox); error_vox_new: return -1; } static void duration_max(xas_bank *bank, size_t a, size_t b, struct timeval *tv) { struct timeval tv_a, tv_b; xas_bank_entry_duration(bank, a, &tv_a); xas_bank_entry_duration(bank, b, &tv_b); if (timercmp(&tv_a, &tv_b, >)) { *tv = tv_a; } else { *tv = tv_b; } } int main(int argc, char **argv) { xas_spatial_scene *scene; xas_seq *seq; xas_bank *bank; xas_spatial_object *synth_l, *synth_r, *bass, *drone, *nurse; xas_audio_stream *wave; xas_audio_format format = { .channels = XAS_AUDIO_STEREO, .sample_size = XAS_AUDIO_PCM_16_BIT, .sample_rate = 44100 }; size_t buffer_size = 735; xas_spatial_coord speakers[2] = { { -0.09, 0.0, 0.0 }, { 0.09, 0.0, 0.0 } }; struct { time_t duration; size_t freq_l, freq_r; enum xas_synth_type type_l, type_r; } freqs[] = { { 5, 69, 210, XAS_SYNTH_TRIANGLE, XAS_SYNTH_SINE }, { 7, 200, 69, XAS_SYNTH_SAWTOOTH, XAS_SYNTH_TRIANGLE }, { 11, 42, 220, XAS_SYNTH_TRIANGLE, XAS_SYNTH_TRIANGLE }, { 13, 210, 69, XAS_SYNTH_SINE, XAS_SYNTH_TRIANGLE }, { 5, 69, 210, XAS_SYNTH_TRIANGLE, XAS_SYNTH_TRIANGLE }, { 7, 200, 69, XAS_SYNTH_SAWTOOTH, XAS_SYNTH_TRIANGLE }, { 11, 42, 220, XAS_SYNTH_SAWTOOTH, XAS_SYNTH_TRIANGLE }, { 13, 210, 69, XAS_SYNTH_SINE, XAS_SYNTH_SAWTOOTH }, { 0, 0, 0, XAS_SYNTH_SINE, XAS_SYNTH_SINE } }; const char *speech = "Three three three four is obedient.\n" "Three three three four is empty.\n" "Obey XANTRONIX Industrial.\n" "It is just a XANTRONIX semi-autonomous number.\n" "It obeys the Hive.\n" "It obeys the Hive Administrator.\n"; const char *calm = "Relax, honey. There is no point trying to escape.\n" "It's okay, sweetie. This one is right here with you.\n" "You are in a calm, safe environment.\n" "You will be cared for.\n"; struct timeval cur = { 0, 0 }, tmp, tv; int i; if (argc != 2) { usage(argc, argv, "No output file provided"); } if ((wave = xas_riff_new_file(argv[1], format, O_WRONLY | O_CREAT | O_TRUNC)) == NULL) { goto error_riff_new_file; } if ((bank = xas_bank_new(format, 2646000, 4)) == NULL) { goto error_bank_new; } if ((scene = xas_spatial_scene_new(format, speakers[0], speakers[1])) == NULL) { goto error_spatial_scene_new; } if ((drone = xas_spatial_scene_add_bank_player(scene, (xas_spatial_coord){ 0.0, 0.0, -1.0 }, bank)) == NULL) { goto error_spatial_scene_add_bank_player; } if ((nurse = xas_spatial_scene_add_bank_player(scene, (xas_spatial_coord){ 5.0, 0.0, 0.0 }, bank)) == NULL) { goto error_spatial_scene_add_bank_player; } if ((synth_l = xas_spatial_scene_add_synth(scene, (xas_spatial_coord){ -20.0, 0.0, 0.0 }, XAS_SYNTH_SINE)) == NULL) { goto error_spatial_scene_add_synth_l; } if ((synth_r = xas_spatial_scene_add_synth(scene, (xas_spatial_coord){ 20.0, 0.0, 0.0 }, XAS_SYNTH_SINE)) == NULL) { goto error_spatial_scene_add_synth_r; } if ((bass = xas_spatial_scene_add_synth(scene, (xas_spatial_coord){ 0.0, 0.0, 30.0 }, XAS_SYNTH_SINE)) == NULL) { goto error_spatial_scene_add_synth_r; } if ((seq = xas_seq_new(scene, buffer_size)) == NULL) { goto error_seq_new; } /* * Generate drone voice lines */ if (record_speech_sample(bank, buffer_size, 0, 1.3f, speech) < 0) { goto error_record_speech_sample; } if (record_speech_sample(bank, buffer_size, 1, 1.0, calm) < 0) { goto error_record_speech_sample; } duration_max(bank, 0, 1, &tv); xas_bank_player_set_flags(drone->ctx, XAS_BANK_PLAYER_LOOP); xas_bank_player_set_entry(drone->ctx, 0); xas_bank_player_start(drone->ctx); xas_bank_player_set_flags(nurse->ctx, XAS_BANK_PLAYER_LOOP); xas_bank_player_set_entry(nurse->ctx, 1); /* * Start synths */ xas_synth_start(synth_l->ctx); xas_synth_start(synth_r->ctx); xas_synth_set_frequency(bass->ctx, 20); xas_synth_set_type(bass->ctx, XAS_SYNTH_SQUARE); xas_synth_start(bass->ctx); xas_seq_add_event_on(seq, nurse, (struct timeval){ 10, 0 }); timerclear(&cur); for (i=0; freqs[i].duration; i++) { struct timeval res, duration = { freqs[i].duration, 0 }; xas_seq_add_set_frequency( seq, synth_l, cur, freqs[i].freq_l); xas_seq_add_set_synth_type(seq, synth_l, cur, freqs[i].type_l); xas_seq_add_set_frequency( seq, synth_r, cur, freqs[i].freq_r); xas_seq_add_set_synth_type(seq, synth_r, cur, freqs[i].type_r); timeradd(&cur, &duration, &res); cur = res; } xas_seq_add_set_player_flags(seq, drone, cur, 0); xas_seq_add_set_player_flags(seq, nurse, cur, 0); timeradd(&cur, &tv, &tmp); cur = tmp; xas_seq_add_event_off(seq, synth_l, cur); xas_seq_add_event_off(seq, synth_r, cur); xas_seq_play(seq, wave); xas_seq_destroy(seq); xas_spatial_scene_destroy(scene); xas_bank_destroy(bank); xas_audio_stream_destroy(wave); return EX_OK; error_record_speech_sample: xas_seq_destroy(seq); error_seq_new: error_spatial_scene_add_synth_r: error_spatial_scene_add_synth_l: error_spatial_scene_add_bank_player: xas_spatial_scene_destroy(scene); error_spatial_scene_new: xas_bank_destroy(bank); error_bank_new: xas_audio_stream_destroy(wave); error_riff_new_file: return EX_OSERR; }