arduino-audio-tools
I2SCodecStream.h
1 #pragma once
2 #include "AudioBoard.h" // install audio-driver library
3 #include "AudioConfig.h"
4 #include "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 
30  bool operator==(I2SCodecConfig alt) {
31  return input_device == alt.input_device &&
32  output_device == alt.output_device && *((AudioInfo *)this) == alt;
33  }
34 
35  bool operator!=(I2SCodecConfig alt) { return !(*this == alt); }
36 };
37 
44 class I2SCodecStream : public AudioStream, public VolumeSupport {
45  public:
47  I2SCodecStream() = default;
55  I2SCodecStream(AudioBoard &board) { setBoard(board); }
57  I2SCodecStream(AudioBoard *board) { setBoard(board); }
58 
61  auto cfg1 = i2s.defaultConfig(mode);
62  I2SCodecConfig cfg;
63  memcpy(&cfg, &cfg1, sizeof(cfg1));
64  cfg.input_device = ADC_INPUT_LINE1;
65  cfg.output_device = DAC_OUTPUT_ALL;
66  cfg.sd_active = true;
67  return cfg;
68  }
69 
70  bool begin() {
71  TRACED();
72  return begin(cfg);
73  }
74 
76  virtual bool begin(I2SCodecConfig cfg) {
77  TRACED();
78  this->cfg = cfg;
79  return begin1();
80  }
81 
83  void end() {
84  TRACED();
85  if (p_board) p_board->end();
86  i2s.end();
87  is_active = false;
88  }
89 
91  virtual void setAudioInfo(AudioInfo info) {
92  TRACEI();
94  i2s.setAudioInfo(info);
95 
96  cfg.sample_rate = info.sample_rate;
98  cfg.channels = info.channels;
99 
100  // update codec_cfg
101  codec_cfg.i2s.bits = toCodecBits(cfg.bits_per_sample);
102  codec_cfg.i2s.rate = toRate(cfg.sample_rate);
103 
104  // return if we we are not ready
105  if (!is_active || p_board == nullptr) {
106  return;
107  }
108 
109  // return if there is nothing to do
110  if (cfg.sample_rate == info.sample_rate &&
111  cfg.bits_per_sample == info.bits_per_sample &&
112  cfg.channels == info.channels) {
113  return;
114  }
115 
116  // update cfg
117  p_board->setConfig(codec_cfg);
118  }
119 
121  virtual size_t write(const uint8_t *data, size_t len) {
122  LOGD("I2SStream::write: %d", len);
123  return i2s.write(data, len);
124  }
125 
127  virtual size_t readBytes(uint8_t *data, size_t len) override {
128  return i2s.readBytes(data, len);
129  }
130 
132  virtual int available() override { return i2s.available(); }
133 
135  virtual int availableForWrite() override { return i2s.availableForWrite(); }
136 
138  bool setVolume(float vol) override {
140  if (!is_active || p_board == nullptr) return false;
141  return p_board->setVolume(vol * 100.0);
142  }
143 
145  float volume() override {
146  if (p_board == nullptr) return 0.0f;
147  return static_cast<float>(p_board->getVolume()) / 100.0f;
148  }
150  float getVolume() { return volume(); }
151 
153  bool setMute(bool mute) {
154  if (p_board == nullptr) return false;
155  return p_board->setMute(mute);
156  }
158  bool setMute(bool mute, int line) {
159  if (p_board == nullptr) return false;
160  return p_board->setMute(mute, line);
161  }
162 
164  bool setPAPower(bool active) {
165  if (p_board == nullptr) return false;
166  return p_board->setPAPower(active);
167  }
168 
170  AudioBoard &board() { return *p_board; }
172  void setBoard(AudioBoard &board) { p_board = &board; }
174  void setBoard(AudioBoard *board) { p_board = board; }
176  bool hasBoard() { return p_board != nullptr; }
177 
179  GpioPin getPinID(PinFunction function) {
180  if (p_board == nullptr) return -1;
181  return p_board->getPins().getPinID(function);
182  }
183 
185  GpioPin getPinID(PinFunction function, int pos) {
186  if (p_board == nullptr) return -1;
187  return p_board->getPins().getPinID(function, pos);
188  }
189 
191  GpioPin getKey(int pos) { return getPinID(PinFunction::KEY, pos); }
192 
194  DriverPins &getPins() { return p_board->getPins(); }
195 
197  I2SDriver *driver() { return i2s.driver(); }
198 
199  protected:
200  I2SStream i2s;
201  I2SCodecConfig cfg;
202  CodecConfig codec_cfg;
203  AudioBoard *p_board = nullptr;
204  bool is_active = false;
205 
206  bool begin1() {
207  setupI2SPins();
208  if (!beginCodec(cfg)) {
209  TRACEE();
210  is_active = false;
211  return false;
212  }
213  is_active = i2s.begin(cfg);
214 
215  // if setvolume was called before begin
216  if (is_active && volume() >= 0.0f) {
217  setVolume(volume());
218  }
219  return is_active;
220  }
221 
222 
224  void setupI2SPins() {
225  // setup pins in i2s config
226  auto i2s = p_board->getPins().getI2SPins();
227  if (i2s) {
228  // determine i2s pins from board definition
229  PinsI2S i2s_pins = i2s.value();
230  cfg.pin_bck = i2s_pins.bck;
231  cfg.pin_mck = i2s_pins.mclk;
232  cfg.pin_ws = i2s_pins.ws;
233  switch (cfg.rx_tx_mode) {
234  case RX_MODE:
235  cfg.pin_data = i2s_pins.data_in;
236  break;
237  case TX_MODE:
238  cfg.pin_data = i2s_pins.data_out;
239  break;
240  default:
241  cfg.pin_data = i2s_pins.data_out;
242  cfg.pin_data_rx = i2s_pins.data_in;
243  break;
244  }
245  }
246  }
247 
248  bool beginCodec(I2SCodecConfig info) {
249  switch (cfg.rx_tx_mode) {
250  case RX_MODE:
251  codec_cfg.input_device = info.input_device;
252  codec_cfg.output_device = DAC_OUTPUT_NONE;
253  break;
254  case TX_MODE:
255  codec_cfg.output_device = info.output_device;
256  codec_cfg.input_device = ADC_INPUT_NONE;
257  break;
258  default:
259  codec_cfg.input_device = info.input_device;
260  codec_cfg.output_device = info.output_device;
261  break;
262  }
263  codec_cfg.sd_active = info.sd_active;
264  LOGD("input: %d", info.input_device);
265  LOGD("output: %d", info.output_device);
266  codec_cfg.i2s.bits = toCodecBits(info.bits_per_sample);
267  codec_cfg.i2s.rate = toRate(info.sample_rate);
268  codec_cfg.i2s.fmt = toFormat(info.i2s_format);
269  // use reverse logic for codec setting
270  codec_cfg.i2s.mode = info.is_master ? MODE_SLAVE : MODE_MASTER;
271  if (p_board == nullptr) return false;
272 
273  // setup driver only on changes
274  return p_board->begin(codec_cfg);
275  }
276 
277  sample_bits_t toCodecBits(int bits) {
278  switch (bits) {
279  case 16:
280  LOGD("BIT_LENGTH_16BITS");
281  return BIT_LENGTH_16BITS;
282  case 24:
283  LOGD("BIT_LENGTH_24BITS");
284  return BIT_LENGTH_24BITS;
285  case 32:
286  LOGD("BIT_LENGTH_32BITS");
287  return BIT_LENGTH_32BITS;
288  }
289  LOGE("Unsupported bits: %d", bits);
290  return BIT_LENGTH_16BITS;
291  }
292  samplerate_t toRate(int rate) {
293  if (rate <= 8000) {
294  LOGD("RATE_8K");
295  return RATE_8K;
296  }
297  if (rate <= 11000) {
298  LOGD("RATE_11K");
299  return RATE_11K;
300  }
301  if (rate <= 16000) {
302  LOGD("RATE_16K");
303  return RATE_16K;
304  }
305  if (rate <= 22050) {
306  LOGD("RATE_22K");
307  return RATE_22K;
308  }
309  if (rate <= 32000) {
310  LOGD("RATE_32K");
311  return RATE_32K;
312  }
313  if (rate <= 44100) {
314  LOGD("RATE_44K");
315  return RATE_44K;
316  }
317  if (rate <= 48000 || rate > 48000) {
318  LOGD("RATE_48K");
319  return RATE_44K;
320  }
321  LOGE("Invalid rate: %d using 44K", rate);
322  return RATE_44K;
323  }
324 
325  i2s_format_t toFormat(I2SFormat fmt) {
326  switch (fmt) {
327  case I2S_PHILIPS_FORMAT:
328  case I2S_STD_FORMAT:
329  LOGD("I2S_NORMAL");
330  return I2S_NORMAL;
331  case I2S_LEFT_JUSTIFIED_FORMAT:
332  case I2S_MSB_FORMAT:
333  LOGD("I2S_LEFT");
334  return I2S_LEFT;
335  case I2S_RIGHT_JUSTIFIED_FORMAT:
336  case I2S_LSB_FORMAT:
337  LOGD("I2S_RIGHT");
338  return I2S_RIGHT;
339  case I2S_PCM:
340  LOGD("I2S_DSP");
341  return I2S_DSP;
342  default:
343  LOGE("unsupported mode");
344  return I2S_NORMAL;
345  }
346  }
347 };
348 
349 } // 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:44
bool hasBoard()
checks if a board has been defined
Definition: I2SCodecStream.h:176
I2SCodecStream(AudioBoard &board)
Default constructor: for available AudioBoard values check audioboard variables in https://pschatzman...
Definition: I2SCodecStream.h:55
bool setMute(bool mute, int line)
Mute / unmute of an individual line (codec)
Definition: I2SCodecStream.h:158
virtual size_t readBytes(uint8_t *data, size_t len) override
Reads the audio data.
Definition: I2SCodecStream.h:127
void setBoard(AudioBoard &board)
(re)defines the board
Definition: I2SCodecStream.h:172
I2SCodecStream(AudioBoard *board)
Provide board via pointer.
Definition: I2SCodecStream.h:57
I2SDriver * driver()
Provides the i2s driver.
Definition: I2SCodecStream.h:197
AudioBoard & board()
Provides the board.
Definition: I2SCodecStream.h:170
virtual int availableForWrite() override
Provides the available audio data.
Definition: I2SCodecStream.h:135
I2SCodecConfig defaultConfig(RxTxMode mode=TX_MODE)
Provides the default configuration.
Definition: I2SCodecStream.h:60
float volume() override
Provides the actual volume (0.0f - 1.0f)
Definition: I2SCodecStream.h:145
virtual void setAudioInfo(AudioInfo info)
updates the sample rate dynamically
Definition: I2SCodecStream.h:91
virtual size_t write(const uint8_t *data, size_t len)
Writes the audio data to I2S.
Definition: I2SCodecStream.h:121
GpioPin getKey(int pos)
Provides the gpio for the indicated key pos.
Definition: I2SCodecStream.h:191
I2SCodecStream()=default
Default Constructor (w/o codec)
bool setMute(bool mute)
Mute / unmote.
Definition: I2SCodecStream.h:153
virtual bool begin(I2SCodecConfig cfg)
Starts the I2S interface.
Definition: I2SCodecStream.h:76
void end()
Stops the I2S interface.
Definition: I2SCodecStream.h:83
void setupI2SPins()
We use the board pins if they are available.
Definition: I2SCodecStream.h:224
GpioPin getPinID(PinFunction function, int pos)
Provides the gpio for the indicated function.
Definition: I2SCodecStream.h:185
float getVolume()
legacy: same as volume()
Definition: I2SCodecStream.h:150
void setBoard(AudioBoard *board)
(re)defines the board
Definition: I2SCodecStream.h:174
bool setPAPower(bool active)
Sets the output of the PA Power Pin.
Definition: I2SCodecStream.h:164
bool setVolume(float vol) override
sets the volume (range 0.0f - 1.0f)
Definition: I2SCodecStream.h:138
GpioPin getPinID(PinFunction function)
Provides the gpio for the indicated function.
Definition: I2SCodecStream.h:179
DriverPins & getPins()
Provides access to the pin information.
Definition: I2SCodecStream.h:194
virtual int available() override
Provides the available audio data.
Definition: I2SCodecStream.h:132
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:205
virtual bool setVolume(float volume)
define the actual volume in the range of 0.0f to 1.0f
Definition: AudioTypes.h:210
RxTxMode
The Microcontroller is the Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Si...
Definition: AudioTypes.h:26
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AnalogAudioArduino.h:12
I2SFormat
I2S Formats.
Definition: AudioTypes.h:419
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
Configuration for I2SCodecStream.
Definition: I2SCodecStream.h:24