diff --git a/AudioConfigESP.h b/AudioConfigESP.h deleted file mode 100644 index b2c9855cd..000000000 --- a/AudioConfigESP.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef AUDIOCONFIGESP_H -#define AUDIOCONFIGESP_H - -#if not IS_ESP8266() -#error This header should be included for ESP architecture, only -#endif - -// AUDIO output modes. See README.md -#define PDM_VIA_I2S 1 -#define PDM_VIA_SERIAL 2 -#define EXTERNAL_DAC_VIA_I2S 3 // output via external DAC connected to I2S (PT8211 or similar) - -//******* BEGIN: These are the defines you may want to change. Best not to touch anything outside this range. ************/ -#define ESP_AUDIO_OUT_MODE PDM_VIA_SERIAL -#define PDM_RESOLUTION 2 // 1 corresponds to 32 PDM clocks per sample, 2 corresponds to 64 PDM clocks, etc. (and at some level you're going hit the hardware limits) -//******* END: These are the defines you may want to change. Best not to touch anything outside this range. ************/ - -#if (ESP_AUDIO_OUT_MODE == EXTERNAL_DAC_VIA_I2S) -#define PDM_RESOLUTION 1 // DO NOT CHANGE THIS VALUE! Not actually PDM coded, but this define is useful to keep code simple. -#endif - -#if (AUDIO_MODE == HIFI) -#error HIFI mode is not available for this CPU architecture (but check ESP_AUDIO_OUT_MODE, and PDM_RESOLUTION) -#endif - -#if (AUDIO_CHANNELS > 1) -#if (ESP_AUDIO_OUT_MODE != EXTERNAL_DAC_VIA_I2S) -#error Stereo is not available for the configured audio output mode -#endif -#endif - -#if (ESP_AUDIO_OUT_MODE != PDM_VIA_SERIAL) -// NOTE: On ESP / output via I2S, we simply use the I2S buffer as the output -// buffer, which saves RAM, but also simplifies things a lot -// esp. since i2s output already has output rate control -> no need for a -// separate output timer -#define BYPASS_MOZZI_OUTPUT_BUFFER true -#endif - -#define AUDIO_BITS 16 -#define AUDIO_BIAS ((uint16_t) 1<<(AUDIO_BITS-1)) - -#endif // #ifndef AUDIOCONFIGESP_H diff --git a/AudioConfigESP32.h b/AudioConfigESP32.h deleted file mode 100644 index fd3fcba76..000000000 --- a/AudioConfigESP32.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef AUDIOCONFIGESP32_H -#define AUDIOCONFIGESP32_H - -#if not IS_ESP32() -#error This header should be included for ESP32 architecture, only -#endif - -#if (AUDIO_MODE == HIFI) -#error HIFI mode is not available for this CPU architecture (but check ESP32_AUDIO_OUT_MODE, and PDM_RESOLUTION) -#endif - -// Audio output options -#define INTERNAL_DAC 1 // output using internal DAC via I2S, output on pin 26 -#define PT8211_DAC 2 // output using an external PT8211 DAC via I2S -#define PDM_VIA_I2S 3 // output PDM coded sample on the I2S data pin (pin 33, by default, configurable, below) - -// Set output mode -#define ESP32_AUDIO_OUT_MODE INTERNAL_DAC - -// For external I2S output, only: I2S_PINS -#define ESP32_I2S_BCK_PIN 26 -#define ESP32_I2S_WS_PIN 25 -#define ESP32_I2S_DATA_PIN 33 - -#include -const i2s_port_t i2s_num = I2S_NUM_0; -/// User config end. Do not modify below this line - -#if (ESP32_AUDIO_OUT_MODE == INTERNAL_DAC) -#define AUDIO_BITS 8 -#define PDM_RESOLUTION 1 -#elif (ESP32_AUDIO_OUT_MODE == PT8211_DAC) -#define AUDIO_BITS 16 -#define PDM_RESOLUTION 1 -#elif (ESP32_AUDIO_OUT_MODE == PDM_VIA_I2S) -#define AUDIO_BITS 16 -#define PDM_RESOLUTION 4 -#else -#error Invalid output mode configured in AudioConfigESP32.h -#endif - -#define AUDIO_BIAS ((uint16_t) 1<<(AUDIO_BITS-1)) -#define BYPASS_MOZZI_OUTPUT_BUFFER true - -#endif // #ifndef AUDIOCONFIGESP_H diff --git a/AudioConfigHiSpeed14bitPwm.h b/AudioConfigHiSpeed14bitPwm.h deleted file mode 100644 index 6123257b4..000000000 --- a/AudioConfigHiSpeed14bitPwm.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef AUDIOCONFIGHISPEED14BITPWM_H -#define AUDIOCONFIGHISPEED14BITPWM_H - -/* -14 bit sound at 16384 Hz and 125kHz pwm rate -Timer 1: PWM 125kHz -Timer 2: called at AUDIO_RATE 16384 Hz, setting Timer1 pwm levels -Output on Timer1, low uint8_t on Pin 10, and high uint8_t on Pin 9 (on 328 based Arduino boards) -Add signals through a 3.9k resistor on high uint8_t pin and 499k resistor on low uint8_t pin. -Use 0.5% resistors or select the most accurate from a batch. -As discussed on http://www.openmusiclabs.com/learning/digital/pwm-dac/dual-pwm-circuits/ -Also, there are higher quality output circuits are on the site. - -Boards, pins and resistor positions are documented in MozziGuts.h -*/ - -/* PWM carrier frequency, for HIFI this should be well out of hearing range, about 5 times the nyquist frequency if possible. */ -#define PWM_RATE 125000 -// following doesn't play nice -//#define PWM_RATE 65536 // count will be 244 (7+ bits) on each pin = 14+ bits - - -// pins defined in TimerOne/config/known_16bit_timers.h -#define AUDIO_CHANNEL_1_highByte_PIN TIMER1_A_PIN // 3.9k resistor -#define AUDIO_CHANNEL_1_lowByte_PIN TIMER1_B_PIN // 499k resistor -#define AUDIO_CHANNEL_1_highByte_REGISTER OCR1AL -#define AUDIO_CHANNEL_1_lowByte_REGISTER OCR1BL - -#define AUDIO_BITS_PER_REGISTER 7 -#define AUDIO_BITS 14 - -/* Used internally to put the 0-biased generated audio into the right range for PWM output.*/ -// 14 bit -#define AUDIO_BIAS ((uint16_t) 1<<(AUDIO_BITS-1)) - - -#endif // #ifndef AUDIOCONFIGHISPEED14BITPWM_H diff --git a/AudioConfigMBED.h b/AudioConfigMBED.h deleted file mode 100644 index eb7d93947..000000000 --- a/AudioConfigMBED.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef AUDIOCONFIGMBED_H -#define AUDIOCONFIGMBED_H - -#if not IS_MBED() -#error This header should be included for MBED OS boards, only -#endif - -#if (AUDIO_MODE == HIFI) -#error HIFI mode is not available for this CPU architecture (but several high quality output options are available) -#endif - -// Audio output options -#define INTERNAL_DAC 1 // output using internal DAC driven via DMA. Output is only possible on the DAC pins (A12, and A13 on the Giga) -#define PDM_VIA_SERIAL 2 // output PDM coded sample on a hardware serial UART. NOTE: currently to be considered experimental. Tune is not correct for all combinatinos of AUDIO_RATE & PDM_RESOLUTION - // Also NOTE that you will almost certainly want to use at least some basic RC filter circuit with this mode - -// Set output mode -#define MBED_AUDIO_OUT_MODE INTERNAL_DAC - -#if (MBED_AUDIO_OUT_MODE == PDM_VIA_SERIAL) -// For use in PDM_VIA_SERIAL, only: Peripheral to use. Note that only the TX channel is actually used, but sine this is a hardware UART, the corresponding -// RX channel needs to be claimed, as well. NOTE: This does not necessarily correspond to the labeling on your board! E.g. SERIAL2_TX is TX1 on the Arduino Giga. -#define PDM_SERIAL_UART_TX_CHANNEL_1 SERIAL2_TX -#define PDM_SERIAL_UART_RX_CHANNEL_1 SERIAL2_RX -#endif - -/// User config end. Do not modify below this line - -#if (MBED_AUDIO_OUT_MODE == INTERNAL_DAC) -#define AUDIO_BITS 12 -#define AUDIO_CHANNEL_1_PIN A13 -#define AUDIO_CHANNEL_2_PIN A12 -#define BYPASS_MOZZI_OUTPUT_BUFFER true -#elif (MBED_AUDIO_OUT_MODE == PDM_VIA_SERIAL) -#define AUDIO_BITS 16 // well, used internally, at least. The pins will not be able to actually produce this many bits -#define PDM_RESOLUTION 2 -#define BYPASS_MOZZI_OUTPUT_BUFFER true -#else -#error Invalid output mode configured in AudioConfigMBED.h -#endif - -//#define BYPASS_MOZZI_INPUT_BUFFER true -#define AUDIO_BIAS ((uint16_t) 1<<(AUDIO_BITS-1)) - -#endif // #ifndef AUDIOCONFIGMBED_H diff --git a/AudioConfigRP2040.h b/AudioConfigRP2040.h deleted file mode 100644 index eed5201b4..000000000 --- a/AudioConfigRP2040.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef AUDIOCONFIGRP2040_H -#define AUDIOCONFIGRP2040_H - -#if not IS_RP2040() -#error This header should be included for RP2040, only -#endif - - -// AUDIO output modes -#define PWM_VIA_BARE_CHIP 1 // output using one of the gpio of the board -#define EXTERNAL_DAC_VIA_I2S 2 // output via external DAC connected to I2S (PT8211 or similar) - -//******* BEGIN: These are the defines you may want to change. Best not to touch anything outside this range. ************/ -#define RP2040_AUDIO_OUT_MODE PWM_VIA_BARE_CHIP -//******* END: These are the defines you may want to change. Best not to touch anything outside this range. ************/ - - -#if (RP2040_AUDIO_OUT_MODE == PWM_VIA_BARE_CHIP) -#define AUDIO_CHANNEL_1_PIN 0 -#if (AUDIO_CHANNELS > 1) -// Audio channel pins for stereo or HIFI must be on the same PWM slice (which is the case for the pairs (0,1), (2,3), (4,5), etc. -#define AUDIO_CHANNEL_2_PIN 1 -#endif - -// The more audio bits you use, the slower the carrier frequency of the PWM signal. 11 bits yields ~ 60kHz on a 133Mhz CPU (which appears to be a reasonable compromise) -#define AUDIO_BITS 11 -#endif - -#if (RP2040_AUDIO_OUT_MODE == EXTERNAL_DAC_VIA_I2S) -// ****** BEGIN: These are define you may want to change. Best not to touch anything outside this range. ************/ -#define BCLK_PIN 20 -#define WS_PIN (pBCLK+1) // CANNOT BE CHANGED, HAS TO BE NEXT TO pBCLK -#define DOUT_PIN 22 -#define LSBJ_FORMAT false // some DAC, like the PT8211, use a variant of I2S data format called LSBJ - // set this to true to use this kind of DAC or false for plain I2S. -#define AUDIO_BITS 16 // available values are 8, 16, 24 (LEFT ALIGN in 32 bits type!!) and 32 bits -// ****** END: These are define you may want to change. Best not to touch anything outside this range. ************/ - -#define BYPASS_MOZZI_OUTPUT_BUFFER true - -// Configuration of the I2S port, especially DMA. Set in stone here as default of the library when this was written. -// Probably do not change if you are not sure of what you are doing -#define BUFFERS 8 // number of DMA buffers used -#define BUFFER_SIZE 256 // total size of the buffer, in samples -#endif - - -#define AUDIO_BITS_PER_CHANNEL AUDIO_BITS - -#define AUDIO_BIAS ((uint16_t) 1<<(AUDIO_BITS-1)) - -#endif // #ifndef AUDIOCONFIGRP2040_H - diff --git a/AudioConfigRenesas.h b/AudioConfigRenesas.h deleted file mode 100644 index 2fb3523c0..000000000 --- a/AudioConfigRenesas.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef AUDIOCONFIGRENESAS_H -#define AUDIOCONFIGRENESAS_H - -#if not IS_RENESAS() -#error This header should be included for Arduino FSB board (Uno R4/Renesa) family, only -#endif - - - - -#define AUDIO_CHANNEL_1_PIN A0 -#if (AUDIO_CHANNELS > 1) -#define AUDIO_CHANNEL_2_PIN 10 -#endif - -#define AUDIO_BITS 12 // outputting resolution of the on-bard DAC. Other values are *NOT* expected to work. - -#define AUDIO_BITS_PER_CHANNEL AUDIO_BITS - -#define AUDIO_BIAS ((uint16_t) 1<<(AUDIO_BITS-1)) - -#define BYPASS_MOZZI_OUTPUT_BUFFER true // Mozzi initial buffer are not of the good type, so we bypass it and create our own - -#endif // #ifndef AUDIOCONFIGRENESAS_H - diff --git a/AudioConfigSAMD21.h b/AudioConfigSAMD21.h deleted file mode 100644 index 61984f19b..000000000 --- a/AudioConfigSAMD21.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef AUDIOCONFIGSAMD21_H -#define AUDIOCONFIGSAMD21_H - -/* Note: SAMD21 has 12 bits ADC, but only 10 bits DAC. See https://github.com/sensorium/Mozzi/issues/75 */ -#define AUDIO_BITS 10 - -/** @ingroup core -*/ -/* Used internally to put the 0-biased generated audio into the centre of the output range (10 bits) */ -#define AUDIO_BIAS ((uint16_t) 1 << (AUDIO_BITS - 1)) - -#define AUDIO_CHANNEL_1_PIN DAC0 - -#endif // #ifndef AUDIOCONFIGSAMD21_H - diff --git a/AudioConfigSTM32.h b/AudioConfigSTM32.h deleted file mode 100644 index e8a956d42..000000000 --- a/AudioConfigSTM32.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef AUDIOCONFIGSTM32_H -#define AUDIOCONFIGSTM32_H - -#if not IS_STM32() -#error This header should be included for STM32, only -#endif - -// Audio output pin. If you want to change this, make sure to also set AUDIO_PWM_TIMER to whichever timer is responsible for your PWM pin, and set the other timers to non-conflicting values -#define AUDIO_CHANNEL_1_PIN PB8 -#define AUDIO_PWM_TIMER 4 -// The timer used for running the audio update loop. NOTE: Timer 3 appears to clash with SPI DMA transfers under some circumstances -#define AUDIO_UPDATE_TIMER 2 - -#if (AUDIO_MODE == HIFI) -// Second out pin for HIFI mode. This must be on the same timer as AUDIO_CHANNEL_1_PIN! -// Note that by default we are not using adjacent pins. This is to leave the "Serial1" pins available (often used for upload/communication with Arduino IDE). If you don't need that, PA9 is a good choice. -#define AUDIO_CHANNEL_1_PIN_HIGH PB9 -// Total audio bits. -#define AUDIO_BITS 14 -#define AUDIO_BITS_PER_CHANNEL 7 -#else -// The more audio bits you use, the slower the carrier frequency of the PWM signal. 10 bits yields ~ 70kHz on a 72Mhz CPU (which appears to be a reasonable compromise) -#define AUDIO_BITS 10 -#define AUDIO_BITS_PER_CHANNEL AUDIO_BITS -#if (AUDIO_CHANNELS > 1) -#define AUDIO_CHANNEL_2_PIN PB9 -#endif -#endif - -#define AUDIO_BIAS ((uint16_t) 1<<(AUDIO_BITS-1)) - -#endif // #ifndef AUDIOCONFIGSTM32_H - diff --git a/AudioConfigSTM32duino.h b/AudioConfigSTM32duino.h deleted file mode 100644 index 4494f76b2..000000000 --- a/AudioConfigSTM32duino.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef AUDIOCONFIGSTM32_H -#define AUDIOCONFIGSTM32_H - -#if not IS_STM32DUINO() -#error This header should be included for STM32 (stm32duino.com core), only -#endif - -// Audio output pin. If you want to change this, make sure to also set AUDIO_PWM_TIMER to whichever timer is responsible for your PWM pin, and set the other timers to non-conflicting values -#define AUDIO_CHANNEL_1_PIN PA8 // Note: PB8 does not appear to be available as a PWM pin with this core. -// The timer used for running the audio update loop. This must _not_ be the same timer responsible for PWM on the output pins! NOTE: Timer 3 appears to clash with SPI DMA transfers under some circumstances -#define AUDIO_UPDATE_TIMER TIM2 - -#if (AUDIO_MODE == HIFI) -// Second out pin for HIFI mode. This must be on the same timer as AUDIO_CHANNEL_1_PIN! -// Note that by default we are not using adjacent pins. This is to leave the "Serial1" pins available (often used for upload/communication with Arduino IDE). If you don't need that, PA9 is a good choice. -#define AUDIO_CHANNEL_1_PIN_HIGH PA9 -// Total audio bits. -#define AUDIO_BITS 14 -#define AUDIO_BITS_PER_CHANNEL 7 -#else -// The more audio bits you use, the slower the carrier frequency of the PWM signal. 10 bits yields ~ 70kHz on a 72Mhz CPU (which appears to be a reasonable compromise) -#define AUDIO_BITS 10 -#define AUDIO_BITS_PER_CHANNEL AUDIO_BITS -#if (AUDIO_CHANNELS > 1) -// Second out pin for stereo mode. This must be on the same timer as AUDIO_CHANNEL_1_PIN! -#define AUDIO_CHANNEL_2_PIN PA9 -#endif -#endif - -#define AUDIO_BIAS ((uint16_t) 1<<(AUDIO_BITS-1)) - -#endif // #ifndef AUDIOCONFIGSTM32_H - diff --git a/AudioConfigStandard9bitPwm.h b/AudioConfigStandard9bitPwm.h deleted file mode 100644 index e7c9402c2..000000000 --- a/AudioConfigStandard9bitPwm.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef AUDIOCONFIGSTANDARD9BITPWM_H -#define AUDIOCONFIGSTANDARD9BITPWM_H - - -/** @ingroup core -This is the dynamic range of Mozzi's audio output in STANDARD mode. -It is equal to Timer1.pwmPeriod calculated for interrupt rate 16384. -It's included in the documentation because it's a slightly unusual number and useful to know -about when you're writing sketches. -*/ -#define STANDARD_PWM_RESOLUTION 488 - -/* PWM carrier frequency, for standard mode this will be the same as the audio rate. */ -#define PWM_RATE 16384 - -/* Used internally to put the 0-biased generated audio into the right range for PWM output.*/ -#define AUDIO_BIAS ((uint8_t) 244) - -// Used internally. If there was a channel 2, it would be OCR1B. -#define AUDIO_CHANNEL_1_OUTPUT_REGISTER OCR1A - -#define AUDIO_CHANNEL_1_PIN TIMER1_A_PIN // defined in TimerOne/config/known_16bit_timers.h - -#endif // #ifndef AUDIOCONFIGSTANDARD9BITPWM_H - diff --git a/AudioConfigStandardPlus.h b/AudioConfigStandardPlus.h deleted file mode 100644 index 0d0b3c27a..000000000 --- a/AudioConfigStandardPlus.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef AUDIOCONFIGSTANDARDPLUS_H -#define AUDIOCONFIGSTANDARDPLUS_H - - -/** @ingroup core -This is the dynamic range of Mozzi's audio output in STANDARD mode. -It is equal to Timer1.pwmPeriod calculated for interrupt rate 16384. -It's included in the documentation because it's a slightly unusual number and useful to know -about when you're writing sketches. -*/ -#define STANDARD_PWM_RESOLUTION 488 - -/* Used internally for standard mode because the audio gets updated every alternate ISR, so the PWM rate is double the audio update rate */ -#define PWM_RATE 32768 - -/* Used internally to put the 0-biased generated audio into the right range for PWM output.*/ -#define AUDIO_BIAS ((uint8_t) 244) - -// Used internally. If there was a channel 2, it would be OCR1B. -#define AUDIO_CHANNEL_1_OUTPUT_REGISTER OCR1A -#define AUDIO_CHANNEL_2_OUTPUT_REGISTER OCR1B - -#define AUDIO_CHANNEL_1_PIN TIMER1_A_PIN // defined in TimerOne/config/known_16bit_timers.h -#define AUDIO_CHANNEL_2_PIN TIMER1_B_PIN - -#endif // #ifndef AUDIOCONFIGSTANDARDPLUS_H diff --git a/AudioConfigTeensy3_12bit.h b/AudioConfigTeensy3_12bit.h deleted file mode 100644 index 0f9a7d550..000000000 --- a/AudioConfigTeensy3_12bit.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef AUDIOCONFIGTEENSY3_12BIT_H -#define AUDIOCONFIGTEENSY3_12BIT_H - -#warning If you get a compilation error you should probably update Teensyduino to its latest version - -/** @ingroup core -*/ -/* Used internally to put the 0-biased generated audio into the centre of the output range (12 bits on Teensy 3) */ -#define AUDIO_BIAS ((uint16_t) 2048) -#define AUDIO_BITS 12 - -#if !defined(AUDIO_CHANNEL_1_PIN) && defined(__arm__) && defined(CORE_TEENSY) -#if defined(__MKL26Z64__) -#define AUDIO_CHANNEL_1_PIN A12 -#elif defined(__MK20DX128__) || defined(__MK20DX256__) -#define AUDIO_CHANNEL_1_PIN A14 -#elif defined(__MK64FX512__) || defined(__MK66FX1M0__) -#define AUDIO_CHANNEL_1_PIN A21 -#else -#error "Unknown Teensy" -#endif -#endif - -#endif // #ifndef AUDIOCONFIGTEENSY3_12BIT_H - diff --git a/AudioConfigTeensy4.h b/AudioConfigTeensy4.h deleted file mode 100644 index e320d385d..000000000 --- a/AudioConfigTeensy4.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef AUDIOCONFIGTEENSY4_H -#define AUDIOCONFIGTEENSY4_H - -#warning If you get a compilation error you should probably update Teensyduino to its latest version - -/** @ingroup core -*/ -/* Used internally to put the 0-biased generated audio into the centre of the output range (10 bits on Teensy 4 using PWM) */ -#define AUDIO_BIAS ((uint16_t) 512) -#define AUDIO_BITS 10 - -#define AUDIO_CHANNEL_1_PIN A8 -#define AUDIO_CHANNEL_2_PIN A9 - - -#endif // #ifndef AUDIOCONFIGTEENSY4_H - diff --git a/AudioOutput.h b/AudioOutput.h index 8ad297844..0fc3b3f94 100644 --- a/AudioOutput.h +++ b/AudioOutput.h @@ -1,6 +1,5 @@ -/** @file AudioOutput - * - * Platform independent audio output and adding support for new platforms or output methods. +/** @ingroup conre + * @page mozzi_audio_output_architecture Basic architecture of audio generation, buffering, and output in Mozzi * * Mozzi provides support for audio ouput on a range of different boards and CPUs. This page is about the following related topics: * @@ -10,52 +9,52 @@ * * For all of these topics, it is helpful to have a basic understanding of the basic output steps in Mozzi: * - * 1. Inside the loop() function in your sketch you call audioHook(). This function is responsible for calling updateAudio(), whenever there is room in the output buffer, - * adding the generated sample to the output buffer, calling updateControl() at an appropriate rate, and a few other details that are not important for this discussion. + * 1. Inside the loop() function in your sketch you call audioHook(). + * 1a. If the audio output buffer is currently filled, this does nothing. + * 1b. Otherwise, this calls updateAudio(). The generated sample is then added to the audio output buffer. (Also, updateControl() will be called at an appropriate rate, + * and a few other details that are not important for this discussion.) * * 2. A platform-specific timer is triggered at audio rate (usually), takes a sample from the output buffer and sends it to audioOutput(). * * 3. The audioOutput() function - usually predefined inside Mozzi - takes care of sending the sample to the hardware. * - * This basic output pipeline can be customized in several ways. First, defining EXTERNAL_AUDIO_OUTPUT to true in mozzi_config.h will allow you to define your own audioOutput() - * fuction. The library ships with some sample sketches for output to external DACs using this mechanism. + * These output steps are not always followed, however. Firstly, when using @ref external_audio output, the audioOutput() funtion is supplied by the user sketch, + * instead of Mozzi. @ref external_audio output. * - * In some cases, you will additionally want to bypass Mozzis output buffer, for example, if your board, or your external DAC already comes with an efficient built-in buffer. - * In this case, define BYPASS_MOZZI_OUTPUT_BUFFER to true. You will then have to provide a custom definition of canBufferAudioOutput(), returning true whenever your hardware - * is ready toaccept a new sample of output. This is called from inside audioHook(), and whenever there is room for a new sample, it is generated and sent to audioOutput(), - * immediately. In this case, should you need a timer running at AUDIO_RATE, you will have to set up one, yourself, if needed. + * Some ports will also want to bypass the Mozzi audio output buffer. For instance, an internal DAC may be addressable via an efficient DMA-connected + * buffer, already, and also have a built-in rate control. In this case, ports will internally set the define @ref BYPASS_MOZZI_OUTPUT_BUFFER to true. Such a port will + * have to provide a custom definition of canBufferAudioOutput(), returning true, whenever a new sample of output can be accepted. No timer at audio-rate is set up in this + * case. * - * In custom code, setting BYPASS_MOZZI_OUTPUT_BUFFER does not make much sense without EXTERNAL_AUDIO_OUTPUT also set to true. However, some platform ports (e.g. ESP32) actually - * use this combination, internally. + * Finally, the @ref external_audio output mode @ref MOZZI_OUTPUT_EXTERNAL_CUSTOM is essentially a combination of the two. Here, the user sketch needs to provide + * both audioOutput() and canBufferAudioOutput(). The latter is again called from audioHook(), and whenever it returns true, a new sample is generated and passed to + * audioOutput(). * + * @section audio_shifting Platform specific audio resolution * Different output methods often support a different resolution of output samples. To provide best performance on slow boards, Mozzi expects your updateAudio() function to * return samples in exactly the width that is needed at the output stage. Thus, defining this naively, an updateAudio() function designed for 8 bit output will produce very * low volume output on a 16 bit DAC, while the other way around overflows will result in way too loud and heavily distored output. Fortunately, all that is needed to write * portable sketches is to specify how many bits your updateAudio() function provides. The (inline) functions in the AudioOutput namespace do just that. Using them makes sure * your audio output is shifted if, and as much as needed on all platforms. + * + * @see MonoOutput::fromNBit(), StereoOutput::fromNBit() */ #ifndef AUDIOOUTPUT #define AUDIOOUTPUT -#include "MozziGuts.h" +#include "mozzi_config.h" /** The type used to store a single channel of a single frame, internally. For compatibility with earlier versions of Mozzi this is defined as int. * If you do not care about keeping old sketches working, you may be able to save some RAM by using int16_t, instead (on boards where int is larger * than 16 bits). */ #define AudioOutputStorage_t int -#if IS_AVR() && ((AUDIO_MODE == STANDARD_PLUS) || (AUDIO_MODE == STANDARD)) -#define SCALE_AUDIO(x,bits) (bits > 8 ? (x) >> (bits - 8) : (x) << (8 - bits)) -#define SCALE_AUDIO_NEAR(x,bits) (bits > 9 ? (x) >> (bits - 9) : (x) << (9 - bits)) -#define CLIP_AUDIO(x) constrain((x), -244,243) -#else -#define SCALE_AUDIO(x,bits) (bits > AUDIO_BITS ? (x) >> (bits - AUDIO_BITS) : (x) << (AUDIO_BITS - bits)) -#define SCALE_AUDIO_NEAR(x,bits) SCALE_AUDIO(x,bits) -#define CLIP_AUDIO(x) constrain((x), (-(AudioOutputStorage_t) AUDIO_BIAS), (AudioOutputStorage_t) AUDIO_BIAS-1) -#endif +#define SCALE_AUDIO(x,bits) (bits > MOZZI_AUDIO_BITS ? (x) >> (bits - MOZZI_AUDIO_BITS) : (x) << (MOZZI_AUDIO_BITS - bits)) +#define SCALE_AUDIO_NEAR(x,bits) (bits > MOZZI_AUDIO_BITS_OPTIMISTIC ? (x) >> (bits - MOZZI_AUDIO_BITS_OPTIMISTIC) : (x) << (MOZZI_AUDIO_BITS_OPTIMISTIC - bits)) +#define CLIP_AUDIO(x) constrain((x), (-(AudioOutputStorage_t) MOZZI_AUDIO_BIAS), (AudioOutputStorage_t) MOZZI_AUDIO_BIAS-1) -#if (AUDIO_CHANNELS == STEREO) +#if MOZZI_IS(MOZZI_AUDIO_CHANNELS, MOZZI_STEREO) #define AudioOutput StereoOutput #if (STEREO_HACK == true) #define AudioOutput_t void @@ -79,19 +78,19 @@ * in mozzi_config.h. Since the two are source compatible to a large degree, it often isn't even necessary to test, which it is, in your code. E.g. * both have functions l() and r(), to return "two" audio channels (which will be the same in case of mono). * - * You will not usually use or encounter this definition, unless using EXTERNAL_AUDIO_OUTPUT. + * You will not usually use or encounter this definition, unless using @ref external_audio output mode. */ #define AudioOutput MonoOutput #endif /** This struct encapsulates one frame of mono audio output. Internally, it really just boils down to two int values, but the struct provides - * useful API an top of that. For more detail @see MonoOutput . */ + * useful API an top of that. For more detail see @ref MonoOutput . */ struct StereoOutput { /** Construct an audio frame from raw values (zero-centered) */ StereoOutput(AudioOutputStorage_t l, AudioOutputStorage_t r) : _l(l), _r(r) {}; /** Default contstructor */ StereoOutput() : _l(0), _r(0) {}; -#if (AUDIO_CHANNELS != STEREO) +#if !MOZZI_IS(MOZZI_AUDIO_CHANNELS, MOZZI_STEREO) /** Conversion to int operator: If used in a mono config, returns only the left channel (and gives a compile time warning). This _could_ be turned into an operator for implicit conversion in this case. For now we chose to apply conversion on demand, only, as most of the time using StereoOutput in a mono config, is not intended. */ @@ -99,16 +98,16 @@ struct StereoOutput { #endif AudioOutputStorage_t l() const { return _l; }; AudioOutputStorage_t r() const { return _r; }; - /** @see MonoOutput::clip(). Clips both channels. */ + /** See @ref MonoOutput::clip(). Clips both channels. */ StereoOutput& clip() { _l = CLIP_AUDIO(_l); _r = CLIP_AUDIO(_r); return *this; }; - /** @see MonoOutput::fromNBit(), stereo variant */ + /** See @ref MonoOutput::fromNBit(), stereo variant */ template static inline StereoOutput fromNBit(uint8_t bits, T l, T r) { return StereoOutput(SCALE_AUDIO(l, bits), SCALE_AUDIO(r, bits)); } - /** @see MonoOutput::from8Bit(), stereo variant */ + /** See @ref MonoOutput::from8Bit(), stereo variant */ static inline StereoOutput from8Bit(int16_t l, int16_t r) { return fromNBit(8, l, r); } - /** @see MonoOutput::from16Bit(), stereo variant */ + /** See @ref MonoOutput::from16Bit(), stereo variant */ static inline StereoOutput from16Bit(int16_t l, int16_t r) { return fromNBit(16, l, r); } - /** @see MonoOutput::fromAlmostNBit(), stereo variant */ + /** See @ref MonoOutput::fromAlmostNBit(), stereo variant */ template static inline StereoOutput fromAlmostNBit(A bits, B l, B r) { return StereoOutput(SCALE_AUDIO_NEAR(l, bits), SCALE_AUDIO_NEAR(r, bits)); } private: AudioOutputStorage_t _l; @@ -126,13 +125,13 @@ template static inline StereoOutput fromNBit(uint8_t bits, T l, T r) * c) The struct provides accessors l() and r() that are source-compatible with StereoOutput, making it easy to e.g. implement support for an external DAC in both mono * and stereo. * d) Finally, an automatic conversion operator to int aka AudioOutput_t provides backward compatibility with old Mozzi sketches. Internally, the compiler will actually - * do away with this wholw struct, leaving just the same basic fast integer operations as in older Mozzi sketches. However, now, you don't have to rewrite those for + * do away with this whole struct, leaving just the same basic fast integer operations as in older Mozzi sketches. However, now, you don't have to rewrite those for * different configurations. */ struct MonoOutput { /** Construct an audio frame from raw values (zero-centered) */ MonoOutput(AudioOutputStorage_t l=0) : _l(l) {}; -#if (AUDIO_CHANNELS > 1) +#if (MOZZI_AUDIO_CHANNELS > 1) /** Conversion to stereo operator: If used in a stereo config, returns identical channels (and gives a compile time warning). This _could_ be turned into an operator for implicit conversion in this case. For now we chose to apply conversion on demand, only, as most of the time using StereoOutput in a mono config, is not intended. */ @@ -151,7 +150,7 @@ struct MonoOutput { * bits available. While this function takes care of the shifting, beware of potential overflow issues, if your intermediary results exceed the 16 bit range. Use proper * casts to int32_t or larger in that case (and the compiler will automatically pick the 32 bit overload in this case) */ template static inline MonoOutput fromNBit(uint8_t bits, T l) { return MonoOutput(SCALE_AUDIO(l, bits)); } - /** Construct an audio frame from a zero-centered value known to be in the 8 bit range. On AVR, STANDADR or STANDARD_PLUS mode, this is effectively the same as calling the + /** Construct an audio frame from a zero-centered value known to be in the 8 bit range. On AVR, if MOZZI_OUTPUT_PWM mode, this is effectively the same as calling the * constructor, directly (no scaling gets applied). On platforms/configs using more bits, an appropriate left-shift will be performed. */ static inline MonoOutput from8Bit(int16_t l) { return fromNBit(8, l); } /** Construct an audio frame a zero-centered value known to be in the 16 bit range. This is jsut a shortcut for fromNBit(16, ...) provided for convenience. */ @@ -159,10 +158,12 @@ struct MonoOutput { /** Construct an audio frame a zero-centered value known to be above at almost but not quite the N bit range, e.g. at N=8 bits and a litte. On most platforms, this is * exactly the same as fromNBit(), shifting up or down to the platforms' available resolution. * - * However, on AVR, STANDARD(_PLUS) (where about 8.5 bits are usable), the value will be shifted to the (almost) 9 bit range, instead of to the 8 bit range. allowing to - * make use of that extra half bit of resolution. In many cases it is useful to follow up this call with clip(). + * However, on AVR, MOZZI_OUTPUT_PWM mode (where about 8.5 bits are usable), the value will be shifted to the (almost) 9 bit range, instead of to the 8 bit range. allowing to + * make use of that extra half bit of resolution. In many cases it is useful to follow up this call with clip(). E.g.: * - * @example fromAlmostNBit(10, oscilA.next() + oscilB.next() + oscilC.next()); + * @code + * return MonoOutput::fromAlmostNBit(10, oscilA.next() + oscilB.next() + oscilC.next()).clip(); + * @endcode */ template static inline MonoOutput fromAlmostNBit(A bits, B l) { return MonoOutput(SCALE_AUDIO_NEAR(l, bits)); } @@ -171,15 +172,17 @@ struct MonoOutput { }; -/** When setting EXTERNAL_AUDIO_OUTPUT to true, implement this function to take care of writing samples to the hardware. */ +/** When setting using one of the external output modes (@ref MOZZI_OUTPUT_EXTERNAL_TIMED or @ref MOZZI_OUTPUT_EXTERNAL_CUSTOM) implement this function to take care of writing samples to the hardware. + * In all otther cases, it will be provided by the platform implementation. You should never call this function, directly, in your sketch. */ void audioOutput(const AudioOutput f); -#if BYPASS_MOZZI_OUTPUT_BUFFER -/** When setting BYPASS_MOZZI_OUTPUT_BUFFER to true, implement this function to return true, if and only if your hardware (or custom buffer) is ready to accept the next sample. */ +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_CUSTOM) +/** For @ref MOZZI_OUTPUT_EXTERNAL_CUSTOM implement this function to return true, if and only if your hardware (or custom buffer) is ready to accept the next sample. */ inline bool canBufferAudioOutput(); #endif /** Perform one step of (fast) pdm encoding, returning 8 "bits" (i.e. 8 ones and zeros). - * You will usually call this at least four or eight times for a single input sample. + * You will usually call this at least four or eight times, and possibly much more often + * for a single input sample. * * The return type is defined as uint32_t to avoid conversion steps. Actually, only the 8 lowest * bits of the return value are set. */ @@ -195,7 +198,9 @@ inline uint32_t pdmCode8(uint16_t sample) { // Note that sample only has 16 bits, while the // highest bit we consider for writing is bit 17. // Thus, if the highest bit is set, the next - // three bits cannot be. + // three bits cannot be. (highest possible values: + // nexttarget-lastwritten == 0b00001111111111111, + // sample == 0b01111111111111111) nexttarget += sample; nexttarget -= lastwritten; lastwritten = nexttarget & 0b11110000000000000; @@ -212,8 +217,4 @@ inline uint32_t pdmCode32(uint16_t sample) { return outbits; } -#if (EXTERNAL_AUDIO_OUTPUT == true) -#warning "Mozzi is configured to use an external void 'audioOutput(const AudioOutput f)' function. Please define one in your sketch" -#endif - #endif diff --git a/MozziConfigValues.h b/MozziConfigValues.h new file mode 100644 index 000000000..8470158d4 --- /dev/null +++ b/MozziConfigValues.h @@ -0,0 +1,44 @@ +/** This file keeps a list of named configuration values. + +Note that these are all given as defines, instead of e.g. const ints or enum values, because they need to be usable at preprocessor level, in order to control conditional compilation. + +TODO: Fix documentation +*/ + +#ifndef MOZZICONFIG_VALUES_H +#define MOZZICONFIG_VALUES_H + + + +#define MOZZI_MONO 1 +#define MOZZI_STEREO 2 + +// We try to use distinct values as much as possible so we can catch semantic errors like "#define MOZZI_AUDIO_MODE MOZZI_STEREO" +#define MOZZI_OUTPUT_PWM 101 +#define MOZZI_OUTPUT_2PIN_PWM 102 +#define MOZZI_OUTPUT_EXTERNAL_TIMED 103 +#define MOZZI_OUTPUT_EXTERNAL_CUSTOM 104 +#define MOZZI_OUTPUT_PDM_VIA_I2S 105 +#define MOZZI_OUTPUT_PDM_VIA_SERIAL 106 +#define MOZZI_OUTPUT_I2S_DAC 107 +#define MOZZI_OUTPUT_INTERNAL_DAC 108 + +#define MOZZI_AUDIO_INPUT_NONE 201 +#define MOZZI_AUDIO_INPUT_STANDARD 202 + +#define MOZZI_ANALOG_READ_NONE 301 +#define MOZZI_ANALOG_READ_STANDARD 302 + +#define MOZZI_I2S_FORMAT_PLAIN 401 +#define MOZZI_I2S_FORMAT_LSBJ 402 + +// defined with some space in between, just in case. This should be numerically ordered. +#define MOZZI_COMPATIBILITY_1_1 1100 +#define MOZZI_COMPATIBILITY_2_0 2000 +#define MOZZI_COMPATIBILITY_LATEST 9000 // May be upped, arbitrarily + +// For convenience +#include "hardware_defines.h" + + +#endif diff --git a/MozziGuts.cpp b/MozziGuts.cpp index b6aad9dda..5fe2676ee 100644 --- a/MozziGuts.cpp +++ b/MozziGuts.cpp @@ -14,26 +14,23 @@ #include "CircularBuffer.h" #include "MozziGuts.h" #include "mozzi_analog.h" -#include "mozzi_config.h" // at the top of all MozziGuts and analog files //#include "mozzi_utils.h" #include "AudioOutput.h" -#define AUDIO_INPUT_NONE 0 -#define AUDIO_INPUT_LEGACY 1 -#define AUDIO_INPUT_CUSTOM 2 - - // Forward declarations of functions to be provided by platform specific implementations #if (!BYPASS_MOZZI_OUTPUT_BUFFER) static void CACHED_FUNCTION_ATTR defaultAudioOutput(); #endif -static void advanceADCStep(); -static void startSecondADCReadOnCurrentChannel(); +#if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_STANDARD) +static void advanceADCStep(); // to be provided by platform implementation +static void startSecondADCReadOnCurrentChannel(); // to be provided by platform implementation +static uint8_t adc_count = 0; // needed below +#endif // Include the appropriate implementation #if IS_AVR() # include "MozziGuts_impl_AVR.hpp" -#elif IS_STM32() +#elif IS_STM32MAPLE() # include "MozziGuts_impl_STM32.hpp" #elif IS_STM32DUINO() # include "MozziGuts_impl_STM32duino.hpp" @@ -58,15 +55,14 @@ static void startSecondADCReadOnCurrentChannel(); /* Retro-compatibility with "legacy" boards which use the async ADC for getting AUDIO_INPUT */ -#if (defined(USE_AUDIO_INPUT) && !defined(AUDIO_INPUT_MODE)) - #define AUDIO_INPUT_MODE AUDIO_INPUT_LEGACY -#elif !defined(AUDIO_INPUT_MODE) - #define AUDIO_INPUT_MODE AUDIO_INPUT_NONE +#if !defined(MOZZI__LEGACY_AUDIO_INPUT_IMPL) +# if !MOZZI_IS(MOZZI_AUDIO_INPUT, MOZZI_AUDIO_INPUT_NONE) +# define MOZZI__LEGACY_AUDIO_INPUT_IMPL 1 +# else +# define MOZZI__LEGACY_AUDIO_INPUT_IMPL 0 +# endif #endif - -static uint8_t adc_count = 0; - ////// BEGIN Output buffering ///// #if BYPASS_MOZZI_OUTPUT_BUFFER == true uint64_t samples_written_to_buffer = 0; @@ -86,7 +82,8 @@ CircularBuffer output_buffer; // fixed size 256 # define bufferAudioOutput(f) output_buffer.write(f) static void CACHED_FUNCTION_ATTR defaultAudioOutput() { -#if (AUDIO_INPUT_MODE == AUDIO_INPUT_LEGACY) // in that case, we rely on asynchroneous ADC reads implemented for mozziAnalogRead to get the audio in samples +#if MOZZI_IS(MOZZI__LEGACY_AUDIO_INPUT_IMPL, 1) // in that case, we rely on asynchroneous ADC reads implemented for mozziAnalogRead to get the audio in samples + MOZZI_ASSERT_NOTEQUAL(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_NONE); adc_count = 0; startSecondADCReadOnCurrentChannel(); // the current channel is the AUDIO_INPUT pin # endif @@ -102,6 +99,8 @@ jRaskell, bobgardner, theusch, Koshchi, and code by jRaskell. http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=789581 */ +#if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_STANDARD) + #include "Stack.h" static volatile int analog_readings[NUM_ANALOG_INPUTS]; static Stack adc_channels_to_read; @@ -120,8 +119,8 @@ void adcReadSelectedChannels() { __attribute__((noinline)) void adcStartReadCycle() { if (current_channel < 0) // last read of adc_channels_to_read stack was empty, ie. all channels from last time have been read { -#if (AUDIO_INPUT_MODE == AUDIO_INPUT_LEGACY) // use of async ADC for audio input - adc_channels_to_read.push(AUDIO_INPUT_PIN); // for audio +#if MOZZI_IS(MOZZI__LEGACY_AUDIO_INPUT_IMPL, 1) // use of async ADC for audio input + adc_channels_to_read.push(MOZZI_AUDIO_INPUT_PIN); // for audio #else adcReadSelectedChannels(); adc_count = 0; @@ -130,22 +129,17 @@ __attribute__((noinline)) void adcStartReadCycle() { } int mozziAnalogRead(uint8_t pin) { -#if defined(MOZZI_FAST_ANALOG_IMPLEMENTED) pin = adcPinToChannelNum(pin); // allow for channel or pin numbers; on most platforms other than AVR this has no effect. See note on pins/channels adc_channels_to_read.push(pin); return analog_readings[channelNumToIndex(pin)]; -#else -# warning Asynchronouos analog reads not implemented for this platform - return analogRead(pin); -#endif } -#if (USE_AUDIO_INPUT == true) +#if !MOZZI_IS(MOZZI_AUDIO_INPUT, MOZZI_AUDIO_INPUT_NONE) static AudioOutputStorage_t audio_input; // holds the latest audio from input_buffer AudioOutputStorage_t getAudioInput() { return audio_input; } #endif -#if (AUDIO_INPUT_MODE == AUDIO_INPUT_LEGACY) +#if MOZZI_IS(MOZZI__LEGACY_AUDIO_INPUT_IMPL, 1) // ring buffer for audio input CircularBuffer input_buffer; // fixed size 256 #define audioInputAvailable() (!input_buffer.isEmpty()) @@ -175,7 +169,7 @@ inline void advanceADCStep() { } adc_count++; } -#else +#else // no (legacy) audio input /** NOTE: Triggered at CONTROL_RATE via advanceControlLoop(). This interrupt handler cycles through all analog inputs on the adc_channels_to_read Stack, @@ -195,6 +189,15 @@ inline void advanceADCStep() { } #endif +#else +MOZZI_ASSERT_EQUAL(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_NONE) + +int mozziAnalogRead(uint8_t pin) { + return analogRead(pin); +} + +#endif // MOZZI_ANALOG_READ + ////// END analog input code //////// @@ -206,7 +209,9 @@ inline void advanceControlLoop() { if (!update_control_counter) { update_control_counter = update_control_timeout; updateControl(); +#if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_STANDARD) adcStartReadCycle(); +#endif } else { --update_control_counter; } @@ -228,7 +233,7 @@ void audioHook() // 2us on AVR excluding updateAudio() LOOP_YIELD #endif -#if (USE_AUDIO_INPUT == true) +#if !MOZZI_IS(MOZZI_AUDIO_INPUT, MOZZI_AUDIO_INPUT_NONE) if (audioInputAvailable()) audio_input = readAudioInput(); #endif } @@ -260,12 +265,20 @@ unsigned long mozziMicros() { return audioTicks() * MICROS_PER_AUDIO_TICK; } ////// BEGIN initialization /////// void startMozzi(int control_rate_hz) { +#if !MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_NONE) setupMozziADC(); // you can use setupFastAnalogRead() with FASTER_ADC or FASTEST_ADC // in setup() if desired (not for Teensy 3.* ) - setupFastAnalogRead(); +#endif // delay(200); // so AutoRange doesn't read 0 to start with - update_control_timeout = AUDIO_RATE / control_rate_hz; + update_control_timeout = MOZZI_AUDIO_RATE / control_rate_hz; startAudio(); } ////// END initialization /////// + +// reduce Macro leakage +#undef LOOP_YIELD +#undef BYPASS_MOZZI_OUTPUT_BUFFER +#undef AUDIO_HOOK_HOOK +#undef AUDIOTICK_ADJUSTMENT +#undef MOZZI__LEGACY_AUDIO_INPUT_IMPL diff --git a/MozziGuts.h b/MozziGuts.h index f3dad8a46..0056c51ca 100644 --- a/MozziGuts.h +++ b/MozziGuts.h @@ -21,6 +21,7 @@ #endif #include "hardware_defines.h" +#include "mozzi_config.h" #if IS_TEENSY3() || IS_TEENSY4() // required from http://github.com/pedvide/ADC for Teensy 3.* @@ -29,211 +30,8 @@ #include "mozzi_analog.h" -#if not defined (CONTROL_RATE) -/** @ingroup core -Control rate setting. -Mozzi's CONTROL_RATE sets how many times per second updateControl() is called. -CONTROL_RATE has a default of 64 Hz, but it can be changed at the top of your sketch, -(before the \#includes), for example: \#define CONTROL_RATE 256. -It is useful to have CONTROL_RATE set at a power of 2 (such as 64,128,256 etc), -to have exact timing of audio and control operations. -Non-power-of-2 CONTROL_RATE can cause glitches due to audio and control -events not lining up precisely. If this happens a power of two CONTROL_RATE might solve it. -Try to keep CONTROL_RATE low, for efficiency, though higher rates up to about 1000 -can sometimes give smoother results, avoiding the need to interpolate -sensitive variables at audio rate in updateAudio(). -*/ -#define CONTROL_RATE 64 -#endif - - - -/** @ingroup core -Used to set AUDIO_MODE to STANDARD, STANDARD_PLUS, or HIFI. - -STANDARD / STANDARD_PLUS ---------------- -Use \#define AUDIO_MODE STANDARD_PLUS in Mozzi/config.h to select this -output configuration, which is nearly 9 bit sound (-244 to 243) at 16384 Hz sample rate (AUDIO_RATE) and -32768 Hz PWM rate. It uses Timer 1 for PWM and the sample updating routine (as an interrupt). - -STANDARD is obsolete now, replaced by STANDARD_PLUS which is the default audio mode. -STANDARD mode uses 16384 Hz PWM rate with an output interrupt at the same frequency. -Some people can hear the PWM carrier frequency as an annoying whine. - -STANDARD_PLUS mode uses 32768 Hz PWM rate, so the PWM carrier is out of hearing range. -In this mode every alternate interrupt is used for the sample update (unless you /#define AUDIO_RATE 32768 in mozzi_config.h), -which makes it slightly less efficient than STANDARD, but almost always better. - -Advantages: Only uses one timer for audio, and one output pin. -Disadvantages: low dynamic range. - -Below is a list of the Digital Pins used by Mozzi for STANDARD and STANDARD_PLUS audio out on different boards. -Those which have been tested and reported to work have an x. -Feedback about others is welcome. - -Model | Pin | Tested ------ | --- | ------ -Arduino Uno | 9 | yes -Arduino Duemilanove | 9 | yes -Arduino Nano | 9 | yes -Arduino Pro Mini | 9 | yes -Arduino Leonardo | 9 | yes -Arduino Mega | 11 | yes -Freetronics EtherMega | 11 | yes -Ardweeny | 9 | yes -Boarduino | 9 | yes -Teensy | 14 | - -Teensy2 | B5 | yes -Teensy2++ | B5(25) | yes -Teensy 3.0 3.1 LC 3.2 | DAC/D | yes -Teensy 3.4, 3.5 | DAC/D | - -Teensy 4.0 4.1 | A8 | yes -Gemma M0 | A0 | yes -Adafruit Playground Express | Built in Speaker | yes -Sanguino | 13 | - -STM32duino (see "Hardware specific notes", below) | PB8 | yes -ESP8266 *see details in README* | GPIO2 | yes -RP2040 | 0 | yes - - -On Teensy 3.* STANDARD and STANDARD_PLUS are the same, providing 16384Hz sample rate and 12 bit resolution on pin A14/ADC. -The Teensy 3.* DAC output does not rely on PWM. - - -@ingroup core - -Used to set AUDIO_MODE to HIFI. - -HIFI for AVR and STM32 (not for Teensy 3.*) ----- -Use \#define AUDIO_MODE HIFI in Mozzi/config.h to set the audio mode to HIFI for output 14 bit sound at 16384 Hz sample rate and 125kHz PWM rate. -The high PWM rate of HIFI mode places the carrier frequency beyond audible range. - -Also, 14 bits of dynamic range in HIFI mode provides more definition than the nearly 9 bits in STANDARD_PLUS mode. -HIFI mode takes about the same amount of processing time as STANDARD_PLUS mode, and should sound clearer and brighter. -However, it requires an extra timer to be used on the Arduino, which could increase the chances of -conflicts with other libraries or processes if they rely on Timer 2. - -Timer 1 is used to provide the PWM output at 125kHz. -Timer 2 generates an interrupt at AUDIO_RATE 16384 Hz, which sets the Timer1 PWM levels. -HIFI mode uses 2 output pins, and sums their outputs with resistors, so is slightly less convenient for -rapid prototyping where you could listen to STANDARD_PLUS mode by connecting the single output pin -directly to a speaker or audio input (though a resistor of about 100 ohms is recommended). - -The resistors needed for HIFI output are 3.9k and 499k, with 0.5% or better tolerance. -If you can only get 1% resistors, use a multimeter to find the most accurate. -Use two 1M resistors in parallel if you can't find 499k. - -On 328 based Arduino boards, output is on Timer1, with the high byte on Pin 9 and low byte on Pin 10. -Add the signals through a 3.9k resistor on high byte pin (9) and 499k resistor on low byte pin (10). -Also, a 4.7nF capacitor is recommended between the summing junction of the resistors and ground. - -This dual PWM technique is discussed on http://www.openmusiclabs.com/learning/digital/pwm-dac/dual-pwm-circuits/ -Also, there are higher quality output circuits are on the site. - -Advantages: should be higher quality sound than STANDARD_PLUS mode. Doesn't need a notch filter on -the audio signal (like STANDARD which is now obsolete) because the carrier frequency is out of hearing range. - -Disadvantages: requires 2 pins, 2 resistors and a capacitor, so it's not so quick to set up compared -to a rough, direct single-pin output in STANDARD_PLUS mode. - -Pins and where to put the resistors on various boards for HIFI mode. -Boards tested in HIFI mode have an x, though most of these have been tested in STANDARD_PLUS mode -and there's no reason for them not to work in HIFI (unless the pin number is wrong or something). -Any reports are welcome. \n - -resistor.....3.9k......499k \n -x................9..........10...............Arduino Uno \n -x................9..........10...............Arduino Duemilanove \n -x................9..........10...............Arduino Nano \n -x................9..........10...............Arduino Leonardo \n -x................9..........10...............Ardweeny \n -x................9..........10...............Boarduino \n -x...............11.........12...............Freetronics EtherMega \n -.................11.........12...............Arduino Mega \n -.................14.........15...............Teensy \n -.............B5(14)...B6(15)...........Teensy2 \n -x...........B5(25)...B6(26)...........Teensy2++ \n -.................13.........12...............Sanguino \n - -HIFI is not available/not required on Teensy 3.* or ARM. -*/ - -//enum audio_modes {STANDARD,STANDARD_PLUS,HIFI}; -#define STANDARD 0 -#define STANDARD_PLUS 1 -#define HIFI 2 - -//enum audio_channels {MONO,STEREO,...}; -#define MONO 1 -#define STEREO 2 - -#include "mozzi_config.h" // User can change the config file to set audio mode - -#if (AUDIO_MODE == STANDARD) && (AUDIO_RATE == 32768) -#error AUDIO_RATE 32768 does not work when AUDIO_MODE is STANDARD, try setting the AUDIO_MODE to STANDARD_PLUS in Mozzi/mozzi_config.h -#endif - -#if (STEREO_HACK == true) -#warning Use of STEREO_HACK is deprecated. Use AUDIO_CHANNELS STEREO, instead. -#define AUDIO_CHANNELS STEREO -#endif -#if !defined(AUDIO_CHANNELS) -#define AUDIO_CHANNELS MONO -#endif - -#define CLOCK_TICKS_PER_AUDIO_TICK (F_CPU / AUDIO_RATE) - -#if AUDIO_RATE == 16384 -#define AUDIO_RATE_AS_LSHIFT 14 -#define MICROS_PER_AUDIO_TICK 61 // 1000000 / 16384 = 61.035, ...* 256 = 15625 -#elif AUDIO_RATE == 32768 -#define AUDIO_RATE_AS_LSHIFT 15 -#define MICROS_PER_AUDIO_TICK 31 // = 1000000 / 32768 = 30.518, ...* 256 = 7812.6 -#endif - -// for compatibility with old (local) versions of mozzi_config.h -#if !defined(EXTERNAL_AUDIO_OUTPUT) -#define EXTERNAL_AUDIO_OUTPUT false -#endif - -#if (EXTERNAL_AUDIO_OUTPUT != true) -#if IS_TEENSY3() -#include "AudioConfigTeensy3_12bit.h" -#elif IS_TEENSY4() -#include "AudioConfigTeensy4.h" -#elif IS_STM32() -#include "AudioConfigSTM32.h" -#elif IS_STM32DUINO() -#include "AudioConfigSTM32duino.h" -#elif IS_ESP8266() -#include "AudioConfigESP.h" -#elif IS_ESP32() -#include "AudioConfigESP32.h" -#elif IS_SAMD21() -#include "AudioConfigSAMD21.h" -#elif IS_RP2040() -#include "AudioConfigRP2040.h" -#elif IS_MBED() -#include "AudioConfigMBED.h" -#elif IS_AVR() && (AUDIO_MODE == STANDARD) -#include "AudioConfigStandard9bitPwm.h" -#elif IS_AVR() && (AUDIO_MODE == STANDARD_PLUS) -#include "AudioConfigStandardPlus.h" -#elif IS_AVR() && (AUDIO_MODE == HIFI) -#include "AudioConfigHiSpeed14bitPwm.h" -#elif IS_RENESAS() -#include "AudioConfigRenesas.h" -#endif -#else // EXTERNAL_AUDIO_OUTPUT==true -#if !defined(EXTERNAL_AUDIO_BITS) -#define EXTERNAL_AUDIO_BITS 16 -#endif -#define AUDIO_BITS EXTERNAL_AUDIO_BITS -#define AUDIO_BIAS (1 << (AUDIO_BITS - 1)) -#endif +#include "internal/config_checks_generic.h" #if (STEREO_HACK == true) extern int audio_out_1, audio_out_2; @@ -241,6 +39,7 @@ extern int audio_out_1, audio_out_2; #include "AudioOutput.h" +// TODO Mozzi 2.0: These typedef probably obsolete? // common numeric types typedef unsigned char uchar; typedef unsigned int uint; @@ -264,16 +63,8 @@ Sets up the timers for audio and control rate processes, storing the timer registers so they can be restored when Mozzi stops. startMozzi() goes in your sketch's setup() routine. -Contrary to earlier versions of Mozzi, this version does not take over Timer 0, and thus Arduino -functions delay(), millis(), micros() and delayMicroseconds() remain usable in theory. That said, -you should avoid these functions, as they are slow (or even blocking). For measuring time, refer -to mozziMircos(). For delaying events, you can use Mozzi's EventDelay() unit instead -(not to be confused with AudioDelay()). - -In STANDARD mode, startMozzi() starts Timer 1 for PWM output and audio output interrupts, -and in STANDARD_PLUS and HIFI modes, Mozzi uses Timer 1 for PWM and Timer2 for audio interrupts. - -The audio rate defaults to 16384 Hz, but you can experiment with 32768 Hz by changing AUDIO_RATE in mozzi_config.h. +This function intializes the timer(s) needed to move audio samples to the output according to the +configured @ref MOZZI_AUDIO_MODE . @param control_rate_hz Sets how often updateControl() is called. It must be a power of 2. If no parameter is provided, control_rate_hz is set to CONTROL_RATE, @@ -303,22 +94,12 @@ reading sensors. As it is, stopMozzi restores all the Timers used by Mozzi to their previous settings. Another scenario which could be easily hacked in MozziGuts.cpp could involve individually saving and restoring particular Timer registers depending -on which one(s) are required for other tasks. */ -void stopMozzi(); - +on which one(s) are required for other tasks. -/** @ingroup core -Obsolete function, use stopMozzi() instead. +@note This function is not actually implemented on all platforms. */ -void pauseMozzi(); +void stopMozzi(); -//TB2017-19 -/** @ingroup core -Obsolete function, use startMozzi() instead. -Restores Mozzi audio and control interrupts, if they have been temporarily -disabled with pauseMozzi(). -*/ -void unPauseMozzi(); /** @ingroup core @@ -327,6 +108,8 @@ AUDIO_RATE of 16384 Hz, so to keep things running smoothly, avoid doing any calculations here which could be done in setup() or updateControl(). @return an audio sample. In STANDARD modes this is between -244 and 243 inclusive. In HIFI mode, it's a 14 bit number between -16384 and 16383 inclusive. + +TODO: Update documentation */ AudioOutput_t updateAudio(); @@ -342,7 +125,7 @@ void updateControl(); /** @ingroup core This is required in Arduino's loop(). If there is room in Mozzi's output buffer, audioHook() calls updateAudio() once and puts the result into the output -buffer. Also, if \#define USE_AUDIO_INPUT true is in Mozzi/mozzi_config.h, +buffer. Also, if \@ref MOZZI_AUDIO_INPUT is enabled in the config, audioHook() takes care of moving audio input from the input buffer so it can be accessed with getAudioInput() in your updateAudio() routine. If other functions are called in loop() along with audioHook(), see if @@ -358,10 +141,9 @@ void audioHook(); /** @ingroup analog This returns audio input from the input buffer, if -\#define USE_AUDIO_INPUT true is in the Mozzi/mozzi_config.h file. -The pin used for audio input is set in Mozzi/mozzi_config.h with -\#define AUDIO_INPUT_PIN 0 (or other analog input pin). -The audio signal needs to be in the range 0 to 5 volts. +\@ref MOZZI_AUDIO_INPUT is enabled in the config (see also the related option MOZZI_AUDIO_INPUT_PIN). + +The audio signal needs to be in the range 0 to VCC volts (i.e. 5 volts on Arduino Uno R3). Circuits and discussions about biasing a signal in the middle of this range can be found at http://electronics.stackexchange.com/questions/14404/dc-biasing-audio-signal @@ -371,7 +153,7 @@ A circuit and instructions for amplifying and biasing a microphone signal can be http://www.instructables.com/id/Arduino-Audio-Input/?ALLSTEPS @return audio data from the input buffer */ -#if (USE_AUDIO_INPUT == true) +#if !MOZZI_IS(MOZZI_AUDIO_INPUT, MOZZI_AUDIO_INPUT_NONE) int getAudioInput(); #endif diff --git a/MozziGuts_impl_AVR.hpp b/MozziGuts_impl_AVR.hpp index dd0f474dc..e877c87b0 100644 --- a/MozziGuts_impl_AVR.hpp +++ b/MozziGuts_impl_AVR.hpp @@ -19,7 +19,7 @@ #endif ////// BEGIN analog input code //////// -#define MOZZI_FAST_ANALOG_IMPLEMENTED +#if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_STANDARD) extern uint8_t analog_reference; #define getADCReading() ADC /* officially (ADCL | (ADCH << 8)) but the compiler works it out */ @@ -79,6 +79,12 @@ ISR(ADC_vect, ISR_BLOCK) advanceADCStep(); } +void setupMozziADC(int8_t speed) { + ADCSRA |= (1 << ADIE); // adc Enable Interrupt + adcDisconnectAllDigitalIns(); + setupFastAnalogRead(speed); +} + void setupFastAnalogRead(int8_t speed) { if (speed == FAST_ADC){ // divide by 16 ADCSRA |= (1 << ADPS2); @@ -95,10 +101,7 @@ void setupFastAnalogRead(int8_t speed) { } } -void setupMozziADC(int8_t speed) { - ADCSRA |= (1 << ADIE); // adc Enable Interrupt - adcDisconnectAllDigitalIns(); -} +#endif ////// END analog input code //////// @@ -133,7 +136,7 @@ carrier freq noise can be an issue static uint8_t pre_mozzi_TCCR1A, pre_mozzi_TCCR1B, pre_mozzi_OCR1A, pre_mozzi_TIMSK1; -#if (AUDIO_MODE == HIFI) +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM) #if defined(TCCR2A) static uint8_t pre_mozzi_TCCR2A, pre_mozzi_TCCR2B, pre_mozzi_OCR2A, pre_mozzi_TIMSK2; @@ -153,7 +156,7 @@ static void backupPreMozziTimer1() { pre_mozzi_TIMSK1 = TIMSK1; } -#if (AUDIO_MODE == HIFI) +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM) #if defined(TCCR2A) static uint8_t mozzi_TCCR2A, mozzi_TCCR2B, mozzi_OCR2A, mozzi_TIMSK2; #elif defined(TCCR2) @@ -164,7 +167,7 @@ static uint8_t mozzi_TCCR4A, mozzi_TCCR4B, mozzi_TCCR4C, mozzi_TCCR4D, #endif #endif -#if (EXTERNAL_AUDIO_OUTPUT == true) +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED) // TODO: Check: This block used to compile also for BYPASS_MOZZI_OUTPUT_BUFFER, but I think unneccessarily so. static void startAudio() { backupPreMozziTimer1(); Timer1.initializeCPUCycles( @@ -181,43 +184,38 @@ static void startAudio() { ISR(TIMER1_OVF_vect, ISR_BLOCK) { defaultAudioOutput(); } -#elif (AUDIO_MODE == STANDARD) || (AUDIO_MODE == STANDARD_PLUS) -# if (AUDIO_MODE == STANDARD_PLUS) -# include "AudioConfigStandardPlus.h" -# else -# include "AudioConfigStandard9bitPwm.h" -# endif +#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM) inline void audioOutput(const AudioOutput f) { - AUDIO_CHANNEL_1_OUTPUT_REGISTER = f.l()+AUDIO_BIAS; -# if (AUDIO_CHANNELS > 1) - AUDIO_CHANNEL_2_OUTPUT_REGISTER = f.r()+AUDIO_BIAS; + MOZZI_AUDIO_PIN_1_REGISTER = f.l()+MOZZI_AUDIO_BIAS; +# if (MOZZI_AUDIO_CHANNELS > 1) + MOZZI_AUDIO_PIN_2_REGISTER = f.r()+MOZZI_AUDIO_BIAS; # endif } static void startAudio() { backupPreMozziTimer1(); - pinMode(AUDIO_CHANNEL_1_PIN, OUTPUT); // set pin to output for audio - // pinMode(AUDIO_CHANNEL_2_PIN, OUTPUT); // set pin to output for audio -# if (AUDIO_MODE == STANDARD) + pinMode(MOZZI_AUDIO_PIN_1, OUTPUT); // set pin to output for audio +# if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM) && (MOZZI_PWM_RATE < 32768) // Formerly known as the - long since deprecated - "STANDARD" mode Timer1.initializeCPUCycles( - (F_CPU/AUDIO_RATE)-1,// the -1 here is a result of empirical tests + (F_CPU/MOZZI_AUDIO_RATE)-1,// the -1 here is a result of empirical tests // that showed that it brings the resulting frequency // closer to what is expected. // see: https://github.com/sensorium/Mozzi/pull/202 PHASE_FREQ_CORRECT); // set period, phase and frequency correct -# else // (AUDIO_MODE == STANDARD_PLUS) - Timer1.initializeCPUCycles((F_CPU/PWM_RATE)-1, // the -1 here is a result of empirical tests +# else // Formerly known as "STANDARD_PLUS" mode + Timer1.initializeCPUCycles((F_CPU/MOZZI_PWM_RATE)-1, // the -1 here is a result of empirical tests // that showed that it brings the resulting frequency // closer to what is expected. // see: https://github.com/sensorium/Mozzi/pull/202 FAST); // fast mode enables higher PWM rate # endif - Timer1.pwm(AUDIO_CHANNEL_1_PIN, - AUDIO_BIAS); // pwm pin, 50% of Mozzi's duty cycle, ie. 0 signal -# if (AUDIO_CHANNELS > 1) - Timer1.pwm(AUDIO_CHANNEL_2_PIN, AUDIO_BIAS); // sets pin to output + Timer1.pwm(MOZZI_AUDIO_PIN_1, + MOZZI_AUDIO_BIAS); // pwm pin, 50% of Mozzi's duty cycle, ie. 0 signal +# if (MOZZI_AUDIO_CHANNELS > 1) + pinMode(MOZZI_AUDIO_PIN_2, OUTPUT); // set pin to output for audio + Timer1.pwm(MOZZI_AUDIO_PIN_2, MOZZI_AUDIO_BIAS); # endif TIMSK1 = _BV(TOIE1); // Overflow Interrupt Enable (when not using // Timer1.attachInterrupt()) @@ -227,7 +225,8 @@ static void startAudio() { Arduino output register, running at AUDIO_RATE. */ ISR(TIMER1_OVF_vect, ISR_BLOCK) { -# if (AUDIO_MODE == STANDARD_PLUS) && (AUDIO_RATE == 16384) // only update every second ISR, if lower audio rate +# if (MOZZI_AUDIO_RATE < MOZZI_PWM_RATE) // only update every second ISR, if lower audio rate + static_assert(2l*MOZZI_AUDIO_RATE == MOZZI_PWM_RATE, "audio rate must the same, or exactly half of the pwm rate!"); static boolean alternate; alternate = !alternate; if (alternate) return; @@ -236,9 +235,7 @@ ISR(TIMER1_OVF_vect, ISR_BLOCK) { defaultAudioOutput(); } -#elif (AUDIO_MODE == HIFI) -# if (EXTERNAL_AUDIO_OUTPUT != true) -# include "AudioConfigHiSpeed14bitPwm.h" +#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM) inline void audioOutput(const AudioOutput f) { // read about dual pwm at // http://www.openmusiclabs.com/learning/digital/pwm-dac/dual-pwm-circuits/ @@ -247,13 +244,13 @@ inline void audioOutput(const AudioOutput f) { // if (!output_buffer.isEmpty()){ //unsigned int out = output_buffer.read(); // 14 bit, 7 bits on each pin - // AUDIO_CHANNEL_1_highByte_REGISTER = out >> 7; // B00111111 10000000 becomes + // MOZZI_AUDIO_PIN_1_REGISTER = out >> 7; // B00111111 10000000 becomes // B1111111 // try to avoid looping over 7 shifts - need to check timing or disassemble to // see what really happens unsigned int out_high = out<<1; // B00111111 // 10000000 becomes B01111111 00000000 - // AUDIO_CHANNEL_1_highByte_REGISTER = out_high >> 8; // B01111111 00000000 - // produces B01111111 AUDIO_CHANNEL_1_lowByte_REGISTER = out & 127; + // MOZZI_AUDIO_PIN_1_REGISTER = out_high >> 8; // B01111111 00000000 + // produces B01111111 MOZZI_AUDIO_PIN_1_LOW_REGISTER = out & 127; /* Atmega manual, p123 The high byte (OCR1xH) has to be written first. When the high byte I/O location is written by the CPU, @@ -263,26 +260,21 @@ inline void audioOutput(const AudioOutput f) { either the OCR1x buffer or OCR1x Compare Register in the same system clock cycle. */ - AUDIO_CHANNEL_1_highByte_REGISTER = (f.l()+AUDIO_BIAS) >> AUDIO_BITS_PER_REGISTER; - AUDIO_CHANNEL_1_lowByte_REGISTER = (f.l()+AUDIO_BIAS) & ((1 << AUDIO_BITS_PER_REGISTER) - 1); + MOZZI_AUDIO_PIN_1_REGISTER = (f.l()+MOZZI_AUDIO_BIAS) >> MOZZI_AUDIO_BITS_PER_CHANNEL; + MOZZI_AUDIO_PIN_1_LOW_REGISTER = (f.l()+MOZZI_AUDIO_BIAS) & ((1 << MOZZI_AUDIO_BITS_PER_CHANNEL) - 1); } -# endif static void setupTimer2(); static void startAudio() { backupPreMozziTimer1(); // pwm on timer 1 - pinMode(AUDIO_CHANNEL_1_highByte_PIN, - OUTPUT); // set pin to output for audio, use 3.9k resistor - pinMode(AUDIO_CHANNEL_1_lowByte_PIN, - OUTPUT); // set pin to output for audio, use 499k resistor + pinMode(MOZZI_AUDIO_PIN_1, OUTPUT); // set pin to output for audio, use 3.9k resistor + pinMode(MOZZI_AUDIO_PIN_1_LOW, OUTPUT); // set pin to output for audio, use 499k resistor Timer1.initializeCPUCycles( F_CPU/125000, FAST); // set period for 125000 Hz fast pwm carrier frequency = 14 bits - Timer1.pwm(AUDIO_CHANNEL_1_highByte_PIN, - 0); // pwm pin, 0% duty cycle, ie. 0 signal - Timer1.pwm(AUDIO_CHANNEL_1_lowByte_PIN, - 0); // pwm pin, 0% duty cycle, ie. 0 signal + Timer1.pwm(MOZZI_AUDIO_PIN_1, 0); // pwm pin, 0% duty cycle, ie. 0 signal + Timer1.pwm(MOZZI_AUDIO_PIN_1_LOW, 0); // audio output interrupt on timer 2, sets the pwm levels of timer 1 setupTimer2(); } @@ -316,7 +308,7 @@ static void backupPreMozziTimer2() { // levels of timer 2 static void setupTimer2() { backupPreMozziTimer2(); // to reset while pausing - unsigned long period = F_CPU / AUDIO_RATE; + unsigned long period = F_CPU / MOZZI_AUDIO_RATE; FrequencyTimer2::setPeriodCPUCycles(period); FrequencyTimer2::setOnOverflow(dummy); FrequencyTimer2::enable(); @@ -354,7 +346,7 @@ void stopMozzi() { TIMSK1 = pre_mozzi_TIMSK1; -#if (AUDIO_MODE == HIFI) +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM) #if defined(TCCR2A) TCCR2A = pre_mozzi_TCCR2A; TCCR2B = pre_mozzi_TCCR2B; diff --git a/MozziGuts_impl_ESP32.hpp b/MozziGuts_impl_ESP32.hpp index 7657eb62c..b20c92772 100644 --- a/MozziGuts_impl_ESP32.hpp +++ b/MozziGuts_impl_ESP32.hpp @@ -15,46 +15,45 @@ #endif ////// BEGIN analog input code //////// -//#define MOZZI_FAST_ANALOG_IMPLEMENTED // not yet +#if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_STANDARD) +#error not yet implemented + #define getADCReading() 0 #define channelNumToIndex(channel) channel uint8_t adcPinToChannelNum(uint8_t pin) { return pin; } void adcStartConversion(uint8_t channel) { -#warning Fast analog read not implemented on this platform } void startSecondADCReadOnCurrentChannel() { -#warning Fast analog read not implemented on this platform -} -void setupFastAnalogRead(int8_t speed) { -#warning Fast analog read not implemented on this platform } void setupMozziADC(int8_t speed) { -#warning Fast analog read not implemented on this platform } +void setupFastAnalogRead(int8_t speed) { +} + +#endif ////// END analog input code //////// //// BEGIN AUDIO OUTPUT code /////// -#include // for I2S-based output modes -#include // for EXTERNAL_AUDIO_OUTPUT +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) || MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC) || MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S) +# include // for I2S-based output modes, including - technically - internal DAC +const i2s_port_t i2s_num = MOZZI_I2S_PORT; -#if (EXTERNAL_AUDIO_OUTPUT != true) -# include "AudioConfigESP32.h" // On ESP32 we cannot test wether the DMA buffer has room. Instead, we have to use a one-sample mini buffer. In each iteration we // _try_ to write that sample to the DMA buffer, and if successful, we can buffer the next sample. Somewhat cumbersome, but works. // TODO: Should ESP32 gain an implemenation of i2s_available(), we should switch to using that, instead. static bool _esp32_can_buffer_next = true; -# if (ESP32_AUDIO_OUT_MODE == INTERNAL_DAC) +# if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) static uint16_t _esp32_prev_sample[2]; # define ESP_SAMPLE_SIZE (2*sizeof(uint16_t)) -# elif (ESP32_AUDIO_OUT_MODE == PT8211_DAC) +# elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC) static int16_t _esp32_prev_sample[2]; # define ESP_SAMPLE_SIZE (2*sizeof(int16_t)) -# elif (ESP32_AUDIO_OUT_MODE == PDM_VIA_I2S) +# elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S) static uint32_t _esp32_prev_sample[PDM_RESOLUTION]; # define ESP_SAMPLE_SIZE (PDM_RESOLUTION*sizeof(uint32_t)) # endif @@ -72,17 +71,17 @@ inline bool canBufferAudioOutput() { } inline void audioOutput(const AudioOutput f) { -# if (ESP32_AUDIO_OUT_MODE == INTERNAL_DAC) - _esp32_prev_sample[0] = (f.l() + AUDIO_BIAS) << 8; -# if (AUDIO_CHANNELS > 1) - _esp32_prev_sample[1] = (f.r() + AUDIO_BIAS) << 8; +# if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) + _esp32_prev_sample[0] = (f.l() + MOZZI_AUDIO_BIAS) << 8; +# if (MOZZI_AUDIO_CHANNELS > 1) + _esp32_prev_sample[1] = (f.r() + MOZZI_AUDIO_BIAS) << 8; # else // For simplicity of code, even in mono, we're writing stereo samples _esp32_prev_sample[1] = _esp32_prev_sample[0]; # endif -# elif (ESP32_AUDIO_OUT_MODE == PDM_VIA_I2S) - for (uint8_t i=0; i void CACHED_FUNCTION_ATTR timer0_audio_output_isr(void *) { TIMERG0.int_clr_timers.t0 = 1; TIMERG0.hw_timer[0].config.alarm_en = 1; @@ -102,7 +102,7 @@ void CACHED_FUNCTION_ATTR timer0_audio_output_isr(void *) { #endif static void startAudio() { -#if (BYPASS_MOZZI_OUTPUT_BUFFER != true) // for external audio output, set up a timer running a audio rate +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED) // for external audio output, set up a timer running a audio rate static intr_handle_t s_timer_handle; const int div = 2; timer_config_t config = { @@ -116,19 +116,19 @@ static void startAudio() { }; timer_init(TIMER_GROUP_0, TIMER_0, &config); timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0); - timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, 80000000UL / AUDIO_RATE / div); + timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, 80000000UL / MOZZI_AUDIO_RATE / div); timer_enable_intr(TIMER_GROUP_0, TIMER_0); timer_isr_register(TIMER_GROUP_0, TIMER_0, &timer0_audio_output_isr, nullptr, 0, &s_timer_handle); timer_start(TIMER_GROUP_0, TIMER_0); #else static const i2s_config_t i2s_config = { -# if (ESP32_AUDIO_OUT_MODE == PT8211_DAC) || (ESP32_AUDIO_OUT_MODE == PDM_VIA_I2S) +# if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC) || MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S) .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), -# elif (ESP32_AUDIO_OUT_MODE == INTERNAL_DAC) +# elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN), # endif - .sample_rate = AUDIO_RATE * PDM_RESOLUTION, + .sample_rate = MOZZI_AUDIO_RATE * MOZZI_PDM_RESOLUTION, .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, // only the top 8 bits will actually be used by the internal DAC, but using 8 bits straight away seems buggy .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // always use stereo output. mono seems to be buggy, and the overhead is insignifcant on the ESP32 .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_LSB), // this appears to be the correct setting for internal DAC and PT8211, but not for other dacs @@ -139,15 +139,15 @@ static void startAudio() { }; i2s_driver_install(i2s_num, &i2s_config, 0, NULL); -# if (ESP32_AUDIO_OUT_MODE == PT8211_DAC) || (ESP32_AUDIO_OUT_MODE == PDM_VIA_I2S) +# if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC) || MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S) static const i2s_pin_config_t pin_config = { - .bck_io_num = ESP32_I2S_BCK_PIN, - .ws_io_num = ESP32_I2S_WS_PIN, - .data_out_num = ESP32_I2S_DATA_PIN, + .bck_io_num = MOZZI_I2S_PIN_BCK, + .ws_io_num = MOZZI_I2S_PIN_WS, + .data_out_num = MOZZI_I2S_PIN_DATA, .data_in_num = -1 }; i2s_set_pin((i2s_port_t)i2s_num, &pin_config); -# elif (ESP32_AUDIO_OUT_MODE == INTERNAL_DAC) +# elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) i2s_set_pin((i2s_port_t)i2s_num, NULL); i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN); # endif @@ -160,3 +160,5 @@ void stopMozzi() { // TODO: implement me } //// END AUDIO OUTPUT code /////// + +#undef ESP_SAMPLE_SIZE // only used inside this file diff --git a/MozziGuts_impl_ESP8266.hpp b/MozziGuts_impl_ESP8266.hpp index c9f925b9f..3d4f3c11d 100644 --- a/MozziGuts_impl_ESP8266.hpp +++ b/MozziGuts_impl_ESP8266.hpp @@ -15,24 +15,23 @@ #endif ////// BEGIN analog input code //////// -//#define MOZZI_FAST_ANALOG_IMPLEMENTED // not yet +#if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_STANDARD) +#error not yet implemented + #define getADCReading() 0 #define channelNumToIndex(channel) channel uint8_t adcPinToChannelNum(uint8_t pin) { return pin; } void adcStartConversion(uint8_t channel) { -#warning Fast analog read not implemented on this platform } void startSecondADCReadOnCurrentChannel() { -#warning Fast analog read not implemented on this platform -} -void setupFastAnalogRead(int8_t speed) { -#warning Fast analog read not implemented on this platform } void setupMozziADC(int8_t speed) { -#warning Fast analog read not implemented on this platform } +void setupFastAnalogRead(int8_t speed) { +} +#endif ////// END analog input code //////// @@ -44,55 +43,55 @@ void setupMozziADC(int8_t speed) { #include uint16_t output_buffer_size = 0; -#if (EXTERNAL_AUDIO_OUTPUT != true) // otherwise, the last stage - audioOutput() - will be provided by the user -# include "AudioConfigESP.h" +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S, MOZZI_OUTPUT_PDM_VIA_SERIAL, MOZZI_OUTPUT_I2S_DAC) // i.e. not external -# if (ESP_AUDIO_OUT_MODE == PDM_VIA_I2S) +# if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S) # include inline bool canBufferAudioOutput() { - return (i2s_available() >= PDM_RESOLUTION); + return (i2s_available() >= MOZZI_PDM_RESOLUTION); } inline void audioOutput(const AudioOutput f) { - for (uint8_t words = 0; words < PDM_RESOLUTION; ++words) { - i2s_write_sample(pdmCode32(f.l()+AUDIO_BIAS)); + for (uint8_t words = 0; words < MOZZI_PDM_RESOLUTION; ++words) { + i2s_write_sample(pdmCode32(f.l()+MOZZI_AUDIO_BIAS)); } } -# elif (ESP_AUDIO_OUT_MODE == EXTERNAL_DAC_VIA_I2S) +# elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC) # include inline bool canBufferAudioOutput() { - return (i2s_available() >= PDM_RESOLUTION); + return (i2s_available() >= MOZZI_PDM_RESOLUTION); } inline void audioOutput(const AudioOutput f) { i2s_write_lr(f.l(), f.r()); // Note: i2s_write expects zero-centered output } -# else // (ESP_AUDIO_OUT_MODE == PDM_VIA_SERIAL) +# else +MOZZI_ASSERT_EQUAL(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_SERIAL) // NOTE: This intermediate step is needed because the output timer is running at a rate higher than AUDIO_RATE, and we need to rely on the (tiny) // serial buffer itself to achieve appropriate rate control void CACHED_FUNCTION_ATTR esp8266_serial_audio_output() { // Note: That unreadble mess is an optimized version of Serial1.availableForWrite() - while ((UART_TX_FIFO_SIZE - ((U1S >> USTXC) & 0xff)) > (PDM_RESOLUTION * 4)) { + while ((UART_TX_FIFO_SIZE - ((U1S >> USTXC) & 0xff)) > (MOZZI_PDM_RESOLUTION * 4)) { defaultAudioOutput(); } } inline void audioOutput(const AudioOutput f) { // optimized version of: Serial1.write(...); - for (uint8_t i = 0; i < PDM_RESOLUTION*4; ++i) { - U1F = pdmCode8(f+AUDIO_BIAS); + for (uint8_t i = 0; i < MOZZI_PDM_RESOLUTION*4; ++i) { + U1F = pdmCode8(f+MOZZI_AUDIO_BIAS); } } # endif #endif static void startAudio() { -#if (EXTERNAL_AUDIO_OUTPUT == true) && (BYPASS_MOZZI_OUTPUT_BUFFER != true) // for external audio output, set up a timer running a audio rate +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED) // for external audio output, set up a timer running a audio rate timer1_isr_init(); timer1_attachInterrupt(defaultAudioOutput); timer1_enable(TIM_DIV1, TIM_EDGE, TIM_LOOP); - timer1_write(F_CPU / AUDIO_RATE); -#elif (ESP_AUDIO_OUT_MODE == PDM_VIA_SERIAL) + timer1_write(F_CPU / MOZZI_AUDIO_RATE); +#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_SERIAL) Serial1.begin( - AUDIO_RATE * (PDM_RESOLUTION * 40), SERIAL_8N1, + MOZZI_AUDIO_RATE * (MOZZI_PDM_RESOLUTION * 40), SERIAL_8N1, SERIAL_TX_ONLY); // Note: PDM_RESOLUTION corresponds to packets of 32 // encoded bits However, the UART (unfortunately) adds a // start and stop bit each around each byte, thus sending @@ -105,15 +104,15 @@ static void startAudio() { // interval to the time needed to write half of that. PDM_RESOLUTION * 4 bytes // per sample written. timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP); - timer1_write(F_CPU / (AUDIO_RATE * PDM_RESOLUTION)); + timer1_write(F_CPU / (MOZZI_AUDIO_RATE * MOZZI_PDM_RESOLUTION)); #else i2s_begin(); -# if (ESP_AUDIO_OUT_MODE == PDM_VIA_I2S) +# if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S) pinMode(2, INPUT); // Set the two unneeded I2S pins to input mode, to reduce // side effects pinMode(15, INPUT); # endif - i2s_set_rate(AUDIO_RATE * PDM_RESOLUTION); + i2s_set_rate(MOZZI_AUDIO_RATE * MOZZI_PDM_RESOLUTION); if (output_buffer_size == 0) output_buffer_size = i2s_available(); // Do not reset count when stopping / restarting @@ -122,16 +121,16 @@ static void startAudio() { void stopMozzi() { -#if (ESP_AUDIO_OUT_MODE != PDM_VIA_SERIAL) +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S, MOZZI_OUTPUT_I2S_DAC) i2s_end(); -#else +#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_SERIAL, MOZZI_OUTPUT_EXTERNAL_TIMED) timer1_disable(); #endif interrupts(); } -#if ((ESP_AUDIO_OUT_MODE == PDM_VIA_I2S) && (PDM_RESOLUTION != 1)) -# define AUDIOTICK_ADJUSTMENT ((output_buffer_size - i2s_available()) / PDM_RESOLUTION) +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S) && (MOZZI_PDM_RESOLUTION != 1) +# define AUDIOTICK_ADJUSTMENT ((output_buffer_size - i2s_available()) / MOZZI_PDM_RESOLUTION) #else # define AUDIOTICK_ADJUSTMENT (output_buffer_size - i2s_available()) #endif diff --git a/MozziGuts_impl_MBED.hpp b/MozziGuts_impl_MBED.hpp index e3a642218..5f612cc14 100644 --- a/MozziGuts_impl_MBED.hpp +++ b/MozziGuts_impl_MBED.hpp @@ -18,12 +18,12 @@ ////// BEGIN analog input code //////// -#if (USE_AUDIO_INPUT) -#define AUDIO_INPUT_MODE AUDIO_INPUT_CUSTOM +#if MOZZI_IS(MOZZI_AUDIO_INPUT, MOZZI_AUDIO_INPUT_STANDARD) +#define MOZZI__LEGACY_AUDIO_INPUT_IMPL 0 #include -AdvancedADC adc(AUDIO_INPUT_PIN); +AdvancedADC adc(MOZZI_AUDIO_INPUT_PIN); Sample inbuf[CHUNKSIZE]; int inbufpos=0; @@ -44,18 +44,20 @@ AudioOutputStorage_t readAudioInput(){ static void startAudioInput() { - if (!adc.begin(AN_RESOLUTION_12, AUDIO_RATE, CHUNKSIZE, 256/CHUNKSIZE)) { + if (!adc.begin(AN_RESOLUTION_12, MOZZI_AUDIO_RATE, CHUNKSIZE, 256/CHUNKSIZE)) { Serial.println("Failed to start analog acquisition!"); while (1); } } +#else +static void startAudioInput() {}; // dummy to ease coding #endif - +#if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_STANDARD) +#error not yet implemented /** NOTE: This section deals with implementing (fast) asynchronous analog reads, which form the backbone of mozziAnalogRead(), but also of USE_AUDIO_INPUT (if enabled). * This template provides empty/dummy implementations to allow you to skip over this section, initially. Once you have an implementation, be sure to enable the - * #define, below: */ -//#define MOZZI_FAST_ANALOG_IMPLEMENTED + * #define, above */ // Insert here code to read the result of the latest asynchronous conversion, when it is finished. // You can also provide this as a function returning unsigned int, should it be more complex on your platform @@ -74,52 +76,37 @@ static void startAudioInput() { * Implement adcPinToChannelNum() and channelNumToIndex() to perform the appropriate mapping. */ // NOTE: Theoretically, adcPinToChannelNum is public API for historical reasons, thus cannot be replaced by a define -#define channelNumToIndex(channel) channel uint8_t adcPinToChannelNum(uint8_t pin) { return pin; } /** NOTE: Code needed to trigger a conversion on a new channel */ void adcStartConversion(uint8_t channel) { -#warning Fast analog read not implemented on this platform } /** NOTE: Code needed to trigger a subsequent conversion on the latest channel. If your platform has no special code for it, you should store the channel from * adcStartConversion(), and simply call adcStartConversion(previous_channel), here. */ void startSecondADCReadOnCurrentChannel() { -#warning Fast analog read not implemented on this platform } /** NOTE: Code needed to set up faster than usual analog reads, e.g. specifying the number of CPU cycles that the ADC waits for the result to stabilize. * This particular function is not super important, so may be ok to leave empty, at least, if the ADC is fast enough by default. */ void setupFastAnalogRead(int8_t speed) { -#warning Fast analog read not implemented on this platform } /** NOTE: Code needed to initialize the ADC for asynchronous reads. Typically involves setting up an interrupt handler for when conversion is done, and * possibly calibration. */ void setupMozziADC(int8_t speed) { -#warning Fast analog read not implemented on this platform - #if (USE_AUDIO_INPUT) - startAudioInput(); - #endif -} - -/* NOTE: Most platforms call a specific function/ISR when conversion is complete. Provide this function, here. - * From inside its body, simply call advanceADCStep(). E.g.: -void stm32_adc_eoc_handler() { - advanceADCStep(); } -*/ - +#endif ////// END analog input code //////// ////// BEGIN audio output code ////// -#if (EXTERNAL_AUDIO_OUTPUT == true) +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED) -#define US_PER_AUDIO_TICK (1000000L / AUDIO_RATE) +#define US_PER_AUDIO_TICK (1000000L / MOZZI_AUDIO_RATE) #include mbed::Ticker audio_output_timer; @@ -132,20 +119,21 @@ inline void defaultAudioOutputCallback() { static void startAudio() { audio_output_timer.attach_us(&defaultAudioOutputCallback, US_PER_AUDIO_TICK); + startAudioInput(); } void stopMozzi() { audio_output_timer.detach(); } -#elif (MBED_AUDIO_OUT_MODE == INTERNAL_DAC) +#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) #include -AdvancedDAC dac1(AUDIO_CHANNEL_1_PIN); +AdvancedDAC dac1(MOZZI_AUDIO_PIN_1); Sample buf1[CHUNKSIZE]; -#if (AUDIO_CHANNELS > 1) -AdvancedDAC dac2(AUDIO_CHANNEL_2_PIN); +#if (MOZZI_AUDIO_CHANNELS > 1) +AdvancedDAC dac2(MOZZI_AUDIO_PIN_2); Sample buf2[CHUNKSIZE]; #endif int bufpos = 0; @@ -157,25 +145,24 @@ inline void commitBuffer(Sample buffer[], AdvancedDAC &dac) { dac.write(dmabuf); } -/** NOTE: This is the function that actually write a sample to the output. In case of EXTERNAL_AUDIO_OUTPUT == true, it is provided by the library user, instead. */ inline void audioOutput(const AudioOutput f) { if (bufpos >= CHUNKSIZE) { commitBuffer(buf1, dac1); -#if (AUDIO_CHANNELS > 1) +#if (MOZZI_AUDIO_CHANNELS > 1) commitBuffer(buf2, dac2); #endif bufpos = 0; } - buf1[bufpos] = f.l()+AUDIO_BIAS; -#if (AUDIO_CHANNELS > 1) - buf2[bufpos] = f.r()+AUDIO_BIAS; + buf1[bufpos] = f.l()+MOZZI_AUDIO_BIAS; +#if (MOZZI_AUDIO_CHANNELS > 1) + buf2[bufpos] = f.r()+MOZZI_AUDIO_BIAS; #endif ++bufpos; } bool canBufferAudioOutput() { return (bufpos < CHUNKSIZE || (dac1.available() -#if (AUDIO_CHANNELS > 1) +#if (MOZZI_AUDIO_CHANNELS > 1) && dac2.available() #endif )); @@ -183,45 +170,46 @@ bool canBufferAudioOutput() { static void startAudio() { //NOTE: DAC setup currently affected by https://github.com/arduino-libraries/Arduino_AdvancedAnalog/issues/35 . Don't expect this to work, until using a fixed version fo Arduino_AdvancedAnalog! - if (!dac1.begin(AN_RESOLUTION_12, AUDIO_RATE, CHUNKSIZE, 256/CHUNKSIZE)) { + if (!dac1.begin(AN_RESOLUTION_12, MOZZI_AUDIO_RATE, CHUNKSIZE, 256/CHUNKSIZE)) { Serial.println("Failed to start DAC1 !"); while (1); } -#if (AUDIO_CHANNELS > 1) - if (!dac2.begin(AN_RESOLUTION_12, AUDIO_RATE, CHUNKSIZE, 256/CHUNKSIZE)) { +#if (MOZZI_AUDIO_CHANNELS > 1) + if (!dac2.begin(AN_RESOLUTION_12, MOZZI_AUDIO_RATE, CHUNKSIZE, 256/CHUNKSIZE)) { Serial.println("Failed to start DAC2 !"); while (1); } #endif + startAudioInput(); } void stopMozzi() { dac1.stop(); -#if (AUDIO_CHANNELS > 1) +#if (MOZZI_AUDIO_CHANNELS > 1) dac2.stop(); #endif } -#elif (MBED_AUDIO_OUT_MODE == PDM_VIA_SERIAL) +#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_SERIAL) #include -mbed::BufferedSerial serial_out1(digitalPinToPinName(PDM_SERIAL_UART_TX_CHANNEL_1), digitalPinToPinName(PDM_SERIAL_UART_RX_CHANNEL_1)); -uint8_t buf[PDM_RESOLUTION*4]; +mbed::BufferedSerial serial_out1(digitalPinToPinName(MOZZI_SERIAL_PIN_TX), digitalPinToPinName(MOZZI_SERIAL_PIN_RX)); +uint8_t buf[MOZZI_PDM_RESOLUTION*4]; bool canBufferAudioOutput() { return serial_out1.writable(); } inline void audioOutput(const AudioOutput f) { - for (uint8_t i = 0; i < PDM_RESOLUTION*4; ++i) { - buf[i] = pdmCode8(f.l()+AUDIO_BIAS); + for (uint8_t i = 0; i < MOZZI_PDM_RESOLUTION*4; ++i) { + buf[i] = pdmCode8(f.l()+MOZZI_AUDIO_BIAS); } - serial_out1.write(&buf, PDM_RESOLUTION*4); + serial_out1.write(&buf, MOZZI_PDM_RESOLUTION*4); } static void startAudio() { - serial_out1.set_baud(AUDIO_RATE*PDM_RESOLUTION*40); // NOTE: 40 = 4 * (8 bits + stop-bits) + serial_out1.set_baud(MOZZI_AUDIO_RATE*MOZZI_PDM_RESOLUTION*40); // NOTE: 40 == 4 * (8 bits + stop-bits) serial_out1.set_format(8, mbed::BufferedSerial::None, 1); } @@ -232,3 +220,6 @@ void stopMozzi() { #endif ////// END audio output code ////// + +#undef CHUNKSIZE +#undef US_PER_AUDIO_TICK diff --git a/MozziGuts_impl_RENESAS.hpp b/MozziGuts_impl_RENESAS.hpp index f5c0e4c34..ddc9edeaf 100644 --- a/MozziGuts_impl_RENESAS.hpp +++ b/MozziGuts_impl_RENESAS.hpp @@ -19,6 +19,7 @@ ////// BEGIN analog input code //////// +#if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_STANDARD) #define channelNumToIndex(channel) channel-14 // A0=14 void const *const p_context = 0; // unused but needed for the ADC call @@ -32,8 +33,6 @@ void adc_callback(adc_callback_args_t *p_args) { #include "MozziGuts_impl_RENESAS_ADC.hpp" -#define MOZZI_FAST_ANALOG_IMPLEMENTED - #define getADCReading() readADC(r4_pin) uint8_t adcPinToChannelNum(uint8_t pin) { @@ -56,7 +55,7 @@ void setupFastAnalogRead(int8_t speed) { void setupMozziADC(int8_t speed) { IRQManager::getInstance().addADCScanEnd(&adc, NULL); // this is needed to change some config inside the ADC, even though we do not give the callback here (doing so crashes the board). The callback is declared to the ADC by: R_ADC_CallbackSet(&(_adc->ctrl), adc_callback, p_context, p_callback_memory); in MozziGuts_impl_RENESAS_ADC.hpp. } - +#endif ////// END analog input code //////// @@ -78,9 +77,11 @@ As a consequence we need to artificially empty the buffer at the same rate that it. */ +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC, MOZZI_OUTPUT_EXTERNAL_TIMED) FspTimer timer; +#endif -#if (EXTERNAL_AUDIO_OUTPUT != true) // otherwise, the last stage - audioOutput() - will be provided by +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) CircularBuffer output_buffer; #include "MozziGuts_impl_RENESAS_analog.hpp" #endif @@ -88,13 +89,14 @@ CircularBuffer output_buffer; //////////////// TIMER //////////////// -#if EXTERNAL_AUDIO_OUTPUT == true +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED) void timer_callback_dummy(timer_callback_args_t __attribute__((unused)) *args){defaultAudioOutput();}; -#else +#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) //void timer_callback_dummy(timer_callback_args_t __attribute__((unused)) *args){ void timer_callback_dummy(timer_callback_args_t __attribute__((unused)) *args){output_buffer.read();}; // to empty the buffer (the dac does not take care of it), a bit a waste of timer... #endif +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC, MOZZI_OUTPUT_EXTERNAL_TIMED) void timer_init() { uint8_t type; int8_t tindex = FspTimer::get_available_timer(type); @@ -104,14 +106,15 @@ void timer_init() { } if (tindex >= 0) { - timer.begin(TIMER_MODE_PERIODIC, type, tindex, AUDIO_RATE, 50.0,timer_callback_dummy); + timer.begin(TIMER_MODE_PERIODIC, type, tindex, MOZZI_AUDIO_RATE, 50.0,timer_callback_dummy); timer.setup_overflow_irq(); } - -#if EXTERNAL_AUDIO_OUTPUT != true // we need to set up another timer for dac caring + +# if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) + // we need to set up another timer for dac caring // note: it is running at the same speed than the other one, but could not manage // to get the other one updating the dac and removing the samples from the buffer… - tindex = FspTimer::get_available_timer(type); + tindex = FspTimer::get_available_timer(type); if (tindex < 0) { tindex = FspTimer::get_available_timer(type, true); @@ -119,28 +122,31 @@ void timer_init() { if (tindex >= 0) { FspTimer::force_use_of_pwm_reserved_timer(); - timer_dac.begin(TIMER_MODE_PERIODIC, type, tindex, AUDIO_RATE, 50.0); - timer_dac.setup_overflow_irq(); - dtc_cfg_extend.activation_source = timer_dac.get_cfg()->cycle_end_irq; - timer_dac.open(); -#endif - timer.open(); + timer_dac.begin(TIMER_MODE_PERIODIC, type, tindex, MOZZI_AUDIO_RATE, 50.0); + timer_dac.setup_overflow_irq(); + dtc_cfg_extend.activation_source = timer_dac.get_cfg()->cycle_end_irq; + timer_dac.open(); } - } - +# endif // TODO: This endif used to be two lines up from here (above timer.open), which does not make sense syntactically, for external output + timer.open(); +} +#endif +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) inline void audioOutput(const AudioOutput f) { - output_buffer.write(f+AUDIO_BIAS); + output_buffer.write(f+MOZZI_AUDIO_BIAS); } -#define canBufferAudioOutput() (!output_buffer.isFull()) - +# define canBufferAudioOutput() (!output_buffer.isFull()) +#endif static void startAudio() { -#if EXTERNAL_AUDIO_OUTPUT != true - dac_creation(AUDIO_CHANNEL_1_PIN); +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) + dac_creation(MOZZI_AUDIO_PIN_1); #endif +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC, MOZZI_OUTPUT_EXTERNAL_TIMED) timer_init(); // this need to be done between the DAC creation and initialization in the case where the on-board DAC is used, hence the ugly repetition here. -#if EXTERNAL_AUDIO_OUTPUT != true +#endif +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) dac_init(); R_DTC_Open(&dtc_ctrl, &dtc_cfg); R_DTC_Enable(&dtc_ctrl); @@ -151,11 +157,14 @@ static void startAudio() { R_DTC_Reconfigure(&dtc_ctrl, dtc_cfg.p_info); timer_dac.start(); #endif +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC, MOZZI_OUTPUT_EXTERNAL_TIMED) timer.start(); - +#endif } void stopMozzi() { +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC, MOZZI_OUTPUT_EXTERNAL_TIMED) timer.stop(); +#endif } //// END AUDIO OUTPUT code /////// diff --git a/MozziGuts_impl_RENESAS_ADC.hpp b/MozziGuts_impl_RENESAS_ADC.hpp index c9a01fb24..079486e67 100644 --- a/MozziGuts_impl_RENESAS_ADC.hpp +++ b/MozziGuts_impl_RENESAS_ADC.hpp @@ -5,12 +5,97 @@ It contains functions to interact with the ADC in order to implement async ADC r */ #include -#include +//#include #include +/** VERBATIM from Arduino's analog.cpp + */ +#define MAX_ADC_CHANNELS 29 +static uint16_t analog_values_by_channels[MAX_ADC_CHANNELS] = {0}; +static void ADC_irq_cbk(adc_callback_args_t * cb_data); +static ADC_Container adc(0,ADC_irq_cbk); +static ADC_Container adc1(1,ADC_irq_cbk); +static ADCIrqCbk_f scan_complete_cbk = nullptr; +static ADCIrqCbk_f scan_complete_b_cbk = nullptr; +static ADCIrqCbk_f window_compare_a_cbk = nullptr; +static ADCIrqCbk_f window_compare_b_cbk = nullptr; +static void readAllGroupA(ADC_Container *_adc) { + for(int i = 0; i < MAX_ADC_CHANNELS; i++) { + if(_adc->channel_cfg.scan_mask & (1 << i)) { + //is the channel active -> yes, read it + R_ADC_Read(&(_adc->ctrl), (adc_channel_t)i, analog_values_by_channels + i); + } + } +} + +static void readAllGroupB(ADC_Container *_adc) { + for(int i = 0; i < MAX_ADC_CHANNELS; i++) { + if(_adc->channel_cfg.scan_mask_group_b & (1 << i)) { + //is the channel active -> yes, read it + R_ADC_Read(&(_adc->ctrl), (adc_channel_t)i, analog_values_by_channels + i); + } + } +} + + +static void ADC_irq_cbk(adc_callback_args_t * cb_data) { + if(cb_data->event == ADC_EVENT_SCAN_COMPLETE) { + if(scan_complete_cbk != nullptr) { + if(cb_data->unit == 0) { + readAllGroupA(&adc); + } + else if(cb_data->unit == 1) { + readAllGroupA(&adc1); + } + scan_complete_cbk(cb_data->unit); + } + } + else if(cb_data->event == ADC_EVENT_SCAN_COMPLETE_GROUP_B) { + if(scan_complete_b_cbk != nullptr) { + if(cb_data->unit == 0) { + readAllGroupB(&adc); + } + else if(cb_data->unit == 1) { + readAllGroupB(&adc1); + } + scan_complete_b_cbk(cb_data->unit); + } + } + else if(cb_data->event == ADC_EVENT_WINDOW_COMPARE_A) { + if(window_compare_a_cbk != nullptr) { + window_compare_a_cbk(cb_data->unit); + } + } + else if(cb_data->event == ADC_EVENT_WINDOW_COMPARE_B) { + if(window_compare_b_cbk != nullptr) { + window_compare_b_cbk(cb_data->unit); + } + } +} + +/* -------------------------------------------------------------------------- */ +static ADC_Container *get_ADC_container_ptr(int32_t pin, uint16_t &cfg) { +/* -------------------------------------------------------------------------- */ + ADC_Container *rv = nullptr; + auto cfg_adc = getPinCfgs(pin, PIN_CFG_REQ_ADC); + if(cfg_adc[0] > 0 ) { + if(IS_ADC1(cfg_adc[0])) { + rv = &adc1; + } + else { + rv = &adc; + } + } + cfg = cfg_adc[0]; + return rv; + +} + +/* END of verbatim + */ //////////////////// ADC ////////////// void startScan(int pin) diff --git a/MozziGuts_impl_RP2040.hpp b/MozziGuts_impl_RP2040.hpp index 096a292ca..1b2b87623 100644 --- a/MozziGuts_impl_RP2040.hpp +++ b/MozziGuts_impl_RP2040.hpp @@ -15,10 +15,11 @@ # error "Wrong implementation included for this platform" #endif +#include ////// BEGIN analog input code //////// -#define MOZZI_FAST_ANALOG_IMPLEMENTED +#if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_STANDARD) /** Implementation notes: * - So nobody on the nets seems to have quite figured out, how to run the RP2040 ADC in "regular" asynchronous mode. @@ -28,7 +29,6 @@ */ #include -#include #define getADCReading() rp2040_adc_result #define channelNumToIndex(channel) channel @@ -116,6 +116,8 @@ void rp2040_adc_queue_handler() { dma_channel_set_trans_count(rp2040_adc_dma_chan, 1, true); // set up for another read advanceADCStep(); } +#endif // MOZZI_ANALOG_READ + ////// END analog input code //////// @@ -123,16 +125,17 @@ void rp2040_adc_queue_handler() { #define LOOP_YIELD tight_loop_contents(); // apparently needed, among other things, to service the alarm pool -#if (RP2040_AUDIO_OUT_MODE == PWM_VIA_BARE_CHIP) || (EXTERNAL_AUDIO_OUTPUT == true) +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_EXTERNAL_TIMED) #include -#if (EXTERNAL_AUDIO_OUTPUT != true) // otherwise, the last stage - audioOutput() - will be provided by the user + +# if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM) inline void audioOutput(const AudioOutput f) { - pwm_set_gpio_level(AUDIO_CHANNEL_1_PIN, f.l()+AUDIO_BIAS); -#if (AUDIO_CHANNELS > 1) - pwm_set_gpio_level(AUDIO_CHANNEL_2_PIN, f.r()+AUDIO_BIAS); -#endif + pwm_set_gpio_level(MOZZI_AUDIO_PIN_1, f.l()+MOZZI_AUDIO_BIAS); +# if (MOZZI_AUDIO_CHANNELS > 1) + pwm_set_gpio_level(MOZZI_AUDIO_PIN_2, f.r()+MOZZI_AUDIO_BIAS); +# endif } -#endif // #if (EXTERNAL_AUDIO_OUTPUT != true) +# endif // MOZZI_OUTPUT_PWM #include /** Implementation notes: @@ -155,76 +158,74 @@ void audioOutputCallback(uint) { } while (hardware_alarm_set_target(audio_update_alarm_num, next_audio_update)); } -#elif (RP2040_AUDIO_OUT_MODE == EXTERNAL_DAC_VIA_I2S) +#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC) #include I2S i2s(OUTPUT); - inline bool canBufferAudioOutput() { return (i2s.availableForWrite()); } inline void audioOutput(const AudioOutput f) { -#if (AUDIO_BITS == 8) -#if (AUDIO_CHANNELS > 1) +# if (MOZZI_AUDIO_BITS == 8) +# if (MOZZI_AUDIO_CHANNELS > 1) i2s.write8(f.l(), f.r()); -#else +# else i2s.write8(f.l(), 0); -#endif +# endif -#elif (AUDIO_BITS == 16) -#if (AUDIO_CHANNELS > 1) +# elif (MOZZI_AUDIO_BITS == 16) +# if (MOZZI_AUDIO_CHANNELS > 1) i2s.write16(f.l(), f.r()); -#else +# else i2s.write16(f.l(), 0); -#endif +# endif -#elif (AUDIO_BITS == 24) -#if (AUDIO_CHANNELS > 1) +# elif (MOZZI_AUDIO_BITS == 24) +# if (MOZZI_AUDIO_CHANNELS > 1) i2s.write24(f.l(), f.r()); -#else +# else i2s.write24(f.l(), 0); -#endif +# endif -#elif (AUDIO_BITS == 32) -#if (AUDIO_CHANNELS > 1) +# elif (MOZZI_AUDIO_BITS == 32) +# if (MOZZI_AUDIO_CHANNELS > 1) i2s.write32(f.l(), f.r()); -#else +# else i2s.write32(f.l(), 0); -#endif -#else - #error The number of AUDIO_BITS set in AudioConfigRP2040.h is incorrect -#endif +# endif +# else +# error Invalid number of MOZZI_AUDIO_BITS configured +# endif - } #endif static void startAudio() { -#if (RP2040_AUDIO_OUT_MODE == PWM_VIA_BARE_CHIP) || (EXTERNAL_AUDIO_OUTPUT == true) // EXTERNAL AUDIO needs the timers set here -#if (EXTERNAL_AUDIO_OUTPUT != true) +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM) // calling analogWrite for the first time will try to init the pwm frequency and range on all pins. We don't want that happening after we've set up our own, // so we start off with a dummy call to analogWrite: - analogWrite(AUDIO_CHANNEL_1_PIN, AUDIO_BIAS); + analogWrite(MOZZI_AUDIO_PIN_1, MOZZI_AUDIO_BIAS); // Set up fast PWM on the output pins // TODO: This is still very crude! pwm_config c = pwm_get_default_config(); pwm_config_set_clkdiv(&c, 1); // Fastest we can get: PWM clock running at full CPU speed - pwm_config_set_wrap(&c, 1l << AUDIO_BITS); // 11 bits output resolution means FCPU / 2048 values per second, which is around 60kHz for 133Mhz clock speed. - pwm_init(pwm_gpio_to_slice_num(AUDIO_CHANNEL_1_PIN), &c, true); - gpio_set_function(AUDIO_CHANNEL_1_PIN, GPIO_FUNC_PWM); - gpio_set_drive_strength(AUDIO_CHANNEL_1_PIN, GPIO_DRIVE_STRENGTH_12MA); // highest we can get -# if (AUDIO_CHANNELS > 1) -# if ((AUDIO_CHANNEL_1_PIN / 2) != (AUDIO_CHANNEL_2_PIN / 2)) -# error Audio channel pins for stereo or HIFI must be on the same PWM slice (which is the case for the pairs (0,1), (2,3), (4,5), etc. Adjust AudioConfigRP2040.h . + pwm_config_set_wrap(&c, 1l << MOZZI_AUDIO_BITS); // 11 bits output resolution means FCPU / 2048 values per second, which is around 60kHz for 133Mhz clock speed. + pwm_init(pwm_gpio_to_slice_num(MOZZI_AUDIO_PIN_1), &c, true); + gpio_set_function(MOZZI_AUDIO_PIN_1, GPIO_FUNC_PWM); + gpio_set_drive_strength(MOZZI_AUDIO_PIN_2, GPIO_DRIVE_STRENGTH_12MA); // highest we can get +# if (MOZZI_AUDIO_CHANNELS > 1) +# if ((MOZZI_AUDIO_PIN_1 / 2) != (MOZZI_AUDIO_PIN_1 / 2)) +# error Audio channel pins for stereo or HIFI must be on the same PWM slice (which is the case for the pairs (0,1), (2,3), (4,5), etc. Adjust MOZZI_AUDIO_PIN_1/2 . # endif - gpio_set_function(AUDIO_CHANNEL_2_PIN, GPIO_FUNC_PWM); - gpio_set_drive_strength(AUDIO_CHANNEL_2_PIN, GPIO_DRIVE_STRENGTH_12MA); // highest we can get + gpio_set_function(MOZZI_AUDIO_PIN_2, GPIO_FUNC_PWM); + gpio_set_drive_strength(MOZZI_AUDIO_PIN_2, GPIO_DRIVE_STRENGTH_12MA); // highest we can get # endif #endif +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_EXTERNAL_TIMED) for (audio_update_alarm_num = 0; audio_update_alarm_num < 4; ++audio_update_alarm_num) { if (!hardware_alarm_is_claimed(audio_update_alarm_num)) { hardware_alarm_claim(audio_update_alarm_num); @@ -232,35 +233,38 @@ static void startAudio() { break; } } - micros_per_update = 1000000l / AUDIO_RATE; + micros_per_update = 1000000l / MOZZI_AUDIO_RATE; do { next_audio_update = make_timeout_time_us(micros_per_update); // See audioOutputCallback(), above. In _theory_ some interrupt stuff might delay us, here, causing us to miss the first beat (and everything that follows) } while (hardware_alarm_set_target(audio_update_alarm_num, next_audio_update)); -#elif (RP2040_AUDIO_OUT_MODE == EXTERNAL_DAC_VIA_I2S) - i2s.setBCLK(BCLK_PIN); - i2s.setDATA(DOUT_PIN); - i2s.setBitsPerSample(AUDIO_BITS); +#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC) + i2s.setBCLK(MOZZI_I2S_PIN_BCK); + i2s.setDATA(MOZZI_I2S_PIN_DATA); + i2s.setBitsPerSample(MOZZI_AUDIO_BITS); -#if (AUDIO_BITS > 16) - i2s.setBuffers(BUFFERS, (size_t) (BUFFER_SIZE/BUFFERS), 0); -#else - i2s.setBuffers(BUFFERS, (size_t) (BUFFER_SIZE/BUFFERS/2), 0); -#endif -#if (LSBJ_FORMAT == true) +# if (MOZZI_AUDIO_BITS > 16) + i2s.setBuffers(MOZZI_RP2040_BUFFERS, (size_t) (MOZZI_RP2040_BUFFER_SIZE/MOZZI_RP2040_BUFFERS), 0); +# else + i2s.setBuffers(MOZZI_RP2040_BUFFERS, (size_t) (MOZZI_RP2040_BUFFER_SIZE/MOZZI_RP2040_BUFFERS/2), 0); +# endif +# if MOZZI_IS(MOZZI_I2S_FORMAT, MOZZI_I2S_FORMAT_LSBJ) i2s.setLSBJFormat(); -#endif - i2s.begin(AUDIO_RATE); +# endif + i2s.begin(MOZZI_AUDIO_RATE); #endif } void stopMozzi() { -#if (RP2040_AUDIO_OUT_MODE == PWM_VIA_BARE_CHIP) || (EXTERNAL_AUDIO_OUTPUT == true) +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_EXTERNAL_TIMED) hardware_alarm_set_callback(audio_update_alarm_num, NULL); -#elif (RP2040_AUDIO_OUT_MODE == EXTERNAL_DAC_VIA_I2S) +#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC) i2s.end(); #endif } ////// END audio output code ////// + +#undef MOZZI_RP2040_BUFFERS +#undef MOZZI_RP2040_BUFFER_SIZE diff --git a/MozziGuts_impl_SAMD.hpp b/MozziGuts_impl_SAMD.hpp index 03cc61910..b885a28fe 100644 --- a/MozziGuts_impl_SAMD.hpp +++ b/MozziGuts_impl_SAMD.hpp @@ -15,29 +15,28 @@ #endif ////// BEGIN analog input code //////// -//#define MOZZI_FAST_ANALOG_IMPLEMENTED // not yet +#if MOZZI_IS(MOZZI_ANALOG_READ, NOZZI_ANALOG_READ_STANDARD) +#error not yet implemented #define getADCReading() 0 #define channelNumToIndex(channel) channel uint8_t adcPinToChannelNum(uint8_t pin) { return pin; } void adcStartConversion(uint8_t channel) { -#warning Fast analog read not implemented on this platform } void startSecondADCReadOnCurrentChannel() { -#warning Fast analog read not implemented on this platform } void setupFastAnalogRead(int8_t speed) { -#warning Fast analog read not implemented on this platform } void setupMozziADC(int8_t speed) { -#warning Fast analog read not implemented on this platform } +#endif ////// END analog input code //////// //// BEGIN AUDIO OUTPUT code /////// +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC, MOZZI_OUTPUT_EXTERNAL_TIMED) // These are ARM SAMD21 Timer 5 routines to establish a sample rate interrupt static bool tcIsSyncing() { return TC5->COUNT16.STATUS.reg & TC_STATUS_SYNCBUSY; @@ -106,29 +105,32 @@ void samd21AudioOutput() { #ifdef __cplusplus } #endif +#endif // MOZZI_AUDIO_MODE -#if (EXTERNAL_AUDIO_OUTPUT != true) // otherwise, the last stage - audioOutput() - will be provided by the user -#include "AudioConfigSAMD21.h" +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) inline void audioOutput(const AudioOutput f) { - analogWrite(AUDIO_CHANNEL_1_PIN, f.l()+AUDIO_BIAS); + analogWrite(MOZZI_AUDIO_PIN_1, f.l()+MOZZI_AUDIO_BIAS); } #endif static void startAudio() { -#ifdef ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) +# ifdef ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS { static const int CPLAY_SPEAKER_SHUTDOWN = 11; pinMode(CPLAY_SPEAKER_SHUTDOWN, OUTPUT); digitalWrite(CPLAY_SPEAKER_SHUTDOWN, HIGH); } -#endif - analogWriteResolution(AUDIO_BITS); -#if (EXTERNAL_AUDIO_OUTPUT != true) - analogWrite(AUDIO_CHANNEL_1_PIN, 0); +# endif + + analogWriteResolution(MOZZI_AUDIO_BITS); + analogWrite(MOZZI_AUDIO_PIN_1, 0); #endif - tcConfigure(AUDIO_RATE); +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC, MOZZI_OUTPUT_EXTERNAL_TIMED) + tcConfigure(MOZZI_AUDIO_RATE); +#endif } void stopMozzi() { diff --git a/MozziGuts_impl_STM32.hpp b/MozziGuts_impl_STM32.hpp index bf8164d24..3c5969ba0 100644 --- a/MozziGuts_impl_STM32.hpp +++ b/MozziGuts_impl_STM32.hpp @@ -13,8 +13,9 @@ #include "HardwareTimer.h" ////// BEGIN analog input code //////// -#define MOZZI_FAST_ANALOG_IMPLEMENTED -//#include // Disabled, here. See AudioConfigSTM32.h +#if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_STANDARD) + +//#include // Disabled, here. See hardware_defines.h STM32ADC adc(ADC1); uint8_t stm32_current_adc_pin; // TODO: this is actually a "channel" according to our terminology, but "channel" and "pin" are equal on this platform #define getADCReading() adc.getData() @@ -38,13 +39,6 @@ void stm32_adc_eoc_handler() { advanceADCStep(); } -void setupFastAnalogRead(int8_t speed) { - // NOTE: These picks are pretty arbitrary. Further available options are 7_5, 28_5, 55_5, 71_5 and 239_5 (i.e. 7.5 ADC cylces, etc.) - if (speed == FASTEST_ADC) adc.setSampleRate(ADC_SMPR_1_5); - else if (speed == FASTER_ADC) adc.setSampleRate(ADC_SMPR_13_5); - else (adc.setSampleRate(ADC_SMPR_41_5)); -} - void setupMozziADC(int8_t speed) { adc.attachInterrupt(stm32_adc_eoc_handler); } @@ -56,32 +50,40 @@ inline uint8_t STM32PinMap(uint8_t pin) else return pin; } +void setupFastAnalogRead(int8_t speed) { + // NOTE: These picks are pretty arbitrary. Further available options are 7_5, 28_5, 55_5, 71_5 and 239_5 (i.e. 7.5 ADC cylces, etc.) + if (speed == FASTEST_ADC) adc.setSampleRate(ADC_SMPR_1_5); + else if (speed == FASTER_ADC) adc.setSampleRate(ADC_SMPR_13_5); + else (adc.setSampleRate(ADC_SMPR_41_5)); +} +#endif + ////// END analog input code //////// //// BEGIN AUDIO OUTPUT code /////// -#if (EXTERNAL_AUDIO_OUTPUT == true) -HardwareTimer audio_update_timer(2); -#else -HardwareTimer audio_update_timer(AUDIO_UPDATE_TIMER); -HardwareTimer audio_pwm_timer(AUDIO_PWM_TIMER); +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED) +HardwareTimer audio_update_timer(MOZZI_AUDIO_UPDATE_TIMER); +#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM) +HardwareTimer audio_update_timer(MOZZI_AUDIO_UPDATE_TIMER); +HardwareTimer audio_pwm_timer(MOZZI_AUDIO_PWM_TIMER); -#include "AudioConfigSTM32.h" inline void audioOutput(const AudioOutput f) { -# if (AUDIO_MODE == HIFI) - pwmWrite(AUDIO_CHANNEL_1_PIN, (f.l()+AUDIO_BIAS) & ((1 << AUDIO_BITS_PER_CHANNEL) - 1)); - pwmWrite(AUDIO_CHANNEL_1_PIN_HIGH, (f.l()+AUDIO_BIAS) >> AUDIO_BITS_PER_CHANNEL); +# if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM) + pwmWrite(MOZZI_AUDIO_PIN_1, (f.l()+MOZZI_AUDIO_BIAS) >> MOZZI_AUDIO_BITS_PER_CHANNEL); + pwmWrite(MOZZI_AUDIO_PIN_1_LOW, (f.l()+MOZZI_AUDIO_BIAS) & ((1 << MOZZI_AUDIO_BITS_PER_CHANNEL) - 1)); # else - pwmWrite(AUDIO_CHANNEL_1_PIN, f.l()+AUDIO_BIAS); -# if (AUDIO_CHANNELS > 1) - pwmWrite(AUDIO_CHANNEL_2_PIN, f.r()+AUDIO_BIAS); + pwmWrite(MOZZI_AUDIO_PIN_1, f.l()+MOZZI_AUDIO_BIAS); +# if (MOZZI_AUDIO_CHANNELS > 1) + pwmWrite(MOZZI_AUDIO_PIN_2, f.r()+MOZZI_AUDIO_BIAS); # endif #endif } #endif static void startAudio() { +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM, MOZZI_OUTPUT_EXTERNAL_TIMED) audio_update_timer.pause(); //audio_update_timer.setPeriod(1000000UL / AUDIO_RATE); // Manually calculate prescaler and overflow instead of using setPeriod, to avoid rounding errors @@ -96,39 +98,43 @@ static void startAudio() { audio_update_timer.attachInterrupt(TIMER_CH1, defaultAudioOutput); audio_update_timer.refresh(); audio_update_timer.resume(); +#endif -#if (EXTERNAL_AUDIO_OUTPUT != true) - pinMode(AUDIO_CHANNEL_1_PIN, PWM); -# if (AUDIO_MODE == HIFI) - pinMode(AUDIO_CHANNEL_1_PIN_HIGH, PWM); -# elif (AUDIO_CHANNELS > 1) - pinMode(AUDIO_CHANNEL_2_PIN, PWM); +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM) + pinMode(MOZZI_AUDIO_PIN_1, PWM); +# if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM) + pinMode(MOZZI_AUDIO_PIN_1_LOW, PWM); +# elif (MOZZI_AUDIO_CHANNELS > 1) + pinMode(MOZZI_AUDIO_PIN_2, PWM); # endif -# define MAX_CARRIER_FREQ (F_CPU / (1 << AUDIO_BITS_PER_CHANNEL)) -# if MAX_CARRIER_FREQ < AUDIO_RATE +# define MAX_CARRIER_FREQ (F_CPU / (1 << MOZZI_AUDIO_BITS_PER_CHANNEL)) +# if MAX_CARRIER_FREQ < MOZZI_AUDIO_RATE # error Configured audio resolution is definitely too high at the configured audio rate (and the given CPU speed) -# elif MAX_CARRIER_FREQ < (AUDIO_RATE * 3) +# elif MAX_CARRIER_FREQ < (MOZZI_AUDIO_RATE * 3) # warning Configured audio resolution may be higher than optimal at the configured audio rate (and the given CPU speed) # endif -# if MAX_CARRIER_FREQ < (AUDIO_RATE * 5) +# if MAX_CARRIER_FREQ < (MOZZI_AUDIO_RATE * 5) // Generate as fast a carrier as possible audio_pwm_timer.setPrescaleFactor(1); # else // No point in generating arbitrarily high carrier frequencies. In fact, if // there _is_ any headroom, give the PWM pin more time to swing from HIGH to // LOW and BACK, cleanly - audio_pwm_timer.setPrescaleFactor((int)MAX_CARRIER_FREQ / (AUDIO_RATE * 5)); + audio_pwm_timer.setPrescaleFactor((int)MAX_CARRIER_FREQ / (MOZZI_AUDIO_RATE * 5)); # endif audio_pwm_timer.setOverflow( - 1 << AUDIO_BITS_PER_CHANNEL); // Allocate enough room to write all + 1 << MOZZI_AUDIO_BITS_PER_CHANNEL); // Allocate enough room to write all // intended bits +# undef MAX_CARRIER_FREQ // no longer needed #endif } void stopMozzi() { +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM, MOZZI_OUTPUT_EXTERNAL_TIMED) audio_update_timer.pause(); +#endif } //// END AUDIO OUTPUT code /////// diff --git a/MozziGuts_impl_STM32duino.hpp b/MozziGuts_impl_STM32duino.hpp index aca71c978..b24f5a749 100644 --- a/MozziGuts_impl_STM32duino.hpp +++ b/MozziGuts_impl_STM32duino.hpp @@ -14,8 +14,7 @@ ////// BEGIN analog input code //////// -#define MOZZI_FAST_ANALOG_IMPLEMENTED - +#if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_STANDARD) // Notes on ADC implementation: So in hours I could not get IRQ-driven ADC to work, much less in a way that should work across the whole STM32 family. // Instead, this code resorts to polling, but contrary to the regular implementation, it sets does so non-blocking. Polling is done from inside audioHook(). // Not terribly efficient, but seems to work ok. @@ -61,9 +60,6 @@ void startSecondADCReadOnCurrentChannel() { conversion_running = true; } -void setupFastAnalogRead(int8_t /*speed*/) { -} - void setupMozziADC(int8_t /*speed*/) { } @@ -77,47 +73,52 @@ void checkADCConversionComplete() { } } +#endif + +void setupFastAnalogRead(int8_t /*speed*/) { +} + ////// END analog input code //////// ////// END analog input code //////// //// BEGIN AUDIO OUTPUT code /////// -#if (EXTERNAL_AUDIO_OUTPUT == true) -HardwareTimer audio_update_timer(TIM2); -#else -HardwareTimer audio_update_timer(AUDIO_UPDATE_TIMER); +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED) +HardwareTimer audio_update_timer(MOZZI_AUDIO_UPDATE_TIMER); +#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM) +HardwareTimer audio_update_timer(MOZZI_AUDIO_UPDATE_TIMER); HardwareTimer *pwm_timer_ht; -PinName output_pin_1 = digitalPinToPinName(AUDIO_CHANNEL_1_PIN); +PinName output_pin_1 = digitalPinToPinName(MOZZI_AUDIO_PIN_1); uint32_t pwm_timer_channel_1 = STM_PIN_CHANNEL(pinmap_function(output_pin_1, PinMap_TIM)); -# if (AUDIO_MODE == HIFI) -PinName output_pin_1_high = digitalPinToPinName(AUDIO_CHANNEL_1_PIN_HIGH); -uint32_t pwm_timer_channel_1_high = STM_PIN_CHANNEL(pinmap_function(output_pin_1_high, PinMap_TIM)); -# elif (AUDIO_CHANNELS > 1) -PinName output_pin_2 = digitalPinToPinName(AUDIO_CHANNEL_2); +# if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM) +PinName output_pin_1_low = digitalPinToPinName(MOZZI_AUDIO_PIN_1_LOW); +uint32_t pwm_timer_channel_1_low = STM_PIN_CHANNEL(pinmap_function(output_pin_1_low, PinMap_TIM)); +# elif (MOZZI_AUDIO_CHANNELS > 1) +PinName output_pin_2 = digitalPinToPinName(MOZZI_AUDIO_PIN_2); uint32_t pwm_timer_channel_2 = STM_PIN_CHANNEL(pinmap_function(output_pin_2, PinMap_TIM)); # endif -#include "AudioConfigSTM32.h" inline void audioOutput(const AudioOutput f) { -# if (AUDIO_MODE == HIFI) - pwm_timer_ht->setCaptureCompare(pwm_timer_channel_1, (f.l()+AUDIO_BIAS) & ((1 << AUDIO_BITS_PER_CHANNEL) - 1)); - pwm_timer_ht->setCaptureCompare(pwm_timer_channel_1_high, (f.l()+AUDIO_BIAS) >> AUDIO_BITS_PER_CHANNEL); +# if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM) + pwm_timer_ht->setCaptureCompare(pwm_timer_channel_1, (f.l()+MOZZI_AUDIO_BIAS) >> MOZZI_AUDIO_BITS_PER_CHANNEL); + pwm_timer_ht->setCaptureCompare(pwm_timer_channel_1_low, (f.l()+MOZZI_AUDIO_BIAS) & ((1 << MOZZI_AUDIO_BITS_PER_CHANNEL) - 1)); # else - pwm_timer_ht->setCaptureCompare(pwm_timer_channel_1, f.l()+AUDIO_BIAS); -# if (AUDIO_CHANNELS > 1) - pwm_timer_ht->setCaptureCompare(pwm_timer_channel_2, f.r()+AUDIO_BIAS); + pwm_timer_ht->setCaptureCompare(pwm_timer_channel_1, f.l()+MOZZI_AUDIO_BIAS); +# if (MOZZI_AUDIO_CHANNELS > 1) + pwm_timer_ht->setCaptureCompare(pwm_timer_channel_2, f.r()+MOZZI_AUDIO_BIAS); # endif #endif } #endif static void startAudio() { +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM, MOZZI_OUTPUT_EXTERNAL_TIMED) audio_update_timer.pause(); //audio_update_timer.setPeriod(1000000UL / AUDIO_RATE); // Manually calculate prescaler and overflow instead of using setPeriod, to avoid rounding errors - uint32_t period_cyc = F_CPU / AUDIO_RATE; + uint32_t period_cyc = F_CPU / MOZZI_AUDIO_RATE; uint16_t prescaler = (uint16_t)(period_cyc / 65535 + 1); uint16_t overflow = (uint16_t)((period_cyc + (prescaler / 2)) / prescaler); audio_update_timer.setPrescaleFactor(prescaler); @@ -126,42 +127,47 @@ static void startAudio() { audio_update_timer.setCaptureCompare(/* channel */ 1, 1); // Interrupt 1 count after each update audio_update_timer.attachInterrupt(/* channel */ 1, defaultAudioOutput); audio_update_timer.refresh(); +#endif -#if (EXTERNAL_AUDIO_OUTPUT != true) +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM) // Configure PWM output - pinMode(AUDIO_CHANNEL_1_PIN, OUTPUT); -# if (AUDIO_MODE == HIFI) - pinMode(AUDIO_CHANNEL_1_PIN_HIGH, OUTPUT); -# elif (AUDIO_CHANNELS > 1) - pinMode(AUDIO_CHANNEL_2_PIN, OUTPUT); + pinMode(MOZZI_AUDIO_PIN_1, OUTPUT); +# if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM) + pinMode(MOZZI_AUDIO_PIN_1_LOW, OUTPUT); +# elif (MOZZI_AUDIO_CHANNELS > 1) + pinMode(MOZZI_AUDIO_PIN_2, OUTPUT); # endif -# define MAX_CARRIER_FREQ (F_CPU / (1 << AUDIO_BITS_PER_CHANNEL)) +# define MAX_CARRIER_FREQ (F_CPU / (1 << MOZZI_AUDIO_BITS_PER_CHANNEL)) // static_assert(MAX_CARRIER_FREQ >= AUDIO_RATE); // Unfortunately, we cannot test this at compile time. F_CPU expands to a runtime variable TIM_TypeDef *pwm_timer_tim = (TIM_TypeDef *) pinmap_peripheral(output_pin_1, PinMap_TIM); pwm_timer_ht = new HardwareTimer(pwm_timer_tim); pwm_timer_ht->setMode(pwm_timer_channel_1, TIMER_OUTPUT_COMPARE_PWM1, output_pin_1); -# if MAX_CARRIER_FREQ < (AUDIO_RATE * 5) +# if MAX_CARRIER_FREQ < (MOZZI_AUDIO_RATE * 5) // Generate as fast a carrier as possible pwm_timer_ht->setPrescaleFactor(1); # else // No point in generating arbitrarily high carrier frequencies. In fact, if // there _is_ any headroom, give the PWM pin more time to swing from HIGH to // LOW and BACK, cleanly - pwm_timer_ht->setPrescaleFactor((int)MAX_CARRIER_FREQ / (AUDIO_RATE * 5)); // as fast as possible + pwm_timer_ht->setPrescaleFactor((int)MAX_CARRIER_FREQ / (MOZZI_AUDIO_RATE * 5)); // as fast as possible # endif // Allocate enough room to write all intended bits - pwm_timer_ht->setOverflow(1 << AUDIO_BITS_PER_CHANNEL); - pwm_timer_ht->setCaptureCompare(pwm_timer_channel_1, AUDIO_BIAS /*, resolution */); + pwm_timer_ht->setOverflow(1 << MOZZI_AUDIO_BITS_PER_CHANNEL); + pwm_timer_ht->setCaptureCompare(pwm_timer_channel_1, MOZZI_AUDIO_BIAS /*, resolution */); pwm_timer_ht->resume(); #endif +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM, MOZZI_OUTPUT_EXTERNAL_TIMED) audio_update_timer.resume(); +#endif } void stopMozzi() { +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM, MOZZI_OUTPUT_EXTERNAL_TIMED) audio_update_timer.pause(); +#endif } //// END AUDIO OUTPUT code /////// diff --git a/MozziGuts_impl_TEENSY.hpp b/MozziGuts_impl_TEENSY.hpp index ef0262d22..04c18ac73 100644 --- a/MozziGuts_impl_TEENSY.hpp +++ b/MozziGuts_impl_TEENSY.hpp @@ -14,18 +14,16 @@ # error "Wrong implementation included for this platform" #endif -// required from http://github.com/pedvide/ADC for Teensy 3.* -#include "IntervalTimer.h" -#include -#include "teensyPinMap.h" - #if (IS_TEENSY3() && F_CPU != 48000000) #warning \ "Mozzi has been tested with a cpu clock speed of 16MHz on Arduino and 48MHz on Teensy 3! Results may vary with other speeds." #endif ////// BEGIN analog input code //////// -#define MOZZI_FAST_ANALOG_IMPLEMENTED +#if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_STANDARD) +// required from http://github.com/pedvide/ADC for Teensy 3.* +#include + ADC *adc; // adc object uint8_t teensy_pin; // TODO: this is actually a "channel" according to our terminology, but "channel" and "pin" are equal on this platform int8_t teensy_adc=0; @@ -69,45 +67,48 @@ void adcStartConversion(uint8_t channel) { static void startSecondADCReadOnCurrentChannel() { adc->startSingleRead(teensy_pin,teensy_adc); } +#endif // MOZZI_ANALOG_READ ////// END analog input code //////// //// BEGIN AUDIO OUTPUT code /////// +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_INTERNAL_DAC, MOZZI_OUTPUT_EXTERNAL_TIMED) +#include "IntervalTimer.h" IntervalTimer timer1; - -#if (EXTERNAL_AUDIO_OUTPUT != true) // otherwise, the last stage - audioOutput() - will be provided by the user -#if IS_TEENSY3() -#include "AudioConfigTeensy3_12bit.h" -#elif IS_TEENSY4() -#include "AudioConfigTeensy4.h" #endif + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_INTERNAL_DAC) inline void audioOutput(const AudioOutput f) { - analogWrite(AUDIO_CHANNEL_1_PIN, f.l()+AUDIO_BIAS); -#if (AUDIO_CHANNELS > 1) - analogWrite(AUDIO_CHANNEL_2_PIN, f.r()+AUDIO_BIAS); -#endif + analogWrite(MOZZI_AUDIO_PIN_1, f.l()+MOZZI_AUDIO_BIAS); +# if (MOZZI_AUDIO_CHANNELS > 1) + analogWrite(MOZZI_AUDIO_PIN_2, f.r()+MOZZI_AUDIO_BIAS); +# endif } #endif static void startAudio() { -#if IS_TEENSY3() +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_INTERNAL_DAC) +# if IS_TEENSY3() analogWriteResolution(12); -#elif IS_TEENSY4() +# elif IS_TEENSY4() analogWriteResolution(10); -# if (!EXTERNAL_AUDIO_OUTPUT) - analogWriteFrequency(AUDIO_CHANNEL_1_PIN, 146484.38f); -# if (AUDIO_CHANNELS > 1) - analogWriteFrequency(AUDIO_CHANNEL_2_PIN, 146484.38f); -# endif // end #if (AUDIO_CHANNELS > 1) -# endif // end #if (!EXTERNAL_AUDIO_OUTPUT) + analogWriteFrequency(MOZZI_AUDIO_PIN_1, 146484.38f); +# if (MOZZI_AUDIO_CHANNELS > 1) + analogWriteFrequency(MOZZI_AUDIO_PIN_2, 146484.38f); +# endif // end #if (MOZZI_AUDIO_CHANNELS > 1) +# endif // TEENSY3/4 +#endif +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_INTERNAL_DAC, MOZZI_OUTPUT_EXTERNAL_TIMED) + timer1.begin(defaultAudioOutput, 1000000. / MOZZI_AUDIO_RATE); #endif - timer1.begin(defaultAudioOutput, 1000000. / AUDIO_RATE); } void stopMozzi() { +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_INTERNAL_DAC, MOZZI_OUTPUT_EXTERNAL_TIMED) timer1.end(); +#endif interrupts(); } //// END AUDIO OUTPUT code /////// diff --git a/MozziGuts_impl_template.hpp b/MozziGuts_impl_template.hpp index a478cf57e..154c1ee29 100644 --- a/MozziGuts_impl_template.hpp +++ b/MozziGuts_impl_template.hpp @@ -16,9 +16,9 @@ * Files involved: * 1. Modify hardware_defines.h, adding a macro to detect your target platform * 2. Modify MozziGuts.cpp to include MozziGuts_impl_YOURPLATFORM.hpp - * 3. Modify MozziGuts.h to include AudioConfigYOURPLATFORM.h - * 4. Copy this file to MozziGuts_impl_YOURPLATFORM.hpp and adjust as necessary - * (If your platform is very similar to an existing port, it may instead be better to modify the existing MozziGuts_impl_XYZ.hpp/AudioConfigYOURPLATFORM.h, + * 3. Modify internal/config_checks_generic.h to include internal/config_checks_YOURPLATFORM.h + * 4. Copy this file to MozziGuts_impl_YOURPLATFORM.hpp and adjust as necessary, same for internal/config_checks_template.h + * (If your platform is very similar to an existing port, it may instead be better to modify the existing MozziGuts_impl_XYZ.hpp/config_checks_XYZ.h, * instead of steps 2-3.). * Some platforms may need small modifications to other files as well, e.g. mozzi_pgmspace.h * @@ -40,14 +40,24 @@ ////// BEGIN analog input code //////// -/** NOTE: This section deals with implementing (fast) asynchronous analog reads, which form the backbone of mozziAnalogRead(), but also of USE_AUDIO_INPUT (if enabled). - * This template provides empty/dummy implementations to allow you to skip over this section, initially. Once you have an implementation, be sure to enable the - * #define, below: */ -//#define MOZZI_FAST_ANALOG_IMPLEMENTED +#if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_STANDARD) +/** NOTE: This section deals with implementing (fast) asynchronous analog reads, which form the backbone of mozziAnalogRead(), but also of MOZZI_AUDIO_INPUT (if enabled). + * + * It is possible, and even recommended, to skip over this section, initially, when starting a new port. Once you have an implementation, be sure to include something like this + * in your platform configuration checks: + * + * // analog reads shall be enabled by default on platforms that support it + * #if not defined(MOZZI_ANALOG_READ) + * #define MOZZI_ANALOG_READ MOZZI_ANALOG_READ_STANDARD + * #endif + * + * Also, of course, remove the #error line, below + */ +#error not yet implemented // Insert here code to read the result of the latest asynchronous conversion, when it is finished. // You can also provide this as a function returning unsigned int, should it be more complex on your platform -#define getADCReading() 0 +#define getADCReading() GET_MY_PLATFORM_ADC_REGISTER /** NOTE: On "pins" vs. "channels" vs. "indices" * "Pin" is the pin number as would usually be specified by the user in mozziAnalogRead(). @@ -69,25 +79,18 @@ uint8_t adcPinToChannelNum(uint8_t pin) { /** NOTE: Code needed to trigger a conversion on a new channel */ void adcStartConversion(uint8_t channel) { -#warning Fast analog read not implemented on this platform } /** NOTE: Code needed to trigger a subsequent conversion on the latest channel. If your platform has no special code for it, you should store the channel from * adcStartConversion(), and simply call adcStartConversion(previous_channel), here. */ void startSecondADCReadOnCurrentChannel() { -#warning Fast analog read not implemented on this platform -} - -/** NOTE: Code needed to set up faster than usual analog reads, e.g. specifying the number of CPU cycles that the ADC waits for the result to stabilize. - * This particular function is not super important, so may be ok to leave empty, at least, if the ADC is fast enough by default. */ -void setupFastAnalogRead(int8_t speed) { -#warning Fast analog read not implemented on this platform } /** NOTE: Code needed to initialize the ADC for asynchronous reads. Typically involves setting up an interrupt handler for when conversion is done, and * possibly calibration. */ void setupMozziADC(int8_t speed) { -#warning Fast analog read not implemented on this platform + setupFastAnalogRead(speed); + // insert further custom code } /* NOTE: Most platforms call a specific function/ISR when conversion is complete. Provide this function, here. @@ -96,6 +99,14 @@ void stm32_adc_eoc_handler() { advanceADCStep(); } */ + +/** NOTE: Code needed to set up faster than usual analog reads, e.g. specifying the number of CPU cycles that the ADC waits for the result to stabilize. + * This particular function is not super important, so may be ok to leave empty, at least, if the ADC is fast enough by default. */ +void setupFastAnalogRead(int8_t speed) { +} + +#endif + ////// END analog input code //////// ////// BEGIN audio output code ////// @@ -110,23 +121,34 @@ void stm32_adc_eoc_handler() { * analog reads for instance. */ //#define AUDIO_HOOK_HOOK -#if (EXTERNAL_AUDIO_OUTPUT != true) // otherwise, the last stage - audioOutput() - will be provided by the user -/** NOTE: This is the function that actually write a sample to the output. In case of EXTERNAL_AUDIO_OUTPUT == true, it is provided by the library user, instead. */ +/* NOTE: Code sections that are needed for a certain audio mode, only, should be guarded as follows (in this example, code will compile for the + * two modes MOZZI_OUTPUT_PWM, and MOZZI_OUTPUT_INTERNAL_DAC (should your port happen to support these two). + * + * Keep in mind that you probably want to support MOZZI_OUTPUT_EXTERNAL_TIMED, and MOZZI_OUTPUT_EXTERNAL_CUSTOM, too, which is usually very + * easy: For both, do *not* provide an audioOutput() function, as this will be provided by the user. For MOZZI_OUTPUT_EXTERNAL_TIMED make sure some + * timer is set up to call defaultAudioOutput() at MOZZI_AUDIO_RATE. For MOZZI_OUTPUT_EXTERNAL_CUSTOM, nothing else will be needed. */ + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_INTERNAL_DAC) // just an example! +/** NOTE: This is the function that actually write a sample to the output. In case of the two EXTERNAL modes, it is provided by the library user, instead. */ inline void audioOutput(const AudioOutput f) { - // e.g. analogWrite(AUDIO_CHANNEL_1_PIN, f.l()+AUDIO_BIAS); -#if (AUDIO_CHANNELS > 1) - // e.g. analogWrite(AUDIO_CHANNEL_2_PIN, f.r()+AUDIO_BIAS); -#endif + // e.g. analogWrite(MOZZI_AUDIO_CHANNEL_1_PIN, f.l()+MOZZI_AUDIO_BIAS); +# if (MOZZI_AUDIO_CHANNELS > 1) + // e.g. analogWrite(MOZZI_AUDIO_CHANNEL_2_PIN, f.r()+MOZZI_AUDIO_BIAS); +# endif } #endif static void startAudio() { // Add here code to get audio output going. This usually involves: // 1) setting up some DAC mechanism (e.g. setting up a PWM pin with appropriate resolution - // 2a) setting up a timer to call defaultAudioOutput() at AUDIO_RATE + // 2a) setting up a timer to call defaultAudioOutput() at MOZZI_AUDIO_RATE // OR 2b) setting up a buffered output queue such as I2S (see ESP32 / ESP8266 for examples for this setup) -#if (EXTERNAL_AUDIO_OUTPUT != true) - // remember that the user may configure EXTERNAL_AUDIO_OUTPUT, in which case, you'll want to provide step 2a), and only that. +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM) + // [...] +#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) + // [...] +#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED) + // remember that the user may configure MOZZI_OUTPUT_EXTERNAL_TIMED, in which case, you'll want to provide step 2a), and only that. #endif } diff --git a/README.md b/README.md index 5bcdc23d9..bf9d131ca 100644 --- a/README.md +++ b/README.md @@ -74,9 +74,9 @@ Teensy 4.0 4.1 | A8 | yes Gemma M0 | A0 | yes Adafruit Playground Express | Built in Speaker | yes Sanguino | 13 | - -STM32F1 maple core (see "Hardware specific notes", below) | PB8 | yes -STM32F1 STM core (see "Hardware specific notes", below) | PA8 | yes -STM32F4 STM core (see "Hardware specific notes", below) | PA8 | yes +STM32F1 maple core | PB8 | yes +STM32F1 STM core | PA8 | yes +STM32F4 STM core | PA8 | yes ESP8266 *see details* | GPIO2 | yes RP2040 | 0 | yes @@ -99,8 +99,9 @@ void updateControl(){ // your control code } -int updateAudio(){ - // your audio code which returns an int between -244 and 243 +AudioOutput_t updateAudio() { + // your audio code which returns a 0-centered integer, typically in the 8bit or 16-bit range + return MonoOutput::from16Bit( [myvalue] ); } void loop() { @@ -122,21 +123,20 @@ Look for code and usage changes [here](extras/NEWS.txt). *** -## Caveats and Workarounds - -#### AVR +## General caveats and Workarounds * While Mozzi is running, calling `delay()`, `delayMicroseconds()`, or other functions which wait or cycle through loops can cause audio glitches. -Mozzi provides `EventDelay()` for scheduling instead of `delay`. - -* `analogRead()` is replaced by `mozziAnalogRead()`, which works in the background instead of blocking the processor. +Mozzi provides `EventDelay()` for scheduling instead of `delay`. In general, make sure to write non-blocking code! -* Mozzi interferes with `analogWrite()`. In `STANDARD` and `STANDARD_PLUS` audio modes, Mozzi takes over Timer1 (pins 9 and 10), but you can use the Timer2 pins, 3 and 11 (your board may differ). In `HIFI` mode, Mozzi uses Timer1 (or Timer4 on some boards), and Timer2, so pins 3 and 11 are also out. -If you need `analogWrite()`, you can do PWM output on any digital pins using the technique in *Mozzi>examples>11.Communication>Sinewave_PWM_pins_HIFI*. +* `analogRead()` is a time-consuming operation especially on the classic AVR boards. Mozzi provides `mozziAnalogRead()` as a drop-in replacement, which works in the +background instead of blocking the processor. +* Depending on the hardware and configured audio output mode, Mozzi will usually claim one or more hardware timers. This may affect, among other things, the ability to +use `analogWrite()`. Check the [Hardware Section of the Documentation](https://sensorium.github.io/Mozzi/doc/html/group__hardware.html) to options to configure ressource +usage. + - If you need `analogWrite()`, you can do PWM output on any digital pins using the technique in *Mozzi>examples>11.Communication>Sinewave_PWM_pins_HIFI*. -#### Last Resort -The timers can be made available with `stopMozzi()`, which stops audio interrupts, until you call `startMozzi()`. +* As last resort, the timers can be made available with `stopMozzi()`, which stops audio interrupts, until you call `startMozzi()`. *** @@ -150,6 +150,9 @@ It’s explained more thoroughly (for Windows) [here](http://www.instructables.c If you still need more speed, Arduino 1.0.5 produces slightly faster code. +Mozzi itself offers many helpers for producing faster code, such as Fixed-Point integer maths, fast replacements for `map()`, `random()`, `millis()`, and other functions. +Be sure to use these! + *** ## Using external chips to produce the sound @@ -188,185 +191,25 @@ Various examples from [Pure Data](http://puredata.info/) by Miller Puckette [Practical synthesis tutorials](http://www.obiwannabe.co.uk/) by Andy Farnell -## Hardware specific notes - -### STM32 -The situation on STM32-based boards is rather confusing, as there are several competing Arduino cores. Importantly: -- Some boards use dedicated cores (e.g. Arduino Giga / Protenta) etc. For those, see the relevant sections (if we support them) -- There is a series of libmaple-based cores, including [Roger Clark's libmaple-based core](https://github.com/rogerclarkmelbourne/Arduino_STM32). These are highly optimized, and provide very complete support, but only for a limited number of boards. Unfortunately, at the time of this writing (2023/04), they are not available for installation via the Arduino Board Manager, and they do not currently seem actively maintained. -- A generic Arduino core for STM32 is the [STM32duino core](https://github.com/stm32duino/Arduino_Core_STM32). It supports a huge step of boards, and seems to have offical backing by STM, but some features of the libmaple based cores are still lacking. To complete confusion, this core now uses the label "STM32duino", which used to be what the libmaple cores above were known by (don't blame Mozzi for this mess!). - -Mozzi supports both of the latter, but currently not at the same level of completeness. - -#### STM32 libmaple based -port by Thomas Friedrichsmeier - -Compiles for and runs on a STM32F103C8T6 blue pill board, with a bunch of caveats (see below), i.e. on a board _without_ a -real DAC. Should probably run on any other board supported by [Roger Clark's libmaple-based core](https://github.com/rogerclarkmelbourne/Arduino_STM32) (although this theory is untested). - -*NOTE* that at the time of this writing, [Stev Strong's slightliy more recent fork of this core](https://github.com/stevstrong/Arduino_STM32/) does *not* work with -Mozzi, apparently due to a bug in pwmWrite(). - -- You will need a very recent checkout of the Arduino_STM32 repository, otherwise compilation will fail. -- Audio output is to pin PB8, by default (HIFI-mode: PB8 and PB9) -- If you want to use MIDI, be sure to replace "MIDI_CREATE_DEFAULT_INSTANCE()" with "MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI)" (or Serial2) -- Timers 4 (PWM output), and 2 (audio rate) are used by default. -- The STM32-specific options (pins, timers) can be configured in AudioConfigSTM32.h -- Default audio resolution is currently set to 10 bits, which yields 70khZ PWM frequency on a 72MHz CPU. HIFI mode is 2*7bits at up to 560Khz (but limited to 5 times audio rate) -- AUDIO_INPUT is completely untested (but implemented in theory) -- Note that AUDIO_INPUT and mozziAnalogRead() return values in the STM32's full ADC resolution of 0-4095 rather than AVR's 0-1023. -- twi_nonblock is not ported - -#### STM32 STM32duino -port by Thomas Friedrichsmeier - -Tested on a STM32F103C8T6 blue pill board as well as an STM32F411CE black pill board, i.e. on sboards _without_ a -real DAC. Compiles and runs, with a bunch of caveats (see below). Should probably run on any other board supported by the -[STM32duino core](https://github.com/stm32duino/Arduino_Core_STM32) (although this theory is untested). -When trying any other board, you probably want to check the platform specific settings (see below), carefully, importantly, whether the desired output resolution is -achievable, and whether the desired output pins are PWM capable. - -- Audio output is PWM-based to pin PA8, by default (HIFI-mode: PA8 and PA9) -- Timers 3 (PWM output), and 2 (audio rate) are used by default. -- The STM32-specific options (pins, timers) can be configured in AudioConfigSTM32duino.h -- Default audio resolution is currently set to 10 bits, which yields 70khZ PWM frequency on a 72MHz CPU. IMPORTANT: Should your CPU be slower, you will need to lower the audio resolution! -- HIFI mode is 2*7bits at up to 560Khz with a 72MHz CPU (but limited to 5 times audio rate) -- Analog input implementation is somewhat experimental, and may not be able to service a whole lot of pins (contributions welcome) -- AUDIO_INPUT is completely untested (but implemented in theory) -- Note that AUDIO_INPUT and mozziAnalogRead() return values in the STM32's full ADC resolution (the exact range depending on the board in use) rather than AVR's 0-1023. -- twi_nonblock is not ported - -### Teensy 3.0/3.1/3.2/3.4/3.5/LC - -This port is working with the latest version of Teensyduino (1.8.5) -Extra libraries required for use with Teensy 3.*: -These are included in the standard Teensyduino install unless you explicitly disable them -- [Timer library](https://github.com/loglow/IntervalTimer) for Teensy 3.* by Daniel Gilbert -- [ADC library](http://github.com/pedvide/ADC) by Pedro Villanueva - -Some of the differences for Teensy 3.* which will affect users include: - -- On Teeensy 3.0/3.1/3.2/Audio output is on pin A14/DAC, in STANDARD or STANDARD_PLUS audio modes. - These modes are identical on Teensy 3.0/3.1/3.2, as the output is via DAC rather than PWM. -- Output is 12 bits in STANDARD and STANDARD_PLUS modes, up from nearly 9 bits for Atmel based boards. HIFI audio, which works by summing two output pins, is not available on Teensy 3.0/3.1. -- twi_nonblock code by Marije Baalman for non-blocking I2C is not compatible with Teensy 3.0/3.1/3.2. - -### Teensy 4.0/4.1 -port by Thomas Combriat - -This port is working with the latest version of Teensyduino (1.8.5) -Extra libraries required for use with Teensy 4.*: -These are included in the standard Teensyduino install unless you explicitly disable them -- [Timer library](https://github.com/loglow/IntervalTimer) for Teensy 3.* by Daniel Gilbert -- [ADC library](http://github.com/pedvide/ADC) by Pedro Villanueva - -Some of the differences for Teensy 4.*: - -- Contrary to the Teensy 3, the Teensy 4 do not have any DAC. The output is done on pin A8 (PWM) by default (editable in `AudioConfigTeensy4.h` - -### SAMD21 architecture (Arduino Circuitplayground M0 and others) -port by Adrian Freed - -- Currently, only output on the inbuilt DAC (pin DAC0) is supported. So, obviously, boards without a DAC are not yet convered (in theory you can still use EXTERNAL_AUDIO_OUTPUT) -- Output resolution is fixed at 10 bits. If your board supports more, configure in AudioConfigSAMD21.h -- mozziAnalogRead() and AUDIO_INPUT are not implemented (contributions welcome) -- We don't have a lot of data, which boards this port has been tested on. Success or not, let us know, if you are using Mozzi on SAMD21 boards - -### ESP8266 -port by Thomas Friedrichsmeier - -- Since flash memory is not built into the ESP8266, but connected, externally, it is much too slow for keeping wave tables, audio samples, etc. Instead, these are kept in RAM on this platform. -- Asynchronous analog reads are not implemented. `mozziAnalogRead()` relays to `analogRead()`. -- AUDIO_INPUT is not implemented. -- twi_nonblock is not ported -- Several audio output modes exist, the default being PDM_VIA_SERIAL (configurable in AudioConfigESP.h): - - PDM_VIA_SERIAL: Output is coded using pulse density modulation, and sent via GPIO2 (Serial1 TX). - - This output mode uses timer1 for queuing audio sample, so that timer is not available for other uses. - - Note that this mode has slightly lower effective analog output range than PDM_VIA_I2S, due to start/stop bits being added to the output stream. - - PDM_VIA_I2S: Output is coded using pulse density modulation, and sent via the I2S pins. The I2S data out pin (GPIO3, which is also "RX") will have the output, - but *all* I2S output pins (RX, GPIO2 and GPIO15) will be affected. Mozzi tries to set GPIO2 and GPIO15 to input mode, and *at the time of this writing*, this allows - I2S output on RX even on boards such as the ESP01 (where GPIO15 is tied to Gnd). However, it seems safest to assume that this mode may not be useable on boards where - GPIO2 or GPIO15 are not available as output pins. - - EXTERNAL_DAC_VIA_I2S: Output is sent to an external DAC (such as a PT8211), digitally coded. This is the only mode that supports STEREO. It also needs the least processing power. -- There is no "HIFI_MODE" in addition to the above output options. For high quality output, either use an external DAC, or increase the PDM_RESOLUTION value. -- Note that the ESP8266 pins can output less current than the other supported CPUs. The maximum is 12mA, with a recommendation to stay below 6mA. - - WHEN CONNECTING A HEADPHONE, DIRECTLY, USE APPROPRIATE CURRENT LIMITING RESISTORS (>= 500Ohms). -- _Any_ WiFi-activity can cause severe spikes in power consumption. This can cause audible "ticking" artifacts, long before any other symptoms. - - If you do not require WiFi in your sketch, you should turn it off, _explicitly_, using `WiFi.mode(WIFI_OFF)`. - - A juicy enough, well regulated power supply, and a stabilizing capacitor between VCC and Gnd can help a lot. - - As the (PDM) output signal is digital, a single transistor can be used to amplify it to an independent voltage level. -- The audio output resolution is always 16 bits on this platform, _internally_. Thus, in updateAudio(), you should scale your output samples to a full 16 bit range. The effective number of output bits cannot easily - be quantified, due to PDM coding. -- audioHook() calls `yield()` once for every audio sample generated. Thus, as long as your audio output buffer does not run empty, you should not need any additional `yield()`s inside `loop()`. - -### ESP32 -port by Dieter Vandoren and Thomas Friedrichsmeier - -- Since flash memory is not built into the ESP32, but connected, externally, it is much too slow for keeping wave tables, audio samples, etc. Instead, these are kept in RAM on this platform. -- Asynchronous analog reads are not implemented. `mozziAnalogRead()` relays to `analogRead()`. -- AUDIO_INPUT is not implemented. -- twi_nonblock is not ported -- Currently, three audio output modes exist, the default being INTERNAL_DAC (configurable in AudioConfigESP32.h). *The configuration details may still be subject to change; please be prepared to make some minimal adjustments to your code, when upgrading Mozzi*: - - INTERNAL_DAC: Output using the built-in DAC on GPIO pins 25 and 26. - - 8 bits resolution, mono (on both pins) or stereo - - For simplicity of code, both pins are always used, even in mono output mode - - PT8211_DAC: Output is sent via I2S in a format suitable for the PT8211 external EXTERNAL_DAC - - 16 bits resolution, mono or stereo. Remember to shift your audio accordingly. - - Output pins can be configured in AudioConfigESP32.h. Default is BCK: 26, WS: 15, DATA: 33 - - PDM_VIA_I2S: Output is converted using pulse density modulation, sent to the I2S data pin. No external hardware needed. - - 16 bits resolution. Remember to shift your audio accordingly. - - Output (DATA) pin can be configured in AudioConfigESP32.h. Default 33. Note that the BCK and WS pins are also used in this mode. - - The PDM_RESOLUTION parameter can be used to reduce noise at the cost of more CPU power. - - Mono, only. -- "HIFI_MODE" is not currently implemented, but could conceivably be realized for the INTERNAL_DAC mode. Patches welcome. -- WIFI-activity not yet tested, but likely the same notes as for ESP8266 apply. Consider turning off WIFI. -- The implementation of audioTicks() may be slightly inaccurate on this platform. - -### RP2040 (Raspberry Pi Pico) -port by Thomas Friedrichsmeier - -Compiles and runs using [this core](https://github.com/earlephilhower/arduino-pico). Can probably be ported to the Mbed core for RP2040, relatively easily, as it relies mostly -on the RP2040 SDK API. Tested on a Pi Pico. - -- This is a recent addition, implementation details may still change (currently just PWM driven by a timer; this may be worth changing to a DMA driven output) -- Wavetables and samples are not kept in progmem on this platform. While apparently speed (of the external flash) is not much of an issue, the data always seems to be copied into RAM, anyway. -- Currently, two audio output modes exist (configurable in AudioConfigRP2040.h) in addition to using an user-defined `audioOutput` function, with the default being PWM_VIA_BARE_CHIP: - - PWM_VIA_BARE_CHIP: PWM audio output on pin 0, by default, with 11 bits default output resolution - - One hardware timer interrupt and one DMA channel are claimed (number not hardcoded), a non-exclusive handler is installed on DMA_IRQ_0. - - HIFI_MODE not yet implemented (although that should not be too hard to do). - - EXTERNAL_DAC_VIA_I2S: I2S output to be connected to an external DAC - - 16 bits resolution by default (configurable in AudioConfigRP2040.h), 8, 16, 24 (left aligned) and 32 resolution are available. - - Both plain I2S and LSBJ_FORMAT (for the PT8211 for instance) are available (configurable in AudioConfigRP2040.h), default is LSBJ. - - Outputs pins can be configured in AudioConfigRP2040.h. Default is BCK: 20, WS: 21, DATA: 22. - - Two DMA channels are claimed (numbers not hardcoded), non-exclusive handlers are installed on DMA_IRQ_0. - - At the time of writing, LSBJ is only available with github arduino-pico core. -- Note that AUDIO_INPUT and mozziAnalogRead() return values in the RP2040's full ADC resolution of 0-4095 rather than AVR's 0-1023. -- twi_nonblock is not ported -- Code uses only one CPU core - -### Arduino Giga/MBED -port by Thomas Friedrichsmeier & Thomas Combriat - -Compiles and runs using Arduino's standard and Arduino_AdvancedAnalog libraries. This port is still **experimental**, testing reveals some instabilities for some configurations (in particular with USE_AUDIO_INPUT) that are under active investigations. - -- This port is not complete yet, in particular: - - Asynchroneous analog reads are not implemented (yet), `mozziAnalogRead()` relays to `analogRead()`. - - HIFI mode is not implemented. -- In addition to using an user-defined `audioOutput()` by setting `EXTERNAL_AUDIO_OUTPUT` to `true` in mozzi_config.h, two bare chip output modes exist (configurable in AudioConfigMBED.h): - - INTERNAL_DAC: uses the DAC present on the board and outputs by default on pin A13 (3.5mm jack connector's tip). Stereo mode uses pin A12 (3.5mm jack connector's first ring) additionally. - - PDM_VIA_SERIAL: returns a pulse-density modulated signal on one of the hardware UART of the board (Serial ports). Default is using the SERIAL2, on pin D18. -- This port should support other MBED based Arduino boards like the Arduino Portenta, in *theory*. It has only been tested on the giga but feedbacks are welcome! - -### Arduino Uno R4 -port by Thomas Combriat - -Compiles and runs using Arduino's standard library (Renesas 0.8.7 at the time of this writing). - -- A few particularities: - - - Because this board has an on-board DAC (A0), but only one, STEREO is not implemented and Mozzi uses this pin. Usage of other pins using PWM for instance is not implemented yet. - - Two timers are claimed by Mozzi when using the on-board DAC, one when using `EXTERNAL_AUDIO_OUTPUT`. - - `mozziAnalogRead()` returns values in the Renesas' full ADC resolution of 0-16384 rather than AVR's 0-1023. *This might change in the near future for speed purposes.* +## Hardware compatibility and hardware specific notes + +While originating on the 8 bit AVR based "classic Arduino" boards, Mozzi supports a wide variety of different MCUs, these days, including: + - Classic AVR boards: Arduino Uno, Duemilanove, Nano, Nano 33, Pro Mini, Leonardo, Mega, many others, and countless clones, Teensy 1 and 2.x, ... + - Various Teensy 3.x boards: Teensy 3.0/3.1/3.2/3.4/3.5/LC + - Teensy 4.x boards: Teensy 4.0 and 4.1 are known to work, future boards stand good chances of working with Mozzi out of the box + - STM32-based boards: Confusingly, several popular Arduino cores exist for these MCUs - and Mozzi supports several. Refer to the documentation + linked below. Mozzi has been tested on the popule STM32F1 "blue pill" and STM32F4 "black pill" boards, but should support a very wide range + of further boards out of the box. + - SAMD21-based boards: Tested on the Arduino Circuitplayground M0, expect to work on others, too. + - ESP8266: Should run on the whole range of boards, including the minimal ESP-01 + - ESP32: Should run on the wohle range of boards + - Arduiono Giga/MBED: The Arduino Giga, and - in untested theory - the Arduion Portenta + - RP2040: The Raspberry Pi Pico and other boards using RP2040 + - Arduino Uno R4: The new Uno, based on a 32-bit Renesas MCU + +Since the hardware capatibilies of these boards are vastly different, the ports, too, differ in their internals and their capabilities. Importantly, +of course, they also differ in what pin(s) are used for audio output by default. Refer to the [Hardware Section of the Documentation](https://sensorium.github.io/Mozzi/doc/html/group__hardware.html) for the details +applicable to your hardware. *** diff --git a/Readme_Mozzi_2_0.md b/Readme_Mozzi_2_0.md new file mode 100644 index 000000000..a4907f90a --- /dev/null +++ b/Readme_Mozzi_2_0.md @@ -0,0 +1,43 @@ +Porting to Mozzi 2.0 + +// TODO: properly type up + + +changed config names and semantics TODO (incomplete) + +audio modes mapping + + - STANDARD: MOZZI_OUTPUT_PWM with PWM_RATE == AUDIO_RATE + - STANDARD_PLUS: MOZZI_OUTPUT_PWM with PWM_RATE == 32768 + - HIFI: MOZZI_OUTPUT_2PIN_PWM + - EXTERNAL_AUDIO_OUTPUT (without BYPASS_MOZZI_BUFFER): MOZZI_OUTPUT_EXTERNAL_TIMED + - EXTERNAL_AUDIO_OUTPUT (with BYPASS_MOZZI_BUFFER): MOZZI_OUTPUT_EXTERNAL_CUSTOM + +further + - USE_AUDIO_INPUT: MOZZI_AUDIO_INPUT + - IS_STM32() -> IS_STM32MAPLE() + +simple renames: + - AUDIO_RATE: MOZZI_AUDIO_RATE + - CONTROL_RATE: MOZZI_CONTROL_RATE + +all new + - MOZZI_ANALOG_READS -> allows to disable, explicitly + - MOZZI_COMPATIBILITY_LEVEL + +general: + - Added many config sanity checks. Some may be too strict, if so please mention + + Other removed stuff: + - pauseMozzi() - was still declared but not defined -> not usable, anyway + - unpauseMozzi() - was still declared but not defined -> not usable, anyway + + + +Documentation bits that still need to find a new home (many other bits were moved around, many, many duplicates merged into a common place, and seom obsoleted bits discarded): + +Contrary to earlier versions of Mozzi, this version does not take over Timer 0, and thus Arduino +functions delay(), millis(), micros() and delayMicroseconds() remain usable in theory. That said, +you should avoid these functions, as they are slow (or even blocking). For measuring time, refer +to mozziMircos(). For delaying events, you can use Mozzi's EventDelay() unit instead +(not to be confused with AudioDelay()). diff --git a/config/config_example_avr_hifi.h b/config/config_example_avr_hifi.h new file mode 100644 index 000000000..1d02316c5 --- /dev/null +++ b/config/config_example_avr_hifi.h @@ -0,0 +1,19 @@ +/* Configuration example + +This example is targetted at the AVR platform (Arduino Uno & friends), only! + +Set configuration options according to the mode that was formerly known as "HIFI". +Do read up on the required hardware circuitry! */ + +#include "MozziConfigValues.h" // for named option values + +#define MOZZI_AUDIO_MODE MOZZI_OUTPUT_2PIN_PWM +//#define MOZZI_AUDIO_RATE 32768 // the default, in this mode +//#define MOZZI_PWM_RATE 125000 // the default, in this mode +//#define MOZZI_AUDIO_BITS_PER_CHANNEL 2 // the default, in this mode + +// should you wish to customize the output pins: +//#define AUDIO_AUDIO_PIN_1 TIMER1_A_PIN +//#define MOZZI_AUDIO_PIN_1_REGISTER OCR1A // must also specify the hardware register responsible for this pin +//#define AUDIO_AUDIO_PIN_1_LOW TIMER1_B_PIN +//#define MOZZI_AUDIO_PIN_1_LOW_REGISTER OCR1B // must also specify the hardware register responsible for this pin diff --git a/config/config_example_avr_legacy_standard_mode.h b/config/config_example_avr_legacy_standard_mode.h new file mode 100644 index 000000000..87efbe3a6 --- /dev/null +++ b/config/config_example_avr_legacy_standard_mode.h @@ -0,0 +1,15 @@ +/* Configuration example + +This example is targetted at the AVR platform (Arduino Uno & friends), only! + +Set configuration options according to the mode that was formerly known as "STANDARD" (not STANDARD_PLUS). */ + +#include "MozziConfigValues.h" // for named option values + +#define MOZZI_AUDIO_MODE MOZZI_OUTPUT_MODE_PWM +#define MOZZI_AUDIO_RATE 16384 +#define MOZZI_PWM_RATE 16384 + +// should you wish to customize the output pin: +//#define AUDIO_AUDIO_PIN_1 TIMER1_A_PIN +//#define MOZZI_AUDIO_PIN_1_REGISTER OCR1A // must also specify the hardware register responsible for this pin diff --git a/config/config_example_avr_legacy_standardplus_mode.h b/config/config_example_avr_legacy_standardplus_mode.h new file mode 100644 index 000000000..22acf93a4 --- /dev/null +++ b/config/config_example_avr_legacy_standardplus_mode.h @@ -0,0 +1,18 @@ +/* Configuration example + +This example is targetted at the AVR platform (Arduino Uno & friends), only! + +The configuration formerly known as STANDARD_PLUS is still the default on AVR, so you +do not need to configure anything! This file just lists the relevant settings involved: */ + +#include "MozziConfigValues.h" // for named option values + +// all of these are the defaults on AVR, anyway, thus commented +//#define MOZZI_AUDIO_MODE MOZZI_OUTPUT_MODE_PWM +//#define MOZZI_AUDIO_RATE 16384 +//#define MOZZI_PWM_RATE 32768 + + +// should you wish to customize the output pin: +//#define AUDIO_AUDIO_PIN_1 TIMER1_A_PIN +//#define MOZZI_AUDIO_PIN_1_REGISTER OCR1A // must also specify the hardware register responsible for this pin diff --git a/config/config_example_avr_stereo.h b/config/config_example_avr_stereo.h new file mode 100644 index 000000000..57d402d20 --- /dev/null +++ b/config/config_example_avr_stereo.h @@ -0,0 +1,15 @@ +/* Configuration example + +This example is targetted at the AVR platform (Arduino Uno & friends), only! + +This example shows setting up stereo mode on AVR. */ + +#include "MozziConfigValues.h" // for named option values + +#define MOZZI_AUDIO_CHANNELS MOZZI_STEREO + +// should you wish to customize the output pins: +//#define AUDIO_AUDIO_PIN_1 TIMER1_A_PIN +//#define MOZZI_AUDIO_PIN_1_REGISTER OCR1A // must also specify the hardware register responsible for this pin +//#define AUDIO_AUDIO_PIN_2 TIMER1_B_PIN +//#define MOZZI_AUDIO_PIN_2_REGISTER OCR1B // must also specify the hardware register responsible for this pin diff --git a/config/config_example_external.h b/config/config_example_external.h new file mode 100644 index 000000000..2017f61bf --- /dev/null +++ b/config/config_example_external.h @@ -0,0 +1,15 @@ +/* Configuration example + +Configure Mozzi for "external" audio output. You will need to provide an audioOutput() function in your sketch. + +See TODO: link to relevant tutorial +*/ + +#include "MozziConfigValues.h" // for named option values + +#define MOZZI_AUDIO_MODE MOZZI_OUTPUT_EXTERNAL_TIMED +// or use this: +//#define MOZZI_AUDIO_MODE MOZZI_OUTPUT_EXTERNAL_CUSTOM + +//#define MOZZI_AUDIO_RATE 32768 // the default, in this mode +//#define MOZZI_AUDIO_BITS 16 // the default in this mode, but e.g. when connecting to a 24-bit DAC, you'd set 24, here. diff --git a/config/config_example_rp2040_i2s_pt8211_mono.h b/config/config_example_rp2040_i2s_pt8211_mono.h new file mode 100644 index 000000000..ed1611ae7 --- /dev/null +++ b/config/config_example_rp2040_i2s_pt8211_mono.h @@ -0,0 +1,18 @@ +/* Configuration example + +This example is targetted at the RP2040 (raspberry Pi pico) platform only! + +Configure the Raspberry Pico to output sound in mono on a I2S DAC on LSBJ format (like the PT8211). */ + + +#include "MozziConfigValues.h" // for named option values + +#define MOZZI_AUDIO_MODE MOZZI_OUTPUT_I2S_DAC +#define MOZZI_I2S_FORMAT MOZZI_I2S_FORMAT_LSBJ // PT8211 is on LSBJ format + +// all of these are the defaults on RP2040 outputting on I2S, anyway, thus commented +#define MOZZI_AUDIO_BITS 16 +#define MOZZI_I2S_PIN_BCK 20 +#define MOZZI_I2S_PIN_WS (MOZZI_I2S_PIN_BCK+1) // CANNOT BE CHANGED, HAS TO BE NEXT TO pBCLK, i.e. default is 21 +#define MOZZI_I2S_PIN_DATA 22 + diff --git a/config/config_example_rp2040_pwm.h b/config/config_example_rp2040_pwm.h new file mode 100644 index 000000000..9f94b56e2 --- /dev/null +++ b/config/config_example_rp2040_pwm.h @@ -0,0 +1,16 @@ +/* Configuration example + +This example is targetted at the RP2040 (raspberry Pi pico) platform only! + +The configuration formerly known as STANDARD_PLUS is still the default on RP2040, so you +do not need to configure anything! This file just lists the relevant settings involved: */ + +#include "MozziConfigValues.h" // for named option values + +// all of these are the defaults on RP2040, anyway, thus commented +//#define MOZZI_AUDIO_MODE MOZZI_OUTPUT_MODE_PWM +//#define MOZZI_AUDIO_RATE 32768 + + +// should you wish to customize the output pin: +//#define AUDIO_AUDIO_PIN_1 0 diff --git a/config/mozzi_config_documentation.h b/config/mozzi_config_documentation.h new file mode 100644 index 000000000..b9c0eb538 --- /dev/null +++ b/config/mozzi_config_documentation.h @@ -0,0 +1,258 @@ +/*! @defgroup config Mozzi Configuration options + * @section Mozzi Configuration + * + * @brief Mozzi Configuration + * + * @note + * It is generally safe to leave the Mozzi Configuration unchanged, and that's very much recommended _until_ you have a very specific need to customize something. + * Contrary to past versions of Mozzi, all configuration options have a (usually sensible) default value, so you do not have to configure _anything_, unless you + * actually want to change something. + * + * Configuring Mozzi is mostly done using various preprocessor definitions. This approach is used to move as much of the processing involved to compile time, in order + * to save Flash, RAM, and CPU use at runtime. This section lists various global options, but in addition, most ports allow additional hardware dependent + * configuration options. See @ref hardware. + * + * Several configuration examples are provided in the "config" folder of the Mozzi sources. You may want to look at these, first. + * + * TODO: Fix and complete Doxygen coverage +*/ + +/** @ingroup config + * @def MOZZI_COMPATIBILITY_LEVEL + * + * Mozzi generally tries to keep your old sketches working, but we continue to find new (and hopefully better) approaches to old problems. + * Sometimes, keeping API compatibilty with the pre-existing solution may come with a smaller or larger penalty in terms of performance or code size. + * Therefore - if your sketch supports it - you may be able to get some minor benefit from disabling compatibility code. + * + * Currently supported values are: + * - MOZZI_COMPATIBILITY_1_1 - try to support sketches written for Mozzi version 1.1 (or possibly lower); this is the default when including MozziGuts.h + * - MOZZI_COMPATIBILITY_2_0 - try to support sketches written for Mozzi version 2.0; this is - currently - the default when including Mozzi.h + * - MOZZI_COMPATIBILITY_LATEST - always live on the bleeding edge + * + * @note + * MOZZI_COMPATIBILITY_V1_1 does not guarantee, that *everything* from Mozzi 1.1 will continue to work, just that we're doing a reasonable effort. +*/ + +/** @ingroup config + * @def MOZZI_AUDIO_MODE + * + * @brief Configure how Mozzi outputs generated sounds. + * + * @note + * Not all options are available on all platforms, and several options require specific wiring or external components to function on top of this! + * When customizing this, it is highly recommended to start experimenting with a simple and known-to-work sketch (such as a basic sinewave) to verify that your + * hardware setup is correct. Similarly, if you observe problems running your "real" sketch, it is often a good idea ot test your sketch with the default audio mode, + * too (by leaving this option, and preferrably all others, unset). + * + * Refer to the @ref hardware specific documentation for which modes are supported on your hardware, and further details! + * + * Supported values: + * - MOZZI_OUTPUT_PWM Output using pulse width modulation (PWM) on a GPIO pin. This is the default on most platforms. + * On the Arduino Uno (more generally ATMEGA328P), this allows for a sample resolution of 488 (almost 9 bits) on pin 9. + * Usable pins and resolution will be different on other boards. + * - MOZZI_OUTPUT_2PIN_PWM Output using pulse width modulation on two GPIO pins, where one pin represents the lower bits, and the other the higer bits of the sample. + * On the Aduino Uno, this allows for 14 bits of resolution on pins 9 (low) and 10 (high). For further information (wiring etc.) see @ref hardware_avr_2pin. + * - MOZZI_OUTPUT_EXTERNAL_TIMED Output is not controlled by Mozzi itself, but left to the user sketch. This setting allows to completely customize the audio output, e.g. + * for connecting to external DACs. For more detail, see @ref external_audio + * - MOZZI_OUTPUT_EXTERNAL_CUSTOM As above, but additionally bypassing Mozzi's sample buffer. For more detail, see @ref external_audio + * - MOZZI_OUTPUT_PDM_VIA_I2S Output pulse density modulated (PDM) samples via a (hardware) I2S interface (without a DAC connected to it). + * - MOZZI_OUTPUT_PDM_VIA_SERIAL Output pulse density modulated (PDM) samples via a hardware serial interface. + * - MOZZI_OUTPUT_I2S_DAC Output samples to a PT8211 (or compatible) DAC connected to a hardware I2S interface. + * - MOZZI_OUTPUT_INTERNAL_DAC Output to the interal DAC on boards that support one. + * + * TODO: Adding an R2R-DAC option would be cool, http://blog.makezine.com/2008/05/29/makeit-protodac-shield-fo/ , some discussion on Mozzi-users. +*/ + + +/** @ingroup config + * @def MOZZI_AUDIO_CHANNELS + * + * This sets allows to change from a single/mono audio output channel to + * stereo output. To actually generate two channels, your updateAudio()-function + * should return a StereoOutput(). Sketches returning a MonoOutput() in a stereo + * config, or vice versa will continue to work, but will generate a warning a + * compile time. + * + * @note This option superseeds the earlier STEREO_HACK, which is still available at + * the time of this writing, but should not be used in new sketches. + * + * @note At the time of this writing, only MOZZI_MONO and MOZZI_STEREO are supported. The value of + * MOZZI_MONO is 1 and the value of MOZZI_STEREO is 2, so future extensions are also expected + * to set this to the number of available channels, and it's ok to use numerical comparison. */ + + +/** @ingroup config + * @def MOZZI_AUDIO_RATE + * + * @brief Defines the audio rate, i.e. rate of samples output per second. + * + * The default rate on the classis Arduino Uno is 16384 Hz, but can be increased to 32768 Hz, subject to the caveats, detailed below. For most other platforms 32678 Hz + * is the default, but even higher rates may be supported. + *. + * Increasing the rate allows for better frequency response, but generally all affects achievable sample bitdepth (especially from PWM output). + * Also, of course, doubling the sample rate also halves the amount of time available to calculate the each sample, so it + * may only be useful for relatively simple sketches. The increased frequency response can also make + * unwanted artefacts of low resolution synthesis calculations more apparent, so it's not always a bonus. + * + * It is highly recommended to keep the audio rate a power of two (16384, 32678, 64536, etc.), as some internal calculations can be highly be optimised for speed, this way. + * + * @note + * For compatibility reasons, the option AUDIO_RATE is automatically set to the same value as this option, and you will find some uses of that in old (pre Mozzi 2.0) code examples. + * It is advised to use only MOZZI_AUDIO_RATE in new code, however. + * TODO: Only do the above, if MozziGuts.h, rather than Mozzi.h was included? + */ + + + +/** @ingroup config + * @def MOZZI_CONTROL_RATE + * + * @brief Control rate setting. + * + * Mozzi's MOZZI_CONTROL_RATE sets how many times per second updateControl() is called. + * MOZZI_CONTROL_RATE has a default of 64 Hz. It is useful to have MOZZI_CONTROL_RATE set at a power of 2 (such as 64,128,256 etc), + * to have exact timing of audio and control operations. Non-power-of-2 MOZZI_CONTROL_RATE can cause glitches due to audio and control + * events not lining up precisely. If this happens a power of two MOZZI_CONTROL_RATE might solve it. + * + * Try to keep MOZZI_CONTROL_RATE low, for efficiency, though higher rates up to about 1000 + * can sometimes give smoother results, avoiding the need to interpolate + * sensitive variables at audio rate in updateAudio(). + * + * TODO: If a definition of CONTROL_RATE is detected, apply that with a warning. +*/ + + +/** @ingroup config + * @def MOZZI_ANALOG_READ + * + * Whether to compile in support for non-blocking analog reads. This is enabled by default on platforms that support it, but may be + * disabled, explicitly, to save resources, or in order to implement custom read schemes (e.g. with IO multiplexing). + * + * For simplicity, mozziAnalogRead() is always defined, but when MOZZI_ANALOG_READ s are disabled or unsupported, it simply relays + * to Arduino's regular analogRead(). It is thus quite recommended _not_ to depend on mozziAnalogRead() when disabling this. + * + * As a rough estimate (your numbers may differ a bit, depending on compiler version, etc.), on an ATMEGA328P (aka Arduino Uno), + * disabling analog reads saves 33 bytes of RAM and 340 bytes of FLASH. The performance savings are theorized to be neglegible, however. + * + * Currently allowed values are: + * - MOZZI_ANALOG_READ_NONE + * Disabled + * - MOZZI_ANALOG_READ_STANDARD + * Analog read implementation enabled (currently there is only the "standard" method, but future versions might allow additional choice, here). +*/ + + +/** @ingroup config + * @def MOZZI_AUDIO_INPUT + * + * Whether to enable built in audio input feature. This is not supported on all platforms, and + * on platforms that do support it may come with a considerable performance overhead. Don't enable, unless you need this. + * + * Currently allowed values are: + * - MOZZI_AUDIO_INPUT_NONE + * No audio input + * - MOZZI_AUDIO_INPUT_STANDARD + * Audio input enabled (currently there is only the "standard" method, but future versions might allow additional choice, here). + * This mode implies that MOZZI_ANALOG_READ s are enabled and supported. You may have to call setupFastAnalogReads(FASTEST_ADC) + * after setupMozzi(), when using this. + * + * Further reading and config: @ref getAudioInput() @ref MOZZI_AUDIO_INPUT_PIN +*/ + + +/** @ingroup config + * @def MOZZI_AUDIO_INPUT_PIN + * + * This sets which analog input channel to use for audio input, if you have enabled MOZZI_AUDIO_INPUT, above. + * Not all pins may be available for this, be sure to check the documentation for your platform. +*/ + +/** @ingroup config + * @def MOZZI_PWM_RATE + * + * Only for MOZZI_AUDIO_MODE s MOZZI_OUTPUT_PWM and MOZZI_OUTPUT_2PIN_PWM. On some platforms, the rate at which PWM signals are repeated may be higher + * than that at with audio signals are produced (i.e. MOZZI_AUDIO_RATE). E.g. for MOZZI_OUTPUT_PWM on the classic Arduino, the pwm defaults to 32768 while the + * audio rate defaults to 16384. The reasoning behind this is that 16384 Hz audio rate turned out to be te most useful compromise - in most casses - between + * output quality, and available computing power. However, output at that rate produced high-frequency whine, audible to some people, which could be mitigated + * by the higher PWM rate. + * + * In other words, increasing this improves the signal quality at less cost than doubling the audio rate itself. However, increasing this too far will limit the dynamic resolution of the samples that can be + * written to the output pin(s): 2 ^ (output bits) * MOZZI_PWM_RATE cannot be higher than the CPU frequency! +*/ + +/** @ingroup config + * @def MOZZI_AUDIO_BITS_PER_CHANNEL + * + * Only for MOZZI_AUDIO_MODE MOZZI_OUTPUT_2PIN_PWM. Sample resolution per channel to use in 2 pin output, given in bits (i.e. total resolution is twice as much). + * Defaults to 7 bits per channel. Note that increasing this requires very, very well matched output resistors. + * + * See @ref hardware_avr for a more detailed description. +*/ + +/** @ingroup config + * @def MOZZI_AUDIO_PIN_1 + * + * Only for MOZZI_AUDIO_MODE s MOZZI_OUTPUT_PWM and MOZZI_OUTPUT_2PIN_PWM: The IO pin to use as (first) audio output. This **must** be attached to Timer1. + * When settings this, you alsso need to specify the output compare register responsible for this pin (either OCR1A or OCR1B). + * + * Example: + * @code + * #define MOZZI_AUDIO_PIN_1 TIMER1_B_PIN + * #define MOZZI_AUDIO_PIN_1_REGISTER OCR1B // must also specify this, when customizing MOZZI_AUDIO_PIN_1 + * @endcode + * + * Equivalent definitions can be used to control the pin for the right audio channel (in stereo mode), or the low byte channel (in 2 Pin PWM mode): + * + * @code + * #define MOZZI_AUDIO_PIN_2 [...] + * #define MOZZI_AUDIO_PIN_2_REGISTER [the matching OCR] + * // or + * #define MOZZI_AUDIO_PIN_1_LOW [...] + * #define MOZZI_AUDIO_PIN_1_LOW_REGISTER [the matching OCR] + * @endcode + * + * @see config/known_16bit_timers.h + * */ + + +/***************************************** ADVANCED SETTTINGS -- External audio output ****************************************** + * + * The settings in the following section applies to MOZZI_OUTPUT_EXTERNAL_TIMED, and MOZZI_OUTPUT_EXTERNAL_CUSTOM, only. + * +********************************************************************************************************************************/ + +/** @ingroup hardware + * @page external_audio External audio output + * + * Only for MOZZI_AUDIO_MODE s MOZZI_OUTPUT_EXTERNAL_TIMED and MOZZI_OUTPUT_EXTERNAL_CUSTOM. Most (all?) platforms support + * output using an "external" function. When using this option, you will need to provide a suitable definition for audioOutput() in + * your own sketch, yourself. Some understanding of the general Mozzi audio output architecture may be recommendable, when using this + * mode: See @ref AudioOutput . + * + * In the more simple case (MOZZI_OUTPUT_EXTERNAL_TIMED), Mozzi will still take care of buffering the samples, and calling this function + * at audio rate (hence "timed"). This generally involves use of a timer, which should be detailed in the @ref hardware details for + * your platform. + * + * Should you desire even more control - perhaps because your board, or your external DAC already comes with a rate controlled DMA buffer - + * using MOZZI_OUTPUT_EXTERNAL_CUSTOM also bypasses Mozzis sample buffer. In addition to audioOutput(), you will then need to provide + * a definition for canBufferAudioOutput(), which will control the rate at which samples are produced. In essence, whenever + * canBufferAudioOutput() returns true, Mozzi will call updateAudio(), and pass the produced sample to audioOutput(), unbuffered. It is + * entirely your job to make sure that this actually happens at MOZZI_AUDIO_RATE, and / or an appropriate buffer gets used. + * + * One additional configuration setting is MOZZI_AUDIO_BITS, which defaults to 16 bits for this mode, but might be set higher, if your + * hardware supports it. +*/ + + +/** @ingroup config + * @def MOZZI_AUDIO_BITS + * + * Output resolution of audio samples. In most cases you should leave this value untouched (for the defaults that get applied, see @ref hardware . + * However, for MOZZI_AUDIO_MODE s MOZZI_OUTPUT_EXTERNAL_TIMED and MOZZI_OUTPUT_EXTERNAL_CUSTOM you way wish to customize the default value + * of 16 bits. + * + * @note + * At the time of this writng single audio samples are stored as "int", unconditionally. This limits MOZZI_AUDIO_BITS to a maximum of 16 bits on + * some 8 bit boards! + */ + diff --git a/hardware_defines.h b/hardware_defines.h index e663939c1..b93fc34e3 100644 --- a/hardware_defines.h +++ b/hardware_defines.h @@ -50,9 +50,9 @@ // STM32 boards (libmaple based) // https://github.com/stevstrong/Arduino_STM32 #if (defined(__arm__) && !IS_TEENSY3() && !IS_TEENSY4() && __has_include("libmaple/libmaple.h")) -#define IS_STM32() 1 +#define IS_STM32MAPLE() 1 #else -#define IS_STM32() 0 +#define IS_STM32MAPLE() 0 #endif // Mbed OS based boards @@ -76,7 +76,7 @@ #define IS_RENESAS() 0 #endif -#if (defined(__arm__) && !IS_STM32() && !IS_TEENSY3() && !IS_TEENSY4() && !IS_RP2040() && !IS_SAMD21() && !IS_MBED() && !IS_RENESAS()) +#if (defined(__arm__) && !IS_STM32MAPLE() && !IS_TEENSY3() && !IS_TEENSY4() && !IS_RP2040() && !IS_SAMD21() && !IS_MBED() && !IS_RENESAS()) #define IS_STM32DUINO() 1 #else #define IS_STM32DUINO() 0 @@ -94,32 +94,25 @@ #define IS_ESP32() 0 #endif -#if !(IS_AVR() || IS_TEENSY3() || IS_TEENSY4() || IS_STM32() || 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()) +// 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. #endif // Hardware detail defines -#if IS_STM32() +#if IS_STM32MAPLE() #define NUM_ANALOG_INPUTS 16 // probably wrong, but mostly needed to allocate an array of readings #elif IS_ESP8266() #define NUM_ANALOG_INPUTS 1 #endif -#if IS_AVR() -#define AUDIO_RATE_PLATFORM_DEFAULT 16384 -#else -#define AUDIO_RATE_PLATFORM_DEFAULT 32768 -#endif - -#if IS_ESP8266() -#define CACHED_FUNCTION_ATTR ICACHE_RAM_ATTR -#elif IS_ESP32() +#if IS_ESP8266() || IS_ESP32() #define CACHED_FUNCTION_ATTR IRAM_ATTR #else #define CACHED_FUNCTION_ATTR #endif -#if IS_STM32() +#if IS_STM32MAPLE() // This is a little silly, but with Arduino 1.8.13, including this header inside MozziGuts.cpp does not work (fails to detect the proper include path). // Putting it here, instead, seem to work. #include diff --git a/internal/Readme.md b/internal/Readme.md new file mode 100644 index 000000000..95448668c --- /dev/null +++ b/internal/Readme.md @@ -0,0 +1,4 @@ +# About this directory + +These files are part of the Mozzi project, but not meant for direct inclusion by the user. Functions and macros in here may be subject to change without notice. + diff --git a/internal/config_checks_avr.h b/internal/config_checks_avr.h new file mode 100644 index 000000000..70163c880 --- /dev/null +++ b/internal/config_checks_avr.h @@ -0,0 +1,160 @@ +/** For Mozzi-internal use: Apply hardware specific config defaults and config checks: AVR */ + +/** @ingroup hardware + * @page hardware_avr Mozzi on classic Arduino, Teensy 2.x, Arduino Mega, and other 8 bit "AVR"/ATMEGA architecture boards + * + * @section avr_status Port status and notes + * This is the original "port" of Mozzi, and thus very elaborate. The main challenges on this platform, compared to other MCUs, are limitations in + * flash, RAM, and CPU power. + * + * @section avr_output Output modes + * The following audio modes (see @ref MOZZI_AUDIO_MODE) are currently supported on this hardware. In all cases, Timer 1 is claimed, and + * is not available for any other purpose: + * - MOZZI_OUTPUT_PWM + * - MOZZI_OUTPUT_2PIN_PWM + * - MOZZI_OUTPUT_EXTERNAL_TIMED + * - MOZZI_OUTPUT_EXTERNAL_CUSTOM + * + * In all cases, except MOZZI_OUTPUT_EXTERNAL_CUSTOM, Timer 1 is claimed, and is not available for any other purpose. + * + * @section avr_pwm MOZZI_OUTPUT_PWM + * For MOZZI_OUTPUT_PWM, output is restricted to pins that are hardware-attached to Timer 1, but can be configured + * within this tight limit. In particular, the default setup on the + * Arduino UNO is: + * - Mono: Pin 9 -> configurable using MOZZI_AUDIO_PIN_1 + * - Stereo: Pin 9 (left) and Pin 10 (right) -> configurable using MOZZI_AUDIO_PIN_1 and MOZZI_AUDIO_PIN_2 + * For pinouts on other boards, refer to config/known_16bit_timers. + * + * Rate of the PWM output can be controlled separately from MOZZI_AUDIO_RATE: MOZZI_PWM_RATE. + * + * The available sample resolution is 488, i.e. almost 9 bits, providing some headroom above the 8 bit table resolution + * currently used by the oscillators. You can look at the TimerOne library for more info about how interrupt rate and pwm resolution relate. + * + * @section avr_2pin_pwm MOZZI_OUTPUT_2PIN_PWM + * In this mode, output is split across two pins (again, both connected to Timer 1), with each outputting 7 bits for a total of 14. This allows for + * much better dynamic range, providing much more definition than can be achieved using @ref avr_pwm , while using + * only modestly more processing power. Further, it allows for a much higher PWM carrier rate can be much higher (125 kHz by default; see @ref + * MOZZI_PWM_RATE), which is well beyond the audible range. + * + * On the downside, this mode requires an extra hardware timer (Timer2 in addition to Timer1), which may increase compatibility + * problems with other Arduino libraries that also require a timer. Further, a more elaborate hardware setup is needed, making it less convenient + * for rapid prototyping: + * + * TODO: Move this bit to a dedicated page on output circuits? + * In hardware, add the two signals through a 3.9k resistor on the "high" pin and 499k resistor on "low" pin. + * Use 0.5% resistors or select the most accurate from a batch. It is further recommended to place a 4.7nF capacitor between the summing junction + * of the resistors and ground. This is discussed in much more detail on http://www.openmusiclabs.com/learning/digital/pwm-dac/dual-pwm-circuits/ + * Also, there are higher quality output circuits are on the site. + * + * On the classic Arduino Uno, the default pinout in this mode is: + * - Pin 9 (high bits) and Pin 10 (low bits) -> configurable using MOZZI_AUDIO_PIN_1 and MOZZI_AUDIO_PIN_1_LOW + * + * Here is table of the default pins on some other boards. Rows with an x on it have actually been tested (and importantly, the outcome recoreded; + * input welcome on further boards.) + * + * resistor.....3.9k......499k \n + * x................9..........10...............Arduino Uno \n + * x................9..........10...............Arduino Duemilanove \n + * x................9..........10...............Arduino Nano \n + * x................9..........10...............Arduino Leonardo \n + * x................9..........10...............Ardweeny \n + * x................9..........10...............Boarduino \n + * x...............11.........12...............Freetronics EtherMega \n + * .................11.........12...............Arduino Mega \n + * .................14.........15...............Teensy \n + * .............B5(14)...B6(15)...........Teensy2 \n + * x...........B5(25)...B6(26)...........Teensy2++ \n + * .................13.........12...............Sanguino \n + * + * For pinouts on other AVR boards, config/known_16bit_timers might contain some hints. + * + * Rate of the PWM output can be controlled separately from @ref MOZZI_AUDIO_RATE, and is much higher (125kHz), by default: @ref MOZZI_PWM_RATE. + * + * The default sample resolution is 7 bits per pin, for a total of 14 bits. This can be configured within hardware limits (@ref MOZZI_PWM_RATE) using + * @ref MOZZI_AUDIO_BITS_PER_CHANNEL, but beware that increasing this will require even more accuracy in our output resistors (see the linked documentation, above). + * + * @section avr_external MOZZI_OUTPUT_EXTERNAL_TIMED and MOZZI_OUTPUT_EXTERNAL_CUSTOM + * See @ref external_audio +*/ + + +#if not defined(MOZZI_AUDIO_MODE) +#define MOZZI_AUDIO_MODE MOZZI_OUTPUT_PWM +#endif + +#if not defined(MOZZI_AUDIO_RATE) +#define MOZZI_AUDIO_RATE 16384 +#endif + +#if not defined(MOZZI_ANALOG_READ) +#define MOZZI_ANALOG_READ MOZZI_ANALOG_READ_STANDARD +#endif + +// Pins for regular PWM output +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM) +# if !defined(MOZZI_AUDIO_PIN_1) +#define MOZZI_AUDIO_PIN_1 TIMER1_A_PIN +#define MOZZI_AUDIO_PIN_1_REGISTER OCR1A +# endif +# if (MOZZI_AUDIO_CHANNELS > 1) && !defined(MOZZI_AUDIO_PIN_2) +#define MOZZI_AUDIO_PIN_2 TIMER1_B_PIN +#define MOZZI_AUDIO_PIN_2_REGISTER OCR1B +# endif + +# if !defined(MOZZI_PWM_RATE) +#define MOZZI_PWM_RATE 32768 +# endif + +#define MOZZI_AUDIO_BITS 8 +#define MOZZI_AUDIO_BITS_OPTIMISTIC 9 +#define MOZZI_AUDIO_BIAS ((uint8_t) 244) +#endif + +// Pins for 2 pin HIFI PWM output +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM) +# if !defined(MOZZI_AUDIO_PIN_1) +#define MOZZI_AUDIO_PIN_1 TIMER1_A_PIN +#define MOZZI_AUDIO_PIN_1_REGISTER OCR1A +# endif +# if !defined(MOZZI_AUDIO_PIN_1_LOW) +#define MOZZI_AUDIO_PIN_1_LOW TIMER1_B_PIN +#define MOZZI_AUDIO_PIN_1_LOW_REGISTER OCR1B +# endif + +# if !defined(MOZZI_PWM_RATE) +#define MOZZI_PWM_RATE 125000 +# endif + +# if !defined(MOZZI_AUDIO_BITS_PER_CHANNEL) +#define MOZZI_AUDIO_BITS_PER_CHANNEL 7 +# endif + +#define MOZZI_AUDIO_BITS (2*MOZZI_AUDIO_BITS_PER_CHANNEL) +#endif + +// Step 2: Check +// NOTE: This step is not technically required, but a good idea in any port + +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPUT_EXTERNAL_CUSTOM) + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM) +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_CHANNELS, MOZZI_MONO, MOZZI_STEREO) +#endif +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM) +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_CHANNELS, MOZZI_MONO) +#endif + +/** should we enforce the following? +#if (MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM) || MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM)) +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_RATE, 16384, 32768) +#endif */ + +MOZZI_CHECK_SUPPORTED(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_NONE, MOZZI_ANALOG_READ_STANDARD) +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_INPUT, MOZZI_AUDIO_INPUT_NONE, MOZZI_AUDIO_INPUT_STANDARD) +#include "../config/known_16bit_timers.h" +#if defined(TIMER1_C_PIN) +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_PIN_1, TIMER1_A_PIN, TIMER1_B_PIN, TIMER1_C_PIN); +#else +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_PIN_1, TIMER1_A_PIN, TIMER1_B_PIN); +#endif + diff --git a/internal/config_checks_esp32.h b/internal/config_checks_esp32.h new file mode 100644 index 000000000..1d462385f --- /dev/null +++ b/internal/config_checks_esp32.h @@ -0,0 +1,133 @@ +#ifndef CONFIG_CHECK_ESP32_H +#define CONFIG_CHECK_ESP32_H + +/** @ingroup hardware + * @page hardware_esp32 Mozzi on ESP32-based boards. + * + * port by Dieter Vandoren and Thomas Friedrichsmeier + * + * @section esp32_status Port status and notes + * - Since flash memory is not built into the ESP32, but connected, externally, it is much too slow for keeping wave tables, audio samples, etc. Instead, these are kept in RAM on this platform. + * - Asynchronous analog reads are not implemented. `mozziAnalogRead()` relays to `analogRead()`. @ref MOZZI_AUDIO_INPUT is not implemented + * - twi_nonblock is not ported + * - WIFI-activity not yet tested, but likely the same notes as for ESP8266 apply, i.e. @em any Wifi activity is likely to introdcue considerable nose. Consider turning off WIFI. + * - The implementation of audioTicks() may be slightly inaccurate on this platform. + * + * @section esp32_output Output modes + * The following audio modes (see @ref MOZZI_AUDIO_MODE) are currently supported on this hardware: + * - MOZZI_OUTPUT_EXTERNAL_TIMED + * - MOZZI_OUTPUT_EXTERNAL_CUSTOM + * - MOZZI_OUTPUT_PDM_VIA_I2S + * - MOZZI_OUTPUT_I2S_DAC + * - MOZZI_OUTPUT_INTERNAL_DAC + * + * The default mode is @ref esp32_internal_dac . + * + * @section esp32_internal_dac MOZZI_OUTPUT_INTERNAL_DAC + * The internal DAC has 8 bit resolution, and outputs to GPIO pins 25 and 26 (non-configurable). For simplicity of code, both pins are always used. + * In a mono configuration, both pins output the same sample. + * + * TODO: We could really use this to hack in a 2 PIN mode! + * + * @note + * The number 25 refers to "GPIO 25" or sometimes labelled "D25". Confusingly, many boards come with an additional, totally different numbering scheme on top of that. + * Check a detailed pinout diagram, if in any doubt. + * + * Internally, the inbuilt DAC is connected via an I2S interface. Which interface number to use can be configured using: + * + * @code + * #define MOZZI_I2S_PORT ... // (default: I2S_NUM_0) + * @endcode + * + * @section esp32_i2s_dac MOZZI_OUTPUT_I2S_DAC + * This mode outputs to a PT8211 (or compatible) I2S DAC, which allows for very high quality (mono or stereo) output. Communication needs the BCK, WS, and DATA(out) pins + * of one I2S interface. Presumably, any pins qualify, and you can configure this using: + * @code + * #define MOZZI_I2S_PIN_BCK ... // (default: 26) + * #define MOZZI_I2S_PIN_WS ... // (default: 15) + * #define MOZZI_I2S_PIN_DATA ... // (default: 33) + * #define MOZZI_I2S_PORT ... // (default: I2S_NUM_0) + * @endcode + * + * See the note above (@ref esp_internal_dac) regarding pin numbering. Also, please always test the default pinout, should a custom setting fail! + * + * As a technical note, I2S support in the ESP32 SDK has been reworked since this was implemented in Mozzi, and Mozzi uses the "legacy" implementation "i2s.h". + * This should not be an issue, unless you want to connect additional I2S components, yourself. In which case contributions are certainly welcome! + * + * @section esp32_pdm_via_i2s MOZZI_OUTPUT_PDM_VIA_I2S + * This mode uses the same setup as @ref esp32_i2s_dac, but rather than using an external DAC, the communication signal itself is modulated in PDM + * (pulse density modulation) encoded form. Thus not extra hardware is needed, and the signal is output on the DATA pin (see above). The BCK and + * WS pins are also claimed, but should be left non-connected, and do not produce anything meaningful. This can only be used in mono mode. + * + * Output resolution may be adjusted by defining MOZZI_PDM_RESOLUTION , where the default value of 4 means that each audio sample is encoded into four 32 bit blocks + * of ones and zeros. Obviously, more is potentially better, but at the cost of considerable computation power. + * + * @section esp32_external MOZZI_OUTPUT_EXTERNAL_TIMED and MOZZI_OUTPUT_EXTERNAL_CUSTOM + * See @ref external_audio +*/ + +#if not IS_ESP32() +#error This header should be included for ESP32 architecture, only +#endif + +#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) + +#if !defined(MOZZI_AUDIO_RATE) +#define MOZZI_AUDIO_RATE 32768 +#endif + +#if defined(MOZZI_PWM_RATE) +#error Configuration of MOZZI_PWM_RATE is not currently supported on this platform (always same as AUDIO_RATE) +#endif + +#if !defined(MOZZI_ANALOG_READ) +# define MOZZI_ANALOG_READ MOZZI_ANALOG_READ_NONE +#endif + +MOZZI_CHECK_SUPPORTED(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_NONE) +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_INPUT, MOZZI_AUDIO_INPUT_NONE) + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC, MOZZI_OUTPUT_PDM_VIA_I2S) +# if !defined(MOZZI_I2S_PIN_BCK) +# define MOZZI_I2S_PIN_BCK 26 +# endif +# if !defined(MOZZI_I2S_PIN_WS) +# define MOZZI_I2S_PIN_WS 25 +# endif +# if !defined(MOZZI_I2S_PIN_DATA) +# define MOZZI_I2S_PIN_DATA 33 +# endif +#endif + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC, MOZZI_OUTPUT_I2S_DAC, MOZZI_OUTPUT_PDM_VIA_I2S) +# include +# if !defined(MOZZI_IS2_PORT) +# define MOZZI_I2S_PORT I2S_NUM_0 +# endif +#endif + +#if !defined(MOZZI_AUDIO_BITS) +# if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) +# define MOZZI_AUDIO_BITS 8 +# else +# define MOZZI_AUDIO_BITS 16 +# endif +#endif + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S) +# if !defined(MOZZI_PDM_RESOLUTION) +# define MOZZI_PDM_RESOLUTION 8 +# endif +#else +# 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) +# define BYPASS_MOZZI_OUTPUT_BUFFER true +#endif + +#endif // #ifndef CONFIG_CHECK_ESP32_H diff --git a/internal/config_checks_esp8266.h b/internal/config_checks_esp8266.h new file mode 100644 index 000000000..b4ed95087 --- /dev/null +++ b/internal/config_checks_esp8266.h @@ -0,0 +1,106 @@ +#ifndef CONFIG_CHECK_ESP8266_H +#define CONFIG_CHECK_ESP8266_H + +/** @ingroup hardware + * @page hardware_esp8266 Mozzi on ESP32-based boards. + * + * port by Thomas Friedrichsmeier + * + * @section esp8266_status Port status and notes + * - Since flash memory is not built into the ESP8266, but connected, externally, it is much too slow for keeping wave tables, audio samples, etc. Instead, these are kept in RAM on this platform. + * - Asynchronous analog reads are not implemented. `mozziAnalogRead()` relays to `analogRead()`. @ref MOZZI_AUDIO_INPUT is not available + * - twi_nonblock is not ported + * - Note that the ESP8266 pins can output less current than the other supported CPUs. The maximum is 12mA, with a recommendation to stay below 6mA. + * - WHEN CONNECTING A HEADPHONE, DIRECTLY, USE APPROPRIATE CURRENT LIMITING RESISTORS (>= 500Ohms). + * - _Any_ WiFi-activity can cause severe spikes in power consumption. This can cause audible "ticking" artifacts, long before any other symptoms. + * - If you do not require WiFi in your sketch, you should turn it off, _explicitly_, using @code WiFi.mode(WIFI_OFF) @endcode. + * - A juicy enough, well regulated power supply, and a stabilizing capacitor between VCC and Gnd can help a lot. + * - As the (PDM) output signal is digital, a single (fast!) transistor can be used to amplify it to an independent voltage level. + * - audioHook() calls `yield()` once for every audio sample generated. Thus, as long as your audio output buffer does not run empty, you should not need any additional `yield()`s inside `loop()`. + * + * @section esp8266_output Output modes + * The following audio modes (see @ref MOZZI_AUDIO_MODE) are currently supported on this hardware: + * - MOZZI_OUTPUT_EXTERNAL_TIMED + * - MOZZI_OUTPUT_EXTERNAL_CUSTOM + * - MOZZI_OUTPUT_PDM_VIA_I2S + * - MOZZI_OUTPUT_PDM_VIA_SERIAL + * - MOZZI_OUTPUT_I2S_DAC + * + * The default mode is @ref esp8266_pdm_via_serial . + * + * @note + * This port really does not currently come with a PWM mode! + * + * @section esp8266_pdm_via_serial MOZZI_OUTPUT_PDM_VIA_SERIAL + * Output is coded using pulse density modulation, and sent via GPIO2 (Serial1 TX). + * - This output mode uses timer1 for queuing audio sample, so that timer is not available for other uses. + * - Note that this mode has slightly lower effective analog output range than @ref esp8266_pdm_via_i2s, due to start/stop bits being added to the output stream. + * - Supports mono output, only, pins not configurable. + * + * The option @ref MOZZI_PDM_RESOLTUON (default value 2, corresponding to 64 ones and zeros per audio sample) can be used to adjust the output resolution. + * Obviously higher values demand more computation power. + * + * @section esp8266_pdm_via_serial MOZZI_OUTPUT_PDM_VIA_I2S + * Output is coded using pulse density modulation, and sent via the I2S pins. The I2S data out pin (GPIO3, which is also "RX") will have the output, + * but *all* I2S output pins (RX, GPIO2 and GPIO15) will be affected. Mozzi tries to set GPIO2 and GPIO15 to input mode, and *at the time of this writing*, this allows + * I2S output on RX even on boards such as the ESP01 (where GPIO15 is tied to Ground). However, it seems safest to assume that this mode may not be useable on boards where + * GPIO2 or GPIO15 are not available as output pins. + * + * Supports mono output, only, pins not configurable. + * + * Resolution may be controlled using MOZZI_PDM_RESOLUTION (see above; default value is 2). + * + * @section esp8266_i2s_dac MOZZI_OUTPUT_I2S_DAC + * Output is sent to an external DAC (such as a PT8211), digitally coded. This is the only mode that supports stereo (@ref MOZZI_AUDIO_CHANNELS). It also needs the least processing power. + * The pins cannot be configured (GPIO3/RX, GPIO2, and GPIO15). + * + * @section esp8266_external MOZZI_OUTPUT_EXTERNAL_TIMED and MOZZI_OUTPUT_EXTERNAL_CUSTOM + * See @ref external_audio +*/ + +#if not IS_ESP8266() +#error This header should be included for ESP architecture, only +#endif + +#if !defined(MOZZI_AUDIO_MODE) +#define MOZZI_AUDIO_MODE MOZZI_OUTPUT_PDM_VIA_SERIAL +#endif +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPUT_EXTERNAL_CUSTOM, MOZZI_OUTPUT_PDM_VIA_I2S, MOZZI_OUTPUT_PDM_VIA_SERIAL, MOZZI_OUTPUT_I2S_DAC) + +#if !defined(MOZZI_AUDIO_RATE) +#define MOZZI_AUDIO_RATE 32768 +#endif + +#if !defined(MOZZI_AUDIO_BITS) +# define MOZZI_AUDIO_BITS 16 +#endif + +#if !defined(MOZZI_ANALOG_READ) +# define MOZZI_ANALOG_READ MOZZI_ANALOG_READ_NONE +#endif + +MOZZI_CHECK_SUPPORTED(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_NONE) +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_INPUT, MOZZI_AUDIO_INPUT_NONE) + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S, MOZZI_OUTPUT_PDM_VIA_SERIAL) +# if !defined(PDM_RESOLUTION) +# define MOZZI_PDM_RESOLUTION 2 +# endif +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_CHANNELS, 1) +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_BITS, 16) +#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC) +# define MOZZI_PDM_RESOLUTION 1 // DO NOT CHANGE THIS VALUE! Not actually PDM coded, but this define is useful to keep code simple. +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_BITS, 16) +#endif + +#define PDM_RESOLUTION 2 // 1 corresponds to 32 PDM clocks per sample, 2 corresponds to 64 PDM clocks, etc. (and at some level you're going hit the hardware limits) + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S, MOZZI_OUTPUT_I2S_DAC) +// NOTE: On ESP / output via I2S, we simply use the I2S buffer as the output +// buffer, which saves RAM, but also simplifies things a lot +// esp. since i2s output already has output rate control -> no need for a +// separate output timer +#define BYPASS_MOZZI_OUTPUT_BUFFER true +#endif + +#endif // #ifndef CONFIG_CHECK_ESP8266_H diff --git a/internal/config_checks_generic.h b/internal/config_checks_generic.h new file mode 100644 index 000000000..df29fe621 --- /dev/null +++ b/internal/config_checks_generic.h @@ -0,0 +1,162 @@ +/** For Mozzi-internal use: Check configuration options for (some) invalid settings, and apply default for options that have not been set, so far. + * */ + +#ifndef MOZZI_CONFIG_CHECK_GENERIC_H +#define MOZZI_CONFIG_CHECK_GENERIC_H + +#include "../MozziConfigValues.h" // in case not user-included +#include "mozzi_macros.h" + +/// Step 0: Check for some stuff that user should never configure directly (but may be set, indirectly from the hardware-specific setups) +#if defined(BYPASS_MOZZI_OUTPUT_BUFFER) +#error "BYPASS_MOZZI_OUTPUT_BUFFER may not be customized via config" +#endif + + +//// Step 1: Apply missing defaults for generic config options (not the hardware specific ones) +#if not defined(MOZZI_COMPATIBILITY_LEVEL) +#define MOZZI_COMPATIBILITY_LEVEL MOZZI_COMPATIBILITY_2_0 +#endif + +#if not defined(MOZZI_AUDIO_CHANNELS) +#if (MOZZI_COMPATIBILITY_LEVEL <= MOZZI_COMPATIBILITY_1_1) && (STEREO_HACK == true) +#warning Use of STEREO_HACK is deprecated. Use AUDIO_CHANNELS STEREO, instead. +#define MOZZI_AUDIO_CHANNELS MOZZI_STEREO +#else +#define MOZZI_AUDIO_CHANNELS MOZZI_MONO +#endif +#endif + +//MOZZI_AUDIO_MODE -> hardware specific +//MOZZI_AUDIO_RATE -> hardware specific + +#if not defined(MOZZI_CONTROL_RATE) +#define MOZZI_CONTROL_RATE 64 +#endif + +//MOZZI_ANALOG_READ -> hardware specific, but we want to insert a warning, if not supported, and user has not explicitly configured anything +#if not defined(MOZZI_ANALOG_READ) +#define MOZZI__ANALOG_READ_NOT_CONFIGURED +#endif + +#if not defined(MOZZI_AUDIO_INPUT) +#define MOZZI_AUDIO_INPUT MOZZI_AUDIO_INPUT_NONE +#endif + +#if !MOZZI_IS(MOZZI_AUDIO_INPUT, MOZZI_AUDIO_INPUT_NONE) && !defined(MOZZI_AUDIO_INPUT_PIN) +#define MOZZI_AUDIO_INPUT_PIN 0 +#endif + +//MOZZI_PWM_RATE -> hardware specific +//MOZZI_AUDIO_PIN_1 -> hardware specific +//MOZZI_AUDIO_PIN_1_LOW -> hardware specific +//MOZZI_AUDIO_PIN_2 -> hardware specific +//MOZZI_AUDIO_PIN_2_LOW -> hardware specific + + +/// Step 2: Include the hardware specific checks-and-defaults-header +#if IS_AVR() +#include "config_checks_avr.h" +#elif IS_ESP32() +#include "config_checks_esp32.h" +#elif IS_ESP8266() +#include "config_checks_esp8266.h" +#elif IS_MBED() +#include "config_checks_mbed.h" +#elif IS_RENESAS() +#include "config_checks_renesas.h" +#elif IS_RP2040() +#include "config_checks_rp2040.h" +#elif IS_SAMD21() +#include "config_checks_samd21.h" +#elif IS_STM32DUINO() +#include "config_checks_stm32duino.h" +#elif IS_STM32MAPLE() +#include "config_checks_stm32maple.h" +#elif (IS_TEENSY3() || IS_TEENSY4()) +#include "config_checks_teensy.h" +#else +#error Problem detecting hardware +#endif + + +/// Step 2b: Minimal special handling for MOZZI_OUTPUT_EXTERNAL_TIMED/CUSTOM +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPUT_EXTERNAL_CUSTOM) && !defined(MOZZI_AUDIO_BITS) +# define MOZZI_AUDIO_BITS 16 +#endif + + + +/// 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) + +#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" +#endif + +#if !MOZZI_IS(MOZZI_AUDIO_INPUT, MOZZI_AUDIO_INPUT_NONE) && defined(MOZZI_AUDIO_INPUT_PIN) +#warning "MOZZI_AUDIO_INPUT_PIN defined without MOZZI_AUDIO_INPUT" +#endif + +#if (MOZZI_AUDIO_CHANNELS < MOZZI_MONO) || (MOZZI_AUDIO_CHANNELS > MOZZI_STEREO) +#error "MOZZI_AUDIO_CHANNELS outside of (currently) supported range" +#endif + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED) || MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_CUSTOM) +#warning "Mozzi is configured to use an external void 'audioOutput(const AudioOutput f)' function. Please define one in your sketch" +#endif + +// Hardware-specific checks file should have more narrow checks for most options, below, but is not required to, so let's check for anything that is wildly out of scope: +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPUT_EXTERNAL_CUSTOM, MOZZI_OUTPUT_PDM_VIA_I2S, MOZZI_OUTPUT_PDM_VIA_SERIAL, MOZZI_OUTPUT_I2S_DAC, MOZZI_OUTPUT_INTERNAL_DAC) +MOZZI_CHECK_SUPPORTED(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_NONE, MOZZI_ANALOG_READ_STANDARD) + +#if defined(MOZZI__ANALOG_READ_NOT_CONFIGURED) +# if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_NONE) +# warning Asynchronous analog reads not implemented on this platform +# endif +# undef MOZZI__ANALOG_READ_NOT_CONFIGURED +#endif + +/// Step 4: Init Read-only defines that depend on other values +#if !defined(MOZZI_AUDIO_BIAS) +#define MOZZI_AUDIO_BIAS ((uint16_t) 1<<(MOZZI_AUDIO_BITS-1)) +#endif + +#if !defined(MOZZI_AUDIO_BITS_OPTIMISTIC) +#define MOZZI_AUDIO_BITS_OPTIMISTIC MOZZI_AUDIO_BITS +#endif + +// TODO: Rename these defines +#if MOZZI_AUDIO_RATE == 8192 +#define AUDIO_RATE_AS_LSHIFT 13 +#define MICROS_PER_AUDIO_TICK 122 +#elif MOZZI_AUDIO_RATE == 16384 +#define AUDIO_RATE_AS_LSHIFT 14 +#define MICROS_PER_AUDIO_TICK 61 // 1000000 / 16384 = 61.035, ...* 256 = 15625 +#elif MOZZI_AUDIO_RATE == 32768 +#define AUDIO_RATE_AS_LSHIFT 15 +#define MICROS_PER_AUDIO_TICK 31 // = 1000000 / 32768 = 30.518, ...* 256 = 7812.6 +#elif MOZZI_AUDIO_RATE == 65336 +#define AUDIO_RATE_AS_LSHIFT 16 +#define MICROS_PER_AUDIO_TICK 15 +#else +#error Whoopsie, not LSHIFT defined for this audio rate. Please report and/or fix +#endif + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_CUSTOM) +#define BYPASS_MOZZI_OUTPUT_BUFFER true +#endif + +/// Step 5: Patch up some backwards compatibility issues as far as config-related +#if MOZZI_COMPATIBILITY_LEVEL < MOZZI_COMPATIBILITY_LATEST +#define AUDIO_RATE MOZZI_AUDIO_RATE +#define CONTROL_RATE MOZZI_CONTROL_RATE +#endif + +/// Step 6: Some more checks that need to be at the end, because of requiring end of the foodchain headers +// TODO: Rather move this up again, and make AudioOutputStorage_t a primary config option +#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 diff --git a/internal/config_checks_mbed.h b/internal/config_checks_mbed.h new file mode 100644 index 000000000..e92b33414 --- /dev/null +++ b/internal/config_checks_mbed.h @@ -0,0 +1,113 @@ +#ifndef CONFIG_CHECK_MBED_H +#define CONFIG_CHECK_MBED_H + +/** @ingroup hardware + * @page hardware_mbed Mozzi on MBED-based boards (Arduino Giga / Portenta). + * + * port by Thomas Friedrichsmeier & Thomas Combriat + * + * @section mbed_status Port status and notes + * Compiles and runs using Arduino's standard and Arduino_AdvancedAnalog libraries. This port is still **experimental**, testing reveals + * some instabilities for some configurations (in particular with @ref MOZZI_AUDIO_INPUT) that are under active investigations. + * + * This port is not complete yet, in particular: + * - Asynchroneous analog reads are not implemented (yet), `mozziAnalogRead()` relays to `analogRead()`. (Note that @ref MOZZI_AUDIO_INPUT @em is implemented!) + * - This port should support other MBED based Arduino boards like the Arduino Portenta, in *theory*, but the developer have only tested it on the Giga. Feedback welcome! + * + * @section mbed_output Output modes + * The following audio modes (see @ref MOZZI_AUDIO_MODE) are currently supported on this hardware: + * - MOZZI_OUTPUT_EXTERNAL_TIMED + * - MOZZI_OUTPUT_EXTERNAL_CUSTOM + * - MOZZI_OUTPUT_PDM_VIA_SERIAL + * - MOZZI_OUTPUT_INTERNAL_DAC + * + * The default mode is @ref mbed_internal_dac . + * + * @section mbed_internal_dac MOZZI_OUTPUT_INTERNAL_DAC + * This uses the inbuild DAC on the board. The default is setting is appropriate for the Arduino Giga: 12 bits going to A13 (3.5mm jack connector's tip), + * and in stereo mode to pin A12 (3.5mm jack connector's first ring) additionally. + * + * For other boards is may be appropriate to customize: + * @code + * #define MOZZI_AUDIO_PIN_1 ... // mono / left channel; default: A13 + * #define MOZZI_AUDIO_PIN_2 ... // stereo only: right channel; default: A12 + * #define MOZZI_AUDIO_BITS ... // default is 12 + * @endcode + * + * @section mbed_pdm_via_serial MOZZI_PDM_VIA_SERIAL + * Returns a pulse-density modulated (mono only) signal on one of the hardware UARTs of the board (Serial ports). Default is using the SERIAL2, on pin D18. + * You can confiugre the pins to use, but it must be connected to a hardware UART. Output is written to the TX pin, only, but the RX pin needs to be + * claimed as well. Beware of confusing pinout labelling, for instance SERIAL2_TX iss labelled "TX1" on the Arduino Giga. The audio resolution can be enhanced + * using @ref MOZZI_PDM_RESOLUTION, which is described in more detail here: @esp32_pdm_via_i2s . + * + * Configuration options: + * @code + * #define MOZZI_SERIAL_PIN_TX ... // default: SERIAL2_TX + * #define MOZZI_SERIAL_PIN_RX ... // *must* specify the matching one, if customizing the above; default: SERIAL2_RX + * #define MOZZI_PDM_RESOLUTION ... // default value is 2, for 2*32 ones and zeros per audio sample + * @endcode + * + * @section mbed_external MOZZI_OUTPUT_EXTERNAL_TIMED and MOZZI_OUTPUT_EXTERNAL_CUSTOM + * See @ref external_audio +*/ + + +#if not IS_MBED() +#error This header should be included for MBED architecture, only +#endif + +#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_SERIAL, MOZZI_OUTPUT_INTERNAL_DAC) + +#if !defined(MOZZI_AUDIO_RATE) +#define MOZZI_AUDIO_RATE 32768 +#endif + +#if defined(MOZZI_PWM_RATE) +#error Configuration of MOZZI_PWM_RATE is not currently supported on this platform (always same as AUDIO_RATE) +#endif + +#if !defined(MOZZI_ANALOG_READ) +# define MOZZI_ANALOG_READ MOZZI_ANALOG_READ_NONE +#endif + +// yes, correct: async "single" reads are not implemented, but audio input is +MOZZI_CHECK_SUPPORTED(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_NONE) +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_INPUT, MOZZI_AUDIO_INPUT_NONE, MOZZI_AUDIO_INPUT_STANDARD) + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) +# if !defined(MOZZI_AUDIO_BITS) +# define MOZZI_AUDIO_BITS 12 +# endif +# if !defined(MOZZI_AUDIO_PIN_1) +# define MOZZI_AUDIO_PIN_1 A13 +# endif +# if !defined(MOZZI_AUDIO_PIN_2) +# define MOZZI_AUDIO_PIN_2 A12 +# endif +#endif + +#if !defined(MOZZI_AUDIO_BITS) +# define MOZZI_AUDIO_BITS 16 +#endif + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_SERIAL) +# if !defined(MOZZI_PDM_RESOLUTION) +# define MOZZI_PDM_RESOLUTION 2 +# endif +# if !defined(MOZZI_SERIAL_PIN_TX) +# define MOZZI_SERIAL_PIN_TX SERIAL2_TX +# endif +# if !defined(MOZZI_SERIAL_PIN_RX) +# define MOZZI_SERIAL_PIN_RX SERIAL2_RX +# endif +#endif + +// All modes besides timed external bypass the output buffer! +#if !MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED) +# define BYPASS_MOZZI_OUTPUT_BUFFER true +#endif + +#endif // #ifndef CONFIG_CHECK_MBED_H diff --git a/internal/config_checks_renesas.h b/internal/config_checks_renesas.h new file mode 100644 index 000000000..53e33a40c --- /dev/null +++ b/internal/config_checks_renesas.h @@ -0,0 +1,68 @@ +#ifndef CONFIG_CHECK_RENESAS_H +#define CONFIG_CHECK_RENESAS_H + +/** @ingroup hardware + * @page hardware_renesas Mozzi on Arduino Uno R4 - Renesas. + * + * port by Thomas Combriat + * + * @section renesas_status Port status and notes + * Compiles and runs using Arduino's standard library (Renesas 0.8.7 at the time of this writing). + * + * A few particularities: + * - Because this board has an on-board DAC (A0), but only one, STEREO is not implemented and Mozzi uses this pin. Usage of other pins using PWM for instance is not implemented yet. + * - getAudioInput() and mozziAnalogRead() return values in the Renesas' full ADC resolution of 0-16384 rather than AVR's 0-1023. *This might change in the near future for speed purposes.* + * + * @section rensesas_output Output modes + * The following audio modes (see @ref MOZZI_AUDIO_MODE) are currently supported on this hardware: + * - MOZZI_OUTPUT_EXTERNAL_TIMED + * - MOZZI_OUTPUT_EXTERNAL_CUSTOM + * - MOZZI_OUTPUT_INTERNAL_DAC + * + * The default mode is @ref renesas_internal_dac. Further modes may be added in the future. + * + * @section renesas_internal_dac MOZZI_OUTPUT_INTERNAL_DAC + * This uses the inbuild DAC on the board on pin A0. Mono output only, and the pin is not configurable. Audio resolution is also fixed at 12 bits (which is what the board supports). + * + * This mode claims two timers (but it is not hardcoded, which ones). + * + * @section renesas_external MOZZI_OUTPUT_EXTERNAL_TIMED and MOZZI_OUTPUT_EXTERNAL_CUSTOM + * MOZZI_OUTPUT_EXTERNAL_TIMED claimes one timer, MOZZI_OUTPUT_EXTERNAL_CUSTOM does not claim any timer. + * See @ref external_audio +*/ + +#if not IS_RENESAS() +#error This header should be included for RENESAS (Arduino Uno R4) architecture, only +#endif + +#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_INTERNAL_DAC) + +#if !defined(MOZZI_AUDIO_RATE) +#define MOZZI_AUDIO_RATE 32768 +#endif + +#if defined(MOZZI_PWM_RATE) +#error Configuration of MOZZI_PWM_RATE is not currently supported on this platform (always same as AUDIO_RATE) +#endif + +#if !defined(MOZZI_ANALOG_READ) +# define MOZZI_ANALOG_READ MOZZI_ANALOG_READ_STANDARD +#endif + +MOZZI_CHECK_SUPPORTED(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_NONE, MOZZI_ANALOG_READ_STANDARD) + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) +# if !defined(MOZZI_AUDIO_BITS) +# define MOZZI_AUDIO_BITS 12 +# endif +# if !defined(MOZZI_AUDIO_PIN_1) +# define MOZZI_AUDIO_PIN_1 A0 +# endif +# define BYPASS_MOZZI_OUTPUT_BUFFER true +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_CHANNELS, 1) +#endif + +#endif // #ifndef CONFIG_CHECK_RENESAS_H diff --git a/internal/config_checks_rp2040.h b/internal/config_checks_rp2040.h new file mode 100644 index 000000000..467f9948b --- /dev/null +++ b/internal/config_checks_rp2040.h @@ -0,0 +1,113 @@ +#ifndef CONFIG_CHECK_RP2040_H +#define CONFIG_CHECK_RP2040_H + +/** @ingroup hardware + * @page hardware_rp2040 Mozzi on RP2040 (Raspberry Pi Pico) + * + * port by Thomas Friedrichsmeier + * + * @section rp2040_status Port status and notes + * Compiles and runs using [this core](https://github.com/earlephilhower/arduino-pico). Can probably be ported to the Mbed core for RP2040, relatively easily, + * as it relies mostly on the RP2040 SDK API. Tested on a Pi Pico. + * + * - This is a recent addition, implementation details may still change (currently just PWM driven by a timer; this may be worth changing to a DMA driven output) + * - Wavetables and samples are not kept in progmem on this platform. While apparently speed (of the external flash) is not much of an issue, the data always seems to be copied into RAM, anyway. + * - Note that getAudioInput() and mozziAnalogRead() return values in the RP2040's full ADC resolution of 0-4095 rather than AVR's 0-1023. + * - twi_nonblock is not ported + * - Code uses only one CPU core + * + * @section rp2040_output Output modes + * The following audio modes (see @ref MOZZI_AUDIO_MODE) are currently supported on this hardware: + * - MOZZI_OUTPUT_EXTERNAL_TIMED + * - MOZZI_OUTPUT_EXTERNAL_CUSTOM + * - MOZZI_OUTPUT_PWM + * - MOZZI_OUTPUT_I2S_DAC + * + * The default mode is @ref rp2040_pwm . + * + * @section rp2040_pdm MOZZI_OUTPUT_PWM + * Audio output is written to pin 0 (mono) or 0 and 1 (stereo), by default, with 11 bits of ouput resolution. + * One hardware timer interrupt and one DMA channel are claimed (number not hardcoded), a non-exclusive handler is installed on DMA_IRQ_0. + * + * Configuration options: + * @code + * #define MOZZI_AUDIO_PIN_1 ... // default is 0 + * #define MOZZI_AUDIO_BITS ... // output resolution (bits); default is 11 + * // additionally, for stereo: + * #define MOZZI_AUDIO_PIN_2 ... // default is 1; this must be on the same PWM slice as the first pin (i.e. neighboring) + * @endcode + * + * @section rp2040_i2s_dac MOZZI_OUTPUT_I2S_DAC + * Output to an external DAC, connected via I2S. This uses 16 bit (per audio channel), but can be changed to 8, 16, 24 (left aligned) and 32 resolution. + * Both plain I2S and LSBJ format (PT8211 needs this, for instance) are available. Plain format is used by default. The GPIO pins to use can be configured, + * - almost - freely (see below). Two DMA channels are claimed (numbers not hardcoded), non-exclusive handlers are installed on DMA_IRQ_0. + * + * Configuration options: + * @code + * #define MOZZI_AUDIO_BITS ... // available values are 8, 16 (default), 24 (LEFT ALIGN in 32 bits type!!) and 32 bits + * #define MOZZI_I2S_PIN_BCK ... // /BLCK) default is 20 + * //#define MOZZI_I2S_PIN_WS (MOZZI_I2S_PIN_BCK+1) ... // CANNOT BE CHANGED, HAS TO BE NEXT TO pBCLK, i.e. default is 21 + * #define MOZZI_I2S_PIN_DATA ... // (DOUT) default is 22 + * #define MOZZI_I2S_FORMAT ... // may be MOZZI_I2S_FORMAT_LSBJ or MOZZI_I2S_FORMAT_PLAIN (default) + * @endcode + * + * @note + * The MOZZI_I2S_FORMAT_LSBJ option may require a relatively recent git-hub checkout of the arduino-pico core. + * + * @section rp2040_external MOZZI_OUTPUT_EXTERNAL_TIMED and MOZZI_OUTPUT_EXTERNAL_CUSTOM + * See @ref external_audio +*/ + +#if not IS_RP2040() +#error This header should be included for RP2040 architecture (Raspberry Pi Pico and others), only +#endif + +#if !defined(MOZZI_AUDIO_MODE) +# define MOZZI_AUDIO_MODE MOZZI_OUTPUT_PWM +#endif +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPUT_EXTERNAL_CUSTOM, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_I2S_DAC) + +#if !defined(MOZZI_AUDIO_RATE) +#define MOZZI_AUDIO_RATE 32768 +#endif + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM) +# if !defined(MOZZI_AUDIO_BITS) +# define MOZZI_AUDIO_BITS 11 +# endif +# if !defined(MOZZI_AUDIO_PIN_1) +# define MOZZI_AUDIO_PIN_1 0 +# endif +# if !defined(MOZZI_AUDIO_PIN_2) +# define MOZZI_AUDIO_PIN_2 1 +# endif +#endif + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC) +# if !defined(MOZZI_AUDIO_BITS) +# define MOZZI_AUDIO_BITS 16 +# endif +# if !defined(MOZZI_I2S_PIN_BCK) +# define MOZZI_I2S_PIN_BCK 20 +# endif +//# define MOZZI_IS_PIN_WS(MOZZI_I2S_PIN_BCK + 1) // implicit +# if !defined(MOZZI_I2S_PIN_DATA) +# define MOZZI_I2S_PIN_DATA 22 +# endif +# if !defined(MOZZI_I2S_FORMAT) +# define MOZZI_I2S_FORMAT MOZZI_I2S_FORMAT_PLAIN +# endif +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 +#endif + +#if !defined(MOZZI_ANALOG_READ) +# define MOZZI_ANALOG_READ MOZZI_ANALOG_READ_STANDARD +#endif + +MOZZI_CHECK_SUPPORTED(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_NONE, MOZZI_ANALOG_READ_STANDARD) +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_INPUT, MOZZI_AUDIO_INPUT_NONE, MOZZI_ANALOG_READ_STANDARD) + +#endif // #ifndef CONFIG_CHECK_RP2040_H diff --git a/internal/config_checks_samd21.h b/internal/config_checks_samd21.h new file mode 100644 index 000000000..dc340fb2d --- /dev/null +++ b/internal/config_checks_samd21.h @@ -0,0 +1,67 @@ +#ifndef CONFIG_CHECK_SAMD21_H +#define CONFIG_CHECK_SAMD21_H + +/** @ingroup hardware + * @page hardware_samd Mozzi on SAMD21 based boards (Arduino Circuitplayground M0 and others) + * + * port by Adrian Freed + * + * @section samd_status Port status and notes + * - @def MOZZI_ANALOG_READ and MOZZI_ANALOG_INPUT are not implemented (contributions welcome) + * - We don't have a lot of data, which boards this port has been tested on. Success or not, let us know, if you are using Mozzi on SAMD21 boards + * + * @section samd_output_modes Output modes + * The following audio modes (see @ref MOZZI_AUDIO_MODE) are currently supported on this hardware: + * - MOZZI_OUTPUT_EXTERNAL_TIMED + * - MOZZI_OUTPUT_EXTERNAL_CUSTOM + * - MOZZI_OUTPUT_INTERNAL_DAC + * + * The default mode is @ref samd_internal_dac , meaning, only boards with an inbuilt DAC are covered by default + * (you could stil use one of the external output modes, however). + * + * @section samd_internal_dac MOZZI_OUTPUT_INTERNAL_DAC + * Output resolution is 10 bits by default, and goes to pin DAC0. Only mono output is supported. Within the hardware limits of your board, you can configure the following: + * + * @code + * #define MOZZI_AUDIO_PIN_1 ... // default is DAC0 + * #define MOZZI_AUDIO_BITS ... // default is 10 + * @endcode + * + * @section samd_external MOZZI_OUTPUT_EXTERNAL_TIMED and MOZZI_OUTPUT_EXTERNAL_CUSTOM + * See @ref external_audio +*/ + +#if not IS_SAMD21() +#error This header should be included for SAMD21 architecture (Arduino Circuitplayground M0 and others), only +#endif + +#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_INTERNAL_DAC) + +#if !defined(MOZZI_AUDIO_RATE) +#define MOZZI_AUDIO_RATE 32768 +#endif + +#if defined(MOZZI_PWM_RATE) +#error Configuration of MOZZI_PWM_RATE is not currently supported on this platform (always same as AUDIO_RATE) +#endif + +#if !defined(MOZZI_ANALOG_READ) +# define MOZZI_ANALOG_READ MOZZI_ANALOG_READ_NONE +#endif + +MOZZI_CHECK_SUPPORTED(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_NONE) + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) +# if !defined(MOZZI_AUDIO_BITS) +# define MOZZI_AUDIO_BITS 10 +# endif +# if !defined(MOZZI_AUDIO_PIN_1) +# define MOZZI_AUDIO_PIN_1 DAC0 +# endif +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_CHANNELS, 1) +#endif + +#endif // #ifndef CONFIG_CHECK_SAMD21_H diff --git a/internal/config_checks_stm32duino.h b/internal/config_checks_stm32duino.h new file mode 100644 index 000000000..3c3f1d419 --- /dev/null +++ b/internal/config_checks_stm32duino.h @@ -0,0 +1,132 @@ +#ifndef CONFIG_CHECKS_STM32DUINO_H +#define CONFIG_CHECKS_STM32DUINO_H + +/** @ingroup hardware +* @page hardware_stm32_disambiguation Mozzi on STM32-based boards - disambiguation +* +* * The situation on STM32-based boards is rather confusing, as there are several competing Arduino cores. Importantly: +* - Some boards use dedicated cores (e.g. Arduino Giga / Portenta @ref hardware_mbed) etc. For those, see the relevant sections (if we support them). +* - There is a series of libmaple-based cores, including [Roger Clark's libmaple-based core](https://github.com/rogerclarkmelbourne/Arduino_STM32). These are highly optimized, +* and provide very complete support, but only for a limited number of boards. Unfortunately, at the time of this writing (2023/04), they are not available for installation +* via the Arduino Board Manager, and they do not currently seem actively maintained. +* For using these with Mozzi, see @ref hardware_stm32_maple +* - A generic Arduino core for STM32 is the [STM32duino core](https://github.com/stm32duino/Arduino_Core_STM32). It supports a huge set of boards, and seems to have offical +* backing by STM, but some features of the libmaple based cores are still lacking. To complete confusion, this core now uses the label "STM32duino", which used to be what +* the libmaple cores above were known by (don't blame Mozzi for this mess!). +* For using this with Mozzi, see @ref hardware_stm32duino +* */ + +/** @ingroup hardware + * @page hardware_stm32duino Mozzi on STM32duino-based boards. + * + * port by Thomas Friedrichsmeier + * + * @note + * Be sure to understand the info given at @ref hardwware_stm32_disambiguation . This page is about using Mozzi with the STM32duino core. + * + * @section stm32duino_status Port status and usage notes + * Tested on a STM32F103C8T6 blue pill board as well as an STM32F411CE black pill board, i.e. on sboards _without_ a + * real DAC. Compiles and runs, with a bunch of caveats (see below). Should probably run on any other board supported by the + * [STM32duino core](https://github.com/stm32duino/Arduino_Core_STM32) (although this theory is untested). + * When trying any other board, you probably want to check the platform specific settings (see below), carefully, importantly, whether the desired output resolution is + * achievable, and whether the desired output pins are PWM capable. + * + * - @ref MOZZI_ANALOG_READ input implementation is somewhat experimental, and may not be able to service a whole lot of pins (contributions welcome) + * - @ref MOZZI_AUDIO_INPUT is completely untested (but implemented in theory; feedback welcome!) + * - getAudioInput() and mozziAnalogRead() return values in the STM32's full ADC resolution (the exact range depending on the board in use) rather than AVR's 0-1023. + * - twi_nonblock is not ported + * + * @section stm32duino_output_modes Output modes + * The following audio modes (see @ref MOZZI_AUDIO_MODE) are currently supported on this hardware: + * - MOZZI_OUTPUT_EXTERNAL_TIMED + * - MOZZI_OUTPUT_EXTERNAL_CUSTOM + * - MOZZI_OUTPUT_PWM + * - MOZZI_OUTPUT_PWM_2PIN + * + * The default mode is @ref stm32duino_pwm . + * + * @note + * This port may look similar to, but uses a different default GPIO pinout than @hardware_stm32_maple ! + * + * @section stm32duino_pwm MOZZI_OUTPUT_PWM + * Standard pulse width modulated output to one (mono) or two (stereo, see @ref MOZZI_AUDIO_CHANNELS) GPIO pins. Default pinout: PA8 (mono/left), PA9 (right channel in stereo). + * This mode uses two hardware timers: One for the PWM (Timer 3 when using the default pin configuration), and a second for updating the output at audio rate. + * The default audio resolution is 10 bits, which results in a carrier frequency of ~70kHz on a 72MHz CPU. On slower boards you will have to descrease this. + * The following settings may be costumized, if desired: + * + * @code + * #define MOZZI_AUDIO_PIN_1 ... // Left / mono output pin. Default: PA8 + * #define MOZZI_AUDIO_UPDATE_TIMER ... // Second hardware timer to claim, must not be the same of the timer for the above pin. Default TIM2 + * #define MOZZI_AUDIO_BITS ... // Output resolution in bits. Default is 10 + * // For stereo, only: + * #define MOZZI_AUDIO_PIN_2 ... // Right channel output pin. This *must* be connected to the same hardware timer as MOZZI_AUDIO_PIN_1 ! Default: PA9 + * @endcode + * + * @section stm32duino_pwm MOZZI_OUTPUT_2PIN_PWM + * This mode is very similar to @ref stm32duino_pwm, but splitting output for a single channel across two GPIO pins for better resolution. For details on the required + * hardware setup, and configuration tradeoffs, see @ref avr_2pin_pwm . Stereo output is not available in this mode. + * Output is at 2*7 bits at up to 560kHz carrier frequency (but limited to 5 times audio rate). + * + * Customizable configuration options: + * @code + * #define MOZZI_AUDIO_PIN_1 ... // High byte of the output. Default: PA8 + * #define MOZZI_AUDIO_PIN_1_LOW ... // Low byte of the output. Default: PA9 + * #define MOZZI_AUDIO_UPDATE_TIMER ... // Second hardware timer to claim. Default TIM2 + * #define MOZZI_AUDIO_BITS_PER_CHANNEL ... // Bits per pin. Default is 7 + * @endcode + * + * @section stm32duino_external MOZZI_OUTPUT_EXTERNAL_TIMED and MOZZI_OUTPUT_EXTERNAL_CUSTOM + * See @ref external_audio . + * The (single) hardware timer claimed for MOZZI_OUTPUT_EXTERNAL_TIMED may be configured using "MOZZI_AUDIO_UPDATE_TIMER" (default: TIM2). +*/ + +#if not IS_STM32DUINO() +#error This header should be included for STM32 (stm32duino.com core), only +#endif + +#if !defined(MOZZI_AUDIO_MODE) +# define MOZZI_AUDIO_MODE MOZZI_OUTPUT_PWM +#endif +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPUT_EXTERNAL_CUSTOM, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM) + +#if !defined(MOZZI_AUDIO_RATE) +# define MOZZI_AUDIO_RATE 32768 +#endif + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM, MOZZI_OUTPUT_EXTERNAL_TIMED) +# if !defined(MOZZI_AUDIO_UPDATE_TIMER) +# define MOZZI_AUDIO_UPDATE_TIMER TIM2 +# endif +#endif + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM) +# if !defined(MOZZI_AUDIO_PIN_1) +# define MOZZI_AUDIO_PIN_1 PA8 +# endif +# if (MOZZI_AUDIO_CHANNELS > 1) && !defined(MOZZI_AUDIO_PIN_1) +# define MOZZI_AUDIO_PIN_2 PA9 +# endif +# if !defined(MOZZI_AUDIO_BITS) +# define MOZZI_AUDIO_BITS 10 +# endif +# define MOZZI_AUDIO_BITS_PER_CHANNEL MOZZI_AUDIO_BITS +#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM) +# if !defined(MOZZI_AUDIO_PIN_1) +# define MOZZI_AUDIO_PIN_1 PA8 +# endif +# if !defined(MOZZI_AUDIO_PIN_1_LOW) +# define MOZZI_AUDIO_PIN_1_LOW PA9 +# endif +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_CHANNELS, 1) +# if !defined(MOZZI_AUDIO_PER_CHANNEL) +# define MOZZI_AUDIO_PER_CHANNEL 7 +# endif +# define MOZZI_AUDIO_BITS MOZZI_AUDIO_BITS_PER_CHANNEL * 2 +#endif + +#if !defined(MOZZI_ANALOG_READ) +#define MOZZI_ANALOG_READ MOZZI_ANALOG_READ_STANDARD +#endif + + +#endif // #ifndef CONFIG_CHECKS_STM32DUINO_H diff --git a/internal/config_checks_stm32maple.h b/internal/config_checks_stm32maple.h new file mode 100644 index 000000000..27b08736f --- /dev/null +++ b/internal/config_checks_stm32maple.h @@ -0,0 +1,123 @@ +#ifndef CONFIG_CHECKS_STM32MAPLE_H +#define CONFIG_CHECKS_STM32MAPLE_H + +/** @ingroup hardware + * @page hardware_stm32_maple Mozzi on STM32duino-based boards. + * port by Thomas Friedrichsmeier + * + * @note + * Be sure to understand the info given at @ref hardwware_stm32_disambiguation . This page is about using Mozzi with the STM32 "libmaple based" core. + * + * @note + * This port may look similar to, but uses a different default GPIO pinout than @hardware_stm32duino ! + * + * @section stm32_maple_status Status and peculiarities of this port + * Compiles for and runs on a STM32F103C8T6 blue pill board, with a bunch of caveats (see below), i.e. on a board _without_ a + * real DAC. Should probably run on any other board supported by [Roger Clark's libmaple-based core](https://github.com/rogerclarkmelbourne/Arduino_STM32) (although this theory is untested). + * + * @note that at the time of this writing, [Stev Strong's slightliy more recent fork of this core](https://github.com/stevstrong/Arduino_STM32/) does *not* work with + * Mozzi, apparently due to a bug in pwmWrite(). + * + * - If you want to use MIDI, be sure to replace "MIDI_CREATE_DEFAULT_INSTANCE()" with "MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI)" (or Serial2) + * - @ref MOZZI_AUDIO_INPUT_STANDARD is implemented in theory, but untested (feedback welcome) + * - getAudioInput() and mozziAnalogRead() return values in the STM32's full ADC resolution of 0-4095 rather than AVR's 0-1023. + *- twi_nonblock is not ported + * + * @section stm32_output_modes Output modes + * + * The following audio modes (see @ref MOZZI_AUDIO_MODE) are currently supported on this hardware: + * - MOZZI_OUTPUT_EXTERNAL_TIMED + * - MOZZI_OUTPUT_EXTERNAL_CUSTOM + * - MOZZI_OUTPUT_PWM + * - MOZZI_OUTPUT_PWM_2PIN + * + * The default mode is @ref stm32_maple_pwm . + * + * @section stm32_maple_pwm MOZZI_OUTPUT_PWM + * Standard pulse width modulated output to one (mono) or two (stereo, see @ref MOZZI_AUDIO_CHANNELS) GPIO pins. Default pinout: PB8 (mono/left), PB9 (right channel in stereo). + * This mode uses two hardware timers: One for the PWM (Timer 4 when using the default pin configuration), and a second for updating the output at audio rate. + * The default audio resolution is 10 bits, which results in a carrier frequency of ~70kHz on a 72MHz CPU. On slower boards you will have to descrease this. + * The following settings may be costumized, if desired: + * + * @code + * #define MOZZI_AUDIO_PIN_1 ... // Left / mono output pin. Default: PB8 + * #define MOZZI_AUDIO_PWM_TIMER ... // Must be set ot the hardware timer connected to the above pin. Default: 4 + * #define MOZZI_AUDIO_UPDATE_TIMER ... // Second hardware timer to claim. Default 2 + * #define MOZZI_AUDIO_BITS ... // Output resolution in bits. Default is 10 + * // For stereo, only: + * #define MOZZI_AUDIO_PIN_2 ... // Right channel output pin. This *must* be connected to the same hardware timer as MOZZI_AUDIO_PIN_1 ! Default: PB9 + * @endcode + * + * @section stm32_maple_2pin_pwm MOZZI_OUTPUT_2PIN_PWM + * This mode is very similar to @ref stm32_maple_pwm, but splitting output for a single channel across two GPIO pins for better resolution. For details on the required + * hardware setup, and configuration tradeoffs, see @ref avr_2pin_pwm . Stereo output is not available in this mode. + * Output is at 2*7 bits at up to 560kHz carrier frequency (but limited to 5 times audio rate). + * + * Customizable configuration options: + * @code + * #define MOZZI_AUDIO_PIN_1 ... // High byte of the output. Default: PB8 + * #define MOZZI_AUDIO_PIN_2 ... // Low byte of the output. Default: PB9 + * #define MOZZI_AUDIO_PWM_TIMER ... // Must be set to the number of the hardware timer connect to the above pins. Default: 4 + * #define MOZZI_AUDIO_UPDATE_TIMER ... // Second hardware timer to claim. Default TIM2 + * #define MOZZI_AUDIO_BITS_PER_CHANNEL ... // Bits per pin. Default is 7 + * @endcode + * + * @section stm32_maple_external MOZZI_OUTPUT_EXTERNAL_TIMED and MOZZI_OUTPUT_EXTERNAL_CUSTOM + * See @ref external_audio + * The (single) hardware timer claimed for MOZZI_OUTPUT_EXTERNAL_TIMED may be configured using "MOZZI_AUDIO_UPDATE_TIMER" (default: TIM2). +*/ + + +#if not IS_STM32MAPLE() +#error This header should be included for STM32 (libmaple based core), only +#endif + +#if !defined(MOZZI_AUDIO_MODE) +# define MOZZI_AUDIO_MODE MOZZI_OUTPUT_PWM +#endif +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPUT_EXTERNAL_CUSTOM, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM) + +#if !defined(MOZZI_AUDIO_RATE) +# define MOZZI_AUDIO_RATE 32768 +#endif + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM, MOZZI_OUTPUT_EXTERNAL_TIMED) +# if !defined(MOZZI_AUDIO_UPDATE_TIMER) +# define MOZZI_AUDIO_UPDATE_TIMER 2 +# endif +# if !defined(MOZZI_AUDIO_PWM_TIMER) +# define MOZZI_AUDIO_PWM_TIMER 4 +# endif +#endif + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM) +# if !defined(MOZZI_AUDIO_PIN_1) +# define MOZZI_AUDIO_PIN_1 PB8 +# endif +# if (MOZZI_AUDIO_CHANNELS > 1) && !defined(MOZZI_AUDIO_PIN_1) +# define MOZZI_AUDIO_PIN_2 PB9 +# endif +# if !defined(MOZZI_AUDIO_BITS) +# define MOZZI_AUDIO_BITS 10 +# endif +# define MOZZI_AUDIO_BITS_PER_CHANNEL MOZZI_AUDIO_BITS +#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_2PIN_PWM) +# if !defined(MOZZI_AUDIO_PIN_1) +# define MOZZI_AUDIO_PIN_1 PB8 +# endif +# if !defined(MOZZI_AUDIO_PIN_1_LOW) +# define MOZZI_AUDIO_PIN_1_LOW PB9 +# endif +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_CHANNELS, 1) +# if !defined(MOZZI_AUDIO_PER_CHANNEL) +# define MOZZI_AUDIO_PER_CHANNEL 7 +# endif +# define MOZZI_AUDIO_BITS MOZZI_AUDIO_BITS_PER_CHANNEL * 2 +#endif + +#if !defined(MOZZI_ANALOG_READ) +#define MOZZI_ANALOG_READ MOZZI_ANALOG_READ_STANDARD +#endif + + +#endif // #ifndef CONFIG_CHECKS_STM32MAPLE_H diff --git a/internal/config_checks_teensy.h b/internal/config_checks_teensy.h new file mode 100644 index 000000000..6f2e642a9 --- /dev/null +++ b/internal/config_checks_teensy.h @@ -0,0 +1,138 @@ +#ifndef CONFIG_CHECK_TEENSY_H +#define CONFIG_CHECK_TEENSY_H + +/** @ingroup hardware + * @page hardware_teensy3 Mozzi on Teensy 3.0/3.1/3.2/3.4/3.5/LC boards. + * + * port by Tim Barrass + * + * @note + * For Teensy 4.x see @ref hardware_teensy4 + * + * @section teensy3_status Port status and ussage notes + * This port requires the following two libraries (which should be part of a default installtion, however): + * - [Timer library](https://github.com/loglow/IntervalTimer) for Teensy 3.* by Daniel Gilbert + * - [ADC library](http://github.com/pedvide/ADC) by Pedro Villanueva + * + * Some of the differences for Teensy 3.* which will affect users include: + * - twi_nonblock code by Marije Baalman for non-blocking I2C is not compatible with Teensy 3.0/3.1/3.2. + * + * @section teensy3_output Output modes + * The following audio modes (see @ref MOZZI_AUDIO_MODE) are currently supported on this hardware: + * - MOZZI_OUTPUT_EXTERNAL_TIMED + * - MOZZI_OUTPUT_EXTERNAL_CUSTOM + * - MOZZI_OUTPUT_INTERNAL_DAC + * + * The default mode is @ref teensy3_internal_dac . + * + * @section teensy3_internal_dac MOZZI_OUTPUT_INTERNAL_DAC + * Output is to the inbuilt DAC. Output resolution is 12 bits. Mono, only. The DAC output pin differs from board to boards. + * In most cases the appropriate pin will be set outmatically. If needed, you can specify the DAC pin, explicitly: + * + * @code + * #define MOZZI_AUDIO_PIN_1 ... // Default: A14, A12, or A21, depending on board + * @endcode + * + * @section teensy3_external MOZZI_OUTPUT_EXTERNAL_TIMED and MOZZI_OUTPUT_EXTERNAL_CUSTOM + * See @ref external_audio +*/ + + +/** @ingroup hardware + * @page hardware_teensy4 Mozzi on Teensy 4.x boards. + * + * port by Thomas Combriat + * + * @note + * For Teensy 3.x see @ref hardware_teensy3 + * + * @section teensy4_status Port status and ussage notes + * This port requires the following two libraries (which should be part of a default installtion, however): + * - [Timer library](https://github.com/loglow/IntervalTimer) for Teensy 4.* by Daniel Gilbert + * - [ADC library](http://github.com/pedvide/ADC) by Pedro Villanueva + * + * Contrary to the Teensy 3, the Teensy 4 do not have any DAC. The output is done on pin A8 (PWM) by default (see below). + * twi_nonblock is not ported. + * + * @section teensy3_output Output modes + * The following audio modes (see @ref MOZZI_AUDIO_MODE) are currently supported on this hardware: + * - MOZZI_OUTPUT_EXTERNAL_TIMED + * - MOZZI_OUTPUT_EXTERNAL_CUSTOM + * - MOZZI_OUTPUT_PWM + * + * The default mode is @ref teensy4_pwm . + * + * @section teensy4_pwm MOZZI_OUTPUT_PWM + * Output is to a GPIO pin (or two in stereo). The output resolution is fixed at 10 bits, and a 146484 kHz carrier frequency. + * The output pins can be configured as: + * + * @code + * #define MOZZI_AUDIO_PIN_1 ... // Left / mono output pin. Default: A8 + * // For stereo, only: + * #define MOZZI_AUDIO_PIN_2 ... // Right channel output pin. Default: A9 + * @endcode + * + * @section teensy4_external MOZZI_OUTPUT_EXTERNAL_TIMED and MOZZI_OUTPUT_EXTERNAL_CUSTOM + * See @ref external_audio +*/ + + +#if !(IS_TEENSY3() || IS_TEENSY4()) +#error This header should be included for Teensy (3.x or 4.x) boards, only +#endif + + + +#if IS_TEENSY3() +# 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_INTERNAL_DAC) +#elif IS_TEENSY4() +# if !defined(MOZZI_AUDIO_MODE) +# define MOZZI_AUDIO_MODE MOZZI_OUTPUT_PWM +# endif +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPUT_EXTERNAL_CUSTOM, MOZZI_OUTPUT_PWM) +#endif + +#if !defined(MOZZI_AUDIO_RATE) +#define MOZZI_AUDIO_RATE 32768 +#endif + +#if defined(MOZZI_PWM_RATE) +#error Configuration of MOZZI_PWM_RATE is not currently supported on this platform (always same as AUDIO_RATE) +#endif + +#if !defined(MOZZI_ANALOG_READ) +# define MOZZI_ANALOG_READ MOZZI_ANALOG_READ_STANDARD +#endif + +MOZZI_CHECK_SUPPORTED(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_NONE, MOZZI_ANALOG_READ_STANDARD) + +#include "teensyPinMap.h" + +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC) +# define MOZZI_AUDIO_BITS 12 // not configurable +# if !defined(MOZZI_AUDIO_PIN_1) +# if defined(__MKL26Z64__) +# define MOZZI_AUDIO_PIN_1 A12 +# elif defined(__MK20DX128__) || defined(__MK20DX256__) +# define MOZZI_AUDIO_PIN_1 A14 +# elif defined(__MK64FX512__) || defined(__MK66FX1M0__) +# define MOZZI_AUDIO_PIN_1 A21 +# else +# error DAC pin not know for this board. Please define MOZZI_AUDIO_PIN_1 as appropriate +# endif +# endif +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_CHANNELS, 1) +#elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM) +# define MOZZI_AUDIO_BITS 10 // not configurable +# if !defined(MOZZI_AUDIO_PIN_1) +# define MOZZI_AUDIO_PIN_1 A8 +# endif +# if !defined(MOZZI_AUDIO_PIN_2) +# define MOZZI_AUDIO_PIN_2 A9 +# endif +#endif + +#endif // #ifndef CONFIG_CHECK_TEENSY_H diff --git a/internal/config_checks_template.h b/internal/config_checks_template.h new file mode 100644 index 000000000..f957545f0 --- /dev/null +++ b/internal/config_checks_template.h @@ -0,0 +1,88 @@ +/** NOTE Template only: Copy and adjust this file when adding a new port. + * + * More instructions on the process are to be found in MozziGuts_impl_template.hpp. Read those first! + * + * What this file shall do: + * 1) Set default values for certain configurable options. Try to stick to the defines already in use as much as possible, + * so configuration is consistent across ports. + * 2) Have some checks for configuration values that are not supported on this port + * 3) Document some details of your port + */ + +/** NOTE: All ports need to provide a default for this */ +#if not defined(MOZZI_AUDIO_MODE) +# define MOZZI_AUDIO_MODE MOZZI_OUTPUT_PWM +#endif +/** NOTE: And all ports should allow only supported output modes. Note that MOZZI_OUTPUT_EXTERNAL_TIMED and MOZZI_OUTPUT_EXTERNAL_CUSTOM +are usually trivial to implement, so we'll assume your port allows them, too: */ +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPUT_EXTERNAL_CUSTOM) + +/** NOTE: All ports need to provide a default for this. 32768 is reasonable on most modern MCUs, but 16384 may be useful for weaker MCUs. */ +#if not defined(MOZZI_AUDIO_RATE) +# define MOZZI_AUDIO_RATE 32768 +#endif + +/** NOTE: *If* your port has an implementation for asynchronous analog reads, these shall be enabled by default. */ +#if not defined(MOZZI_ANALOG_READ) +# define MOZZI_ANALOG_READ MOZZI_ANALOG_READ_STANDARD +#endif +/** NOTE: *Otherwise* you may additionally document that as not-yet-supported like so: */ +// MOZZI_CHECK_SUPPORTED(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ) + +/** NOTE: Example for additional config options depending on a specific output mode: */ +#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_INTERNAL_DAC) +# if !defined(MOZZI_AUDIO_PIN_1) +# define MOZZI_AUDIO_PIN_1 5 +# endif +/** NOTE: All ports need to provide a default for this, for all audio modes _except_ for the external ones: */ +# if !defined(MOZZI_AUDIO_BITS) +# define MOZZI_AUDIO_BITS 10 +# endif +/** NOTE: If only mono is supported in this output mode: */ +MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_CHANNELS, MOZZI_MONO) +#endif + +/** NOTE: You may also set further, implementation-specific defines, here. E.g. BYPASS_MOZZI_OUTPUT_BUFFER. Defines that are purely + * specific to your port (i.e. only needed inside MozziGuts_impt_YOURPLATFORM.h, should be #undef'ed at the end of that file. */ + + +/** NOTE: The following is an example documentation for your port. In order not to end up in the real documentation, the comment starts + * with "/*", only. Change that to "/**" to enable documentation to be extracted. */ + +/* @ingroup hardware + * @page hardware_MYPORT Mozzi on MYPORT architecture based boards (e.g. MOST PROMINENT BOARD) + * + * Port added by YOUR_NAME. + * + * @section MYPORT_status Status of this port and usage notes + * This port has been tested on BOARD A, BOARD B, but is expected to work on other boards using this family of MCUs, too. + * The default MOZZI_AUDIO_RATE is set to 32678 Hz. Asynchronous analog reads are not yet implemented, so be careful + * not to use mozziAnalogRead() too much. Also @ref MOZZI_AUDIO_INPUT is not available. + * + * When connecting external circuitry, keep in mind that the GPIO pins on this CPU are rated at a max of 3 mA. Be careful + * not to over-stress them. It is further recommended not to make use of feature X when using Mozzi, as it will + * introduce considerable noise into the audio. + * + * Also, something else to keep in mind about this port. + * + * @section MYPORT_output_modes Output modes + * The following audio modes (see @ref MOZZI_AUDIO_MODE) are currently supported on this hardware: + * - MOZZI_OUTPUT_EXTERNAL_TIMED + * - MOZZI_OUTPUT_EXTERNAL_CUSTOM + * - MOZZI_OUTPUT_INTERNAL_DAC + * + * The default mode is @ref MYPORT_internal_dac , meaning, only boards with an inbuilt DAC are covered by default + * (you could stil use one of the external output modes, however). + * + * @section MYPORT_internal_dac MOZZI_OUTPUT_INTERNAL_DAC + * Output resolution is 10 bits by default, and goes to pin DAC0. Hardware timer 1 is claimed by Mozzi. + * Only mono output is supported. Within the hardware limits of your board, you can configure the following: + * + * @code + * #define MOZZI_AUDIO_PIN_1 ... // default is DAC0 + * #define MOZZI_AUDIO_BITS ... // default is 10 + * @endcode + * + * @section MYPORT_external MOZZI_OUTPUT_EXTERNAL_TIMED and MOZZI_OUTPUT_EXTERNAL_CUSTOM + * See @ref external_audio +*/ diff --git a/internal/mozzi_macros.h b/internal/mozzi_macros.h new file mode 100644 index 000000000..cbd021d97 --- /dev/null +++ b/internal/mozzi_macros.h @@ -0,0 +1,72 @@ +/** This file contains some macros used internally inside Mozzi. These are not meant to be useful in user code. */ + +#ifndef MOZZI_MACROS_H +#define MOZZI_MACROS_H + + +// internal dummy that should be distinct from any valid config value +#define MOZZI__INVALID_CONFIG_VALUE 9999976543210 + +// internal implementation of MOZZI_CHECK_SUPPORTED +#define MOZZI__CHECK_SUPPORTED(X, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, ...) \ + static_assert(X != MOZZI__INVALID_CONFIG_VALUE && \ + (X == A0 || X == A1 || X == A2 || X == A3 || X == A4 || X == A5 || X == A6 || X == A7 || X == A8 || X == A9 || \ + X == B0 || X == B1 || X == B2 || X == B3 || X == B4 || X == B5 || X == B6 || X == B7 || X == B8 || X == B9), "Compile time option " M " does not support value " #X " on this platform."); + +// MSVC needs this indirection for proper __VA_ARGS__ expansion. I case we ever need to support MSVC... +#define MOZZI__MACRO_EVAL(...) __VA_ARGS__ + +/** @file mozzi_internal_macros + * compile time check whether the given first value (usually specified as a #define) is among the values specified in subsequent args (up to 20) + * + * Example: @code MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_EXTERNAL) @endcode +*/ +#define MOZZI_CHECK_SUPPORTED(X, ...) MOZZI__MACRO_EVAL(MOZZI__CHECK_SUPPORTED(X, #X, __VA_ARGS__, \ + MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, \ + MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, \ + MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, \ + MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE)) + +/** Compile time check to complain if the given argument is not a power of two */ +#define MOZZI_CHECK_POW2(X) static_assert((X & (X - 1)) == 0, #X " must be a power of two"); + +#define MOZZI__IS(X, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, ...) \ + ((X == A0) || (X == A1) || (X == A2) || (X == A3) || (X == A4) || (X == A5) || (X == A6) || (X == A7) || (X == A8) || (X == A9) || (X == B0) || (X == B1) || (X == B2) || (X == B3) || (X == B4) || (X == B5) || (X == B6) || (X == B7) || (X == B8) || (X == B9)) +/** Short-hand to check if given first value is any of the following values (up to 20). + * + * (Orgignally, this macro was intended to also produce an error, should any of the values be non-defined (such as because it's a typo), but alas, the preprocessor would + * let me have that). + * + * Example: @code + * #if MOZZI_IS_ANY(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PWM, MOZZI_OUTPUT_2PIN_PWM, MOZZI_OUTPUT_EXTERNAL_TIMED) + * [...] + * #endif + * @endcode + * + * See also @ref MOZZI_CHECK_SUPPORTED, which throws an error, if the first value is not among the latter values. + */ +#define MOZZI_IS(X, ...) MOZZI__MACRO_EVAL(MOZZI__IS(X, __VA_ARGS__, \ + MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, \ + MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, \ + MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, \ + MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE, MOZZI__INVALID_CONFIG_VALUE)) + +/** Short-hand for a compile time complaint, if the given define does not have the expected value. + * + * Use this to check - and clarify - complex nested logic inside #fidefs. + * + * Example: @code + * #if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_STANDARD) + * [long difficult to read logic, with further nested #if's]] + * #else + * MOZZI_ASSERT_EQUAL(MOZZI_ANALOG_READ, MOZZI_ANALOG_NONE) + * [more complex logic] + * #endif + * @endcode + */ +#define MOZZI_ASSERT_EQUAL(X, Y) static_assert(X == Y, "Internal error in preprocessor logic: " #X " != " #Y "."); + +/** See MOZZI_ASSERT_EQUAL, but reversed */ +#define MOZZI_ASSERT_NOTEQUAL(X, Y) static_assert(X != Y, "Internal error in preprocessor logic: " #X " == " #Y "."); + +#endif diff --git a/mozzi_analog.cpp b/mozzi_analog.cpp index 927bc40a9..72fd2ab82 100644 --- a/mozzi_analog.cpp +++ b/mozzi_analog.cpp @@ -10,10 +10,10 @@ */ -#include "mozzi_config.h" #include "mozzi_analog.h" #include "hardware_defines.h" +#include "mozzi_config.h" /** NOTE: Since analog input code is heavily hardware dependent, and also heavily interweaved with AUDIO_INPUT, * it was moved to MozziGuts.cpp / MozziGuts_impl_XYZ.hpp for better maintainability. diff --git a/mozzi_config.h b/mozzi_config.h index 1f9d4dba1..ead877179 100644 --- a/mozzi_config.h +++ b/mozzi_config.h @@ -1,107 +1,18 @@ -#ifndef MOZZI_CONFIG_H -#define MOZZI_CONFIG_H -#include "hardware_defines.h" +/** TODO: Temporarily brought back to life for testing config rework. + * + * This file is not meant to stay, but removing it for good requirs the "single compilation unit". + * + * At the moment it can be used to quickly configure some options to non-default values, as shown in the commented exampales. + * Alternatively, one can include a config_examples_xyz.h file from here. + */ -/* -Edit this file if you want to choose your own configuration options. -*/ +#include "MozziConfigValues.h" +//#define MOZZI_AUDIO_MODE MOZZI_OUTPUT_2PIN_PWM +//#define MOZZI_AUDIO_CHANNELS MOZZI_STEREO +//#define MOZZI_ANALOG_READ MOZZI_ANALOG_READ_NONE -/** @ingroup core -AUDIO_MODE holds the audio mode setting. -Select STANDARD (deprecated), STANDARD_PLUS or HIFI audio output mode in the Mozzi/mozzi_config.h file with -\#define AUDIO_MODE STANDARD_PLUS or \#define AUDIO_MODE HIFI. -In Mozzi/config.h, comment one of these options in and the others out to set the audio mode. +//#include "config/config_example_avr_legacy_standard.h" -In STANDARD_PLUS mode the sample resolution is 488, -which provides some headroom above the 8 bit table resolution currently used by -the oscillators. You can look at utility/TimerOne library for more info about how -interrupt rate and pwm resolution relate. +#include "internal/config_checks_generic.h" -HIFI audio mode enables much higher quality output by combining signals from pins 9 and 10. -For HIFI mode, edit Mozzi/mozzi_config.h to contain \#define AUDIO_MODE HIFI, -and comment out \#define AUDIO_MODE STANDARD and \#define AUDIO_MODE STANDARD_PLUS. - -@note Teensy 3.* plays 12 bit audio in STANDARD or STANDARD_PLUS modes, and has no HIFI mode. -*/ -//#define AUDIO_MODE STANDARD -#define AUDIO_MODE STANDARD_PLUS -//#define AUDIO_MODE HIFI - - -/** @ingroup core -Holds the audio rate setting. -AUDIO_RATE can be \#defined as 16384 or 32768 Hertz in Mozzi/mozzi_config.h. - -Mozzi's original audio mode, now called STANDARD, uses 16384 Hz, chosen as a -compromise between the sample rate (interrupt rate) and sample bitdepth (pwm -width), which are interdependent due to the way pulse wave modulation is used to -generate the sound output. -An AUDIO_RATE of 32768 Hz works in STANDARD_PLUS and HIFI modes. -Of course, doubling the sample rate halves the amount of time available to calculate the each sample, so it -may only be useful for relatively simple sketches. The increased frequency response can also make -unwanted artefacts of low resolution synthesis calculations more apparent, so it's not always a bonus. - -Another factor which is important for Mozzi's operation is that with AUDIO_RATE -being a power of two, some internal calculations can be highly optimised for -speed. - -In STANDARD and STANDARD_PLUS modes, the sample resolution is 488, -which provides some headroom above the 8 bit table resolution currently used by -the oscillators. You can look at the TimerOne library for more info about how -interrupt rate and pwm resolution relate. - -HIFI audio mode enables much higher quality output by combining signals from pins 9 and 10. -For HIFI mode, edit Mozzi/mozzi_config.h to contain \#define AUDIO_MODE HIFI, -and comment out \#define AUDIO_MODE STANDARD and \#define AUDIO_MODE STANDARD_PLUS. - -@todo Possible option for output to R/2R DAC circuit, like -http://blog.makezine.com/2008/05/29/makeit-protodac-shield-fo/ . -Mozzi-users list has a thread on this. -*/ -#define AUDIO_RATE AUDIO_RATE_PLATFORM_DEFAULT -//#define AUDIO_RATE 16384 // default on AVR / classic Arduino -//#define AUDIO_RATE 32768 // default on most other platforms -//#define AUDIO_RATE 65536 // try on Teensy3/3.1 or other strong cpus - - -//#define USE_AUDIO_INPUT true - -/** @ingroup core -This sets which analog input channel to use for audio input, if you have uncommented -\#define USE_AUDIO_INPUT true -in mozz_config.h -@note You may have to call setupFastAnalogReads(FASTEST_ADC) after setupMozzi(), when using this. -*/ -#define AUDIO_INPUT_PIN 0 - - -/** @ingroup core -This sets allows to change from a single/mono audio output channel to -stereo output. To actually generate two channels, your updateAudio()-function -should return a StereoOutput(). Sketches returning a MonoOutput() in a stereo -config, or vice versa will continue to work, but will generate a warning a -compile time. - -@note This option superseeds the earlier STEREO_HACK, which is still available at - the time of this writing, but should not be used in new sketches. - -@note At the time of this writing, only MONO and STEREO are supported. The value of - MONO is 1 and the value of STEREO is 2, so future extensions are also expected - to set this to the number of available channels. */ -#define AUDIO_CHANNELS MONO -//#define AUDIO_CHANNELS STEREO - -/** @ingroup core -Defining this option as true in mozzi_config.h allows to completely customize the audio output, e.g. for connecting to external DACs. -For more detail, @see AudioOuput . -*/ -#define EXTERNAL_AUDIO_OUTPUT false -//#define EXTERNAL_AUDIO_OUTPUT true - -/** @ingroup core -Only used when EXTERNAL_AUDIO_OUTPUT is set to true: The resolution to use for audio samples, internally. You will usually set this to match the -output resolution of your DAC. 16 is the default value, here. Note that 16 bits is also the maximum currently supported on AVR. */ -//#define EXTERNAL_AUDIO_BITS 16 - -#endif // #ifndef MOZZI_CONFIG_H diff --git a/mozzi_pgmspace.h b/mozzi_pgmspace.h index 4620a0425..a7e4514f5 100644 --- a/mozzi_pgmspace.h +++ b/mozzi_pgmspace.h @@ -14,8 +14,8 @@ template inline T FLASH_OR_RAM_READ(T* address) { #else #include // work around missing std::is_const -template inline bool mozzi_is_const_pointer(T* x) { return false; } -template inline bool mozzi_is_const_pointer(const T* x) { return true; } +template inline bool mozzi_is_const_pointer(T* ) { return false; } +template inline bool mozzi_is_const_pointer(const T* ) { return true; } /** @ingroup core * Helper function to FLASH_OR_RAM_READ(). You do not want to call this, directly. */ template inline T mozzi_pgm_read_wrapper(const T* address) { diff --git a/mozzi_rand.cpp b/mozzi_rand.cpp index d6f2011fb..8b3c72b78 100644 --- a/mozzi_rand.cpp +++ b/mozzi_rand.cpp @@ -2,7 +2,7 @@ #include "hardware_defines.h" -#if IS_STM32() +#if IS_STM32MAPLE() //#include extern STM32ADC adc; #elif IS_ESP8266() @@ -126,7 +126,7 @@ void randSeed() { z=longRandom(); //analogReference(analog_reference_orig); // change back to original ADCSRA |= (1 << ADIE); // adc re-Enable Interrupt -#elif IS_STM32() +#elif IS_STM32MAPLE() // Unfortunately the internal temp sensor on STM32s does _not_ appear to create a lot of noise. // Ironically, the calls to calibrate help induce some random noise. You're still fairly likely to produce two equal // random seeds in two subsequent runs, however.