diff --git a/robot_voice/CMakeLists.txt b/robot_voice/CMakeLists.txt
index 3f8b561..6323d27 100644
--- a/robot_voice/CMakeLists.txt
+++ b/robot_voice/CMakeLists.txt
@@ -12,6 +12,7 @@ find_package(catkin REQUIRED COMPONENTS
rospy
std_msgs
geometry_msgs
+ message_generation
)
## System dependencies are found with CMake's conventions
@@ -55,11 +56,10 @@ find_package(catkin REQUIRED COMPONENTS
# )
## Generate services in the 'srv' folder
-# add_service_files(
-# FILES
-# Service1.srv
-# Service2.srv
-# )
+add_service_files(
+ FILES
+ StringToVoice.srv
+)
## Generate actions in the 'action' folder
# add_action_files(
@@ -69,10 +69,10 @@ find_package(catkin REQUIRED COMPONENTS
# )
## Generate added messages and services with any dependencies listed here
-# generate_messages(
-# DEPENDENCIES
-# std_msgs
-# )
+generate_messages(
+ DEPENDENCIES
+ std_msgs
+)
################################################
## Declare ROS dynamic reconfigure parameters ##
@@ -106,7 +106,7 @@ find_package(catkin REQUIRED COMPONENTS
catkin_package(
# INCLUDE_DIRS include
# LIBRARIES robot_voice
-# CATKIN_DEPENDS roscpp rospy std_msgs
+ CATKIN_DEPENDS message_runtime roscpp rospy std_msgs
# DEPENDS system_lib
)
@@ -134,11 +134,14 @@ include_directories(
## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
-add_executable(voice_controller
- src/voice_controller.cpp
+add_executable(voice_to_string
+ src/voice_to_string.cpp
ifly/speech_recognizer.c
ifly/linuxrec.c)
+add_executable(robot_controller src/robot_controller.cpp)
+
+add_executable(string_to_voice src/string_to_voice.cpp)
## Rename C++ executable without prefix
## The above recommended prefix causes long target names, the following renames the
@@ -155,10 +158,21 @@ add_executable(voice_controller
# ${catkin_LIBRARIES}
# )
-target_link_libraries(voice_controller
- ${catkin_LIBRARIES}
- libmsc.so -ldl -lpthread -lm -lrt -lasound
- )
+target_link_libraries(voice_to_string
+ ${catkin_LIBRARIES}
+ libmsc.so -ldl -lpthread -lm -lrt -lasound
+)
+
+add_dependencies(robot_controller ${PROJECT_NAME}_generate_messages_cpp)
+target_link_libraries(robot_controller
+ ${catkin_LIBRARIES}
+)
+
+add_dependencies(string_to_voice ${PROJECT_NAME}_generate_messages_cpp)
+target_link_libraries(string_to_voice
+ ${catkin_LIBRARIES}
+ libmsc.so -ldl -pthread
+)
#############
## Install ##
diff --git a/robot_voice/package.xml b/robot_voice/package.xml
index 19aa222..cd4417e 100644
--- a/robot_voice/package.xml
+++ b/robot_voice/package.xml
@@ -53,15 +53,21 @@
rospy
std_msgs
geometry_msgs
+ message_generation
+ message_runtime
roscpp
rospy
std_msgs
geometry_msgs
+ message_generation
+ message_runtime
roscpp
rospy
std_msgs
geometry_msgs
-
+ message_generation
+ message_runtime
+
diff --git a/robot_voice/src/ifly_spark.cpp b/robot_voice/src/ifly_spark.cpp
new file mode 100644
index 0000000..e69de29
diff --git a/robot_voice/src/robot_controller.cpp b/robot_voice/src/robot_controller.cpp
new file mode 100644
index 0000000..e69de29
diff --git a/robot_voice/src/string_to_voice.cpp b/robot_voice/src/string_to_voice.cpp
new file mode 100644
index 0000000..035c87f
--- /dev/null
+++ b/robot_voice/src/string_to_voice.cpp
@@ -0,0 +1,208 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include "ifly/qisr.h"
+#include "ifly/qtts.h"
+#include "ifly/msp_cmn.h"
+#include "ifly/formats.h"
+#include "ifly/msp_errors.h"
+#include "ifly/speech_recognizer.h"
+#include
+
+
+class Helper {
+public:
+ static void SignalHandler(int signal) {
+ ROS_INFO("\nCaught signal %d. Exiting gracefully...\n", signal);
+ exit(0);
+ }
+};
+
+
+class VoiceResponse {
+public:
+ VoiceResponse() {
+ ROS_INFO("voice Response Constructor");
+ }
+
+ ~VoiceResponse() {
+ ROS_INFO("voice Response Destructor ");
+ }
+
+ int ProcessTxt(std::string& txt) {
+ int ret = -1;
+ FILE* fp = NULL;
+ const char* sessionID = NULL;
+ unsigned int audio_len = 0;
+ int synth_status = MSP_TTS_FLAG_STILL_HAVE_DATA;
+ WavePcmHdr_t wav_hdr = {
+ { 'R', 'I', 'F', 'F' },
+ 0,
+ {'W', 'A', 'V', 'E'},
+ {'f', 'm', 't', ' '},
+ 16,
+ 1,
+ 1,
+ 16000,
+ 32000,
+ 2,
+ 16,
+ {'d', 'a', 't', 'a'},
+ 0
+ };
+
+ const char* src_text = txt.c_str();
+ const char* des_path = filename_.c_str();
+ const char* params = session_begin_params_.c_str();
+
+ if (NULL == src_text || NULL == des_path) {
+ ROS_ERROR("params is error!");
+ return ret;
+ }
+
+ fp = fopen(des_path, "wb");
+ if (NULL == fp) {
+ ROS_ERROR("open %s error", des_path);
+ return ret;
+ }
+
+ /* 开始合成 */
+ sessionID = QTTSSessionBegin(params, &ret);
+ if (MSP_SUCCESS != ret) {
+ ROS_ERROR("QTTSSessionBegin failed, error code: %d", ret);
+ fclose(fp);
+ return ret;
+ }
+
+ ret = QTTSTextPut(sessionID, src_text, (unsigned int)strlen(src_text), NULL);
+ if (MSP_SUCCESS != ret) {
+ ROS_ERROR("QTTSTextPut failed, error code: %d",ret);
+ QTTSSessionEnd(sessionID, "TextPutError");
+ fclose(fp);
+ return ret;
+ }
+
+ printf("正在合成 ...\n");
+ fwrite(&wav_hdr, sizeof(wav_hdr) ,1, fp); //添加wav音频头,使用采样率为16000
+ while (1) {
+ /* 获取合成音频 */
+ const void* data = QTTSAudioGet(sessionID, &audio_len, &synth_status, &ret);
+ if (MSP_SUCCESS != ret) {
+ break;
+ }
+
+ if (NULL != data) {
+ fwrite(data, audio_len, 1, fp);
+ wav_hdr.data_size += audio_len; //计算data_size大小
+ }
+
+ if (MSP_TTS_FLAG_DATA_END == synth_status) {
+ break;
+ }
+
+ printf(">");
+ usleep(150*1000); //防止频繁占用CPU
+ }
+ printf("\n");
+
+ if (MSP_SUCCESS != ret) {
+ ROS_ERROR("QTTSAudioGet failed, error code: %d",ret);
+ QTTSSessionEnd(sessionID, "AudioGetError");
+ fclose(fp);
+ return ret;
+ }
+
+ /* 修正wav文件头数据的大小 */
+ wav_hdr.size_8 += wav_hdr.data_size + (sizeof(wav_hdr) - 8);
+
+ /* 将修正过的数据写回文件头部,音频文件为wav格式 */
+ fseek(fp, 4, 0);
+ fwrite(&wav_hdr.size_8,sizeof(wav_hdr.size_8), 1, fp); //写入size_8的值
+ fseek(fp, 40, 0); //将文件指针偏移到存储data_size值的位置
+ fwrite(&wav_hdr.data_size,sizeof(wav_hdr.data_size), 1, fp); //写入data_size的值
+ fclose(fp);
+ fp = NULL;
+ /* 合成完毕 */
+ ret = QTTSSessionEnd(sessionID, "Normal");
+ if (MSP_SUCCESS != ret) {
+ ROS_ERROR("QTTSSessionEnd failed, error code: %d", ret);
+ return ret;
+ }
+
+ fp = popen(play_cmd_.c_str(),"r");
+ if (fp == NULL) {
+ ROS_ERROR("play /tmp/tts_sample.wav failed");
+ return -1;
+ }
+ pclose(fp);
+
+ return 0;
+ }
+
+ bool Speeking(robot_voice::StringToVoice::Request &req, robot_voice::StringToVoice::Response &resp) {
+ int ret = -1;
+ ret = ProcessTxt(req.data);
+ if (MSP_SUCCESS != ret) {
+ ROS_ERROR("AnswerVoice failed, error code: %d", ret);
+ resp.success = false;
+ return false;
+ } else {
+ resp.success = true;
+ }
+
+ return resp.success;
+ }
+
+ void Start(ros::NodeHandle& nh) {
+ server_ = nh.advertiseService("str2voice", &VoiceResponse::Speeking, this);
+ ROS_INFO("voice Response Start");
+ }
+
+private:
+ ros::ServiceServer server_;
+
+ const std::string session_begin_params_ =
+ "voice_name = xiaoyan, text_encoding = utf8, "
+ "sample_rate = 16000, speed = 50, volume = 50, "
+ "pitch = 50, rdn = 2";
+ const std::string filename_ = "/tmp/tts_sample.wav"; //合成的语音文件名称
+ const std::string play_cmd_ = "play /tmp/tts_sample.wav";
+
+ /* wav音频头部格式 */
+ typedef struct WavePcmHdr {
+ char riff[4]; // = "RIFF"
+ int size_8; // = FileSize - 8
+ char wave[4]; // = "WAVE"
+ char fmt[4]; // = "fmt "
+ int fmt_size; // = 下一个结构体的大小 : 16
+
+ short int format_tag; // = PCM : 1
+ short int channels; // = 通道数 : 1
+ int samples_per_sec; // = 采样率 : 8000 | 6000 | 11025 | 16000
+ int avg_bytes_per_sec; // = 每秒字节数 : samples_per_sec * bits_per_sample / 8
+ short int block_align; // = 每采样点字节数 : wBitsPerSample / 8
+ short int bits_per_sample; // = 量化比特数: 8 | 16
+
+ char data[4]; // = "data";
+ int data_size; // = 纯数据长度 : FileSize - 44
+ } WavePcmHdr_t;
+
+};
+
+int main(int argc, char ** argv) {
+ ros::init(argc, argv, "string_to_voice");
+ ros::NodeHandle nh;
+
+ if (signal(SIGINT, Helper::SignalHandler) == SIG_ERR) {
+ return -1;
+ }
+
+ VoiceResponse vr;
+ vr.Start(nh);
+
+ ros::spin();
+ return 0;
+}
diff --git a/robot_voice/src/voice_controller.cpp b/robot_voice/src/voice_controller.cpp
index c6d5b67..9563111 100644
--- a/robot_voice/src/voice_controller.cpp
+++ b/robot_voice/src/voice_controller.cpp
@@ -1,4 +1,3 @@
-#include
#include
#include
#include
diff --git a/robot_voice/src/voice_to_string.cpp b/robot_voice/src/voice_to_string.cpp
new file mode 100644
index 0000000..0705535
--- /dev/null
+++ b/robot_voice/src/voice_to_string.cpp
@@ -0,0 +1,168 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include "ifly/qisr.h"
+#include "ifly/qtts.h"
+#include "ifly/msp_cmn.h"
+#include "ifly/formats.h"
+#include "ifly/msp_errors.h"
+#include "ifly/speech_recognizer.h"
+#include
+
+class Helper {
+public:
+ static void SignalHandler(int signal) {
+ ROS_INFO("\nCaught signal %d. Exiting gracefully...\n", signal);
+ exit(0);
+ }
+};
+
+class VoiceDetector {
+public:
+ VoiceDetector() {
+ ROS_INFO("voice detector Constructor");
+ }
+ ~VoiceDetector() {
+ ROS_INFO("voice detector Destructor ");
+ }
+
+ int Init() {
+ int ret = MSP_SUCCESS;
+ ret = MSPLogin(NULL, NULL, login_params_.c_str());
+ if (MSP_SUCCESS != ret) {
+ ROS_ERROR("MSPLogin failed , Error code %d", ret);
+ MSPLogout(); // Logout...
+ return -1;
+ }
+
+ ROS_INFO("MSP Login for update, waiting for seconds...");
+
+ return 0;
+ }
+
+ static void JoinTxt(const char *result, char is_last) {
+ if (result) {
+ std::string slice_txt = result;
+
+ VoiceDetector::voice_txt_ += slice_txt;
+ }
+ if (is_last) {
+ printf("voice txt : %s\n", VoiceDetector::voice_txt_.c_str());
+ }
+ }
+
+ static void InitSpeech() {
+ VoiceDetector::voice_txt_ = "";
+
+ printf("Start Listening...\n");
+ }
+
+ static void EndSpeech(int reason) {
+ if (reason == END_REASON_VAD_DETECT) {
+ printf("\nSpeaking done \n");
+ } else {
+ printf("\nRecognizer error %d\n", reason);
+ }
+ }
+
+ int SpeechOnce() {
+ int ret;
+ int i = 0;
+
+ struct speech_rec iat;
+
+ struct speech_rec_notifier recnotifier = {
+ JoinTxt,
+ InitSpeech,
+ EndSpeech
+ };
+
+ ret = sr_init(&iat, session_begin_params_.c_str(), SR_MIC, &recnotifier);
+ if (ret) {
+ ROS_ERROR("speech recognizer init failed");
+ return -1;
+ }
+
+ ret = sr_start_listening(&iat);
+ if (ret) {
+ printf("start listen failed %d\n", ret);
+ }
+
+ /* demo 15 seconds recording */
+ sleep(15);
+
+ ret = sr_stop_listening(&iat);
+ if (ret) {
+ printf("stop listening failed %d\n", ret);
+ }
+
+ sr_uninit(&iat);
+
+ return 0;
+ }
+
+ static std::string get_voice_txt_() {
+ return voice_txt_;
+ }
+
+
+private:
+
+
+ const std::string login_params_ = "appid = bb839ccf, work_dir = .";
+ const std::string session_begin_params_ =
+ "sub = iat, domain = iat, language = zh_cn, "
+ "accent = mandarin, sample_rate = 16000, "
+ "result_type = plain, result_encoding = utf8";
+
+ const uint32_t BufferSize = 4096;
+ uint64_t g_buffersize = BufferSize;
+ static std::string voice_txt_;
+};
+
+std::string VoiceDetector::voice_txt_ = "";
+
+
+int main(int argc, char* argv[]) {
+ int ret = 0;
+ ros::init(argc, argv, "voice_to_string");
+ ros::NodeHandle nh;
+ ros::Publisher str_pub_ = nh.advertise("/human/chatter", 1000);
+
+ if (signal(SIGINT, Helper::SignalHandler) == SIG_ERR) {
+ return -1;
+ }
+
+ VoiceDetector vd;
+ ret = vd.Init();
+ if (ret < 0) {
+ return -1;
+ }
+
+ while (1) {
+ ret = vd.SpeechOnce();
+ if (ret < 0) {
+ return -1;
+ }
+
+ std::string voice_txt = VoiceDetector::get_voice_txt_();
+ if (voice_txt == "") {
+ continue;
+ } else if (voice_txt.find("结束") != std::string::npos) {
+ break;
+ }
+
+ std_msgs::String msg;
+ msg.data = voice_txt;
+ str_pub_.publish(msg);
+ }
+
+ ros::spin();
+
+ return 0;
+}
+
+
\ No newline at end of file
diff --git a/robot_voice/srv/StringToVoice.srv b/robot_voice/srv/StringToVoice.srv
new file mode 100644
index 0000000..cfec3c5
--- /dev/null
+++ b/robot_voice/srv/StringToVoice.srv
@@ -0,0 +1,3 @@
+string data
+---
+bool success
\ No newline at end of file