diff --git a/CircularBuffer.h b/CircularBuffer.h index e8c164a5b..d05d2f0a6 100644 --- a/CircularBuffer.h +++ b/CircularBuffer.h @@ -14,19 +14,113 @@ Modified from https://en.wikipedia.org/wiki/Circular_buffer Mirroring version On 18 April 2014, the simplified version on the Wikipedia page for power of 2 sized buffers doesn't work - cbIsEmpty() returns true whether the buffer is full or empty. + +April 2025: modified for different buffer sizes under the suggestion +of Meebleeps (https://github.com/sensorium/Mozzi/issues/281) */ -#define MOZZI_BUFFER_SIZE 256 // do not expect to change and it to work. - // just here for forward compatibility if one day - // the buffer size might be editable -/** Circular buffer object. Has a fixed number of cells, set to 256. + +/** Circular buffer object. Has a fixed number of cells, set by BUFFER_SIZE. @tparam ITEM_TYPE the kind of data to store, eg. int, int8_t etc. +@tparam BUFFER_SIZE the size of the circular buffer */ -template +template class CircularBuffer { +public: + /** Constructor + */ + CircularBuffer(): start(0),end(0),s_msb(0),e_msb(0) + { + } + + inline + bool isFull() { + return end == start && e_msb != s_msb; + } + + inline + bool isEmpty() { + return end == start && e_msb == s_msb; + } + + inline + void write(ITEM_TYPE in) { + items[end] = in; + //if (isFull()) cbIncrStart(); /* full, overwrite moves start pointer */ + cbIncrEnd(); + } + + inline + ITEM_TYPE read() { + ITEM_TYPE out = items[start]; + cbIncrStart(); + return out; + } + + inline + unsigned long count() { + return (num_buffers_read << COUNT_LSHIFT) + start; + } + inline + ITEM_TYPE * address() { + return items; + } + +private: + ITEM_TYPE items[BUFFER_SIZE]; + uint8_t start; /* index of oldest itement */ + uint8_t end; /* index at which to write new itement */ + uint8_t s_msb; + uint8_t e_msb; + unsigned long num_buffers_read; + static constexpr unsigned long COUNT_LSHIFT = + (BUFFER_SIZE == 256) ? 8 : + (BUFFER_SIZE == 128) ? 7 : + (BUFFER_SIZE == 64) ? 6 : + (BUFFER_SIZE == 32) ? 5 : + (BUFFER_SIZE == 16) ? 4 : + (BUFFER_SIZE == 8) ? 3 : + (BUFFER_SIZE == 4) ? 2 : + (BUFFER_SIZE == 2) ? 1 : 0; + + inline + void cbIncrStart() { + start++; + if (start == BUFFER_SIZE) + { + start = 0; + s_msb ^= 1; + num_buffers_read++; + } + } + + inline + void cbIncrEnd() + { + end++; + if (end == BUFFER_SIZE) + { + end = 0; + e_msb ^= 1; + } + } + + +}; + + + +/** Circular buffer object. Specialization for size of 256. +Note: Lot of duplication but C++ does not allow for specialization of the +function member only (partial specialization). +@tparam ITEM_TYPE the kind of data to store, eg. int, int8_t etc. +*/ +template +class CircularBuffer +{ public: /** Constructor */ @@ -68,7 +162,7 @@ class CircularBuffer } private: - ITEM_TYPE items[MOZZI_BUFFER_SIZE]; + ITEM_TYPE items[256]; uint8_t start; /* index of oldest itement */ uint8_t end; /* index at which to write new itement */ uint8_t s_msb; @@ -90,5 +184,5 @@ class CircularBuffer end++; if (end == 0) e_msb ^= 1; } - }; + diff --git a/README.md b/README.md index b6f2278a8..aa2204964 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ passing or external synths. *** ## Features -- Available for a wide and growing range of MCUs, with and without inbuilt DACs: Arduino Uno R3 and R4, STM32, Teensy, ESP8266, ESP32, Raspberry Pi Pico, and more. +- Available for a wide and growing range of MCUs, with and without inbuilt DACs: Arduino Uno R3 and R4, STM32, Teensy, ESP8266, ESP32, Raspberry Pi Pico, CH32, and more. - Configurable sample rate, usually in powers of two (16384 Hz, or 32768 Hz). - Variable control rate from 64 Hz upwards. - Various inbuilt output modes, including 16 bit output to an external DAC. @@ -69,6 +69,7 @@ If desparate, there is still a "Mozzi_1" branch in the git repository which cont | *ESP32: that has an external DAC (only ESP32) note: Beware of vastly different pin labels across board variants* | 15 (+16) | | yes | **GPIO25 (+GPIO26)** | yes | | *ESP32-S/C/H/P: that do not have an external DAC note: Beware of vastly different pin labels across board variants* | **15 (+16)** | | yes | | yes | | *RP2040*: Raspberry Pi Pico and friends | **0 (+1)** | 0, 1 | | | yes | +| *CH32*: WCH CH32X035 (RISC-V) | **PA6** | PA6, PA7 | | | | > - PWM-1: 1-pin PWM mode (`MOZZI_OUTPUT_PWM`) > - PWM-2: 2-pin PWM mode (`MOZZI_OUTPUT_2PIN_PWM`) diff --git a/config/mozzi_config_documentation.h b/config/mozzi_config_documentation.h index 032c6f29e..f98e13544 100644 --- a/config/mozzi_config_documentation.h +++ b/config/mozzi_config_documentation.h @@ -249,6 +249,22 @@ * */ #define MOZZI_AUDIO_PIN_1 FOR_DOXYGEN_ONLY +/** @ingroup config + * @def MOZZI_OUTPUT_BUFFER_SIZE + * + * @brief Audio buffer setting. + * + * For a lot of outputting modes, Mozzi is buffering the audio samples in order to be able to coop with varying loads on the processor. + * The bigger the buffer, the more able Mozzi will be to coop with big change of processor loads as the buffered values can compensate for that. + * At the same time, a bigger buffer produces a bigger latency as the time between when Mozzi produces the sample and the time it is actually outputted increases. For instance, for a long time Mozzi's buffer size was of a fixed size of 256. This produces a potential latency of 15.6 ms for a MOZZI_AUDIO_RATE of 16384, and half this value for a MOZZI_AUDIO_RATE of 32768. + * Depending on the application, this is usually not a problem but can lead to synchronisation issues in some cases (for instance when working with clocks). + * MOZZI_OUTPUT_BUFFER_SIZE can be reduced to smaller values with this config, leading to more accurate timings but potentially to glitches if the buffer runs low. + * Valid values are power of two from 256 downward (128, 64, …). + * Note that this might not have an effect in all modes/platforms combination as Mozzi is sometimes using an external buffer which is not always configurable. + * +*/ +#define MOZZI_OUTPUT_BUFFER_SIZE FOR_DOXYGEN_ONLY + /***************************************** ADVANCED SETTTINGS -- External audio output ****************************************** * diff --git a/extras/website/_includes/boards-tested.markdown b/extras/website/_includes/boards-tested.markdown index da328b318..f73d84197 100644 --- a/extras/website/_includes/boards-tested.markdown +++ b/extras/website/_includes/boards-tested.markdown @@ -47,6 +47,8 @@ td[data-sd] { border: 2px solid; background: linear-gradient(-45deg,rgba(0,0,255 ESP32-S/C/H/P: that do not have an internal DAC note: Beware of vastly different pin labels across board variants 15 (+16) yes yes RP2040: Raspberry Pi Pico and friends 0 (+1) 0, 1 - - yes + + CH32: WCH CH32X035 (RISC-V) PA6 PA6, PA7 - - - diff --git a/extras/website/index.markdown b/extras/website/index.markdown index f8435478f..a8acfb5ff 100644 --- a/extras/website/index.markdown +++ b/extras/website/index.markdown @@ -58,7 +58,7 @@ Your browser does not support the audio element. ## Features -- Available for a wide and growing range of MCUs, with and without inbuilt DACs: Arduino Uno R3 and R4, STM32, Teensy, ESP8266, ESP32, Raspberry Pi Pico, and more. +- Available for a wide and growing range of MCUs, with and without inbuilt DACs: Arduino Uno R3 and R4, STM32, Teensy, ESP8266, ESP32, Raspberry Pi Pico, CH32, and more. - Configurable sample rate, usually in powers of two (16384 Hz, or 32768 Hz). - Variable control rate from 64 Hz upwards. - Various inbuilt output modes, including 16 bit output to an external DAC. diff --git a/hardware_defines.h b/hardware_defines.h index 7d61adf46..5e939b78b 100644 --- a/hardware_defines.h +++ b/hardware_defines.h @@ -1,5 +1,5 @@ /* - * hardware_defines.h.h + * hardware_defines.h * * This file is part of Mozzi. * @@ -84,6 +84,14 @@ #define IS_RENESAS() 0 #endif +// CH32 Architecture (WCH RISC-V) +// Explicit check for CH32X035 is needed as ARDUINO_ARCH macros might not be defined by all cores/tools +#if (defined(ARDUINO_ARCH_CH32V) || defined(ARDUINO_ARCH_CH32) || defined(CH32X035)) +#define IS_CH32() 1 +#else +#define IS_CH32() 0 +#endif + #if (defined(__arm__) && !IS_STM32MAPLE() && !IS_TEENSY3() && !IS_TEENSY4() && !IS_RP2040() && !IS_SAMD21() && !IS_MBED() && !IS_RENESAS()) #define IS_STM32DUINO() 1 #else @@ -102,9 +110,9 @@ #define IS_ESP32() 0 #endif -#if !(IS_AVR() || IS_TEENSY3() || IS_TEENSY4() || IS_STM32MAPLE() || IS_STM32DUINO() || IS_ESP8266() || IS_SAMD21() || IS_ESP32() || IS_RP2040() || IS_MBED() || IS_RENESAS()) +#if !(IS_AVR() || IS_TEENSY3() || IS_TEENSY4() || IS_STM32MAPLE() || IS_STM32DUINO() || IS_ESP8266() || IS_SAMD21() || IS_ESP32() || IS_RP2040() || IS_MBED() || IS_RENESAS() || IS_CH32()) // TODO: add an exception for MOZZI_OUTPUT_EXTERNAL_CUSTOM -#error Your hardware is not supported by Mozzi or not recognized. Edit hardware_defines.h to proceed. +#error Your hardware is not supported by Mozzi or not recognized. Edit hardware_defines.h to proceed. #endif // Hardware detail defines diff --git a/internal/MozziGuts.hpp b/internal/MozziGuts.hpp index 660171f28..e79cb6c52 100644 --- a/internal/MozziGuts.hpp +++ b/internal/MozziGuts.hpp @@ -61,6 +61,8 @@ template constexpr T smartShift(T value # include "MozziGuts_impl_MBED.hpp" #elif (IS_RENESAS()) # include "MozziGuts_impl_RENESAS.hpp" +#elif (IS_CH32()) +# include "MozziGuts_impl_CH32.hpp" #else # error "Platform not (yet) supported. Check MozziGuts_impl_template.hpp and existing implementations for a blueprint for adding your favorite MCU." #endif @@ -86,7 +88,7 @@ inline void bufferAudioOutput(const AudioOutput f) { ++samples_written_to_buffer; } #else -CircularBuffer output_buffer; // fixed size 256 + CircularBuffer output_buffer; # define canBufferAudioOutput() (!output_buffer.isFull()) # define bufferAudioOutput(f) output_buffer.write(f) static void CACHED_FUNCTION_ATTR defaultAudioOutput() { @@ -150,7 +152,7 @@ uint16_t getAudioInput() { return audio_input; } #if MOZZI_IS(MOZZI__LEGACY_AUDIO_INPUT_IMPL, 1) // ring buffer for audio input -CircularBuffer input_buffer; // fixed size 256 + CircularBuffer input_buffer; // fixed size 256 #define audioInputAvailable() (!input_buffer.isEmpty()) #define readAudioInput() (input_buffer.read()) /** NOTE: Triggered at MOZZI_AUDIO_RATE via defaultAudioOutput(). In addition to the MOZZI_AUDIO_INPUT_PIN, at most one reading is taken for mozziAnalogRead(). */ @@ -319,4 +321,4 @@ MOZZI_DEPRECATED("n/a", "Sketch has audioOutput() function, although external ou #if !MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_CUSTOM) // TODO: This won't work without a rename: //MOZZI_DEPRECATED("n/a", "Sketch has canBufferAudioOutput() function, although custom external output is not configured.") bool canBufferAudioOutput() {}; -#endif +#endif \ No newline at end of file diff --git a/internal/MozziGuts_impl_CH32.hpp b/internal/MozziGuts_impl_CH32.hpp new file mode 100644 index 000000000..67688ed84 --- /dev/null +++ b/internal/MozziGuts_impl_CH32.hpp @@ -0,0 +1,250 @@ +/* + * MozziGuts_impl_CH32.hpp + * + * Implementation for WCH CH32X035 (RISC-V) using Standard Peripheral Library (SPL) + * + */ + +// Suppress "narrowing conversion" error for CH32 (RISC-V GCC is strict) +// This allows legacy Mozzi tables (declared as int8_t but containing uint8_t values) to compile. +#pragma GCC diagnostic ignored "-Wnarrowing" + +#if !(IS_CH32()) +# error "Wrong implementation included for this platform" +#endif + +#if !defined(CH32X035) +# error "This CH32 chip is not supported by Mozzi yet. Only CH32X035 is currently supported." +#endif + +#include + +// Forward declare the ISR to ensure C linkage +extern "C" void TIM1_UP_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); +extern "C" void ADC1_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); + +namespace MozziPrivate { + +#define MOZZI_AUDIO_PIN_1 PA6 // High Byte (Main) +#define MOZZI_AUDIO_PIN_2 PA7 // Low Byte (Fine tune) for 2PIN_PWM + +static void startAudio() { + // --- 1. Audio Interrupt Timer Setup (TIM1) --- + TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; + NVIC_InitTypeDef NVIC_InitStructure; + + RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); + + uint32_t period = (SystemCoreClock / MOZZI_AUDIO_RATE) - 1; + + TIM_DeInit(TIM1); + TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); + TIM_TimeBaseStructure.TIM_Prescaler = 0; + TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; + TIM_TimeBaseStructure.TIM_Period = period; + TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; + TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; + TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); + + TIM_ClearFlag(TIM1, TIM_FLAG_Update); + TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); + + NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + TIM_Cmd(TIM1, ENABLE); + TIM_CtrlPWMOutputs(TIM1, ENABLE); + + // --- 2. PWM Output Setup (TIM3) --- + GPIO_InitTypeDef GPIO_InitStructure; + TIM_OCInitTypeDef TIM_OCInitStructure; + + // Enable Clocks + RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); + + // Configure TIM3 Time Base + TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); + TIM_TimeBaseStructure.TIM_Period = 255; // 8-bit resolution + TIM_TimeBaseStructure.TIM_Prescaler = 0; + TIM_TimeBaseStructure.TIM_ClockDivision = 0; + TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; + TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); + + // Common PWM Configuration + TIM_OCStructInit(&TIM_OCInitStructure); + TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; + TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; + TIM_OCInitStructure.TIM_Pulse = 128; + TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; + + // --- Setup Pin 1 (PA6 / CH1) --- + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_Init(GPIOA, &GPIO_InitStructure); + + TIM_OC1Init(TIM3, &TIM_OCInitStructure); + TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM) + // --- Setup Pin 2 (PA7 / CH2) for Hi-Fi --- + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; + GPIO_Init(GPIOA, &GPIO_InitStructure); // Init PA7 as AF_PP too + + TIM_OC2Init(TIM3, &TIM_OCInitStructure); // Init CH2 + TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); +#endif + + TIM_ARRPreloadConfig(TIM3, ENABLE); + + // Enable TIM3 + TIM_Cmd(TIM3, ENABLE); + TIM_CtrlPWMOutputs(TIM3, ENABLE); +} + +void stopMozzi() { + TIM_Cmd(TIM1, DISABLE); + TIM_Cmd(TIM3, DISABLE); +} + +void MozziRandPrivate::autoSeed() {} + +#if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_STANDARD) + +// --- Macros required by MozziGuts.hpp --- +#define getADCReading() ADC_GetConversionValue(ADC1) +#define channelNumToIndex(channel) channel + +// --- ADC Helper Variables --- +static uint8_t ch32_current_adc_channel = 0; + +// --- Helper for pin mapping --- +uint8_t adcPinToChannelNum(uint8_t pin) { +#if defined(analogPinToChannel) + return analogPinToChannel(pin); +#else + // Fallback: If A0 is defined, assume standard Arduino mapping (A0 -> 0, A1 -> 1...) + // If pin is small (e.g. 0-15), assume it's already a channel. + if (pin >= A0) return pin - A0; + return pin; +#endif +} + +void setupFastAnalogRead(int8_t speed) { + ADC_InitTypeDef ADC_InitStructure; + NVIC_InitTypeDef NVIC_InitStructure; + + // 1. Enable ADC1 Clock + RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); + + // 2. Configure ADC Clock Divider + if (speed == FASTEST_ADC) { + ADC_CLKConfig(ADC1, ADC_CLK_Div4); + } else if (speed == FASTER_ADC) { + ADC_CLKConfig(ADC1, ADC_CLK_Div6); + } else { + ADC_CLKConfig(ADC1, ADC_CLK_Div8); // Default safe speed + } + + // 3. Reset ADC + ADC_DeInit(ADC1); + + // 4. Configure ADC Parameters + ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; + ADC_InitStructure.ADC_ScanConvMode = DISABLE; // Single channel mode for manual control + ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // We trigger manually + ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // Software trigger + ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; + ADC_InitStructure.ADC_NbrOfChannel = 1; + ADC_Init(ADC1, &ADC_InitStructure); + + // 5. Enable ADC + ADC_Cmd(ADC1, ENABLE); + + // 6. Configure Interrupts (NVIC) + // ADC1 is usually IRQ channel 29 on CH32 + NVIC_InitStructure.NVIC_IRQChannel = ADC1_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // Lower priority than Audio (1) + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + // 7. Enable End-Of-Conversion Interrupt + ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE); +} + +void setupMozziADC(int8_t speed) { + setupFastAnalogRead(speed); +} + +void adcStartConversion(uint8_t channel) { + ch32_current_adc_channel = channel; // Store for context if needed + + // Select the channel + // Rank 1, and sample time. + ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_11Cycles); + + // Trigger conversion + ADC_SoftwareStartConvCmd(ADC1, ENABLE); +} + +void startSecondADCReadOnCurrentChannel() { + // Just trigger again on the already configured channel + ADC_SoftwareStartConvCmd(ADC1, ENABLE); +} +#endif + +// --- Audio Output Functions --- + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM) +inline void audioOutput(const AudioOutput f) { + long out = (long)f.l() + MOZZI_AUDIO_BIAS; + TIM_SetCompare1(TIM3, (uint16_t)out); +} + +#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM) +inline void audioOutput(const AudioOutput f) { + // 16-bit output split into two 8-bit channels + long out = (long)f.l() + MOZZI_AUDIO_BIAS; + + // High Byte -> CH1 (PA6) + TIM_SetCompare1(TIM3, (uint16_t)(out >> 8)); + + // Low Byte -> CH2 (PA7) + TIM_SetCompare2(TIM3, (uint16_t)(out & 0xFF)); +} +#endif + +} // namespace MozziPrivate + +extern "C" void TIM1_UP_IRQHandler(void) { + if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET) { + TIM_ClearITPendingBit(TIM1, TIM_IT_Update); +#if (BYPASS_MOZZI_OUTPUT_BUFFER != true) + MozziPrivate::defaultAudioOutput(); +#endif + } +} + +// --- ADC Interrupt Handler --- +extern "C" void ADC1_IRQHandler(void) { + if(ADC_GetITStatus(ADC1, ADC_IT_EOC) != RESET) { + // Read value (this also might clear the EOC flag on some families, but explicit clear is safer) + // We don't store it here, Mozzi's advanceADCStep will call getADCReading() (macro) to get it. + // Wait! Mozzi architecture expects getADCReading() to return the value. + // But advanceADCStep() is a void function. + // Let's look at AVR implementation. + // AVR ISR calls advanceADCStep(). advanceADCStep calls getADCReading(). + + // So we need to ensure getADCReading() works inside advanceADCStep. + // We can't pass the value. + // We just clear the flag and let Mozzi read the register. + + ADC_ClearITPendingBit(ADC1, ADC_IT_EOC); + MozziPrivate::advanceADCStep(); + } +} \ No newline at end of file diff --git a/internal/MozziGuts_impl_RENESAS.hpp b/internal/MozziGuts_impl_RENESAS.hpp index 7ba7afda4..c30c18177 100644 --- a/internal/MozziGuts_impl_RENESAS.hpp +++ b/internal/MozziGuts_impl_RENESAS.hpp @@ -86,7 +86,7 @@ FspTimer timer; #endif #if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) -CircularBuffer output_buffer; + CircularBuffer output_buffer; } // namespace MozziPrivate #include "MozziGuts_impl_RENESAS_analog.hpp" namespace MozziPrivate { @@ -159,7 +159,7 @@ static void startAudio() { // The following branches the DAC straight on Mozzi's circular buffer. dtc_cfg.p_info->p_src = output_buffer.address(); - dtc_cfg.p_info->length = MOZZI_BUFFER_SIZE; + dtc_cfg.p_info->length = MOZZI_OUTPUT_BUFFER_SIZE; R_DTC_Reconfigure(&dtc_ctrl, dtc_cfg.p_info); timer_dac.start(); #endif diff --git a/internal/config_checks_CH32.h b/internal/config_checks_CH32.h new file mode 100644 index 000000000..dfaa274bb --- /dev/null +++ b/internal/config_checks_CH32.h @@ -0,0 +1,29 @@ +#ifndef MOZZI_CONFIG_CHECKS_CH32_H +#define MOZZI_CONFIG_CHECKS_CH32_H + +#if !defined(MOZZI_AUDIO_MODE) +#define MOZZI_AUDIO_MODE MOZZI_OUTPUT_PWM +#endif + +// Configure Audio Bits based on Mode +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM) + #if !defined(MOZZI_AUDIO_BITS) + #define MOZZI_AUDIO_BITS 8 // Output resolution is 8-bit + #endif +#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM) + #if !defined(MOZZI_AUDIO_BITS) + #define MOZZI_AUDIO_BITS 16 // Full 16-bit output + #endif +#endif + +#if !defined(MOZZI_AUDIO_RATE) +#define MOZZI_AUDIO_RATE 16384 +#endif + +#if !defined(MOZZI_ANALOG_READ) +#define MOZZI_ANALOG_READ MOZZI_ANALOG_READ_STANDARD +#endif + +#define MOZZI__INTERNAL_ANALOG_READ_RESOLUTION 12 + +#endif \ No newline at end of file diff --git a/internal/config_checks_esp32.h b/internal/config_checks_esp32.h index e4f1d6cc7..989a3bb1c 100644 --- a/internal/config_checks_esp32.h +++ b/internal/config_checks_esp32.h @@ -220,6 +220,9 @@ MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_INPUT, MOZZI_AUDIO_INPUT_NONE) // All modes besides timed external bypass the output buffer! #if !MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPUT_INTERNAL_DAC, MOZZI_OUTPUT_PWM) # define BYPASS_MOZZI_OUTPUT_BUFFER true +#if (MOZZI_OUTPUT_BUFFER_SIZE != 256) +# warning MOZZI_OUTPUT_BUFFER_SIZE does not have an effect in this mode. +#endif #endif #define MOZZI__INTERNAL_ANALOG_READ_RESOLUTION 12 diff --git a/internal/config_checks_esp8266.h b/internal/config_checks_esp8266.h index 00cfff8ab..ab6c516f8 100644 --- a/internal/config_checks_esp8266.h +++ b/internal/config_checks_esp8266.h @@ -114,6 +114,9 @@ MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_BITS, 16) // esp. since i2s output already has output rate control -> no need for a // separate output timer #define BYPASS_MOZZI_OUTPUT_BUFFER true +#if (MOZZI_OUTPUT_BUFFER_SIZE != 256) +# warning MOZZI_OUTPUT_BUFFER_SIZE does not have an effect in this mode. +#endif #endif #define MOZZI__INTERNAL_ANALOG_READ_RESOLUTION 10 diff --git a/internal/config_checks_generic.h b/internal/config_checks_generic.h index 3bd8f0b23..6adda4230 100644 --- a/internal/config_checks_generic.h +++ b/internal/config_checks_generic.h @@ -78,6 +78,10 @@ #define MOZZI_AUDIO_INPUT_PIN 0 #endif +#if not defined(MOZZI_OUTPUT_BUFFER_SIZE) +#define MOZZI_OUTPUT_BUFFER_SIZE 256 +#endif + //MOZZI_PWM_RATE -> hardware specific //MOZZI_AUDIO_PIN_1 -> hardware specific //MOZZI_AUDIO_PIN_1_LOW -> hardware specific @@ -104,6 +108,8 @@ #include "config_checks_stm32duino.h" #elif IS_STM32MAPLE() #include "config_checks_stm32maple.h" +#elif IS_CH32() +#include "config_checks_CH32.h" #elif (IS_TEENSY3() || IS_TEENSY4()) #include "config_checks_teensy.h" #else @@ -121,6 +127,10 @@ /// Step 3: Apply various generic checks that make sense on more than one platform MOZZI_CHECK_POW2(MOZZI_AUDIO_RATE) MOZZI_CHECK_POW2(MOZZI_CONTROL_RATE) +MOZZI_CHECK_POW2(MOZZI_OUTPUT_BUFFER_SIZE) +#if (MOZZI_OUTPUT_BUFFER_SIZE > 256) +#error "Mozzi does not support buffer sizes greated than 256 at the moment" +#endif #if MOZZI_IS(MOZZI_AUDIO_INPUT, MOZZI_AUDIO_INPUT_STANDARD) && MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_NONE) #error "MOZZI_AUDIO_INPUT depends on MOZZI_ANALOG_READ option" @@ -199,4 +209,4 @@ MOZZI_CHECK_SUPPORTED(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_NONE, MOZZI_ANALOG_RE #include "../AudioOutput.h" static_assert(MOZZI_AUDIO_BITS <= (8*sizeof(AudioOutputStorage_t)), "Configured MOZZI_AUDIO_BITS is too large for the internal storage type"); -#endif +#endif \ No newline at end of file diff --git a/internal/config_checks_mbed.h b/internal/config_checks_mbed.h index 7ac69ba91..a99c8de7f 100644 --- a/internal/config_checks_mbed.h +++ b/internal/config_checks_mbed.h @@ -117,9 +117,12 @@ MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_INPUT, MOZZI_AUDIO_INPUT_NONE, MOZZI_AUDIO_INP # endif #endif -// All modes besides timed external bypass the output buffer! +// All modes besides timed external bypass the output buffer! In these modes, the buffer size is not configurable at the moment: throw an error if the user tries to change it. #if !MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED) # define BYPASS_MOZZI_OUTPUT_BUFFER true +# if (MOZZI_OUTPUT_BUFFER_SIZE != 256) // has been modified +# warning MOZZI_OUTPUT_BUFFER_SIZE does not have an effect in this mode. +# endif #endif // TODO: This value is correct for Arduino Giga and Arduino Portenta, but not necessarily everywhere else diff --git a/internal/config_checks_rp2040.h b/internal/config_checks_rp2040.h index 5ba884415..569e3406d 100644 --- a/internal/config_checks_rp2040.h +++ b/internal/config_checks_rp2040.h @@ -95,7 +95,12 @@ MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPU # endif # define BYPASS_MOZZI_OUTPUT_BUFFER true # define MOZZI_RP2040_BUFFERS 8 // number of DMA buffers used -# define MOZZI_RP2040_BUFFER_SIZE 256 // total size of the buffer, in samples +# if !defined MOZZI_RP2040_BUFFER_SIZE +# define MOZZI_RP2040_BUFFER_SIZE MOZZI_OUTPUT_BUFFER_SIZE // total size of the buffer, in samples +# if (MOZZI_OUTPUT_BUFFER_SIZE < MOZZI_RP2040_BUFFERS) +# error MOZZI_OUTPUT_BUFFER_SIZE cannot be lower than 8 on this platform at the moment +# endif +# endif #endif #if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC) @@ -115,7 +120,12 @@ MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPU MOZZI_CHECK_SUPPORTED(MOZZI_I2S_FORMAT, MOZZI_I2S_FORMAT_PLAIN, MOZZI_I2S_FORMAT_LSBJ) # define BYPASS_MOZZI_OUTPUT_BUFFER true # define MOZZI_RP2040_BUFFERS 8 // number of DMA buffers used -# define MOZZI_RP2040_BUFFER_SIZE 256 // total size of the buffer, in samples +# if !defined MOZZI_RP2040_BUFFER_SIZE +# define MOZZI_RP2040_BUFFER_SIZE MOZZI_OUTPUT_BUFFER_SIZE // total size of the buffer, in samples +# if (MOZZI_OUTPUT_BUFFER_SIZE < MOZZI_RP2040_BUFFERS) +# error MOZZI_OUTPUT_BUFFER_SIZE cannot be lower than 8 on this platform at the moment +# endif +# endif #endif #if !defined(MOZZI_ANALOG_READ) diff --git a/library.properties b/library.properties index 23e5622b5..9de07e230 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Mozzi -version=2.0.2 +version=2.0.3 author=Tim Barrass and contributors as documented in source, and at https://github.com/sensorium/Mozzi/graphs/contributors maintainer=Tim Barrass sentence=Sound synthesis library for Arduino