arduino-audio-tools
PDMStream.h
1 #include "AudioConfig.h"
2 #include "Stream.h"
3 
4 #define DELAY(j) {for(volatile int i=0;i<j;i++);}
5 
6 namespace audio_tools {
7 
18 template <typename T>
20  public:
21  DecimationStreamExt() = default;
22  DecimationStreamExt(Stream &in) { setStream(in); }
23 
24  void setStream(Stream &in) { p_in = &in; }
25 
26  virtual bool begin(AudioInfo cfg) {
27  setAudioInfo(cfg);
28  return true;
29  }
30 
31  // defines the decimation factor: as multiple of bits_per sample
32  void setDecimationFactor(int factor) { dec_factor = factor; }
33 
34  size_t readBytes(uint8_t *data, size_t len) {
35  LOGD("readBytes:%d", len);
36  assert(data != nullptr);
37  // make sure that we read full samples
38  int samples = len / sizeof(T);
39  int result_bytes = samples * sizeof(T);
40 
41  // calculated scaling factor and offset for unsigned to signed conversion
42  int max_value = sizeof(T) * 8 * dec_factor; // e.g. 16 * 32 = 512
43  int scaled_max = pow(2, sizeof(T) * 8); // e.g. 65536
44  int factor = scaled_max / max_value; // e.g. 128
45  int unsigned_to_signed = scaled_max / 2; // e.g. 32768
46 
47  // provide subsequent samples
48  T *data_typed = (T *)data;
49  T tmp_in;
50  for (int idx = 0; idx < samples; idx++) {
51  // decimate subsequent samples
52  unsigned decimated = 0;
53  for (int dec = 0; dec < dec_factor; dec++) {
54  int sample_bytes = sizeof(T);
55  LOGD("readBytes: %d", sample_bytes);
56  if (p_in->readBytes((uint8_t *)&tmp_in, sample_bytes) != sample_bytes) {
57  LOGE("readBytes failed");
58  }
59  decimated += countSetBits(tmp_in);
60  }
61  // store scaled decimated as singned value
62  //data[idx] = (decimated * factor) - unsigned_to_signed;
63  data_typed[idx] = decimated;
64  }
65 
66  return result_bytes;
67  }
68 
69  int available() {
70  TRACED();
71  return 1024;
72  }
73 
74  AudioInfo audioInfoPDM() {
75  AudioInfo result = audioInfo();
76  // sample_rate_pcm = sample_rate_pdm / decimation
77  result.sample_rate = info.sample_rate * dec_factor;
78  return result;
79  }
80 
81  protected:
82  Stream *p_in = nullptr;
83  int dec_factor = 32; // usually set between 25 and 64×
84 
85  unsigned countSetBits(unsigned n) {
86  TRACED();
87  T count = 0;
88  while (n) {
89  count += n & 1;
90  n >>= 1;
91  }
92  return count;
93  }
94 };
95 
96 
97 /*
98 We read the raw PDM data with the help of the Arduino digitalRead(). The SEL pin
99 needs to be connected to GND, so that the data is valid at clock pin low.
100 
101 COMMON CLOCK AND DECIMATION RATIO SETTINGS
102 PDM Clock Frequency(fPDM)Decimation Ratio Baseband Sampling Rate (fs) Audio Signal Bandwidth (fs/2)Applications
103 4.8 MHz 25× 192 kHz 96 kHz Ultrasound
104 4.8 MHz 50× 96 kHz 48 kHz
105 3.072 MHz 64× 48 kHz 24 kHz Full-bandwidth audio
106 2.4 MHz 50× 48 kHz 24 kHz
107 1.536 MHz 48× 32 kHz 16 kHz Wide-bandwidth audio
108 1.024 MHz 64× 16 kHz 8 kHz High-quality voice
109 768 kHz 48× 16 kHz 8 kHz
110 512 kHz 32× 16 kHz 8 kHz
111 512 kHz 64× 8 kHz 4 kHz Standard voice
112 */
113 
114 template <typename T>
116 
117 public:
118  bool begin(AudioInfo cfg) {
120  pinMode(pin_clock, OUTPUT);
121  pinMode(pin_data, INPUT);
122  return true;
123  }
124 
125  size_t readBytes(uint8_t *data, size_t len) {
126  LOGD("readBytes:%d", len);
127  assert(data != nullptr);
128  // make sure that we read full samples
129  int samples = len / sizeof(T);
130  int result_bytes = samples * sizeof(T);
131 
132  // calculated scaling factor and offset for unsigned to signed conversion
133  int max_value = sizeof(T) * 8 * DecimationStreamExt<T>::dec_factor; // e.g. 16 * 32 = 512
134  int scaled_max = pow(2, sizeof(T) * 8); // e.g. 65536
135  int factor = scaled_max / max_value; // e.g. 128
136  int unsigned_to_signed = scaled_max / 2; // e.g. 32768
137 
138  // provide subsequent samples
139  T *data_typed = (T *)data;
140  T tmp_in;
141  for (int idx = 0; idx < samples; idx++) {
142  // decimate subsequent samples
143  unsigned decimated = 0;
144  for (int dec = 0; dec < DecimationStreamExt<T>::dec_factor * sizeof(T)*8; dec++) {
145  int sample_bytes = sizeof(T);
146  digitalWrite(pin_clock, HIGH);
147  DELAY(1);
148  digitalWrite(pin_clock, LOW);
149  bool bit = digitalRead(pin_data);
150  DELAY(1);
151  // sum up active bits
152  decimated += bit;
153  }
154  // store scaled decimated as singned value
155  //data[idx] = (decimated * factor) - unsigned_to_signed;
156  data_typed[idx] = decimated;
157  }
158 
159  return result_bytes;
160  }
161 
162 protected:
163  int pin_clock = 14;
164  int pin_data = 32;
165 };
166 
175 template <typename T>
176 class PDMMonoStreamT : public AudioStream {
177  public:
178  PDMMonoStreamT(Stream &in) {
179  decimation_stream.setStream(in);
180  info.sample_rate = 44100;
181  info.channels = 1;
182  info.bits_per_sample = bitsPerSample();
183  }
184 
185  // provides the audio info of the PDM stream (with the much higher sample
186  // rate)
187  AudioInfo audioInfoPDM() {
188  AudioInfo result = audioInfo();
189  // sample_rate_pcm = sample_rate_pdm / decimation
190  result.sample_rate = info.sample_rate * decimation();
191  return result;
192  }
193 
194  // provides the decimation factor that was used in the processing
195  int decimation() { return decimation_factor; }
196 
197  // defines the decimation factor: must be multiple of bits_per sample
198  void setDecimationFactor(int factor) {
199  decimation_factor = factor;
200  }
201 
202  bool begin(AudioInfo info) {
203  setAudioInfo(info);
204  if (info.channels != 1) {
205  LOGE("channels must be 1");
206  }
207  // set the factor for the decimator
208  decimation_stream.setDecimationFactor(decimation_factor);
209  return begin();
210  }
211 
212  bool begin() {
213  decimation_stream.begin(info);
214  bool rc = in_filtered.begin(info);
215  in_filtered.setFilter(0, fir);
216  return rc;
217  }
218 
219  size_t readBytes(uint8_t *data, size_t len) {
220  LOGD("readBytes:%d", len);
221  assert(data != nullptr);
222  // make sure that we read full samples
223  int samples = len / sizeof(T);
224  int result_bytes = samples * sizeof(T);
225  assert(result_bytes <= len);
226 
227  if (in_filtered.readBytes(data, result_bytes) != result_bytes) {
228  LOGE("readBytes failed");
229  }
230 
231  return result_bytes;
232  }
233 
234  int available() {
235  TRACED();
236  return in_filtered.available();
237  }
238 
239  template <size_t B>
240  void setFilterValues(const T (&b)[B]) {
241  fir.setValues(b);
242  }
243 
244  protected:
245  int decimation_factor = sizeof(T) * 2;
246  // 44100 hz FIR: cut off: 18040, transition bandwidth: 7380
247  float coef[19] = {
248  -0.000704420658475743, -0.000537879918926308, 0.004114637509913062,
249  -0.012685775806621488, 0.027889173789107543, -0.049285026985058301,
250  0.074005079283040689, -0.097330704866957815, 0.114052040962871595,
251  0.880965753382213723, 0.114052040962871595, -0.097330704866957843,
252  0.074005079283040717, -0.049285026985058301, 0.027889173789107550,
253  -0.012685775806621504, 0.004114637509913064, -0.000537879918926308,
254  -0.000704420658475743};
255  DecimationStreamExt<T> decimation_stream;
256  FilteredStream<T, float> in_filtered{decimation_stream, 1};
257  FIR<float> fir{coef};
258 
259  constexpr int8_t bitsPerSample() { return sizeof(T) * 8; }
260 };
261 
262 // Define PDMStream
264 
265 } // namespace audio_tools
int digitalRead(int pin)
e.g. for AudioActions
Definition: NoArduino.h:206
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition: BaseStream.h:109
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition: BaseStream.h:117
virtual AudioInfo audioInfo() override
provides the actual input AudioInfo
Definition: BaseStream.h:140
Definition: PDMStream.h:115
Deciates an sample stream by the indicated factor: Decimation counts the number of set bits....
Definition: PDMStream.h:19
void setFilter(int channel, Filter< TF > *filter)
Definition: AudioStreams.h:1582
Applies low pass filter to a decimated pdm signal to convert it to pcm.
Definition: PDMStream.h:176
Definition: NoArduino.h:125
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AudioConfig.h:821
Basic Audio information which drives e.g. I2S.
Definition: AudioTypes.h:50
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition: AudioTypes.h:53
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition: AudioTypes.h:55
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition: AudioTypes.h:57