FastLED 3.9.15
Loading...
Searching...
No Matches
input.h
Go to the documentation of this file.
1#pragma once
2
3#include "fl/audio/audio.h"
6#include "fl/stl/int.h"
7#include "fl/stl/noexcept.h"
8#include "fl/stl/variant.h"
9#include "fl/stl/vector.h"
10#include "platforms/audio.h"
11
12#ifndef FASTLED_HAS_AUDIO_INPUT
13#error "platforms/audio.h must define FASTLED_HAS_AUDIO_INPUT"
14#endif
15
16#define I2S_AUDIO_BUFFER_LEN 512
17#define AUDIO_DEFAULT_SAMPLE_RATE 44100ul
18#define AUDIO_DEFAULT_BIT_RESOLUTION 16
19#define AUDIO_DMA_BUFFER_COUNT 8
20
21namespace fl {
22namespace audio {
23
24// Note: Right now these are esp specific, but they are designed to migrate to a
25// common api.
26
27enum class AudioChannel {
28 Left = 0,
29 Right = 1,
30 Both = 2, // Two microphones can be used to capture both channels with one
31 // AudioSource.
32};
33
34enum class I2SCommFormat {
35 Philips = 0X01, // I2S communication I2S Philips standard, data launch at
36 // second BCK
37 MSB = 0X02, // I2S communication MSB alignment standard, data launch at
38 // first BCK
39 PCMShort = 0x04, // PCM Short standard, also known as DSP mode. The period
40 // of synchronization signal (WS) is 1 bck cycle.
41 PCMLong = 0x0C, // PCM Long standard. The period of synchronization signal
42 // (WS) is channel_bit*bck cycles.
43 Max = 0x0F, // standard max
44};
45
46struct ConfigI2S {
47 int mPinWs;
48 int mPinSd;
55 bool mInvert;
56 ConfigI2S(int pin_ws, int pin_sd, int pin_clk, int i2s_num,
57 AudioChannel mic_channel, u16 sample_rate, u8 bit_resolution,
59 bool invert = false) FL_NOEXCEPT : mPinWs(pin_ws),
60 mPinSd(pin_sd),
61 mPinClk(pin_clk),
62 mI2sNum(i2s_num),
63 mAudioChannel(mic_channel),
64 mSampleRate(sample_rate),
65 mBitResolution(bit_resolution),
66 mCommFormat(comm_format),
67 mInvert(invert) {}
68};
69
70struct ConfigPdm {
75 bool mInvert = false;
76
77 ConfigPdm(int pin_din, int pin_clk, int i2s_num,
78 u16 sample_rate = AUDIO_DEFAULT_SAMPLE_RATE,
79 bool invert = false) FL_NOEXCEPT : mPinDin(pin_din),
80 mPinClk(pin_clk),
81 mI2sNum(i2s_num),
82 mSampleRate(sample_rate),
83 mInvert(invert) {}
84};
85
86// Teensy Audio Library configuration helpers
87// Note: Teensy uses FIXED hardware pins that cannot be changed in software.
88// Pin assignments vary by board model - see Teensy Audio Library documentation.
89//
90// Teensy 3.x I2S pins (only I2S1 available):
91// BCLK=9, MCLK=11, RX=13, LRCLK=23
92//
93// Teensy 4.x I2S1 pins:
94// BCLK=21, MCLK=23, RX=8, LRCLK=20
95//
96// Teensy 4.x I2S2 pins:
97// BCLK=4, MCLK=33, RX=5, LRCLK=3
98namespace TeensyI2S {
99enum class I2SPort {
100 I2S1 = 0, // Primary I2S (available on all Teensy 3.x and 4.x)
101 I2S2 = 1 // Secondary I2S (Teensy 4.x only)
102};
103
104// Get LRCLK (WS) pin for given I2S port
105constexpr int getPinWS(I2SPort port) FL_NOEXCEPT {
106#if defined(FL_IS_TEENSY_3X) || defined(FL_IS_TEENSY_35) || \
107 defined(FL_IS_TEENSY_36)
108 // Teensy 3.x - only I2S1 available
109 return (port == I2SPort::I2S1) ? 23 : -1;
110#elif defined(FL_IS_TEENSY_4X)
111 // Teensy 4.x - I2S1 and I2S2 available
112 return (port == I2SPort::I2S1) ? 20 : 3;
113#else
114 // Unknown platform - evaluate parameter to avoid unused warning
115 return (port == I2SPort::I2S1 || port == I2SPort::I2S2) ? -1 : -1;
116#endif
117}
118
119// Get RX (SD) pin for given I2S port
120constexpr int getPinSD(I2SPort port) FL_NOEXCEPT {
121#if defined(FL_IS_TEENSY_3X) || defined(FL_IS_TEENSY_35) || \
122 defined(FL_IS_TEENSY_36)
123 // Teensy 3.x
124 return (port == I2SPort::I2S1) ? 13 : -1;
125#elif defined(FL_IS_TEENSY_4X)
126 // Teensy 4.x
127 return (port == I2SPort::I2S1) ? 8 : 5;
128#else
129 // Unknown platform - evaluate parameter to avoid unused warning
130 return (port == I2SPort::I2S1 || port == I2SPort::I2S2) ? -1 : -1;
131#endif
132}
133
134// Get BCLK pin for given I2S port
135constexpr int getPinCLK(I2SPort port) FL_NOEXCEPT {
136#if defined(FL_IS_TEENSY_3X) || defined(FL_IS_TEENSY_35) || \
137 defined(FL_IS_TEENSY_36)
138 // Teensy 3.x
139 return (port == I2SPort::I2S1) ? 9 : -1;
140#elif defined(FL_IS_TEENSY_4X)
141 // Teensy 4.x
142 return (port == I2SPort::I2S1) ? 21 : 4;
143#else
144 // Unknown platform - evaluate parameter to avoid unused warning
145 return (port == I2SPort::I2S1 || port == I2SPort::I2S2) ? -1 : -1;
146#endif
147}
148} // namespace TeensyI2S
149
150class Config : public fl::variant<ConfigI2S, ConfigPdm> {
151 public:
152 // The most common microphone on Amazon as of 2025-September.
153 static Config CreateInmp441(int pin_ws, int pin_sd, int pin_clk,
154 AudioChannel channel, u16 sample_rate = 44100ul,
155 int i2s_num = 0) FL_NOEXCEPT {
156 ConfigI2S config(pin_ws, pin_sd, pin_clk, i2s_num, channel, sample_rate,
157 16);
158 Config out(config);
160 return out;
161 }
162
163 // ICS-43434 I2S MEMS microphone (TDK InvenSense).
164 static Config CreateIcs43434(int pin_ws, int pin_sd, int pin_clk,
165 AudioChannel channel,
166 u16 sample_rate = 44100ul,
167 int i2s_num = 0) FL_NOEXCEPT {
168 ConfigI2S config(pin_ws, pin_sd, pin_clk, i2s_num, channel, sample_rate,
169 16);
170 Config out(config);
172 return out;
173 }
174
175 // Generic I2S MEMS microphone (applies average MEMS correction).
176 static Config CreateGenericMEMS(int pin_ws, int pin_sd, int pin_clk,
177 AudioChannel channel,
178 u16 sample_rate = 44100ul,
179 int i2s_num = 0) FL_NOEXCEPT {
180 ConfigI2S config(pin_ws, pin_sd, pin_clk, i2s_num, channel, sample_rate,
181 16);
182 Config out(config);
184 return out;
185 }
186
187 // Factory method for Teensy I2S microphones (INMP441, ICS43432,
188 // SPH0645LM4H, etc.) Teensy uses fixed hardware pins - see TeensyI2S
189 // namespace for pin assignments. Example: auto config =
190 // AudioConfig::CreateTeensyI2S(TeensyI2S::I2S1, Right, 44100);
194 u16 sample_rate = AUDIO_DEFAULT_SAMPLE_RATE,
195 u8 bit_resolution = AUDIO_DEFAULT_BIT_RESOLUTION,
197 ConfigI2S config(
198 TeensyI2S::getPinWS(port), // pin_ws (LRCLK)
199 TeensyI2S::getPinSD(port), // pin_sd (RX)
200 TeensyI2S::getPinCLK(port), // pin_clk (BCLK)
201 static_cast<int>(port), // i2s_num
202 channel, sample_rate, bit_resolution,
203 I2SCommFormat::Philips, // comm_format (Teensy uses I2S Philips)
204 false // invert
205 );
206 Config out(config);
207 out.setMicProfile(profile);
208 return out;
209 }
210
211 // SPM1423 PDM microphone (Knowles, common on ESP32-S3 Sense boards).
212 static Config CreateSpm1423Pdm(int pin_din, int pin_clk,
213 u16 sample_rate = AUDIO_DEFAULT_SAMPLE_RATE,
214 int i2s_num = 0,
215 bool invert = false) FL_NOEXCEPT {
216 Config out(ConfigPdm(pin_din, pin_clk, i2s_num, sample_rate, invert));
218 return out;
219 }
220
221 // Generic PDM microphone factory method.
222 // pin_din: PDM data in pin, pin_clk: PDM clock pin.
223 static Config CreatePdm(int pin_din, int pin_clk,
224 u16 sample_rate = AUDIO_DEFAULT_SAMPLE_RATE,
225 int i2s_num = 0, bool invert = false,
227 Config out(
228 ConfigPdm(pin_din, pin_clk, i2s_num, sample_rate, invert));
229 out.setMicProfile(profile);
230 return out;
231 }
232
234 : fl::variant<ConfigI2S, ConfigPdm>(config) {}
236 : fl::variant<ConfigI2S, ConfigPdm>(config) {}
237
239 void setGain(float gain) FL_NOEXCEPT { mGain = gain; }
240 float getGain() const FL_NOEXCEPT { return mGain; }
241
245 mMicProfile = profile;
246 }
248
249 private:
250 float mGain = 1.0f;
252};
253
254class IInput {
255 public:
256 // This is the single factory function for creating the audio source. If the
257 // creation was successful, then the return value will be non-null. If the
258 // creation was not successful, then the return value will be null and the
259 // error_message will be set to a non-empty string. Keep in mind that the
260 // Config is a variant type. Many esp types do not support all the types in
261 // the variant. For example, the ConfigPdm is not supported on the ESP32-C3
262 // and in this case it will return a null pointer and the error_message will
263 // be set to a non-empty string. Implimentation notes:
264 // It's very important that the implimentation uses a esp task / interrupt
265 // to fill in the buffer. The reason is that there will be looooong delays
266 // during FastLED show() on some esp platforms, for example idf 4.4. If we
267 // do poll only, then audio buffers can be dropped. However if using a
268 // task then the audio buffers will be set internally via an interrupt /
269 // queue and then they can just be popped off the queue.
271 create(const Config &config,
272 fl::string *error_message = nullptr) FL_NOEXCEPT;
273
274 virtual ~IInput() FL_NOEXCEPT = default;
275 // Starts the audio source.
276 virtual void start() FL_NOEXCEPT = 0;
277 // Stops the audio source, call this before light sleep.
278 virtual void stop() FL_NOEXCEPT = 0;
279
280 virtual bool error(fl::string *msg = nullptr)
281 FL_NOEXCEPT = 0; // if an error occured then query it here.
282 // Read audio data and return as Sample with calculated timestamp.
283 // Returns invalid Sample on error or when no data is available.
284 virtual Sample read() FL_NOEXCEPT = 0;
285
287 void setGain(float gain) FL_NOEXCEPT { mGain = gain; }
288 float getGain() const FL_NOEXCEPT { return mGain; }
289
290 // Read all available audio data and return as Sample. All AudioSamples
291 // returned by this will be valid. Gain is applied to each sample.
293 size_t count = 0;
294 while (true) {
295 Sample sample = read();
296 if (sample.isValid()) {
297 if (mGain != 1.0f) {
298 sample.applyGain(mGain);
299 }
300 out->push_back(sample);
301 count++;
302 } else {
303 break;
304 }
305 }
306 return count;
307 }
308
309 private:
310 float mGain = 1.0f;
311};
312
313// Free function for audio input creation - can be overridden by
314// platform-specific implementations
316 const Config &config,
317 fl::string *error_message = nullptr) FL_NOEXCEPT FL_LINK_WEAK;
318
319} // namespace audio
320} // namespace fl
fl::UIAudio audio("Audio Input")
float getGain() const FL_NOEXCEPT
Definition input.h:240
MicProfile mMicProfile
Definition input.h:251
static Config CreateTeensyI2S(TeensyI2S::I2SPort port=TeensyI2S::I2SPort::I2S1, AudioChannel channel=AudioChannel::Right, u16 sample_rate=AUDIO_DEFAULT_SAMPLE_RATE, u8 bit_resolution=AUDIO_DEFAULT_BIT_RESOLUTION, MicProfile profile=MicProfile::GenericMEMS) FL_NOEXCEPT
Definition input.h:191
Config(const ConfigPdm &config) FL_NOEXCEPT
Definition input.h:235
static Config CreateGenericMEMS(int pin_ws, int pin_sd, int pin_clk, AudioChannel channel, u16 sample_rate=44100ul, int i2s_num=0) FL_NOEXCEPT
Definition input.h:176
static Config CreateSpm1423Pdm(int pin_din, int pin_clk, u16 sample_rate=AUDIO_DEFAULT_SAMPLE_RATE, int i2s_num=0, bool invert=false) FL_NOEXCEPT
Definition input.h:212
void setMicProfile(MicProfile profile) FL_NOEXCEPT
Microphone pink noise correction profile.
Definition input.h:244
void setGain(float gain) FL_NOEXCEPT
Digital gain applied to all input samples. Default 1.0 (no change).
Definition input.h:239
MicProfile getMicProfile() const FL_NOEXCEPT
Definition input.h:247
static Config CreateInmp441(int pin_ws, int pin_sd, int pin_clk, AudioChannel channel, u16 sample_rate=44100ul, int i2s_num=0) FL_NOEXCEPT
Definition input.h:153
static Config CreateIcs43434(int pin_ws, int pin_sd, int pin_clk, AudioChannel channel, u16 sample_rate=44100ul, int i2s_num=0) FL_NOEXCEPT
Definition input.h:164
Config(const ConfigI2S &config) FL_NOEXCEPT
Definition input.h:233
static Config CreatePdm(int pin_din, int pin_clk, u16 sample_rate=AUDIO_DEFAULT_SAMPLE_RATE, int i2s_num=0, bool invert=false, MicProfile profile=MicProfile::None) FL_NOEXCEPT
Definition input.h:223
virtual void start() FL_NOEXCEPT=0
void setGain(float gain) FL_NOEXCEPT
Digital gain applied to raw PCM samples. Default 1.0 (no change).
Definition input.h:287
virtual Sample read() FL_NOEXCEPT=0
virtual bool error(fl::string *msg=nullptr) FL_NOEXCEPT=0
virtual void stop() FL_NOEXCEPT=0
size_t readAll(fl::vector_inlined< Sample, 16 > *out) FL_NOEXCEPT
Definition input.h:292
virtual ~IInput() FL_NOEXCEPT=default
float getGain() const FL_NOEXCEPT
Definition input.h:288
static fl::shared_ptr< IInput > create(const Config &config, fl::string *error_message=nullptr) FL_NOEXCEPT
#define AUDIO_DEFAULT_SAMPLE_RATE
Definition input.h:17
#define AUDIO_DEFAULT_BIT_RESOLUTION
Definition input.h:18
constexpr int getPinSD(I2SPort port) FL_NOEXCEPT
Definition input.h:120
constexpr int getPinWS(I2SPort port) FL_NOEXCEPT
Definition input.h:105
constexpr int getPinCLK(I2SPort port) FL_NOEXCEPT
Definition input.h:135
FL_LINK_WEAK fl::shared_ptr< IInput > platform_create_audio_input(const Config &config, fl::string *error_message)
MicProfile
Microphone frequency response correction profile.
@ GenericMEMS
Generic MEMS microphone (moderate bass rolloff)
@ None
No correction (flat response assumed)
@ INMP441
InvenSense INMP441 MEMS mic (most common)
@ ICS43434
InvenSense ICS-43434 MEMS mic.
@ SPM1423
Knowles SPM1423 MEMS mic.
I2SCommFormat
Definition input.h:34
AudioChannel
Definition input.h:27
unsigned char u8
Definition stdint.h:131
CRGB sample(const CRGB *grid, const XYMap &xyMap, float x, float y, SampleMode mode)
Sample a pixel from a 2D CRGB grid at floating-point coordinates.
Definition sample.cpp.hpp:9
VectorN< T, INLINED_SIZE > vector_inlined
Definition vector.h:1133
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NOEXCEPT
#define FL_LINK_WEAK
I2SCommFormat mCommFormat
Definition input.h:54
ConfigI2S(int pin_ws, int pin_sd, int pin_clk, int i2s_num, AudioChannel mic_channel, u16 sample_rate, u8 bit_resolution, I2SCommFormat comm_format=I2SCommFormat::Philips, bool invert=false) FL_NOEXCEPT
Definition input.h:56
AudioChannel mAudioChannel
Definition input.h:51
ConfigPdm(int pin_din, int pin_clk, int i2s_num, u16 sample_rate=AUDIO_DEFAULT_SAMPLE_RATE, bool invert=false) FL_NOEXCEPT
Definition input.h:77