|
| 1 | +/* |
| 2 | + An example sketch for Edge Impulse trained model inference for Audio scene classification |
| 3 | +
|
| 4 | + Copyright (c) 2021 Seeed technology co., ltd. |
| 5 | + Author : Dmitry Maslov |
| 6 | + Create Time : January 2021 |
| 7 | + Change Log : |
| 8 | +
|
| 9 | + The MIT License (MIT) |
| 10 | +
|
| 11 | + Permission is hereby granted, free of charge, to any person obtaining a copy |
| 12 | + of this software and associated documentation files (the "Software"), to deal |
| 13 | + in the Software without restriction, including without limitation the rights |
| 14 | + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 15 | + copies of the Software, and to permit persons to whom the Software is |
| 16 | + furnished to do so, subject to the following conditions: |
| 17 | +
|
| 18 | + The above copyright notice and this permission notice shall be included in |
| 19 | + all copies or substantial portions of the Software. |
| 20 | +
|
| 21 | + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 22 | + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 23 | + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 24 | + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 25 | + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 26 | + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 27 | + THE SOFTWARE. |
| 28 | +*/ |
| 29 | + |
| 30 | +// If your target is limited in memory remove this macro to save 10K RAM |
| 31 | +#define EIDSP_QUANTIZE_FILTERBANK 0 |
| 32 | + |
| 33 | +/* Includes ---------------------------------------------------------------- */ |
| 34 | +#include <audio_scene_mfe_conv1d_v1_inference.h> |
| 35 | + |
| 36 | +/** Audio buffers, pointers and selectors */ |
| 37 | +typedef struct { |
| 38 | + int16_t *buffer; |
| 39 | + uint8_t buf_ready; |
| 40 | + uint32_t buf_count; |
| 41 | + uint32_t n_samples; |
| 42 | +} inference_t; |
| 43 | + |
| 44 | +static inference_t inference; |
| 45 | +static signed short sampleBuffer[2048]; |
| 46 | +unsigned int sampling_period_us = round(600000 * (1.0 / 16000)); |
| 47 | +static bool debug_nn = false; // Set this to true to see e.g. features generated from the raw signal |
| 48 | + |
| 49 | +/** |
| 50 | + * @brief Arduino setup function |
| 51 | + */ |
| 52 | +void setup() |
| 53 | +{ |
| 54 | + // put your setup code here, to run once: |
| 55 | + Serial.begin(115200); |
| 56 | + |
| 57 | + Serial.println("Edge Impulse Inferencing Demo"); |
| 58 | + |
| 59 | + // summary of inferencing settings (from model_metadata.h) |
| 60 | + ei_printf("Inferencing settings:\n"); |
| 61 | + ei_printf("\tInterval: %.2f ms.\n", (float)EI_CLASSIFIER_INTERVAL_MS); |
| 62 | + ei_printf("\tFrame size: %d\n", EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE); |
| 63 | + ei_printf("\tSample length: %d ms.\n", EI_CLASSIFIER_RAW_SAMPLE_COUNT / 16); |
| 64 | + ei_printf("\tNo. of classes: %d\n", sizeof(ei_classifier_inferencing_categories) / sizeof(ei_classifier_inferencing_categories[0])); |
| 65 | + |
| 66 | + if (microphone_inference_start(EI_CLASSIFIER_RAW_SAMPLE_COUNT) == false) { |
| 67 | + ei_printf("ERR: Failed to setup audio sampling\r\n"); |
| 68 | + return; |
| 69 | + } |
| 70 | +} |
| 71 | + |
| 72 | +/** |
| 73 | + * @brief Arduino main function. Runs the inferencing loop. |
| 74 | + */ |
| 75 | +void loop() |
| 76 | +{ |
| 77 | + |
| 78 | + ei_printf("Recording...\n"); |
| 79 | + |
| 80 | + bool m = microphone_inference_record(); |
| 81 | + if (!m) { |
| 82 | + ei_printf("ERR: Failed to record audio...\n"); |
| 83 | + return; |
| 84 | + } |
| 85 | + |
| 86 | + ei_printf("Recording done\n"); |
| 87 | + |
| 88 | + signal_t signal; |
| 89 | + signal.total_length = EI_CLASSIFIER_RAW_SAMPLE_COUNT; |
| 90 | + signal.get_data = µphone_audio_signal_get_data; |
| 91 | + ei_impulse_result_t result = { 0 }; |
| 92 | + |
| 93 | + EI_IMPULSE_ERROR r = run_classifier(&signal, &result, debug_nn); |
| 94 | + if (r != EI_IMPULSE_OK) { |
| 95 | + ei_printf("ERR: Failed to run classifier (%d)\n", r); |
| 96 | + return; |
| 97 | + } |
| 98 | + |
| 99 | + // print the predictions |
| 100 | + ei_printf("Predictions "); |
| 101 | + ei_printf("(DSP: %d ms., Classification: %d ms., Anomaly: %d ms.)", |
| 102 | + result.timing.dsp, result.timing.classification, result.timing.anomaly); |
| 103 | + ei_printf(": \n"); |
| 104 | + for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) { |
| 105 | + ei_printf(" %s: %.5f\n", result.classification[ix].label, result.classification[ix].value); |
| 106 | + } |
| 107 | +#if EI_CLASSIFIER_HAS_ANOMALY == 1 |
| 108 | + ei_printf(" anomaly score: %.3f\n", result.anomaly); |
| 109 | +#endif |
| 110 | +} |
| 111 | + |
| 112 | +/** |
| 113 | + * @brief Printf function uses vsnprintf and output using Arduino Serial |
| 114 | + * |
| 115 | + * @param[in] format Variable argument list |
| 116 | + */ |
| 117 | +void ei_printf(const char *format, ...) { |
| 118 | + static char print_buf[1024] = { 0 }; |
| 119 | + |
| 120 | + va_list args; |
| 121 | + va_start(args, format); |
| 122 | + int r = vsnprintf(print_buf, sizeof(print_buf), format, args); |
| 123 | + va_end(args); |
| 124 | + |
| 125 | + if (r > 0) { |
| 126 | + Serial.write(print_buf); |
| 127 | + } |
| 128 | +} |
| 129 | + |
| 130 | + |
| 131 | +/** |
| 132 | + * @brief Init inferencing struct and setup/start PDM |
| 133 | + * |
| 134 | + * @param[in] n_samples The n samples |
| 135 | + * |
| 136 | + * @return { description_of_the_return_value } |
| 137 | + */ |
| 138 | +static bool microphone_inference_start(uint32_t n_samples) |
| 139 | +{ |
| 140 | + inference.buffer = (int16_t *)malloc(n_samples * sizeof(int16_t)); |
| 141 | + |
| 142 | + if(inference.buffer == NULL) { |
| 143 | + return false; |
| 144 | + } |
| 145 | + |
| 146 | + inference.buf_count = 0; |
| 147 | + inference.n_samples = n_samples; |
| 148 | + inference.buf_ready = 0; |
| 149 | + pinMode(WIO_MIC, INPUT); |
| 150 | + |
| 151 | + return true; |
| 152 | +} |
| 153 | + |
| 154 | +/** |
| 155 | + * @brief Wait on new data |
| 156 | + * |
| 157 | + * @return True when finished |
| 158 | + */ |
| 159 | +static bool microphone_inference_record(void) |
| 160 | +{ |
| 161 | + inference.buf_ready = 0; |
| 162 | + inference.buf_count = 0; |
| 163 | + |
| 164 | + if (inference.buf_ready == 0) { |
| 165 | + for(int i = 0; i < 8001; i++) { |
| 166 | + inference.buffer[inference.buf_count++] = map(analogRead(WIO_MIC), 0, 1023, -32768, 32767); |
| 167 | + delayMicroseconds(sampling_period_us); |
| 168 | + |
| 169 | + if(inference.buf_count >= inference.n_samples) { |
| 170 | + inference.buf_count = 0; |
| 171 | + inference.buf_ready = 1; |
| 172 | + break; |
| 173 | + } |
| 174 | + } |
| 175 | + } |
| 176 | + |
| 177 | + return true; |
| 178 | +} |
| 179 | + |
| 180 | +/** |
| 181 | + * Get raw audio signal data |
| 182 | + */ |
| 183 | +static int microphone_audio_signal_get_data(size_t offset, size_t length, float *out_ptr) |
| 184 | +{ |
| 185 | + numpy::int16_to_float(&inference.buffer[offset], out_ptr, length); |
| 186 | + |
| 187 | + return 0; |
| 188 | +} |
| 189 | + |
| 190 | +/** |
| 191 | + * @brief Stop PDM and release buffers |
| 192 | + */ |
| 193 | +static void microphone_inference_end(void) |
| 194 | +{ |
| 195 | + free(inference.buffer); |
| 196 | +} |
| 197 | + |
| 198 | +#if !defined(EI_CLASSIFIER_SENSOR) || EI_CLASSIFIER_SENSOR != EI_CLASSIFIER_SENSOR_MICROPHONE |
| 199 | +#error "Invalid model for current sensor." |
| 200 | +#endif |
0 commit comments