demo.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. // example/C/demo.c
  2. // Copyright 2017 KITT.AI (author: Guoguo Chen)
  3. #include <assert.h>
  4. #include <pa_ringbuffer.h>
  5. #include <pa_util.h>
  6. #include <portaudio.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <signal.h>
  10. #include "snowboy-detect-c-wrapper.h"
  11. // Pointer to the ring buffer memory.
  12. char* g_ringbuffer;
  13. // Ring buffer wrapper used in PortAudio.
  14. PaUtilRingBuffer g_pa_ringbuffer;
  15. // Pointer to PortAudio stream.
  16. PaStream* g_pa_stream;
  17. // Number of lost samples at each LoadAudioData() due to ring buffer overflow.
  18. int g_num_lost_samples;
  19. // Wait for this number of samples in each LoadAudioData() call.
  20. int g_min_read_samples;
  21. // Pointer to the audio data.
  22. int16_t* g_data;
  23. int PortAudioCallback(const void* input,
  24. void* output,
  25. unsigned long frame_count,
  26. const PaStreamCallbackTimeInfo* time_info,
  27. PaStreamCallbackFlags status_flags,
  28. void* user_data) {
  29. ring_buffer_size_t num_written_samples =
  30. PaUtil_WriteRingBuffer(&g_pa_ringbuffer, input, frame_count);
  31. g_num_lost_samples += frame_count - num_written_samples;
  32. return paContinue;
  33. }
  34. void StartAudioCapturing(int sample_rate,
  35. int num_channels, int bits_per_sample) {
  36. g_data = NULL;
  37. g_num_lost_samples = 0;
  38. g_min_read_samples = sample_rate * 0.1;
  39. // Allocates ring buffer memory.
  40. int ringbuffer_size = 16384;
  41. g_ringbuffer = (char*)(
  42. PaUtil_AllocateMemory(bits_per_sample / 8 * ringbuffer_size));
  43. if (g_ringbuffer == NULL) {
  44. fprintf(stderr, "Fail to allocate memory for ring buffer.\n");
  45. exit(1);
  46. }
  47. // Initializes PortAudio ring buffer.
  48. ring_buffer_size_t rb_init_ans =
  49. PaUtil_InitializeRingBuffer(&g_pa_ringbuffer, bits_per_sample / 8,
  50. ringbuffer_size, g_ringbuffer);
  51. if (rb_init_ans == -1) {
  52. fprintf(stderr, "Ring buffer size is not power of 2.\n");
  53. exit(1);
  54. }
  55. // Initializes PortAudio.
  56. PaError pa_init_ans = Pa_Initialize();
  57. if (pa_init_ans != paNoError) {
  58. fprintf(stderr, "Fail to initialize PortAudio, error message is %s.\n",
  59. Pa_GetErrorText(pa_init_ans));
  60. exit(1);
  61. }
  62. PaError pa_open_ans;
  63. if (bits_per_sample == 8) {
  64. pa_open_ans = Pa_OpenDefaultStream(
  65. &g_pa_stream, num_channels, 0, paUInt8, sample_rate,
  66. paFramesPerBufferUnspecified, PortAudioCallback, NULL);
  67. } else if (bits_per_sample == 16) {
  68. pa_open_ans = Pa_OpenDefaultStream(
  69. &g_pa_stream, num_channels, 0, paInt16, sample_rate,
  70. paFramesPerBufferUnspecified, PortAudioCallback, NULL);
  71. } else if (bits_per_sample == 32) {
  72. pa_open_ans = Pa_OpenDefaultStream(
  73. &g_pa_stream, num_channels, 0, paInt32, sample_rate,
  74. paFramesPerBufferUnspecified, PortAudioCallback, NULL);
  75. } else {
  76. fprintf(stderr, "Unsupported BitsPerSample: %d.\n", bits_per_sample);
  77. exit(1);
  78. }
  79. if (pa_open_ans != paNoError) {
  80. fprintf(stderr, "Fail to open PortAudio stream, error message is %s.\n",
  81. Pa_GetErrorText(pa_open_ans));
  82. exit(1);
  83. }
  84. PaError pa_stream_start_ans = Pa_StartStream(g_pa_stream);
  85. if (pa_stream_start_ans != paNoError) {
  86. fprintf(stderr, "Fail to start PortAudio stream, error message is %s.\n",
  87. Pa_GetErrorText(pa_stream_start_ans));
  88. exit(1);
  89. }
  90. }
  91. void StopAudioCapturing() {
  92. if (g_data != NULL) {
  93. free(g_data);
  94. g_data = NULL;
  95. }
  96. Pa_StopStream(g_pa_stream);
  97. Pa_CloseStream(g_pa_stream);
  98. Pa_Terminate();
  99. PaUtil_FreeMemory(g_ringbuffer);
  100. }
  101. int LoadAudioData() {
  102. if (g_data != NULL) {
  103. free(g_data);
  104. g_data = NULL;
  105. }
  106. // Checks ring buffer overflow.
  107. if (g_num_lost_samples > 0) {
  108. fprintf(stderr, "Lost %d samples due to ring buffer overflow.\n",
  109. g_num_lost_samples);
  110. g_num_lost_samples = 0;
  111. }
  112. ring_buffer_size_t num_available_samples = 0;
  113. while (true) {
  114. num_available_samples =
  115. PaUtil_GetRingBufferReadAvailable(&g_pa_ringbuffer);
  116. if (num_available_samples >= g_min_read_samples) {
  117. break;
  118. }
  119. Pa_Sleep(5);
  120. }
  121. // Reads data.
  122. num_available_samples = PaUtil_GetRingBufferReadAvailable(&g_pa_ringbuffer);
  123. g_data = malloc(num_available_samples * sizeof(int16_t));
  124. ring_buffer_size_t num_read_samples = PaUtil_ReadRingBuffer(
  125. &g_pa_ringbuffer, g_data, num_available_samples);
  126. if (num_read_samples != num_available_samples) {
  127. fprintf(stderr, "%d samples were available, but only %d samples were read"
  128. ".\n", num_available_samples, num_read_samples);
  129. }
  130. return num_read_samples;
  131. }
  132. void SignalHandler(int signal) {
  133. fprintf(stderr, "Caught signal %d, terminating...\n", signal);
  134. exit(0);
  135. }
  136. int main(int argc, char* argv[]) {
  137. const char usage[] =
  138. "Example that shows how to use Snowboy in pure C. Snowboy was written\n"
  139. "in C++, so we have to write a wrapper in order to use Snowboy in pure\n"
  140. "C. See snowboy-detect-c-wrapper.h and snowboy-detect-c-wrapper.cc for\n"
  141. "more details.\n"
  142. "\n"
  143. "Parameters are hard-coded in the parameter section for this example.\n"
  144. "Please check the source code for more details.\n"
  145. "\n"
  146. "Audio is captured by PortAudio, feel free to replace PortAudio with\n"
  147. "your own audio capturing tool.\n"
  148. "\n"
  149. "To run the example:\n"
  150. " ./demo\n";
  151. // Checks the command.
  152. if (argc > 1) {
  153. printf("%s", usage);
  154. exit(1);
  155. }
  156. // Configures signal handling.
  157. struct sigaction sig_int_handler;
  158. sig_int_handler.sa_handler = SignalHandler;
  159. sigemptyset(&sig_int_handler.sa_mask);
  160. sig_int_handler.sa_flags = 0;
  161. sigaction(SIGINT, &sig_int_handler, NULL);
  162. // Parameter section.
  163. // If you have multiple hotword models (e.g., 2), you should set
  164. // <model_filename> and <sensitivity_str> as follows:
  165. // model_filename =
  166. // "resources/models/snowboy.umdl,resources/models/smart_mirror.umdl";
  167. // sensitivity_str = "0.5,0.5";
  168. const char resource_filename[] = "resources/common.res";
  169. const char model_filename[] = "resources/models/snowboy.umdl";
  170. const char sensitivity_str[] = "0.5";
  171. float audio_gain = 1;
  172. bool apply_frontend = false;
  173. // Initializes Snowboy detector.
  174. SnowboyDetect* detector = SnowboyDetectConstructor(resource_filename,
  175. model_filename);
  176. SnowboyDetectSetSensitivity(detector, sensitivity_str);
  177. SnowboyDetectSetAudioGain(detector, audio_gain);
  178. SnowboyDetectApplyFrontend(detector, apply_frontend);
  179. // Initializes PortAudio. You may use other tools to capture the audio.
  180. StartAudioCapturing(SnowboyDetectSampleRate(detector),
  181. SnowboyDetectNumChannels(detector),
  182. SnowboyDetectBitsPerSample(detector));
  183. // Runs the detection.
  184. printf("Listening... Press Ctrl+C to exit\n");
  185. while (true) {
  186. int array_length = LoadAudioData();
  187. if (array_length != 0) {
  188. int result = SnowboyDetectRunDetection(detector,
  189. g_data, array_length, false);
  190. if (result > 0) {
  191. printf("Hotword %d detected!\n", result);
  192. }
  193. }
  194. }
  195. StopAudioCapturing();
  196. SnowboyDetectDestructor(detector);
  197. return 0;
  198. }