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