diff --git a/.github/actions/deps/ports/espressif/action.yml b/.github/actions/deps/ports/espressif/action.yml index 122940bb95a1..25965eb7ef04 100644 --- a/.github/actions/deps/ports/espressif/action.yml +++ b/.github/actions/deps/ports/espressif/action.yml @@ -7,6 +7,7 @@ runs: run: | echo >> $GITHUB_ENV "IDF_PATH=$GITHUB_WORKSPACE/ports/espressif/esp-idf" echo >> $GITHUB_ENV "IDF_TOOLS_PATH=$GITHUB_WORKSPACE/.idf_tools" + echo >> $GITHUB_ENV "ESP_ROM_ELF_DIR=$GITHUB_WORKSPACE/.idf_tools" shell: bash - name: Get IDF commit diff --git a/.gitmodules b/.gitmodules index 3c6aac61cadc..a5226ffafdd5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -143,7 +143,7 @@ [submodule "ports/espressif/esp-idf"] path = ports/espressif/esp-idf url = https://github.com/adafruit/esp-idf.git - branch = circuitpython-v5.3.2 + branch = circuitpython-v5.4.1 [submodule "ports/espressif/esp-protocols"] path = ports/espressif/esp-protocols url = https://github.com/adafruit/esp-protocols.git diff --git a/lib/mbedtls_config/mbedtls_config_port.h b/lib/mbedtls_config/mbedtls_config_port.h deleted file mode 100644 index 1752d946529f..000000000000 --- a/lib/mbedtls_config/mbedtls_config_port.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018-2019 Damien P. George - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef MICROPY_INCLUDED_MBEDTLS_CONFIG_H -#define MICROPY_INCLUDED_MBEDTLS_CONFIG_H - -// If you want to debug MBEDTLS uncomment the following and -// Pass 3 to mbedtls_debug_set_threshold in socket_new -// #define MBEDTLS_DEBUG_C - -// Set mbedtls configuration -#define MBEDTLS_PLATFORM_C -#define MBEDTLS_PLATFORM_MEMORY -#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS -#define MBEDTLS_DEPRECATED_REMOVED -#define MBEDTLS_ENTROPY_HARDWARE_ALT - -// Enable mbedtls modules -#define MBEDTLS_MD_C -#define MBEDTLS_MD5_C -#define MBEDTLS_SHA1_C -#define MBEDTLS_SHA256_C -#define MBEDTLS_SHA512_C -#undef MBEDTLS_HAVE_TIME_DATE - -<<<<<<<< HEAD:lib/mbedtls_config/mbedtls_config_hashlib.h -// Memory allocation hooks -#include -#include -void *m_tracked_calloc(size_t nmemb, size_t size); -void m_tracked_free(void *ptr); -#define MBEDTLS_PLATFORM_STD_CALLOC m_tracked_calloc -#define MBEDTLS_PLATFORM_STD_FREE m_tracked_free -#define MBEDTLS_PLATFORM_SNPRINTF_MACRO snprintf -======== -// Time hook -#include -time_t rp2_rtctime_seconds(time_t *timer); -#define MBEDTLS_PLATFORM_TIME_MACRO rp2_rtctime_seconds -#define MBEDTLS_PLATFORM_MS_TIME_ALT mbedtls_ms_time ->>>>>>>> v1.23.0:lib/mbedtls_config/mbedtls_config_port.h - -#include "mbedtls/check_config.h" - -#endif /* MICROPY_INCLUDED_MBEDTLS_CONFIG_H */ diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index 86e821eb442c..bdf0fd53f687 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -83,6 +83,9 @@ INC += \ -isystem esp-idf/components/esp_psram/include \ -isystem esp-idf/components/esp_ringbuf/include \ -isystem esp-idf/components/esp_rom/include \ + -isystem esp-idf/components/esp_rom/$(IDF_TARGET)/include \ + -isystem esp-idf/components/esp_rom/$(IDF_TARGET)/include/$(IDF_TARGET) \ + -isystem esp-idf/components/esp_security/include \ -isystem esp-idf/components/esp_system/include \ -isystem esp-idf/components/esp_timer/include \ -isystem esp-idf/components/esp_wifi/include \ @@ -115,6 +118,7 @@ INC += \ -isystem esp-idf/components/sdmmc/include \ -isystem esp-idf/components/soc/include \ -isystem esp-idf/components/soc/$(IDF_TARGET)/include \ + -isystem esp-idf/components/soc/$(IDF_TARGET)/register \ -isystem esp-idf/components/spi_flash/include \ -isystem esp-idf/components/usb/include \ -isystem esp-idf/components/ulp/ulp_fsm/include \ @@ -171,12 +175,12 @@ ifeq ($(DEBUG), 1) OPTIMIZATION_FLAGS ?= -Og CFLAGS += -DDEBUG endif - # You may want to enable these flags to make setting breakpoints easier. - # CFLAGS += -fno-inline -fno-ipa-sra +# You may want to enable these flags to make setting breakpoints easier. +# CFLAGS += -fno-inline -fno-ipa-sra else CFLAGS += -DNDEBUG - # RISC-V is larger than xtensa - # Use -Os for RISC-V when it overflows +# RISC-V is larger than xtensa +# Use -Os for RISC-V when it overflows ifeq ($(IDF_TARGET_ARCH),riscv) OPTIMIZATION_FLAGS ?= -Os else @@ -238,7 +242,6 @@ ifeq ($(IDF_TARGET),esp32) LDFLAGS += \ -Tesp32.rom.newlib-data.ld \ -Tesp32.rom.newlib-funcs.ld \ - -Tesp32.rom.newlib-time.ld \ -Tesp32.rom.spiflash_legacy.ld CHIP_COMPONENTS = \ @@ -261,7 +264,6 @@ CHIP_COMPONENTS = \ else ifeq ($(IDF_TARGET),esp32c3) LDFLAGS += \ -Tesp32c3.rom.newlib.ld \ - -Tesp32c3.rom.newlib-time.ld \ -Tesp32c3.rom.version.ld \ -Tesp32c3.rom.eco3.ld \ -Tesp32c3.rom.bt_funcs.ld @@ -308,7 +310,6 @@ else ifeq ($(IDF_TARGET),esp32s2) LDFLAGS += \ -Tesp32s2.rom.newlib-data.ld \ -Tesp32s2.rom.newlib-funcs.ld \ - -Tesp32s2.rom.newlib-time.ld \ -Tesp32s2.rom.spiflash_legacy.ld CHIP_COMPONENTS = \ @@ -392,7 +393,7 @@ SRC_C += \ peripherals/$(IDF_TARGET)/pins.c ifeq ($(CIRCUITPY_SSL),1) -SRC_C += lib/mbedtls_config/crt_bundle.c +SRC_C += common-hal/ssl/crt_bundle.c endif SRC_C += $(wildcard common-hal/espidf/*.c) @@ -603,7 +604,7 @@ endif ESP_IDF_COMPONENTS_LINK = $(IDF_TARGET_ARCH) $(CHIP_COMPONENTS) app_update bootloader_support driver esp_driver_gpio esp_driver_gptimer esp_driver_i2c esp_driver_ledc esp_driver_spi esp_driver_uart efuse esp_adc esp_app_format esp_common esp_event esp_hw_support esp_mm esp_partition esp_pm esp_ringbuf esp_rom esp_system esp_timer freertos hal heap log newlib nvs_flash pthread soc spi_flash vfs esp_vfs_console ifneq ($(CIRCUITPY_WIFI),0) - ESP_IDF_COMPONENTS_LINK += esp_coex esp_netif esp-tls esp_wifi lwip mbedtls mdns wpa_supplicant esp_phy + ESP_IDF_COMPONENTS_LINK += esp_coex esp_netif esp_security esp-tls esp_wifi lwip mbedtls mdns wpa_supplicant esp_phy endif ifneq ($(CIRCUITPY_BLEIO_NATIVE),0) BLE_IMPL_esp32 := esp32 @@ -614,10 +615,10 @@ ifneq ($(CIRCUITPY_BLEIO_NATIVE),0) BLE_IMPL_esp32h2 := libble BLE_IMPL = $(BLE_IMPL_$(IDF_TARGET)) - ESP_IDF_COMPONENTS_LINK += bt esp_phy + ESP_IDF_COMPONENTS_LINK += bt esp_phy esp_security ifeq ($(BLE_IMPL),esp32) - # BLE will hang the ESP32 and trigger an interrupt watchdog without this undefined symbol at - # link because a weak version of the interrupt that BLE uses will be linked incorrectly. +# BLE will hang the ESP32 and trigger an interrupt watchdog without this undefined symbol at +# link because a weak version of the interrupt that BLE uses will be linked incorrectly. REGISTRATION_FUNCTIONS += -u ld_include_hli_vectors_bt BINARY_BLOBS += esp-idf/components/bt/controller/lib_esp32/$(IDF_TARGET)/libbtdm_app.a endif @@ -636,6 +637,9 @@ ifneq ($(CIRCUITPY_BLEIO_NATIVE),0) endif endif endif +ifeq ($(IDF_TARGET),esp32p4) + ESP_IDF_COMPONENTS_LINK += esp_security +endif ifneq ($(CIRCUITPY_ESPULP),0) ESP_IDF_COMPONENTS_LINK += ulp endif diff --git a/ports/espressif/boards/mixgo_ce_udisk/mpconfigboard.mk b/ports/espressif/boards/mixgo_ce_udisk/mpconfigboard.mk index 691da625e035..a6a8cd7fa00e 100644 --- a/ports/espressif/boards/mixgo_ce_udisk/mpconfigboard.mk +++ b/ports/espressif/boards/mixgo_ce_udisk/mpconfigboard.mk @@ -10,6 +10,7 @@ CIRCUITPY_ESP_FLASH_FREQ = 80m CIRCUITPY_ESP_FLASH_SIZE = 4MB CIRCUITPY_AESIO = 0 +CIRCUITPY_CANIO = 0 CIRCUITPY_CODEOP = 0 CIRCUITPY_ESPCAMERA = 0 diff --git a/ports/espressif/common-hal/ssl/crt_bundle.c b/ports/espressif/common-hal/ssl/crt_bundle.c new file mode 100644 index 000000000000..53a07f4db1b2 --- /dev/null +++ b/ports/espressif/common-hal/ssl/crt_bundle.c @@ -0,0 +1,37 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +// In ESP-IDF v5.4, Espressif changed the format of the in-flash cert bundle, which +// made lib/mbedtls_config/crt_bundle.c no longer work. Rather than update that, +// just wrap those functions and use the ESP-IDF versions. + +#include "py/mperrno.h" +#include "mbedtls/x509_crt.h" +#include "lib/mbedtls_config/crt_bundle.h" +#include "esp_crt_bundle.h" + +static int convert_esp_err(esp_err_t ret) { + switch (ret) { + case ESP_OK: + return 0; + default: + // Right now esp_crt_bundle.c doesn't return very specific errors. + case ESP_ERR_INVALID_ARG: + return -MP_EINVAL; + } +} + +int crt_bundle_attach(mbedtls_ssl_config *ssl_conf) { + return convert_esp_err(esp_crt_bundle_attach(ssl_conf)); +} + +void crt_bundle_detach(mbedtls_ssl_config *conf) { + esp_crt_bundle_detach(conf); +} + +int crt_bundle_set(const uint8_t *x509_bundle, size_t bundle_size) { + return convert_esp_err(esp_crt_bundle_set(x509_bundle, bundle_size)); +} diff --git a/ports/espressif/esp-idf b/ports/espressif/esp-idf index f388200aba73..f50ec8ecdb31 160000 --- a/ports/espressif/esp-idf +++ b/ports/espressif/esp-idf @@ -1 +1 @@ -Subproject commit f388200aba73826b9039be6f4fb5385c6b7136c6 +Subproject commit f50ec8ecdb31f681e6a778f145de95f849c1089d diff --git a/ports/espressif/esp-idf-config/sdkconfig-esp32s2.defaults b/ports/espressif/esp-idf-config/sdkconfig-esp32s2.defaults index 66e06667a1a8..5c748bd0e6f0 100644 --- a/ports/espressif/esp-idf-config/sdkconfig-esp32s2.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig-esp32s2.defaults @@ -58,6 +58,12 @@ CONFIG_ULP_COPROC_TYPE_RISCV=y # Note: enabling both ULPs simultaneously only w CONFIG_ULP_COPROC_RESERVE_MEM=8176 # end of Ultra Low Power (ULP) Co-processor +# +# FreeRTOS +# +CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y +# end of FreeRTOS + # end of Component config # end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/esp-idf-config/sdkconfig-flash-120m.defaults b/ports/espressif/esp-idf-config/sdkconfig-flash-120m.defaults index fadf55d8d796..6a2285a2936a 100644 --- a/ports/espressif/esp-idf-config/sdkconfig-flash-120m.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig-flash-120m.defaults @@ -1,15 +1,2 @@ CONFIG_ESPTOOLPY_FLASHFREQ_120M=y -# CONFIG_ESPTOOLPY_FLASHFREQ_80M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_64M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_60M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_48M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_40M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_32M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_30M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_26M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_24M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_16M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_15M is not set -CONFIG_ESPTOOLPY_FLASHFREQ_80M_DEFAULT=y CONFIG_SPI_FLASH_UNDER_HIGH_FREQ=y diff --git a/ports/espressif/esp-idf-config/sdkconfig-flash-40m.defaults b/ports/espressif/esp-idf-config/sdkconfig-flash-40m.defaults index 235a62a57ee0..ffc4b5c1cb8f 100644 --- a/ports/espressif/esp-idf-config/sdkconfig-flash-40m.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig-flash-40m.defaults @@ -1,14 +1 @@ -# CONFIG_ESPTOOLPY_FLASHFREQ_120M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_80M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_64M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_60M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_48M is not set CONFIG_ESPTOOLPY_FLASHFREQ_40M=y -# CONFIG_ESPTOOLPY_FLASHFREQ_32M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_30M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_26M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_24M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_16M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_15M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_80M_DEFAULT is not set diff --git a/ports/espressif/esp-idf-config/sdkconfig-flash-48m.defaults b/ports/espressif/esp-idf-config/sdkconfig-flash-48m.defaults index b710fd22554e..f6838e88703b 100644 --- a/ports/espressif/esp-idf-config/sdkconfig-flash-48m.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig-flash-48m.defaults @@ -1,14 +1 @@ -# CONFIG_ESPTOOLPY_FLASHFREQ_120M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_80M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_64M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_60M is not set CONFIG_ESPTOOLPY_FLASHFREQ_48M=y -# CONFIG_ESPTOOLPY_FLASHFREQ_40M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_32M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_30M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_26M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_24M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_16M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_15M is not set -CONFIG_ESPTOOLPY_FLASHFREQ_48M_DEFAULT=y diff --git a/ports/espressif/esp-idf-config/sdkconfig-flash-60m.defaults b/ports/espressif/esp-idf-config/sdkconfig-flash-60m.defaults index 7068d0ce7408..797665f527fb 100644 --- a/ports/espressif/esp-idf-config/sdkconfig-flash-60m.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig-flash-60m.defaults @@ -1,14 +1 @@ -# CONFIG_ESPTOOLPY_FLASHFREQ_120M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_80M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_64M is not set CONFIG_ESPTOOLPY_FLASHFREQ_60M=y -# CONFIG_ESPTOOLPY_FLASHFREQ_48M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_40M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_32M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_30M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_26M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_24M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_16M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_15M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_80M_DEFAULT is not set diff --git a/ports/espressif/esp-idf-config/sdkconfig-flash-80m.defaults b/ports/espressif/esp-idf-config/sdkconfig-flash-80m.defaults index 2ea441900379..7014fa95495b 100644 --- a/ports/espressif/esp-idf-config/sdkconfig-flash-80m.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig-flash-80m.defaults @@ -1,14 +1 @@ -# CONFIG_ESPTOOLPY_FLASHFREQ_120M is not set CONFIG_ESPTOOLPY_FLASHFREQ_80M=y -# CONFIG_ESPTOOLPY_FLASHFREQ_64M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_60M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_48M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_40M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_32M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_30M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_26M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_24M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_16M is not set -# CONFIG_ESPTOOLPY_FLASHFREQ_15M is not set -CONFIG_ESPTOOLPY_FLASHFREQ_80M_DEFAULT=y diff --git a/ports/espressif/mpconfigport.mk b/ports/espressif/mpconfigport.mk index 81ad652bb236..ccc305761c4d 100644 --- a/ports/espressif/mpconfigport.mk +++ b/ports/espressif/mpconfigport.mk @@ -181,6 +181,9 @@ CIRCUITPY_TOUCHIO_USE_NATIVE = 0 CIRCUITPY_USB_DEVICE = 0 CIRCUITPY_ESP_USB_SERIAL_JTAG ?= 1 +# Remove temporarily until 10265 is merged +CIRCUITPY_ULAB = 0 + else ifeq ($(IDF_TARGET),esp32h2) # Modules CIRCUITPY_ESPCAMERA = 0 diff --git a/ports/espressif/mphalport.c b/ports/espressif/mphalport.c index 87b16ac3e4ca..d6a7ef1bfce4 100644 --- a/ports/espressif/mphalport.c +++ b/ports/espressif/mphalport.c @@ -8,25 +8,7 @@ #include "py/mphal.h" #include "supervisor/cpu.h" -#if defined(CONFIG_IDF_TARGET_ESP32) -#include "components/esp_rom/include/esp32/rom/ets_sys.h" -#elif defined(CONFIG_IDF_TARGET_ESP32C2) -#include "components/esp_rom/include/esp32c2/rom/ets_sys.h" -#elif defined(CONFIG_IDF_TARGET_ESP32P4) -#include "components/esp_rom/include/esp32p4/rom/ets_sys.h" -#elif defined(CONFIG_IDF_TARGET_ESP32C3) -#include "components/esp_rom/include/esp32c3/rom/ets_sys.h" -#elif defined(CONFIG_IDF_TARGET_ESP32C6) -#include "components/esp_rom/include/esp32c6/rom/ets_sys.h" -#elif defined(CONFIG_IDF_TARGET_ESP32H2) -#include "components/esp_rom/include/esp32h2/rom/ets_sys.h" -#elif defined(CONFIG_IDF_TARGET_ESP32S2) -#include "components/esp_rom/include/esp32s2/rom/ets_sys.h" -#elif defined(CONFIG_IDF_TARGET_ESP32S3) -#include "components/esp_rom/include/esp32s3/rom/ets_sys.h" -#else -#error Unknown CONFIG_IDF_TARGET_xxx -#endif +#include "rom/ets_sys.h" #include "esp_attr.h" diff --git a/ports/espressif/supervisor/port.c b/ports/espressif/supervisor/port.c index c99b1ca87dee..e72eab7e32a5 100644 --- a/ports/espressif/supervisor/port.c +++ b/ports/espressif/supervisor/port.c @@ -190,8 +190,8 @@ static void _never_reset_spi_ram_flash(void) { const uint32_t spiconfig = esp_rom_efuse_get_flash_gpio_info(); if (spiconfig == ESP_ROM_EFUSE_FLASH_DEFAULT_SPI) { - never_reset_pin_number(SPI_IOMUX_PIN_NUM_CLK); - never_reset_pin_number(SPI_IOMUX_PIN_NUM_CS); + never_reset_pin_number(MSPI_IOMUX_PIN_NUM_CLK); + never_reset_pin_number(MSPI_IOMUX_PIN_NUM_CS0); never_reset_pin_number(PSRAM_SPIQ_SD0_IO); never_reset_pin_number(PSRAM_SPID_SD1_IO); never_reset_pin_number(PSRAM_SPIWP_SD3_IO); diff --git a/ports/espressif/supervisor/usb.c b/ports/espressif/supervisor/usb.c index c2bf90b21b1f..612abaa808ae 100644 --- a/ports/espressif/supervisor/usb.c +++ b/ports/espressif/supervisor/usb.c @@ -20,15 +20,7 @@ #include "driver/gpio.h" #include "esp_private/periph_ctrl.h" -#if defined(CONFIG_IDF_TARGET_ESP32C3) -#include "components/esp_rom/include/esp32c3/rom/gpio.h" -#elif defined(CONFIG_IDF_TARGET_ESP32C6) -#include "components/esp_rom/include/esp32c6/rom/gpio.h" -#elif defined(CONFIG_IDF_TARGET_ESP32S2) -#include "components/esp_rom/include/esp32s2/rom/gpio.h" -#elif defined(CONFIG_IDF_TARGET_ESP32S3) -#include "components/esp_rom/include/esp32s3/rom/gpio.h" -#endif +#include "rom/gpio.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" diff --git a/ports/raspberrypi/boards/adafruit_feather_rp2350_adalogger/board.c b/ports/raspberrypi/boards/adafruit_feather_rp2350_adalogger/board.c new file mode 100644 index 000000000000..e6a868ab2122 --- /dev/null +++ b/ports/raspberrypi/boards/adafruit_feather_rp2350_adalogger/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/adafruit_feather_rp2350_adalogger/mpconfigboard.h b/ports/raspberrypi/boards/adafruit_feather_rp2350_adalogger/mpconfigboard.h new file mode 100644 index 000000000000..b77664cf85c1 --- /dev/null +++ b/ports/raspberrypi/boards/adafruit_feather_rp2350_adalogger/mpconfigboard.h @@ -0,0 +1,29 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#define MICROPY_HW_BOARD_NAME "Adafruit Feather RP2350 Adalogger" +#define MICROPY_HW_MCU_NAME "rp2350a" + +#define MICROPY_HW_NEOPIXEL (&pin_GPIO21) + +#define DEFAULT_I2C_BUS_SCL (&pin_GPIO3) +#define DEFAULT_I2C_BUS_SDA (&pin_GPIO2) + +#define DEFAULT_SPI_BUS_SCK (&pin_GPIO22) +#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO23) +#define DEFAULT_SPI_BUS_MISO (&pin_GPIO20) + +#define DEFAULT_UART_BUS_RX (&pin_GPIO1) +#define DEFAULT_UART_BUS_TX (&pin_GPIO0) + +#define CIRCUITPY_PSRAM_CHIP_SELECT (&pin_GPIO8) + +#define DEFAULT_SD_SCK (&pin_GPIO14) +#define DEFAULT_SD_MOSI (&pin_GPIO15) +#define DEFAULT_SD_MISO (&pin_GPIO16) +#define DEFAULT_SD_CS (&pin_GPIO19) +#define DEFAULT_SD_CARD_DETECT (&pin_GPIO13) +#define DEFAULT_SD_CARD_INSERTED false diff --git a/ports/raspberrypi/boards/adafruit_feather_rp2350_adalogger/mpconfigboard.mk b/ports/raspberrypi/boards/adafruit_feather_rp2350_adalogger/mpconfigboard.mk new file mode 100644 index 000000000000..88cce7c4a4ce --- /dev/null +++ b/ports/raspberrypi/boards/adafruit_feather_rp2350_adalogger/mpconfigboard.mk @@ -0,0 +1,10 @@ +USB_VID = 0x239A +USB_PID = 0x816E +USB_PRODUCT = "Feather RP2350 Adalogger" +USB_MANUFACTURER = "Adafruit" + +CHIP_VARIANT = RP2350 +CHIP_PACKAGE = A +CHIP_FAMILY = rp2 + +EXTERNAL_FLASH_DEVICES = "GD25Q64C,W25Q64JVxQ" diff --git a/ports/raspberrypi/boards/adafruit_feather_rp2350_adalogger/pico-sdk-configboard.h b/ports/raspberrypi/boards/adafruit_feather_rp2350_adalogger/pico-sdk-configboard.h new file mode 100644 index 000000000000..2d9283a9192f --- /dev/null +++ b/ports/raspberrypi/boards/adafruit_feather_rp2350_adalogger/pico-sdk-configboard.h @@ -0,0 +1,10 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +// Put board-specific pico-sdk definitions here. This file must exist. + +// Allow extra time for xosc to start. +#define PICO_XOSC_STARTUP_DELAY_MULTIPLIER 64 diff --git a/ports/raspberrypi/boards/adafruit_feather_rp2350_adalogger/pins.c b/ports/raspberrypi/boards/adafruit_feather_rp2350_adalogger/pins.c new file mode 100644 index 000000000000..ac9a29abaad9 --- /dev/null +++ b/ports/raspberrypi/boards/adafruit_feather_rp2350_adalogger/pins.c @@ -0,0 +1,69 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO29) }, + + { MP_ROM_QSTR(MP_QSTR_D24), MP_ROM_PTR(&pin_GPIO24) }, + { MP_ROM_QSTR(MP_QSTR_D25), MP_ROM_PTR(&pin_GPIO25) }, + + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO23) }, + { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO20) }, + + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_GPIO1) }, + + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO0) }, + + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO3) }, + + { MP_ROM_QSTR(MP_QSTR_IO4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_D12), MP_ROM_PTR(&pin_GPIO4) }, + + { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_D11), MP_ROM_PTR(&pin_GPIO11) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_IO7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_GPIO7) }, + + + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_SD_CARD_DETECT), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_SD_CLK), MP_ROM_PTR(&pin_GPIO14) }, + + { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_SD_CMD), MP_ROM_PTR(&pin_GPIO15) }, + + { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_SD_DAT0), MP_ROM_PTR(&pin_GPIO16) }, + + { MP_ROM_QSTR(MP_QSTR_SD_DAT1), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_SD_DAT2), MP_ROM_PTR(&pin_GPIO18) }, + + { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_SD_DAT3), MP_ROM_PTR(&pin_GPIO19) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_STEMMA_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/unix/variants/coverage/mpconfigvariant.mk b/ports/unix/variants/coverage/mpconfigvariant.mk index e1924479bbf5..579e42cc05cf 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.mk +++ b/ports/unix/variants/coverage/mpconfigvariant.mk @@ -40,6 +40,7 @@ SRC_BITMAP := \ shared-bindings/audiodelays/__init__.c \ shared-bindings/audiofilters/Distortion.c \ shared-bindings/audiofilters/Filter.c \ + shared-bindings/audiofilters/Phaser.c \ shared-bindings/audiofilters/__init__.c \ shared-bindings/audiofreeverb/Freeverb.c \ shared-bindings/audiofreeverb/__init__.c \ @@ -87,6 +88,7 @@ SRC_BITMAP := \ shared-module/audiodelays/__init__.c \ shared-module/audiofilters/Distortion.c \ shared-module/audiofilters/Filter.c \ + shared-module/audiofilters/Phaser.c \ shared-module/audiofilters/__init__.c \ shared-module/audiofreeverb/Freeverb.c \ shared-module/audiofreeverb/__init__.c \ diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index fa90481e648e..f73d52a6c4b1 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -674,6 +674,7 @@ SRC_SHARED_MODULE_ALL = \ audiodelays/__init__.c \ audiofilters/Distortion.c \ audiofilters/Filter.c \ + audiofilters/Phaser.c \ audiofilters/__init__.c \ audiofreeverb/__init__.c \ audiofreeverb/Freeverb.c \ diff --git a/py/malloc.c b/py/malloc.c index c83dd4bd6c7d..fcf930ecfaa8 100644 --- a/py/malloc.c +++ b/py/malloc.c @@ -55,18 +55,8 @@ // example. On the other hand, some (e.g. bare-metal) ports may use GC // heap as system heap, so, to avoid warnings, we do undef's first. // CIRCUITPY-CHANGE: Add selective collect support to malloc to optimize GC for large buffers -#undef malloc #undef free #undef realloc -#if MICROPY_ENABLE_SELECTIVE_COLLECT -#define malloc(b) gc_alloc((b), GC_ALLOC_FLAG_DO_NOT_COLLECT) -#define malloc_with_collect(b) gc_alloc((b), 0) -#define malloc_without_collect(b) gc_alloc((b), GC_ALLOC_FLAG_DO_NOT_COLLECT) -#else -#define malloc(b) gc_alloc((b), 0) -#define malloc_with_collect(b) gc_alloc((b), 0) -#endif -#define malloc_with_finaliser(b) gc_alloc((b), GC_ALLOC_FLAG_HAS_FINALISER) #define free gc_free #define realloc(ptr, n) gc_realloc(ptr, n, true) #define realloc_ext(ptr, n, mv) gc_realloc(ptr, n, mv) @@ -99,15 +89,18 @@ static void *realloc_ext(void *ptr, size_t n_bytes, bool allow_move) { void *m_malloc_helper(size_t num_bytes, uint8_t flags) { void *ptr; #if MICROPY_ENABLE_GC + uint8_t gc_flags = 0; #if MICROPY_ENABLE_SELECTIVE_COLLECT if ((flags & M_MALLOC_COLLECT) == 0) { - ptr = malloc_without_collect(num_bytes); - } else { - ptr = malloc_with_collect(num_bytes); + gc_flags |= GC_ALLOC_FLAG_DO_NOT_COLLECT; + } + #endif + #if MICROPY_ENABLE_FINALISER + if ((flags & M_MALLOC_WITH_FINALISER) != 0) { + gc_flags |= GC_ALLOC_FLAG_HAS_FINALISER; } - #else - ptr = malloc_with_collect(num_bytes); #endif + ptr = gc_alloc(num_bytes, gc_flags); #else ptr = malloc(num_bytes); #endif diff --git a/shared-bindings/audiofilters/Phaser.c b/shared-bindings/audiofilters/Phaser.c new file mode 100644 index 000000000000..e7ddd986176b --- /dev/null +++ b/shared-bindings/audiofilters/Phaser.c @@ -0,0 +1,287 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Cooper Dalrymple +// +// SPDX-License-Identifier: MIT + +#include + +#include "shared-bindings/audiofilters/Phaser.h" +#include "shared-bindings/audiocore/__init__.h" +#include "shared-module/audiofilters/Phaser.h" + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/util.h" +#include "shared-module/synthio/block.h" + +//| class Phaser: +//| """A Phaser effect""" +//| +//| def __init__( +//| self, +//| frequency: synthio.BlockInput = 1000.0, +//| feedback: synthio.BlockInput = 0.7, +//| mix: synthio.BlockInput = 1.0, +//| stages: int = 6, +//| buffer_size: int = 512, +//| sample_rate: int = 8000, +//| bits_per_sample: int = 16, +//| samples_signed: bool = True, +//| channel_count: int = 1, +//| ) -> None: +//| """Create a Phaser effect where the original sample is processed through a variable +//| number of all-pass filter stages. This slightly delays the signal so that it is out +//| of phase with the original signal. When the amount of phase is modulated and mixed +//| back into the original signal with the mix parameter, it creates a distinctive +//| phasing sound. +//| +//| :param synthio.BlockInput frequency: The target frequency which is affected by the effect in hz. +//| :param int stages: The number of all-pass filters which will be applied to the signal. +//| :param synthio.BlockInput feedback: The amount that the previous output of the filters is mixed back into their input along with the unprocessed signal. +//| :param synthio.BlockInput mix: The mix as a ratio of the sample (0.0) to the effect (1.0). +//| :param int buffer_size: The total size in bytes of each of the two playback buffers to use +//| :param int sample_rate: The sample rate to be used +//| :param int channel_count: The number of channels the source samples contain. 1 = mono; 2 = stereo. +//| :param int bits_per_sample: The bits per sample of the effect +//| :param bool samples_signed: Effect is signed (True) or unsigned (False) +//| +//| Playing adding a phaser to a synth:: +//| +//| import time +//| import board +//| import audiobusio +//| import audiofilters +//| import synthio +//| +//| audio = audiobusio.I2SOut(bit_clock=board.GP20, word_select=board.GP21, data=board.GP22) +//| synth = synthio.Synthesizer(channel_count=1, sample_rate=44100) +//| effect = audiofilters.Phaser(channel_count=1, sample_rate=44100) +//| effect.frequency = synthio.LFO(offset=1000.0, scale=600.0, rate=0.5) +//| effect.play(synth) +//| audio.play(effect) +//| +//| synth.press(48)""" +//| ... +//| +static mp_obj_t audiofilters_phaser_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_frequency, ARG_feedback, ARG_mix, ARG_stages, ARG_buffer_size, ARG_sample_rate, ARG_bits_per_sample, ARG_samples_signed, ARG_channel_count, }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_frequency, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(1000) } }, + { MP_QSTR_feedback, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } }, + { MP_QSTR_mix, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(1)} }, + { MP_QSTR_stages, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 6 } }, + { MP_QSTR_buffer_size, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 512} }, + { MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 8000} }, + { MP_QSTR_bits_per_sample, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 16} }, + { MP_QSTR_samples_signed, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + { MP_QSTR_channel_count, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1 } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_int_t channel_count = mp_arg_validate_int_range(args[ARG_channel_count].u_int, 1, 2, MP_QSTR_channel_count); + mp_int_t sample_rate = mp_arg_validate_int_min(args[ARG_sample_rate].u_int, 1, MP_QSTR_sample_rate); + mp_int_t bits_per_sample = args[ARG_bits_per_sample].u_int; + if (bits_per_sample != 8 && bits_per_sample != 16) { + mp_raise_ValueError(MP_ERROR_TEXT("bits_per_sample must be 8 or 16")); + } + + audiofilters_phaser_obj_t *self = mp_obj_malloc(audiofilters_phaser_obj_t, &audiofilters_phaser_type); + common_hal_audiofilters_phaser_construct(self, args[ARG_frequency].u_obj, args[ARG_feedback].u_obj, args[ARG_mix].u_obj, args[ARG_stages].u_int, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitialises the Phaser.""" +//| ... +//| +static mp_obj_t audiofilters_phaser_deinit(mp_obj_t self_in) { + audiofilters_phaser_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiofilters_phaser_deinit(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_phaser_deinit_obj, audiofilters_phaser_deinit); + +static void check_for_deinit(audiofilters_phaser_obj_t *self) { + audiosample_check_for_deinit(&self->base); +} + +//| def __enter__(self) -> Phaser: +//| """No-op used by Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +// Provided by context manager helper. + + +//| frequency: synthio.BlockInput +//| """The target frequency in hertz at which the phaser is delaying the signal.""" +static mp_obj_t audiofilters_phaser_obj_get_frequency(mp_obj_t self_in) { + return common_hal_audiofilters_phaser_get_frequency(self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_phaser_get_frequency_obj, audiofilters_phaser_obj_get_frequency); + +static mp_obj_t audiofilters_phaser_obj_set_frequency(mp_obj_t self_in, mp_obj_t frequency_in) { + audiofilters_phaser_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiofilters_phaser_set_frequency(self, frequency_in); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiofilters_phaser_set_frequency_obj, audiofilters_phaser_obj_set_frequency); + +MP_PROPERTY_GETSET(audiofilters_phaser_frequency_obj, + (mp_obj_t)&audiofilters_phaser_get_frequency_obj, + (mp_obj_t)&audiofilters_phaser_set_frequency_obj); + + +//| feedback: synthio.BlockInput +//| """The amount of which the incoming signal is fed back into the phasing filters from 0.1 to 0.9.""" +static mp_obj_t audiofilters_phaser_obj_get_feedback(mp_obj_t self_in) { + return common_hal_audiofilters_phaser_get_feedback(self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_phaser_get_feedback_obj, audiofilters_phaser_obj_get_feedback); + +static mp_obj_t audiofilters_phaser_obj_set_feedback(mp_obj_t self_in, mp_obj_t feedback_in) { + audiofilters_phaser_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiofilters_phaser_set_feedback(self, feedback_in); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiofilters_phaser_set_feedback_obj, audiofilters_phaser_obj_set_feedback); + +MP_PROPERTY_GETSET(audiofilters_phaser_feedback_obj, + (mp_obj_t)&audiofilters_phaser_get_feedback_obj, + (mp_obj_t)&audiofilters_phaser_set_feedback_obj); + + +//| mix: synthio.BlockInput +//| """The amount that the effect signal is mixed into the output between 0 and 1 where 0 is only the original sample and 1 is all effect.""" +static mp_obj_t audiofilters_phaser_obj_get_mix(mp_obj_t self_in) { + return common_hal_audiofilters_phaser_get_mix(self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_phaser_get_mix_obj, audiofilters_phaser_obj_get_mix); + +static mp_obj_t audiofilters_phaser_obj_set_mix(mp_obj_t self_in, mp_obj_t mix_in) { + audiofilters_phaser_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiofilters_phaser_set_mix(self, mix_in); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiofilters_phaser_set_mix_obj, audiofilters_phaser_obj_set_mix); + +MP_PROPERTY_GETSET(audiofilters_phaser_mix_obj, + (mp_obj_t)&audiofilters_phaser_get_mix_obj, + (mp_obj_t)&audiofilters_phaser_set_mix_obj); + + +//| stages: int +//| """The number of allpass filters to pass the signal through. More stages requires more processing but produces a more pronounced effect. Requires a minimum value of 1.""" +static mp_obj_t audiofilters_phaser_obj_get_stages(mp_obj_t self_in) { + return MP_OBJ_NEW_SMALL_INT(common_hal_audiofilters_phaser_get_stages(self_in)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_phaser_get_stages_obj, audiofilters_phaser_obj_get_stages); + +static mp_obj_t audiofilters_phaser_obj_set_stages(mp_obj_t self_in, mp_obj_t stages_in) { + audiofilters_phaser_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiofilters_phaser_set_stages(self, mp_obj_get_int(stages_in)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiofilters_phaser_set_stages_obj, audiofilters_phaser_obj_set_stages); + +MP_PROPERTY_GETSET(audiofilters_phaser_stages_obj, + (mp_obj_t)&audiofilters_phaser_get_stages_obj, + (mp_obj_t)&audiofilters_phaser_set_stages_obj); + + +//| playing: bool +//| """True when the effect is playing a sample. (read-only)""" +//| +static mp_obj_t audiofilters_phaser_obj_get_playing(mp_obj_t self_in) { + audiofilters_phaser_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_audiofilters_phaser_get_playing(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_phaser_get_playing_obj, audiofilters_phaser_obj_get_playing); + +MP_PROPERTY_GETTER(audiofilters_phaser_playing_obj, + (mp_obj_t)&audiofilters_phaser_get_playing_obj); + +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| """Plays the sample once when loop=False and continuously when loop=True. +//| Does not block. Use `playing` to block. +//| +//| The sample must match the encoding settings given in the constructor.""" +//| ... +//| +static mp_obj_t audiofilters_phaser_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_sample, ARG_loop }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED, {} }, + { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + audiofilters_phaser_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + + mp_obj_t sample = args[ARG_sample].u_obj; + common_hal_audiofilters_phaser_play(self, sample, args[ARG_loop].u_bool); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audiofilters_phaser_play_obj, 1, audiofilters_phaser_obj_play); + +//| def stop(self) -> None: +//| """Stops playback of the sample.""" +//| ... +//| +//| +static mp_obj_t audiofilters_phaser_obj_stop(mp_obj_t self_in) { + audiofilters_phaser_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_audiofilters_phaser_stop(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_phaser_stop_obj, audiofilters_phaser_obj_stop); + +static const mp_rom_map_elem_t audiofilters_phaser_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audiofilters_phaser_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&default___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audiofilters_phaser_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audiofilters_phaser_stop_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiofilters_phaser_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&audiofilters_phaser_frequency_obj) }, + { MP_ROM_QSTR(MP_QSTR_feedback), MP_ROM_PTR(&audiofilters_phaser_feedback_obj) }, + { MP_ROM_QSTR(MP_QSTR_mix), MP_ROM_PTR(&audiofilters_phaser_mix_obj) }, + { MP_ROM_QSTR(MP_QSTR_stages), MP_ROM_PTR(&audiofilters_phaser_stages_obj) }, + AUDIOSAMPLE_FIELDS, +}; +static MP_DEFINE_CONST_DICT(audiofilters_phaser_locals_dict, audiofilters_phaser_locals_dict_table); + +static const audiosample_p_t audiofilters_phaser_proto = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_audiosample) + .reset_buffer = (audiosample_reset_buffer_fun)audiofilters_phaser_reset_buffer, + .get_buffer = (audiosample_get_buffer_fun)audiofilters_phaser_get_buffer, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + audiofilters_phaser_type, + MP_QSTR_Phaser, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, audiofilters_phaser_make_new, + locals_dict, &audiofilters_phaser_locals_dict, + protocol, &audiofilters_phaser_proto + ); diff --git a/shared-bindings/audiofilters/Phaser.h b/shared-bindings/audiofilters/Phaser.h new file mode 100644 index 000000000000..dbab22f57102 --- /dev/null +++ b/shared-bindings/audiofilters/Phaser.h @@ -0,0 +1,34 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Cooper Dalrymple +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-module/audiofilters/Phaser.h" + +extern const mp_obj_type_t audiofilters_phaser_type; + +void common_hal_audiofilters_phaser_construct(audiofilters_phaser_obj_t *self, + mp_obj_t frequency, mp_obj_t feedback, mp_obj_t mix, uint8_t stages, + uint32_t buffer_size, uint8_t bits_per_sample, bool samples_signed, + uint8_t channel_count, uint32_t sample_rate); + +void common_hal_audiofilters_phaser_deinit(audiofilters_phaser_obj_t *self); + +mp_obj_t common_hal_audiofilters_phaser_get_frequency(audiofilters_phaser_obj_t *self); +void common_hal_audiofilters_phaser_set_frequency(audiofilters_phaser_obj_t *self, mp_obj_t arg); + +mp_obj_t common_hal_audiofilters_phaser_get_feedback(audiofilters_phaser_obj_t *self); +void common_hal_audiofilters_phaser_set_feedback(audiofilters_phaser_obj_t *self, mp_obj_t arg); + +mp_obj_t common_hal_audiofilters_phaser_get_mix(audiofilters_phaser_obj_t *self); +void common_hal_audiofilters_phaser_set_mix(audiofilters_phaser_obj_t *self, mp_obj_t arg); + +uint8_t common_hal_audiofilters_phaser_get_stages(audiofilters_phaser_obj_t *self); +void common_hal_audiofilters_phaser_set_stages(audiofilters_phaser_obj_t *self, uint8_t arg); + +bool common_hal_audiofilters_phaser_get_playing(audiofilters_phaser_obj_t *self); +void common_hal_audiofilters_phaser_play(audiofilters_phaser_obj_t *self, mp_obj_t sample, bool loop); +void common_hal_audiofilters_phaser_stop(audiofilters_phaser_obj_t *self); diff --git a/shared-bindings/audiofilters/__init__.c b/shared-bindings/audiofilters/__init__.c index 7a17ec655e62..ae43af9bfef8 100644 --- a/shared-bindings/audiofilters/__init__.c +++ b/shared-bindings/audiofilters/__init__.c @@ -12,6 +12,7 @@ #include "shared-bindings/audiofilters/__init__.h" #include "shared-bindings/audiofilters/Distortion.h" #include "shared-bindings/audiofilters/Filter.h" +#include "shared-bindings/audiofilters/Phaser.h" //| """Support for audio filter effects //| @@ -23,6 +24,7 @@ static const mp_rom_map_elem_t audiofilters_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audiofilters) }, { MP_ROM_QSTR(MP_QSTR_Filter), MP_ROM_PTR(&audiofilters_filter_type) }, { MP_ROM_QSTR(MP_QSTR_Distortion), MP_ROM_PTR(&audiofilters_distortion_type) }, + { MP_ROM_QSTR(MP_QSTR_Phaser), MP_ROM_PTR(&audiofilters_phaser_type) }, // Enum-like Classes. { MP_ROM_QSTR(MP_QSTR_DistortionMode), MP_ROM_PTR(&audiofilters_distortion_mode_type) }, diff --git a/shared-module/audiofilters/Phaser.c b/shared-module/audiofilters/Phaser.c new file mode 100644 index 000000000000..81d9c0bea308 --- /dev/null +++ b/shared-module/audiofilters/Phaser.c @@ -0,0 +1,302 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Cooper Dalrymple +// +// SPDX-License-Identifier: MIT +#include "shared-bindings/audiofilters/Phaser.h" +#include "shared-bindings/audiocore/__init__.h" + +#include +#include "py/runtime.h" + +void common_hal_audiofilters_phaser_construct(audiofilters_phaser_obj_t *self, + mp_obj_t frequency, mp_obj_t feedback, mp_obj_t mix, uint8_t stages, + uint32_t buffer_size, uint8_t bits_per_sample, + bool samples_signed, uint8_t channel_count, uint32_t sample_rate) { + + // Basic settings every effect and audio sample has + // These are the effects values, not the source sample(s) + self->base.bits_per_sample = bits_per_sample; // Most common is 16, but 8 is also supported in many places + self->base.samples_signed = samples_signed; // Are the samples we provide signed (common is true) + self->base.channel_count = channel_count; // Channels can be 1 for mono or 2 for stereo + self->base.sample_rate = sample_rate; // Sample rate for the effect, this generally needs to match all audio objects + self->base.single_buffer = false; + self->base.max_buffer_length = buffer_size; + + // To smooth things out as CircuitPython is doing other tasks most audio objects have a buffer + // A double buffer is set up here so the audio output can use DMA on buffer 1 while we + // write to and create buffer 2. + // This buffer is what is passed to the audio component that plays the effect. + // Samples are set sequentially. For stereo audio they are passed L/R/L/R/... + self->buffer_len = buffer_size; // in bytes + + self->buffer[0] = m_malloc_without_collect(self->buffer_len); + memset(self->buffer[0], 0, self->buffer_len); + + self->buffer[1] = m_malloc_without_collect(self->buffer_len); + memset(self->buffer[1], 0, self->buffer_len); + + self->last_buf_idx = 1; // Which buffer to use first, toggle between 0 and 1 + + // Initialize other values most effects will need. + self->sample = NULL; // The current playing sample + self->sample_remaining_buffer = NULL; // Pointer to the start of the sample buffer we have not played + self->sample_buffer_length = 0; // How many samples do we have left to play (these may be 16 bit!) + self->loop = false; // When the sample is done do we loop to the start again or stop (e.g. in a wav file) + self->more_data = false; // Is there still more data to read from the sample or did we finish + + // The below section sets up the effect's starting values. + + // Create buffer to hold the last processed word + self->word_buffer = m_malloc_without_collect(self->base.channel_count * sizeof(int16_t)); + memset(self->word_buffer, 0, self->base.channel_count * sizeof(int16_t)); + + self->nyquist = (mp_float_t)self->base.sample_rate / 2; + + if (feedback == mp_const_none) { + feedback = mp_obj_new_float(MICROPY_FLOAT_CONST(0.7)); + } + + synthio_block_assign_slot(frequency, &self->frequency, MP_QSTR_frequency); + synthio_block_assign_slot(feedback, &self->feedback, MP_QSTR_feedback); + synthio_block_assign_slot(mix, &self->mix, MP_QSTR_mix); + + common_hal_audiofilters_phaser_set_stages(self, stages); +} + +void common_hal_audiofilters_phaser_deinit(audiofilters_phaser_obj_t *self) { + audiosample_mark_deinit(&self->base); + self->buffer[0] = NULL; + self->buffer[1] = NULL; + self->word_buffer = NULL; + self->allpass_buffer = NULL; +} + +mp_obj_t common_hal_audiofilters_phaser_get_frequency(audiofilters_phaser_obj_t *self) { + return self->frequency.obj; +} + +void common_hal_audiofilters_phaser_set_frequency(audiofilters_phaser_obj_t *self, mp_obj_t arg) { + synthio_block_assign_slot(arg, &self->frequency, MP_QSTR_frequency); +} + +mp_obj_t common_hal_audiofilters_phaser_get_feedback(audiofilters_phaser_obj_t *self) { + return self->feedback.obj; +} + +void common_hal_audiofilters_phaser_set_feedback(audiofilters_phaser_obj_t *self, mp_obj_t arg) { + synthio_block_assign_slot(arg, &self->feedback, MP_QSTR_feedback); +} + +mp_obj_t common_hal_audiofilters_phaser_get_mix(audiofilters_phaser_obj_t *self) { + return self->mix.obj; +} + +void common_hal_audiofilters_phaser_set_mix(audiofilters_phaser_obj_t *self, mp_obj_t arg) { + synthio_block_assign_slot(arg, &self->mix, MP_QSTR_mix); +} + +uint8_t common_hal_audiofilters_phaser_get_stages(audiofilters_phaser_obj_t *self) { + return self->stages; +} + +void common_hal_audiofilters_phaser_set_stages(audiofilters_phaser_obj_t *self, uint8_t arg) { + if (!arg) { + arg = 1; + } + + self->allpass_buffer = (int16_t *)m_realloc(self->allpass_buffer, + #if MICROPY_MALLOC_USES_ALLOCATED_SIZE + self->base.channel_count * self->stages * sizeof(int16_t), // Old size + #endif + self->base.channel_count * arg * sizeof(int16_t)); + self->stages = arg; + + memset(self->allpass_buffer, 0, self->base.channel_count * self->stages * sizeof(int16_t)); +} + +void audiofilters_phaser_reset_buffer(audiofilters_phaser_obj_t *self, + bool single_channel_output, + uint8_t channel) { + + memset(self->buffer[0], 0, self->buffer_len); + memset(self->buffer[1], 0, self->buffer_len); + memset(self->word_buffer, 0, self->base.channel_count * sizeof(int16_t)); + memset(self->allpass_buffer, 0, self->base.channel_count * self->stages * sizeof(int16_t)); +} + +bool common_hal_audiofilters_phaser_get_playing(audiofilters_phaser_obj_t *self) { + return self->sample != NULL; +} + +void common_hal_audiofilters_phaser_play(audiofilters_phaser_obj_t *self, mp_obj_t sample, bool loop) { + audiosample_must_match(&self->base, sample); + + self->sample = sample; + self->loop = loop; + + audiosample_reset_buffer(self->sample, false, 0); + audioio_get_buffer_result_t result = audiosample_get_buffer(self->sample, false, 0, (uint8_t **)&self->sample_remaining_buffer, &self->sample_buffer_length); + + // Track remaining sample length in terms of bytes per sample + self->sample_buffer_length /= (self->base.bits_per_sample / 8); + // Store if we have more data in the sample to retrieve + self->more_data = result == GET_BUFFER_MORE_DATA; + + return; +} + +void common_hal_audiofilters_phaser_stop(audiofilters_phaser_obj_t *self) { + // When the sample is set to stop playing do any cleanup here + self->sample = NULL; + return; +} + +audioio_get_buffer_result_t audiofilters_phaser_get_buffer(audiofilters_phaser_obj_t *self, bool single_channel_output, uint8_t channel, + uint8_t **buffer, uint32_t *buffer_length) { + (void)channel; + + if (!single_channel_output) { + channel = 0; + } + + // Switch our buffers to the other buffer + self->last_buf_idx = !self->last_buf_idx; + + // If we are using 16 bit samples we need a 16 bit pointer, 8 bit needs an 8 bit pointer + int16_t *word_buffer = (int16_t *)self->buffer[self->last_buf_idx]; + int8_t *hword_buffer = self->buffer[self->last_buf_idx]; + uint32_t length = self->buffer_len / (self->base.bits_per_sample / 8); + + // Loop over the entire length of our buffer to fill it, this may require several calls to get data from the sample + while (length != 0) { + // Check if there is no more sample to play, we will either load more data, reset the sample if loop is on or clear the sample + if (self->sample_buffer_length == 0) { + if (!self->more_data) { // The sample has indicated it has no more data to play + if (self->loop && self->sample) { // If we are supposed to loop reset the sample to the start + audiosample_reset_buffer(self->sample, false, 0); + } else { // If we were not supposed to loop the sample, stop playing it + self->sample = NULL; + } + } + if (self->sample) { + // Load another sample buffer to play + audioio_get_buffer_result_t result = audiosample_get_buffer(self->sample, false, 0, (uint8_t **)&self->sample_remaining_buffer, &self->sample_buffer_length); + // Track length in terms of words. + self->sample_buffer_length /= (self->base.bits_per_sample / 8); + self->more_data = result == GET_BUFFER_MORE_DATA; + } + } + + if (self->sample == NULL) { + // tick all block inputs + shared_bindings_synthio_lfo_tick(self->base.sample_rate, length / self->base.channel_count); + (void)synthio_block_slot_get(&self->frequency); + (void)synthio_block_slot_get(&self->feedback); + (void)synthio_block_slot_get(&self->mix); + + if (self->base.samples_signed) { + memset(word_buffer, 0, length * (self->base.bits_per_sample / 8)); + } else { + // For unsigned samples set to the middle which is "quiet" + if (MP_LIKELY(self->base.bits_per_sample == 16)) { + uint16_t *uword_buffer = (uint16_t *)word_buffer; + while (length--) { + *uword_buffer++ = 32768; + } + } else { + memset(hword_buffer, 128, length * (self->base.bits_per_sample / 8)); + } + } + + length = 0; + } else { + // we have a sample to play and filter + // Determine how many bytes we can process to our buffer, the less of the sample we have left and our buffer remaining + uint32_t n = MIN(MIN(self->sample_buffer_length, length), SYNTHIO_MAX_DUR * self->base.channel_count); + + int16_t *sample_src = (int16_t *)self->sample_remaining_buffer; // for 16-bit samples + int8_t *sample_hsrc = (int8_t *)self->sample_remaining_buffer; // for 8-bit samples + + // get the effect values we need from the BlockInput. These may change at run time so you need to do bounds checking if required + shared_bindings_synthio_lfo_tick(self->base.sample_rate, n / self->base.channel_count); + mp_float_t frequency = synthio_block_slot_get_limited(&self->frequency, MICROPY_FLOAT_CONST(0.0), self->nyquist); + int16_t feedback = (int16_t)(synthio_block_slot_get_limited(&self->feedback, MICROPY_FLOAT_CONST(0.1), MICROPY_FLOAT_CONST(0.9)) * 32767); + int16_t mix = (int16_t)(synthio_block_slot_get_limited(&self->mix, MICROPY_FLOAT_CONST(0.0), MICROPY_FLOAT_CONST(1.0)) * 32767); + + if (mix <= 328) { // if mix is zero (0.01 in fixed point), pure sample only + for (uint32_t i = 0; i < n; i++) { + if (MP_LIKELY(self->base.bits_per_sample == 16)) { + word_buffer[i] = sample_src[i]; + } else { + hword_buffer[i] = sample_hsrc[i]; + } + } + } else { + // Update all-pass filter coefficient + frequency /= self->nyquist; // scale relative to frequency range + int16_t allpasscoef = (int16_t)((MICROPY_FLOAT_CONST(1.0) - frequency) / (MICROPY_FLOAT_CONST(1.0) + frequency) * 32767); + + for (uint32_t i = 0; i < n; i++) { + bool right_channel = (single_channel_output && channel == 1) || (!single_channel_output && (i % self->base.channel_count) == 1); + uint32_t allpass_buffer_offset = self->stages * right_channel; + + int32_t sample_word = 0; + if (MP_LIKELY(self->base.bits_per_sample == 16)) { + sample_word = sample_src[i]; + } else { + if (self->base.samples_signed) { + sample_word = sample_hsrc[i]; + } else { + // Be careful here changing from an 8 bit unsigned to signed into a 32-bit signed + sample_word = (int8_t)(((uint8_t)sample_hsrc[i]) ^ 0x80); + } + } + + int32_t word = synthio_sat16(sample_word + synthio_sat16((int32_t)self->word_buffer[right_channel] * feedback, 15), 0); + int32_t allpass_word = 0; + + // Update all-pass filters + for (uint32_t j = 0; j < self->stages; j++) { + allpass_word = synthio_sat16(synthio_sat16(word * -allpasscoef, 15) + self->allpass_buffer[j + allpass_buffer_offset], 0); + self->allpass_buffer[j + allpass_buffer_offset] = synthio_sat16(synthio_sat16(allpass_word * allpasscoef, 15) + word, 0); + word = allpass_word; + } + self->word_buffer[(bool)allpass_buffer_offset] = (int16_t)word; + + // Add original sample + effect + word = sample_word + (int32_t)(synthio_sat16(word * mix, 15)); + word = synthio_mix_down_sample(word, 2); + + if (MP_LIKELY(self->base.bits_per_sample == 16)) { + word_buffer[i] = word; + if (!self->base.samples_signed) { + word_buffer[i] ^= 0x8000; + } + } else { + int8_t out = word; + if (self->base.samples_signed) { + hword_buffer[i] = out; + } else { + hword_buffer[i] = (uint8_t)out ^ 0x80; + } + } + } + } + + // Update the remaining length and the buffer positions based on how much we wrote into our buffer + length -= n; + word_buffer += n; + hword_buffer += n; + self->sample_remaining_buffer += (n * (self->base.bits_per_sample / 8)); + self->sample_buffer_length -= n; + } + } + + // Finally pass our buffer and length to the calling audio function + *buffer = (uint8_t *)self->buffer[self->last_buf_idx]; + *buffer_length = self->buffer_len; + + // Phaser always returns more data but some effects may return GET_BUFFER_DONE or GET_BUFFER_ERROR (see audiocore/__init__.h) + return GET_BUFFER_MORE_DATA; +} diff --git a/shared-module/audiofilters/Phaser.h b/shared-module/audiofilters/Phaser.h new file mode 100644 index 000000000000..f627b147014a --- /dev/null +++ b/shared-module/audiofilters/Phaser.h @@ -0,0 +1,49 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Cooper Dalrymple +// +// SPDX-License-Identifier: MIT +#pragma once + +#include "py/obj.h" + +#include "shared-module/audiocore/__init__.h" +#include "shared-module/synthio/__init__.h" +#include "shared-module/synthio/block.h" + +extern const mp_obj_type_t audiofilters_phaser_type; + +typedef struct { + audiosample_base_t base; + synthio_block_slot_t frequency; + synthio_block_slot_t feedback; + synthio_block_slot_t mix; + uint8_t stages; + + mp_float_t nyquist; + + int8_t *buffer[2]; + uint8_t last_buf_idx; + uint32_t buffer_len; // max buffer in bytes + + uint8_t *sample_remaining_buffer; + uint32_t sample_buffer_length; + + bool loop; + bool more_data; + + int16_t *allpass_buffer; + int16_t *word_buffer; + + mp_obj_t sample; +} audiofilters_phaser_obj_t; + +void audiofilters_phaser_reset_buffer(audiofilters_phaser_obj_t *self, + bool single_channel_output, + uint8_t channel); + +audioio_get_buffer_result_t audiofilters_phaser_get_buffer(audiofilters_phaser_obj_t *self, + bool single_channel_output, + uint8_t channel, + uint8_t **buffer, + uint32_t *buffer_length); // length in bytes diff --git a/shared-module/ssl/SSLSocket.c b/shared-module/ssl/SSLSocket.c index 715d75c3fd5b..8911fa2f454d 100644 --- a/shared-module/ssl/SSLSocket.c +++ b/shared-module/ssl/SSLSocket.c @@ -37,7 +37,7 @@ static void mbedtls_debug(void *ctx, int level, const char *file, int line, const char *str) { (void)ctx; (void)level; - mp_printf(&mp_plat_print, "DBG:%s:%04d: %s\n", file, line, str); + mp_printf(&mp_plat_print, "DBG:%s:%04d: %s", file, line, str); } #define DEBUG_PRINT(fmt, ...) mp_printf(&mp_plat_print, "DBG:%s:%04d: " fmt "\n", __FILE__, __LINE__,##__VA_ARGS__) #else diff --git a/shared-module/tilepalettemapper/TilePaletteMapper.c b/shared-module/tilepalettemapper/TilePaletteMapper.c index f46810b6a351..fb784fd441b9 100644 --- a/shared-module/tilepalettemapper/TilePaletteMapper.c +++ b/shared-module/tilepalettemapper/TilePaletteMapper.c @@ -18,7 +18,7 @@ void common_hal_tilepalettemapper_tilepalettemapper_construct(tilepalettemapper_ self->input_color_count = input_color_count; self->needs_refresh = false; int mappings_len = width * height; - self->tile_mappings = (uint32_t **)m_malloc_without_collect(mappings_len * sizeof(uint32_t *)); + self->tile_mappings = (uint32_t **)m_malloc(mappings_len * sizeof(uint32_t *)); for (int i = 0; i < mappings_len; i++) { self->tile_mappings[i] = (uint32_t *)m_malloc_without_collect(input_color_count * sizeof(uint32_t)); if (mp_obj_is_type(self->pixel_shader, &displayio_palette_type)) { diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index 14e5e51a49aa..df2b60e207c9 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -91,7 +91,9 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input nlr_buf_t nlr; nlr.ret_val = NULL; if (nlr_push(&nlr) == 0) { - mp_obj_t module_fun = mp_const_none; + // CIRCUITPY-CHANGE: Made volatile to prevent gcc from re-ordering store of function pointer into stack frame + // after call to gc_collect. For RISC-V this was causing free of the compiled function before execution. + volatile mp_obj_t module_fun = mp_const_none; // CIRCUITPY-CHANGE #if CIRCUITPY_ATEXIT if (!(exec_flags & EXEC_FLAG_SOURCE_IS_ATEXIT)) @@ -157,6 +159,7 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input mp_call_function_n_kw(callback->func, callback->n_pos, callback->n_kw, callback->args); } else #endif + // CIRCUITPY-CHANGE if (module_fun != mp_const_none) { mp_call_function_0(module_fun); }