summaryrefslogtreecommitdiff
path: root/src/speech-service/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/speech-service/main.cpp')
-rw-r--r--src/speech-service/main.cpp276
1 files changed, 276 insertions, 0 deletions
diff --git a/src/speech-service/main.cpp b/src/speech-service/main.cpp
new file mode 100644
index 0000000..df45857
--- /dev/null
+++ b/src/speech-service/main.cpp
@@ -0,0 +1,276 @@
+/**
+* @licence app begin@
+* SPDX-License-Identifier: MPL-2.0
+*
+* \copyright Copyright (C) 2013-2017, PSA GROUP
+*
+* \file main.cpp
+*
+* \brief This file is part of the speech service proof of concept.
+*
+* \author Philippe Colliot <philippe.colliot@mpsa.com>
+* \brief Some parts of the code has been inspired by the following people:
+* \brief Mario Thielert, David Kämpf, Dominique Massonie
+*
+* \version 1.0
+*
+* This Source Code Form is subject to the terms of the
+* Mozilla Public License (MPL), v. 2.0.
+* If a copy of the MPL was not distributed with this file,
+* You can obtain one at http://mozilla.org/MPL/2.0/.
+*
+* For further information see http://www.genivi.org/.
+*
+* List of changes:
+* <date>, <name>, <description of change>
+*
+* @licence end@
+*/
+
+#include <stdbool.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <iostream>
+#include <cmath>
+#include <typeinfo>
+#include <dbus-c++/glib-integration.h>
+#include "genivi-speechservice-constants.h"
+#include "genivi-speechservice-speechoutput_adaptor.h"
+#include "log.h"
+#include <semaphore.h>
+#include <flite.h>
+
+extern "C" {
+ cst_voice* register_cmu_us_kal(const char *voxdir);
+ void unregister_cmu_us_kal(cst_voice *vox);
+}
+
+DLT_DECLARE_CONTEXT(gCtx)
+
+static const char* speech_SERVICE_NAME = "org.genivi.hmi.speechservice.SpeechOutput";
+static const char* speech_OBJECT_PATH = "/org/genivi/hmi/speechservice/SpeechOutput";
+
+static DBus::Glib::BusDispatcher *dispatcher;
+static DBus::Connection *dbusConnection;
+
+static int m_lenLastChunk;
+static int m_slotCount;
+static const uint32_t MAX_CHUNK_SIZE = 1024 + 1; /**< Maximum size of any chunk which can be stored within the queue. */
+static const uint32_t MAX_SLOT_COUNT = 16; /**< Maximum number of chunks which can be present in the queue at the same time. */
+static const uint32_t MAX_SLOT_COUNT_NO_WARN = (3 * MAX_SLOT_COUNT) / 4; /**< When pushing a chunk, TAQS_WARNING shall be reported if the new occupied slot count exceeds MAX_SLOT_COUNT_NO_WARN. */
+static const uint32_t MAX_CHUNK_LENGTH = 1024; /**< max length of a single prompt part */
+static const uint32_t MAX_MARKER_LENGTH = 32; /**< marker tag inside the prompt chunk */
+static const uint32_t PROCESS_CHUNKS_LOOP = 100;
+static cst_voice* mp_voice;
+static cst_audio_streaming_info* mp_asi;
+static pthread_mutex_t mutex;
+
+static std::string m_chunkBuffer; /** max size = MAX_CHUNK_SIZE*MAX_SLOT_COUNT */
+
+gboolean processChunks(gpointer data) {
+ //worker thread to process chunks in buffer
+
+ pthread_mutex_lock(&mutex);
+
+ if (m_chunkBuffer.length() > 0)
+ {
+ LOG_DEBUG_MSG(gCtx,"processChunks");
+ std::string tempBuffer;
+ tempBuffer.assign(m_chunkBuffer);
+ m_chunkBuffer.clear(); //buffer empty
+ m_slotCount=0; //reset counter of slots
+
+ pthread_mutex_unlock(&mutex);
+
+ // pass string to TTS engine
+ flite_text_to_speech(tempBuffer.c_str(),mp_voice,"play");
+ }
+ else
+ {
+ pthread_mutex_unlock(&mutex);
+ }
+ return(true);
+}
+
+static int fliteCallback(const cst_wave *w, int start, int size,
+ int last, cst_audio_streaming_info_struct *asi)
+{
+ static cst_audiodev *ad = 0;
+
+ if (start == 0)
+ ad = audio_open(w->sample_rate,w->num_channels,CST_AUDIO_LINEAR16);
+
+ audio_write(ad,&w->samples[start],size*sizeof(short));
+
+ if (last == 1)
+ {
+ audio_drain(ad);
+ audio_close(ad);
+ ad = NULL;
+ LOG_DEBUG_MSG(gCtx,"end of processing chunks");
+ }
+
+ /* if you want to stop return CST_AUDIO_STREAM_STOP */
+ return CST_AUDIO_STREAM_CONT;
+}
+
+class SpeechOutput
+: public org::genivi::hmi::speechservice::SpeechOutput_adaptor,
+ public DBus::IntrospectableAdaptor,
+ public DBus::ObjectAdaptor
+{
+public:
+
+ SpeechOutput(DBus::Connection &connection) : DBus::ObjectAdaptor(connection, speech_OBJECT_PATH){
+ m_version._1=1;
+ m_version._2=0;
+ m_version._3=0;
+ m_version._4="12-10-2016";
+
+ m_lenLastChunk = 0;
+ m_slotCount = 0;
+
+ flite_init();
+
+ mp_voice = register_cmu_us_kal(NULL);
+ mp_asi = new_audio_streaming_info();
+ mp_asi->asc = fliteCallback;
+
+ feat_set(mp_voice->features,"streaming_info",audio_streaming_info_val(mp_asi));
+ }
+
+ ~SpeechOutput(){
+ unregister_cmu_us_kal(mp_voice);
+ }
+
+ ::DBus::Struct< uint16_t, uint16_t, uint16_t, std::string > getVersion(){
+ return m_version;
+ }
+
+ void openPrompter(const int32_t& connectionType, const int32_t& preProcessingType){
+ LOG_DEBUG(gCtx,"openPrompter: connection %d preprocessing %d",connectionType,preProcessingType);
+ }
+
+ /**
+ * description: The prompter must be opened to trigger the playback of the provided prompt.
+ *
+ The prompt length must not exceed the length of a PromptChunk
+ * buffer.
+ Synthesizes the provided text or if using the escape sequence of the
+ * engine supplier a wave file in a supported sampling rate is provided, the
+ * system will back back also wave files.
+ The text will be normalized using the
+ * context identifier provided to openprompter. This applies to matching
+ * prerecorded files as well as the synthesis of number and words that are
+ * matched to a lexical dictionary.
+ The synthesize will start if the prompter is
+ * idle, if the prompter is already playing the playback will be delayed until
+ * all previously added text chunks are played back. For every text chunk
+ * provided a notification will be send.
+ */
+ uint32_t addTextChunk(const std::string& chunk){
+ LOG_DEBUG(gCtx,"addTextChunk: %s", chunk.c_str());
+
+ uint32_t _chunkID;
+
+ // todo: manage _chunkID
+
+ int32_t qStatus = GENIVI_SPEECHSERVICE_CS_UNKNOWN;
+
+ if (chunk.size() > 0 && chunk.size() < MAX_CHUNK_LENGTH)
+ {
+ if (m_slotCount < (int) MAX_SLOT_COUNT)
+ {
+ if (m_slotCount <= (int) MAX_SLOT_COUNT_NO_WARN)
+ {
+ qStatus = GENIVI_SPEECHSERVICE_QS_LOW_FILL;
+ }
+ else
+ {
+ qStatus = GENIVI_SPEECHSERVICE_QS_HIGH_FILL;
+ }
+ m_chunkBuffer.append(chunk);
+ m_lenLastChunk = chunk.size();
+ ++m_slotCount;
+ }
+ else
+ {
+ qStatus = GENIVI_SPEECHSERVICE_QS_FULL;
+ }
+ }
+
+ notifyQueueStatus(qStatus);
+
+ return(_chunkID);
+ }
+
+ /**
+ * description: A prompt must be playing to perform an abort action. If no prompting operation
+ * in progress there will be no reaction of the system.
+ */
+ void abortPrompter(){
+ LOG_DEBUG_MSG(gCtx,"abortPrompter");
+ }
+
+ /**
+ * description: The prompter is closed after the last text chunk submitted has finished playing.
+ */
+ void closePrompter(){
+ LOG_DEBUG_MSG(gCtx,"closePrompter");
+ }
+
+private:
+ ::DBus::Struct< uint16_t, uint16_t, uint16_t, std::string > m_version;
+};
+
+static SpeechOutput* serverSpeechOutput;
+
+int main(int argc , char** argv )
+{
+ DLT_REGISTER_APP("SPCS","SPEECH SERVER");
+ DLT_REGISTER_CONTEXT(gCtx,"SPCS","Global Context");
+
+ GMainLoop * mainloop ;
+ guint processChunksID;
+
+ // Set the global C and C++ locale to the user-configured locale,
+ // so we can use std::cout with UTF-8, via Glib::ustring, without exceptions.
+ std::locale::global(std::locale(""));
+
+ // creating the dispatcher
+ dispatcher = new DBus::Glib::BusDispatcher();
+ DBus::default_dispatcher = dispatcher;
+ dispatcher->attach(NULL);
+
+ // create a connection on the session bus
+ dbusConnection = new DBus::Connection(DBus::Connection::SessionBus());
+ dbusConnection->setup(dispatcher);
+
+ // create the server for SpeechOutput
+ dbusConnection->request_name(speech_SERVICE_NAME);
+ serverSpeechOutput=new SpeechOutput(*dbusConnection);
+
+ processChunksID = g_timeout_add(PROCESS_CHUNKS_LOOP,processChunks,NULL);
+
+ // Create a new GMainLoop with default context and initial state of "not running "
+ mainloop = g_main_loop_new (g_main_context_default() , FALSE );
+
+ // Send a feedback to the user
+ LOG_INFO_MSG(gCtx,"Speech output server started");
+
+ // loop listening
+ g_main_loop_run ( mainloop );
+
+ // clean memory
+ delete serverSpeechOutput;
+ delete dbusConnection;
+ delete dispatcher;
+
+ LOG_INFO_MSG(gCtx,"Speech output server stopped");
+
+ return EXIT_SUCCESS;
+}
+