arduino-audio-tools
I2SCodecStream.h
1 #pragma once
2 #include "AudioBoard.h" // install audio-driver library
3 #include "AudioConfig.h"
4 #include "AudioTools/CoreAudio/AudioI2S/I2SStream.h"
5 
6 //#pragma GCC diagnostic ignored "-Wclass-memaccess"
7 
8 // Added to be compatible with the AudioKitStream.h
9 #ifndef PIN_AUDIO_KIT_SD_CARD_CS
10 #define PIN_AUDIO_KIT_SD_CARD_CS 13
11 #define PIN_AUDIO_KIT_SD_CARD_MISO 2
12 #define PIN_AUDIO_KIT_SD_CARD_MOSI 15
13 #define PIN_AUDIO_KIT_SD_CARD_CLK 14
14 #endif
15 
16 namespace audio_tools {
17 
24 struct I2SCodecConfig : public I2SConfig {
25  input_device_t input_device = ADC_INPUT_LINE1;
26  output_device_t output_device = DAC_OUTPUT_ALL;
27  // to be compatible with the AudioKitStream -> do not activate SD spi if false
28  bool sd_active = true;
29  // define pin source in driver configuration
30  PinFunction i2s_function = PinFunction::UNDEFINED; //CODEC;
31  bool operator==(I2SCodecConfig alt) {
32  return input_device == alt.input_device &&
33  output_device == alt.output_device && *((AudioInfo *)this) == alt;
34  }
35 
36  bool operator!=(I2SCodecConfig alt) { return !(*this == alt); }
37 };
38 
45 class I2SCodecStream : public AudioStream, public VolumeSupport {
46  public:
48  I2SCodecStream() = default;
56  I2SCodecStream(AudioBoard &board) { setBoard(board); }
58  I2SCodecStream(AudioBoard *board) { setBoard(board); }
59 
62  auto cfg1 = i2s.defaultConfig(mode);
63  I2SCodecConfig cfg;
64  memcpy(&cfg, &cfg1, sizeof(cfg1));
65  cfg.input_device = ADC_INPUT_LINE1;
66  cfg.output_device = DAC_OUTPUT_ALL;
67  cfg.sd_active = true;
68  cfg.rx_tx_mode = mode;
69  return cfg;
70  }
71 
72  bool begin() {
73  TRACED();
74  return begin(cfg);
75  }
76 
78  virtual bool begin(I2SCodecConfig cfg) {
79  TRACED();
80  this->cfg = cfg;
81  this->info = cfg;
82  return begin1();
83  }
84 
86  void end() {
87  TRACED();
88  if (p_board) p_board->end();
89  i2s.end();
90  is_active = false;
91  }
92 
94  virtual void setAudioInfo(AudioInfo info) {
95  TRACEI();
97  i2s.setAudioInfo(info);
98 
99  cfg.sample_rate = info.sample_rate;
100  cfg.bits_per_sample = info.bits_per_sample;
101  cfg.channels = info.channels;
102 
103  // update codec_cfg
104  codec_cfg.i2s.bits = toCodecBits(cfg.bits_per_sample);
105  codec_cfg.i2s.rate = toRate(cfg.sample_rate);
106 
107  // return if we we are not ready
108  if (!is_active || p_board == nullptr) {
109  return;
110  }
111 
112  // return if there is nothing to do
113  if (cfg.sample_rate == info.sample_rate &&
114  cfg.bits_per_sample == info.bits_per_sample &&
115  cfg.channels == info.channels) {
116  return;
117  }
118 
119  // update cfg
120  p_board->setConfig(codec_cfg);
121  }
122 
124  virtual size_t write(const uint8_t *data, size_t len) {
125  LOGD("I2SStream::write: %d", len);
126  return i2s.write(data, len);
127  }
128 
130  virtual size_t readBytes(uint8_t *data, size_t len) override {
131  return i2s.readBytes(data, len);
132  }
133 
135  virtual int available() override { return i2s.available(); }
136 
138  virtual int availableForWrite() override { return i2s.availableForWrite(); }
139 
141  bool setVolume(float vol) override {
143  if (!is_active || p_board == nullptr) return false;
144  return p_board->setVolume(vol * 100.0);
145  }
146 
148  float volume() override {
149  if (p_board == nullptr) return 0.0f;
150  return static_cast<float>(p_board->getVolume()) / 100.0f;
151  }
153  float getVolume() { return volume(); }
154 
156  bool setMute(bool mute) {
157  if (p_board == nullptr) return false;
158  return p_board->setMute(mute);
159  }
161  bool setMute(bool mute, int line) {
162  if (p_board == nullptr) return false;
163  return p_board->setMute(mute, line);
164  }
165 
167  bool setPAPower(bool active) {
168  if (p_board == nullptr) return false;
169  return p_board->setPAPower(active);
170  }
171 
173  bool setInputVolume(float vol){
174  if (!is_active || p_board == nullptr) return false;
175  return p_board->setInputVolume(100.0 * vol);
176  }
177 
179  AudioBoard &board() { return *p_board; }
181  void setBoard(AudioBoard &board) { p_board = &board; }
183  void setBoard(AudioBoard *board) { p_board = board; }
185  bool hasBoard() { return p_board != nullptr; }
186 
188  GpioPin getPinID(PinFunction function) {
189  if (p_board == nullptr) return -1;
190  return p_board->getPins().getPinID(function);
191  }
192 
194  GpioPin getPinID(PinFunction function, int pos) {
195  if (p_board == nullptr) return -1;
196  return p_board->getPins().getPinID(function, pos);
197  }
198 
200  GpioPin getKey(int pos) { return getPinID(PinFunction::KEY, pos); }
201 
203  DriverPins &getPins() { return p_board->getPins(); }
204 
206  I2SDriver *driver() { return i2s.driver(); }
207 
208  protected:
209  I2SStream i2s;
210  I2SCodecConfig cfg;
211  CodecConfig codec_cfg;
212  AudioBoard *p_board = nullptr;
213  bool is_active = false;
214 
215  bool begin1() {
216  TRACED();
218  setupI2SPins();
219  if (!beginCodec(cfg)) {
220  TRACEE();
221  is_active = false;
222  return false;
223  }
224  is_active = i2s.begin(cfg);
225 
226  // if setvolume was called before begin
227  float tobeVol = VolumeSupport::volume();
228  if (is_active && tobeVol >= 0.0f) {
229  setVolume(tobeVol);
230  }
231  return is_active;
232  }
233 
236  if (cfg.i2s_function == PinFunction::UNDEFINED){
237  if (cfg.rx_tx_mode == RX_MODE){
238  auto i2s = p_board->getPins().getI2SPins(PinFunction::CODEC_ADC);
239  if (i2s){
240  cfg.i2s_function = PinFunction::CODEC_ADC;
241  LOGI("using i2s_function: CODEC_ADC");
242  } else {
243  cfg.i2s_function = PinFunction::CODEC;
244  }
245  } else {
246  cfg.i2s_function = PinFunction::CODEC;
247  }
248  }
249  }
250 
252  void setupI2SPins() {
253  TRACED();
254  // determine relevant I2S pins from driver configuration
255  auto i2s = getI2SPins();
256  if (i2s) {
257  // determine i2s pins from board definition
258  PinsI2S i2s_pins = i2s.value();
259  cfg.pin_bck = i2s_pins.bck;
260  cfg.pin_mck = i2s_pins.mclk;
261  cfg.pin_ws = i2s_pins.ws;
262  switch (cfg.rx_tx_mode) {
263  case RX_MODE:
264  cfg.pin_data = i2s_pins.data_in;
265  break;
266  case TX_MODE:
267  cfg.pin_data = i2s_pins.data_out;
268  break;
269  default:
270  cfg.pin_data = i2s_pins.data_out;
271  cfg.pin_data_rx = i2s_pins.data_in;
272  break;
273  }
274  }
275  }
276 
277  audio_driver_local::Optional<PinsI2S> getI2SPins(){
278  TRACED();
279  audio_driver_local::Optional<PinsI2S> i2s;
280  // Deterine I2S pins
281  return p_board->getPins().getI2SPins(cfg.i2s_function);
282  }
283 
284  bool beginCodec(I2SCodecConfig info) {
285  TRACED();
286  switch (cfg.rx_tx_mode) {
287  case RX_MODE:
288  codec_cfg.input_device = info.input_device;
289  codec_cfg.output_device = DAC_OUTPUT_NONE;
290  break;
291  case TX_MODE:
292  codec_cfg.output_device = info.output_device;
293  codec_cfg.input_device = ADC_INPUT_NONE;
294  break;
295  default:
296  codec_cfg.input_device = info.input_device;
297  codec_cfg.output_device = info.output_device;
298  break;
299  }
300  codec_cfg.sd_active = info.sd_active;
301  LOGD("input: %d", info.input_device);
302  LOGD("output: %d", info.output_device);
303  codec_cfg.i2s.bits = toCodecBits(info.bits_per_sample);
304  codec_cfg.i2s.rate = toRate(info.sample_rate);
305  codec_cfg.i2s.fmt = toFormat(info.i2s_format);
306  // use reverse logic for codec setting
307  codec_cfg.i2s.mode = info.is_master ? MODE_SLAVE : MODE_MASTER;
308  if (p_board == nullptr) return false;
309 
310  // setup driver only on changes
311  return p_board->begin(codec_cfg);
312  }
313 
314  sample_bits_t toCodecBits(int bits) {
315  switch (bits) {
316  case 16:
317  LOGD("BIT_LENGTH_16BITS");
318  return BIT_LENGTH_16BITS;
319  case 24:
320  LOGD("BIT_LENGTH_24BITS");
321  return BIT_LENGTH_24BITS;
322  case 32:
323  LOGD("BIT_LENGTH_32BITS");
324  return BIT_LENGTH_32BITS;
325  }
326  LOGE("Unsupported bits: %d", bits);
327  return BIT_LENGTH_16BITS;
328  }
329  samplerate_t toRate(int rate) {
330  if (rate <= 8000) {
331  LOGD("RATE_8K");
332  return RATE_8K;
333  }
334  if (rate <= 11000) {
335  LOGD("RATE_11K");
336  return RATE_11K;
337  }
338  if (rate <= 16000) {
339  LOGD("RATE_16K");
340  return RATE_16K;
341  }
342  if (rate <= 22050) {
343  LOGD("RATE_22K");
344  return RATE_22K;
345  }
346  if (rate <= 32000) {
347  LOGD("RATE_32K");
348  return RATE_32K;
349  }
350  if (rate <= 44100) {
351  LOGD("RATE_44K");
352  return RATE_44K;
353  }
354  if (rate <= 48000 || rate > 48000) {
355  LOGD("RATE_48K");
356  return RATE_44K;
357  }
358  LOGE("Invalid rate: %d using 44K", rate);
359  return RATE_44K;
360  }
361 
362  i2s_format_t toFormat(I2SFormat fmt) {
363  switch (fmt) {
364  case I2S_PHILIPS_FORMAT:
365  case I2S_STD_FORMAT:
366  LOGD("I2S_NORMAL");
367  return I2S_NORMAL;
368  case I2S_LEFT_JUSTIFIED_FORMAT:
369  case I2S_MSB_FORMAT:
370  LOGD("I2S_LEFT");
371  return I2S_LEFT;
372  case I2S_RIGHT_JUSTIFIED_FORMAT:
373  case I2S_LSB_FORMAT:
374  LOGD("I2S_RIGHT");
375  return I2S_RIGHT;
376  case I2S_PCM:
377  LOGD("I2S_DSP");
378  return I2S_DSP;
379  default:
380  LOGE("unsupported mode");
381  return I2S_NORMAL;
382  }
383  }
384 };
385 
386 } // namespace audio_tools
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
I2S Stream which also sets up a codec chip and i2s.
Definition: I2SCodecStream.h:45
bool hasBoard()
checks if a board has been defined
Definition: I2SCodecStream.h:185
I2SCodecStream(AudioBoard &board)
Default constructor: for available AudioBoard values check audioboard variables in https://pschatzman...
Definition: I2SCodecStream.h:56
bool setMute(bool mute, int line)
Mute / unmute of an individual line (codec)
Definition: I2SCodecStream.h:161
virtual size_t readBytes(uint8_t *data, size_t len) override
Reads the audio data.
Definition: I2SCodecStream.h:130
void setBoard(AudioBoard &board)
(re)defines the board
Definition: I2SCodecStream.h:181
I2SCodecStream(AudioBoard *board)
Provide board via pointer.
Definition: I2SCodecStream.h:58
I2SDriver * driver()
Provides the i2s driver.
Definition: I2SCodecStream.h:206
AudioBoard & board()
Provides the board.
Definition: I2SCodecStream.h:179
virtual int availableForWrite() override
Provides the available audio data.
Definition: I2SCodecStream.h:138
bool setInputVolume(float vol)
Sets the volume of the microphone (if available)
Definition: I2SCodecStream.h:173
I2SCodecConfig defaultConfig(RxTxMode mode=TX_MODE)
Provides the default configuration.
Definition: I2SCodecStream.h:61
float volume() override
Provides the actual volume (0.0f - 1.0f)
Definition: I2SCodecStream.h:148
virtual void setAudioInfo(AudioInfo info)
updates the sample rate dynamically
Definition: I2SCodecStream.h:94
virtual size_t write(const uint8_t *data, size_t len)
Writes the audio data to I2S.
Definition: I2SCodecStream.h:124
GpioPin getKey(int pos)
Provides the gpio for the indicated key pos.
Definition: I2SCodecStream.h:200
I2SCodecStream()=default
Default Constructor (w/o codec)
bool setMute(bool mute)
Mute / unmote.
Definition: I2SCodecStream.h:156
virtual bool begin(I2SCodecConfig cfg)
Starts the I2S interface.
Definition: I2SCodecStream.h:78
void setupI2SFunction()
if the cfg.i2s_function was not defined we determine the "correct" default value
Definition: I2SCodecStream.h:235
void end()
Stops the I2S interface.
Definition: I2SCodecStream.h:86
void setupI2SPins()
We use the board pins if they are available.
Definition: I2SCodecStream.h:252
GpioPin getPinID(PinFunction function, int pos)
Provides the gpio for the indicated function.
Definition: I2SCodecStream.h:194
float getVolume()
legacy: same as volume()
Definition: I2SCodecStream.h:153
void setBoard(AudioBoard *board)
(re)defines the board
Definition: I2SCodecStream.h:183
bool setPAPower(bool active)
Sets the output of the PA Power Pin.
Definition: I2SCodecStream.h:167
bool setVolume(float vol) override
sets the volume (range 0.0f - 1.0f)
Definition: I2SCodecStream.h:141
GpioPin getPinID(PinFunction function)
Provides the gpio for the indicated function.
Definition: I2SCodecStream.h:188
DriverPins & getPins()
Provides access to the pin information.
Definition: I2SCodecStream.h:203
virtual int available() override
Provides the available audio data.
Definition: I2SCodecStream.h:135
Configuration for ESP32 legacy i2s.
Definition: I2SConfigESP32.h:23
RxTxMode rx_tx_mode
public settings
Definition: I2SConfigESP32.h:59
Basic I2S API - for the ESP32. If we receive 1 channel, we expand the result to 2 channels.
Definition: I2SESP32.h:27
We support the Stream interface for the I2S access. In addition we allow a separate mute pin which mi...
Definition: I2SStream.h:33
virtual size_t readBytes(uint8_t *data, size_t len) override
Reads the audio data.
Definition: I2SStream.h:122
I2SDriver * driver()
Provides access to the driver.
Definition: I2SStream.h:135
virtual int availableForWrite() override
Provides the available audio data.
Definition: I2SStream.h:130
I2SConfig defaultConfig(RxTxMode mode=TX_MODE)
Provides the default configuration.
Definition: I2SStream.h:49
virtual void setAudioInfo(AudioInfo info)
updates the sample rate dynamically
Definition: I2SStream.h:92
virtual size_t write(const uint8_t *data, size_t len)
Writes the audio data to I2S.
Definition: I2SStream.h:115
void end()
Stops the I2S interface.
Definition: I2SStream.h:84
virtual int available() override
Provides the available audio data.
Definition: I2SStream.h:127
Supports the setting and getting of the volume.
Definition: AudioTypes.h:207
virtual float volume()
provides the actual volume in the range of 0.0f to 1.0f
Definition: AudioTypes.h:210
virtual bool setVolume(float volume)
define the actual volume in the range of 0.0f to 1.0f
Definition: AudioTypes.h:212
RxTxMode
The Microcontroller is the Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Si...
Definition: AudioTypes.h:28
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AudioConfig.h:868
I2SFormat
I2S Formats.
Definition: AudioTypes.h:436
Basic Audio information which drives e.g. I2S.
Definition: AudioTypes.h:52
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition: AudioTypes.h:55
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition: AudioTypes.h:57
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition: AudioTypes.h:59
Configuration for I2SCodecStream.
Definition: I2SCodecStream.h:24