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  }