tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/microphone/microphone.go (about) 1 // Package microphone implements a driver for a PDM microphone. 2 // For example, the Adafruit PDM MEMS breakout board (https://www.adafruit.com/product/3492) 3 // 4 // Datasheet: https://cdn-learn.adafruit.com/assets/assets/000/049/977/original/MP34DT01-M.pdf 5 package microphone // import "tinygo.org/x/drivers/microphone" 6 7 import ( 8 "machine" 9 "math" 10 ) 11 12 const ( 13 defaultSampleRate = 22000 14 quantizeSteps = 64 15 msForSPLSample = 50 16 defaultSampleCountForSPL = (defaultSampleRate / 1000) * msForSPLSample 17 defaultGain = 9.0 18 defaultRefLevel = 0.00002 19 ) 20 21 // Device wraps an I2S connection to a PDM microphone device. 22 type Device struct { 23 bus machine.I2S 24 25 // data buffer used for SPL sound pressure level samples 26 data []int32 27 28 // buf buffer used for sinc filter 29 buf []uint32 30 31 // SampleCountForSPL is number of samples aka size of data buffer to be used 32 // for sound pressure level measurement. 33 // Once Configure() is called, changing this value has no effect. 34 SampleCountForSPL int 35 36 // Gain setting used to calculate sound pressure level 37 Gain float64 38 39 // ReferenceLevel setting used to calculate sound pressure level. 40 ReferenceLevel float64 41 } 42 43 // New creates a new microphone connection. The I2S bus must already be 44 // configured. 45 // 46 // This function only creates the Device object, it does not touch the device. 47 func New(bus machine.I2S) Device { 48 return Device{ 49 bus: bus, 50 SampleCountForSPL: defaultSampleCountForSPL, 51 Gain: defaultGain, 52 ReferenceLevel: defaultRefLevel, 53 } 54 } 55 56 // Configure the microphone. 57 func (d *Device) Configure() { 58 d.data = make([]int32, d.SampleCountForSPL) 59 d.buf = make([]uint32, (quantizeSteps / 16)) 60 } 61 62 // Read the raw microphone data. 63 func (d *Device) Read(r []int32) (int, error) { 64 count := len(r) 65 66 // get the next group of samples 67 machine.I2S0.Read(d.buf) 68 69 if len(r) > len(d.buf) { 70 count = len(d.buf) 71 } 72 for i := 0; i < count; i++ { 73 r[i] = int32(d.buf[i]) 74 } 75 76 return count, nil 77 } 78 79 // ReadWithFilter reads the microphone and filters the buffer using the sinc filter. 80 func (d *Device) ReadWithFilter(r []int32) (int, error) { 81 // read/filter the samples 82 var sum uint16 83 for i := 0; i < len(r); i++ { 84 85 // get the next group of samples 86 machine.I2S0.Read(d.buf) 87 88 // filter 89 sum = applySincFilter(d.buf) 90 91 // adjust to 10 bit value 92 s := int32(sum >> 6) 93 94 // make it close to 0-offset signed 95 s -= 512 96 97 r[i] = s 98 } 99 100 return len(r), nil 101 } 102 103 // GetSoundPressure returns the sound pressure in milli-decibels. 104 func (d *Device) GetSoundPressure() (int32, int32) { 105 // read/filter the samples 106 d.ReadWithFilter(d.data) 107 108 // remove offset 109 var avg int32 110 for i := 0; i < len(d.data); i++ { 111 avg += d.data[i] 112 } 113 avg /= int32(len(d.data)) 114 115 for i := 0; i < len(d.data); i++ { 116 d.data[i] -= avg 117 } 118 119 // get max value 120 var maxval int32 121 for i := 0; i < len(d.data); i++ { 122 v := d.data[i] 123 if v < 0 { 124 v = -v 125 } 126 if maxval < v { 127 maxval = v 128 } 129 } 130 131 // calculate SPL 132 spl := float64(maxval) / 1023.0 * d.Gain 133 spl = 20 * math.Log10(spl/d.ReferenceLevel) 134 135 return int32(spl * 1000), maxval 136 } 137 138 // sinc filter for 44 khz with 64 samples 139 // each value matches the corresponding bit in the 8-bit value 140 // for that sample. 141 // 142 // For more information: https://en.wikipedia.org/wiki/Sinc_filter 143 var sincfilter = [quantizeSteps]uint16{ 144 0, 2, 9, 21, 39, 63, 94, 132, 145 179, 236, 302, 379, 467, 565, 674, 792, 146 920, 1055, 1196, 1341, 1487, 1633, 1776, 1913, 147 2042, 2159, 2263, 2352, 2422, 2474, 2506, 2516, 148 2506, 2474, 2422, 2352, 2263, 2159, 2042, 1913, 149 1776, 1633, 1487, 1341, 1196, 1055, 920, 792, 150 674, 565, 467, 379, 302, 236, 179, 132, 151 94, 63, 39, 21, 9, 2, 0, 0, 152 } 153 154 // applySincFilter uses the sinc filter to process a single set of sample values. 155 func applySincFilter(samples []uint32) (result uint16) { 156 var sample uint16 157 pos := 0 158 for j := 0; j < len(samples); j++ { 159 // takes only the low order 16-bits 160 sample = uint16(samples[j] & 0xffff) 161 for i := 0; i < 16; i++ { 162 if (sample & 0x1) > 0 { 163 result += sincfilter[pos] 164 pos++ 165 } 166 sample >>= 1 167 } 168 } 169 170 return 171 }