text: /*
   * ESPRESSIF MIT License
   *
   * Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
   *
   * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
   * it is free of charge, to any person obtaining a copy of this software and associated
   * documentation files (the "Software"), to deal in the Software without restriction, including
   * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
   * and/or sell copies of the Software, and to permit persons to whom the Software is furnished
   * to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in all copies or
   * substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
   * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
   * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
   * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   *
   */
  
  #include <stdlib.h>
  #include <string.h>
  
  #include "freertos/FreeRTOS.h"
  #include "freertos/ringbuf.h"
  #include "freertos/semphr.h"
  #include "freertos/task.h"
  
  #include "driver/i2s.h"
  #include "driver/dac.h"
  #include "esp_log.h"
  #include "esp_err.h"
  
  #include "audio_common.h"
  #include "audio_mem.h"
  #include "audio_element.h"
  #include "i2s_stream.h"
  #include "esp_alc.h"
  #include "board_pins_config.h"
  
  static const char *TAG = "I2S_STREAM";
  
  #if defined(ESP_IDF_VERSION)
  #if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 2, 0))
  #define SOC_I2S_SUPPORTS_ADC_DAC 1
  #endif
  
  #else
  #define SOC_I2S_SUPPORTS_ADC_DAC 1
  #endif // defined(ESP_IDF_VERSION)
  
  typedef struct i2s_stream {
      audio_stream_type_t type;
      i2s_stream_cfg_t    config;
      bool                is_open;
      bool                use_alc;
      void                *volume_handle;
      int                 volume;
      bool                uninstall_drv;
  } i2s_stream_t;
  
  static esp_err_t i2s_mono_fix(int bits, uint8_t *sbuff, uint32_t len)
  {
      if (bits == 16) {
          int16_t *temp_buf = (int16_t *)sbuff;
          int16_t temp_box;
          int k = len >> 1;
          for (int i = 0; i < k; i += 2) {
              temp_box = temp_buf[i];
              temp_buf[i] = temp_buf[i + 1];
              temp_buf[i + 1] = temp_box;
          }
      } else if (bits == 32) {
          int32_t *temp_buf = (int32_t *)sbuff;
          int32_t temp_box;
          int k = len >> 2;
          for (int i = 0; i < k; i += 4) {
              temp_box = temp_buf[i];
              temp_buf[i] = temp_buf[i + 1];
              temp_buf[i + 1] = temp_box;
          }
      } else {
          ESP_LOGE(TAG, "%s %dbits is not supported", __func__, bits);
          return ESP_FAIL;
      }
      return ESP_OK;
  }
  
  #if SOC_I2S_SUPPORTS_ADC_DAC
  /**
   * @brief Scale data to 16bit/32bit for I2S DMA output.
   *        DAC can only output 8bit data value.
   *        I2S DMA will still send 16bit or 32bit data, the highest 8bit contains DAC data.
   */
  static int i2s_dac_data_scale(int bits, uint8_t *sBuff, uint32_t len)
  {
      if (bits == 16) {
          short *buf16 = (short *)sBuff;
          int k = len >> 1;
          for (int i = 0; i < k; i++) {
              buf16[i] &= 0xff00;
              buf16[i] += 0x8000;//turn signed value into unsigned, expand negative value into positive range
          }
      } else if (bits == 32) {
          int *buf32 = (int *)sBuff;
          int k = len >> 2;
          for (int i = 0; i < k; i++) {
              buf32[i] &= 0xff000000;
              buf32[i] += 0x80000000;//turn signed value into unsigned
          }
      } else {
          ESP_LOGE(TAG, "in %s %dbits is not supported", __func__, bits);
          return -1;
      }
  
      return 0;
  }
  #endif
  
  static int i2s_stream_clear_dma_buffer(audio_element_handle_t self)
  {
      i2s_stream_t *i2s = (i2s_stream_t *)audio_element_getdata(self);
      int index = i2s->config.i2s_config.dma_buf_count;
      uint8_t *buf = audio_calloc(1, i2s->config.i2s_config.dma_buf_len * 4);
      AUDIO_MEM_CHECK(TAG, buf, return ESP_ERR_NO_MEM);
  #if SOC_I2S_SUPPORTS_ADC_DAC
      if ((i2s->config.i2s_config.mode & I2S_MODE_DAC_BUILT_IN) != 0) {
          memset(buf, 0x80, i2s->config.i2s_config.dma_buf_len * 4);
      }
  #endif
      while (index--) {
          audio_element_output(self, (char *)buf, i2s->config.i2s_config.dma_buf_len * 4);
      }
      if (buf) {
          free(buf);
      }
      return ESP_OK;
  }
  
  static esp_err_t _i2s_open(audio_element_handle_t self)
  {
      i2s_stream_t *i2s = (i2s_stream_t *)audio_element_getdata(self);
      ESP_LOGD(TAG, "_i2s_open %d", (int)i2s->config.i2s_port);
      if (i2s->is_open) {
          return ESP_OK;
      }
      if (i2s->type == AUDIO_STREAM_READER) {
          audio_element_info_t i2s_info = {0};
          i2s_info.bits = 16;
          i2s_info.channels = 1;
          i2s_info.sample_rates = 16000;
          audio_element_getinfo(self, &i2s_info);
          ESP_LOGI(TAG, "AUDIO_STREAM_READER,Rate:%d,ch:%d", i2s_info.sample_rates, i2s_info.channels);
          if (i2s_set_clk(i2s->config.i2s_port, i2s_info.sample_rates, i2s_info.bits, i2s_info.channels) == ESP_FAIL) {
              ESP_LOGE(TAG, "i2s_set_clk failed, type = %d", i2s->config.type);
              return ESP_FAIL;
          }
      } else if (i2s->type == AUDIO_STREAM_WRITER) {
          audio_element_set_input_timeout(self, 10 / portTICK_RATE_MS);
          ESP_LOGI(TAG, "AUDIO_STREAM_WRITER");
      }
      i2s->is_open = true;
      if (i2s->use_alc) {
          i2s->volume_handle = alc_volume_setup_open();
          if (i2s->volume_handle == NULL) {
              ESP_LOGE(TAG, "i2s create the handle for setting volume failed, in line(%d)", __LINE__);
              return ESP_FAIL;
          }
      }
      return ESP_OK;
  }
  
  static esp_err_t _i2s_destroy(audio_element_handle_t self)
  {
      i2s_stream_t *i2s = (i2s_stream_t *)audio_element_getdata(self);
      if (i2s->uninstall_drv) {
          i2s_driver_uninstall(i2s->config.i2s_port);
      }
      audio_free(i2s);
      return ESP_OK;
  }
  
  static esp_err_t _i2s_close(audio_element_handle_t self)
  {
      i2s_stream_t *i2s = (i2s_stream_t *)audio_element_getdata(self);
      esp_err_t ret = i2s_stream_clear_dma_buffer(self);
      if (ret != ESP_OK) {
          return ret;
      }
      i2s->is_open = false;
      if (AEL_STATE_PAUSED != audio_element_get_state(self)) {
          audio_element_report_pos(self);
          audio_element_info_t info = {0};
          audio_element_getinfo(self, &info);
          info.byte_pos = 0;
          audio_element_setinfo(self, &info);
      }
      if (i2s->use_alc) {
          if (i2s->volume_handle != NULL) {
              alc_volume_setup_close(i2s->volume_handle);
          }
      }
      return ESP_OK;
  }
  
  static int _i2s_read(audio_element_handle_t self, char *buffer, int len, TickType_t ticks_to_wait, void *context)
  {
      i2s_stream_t *i2s = (i2s_stream_t *)audio_element_getdata(self);
      size_t bytes_read = 0;
      i2s_read(i2s->config.i2s_port, buffer, len, &bytes_read, ticks_to_wait);
      audio_element_info_t info;
      audio_element_getinfo(self, &info);
      if (bytes_read > 0) {
          if (info.channels == 1) {
              i2s_mono_fix(info.bits, (uint8_t *)buffer, bytes_read);
          }
          info.byte_pos += bytes_read;
          audio_element_setinfo(self, &info);
      }
      return bytes_read;
  }
  
  static int _i2s_write(audio_element_handle_t self, char *buffer, int len, TickType_t ticks_to_wait, void *context)
  {
      i2s_stream_t *i2s = (i2s_stream_t *)audio_element_getdata(self);
      size_t bytes_written = 0;
      i2s_write(i2s->config.i2s_port, buffer, len, &bytes_written, ticks_to_wait);
      return bytes_written;
  }
  
  static int _i2s_process(audio_element_handle_t self, char *in_buffer, int in_len)
  {
      int r_size = audio_element_input(self, in_buffer, in_len);
      int w_size = 0;
      i2s_stream_t *i2s = (i2s_stream_t *)audio_element_getdata(self);
      if (r_size == AEL_IO_TIMEOUT) {
  #if SOC_I2S_SUPPORTS_ADC_DAC
          if ((i2s->config.i2s_config.mode & I2S_MODE_DAC_BUILT_IN) != 0) {
              memset(in_buffer, 0x80, in_len);
          } else
  #endif
          {
              memset(in_buffer, 0x00, in_len);
          }
          r_size = in_len;
      }
      if ((r_size > 0)) {
          if (i2s->use_alc) {
              audio_element_info_t i2s_info = {0};
              audio_element_getinfo(self, &i2s_info);
              alc_volume_setup_process(in_buffer, r_size, i2s_info.channels, i2s->volume_handle, i2s->volume);
          }
          audio_element_multi_output(self, in_buffer, r_size, 0);
          // Fix output by I2S only
          audio_element_info_t info;
          audio_element_getinfo(self, &info);
          if (info.channels == 1) {
              i2s_mono_fix(info.bits, (uint8_t *)in_buffer, r_size);
          }
  #if SOC_I2S_SUPPORTS_ADC_DAC
          if ((i2s->config.i2s_config.mode & I2S_MODE_DAC_BUILT_IN) != 0) {
              i2s_dac_data_scale(info.bits, (uint8_t *)in_buffer, r_size);
          }
  #endif
          w_size = audio_element_output(self, in_buffer, r_size);
  
          audio_element_getinfo(self, &info);
          info.byte_pos += w_size;
          audio_element_setinfo(self, &info);
      } else {
          esp_err_t ret = i2s_stream_clear_dma_buffer(self);
          if (ret != ESP_OK) {
              return ret;
          }
          w_size = r_size;
      }
      return w_size;
  }
  
  esp_err_t i2s_stream_set_clk(audio_element_handle_t i2s_stream, int rate, int bits, int ch)
  {
      esp_err_t err = ESP_OK;
      i2s_stream_t *i2s = (i2s_stream_t *)audio_element_getdata(i2s_stream);
      audio_element_info_t i2s_info = {0};
      audio_element_state_t state = audio_element_get_state(i2s_stream);
      if (state == AEL_STATE_RUNNING) {
          audio_element_pause(i2s_stream);
      }
      audio_element_getinfo(i2s_stream, &i2s_info);
      i2s_info.bits = bits;
      i2s_info.channels = ch;
      i2s_info.sample_rates = rate;
      audio_element_setinfo(i2s_stream, &i2s_info);
  
      if (i2s_set_clk(i2s->config.i2s_port, rate, bits, ch) == ESP_FAIL) {
          ESP_LOGE(TAG, "i2s_set_clk failed, type = %d,port:%d", i2s->config.type, i2s->config.i2s_port);
          err = ESP_FAIL;
      }
      if (state == AEL_STATE_RUNNING) {
          audio_element_resume(i2s_stream, 0, 0);
      }
      return err;
  }
  
  int i2s_alc_volume_set(audio_element_handle_t i2s_stream, int volume)
  {
      i2s_stream_t *i2s = (i2s_stream_t *)audio_element_getdata(i2s_stream);
      if (i2s->use_alc) {
          i2s->volume = volume;
          return ESP_OK;
      } else {
          ESP_LOGW(TAG, "The ALC don't be used. It can not be set.");
          return ESP_FAIL;
      }
  }
  
  int i2s_alc_volume_get(audio_element_handle_t i2s_stream, int *volume)
  {
      i2s_stream_t *i2s = (i2s_stream_t *)audio_element_getdata(i2s_stream);
      if (i2s->use_alc) {
          *volume = i2s->volume;
          return ESP_OK;
      } else {
          ESP_LOGW(TAG, "The ALC don't be used");
          return ESP_FAIL;
      }
  }
  
  audio_element_handle_t i2s_stream_init(i2s_stream_cfg_t *config)
  {
      audio_element_cfg_t cfg = DEFAULT_AUDIO_ELEMENT_CONFIG();
      audio_element_handle_t el;
      cfg.open = _i2s_open;
      cfg.close = _i2s_close;
      cfg.process = _i2s_process;
      cfg.destroy = _i2s_destroy;
      cfg.task_stack = config->task_stack;
      cfg.task_prio = config->task_prio;
      cfg.task_core = config->task_core;
      cfg.stack_in_ext = config->stack_in_ext;
      cfg.out_rb_size = config->out_rb_size;
      cfg.multi_out_rb_num = config->multi_out_num;
      cfg.tag = "iis";
      cfg.buffer_len = I2S_STREAM_BUF_SIZE;
  
      i2s_stream_t *i2s = audio_calloc(1, sizeof(i2s_stream_t));
      AUDIO_MEM_CHECK(TAG, i2s, return NULL);
      memcpy(&i2s->config, config, sizeof(i2s_stream_cfg_t));
  
      i2s->type = config->type;
      i2s->use_alc = config->use_alc;
      i2s->volume = config->volume;
      i2s->uninstall_drv = config->uninstall_drv;
  
      if (config->type == AUDIO_STREAM_READER) {
          cfg.read = _i2s_read;
      } else if (config->type == AUDIO_STREAM_WRITER) {
          cfg.write = _i2s_write;
      }
      if (i2s_driver_install(i2s->config.i2s_port, &i2s->config.i2s_config, 0, NULL) != ESP_OK) {
          audio_free(i2s);
          return NULL;
      }
  
      el = audio_element_init(&cfg);
      AUDIO_MEM_CHECK(TAG, el, {
          audio_free(i2s);
          return NULL;
      });
      audio_element_setdata(el, i2s);
  
      audio_element_info_t info = {0};
      info.sample_rates = config->i2s_config.sample_rate;
      info.channels = config->i2s_config.channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 2 : 1;
      info.bits = config->i2s_config.bits_per_sample;
      audio_element_setinfo(el, &info);
  #if SOC_I2S_SUPPORTS_ADC_DAC
      if ((config->i2s_config.mode & I2S_MODE_DAC_BUILT_IN) != 0) {
          i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN);
      } else
  #endif
      {
          i2s_pin_config_t i2s_pin_cfg = {0};
          get_i2s_pins(i2s->config.i2s_port, &i2s_pin_cfg);
          i2s_set_pin(i2s->config.i2s_port, &i2s_pin_cfg);
      }
      i2s_mclk_gpio_select(i2s->config.i2s_port, GPIO_NUM_0);
  
      return el;
  }
  