/*
 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
 * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
 *
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
 *
 * The Initial Developer of the Original Code is
 * Anthony Minessale II <anthm@freeswitch.org>
 * Portions created by the Initial Developer are Copyright (C)
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Brian West <brian@freeswitch.org>
 * Raymond Chandler <intralanman@freeswitch.org>
 *
 * mod_flite.c -- Flite Interface
 *
 */

#include <switch.h>
#include <flite/flite.h>

cst_voice *register_cmu_us_awb(void);
void unregister_cmu_us_awb(cst_voice * v);

cst_voice *register_cmu_us_kal(void);
void unregister_cmu_us_kal(cst_voice * v);

cst_voice *register_cmu_us_rms(void);
void unregister_cmu_us_rms(cst_voice * v);

cst_voice *register_cmu_us_slt(void);
void unregister_cmu_us_slt(cst_voice * v);

cst_voice *register_cmu_us_kal16(void);
void unregister_cmu_us_kal16(cst_voice * v);


SWITCH_MODULE_LOAD_FUNCTION(mod_flite_load);
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_flite_shutdown);
SWITCH_MODULE_DEFINITION(mod_flite, mod_flite_load, mod_flite_shutdown, NULL);

static struct {
	cst_voice *awb;
	cst_voice *kal;
	cst_voice *rms;
	cst_voice *slt;
	cst_voice *kal16;
} globals;

struct flite_data {
	cst_voice *v;
	cst_wave *w;
	switch_buffer_t *audio_buffer;
};

typedef struct flite_data flite_t;

#define free_wave(w) if (w) {delete_wave(w) ; w = NULL; }
#define FLITE_BLOCK_SIZE 1024 * 32

static switch_status_t flite_speech_open(switch_speech_handle_t *sh, const char *voice_name, int rate, int channels, switch_speech_flag_t *flags)
{
	flite_t *flite = switch_core_alloc(sh->memory_pool, sizeof(*flite));

	sh->native_rate = 16000;

	if (!voice_name) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "A voice is required. Valid voice names are awb, rms, slt or kal.\n");
		return SWITCH_STATUS_FALSE;
	}

	if (!strcasecmp(voice_name, "awb")) {
		flite->v = globals.awb;
	} else if (!strcasecmp(voice_name, "kal")) {
/*  "kal" is 8kHz and the native rate is set to 16kHz
 *  so kal talks a little bit too fast ...
 *  for now: "symlink" kal to kal16
 */		flite->v = globals.kal16;
	} else if (!strcasecmp(voice_name, "rms")) {
		flite->v = globals.rms;
	} else if (!strcasecmp(voice_name, "slt")) {
		flite->v = globals.slt;
	} else if (!strcasecmp(voice_name, "kal16")) {
		flite->v = globals.kal16;
	} else {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Valid voice names are awb, rms, slt or kal.\n");
	}

	if (flite->v) {
		sh->private_info = flite;
		return SWITCH_STATUS_SUCCESS;
	}

	return SWITCH_STATUS_FALSE;
}

static switch_status_t flite_speech_close(switch_speech_handle_t *sh, switch_speech_flag_t *flags)
{
	flite_t *flite = (flite_t *) sh->private_info;

	if (flite->audio_buffer) {
		switch_buffer_destroy(&flite->audio_buffer);
	}

	free_wave(flite->w);

	return SWITCH_STATUS_SUCCESS;
}

static switch_status_t flite_speech_feed_tts(switch_speech_handle_t *sh, char *text, switch_speech_flag_t *flags)
{
	flite_t *flite = (flite_t *) sh->private_info;

	flite->w = flite_text_to_wave(text, flite->v);

	return SWITCH_STATUS_SUCCESS;
}

static void flite_speech_flush_tts(switch_speech_handle_t *sh)
{
	flite_t *flite = (flite_t *) sh->private_info;

	if (flite->audio_buffer) {
		switch_buffer_zero(flite->audio_buffer);
	}
	free_wave(flite->w);
}

static switch_status_t flite_speech_read_tts(switch_speech_handle_t *sh, void *data, size_t *datalen, switch_speech_flag_t *flags)
{
	size_t bytes_read;
	flite_t *flite = (flite_t *) sh->private_info;

	if (!flite->audio_buffer) {
		int32_t len;

		if (flite->w) {
			len = flite->w->num_samples * 2;
		} else {
			len = FLITE_BLOCK_SIZE;
		}

		switch_buffer_create_dynamic(&flite->audio_buffer, FLITE_BLOCK_SIZE, len, 0);
		switch_assert(flite->audio_buffer);
	}

	if (flite->w) {
		switch_buffer_write(flite->audio_buffer, flite->w->samples, flite->w->num_samples * 2);
		free_wave(flite->w);
	}

	if ((bytes_read = switch_buffer_read(flite->audio_buffer, data, *datalen))) {
		*datalen = bytes_read;
		return SWITCH_STATUS_SUCCESS;
	}

	return SWITCH_STATUS_FALSE;
}

static void flite_text_param_tts(switch_speech_handle_t *sh, char *param, const char *val)
{

}

static void flite_numeric_param_tts(switch_speech_handle_t *sh, char *param, int val)
{

}

static void flite_float_param_tts(switch_speech_handle_t *sh, char *param, double val)
{

}

SWITCH_MODULE_LOAD_FUNCTION(mod_flite_load)
{
	switch_speech_interface_t *speech_interface;

	flite_init();
	globals.awb = register_cmu_us_awb();
	globals.kal = register_cmu_us_kal();
	globals.rms = register_cmu_us_rms();
	globals.slt = register_cmu_us_slt();
	globals.kal16 = register_cmu_us_kal16();

	/* connect my internal structure to the blank pointer passed to me */
	*module_interface = switch_loadable_module_create_module_interface(pool, modname);
	speech_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_SPEECH_INTERFACE);
	speech_interface->interface_name = "flite";
	speech_interface->speech_open = flite_speech_open;
	speech_interface->speech_close = flite_speech_close;
	speech_interface->speech_feed_tts = flite_speech_feed_tts;
	speech_interface->speech_read_tts = flite_speech_read_tts;
	speech_interface->speech_flush_tts = flite_speech_flush_tts;
	speech_interface->speech_text_param_tts = flite_text_param_tts;
	speech_interface->speech_numeric_param_tts = flite_numeric_param_tts;
	speech_interface->speech_float_param_tts = flite_float_param_tts;

	/* indicate that the module should continue to be loaded */
	return SWITCH_STATUS_SUCCESS;
}

SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_flite_shutdown)
{
	unregister_cmu_us_awb(globals.awb);
	unregister_cmu_us_kal(globals.kal);
	unregister_cmu_us_rms(globals.rms);
	unregister_cmu_us_slt(globals.slt);
	unregister_cmu_us_kal16(globals.kal16);

	return SWITCH_STATUS_UNLOAD;
}

/* For Emacs:
 * Local Variables:
 * mode:c
 * indent-tabs-mode:t
 * tab-width:4
 * c-basic-offset:4
 * End:
 * For VIM:
 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
 */
