| 
						
						
							
								
							
						
						
					 | 
					 | 
					@ -8,9 +8,11 @@ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#include "driver/i2s_common.h" | 
					 | 
					 | 
					 | 
					#include "driver/i2s_common.h" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#include "driver/i2s_std.h" | 
					 | 
					 | 
					 | 
					#include "driver/i2s_std.h" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#include "driver/i2s_types.h" | 
					 | 
					 | 
					 | 
					#include "driver/i2s_types.h" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					#include "esp_attr.h" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#include "esp_err.h" | 
					 | 
					 | 
					 | 
					#include "esp_err.h" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#include "esp_log.h" | 
					 | 
					 | 
					 | 
					#include "esp_log.h" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#include "freertos/portmacro.h" | 
					 | 
					 | 
					 | 
					#include "freertos/portmacro.h" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					#include "freertos/projdefs.h" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#include "hal/i2c_types.h" | 
					 | 
					 | 
					 | 
					#include "hal/i2c_types.h" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#include "gpio_expander.hpp" | 
					 | 
					 | 
					 | 
					#include "gpio_expander.hpp" | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -28,22 +30,13 @@ static const AudioDac::SampleRate kDefaultSampleRate = | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    AudioDac::SAMPLE_RATE_44_1; | 
					 | 
					 | 
					 | 
					    AudioDac::SAMPLE_RATE_44_1; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					static const AudioDac::BitsPerSample kDefaultBps = AudioDac::BPS_16; | 
					 | 
					 | 
					 | 
					static const AudioDac::BitsPerSample kDefaultBps = AudioDac::BPS_16; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					extern "C" { | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					bool dma_callback(i2s_chan_handle_t handle, | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                  i2s_event_data_t* event, | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                  void* user_ctx) { | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  AudioDac* dac = static_cast<AudioDac*>(user_ctx); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  return dac->WriteDataFromISR(static_cast<std::byte*>(event->data), | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                               event->size); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					} | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					} | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					auto AudioDac::create(GpioExpander* expander) | 
					 | 
					 | 
					 | 
					auto AudioDac::create(GpioExpander* expander) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    -> cpp::result<std::unique_ptr<AudioDac>, Error> { | 
					 | 
					 | 
					 | 
					    -> cpp::result<std::unique_ptr<AudioDac>, Error> { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  // TODO: tune.
 | 
					 | 
					 | 
					 | 
					  // TODO: tune.
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  i2s_chan_handle_t i2s_handle; | 
					 | 
					 | 
					 | 
					  i2s_chan_handle_t i2s_handle; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  i2s_chan_config_t channel_config = | 
					 | 
					 | 
					 | 
					  i2s_chan_config_t channel_config = | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      I2S_CHANNEL_DEFAULT_CONFIG(kI2SPort, I2S_ROLE_MASTER); | 
					 | 
					 | 
					 | 
					      I2S_CHANNEL_DEFAULT_CONFIG(kI2SPort, I2S_ROLE_MASTER); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  ESP_ERROR_CHECK(i2s_new_channel(&channel_config, &i2s_handle, NULL)); | 
					 | 
					 | 
					 | 
					  ESP_ERROR_CHECK(i2s_new_channel(&channel_config, &i2s_handle, NULL)); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  //
 | 
					 | 
					 | 
					 | 
					  //
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  // First, instantiate the instance so it can do all of its power on
 | 
					 | 
					 | 
					 | 
					  // First, instantiate the instance so it can do all of its power on
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -108,8 +101,7 @@ AudioDac::AudioDac(GpioExpander* gpio, i2s_chan_handle_t i2s_handle) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      i2s_handle_(i2s_handle), | 
					 | 
					 | 
					 | 
					      i2s_handle_(i2s_handle), | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      clock_config_(I2S_STD_CLK_DEFAULT_CONFIG(44100)), | 
					 | 
					 | 
					 | 
					      clock_config_(I2S_STD_CLK_DEFAULT_CONFIG(44100)), | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      slot_config_(I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, | 
					 | 
					 | 
					 | 
					      slot_config_(I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                                                   I2S_SLOT_MODE_STEREO)), | 
					 | 
					 | 
					 | 
					                                                   I2S_SLOT_MODE_STEREO)) { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      dma_queue_(nullptr) { | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  gpio_->set_pin(GpioExpander::AUDIO_POWER_ENABLE, true); | 
					 | 
					 | 
					 | 
					  gpio_->set_pin(GpioExpander::AUDIO_POWER_ENABLE, true); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  gpio_->Write(); | 
					 | 
					 | 
					 | 
					  gpio_->Write(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					} | 
					 | 
					 | 
					 | 
					} | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
					 | 
					@ -167,9 +159,7 @@ bool AudioDac::WaitForPowerState( | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  return has_matched; | 
					 | 
					 | 
					 | 
					  return has_matched; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					} | 
					 | 
					 | 
					 | 
					} | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					auto AudioDac::Reconfigure(BitsPerSample bps, | 
					 | 
					 | 
					 | 
					auto AudioDac::Reconfigure(BitsPerSample bps, SampleRate rate) -> void { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                           SampleRate rate, | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					                           QueueHandle_t dma_queue) -> std::size_t { | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  // TODO(jacqueline): investigate how reliable the auto-clocking of the dac
 | 
					 | 
					 | 
					 | 
					  // TODO(jacqueline): investigate how reliable the auto-clocking of the dac
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  // is. We might need to explicit reconfigure the dac here as well if it's not
 | 
					 | 
					 | 
					 | 
					  // is. We might need to explicit reconfigure the dac here as well if it's not
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  // good enough.
 | 
					 | 
					 | 
					 | 
					  // good enough.
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -183,44 +173,17 @@ auto AudioDac::Reconfigure(BitsPerSample bps, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      bps == BPS_24 ? I2S_MCLK_MULTIPLE_384 : I2S_MCLK_MULTIPLE_256; | 
					 | 
					 | 
					 | 
					      bps == BPS_24 ? I2S_MCLK_MULTIPLE_384 : I2S_MCLK_MULTIPLE_256; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  ESP_ERROR_CHECK(i2s_channel_reconfig_std_clock(i2s_handle_, &clock_config_)); | 
					 | 
					 | 
					 | 
					  ESP_ERROR_CHECK(i2s_channel_reconfig_std_clock(i2s_handle_, &clock_config_)); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  dma_queue_ = dma_queue; | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  // TODO: less spooky action here plz.
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  // dma_buffer_size = dma_frame_num (channel config) * slot_num (always 2?) *
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  // slot_bit_width / 8
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  //size_t dma_size = 240 * 2 * slot_config_.slot_bit_width / 8;
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  size_t dma_size = 960; | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  ESP_LOGI(kTag, "new dma size: %u bytes", dma_size); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  i2s_event_callbacks_t callbacks = { | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      .on_recv = NULL, | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      .on_recv_q_ovf = NULL, | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      .on_sent = &dma_callback, | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      .on_send_q_ovf = NULL, | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  }; | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  ESP_ERROR_CHECK( | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      i2s_channel_register_event_callback(i2s_handle_, &callbacks, this)); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  ESP_ERROR_CHECK(i2s_channel_enable(i2s_handle_)); | 
					 | 
					 | 
					 | 
					  ESP_ERROR_CHECK(i2s_channel_enable(i2s_handle_)); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  return dma_size; | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					} | 
					 | 
					 | 
					 | 
					} | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					auto AudioDac::WriteDataFromISR(std::byte* data, std::size_t size) -> bool { | 
					 | 
					 | 
					 | 
					auto AudioDac::WriteData(cpp::span<std::byte> data) -> std::size_t { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  std::byte* new_data; | 
					 | 
					 | 
					 | 
					  std::size_t bytes_written = 0; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  BaseType_t high_priority_task_awoken = pdFALSE; | 
					 | 
					 | 
					 | 
					  esp_err_t err = i2s_channel_write(i2s_handle_, data.data(), data.size_bytes(), | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					                                    &bytes_written, 0); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  if (xQueueReceiveFromISR(dma_queue_, &new_data, &high_priority_task_awoken)) { | 
					 | 
					 | 
					 | 
					  if (err != ESP_ERR_TIMEOUT) { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    // Item was received. Copy it into the DMA buffer.
 | 
					 | 
					 | 
					 | 
					    ESP_ERROR_CHECK(err); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    memcpy(data, new_data, size); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    free(new_data); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    ESP_DRAM_LOGI(kTag, "wrote dma"); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } else { | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    // No item was received. Write empty data.
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    memset(data, 0, size); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					  return bytes_written; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  return high_priority_task_awoken; | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					} | 
					 | 
					 | 
					 | 
					} | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					void AudioDac::WriteRegister(Register reg, uint8_t val) { | 
					 | 
					 | 
					 | 
					void AudioDac::WriteRegister(Register reg, uint8_t val) { | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
					 | 
					
  |