From 4964133117e41a50cb5cb68218da7b558183d476 Mon Sep 17 00:00:00 2001
From: tomcombriat <tomcombriat@live.fr>
Date: Sun, 19 Jan 2025 23:59:41 +0100
Subject: [PATCH] ESP32: added PWM output support

---
 internal/MozziGuts_impl_ESP32.hpp | 30 ++++++++++++++++++++++++------
 internal/config_checks_esp32.h    | 15 +++++++++++++--
 2 files changed, 37 insertions(+), 8 deletions(-)

diff --git a/internal/MozziGuts_impl_ESP32.hpp b/internal/MozziGuts_impl_ESP32.hpp
index 3103528fb..0e9b3624c 100644
--- a/internal/MozziGuts_impl_ESP32.hpp
+++ b/internal/MozziGuts_impl_ESP32.hpp
@@ -43,12 +43,12 @@ namespace MozziPrivate {
 
 //#  include <driver/i2s.h>   // for I2S-based output modes, including - technically - internal DAC
 #include<driver/i2s_std.h>
-
+/*
 #elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC)
 #include<driver/dac_continuous.h>
 namespace MozziPrivate {
-  /* static*/ dac_continuous_handle_t dac_handle;
-}
+  static dac_continuous_handle_t dac_handle;
+  }*/
 #endif
 
 //#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S)
@@ -99,7 +99,8 @@ namespace MozziPrivate {
     return _esp32_can_buffer_next;
   }
 
-# if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC) || MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S) || MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC)
+  
+# if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC) || MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S) || MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC)  || MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM)
   inline void audioOutput(const AudioOutput f) {
     /*
 #  if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC)
@@ -115,6 +116,13 @@ namespace MozziPrivate {
 #    if (MOZZI_AUDIO_CHANNELS > 1)
     dacWrite(26, f.l() + MOZZI_AUDIO_BIAS);
 #    endif
+    
+#  elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM)
+    ledcWrite(MOZZI_AUDIO_PIN_1,(f.l()+MOZZI_AUDIO_BIAS));
+#    if (MOZZI_AUDIO_CHANNELS > 1)
+    ledcWrite(MOZZI_AUDIO_PIN_2,(f.l()+MOZZI_AUDIO_BIAS));
+#    endif
+    
 #  elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S)
     for (uint8_t i=0; i<MOZZI_PDM_RESOLUTION; ++i) {
       _esp32_prev_sample[i] = pdmCode32(f.l() + MOZZI_AUDIO_BIAS);
@@ -124,14 +132,16 @@ namespace MozziPrivate {
     _esp32_prev_sample[0] = f.l();
     _esp32_prev_sample[1] = f.r();
 #  endif
+#  if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC) || MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S)
      _esp32_can_buffer_next = esp32_tryWriteSample();
+#  endif
   }
   #endif
   } // namespace MozziPrivate
 
 
 namespace MozziPrivate {
-#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPUT_INTERNAL_DAC)
+#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPUT_INTERNAL_DAC, MOZZI_OUTPUT_PWM)
 #include <driver/gptimer.h>
 
   bool CACHED_FUNCTION_ATTR timer_on_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
@@ -144,7 +154,7 @@ namespace MozziPrivate {
   static void startAudio() {
     /* Normally, the internal DAC can run on DMA, hence self triggering. Did not managed to get that to work (see: https://github.com/espressif/arduino-esp32/issues/10851) so, for now, we are just using the Mozzi buffer and send dacWrite orders.
      */
-#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED) || MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) ||  MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM) // set up a timer running a audio rate
+#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPUT_INTERNAL_DAC, MOZZI_OUTPUT_PWM) //|| MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) ||  MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM) // set up a timer running a audio rate
   
     gptimer_handle_t gptimer = NULL;
     gptimer_config_t timer_config = {
@@ -166,9 +176,17 @@ namespace MozziPrivate {
       .on_alarm = timer_on_alarm_cb, // register user callback
     };
 
+
     gptimer_register_event_callbacks(gptimer,&cbs,NULL);
     gptimer_enable(gptimer);
     gptimer_start(gptimer);
+    
+#  if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM)   
+    ledcAttach(MOZZI_AUDIO_PIN_1, MOZZI_AUDIO_RATE, MOZZI_AUDIO_BITS);
+#     if (MOZZI_AUDIO_CHANNELS > 1)
+    ledcAttach(MOZZI_AUDIO_PIN_2, MOZZI_AUDIO_RATE, MOZZI_AUDIO_BITS);
+#    endif
+#  endif
 
 #  elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC) || MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S)
 
diff --git a/internal/config_checks_esp32.h b/internal/config_checks_esp32.h
index 0184c8826..0b6eda631 100644
--- a/internal/config_checks_esp32.h
+++ b/internal/config_checks_esp32.h
@@ -100,7 +100,7 @@
 #if !defined(MOZZI_AUDIO_MODE)
 #define MOZZI_AUDIO_MODE MOZZI_OUTPUT_INTERNAL_DAC
 #endif
-MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPUT_EXTERNAL_CUSTOM, MOZZI_OUTPUT_PDM_VIA_I2S, MOZZI_OUTPUT_I2S_DAC, MOZZI_OUTPUT_INTERNAL_DAC)
+MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPUT_EXTERNAL_CUSTOM, MOZZI_OUTPUT_PDM_VIA_I2S, MOZZI_OUTPUT_I2S_DAC, MOZZI_OUTPUT_INTERNAL_DAC, MOZZI_OUTPUT_PWM)
 
 #if !defined(MOZZI_AUDIO_RATE)
 #define MOZZI_AUDIO_RATE 32768
@@ -155,6 +155,16 @@ MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_INPUT, MOZZI_AUDIO_INPUT_NONE)
 #  endif
 #endif
 
+#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM)
+#  define MOZZI_AUDIO_BITS 10   // not configurable (could be 8)
+#  if !defined(MOZZI_AUDIO_PIN_1)
+#    define MOZZI_AUDIO_PIN_1 18
+#  endif
+#  if !defined(MOZZI_AUDIO_PIN_2)
+#    define MOZZI_AUDIO_PIN_2 19
+#  endif
+#endif
+
 #if !defined(MOZZI_AUDIO_BITS)
 #  if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC)
 #    define MOZZI_AUDIO_BITS 8
@@ -171,8 +181,9 @@ MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_INPUT, MOZZI_AUDIO_INPUT_NONE)
 #  define MOZZI_PDM_RESOLUTION 1  // unconditionally, no other value allowed
 #endif
 
+
 // All modes besides timed external bypass the output buffer!
-#if !MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPUT_INTERNAL_DAC)
+#if !MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPUT_INTERNAL_DAC, MOZZI_OUTPUT_PWM)
 #  define BYPASS_MOZZI_OUTPUT_BUFFER true
 #endif