Arduino SAM
All Classes Files Functions Enumerations Pages
sam_arduino_out.h
1 
12 #include "Arduino.h"
13 #include "reciter.h"
14 #include "sam.h"
15 #include "sam_config.h"
16 
17 #if defined(ESP32) && LEGACY_ESP_I2S
18 #include "driver/i2s.h"
19 #include "freertos/queue.h"
20 #endif
21 
22 // User Callback
23 typedef void (*sam_callback)(size_t size, int16_t *values);
24 
25 // Application Callback
26 static void OutputByteCallback(void *cbdata, unsigned char b);
27 static uint32_t SAM_sample_rate = 22050;
33  public:
34  virtual ~SAMOutputBase() {}
35 
36  virtual void open() {
37  SAM_LOG("SAMOutputBase::open");
38  is_open = true;
39  };
40 
41  virtual int close() {
42  is_open = false;
43  return 0;
44  }
45 
46  virtual int drain() { return 0; }
47 
48  virtual int channels() { return channel_count; }
49 
50  virtual void setChannels(int channels) { channel_count = channels; }
51 
52  virtual int bitsPerSample() { return 16; }
53 
54  virtual bool isOpen() { return is_open; }
55 
56  static uint32_t sampleRate() {
57  return SAM_sample_rate; // 44100;
58  }
59 
60  static void setSampleRate(uint32_t rate) {
61  SAM_sample_rate = rate; // 44100;
62  }
63 
64  virtual const char *name() = 0;
65 
66  virtual bool write(byte *buff, int bytes_count) = 0;
67 
68  protected:
69  bool is_open = false;
70  int channel_count = -1;
71  int bits_per_sample = -1;
72 };
73 
79  public:
80  SAMOutputCallback(sam_callback cb) { callback = cb; }
81 
82  virtual bool write(byte *buffer, int bytes_count) {
83  SAM_LOG("SAMOutputCallback::write: %d", bytes_count);
84  int size = bytes_count;
85  callback(size, (int16_t *)buffer);
86  return true;
87  }
88 
89  const char *name() { return "SAMOutputCallback"; }
90 
91  protected:
92  sam_callback callback;
93 };
94 
95 #if defined(ESP32) && LEGACY_ESP_I2S
96 #if __has_include("esp_arduino_version.h")
97 #include "esp_arduino_version.h"
98 #endif
99 
100 #if !defined(ESP_IDF_VERSION_MAJOR)
101 #define ESP_IDF_VERSION_MAJOR 2
102 #endif
103 
104 #if ESP_IDF_VERSION_MAJOR < 4
105 #define I2S_COMM_FORMAT_STAND_I2S \
106  (I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB)
107 #endif
108 
116 class SAMOutputI2S : public SAMOutputBase {
117  public:
118  SAMOutputI2S(i2s_port_t i2s_num = I2S_NUM_0) {
119  this->i2s_num = i2s_num;
120  setupDefaultConfig();
121  setupDefaultPins();
122  }
123 
124  SAMOutputI2S(i2s_port_t i2s_num, i2s_config_t cfg) {
125  this->i2s_num = i2s_num;
126  this->i2s_config = cfg;
127  setupDefaultPins();
128  }
129 
130  SAMOutputI2S(i2s_port_t i2s_num, i2s_config_t cfg, i2s_pin_config_t pins) {
131  this->i2s_num = i2s_num;
132  this->i2s_config = cfg;
133  this->pin_config = pins;
134  }
135 
136  ~SAMOutputI2S() {
137  if (isOpen()) {
138  close();
139  }
140  }
141 
142  const char *name() { return "SAMOutputI2S"; }
143 
144  int channels() { return 2; }
145 
146  virtual void open() {
147  // update sample sample_rate
148  i2s_config.sample_rate = SAMOutputBase::sampleRate();
149  SAM_LOG("setting sample rate for I2S: %d", i2s_config.sample_rate);
150  // install driver
151  if (i2s_driver_install(i2s_num, &i2s_config, 0, NULL) != ESP_OK) {
152  ESP_LOGE(TAG, "Failed to install i2s");
153  }
154  if (i2s_config.mode & I2S_MODE_DAC_BUILT_IN) {
155  // for internal DAC, this will enable both of the internal channels
156  SAM_LOG("i2s_set_pin: %s", "internal DAC");
157  if (i2s_set_pin(i2s_num, NULL) != ESP_OK) {
158  ESP_LOGE(TAG, "Failed to set i2s pins");
159  }
160  } else {
161  // define pins for external DAC
162  SAM_LOG("i2s_set_pin: %s", "external DAC");
163  if (i2s_set_pin(i2s_num, &pin_config) != ESP_OK) {
164  ESP_LOGE(TAG, "Failed to set i2s pins");
165  }
166  }
167  SAMOutputBase::open();
168  };
169 
170  virtual int close() {
171  if (i2s_driver_uninstall(i2s_num) != ESP_OK) {
172  ESP_LOGE(TAG, "Failed to uninstall i2s");
173  }
174 
175  return SAMOutputBase::close();
176  }
177 
178  virtual int drain() {
179  i2s_zero_dma_buffer(i2s_num);
180  return 0;
181  }
182 
183  virtual bool write(byte *buffer, int bytes_count) {
184  SAM_LOG("SAMOutputI2S::write: %d", bytes_count);
185  size_t i2s_bytes_write;
186 
187  if (this->i2s_config.mode & I2S_MODE_DAC_BUILT_IN) {
188  for (int i = 0; i < bytes_count; i++) {
189  buffer[i] = buffer[i] + 0x8000;
190  }
191  }
192 
193  if (i2s_write(i2s_num, buffer, bytes_count,
194  &i2s_bytes_write, portMAX_DELAY) != ESP_OK) {
195  ESP_LOGE(TAG, "i2s_write failed!");
196  return false;
197  }
198  return true;
199  ;
200  }
201 
202  void setChannels(int ch) {
203  if (ch != 2) SAM_LOG("Channels is not supported for this output type");
204  }
205 
206  void setBitsPerSample(int bps) {
207  if (bps != 16) SAM_LOG("Channels is not supported for this output type");
208  }
209 
210  protected:
211  i2s_port_t i2s_num;
212  i2s_config_t i2s_config;
213  i2s_pin_config_t pin_config;
214  const char *TAG = "SAMOutputI2S";
215 
216  void setupDefaultConfig() {
217  const i2s_config_t i2s_config_default = {
218  .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
219  .sample_rate = this->sampleRate(),
220  .bits_per_sample = (i2s_bits_per_sample_t)16,
221  .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
222  .communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_I2S,
223  .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // lowest interrupt priority
224  .dma_buf_count = 10,
225  .dma_buf_len = 64,
226  .use_apll = false,
227  .tx_desc_auto_clear =
228  true // avoiding noise in case of data unavailability
229  };
230  this->i2s_config = i2s_config_default;
231  }
232 
233  void setupDefaultPins() {
234  static const i2s_pin_config_t pin_config_default = {
235  .bck_io_num = 26,
236  .ws_io_num = 25,
237  .data_out_num = 22,
238  .data_in_num = I2S_PIN_NO_CHANGE};
239  this->pin_config = pin_config_default;
240  }
241 };
242 #endif
243 
249  public:
250  SAMOutputStream(Print &out) { this->out_ptr = &out; }
251  const char *name() { return "SAMOutputStream"; }
252 
253  virtual int drain() { return 0; }
254 
255  virtual bool write(byte *buffer, int bytes_count) {
256  SAM_LOG("SAMOutputStream::write: %d", bytes_count);
257  size_t len = bytes_count;
258 
259  size_t len_written = out_ptr->write((const uint8_t *)buffer, len);
260  if (len != len_written) {
261  SAM_LOG("Error - Could not write all data: %d of %d", len_written, len);
262  return false;
263  }
264  return true;
265  }
266 
267  protected:
268  Print *out_ptr;
269  int output_channels;
270 };
271 
277  public:
278  SAMPrintStream(Print &out) : SAMOutputStream(out) {}
279 
280  const char *name() { return "SAMPrintStream"; }
281 
282  virtual bool write(byte *buffer, int bytes_count) {
283  // copy from 1 to 2 channels
284  int16_t *buffer16 = (int16_t *)buffer;
285  for (int j = 0; j < bytes_count / 2; j++) {
286  out_ptr->print(buffer16[j]);
287  if (j < channels() - 1) {
288  out_ptr->print(",");
289  }
290  if (j == channels() - 1) {
291  out_ptr->println();
292  }
293  }
294 
295  return true;
296  }
297 };
Base Output Class with common functionality.
Definition: sam_arduino_out.h:32
Output via Callback method.
Definition: sam_arduino_out.h:78
Output to I2S for ESP32: This class is obsolete and has been deactivated in the sam_config....
Definition: sam_arduino_out.h:116
Output to Arduino Stream.
Definition: sam_arduino_out.h:248
Write readable string to Arduino Stream.
Definition: sam_arduino_out.h:276