1 /* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 // Apollo3 EVB specific features compile options:
17 // USE AM_BSP_NUM_LEDS : LED initialization and management per EVB target (# of
18 // LEDs defined in EVB BSP) USE_TIME_STAMP : Enable timers and time stamping for
19 // debug and performance profiling (customize per application) USE_DEBUG_GPIO :
20 // Enable GPIO flag polling for debug and performance profiling (customize per
21 // application) USE_MAYA : Enable specific pin configuration and features for
22 // AP3B "quarter" sized board
23 
24 #include "tensorflow/lite/micro/examples/micro_speech/audio_provider.h"
25 
26 #include <limits>
27 
28 // These are headers from Ambiq's Apollo3 SDK.
29 #include "am_bsp.h"         // NOLINT
30 #include "am_mcu_apollo.h"  // NOLINT
31 #include "am_util.h"        // NOLINT
32 #include "tensorflow/lite/micro/examples/micro_speech/micro_features/micro_model_settings.h"
33 
34 namespace {
35 
36 // These are the raw buffers that are filled by the PDM during DMA
37 constexpr int kPdmNumSlots = 1;
38 constexpr int kPdmSamplesPerSlot = 256;
39 constexpr int kPdmSampleBufferSize = (kPdmNumSlots * kPdmSamplesPerSlot);
40 uint32_t g_ui32PDMSampleBuffer0[kPdmSampleBufferSize];
41 uint32_t g_ui32PDMSampleBuffer1[kPdmSampleBufferSize];
42 uint32_t g_PowerOff = 0;
43 
44 // Controls the double buffering between the two DMA buffers.
45 int g_dma_destination_index = 0;
46 // PDM Device Handle.
47 static void* g_pdm_handle;
48 // PDM DMA error flag.
49 volatile bool g_pdm_dma_error;
50 // So the interrupt can use the passed-in error handler to report issues.
51 tflite::ErrorReporter* g_pdm_dma_error_reporter = nullptr;
52 
53 // Holds a longer history of audio samples in a ring buffer.
54 constexpr int kAudioCaptureBufferSize = 16000;
55 int16_t g_audio_capture_buffer[kAudioCaptureBufferSize] = {};
56 int g_audio_capture_buffer_start = 0;
57 int64_t g_total_samples_captured = 0;
58 int32_t g_latest_audio_timestamp = 0;
59 
60 // Copy of audio samples returned to the caller.
61 int16_t g_audio_output_buffer[kMaxAudioSampleSize];
62 bool g_is_audio_initialized = false;
63 
64 //*****************************************************************************
65 //
66 // Globals
67 //
68 //*****************************************************************************
69 #if USE_TIME_STAMP
70 // Select the CTIMER number to use for timing.
71 // The entire 32-bit timer is used.
72 #define SELFTEST_TIMERNUM 0
73 
74 // Timer configuration.
75 static am_hal_ctimer_config_t g_sContTimer = {
76     // Create 32-bit timer
77     1,
78 
79     // Set up TimerA.
80     (AM_HAL_CTIMER_FN_CONTINUOUS | AM_HAL_CTIMER_HFRC_12MHZ),
81 
82     // Set up Timer0B.
83     0};
84 
85 #endif  // USE_TIME_STAMP
86 
87 // ARPIT TODO : Implement low power configuration
custom_am_bsp_low_power_init(void)88 void custom_am_bsp_low_power_init(void) {
89 #if USE_MAYA
90   // Make sure SWO/ITM/TPIU is disabled.
91   // SBL may not get it completely shut down.
92   am_bsp_itm_printf_disable();
93 #else
94   // Initialize the printf interface for AP3B ITM/SWO output.
95   am_bsp_itm_printf_enable();
96 #endif
97 
98   // Initialize for low power in the power control block
99   // am_hal_pwrctrl_low_power_init();
100 
101   // Run the RTC off the LFRC.
102   // am_hal_rtc_osc_select(AM_HAL_RTC_OSC_LFRC);
103 
104   // Stop the XTAL.
105   // am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_XTAL_STOP, 0);
106 
107   // Disable the RTC.
108   // am_hal_rtc_osc_disable();
109 
110 #ifdef AM_BSP_NUM_LEDS
111   //
112   // Initialize the LEDs.
113   // On the apollo3_evb, when the GPIO outputs are disabled (the default at
114   // power up), the FET gates are floating and
115   // partially illuminating the LEDs.
116   //
117   uint32_t ux, ui32GPIONumber;
118   for (ux = 0; ux < AM_BSP_NUM_LEDS; ux++) {
119     ui32GPIONumber = am_bsp_psLEDs[ux].ui32GPIONumber;
120 
121     //
122     // Configure the pin as a push-pull GPIO output
123     // (aka AM_DEVICES_LED_POL_DIRECT_DRIVE_M).
124     //
125     am_hal_gpio_pinconfig(ui32GPIONumber, g_AM_HAL_GPIO_OUTPUT);
126 
127     //
128     // Turn off the LED.
129     //
130     am_hal_gpio_state_write(ui32GPIONumber,
131                             AM_HAL_GPIO_OUTPUT_TRISTATE_DISABLE);
132     am_hal_gpio_state_write(ui32GPIONumber, AM_HAL_GPIO_OUTPUT_CLEAR);
133   }
134 #endif  // AM_BSP_NUM_LEDS
135 
136 }  // am_bsp_low_power_init()
137 
138 // Make sure the CPU is running as fast as possible.
enable_burst_mode(tflite::ErrorReporter * error_reporter)139 void enable_burst_mode(tflite::ErrorReporter* error_reporter) {
140   am_hal_burst_avail_e eBurstModeAvailable;
141   am_hal_burst_mode_e eBurstMode;
142 
143   // Check that the Burst Feature is available.
144   if (AM_HAL_STATUS_SUCCESS ==
145       am_hal_burst_mode_initialize(&eBurstModeAvailable)) {
146     if (AM_HAL_BURST_AVAIL == eBurstModeAvailable) {
147       TF_LITE_REPORT_ERROR(error_reporter, "Apollo3 Burst Mode is Available\n");
148     } else {
149       TF_LITE_REPORT_ERROR(error_reporter,
150                            "Apollo3 Burst Mode is Not Available\n");
151     }
152   } else {
153     TF_LITE_REPORT_ERROR(error_reporter,
154                          "Failed to Initialize for Burst Mode operation\n");
155   }
156 
157   // Put the MCU into "Burst" mode.
158   if (AM_HAL_STATUS_SUCCESS == am_hal_burst_mode_enable(&eBurstMode)) {
159     if (AM_HAL_BURST_MODE == eBurstMode) {
160       TF_LITE_REPORT_ERROR(error_reporter,
161                            "Apollo3 operating in Burst Mode (96MHz)\n");
162     }
163   } else {
164     TF_LITE_REPORT_ERROR(error_reporter,
165                          "Failed to Enable Burst Mode operation\n");
166   }
167 }
168 
169 }  // namespace
170 
171 //*****************************************************************************
172 // PDM configuration information.
173 //*****************************************************************************
174 am_hal_pdm_config_t g_sPdmConfig = {
175     .eClkDivider = AM_HAL_PDM_MCLKDIV_1,
176     .eLeftGain = AM_HAL_PDM_GAIN_P165DB,
177     .eRightGain = AM_HAL_PDM_GAIN_P165DB,
178     .ui32DecimationRate =
179         48,  // OSR = 1500/16 = 96 = 2*SINCRATE --> SINC_RATE = 48
180     .bHighPassEnable = 1,
181     .ui32HighPassCutoff = 0x2,
182     .ePDMClkSpeed = AM_HAL_PDM_CLK_1_5MHZ,
183     .bInvertI2SBCLK = 0,
184     .ePDMClkSource = AM_HAL_PDM_INTERNAL_CLK,
185     .bPDMSampleDelay = 0,
186     .bDataPacking = 0,
187     .ePCMChannels = AM_HAL_PDM_CHANNEL_LEFT,
188     .ui32GainChangeDelay = 1,
189     .bI2SEnable = 0,
190     .bSoftMute = 0,
191     .bLRSwap = 0,
192 };
193 
194 //*****************************************************************************
195 // PDM initialization.
196 //*****************************************************************************
pdm_init(void)197 extern "C" void pdm_init(void) {
198   //
199   // Initialize, power-up, and configure the PDM.
200   //
201   am_hal_pdm_initialize(0, &g_pdm_handle);
202   am_hal_pdm_power_control(g_pdm_handle, AM_HAL_PDM_POWER_ON, false);
203   am_hal_pdm_configure(g_pdm_handle, &g_sPdmConfig);
204 
205   //
206   // Configure the necessary pins.
207   //
208   am_hal_gpio_pincfg_t sPinCfg = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
209 
210   //
211   // AP3B EVB w/ PDM MIC in slot3
212   //
213   sPinCfg.uFuncSel = AM_HAL_PIN_12_PDMCLK;
214   am_hal_gpio_pinconfig(12, sPinCfg);
215 
216   sPinCfg.uFuncSel = AM_HAL_PIN_11_PDMDATA;
217   am_hal_gpio_pinconfig(11, sPinCfg);
218 
219   //
220   // Configure and enable PDM interrupts (set up to trigger on DMA
221   // completion).
222   //
223   am_hal_pdm_interrupt_enable(g_pdm_handle,
224                               (AM_HAL_PDM_INT_DERR | AM_HAL_PDM_INT_DCMP |
225                                AM_HAL_PDM_INT_UNDFL | AM_HAL_PDM_INT_OVF));
226 
227   NVIC_EnableIRQ(PDM_IRQn);
228 
229   // Enable PDM
230   am_hal_pdm_enable(g_pdm_handle);
231 }
232 
233 // Start the DMA fetch of PDM samples.
pdm_start_dma(tflite::ErrorReporter * error_reporter)234 void pdm_start_dma(tflite::ErrorReporter* error_reporter) {
235   // Configure DMA and target address.
236   am_hal_pdm_transfer_t sTransfer;
237 
238   if (g_dma_destination_index == 0) {
239     sTransfer.ui32TargetAddr = (uint32_t)g_ui32PDMSampleBuffer0;
240   } else {
241     sTransfer.ui32TargetAddr = (uint32_t)g_ui32PDMSampleBuffer1;
242   }
243 
244   sTransfer.ui32TotalCount = 4 * kPdmSampleBufferSize;
245   // PDM DMA count is in Bytes
246 
247   // Start the data transfer.
248   if (AM_HAL_STATUS_SUCCESS != am_hal_pdm_dma_start(g_pdm_handle, &sTransfer)) {
249     TF_LITE_REPORT_ERROR(error_reporter, "Error - configuring PDM DMA failed.");
250   }
251 
252   // Reset the PDM DMA flags.
253   g_pdm_dma_error = false;
254 }
255 
256 #if USE_MAYA
power_down_sequence(void)257 extern "C" void power_down_sequence(void) {
258   am_hal_gpio_read_type_e eReadType;
259   eReadType = AM_HAL_GPIO_INPUT_READ;
260 
261   // Reconfigure PDM Pins for low power
262   // Drive PDMCLK low so Mics go in standby mode of ~ 10 to 20uA each
263   am_hal_gpio_pinconfig(12, g_AM_HAL_GPIO_OUTPUT);
264   am_hal_gpio_state_write(12, AM_HAL_GPIO_OUTPUT_SET);
265 
266   // Disable PDMDATA pin so no input buffer leakage current from floating pin
267   am_hal_gpio_pinconfig(11, g_AM_HAL_GPIO_DISABLE);
268 
269   // Disable PDM
270   am_hal_pdm_disable(g_pdm_handle);
271   am_hal_pdm_power_control(g_pdm_handle, AM_HAL_PDM_POWER_OFF, false);
272   am_hal_interrupt_master_disable();
273 
274   am_hal_gpio_interrupt_clear(AM_HAL_GPIO_BIT(AM_BSP_GPIO_BUTTON0));
275   am_hal_gpio_interrupt_disable(AM_HAL_GPIO_BIT(AM_BSP_GPIO_BUTTON0));
276   am_util_delay_ms(200);  // Debounce Delay
277   am_hal_gpio_interrupt_clear(AM_HAL_GPIO_BIT(AM_BSP_GPIO_BUTTON0));
278   am_hal_gpio_interrupt_enable(AM_HAL_GPIO_BIT(AM_BSP_GPIO_BUTTON0));
279 
280   for (int ix = 0; ix < AM_BSP_NUM_LEDS; ix++) {
281     am_devices_led_off(am_bsp_psLEDs, ix);
282   }
283 
284   am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP);
285   // Apollo3 will be < 3uA in deep sleep
286 
287   am_hal_reset_control(AM_HAL_RESET_CONTROL_SWPOR, 0);
288   // Use Reset to perform clean power-on from sleep
289 }
290 
291 //*****************************************************************************
292 //
293 // GPIO ISR
294 //
295 //*****************************************************************************
am_gpio_isr(void)296 extern "C" void am_gpio_isr(void) {
297   uint64_t ui64Status;
298   // Read and clear the GPIO interrupt status then service the interrupts.
299   am_hal_gpio_interrupt_status_get(false, &ui64Status);
300   am_hal_gpio_interrupt_clear(ui64Status);
301   am_hal_gpio_interrupt_service(ui64Status);
302 }
303 
power_button_handler(void)304 extern "C" void power_button_handler(void) { g_PowerOff = 1; }
305 
306 #endif  // USE_MAYA
307 
308 // Interrupt handler for the PDM.
am_pdm0_isr(void)309 extern "C" void am_pdm0_isr(void) {
310   uint32_t ui32IntMask;
311 
312   // Read the interrupt status.
313   if (AM_HAL_STATUS_SUCCESS !=
314       am_hal_pdm_interrupt_status_get(g_pdm_handle, &ui32IntMask, false)) {
315     TF_LITE_REPORT_ERROR(g_pdm_dma_error_reporter,
316                          "Error reading PDM0 interrupt status.");
317   }
318 
319   // Clear the PDM interrupt.
320   if (AM_HAL_STATUS_SUCCESS !=
321       am_hal_pdm_interrupt_clear(g_pdm_handle, ui32IntMask)) {
322     TF_LITE_REPORT_ERROR(g_pdm_dma_error_reporter,
323                          "Error clearing PDM interrupt status.");
324   }
325 
326 #if USE_DEBUG_GPIO
327   // DEBUG : GPIO flag polling.
328   am_hal_gpio_state_write(31, AM_HAL_GPIO_OUTPUT_SET);  // Slot1 AN pin
329 #endif
330 
331   // If we got a DMA complete, set the flag.
332   if (ui32IntMask & AM_HAL_PDM_INT_OVF) {
333     am_util_stdio_printf("\n%s\n", "\nPDM ISR OVF.");
334   }
335   if (ui32IntMask & AM_HAL_PDM_INT_UNDFL) {
336     am_util_stdio_printf("\n%s\n", "\nPDM ISR UNDLF.");
337   }
338   if (ui32IntMask & AM_HAL_PDM_INT_DCMP) {
339     uint32_t* source_buffer;
340     if (g_dma_destination_index == 0) {
341       source_buffer = g_ui32PDMSampleBuffer0;
342       g_dma_destination_index = 1;
343     } else {
344       source_buffer = g_ui32PDMSampleBuffer1;
345       g_dma_destination_index = 0;
346     }
347     pdm_start_dma(g_pdm_dma_error_reporter);
348 
349     uint32_t slotCount = 0;
350     for (uint32_t indi = 0; indi < kPdmSampleBufferSize; indi++) {
351       g_audio_capture_buffer[g_audio_capture_buffer_start] =
352           source_buffer[indi];
353       g_audio_capture_buffer_start =
354           (g_audio_capture_buffer_start + 1) % kAudioCaptureBufferSize;
355       slotCount++;
356     }
357 
358     g_total_samples_captured += slotCount;
359     g_latest_audio_timestamp =
360         (g_total_samples_captured / (kAudioSampleFrequency / 1000));
361   }
362 
363   // If we got a DMA error, set the flag.
364   if (ui32IntMask & AM_HAL_PDM_INT_DERR) {
365     g_pdm_dma_error = true;
366   }
367 
368 #if USE_DEBUG_GPIO
369   // DEBUG : GPIO flag polling.
370   am_hal_gpio_state_write(31, AM_HAL_GPIO_OUTPUT_CLEAR);  // Slot1 AN pin
371 #endif
372 }
373 
InitAudioRecording(tflite::ErrorReporter * error_reporter)374 TfLiteStatus InitAudioRecording(tflite::ErrorReporter* error_reporter) {
375   // Set the clock frequency.
376   if (AM_HAL_STATUS_SUCCESS !=
377       am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX, 0)) {
378     TF_LITE_REPORT_ERROR(error_reporter,
379                          "Error - configuring the system clock failed.");
380     return kTfLiteError;
381   }
382 
383   // Individually select elements of am_bsp_low_power_init
384   custom_am_bsp_low_power_init();
385 
386   // Set the default cache configuration and enable it.
387   if (AM_HAL_STATUS_SUCCESS !=
388       am_hal_cachectrl_config(&am_hal_cachectrl_defaults)) {
389     TF_LITE_REPORT_ERROR(error_reporter,
390                          "Error - configuring the system cache failed.");
391     return kTfLiteError;
392   }
393   if (AM_HAL_STATUS_SUCCESS != am_hal_cachectrl_enable()) {
394     TF_LITE_REPORT_ERROR(error_reporter,
395                          "Error - enabling the system cache failed.");
396     return kTfLiteError;
397   }
398 
399   // Configure Flash wait states.
400   CACHECTRL->FLASHCFG_b.RD_WAIT = 1;      // Default is 3
401   CACHECTRL->FLASHCFG_b.SEDELAY = 6;      // Default is 7
402   CACHECTRL->FLASHCFG_b.LPM_RD_WAIT = 5;  // Default is 8
403 
404   // Enable cache sleep states.
405   uint32_t ui32LPMMode = CACHECTRL_FLASHCFG_LPMMODE_STANDBY;
406   if (am_hal_cachectrl_control(AM_HAL_CACHECTRL_CONTROL_LPMMODE_SET,
407                                &ui32LPMMode)) {
408     TF_LITE_REPORT_ERROR(error_reporter,
409                          "Error - enabling cache sleep state failed.");
410   }
411 
412   // Enable Instruction & Data pre-fetching.
413   MCUCTRL->SRAMMODE_b.DPREFETCH = 1;
414   MCUCTRL->SRAMMODE_b.DPREFETCH_CACHE = 1;
415   MCUCTRL->SRAMMODE_b.IPREFETCH = 1;
416   MCUCTRL->SRAMMODE_b.IPREFETCH_CACHE = 1;
417 
418   // Enable the floating point module, and configure the core for lazy stacking.
419   am_hal_sysctrl_fpu_enable();
420   am_hal_sysctrl_fpu_stacking_enable(true);
421   TF_LITE_REPORT_ERROR(error_reporter, "FPU Enabled.");
422 
423   // Configure the LEDs.
424   am_devices_led_array_init(am_bsp_psLEDs, AM_BSP_NUM_LEDS);
425   // Turn the LEDs off
426   for (int ix = 0; ix < AM_BSP_NUM_LEDS; ix++) {
427     am_devices_led_off(am_bsp_psLEDs, ix);
428   }
429 
430 #if USE_MAYA
431   // Configure Power Button
432   am_hal_gpio_pinconfig(AM_BSP_GPIO_BUTTON_POWER, g_AM_BSP_GPIO_BUTTON_POWER);
433 
434   // Clear and Enable the GPIO Interrupt (write to clear).
435   am_hal_gpio_interrupt_clear(AM_HAL_GPIO_BIT(AM_BSP_GPIO_BUTTON_POWER));
436   am_hal_gpio_interrupt_register(AM_BSP_GPIO_BUTTON_POWER,
437                                  power_button_handler);
438   am_hal_gpio_interrupt_enable(AM_HAL_GPIO_BIT(AM_BSP_GPIO_BUTTON_POWER));
439 
440   // Enable GPIO interrupts to the NVIC.
441   NVIC_EnableIRQ(GPIO_IRQn);
442 #endif  // USE_MAYA
443 
444 #if USE_DEBUG_GPIO
445   // DEBUG : GPIO flag polling.
446   // Configure the GPIOs for flag polling.
447   am_hal_gpio_pinconfig(31, g_AM_HAL_GPIO_OUTPUT);  // Slot1 AN pin
448   am_hal_gpio_pinconfig(39, g_AM_HAL_GPIO_OUTPUT);  // Slot1 RST pin
449   am_hal_gpio_pinconfig(44, g_AM_HAL_GPIO_OUTPUT);  // Slot1 CS pin
450   am_hal_gpio_pinconfig(48, g_AM_HAL_GPIO_OUTPUT);  // Slot1 PWM pin
451 
452   am_hal_gpio_pinconfig(32, g_AM_HAL_GPIO_OUTPUT);  // Slot2 AN pin
453   am_hal_gpio_pinconfig(46, g_AM_HAL_GPIO_OUTPUT);  // Slot2 RST pin
454   am_hal_gpio_pinconfig(42, g_AM_HAL_GPIO_OUTPUT);  // Slot2 CS pin
455   am_hal_gpio_pinconfig(47, g_AM_HAL_GPIO_OUTPUT);  // Slot2 PWM pin
456 #endif
457 
458   // Ensure the CPU is running as fast as possible.
459   // enable_burst_mode(error_reporter);
460 
461 #if USE_TIME_STAMP
462   //
463   // Set up and start the timer.
464   //
465   am_hal_ctimer_stop(SELFTEST_TIMERNUM, AM_HAL_CTIMER_BOTH);
466   am_hal_ctimer_clear(SELFTEST_TIMERNUM, AM_HAL_CTIMER_BOTH);
467   am_hal_ctimer_config(SELFTEST_TIMERNUM, &g_sContTimer);
468   am_hal_ctimer_start(SELFTEST_TIMERNUM, AM_HAL_CTIMER_TIMERA);
469 #endif  // USE_TIME_STAMP
470 
471   // Configure, turn on PDM
472   g_pdm_dma_error_reporter = error_reporter;
473   pdm_init();
474   am_hal_interrupt_master_enable();
475   am_hal_pdm_fifo_flush(g_pdm_handle);
476   // Trigger the PDM DMA for the first time manually.
477   pdm_start_dma(error_reporter);
478 
479   TF_LITE_REPORT_ERROR(error_reporter, "\nPDM DMA Threshold = %d",
480                        PDMn(0)->FIFOTHR);
481 
482   // Turn on LED 0 to indicate PDM initialized
483   am_devices_led_on(am_bsp_psLEDs, 0);
484 
485   return kTfLiteOk;
486 }
487 
GetAudioSamples(tflite::ErrorReporter * error_reporter,int start_ms,int duration_ms,int * audio_samples_size,int16_t ** audio_samples)488 TfLiteStatus GetAudioSamples(tflite::ErrorReporter* error_reporter,
489                              int start_ms, int duration_ms,
490                              int* audio_samples_size, int16_t** audio_samples) {
491 #if USE_MAYA
492   if (g_PowerOff) {
493     power_down_sequence();
494   }
495 #endif  // USE_MAYA
496 
497   if (!g_is_audio_initialized) {
498     TfLiteStatus init_status = InitAudioRecording(error_reporter);
499     if (init_status != kTfLiteOk) {
500       return init_status;
501     }
502     g_is_audio_initialized = true;
503   }
504 
505 #if USE_DEBUG_GPIO
506   // DEBUG : GPIO flag polling.
507   am_hal_gpio_state_write(39, AM_HAL_GPIO_OUTPUT_SET);  // Slot1 RST pin
508 #endif
509 
510   // This should only be called when the main thread notices that the latest
511   // audio sample data timestamp has changed, so that there's new data in the
512   // capture ring buffer. The ring buffer will eventually wrap around and
513   // overwrite the data, but the assumption is that the main thread is checking
514   // often enough and the buffer is large enough that this call will be made
515   // before that happens.
516   const int start_offset =
517       (start_ms < 0) ? 0 : start_ms * (kAudioSampleFrequency / 1000);
518   const int duration_sample_count =
519       duration_ms * (kAudioSampleFrequency / 1000);
520   for (int i = 0; i < duration_sample_count; ++i) {
521     const int capture_index = (start_offset + i) % kAudioCaptureBufferSize;
522     g_audio_output_buffer[i] = g_audio_capture_buffer[capture_index];
523   }
524 
525   *audio_samples_size = kMaxAudioSampleSize;
526   *audio_samples = g_audio_output_buffer;
527 
528 #if USE_DEBUG_GPIO
529   // DEBUG : GPIO flag polling.
530   am_hal_gpio_state_write(39, AM_HAL_GPIO_OUTPUT_CLEAR);  // Slot1 RST pin
531 #endif
532 
533   return kTfLiteOk;
534 }
535 
LatestAudioTimestamp()536 int32_t LatestAudioTimestamp() { return g_latest_audio_timestamp; }
537