summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXANTRONIX Development2022-03-13 17:16:26 -0400
committerXANTRONIX Development2022-03-13 17:16:26 -0400
commit7b8375ee2026d42b5444f8ae0e7b98e272e87d69 (patch)
tree38382c0062f095c9efcd0d6bc29e1b5a5e6ac2f1
parent98de824b0eb038d37f62f5317a367b2bfbd38b9d (diff)
downloadxas-7b8375ee2026d42b5444f8ae0e7b98e272e87d69.tar.gz
xas-7b8375ee2026d42b5444f8ae0e7b98e272e87d69.tar.bz2
xas-7b8375ee2026d42b5444f8ae0e7b98e272e87d69.zip
Refactor drone speech modules
Changes: * Implement xas_drone_vox type to wrap a xas_vox and audio stream object in one, and save rendered speech to a drone's audio bank * Implement xas_drone_speech_import() as a convenience wrapper to xas_drone_vox methods
-rw-r--r--examples/seq.c18
-rw-r--r--include/xas/drone.h66
-rw-r--r--src/drone.c220
3 files changed, 226 insertions, 78 deletions
diff --git a/examples/seq.c b/examples/seq.c
index 601e46a..ab8da55 100644
--- a/examples/seq.c
+++ b/examples/seq.c
@@ -195,19 +195,15 @@ int main(int argc, char **argv) {
if ((drone = xas_drone_new(scene,
(xas_spatial_coord){ 0.0, 0.0, -1.0 },
- 1.3f,
2646000,
- 4,
- drone_lines)) == NULL) {
+ 4)) == NULL) {
goto error_drone_new;
}
if ((nurse = xas_drone_new(scene,
(xas_spatial_coord){ 1.0, 0.0, 0.0 },
- 1.0f,
2646000,
- 4,
- nurse_lines)) == NULL) {
+ 4)) == NULL) {
goto error_drone_new_nurse;
}
@@ -217,6 +213,14 @@ int main(int argc, char **argv) {
goto error_drone_chamber_new;
}
+ if (xas_drone_speech_import(drone, NULL, 0.75f, 0, 4, drone_lines) < 0) {
+ goto error_drone_speech_import;
+ }
+
+ if (xas_drone_speech_import(nurse, NULL, 1.0f, 0, 4, nurse_lines) < 0) {
+ goto error_drone_speech_import_nurse;
+ }
+
xas_drone_chamber_insert_drone(chamber, drone, 0);
xas_drone_chamber_insert_drone(chamber, nurse, 1);
xas_drone_chamber_bass_start(chamber);
@@ -291,6 +295,8 @@ int main(int argc, char **argv) {
return EX_OK;
error_seq:
+error_drone_speech_import_nurse:
+error_drone_speech_import:
xas_drone_chamber_destroy(chamber);
error_drone_chamber_new:
diff --git a/include/xas/drone.h b/include/xas/drone.h
index b790cc5..d7dc4c0 100644
--- a/include/xas/drone.h
+++ b/include/xas/drone.h
@@ -1,24 +1,37 @@
#ifndef _XAS_DRONE_H
#define _XAS_DRONE_H
+#include <stdarg.h>
#include <sys/time.h>
#include <xas/spatial.h>
+#include <xas/vox.h>
#include <xas/seq.h>
+#define XAS_DRONE_VOX_BUFFER_SIZE 735
+#define XAS_DRONE_VOX_TEXT2WAVE_PATH "/usr/bin/text2wave"
+
+#define XAS_DRONE_VOX_DEFAULT_SPEED 1.0f
+#define XAS_DRONE_VOX_DEFAULT_VOICE "voice_cmu_us_slt_cg"
+
#define XAS_DRONE_CHAMBER_BASS_FREQUENCY 20 /* Hz */
#define XAS_DRONE_CHAMBER_BASS_TYPE XAS_SYNTH_SQUARE
-typedef struct _xas_drone xas_drone;
-
-struct _xas_drone {
+typedef struct _xas_drone {
xas_bank *bank;
xas_spatial_scene *scene;
xas_spatial_object *obj;
- float speech_speed;
- const char **speech_lines;
-};
+} xas_drone;
+
+typedef struct _xas_drone_vox {
+ xas_drone *drone;
+ xas_vox *obj;
+ xas_audio_stream *source;
+
+ const char *voice;
+ float speed;
+} xas_drone_vox;
typedef struct _xas_drone_chamber_interval {
struct timeval duration;
@@ -36,7 +49,6 @@ typedef struct _xas_drone_chamber_speech {
} xas_drone_chamber_speech;
typedef struct _xas_drone_chamber {
- xas_spatial_scene *scene;
xas_drone **drones;
size_t drone_count;
@@ -45,17 +57,49 @@ typedef struct _xas_drone_chamber {
*synth_r;
} xas_drone_chamber;
+/*
+ * Methods for individual drones
+ */
xas_drone *xas_drone_new(xas_spatial_scene *scene,
xas_spatial_coord position,
- float speech_speed,
- size_t speech_sample_count,
- size_t speech_line_count,
- const char **speech_lines);
+ size_t bank_entry_size,
+ size_t bank_entry_count);
void xas_drone_destroy(xas_drone *drone);
xas_spatial_object *xas_drone_get_spatial_object(xas_drone *drone);
+int xas_drone_speech_import(xas_drone *drone,
+ const char *voice,
+ float speed,
+ size_t speech_sample_first,
+ size_t speech_sample_count,
+ const char **speech_lines);
+
+/*
+ * Methods for drone vocalisations
+ */
+xas_drone_vox *xas_drone_vox_new(xas_drone *drone);
+
+void xas_drone_vox_destroy(xas_drone_vox *vox);
+
+void xas_drone_vox_set_voice(xas_drone_vox *vox, const char *voice);
+
+void xas_drone_vox_set_speed(xas_drone_vox *vox, float speed);
+
+int xas_drone_vox_vsayf(xas_drone_vox *vox,
+ const char *format,
+ va_list args);
+
+int xas_drone_vox_sayf(xas_drone_vox *vox, const char *format, ...);
+
+int xas_drone_vox_say(xas_drone_vox *vox, const char *text);
+
+int xas_drone_vox_save(xas_drone_vox *vox, size_t sample_index);
+
+/*
+ * Drone conversion chamber methods
+ */
xas_drone_chamber *xas_drone_chamber_new(xas_spatial_scene *scene,
xas_spatial_coord location,
size_t drone_count);
diff --git a/src/drone.c b/src/drone.c
index dc8c084..e4a0cb7 100644
--- a/src/drone.c
+++ b/src/drone.c
@@ -1,91 +1,37 @@
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
#include <xas/vox.h>
#include <xas/drone.h>
-static size_t DEFAULT_BUFFER_SIZE = 735;
-
-static int record_speech_sample(xas_bank *bank,
- size_t entry_index,
- float speed,
- const char *text) {
- xas_vox *vox;
- xas_audio_stream *source;
-
- if ((vox = xas_vox_new(bank->format,
- DEFAULT_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;
-}
-
xas_drone *xas_drone_new(xas_spatial_scene *scene,
xas_spatial_coord position,
- float speech_speed,
- size_t speech_sample_count,
- size_t speech_line_count,
- const char **speech_lines) {
+ size_t bank_entry_size,
+ size_t bank_entry_count) {
xas_drone *drone;
- size_t i;
-
if ((drone = malloc(sizeof(*drone))) == NULL) {
goto error_malloc_drone;
}
if ((drone->bank = xas_bank_new(scene->format,
- speech_sample_count,
- speech_line_count)) == NULL) {
+ bank_entry_size,
+ bank_entry_count)) == NULL) {
goto error_bank_new;
}
- for (i=0; i<speech_line_count; i++) {
- if (record_speech_sample(drone->bank,
- i,
- speech_speed,
- speech_lines[i]) < 0) {
- goto error_record_speech_sample;
- }
- }
-
if ((drone->obj = xas_spatial_scene_add_bank_player(scene,
position,
drone->bank)) == NULL) {
goto error_spatial_scene_add_bank_player;
}
+ drone->scene = scene;
+
return drone;
error_spatial_scene_add_bank_player:
-error_record_speech_sample:
xas_bank_destroy(drone->bank);
error_bank_new:
@@ -105,6 +51,158 @@ xas_spatial_object *xas_drone_get_spatial_object(xas_drone *drone) {
return drone->obj;
}
+int xas_drone_speech_import(xas_drone *drone,
+ const char *voice,
+ float speed,
+ size_t speech_sample_first,
+ size_t speech_sample_count,
+ const char **speech_lines) {
+ xas_drone_vox *vox;
+ size_t i, o;
+
+ if (speech_sample_first + speech_sample_count - 1 >= drone->bank->entry_count) {
+ errno = EINVAL;
+
+ goto error_invalid;
+ }
+
+ if ((vox = xas_drone_vox_new(drone)) == NULL) {
+ goto error_vox_new;
+ }
+
+ if (voice) {
+ xas_drone_vox_set_voice(vox, voice);
+ }
+
+ xas_drone_vox_set_speed(vox, speed);
+
+ for (i=0, o=speech_sample_first; i<speech_sample_count; i++, o++) {
+ if (xas_drone_vox_say(vox, speech_lines[i]) < 0) {
+ goto error_vox_say;
+ }
+
+ if (xas_drone_vox_save(vox, o) < 0) {
+ goto error_vox_save;
+ }
+
+ if (xas_vox_stop(vox->obj) < 0) {
+ goto error_vox_stop_obj;
+ }
+ }
+
+ xas_drone_vox_destroy(vox);
+
+ return 0;
+
+error_vox_stop_obj:
+error_vox_save:
+error_vox_say:
+error_vox_new:
+error_invalid:
+ return -1;
+}
+
+xas_drone_vox *xas_drone_vox_new(xas_drone *drone) {
+ xas_drone_vox *vox;
+
+ if ((vox = malloc(sizeof(*vox))) == NULL) {
+ goto error_malloc_vox;
+ }
+
+ if ((vox->obj = xas_vox_new(drone->scene->format,
+ XAS_DRONE_VOX_BUFFER_SIZE,
+ XAS_DRONE_VOX_TEXT2WAVE_PATH)) == NULL) {
+ goto error_vox_new;
+ }
+
+ if ((vox->source = xas_vox_stream_new(vox->obj)) == NULL) {
+ goto error_vox_stream_new;
+ }
+
+ vox->drone = drone;
+ vox->voice = XAS_DRONE_VOX_DEFAULT_VOICE;
+ vox->speed = XAS_DRONE_VOX_DEFAULT_SPEED;
+
+ return vox;
+
+error_vox_stream_new:
+ xas_vox_destroy(vox->obj);
+
+error_vox_new:
+ free(vox);
+
+error_malloc_vox:
+ return NULL;
+}
+
+void xas_drone_vox_destroy(xas_drone_vox *vox) {
+ xas_audio_stream_destroy(vox->source);
+ xas_vox_destroy(vox->obj);
+ free(vox);
+}
+
+void xas_drone_vox_set_voice(xas_drone_vox *vox, const char *voice) {
+ vox->voice = voice;
+}
+
+void xas_drone_vox_set_speed(xas_drone_vox *vox, float speed) {
+ vox->speed = speed;
+}
+
+int xas_drone_vox_say(xas_drone_vox *vox, const char *text) {
+ return xas_vox_say(vox->obj, text);
+}
+
+int xas_drone_vox_vsayf(xas_drone_vox *vox,
+ const char *format,
+ va_list args) {
+ return xas_vox_vsayf(vox->obj, format, args);
+}
+
+int xas_drone_vox_sayf(xas_drone_vox *vox, const char *format, ...) {
+ int ret;
+ va_list args;
+
+ va_start(args, format);
+
+ ret = xas_vox_vsayf(vox->obj, format, args);
+
+ va_end(args);
+
+ return ret;
+}
+
+int xas_drone_vox_save(xas_drone_vox *vox, size_t sample_index) {
+ if (vox->voice && xas_vox_set_voice(vox->obj, vox->voice) < 0) {
+ goto error_vox_set_voice;
+ }
+
+ if (xas_vox_set_parameter_float(vox->obj,
+ "Duration_Stretch",
+ 1.0f / vox->speed) < 0) {
+ goto error_vox_set_parameter_float;
+ }
+
+ if (xas_vox_generate(vox->obj) < 0) {
+ goto error_vox_generate;
+ }
+
+ if (xas_bank_record(vox->drone->bank,
+ vox->source,
+ sample_index,
+ vox->drone->bank->entry_size) < 0) {
+ goto error_bank_record;
+ }
+
+ return 0;
+
+error_bank_record:
+error_vox_generate:
+error_vox_set_parameter_float:
+error_vox_set_voice:
+ return -1;
+}
+
xas_drone_chamber *xas_drone_chamber_new(xas_spatial_scene *scene,
xas_spatial_coord location,
size_t drone_count) {