snowboy_ros
hotword_detector_node.cpp
Go to the documentation of this file.
2 
3 #include <ros/node_handle.h>
4 #include <ros/debug.h>
5 #include <audio_common_msgs/AudioData.h>
6 #include <std_msgs/String.h>
7 
8 #include <dynamic_reconfigure/server.h>
9 #include <snowboy_ros/SnowboyReconfigureConfig.h>
10 
11 #include <boost/filesystem.hpp>
12 
13 namespace snowboy_ros
14 {
15 
20 {
21 public:
23  nh_(""),
24  nh_p_("~")
25  {
26  }
27 
28  bool initialize()
29  {
30  audio_sub_ = nh_.subscribe("microphone", 1000, &HotwordDetectorNode::audioCallback, this);
31  hotword_pub_ = nh_.advertise<std_msgs::String>("hotword_detection", 10);
32 
33  std::string resource_filename;
34  if (!nh_p_.getParam("resource_filename", resource_filename))
35  {
36  ROS_ERROR("Mandatory parameter 'resource_filename' not present on the parameter server");
37  return false;
38  }
39 
40  if ( !boost::filesystem::exists( resource_filename ) )
41  {
42  ROS_ERROR("Resource '%s' does not exist", resource_filename.c_str());
43  return false;
44  }
45 
46  std::string resource_extension = boost::filesystem::extension(resource_filename);
47  if ( resource_extension != ".res" )
48  {
49  ROS_ERROR("'%s' not a valid Snowboy resource extension ('.res').", resource_filename.c_str());
50  return false;
51  }
52 
53  std::string model_filename;
54  if (!nh_p_.getParam("model_filename", model_filename))
55  {
56  ROS_ERROR("Mandatory parameter 'model_filename' not present on the parameter server");
57  return false;
58  }
59 
60  if ( !boost::filesystem::exists( model_filename ) )
61  {
62  ROS_ERROR("Model '%s' does not exist", model_filename.c_str());
63  return false;
64  }
65 
66  std::string model_extension = boost::filesystem::extension(model_filename);
67  if ( model_extension != ".pmdl" && model_extension != ".umdl" )
68  {
69  ROS_ERROR("Model '%s', not a valid Snowboy model extension ('.pmdl', '.umdl').", resource_filename.c_str());
70  return false;
71  }
72 
73  hotword_string_ = nh_p_.param("hotword_string", std::string("hotword_detection"));
74 
75  detector_.initialize(resource_filename.c_str(), model_filename.c_str());
76 
77  dynamic_reconfigure_server_.setCallback(boost::bind(&HotwordDetectorNode::reconfigureCallback, this, _1, _2));
78 
79  return true;
80  }
81 
82 private:
83 
87  ros::NodeHandle nh_;
88 
92  ros::NodeHandle nh_p_;
93 
97  ros::Subscriber audio_sub_;
98 
102  ros::Publisher hotword_pub_;
103 
107  dynamic_reconfigure::Server<SnowboyReconfigureConfig> dynamic_reconfigure_server_;
108 
113 
118 
123  void reconfigureCallback(SnowboyReconfigureConfig cfg, uint32_t /*level*/)
124  {
125  detector_.configure(cfg.sensitivity, cfg.audio_gain);
126  ROS_INFO("SnowboyROS (Re)Configured");
127  }
128 
133  void audioCallback(const audio_common_msgs::AudioDataConstPtr& msg)
134  {
135  if (msg->data.size() != 0)
136  {
137  /* Sound signal is encoded with bit depth 16 and cut up in a byte array. RunDetection wants it as an array of
138  * int16_t. Therefore, we bit shift the second (MSB) byte of a sample by 8 and cast it to an int16_t and add the
139  * first (LSB) byte of the sample to the result (also after typecast).
140  */
141 
142  if ( msg->data.size() % 2 )
143  {
144  ROS_ERROR("Not an even number of bytes in this message!");
145  }
146 
147  int16_t sample_array[msg->data.size()/2];
148  for ( size_t i = 0; i < msg->data.size(); i+=2 )
149  {
150  sample_array[i/2] = ((int16_t) (msg->data[i+1]) << 8) + (int16_t) (msg->data[i]);
151  }
152 
153  int result = detector_.runDetection( &sample_array[0], msg->data.size()/2);
154  if (result > 0)
155  {
156  ROS_DEBUG("Hotword detected!");
157 
158  std_msgs::String hotword_msg;
159  hotword_msg.data = hotword_string_;
160  hotword_pub_.publish(hotword_msg);
161  }
162  else if (result == -3)
163  {
164  ROS_ERROR("Hotword detector not initialized");
165  }
166  else if (result == -1)
167  {
168  ROS_ERROR("Snowboy error");
169  }
170  }
171  }
172 };
173 
174 }
175 
176 int main(int argc, char** argv)
177 {
178  ros::init(argc, argv, "snowboy_node");
179 
180  snowboy_ros::HotwordDetectorNode ros_hotword_detector_node;
181 
182  if (ros_hotword_detector_node.initialize())
183  {
184  ros::spin();
185  }
186  else
187  {
188  ROS_ERROR("Failed to initialize snowboy_node");
189  return 1;
190  }
191 
192  return 0;
193 }
194 
snowboy_ros::HotwordDetectorNode::detector_
HotwordDetector detector_
detector_ C++ 11 Wrapped Snowboy detect
Definition: hotword_detector_node.cpp:117
snowboy_ros::HotwordDetectorNode::hotword_string_
std::string hotword_string_
hotword_string_ String to be published when hotword is detected
Definition: hotword_detector_node.cpp:112
snowboy_ros::HotwordDetectorNode::initialize
bool initialize()
Definition: hotword_detector_node.cpp:28
snowboy_ros::HotwordDetector
The HotwordDetector class wraps Snowboy detect so we can use C++ 11.
Definition: hotword_detector.h:12
snowboy_ros::HotwordDetectorNode::audio_sub_
ros::Subscriber audio_sub_
audio_sub_ Subscriber to incoming audio feed
Definition: hotword_detector_node.cpp:97
std::string
snowboy_ros::HotwordDetectorNode::nh_p_
ros::NodeHandle nh_p_
nh_p_ Local nodehandle for parameters
Definition: hotword_detector_node.cpp:92
snowboy_ros::HotwordDetector::configure
bool configure(double sensitivity, double audio_gain)
configure Configure the detector on runtime
Definition: hotword_detector.cpp:29
snowboy_ros::HotwordDetectorNode::reconfigureCallback
void reconfigureCallback(SnowboyReconfigureConfig cfg, uint32_t)
reconfigureCallback Reconfigure update for sensitiviy and audio level
Definition: hotword_detector_node.cpp:123
snowboy_ros::HotwordDetector::initialize
void initialize(const char *resource_filename, const char *model_filename)
initialize Initializes the Snowbody
Definition: hotword_detector.cpp:14
snowboy_ros::HotwordDetectorNode::audioCallback
void audioCallback(const audio_common_msgs::AudioDataConstPtr &msg)
audioCallback Audio stream callback
Definition: hotword_detector_node.cpp:133
snowboy_ros
Definition: hotword_detector.h:6
snowboy_ros::HotwordDetectorNode::nh_
ros::NodeHandle nh_
nh_ Global nodehandle for topics
Definition: hotword_detector_node.cpp:87
snowboy_ros::HotwordDetectorNode
The HotwordDetectorNode class Wraps the C++ 11 Snowboy detector in a ROS node.
Definition: hotword_detector_node.cpp:19
main
int main(int argc, char **argv)
Definition: hotword_detector_node.cpp:176
snowboy_ros::HotwordDetectorNode::HotwordDetectorNode
HotwordDetectorNode()
Definition: hotword_detector_node.cpp:22
std::string::c_str
T c_str(T... args)
hotword_detector.h
snowboy_ros::HotwordDetector::runDetection
int runDetection(const int16_t *const data, const int array_length)
runDetection Runs hotword detection of Snowboy, see Snowboy API for more docs
Definition: hotword_detector.cpp:53
snowboy_ros::HotwordDetectorNode::dynamic_reconfigure_server_
dynamic_reconfigure::Server< SnowboyReconfigureConfig > dynamic_reconfigure_server_
dynamic_reconfigure_server_ In order to online tune the sensitivity and audio gain
Definition: hotword_detector_node.cpp:107
snowboy_ros::HotwordDetectorNode::hotword_pub_
ros::Publisher hotword_pub_
hotword_pub_ hotword publisher
Definition: hotword_detector_node.cpp:102