Skip to content

Commit 5ab7e29

Browse files
committed
Split picovoice commands into separate file
1 parent 862d521 commit 5ab7e29

File tree

5 files changed

+170
-109
lines changed

5 files changed

+170
-109
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ else()
7171
${CMAKE_SOURCE_DIR}/Core/Src/audio.c
7272
${CMAKE_SOURCE_DIR}/Core/Src/led.c
7373
${CMAKE_SOURCE_DIR}/Core/Src/speech.c
74+
${CMAKE_SOURCE_DIR}/Core/Src/pv.c
7475
${CMAKE_SOURCE_DIR}/Core/Src/touch.c
7576
${CMAKE_SOURCE_DIR}/Core/Src/touch_targets.c
7677
${CMAKE_SOURCE_DIR}/Core/Src/touch_mapper.c

Core/Inc/pv.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#ifndef PV_H
2+
#define PV_H
3+
4+
#include "picovoice.h"
5+
#include "pv_picovoice.h"
6+
7+
#include <stdint.h>
8+
9+
//! Callback function types
10+
typedef void (*pv_wake_word_callback_t)(void);
11+
typedef void (*pv_inference_callback_t)(pv_inference_t *inference);
12+
13+
//! Public functions
14+
pv_status_t PV_Init(pv_wake_word_callback_t wake_callback, pv_inference_callback_t inference_callback);
15+
void PV_Delete(void);
16+
pv_status_t PV_Process(const int16_t *audio_buffer);
17+
18+
#endif

Core/Inc/speech.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33

44
#include <stdint.h>
55
#include <stdio.h>
6+
#include "pv_picovoice.h"
67

78
uint8_t SPEECH_Init(void *memory_ptr);
89

10+
//! Public callback functions for Picovoice
11+
void SPEECH_WakeWordCallback(void);
12+
void SPEECH_InferenceCallback(pv_inference_t *inference);
13+
914
#endif

Core/Src/pv.c

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*!
2+
* @file pv.c
3+
* @brief Picovoice SDK wrapper module
4+
*/
5+
6+
#include "pv.h"
7+
8+
#include "log.h"
9+
#include "picovoice.h"
10+
#include "pv_access_key.h"
11+
#include "pv_picovoice.h"
12+
#include "pv_porcupine_params.h"
13+
#include "pv_rhino_params.h"
14+
15+
#include <stdbool.h>
16+
#include <stdint.h>
17+
#include <stdlib.h>
18+
#include <string.h>
19+
20+
//! Picovoice specific defines
21+
#define MEMORY_BUFFER_SIZE (70 * 1024)
22+
23+
#ifndef PV_ACCESS_KEY
24+
#error "ACCESS_KEY must be defined in pv_access_key.h"
25+
#endif
26+
static const char *_access_key = PV_ACCESS_KEY;
27+
28+
//! Picovoice statics
29+
static int8_t _memory_buffer[MEMORY_BUFFER_SIZE] __attribute__((aligned(16)));
30+
static pv_picovoice_t *_handle = NULL;
31+
32+
//! Picovoice settings
33+
static const float PORCUPINE_SENSITIVITY = 0.9f;
34+
static const float RHINO_SENSITIVITY = 0.9f;
35+
static const float RHINO_ENDPOINT_DURATION_SEC = 1.0f;
36+
static const bool RHINO_REQUIRE_ENDPOINT = true;
37+
38+
/**
39+
* @brief Prints Picovoice error messages from error stack.
40+
*
41+
* @param message_stack Array of error message strings
42+
* @param message_stack_depth Number of messages in the stack
43+
*/
44+
static void printErrorMessage(char **message_stack, int32_t message_stack_depth)
45+
{
46+
for (int32_t i = 0; i < message_stack_depth; i++)
47+
{
48+
log_error("[%ld] %s", i, message_stack[i]);
49+
}
50+
}
51+
52+
/**
53+
* @brief Initializes the Picovoice engine.
54+
*
55+
* @param wake_callback Callback function for wake word detection
56+
* @param inference_callback Callback function for speech inference
57+
* @return EXIT_SUCCESS on success, EXIT_FAILURE on error
58+
*/
59+
pv_status_t PV_Init(pv_wake_word_callback_t wake_callback, pv_inference_callback_t inference_callback)
60+
{
61+
char **message_stack = NULL;
62+
int32_t message_stack_depth = 0;
63+
pv_status_t error_status;
64+
65+
pv_status_t status = pv_picovoice_init(_access_key, // access key
66+
MEMORY_BUFFER_SIZE, // memory size
67+
_memory_buffer, // memory buffer
68+
sizeof(KEYWORD_ARRAY), // keyword model size
69+
KEYWORD_ARRAY, // keyword model
70+
PORCUPINE_SENSITIVITY, // wake word sensitivity
71+
wake_callback, // wake word callback
72+
sizeof(CONTEXT_ARRAY), // context model size
73+
CONTEXT_ARRAY, // context model
74+
RHINO_SENSITIVITY, // inference sensitivity
75+
RHINO_ENDPOINT_DURATION_SEC, // endpoint duration
76+
RHINO_REQUIRE_ENDPOINT, // require endpoint
77+
inference_callback, // inference callback
78+
&_handle // handle
79+
);
80+
if (status != PV_STATUS_SUCCESS)
81+
{
82+
log_fatal("Picovoice init failed: %s", pv_status_to_string(status));
83+
84+
error_status = pv_get_error_stack(&message_stack, &message_stack_depth);
85+
if (error_status != PV_STATUS_SUCCESS)
86+
{
87+
log_fatal("Failed to get error stack: %s", pv_status_to_string(error_status));
88+
return error_status;
89+
}
90+
91+
printErrorMessage(message_stack, message_stack_depth);
92+
pv_free_error_stack(message_stack);
93+
return status;
94+
}
95+
96+
const char *rhino_context = NULL;
97+
status = pv_picovoice_context_info(_handle, &rhino_context);
98+
if (status != PV_STATUS_SUCCESS)
99+
{
100+
log_error("retrieving context info failed with '%s'", pv_status_to_string(status));
101+
return status;
102+
}
103+
104+
return status;
105+
}
106+
107+
/**
108+
* @brief Deletes the Picovoice engine instance.
109+
*/
110+
void PV_Delete(void)
111+
{
112+
if (_handle == NULL)
113+
{
114+
log_error("Handle == NULL, failed to delete Picovoice instance");
115+
}
116+
else
117+
{
118+
pv_picovoice_delete(_handle);
119+
_handle = NULL;
120+
}
121+
}
122+
123+
/**
124+
* @brief Processes audio buffer through Picovoice.
125+
*
126+
* @param audio_buffer Pointer to audio samples
127+
* @return pv_status_t status of the operation
128+
*/
129+
pv_status_t PV_Process(const int16_t *audio_buffer)
130+
{
131+
return pv_picovoice_process(_handle, audio_buffer);
132+
}

Core/Src/speech.c

Lines changed: 14 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,8 @@
88
#include "audio.h"
99
#include "led.h"
1010
#include "log.h"
11-
#include "pv_access_key.h"
12-
#include "pv_picovoice.h"
13-
#include "pv_porcupine_params.h"
14-
#include "pv_rhino_params.h"
11+
#include "picovoice.h"
12+
#include "pv.h"
1513
#include "stm32wbxx_hal.h"
1614
#include "touch_mapper.h"
1715
#include "touch_targets.h"
@@ -22,27 +20,11 @@
2220
#include <stdlib.h>
2321
#include <string.h>
2422

25-
//! Picovoice specific defines
26-
#define MEMORY_BUFFER_SIZE (70 * 1024)
2723
#define TIMEOUT_PERIOD_TICKS (8 * TX_TIMER_TICKS_PER_SECOND) // 8 seconds
2824

29-
#ifndef PV_ACCESS_KEY
30-
#error "ACCESS_KEY must be defined in pv_access_key.h"
31-
#endif
32-
static const char *_access_key = PV_ACCESS_KEY;
33-
34-
//! Picovoice statics
35-
static int8_t _memory_buffer[MEMORY_BUFFER_SIZE] __attribute__((aligned(16)));
36-
static pv_picovoice_t *_handle = NULL;
3725
//! Temporary storage of speech samples
3826
static int16_t _speech_buffer[512];
3927

40-
//! Picovoice settings
41-
static const float PORCUPINE_SENSITIVITY = 0.9f;
42-
static const float RHINO_SENSITIVITY = 0.9f;
43-
static const float RHINO_ENDPOINT_DURATION_SEC = 1.0f;
44-
static const bool RHINO_REQUIRE_ENDPOINT = true;
45-
4628
//! Azure RTOS settings
4729
#define SPEECH_THREAD_STACK_SIZE (1024)
4830
#define SPEECH_THREAD_PRIO (1)
@@ -65,7 +47,7 @@ static void SPEECH_Reset(void);
6547
*
6648
* Sets LED to listening state, resets touch mapper, and starts timeout timer.
6749
*/
68-
static void wakeWordCallback(void)
50+
void SPEECH_WakeWordCallback(void)
6951
{
7052
log_info("[wake word]\n");
7153
LED_SetState(LED_STATE_LISTENING);
@@ -87,7 +69,7 @@ static void wakeWordCallback(void)
8769
*
8870
* @param target_str The beverage name string from speech recognition
8971
* @return TARGET_T enum value or TARGET_COUNT if not found
90-
* @note Called by inferenceCallback when processing beverage orders
72+
* @note Called by SPEECH_InferenceCallback when processing beverage orders
9173
*/
9274
static TARGET_T getTargetFromString(const char *target_str)
9375
{
@@ -122,7 +104,7 @@ static TARGET_T getTargetFromString(const char *target_str)
122104
* Processes beverage orders and cancel commands, maps them to touch targets.
123105
* Updates LED state and forwards valid targets to touch mapper.
124106
*/
125-
static void inferenceCallback(pv_inference_t *inference)
107+
void SPEECH_InferenceCallback(pv_inference_t *inference)
126108
{
127109
static const char *beverage_slot = "beverage";
128110
static uint8_t beverage_slot_len = 8; // Length of "beverage"
@@ -136,7 +118,6 @@ static void inferenceCallback(pv_inference_t *inference)
136118
log_error("Failed to deactivate SPEECH timeout timer: %d", status);
137119
}
138120

139-
// LED_SetState(LED_3, 0);
140121
if (inference->is_understood)
141122
{
142123
log_info("Command understood");
@@ -178,73 +159,6 @@ static void inferenceCallback(pv_inference_t *inference)
178159
pv_inference_delete(inference);
179160
}
180161

181-
/**
182-
* @brief Prints Picovoice error messages from error stack.
183-
*
184-
* @param message_stack Array of error message strings
185-
* @param message_stack_depth Number of messages in the stack
186-
*/
187-
static void printErrorMessage(char **message_stack, int32_t message_stack_depth)
188-
{
189-
for (int32_t i = 0; i < message_stack_depth; i++)
190-
{
191-
log_error("[%ld] %s", i, message_stack[i]);
192-
}
193-
}
194-
195-
/**
196-
* @brief Initializes the Picovoice engine.
197-
*
198-
* @return EXIT_SUCCESS on success, EXIT_FAILURE on error
199-
*/
200-
static uint8_t initPicovoice(void)
201-
{
202-
char **message_stack = NULL;
203-
int32_t message_stack_depth = 0;
204-
pv_status_t error_status;
205-
206-
pv_status_t status = pv_picovoice_init(_access_key, // access key
207-
MEMORY_BUFFER_SIZE, // memory size
208-
_memory_buffer, // memory buffer
209-
sizeof(KEYWORD_ARRAY), // keyword model size
210-
KEYWORD_ARRAY, // keyword model
211-
PORCUPINE_SENSITIVITY, // wake word sensitivity
212-
wakeWordCallback, // wake word callback
213-
sizeof(CONTEXT_ARRAY), // context model size
214-
CONTEXT_ARRAY, // context model
215-
RHINO_SENSITIVITY, // inference sensitivity
216-
RHINO_ENDPOINT_DURATION_SEC, // endpoint duration
217-
RHINO_REQUIRE_ENDPOINT, // require endpoint
218-
inferenceCallback, // inference callback
219-
&_handle // handle
220-
);
221-
if (status != PV_STATUS_SUCCESS)
222-
{
223-
log_fatal("Picovoice init failed: %s", pv_status_to_string(status));
224-
225-
error_status = pv_get_error_stack(&message_stack, &message_stack_depth);
226-
if (error_status != PV_STATUS_SUCCESS)
227-
{
228-
log_fatal("Failed to get error stack: %s", pv_status_to_string(error_status));
229-
return EXIT_FAILURE;
230-
}
231-
232-
printErrorMessage(message_stack, message_stack_depth);
233-
pv_free_error_stack(message_stack);
234-
return EXIT_FAILURE;
235-
}
236-
237-
const char *rhino_context = NULL;
238-
status = pv_picovoice_context_info(_handle, &rhino_context);
239-
if (status != PV_STATUS_SUCCESS)
240-
{
241-
log_error("retrieving context info failed with '%s'", pv_status_to_string(status));
242-
return EXIT_FAILURE;
243-
}
244-
245-
return EXIT_SUCCESS;
246-
}
247-
248162
/**
249163
* @brief Initializes the speech processing module.
250164
*
@@ -253,17 +167,16 @@ static uint8_t initPicovoice(void)
253167
*/
254168
uint8_t SPEECH_Init(void *memory_ptr)
255169
{
256-
257170
// Initialize Picovoice
258-
uint8_t ret = initPicovoice();
259-
if (ret != EXIT_SUCCESS)
171+
pv_status_t pv_status = PV_Init(SPEECH_WakeWordCallback, SPEECH_InferenceCallback);
172+
if (pv_status != PV_STATUS_SUCCESS)
260173
{
261174
log_fatal("Failed to initialize Picovoice");
262175
return EXIT_FAILURE;
263176
}
264177

265178
// Setup AUDIO
266-
ret = AUDIO_Init(memory_ptr);
179+
uint8_t ret = AUDIO_Init(memory_ptr);
267180
if (ret != EXIT_SUCCESS)
268181
{
269182
log_fatal("AUDIO_Init failed");
@@ -347,6 +260,7 @@ static void SPEECH_Process(ULONG thread_input)
347260
(void)thread_input;
348261
int16_t *buffer;
349262
uint8_t status;
263+
pv_status_t pv_status;
350264

351265
log_info("AUDIO starting...");
352266
AUDIO_Start();
@@ -368,11 +282,10 @@ static void SPEECH_Process(ULONG thread_input)
368282
#ifdef AUDIO_OVER_USART
369283
// HAL_UART_Transmit(&huart1, (uint8_t *)speech_buffer, 512 * sizeof(int16_t), HAL_MAX_DELAY);
370284
#else
371-
372-
const pv_status_t status = pv_picovoice_process(_handle, _speech_buffer);
373-
if (status != PV_STATUS_SUCCESS)
285+
pv_status = PV_Process(_speech_buffer);
286+
if (pv_status != PV_STATUS_SUCCESS)
374287
{
375-
log_error("Picovoice process failed: %s", pv_status_to_string(status));
288+
log_error("Picovoice process failed");
376289
}
377290
#endif
378291
}
@@ -394,17 +307,9 @@ static void SPEECH_Reset(void)
394307
AUDIO_Stop();
395308

396309
// Delete Picovoice instance
397-
if (_handle == NULL)
398-
{
399-
log_error("Handle == NULL, failed to delete Picovoice instance");
400-
}
401-
else
402-
{
403-
pv_picovoice_delete(_handle);
404-
_handle = NULL;
405-
}
310+
PV_Delete();
406311

407-
if (initPicovoice() != EXIT_SUCCESS)
312+
if (PV_Init(SPEECH_WakeWordCallback, SPEECH_InferenceCallback) != PV_STATUS_SUCCESS)
408313
{
409314
log_fatal("Failed to re-initialize Picovoice");
410315
return;

0 commit comments

Comments
 (0)