Merge branch 'adafruit:main' into m5timer
This commit is contained in:
commit
d426ba79f9
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -209,7 +209,7 @@ jobs:
|
|||||||
TWINE_USERNAME: ${{ secrets.pypi_username }}
|
TWINE_USERNAME: ${{ secrets.pypi_username }}
|
||||||
TWINE_PASSWORD: ${{ secrets.pypi_password }}
|
TWINE_PASSWORD: ${{ secrets.pypi_password }}
|
||||||
run: |
|
run: |
|
||||||
# setup.py sdist was run by 'make stubs'
|
# python -m build was run by 'make stubs'
|
||||||
[ -z "$TWINE_USERNAME" ] || echo "Uploading dev release to PyPi"
|
[ -z "$TWINE_USERNAME" ] || echo "Uploading dev release to PyPi"
|
||||||
[ -z "$TWINE_USERNAME" ] || twine upload circuitpython-stubs/dist/*
|
[ -z "$TWINE_USERNAME" ] || twine upload circuitpython-stubs/dist/*
|
||||||
|
|
||||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -338,3 +338,6 @@
|
|||||||
[submodule "frozen/circuitpython-pcf85063a"]
|
[submodule "frozen/circuitpython-pcf85063a"]
|
||||||
path = frozen/circuitpython-pcf85063a
|
path = frozen/circuitpython-pcf85063a
|
||||||
url = https://github.com/bablokb/circuitpython-pcf85063a
|
url = https://github.com/bablokb/circuitpython-pcf85063a
|
||||||
|
[submodule "frozen/Adafruit_CircuitPython_Wave"]
|
||||||
|
path = frozen/Adafruit_CircuitPython_Wave
|
||||||
|
url = http://github.com/adafruit/Adafruit_CircuitPython_Wave.git
|
||||||
|
2
Makefile
2
Makefile
@ -266,7 +266,7 @@ stubs:
|
|||||||
@cp setup.py-stubs circuitpython-stubs/setup.py
|
@cp setup.py-stubs circuitpython-stubs/setup.py
|
||||||
@cp README.rst-stubs circuitpython-stubs/README.rst
|
@cp README.rst-stubs circuitpython-stubs/README.rst
|
||||||
@cp MANIFEST.in-stubs circuitpython-stubs/MANIFEST.in
|
@cp MANIFEST.in-stubs circuitpython-stubs/MANIFEST.in
|
||||||
@(cd circuitpython-stubs && $(PYTHON) setup.py -q sdist)
|
@$(PYTHON) -m build circuitpython-stubs
|
||||||
|
|
||||||
.PHONY: check-stubs
|
.PHONY: check-stubs
|
||||||
check-stubs: stubs
|
check-stubs: stubs
|
||||||
|
@ -60,6 +60,12 @@ CIRCUITPY_BLE_NAME
|
|||||||
~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~
|
||||||
Default BLE name the board advertises as, including for the BLE workflow.
|
Default BLE name the board advertises as, including for the BLE workflow.
|
||||||
|
|
||||||
|
CIRCUITPY_PYSTACK_SIZE
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Sets the size of the python stack. Must be a multiple of 4. The default value is currently 1536.
|
||||||
|
Increasing the stack reduces the size of the heap available to python code.
|
||||||
|
Used to avoid "Pystack exhausted" errors when the code can't be reworked to avoid it.
|
||||||
|
|
||||||
CIRCUITPY_RESERVED_PSRAM
|
CIRCUITPY_RESERVED_PSRAM
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
On boards with Espressif microcontrollers with PSRAM (also called SPIRAM), permanently reserve a portion of PSRAM for use by esp-idf.
|
On boards with Espressif microcontrollers with PSRAM (also called SPIRAM), permanently reserve a portion of PSRAM for use by esp-idf.
|
||||||
|
1
frozen/Adafruit_CircuitPython_Wave
Submodule
1
frozen/Adafruit_CircuitPython_Wave
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 02b748f2e6826dc442c842885e58b07ad10d9287
|
@ -8,8 +8,8 @@ msgstr ""
|
|||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2021-01-04 12:55-0600\n"
|
"POT-Creation-Date: 2021-01-04 12:55-0600\n"
|
||||||
"PO-Revision-Date: 2023-04-22 15:51+0000\n"
|
"PO-Revision-Date: 2023-05-16 19:50+0000\n"
|
||||||
"Last-Translator: Jose David M <jquintana202020@gmail.com>\n"
|
"Last-Translator: Luis Ruiz San Segundo <luisan00@hotmail.com>\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
"Language: es\n"
|
"Language: es\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -206,7 +206,7 @@ msgstr "%q debe ser un bytearray o array de tipo 'H' o 'B'"
|
|||||||
|
|
||||||
#: shared-bindings/audiocore/RawSample.c
|
#: shared-bindings/audiocore/RawSample.c
|
||||||
msgid "%q must be a bytearray or array of type 'h', 'H', 'b', or 'B'"
|
msgid "%q must be a bytearray or array of type 'h', 'H', 'b', or 'B'"
|
||||||
msgstr "%q debe ser un byte-matriz o matriz de tipo 'h', 'H', 'b', o 'B'"
|
msgstr "%q debe ser un bytearray o array de tipo 'h', 'H', 'b', o 'B'"
|
||||||
|
|
||||||
#: ports/espressif/common-hal/analogbufio/BufferedIn.c
|
#: ports/espressif/common-hal/analogbufio/BufferedIn.c
|
||||||
msgid "%q must be array of type 'H'"
|
msgid "%q must be array of type 'H'"
|
||||||
@ -601,8 +601,7 @@ msgstr "Por debajo de la tasa mínima de refrescamiento"
|
|||||||
|
|
||||||
#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c
|
#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c
|
||||||
msgid "Bit clock and word select must be sequential pins"
|
msgid "Bit clock and word select must be sequential pins"
|
||||||
msgstr ""
|
msgstr "Los pines \"clock\" y \"word-select\" deben ser consecutivos"
|
||||||
"Los pines de reloj de bit y de selector de palabra deben ser secuenciales"
|
|
||||||
|
|
||||||
#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c
|
#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c
|
||||||
msgid "Bit clock and word select must share a clock unit"
|
msgid "Bit clock and word select must share a clock unit"
|
||||||
|
4
main.c
4
main.c
@ -666,8 +666,10 @@ STATIC bool run_code_py(safe_mode_t safe_mode, bool *simulate_reset) {
|
|||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
// Refresh the ePaper display if we have one. That way it'll show an error message.
|
// Refresh the ePaper display if we have one. That way it'll show an error message.
|
||||||
|
// Skip if we're about to autoreload. Otherwise we may delay when user code can update
|
||||||
|
// the display.
|
||||||
#if CIRCUITPY_DISPLAYIO
|
#if CIRCUITPY_DISPLAYIO
|
||||||
if (time_to_epaper_refresh > 0) {
|
if (time_to_epaper_refresh > 0 && !autoreload_pending()) {
|
||||||
time_to_epaper_refresh = maybe_refresh_epaperdisplay();
|
time_to_epaper_refresh = maybe_refresh_epaperdisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,5 +12,6 @@ LONGINT_IMPL = MPZ
|
|||||||
|
|
||||||
CIRCUITPY__EVE = 1
|
CIRCUITPY__EVE = 1
|
||||||
CIRCUITPY_CANIO = 1
|
CIRCUITPY_CANIO = 1
|
||||||
|
CIRCUITPY_SYNTHIO = 0
|
||||||
|
|
||||||
CIRCUITPY_LTO_PARTITION = one
|
CIRCUITPY_LTO_PARTITION = one
|
||||||
|
@ -11,3 +11,4 @@ EXTERNAL_FLASH_DEVICES = GD25Q16C
|
|||||||
LONGINT_IMPL = MPZ
|
LONGINT_IMPL = MPZ
|
||||||
|
|
||||||
CIRCUITPY__EVE = 1
|
CIRCUITPY__EVE = 1
|
||||||
|
CIRCUITPY_SYNTHIO = 0
|
||||||
|
@ -11,3 +11,4 @@ EXTERNAL_FLASH_DEVICES = "S25FL116K, S25FL216K, GD25Q16C"
|
|||||||
LONGINT_IMPL = MPZ
|
LONGINT_IMPL = MPZ
|
||||||
|
|
||||||
CIRCUITPY__EVE = 1
|
CIRCUITPY__EVE = 1
|
||||||
|
CIRCUITPY_SYNTHIO = 0
|
||||||
|
@ -11,3 +11,4 @@ EXTERNAL_FLASH_DEVICES = "S25FL116K, S25FL216K, GD25Q16C"
|
|||||||
LONGINT_IMPL = MPZ
|
LONGINT_IMPL = MPZ
|
||||||
|
|
||||||
CIRCUITPY__EVE = 1
|
CIRCUITPY__EVE = 1
|
||||||
|
CIRCUITPY_SYNTHIO = 0
|
||||||
|
@ -120,19 +120,25 @@ CFLAGS += -DSTACK_CANARY_VALUE=0xa5a5a5a5
|
|||||||
|
|
||||||
#Debugging/Optimization
|
#Debugging/Optimization
|
||||||
ifeq ($(DEBUG), 1)
|
ifeq ($(DEBUG), 1)
|
||||||
CFLAGS += -DDEBUG -ggdb
|
CFLAGS += -ggdb
|
||||||
OPTIMIZATION_FLAGS ?= -Og
|
ifeq ($(IDF_TARGET_ARCH),riscv)
|
||||||
# You may want to enable these flags to make setting breakpoints easier.
|
OPTIMIZATION_FLAGS ?= -Os
|
||||||
# CFLAGS += -fno-inline -fno-ipa-sra
|
CFLAGS += -DNDEBUG
|
||||||
|
else
|
||||||
|
OPTIMIZATION_FLAGS ?= -Og
|
||||||
|
CFLAGS += -DDEBUG
|
||||||
|
endif
|
||||||
|
# You may want to enable these flags to make setting breakpoints easier.
|
||||||
|
# CFLAGS += -fno-inline -fno-ipa-sra
|
||||||
else
|
else
|
||||||
CFLAGS += -DNDEBUG -ggdb3
|
CFLAGS += -DNDEBUG -ggdb3
|
||||||
# RISC-V is larger than xtensa
|
# RISC-V is larger than xtensa
|
||||||
# Use -Os for RISC-V when it overflows
|
# Use -Os for RISC-V when it overflows
|
||||||
ifeq ($(IDF_TARGET_ARCH),riscv)
|
ifeq ($(IDF_TARGET_ARCH),riscv)
|
||||||
OPTIMIZATION_FLAGS ?= -Os
|
OPTIMIZATION_FLAGS ?= -Os
|
||||||
else
|
else
|
||||||
OPTIMIZATION_FLAGS ?= -O2
|
OPTIMIZATION_FLAGS ?= -O2
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# option to override compiler optimization level, set in boards/$(BOARD)/mpconfigboard.mk
|
# option to override compiler optimization level, set in boards/$(BOARD)/mpconfigboard.mk
|
||||||
|
@ -56,8 +56,10 @@ void spi_reset(void) {
|
|||||||
|
|
||||||
static void set_spi_config(busio_spi_obj_t *self,
|
static void set_spi_config(busio_spi_obj_t *self,
|
||||||
uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) {
|
uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) {
|
||||||
|
// 128 is a 50% duty cycle.
|
||||||
|
const int closest_clock = spi_get_actual_clock(APB_CLK_FREQ, baudrate, 128);
|
||||||
const spi_device_interface_config_t device_config = {
|
const spi_device_interface_config_t device_config = {
|
||||||
.clock_speed_hz = baudrate,
|
.clock_speed_hz = closest_clock,
|
||||||
.mode = phase | (polarity << 1),
|
.mode = phase | (polarity << 1),
|
||||||
.spics_io_num = -1, // No CS pin
|
.spics_io_num = -1, // No CS pin
|
||||||
.queue_size = MAX_SPI_TRANSACTIONS,
|
.queue_size = MAX_SPI_TRANSACTIONS,
|
||||||
@ -67,7 +69,7 @@ static void set_spi_config(busio_spi_obj_t *self,
|
|||||||
if (result != ESP_OK) {
|
if (result != ESP_OK) {
|
||||||
mp_raise_RuntimeError(translate("SPI configuration failed"));
|
mp_raise_RuntimeError(translate("SPI configuration failed"));
|
||||||
}
|
}
|
||||||
self->baudrate = baudrate;
|
self->baudrate = closest_clock;
|
||||||
self->polarity = polarity;
|
self->polarity = polarity;
|
||||||
self->phase = phase;
|
self->phase = phase;
|
||||||
self->bits = bits;
|
self->bits = bits;
|
||||||
|
@ -62,6 +62,11 @@ static const uint64_t pin_mask_reset_forbidden =
|
|||||||
GPIO_SEL_18 | // USB D-
|
GPIO_SEL_18 | // USB D-
|
||||||
GPIO_SEL_19 | // USB D+
|
GPIO_SEL_19 | // USB D+
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(CONFIG_ESP_CONSOLE_UART_DEFAULT) && CONFIG_ESP_CONSOLE_UART_DEFAULT && CONFIG_ESP_CONSOLE_UART_NUM == 0
|
||||||
|
// Never reset debug UART/console pins.
|
||||||
|
GPIO_SEL_20 |
|
||||||
|
GPIO_SEL_21 |
|
||||||
|
#endif
|
||||||
#endif // ESP32C3
|
#endif // ESP32C3
|
||||||
|
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
@ -304,5 +304,11 @@ STATIC const mp_rom_map_elem_t mcu_pin_global_dict_table[] = {
|
|||||||
#ifdef GPIO46_EXISTS
|
#ifdef GPIO46_EXISTS
|
||||||
{ MP_ROM_QSTR(MP_QSTR_GPIO46), MP_ROM_PTR(&pin_GPIO46) },
|
{ MP_ROM_QSTR(MP_QSTR_GPIO46), MP_ROM_PTR(&pin_GPIO46) },
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef GPIO47_EXISTS
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_GPIO47), MP_ROM_PTR(&pin_GPIO47) },
|
||||||
|
#endif
|
||||||
|
#ifdef GPIO48_EXISTS
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_GPIO48), MP_ROM_PTR(&pin_GPIO48) },
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
MP_DEFINE_CONST_DICT(mcu_pin_globals, mcu_pin_global_dict_table);
|
MP_DEFINE_CONST_DICT(mcu_pin_globals, mcu_pin_global_dict_table);
|
||||||
|
@ -475,8 +475,16 @@ void common_hal_wifi_radio_stop_dhcp_client(wifi_radio_obj_t *self) {
|
|||||||
esp_netif_dhcpc_stop(self->netif);
|
esp_netif_dhcpc_stop(self->netif);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void common_hal_wifi_radio_start_dhcp_server(wifi_radio_obj_t *self) {
|
||||||
|
esp_netif_dhcps_start(self->ap_netif);
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_wifi_radio_stop_dhcp_server(wifi_radio_obj_t *self) {
|
||||||
|
esp_netif_dhcps_stop(self->ap_netif);
|
||||||
|
}
|
||||||
|
|
||||||
void common_hal_wifi_radio_set_ipv4_address(wifi_radio_obj_t *self, mp_obj_t ipv4, mp_obj_t netmask, mp_obj_t gateway, mp_obj_t ipv4_dns) {
|
void common_hal_wifi_radio_set_ipv4_address(wifi_radio_obj_t *self, mp_obj_t ipv4, mp_obj_t netmask, mp_obj_t gateway, mp_obj_t ipv4_dns) {
|
||||||
common_hal_wifi_radio_stop_dhcp_client(self); // Must stop DHCP to set a manual address
|
common_hal_wifi_radio_stop_dhcp_client(self); // Must stop station DHCP to set a manual address
|
||||||
|
|
||||||
esp_netif_ip_info_t ip_info;
|
esp_netif_ip_info_t ip_info;
|
||||||
ipaddress_ipaddress_to_esp_idf_ip4(ipv4, &ip_info.ip);
|
ipaddress_ipaddress_to_esp_idf_ip4(ipv4, &ip_info.ip);
|
||||||
@ -490,6 +498,19 @@ void common_hal_wifi_radio_set_ipv4_address(wifi_radio_obj_t *self, mp_obj_t ipv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void common_hal_wifi_radio_set_ipv4_address_ap(wifi_radio_obj_t *self, mp_obj_t ipv4, mp_obj_t netmask, mp_obj_t gateway) {
|
||||||
|
common_hal_wifi_radio_stop_dhcp_server(self); // Must stop access point DHCP to set a manual address
|
||||||
|
|
||||||
|
esp_netif_ip_info_t ip_info;
|
||||||
|
ipaddress_ipaddress_to_esp_idf_ip4(ipv4, &ip_info.ip);
|
||||||
|
ipaddress_ipaddress_to_esp_idf_ip4(netmask, &ip_info.netmask);
|
||||||
|
ipaddress_ipaddress_to_esp_idf_ip4(gateway, &ip_info.gw);
|
||||||
|
|
||||||
|
esp_netif_set_ip_info(self->ap_netif, &ip_info);
|
||||||
|
|
||||||
|
common_hal_wifi_radio_start_dhcp_server(self); // restart access point DHCP
|
||||||
|
}
|
||||||
|
|
||||||
mp_int_t common_hal_wifi_radio_ping(wifi_radio_obj_t *self, mp_obj_t ip_address, mp_float_t timeout) {
|
mp_int_t common_hal_wifi_radio_ping(wifi_radio_obj_t *self, mp_obj_t ip_address, mp_float_t timeout) {
|
||||||
esp_ping_config_t ping_config = ESP_PING_DEFAULT_CONFIG();
|
esp_ping_config_t ping_config = ESP_PING_DEFAULT_CONFIG();
|
||||||
ipaddress_ipaddress_to_esp_idf(ip_address, &ping_config.target_addr);
|
ipaddress_ipaddress_to_esp_idf(ip_address, &ping_config.target_addr);
|
||||||
|
@ -101,21 +101,28 @@ bool usb_serial_jtag_connected(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char usb_serial_jtag_read_char(void) {
|
char usb_serial_jtag_read_char(void) {
|
||||||
if (ringbuf_num_filled(&ringbuf) == 0) {
|
if (ringbuf_num_filled(&ringbuf) == 0 && !usb_serial_jtag_ll_rxfifo_data_available()) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
char c = ringbuf_get(&ringbuf);
|
char c = -1;
|
||||||
|
if (ringbuf_num_filled(&ringbuf) > 0) {
|
||||||
|
c = ringbuf_get(&ringbuf);
|
||||||
|
}
|
||||||
// Maybe re-enable the recv interrupt if we've emptied the ringbuf.
|
// Maybe re-enable the recv interrupt if we've emptied the ringbuf.
|
||||||
if (ringbuf_num_filled(&ringbuf) == 0) {
|
if (ringbuf_num_filled(&ringbuf) == 0) {
|
||||||
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT);
|
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT);
|
||||||
_copy_out_of_fifo();
|
_copy_out_of_fifo();
|
||||||
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT);
|
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT);
|
||||||
|
// May have only been ctrl-c.
|
||||||
|
if (c == -1 && ringbuf_num_filled(&ringbuf) > 0) {
|
||||||
|
c = ringbuf_get(&ringbuf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool usb_serial_jtag_bytes_available(void) {
|
bool usb_serial_jtag_bytes_available(void) {
|
||||||
return ringbuf_num_filled(&ringbuf) > 0;
|
return ringbuf_num_filled(&ringbuf) > 0 || usb_serial_jtag_ll_rxfifo_data_available();
|
||||||
}
|
}
|
||||||
|
|
||||||
void usb_serial_jtag_write(const char *text, uint32_t length) {
|
void usb_serial_jtag_write(const char *text, uint32_t length) {
|
||||||
|
@ -50,7 +50,7 @@ CFLAGS += -ftree-vrp -DNDEBUG
|
|||||||
# TinyUSB defines
|
# TinyUSB defines
|
||||||
CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_MIMXRT10XX -DCFG_TUD_CDC_RX_BUFSIZE=512 -DCFG_TUD_CDC_TX_BUFSIZE=512
|
CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_MIMXRT10XX -DCFG_TUD_CDC_RX_BUFSIZE=512 -DCFG_TUD_CDC_TX_BUFSIZE=512
|
||||||
ifeq ($(CHIP_FAMILY),$(filter $(CHIP_FAMILY),MIMXRT1011 MIMXRT1015))
|
ifeq ($(CHIP_FAMILY),$(filter $(CHIP_FAMILY),MIMXRT1011 MIMXRT1015))
|
||||||
CFLAGS += -DCFG_TUD_MIDI_RX_BUFSIZE=64 -DCFG_TUD_MIDI_TX_BUFSIZE=64 -DCFG_TUD_MSC_BUFSIZE=512
|
CFLAGS += -DCFG_TUD_MIDI_RX_BUFSIZE=512 -DCFG_TUD_MIDI_TX_BUFSIZE=64 -DCFG_TUD_MSC_BUFSIZE=512
|
||||||
else
|
else
|
||||||
CFLAGS += -DCFG_TUD_MIDI_RX_BUFSIZE=512 -DCFG_TUD_MIDI_TX_BUFSIZE=512 -DCFG_TUD_MSC_BUFSIZE=1024
|
CFLAGS += -DCFG_TUD_MIDI_RX_BUFSIZE=512 -DCFG_TUD_MIDI_TX_BUFSIZE=512 -DCFG_TUD_MSC_BUFSIZE=1024
|
||||||
endif
|
endif
|
||||||
|
@ -41,9 +41,6 @@ const mcu_pin_obj_t *mimxrt10xx_reset_forbidden_pins[] = {
|
|||||||
&pin_GPIO_SD_08,
|
&pin_GPIO_SD_08,
|
||||||
&pin_GPIO_SD_07,
|
&pin_GPIO_SD_07,
|
||||||
&pin_GPIO_SD_06,
|
&pin_GPIO_SD_06,
|
||||||
// USB Pins
|
|
||||||
&pin_USB_OTG1_DN,
|
|
||||||
&pin_USB_OTG1_DP,
|
|
||||||
NULL, // Must end in NULL.
|
NULL, // Must end in NULL.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -40,9 +40,6 @@ const mcu_pin_obj_t *mimxrt10xx_reset_forbidden_pins[] = {
|
|||||||
&pin_GPIO_SD_B1_09,
|
&pin_GPIO_SD_B1_09,
|
||||||
&pin_GPIO_SD_B1_10,
|
&pin_GPIO_SD_B1_10,
|
||||||
&pin_GPIO_SD_B1_11,
|
&pin_GPIO_SD_B1_11,
|
||||||
// USB Pins
|
|
||||||
&pin_USB_OTG1_DN,
|
|
||||||
&pin_USB_OTG1_DP,
|
|
||||||
NULL, // Must end in NULL.
|
NULL, // Must end in NULL.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -42,10 +42,6 @@ const mcu_pin_obj_t *mimxrt10xx_reset_forbidden_pins[] = {
|
|||||||
&pin_GPIO_SD_B1_09,
|
&pin_GPIO_SD_B1_09,
|
||||||
&pin_GPIO_SD_B1_10,
|
&pin_GPIO_SD_B1_10,
|
||||||
&pin_GPIO_SD_B1_11,
|
&pin_GPIO_SD_B1_11,
|
||||||
|
|
||||||
// USB Pins
|
|
||||||
&pin_USB_OTG1_DN,
|
|
||||||
&pin_USB_OTG1_DP,
|
|
||||||
NULL, // Must end in NULL.
|
NULL, // Must end in NULL.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -44,9 +44,6 @@ const mcu_pin_obj_t *mimxrt10xx_reset_forbidden_pins[] = {
|
|||||||
&pin_GPIO_SD_B1_10,
|
&pin_GPIO_SD_B1_10,
|
||||||
&pin_GPIO_SD_B1_11,
|
&pin_GPIO_SD_B1_11,
|
||||||
|
|
||||||
&pin_USB_OTG1_DN,
|
|
||||||
&pin_USB_OTG1_DP,
|
|
||||||
|
|
||||||
NULL, // Must end in NULL.
|
NULL, // Must end in NULL.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,8 +45,8 @@ const mcu_pin_obj_t *mimxrt10xx_reset_forbidden_pins[] = {
|
|||||||
&pin_GPIO_SD_B1_11,
|
&pin_GPIO_SD_B1_11,
|
||||||
|
|
||||||
// USB Pins
|
// USB Pins
|
||||||
&pin_GPIO_AD_B0_01,
|
&pin_GPIO_AD_B0_01, // ID Pin
|
||||||
&pin_GPIO_AD_B0_03,
|
&pin_GPIO_AD_B0_03, // OC/Fault Pin
|
||||||
NULL, // Must end in NULL.
|
NULL, // Must end in NULL.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -50,8 +50,8 @@ const mcu_pin_obj_t *mimxrt10xx_reset_forbidden_pins[] = {
|
|||||||
&pin_GPIO_SD_B1_11,
|
&pin_GPIO_SD_B1_11,
|
||||||
|
|
||||||
// USB Pins
|
// USB Pins
|
||||||
&pin_GPIO_AD_B0_01,
|
&pin_GPIO_AD_B0_01, // ID Pin
|
||||||
&pin_GPIO_AD_B0_03,
|
&pin_GPIO_AD_B0_03, // OC/Fault Pin
|
||||||
NULL, // Must end in NULL.
|
NULL, // Must end in NULL.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,8 +31,8 @@
|
|||||||
|
|
||||||
#include "py/gc.h"
|
#include "py/gc.h"
|
||||||
|
|
||||||
STATIC bool claimed_pins[IOMUXC_SW_PAD_CTL_PAD_COUNT];
|
STATIC bool claimed_pins[PAD_COUNT];
|
||||||
STATIC bool never_reset_pins[IOMUXC_SW_PAD_CTL_PAD_COUNT];
|
STATIC bool never_reset_pins[PAD_COUNT];
|
||||||
|
|
||||||
// Default is that no pins are forbidden to reset.
|
// Default is that no pins are forbidden to reset.
|
||||||
MP_WEAK const mcu_pin_obj_t *mimxrt10xx_reset_forbidden_pins[] = {
|
MP_WEAK const mcu_pin_obj_t *mimxrt10xx_reset_forbidden_pins[] = {
|
||||||
@ -55,10 +55,10 @@ STATIC bool _reset_forbidden(const mcu_pin_obj_t *pin) {
|
|||||||
// and GPIO port and number, used to store claimed and reset tagging. The two number
|
// and GPIO port and number, used to store claimed and reset tagging. The two number
|
||||||
// systems are not related and one cannot determine the other without a pin object
|
// systems are not related and one cannot determine the other without a pin object
|
||||||
void reset_all_pins(void) {
|
void reset_all_pins(void) {
|
||||||
for (uint8_t i = 0; i < IOMUXC_SW_PAD_CTL_PAD_COUNT; i++) {
|
for (uint8_t i = 0; i < PAD_COUNT; i++) {
|
||||||
claimed_pins[i] = never_reset_pins[i];
|
claimed_pins[i] = never_reset_pins[i];
|
||||||
}
|
}
|
||||||
for (uint8_t i = 0; i < IOMUXC_SW_PAD_CTL_PAD_COUNT; i++) {
|
for (uint8_t i = 0; i < PAD_COUNT; i++) {
|
||||||
mcu_pin_obj_t *pin = mcu_pin_globals.map.table[i].value;
|
mcu_pin_obj_t *pin = mcu_pin_globals.map.table[i].value;
|
||||||
if (never_reset_pins[pin->mux_idx]) {
|
if (never_reset_pins[pin->mux_idx]) {
|
||||||
continue;
|
continue;
|
||||||
@ -90,6 +90,11 @@ void common_hal_reset_pin(const mcu_pin_obj_t *pin) {
|
|||||||
disable_pin_change_interrupt(pin);
|
disable_pin_change_interrupt(pin);
|
||||||
never_reset_pins[pin->mux_idx] = false;
|
never_reset_pins[pin->mux_idx] = false;
|
||||||
claimed_pins[pin->mux_idx] = false;
|
claimed_pins[pin->mux_idx] = false;
|
||||||
|
|
||||||
|
// This should never be true, but protect against it anyway.
|
||||||
|
if (pin->mux_reg == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
*(uint32_t *)pin->mux_reg = pin->mux_reset;
|
*(uint32_t *)pin->mux_reg = pin->mux_reset;
|
||||||
*(uint32_t *)pin->cfg_reg = pin->pad_reset;
|
*(uint32_t *)pin->cfg_reg = pin->pad_reset;
|
||||||
}
|
}
|
||||||
|
@ -40,5 +40,7 @@
|
|||||||
#include "pin_names.h"
|
#include "pin_names.h"
|
||||||
#undef FORMAT_PIN
|
#undef FORMAT_PIN
|
||||||
|
|
||||||
#define PIN_COUNT (IOMUXC_SW_PAD_CTL_PAD_COUNT + 2)
|
// Pads can be reset. Other pins like USB cannot be.
|
||||||
|
#define PAD_COUNT (43)
|
||||||
|
#define PIN_COUNT (PAD_COUNT + 2)
|
||||||
extern const mcu_pin_obj_t mcu_pin_list[PIN_COUNT];
|
extern const mcu_pin_obj_t mcu_pin_list[PIN_COUNT];
|
||||||
|
@ -40,5 +40,7 @@
|
|||||||
#include "pin_names.h"
|
#include "pin_names.h"
|
||||||
#undef FORMAT_PIN
|
#undef FORMAT_PIN
|
||||||
|
|
||||||
#define PIN_COUNT (IOMUXC_SW_PAD_CTL_PAD_COUNT + 2)
|
// Pads can be reset. Other pins like USB cannot be.
|
||||||
|
#define PAD_COUNT (56)
|
||||||
|
#define PIN_COUNT (PAD_COUNT + 2)
|
||||||
extern const mcu_pin_obj_t mcu_pin_list[PIN_COUNT];
|
extern const mcu_pin_obj_t mcu_pin_list[PIN_COUNT];
|
||||||
|
@ -40,5 +40,7 @@
|
|||||||
#include "pin_names.h"
|
#include "pin_names.h"
|
||||||
#undef FORMAT_PIN
|
#undef FORMAT_PIN
|
||||||
|
|
||||||
#define PIN_COUNT (IOMUXC_SW_PAD_CTL_PAD_COUNT + 2)
|
// Pads can be reset. Other pins like USB cannot be.
|
||||||
|
#define PAD_COUNT (93)
|
||||||
|
#define PIN_COUNT (PAD_COUNT + 2)
|
||||||
extern const mcu_pin_obj_t mcu_pin_list[PIN_COUNT];
|
extern const mcu_pin_obj_t mcu_pin_list[PIN_COUNT];
|
||||||
|
@ -40,5 +40,7 @@
|
|||||||
#include "pin_names.h"
|
#include "pin_names.h"
|
||||||
#undef FORMAT_PIN
|
#undef FORMAT_PIN
|
||||||
|
|
||||||
#define PIN_COUNT (IOMUXC_SW_PAD_CTL_PAD_COUNT + 2)
|
// Pads can be reset. Other pins like USB cannot be.
|
||||||
|
#define PAD_COUNT (112)
|
||||||
|
#define PIN_COUNT (PAD_COUNT + 2)
|
||||||
extern const mcu_pin_obj_t mcu_pin_list[PIN_COUNT];
|
extern const mcu_pin_obj_t mcu_pin_list[PIN_COUNT];
|
||||||
|
@ -40,5 +40,7 @@
|
|||||||
#include "pin_names.h"
|
#include "pin_names.h"
|
||||||
#undef FORMAT_PIN
|
#undef FORMAT_PIN
|
||||||
|
|
||||||
#define PIN_COUNT (IOMUXC_SW_PAD_CTL_PAD_COUNT + 4)
|
// Pads can be reset. Other pins like USB cannot be.
|
||||||
|
#define PAD_COUNT (124)
|
||||||
|
#define PIN_COUNT (PAD_COUNT + 4)
|
||||||
extern const mcu_pin_obj_t mcu_pin_list[PIN_COUNT];
|
extern const mcu_pin_obj_t mcu_pin_list[PIN_COUNT];
|
||||||
|
@ -40,5 +40,7 @@
|
|||||||
#include "pin_names.h"
|
#include "pin_names.h"
|
||||||
#undef FORMAT_PIN
|
#undef FORMAT_PIN
|
||||||
|
|
||||||
#define PIN_COUNT (IOMUXC_SW_PAD_CTL_PAD_COUNT + 4)
|
// Pads can be reset. Other pins like USB cannot be.
|
||||||
|
#define PAD_COUNT (124)
|
||||||
|
#define PIN_COUNT (PAD_COUNT + 4)
|
||||||
extern const mcu_pin_obj_t mcu_pin_list[PIN_COUNT];
|
extern const mcu_pin_obj_t mcu_pin_list[PIN_COUNT];
|
||||||
|
@ -40,5 +40,7 @@
|
|||||||
#include "pin_names.h"
|
#include "pin_names.h"
|
||||||
#undef FORMAT_PIN
|
#undef FORMAT_PIN
|
||||||
|
|
||||||
#define PIN_COUNT (IOMUXC_SW_PAD_CTL_PAD_COUNT + 0)
|
// Pads can be reset. Other pins like USB cannot be.
|
||||||
|
#define PAD_COUNT (145)
|
||||||
|
#define PIN_COUNT (PAD_COUNT + 0)
|
||||||
extern const mcu_pin_obj_t mcu_pin_list[PIN_COUNT];
|
extern const mcu_pin_obj_t mcu_pin_list[PIN_COUNT];
|
||||||
|
@ -190,6 +190,7 @@ for device in devices:
|
|||||||
split_pin = name.split("_")
|
split_pin = name.split("_")
|
||||||
pin_name = "_".join(split_pin[4:])
|
pin_name = "_".join(split_pin[4:])
|
||||||
if pin_name not in all_pins:
|
if pin_name not in all_pins:
|
||||||
|
print("skip", pin_name)
|
||||||
continue
|
continue
|
||||||
gpio_base = "_".join(split_pin[4:-1])
|
gpio_base = "_".join(split_pin[4:-1])
|
||||||
|
|
||||||
@ -278,7 +279,10 @@ for device in devices:
|
|||||||
pins_c.append("")
|
pins_c.append("")
|
||||||
|
|
||||||
pins_h.append("")
|
pins_h.append("")
|
||||||
pins_h.append(f"#define PIN_COUNT (IOMUXC_SW_PAD_CTL_PAD_COUNT + {len(usb_pins)})")
|
|
||||||
|
pins_h.append("// Pads can be reset. Other pins like USB cannot be.")
|
||||||
|
pins_h.append(f"#define PAD_COUNT ({pin_number})")
|
||||||
|
pins_h.append(f"#define PIN_COUNT (PAD_COUNT + {len(usb_pins)})")
|
||||||
pins_h.append(f"extern const mcu_pin_obj_t mcu_pin_list[PIN_COUNT];")
|
pins_h.append(f"extern const mcu_pin_obj_t mcu_pin_list[PIN_COUNT];")
|
||||||
pins_h.append("")
|
pins_h.append("")
|
||||||
|
|
||||||
|
136
ports/nrf/common-hal/memorymap/AddressRange.c
Normal file
136
ports/nrf/common-hal/memorymap/AddressRange.c
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the MicroPython project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 microDev
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "nrf.h"
|
||||||
|
|
||||||
|
#include "shared-bindings/memorymap/AddressRange.h"
|
||||||
|
|
||||||
|
#include "py/runtime.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef NRF51_SERIES
|
||||||
|
size_t allow_ranges[][2] = {
|
||||||
|
// FLASH
|
||||||
|
{0x00000000, 0x00040000},
|
||||||
|
// FICR & UICR ranges
|
||||||
|
{0x10000000, 0x10002000},
|
||||||
|
// RAM
|
||||||
|
{0x20000000, 0x20010000},
|
||||||
|
// PERIPHERALS
|
||||||
|
{0x40000000, 0x60000000}
|
||||||
|
};
|
||||||
|
#elif defined NRF52_SERIES
|
||||||
|
size_t allow_ranges[][2] = {
|
||||||
|
// FLASH
|
||||||
|
{0x00000000, 0x00100000},
|
||||||
|
// FICR & UICR ranges
|
||||||
|
{0x10000000, 0x10002000},
|
||||||
|
// RAM
|
||||||
|
{0x20000000, 0x20040000},
|
||||||
|
// PERIPHERALS
|
||||||
|
{0x40000000, 0x60000000}
|
||||||
|
};
|
||||||
|
#elif defined NRF53_SERIES
|
||||||
|
size_t allow_ranges[][2] = {
|
||||||
|
// FLASH
|
||||||
|
{0x00000000, 0x00100000},
|
||||||
|
// FICR & UICR ranges
|
||||||
|
{0x00FF0000, 0x01000000},
|
||||||
|
// RAM
|
||||||
|
{0x20000000, 0x20080000},
|
||||||
|
// PERIPHERALS
|
||||||
|
{0x40000000, 0x60000000},
|
||||||
|
{0xE0000000, 0xE0100000}
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
#error "Unsupported nRF variant"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void common_hal_memorymap_addressrange_construct(memorymap_addressrange_obj_t *self, uint8_t *start_address, size_t length) {
|
||||||
|
bool allowed = false;
|
||||||
|
for (size_t i = 0; i < MP_ARRAY_SIZE(allow_ranges); i++) {
|
||||||
|
uint8_t *allowed_start = (uint8_t *)allow_ranges[i][0];
|
||||||
|
uint8_t *allowed_end = (uint8_t *)allow_ranges[i][1];
|
||||||
|
if (allowed_start <= start_address &&
|
||||||
|
(start_address + length) <= allowed_end) {
|
||||||
|
allowed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allowed) {
|
||||||
|
mp_raise_ValueError(translate("Address range not allowed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
self->start_address = start_address;
|
||||||
|
self->len = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t common_hal_memorymap_addressrange_get_length(const memorymap_addressrange_obj_t *self) {
|
||||||
|
return self->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool common_hal_memorymap_addressrange_set_bytes(const memorymap_addressrange_obj_t *self,
|
||||||
|
uint32_t start_index, uint8_t *values, uint32_t len) {
|
||||||
|
uint8_t *address = self->start_address + start_index;
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wcast-align"
|
||||||
|
if (len == 1) {
|
||||||
|
*((uint8_t *)address) = values[0];
|
||||||
|
} else if (len == sizeof(uint16_t) && (((size_t)address) % sizeof(uint16_t)) == 0) {
|
||||||
|
*((uint16_t *)address) = ((uint16_t *)values)[0];
|
||||||
|
} else if (len == sizeof(uint32_t) && (((size_t)address) % sizeof(uint32_t)) == 0) {
|
||||||
|
*((uint32_t *)address) = ((uint32_t *)values)[0];
|
||||||
|
} else if (len == sizeof(uint64_t) && (((size_t)address) % sizeof(uint64_t)) == 0) {
|
||||||
|
*((uint64_t *)address) = ((uint64_t *)values)[0];
|
||||||
|
} else {
|
||||||
|
memcpy(address, values, len);
|
||||||
|
}
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_memorymap_addressrange_get_bytes(const memorymap_addressrange_obj_t *self,
|
||||||
|
uint32_t start_index, uint32_t len, uint8_t *values) {
|
||||||
|
uint8_t *address = self->start_address + start_index;
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wcast-align"
|
||||||
|
if (len == 1) {
|
||||||
|
values[0] = *((uint8_t *)address);
|
||||||
|
} else if (len == sizeof(uint16_t) && (((size_t)address) % sizeof(uint16_t)) == 0) {
|
||||||
|
((uint16_t *)values)[0] = *((uint16_t *)address);
|
||||||
|
} else if (len == sizeof(uint32_t) && (((size_t)address) % sizeof(uint32_t)) == 0) {
|
||||||
|
((uint32_t *)values)[0] = *((uint32_t *)address);
|
||||||
|
} else if (len == sizeof(uint64_t) && (((size_t)address) % sizeof(uint64_t)) == 0) {
|
||||||
|
((uint64_t *)values)[0] = *((uint64_t *)address);
|
||||||
|
} else {
|
||||||
|
memcpy(values, address, len);
|
||||||
|
}
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
}
|
38
ports/nrf/common-hal/memorymap/AddressRange.h
Normal file
38
ports/nrf/common-hal/memorymap/AddressRange.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the MicroPython project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020 microDev
|
||||||
|
*
|
||||||
|
* 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_NRF_COMMON_HAL_MEMORYMAP_ADDRESSRANGE_H
|
||||||
|
#define MICROPY_INCLUDED_NRF_COMMON_HAL_MEMORYMAP_ADDRESSRANGE_H
|
||||||
|
|
||||||
|
#include "py/obj.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
mp_obj_base_t base;
|
||||||
|
uint8_t *start_address;
|
||||||
|
size_t len;
|
||||||
|
} memorymap_addressrange_obj_t;
|
||||||
|
|
||||||
|
#endif // MICROPY_INCLUDED_ESPRESSIF_COMMON_HAL_MEMORYMAP_ADDRESSRANGE_H
|
1
ports/nrf/common-hal/memorymap/__init__.c
Normal file
1
ports/nrf/common-hal/memorymap/__init__.c
Normal file
@ -0,0 +1 @@
|
|||||||
|
// No memorymap module functions.
|
@ -47,6 +47,7 @@ CIRCUITPY_SERIAL_BLE ?= 1
|
|||||||
|
|
||||||
CIRCUITPY_COMPUTED_GOTO_SAVE_SPACE ?= 1
|
CIRCUITPY_COMPUTED_GOTO_SAVE_SPACE ?= 1
|
||||||
|
|
||||||
|
|
||||||
# nRF52840-specific
|
# nRF52840-specific
|
||||||
|
|
||||||
ifeq ($(MCU_CHIP),nrf52840)
|
ifeq ($(MCU_CHIP),nrf52840)
|
||||||
@ -56,6 +57,7 @@ MCU_SUB_VARIANT = nrf52840
|
|||||||
|
|
||||||
# Fits on nrf52840 but space is tight on nrf52833.
|
# Fits on nrf52840 but space is tight on nrf52833.
|
||||||
CIRCUITPY_AESIO ?= 1
|
CIRCUITPY_AESIO ?= 1
|
||||||
|
CIRCUITPY_MEMORYMAP ?= 1
|
||||||
|
|
||||||
CIRCUITPY_RGBMATRIX ?= 1
|
CIRCUITPY_RGBMATRIX ?= 1
|
||||||
CIRCUITPY_FRAMEBUFFERIO ?= 1
|
CIRCUITPY_FRAMEBUFFERIO ?= 1
|
||||||
|
@ -26,8 +26,11 @@ STATIC const mp_rom_map_elem_t board_module_globals_table[] = {
|
|||||||
{ MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_GPIO10) },
|
{ 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_D11), MP_ROM_PTR(&pin_GPIO11) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_D12), MP_ROM_PTR(&pin_GPIO12) },
|
{ MP_ROM_QSTR(MP_QSTR_D12), MP_ROM_PTR(&pin_GPIO12) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_GPIO13) },
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO13) },
|
{ MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO13) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_GPIO13) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO4) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_BOOT), MP_ROM_PTR(&pin_GPIO4) },
|
||||||
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO16) },
|
{ MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO16) },
|
||||||
|
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
#include "shared-bindings/board/__init__.h"
|
#include "shared-bindings/board/__init__.h"
|
||||||
|
|
||||||
|
#include "supervisor/board.h"
|
||||||
|
#include "shared-module/displayio/__init__.h"
|
||||||
|
|
||||||
STATIC const mp_rom_map_elem_t board_module_globals_table[] = {
|
STATIC const mp_rom_map_elem_t board_module_globals_table[] = {
|
||||||
CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS
|
CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS
|
||||||
|
|
||||||
@ -81,5 +84,7 @@ STATIC const mp_rom_map_elem_t board_module_globals_table[] = {
|
|||||||
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_WHITE_LED), MP_ROM_PTR(&pin_GPIO28) },
|
{ MP_ROM_QSTR(MP_QSTR_WHITE_LED), MP_ROM_PTR(&pin_GPIO28) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)},
|
||||||
|
|
||||||
};
|
};
|
||||||
MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table);
|
MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table);
|
||||||
|
151
ports/raspberrypi/boards/lilygo_t_display_rp2040/board.c
Normal file
151
ports/raspberrypi/boards/lilygo_t_display_rp2040/board.c
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the MicroPython project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "supervisor/board.h"
|
||||||
|
#include "mpconfigboard.h"
|
||||||
|
#include "shared-bindings/microcontroller/Pin.h"
|
||||||
|
#include "shared-module/displayio/__init__.h"
|
||||||
|
#include "shared-module/displayio/mipi_constants.h"
|
||||||
|
|
||||||
|
#define DELAY 0x80
|
||||||
|
#define LCD_POWER 22
|
||||||
|
|
||||||
|
// display init sequence according to LilyGO example app
|
||||||
|
uint8_t display_init_sequence[] = {
|
||||||
|
// sw reset
|
||||||
|
0x01, 0 | DELAY, 150,
|
||||||
|
// sleep out
|
||||||
|
0x11, 0 | DELAY, 255,
|
||||||
|
// normal display mode on
|
||||||
|
0x13, 0,
|
||||||
|
// display and color format settings
|
||||||
|
0x36, 1, 0x08,
|
||||||
|
0xB6, 2, 0x0A, 0x82,
|
||||||
|
0x3A, 1 | DELAY, 0x55, 10,
|
||||||
|
// ST7789V frame rate setting
|
||||||
|
0xB2, 5, 0x0C, 0x0C, 0x00, 0x33, 0x33,
|
||||||
|
// voltages: VGH / VGL
|
||||||
|
0xB7, 1, 0x35,
|
||||||
|
// ST7789V power setting
|
||||||
|
0xBB, 1, 0x28,
|
||||||
|
0xC0, 1, 0x0C,
|
||||||
|
0xC2, 2, 0x01, 0xFF,
|
||||||
|
0xC3, 1, 0x10,
|
||||||
|
0xC4, 1, 0x20,
|
||||||
|
0xC6, 1, 0x0F,
|
||||||
|
0xD0, 2, 0xA4, 0xA1,
|
||||||
|
// ST7789V gamma setting
|
||||||
|
0xE0, 14, 0xD0, 0x00, 0x02, 0x07, 0x0A, 0x28, 0x32, 0x44, 0x42, 0x06, 0x0E, 0x12, 0x14, 0x17,
|
||||||
|
0xE1, 14, 0xD0, 0x00, 0x02, 0x07, 0x0A, 0x28, 0x31, 0x54, 0x47, 0x0E, 0x1C, 0x17, 0x1B, 0x1E,
|
||||||
|
0x21, 0,
|
||||||
|
// display on
|
||||||
|
0x29, 0 | DELAY, 255,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void display_init(void) {
|
||||||
|
displayio_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus;
|
||||||
|
busio_spi_obj_t *spi = &bus->inline_bus;
|
||||||
|
|
||||||
|
common_hal_busio_spi_construct(
|
||||||
|
spi,
|
||||||
|
&pin_GPIO2, // CLK
|
||||||
|
&pin_GPIO3, // MOSI
|
||||||
|
NULL, // MISO not connected
|
||||||
|
false); // Not half-duplex
|
||||||
|
|
||||||
|
common_hal_busio_spi_never_reset(spi);
|
||||||
|
|
||||||
|
bus->base.type = &displayio_fourwire_type;
|
||||||
|
|
||||||
|
common_hal_displayio_fourwire_construct(
|
||||||
|
bus,
|
||||||
|
spi,
|
||||||
|
&pin_GPIO1, // DC
|
||||||
|
&pin_GPIO5, // CS
|
||||||
|
NULL, // RST (Reset pin tie to 0, do not set here)
|
||||||
|
40000000, // baudrate
|
||||||
|
1, // polarity
|
||||||
|
0 // phase
|
||||||
|
);
|
||||||
|
|
||||||
|
displayio_display_obj_t *display = &allocate_display()->display;
|
||||||
|
display->base.type = &displayio_display_type;
|
||||||
|
|
||||||
|
common_hal_displayio_display_construct(
|
||||||
|
display,
|
||||||
|
bus,
|
||||||
|
240, // width (after rotation)
|
||||||
|
135, // height (after rotation)
|
||||||
|
52, // column start
|
||||||
|
40, // row start
|
||||||
|
90, // rotation
|
||||||
|
16, // color depth
|
||||||
|
false, // grayscale
|
||||||
|
false, // pixels in a byte share a row. Only valid for depths < 8
|
||||||
|
1, // bytes per cell. Only valid for depths < 8
|
||||||
|
false, // reverse_pixels_in_byte. Only valid for depths < 8
|
||||||
|
true, // reverse_pixels_in_word
|
||||||
|
MIPI_COMMAND_SET_COLUMN_ADDRESS, // set column command
|
||||||
|
MIPI_COMMAND_SET_PAGE_ADDRESS, // set row command
|
||||||
|
MIPI_COMMAND_WRITE_MEMORY_START, // write memory command
|
||||||
|
display_init_sequence,
|
||||||
|
sizeof(display_init_sequence),
|
||||||
|
&pin_GPIO4, // backlight pin
|
||||||
|
NO_BRIGHTNESS_COMMAND,
|
||||||
|
1.0f, // brightness (ignored)
|
||||||
|
false, // auto_brightness
|
||||||
|
false, // single_byte_bounds
|
||||||
|
false, // data_as_commands
|
||||||
|
true, // auto_refresh
|
||||||
|
60, // native_frames_per_second
|
||||||
|
true, // backlight_on_high
|
||||||
|
false // SH1107_addressing
|
||||||
|
);
|
||||||
|
|
||||||
|
common_hal_never_reset_pin(&pin_GPIO4); // backlight pin
|
||||||
|
}
|
||||||
|
|
||||||
|
void board_init(void) {
|
||||||
|
// Pin 22 has to be pulled high to turn on the LCD
|
||||||
|
const uint PWR_PIN = LCD_POWER;
|
||||||
|
gpio_init(PWR_PIN);
|
||||||
|
gpio_set_dir(PWR_PIN, GPIO_OUT);
|
||||||
|
gpio_put(PWR_PIN, 1);
|
||||||
|
common_hal_never_reset_pin(&pin_GPIO22);
|
||||||
|
|
||||||
|
// Display
|
||||||
|
display_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool board_requests_safe_mode(void) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_board(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void board_deinit(void) {
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
#define MICROPY_HW_BOARD_NAME "LILYGO T-DISPLAY"
|
||||||
|
#define MICROPY_HW_MCU_NAME "rp2040"
|
@ -0,0 +1,11 @@
|
|||||||
|
USB_VID = 0x1209
|
||||||
|
USB_PID = 0x2023
|
||||||
|
USB_PRODUCT = "T-Display"
|
||||||
|
USB_MANUFACTURER = "Lilygo"
|
||||||
|
|
||||||
|
CHIP_VARIANT = RP2040
|
||||||
|
CHIP_FAMILY = rp2
|
||||||
|
|
||||||
|
EXTERNAL_FLASH_DEVICES = "W25Q32JVxQ"
|
||||||
|
|
||||||
|
CIRCUITPY__EVE = 1
|
@ -0,0 +1 @@
|
|||||||
|
// Put board-specific pico-sdk definitions here. This file must exist.
|
65
ports/raspberrypi/boards/lilygo_t_display_rp2040/pins.c
Normal file
65
ports/raspberrypi/boards/lilygo_t_display_rp2040/pins.c
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#include "shared-bindings/board/__init__.h"
|
||||||
|
#include "shared-module/displayio/__init__.h"
|
||||||
|
|
||||||
|
STATIC const mp_rom_map_elem_t board_module_globals_table[] = {
|
||||||
|
CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO1) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_GP1), MP_ROM_PTR(&pin_GPIO1) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO2) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_GP2), MP_ROM_PTR(&pin_GPIO2) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_BUTTON_L), MP_ROM_PTR(&pin_GPIO6) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_GP6), MP_ROM_PTR(&pin_GPIO6) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_BUTTON_R), MP_ROM_PTR(&pin_GPIO6) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_GP7), MP_ROM_PTR(&pin_GPIO7) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_GP8), MP_ROM_PTR(&pin_GPIO8) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_GP9), MP_ROM_PTR(&pin_GPIO9) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO10) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_GP10), MP_ROM_PTR(&pin_GPIO10) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO11) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_GP11), MP_ROM_PTR(&pin_GPIO11) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO12) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_GP12), MP_ROM_PTR(&pin_GPIO12) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_GP13), MP_ROM_PTR(&pin_GPIO13) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO14) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_GP14), MP_ROM_PTR(&pin_GPIO14) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO15) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_GP15), MP_ROM_PTR(&pin_GPIO15) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_GP16), MP_ROM_PTR(&pin_GPIO16) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_GP18), MP_ROM_PTR(&pin_GPIO18) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_GP21), MP_ROM_PTR(&pin_GPIO21) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_TFT_POWER), MP_ROM_PTR(&pin_GPIO22) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_GP22), MP_ROM_PTR(&pin_GPIO22) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO25) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_GP25), MP_ROM_PTR(&pin_GPIO25) },
|
||||||
|
|
||||||
|
// 1.14 inch LCD ST7789
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_LCD_MOSI), MP_ROM_PTR(&pin_GPIO3) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_LCD_CLK), MP_ROM_PTR(&pin_GPIO2) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(&pin_GPIO5) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_LCD_RESET), MP_ROM_PTR(&pin_GPIO0) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_LCD_BACKLIGHT), MP_ROM_PTR(&pin_GPIO4) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_LCD_DC), MP_ROM_PTR(&pin_GPIO1) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_VOLTAGE_MONITOR), MP_ROM_PTR(&pin_GPIO29) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) },
|
||||||
|
};
|
||||||
|
MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table);
|
114
ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c
Normal file
114
ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the MicroPython project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 Scott Shawcroft for Adafruit Industries
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "supervisor/board.h"
|
||||||
|
|
||||||
|
#include "mpconfigboard.h"
|
||||||
|
#include "shared-bindings/busio/SPI.h"
|
||||||
|
#include "shared-bindings/displayio/FourWire.h"
|
||||||
|
#include "shared-bindings/microcontroller/Pin.h"
|
||||||
|
#include "shared-module/displayio/__init__.h"
|
||||||
|
#include "shared-bindings/board/__init__.h"
|
||||||
|
#include "supervisor/shared/board.h"
|
||||||
|
|
||||||
|
#define DELAY 0x80
|
||||||
|
|
||||||
|
// This is an SPD1656 control chip. The display is a 5.7" ACeP EInk.
|
||||||
|
|
||||||
|
const uint8_t display_start_sequence[] = {
|
||||||
|
0x01, 4, 0x37, 0x00, 0x23, 0x23, // power setting
|
||||||
|
0x00, 2, 0xef, 0x08, // panel setting (PSR)
|
||||||
|
0x03, 1, 0x00, // PFS
|
||||||
|
0x06, 3, 0xc7, 0xc7, 0x1d, // booster
|
||||||
|
0x30, 1, 0x3c, // PLL setting
|
||||||
|
0x41, 1, 0x00, // TSE
|
||||||
|
0x50, 1, 0x37, // vcom and data interval setting
|
||||||
|
0x60, 1, 0x22, // tcon setting
|
||||||
|
0x61, 4, 0x02, 0x58, 0x01, 0xc0, // tres
|
||||||
|
0xe3, 1, 0xaa, // PWS
|
||||||
|
0x04, DELAY | 0, 0xc8, // VCM DC and delay 200ms
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t display_stop_sequence[] = {
|
||||||
|
0x02, 1, 0x00, // power off
|
||||||
|
0x07, 1, 0xa5 // deep sleep
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t refresh_sequence[] = {
|
||||||
|
0x12, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
void board_init(void) {
|
||||||
|
displayio_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus;
|
||||||
|
busio_spi_obj_t *spi = common_hal_board_create_spi(0);
|
||||||
|
|
||||||
|
bus->base.type = &displayio_fourwire_type;
|
||||||
|
common_hal_displayio_fourwire_construct(bus,
|
||||||
|
spi,
|
||||||
|
&pin_GPIO28, // EPD_DC Command or data
|
||||||
|
&pin_GPIO17, // EPD_CS Chip select
|
||||||
|
&pin_GPIO27, // EPD_RST Reset
|
||||||
|
1000000, // Baudrate
|
||||||
|
0, // Polarity
|
||||||
|
0); // Phase
|
||||||
|
|
||||||
|
displayio_epaperdisplay_obj_t *display = &allocate_display()->epaper_display;
|
||||||
|
display->base.type = &displayio_epaperdisplay_type;
|
||||||
|
common_hal_displayio_epaperdisplay_construct(
|
||||||
|
display,
|
||||||
|
bus,
|
||||||
|
display_start_sequence, sizeof(display_start_sequence),
|
||||||
|
1.0, // start up time
|
||||||
|
display_stop_sequence, sizeof(display_stop_sequence),
|
||||||
|
600, // width
|
||||||
|
448, // height
|
||||||
|
640, // ram_width
|
||||||
|
480, // ram_height
|
||||||
|
0, // colstart
|
||||||
|
0, // rowstart
|
||||||
|
180, // rotation
|
||||||
|
NO_COMMAND, // set_column_window_command
|
||||||
|
NO_COMMAND, // set_row_window_command
|
||||||
|
NO_COMMAND, // set_current_column_command
|
||||||
|
NO_COMMAND, // set_current_row_command
|
||||||
|
0x10, // write_black_ram_command
|
||||||
|
false, // black_bits_inverted
|
||||||
|
NO_COMMAND, // write_color_ram_command
|
||||||
|
false, // color_bits_inverted
|
||||||
|
0x000000, // highlight_color
|
||||||
|
refresh_sequence, sizeof(refresh_sequence),
|
||||||
|
28.0, // refresh_time
|
||||||
|
NULL, // busy_pin
|
||||||
|
false, // busy_state
|
||||||
|
30.0, // seconds_per_frame
|
||||||
|
false, // always_toggle_chip_select
|
||||||
|
false, // grayscale
|
||||||
|
true, // acep
|
||||||
|
false, // two_byte_sequence_length
|
||||||
|
false); // address_little_endian
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here.
|
1
ports/raspberrypi/boards/pimoroni_inky_frame_5_7/link.ld
Normal file
1
ports/raspberrypi/boards/pimoroni_inky_frame_5_7/link.ld
Normal file
@ -0,0 +1 @@
|
|||||||
|
firmware_size = 1532k;
|
@ -0,0 +1,17 @@
|
|||||||
|
#define MICROPY_HW_BOARD_NAME "Pimoroni Inky Frame 5.7"
|
||||||
|
#define MICROPY_HW_MCU_NAME "rp2040"
|
||||||
|
|
||||||
|
#define CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL (1)
|
||||||
|
#define CIRCUITPY_DIGITALIO_HAVE_INVALID_DRIVE_MODE (1)
|
||||||
|
|
||||||
|
#define MICROPY_HW_LED_STATUS (&pin_GPIO6)
|
||||||
|
|
||||||
|
#define DEFAULT_I2C_BUS_SCL (&pin_GPIO4)
|
||||||
|
#define DEFAULT_I2C_BUS_SDA (&pin_GPIO5)
|
||||||
|
|
||||||
|
#define DEFAULT_UART_BUS_TX (&pin_GPIO0)
|
||||||
|
#define DEFAULT_UART_BUS_RX (&pin_GPIO1)
|
||||||
|
|
||||||
|
#define DEFAULT_SPI_BUS_SCK (&pin_GPIO18)
|
||||||
|
#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO19)
|
||||||
|
#define DEFAULT_SPI_BUS_MISO (&pin_GPIO16)
|
@ -0,0 +1,26 @@
|
|||||||
|
USB_VID = 0x2E8A
|
||||||
|
USB_PID = 0x1018
|
||||||
|
USB_PRODUCT = "Inky Frame 5.7"
|
||||||
|
USB_MANUFACTURER = "Pimoroni"
|
||||||
|
|
||||||
|
CHIP_VARIANT = RP2040
|
||||||
|
CHIP_FAMILY = rp2
|
||||||
|
|
||||||
|
EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ"
|
||||||
|
|
||||||
|
CIRCUITPY__EVE = 1
|
||||||
|
|
||||||
|
CIRCUITPY_CYW43 = 1
|
||||||
|
CIRCUITPY_SSL = 1
|
||||||
|
CIRCUITPY_SSL_MBEDTLS = 1
|
||||||
|
CIRCUITPY_HASHLIB = 1
|
||||||
|
CIRCUITPY_WEB_WORKFLOW = 1
|
||||||
|
CIRCUITPY_MDNS = 1
|
||||||
|
CIRCUITPY_SOCKETPOOL = 1
|
||||||
|
CIRCUITPY_WIFI = 1
|
||||||
|
|
||||||
|
CFLAGS += -DCYW43_PIN_WL_HOST_WAKE=24 -DCYW43_PIN_WL_REG_ON=23 -DCYW43_WL_GPIO_COUNT=3 -DCYW43_WL_GPIO_LED_PIN=0
|
||||||
|
# Must be accompanied by a linker script change
|
||||||
|
CFLAGS += -DCIRCUITPY_FIRMWARE_SIZE='(1536 * 1024)'
|
||||||
|
|
||||||
|
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel
|
@ -0,0 +1 @@
|
|||||||
|
// Put board-specific pico-sdk definitions here. This file must exist.
|
52
ports/raspberrypi/boards/pimoroni_inky_frame_5_7/pins.c
Normal file
52
ports/raspberrypi/boards/pimoroni_inky_frame_5_7/pins.c
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#include "shared-bindings/board/__init__.h"
|
||||||
|
|
||||||
|
#include "supervisor/board.h"
|
||||||
|
#include "shared-module/displayio/__init__.h"
|
||||||
|
|
||||||
|
STATIC const mp_rom_map_elem_t board_module_globals_table[] = {
|
||||||
|
CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO0) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO1) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_HOLD_SYS_EN), MP_ROM_PTR(&pin_GPIO2) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_I2C_INT), MP_ROM_PTR(&pin_GPIO3) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO4) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO5) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_LED_ACT), MP_ROM_PTR(&pin_GPIO6) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO6) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_LED_CONN), MP_ROM_PTR(&pin_GPIO7) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_SWITCH_CLK), MP_ROM_PTR(&pin_GPIO8) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_SWITCH_LATCH), MP_ROM_PTR(&pin_GPIO9) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_SWITCH_OUT), MP_ROM_PTR(&pin_GPIO10) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_LED_A), MP_ROM_PTR(&pin_GPIO11) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_LED_B), MP_ROM_PTR(&pin_GPIO12) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_LED_C), MP_ROM_PTR(&pin_GPIO13) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_LED_D), MP_ROM_PTR(&pin_GPIO14) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_LED_E), MP_ROM_PTR(&pin_GPIO15) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO16) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_INKY_CS), MP_ROM_PTR(&pin_GPIO17) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_SCLK), MP_ROM_PTR(&pin_GPIO18) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO19) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_SD_DAT1), MP_ROM_PTR(&pin_GPIO20) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_SD_DAT2), MP_ROM_PTR(&pin_GPIO21) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO22) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO26) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_INKY_RES), MP_ROM_PTR(&pin_GPIO27) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_INKY_DC), MP_ROM_PTR(&pin_GPIO28) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_SMPS_MODE), MP_ROM_PTR(&pin_CYW1) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_VBUS_SENSE), MP_ROM_PTR(&pin_CYW2) },
|
||||||
|
|
||||||
|
{ 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_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].epaper_display)},
|
||||||
|
};
|
||||||
|
MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table);
|
@ -48,6 +48,8 @@
|
|||||||
#include "lwip/raw.h"
|
#include "lwip/raw.h"
|
||||||
#include "lwip_src/ping.h"
|
#include "lwip_src/ping.h"
|
||||||
|
|
||||||
|
#include "shared/netutils/dhcpserver.h"
|
||||||
|
|
||||||
#define MAC_ADDRESS_LENGTH 6
|
#define MAC_ADDRESS_LENGTH 6
|
||||||
|
|
||||||
#define NETIF_STA (&cyw43_state.netif[CYW43_ITF_STA])
|
#define NETIF_STA (&cyw43_state.netif[CYW43_ITF_STA])
|
||||||
@ -161,7 +163,15 @@ void common_hal_wifi_radio_stop_station(wifi_radio_obj_t *self) {
|
|||||||
// (by tcpip_link_status). However since ap disconnection isn't working
|
// (by tcpip_link_status). However since ap disconnection isn't working
|
||||||
// either, this is not an issue.
|
// either, this is not an issue.
|
||||||
cyw43_wifi_leave(&cyw43_state, CYW43_ITF_AP);
|
cyw43_wifi_leave(&cyw43_state, CYW43_ITF_AP);
|
||||||
|
const size_t timeout_ms = 500;
|
||||||
|
uint64_t start = port_get_raw_ticks(NULL);
|
||||||
|
uint64_t deadline = start + timeout_ms;
|
||||||
|
while (port_get_raw_ticks(NULL) < deadline && (cyw43_tcpip_link_status(&cyw43_state, CYW43_ITF_STA) != CYW43_LINK_DOWN)) {
|
||||||
|
RUN_BACKGROUND_TASKS;
|
||||||
|
if (mp_hal_is_interrupted()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
bindings_cyw43_wifi_enforce_pm();
|
bindings_cyw43_wifi_enforce_pm();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,6 +368,17 @@ void common_hal_wifi_radio_stop_dhcp_client(wifi_radio_obj_t *self) {
|
|||||||
dhcp_stop(NETIF_STA);
|
dhcp_stop(NETIF_STA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void common_hal_wifi_radio_start_dhcp_server(wifi_radio_obj_t *self) {
|
||||||
|
ip4_addr_t ipv4_addr, netmask_addr;
|
||||||
|
ipaddress_ipaddress_to_lwip(common_hal_wifi_radio_get_ipv4_address_ap(self), &ipv4_addr);
|
||||||
|
ipaddress_ipaddress_to_lwip(common_hal_wifi_radio_get_ipv4_subnet_ap(self), &netmask_addr);
|
||||||
|
dhcp_server_init(&cyw43_state.dhcp_server, &ipv4_addr, &netmask_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_wifi_radio_stop_dhcp_server(wifi_radio_obj_t *self) {
|
||||||
|
dhcp_server_deinit(&cyw43_state.dhcp_server);
|
||||||
|
}
|
||||||
|
|
||||||
void common_hal_wifi_radio_set_ipv4_address(wifi_radio_obj_t *self, mp_obj_t ipv4, mp_obj_t netmask, mp_obj_t gateway, mp_obj_t ipv4_dns) {
|
void common_hal_wifi_radio_set_ipv4_address(wifi_radio_obj_t *self, mp_obj_t ipv4, mp_obj_t netmask, mp_obj_t gateway, mp_obj_t ipv4_dns) {
|
||||||
common_hal_wifi_radio_stop_dhcp_client(self);
|
common_hal_wifi_radio_stop_dhcp_client(self);
|
||||||
|
|
||||||
@ -371,6 +392,18 @@ void common_hal_wifi_radio_set_ipv4_address(wifi_radio_obj_t *self, mp_obj_t ipv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void common_hal_wifi_radio_set_ipv4_address_ap(wifi_radio_obj_t *self, mp_obj_t ipv4, mp_obj_t netmask, mp_obj_t gateway) {
|
||||||
|
common_hal_wifi_radio_stop_dhcp_server(self);
|
||||||
|
|
||||||
|
ip4_addr_t ipv4_addr, netmask_addr, gateway_addr;
|
||||||
|
ipaddress_ipaddress_to_lwip(ipv4, &ipv4_addr);
|
||||||
|
ipaddress_ipaddress_to_lwip(netmask, &netmask_addr);
|
||||||
|
ipaddress_ipaddress_to_lwip(gateway, &gateway_addr);
|
||||||
|
netif_set_addr(NETIF_AP, &ipv4_addr, &netmask_addr, &gateway_addr);
|
||||||
|
|
||||||
|
common_hal_wifi_radio_start_dhcp_server(self);
|
||||||
|
}
|
||||||
|
|
||||||
volatile bool ping_received;
|
volatile bool ping_received;
|
||||||
uint32_t ping_time;
|
uint32_t ping_time;
|
||||||
|
|
||||||
|
@ -95,14 +95,16 @@ void port_internal_flash_flush(void) {
|
|||||||
if (_cache_lba == NO_CACHE) {
|
if (_cache_lba == NO_CACHE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Make sure we don't have an interrupt while we do flash operations.
|
||||||
common_hal_mcu_disable_interrupts();
|
common_hal_mcu_disable_interrupts();
|
||||||
flash_range_erase(CIRCUITPY_CIRCUITPY_DRIVE_START_ADDR + _cache_lba, SECTOR_SIZE);
|
flash_range_erase(CIRCUITPY_CIRCUITPY_DRIVE_START_ADDR + _cache_lba, SECTOR_SIZE);
|
||||||
flash_range_program(CIRCUITPY_CIRCUITPY_DRIVE_START_ADDR + _cache_lba, _cache, SECTOR_SIZE);
|
flash_range_program(CIRCUITPY_CIRCUITPY_DRIVE_START_ADDR + _cache_lba, _cache, SECTOR_SIZE);
|
||||||
common_hal_mcu_enable_interrupts();
|
|
||||||
_cache_lba = NO_CACHE;
|
_cache_lba = NO_CACHE;
|
||||||
|
common_hal_mcu_enable_interrupts();
|
||||||
}
|
}
|
||||||
|
|
||||||
mp_uint_t supervisor_flash_read_blocks(uint8_t *dest, uint32_t block, uint32_t num_blocks) {
|
mp_uint_t supervisor_flash_read_blocks(uint8_t *dest, uint32_t block, uint32_t num_blocks) {
|
||||||
|
port_internal_flash_flush(); // we never read out of the cache, so we have to write it if dirty
|
||||||
memcpy(dest,
|
memcpy(dest,
|
||||||
(void *)(XIP_BASE + CIRCUITPY_CIRCUITPY_DRIVE_START_ADDR + block * FILESYSTEM_BLOCK_SIZE),
|
(void *)(XIP_BASE + CIRCUITPY_CIRCUITPY_DRIVE_START_ADDR + block * FILESYSTEM_BLOCK_SIZE),
|
||||||
num_blocks * FILESYSTEM_BLOCK_SIZE);
|
num_blocks * FILESYSTEM_BLOCK_SIZE);
|
||||||
@ -118,6 +120,7 @@ mp_uint_t supervisor_flash_write_blocks(const uint8_t *src, uint32_t lba, uint32
|
|||||||
uint8_t block_offset = block_address % blocks_per_sector;
|
uint8_t block_offset = block_address % blocks_per_sector;
|
||||||
|
|
||||||
if (_cache_lba != block_address) {
|
if (_cache_lba != block_address) {
|
||||||
|
port_internal_flash_flush();
|
||||||
memcpy(_cache,
|
memcpy(_cache,
|
||||||
(void *)(XIP_BASE + CIRCUITPY_CIRCUITPY_DRIVE_START_ADDR + sector_offset),
|
(void *)(XIP_BASE + CIRCUITPY_CIRCUITPY_DRIVE_START_ADDR + sector_offset),
|
||||||
SECTOR_SIZE);
|
SECTOR_SIZE);
|
||||||
@ -133,11 +136,6 @@ mp_uint_t supervisor_flash_write_blocks(const uint8_t *src, uint32_t lba, uint32
|
|||||||
FILESYSTEM_BLOCK_SIZE);
|
FILESYSTEM_BLOCK_SIZE);
|
||||||
block++;
|
block++;
|
||||||
}
|
}
|
||||||
// Make sure we don't have an interrupt while we do flash operations.
|
|
||||||
common_hal_mcu_disable_interrupts();
|
|
||||||
flash_range_erase(CIRCUITPY_CIRCUITPY_DRIVE_START_ADDR + sector_offset, SECTOR_SIZE);
|
|
||||||
flash_range_program(CIRCUITPY_CIRCUITPY_DRIVE_START_ADDR + sector_offset, _cache, SECTOR_SIZE);
|
|
||||||
common_hal_mcu_enable_interrupts();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0; // success
|
return 0; // success
|
||||||
|
@ -104,22 +104,23 @@ safe_mode_t port_init(void) {
|
|||||||
_binary_info();
|
_binary_info();
|
||||||
// Set brown out.
|
// Set brown out.
|
||||||
|
|
||||||
|
// Load from the XIP memory space that doesn't cache. That way we don't
|
||||||
|
// evict anything else. The code we're loading is linked to the RAM address
|
||||||
|
// anyway.
|
||||||
|
size_t nocache = 0x03000000;
|
||||||
|
|
||||||
// Copy all of the "tightly coupled memory" code and data to run from RAM.
|
// Copy all of the "tightly coupled memory" code and data to run from RAM.
|
||||||
// This lets us use the 16k cache for dynamically used data and code.
|
// This lets us use the 16k cache for dynamically used data and code.
|
||||||
// We must do this before we try and call any of its code or load the data.
|
// We must do this before we try and call any of its code or load the data.
|
||||||
|
uint32_t *itcm_flash_copy = (uint32_t *)(((size_t)&_ld_itcm_flash_copy) | nocache);
|
||||||
for (uint32_t i = 0; i < ((size_t)&_ld_itcm_size) / 4; i++) {
|
for (uint32_t i = 0; i < ((size_t)&_ld_itcm_size) / 4; i++) {
|
||||||
(&_ld_itcm_destination)[i] = (&_ld_itcm_flash_copy)[i];
|
(&_ld_itcm_destination)[i] = itcm_flash_copy[i];
|
||||||
// Now zero it out to evict the line from the XIP cache. Without this,
|
|
||||||
// it'll stay in the XIP cache anyway.
|
|
||||||
(&_ld_itcm_flash_copy)[i] = 0x0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy all of the data to run from DTCM.
|
// Copy all of the data to run from DTCM.
|
||||||
|
uint32_t *dtcm_flash_copy = (uint32_t *)(((size_t)&_ld_dtcm_data_flash_copy) | nocache);
|
||||||
for (uint32_t i = 0; i < ((size_t)&_ld_dtcm_data_size) / 4; i++) {
|
for (uint32_t i = 0; i < ((size_t)&_ld_dtcm_data_size) / 4; i++) {
|
||||||
(&_ld_dtcm_data_destination)[i] = (&_ld_dtcm_data_flash_copy)[i];
|
(&_ld_dtcm_data_destination)[i] = dtcm_flash_copy[i];
|
||||||
// Now zero it out to evict the line from the XIP cache. Without this,
|
|
||||||
// it'll stay in the XIP cache anyway.
|
|
||||||
(&_ld_dtcm_data_flash_copy)[i] = 0x0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear DTCM bss.
|
// Clear DTCM bss.
|
||||||
|
@ -188,6 +188,10 @@ void reset_port(void) {
|
|||||||
#if CIRCUITPY_BLEIO
|
#if CIRCUITPY_BLEIO
|
||||||
bleio_reset();
|
bleio_reset();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if CIRCUITPY_RTC
|
||||||
|
rtc_reset();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset_to_bootloader(void) {
|
void reset_to_bootloader(void) {
|
||||||
|
@ -32,7 +32,9 @@
|
|||||||
#include "shared/timeutils/timeutils.h"
|
#include "shared/timeutils/timeutils.h"
|
||||||
|
|
||||||
// Default period for ticks is 1/1024 second
|
// Default period for ticks is 1/1024 second
|
||||||
#define TICK_DIVISOR 1024
|
#define TICKS_PER_SECOND 1024
|
||||||
|
// Based on a 32768 kHz clock
|
||||||
|
#define SUBTICKS_PER_TICK 32
|
||||||
|
|
||||||
STATIC RTC_HandleTypeDef hrtc;
|
STATIC RTC_HandleTypeDef hrtc;
|
||||||
|
|
||||||
@ -47,6 +49,13 @@ volatile uint32_t cached_date = 0;
|
|||||||
volatile uint32_t seconds_to_minute = 0;
|
volatile uint32_t seconds_to_minute = 0;
|
||||||
volatile uint32_t cached_hours_minutes = 0;
|
volatile uint32_t cached_hours_minutes = 0;
|
||||||
|
|
||||||
|
// The RTC starts at 2000-01-01 when it comes up.
|
||||||
|
// If the RTC is set to a later time, the ticks the RTC returns will be offset by the new time.
|
||||||
|
// Remember that offset so it can be removed when returning a monotonic tick count.
|
||||||
|
static int64_t rtc_ticks_offset;
|
||||||
|
// Normalized to be 0-31 inclusive, so always positive.
|
||||||
|
static uint8_t rtc_subticks_offset;
|
||||||
|
|
||||||
volatile bool alarmed_already[2];
|
volatile bool alarmed_already[2];
|
||||||
|
|
||||||
bool peripherals_wkup_on = false;
|
bool peripherals_wkup_on = false;
|
||||||
@ -59,6 +68,9 @@ uint32_t stm32_peripherals_get_rtc_freq(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void stm32_peripherals_rtc_init(void) {
|
void stm32_peripherals_rtc_init(void) {
|
||||||
|
rtc_ticks_offset = 0;
|
||||||
|
rtc_subticks_offset = 0;
|
||||||
|
|
||||||
// RTC oscillator selection is handled in peripherals/<family>/<line>/clocks.c
|
// RTC oscillator selection is handled in peripherals/<family>/<line>/clocks.c
|
||||||
__HAL_RCC_RTC_ENABLE();
|
__HAL_RCC_RTC_ENABLE();
|
||||||
hrtc.Instance = RTC;
|
hrtc.Instance = RTC;
|
||||||
@ -74,49 +86,9 @@ void stm32_peripherals_rtc_init(void) {
|
|||||||
HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
|
HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CIRCUITPY_RTC
|
|
||||||
void stm32_peripherals_rtc_get_time(timeutils_struct_time_t *tm) {
|
|
||||||
RTC_DateTypeDef date = {0};
|
|
||||||
RTC_TimeTypeDef time = {0};
|
|
||||||
|
|
||||||
int code;
|
|
||||||
if ((code = HAL_RTC_GetTime(&hrtc, &time, RTC_FORMAT_BIN)) == HAL_OK &&
|
|
||||||
(code = HAL_RTC_GetDate(&hrtc, &date, RTC_FORMAT_BIN)) == HAL_OK) {
|
|
||||||
tm->tm_hour = time.Hours;
|
|
||||||
tm->tm_min = time.Minutes;
|
|
||||||
tm->tm_sec = time.Seconds;
|
|
||||||
tm->tm_wday = date.WeekDay - 1;
|
|
||||||
tm->tm_mday = date.Date;
|
|
||||||
tm->tm_mon = date.Month;
|
|
||||||
tm->tm_year = date.Year + 2000;
|
|
||||||
tm->tm_yday = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void stm32_peripherals_rtc_set_time(timeutils_struct_time_t *tm) {
|
|
||||||
RTC_DateTypeDef date = {0};
|
|
||||||
RTC_TimeTypeDef time = {0};
|
|
||||||
|
|
||||||
time.Hours = tm->tm_hour;
|
|
||||||
time.Minutes = tm->tm_min;
|
|
||||||
time.Seconds = tm->tm_sec;
|
|
||||||
date.WeekDay = tm->tm_wday + 1;
|
|
||||||
date.Date = tm->tm_mday;
|
|
||||||
date.Month = tm->tm_mon;
|
|
||||||
date.Year = tm->tm_year - 2000;
|
|
||||||
time.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
|
|
||||||
time.StoreOperation = RTC_STOREOPERATION_RESET;
|
|
||||||
|
|
||||||
if (HAL_RTC_SetTime(&hrtc, &time, RTC_FORMAT_BIN) != HAL_OK ||
|
|
||||||
HAL_RTC_SetDate(&hrtc, &date, RTC_FORMAT_BIN) != HAL_OK) {
|
|
||||||
// todo - throw an exception
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// This function is called often for timing so we cache the seconds elapsed computation based on the
|
// This function is called often for timing so we cache the seconds elapsed computation based on the
|
||||||
// register value. The STM HAL always does shifts and conversion if we use it directly.
|
// register value. The STM HAL always does shifts and conversion if we use it directly.
|
||||||
uint64_t stm32_peripherals_rtc_raw_ticks(uint8_t *subticks) {
|
STATIC uint64_t stm32_peripherals_rtc_raw_ticks(uint8_t *subticks) {
|
||||||
// Disable IRQs to ensure we read all of the RTC registers as close in time as possible. Read
|
// Disable IRQs to ensure we read all of the RTC registers as close in time as possible. Read
|
||||||
// SSR twice to make sure we didn't read across a tick.
|
// SSR twice to make sure we didn't read across a tick.
|
||||||
__disable_irq();
|
__disable_irq();
|
||||||
@ -157,13 +129,84 @@ uint64_t stm32_peripherals_rtc_raw_ticks(uint8_t *subticks) {
|
|||||||
uint8_t seconds = (uint8_t)(time & (RTC_TR_ST | RTC_TR_SU));
|
uint8_t seconds = (uint8_t)(time & (RTC_TR_ST | RTC_TR_SU));
|
||||||
seconds = (uint8_t)RTC_Bcd2ToByte(seconds);
|
seconds = (uint8_t)RTC_Bcd2ToByte(seconds);
|
||||||
if (subticks != NULL) {
|
if (subticks != NULL) {
|
||||||
*subticks = subseconds % 32;
|
*subticks = subseconds % SUBTICKS_PER_TICK;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t raw_ticks = ((uint64_t)TICK_DIVISOR) * (seconds_to_date + seconds_to_minute + seconds) + subseconds / 32;
|
uint64_t raw_ticks = ((uint64_t)TICKS_PER_SECOND) * (seconds_to_date + seconds_to_minute + seconds) + subseconds / SUBTICKS_PER_TICK;
|
||||||
return raw_ticks;
|
return raw_ticks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function returns monotonically increasing ticks by adjusting away the RTC tick offset
|
||||||
|
// from the last time the date was set.
|
||||||
|
uint64_t stm32_peripherals_rtc_monotonic_ticks(uint8_t *subticks) {
|
||||||
|
uint8_t raw_subticks;
|
||||||
|
uint64_t monotonic_ticks = stm32_peripherals_rtc_raw_ticks(&raw_subticks) - rtc_ticks_offset;
|
||||||
|
int8_t monotonic_subticks = raw_subticks - rtc_subticks_offset;
|
||||||
|
// Difference might be negative. Normalize to 0-31.
|
||||||
|
// `while` not really necessary; should only loop 0 or 1 times.
|
||||||
|
while (monotonic_subticks < 0) {
|
||||||
|
monotonic_ticks--;
|
||||||
|
monotonic_subticks += SUBTICKS_PER_TICK;
|
||||||
|
}
|
||||||
|
*subticks = (uint8_t)monotonic_subticks;
|
||||||
|
return monotonic_ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CIRCUITPY_RTC
|
||||||
|
void stm32_peripherals_rtc_get_time(timeutils_struct_time_t *tm) {
|
||||||
|
RTC_DateTypeDef date = {0};
|
||||||
|
RTC_TimeTypeDef time = {0};
|
||||||
|
|
||||||
|
int code;
|
||||||
|
if ((code = HAL_RTC_GetTime(&hrtc, &time, RTC_FORMAT_BIN)) == HAL_OK &&
|
||||||
|
(code = HAL_RTC_GetDate(&hrtc, &date, RTC_FORMAT_BIN)) == HAL_OK) {
|
||||||
|
tm->tm_hour = time.Hours;
|
||||||
|
tm->tm_min = time.Minutes;
|
||||||
|
tm->tm_sec = time.Seconds;
|
||||||
|
tm->tm_wday = date.WeekDay - 1;
|
||||||
|
tm->tm_mday = date.Date;
|
||||||
|
tm->tm_mon = date.Month;
|
||||||
|
tm->tm_year = date.Year + 2000;
|
||||||
|
tm->tm_yday = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stm32_peripherals_rtc_set_time(timeutils_struct_time_t *tm) {
|
||||||
|
RTC_DateTypeDef date = {0};
|
||||||
|
RTC_TimeTypeDef time = {0};
|
||||||
|
|
||||||
|
uint8_t current_monotonic_subticks;
|
||||||
|
uint64_t current_monotonic_ticks = stm32_peripherals_rtc_monotonic_ticks(¤t_monotonic_subticks);
|
||||||
|
|
||||||
|
// SubSeconds will always be set to zero.
|
||||||
|
time.Hours = tm->tm_hour;
|
||||||
|
time.Minutes = tm->tm_min;
|
||||||
|
time.Seconds = tm->tm_sec;
|
||||||
|
date.WeekDay = tm->tm_wday + 1;
|
||||||
|
date.Date = tm->tm_mday;
|
||||||
|
date.Month = tm->tm_mon;
|
||||||
|
date.Year = tm->tm_year - 2000;
|
||||||
|
time.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
|
||||||
|
time.StoreOperation = RTC_STOREOPERATION_RESET;
|
||||||
|
|
||||||
|
if (HAL_RTC_SetTime(&hrtc, &time, RTC_FORMAT_BIN) != HAL_OK ||
|
||||||
|
HAL_RTC_SetDate(&hrtc, &date, RTC_FORMAT_BIN) != HAL_OK) {
|
||||||
|
// todo - throw an exception
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t raw_subticks;
|
||||||
|
rtc_ticks_offset = stm32_peripherals_rtc_raw_ticks(&raw_subticks) - current_monotonic_ticks;
|
||||||
|
int8_t rtc_subticks_offset_signed = raw_subticks - current_monotonic_subticks;
|
||||||
|
// Difference might be negative. Normalize subticks to 0-31.
|
||||||
|
// `while` not really necessary; should only loop 0 or 1 times.
|
||||||
|
while (rtc_subticks_offset_signed < 0) {
|
||||||
|
rtc_ticks_offset--;
|
||||||
|
rtc_subticks_offset_signed += SUBTICKS_PER_TICK;
|
||||||
|
}
|
||||||
|
rtc_subticks_offset = (uint8_t)rtc_subticks_offset_signed;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void stm32_peripherals_rtc_assign_wkup_callback(void (*callback)(void)) {
|
void stm32_peripherals_rtc_assign_wkup_callback(void (*callback)(void)) {
|
||||||
wkup_callback = callback;
|
wkup_callback = callback;
|
||||||
}
|
}
|
||||||
@ -177,7 +220,7 @@ void stm32_peripherals_rtc_set_wakeup_mode_seconds(uint32_t seconds) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void stm32_peripherals_rtc_set_wakeup_mode_tick(void) {
|
void stm32_peripherals_rtc_set_wakeup_mode_tick(void) {
|
||||||
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, (rtc_clock_frequency / 16) / TICK_DIVISOR, RTC_WAKEUPCLOCK_RTCCLK_DIV2);
|
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, (rtc_clock_frequency / 16) / TICKS_PER_SECOND, RTC_WAKEUPCLOCK_RTCCLK_DIV2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void stm32_peripherals_rtc_enable_wakeup_timer(void) {
|
void stm32_peripherals_rtc_enable_wakeup_timer(void) {
|
||||||
@ -205,9 +248,9 @@ void stm32_peripherals_rtc_set_alarm(uint8_t alarm_idx, uint32_t ticks) {
|
|||||||
uint64_t raw_ticks = stm32_peripherals_rtc_raw_ticks(NULL) + ticks;
|
uint64_t raw_ticks = stm32_peripherals_rtc_raw_ticks(NULL) + ticks;
|
||||||
|
|
||||||
RTC_AlarmTypeDef alarm;
|
RTC_AlarmTypeDef alarm;
|
||||||
if (ticks > TICK_DIVISOR) {
|
if (ticks > TICKS_PER_SECOND) {
|
||||||
timeutils_struct_time_t tm;
|
timeutils_struct_time_t tm;
|
||||||
timeutils_seconds_since_2000_to_struct_time(raw_ticks / TICK_DIVISOR, &tm);
|
timeutils_seconds_since_2000_to_struct_time(raw_ticks / TICKS_PER_SECOND, &tm);
|
||||||
alarm.AlarmTime.Hours = tm.tm_hour;
|
alarm.AlarmTime.Hours = tm.tm_hour;
|
||||||
alarm.AlarmTime.Minutes = tm.tm_min;
|
alarm.AlarmTime.Minutes = tm.tm_min;
|
||||||
alarm.AlarmTime.Seconds = tm.tm_sec;
|
alarm.AlarmTime.Seconds = tm.tm_sec;
|
||||||
@ -221,7 +264,7 @@ void stm32_peripherals_rtc_set_alarm(uint8_t alarm_idx, uint32_t ticks) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
alarm.AlarmTime.SubSeconds = rtc_clock_frequency - 1 -
|
alarm.AlarmTime.SubSeconds = rtc_clock_frequency - 1 -
|
||||||
((raw_ticks % TICK_DIVISOR) * 32);
|
((raw_ticks % TICKS_PER_SECOND) * SUBTICKS_PER_TICK);
|
||||||
if (alarm.AlarmTime.SubSeconds > rtc_clock_frequency) {
|
if (alarm.AlarmTime.SubSeconds > rtc_clock_frequency) {
|
||||||
alarm.AlarmTime.SubSeconds = alarm.AlarmTime.SubSeconds +
|
alarm.AlarmTime.SubSeconds = alarm.AlarmTime.SubSeconds +
|
||||||
rtc_clock_frequency;
|
rtc_clock_frequency;
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
uint32_t stm32_peripherals_get_rtc_freq(void);
|
uint32_t stm32_peripherals_get_rtc_freq(void);
|
||||||
void stm32_peripherals_rtc_init(void);
|
void stm32_peripherals_rtc_init(void);
|
||||||
uint64_t stm32_peripherals_rtc_raw_ticks(uint8_t *subticks);
|
uint64_t stm32_peripherals_rtc_monotonic_ticks(uint8_t *subticks);
|
||||||
|
|
||||||
void stm32_peripherals_rtc_assign_wkup_callback(void (*callback)(void));
|
void stm32_peripherals_rtc_assign_wkup_callback(void (*callback)(void));
|
||||||
void stm32_peripherals_rtc_set_wakeup_mode_seconds(uint32_t seconds);
|
void stm32_peripherals_rtc_set_wakeup_mode_seconds(uint32_t seconds);
|
||||||
|
@ -385,7 +385,7 @@ __attribute__((used)) void HardFault_Handler(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint64_t port_get_raw_ticks(uint8_t *subticks) {
|
uint64_t port_get_raw_ticks(uint8_t *subticks) {
|
||||||
return stm32_peripherals_rtc_raw_ticks(subticks);
|
return stm32_peripherals_rtc_monotonic_ticks(subticks);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable 1/1024 second tick.
|
// Enable 1/1024 second tick.
|
||||||
|
@ -42,6 +42,7 @@ SRC_BITMAP := \
|
|||||||
shared-bindings/struct/__init__.c \
|
shared-bindings/struct/__init__.c \
|
||||||
shared-bindings/synthio/__init__.c \
|
shared-bindings/synthio/__init__.c \
|
||||||
shared-bindings/synthio/MidiTrack.c \
|
shared-bindings/synthio/MidiTrack.c \
|
||||||
|
shared-bindings/synthio/Note.c \
|
||||||
shared-bindings/synthio/Synthesizer.c \
|
shared-bindings/synthio/Synthesizer.c \
|
||||||
shared-bindings/traceback/__init__.c \
|
shared-bindings/traceback/__init__.c \
|
||||||
shared-bindings/util.c \
|
shared-bindings/util.c \
|
||||||
@ -64,6 +65,7 @@ SRC_BITMAP := \
|
|||||||
shared-module/struct/__init__.c \
|
shared-module/struct/__init__.c \
|
||||||
shared-module/synthio/__init__.c \
|
shared-module/synthio/__init__.c \
|
||||||
shared-module/synthio/MidiTrack.c \
|
shared-module/synthio/MidiTrack.c \
|
||||||
|
shared-module/synthio/Note.c \
|
||||||
shared-module/synthio/Synthesizer.c \
|
shared-module/synthio/Synthesizer.c \
|
||||||
shared-module/traceback/__init__.c \
|
shared-module/traceback/__init__.c \
|
||||||
shared-module/zlib/__init__.c \
|
shared-module/zlib/__init__.c \
|
||||||
|
@ -194,11 +194,16 @@ mp_float_t mp_arg_validate_type_float(mp_obj_t obj, qstr arg_name) {
|
|||||||
return a_float;
|
return a_float;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mp_arg_validate_obj_float_range(mp_obj_t float_in, mp_int_t min, mp_int_t max, qstr arg_name) {
|
mp_float_t mp_arg_validate_obj_float_range(mp_obj_t float_in, mp_int_t min, mp_int_t max, qstr arg_name) {
|
||||||
const mp_float_t f = mp_arg_validate_type_float(float_in, arg_name);
|
const mp_float_t f = mp_arg_validate_type_float(float_in, arg_name);
|
||||||
|
return mp_arg_validate_float_range(f, min, max, arg_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_float_t mp_arg_validate_float_range(mp_float_t f, mp_int_t min, mp_int_t max, qstr arg_name) {
|
||||||
if (f < (mp_float_t)min || f > (mp_float_t)max) {
|
if (f < (mp_float_t)min || f > (mp_float_t)max) {
|
||||||
mp_raise_ValueError_varg(translate("%q must be %d-%d"), arg_name, min, max);
|
mp_raise_ValueError_varg(translate("%q must be %d-%d"), arg_name, min, max);
|
||||||
}
|
}
|
||||||
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
mp_float_t mp_arg_validate_obj_float_non_negative(mp_obj_t float_in, mp_float_t default_for_null, qstr arg_name) {
|
mp_float_t mp_arg_validate_obj_float_non_negative(mp_obj_t float_in, mp_float_t default_for_null, qstr arg_name) {
|
||||||
|
@ -651,6 +651,7 @@ SRC_SHARED_MODULE_ALL = \
|
|||||||
supervisor/__init__.c \
|
supervisor/__init__.c \
|
||||||
supervisor/StatusBar.c \
|
supervisor/StatusBar.c \
|
||||||
synthio/MidiTrack.c \
|
synthio/MidiTrack.c \
|
||||||
|
synthio/Note.c \
|
||||||
synthio/Synthesizer.c \
|
synthio/Synthesizer.c \
|
||||||
synthio/__init__.c \
|
synthio/__init__.c \
|
||||||
terminalio/Terminal.c \
|
terminalio/Terminal.c \
|
||||||
|
4
py/gc.c
4
py/gc.c
@ -1013,6 +1013,7 @@ bool gc_never_free(void *ptr) {
|
|||||||
// Pointers are stored in a linked list where each block is BYTES_PER_BLOCK long and the first
|
// Pointers are stored in a linked list where each block is BYTES_PER_BLOCK long and the first
|
||||||
// pointer is the next block of pointers.
|
// pointer is the next block of pointers.
|
||||||
void **current_reference_block = MP_STATE_MEM(permanent_pointers);
|
void **current_reference_block = MP_STATE_MEM(permanent_pointers);
|
||||||
|
void **last_reference_block = NULL;
|
||||||
while (current_reference_block != NULL) {
|
while (current_reference_block != NULL) {
|
||||||
for (size_t i = 1; i < BYTES_PER_BLOCK / sizeof(void *); i++) {
|
for (size_t i = 1; i < BYTES_PER_BLOCK / sizeof(void *); i++) {
|
||||||
if (current_reference_block[i] == NULL) {
|
if (current_reference_block[i] == NULL) {
|
||||||
@ -1020,6 +1021,7 @@ bool gc_never_free(void *ptr) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
last_reference_block = current_reference_block; // keep a record of last "proper" reference block
|
||||||
current_reference_block = current_reference_block[0];
|
current_reference_block = current_reference_block[0];
|
||||||
}
|
}
|
||||||
void **next_block = gc_alloc(BYTES_PER_BLOCK, false, true);
|
void **next_block = gc_alloc(BYTES_PER_BLOCK, false, true);
|
||||||
@ -1029,7 +1031,7 @@ bool gc_never_free(void *ptr) {
|
|||||||
if (MP_STATE_MEM(permanent_pointers) == NULL) {
|
if (MP_STATE_MEM(permanent_pointers) == NULL) {
|
||||||
MP_STATE_MEM(permanent_pointers) = next_block;
|
MP_STATE_MEM(permanent_pointers) = next_block;
|
||||||
} else {
|
} else {
|
||||||
current_reference_block[0] = next_block;
|
last_reference_block[0] = next_block;
|
||||||
}
|
}
|
||||||
next_block[1] = ptr;
|
next_block[1] = ptr;
|
||||||
return true;
|
return true;
|
||||||
|
@ -103,7 +103,8 @@ mp_int_t mp_arg_validate_int_max(mp_int_t i, mp_int_t j, qstr arg_name);
|
|||||||
mp_int_t mp_arg_validate_int_range(mp_int_t i, mp_int_t min, mp_int_t max, qstr arg_name);
|
mp_int_t mp_arg_validate_int_range(mp_int_t i, mp_int_t min, mp_int_t max, qstr arg_name);
|
||||||
#if MICROPY_PY_BUILTINS_FLOAT
|
#if MICROPY_PY_BUILTINS_FLOAT
|
||||||
mp_float_t mp_arg_validate_obj_float_non_negative(mp_obj_t float_in, mp_float_t default_for_null, qstr arg_name);
|
mp_float_t mp_arg_validate_obj_float_non_negative(mp_obj_t float_in, mp_float_t default_for_null, qstr arg_name);
|
||||||
void mp_arg_validate_obj_float_range(mp_obj_t float_in, mp_int_t min, mp_int_t max, qstr arg_name);
|
mp_float_t mp_arg_validate_obj_float_range(mp_obj_t float_in, mp_int_t min, mp_int_t max, qstr arg_name);
|
||||||
|
mp_float_t mp_arg_validate_float_range(mp_float_t float_in, mp_int_t min, mp_int_t max, qstr arg_name);
|
||||||
mp_float_t mp_arg_validate_type_float(mp_obj_t obj, qstr arg_name);
|
mp_float_t mp_arg_validate_type_float(mp_obj_t obj, qstr arg_name);
|
||||||
#endif
|
#endif
|
||||||
mp_uint_t mp_arg_validate_length_min(mp_uint_t length, mp_uint_t min, qstr arg_name);
|
mp_uint_t mp_arg_validate_length_min(mp_uint_t length, mp_uint_t min, qstr arg_name);
|
||||||
|
@ -3,6 +3,7 @@ mypy
|
|||||||
black
|
black
|
||||||
isort
|
isort
|
||||||
twine
|
twine
|
||||||
|
build
|
||||||
wheel
|
wheel
|
||||||
astroid
|
astroid
|
||||||
setuptools>=45
|
setuptools>=45
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
//| def __init__(
|
//| def __init__(
|
||||||
//| self,
|
//| self,
|
||||||
//| spi: busio.SPI,
|
//| spi: busio.SPI,
|
||||||
//| chip_select: digitalio.DigitalInOut,
|
//| chip_select: Optional[digitalio.DigitalInOut] = None,
|
||||||
//| *,
|
//| *,
|
||||||
//| baudrate: int = 100000,
|
//| baudrate: int = 100000,
|
||||||
//| polarity: int = 0,
|
//| polarity: int = 0,
|
||||||
@ -55,7 +55,7 @@
|
|||||||
//| Represents a single SPI device and manages locking the bus and the device address.
|
//| Represents a single SPI device and manages locking the bus and the device address.
|
||||||
//|
|
//|
|
||||||
//| :param ~busio.SPI spi: The SPI bus the device is on
|
//| :param ~busio.SPI spi: The SPI bus the device is on
|
||||||
//| :param ~digitalio.DigitalInOut chip_select: The chip select pin object that implements the DigitalInOut API.
|
//| :param ~digitalio.DigitalInOut chip_select: The chip select pin object that implements the DigitalInOut API. ``None`` if a chip select pin is not being used.
|
||||||
//| :param bool cs_active_value: Set to true if your device requires CS to be active high. Defaults to false.
|
//| :param bool cs_active_value: Set to true if your device requires CS to be active high. Defaults to false.
|
||||||
//| :param int extra_clocks: The minimum number of clock cycles to cycle the bus after CS is high. (Used for SD cards.)
|
//| :param int extra_clocks: The minimum number of clock cycles to cycle the bus after CS is high. (Used for SD cards.)
|
||||||
//|
|
//|
|
||||||
@ -84,7 +84,7 @@ STATIC mp_obj_t adafruit_bus_device_spidevice_make_new(const mp_obj_type_t *type
|
|||||||
enum { ARG_spi, ARG_chip_select, ARG_cs_active_value, ARG_baudrate, ARG_polarity, ARG_phase, ARG_extra_clocks };
|
enum { ARG_spi, ARG_chip_select, ARG_cs_active_value, ARG_baudrate, ARG_polarity, ARG_phase, ARG_extra_clocks };
|
||||||
static const mp_arg_t allowed_args[] = {
|
static const mp_arg_t allowed_args[] = {
|
||||||
{ MP_QSTR_spi, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
{ MP_QSTR_spi, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||||
{ MP_QSTR_chip_select, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
{ MP_QSTR_chip_select, MP_ARG_OBJ, {.u_obj = mp_const_none} },
|
||||||
{ MP_QSTR_cs_active_value, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
|
{ MP_QSTR_cs_active_value, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
|
||||||
{ MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100000} },
|
{ MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100000} },
|
||||||
{ MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
{ MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
@ -96,12 +96,12 @@ STATIC mp_obj_t adafruit_bus_device_spidevice_make_new(const mp_obj_type_t *type
|
|||||||
|
|
||||||
busio_spi_obj_t *spi = args[ARG_spi].u_obj;
|
busio_spi_obj_t *spi = args[ARG_spi].u_obj;
|
||||||
|
|
||||||
mp_arg_validate_type(args[ARG_chip_select].u_obj, &digitalio_digitalinout_type, MP_QSTR_chip_select);
|
mp_arg_validate_type_or_none(args[ARG_chip_select].u_obj, &digitalio_digitalinout_type, MP_QSTR_chip_select);
|
||||||
|
|
||||||
common_hal_adafruit_bus_device_spidevice_construct(MP_OBJ_TO_PTR(self), spi, args[ARG_chip_select].u_obj, args[ARG_cs_active_value].u_bool, args[ARG_baudrate].u_int, args[ARG_polarity].u_int,
|
common_hal_adafruit_bus_device_spidevice_construct(MP_OBJ_TO_PTR(self), spi, args[ARG_chip_select].u_obj, args[ARG_cs_active_value].u_bool, args[ARG_baudrate].u_int, args[ARG_polarity].u_int,
|
||||||
args[ARG_phase].u_int, args[ARG_extra_clocks].u_int);
|
args[ARG_phase].u_int, args[ARG_extra_clocks].u_int);
|
||||||
|
|
||||||
if (args[ARG_chip_select].u_obj != MP_OBJ_NULL) {
|
if (args[ARG_chip_select].u_obj != mp_const_none) {
|
||||||
digitalinout_result_t result = common_hal_digitalio_digitalinout_switch_to_output(MP_OBJ_TO_PTR(args[ARG_chip_select].u_obj),
|
digitalinout_result_t result = common_hal_digitalio_digitalinout_switch_to_output(MP_OBJ_TO_PTR(args[ARG_chip_select].u_obj),
|
||||||
true, DRIVE_MODE_PUSH_PULL);
|
true, DRIVE_MODE_PUSH_PULL);
|
||||||
#if CIRCUITPY_DIGITALIO_HAVE_INPUT_ONLY
|
#if CIRCUITPY_DIGITALIO_HAVE_INPUT_ONLY
|
||||||
|
@ -249,6 +249,15 @@ STATIC mp_obj_t displayio_group_obj_remove(mp_obj_t self_in, mp_obj_t layer) {
|
|||||||
MP_DEFINE_CONST_FUN_OBJ_2(displayio_group_remove_obj, displayio_group_obj_remove);
|
MP_DEFINE_CONST_FUN_OBJ_2(displayio_group_remove_obj, displayio_group_obj_remove);
|
||||||
|
|
||||||
//| def __bool__(self) -> bool: ...
|
//| def __bool__(self) -> bool: ...
|
||||||
|
//| def __contains__(
|
||||||
|
//| self,
|
||||||
|
//| item: Union[vectorio.Circle, vectorio.Rectangle, vectorio.Polygon, Group, TileGrid],
|
||||||
|
//| ) -> bool: ...
|
||||||
|
//| def __iter__(
|
||||||
|
//| self,
|
||||||
|
//| ) -> Iterator[
|
||||||
|
//| Union[vectorio.Circle, vectorio.Rectangle, vectorio.Polygon, Group, TileGrid]
|
||||||
|
//| ]: ...
|
||||||
//| def __len__(self) -> int:
|
//| def __len__(self) -> int:
|
||||||
//| """Returns the number of layers in a Group"""
|
//| """Returns the number of layers in a Group"""
|
||||||
//| ...
|
//| ...
|
||||||
|
@ -88,9 +88,6 @@ STATIC mp_obj_t synthio_miditrack_make_new(const mp_obj_type_t *type, size_t n_a
|
|||||||
mp_buffer_info_t bufinfo;
|
mp_buffer_info_t bufinfo;
|
||||||
mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ);
|
mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ);
|
||||||
|
|
||||||
mp_buffer_info_t bufinfo_waveform;
|
|
||||||
synthio_synth_parse_waveform(&bufinfo_waveform, args[ARG_waveform].u_obj);
|
|
||||||
|
|
||||||
synthio_miditrack_obj_t *self = m_new_obj(synthio_miditrack_obj_t);
|
synthio_miditrack_obj_t *self = m_new_obj(synthio_miditrack_obj_t);
|
||||||
self->base.type = &synthio_miditrack_type;
|
self->base.type = &synthio_miditrack_type;
|
||||||
|
|
||||||
@ -98,8 +95,8 @@ STATIC mp_obj_t synthio_miditrack_make_new(const mp_obj_type_t *type, size_t n_a
|
|||||||
(uint8_t *)bufinfo.buf, bufinfo.len,
|
(uint8_t *)bufinfo.buf, bufinfo.len,
|
||||||
args[ARG_tempo].u_int,
|
args[ARG_tempo].u_int,
|
||||||
args[ARG_sample_rate].u_int,
|
args[ARG_sample_rate].u_int,
|
||||||
bufinfo_waveform.buf,
|
args[ARG_waveform].u_obj,
|
||||||
bufinfo_waveform.len / 2,
|
mp_const_none,
|
||||||
args[ARG_envelope].u_obj
|
args[ARG_envelope].u_obj
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -27,12 +27,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "shared-module/synthio/MidiTrack.h"
|
#include "shared-module/synthio/MidiTrack.h"
|
||||||
|
#include "py/obj.h"
|
||||||
|
|
||||||
extern const mp_obj_type_t synthio_miditrack_type;
|
extern const mp_obj_type_t synthio_miditrack_type;
|
||||||
|
|
||||||
void common_hal_synthio_miditrack_construct(synthio_miditrack_obj_t *self,
|
void common_hal_synthio_miditrack_construct(synthio_miditrack_obj_t *self, const uint8_t *buffer, uint32_t len, uint32_t tempo, uint32_t sample_rate, mp_obj_t waveform_obj, mp_obj_t filter_obj, mp_obj_t envelope_obj);
|
||||||
const uint8_t *buffer, uint32_t len, uint32_t tempo, uint32_t sample_rate, const int16_t *waveform, uint16_t waveform_len,
|
|
||||||
mp_obj_t envelope);
|
|
||||||
|
|
||||||
void common_hal_synthio_miditrack_deinit(synthio_miditrack_obj_t *self);
|
void common_hal_synthio_miditrack_deinit(synthio_miditrack_obj_t *self);
|
||||||
bool common_hal_synthio_miditrack_deinited(synthio_miditrack_obj_t *self);
|
bool common_hal_synthio_miditrack_deinited(synthio_miditrack_obj_t *self);
|
||||||
|
359
shared-bindings/synthio/Note.c
Normal file
359
shared-bindings/synthio/Note.c
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Micro Python project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 Artyom Skrobov
|
||||||
|
* Copyright (c) 2023 Jeff Epler for Adafruit Industries
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "py/objproperty.h"
|
||||||
|
#include "py/runtime.h"
|
||||||
|
#include "shared-bindings/util.h"
|
||||||
|
#include "shared-bindings/synthio/__init__.h"
|
||||||
|
#include "shared-bindings/synthio/Note.h"
|
||||||
|
#include "shared-module/synthio/Note.h"
|
||||||
|
|
||||||
|
static const mp_arg_t note_properties[] = {
|
||||||
|
{ MP_QSTR_frequency, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = NULL } },
|
||||||
|
{ MP_QSTR_panning, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(0) } },
|
||||||
|
{ MP_QSTR_tremolo_rate, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = NULL } },
|
||||||
|
{ MP_QSTR_tremolo_depth, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = NULL } },
|
||||||
|
{ MP_QSTR_bend_rate, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = NULL } },
|
||||||
|
{ MP_QSTR_bend_depth, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = NULL } },
|
||||||
|
{ MP_QSTR_bend_mode, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = (mp_obj_t)MP_ROM_PTR(&bend_mode_VIBRATO_obj) } },
|
||||||
|
{ MP_QSTR_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },
|
||||||
|
{ MP_QSTR_envelope, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },
|
||||||
|
{ MP_QSTR_filter, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(1) } },
|
||||||
|
{ MP_QSTR_ring_frequency, MP_ARG_OBJ, {.u_obj = NULL } },
|
||||||
|
{ MP_QSTR_ring_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },
|
||||||
|
};
|
||||||
|
//| class Note:
|
||||||
|
//| def __init__(
|
||||||
|
//| self,
|
||||||
|
//| *,
|
||||||
|
//| frequency: float,
|
||||||
|
//| panning: float = 0.0,
|
||||||
|
//| waveform: Optional[ReadableBuffer] = None,
|
||||||
|
//| envelope: Optional[Envelope] = None,
|
||||||
|
//| tremolo_depth: float = 0.0,
|
||||||
|
//| tremolo_rate: float = 0.0,
|
||||||
|
//| bend_depth: float = 0.0,
|
||||||
|
//| bend_rate: float = 0.0,
|
||||||
|
//| bend_mode: "BendMode" = BendMode.VIBRATO,
|
||||||
|
//| ) -> None:
|
||||||
|
//| """Construct a Note object, with a frequency in Hz, and optional panning, waveform, envelope, tremolo (volume change) and bend (frequency change).
|
||||||
|
//|
|
||||||
|
//| If waveform or envelope are `None` the synthesizer object's default waveform or envelope are used.
|
||||||
|
//|
|
||||||
|
//| If the same Note object is played on multiple Synthesizer objects, the result is undefined.
|
||||||
|
//| """
|
||||||
|
STATIC mp_obj_t synthio_note_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||||
|
mp_arg_val_t args[MP_ARRAY_SIZE(note_properties)];
|
||||||
|
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(note_properties), note_properties, args);
|
||||||
|
|
||||||
|
synthio_note_obj_t *self = m_new_obj(synthio_note_obj_t);
|
||||||
|
self->base.type = &synthio_note_type;
|
||||||
|
|
||||||
|
mp_obj_t result = MP_OBJ_FROM_PTR(self);
|
||||||
|
for (size_t i = 0; i < MP_ARRAY_SIZE(note_properties); i++) {
|
||||||
|
if (args[i].u_obj != NULL) {
|
||||||
|
mp_store_attr(result, note_properties[i].qst, args[i].u_obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
//| frequency: float
|
||||||
|
//| """The base frequency of the note, in Hz."""
|
||||||
|
STATIC mp_obj_t synthio_note_get_frequency(mp_obj_t self_in) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
return mp_obj_new_float(common_hal_synthio_note_get_frequency(self));
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_frequency_obj, synthio_note_get_frequency);
|
||||||
|
|
||||||
|
STATIC mp_obj_t synthio_note_set_frequency(mp_obj_t self_in, mp_obj_t arg) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
common_hal_synthio_note_set_frequency(self, mp_obj_get_float(arg));
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_frequency_obj, synthio_note_set_frequency);
|
||||||
|
MP_PROPERTY_GETSET(synthio_note_frequency_obj,
|
||||||
|
(mp_obj_t)&synthio_note_get_frequency_obj,
|
||||||
|
(mp_obj_t)&synthio_note_set_frequency_obj);
|
||||||
|
|
||||||
|
//| filter: bool
|
||||||
|
//| """True if the note should be processed via the synthesizer's FIR filter."""
|
||||||
|
STATIC mp_obj_t synthio_note_get_filter(mp_obj_t self_in) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
return mp_obj_new_bool(common_hal_synthio_note_get_filter(self));
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_filter_obj, synthio_note_get_filter);
|
||||||
|
|
||||||
|
STATIC mp_obj_t synthio_note_set_filter(mp_obj_t self_in, mp_obj_t arg) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
common_hal_synthio_note_set_filter(self, mp_obj_is_true(arg));
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_filter_obj, synthio_note_set_filter);
|
||||||
|
MP_PROPERTY_GETSET(synthio_note_filter_obj,
|
||||||
|
(mp_obj_t)&synthio_note_get_filter_obj,
|
||||||
|
(mp_obj_t)&synthio_note_set_filter_obj);
|
||||||
|
|
||||||
|
//| panning: float
|
||||||
|
//| """Defines the channel(s) in which the note appears.
|
||||||
|
//|
|
||||||
|
//| -1 is left channel only, 0 is both channels, and 1 is right channel.
|
||||||
|
//| For fractional values, the note plays at full amplitude in one channel
|
||||||
|
//| and partial amplitude in the other channel. For instance -.5 plays at full
|
||||||
|
//| amplitude in the left channel and 1/2 amplitude in the right channel."""
|
||||||
|
STATIC mp_obj_t synthio_note_get_panning(mp_obj_t self_in) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
return mp_obj_new_float(common_hal_synthio_note_get_panning(self));
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_panning_obj, synthio_note_get_panning);
|
||||||
|
|
||||||
|
STATIC mp_obj_t synthio_note_set_panning(mp_obj_t self_in, mp_obj_t arg) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
common_hal_synthio_note_set_panning(self, mp_obj_get_float(arg));
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_panning_obj, synthio_note_set_panning);
|
||||||
|
MP_PROPERTY_GETSET(synthio_note_panning_obj,
|
||||||
|
(mp_obj_t)&synthio_note_get_panning_obj,
|
||||||
|
(mp_obj_t)&synthio_note_set_panning_obj);
|
||||||
|
|
||||||
|
|
||||||
|
//| tremolo_depth: float
|
||||||
|
//| """The tremolo depth of the note, from 0 to 1
|
||||||
|
//|
|
||||||
|
//| A depth of 0 disables tremolo. A nonzero value enables tremolo,
|
||||||
|
//| with the maximum decrease in amplitude being equal to the tremolo
|
||||||
|
//| depth. A note with a tremolo depth of 1 will fade out to nothing, while
|
||||||
|
//| a tremolo depth of 0.1 will give a minimum amplitude of 0.9."""
|
||||||
|
STATIC mp_obj_t synthio_note_get_tremolo_depth(mp_obj_t self_in) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
return mp_obj_new_float(common_hal_synthio_note_get_tremolo_depth(self));
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_tremolo_depth_obj, synthio_note_get_tremolo_depth);
|
||||||
|
|
||||||
|
STATIC mp_obj_t synthio_note_set_tremolo_depth(mp_obj_t self_in, mp_obj_t arg) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
common_hal_synthio_note_set_tremolo_depth(self, mp_obj_get_float(arg));
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_tremolo_depth_obj, synthio_note_set_tremolo_depth);
|
||||||
|
MP_PROPERTY_GETSET(synthio_note_tremolo_depth_obj,
|
||||||
|
(mp_obj_t)&synthio_note_get_tremolo_depth_obj,
|
||||||
|
(mp_obj_t)&synthio_note_set_tremolo_depth_obj);
|
||||||
|
|
||||||
|
//| tremolo_rate: float
|
||||||
|
//| """The tremolo rate of the note, in Hz."""
|
||||||
|
STATIC mp_obj_t synthio_note_get_tremolo_rate(mp_obj_t self_in) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
return mp_obj_new_float(common_hal_synthio_note_get_tremolo_rate(self));
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_tremolo_rate_obj, synthio_note_get_tremolo_rate);
|
||||||
|
|
||||||
|
STATIC mp_obj_t synthio_note_set_tremolo_rate(mp_obj_t self_in, mp_obj_t arg) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
common_hal_synthio_note_set_tremolo_rate(self, mp_obj_get_float(arg));
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_tremolo_rate_obj, synthio_note_set_tremolo_rate);
|
||||||
|
MP_PROPERTY_GETSET(synthio_note_tremolo_rate_obj,
|
||||||
|
(mp_obj_t)&synthio_note_get_tremolo_rate_obj,
|
||||||
|
(mp_obj_t)&synthio_note_set_tremolo_rate_obj);
|
||||||
|
|
||||||
|
//|
|
||||||
|
//| bend_mode: BendMode
|
||||||
|
//| """The type of bend operation"""
|
||||||
|
STATIC mp_obj_t synthio_note_get_bend_mode(mp_obj_t self_in) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
return cp_enum_find(&synthio_bend_mode_type, common_hal_synthio_note_get_bend_mode(self));
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_bend_mode_obj, synthio_note_get_bend_mode);
|
||||||
|
|
||||||
|
STATIC mp_obj_t synthio_note_set_bend_mode(mp_obj_t self_in, mp_obj_t arg) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
common_hal_synthio_note_set_bend_mode(self, cp_enum_value(&synthio_bend_mode_type, arg, MP_QSTR_bend_mode));
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_bend_mode_obj, synthio_note_set_bend_mode);
|
||||||
|
MP_PROPERTY_GETSET(synthio_note_bend_mode_obj,
|
||||||
|
(mp_obj_t)&synthio_note_get_bend_mode_obj,
|
||||||
|
(mp_obj_t)&synthio_note_set_bend_mode_obj);
|
||||||
|
|
||||||
|
//
|
||||||
|
//|
|
||||||
|
//| bend_depth: float
|
||||||
|
//| """The bend depth of the note, from -1 to +1
|
||||||
|
//|
|
||||||
|
//| A depth of 0 disables bend. A depth of 1 corresponds to a bend of 1
|
||||||
|
//| octave. A depth of (1/12) = 0.833 corresponds to a bend of 1 semitone,
|
||||||
|
//| and a depth of .00833 corresponds to one musical cent.
|
||||||
|
//| """
|
||||||
|
STATIC mp_obj_t synthio_note_get_bend_depth(mp_obj_t self_in) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
return mp_obj_new_float(common_hal_synthio_note_get_bend_depth(self));
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_bend_depth_obj, synthio_note_get_bend_depth);
|
||||||
|
|
||||||
|
STATIC mp_obj_t synthio_note_set_bend_depth(mp_obj_t self_in, mp_obj_t arg) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
common_hal_synthio_note_set_bend_depth(self, mp_obj_get_float(arg));
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_bend_depth_obj, synthio_note_set_bend_depth);
|
||||||
|
MP_PROPERTY_GETSET(synthio_note_bend_depth_obj,
|
||||||
|
(mp_obj_t)&synthio_note_get_bend_depth_obj,
|
||||||
|
(mp_obj_t)&synthio_note_set_bend_depth_obj);
|
||||||
|
|
||||||
|
//| bend_rate: float
|
||||||
|
//| """The bend rate of the note, in Hz."""
|
||||||
|
STATIC mp_obj_t synthio_note_get_bend_rate(mp_obj_t self_in) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
return mp_obj_new_float(common_hal_synthio_note_get_bend_rate(self));
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_bend_rate_obj, synthio_note_get_bend_rate);
|
||||||
|
|
||||||
|
STATIC mp_obj_t synthio_note_set_bend_rate(mp_obj_t self_in, mp_obj_t arg) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
common_hal_synthio_note_set_bend_rate(self, mp_obj_get_float(arg));
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_bend_rate_obj, synthio_note_set_bend_rate);
|
||||||
|
MP_PROPERTY_GETSET(synthio_note_bend_rate_obj,
|
||||||
|
(mp_obj_t)&synthio_note_get_bend_rate_obj,
|
||||||
|
(mp_obj_t)&synthio_note_set_bend_rate_obj);
|
||||||
|
|
||||||
|
//| waveform: Optional[ReadableBuffer]
|
||||||
|
//| """The waveform of this note. Setting the waveform to a buffer of a different size resets the note's phase."""
|
||||||
|
STATIC mp_obj_t synthio_note_get_waveform(mp_obj_t self_in) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
return common_hal_synthio_note_get_waveform_obj(self);
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_waveform_obj, synthio_note_get_waveform);
|
||||||
|
|
||||||
|
STATIC mp_obj_t synthio_note_set_waveform(mp_obj_t self_in, mp_obj_t arg) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
common_hal_synthio_note_set_waveform(self, arg);
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_waveform_obj, synthio_note_set_waveform);
|
||||||
|
MP_PROPERTY_GETSET(synthio_note_waveform_obj,
|
||||||
|
(mp_obj_t)&synthio_note_get_waveform_obj,
|
||||||
|
(mp_obj_t)&synthio_note_set_waveform_obj);
|
||||||
|
|
||||||
|
|
||||||
|
//| envelope: Envelope
|
||||||
|
//| """The envelope of this note"""
|
||||||
|
//|
|
||||||
|
STATIC mp_obj_t synthio_note_get_envelope(mp_obj_t self_in) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
return common_hal_synthio_note_get_envelope_obj(self);
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_envelope_obj, synthio_note_get_envelope);
|
||||||
|
|
||||||
|
STATIC mp_obj_t synthio_note_set_envelope(mp_obj_t self_in, mp_obj_t arg) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
common_hal_synthio_note_set_envelope(self, arg);
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_envelope_obj, synthio_note_set_envelope);
|
||||||
|
MP_PROPERTY_GETSET(synthio_note_envelope_obj,
|
||||||
|
(mp_obj_t)&synthio_note_get_envelope_obj,
|
||||||
|
(mp_obj_t)&synthio_note_set_envelope_obj);
|
||||||
|
|
||||||
|
//| ring_frequency: float
|
||||||
|
//| """The ring frequency of the note, in Hz. Zero disables.
|
||||||
|
//|
|
||||||
|
//| For ring to take effect, both ``ring_frequency`` and ``ring_waveform`` must be set."""
|
||||||
|
STATIC mp_obj_t synthio_note_get_ring_frequency(mp_obj_t self_in) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
return mp_obj_new_float(common_hal_synthio_note_get_ring_frequency(self));
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_ring_frequency_obj, synthio_note_get_ring_frequency);
|
||||||
|
|
||||||
|
STATIC mp_obj_t synthio_note_set_ring_frequency(mp_obj_t self_in, mp_obj_t arg) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
common_hal_synthio_note_set_ring_frequency(self, mp_obj_get_float(arg));
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_ring_frequency_obj, synthio_note_set_ring_frequency);
|
||||||
|
MP_PROPERTY_GETSET(synthio_note_ring_frequency_obj,
|
||||||
|
(mp_obj_t)&synthio_note_get_ring_frequency_obj,
|
||||||
|
(mp_obj_t)&synthio_note_set_ring_frequency_obj);
|
||||||
|
|
||||||
|
//| ring_waveform: Optional[ReadableBuffer]
|
||||||
|
//| """The ring waveform of this note. Setting the ring_waveform to a buffer of a different size resets the note's phase.
|
||||||
|
//|
|
||||||
|
//| For ring to take effect, both ``ring_frequency`` and ``ring_waveform`` must be set."""
|
||||||
|
//|
|
||||||
|
STATIC mp_obj_t synthio_note_get_ring_waveform(mp_obj_t self_in) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
return common_hal_synthio_note_get_ring_waveform_obj(self);
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_ring_waveform_obj, synthio_note_get_ring_waveform);
|
||||||
|
|
||||||
|
STATIC mp_obj_t synthio_note_set_ring_waveform(mp_obj_t self_in, mp_obj_t arg) {
|
||||||
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
common_hal_synthio_note_set_ring_waveform(self, arg);
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_ring_waveform_obj, synthio_note_set_ring_waveform);
|
||||||
|
MP_PROPERTY_GETSET(synthio_note_ring_waveform_obj,
|
||||||
|
(mp_obj_t)&synthio_note_get_ring_waveform_obj,
|
||||||
|
(mp_obj_t)&synthio_note_set_ring_waveform_obj);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void note_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||||
|
(void)kind;
|
||||||
|
properties_print_helper(print, self_in, note_properties, MP_ARRAY_SIZE(note_properties));
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC const mp_rom_map_elem_t synthio_note_locals_dict_table[] = {
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&synthio_note_frequency_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_filter), MP_ROM_PTR(&synthio_note_filter_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_panning), MP_ROM_PTR(&synthio_note_panning_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_waveform), MP_ROM_PTR(&synthio_note_waveform_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_envelope), MP_ROM_PTR(&synthio_note_envelope_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_tremolo_depth), MP_ROM_PTR(&synthio_note_tremolo_depth_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_tremolo_rate), MP_ROM_PTR(&synthio_note_tremolo_rate_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_bend_depth), MP_ROM_PTR(&synthio_note_bend_depth_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_bend_rate), MP_ROM_PTR(&synthio_note_bend_rate_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_bend_mode), MP_ROM_PTR(&synthio_note_bend_mode_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_ring_frequency), MP_ROM_PTR(&synthio_note_ring_frequency_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_ring_waveform), MP_ROM_PTR(&synthio_note_ring_waveform_obj) },
|
||||||
|
};
|
||||||
|
STATIC MP_DEFINE_CONST_DICT(synthio_note_locals_dict, synthio_note_locals_dict_table);
|
||||||
|
|
||||||
|
const mp_obj_type_t synthio_note_type = {
|
||||||
|
{ &mp_type_type },
|
||||||
|
.name = MP_QSTR_Note,
|
||||||
|
.make_new = synthio_note_make_new,
|
||||||
|
.locals_dict = (mp_obj_dict_t *)&synthio_note_locals_dict,
|
||||||
|
.print = note_print,
|
||||||
|
};
|
43
shared-bindings/synthio/Note.h
Normal file
43
shared-bindings/synthio/Note.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "py/obj.h"
|
||||||
|
|
||||||
|
typedef struct synthio_note_obj synthio_note_obj_t;
|
||||||
|
extern const mp_obj_type_t synthio_note_type;
|
||||||
|
typedef enum synthio_bend_mode_e synthio_bend_mode_t;
|
||||||
|
|
||||||
|
mp_float_t common_hal_synthio_note_get_frequency(synthio_note_obj_t *self);
|
||||||
|
void common_hal_synthio_note_set_frequency(synthio_note_obj_t *self, mp_float_t value);
|
||||||
|
|
||||||
|
bool common_hal_synthio_note_get_filter(synthio_note_obj_t *self);
|
||||||
|
void common_hal_synthio_note_set_filter(synthio_note_obj_t *self, bool value);
|
||||||
|
|
||||||
|
mp_float_t common_hal_synthio_note_get_ring_frequency(synthio_note_obj_t *self);
|
||||||
|
void common_hal_synthio_note_set_ring_frequency(synthio_note_obj_t *self, mp_float_t value);
|
||||||
|
|
||||||
|
mp_float_t common_hal_synthio_note_get_panning(synthio_note_obj_t *self);
|
||||||
|
void common_hal_synthio_note_set_panning(synthio_note_obj_t *self, mp_float_t value);
|
||||||
|
|
||||||
|
mp_float_t common_hal_synthio_note_get_tremolo_rate(synthio_note_obj_t *self);
|
||||||
|
void common_hal_synthio_note_set_tremolo_rate(synthio_note_obj_t *self, mp_float_t value);
|
||||||
|
|
||||||
|
mp_float_t common_hal_synthio_note_get_tremolo_depth(synthio_note_obj_t *self);
|
||||||
|
void common_hal_synthio_note_set_tremolo_depth(synthio_note_obj_t *self, mp_float_t value);
|
||||||
|
|
||||||
|
synthio_bend_mode_t common_hal_synthio_note_get_bend_mode(synthio_note_obj_t *self);
|
||||||
|
void common_hal_synthio_note_set_bend_mode(synthio_note_obj_t *self, synthio_bend_mode_t value);
|
||||||
|
|
||||||
|
mp_float_t common_hal_synthio_note_get_bend_rate(synthio_note_obj_t *self);
|
||||||
|
void common_hal_synthio_note_set_bend_rate(synthio_note_obj_t *self, mp_float_t value);
|
||||||
|
|
||||||
|
mp_float_t common_hal_synthio_note_get_bend_depth(synthio_note_obj_t *self);
|
||||||
|
void common_hal_synthio_note_set_bend_depth(synthio_note_obj_t *self, mp_float_t value);
|
||||||
|
|
||||||
|
mp_obj_t common_hal_synthio_note_get_waveform_obj(synthio_note_obj_t *self);
|
||||||
|
void common_hal_synthio_note_set_waveform(synthio_note_obj_t *self, mp_obj_t value);
|
||||||
|
|
||||||
|
mp_obj_t common_hal_synthio_note_get_ring_waveform_obj(synthio_note_obj_t *self);
|
||||||
|
void common_hal_synthio_note_set_ring_waveform(synthio_note_obj_t *self, mp_obj_t value);
|
||||||
|
|
||||||
|
mp_obj_t common_hal_synthio_note_get_envelope_obj(synthio_note_obj_t *self);
|
||||||
|
void common_hal_synthio_note_set_envelope(synthio_note_obj_t *self, mp_obj_t value);
|
@ -36,11 +36,15 @@
|
|||||||
#include "shared-bindings/synthio/__init__.h"
|
#include "shared-bindings/synthio/__init__.h"
|
||||||
#include "supervisor/shared/translate/translate.h"
|
#include "supervisor/shared/translate/translate.h"
|
||||||
|
|
||||||
|
//| NoteSequence = Sequence[Union[int, Note]]
|
||||||
|
//| """A sequence of notes, which can each be integer MIDI notes or `Note` objects"""
|
||||||
|
//|
|
||||||
//| class Synthesizer:
|
//| class Synthesizer:
|
||||||
//| def __init__(
|
//| def __init__(
|
||||||
//| self,
|
//| self,
|
||||||
//| *,
|
//| *,
|
||||||
//| sample_rate: int = 11025,
|
//| sample_rate: int = 11025,
|
||||||
|
//| channel_count: int = 1,
|
||||||
//| waveform: Optional[ReadableBuffer] = None,
|
//| waveform: Optional[ReadableBuffer] = None,
|
||||||
//| envelope: Optional[Envelope] = None,
|
//| envelope: Optional[Envelope] = None,
|
||||||
//| ) -> None:
|
//| ) -> None:
|
||||||
@ -48,35 +52,38 @@
|
|||||||
//|
|
//|
|
||||||
//| This API is experimental.
|
//| This API is experimental.
|
||||||
//|
|
//|
|
||||||
//| Notes use MIDI note numbering, with 60 being C4 or Middle C, approximately 262Hz.
|
//| Integer notes use MIDI note numbering, with 60 being C4 or Middle C,
|
||||||
|
//| approximately 262Hz. Integer notes use the given waveform & envelope,
|
||||||
|
//| and do not support advanced features like tremolo or vibrato.
|
||||||
//|
|
//|
|
||||||
//| :param int sample_rate: The desired playback sample rate; higher sample rate requires more memory
|
//| :param int sample_rate: The desired playback sample rate; higher sample rate requires more memory
|
||||||
|
//| :param int channel_count: The number of output channels (1=mono, 2=stereo)
|
||||||
//| :param ReadableBuffer waveform: A single-cycle waveform. Default is a 50% duty cycle square wave. If specified, must be a ReadableBuffer of type 'h' (signed 16 bit)
|
//| :param ReadableBuffer waveform: A single-cycle waveform. Default is a 50% duty cycle square wave. If specified, must be a ReadableBuffer of type 'h' (signed 16 bit)
|
||||||
|
//| :param ReadableBuffer filter: Coefficients of an FIR filter to apply to notes with ``filter=True``. If specified, must be a ReadableBuffer of type 'h' (signed 16 bit)
|
||||||
//| :param Optional[Envelope] envelope: An object that defines the loudness of a note over time. The default envelope, `None` provides no ramping, voices turn instantly on and off.
|
//| :param Optional[Envelope] envelope: An object that defines the loudness of a note over time. The default envelope, `None` provides no ramping, voices turn instantly on and off.
|
||||||
//| """
|
//| """
|
||||||
STATIC mp_obj_t synthio_synthesizer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
STATIC mp_obj_t synthio_synthesizer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||||
enum { ARG_sample_rate, ARG_waveform, ARG_envelope };
|
enum { ARG_sample_rate, ARG_channel_count, ARG_waveform, ARG_envelope, ARG_filter };
|
||||||
static const mp_arg_t allowed_args[] = {
|
static const mp_arg_t allowed_args[] = {
|
||||||
{ MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 11025} },
|
{ MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 11025} },
|
||||||
|
{ MP_QSTR_channel_count, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} },
|
||||||
{ MP_QSTR_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none } },
|
{ MP_QSTR_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none } },
|
||||||
{ MP_QSTR_envelope, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none } },
|
{ MP_QSTR_envelope, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none } },
|
||||||
|
{ MP_QSTR_filter, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none } },
|
||||||
};
|
};
|
||||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
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_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||||
|
|
||||||
mp_buffer_info_t bufinfo_waveform;
|
|
||||||
synthio_synth_parse_waveform(&bufinfo_waveform, args[ARG_waveform].u_obj);
|
|
||||||
|
|
||||||
synthio_synthesizer_obj_t *self = m_new_obj(synthio_synthesizer_obj_t);
|
synthio_synthesizer_obj_t *self = m_new_obj(synthio_synthesizer_obj_t);
|
||||||
self->base.type = &synthio_synthesizer_type;
|
self->base.type = &synthio_synthesizer_type;
|
||||||
|
|
||||||
common_hal_synthio_synthesizer_construct(self,
|
common_hal_synthio_synthesizer_construct(self,
|
||||||
args[ARG_sample_rate].u_int,
|
args[ARG_sample_rate].u_int,
|
||||||
bufinfo_waveform.buf,
|
args[ARG_channel_count].u_int,
|
||||||
bufinfo_waveform.len / 2,
|
args[ARG_waveform].u_obj,
|
||||||
|
args[ARG_filter].u_obj,
|
||||||
args[ARG_envelope].u_obj);
|
args[ARG_envelope].u_obj);
|
||||||
|
|
||||||
|
|
||||||
return MP_OBJ_FROM_PTR(self);
|
return MP_OBJ_FROM_PTR(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,12 +93,12 @@ STATIC void check_for_deinit(synthio_synthesizer_obj_t *self) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//| def press(self, /, press: Sequence[int] = ()) -> None:
|
//| def press(self, /, press: NoteSequence = ()) -> None:
|
||||||
//| """Turn some notes on. Notes use MIDI numbering, with 60 being middle C, approximately 262Hz.
|
//| """Turn some notes on.
|
||||||
//|
|
//|
|
||||||
//| Pressing a note that was already pressed has no effect.
|
//| Pressing a note that was already pressed has no effect.
|
||||||
//|
|
//|
|
||||||
//| :param Sequence[int] press: Any sequence of integer notes."""
|
//| :param NoteSequence press: Any sequence of notes."""
|
||||||
STATIC mp_obj_t synthio_synthesizer_press(mp_obj_t self_in, mp_obj_t press) {
|
STATIC mp_obj_t synthio_synthesizer_press(mp_obj_t self_in, mp_obj_t press) {
|
||||||
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
check_for_deinit(self);
|
check_for_deinit(self);
|
||||||
@ -99,12 +106,12 @@ STATIC mp_obj_t synthio_synthesizer_press(mp_obj_t self_in, mp_obj_t press) {
|
|||||||
return mp_const_none;
|
return mp_const_none;
|
||||||
}
|
}
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(synthio_synthesizer_press_obj, synthio_synthesizer_press);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_2(synthio_synthesizer_press_obj, synthio_synthesizer_press);
|
||||||
//| def release(self, /, release: Sequence[int] = ()) -> None:
|
//| def release(self, /, release: NoteSequence = ()) -> None:
|
||||||
//| """Turn some notes off. Notes use MIDI numbering, with 60 being middle C, approximately 262Hz.
|
//| """Turn some notes off.
|
||||||
//|
|
//|
|
||||||
//| Releasing a note that was already released has no effect.
|
//| Releasing a note that was already released has no effect.
|
||||||
//|
|
//|
|
||||||
//| :param Sequence[int] release: Any sequence of integer notes."""
|
//| :param NoteSequence release: Any sequence of notes."""
|
||||||
STATIC mp_obj_t synthio_synthesizer_release(mp_obj_t self_in, mp_obj_t release) {
|
STATIC mp_obj_t synthio_synthesizer_release(mp_obj_t self_in, mp_obj_t release) {
|
||||||
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
check_for_deinit(self);
|
check_for_deinit(self);
|
||||||
@ -113,10 +120,8 @@ STATIC mp_obj_t synthio_synthesizer_release(mp_obj_t self_in, mp_obj_t release)
|
|||||||
}
|
}
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(synthio_synthesizer_release_obj, synthio_synthesizer_release);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_2(synthio_synthesizer_release_obj, synthio_synthesizer_release);
|
||||||
|
|
||||||
//| def release_then_press(
|
//| def release_then_press(self, release: NoteSequence = (), press: NoteSequence = ()) -> None:
|
||||||
//| self, release: Sequence[int] = (), press: Sequence[int] = ()
|
//| """Turn some notes on and/or off.
|
||||||
//| ) -> None:
|
|
||||||
//| """Turn some notes on and/or off. Notes use MIDI numbering, with 60 being middle C.
|
|
||||||
//|
|
//|
|
||||||
//| It is OK to release note that was not actually turned on.
|
//| It is OK to release note that was not actually turned on.
|
||||||
//|
|
//|
|
||||||
@ -125,8 +130,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(synthio_synthesizer_release_obj, synthio_synthe
|
|||||||
//| Releasing and pressing the note again has little effect, but does reset the phase
|
//| Releasing and pressing the note again has little effect, but does reset the phase
|
||||||
//| of the note, which may be perceptible as a small glitch.
|
//| of the note, which may be perceptible as a small glitch.
|
||||||
//|
|
//|
|
||||||
//| :param Sequence[int] release: Any sequence of integer notes.
|
//| :param NoteSequence release: Any sequence of notes.
|
||||||
//| :param Sequence[int] press: Any sequence of integer notes."""
|
//| :param NoteSequence press: Any sequence of notes."""
|
||||||
STATIC mp_obj_t synthio_synthesizer_release_then_press(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
STATIC mp_obj_t synthio_synthesizer_release_then_press(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||||
enum { ARG_release, ARG_press };
|
enum { ARG_release, ARG_press };
|
||||||
static const mp_arg_t allowed_args[] = {
|
static const mp_arg_t allowed_args[] = {
|
||||||
@ -146,13 +151,13 @@ STATIC mp_obj_t synthio_synthesizer_release_then_press(mp_uint_t n_args, const m
|
|||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(synthio_synthesizer_release_then_press_obj, 1, synthio_synthesizer_release_then_press);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(synthio_synthesizer_release_then_press_obj, 1, synthio_synthesizer_release_then_press);
|
||||||
|
|
||||||
//
|
//
|
||||||
//| def release_all_then_press(self, /, press: Sequence[int]) -> None:
|
//| def release_all_then_press(self, /, press: NoteSequence) -> None:
|
||||||
//| """Turn any currently-playing notes off, then turn on the given notes
|
//| """Turn any currently-playing notes off, then turn on the given notes
|
||||||
//|
|
//|
|
||||||
//| Releasing and pressing the note again has little effect, but does reset the phase
|
//| Releasing and pressing the note again has little effect, but does reset the phase
|
||||||
//| of the note, which may be perceptible as a small glitch.
|
//| of the note, which may be perceptible as a small glitch.
|
||||||
//|
|
//|
|
||||||
//| :param Sequence[int] press: Any sequence of integer notes."""
|
//| :param NoteSequence press: Any sequence of notes."""
|
||||||
STATIC mp_obj_t synthio_synthesizer_release_all_then_press(mp_obj_t self_in, mp_obj_t press) {
|
STATIC mp_obj_t synthio_synthesizer_release_all_then_press(mp_obj_t self_in, mp_obj_t press) {
|
||||||
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
check_for_deinit(self);
|
check_for_deinit(self);
|
||||||
@ -183,7 +188,7 @@ STATIC mp_obj_t synthio_synthesizer_deinit(mp_obj_t self_in) {
|
|||||||
}
|
}
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(synthio_synthesizer_deinit_obj, synthio_synthesizer_deinit);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(synthio_synthesizer_deinit_obj, synthio_synthesizer_deinit);
|
||||||
|
|
||||||
//| def __enter__(self) -> MidiTrack:
|
//| def __enter__(self) -> Synthesizer:
|
||||||
//| """No-op used by Context Managers."""
|
//| """No-op used by Context Managers."""
|
||||||
//| ...
|
//| ...
|
||||||
// Provided by context manager helper.
|
// Provided by context manager helper.
|
||||||
@ -232,7 +237,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(synthio_synthesizer_get_sample_rate_obj, synthio_synth
|
|||||||
MP_PROPERTY_GETTER(synthio_synthesizer_sample_rate_obj,
|
MP_PROPERTY_GETTER(synthio_synthesizer_sample_rate_obj,
|
||||||
(mp_obj_t)&synthio_synthesizer_get_sample_rate_obj);
|
(mp_obj_t)&synthio_synthesizer_get_sample_rate_obj);
|
||||||
|
|
||||||
//| pressed: Tuple[int]
|
//| pressed: NoteSequence
|
||||||
//| """A sequence of the currently pressed notes (read-only property)"""
|
//| """A sequence of the currently pressed notes (read-only property)"""
|
||||||
//|
|
//|
|
||||||
STATIC mp_obj_t synthio_synthesizer_obj_get_pressed(mp_obj_t self_in) {
|
STATIC mp_obj_t synthio_synthesizer_obj_get_pressed(mp_obj_t self_in) {
|
||||||
|
@ -32,8 +32,8 @@
|
|||||||
extern const mp_obj_type_t synthio_synthesizer_type;
|
extern const mp_obj_type_t synthio_synthesizer_type;
|
||||||
|
|
||||||
void common_hal_synthio_synthesizer_construct(synthio_synthesizer_obj_t *self,
|
void common_hal_synthio_synthesizer_construct(synthio_synthesizer_obj_t *self,
|
||||||
uint32_t sample_rate, const int16_t *waveform, uint16_t waveform_length,
|
uint32_t sample_rate, int channel_count, mp_obj_t waveform_obj, mp_obj_t filter_obj,
|
||||||
mp_obj_t envelope);
|
mp_obj_t envelope_obj);
|
||||||
void common_hal_synthio_synthesizer_deinit(synthio_synthesizer_obj_t *self);
|
void common_hal_synthio_synthesizer_deinit(synthio_synthesizer_obj_t *self);
|
||||||
bool common_hal_synthio_synthesizer_deinited(synthio_synthesizer_obj_t *self);
|
bool common_hal_synthio_synthesizer_deinited(synthio_synthesizer_obj_t *self);
|
||||||
uint32_t common_hal_synthio_synthesizer_get_sample_rate(synthio_synthesizer_obj_t *self);
|
uint32_t common_hal_synthio_synthesizer_get_sample_rate(synthio_synthesizer_obj_t *self);
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "py/enum.h"
|
||||||
#include "py/mperrno.h"
|
#include "py/mperrno.h"
|
||||||
#include "py/obj.h"
|
#include "py/obj.h"
|
||||||
#include "py/objnamedtuple.h"
|
#include "py/objnamedtuple.h"
|
||||||
@ -35,6 +36,7 @@
|
|||||||
|
|
||||||
#include "shared-bindings/synthio/__init__.h"
|
#include "shared-bindings/synthio/__init__.h"
|
||||||
#include "shared-bindings/synthio/MidiTrack.h"
|
#include "shared-bindings/synthio/MidiTrack.h"
|
||||||
|
#include "shared-bindings/synthio/Note.h"
|
||||||
#include "shared-bindings/synthio/Synthesizer.h"
|
#include "shared-bindings/synthio/Synthesizer.h"
|
||||||
|
|
||||||
#define default_attack_time (MICROPY_FLOAT_CONST(0.1))
|
#define default_attack_time (MICROPY_FLOAT_CONST(0.1))
|
||||||
@ -51,6 +53,7 @@ static const mp_arg_t envelope_properties[] = {
|
|||||||
{ MP_QSTR_sustain_level, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL } },
|
{ MP_QSTR_sustain_level, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL } },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//|
|
||||||
//| """Support for multi-channel audio synthesis
|
//| """Support for multi-channel audio synthesis
|
||||||
//|
|
//|
|
||||||
//| At least 2 simultaneous notes are supported. samd5x, mimxrt10xx and rp2040 platforms support up to 12 notes.
|
//| At least 2 simultaneous notes are supported. samd5x, mimxrt10xx and rp2040 platforms support up to 12 notes.
|
||||||
@ -77,9 +80,9 @@ static const mp_arg_t envelope_properties[] = {
|
|||||||
//|
|
//|
|
||||||
//| :param float attack_time: The time in seconds it takes to ramp from 0 volume to attack_volume
|
//| :param float attack_time: The time in seconds it takes to ramp from 0 volume to attack_volume
|
||||||
//| :param float decay_time: The time in seconds it takes to ramp from attack_volume to sustain_volume
|
//| :param float decay_time: The time in seconds it takes to ramp from attack_volume to sustain_volume
|
||||||
//| :param float release_time: The time in seconds it takes to ramp from sustain_volume to release_volume. When a note is released before it has reached the sustain phase, the release is done with the same slope indicated by ``release_time`` and ``sustain_level``
|
//| :param float release_time: The time in seconds it takes to ramp from sustain_volume to release_volume. When a note is released before it has reached the sustain phase, the release is done with the same slope indicated by ``release_time`` and ``sustain_level``. If the ``sustain_level`` is ``0.0`` then the release slope calculations use the ``attack_level`` instead.
|
||||||
//| :param float attack_level: The relative level, in the range ``0.0`` to ``1.0`` of the peak volume of the attack phase
|
//| :param float attack_level: The level, in the range ``0.0`` to ``1.0`` of the peak volume of the attack phase
|
||||||
//| :param float sustain_level: The relative level, in the range ``0.0`` to ``1.0`` of the volume of the sustain phase
|
//| :param float sustain_level: The level, in the range ``0.0`` to ``1.0`` of the volume of the sustain phase relative to the attack level
|
||||||
//| """
|
//| """
|
||||||
//| attack_time: float
|
//| attack_time: float
|
||||||
//| """The time in seconds it takes to ramp from 0 volume to attack_volume"""
|
//| """The time in seconds it takes to ramp from 0 volume to attack_volume"""
|
||||||
@ -91,10 +94,10 @@ static const mp_arg_t envelope_properties[] = {
|
|||||||
//| """The time in seconds it takes to ramp from sustain_volume to release_volume. When a note is released before it has reached the sustain phase, the release is done with the same slope indicated by ``release_time`` and ``sustain_level``"""
|
//| """The time in seconds it takes to ramp from sustain_volume to release_volume. When a note is released before it has reached the sustain phase, the release is done with the same slope indicated by ``release_time`` and ``sustain_level``"""
|
||||||
//|
|
//|
|
||||||
//| attack_level: float
|
//| attack_level: float
|
||||||
//| """The relative level, in the range ``0.0`` to ``1.0`` of the peak volume of the attack phase"""
|
//| """The level, in the range ``0.0`` to ``1.0`` of the peak volume of the attack phase"""
|
||||||
//|
|
//|
|
||||||
//| sustain_level: float
|
//| sustain_level: float
|
||||||
//| """The relative level, in the range ``0.0`` to ``1.0`` of the volume of the sustain phase"""
|
//| """The level, in the range ``0.0`` to ``1.0`` of the volume of the sustain phase relative to the attack level"""
|
||||||
//|
|
//|
|
||||||
|
|
||||||
STATIC mp_obj_t synthio_envelope_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
STATIC mp_obj_t synthio_envelope_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
|
||||||
@ -157,7 +160,6 @@ const mp_obj_namedtuple_type_t synthio_envelope_type_obj = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
//| def from_file(
|
//| def from_file(
|
||||||
//| file: typing.BinaryIO,
|
//| file: typing.BinaryIO,
|
||||||
//| *,
|
//| *,
|
||||||
@ -205,10 +207,6 @@ STATIC mp_obj_t synthio_from_file(size_t n_args, const mp_obj_t *pos_args, mp_ma
|
|||||||
}
|
}
|
||||||
pyb_file_obj_t *file = MP_OBJ_TO_PTR(args[ARG_file].u_obj);
|
pyb_file_obj_t *file = MP_OBJ_TO_PTR(args[ARG_file].u_obj);
|
||||||
|
|
||||||
|
|
||||||
mp_buffer_info_t bufinfo_waveform;
|
|
||||||
synthio_synth_parse_waveform(&bufinfo_waveform, args[ARG_waveform].u_obj);
|
|
||||||
|
|
||||||
uint8_t chunk_header[14];
|
uint8_t chunk_header[14];
|
||||||
f_rewind(&file->fp);
|
f_rewind(&file->fp);
|
||||||
UINT bytes_read;
|
UINT bytes_read;
|
||||||
@ -248,7 +246,8 @@ STATIC mp_obj_t synthio_from_file(size_t n_args, const mp_obj_t *pos_args, mp_ma
|
|||||||
result->base.type = &synthio_miditrack_type;
|
result->base.type = &synthio_miditrack_type;
|
||||||
|
|
||||||
common_hal_synthio_miditrack_construct(result, buffer, track_size,
|
common_hal_synthio_miditrack_construct(result, buffer, track_size,
|
||||||
tempo, args[ARG_sample_rate].u_int, bufinfo_waveform.buf, bufinfo_waveform.len / 2,
|
tempo, args[ARG_sample_rate].u_int, args[ARG_waveform].u_obj,
|
||||||
|
mp_const_none,
|
||||||
args[ARG_envelope].u_obj
|
args[ARG_envelope].u_obj
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -262,12 +261,73 @@ STATIC mp_obj_t synthio_from_file(size_t n_args, const mp_obj_t *pos_args, mp_ma
|
|||||||
}
|
}
|
||||||
MP_DEFINE_CONST_FUN_OBJ_KW(synthio_from_file_obj, 1, synthio_from_file);
|
MP_DEFINE_CONST_FUN_OBJ_KW(synthio_from_file_obj, 1, synthio_from_file);
|
||||||
|
|
||||||
|
//| def midi_to_hz(midi_note: float) -> float:
|
||||||
|
//| """Converts the given midi note (60 = middle C, 69 = concert A) to Hz"""
|
||||||
|
//|
|
||||||
|
|
||||||
|
STATIC mp_obj_t midi_to_hz(mp_obj_t arg) {
|
||||||
|
mp_float_t note = mp_arg_validate_obj_float_range(arg, 1, 127, MP_QSTR_note);
|
||||||
|
return mp_obj_new_float(common_hal_synthio_midi_to_hz_float(note));
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(synthio_midi_to_hz_obj, midi_to_hz);
|
||||||
|
|
||||||
|
//| def onevo_to_hz(ctrl: float) -> float:
|
||||||
|
//| """Converts a 1v/octave signal to Hz.
|
||||||
|
//|
|
||||||
|
//| 60/12 (5.0) corresponds to middle C, 69/12 is concert A."""
|
||||||
|
//|
|
||||||
|
|
||||||
|
STATIC mp_obj_t onevo_to_hz(mp_obj_t arg) {
|
||||||
|
mp_float_t note = mp_arg_validate_obj_float_range(arg, 0, 11, MP_QSTR_ctrl);
|
||||||
|
return mp_obj_new_float(common_hal_synthio_onevo_to_hz_float(note));
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(synthio_onevo_to_hz_obj, onevo_to_hz);
|
||||||
|
|
||||||
|
MAKE_ENUM_VALUE(synthio_bend_mode_type, bend_mode, STATIC, SYNTHIO_BEND_MODE_STATIC);
|
||||||
|
MAKE_ENUM_VALUE(synthio_bend_mode_type, bend_mode, VIBRATO, SYNTHIO_BEND_MODE_VIBRATO);
|
||||||
|
MAKE_ENUM_VALUE(synthio_bend_mode_type, bend_mode, SWEEP, SYNTHIO_BEND_MODE_SWEEP);
|
||||||
|
MAKE_ENUM_VALUE(synthio_bend_mode_type, bend_mode, SWEEP_IN, SYNTHIO_BEND_MODE_SWEEP_IN);
|
||||||
|
|
||||||
|
//|
|
||||||
|
//| class BendMode:
|
||||||
|
//| """Controls the way the ``Note.pitch_bend_depth`` and ``Note.pitch_bend_rate`` properties are interpreted."""
|
||||||
|
//|
|
||||||
|
//| STATIC: "BendMode"
|
||||||
|
//| """The Note's pitch is modified by its ``pitch_bend_depth``. ``pitch_bend_rate`` is ignored."""
|
||||||
|
//|
|
||||||
|
//| VIBRATO: "BendMode"
|
||||||
|
//| """The Note's pitch varies by ``±pitch_bend_depth`` at a rate of ``pitch_bend_rate`` Hz."""
|
||||||
|
//|
|
||||||
|
//| SWEEP: "BendMode"
|
||||||
|
//| """The Note's pitch starts at ``Note.frequency`` then sweeps up or down by ``pitch_bend_depth`` over ``1/pitch_bend_rate`` seconds."""
|
||||||
|
//|
|
||||||
|
//| SWEEP_IN: "BendMode"
|
||||||
|
//| """The Note's pitch sweep is the reverse of ``SWEEP`` mode, starting at the bent pitch and arriving at the tuned pitch."""
|
||||||
|
//|
|
||||||
|
MAKE_ENUM_MAP(synthio_bend_mode) {
|
||||||
|
MAKE_ENUM_MAP_ENTRY(bend_mode, STATIC),
|
||||||
|
MAKE_ENUM_MAP_ENTRY(bend_mode, VIBRATO),
|
||||||
|
MAKE_ENUM_MAP_ENTRY(bend_mode, SWEEP),
|
||||||
|
MAKE_ENUM_MAP_ENTRY(bend_mode, SWEEP_IN),
|
||||||
|
};
|
||||||
|
|
||||||
|
STATIC MP_DEFINE_CONST_DICT(synthio_bend_mode_locals_dict, synthio_bend_mode_locals_table);
|
||||||
|
|
||||||
|
MAKE_PRINTER(synthio, synthio_bend_mode);
|
||||||
|
|
||||||
|
MAKE_ENUM_TYPE(synthio, BendMode, synthio_bend_mode);
|
||||||
|
|
||||||
|
|
||||||
STATIC const mp_rom_map_elem_t synthio_module_globals_table[] = {
|
STATIC const mp_rom_map_elem_t synthio_module_globals_table[] = {
|
||||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_synthio) },
|
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_synthio) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_BendMode), MP_ROM_PTR(&synthio_bend_mode_type) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_MidiTrack), MP_ROM_PTR(&synthio_miditrack_type) },
|
{ MP_ROM_QSTR(MP_QSTR_MidiTrack), MP_ROM_PTR(&synthio_miditrack_type) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_Note), MP_ROM_PTR(&synthio_note_type) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_Synthesizer), MP_ROM_PTR(&synthio_synthesizer_type) },
|
{ MP_ROM_QSTR(MP_QSTR_Synthesizer), MP_ROM_PTR(&synthio_synthesizer_type) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_from_file), MP_ROM_PTR(&synthio_from_file_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_from_file), MP_ROM_PTR(&synthio_from_file_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_Envelope), MP_ROM_PTR(&synthio_envelope_type_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_Envelope), MP_ROM_PTR(&synthio_envelope_type_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_midi_to_hz), MP_ROM_PTR(&synthio_midi_to_hz_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_onevo_to_hz), MP_ROM_PTR(&synthio_midi_to_hz_obj) },
|
||||||
};
|
};
|
||||||
|
|
||||||
STATIC MP_DEFINE_CONST_DICT(synthio_module_globals, synthio_module_globals_table);
|
STATIC MP_DEFINE_CONST_DICT(synthio_module_globals, synthio_module_globals_table);
|
||||||
|
@ -27,9 +27,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "py/objnamedtuple.h"
|
#include "py/objnamedtuple.h"
|
||||||
|
#include "py/enum.h"
|
||||||
|
|
||||||
|
typedef enum synthio_bend_mode_e {
|
||||||
|
SYNTHIO_BEND_MODE_STATIC, SYNTHIO_BEND_MODE_VIBRATO, SYNTHIO_BEND_MODE_SWEEP, SYNTHIO_BEND_MODE_SWEEP_IN
|
||||||
|
} synthio_bend_mode_t;
|
||||||
|
|
||||||
|
extern const cp_enum_obj_t bend_mode_VIBRATO_obj;
|
||||||
|
extern const mp_obj_type_t synthio_bend_mode_type;
|
||||||
typedef struct synthio_synth synthio_synth_t;
|
typedef struct synthio_synth synthio_synth_t;
|
||||||
extern int16_t shared_bindings_synthio_square_wave[];
|
extern int16_t shared_bindings_synthio_square_wave[];
|
||||||
extern const mp_obj_namedtuple_type_t synthio_envelope_type_obj;
|
extern const mp_obj_namedtuple_type_t synthio_envelope_type_obj;
|
||||||
void synthio_synth_envelope_set(synthio_synth_t *synth, mp_obj_t envelope_obj);
|
void synthio_synth_envelope_set(synthio_synth_t *synth, mp_obj_t envelope_obj);
|
||||||
mp_obj_t synthio_synth_envelope_get(synthio_synth_t *synth);
|
mp_obj_t synthio_synth_envelope_get(synthio_synth_t *synth);
|
||||||
|
mp_float_t common_hal_synthio_midi_to_hz_float(mp_float_t note);
|
||||||
|
mp_float_t common_hal_synthio_onevo_to_hz_float(mp_float_t note);
|
||||||
|
@ -37,5 +37,17 @@ void raise_deinited_error(void) {
|
|||||||
mp_raise_ValueError(translate("Object has been deinitialized and can no longer be used. Create a new object."));
|
mp_raise_ValueError(translate("Object has been deinitialized and can no longer be used. Create a new object."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void properties_print_helper(const mp_print_t *print, mp_obj_t self_in, const mp_arg_t *properties, size_t n_properties) {
|
||||||
|
const mp_obj_type_t *type = mp_obj_get_type(self_in);
|
||||||
|
mp_printf(print, "%q(", type->name);
|
||||||
|
for (size_t i = 0; i < n_properties; i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
mp_print_str(print, ", ");
|
||||||
|
}
|
||||||
|
mp_printf(print, "%q=", properties[i].qst);
|
||||||
|
mp_obj_print_helper(print, mp_load_attr(self_in, properties[i].qst), PRINT_REPR);
|
||||||
|
}
|
||||||
|
mp_print_str(print, ")");
|
||||||
|
}
|
||||||
|
|
||||||
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_UTIL_H
|
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_UTIL_H
|
||||||
|
@ -27,7 +27,11 @@
|
|||||||
#ifndef MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_UTIL_H
|
#ifndef MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_UTIL_H
|
||||||
#define MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_UTIL_H
|
#define MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_UTIL_H
|
||||||
|
|
||||||
|
#include "py/mpprint.h"
|
||||||
|
#include "py/runtime.h"
|
||||||
|
|
||||||
void raise_deinited_error(void);
|
void raise_deinited_error(void);
|
||||||
|
void properties_print_helper(const mp_print_t *print, mp_obj_t self_in, const mp_arg_t *properties, size_t n_properties);
|
||||||
|
|
||||||
|
|
||||||
#endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_UTIL_H
|
#endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_UTIL_H
|
||||||
|
@ -317,7 +317,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_stop_station_obj, wifi_radio_stop_station);
|
|||||||
//| *,
|
//| *,
|
||||||
//| channel: int = 1,
|
//| channel: int = 1,
|
||||||
//| authmode: Optional[AuthMode] = None,
|
//| authmode: Optional[AuthMode] = None,
|
||||||
//| max_connections: Optional[int] = 4
|
//| max_connections: Optional[int] = 4,
|
||||||
//| ) -> None:
|
//| ) -> None:
|
||||||
//| """Starts running an access point with the specified ssid and password.
|
//| """Starts running an access point with the specified ssid and password.
|
||||||
//|
|
//|
|
||||||
@ -416,7 +416,7 @@ MP_PROPERTY_GETTER(wifi_radio_ap_active_obj,
|
|||||||
//| *,
|
//| *,
|
||||||
//| channel: int = 0,
|
//| channel: int = 0,
|
||||||
//| bssid: Optional[Union[str | ReadableBuffer]] = None,
|
//| bssid: Optional[Union[str | ReadableBuffer]] = None,
|
||||||
//| timeout: Optional[float] = None
|
//| timeout: Optional[float] = None,
|
||||||
//| ) -> None:
|
//| ) -> None:
|
||||||
//| """Connects to the given ssid and waits for an ip address. Reconnections are handled
|
//| """Connects to the given ssid and waits for an ip address. Reconnections are handled
|
||||||
//| automatically once one connection succeeds.
|
//| automatically once one connection succeeds.
|
||||||
@ -551,7 +551,7 @@ MP_PROPERTY_GETTER(wifi_radio_ipv4_subnet_ap_obj,
|
|||||||
//| ipv4: ipaddress.IPv4Address,
|
//| ipv4: ipaddress.IPv4Address,
|
||||||
//| netmask: ipaddress.IPv4Address,
|
//| netmask: ipaddress.IPv4Address,
|
||||||
//| gateway: ipaddress.IPv4Address,
|
//| gateway: ipaddress.IPv4Address,
|
||||||
//| ipv4_dns: Optional[ipaddress.IPv4Address]
|
//| ipv4_dns: Optional[ipaddress.IPv4Address],
|
||||||
//| ) -> None:
|
//| ) -> None:
|
||||||
//| """Sets the IP v4 address of the station. Must include the netmask and gateway. DNS address is optional.
|
//| """Sets the IP v4 address of the station. Must include the netmask and gateway. DNS address is optional.
|
||||||
//| Setting the address manually will stop the DHCP client."""
|
//| Setting the address manually will stop the DHCP client."""
|
||||||
@ -574,6 +574,32 @@ STATIC mp_obj_t wifi_radio_set_ipv4_address(size_t n_args, const mp_obj_t *pos_a
|
|||||||
}
|
}
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(wifi_radio_set_ipv4_address_obj, 1, wifi_radio_set_ipv4_address);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(wifi_radio_set_ipv4_address_obj, 1, wifi_radio_set_ipv4_address);
|
||||||
|
|
||||||
|
//| def set_ipv4_address_ap(
|
||||||
|
//| self,
|
||||||
|
//| *,
|
||||||
|
//| ipv4: ipaddress.IPv4Address,
|
||||||
|
//| netmask: ipaddress.IPv4Address,
|
||||||
|
//| gateway: ipaddress.IPv4Address,
|
||||||
|
//| ) -> None:
|
||||||
|
//| """Sets the IP v4 address of the access point. Must include the netmask and gateway."""
|
||||||
|
//| ...
|
||||||
|
STATIC mp_obj_t wifi_radio_set_ipv4_address_ap(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||||
|
enum { ARG_ipv4, ARG_netmask, ARG_gateway };
|
||||||
|
static const mp_arg_t allowed_args[] = {
|
||||||
|
{ MP_QSTR_ipv4, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, },
|
||||||
|
{ MP_QSTR_netmask, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, },
|
||||||
|
{ MP_QSTR_gateway, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, },
|
||||||
|
};
|
||||||
|
|
||||||
|
wifi_radio_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]);
|
||||||
|
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);
|
||||||
|
|
||||||
|
common_hal_wifi_radio_set_ipv4_address_ap(self, args[ARG_ipv4].u_obj, args[ARG_netmask].u_obj, args[ARG_gateway].u_obj);
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(wifi_radio_set_ipv4_address_ap_obj, 1, wifi_radio_set_ipv4_address_ap);
|
||||||
|
|
||||||
//| ipv4_address: Optional[ipaddress.IPv4Address]
|
//| ipv4_address: Optional[ipaddress.IPv4Address]
|
||||||
//| """IP v4 Address of the station when connected to an access point. None otherwise. (read-only)"""
|
//| """IP v4 Address of the station when connected to an access point. None otherwise. (read-only)"""
|
||||||
STATIC mp_obj_t _wifi_radio_get_ipv4_address(mp_obj_t self) {
|
STATIC mp_obj_t _wifi_radio_get_ipv4_address(mp_obj_t self) {
|
||||||
@ -620,7 +646,7 @@ STATIC mp_obj_t wifi_radio_get_ap_info(mp_obj_t self) {
|
|||||||
MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_ap_info_obj, wifi_radio_get_ap_info);
|
MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_get_ap_info_obj, wifi_radio_get_ap_info);
|
||||||
|
|
||||||
//| def start_dhcp(self) -> None:
|
//| def start_dhcp(self) -> None:
|
||||||
//| """Starts the DHCP client."""
|
//| """Starts the station DHCP client."""
|
||||||
//| ...
|
//| ...
|
||||||
STATIC mp_obj_t wifi_radio_start_dhcp_client(mp_obj_t self) {
|
STATIC mp_obj_t wifi_radio_start_dhcp_client(mp_obj_t self) {
|
||||||
common_hal_wifi_radio_start_dhcp_client(self);
|
common_hal_wifi_radio_start_dhcp_client(self);
|
||||||
@ -629,7 +655,7 @@ STATIC mp_obj_t wifi_radio_start_dhcp_client(mp_obj_t self) {
|
|||||||
MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_start_dhcp_client_obj, wifi_radio_start_dhcp_client);
|
MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_start_dhcp_client_obj, wifi_radio_start_dhcp_client);
|
||||||
|
|
||||||
//| def stop_dhcp(self) -> None:
|
//| def stop_dhcp(self) -> None:
|
||||||
//| """Stops the DHCP client. Needed to assign a static IP address."""
|
//| """Stops the station DHCP client. Needed to assign a static IP address."""
|
||||||
//| ...
|
//| ...
|
||||||
STATIC mp_obj_t wifi_radio_stop_dhcp_client(mp_obj_t self) {
|
STATIC mp_obj_t wifi_radio_stop_dhcp_client(mp_obj_t self) {
|
||||||
common_hal_wifi_radio_stop_dhcp_client(self);
|
common_hal_wifi_radio_stop_dhcp_client(self);
|
||||||
@ -637,6 +663,24 @@ STATIC mp_obj_t wifi_radio_stop_dhcp_client(mp_obj_t self) {
|
|||||||
}
|
}
|
||||||
MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_stop_dhcp_client_obj, wifi_radio_stop_dhcp_client);
|
MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_stop_dhcp_client_obj, wifi_radio_stop_dhcp_client);
|
||||||
|
|
||||||
|
//| def start_dhcp_ap(self) -> None:
|
||||||
|
//| """Starts the access point DHCP server."""
|
||||||
|
//| ...
|
||||||
|
STATIC mp_obj_t wifi_radio_start_dhcp_server(mp_obj_t self) {
|
||||||
|
common_hal_wifi_radio_start_dhcp_server(self);
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_start_dhcp_server_obj, wifi_radio_start_dhcp_server);
|
||||||
|
|
||||||
|
//| def stop_dhcp_ap(self) -> None:
|
||||||
|
//| """Stops the access point DHCP server. Needed to assign a static IP address."""
|
||||||
|
//| ...
|
||||||
|
STATIC mp_obj_t wifi_radio_stop_dhcp_server(mp_obj_t self) {
|
||||||
|
common_hal_wifi_radio_stop_dhcp_server(self);
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_stop_dhcp_server_obj, wifi_radio_stop_dhcp_server);
|
||||||
|
|
||||||
MP_PROPERTY_GETTER(wifi_radio_ap_info_obj,
|
MP_PROPERTY_GETTER(wifi_radio_ap_info_obj,
|
||||||
(mp_obj_t)&wifi_radio_get_ap_info_obj);
|
(mp_obj_t)&wifi_radio_get_ap_info_obj);
|
||||||
|
|
||||||
@ -693,6 +737,8 @@ STATIC const mp_rom_map_elem_t wifi_radio_locals_dict_table[] = {
|
|||||||
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_start_dhcp), MP_ROM_PTR(&wifi_radio_start_dhcp_client_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_start_dhcp), MP_ROM_PTR(&wifi_radio_start_dhcp_client_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_stop_dhcp), MP_ROM_PTR(&wifi_radio_stop_dhcp_client_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_stop_dhcp), MP_ROM_PTR(&wifi_radio_stop_dhcp_client_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_start_dhcp_ap), MP_ROM_PTR(&wifi_radio_start_dhcp_server_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_stop_dhcp_ap), MP_ROM_PTR(&wifi_radio_stop_dhcp_server_obj) },
|
||||||
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&wifi_radio_connect_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&wifi_radio_connect_obj) },
|
||||||
// { MP_ROM_QSTR(MP_QSTR_connect_to_enterprise), MP_ROM_PTR(&wifi_radio_connect_to_enterprise_obj) },
|
// { MP_ROM_QSTR(MP_QSTR_connect_to_enterprise), MP_ROM_PTR(&wifi_radio_connect_to_enterprise_obj) },
|
||||||
@ -708,6 +754,7 @@ STATIC const mp_rom_map_elem_t wifi_radio_locals_dict_table[] = {
|
|||||||
{ MP_ROM_QSTR(MP_QSTR_ipv4_address_ap), MP_ROM_PTR(&wifi_radio_ipv4_address_ap_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_ipv4_address_ap), MP_ROM_PTR(&wifi_radio_ipv4_address_ap_obj) },
|
||||||
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_set_ipv4_address), MP_ROM_PTR(&wifi_radio_set_ipv4_address_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_set_ipv4_address), MP_ROM_PTR(&wifi_radio_set_ipv4_address_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_set_ipv4_address_ap), MP_ROM_PTR(&wifi_radio_set_ipv4_address_ap_obj) },
|
||||||
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_ping), MP_ROM_PTR(&wifi_radio_ping_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_ping), MP_ROM_PTR(&wifi_radio_ping_obj) },
|
||||||
};
|
};
|
||||||
|
@ -99,6 +99,8 @@ extern bool common_hal_wifi_radio_get_ap_active(wifi_radio_obj_t *self);
|
|||||||
|
|
||||||
extern void common_hal_wifi_radio_start_dhcp_client(wifi_radio_obj_t *self);
|
extern void common_hal_wifi_radio_start_dhcp_client(wifi_radio_obj_t *self);
|
||||||
extern void common_hal_wifi_radio_stop_dhcp_client(wifi_radio_obj_t *self);
|
extern void common_hal_wifi_radio_stop_dhcp_client(wifi_radio_obj_t *self);
|
||||||
|
extern void common_hal_wifi_radio_start_dhcp_server(wifi_radio_obj_t *self);
|
||||||
|
extern void common_hal_wifi_radio_stop_dhcp_server(wifi_radio_obj_t *self);
|
||||||
|
|
||||||
extern wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t *ssid, size_t ssid_len, uint8_t *password, size_t password_len, uint8_t channel, mp_float_t timeout, uint8_t *bssid, size_t bssid_len);
|
extern wifi_radio_error_t common_hal_wifi_radio_connect(wifi_radio_obj_t *self, uint8_t *ssid, size_t ssid_len, uint8_t *password, size_t password_len, uint8_t channel, mp_float_t timeout, uint8_t *bssid, size_t bssid_len);
|
||||||
extern bool common_hal_wifi_radio_get_connected(wifi_radio_obj_t *self);
|
extern bool common_hal_wifi_radio_get_connected(wifi_radio_obj_t *self);
|
||||||
@ -115,6 +117,7 @@ extern mp_obj_t common_hal_wifi_radio_get_ipv4_address(wifi_radio_obj_t *self);
|
|||||||
extern mp_obj_t common_hal_wifi_radio_get_ipv4_address_ap(wifi_radio_obj_t *self);
|
extern mp_obj_t common_hal_wifi_radio_get_ipv4_address_ap(wifi_radio_obj_t *self);
|
||||||
|
|
||||||
extern void common_hal_wifi_radio_set_ipv4_address(wifi_radio_obj_t *self, mp_obj_t ipv4, mp_obj_t netmask, mp_obj_t gateway, mp_obj_t ipv4_dns_addr);
|
extern void common_hal_wifi_radio_set_ipv4_address(wifi_radio_obj_t *self, mp_obj_t ipv4, mp_obj_t netmask, mp_obj_t gateway, mp_obj_t ipv4_dns_addr);
|
||||||
|
extern void common_hal_wifi_radio_set_ipv4_address_ap(wifi_radio_obj_t *self, mp_obj_t ipv4, mp_obj_t netmask, mp_obj_t gateway);
|
||||||
|
|
||||||
extern mp_int_t common_hal_wifi_radio_ping(wifi_radio_obj_t *self, mp_obj_t ip_address, mp_float_t timeout);
|
extern mp_int_t common_hal_wifi_radio_ping(wifi_radio_obj_t *self, mp_obj_t ip_address, mp_float_t timeout);
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ void common_hal_adafruit_bus_device_spidevice_construct(adafruit_bus_device_spid
|
|||||||
self->polarity = polarity;
|
self->polarity = polarity;
|
||||||
self->phase = phase;
|
self->phase = phase;
|
||||||
self->extra_clocks = extra_clocks;
|
self->extra_clocks = extra_clocks;
|
||||||
|
// May be mp_const_none if CS not used.
|
||||||
self->chip_select = cs;
|
self->chip_select = cs;
|
||||||
self->cs_active_value = cs_active_value;
|
self->cs_active_value = cs_active_value;
|
||||||
}
|
}
|
||||||
@ -66,14 +67,14 @@ mp_obj_t common_hal_adafruit_bus_device_spidevice_enter(adafruit_bus_device_spid
|
|||||||
mp_call_method_n_kw(0, 4, dest);
|
mp_call_method_n_kw(0, 4, dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self->chip_select != MP_OBJ_NULL) {
|
if (self->chip_select != mp_const_none) {
|
||||||
common_hal_digitalio_digitalinout_set_value(MP_OBJ_TO_PTR(self->chip_select), self->cs_active_value);
|
common_hal_digitalio_digitalinout_set_value(MP_OBJ_TO_PTR(self->chip_select), self->cs_active_value);
|
||||||
}
|
}
|
||||||
return self->spi;
|
return self->spi;
|
||||||
}
|
}
|
||||||
|
|
||||||
void common_hal_adafruit_bus_device_spidevice_exit(adafruit_bus_device_spidevice_obj_t *self) {
|
void common_hal_adafruit_bus_device_spidevice_exit(adafruit_bus_device_spidevice_obj_t *self) {
|
||||||
if (self->chip_select != MP_OBJ_NULL) {
|
if (self->chip_select != mp_const_none) {
|
||||||
common_hal_digitalio_digitalinout_set_value(MP_OBJ_TO_PTR(self->chip_select), !(self->cs_active_value));
|
common_hal_digitalio_digitalinout_set_value(MP_OBJ_TO_PTR(self->chip_select), !(self->cs_active_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,22 +28,22 @@
|
|||||||
#include "shared-bindings/synthio/MidiTrack.h"
|
#include "shared-bindings/synthio/MidiTrack.h"
|
||||||
|
|
||||||
|
|
||||||
STATIC void print_midi_stream_error(synthio_miditrack_obj_t *self) {
|
STATIC void record_midi_stream_error(synthio_miditrack_obj_t *self) {
|
||||||
self->error_location = self->pos;
|
self->error_location = self->pos;
|
||||||
self->pos = self->track.len;
|
self->pos = self->track.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
STATIC uint8_t parse_note(synthio_miditrack_obj_t *self) {
|
STATIC mp_obj_t parse_note(synthio_miditrack_obj_t *self) {
|
||||||
uint8_t *buffer = self->track.buf;
|
uint8_t *buffer = self->track.buf;
|
||||||
size_t len = self->track.len;
|
size_t len = self->track.len;
|
||||||
if (self->pos + 1 >= len) {
|
if (self->pos + 1 >= len) {
|
||||||
print_midi_stream_error(self);
|
record_midi_stream_error(self);
|
||||||
}
|
}
|
||||||
uint8_t note = buffer[(self->pos)++];
|
uint8_t note = buffer[(self->pos)++];
|
||||||
if (note > 127 || buffer[(self->pos)++] > 127) {
|
if (note > 127 || buffer[(self->pos)++] > 127) {
|
||||||
print_midi_stream_error(self);
|
record_midi_stream_error(self);
|
||||||
}
|
}
|
||||||
return note;
|
return MP_OBJ_NEW_SMALL_INT(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int decode_duration(synthio_miditrack_obj_t *self) {
|
static int decode_duration(synthio_miditrack_obj_t *self) {
|
||||||
@ -60,7 +60,7 @@ static int decode_duration(synthio_miditrack_obj_t *self) {
|
|||||||
// errors cannot be raised from the background task, so simply end the track.
|
// errors cannot be raised from the background task, so simply end the track.
|
||||||
if (c & 0x80) {
|
if (c & 0x80) {
|
||||||
self->pos = self->track.len;
|
self->pos = self->track.len;
|
||||||
print_midi_stream_error(self);
|
record_midi_stream_error(self);
|
||||||
}
|
}
|
||||||
return delta * self->synth.sample_rate / self->tempo;
|
return delta * self->synth.sample_rate / self->tempo;
|
||||||
}
|
}
|
||||||
@ -72,12 +72,12 @@ static void decode_until_pause(synthio_miditrack_obj_t *self) {
|
|||||||
do {
|
do {
|
||||||
switch (buffer[self->pos++] >> 4) {
|
switch (buffer[self->pos++] >> 4) {
|
||||||
case 8: { // Note Off
|
case 8: { // Note Off
|
||||||
uint8_t note = parse_note(self);
|
mp_obj_t note = parse_note(self);
|
||||||
synthio_span_change_note(&self->synth, note, SYNTHIO_SILENCE);
|
synthio_span_change_note(&self->synth, note, SYNTHIO_SILENCE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 9: { // Note On
|
case 9: { // Note On
|
||||||
uint8_t note = parse_note(self);
|
mp_obj_t note = parse_note(self);
|
||||||
synthio_span_change_note(&self->synth, SYNTHIO_SILENCE, note);
|
synthio_span_change_note(&self->synth, SYNTHIO_SILENCE, note);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -89,14 +89,14 @@ static void decode_until_pause(synthio_miditrack_obj_t *self) {
|
|||||||
case 12:
|
case 12:
|
||||||
case 13: // one data byte to ignore
|
case 13: // one data byte to ignore
|
||||||
if (self->pos >= len || buffer[self->pos++] > 127) {
|
if (self->pos >= len || buffer[self->pos++] > 127) {
|
||||||
print_midi_stream_error(self);
|
record_midi_stream_error(self);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 15: // the full syntax is too complicated, just assume it's "End of Track" event
|
case 15: // the full syntax is too complicated, just assume it's "End of Track" event
|
||||||
self->pos = len;
|
self->pos = len;
|
||||||
break;
|
break;
|
||||||
default: // invalid event
|
default: // invalid event
|
||||||
print_midi_stream_error(self);
|
record_midi_stream_error(self);
|
||||||
}
|
}
|
||||||
if (self->pos < len) {
|
if (self->pos < len) {
|
||||||
self->synth.span.dur = decode_duration(self);
|
self->synth.span.dur = decode_duration(self);
|
||||||
@ -116,14 +116,13 @@ STATIC void start_parse(synthio_miditrack_obj_t *self) {
|
|||||||
|
|
||||||
void common_hal_synthio_miditrack_construct(synthio_miditrack_obj_t *self,
|
void common_hal_synthio_miditrack_construct(synthio_miditrack_obj_t *self,
|
||||||
const uint8_t *buffer, uint32_t len, uint32_t tempo, uint32_t sample_rate,
|
const uint8_t *buffer, uint32_t len, uint32_t tempo, uint32_t sample_rate,
|
||||||
const int16_t *waveform, uint16_t waveform_length,
|
mp_obj_t waveform_obj, mp_obj_t filter_obj, mp_obj_t envelope_obj) {
|
||||||
mp_obj_t envelope) {
|
|
||||||
|
|
||||||
self->tempo = tempo;
|
self->tempo = tempo;
|
||||||
self->track.buf = (void *)buffer;
|
self->track.buf = (void *)buffer;
|
||||||
self->track.len = len;
|
self->track.len = len;
|
||||||
|
|
||||||
synthio_synth_init(&self->synth, sample_rate, waveform, waveform_length, envelope);
|
synthio_synth_init(&self->synth, sample_rate, 1, waveform_obj, mp_const_none, envelope_obj);
|
||||||
|
|
||||||
start_parse(self);
|
start_parse(self);
|
||||||
}
|
}
|
||||||
@ -158,6 +157,10 @@ void synthio_miditrack_reset_buffer(synthio_miditrack_obj_t *self,
|
|||||||
|
|
||||||
audioio_get_buffer_result_t synthio_miditrack_get_buffer(synthio_miditrack_obj_t *self,
|
audioio_get_buffer_result_t synthio_miditrack_get_buffer(synthio_miditrack_obj_t *self,
|
||||||
bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length) {
|
bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length) {
|
||||||
|
if (common_hal_synthio_miditrack_deinited(self)) {
|
||||||
|
*buffer_length = 0;
|
||||||
|
return GET_BUFFER_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
synthio_synth_synthesize(&self->synth, buffer, buffer_length, single_channel_output ? 0 : channel);
|
synthio_synth_synthesize(&self->synth, buffer, buffer_length, single_channel_output ? 0 : channel);
|
||||||
if (self->synth.span.dur == 0) {
|
if (self->synth.span.dur == 0) {
|
||||||
|
252
shared-module/synthio/Note.c
Normal file
252
shared-module/synthio/Note.c
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Micro Python project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Jeff Epler for Adafruit Industries
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include "py/runtime.h"
|
||||||
|
#include "shared-module/synthio/Note.h"
|
||||||
|
#include "shared-bindings/synthio/Note.h"
|
||||||
|
#include "shared-bindings/synthio/__init__.h"
|
||||||
|
|
||||||
|
static int32_t round_float_to_int(mp_float_t f) {
|
||||||
|
return (int32_t)(f + MICROPY_FLOAT_CONST(0.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_float_t common_hal_synthio_note_get_frequency(synthio_note_obj_t *self) {
|
||||||
|
return self->frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_synthio_note_set_frequency(synthio_note_obj_t *self, mp_float_t value_in) {
|
||||||
|
mp_float_t val = mp_arg_validate_float_range(value_in, 0, 32767, MP_QSTR_frequency);
|
||||||
|
self->frequency = val;
|
||||||
|
self->frequency_scaled = synthio_frequency_convert_float_to_scaled(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool common_hal_synthio_note_get_filter(synthio_note_obj_t *self) {
|
||||||
|
return self->filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_synthio_note_set_filter(synthio_note_obj_t *self, bool value_in) {
|
||||||
|
self->filter = value_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_float_t common_hal_synthio_note_get_ring_frequency(synthio_note_obj_t *self) {
|
||||||
|
return self->ring_frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_synthio_note_set_ring_frequency(synthio_note_obj_t *self, mp_float_t value_in) {
|
||||||
|
mp_float_t val = mp_arg_validate_float_range(value_in, 0, 32767, MP_QSTR_ring_frequency);
|
||||||
|
self->ring_frequency = val;
|
||||||
|
self->ring_frequency_scaled = synthio_frequency_convert_float_to_scaled(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_float_t common_hal_synthio_note_get_panning(synthio_note_obj_t *self) {
|
||||||
|
return self->panning;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_synthio_note_set_panning(synthio_note_obj_t *self, mp_float_t value_in) {
|
||||||
|
mp_float_t val = mp_arg_validate_float_range(value_in, -1, 1, MP_QSTR_panning);
|
||||||
|
self->panning = val;
|
||||||
|
if (val >= 0) {
|
||||||
|
self->left_panning_scaled = 32768;
|
||||||
|
self->right_panning_scaled = 32768 - round_float_to_int(val * 32768);
|
||||||
|
} else {
|
||||||
|
self->right_panning_scaled = 32768;
|
||||||
|
self->left_panning_scaled = 32768 + round_float_to_int(val * 32768);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_float_t common_hal_synthio_note_get_tremolo_depth(synthio_note_obj_t *self) {
|
||||||
|
return self->tremolo_descr.amplitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_synthio_note_set_tremolo_depth(synthio_note_obj_t *self, mp_float_t value_in) {
|
||||||
|
mp_float_t val = mp_arg_validate_float_range(value_in, 0, 1, MP_QSTR_tremolo_depth);
|
||||||
|
self->tremolo_descr.amplitude = val;
|
||||||
|
self->tremolo_state.amplitude_scaled = round_float_to_int(val * 32768);
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_float_t common_hal_synthio_note_get_tremolo_rate(synthio_note_obj_t *self) {
|
||||||
|
return self->tremolo_descr.frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_synthio_note_set_tremolo_rate(synthio_note_obj_t *self, mp_float_t value_in) {
|
||||||
|
mp_float_t val = mp_arg_validate_float_range(value_in, 0, 60, MP_QSTR_tremolo_rate);
|
||||||
|
self->tremolo_descr.frequency = val;
|
||||||
|
if (self->sample_rate != 0) {
|
||||||
|
self->tremolo_state.dds = synthio_frequency_convert_float_to_dds(val * 65536, self->sample_rate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_float_t common_hal_synthio_note_get_bend_depth(synthio_note_obj_t *self) {
|
||||||
|
return self->bend_descr.amplitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_synthio_note_set_bend_depth(synthio_note_obj_t *self, mp_float_t value_in) {
|
||||||
|
mp_float_t val = mp_arg_validate_float_range(value_in, -1, 1, MP_QSTR_bend_depth);
|
||||||
|
self->bend_descr.amplitude = val;
|
||||||
|
self->bend_state.amplitude_scaled = round_float_to_int(val * 32768);
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_float_t common_hal_synthio_note_get_bend_rate(synthio_note_obj_t *self) {
|
||||||
|
return self->bend_descr.frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
synthio_bend_mode_t common_hal_synthio_note_get_bend_mode(synthio_note_obj_t *self) {
|
||||||
|
return self->bend_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_synthio_note_set_bend_mode(synthio_note_obj_t *self, synthio_bend_mode_t value) {
|
||||||
|
self->bend_mode = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void common_hal_synthio_note_set_bend_rate(synthio_note_obj_t *self, mp_float_t value_in) {
|
||||||
|
mp_float_t val = mp_arg_validate_float_range(value_in, 0, 60, MP_QSTR_bend_rate);
|
||||||
|
self->bend_descr.frequency = val;
|
||||||
|
if (self->sample_rate != 0) {
|
||||||
|
self->bend_state.dds = synthio_frequency_convert_float_to_dds(val * 65536, self->sample_rate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_obj_t common_hal_synthio_note_get_envelope_obj(synthio_note_obj_t *self) {
|
||||||
|
return self->envelope_obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_synthio_note_set_envelope(synthio_note_obj_t *self, mp_obj_t envelope_in) {
|
||||||
|
if (envelope_in != mp_const_none) {
|
||||||
|
mp_arg_validate_type(envelope_in, (mp_obj_type_t *)&synthio_envelope_type_obj, MP_QSTR_envelope);
|
||||||
|
if (self->sample_rate != 0) {
|
||||||
|
synthio_envelope_definition_set(&self->envelope_def, envelope_in, self->sample_rate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self->envelope_obj = envelope_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_obj_t common_hal_synthio_note_get_waveform_obj(synthio_note_obj_t *self) {
|
||||||
|
return self->waveform_obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_synthio_note_set_waveform(synthio_note_obj_t *self, mp_obj_t waveform_in) {
|
||||||
|
if (waveform_in == mp_const_none) {
|
||||||
|
memset(&self->waveform_buf, 0, sizeof(self->waveform_buf));
|
||||||
|
} else {
|
||||||
|
mp_buffer_info_t bufinfo_waveform;
|
||||||
|
synthio_synth_parse_waveform(&bufinfo_waveform, waveform_in);
|
||||||
|
self->waveform_buf = bufinfo_waveform;
|
||||||
|
}
|
||||||
|
self->waveform_obj = waveform_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_obj_t common_hal_synthio_note_get_ring_waveform_obj(synthio_note_obj_t *self) {
|
||||||
|
return self->ring_waveform_obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void common_hal_synthio_note_set_ring_waveform(synthio_note_obj_t *self, mp_obj_t ring_waveform_in) {
|
||||||
|
if (ring_waveform_in == mp_const_none) {
|
||||||
|
memset(&self->ring_waveform_buf, 0, sizeof(self->ring_waveform_buf));
|
||||||
|
} else {
|
||||||
|
mp_buffer_info_t bufinfo_ring_waveform;
|
||||||
|
synthio_synth_parse_waveform(&bufinfo_ring_waveform, ring_waveform_in);
|
||||||
|
self->ring_waveform_buf = bufinfo_ring_waveform;
|
||||||
|
}
|
||||||
|
self->ring_waveform_obj = ring_waveform_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
void synthio_note_recalculate(synthio_note_obj_t *self, int32_t sample_rate) {
|
||||||
|
if (sample_rate == self->sample_rate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self->sample_rate = sample_rate;
|
||||||
|
|
||||||
|
if (self->envelope_obj != mp_const_none) {
|
||||||
|
synthio_envelope_definition_set(&self->envelope_def, self->envelope_obj, sample_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
synthio_lfo_set(&self->tremolo_state, &self->tremolo_descr, sample_rate);
|
||||||
|
self->tremolo_state.offset_scaled = 32768 - self->tremolo_state.amplitude_scaled;
|
||||||
|
synthio_lfo_set(&self->bend_state, &self->bend_descr, sample_rate);
|
||||||
|
self->bend_state.offset_scaled = 32768;
|
||||||
|
}
|
||||||
|
|
||||||
|
void synthio_note_start(synthio_note_obj_t *self, int32_t sample_rate) {
|
||||||
|
synthio_note_recalculate(self, sample_rate);
|
||||||
|
if (self->bend_mode != SYNTHIO_BEND_MODE_VIBRATO) {
|
||||||
|
self->bend_state.phase = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t synthio_note_envelope(synthio_note_obj_t *self) {
|
||||||
|
return self->amplitude_scaled;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform a pitch bend operation
|
||||||
|
//
|
||||||
|
// bend_value is in the range [0, 65535]. "no change" is 32768. The bend unit is 32768/octave.
|
||||||
|
//
|
||||||
|
// compare to (frequency_scaled * pow(2, (bend_value-32768)/32768))
|
||||||
|
// a 13-entry pitch table
|
||||||
|
#define BEND_SCALE (32768)
|
||||||
|
#define BEND_OFFSET (BEND_SCALE)
|
||||||
|
|
||||||
|
STATIC uint16_t pitch_bend_table[] = { 0, 1948, 4013, 6200, 8517, 10972, 13573, 16329, 19248, 22341, 25618, 29090, 32768 };
|
||||||
|
|
||||||
|
STATIC uint32_t pitch_bend(uint32_t frequency_scaled, uint16_t bend_value) {
|
||||||
|
bool down = (bend_value < 32768);
|
||||||
|
if (!down) {
|
||||||
|
bend_value -= 32768;
|
||||||
|
}
|
||||||
|
uint32_t bend_value_semitone = (uint32_t)bend_value * 24; // 65536/semitone
|
||||||
|
uint32_t semitone = bend_value_semitone >> 16;
|
||||||
|
uint32_t fractone = bend_value_semitone & 0xffff;
|
||||||
|
uint32_t f_lo = pitch_bend_table[semitone];
|
||||||
|
uint32_t f_hi = pitch_bend_table[semitone + 1]; // table has 13 entries, indexing with semitone=12 is OK
|
||||||
|
uint32_t f = ((f_lo * (65535 - fractone) + f_hi * fractone) >> 16) + BEND_OFFSET;
|
||||||
|
return (frequency_scaled * (uint64_t)f) >> (15 + down);
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC int synthio_bend_value(synthio_note_obj_t *self, int16_t dur) {
|
||||||
|
switch (self->bend_mode) {
|
||||||
|
case SYNTHIO_BEND_MODE_STATIC:
|
||||||
|
return self->bend_state.amplitude_scaled + self->bend_state.offset_scaled;
|
||||||
|
case SYNTHIO_BEND_MODE_VIBRATO:
|
||||||
|
return synthio_lfo_step(&self->bend_state, dur);
|
||||||
|
case SYNTHIO_BEND_MODE_SWEEP:
|
||||||
|
return synthio_sweep_step(&self->bend_state, dur);
|
||||||
|
case SYNTHIO_BEND_MODE_SWEEP_IN:
|
||||||
|
return synthio_sweep_in_step(&self->bend_state, dur);
|
||||||
|
default:
|
||||||
|
return 32768;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t synthio_note_step(synthio_note_obj_t *self, int32_t sample_rate, int16_t dur, uint16_t loudness[2]) {
|
||||||
|
int tremolo_value = synthio_lfo_step(&self->tremolo_state, dur);
|
||||||
|
loudness[0] = (((loudness[0] * tremolo_value) >> 15) * self->left_panning_scaled) >> 15;
|
||||||
|
loudness[1] = (((loudness[1] * tremolo_value) >> 15) * self->right_panning_scaled) >> 15;
|
||||||
|
int bend_value = synthio_bend_value(self, dur);
|
||||||
|
uint32_t frequency_scaled = pitch_bend(self->frequency_scaled, bend_value);
|
||||||
|
return frequency_scaled;
|
||||||
|
}
|
59
shared-module/synthio/Note.h
Normal file
59
shared-module/synthio/Note.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the MicroPython project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Jeff Epler for Adafruit Industries
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "shared-module/synthio/__init__.h"
|
||||||
|
#include "shared-bindings/synthio/__init__.h"
|
||||||
|
|
||||||
|
typedef struct synthio_note_obj {
|
||||||
|
mp_obj_base_t base;
|
||||||
|
|
||||||
|
mp_float_t frequency, ring_frequency;
|
||||||
|
mp_float_t panning;
|
||||||
|
mp_obj_t waveform_obj, envelope_obj, ring_waveform_obj;
|
||||||
|
|
||||||
|
int32_t sample_rate;
|
||||||
|
|
||||||
|
int32_t frequency_scaled;
|
||||||
|
int32_t ring_frequency_scaled;
|
||||||
|
int32_t amplitude_scaled;
|
||||||
|
int32_t left_panning_scaled, right_panning_scaled;
|
||||||
|
bool filter;
|
||||||
|
synthio_bend_mode_t bend_mode;
|
||||||
|
synthio_lfo_descr_t tremolo_descr, bend_descr;
|
||||||
|
synthio_lfo_state_t tremolo_state, bend_state;
|
||||||
|
|
||||||
|
mp_buffer_info_t waveform_buf;
|
||||||
|
mp_buffer_info_t ring_waveform_buf;
|
||||||
|
synthio_envelope_definition_t envelope_def;
|
||||||
|
} synthio_note_obj_t;
|
||||||
|
|
||||||
|
void synthio_note_recalculate(synthio_note_obj_t *self, int32_t sample_rate);
|
||||||
|
uint32_t synthio_note_step(synthio_note_obj_t *self, int32_t sample_rate, int16_t dur, uint16_t loudness[2]);
|
||||||
|
void synthio_note_start(synthio_note_obj_t *self, int32_t sample_rate);
|
||||||
|
bool synthio_note_playing(synthio_note_obj_t *self);
|
||||||
|
uint32_t synthio_note_envelope(synthio_note_obj_t *self);
|
@ -26,14 +26,16 @@
|
|||||||
|
|
||||||
#include "py/runtime.h"
|
#include "py/runtime.h"
|
||||||
#include "shared-bindings/synthio/Synthesizer.h"
|
#include "shared-bindings/synthio/Synthesizer.h"
|
||||||
|
#include "shared-bindings/synthio/Note.h"
|
||||||
|
#include "shared-module/synthio/Note.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void common_hal_synthio_synthesizer_construct(synthio_synthesizer_obj_t *self,
|
void common_hal_synthio_synthesizer_construct(synthio_synthesizer_obj_t *self,
|
||||||
uint32_t sample_rate, const int16_t *waveform, uint16_t waveform_length,
|
uint32_t sample_rate, int channel_count, mp_obj_t waveform_obj, mp_obj_t filter_obj,
|
||||||
mp_obj_t envelope) {
|
mp_obj_t envelope_obj) {
|
||||||
|
|
||||||
synthio_synth_init(&self->synth, sample_rate, waveform, waveform_length, envelope);
|
synthio_synth_init(&self->synth, sample_rate, channel_count, waveform_obj, filter_obj, envelope_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void common_hal_synthio_synthesizer_deinit(synthio_synthesizer_obj_t *self) {
|
void common_hal_synthio_synthesizer_deinit(synthio_synthesizer_obj_t *self) {
|
||||||
@ -50,7 +52,7 @@ uint8_t common_hal_synthio_synthesizer_get_bits_per_sample(synthio_synthesizer_o
|
|||||||
return SYNTHIO_BITS_PER_SAMPLE;
|
return SYNTHIO_BITS_PER_SAMPLE;
|
||||||
}
|
}
|
||||||
uint8_t common_hal_synthio_synthesizer_get_channel_count(synthio_synthesizer_obj_t *self) {
|
uint8_t common_hal_synthio_synthesizer_get_channel_count(synthio_synthesizer_obj_t *self) {
|
||||||
return 1;
|
return self->synth.channel_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void synthio_synthesizer_reset_buffer(synthio_synthesizer_obj_t *self,
|
void synthio_synthesizer_reset_buffer(synthio_synthesizer_obj_t *self,
|
||||||
@ -60,6 +62,10 @@ void synthio_synthesizer_reset_buffer(synthio_synthesizer_obj_t *self,
|
|||||||
|
|
||||||
audioio_get_buffer_result_t synthio_synthesizer_get_buffer(synthio_synthesizer_obj_t *self,
|
audioio_get_buffer_result_t synthio_synthesizer_get_buffer(synthio_synthesizer_obj_t *self,
|
||||||
bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length) {
|
bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length) {
|
||||||
|
if (common_hal_synthio_synthesizer_deinited(self)) {
|
||||||
|
*buffer_length = 0;
|
||||||
|
return GET_BUFFER_ERROR;
|
||||||
|
}
|
||||||
self->synth.span.dur = SYNTHIO_MAX_DUR;
|
self->synth.span.dur = SYNTHIO_MAX_DUR;
|
||||||
synthio_synth_synthesize(&self->synth, buffer, buffer_length, single_channel_output ? channel : 0);
|
synthio_synth_synthesize(&self->synth, buffer, buffer_length, single_channel_output ? channel : 0);
|
||||||
return GET_BUFFER_MORE_DATA;
|
return GET_BUFFER_MORE_DATA;
|
||||||
@ -72,40 +78,58 @@ void synthio_synthesizer_get_buffer_structure(synthio_synthesizer_obj_t *self, b
|
|||||||
|
|
||||||
void common_hal_synthio_synthesizer_release_all(synthio_synthesizer_obj_t *self) {
|
void common_hal_synthio_synthesizer_release_all(synthio_synthesizer_obj_t *self) {
|
||||||
for (size_t i = 0; i < CIRCUITPY_SYNTHIO_MAX_CHANNELS; i++) {
|
for (size_t i = 0; i < CIRCUITPY_SYNTHIO_MAX_CHANNELS; i++) {
|
||||||
if (self->synth.span.note[i] != SYNTHIO_SILENCE) {
|
if (self->synth.span.note_obj[i] != SYNTHIO_SILENCE) {
|
||||||
synthio_span_change_note(&self->synth, self->synth.span.note[i], SYNTHIO_SILENCE);
|
synthio_span_change_note(&self->synth, self->synth.span.note_obj[i], SYNTHIO_SILENCE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
STATIC mp_obj_t validate_note(mp_obj_t note_in) {
|
||||||
|
if (mp_obj_is_small_int(note_in)) {
|
||||||
|
mp_arg_validate_int_range(mp_obj_get_int(note_in), 0, 127, MP_QSTR_note);
|
||||||
|
} else {
|
||||||
|
const mp_obj_type_t *note_type = mp_obj_get_type(note_in);
|
||||||
|
if (note_type != &synthio_note_type) {
|
||||||
|
mp_raise_TypeError_varg(translate("%q must be of type %q or %q, not %q"), MP_QSTR_note, MP_QSTR_int, MP_QSTR_Note, note_type->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return note_in;
|
||||||
|
}
|
||||||
|
|
||||||
void common_hal_synthio_synthesizer_release(synthio_synthesizer_obj_t *self, mp_obj_t to_release) {
|
void common_hal_synthio_synthesizer_release(synthio_synthesizer_obj_t *self, mp_obj_t to_release) {
|
||||||
mp_obj_iter_buf_t iter_buf;
|
mp_obj_iter_buf_t iter_buf;
|
||||||
mp_obj_t iterable = mp_getiter(to_release, &iter_buf);
|
mp_obj_t iterable = mp_getiter(to_release, &iter_buf);
|
||||||
mp_obj_t item;
|
mp_obj_t item;
|
||||||
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||||
synthio_span_change_note(&self->synth, mp_arg_validate_int_range(mp_obj_get_int(item), 0, 127, MP_QSTR_note), SYNTHIO_SILENCE);
|
synthio_span_change_note(&self->synth, validate_note(item), SYNTHIO_SILENCE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void common_hal_synthio_synthesizer_press(synthio_synthesizer_obj_t *self, mp_obj_t to_press) {
|
void common_hal_synthio_synthesizer_press(synthio_synthesizer_obj_t *self, mp_obj_t to_press) {
|
||||||
mp_obj_iter_buf_t iter_buf;
|
mp_obj_iter_buf_t iter_buf;
|
||||||
mp_obj_t iterable = mp_getiter(to_press, &iter_buf);
|
mp_obj_t iterable = mp_getiter(to_press, &iter_buf);
|
||||||
mp_obj_t item;
|
mp_obj_t note_obj;
|
||||||
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
while ((note_obj = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||||
synthio_span_change_note(&self->synth, SYNTHIO_SILENCE, mp_arg_validate_int_range(mp_obj_get_int(item), 0, 127, MP_QSTR_note));
|
note_obj = validate_note(note_obj);
|
||||||
|
if (!mp_obj_is_small_int(note_obj)) {
|
||||||
|
synthio_note_obj_t *note = MP_OBJ_TO_PTR(note_obj);
|
||||||
|
synthio_note_start(note, self->synth.sample_rate);
|
||||||
|
}
|
||||||
|
synthio_span_change_note(&self->synth, SYNTHIO_SILENCE, note_obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mp_obj_t common_hal_synthio_synthesizer_get_pressed_notes(synthio_synthesizer_obj_t *self) {
|
mp_obj_t common_hal_synthio_synthesizer_get_pressed_notes(synthio_synthesizer_obj_t *self) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (int chan = 0; chan < CIRCUITPY_SYNTHIO_MAX_CHANNELS; chan++) {
|
for (int chan = 0; chan < CIRCUITPY_SYNTHIO_MAX_CHANNELS; chan++) {
|
||||||
if (self->synth.span.note[chan] != SYNTHIO_SILENCE && self->synth.envelope_state[chan].state != SYNTHIO_ENVELOPE_STATE_RELEASE) {
|
if (self->synth.span.note_obj[chan] != SYNTHIO_SILENCE && SYNTHIO_NOTE_IS_PLAYING(&self->synth, chan)) {
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(count, NULL));
|
mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(count, NULL));
|
||||||
for (size_t chan = 0, j = 0; chan < CIRCUITPY_SYNTHIO_MAX_CHANNELS; chan++) {
|
for (size_t chan = 0, j = 0; chan < CIRCUITPY_SYNTHIO_MAX_CHANNELS; chan++) {
|
||||||
if (self->synth.span.note[chan] != SYNTHIO_SILENCE && self->synth.envelope_state[chan].state != SYNTHIO_ENVELOPE_STATE_RELEASE) {
|
if (self->synth.span.note_obj[chan] != SYNTHIO_SILENCE && SYNTHIO_NOTE_IS_PLAYING(&self->synth, chan)) {
|
||||||
result->items[j++] = MP_OBJ_NEW_SMALL_INT(self->synth.span.note[chan]);
|
result->items[j++] = self->synth.span.note_obj[chan];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return MP_OBJ_FROM_PTR(result);
|
return MP_OBJ_FROM_PTR(result);
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#include "shared-module/synthio/__init__.h"
|
#include "shared-module/synthio/__init__.h"
|
||||||
#include "shared-bindings/synthio/__init__.h"
|
#include "shared-bindings/synthio/__init__.h"
|
||||||
|
#include "shared-module/synthio/Note.h"
|
||||||
#include "py/runtime.h"
|
#include "py/runtime.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -36,17 +37,33 @@ STATIC const int16_t square_wave[] = {-32768, 32767};
|
|||||||
STATIC const uint16_t notes[] = {8372, 8870, 9397, 9956, 10548, 11175, 11840,
|
STATIC const uint16_t notes[] = {8372, 8870, 9397, 9956, 10548, 11175, 11840,
|
||||||
12544, 13290, 14080, 14917, 15804}; // 9th octave
|
12544, 13290, 14080, 14917, 15804}; // 9th octave
|
||||||
|
|
||||||
|
STATIC int32_t round_float_to_int(mp_float_t f) {
|
||||||
|
return (int32_t)(f + MICROPY_FLOAT_CONST(0.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC int64_t round_float_to_int64(mp_float_t f) {
|
||||||
|
return (int64_t)(f + MICROPY_FLOAT_CONST(0.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_float_t common_hal_synthio_midi_to_hz_float(mp_float_t arg) {
|
||||||
|
return common_hal_synthio_onevo_to_hz_float(arg / 12.);
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_float_t common_hal_synthio_onevo_to_hz_float(mp_float_t octave) {
|
||||||
|
return notes[0] * MICROPY_FLOAT_C_FUN(pow)(2., octave - 10);
|
||||||
|
}
|
||||||
|
|
||||||
STATIC int16_t convert_time_to_rate(uint32_t sample_rate, mp_obj_t time_in, int16_t difference) {
|
STATIC int16_t convert_time_to_rate(uint32_t sample_rate, mp_obj_t time_in, int16_t difference) {
|
||||||
mp_float_t time = mp_obj_get_float(time_in);
|
mp_float_t time = mp_obj_get_float(time_in);
|
||||||
int num_samples = (int)MICROPY_FLOAT_C_FUN(round)(time * sample_rate);
|
int num_samples = (int)MICROPY_FLOAT_C_FUN(round)(time * sample_rate);
|
||||||
if (num_samples == 0) {
|
if (num_samples == 0) {
|
||||||
return 0;
|
return 32767;
|
||||||
}
|
}
|
||||||
int16_t result = MIN(32767, MAX(1, abs(difference * SYNTHIO_MAX_DUR) / num_samples));
|
int16_t result = MIN(32767, MAX(1, abs(difference * SYNTHIO_MAX_DUR) / num_samples));
|
||||||
return (difference < 0) ? -result : result;
|
return (difference < 0) ? -result : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
STATIC void synthio_envelope_definition_set(synthio_envelope_definition_t *envelope, mp_obj_t obj, uint32_t sample_rate) {
|
void synthio_envelope_definition_set(synthio_envelope_definition_t *envelope, mp_obj_t obj, uint32_t sample_rate) {
|
||||||
if (obj == mp_const_none) {
|
if (obj == mp_const_none) {
|
||||||
envelope->attack_level = 32767;
|
envelope->attack_level = 32767;
|
||||||
envelope->sustain_level = 32767;
|
envelope->sustain_level = 32767;
|
||||||
@ -62,7 +79,7 @@ STATIC void synthio_envelope_definition_set(synthio_envelope_definition_t *envel
|
|||||||
mp_obj_tuple_get(obj, &len, &fields);
|
mp_obj_tuple_get(obj, &len, &fields);
|
||||||
|
|
||||||
envelope->attack_level = (int)(32767 * mp_obj_get_float(fields[3]));
|
envelope->attack_level = (int)(32767 * mp_obj_get_float(fields[3]));
|
||||||
envelope->sustain_level = (int)(32767 * mp_obj_get_float(fields[4]));
|
envelope->sustain_level = (int)(32767 * mp_obj_get_float(fields[4]) * mp_obj_get_float(fields[3]));
|
||||||
|
|
||||||
envelope->attack_step = convert_time_to_rate(
|
envelope->attack_step = convert_time_to_rate(
|
||||||
sample_rate, fields[0], envelope->attack_level);
|
sample_rate, fields[0], envelope->attack_level);
|
||||||
@ -72,7 +89,7 @@ STATIC void synthio_envelope_definition_set(synthio_envelope_definition_t *envel
|
|||||||
|
|
||||||
envelope->release_step = -convert_time_to_rate(
|
envelope->release_step = -convert_time_to_rate(
|
||||||
sample_rate, fields[2],
|
sample_rate, fields[2],
|
||||||
envelope->decay_step
|
envelope->sustain_level
|
||||||
? envelope->sustain_level
|
? envelope->sustain_level
|
||||||
: envelope->attack_level);
|
: envelope->attack_level);
|
||||||
}
|
}
|
||||||
@ -87,34 +104,19 @@ STATIC void synthio_envelope_state_step(synthio_envelope_state_t *state, synthio
|
|||||||
case SYNTHIO_ENVELOPE_STATE_SUSTAIN:
|
case SYNTHIO_ENVELOPE_STATE_SUSTAIN:
|
||||||
break;
|
break;
|
||||||
case SYNTHIO_ENVELOPE_STATE_ATTACK:
|
case SYNTHIO_ENVELOPE_STATE_ATTACK:
|
||||||
if (def->attack_step != 0) {
|
state->level = MIN(state->level + def->attack_step, def->attack_level);
|
||||||
state->level = MIN(state->level + def->attack_step, def->attack_level);
|
if (state->level == def->attack_level) {
|
||||||
if (state->level == def->attack_level) {
|
state->state = SYNTHIO_ENVELOPE_STATE_DECAY;
|
||||||
state->state = SYNTHIO_ENVELOPE_STATE_DECAY;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
state->state = SYNTHIO_ENVELOPE_STATE_DECAY;
|
|
||||||
MP_FALLTHROUGH;
|
|
||||||
case SYNTHIO_ENVELOPE_STATE_DECAY:
|
|
||||||
if (def->decay_step != 0) {
|
|
||||||
state->level = MAX(state->level + def->decay_step, def->sustain_level);
|
|
||||||
assert(state->level >= 0);
|
|
||||||
if (state->level == def->sustain_level) {
|
|
||||||
state->state = SYNTHIO_ENVELOPE_STATE_SUSTAIN;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
state->state = SYNTHIO_ENVELOPE_STATE_RELEASE;
|
|
||||||
MP_FALLTHROUGH;
|
|
||||||
case SYNTHIO_ENVELOPE_STATE_RELEASE:
|
|
||||||
if (def->release_step != 0) {
|
|
||||||
int delta = def->release_step;
|
|
||||||
state->level = MAX(state->level + delta, 0);
|
|
||||||
} else {
|
|
||||||
state->level = 0;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case SYNTHIO_ENVELOPE_STATE_DECAY:
|
||||||
|
state->level = MAX(state->level + def->decay_step, def->sustain_level);
|
||||||
|
if (state->level == def->sustain_level) {
|
||||||
|
state->state = SYNTHIO_ENVELOPE_STATE_SUSTAIN;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SYNTHIO_ENVELOPE_STATE_RELEASE:
|
||||||
|
state->level = MAX(state->level + def->release_step, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,18 +133,215 @@ STATIC void synthio_envelope_state_release(synthio_envelope_state_t *state, synt
|
|||||||
state->state = SYNTHIO_ENVELOPE_STATE_RELEASE;
|
state->state = SYNTHIO_ENVELOPE_STATE_RELEASE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
STATIC synthio_envelope_definition_t *synthio_synth_get_note_envelope(synthio_synth_t *synth, mp_obj_t note_obj) {
|
||||||
STATIC uint32_t synthio_synth_sum_envelope(synthio_synth_t *synth) {
|
synthio_envelope_definition_t *def = &synth->global_envelope_definition;
|
||||||
uint32_t result = 0;
|
if (!mp_obj_is_small_int(note_obj)) {
|
||||||
for (int chan = 0; chan < CIRCUITPY_SYNTHIO_MAX_CHANNELS; chan++) {
|
synthio_note_obj_t *note = MP_OBJ_TO_PTR(note_obj);
|
||||||
if (synth->span.note[chan] != SYNTHIO_SILENCE) {
|
if (note->envelope_obj != mp_const_none) {
|
||||||
result += synth->envelope_state[chan].level;
|
def = ¬e->envelope_def;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define RANGE_LOW (-28000)
|
||||||
|
#define RANGE_HIGH (28000)
|
||||||
|
#define RANGE_SHIFT (16)
|
||||||
|
#define RANGE_SCALE (0xfffffff / (32768 * CIRCUITPY_SYNTHIO_MAX_CHANNELS - RANGE_HIGH))
|
||||||
|
|
||||||
|
// dynamic range compression via a downward compressor with hard knee
|
||||||
|
//
|
||||||
|
// When the output value is within the range +-28000 (about 85% of full scale),
|
||||||
|
// it is unchanged. Otherwise, it undergoes a gain reduction so that the
|
||||||
|
// largest possible values, (+32768,-32767) * CIRCUITPY_SYNTHIO_MAX_CHANNELS,
|
||||||
|
// still fit within the output range
|
||||||
|
//
|
||||||
|
// This produces a much louder overall volume with multiple voices, without
|
||||||
|
// much additional processing.
|
||||||
|
//
|
||||||
|
// https://en.wikipedia.org/wiki/Dynamic_range_compression
|
||||||
|
STATIC
|
||||||
|
int16_t mix_down_sample(int32_t sample) {
|
||||||
|
if (sample < RANGE_LOW) {
|
||||||
|
sample = (((sample - RANGE_LOW) * RANGE_SCALE) >> RANGE_SHIFT) + RANGE_LOW;
|
||||||
|
} else if (sample > RANGE_HIGH) {
|
||||||
|
sample = (((sample - RANGE_HIGH) * RANGE_SCALE) >> RANGE_SHIFT) + RANGE_HIGH;
|
||||||
|
}
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void synth_note_into_buffer(synthio_synth_t *synth, int chan, int32_t *out_buffer32, int16_t dur) {
|
||||||
|
mp_obj_t note_obj = synth->span.note_obj[chan];
|
||||||
|
|
||||||
|
if (note_obj == SYNTHIO_SILENCE) {
|
||||||
|
synth->accum[chan] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (synth->envelope_state[chan].level == 0) {
|
||||||
|
// note is truly finished, but we only just noticed
|
||||||
|
synth->span.note_obj[chan] = SYNTHIO_SILENCE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t sample_rate = synth->sample_rate;
|
||||||
|
|
||||||
|
// adjust loudness by envelope
|
||||||
|
uint16_t loudness[2] = {synth->envelope_state[chan].level,synth->envelope_state[chan].level};
|
||||||
|
|
||||||
|
uint32_t dds_rate;
|
||||||
|
const int16_t *waveform = synth->waveform_bufinfo.buf;
|
||||||
|
uint32_t waveform_length = synth->waveform_bufinfo.len / sizeof(int16_t);
|
||||||
|
|
||||||
|
uint32_t ring_dds_rate = 0;
|
||||||
|
const int16_t *ring_waveform = NULL;
|
||||||
|
uint32_t ring_waveform_length = 0;
|
||||||
|
|
||||||
|
if (mp_obj_is_small_int(note_obj)) {
|
||||||
|
uint8_t note = mp_obj_get_int(note_obj);
|
||||||
|
uint8_t octave = note / 12;
|
||||||
|
uint16_t base_freq = notes[note % 12];
|
||||||
|
// rate = base_freq * waveform_length
|
||||||
|
// den = sample_rate * 2 ^ (10 - octave)
|
||||||
|
// den = sample_rate * 2 ^ 10 / 2^octave
|
||||||
|
// dds_rate = 2^SHIFT * rate / den
|
||||||
|
// dds_rate = 2^(SHIFT-10+octave) * base_freq * waveform_length / sample_rate
|
||||||
|
dds_rate = (sample_rate / 2 + ((uint64_t)(base_freq * waveform_length) << (SYNTHIO_FREQUENCY_SHIFT - 10 + octave))) / sample_rate;
|
||||||
|
} else {
|
||||||
|
synthio_note_obj_t *note = MP_OBJ_TO_PTR(note_obj);
|
||||||
|
int32_t frequency_scaled = synthio_note_step(note, sample_rate, dur, loudness);
|
||||||
|
if (note->waveform_buf.buf) {
|
||||||
|
waveform = note->waveform_buf.buf;
|
||||||
|
waveform_length = note->waveform_buf.len / sizeof(int16_t);
|
||||||
|
}
|
||||||
|
dds_rate = synthio_frequency_convert_scaled_to_dds((uint64_t)frequency_scaled * waveform_length, sample_rate);
|
||||||
|
if (note->ring_frequency_scaled != 0 && note->ring_waveform_buf.buf) {
|
||||||
|
ring_waveform = note->ring_waveform_buf.buf;
|
||||||
|
ring_waveform_length = note->ring_waveform_buf.len / sizeof(int16_t);
|
||||||
|
ring_dds_rate = synthio_frequency_convert_scaled_to_dds((uint64_t)note->ring_frequency_scaled * ring_waveform_length, sample_rate);
|
||||||
|
uint32_t lim = ring_waveform_length << SYNTHIO_FREQUENCY_SHIFT;
|
||||||
|
if (ring_dds_rate > lim / sizeof(int16_t)) {
|
||||||
|
ring_dds_rate = 0; // can't ring at that frequency
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int synth_chan = synth->channel_count;
|
||||||
|
if (ring_dds_rate) {
|
||||||
|
uint32_t lim = waveform_length << SYNTHIO_FREQUENCY_SHIFT;
|
||||||
|
uint32_t accum = synth->accum[chan];
|
||||||
|
|
||||||
|
if (dds_rate > lim / 2) {
|
||||||
|
// beyond nyquist, can't play note
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// can happen if note waveform gets set mid-note, but the expensive modulo is usually avoided
|
||||||
|
if (accum > lim) {
|
||||||
|
accum %= lim;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t ring_buffer[dur];
|
||||||
|
// first, fill with waveform
|
||||||
|
for (uint16_t i = 0; i < dur; i++) {
|
||||||
|
accum += dds_rate;
|
||||||
|
// because dds_rate is low enough, the subtraction is guaranteed to go back into range, no expensive modulo needed
|
||||||
|
if (accum > lim) {
|
||||||
|
accum -= lim;
|
||||||
|
}
|
||||||
|
int16_t idx = accum >> SYNTHIO_FREQUENCY_SHIFT;
|
||||||
|
ring_buffer[i] = waveform[idx];
|
||||||
|
}
|
||||||
|
synth->accum[chan] = accum;
|
||||||
|
|
||||||
|
// now modulate by ring and accumulate
|
||||||
|
accum = synth->ring_accum[chan];
|
||||||
|
lim = ring_waveform_length << SYNTHIO_FREQUENCY_SHIFT;
|
||||||
|
|
||||||
|
// can happen if note waveform gets set mid-note, but the expensive modulo is usually avoided
|
||||||
|
if (accum > lim) {
|
||||||
|
accum %= lim;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint16_t i = 0, j = 0; i < dur; i++) {
|
||||||
|
accum += ring_dds_rate;
|
||||||
|
// because dds_rate is low enough, the subtraction is guaranteed to go back into range, no expensive modulo needed
|
||||||
|
if (accum > lim) {
|
||||||
|
accum -= lim;
|
||||||
|
}
|
||||||
|
int16_t idx = accum >> SYNTHIO_FREQUENCY_SHIFT;
|
||||||
|
int16_t wi = (ring_waveform[idx] * ring_buffer[i]) / 32768;
|
||||||
|
for (int c = 0; c < synth_chan; c++) {
|
||||||
|
out_buffer32[j] += (wi * loudness[c]) / 32768;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
synth->ring_accum[chan] = accum;
|
||||||
|
} else {
|
||||||
|
uint32_t lim = waveform_length << SYNTHIO_FREQUENCY_SHIFT;
|
||||||
|
uint32_t accum = synth->accum[chan];
|
||||||
|
|
||||||
|
if (dds_rate > lim / 2) {
|
||||||
|
// beyond nyquist, can't play note
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// can happen if note waveform gets set mid-note, but the expensive modulo is usually avoided
|
||||||
|
if (accum > lim) {
|
||||||
|
accum %= lim;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint16_t i = 0, j = 0; i < dur; i++) {
|
||||||
|
accum += dds_rate;
|
||||||
|
// because dds_rate is low enough, the subtraction is guaranteed to go back into range, no expensive modulo needed
|
||||||
|
if (accum > lim) {
|
||||||
|
accum -= lim;
|
||||||
|
}
|
||||||
|
int16_t idx = accum >> SYNTHIO_FREQUENCY_SHIFT;
|
||||||
|
int16_t wi = waveform[idx];
|
||||||
|
for (int c = 0; c < synth_chan; c++) {
|
||||||
|
out_buffer32[j] += (wi * loudness[c]) / 65536;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
synth->accum[chan] = accum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC void run_fir(synthio_synth_t *synth, int32_t *out_buffer32, uint16_t dur) {
|
||||||
|
int16_t *coeff = (int16_t *)synth->filter_bufinfo.buf;
|
||||||
|
size_t fir_len = synth->filter_bufinfo.len / sizeof(int16_t);
|
||||||
|
int32_t *in_buf = synth->filter_buffer;
|
||||||
|
|
||||||
|
|
||||||
|
int synth_chan = synth->channel_count;
|
||||||
|
// FIR and copy values to output buffer
|
||||||
|
for (int16_t i = 0; i < dur * synth_chan; i++) {
|
||||||
|
int32_t acc = 0;
|
||||||
|
for (size_t j = 0; j < fir_len; j++) {
|
||||||
|
// shift 5 here is good for up to 32 filtered voices, else might wrap
|
||||||
|
acc = acc + (in_buf[j * synth_chan] * (coeff[j] >> 5));
|
||||||
|
}
|
||||||
|
*out_buffer32++ = acc >> 10;
|
||||||
|
in_buf++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move values down so that they get filtered next time
|
||||||
|
memmove(synth->filter_buffer, &synth->filter_buffer[dur * synth_chan], fir_len * sizeof(int32_t) * synth_chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC bool synthio_synth_get_note_filtered(mp_obj_t note_obj) {
|
||||||
|
if (note_obj == mp_const_none) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!mp_obj_is_small_int(note_obj)) {
|
||||||
|
synthio_note_obj_t *note = MP_OBJ_TO_PTR(note_obj);
|
||||||
|
return note->filter;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void synthio_synth_synthesize(synthio_synth_t *synth, uint8_t **bufptr, uint32_t *buffer_length, uint8_t channel) {
|
void synthio_synth_synthesize(synthio_synth_t *synth, uint8_t **bufptr, uint32_t *buffer_length, uint8_t channel) {
|
||||||
|
|
||||||
if (channel == synth->other_channel) {
|
if (channel == synth->other_channel) {
|
||||||
@ -154,60 +353,56 @@ void synthio_synth_synthesize(synthio_synth_t *synth, uint8_t **bufptr, uint32_t
|
|||||||
synth->buffer_index = !synth->buffer_index;
|
synth->buffer_index = !synth->buffer_index;
|
||||||
synth->other_channel = 1 - channel;
|
synth->other_channel = 1 - channel;
|
||||||
synth->other_buffer_index = synth->buffer_index;
|
synth->other_buffer_index = synth->buffer_index;
|
||||||
int16_t *out_buffer = (int16_t *)(void *)synth->buffers[synth->buffer_index];
|
|
||||||
|
|
||||||
uint16_t dur = MIN(SYNTHIO_MAX_DUR, synth->span.dur);
|
uint16_t dur = MIN(SYNTHIO_MAX_DUR, synth->span.dur);
|
||||||
synth->span.dur -= dur;
|
synth->span.dur -= dur;
|
||||||
memset(out_buffer, 0, synth->buffer_length);
|
|
||||||
|
|
||||||
int32_t sample_rate = synth->sample_rate;
|
int32_t out_buffer32[dur * synth->channel_count];
|
||||||
uint32_t total_envelope = synthio_synth_sum_envelope(synth);
|
|
||||||
const int16_t *waveform = synth->waveform;
|
if (synth->filter_buffer) {
|
||||||
uint32_t waveform_length = synth->waveform_length;
|
int32_t *filter_start = &synth->filter_buffer[synth->filter_bufinfo.len * synth->channel_count / sizeof(int16_t)];
|
||||||
if (total_envelope > 0) {
|
memset(filter_start, 0, dur * synth->channel_count * sizeof(int32_t));
|
||||||
uint16_t ovl_loudness = 0x7fffffff / MAX(0x8000, total_envelope);
|
|
||||||
for (int chan = 0; chan < CIRCUITPY_SYNTHIO_MAX_CHANNELS; chan++) {
|
for (int chan = 0; chan < CIRCUITPY_SYNTHIO_MAX_CHANNELS; chan++) {
|
||||||
if (synth->span.note[chan] == SYNTHIO_SILENCE) {
|
mp_obj_t note_obj = synth->span.note_obj[chan];
|
||||||
synth->accum[chan] = 0;
|
if (!synthio_synth_get_note_filtered(note_obj)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// adjust loudness by envelope
|
synth_note_into_buffer(synth, chan, filter_start, dur);
|
||||||
uint16_t loudness = (ovl_loudness * synth->envelope_state[chan].level) >> 16;
|
|
||||||
|
|
||||||
if (synth->envelope_state[chan].level == 0) {
|
|
||||||
// note is truly finished
|
|
||||||
synth->span.note[chan] = SYNTHIO_SILENCE;
|
|
||||||
}
|
|
||||||
uint8_t octave = synth->span.note[chan] / 12;
|
|
||||||
uint16_t base_freq = notes[synth->span.note[chan] % 12];
|
|
||||||
uint32_t accum = synth->accum[chan];
|
|
||||||
#define SHIFT (16)
|
|
||||||
// rate = base_freq * waveform_length
|
|
||||||
// den = sample_rate * 2 ^ (10 - octave)
|
|
||||||
// den = sample_rate * 2 ^ 10 / 2^octave
|
|
||||||
// dds_rate = 2^SHIFT * rate / den
|
|
||||||
// dds_rate = 2^(SHIFT-10+octave) * base_freq * waveform_length / sample_rate
|
|
||||||
uint32_t dds_rate = (sample_rate / 2 + ((uint64_t)(base_freq * waveform_length) << (SHIFT - 10 + octave))) / sample_rate;
|
|
||||||
|
|
||||||
for (uint16_t i = 0; i < dur; i++) {
|
|
||||||
accum += dds_rate;
|
|
||||||
if (accum > waveform_length << SHIFT) {
|
|
||||||
accum -= waveform_length << SHIFT;
|
|
||||||
}
|
|
||||||
int16_t idx = accum >> SHIFT;
|
|
||||||
out_buffer[i] += (waveform[idx] * loudness) / 65536;
|
|
||||||
}
|
|
||||||
synth->accum[chan] = accum;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
run_fir(synth, out_buffer32, dur);
|
||||||
|
} else {
|
||||||
|
memset(out_buffer32, 0, sizeof(out_buffer32));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int chan = 0; chan < CIRCUITPY_SYNTHIO_MAX_CHANNELS; chan++) {
|
||||||
|
mp_obj_t note_obj = synth->span.note_obj[chan];
|
||||||
|
if (synth->filter_buffer && synthio_synth_get_note_filtered(note_obj)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
synth_note_into_buffer(synth, chan, out_buffer32, dur);
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t *out_buffer16 = (int16_t *)(void *)synth->buffers[synth->buffer_index];
|
||||||
|
|
||||||
|
// mix down audio
|
||||||
|
for (size_t i = 0; i < MP_ARRAY_SIZE(out_buffer32); i++) {
|
||||||
|
int32_t sample = out_buffer32[i];
|
||||||
|
out_buffer16[i] = mix_down_sample(sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
// advance envelope states
|
// advance envelope states
|
||||||
for (int chan = 0; chan < CIRCUITPY_SYNTHIO_MAX_CHANNELS; chan++) {
|
for (int chan = 0; chan < CIRCUITPY_SYNTHIO_MAX_CHANNELS; chan++) {
|
||||||
synthio_envelope_state_step(&synth->envelope_state[chan], &synth->envelope_definition, dur);
|
mp_obj_t note_obj = synth->span.note_obj[chan];
|
||||||
|
if (note_obj == SYNTHIO_SILENCE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
synthio_envelope_state_step(&synth->envelope_state[chan], synthio_synth_get_note_envelope(synth, note_obj), dur);
|
||||||
}
|
}
|
||||||
|
|
||||||
*buffer_length = synth->last_buffer_length = dur * SYNTHIO_BYTES_PER_SAMPLE;
|
*buffer_length = synth->last_buffer_length = dur * SYNTHIO_BYTES_PER_SAMPLE * synth->channel_count;
|
||||||
*bufptr = (uint8_t *)out_buffer;
|
*bufptr = (uint8_t *)out_buffer16;
|
||||||
}
|
}
|
||||||
|
|
||||||
void synthio_synth_reset_buffer(synthio_synth_t *synth, bool single_channel_output, uint8_t channel) {
|
void synthio_synth_reset_buffer(synthio_synth_t *synth, bool single_channel_output, uint8_t channel) {
|
||||||
@ -222,14 +417,13 @@ bool synthio_synth_deinited(synthio_synth_t *synth) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void synthio_synth_deinit(synthio_synth_t *synth) {
|
void synthio_synth_deinit(synthio_synth_t *synth) {
|
||||||
m_del(uint8_t, synth->buffers[0], synth->buffer_length);
|
synth->filter_buffer = NULL;
|
||||||
m_del(uint8_t, synth->buffers[1], synth->buffer_length);
|
|
||||||
synth->buffers[0] = NULL;
|
synth->buffers[0] = NULL;
|
||||||
synth->buffers[1] = NULL;
|
synth->buffers[1] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void synthio_synth_envelope_set(synthio_synth_t *synth, mp_obj_t envelope_obj) {
|
void synthio_synth_envelope_set(synthio_synth_t *synth, mp_obj_t envelope_obj) {
|
||||||
synthio_envelope_definition_set(&synth->envelope_definition, envelope_obj, synth->sample_rate);
|
synthio_envelope_definition_set(&synth->global_envelope_definition, envelope_obj, synth->sample_rate);
|
||||||
synth->envelope_obj = envelope_obj;
|
synth->envelope_obj = envelope_obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,18 +431,25 @@ mp_obj_t synthio_synth_envelope_get(synthio_synth_t *synth) {
|
|||||||
return synth->envelope_obj;
|
return synth->envelope_obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
void synthio_synth_init(synthio_synth_t *synth, uint32_t sample_rate, const int16_t *waveform, uint16_t waveform_length, mp_obj_t envelope_obj) {
|
void synthio_synth_init(synthio_synth_t *synth, uint32_t sample_rate, int channel_count, mp_obj_t waveform_obj, mp_obj_t filter_obj, mp_obj_t envelope_obj) {
|
||||||
synth->buffer_length = SYNTHIO_MAX_DUR * SYNTHIO_BYTES_PER_SAMPLE;
|
synthio_synth_parse_waveform(&synth->waveform_bufinfo, waveform_obj);
|
||||||
|
synthio_synth_parse_filter(&synth->filter_bufinfo, filter_obj);
|
||||||
|
mp_arg_validate_int_range(channel_count, 1, 2, MP_QSTR_channel_count);
|
||||||
|
synth->buffer_length = SYNTHIO_MAX_DUR * SYNTHIO_BYTES_PER_SAMPLE * channel_count;
|
||||||
synth->buffers[0] = m_malloc(synth->buffer_length, false);
|
synth->buffers[0] = m_malloc(synth->buffer_length, false);
|
||||||
synth->buffers[1] = m_malloc(synth->buffer_length, false);
|
synth->buffers[1] = m_malloc(synth->buffer_length, false);
|
||||||
|
if (synth->filter_bufinfo.len) {
|
||||||
|
synth->filter_buffer_length = (synth->filter_bufinfo.len / 2 + SYNTHIO_MAX_DUR) * channel_count * sizeof(int32_t);
|
||||||
|
synth->filter_buffer = m_malloc(synth->filter_buffer_length, false);
|
||||||
|
}
|
||||||
|
synth->channel_count = channel_count;
|
||||||
synth->other_channel = -1;
|
synth->other_channel = -1;
|
||||||
synth->waveform = waveform;
|
synth->waveform_obj = waveform_obj;
|
||||||
synth->waveform_length = waveform_length;
|
|
||||||
synth->sample_rate = sample_rate;
|
synth->sample_rate = sample_rate;
|
||||||
synthio_synth_envelope_set(synth, envelope_obj);
|
synthio_synth_envelope_set(synth, envelope_obj);
|
||||||
|
|
||||||
for (size_t i = 0; i < CIRCUITPY_SYNTHIO_MAX_CHANNELS; i++) {
|
for (size_t i = 0; i < CIRCUITPY_SYNTHIO_MAX_CHANNELS; i++) {
|
||||||
synth->span.note[i] = SYNTHIO_SILENCE;
|
synth->span.note_obj[i] = SYNTHIO_SILENCE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,61 +458,132 @@ void synthio_synth_get_buffer_structure(synthio_synth_t *synth, bool single_chan
|
|||||||
*single_buffer = false;
|
*single_buffer = false;
|
||||||
*samples_signed = true;
|
*samples_signed = true;
|
||||||
*max_buffer_length = synth->buffer_length;
|
*max_buffer_length = synth->buffer_length;
|
||||||
*spacing = 1;
|
if (single_channel_output) {
|
||||||
|
*spacing = synth->channel_count;
|
||||||
|
} else {
|
||||||
|
*spacing = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool parse_common(mp_buffer_info_t *bufinfo, mp_obj_t o, int16_t what) {
|
STATIC void parse_common(mp_buffer_info_t *bufinfo, mp_obj_t o, int16_t what, mp_int_t max_len) {
|
||||||
if (o != mp_const_none) {
|
if (o != mp_const_none) {
|
||||||
mp_get_buffer_raise(o, bufinfo, MP_BUFFER_READ);
|
mp_get_buffer_raise(o, bufinfo, MP_BUFFER_READ);
|
||||||
if (bufinfo->typecode != 'h') {
|
if (bufinfo->typecode != 'h') {
|
||||||
mp_raise_ValueError_varg(translate("%q must be array of type 'h'"), what);
|
mp_raise_ValueError_varg(translate("%q must be array of type 'h'"), what);
|
||||||
}
|
}
|
||||||
mp_arg_validate_length_range(bufinfo->len / 2, 2, 1024, what);
|
mp_arg_validate_length_range(bufinfo->len / sizeof(int16_t), 2, max_len, what);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void synthio_synth_parse_waveform(mp_buffer_info_t *bufinfo_waveform, mp_obj_t waveform_obj) {
|
void synthio_synth_parse_waveform(mp_buffer_info_t *bufinfo_waveform, mp_obj_t waveform_obj) {
|
||||||
*bufinfo_waveform = ((mp_buffer_info_t) { .buf = (void *)square_wave, .len = 4 });
|
*bufinfo_waveform = ((mp_buffer_info_t) { .buf = (void *)square_wave, .len = 4 });
|
||||||
parse_common(bufinfo_waveform, waveform_obj, MP_QSTR_waveform);
|
parse_common(bufinfo_waveform, waveform_obj, MP_QSTR_waveform, 16384);
|
||||||
}
|
}
|
||||||
|
|
||||||
STATIC int find_channel_with_note(synthio_synth_t *synth, uint8_t note) {
|
void synthio_synth_parse_filter(mp_buffer_info_t *bufinfo_filter, mp_obj_t filter_obj) {
|
||||||
|
*bufinfo_filter = ((mp_buffer_info_t) { .buf = NULL, .len = 0 });
|
||||||
|
parse_common(bufinfo_filter, filter_obj, MP_QSTR_filter, 128);
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC int find_channel_with_note(synthio_synth_t *synth, mp_obj_t note) {
|
||||||
for (int i = 0; i < CIRCUITPY_SYNTHIO_MAX_CHANNELS; i++) {
|
for (int i = 0; i < CIRCUITPY_SYNTHIO_MAX_CHANNELS; i++) {
|
||||||
if (synth->span.note[i] == note) {
|
if (synth->span.note_obj[i] == note) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
int result = -1;
|
||||||
if (note == SYNTHIO_SILENCE) {
|
if (note == SYNTHIO_SILENCE) {
|
||||||
// we need a victim note that is releasing. simple algorithm: lowest numbered slot
|
// replace the releasing note with lowest volume level
|
||||||
for (int i = 0; i < CIRCUITPY_SYNTHIO_MAX_CHANNELS; i++) {
|
int level = 32768;
|
||||||
if (SYNTHIO_VOICE_IS_RELEASING(synth, i)) {
|
for (int chan = 0; chan < CIRCUITPY_SYNTHIO_MAX_CHANNELS; chan++) {
|
||||||
return i;
|
if (!SYNTHIO_NOTE_IS_PLAYING(synth, chan)) {
|
||||||
|
synthio_envelope_state_t *state = &synth->envelope_state[chan];
|
||||||
|
if (state->level < level) {
|
||||||
|
result = chan;
|
||||||
|
level = state->level;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool synthio_span_change_note(synthio_synth_t *synth, uint8_t old_note, uint8_t new_note) {
|
bool synthio_span_change_note(synthio_synth_t *synth, mp_obj_t old_note, mp_obj_t new_note) {
|
||||||
int channel;
|
int channel;
|
||||||
if (new_note != SYNTHIO_SILENCE && (channel = find_channel_with_note(synth, new_note)) != -1) {
|
if (new_note != SYNTHIO_SILENCE && (channel = find_channel_with_note(synth, new_note)) != -1) {
|
||||||
// note already playing, re-strike
|
// note already playing, re-enter attack phase
|
||||||
synthio_envelope_state_init(&synth->envelope_state[channel], &synth->envelope_definition);
|
synth->envelope_state[channel].state = SYNTHIO_ENVELOPE_STATE_ATTACK;
|
||||||
synth->accum[channel] = 0;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
channel = find_channel_with_note(synth, old_note);
|
channel = find_channel_with_note(synth, old_note);
|
||||||
if (channel != -1) {
|
if (channel != -1) {
|
||||||
if (new_note == SYNTHIO_SILENCE) {
|
if (new_note == SYNTHIO_SILENCE) {
|
||||||
synthio_envelope_state_release(&synth->envelope_state[channel], &synth->envelope_definition);
|
synthio_envelope_state_release(&synth->envelope_state[channel], synthio_synth_get_note_envelope(synth, old_note));
|
||||||
} else {
|
} else {
|
||||||
synth->span.note[channel] = new_note;
|
synth->span.note_obj[channel] = new_note;
|
||||||
synthio_envelope_state_init(&synth->envelope_state[channel], &synth->envelope_definition);
|
synthio_envelope_state_init(&synth->envelope_state[channel], synthio_synth_get_note_envelope(synth, new_note));
|
||||||
synth->accum[channel] = 0;
|
synth->accum[channel] = 0;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t synthio_frequency_convert_float_to_scaled(mp_float_t val) {
|
||||||
|
return round_float_to_int64(val * (1 << SYNTHIO_FREQUENCY_SHIFT));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t synthio_frequency_convert_float_to_dds(mp_float_t frequency_hz, int32_t sample_rate) {
|
||||||
|
return synthio_frequency_convert_scaled_to_dds(synthio_frequency_convert_float_to_scaled(frequency_hz), sample_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t synthio_frequency_convert_scaled_to_dds(uint64_t frequency_scaled, int32_t sample_rate) {
|
||||||
|
return (sample_rate / 2 + frequency_scaled) / sample_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void synthio_lfo_set(synthio_lfo_state_t *state, const synthio_lfo_descr_t *descr, uint32_t sample_rate) {
|
||||||
|
state->amplitude_scaled = round_float_to_int(descr->amplitude * 32768);
|
||||||
|
state->dds = synthio_frequency_convert_float_to_dds(descr->frequency * 65536, sample_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC int synthio_lfo_step_common(synthio_lfo_state_t *state, uint16_t dur) {
|
||||||
|
uint32_t phase = state->phase;
|
||||||
|
uint16_t whole_phase = phase >> 16;
|
||||||
|
|
||||||
|
// advance the phase accumulator
|
||||||
|
state->phase = phase + state->dds * dur;
|
||||||
|
|
||||||
|
return whole_phase;
|
||||||
|
}
|
||||||
|
STATIC int synthio_lfo_sweep_common(synthio_lfo_state_t *state, uint16_t dur) {
|
||||||
|
uint32_t old_phase = state->phase;
|
||||||
|
uint16_t whole_phase = synthio_lfo_step_common(state, dur);
|
||||||
|
if (state->phase < old_phase) {
|
||||||
|
state->phase = 0xffffffff;
|
||||||
|
}
|
||||||
|
return whole_phase;
|
||||||
|
}
|
||||||
|
|
||||||
|
int synthio_sweep_step(synthio_lfo_state_t *state, uint16_t dur) {
|
||||||
|
uint16_t whole_phase = synthio_lfo_sweep_common(state, dur);
|
||||||
|
return (state->amplitude_scaled * whole_phase) / 65536 + state->offset_scaled;
|
||||||
|
}
|
||||||
|
|
||||||
|
int synthio_sweep_in_step(synthio_lfo_state_t *state, uint16_t dur) {
|
||||||
|
uint16_t whole_phase = 65535 - synthio_lfo_sweep_common(state, dur);
|
||||||
|
return (state->amplitude_scaled * whole_phase) / 65536 + state->offset_scaled;
|
||||||
|
}
|
||||||
|
|
||||||
|
int synthio_lfo_step(synthio_lfo_state_t *state, uint16_t dur) {
|
||||||
|
uint16_t whole_phase = synthio_lfo_step_common(state, dur);
|
||||||
|
// create a triangle wave, it's quick and easy
|
||||||
|
int v;
|
||||||
|
if (whole_phase < 16384) { // ramp from 0 to amplitude
|
||||||
|
v = (state->amplitude_scaled * whole_phase);
|
||||||
|
} else if (whole_phase < 49152) { // ramp from +amplitude to -amplitude
|
||||||
|
v = (state->amplitude_scaled * (32768 - whole_phase));
|
||||||
|
} else { // from -amplitude to 0
|
||||||
|
v = (state->amplitude_scaled * (whole_phase - 65536));
|
||||||
|
}
|
||||||
|
return v / 16384 + state->offset_scaled;
|
||||||
|
}
|
||||||
|
@ -29,14 +29,16 @@
|
|||||||
#define SYNTHIO_BITS_PER_SAMPLE (16)
|
#define SYNTHIO_BITS_PER_SAMPLE (16)
|
||||||
#define SYNTHIO_BYTES_PER_SAMPLE (SYNTHIO_BITS_PER_SAMPLE / 8)
|
#define SYNTHIO_BYTES_PER_SAMPLE (SYNTHIO_BITS_PER_SAMPLE / 8)
|
||||||
#define SYNTHIO_MAX_DUR (256)
|
#define SYNTHIO_MAX_DUR (256)
|
||||||
#define SYNTHIO_SILENCE (0x80)
|
#define SYNTHIO_SILENCE (mp_const_none)
|
||||||
#define SYNTHIO_VOICE_IS_RELEASING(synth, i) (synth->envelope_state[i].state == SYNTHIO_ENVELOPE_STATE_RELEASE)
|
#define SYNTHIO_NOTE_IS_SIMPLE(note) (mp_obj_is_small_int(note))
|
||||||
|
#define SYNTHIO_NOTE_IS_PLAYING(synth, i) ((synth)->envelope_state[(i)].state != SYNTHIO_ENVELOPE_STATE_RELEASE)
|
||||||
|
#define SYNTHIO_FREQUENCY_SHIFT (16)
|
||||||
|
|
||||||
#include "shared-module/audiocore/__init__.h"
|
#include "shared-module/audiocore/__init__.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint16_t dur;
|
uint16_t dur;
|
||||||
uint8_t note[CIRCUITPY_SYNTHIO_MAX_CHANNELS];
|
mp_obj_t note_obj[CIRCUITPY_SYNTHIO_MAX_CHANNELS];
|
||||||
} synthio_midi_span_t;
|
} synthio_midi_span_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -60,30 +62,53 @@ typedef struct {
|
|||||||
|
|
||||||
typedef struct synthio_synth {
|
typedef struct synthio_synth {
|
||||||
uint32_t sample_rate;
|
uint32_t sample_rate;
|
||||||
|
uint32_t total_envelope;
|
||||||
int16_t *buffers[2];
|
int16_t *buffers[2];
|
||||||
const int16_t *waveform;
|
int32_t *filter_buffer;
|
||||||
uint16_t buffer_length;
|
uint8_t channel_count;
|
||||||
|
uint16_t buffer_length, filter_buffer_length;
|
||||||
uint16_t last_buffer_length;
|
uint16_t last_buffer_length;
|
||||||
uint8_t other_channel, buffer_index, other_buffer_index;
|
uint8_t other_channel, buffer_index, other_buffer_index;
|
||||||
uint16_t waveform_length;
|
mp_buffer_info_t waveform_bufinfo, filter_bufinfo;
|
||||||
synthio_envelope_definition_t envelope_definition;
|
synthio_envelope_definition_t global_envelope_definition;
|
||||||
mp_obj_t envelope_obj;
|
mp_obj_t waveform_obj, filter_obj, envelope_obj;
|
||||||
synthio_midi_span_t span;
|
synthio_midi_span_t span;
|
||||||
uint32_t accum[CIRCUITPY_SYNTHIO_MAX_CHANNELS];
|
uint32_t accum[CIRCUITPY_SYNTHIO_MAX_CHANNELS];
|
||||||
|
uint32_t ring_accum[CIRCUITPY_SYNTHIO_MAX_CHANNELS];
|
||||||
synthio_envelope_state_t envelope_state[CIRCUITPY_SYNTHIO_MAX_CHANNELS];
|
synthio_envelope_state_t envelope_state[CIRCUITPY_SYNTHIO_MAX_CHANNELS];
|
||||||
} synthio_synth_t;
|
} synthio_synth_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
mp_float_t amplitude, frequency;
|
||||||
|
} synthio_lfo_descr_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int32_t amplitude_scaled;
|
||||||
|
uint32_t offset_scaled, dds, phase;
|
||||||
|
} synthio_lfo_state_t;
|
||||||
|
|
||||||
|
|
||||||
void synthio_synth_synthesize(synthio_synth_t *synth, uint8_t **buffer, uint32_t *buffer_length, uint8_t channel);
|
void synthio_synth_synthesize(synthio_synth_t *synth, uint8_t **buffer, uint32_t *buffer_length, uint8_t channel);
|
||||||
void synthio_synth_deinit(synthio_synth_t *synth);
|
void synthio_synth_deinit(synthio_synth_t *synth);
|
||||||
bool synthio_synth_deinited(synthio_synth_t *synth);
|
bool synthio_synth_deinited(synthio_synth_t *synth);
|
||||||
void synthio_synth_init(synthio_synth_t *synth, uint32_t sample_rate, const int16_t *waveform, uint16_t waveform_length,
|
void synthio_synth_init(synthio_synth_t *synth, uint32_t sample_rate, int channel_count, mp_obj_t waveform_obj, mp_obj_t filter_obj, mp_obj_t envelope);
|
||||||
mp_obj_t envelope);
|
|
||||||
void synthio_synth_get_buffer_structure(synthio_synth_t *synth, bool single_channel_output,
|
void synthio_synth_get_buffer_structure(synthio_synth_t *synth, bool single_channel_output,
|
||||||
bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing);
|
bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing);
|
||||||
void synthio_synth_reset_buffer(synthio_synth_t *synth, bool single_channel_output, uint8_t channel);
|
void synthio_synth_reset_buffer(synthio_synth_t *synth, bool single_channel_output, uint8_t channel);
|
||||||
void synthio_synth_parse_waveform(mp_buffer_info_t *bufinfo_waveform, mp_obj_t waveform_obj);
|
void synthio_synth_parse_waveform(mp_buffer_info_t *bufinfo_waveform, mp_obj_t waveform_obj);
|
||||||
|
void synthio_synth_parse_filter(mp_buffer_info_t *bufinfo_filter, mp_obj_t filter_obj);
|
||||||
void synthio_synth_parse_envelope(uint16_t *envelope_sustain_index, mp_buffer_info_t *bufinfo_envelope, mp_obj_t envelope_obj, mp_obj_t envelope_hold_obj);
|
void synthio_synth_parse_envelope(uint16_t *envelope_sustain_index, mp_buffer_info_t *bufinfo_envelope, mp_obj_t envelope_obj, mp_obj_t envelope_hold_obj);
|
||||||
|
|
||||||
bool synthio_span_change_note(synthio_synth_t *synth, uint8_t old_note, uint8_t new_note);
|
bool synthio_span_change_note(synthio_synth_t *synth, mp_obj_t old_note, mp_obj_t new_note);
|
||||||
|
|
||||||
void synthio_envelope_step(synthio_envelope_definition_t *definition, synthio_envelope_state_t *state, int n_samples);
|
void synthio_envelope_step(synthio_envelope_definition_t *definition, synthio_envelope_state_t *state, int n_samples);
|
||||||
|
void synthio_envelope_definition_set(synthio_envelope_definition_t *envelope, mp_obj_t obj, uint32_t sample_rate);
|
||||||
|
|
||||||
|
uint64_t synthio_frequency_convert_float_to_scaled(mp_float_t frequency_hz);
|
||||||
|
uint32_t synthio_frequency_convert_float_to_dds(mp_float_t frequency_hz, int32_t sample_rate);
|
||||||
|
uint32_t synthio_frequency_convert_scaled_to_dds(uint64_t frequency_scaled, int32_t sample_rate);
|
||||||
|
|
||||||
|
void synthio_lfo_set(synthio_lfo_state_t *state, const synthio_lfo_descr_t *descr, uint32_t sample_rate);
|
||||||
|
int synthio_lfo_step(synthio_lfo_state_t *state, uint16_t dur);
|
||||||
|
int synthio_sweep_step(synthio_lfo_state_t *state, uint16_t dur);
|
||||||
|
int synthio_sweep_in_step(synthio_lfo_state_t *state, uint16_t dur);
|
||||||
|
@ -38,6 +38,8 @@ uint64_t common_hal_time_monotonic_ns(void) {
|
|||||||
uint64_t ticks = port_get_raw_ticks(&subticks);
|
uint64_t ticks = port_get_raw_ticks(&subticks);
|
||||||
// A tick is 976562.5 nanoseconds so multiply it by the base and add half instead of doing float
|
// A tick is 976562.5 nanoseconds so multiply it by the base and add half instead of doing float
|
||||||
// math.
|
// math.
|
||||||
|
// A subtick is 1/32 of a tick.
|
||||||
|
// 30518 is 1e9 / 32768
|
||||||
return 976562 * ticks + ticks / 2 + 30518 * subticks;
|
return 976562 * ticks + ticks / 2 + 30518 * subticks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ static void _clobber_points_list(vectorio_polygon_t *self, mp_obj_t points_tuple
|
|||||||
for (uint16_t i = 0; i < len; ++i) {
|
for (uint16_t i = 0; i < len; ++i) {
|
||||||
size_t tuple_len = 0;
|
size_t tuple_len = 0;
|
||||||
mp_obj_t *tuple_items;
|
mp_obj_t *tuple_items;
|
||||||
|
mp_arg_validate_type(items[i], &mp_type_tuple, MP_QSTR_point);
|
||||||
mp_obj_tuple_get(items[i], &tuple_len, &tuple_items);
|
mp_obj_tuple_get(items[i], &tuple_len, &tuple_items);
|
||||||
|
|
||||||
mp_arg_validate_length(tuple_len, 2, MP_QSTR_point);
|
mp_arg_validate_length(tuple_len, 2, MP_QSTR_point);
|
||||||
|
@ -258,7 +258,7 @@ void supervisor_web_workflow_status(void) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void supervisor_start_web_workflow(void) {
|
bool supervisor_start_web_workflow(void) {
|
||||||
#if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI && CIRCUITPY_OS_GETENV
|
#if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI && CIRCUITPY_OS_GETENV
|
||||||
|
|
||||||
// Skip starting the workflow if we're not starting from power on or reset.
|
// Skip starting the workflow if we're not starting from power on or reset.
|
||||||
@ -268,7 +268,7 @@ void supervisor_start_web_workflow(void) {
|
|||||||
reset_reason != RESET_REASON_DEEP_SLEEP_ALARM &&
|
reset_reason != RESET_REASON_DEEP_SLEEP_ALARM &&
|
||||||
reset_reason != RESET_REASON_UNKNOWN &&
|
reset_reason != RESET_REASON_UNKNOWN &&
|
||||||
reset_reason != RESET_REASON_SOFTWARE) {
|
reset_reason != RESET_REASON_SOFTWARE) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char ssid[33];
|
char ssid[33];
|
||||||
@ -276,7 +276,7 @@ void supervisor_start_web_workflow(void) {
|
|||||||
|
|
||||||
os_getenv_err_t result = common_hal_os_getenv_str("CIRCUITPY_WIFI_SSID", ssid, sizeof(ssid));
|
os_getenv_err_t result = common_hal_os_getenv_str("CIRCUITPY_WIFI_SSID", ssid, sizeof(ssid));
|
||||||
if (result != GETENV_OK) {
|
if (result != GETENV_OK) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = common_hal_os_getenv_str("CIRCUITPY_WIFI_PASSWORD", password, sizeof(password));
|
result = common_hal_os_getenv_str("CIRCUITPY_WIFI_PASSWORD", password, sizeof(password));
|
||||||
@ -284,7 +284,7 @@ void supervisor_start_web_workflow(void) {
|
|||||||
// if password is unspecified, assume an open network
|
// if password is unspecified, assume an open network
|
||||||
password[0] = '\0';
|
password[0] = '\0';
|
||||||
} else if (result != GETENV_OK) {
|
} else if (result != GETENV_OK) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = common_hal_os_getenv_str("CIRCUITPY_WEB_INSTANCE_NAME", web_instance_name, sizeof(web_instance_name));
|
result = common_hal_os_getenv_str("CIRCUITPY_WEB_INSTANCE_NAME", web_instance_name, sizeof(web_instance_name));
|
||||||
@ -309,7 +309,7 @@ void supervisor_start_web_workflow(void) {
|
|||||||
|
|
||||||
if (_wifi_status != WIFI_RADIO_ERROR_NONE) {
|
if (_wifi_status != WIFI_RADIO_ERROR_NONE) {
|
||||||
common_hal_wifi_radio_set_enabled(&common_hal_wifi_radio_obj, false);
|
common_hal_wifi_radio_set_enabled(&common_hal_wifi_radio_obj, false);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// (leaves new_port unchanged on any failure)
|
// (leaves new_port unchanged on any failure)
|
||||||
@ -363,6 +363,7 @@ void supervisor_start_web_workflow(void) {
|
|||||||
// Wake polling thread (maybe)
|
// Wake polling thread (maybe)
|
||||||
socketpool_socket_poll_resume();
|
socketpool_socket_poll_resume();
|
||||||
#endif
|
#endif
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void web_workflow_send_raw(socketpool_socket_obj_t *socket, const uint8_t *buf, int len) {
|
void web_workflow_send_raw(socketpool_socket_obj_t *socket, const uint8_t *buf, int len) {
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
void supervisor_web_workflow_background(void *data);
|
void supervisor_web_workflow_background(void *data);
|
||||||
bool supervisor_web_workflow_status_dirty(void);
|
bool supervisor_web_workflow_status_dirty(void);
|
||||||
void supervisor_web_workflow_status(void);
|
void supervisor_web_workflow_status(void);
|
||||||
void supervisor_start_web_workflow(void);
|
bool supervisor_start_web_workflow(void);
|
||||||
void supervisor_stop_web_workflow(void);
|
void supervisor_stop_web_workflow(void);
|
||||||
|
|
||||||
// Share the MDNS object with user code.
|
// Share the MDNS object with user code.
|
||||||
|
@ -45,8 +45,9 @@
|
|||||||
|
|
||||||
#if CIRCUITPY_WEB_WORKFLOW
|
#if CIRCUITPY_WEB_WORKFLOW
|
||||||
#include "supervisor/shared/web_workflow/web_workflow.h"
|
#include "supervisor/shared/web_workflow/web_workflow.h"
|
||||||
#endif
|
|
||||||
static background_callback_t workflow_background_cb = {NULL, NULL};
|
static background_callback_t workflow_background_cb = {NULL, NULL};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// Called during a VM reset. Doesn't actually reset things.
|
// Called during a VM reset. Doesn't actually reset things.
|
||||||
void supervisor_workflow_reset(void) {
|
void supervisor_workflow_reset(void) {
|
||||||
@ -56,17 +57,23 @@ void supervisor_workflow_reset(void) {
|
|||||||
|
|
||||||
#if CIRCUITPY_WEB_WORKFLOW
|
#if CIRCUITPY_WEB_WORKFLOW
|
||||||
if (workflow_background_cb.fun) {
|
if (workflow_background_cb.fun) {
|
||||||
supervisor_start_web_workflow();
|
if (supervisor_start_web_workflow()) {
|
||||||
supervisor_workflow_request_background();
|
supervisor_workflow_request_background();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void supervisor_workflow_request_background(void) {
|
void supervisor_workflow_request_background(void) {
|
||||||
|
#if CIRCUITPY_WEB_WORKFLOW
|
||||||
if (workflow_background_cb.fun) {
|
if (workflow_background_cb.fun) {
|
||||||
workflow_background_cb.data = NULL;
|
workflow_background_cb.data = NULL;
|
||||||
background_callback_add_core(&workflow_background_cb);
|
background_callback_add_core(&workflow_background_cb);
|
||||||
|
} else {
|
||||||
|
// Unblock polling thread if necessary
|
||||||
|
socketpool_socket_poll_resume();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return true if host has completed connection to us (such as USB enumeration).
|
// Return true if host has completed connection to us (such as USB enumeration).
|
||||||
@ -98,9 +105,11 @@ void supervisor_workflow_start(void) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if CIRCUITPY_WEB_WORKFLOW
|
#if CIRCUITPY_WEB_WORKFLOW
|
||||||
supervisor_start_web_workflow();
|
if (supervisor_start_web_workflow()) {
|
||||||
memset(&workflow_background_cb, 0, sizeof(workflow_background_cb));
|
// Enable background callbacks if web_workflow startup successful
|
||||||
workflow_background_cb.fun = supervisor_web_workflow_background;
|
memset(&workflow_background_cb, 0, sizeof(workflow_background_cb));
|
||||||
|
workflow_background_cb.fun = supervisor_web_workflow_background;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
5
tests/circuitpython-manual/synthio/note/README.md
Normal file
5
tests/circuitpython-manual/synthio/note/README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Test synthio without hardware
|
||||||
|
|
||||||
|
Build the unix port then run `....../ports/unix/micropython-coverage code.py`.
|
||||||
|
|
||||||
|
This will create `tune.wav` as output, which you can listen to using any old audio player.
|
142
tests/circuitpython-manual/synthio/note/code.py
Normal file
142
tests/circuitpython-manual/synthio/note/code.py
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(
|
||||||
|
0, f"{__file__.rpartition('/')[0] or '.'}/../../../../frozen/Adafruit_CircuitPython_Wave"
|
||||||
|
)
|
||||||
|
|
||||||
|
import random
|
||||||
|
import audiocore
|
||||||
|
import synthio
|
||||||
|
from ulab import numpy as np
|
||||||
|
import adafruit_wave as wave
|
||||||
|
|
||||||
|
SAMPLE_SIZE = 1024
|
||||||
|
VOLUME = 14700
|
||||||
|
sine = np.array(
|
||||||
|
np.sin(np.linspace(0, 2 * np.pi, SAMPLE_SIZE, endpoint=False)) * VOLUME,
|
||||||
|
dtype=np.int16,
|
||||||
|
)
|
||||||
|
|
||||||
|
envelope = synthio.Envelope(
|
||||||
|
attack_time=0.1, decay_time=0.05, release_time=0.2, attack_level=0.8, sustain_level=0.8
|
||||||
|
)
|
||||||
|
|
||||||
|
synth = synthio.Synthesizer(sample_rate=48000)
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize(synth):
|
||||||
|
random.seed(3)
|
||||||
|
target_notes = [synthio.midi_to_hz(n + o) for n in (60, 64, 67, 70) for o in (-12, 12, 0)]
|
||||||
|
print(target_notes)
|
||||||
|
notes = [
|
||||||
|
synthio.Note(frequency=random.randint(60, 20000), waveform=sine, envelope=envelope)
|
||||||
|
for note in target_notes
|
||||||
|
]
|
||||||
|
synth.press(notes)
|
||||||
|
target = synthio.midi_to_hz(72)
|
||||||
|
factor = 0.98
|
||||||
|
for i in range(600):
|
||||||
|
yield 1
|
||||||
|
for ni, ti in zip(notes, target_notes):
|
||||||
|
print(ni.frequency, ti)
|
||||||
|
break
|
||||||
|
for ni, ti in zip(notes, target_notes):
|
||||||
|
ni.frequency = (ni.frequency * factor) + (ti * (1 - factor))
|
||||||
|
synth.release_all()
|
||||||
|
yield 36
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize2(synth):
|
||||||
|
n = synthio.Note(
|
||||||
|
frequency=synthio.midi_to_hz(60),
|
||||||
|
tremolo_depth=0.2,
|
||||||
|
tremolo_rate=2,
|
||||||
|
waveform=sine,
|
||||||
|
envelope=envelope,
|
||||||
|
)
|
||||||
|
synth.press((n,))
|
||||||
|
yield 360
|
||||||
|
synth.release_all()
|
||||||
|
yield 36
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize3(synth):
|
||||||
|
n = synthio.Note(
|
||||||
|
frequency=synthio.midi_to_hz(60),
|
||||||
|
bend_depth=0.1,
|
||||||
|
bend_rate=8,
|
||||||
|
waveform=sine,
|
||||||
|
envelope=envelope,
|
||||||
|
)
|
||||||
|
synth.press((n,))
|
||||||
|
yield 360
|
||||||
|
synth.release_all()
|
||||||
|
yield 36
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize4(synth):
|
||||||
|
n = synthio.Note(
|
||||||
|
frequency=synthio.midi_to_hz(60),
|
||||||
|
tremolo_depth=0.1,
|
||||||
|
tremolo_rate=1.5,
|
||||||
|
bend_depth=0.1,
|
||||||
|
bend_rate=3,
|
||||||
|
waveform=sine,
|
||||||
|
envelope=envelope,
|
||||||
|
)
|
||||||
|
synth.press((n,))
|
||||||
|
yield 720
|
||||||
|
synth.release_all()
|
||||||
|
yield 36
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize5(synth):
|
||||||
|
notes = [
|
||||||
|
synthio.Note(
|
||||||
|
frequency=synthio.midi_to_hz(60 + i + o),
|
||||||
|
waveform=sine,
|
||||||
|
envelope=envelope,
|
||||||
|
)
|
||||||
|
for i in [0, 4, 7]
|
||||||
|
for o in [0, -12, 12]
|
||||||
|
]
|
||||||
|
|
||||||
|
for n in notes:
|
||||||
|
print(n)
|
||||||
|
synth.press((n,))
|
||||||
|
yield 120
|
||||||
|
synth.release_all()
|
||||||
|
yield 36
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize6(synth):
|
||||||
|
n = synthio.Note(
|
||||||
|
frequency=synthio.midi_to_hz(60),
|
||||||
|
tremolo_depth=0.1,
|
||||||
|
tremolo_rate=1.5,
|
||||||
|
bend_depth=-5 / 12,
|
||||||
|
bend_rate=1 / 2,
|
||||||
|
bend_mode=synthio.BendMode.SWEEP,
|
||||||
|
waveform=sine,
|
||||||
|
envelope=envelope,
|
||||||
|
)
|
||||||
|
synth.press((n,))
|
||||||
|
yield 720
|
||||||
|
synth.release_all()
|
||||||
|
yield 36
|
||||||
|
|
||||||
|
|
||||||
|
def chain(*args):
|
||||||
|
for a in args:
|
||||||
|
yield from a
|
||||||
|
|
||||||
|
|
||||||
|
# sox -r 48000 -e signed -b 16 -c 1 tune.raw tune.wav
|
||||||
|
with wave.open("tune-noenv.wav", "w") as f:
|
||||||
|
f.setnchannels(1)
|
||||||
|
f.setsampwidth(2)
|
||||||
|
f.setframerate(48000)
|
||||||
|
for n in chain(synthesize6(synth)):
|
||||||
|
for i in range(n):
|
||||||
|
result, data = audiocore.get_buffer(synth)
|
||||||
|
f.writeframes(data)
|
282
tests/circuitpython-manual/synthio/note/demo.py
Normal file
282
tests/circuitpython-manual/synthio/note/demo.py
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
import random
|
||||||
|
import audiocore
|
||||||
|
import synthio
|
||||||
|
from ulab import numpy as np
|
||||||
|
import adafruit_wave as wave
|
||||||
|
|
||||||
|
h = np.array(
|
||||||
|
[
|
||||||
|
-0.001229734800309099,
|
||||||
|
-0.008235561806605458,
|
||||||
|
-0.015082497016061390,
|
||||||
|
-0.020940136918319988,
|
||||||
|
-0.024981800822463429,
|
||||||
|
-0.026464233332370746,
|
||||||
|
-0.024803890156806906,
|
||||||
|
-0.019642276775473012,
|
||||||
|
-0.010893620860173042,
|
||||||
|
0.001230341899766145,
|
||||||
|
0.016221637398855598,
|
||||||
|
0.033304135659230648,
|
||||||
|
0.051486665261155681,
|
||||||
|
0.069636961761409016,
|
||||||
|
0.086570197432542767,
|
||||||
|
0.101144354207918147,
|
||||||
|
0.112353938422488253,
|
||||||
|
0.119413577288191297,
|
||||||
|
0.121823886314051028,
|
||||||
|
0.119413577288191297,
|
||||||
|
0.112353938422488253,
|
||||||
|
0.101144354207918147,
|
||||||
|
0.086570197432542767,
|
||||||
|
0.069636961761409016,
|
||||||
|
0.051486665261155681,
|
||||||
|
0.033304135659230648,
|
||||||
|
0.016221637398855598,
|
||||||
|
0.001230341899766145,
|
||||||
|
-0.010893620860173042,
|
||||||
|
-0.019642276775473012,
|
||||||
|
-0.024803890156806906,
|
||||||
|
-0.026464233332370746,
|
||||||
|
-0.024981800822463429,
|
||||||
|
-0.020940136918319988,
|
||||||
|
-0.015082497016061390,
|
||||||
|
-0.008235561806605458,
|
||||||
|
-0.001229734800309099,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
filter_coeffs = np.array(h[::-1] * 32768, dtype=np.int16)
|
||||||
|
|
||||||
|
|
||||||
|
def randf(lo, hi):
|
||||||
|
return random.random() * (hi - lo) + lo
|
||||||
|
|
||||||
|
|
||||||
|
SAMPLE_SIZE = 1024
|
||||||
|
VOLUME = 14700
|
||||||
|
sine = np.array(
|
||||||
|
np.sin(np.linspace(0, 2 * np.pi, SAMPLE_SIZE, endpoint=False)) * VOLUME, dtype=np.int16
|
||||||
|
)
|
||||||
|
square = np.array([24000] * (SAMPLE_SIZE // 2) + [-24000] * (SAMPLE_SIZE // 2), dtype=np.int16)
|
||||||
|
|
||||||
|
noise = np.array(
|
||||||
|
[random.randint(-32768, 32767) for i in range(SAMPLE_SIZE)],
|
||||||
|
dtype=np.int16,
|
||||||
|
)
|
||||||
|
|
||||||
|
envelope = synthio.Envelope(
|
||||||
|
attack_time=0.1, decay_time=0.05, release_time=0.2, attack_level=1, sustain_level=0.8
|
||||||
|
)
|
||||||
|
|
||||||
|
instant = synthio.Envelope(
|
||||||
|
attack_time=0, decay_time=0, release_time=0, attack_level=1, sustain_level=1
|
||||||
|
)
|
||||||
|
synth = synthio.Synthesizer(
|
||||||
|
sample_rate=48000, envelope=None, filter=filter_coeffs, channel_count=2
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize(synth):
|
||||||
|
print(
|
||||||
|
"""you can use arbitrary waveforms, including ones calculated on the fly or read from wave files (up to 1024 points)"""
|
||||||
|
)
|
||||||
|
|
||||||
|
waveform = np.zeros(SAMPLE_SIZE, dtype=np.int16)
|
||||||
|
chord = [
|
||||||
|
synthio.Note(synthio.midi_to_hz(n), waveform=waveform, envelope=envelope)
|
||||||
|
for n in (60, 64, 67, 70)
|
||||||
|
]
|
||||||
|
synth.press(chord)
|
||||||
|
|
||||||
|
for i in range(256):
|
||||||
|
ctrl = i / 255
|
||||||
|
waveform[:] = np.array(square * (1 - ctrl) + sine * ctrl, dtype=np.int16)
|
||||||
|
yield 4
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize2(synth):
|
||||||
|
print("""Envelope controls how notes fade in or out""")
|
||||||
|
|
||||||
|
chord = [
|
||||||
|
synthio.Note(synthio.midi_to_hz(n), waveform=sine, envelope=instant)
|
||||||
|
for n in (60, 64, 67, 70)
|
||||||
|
]
|
||||||
|
|
||||||
|
for i in range(4):
|
||||||
|
for c in chord:
|
||||||
|
synth.release_all_then_press((c,))
|
||||||
|
yield 24
|
||||||
|
synth.release_all()
|
||||||
|
|
||||||
|
chord = [
|
||||||
|
synthio.Note(synthio.midi_to_hz(n), waveform=sine, envelope=envelope)
|
||||||
|
for n in (60, 64, 67, 70)
|
||||||
|
]
|
||||||
|
|
||||||
|
for i in range(4):
|
||||||
|
for c in chord:
|
||||||
|
old = (c,)
|
||||||
|
synth.release_all_then_press((c,))
|
||||||
|
yield 24
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize3(synth):
|
||||||
|
print("""A noise waveform creates percussive sounds""")
|
||||||
|
|
||||||
|
env = synthio.Envelope(
|
||||||
|
attack_time=0,
|
||||||
|
decay_time=0.2,
|
||||||
|
sustain_level=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
notes = [
|
||||||
|
synthio.Note(
|
||||||
|
frequency=synthio.midi_to_hz(1 + i),
|
||||||
|
waveform=noise,
|
||||||
|
envelope=env,
|
||||||
|
)
|
||||||
|
for i in range(12)
|
||||||
|
]
|
||||||
|
|
||||||
|
random.seed(9)
|
||||||
|
for _ in range(16):
|
||||||
|
n = random.choice(notes)
|
||||||
|
d = random.randint(30, 60)
|
||||||
|
synth.press((n,))
|
||||||
|
yield d
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize4(synth):
|
||||||
|
print("""Tremolo varies the note volume within a range at a low frequency""")
|
||||||
|
|
||||||
|
chord = [
|
||||||
|
synthio.Note(synthio.midi_to_hz(n), waveform=sine, envelope=None) for n in (60, 64, 67, 70)
|
||||||
|
]
|
||||||
|
|
||||||
|
synth.press(chord)
|
||||||
|
for i in range(16):
|
||||||
|
for c in chord:
|
||||||
|
c.tremolo_depth = i / 50
|
||||||
|
c.tremolo_rate = (i + 1) / 4
|
||||||
|
yield 48
|
||||||
|
yield 36
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize5(synth):
|
||||||
|
print("""You can add vibrato or frequency sweep to notes""")
|
||||||
|
|
||||||
|
chord = [synthio.Note(synthio.midi_to_hz(n), waveform=sine) for n in (60, 64, 67, 70)]
|
||||||
|
|
||||||
|
synth.press(chord)
|
||||||
|
for i in range(16):
|
||||||
|
for c in chord:
|
||||||
|
c.bend_depth = 1 / 24
|
||||||
|
c.bend_rate = (i + 1) / 2
|
||||||
|
yield 24
|
||||||
|
synth.release_all()
|
||||||
|
yield 100
|
||||||
|
|
||||||
|
for c in chord:
|
||||||
|
synth.release_all()
|
||||||
|
c.bend_mode = synthio.BendMode.SWEEP_IN
|
||||||
|
c.bend_depth = randf(-1, 1)
|
||||||
|
c.bend_rate = 1 / 2
|
||||||
|
synth.press(chord)
|
||||||
|
yield 320
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize6(synth):
|
||||||
|
print("""Ring modulation multiplies two waveforms together to create rich sounds""")
|
||||||
|
|
||||||
|
chord = [
|
||||||
|
synthio.Note(synthio.midi_to_hz(n), waveform=square, ring_waveform=sine, envelope=envelope)
|
||||||
|
for n in (60,)
|
||||||
|
]
|
||||||
|
|
||||||
|
synth.press(chord)
|
||||||
|
yield 200
|
||||||
|
|
||||||
|
random.seed(75)
|
||||||
|
|
||||||
|
for _ in range(3):
|
||||||
|
synth.release_all()
|
||||||
|
yield 36
|
||||||
|
for note in chord:
|
||||||
|
note.ring_frequency = note.frequency * (random.random() * 35 / 1200 + 8)
|
||||||
|
synth.press(chord)
|
||||||
|
yield 200
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize7(synth):
|
||||||
|
print("""FIR filtering can reproduce low, high, notch and band filters""")
|
||||||
|
chord = [
|
||||||
|
synthio.Note(synthio.midi_to_hz(n), waveform=square, filter=False)
|
||||||
|
for n in (60, 64, 67, 70)
|
||||||
|
]
|
||||||
|
|
||||||
|
for i in range(4):
|
||||||
|
for c in chord:
|
||||||
|
synth.release_all_then_press((c,))
|
||||||
|
yield 24
|
||||||
|
synth.release_all()
|
||||||
|
|
||||||
|
for note in chord:
|
||||||
|
note.filter = True
|
||||||
|
|
||||||
|
for i in range(4):
|
||||||
|
for c in chord:
|
||||||
|
synth.release_all_then_press((c,))
|
||||||
|
yield 24
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize8(synth):
|
||||||
|
print("""Notes can be panned between channels""")
|
||||||
|
chord = [
|
||||||
|
synthio.Note(synthio.midi_to_hz(n), waveform=square, envelope=envelope)
|
||||||
|
for n in (60, 64, 67, 70)
|
||||||
|
]
|
||||||
|
synth.press(chord)
|
||||||
|
for p in range(-10, 11, 1):
|
||||||
|
for note in chord:
|
||||||
|
note.panning = p / 10
|
||||||
|
yield 36
|
||||||
|
|
||||||
|
|
||||||
|
def delay(synth):
|
||||||
|
synth.release_all()
|
||||||
|
yield 200
|
||||||
|
|
||||||
|
|
||||||
|
def chain(*args):
|
||||||
|
for a in args:
|
||||||
|
yield from a
|
||||||
|
|
||||||
|
|
||||||
|
# sox -r 48000 -e signed -b 16 -c 1 tune.raw tune.wav
|
||||||
|
with wave.open("demo.wav", "w") as f:
|
||||||
|
f.setnchannels(2)
|
||||||
|
f.setsampwidth(2)
|
||||||
|
f.setframerate(48000)
|
||||||
|
import array
|
||||||
|
|
||||||
|
for n in chain(
|
||||||
|
synthesize(synth),
|
||||||
|
delay(synth),
|
||||||
|
synthesize2(synth),
|
||||||
|
delay(synth),
|
||||||
|
synthesize3(synth),
|
||||||
|
delay(synth),
|
||||||
|
synthesize4(synth),
|
||||||
|
delay(synth),
|
||||||
|
synthesize5(synth),
|
||||||
|
delay(synth),
|
||||||
|
synthesize6(synth),
|
||||||
|
delay(synth),
|
||||||
|
synthesize7(synth),
|
||||||
|
delay(synth),
|
||||||
|
synthesize8(synth),
|
||||||
|
):
|
||||||
|
for i in range(n):
|
||||||
|
result, data = audiocore.get_buffer(synth)
|
||||||
|
f.writeframes(data)
|
58
tests/circuitpython-manual/synthio/note/envelope.py
Normal file
58
tests/circuitpython-manual/synthio/note/envelope.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(
|
||||||
|
0, f"{__file__.rpartition('/')[0] or '.'}/../../../../frozen/Adafruit_CircuitPython_Wave"
|
||||||
|
)
|
||||||
|
|
||||||
|
import random
|
||||||
|
import audiocore
|
||||||
|
import synthio
|
||||||
|
from ulab import numpy as np
|
||||||
|
import adafruit_wave as wave
|
||||||
|
|
||||||
|
SAMPLE_SIZE = 1024
|
||||||
|
VOLUME = 32767
|
||||||
|
sine = np.array(
|
||||||
|
np.sin(np.linspace(0, 2 * np.pi, SAMPLE_SIZE, endpoint=False)) * VOLUME,
|
||||||
|
dtype=np.int16,
|
||||||
|
)
|
||||||
|
|
||||||
|
envelope = synthio.Envelope(attack_time=0.05, decay_time=8, release_time=0.25, sustain_level=0)
|
||||||
|
fast_decay_envelope = synthio.Envelope(
|
||||||
|
attack_time=0.05, decay_time=0.25, release_time=0.25, sustain_level=0
|
||||||
|
)
|
||||||
|
|
||||||
|
synth = synthio.Synthesizer(sample_rate=48000)
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize(synth):
|
||||||
|
notes = (synthio.Note(frequency=440, waveform=sine, envelope=envelope),)
|
||||||
|
synth.press(notes)
|
||||||
|
yield 360
|
||||||
|
notes[0].envelope = fast_decay_envelope
|
||||||
|
yield 180
|
||||||
|
synth.release_all()
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize2(synth):
|
||||||
|
notes = (synthio.Note(frequency=440, waveform=sine, envelope=envelope),)
|
||||||
|
synth.press(notes)
|
||||||
|
yield 360
|
||||||
|
synth.release_all()
|
||||||
|
yield 180
|
||||||
|
|
||||||
|
|
||||||
|
def chain(*args):
|
||||||
|
for a in args:
|
||||||
|
yield from a
|
||||||
|
|
||||||
|
|
||||||
|
# sox -r 48000 -e signed -b 16 -c 1 tune.raw tune.wav
|
||||||
|
with wave.open("envelope.wav", "w") as f:
|
||||||
|
f.setnchannels(1)
|
||||||
|
f.setsampwidth(2)
|
||||||
|
f.setframerate(48000)
|
||||||
|
for n in chain(synthesize(synth), synthesize2(synth)):
|
||||||
|
for i in range(n):
|
||||||
|
result, data = audiocore.get_buffer(synth)
|
||||||
|
f.writeframes(data)
|
104
tests/circuitpython-manual/synthio/note/fir.py
Normal file
104
tests/circuitpython-manual/synthio/note/fir.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(
|
||||||
|
0, f"{__file__.rpartition('/')[0] or '.'}/../../../../frozen/Adafruit_CircuitPython_Wave"
|
||||||
|
)
|
||||||
|
|
||||||
|
import random
|
||||||
|
import audiocore
|
||||||
|
import synthio
|
||||||
|
from ulab import numpy as np
|
||||||
|
import adafruit_wave as wave
|
||||||
|
|
||||||
|
random.seed(9)
|
||||||
|
|
||||||
|
envelope = synthio.Envelope(
|
||||||
|
attack_time=0.1, decay_time=0.05, release_time=0.2, attack_level=0.8, sustain_level=0.8
|
||||||
|
)
|
||||||
|
|
||||||
|
h = np.array(
|
||||||
|
[
|
||||||
|
-0.001229734800309099,
|
||||||
|
-0.008235561806605458,
|
||||||
|
-0.015082497016061390,
|
||||||
|
-0.020940136918319988,
|
||||||
|
-0.024981800822463429,
|
||||||
|
-0.026464233332370746,
|
||||||
|
-0.024803890156806906,
|
||||||
|
-0.019642276775473012,
|
||||||
|
-0.010893620860173042,
|
||||||
|
0.001230341899766145,
|
||||||
|
0.016221637398855598,
|
||||||
|
0.033304135659230648,
|
||||||
|
0.051486665261155681,
|
||||||
|
0.069636961761409016,
|
||||||
|
0.086570197432542767,
|
||||||
|
0.101144354207918147,
|
||||||
|
0.112353938422488253,
|
||||||
|
0.119413577288191297,
|
||||||
|
0.121823886314051028,
|
||||||
|
0.119413577288191297,
|
||||||
|
0.112353938422488253,
|
||||||
|
0.101144354207918147,
|
||||||
|
0.086570197432542767,
|
||||||
|
0.069636961761409016,
|
||||||
|
0.051486665261155681,
|
||||||
|
0.033304135659230648,
|
||||||
|
0.016221637398855598,
|
||||||
|
0.001230341899766145,
|
||||||
|
-0.010893620860173042,
|
||||||
|
-0.019642276775473012,
|
||||||
|
-0.024803890156806906,
|
||||||
|
-0.026464233332370746,
|
||||||
|
-0.024981800822463429,
|
||||||
|
-0.020940136918319988,
|
||||||
|
-0.015082497016061390,
|
||||||
|
-0.008235561806605458,
|
||||||
|
-0.001229734800309099,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
filter_coeffs = np.array(h[::-1] * 32768, dtype=np.int16)
|
||||||
|
|
||||||
|
synth = synthio.Synthesizer(sample_rate=48000, filter=filter_coeffs)
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize(synth):
|
||||||
|
n = synthio.Note(
|
||||||
|
frequency=120,
|
||||||
|
envelope=envelope,
|
||||||
|
filter=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
print(synth, n)
|
||||||
|
synth.press((n,))
|
||||||
|
for _ in range(20):
|
||||||
|
n.frequency *= 1.0595
|
||||||
|
yield 36
|
||||||
|
synth.release_all()
|
||||||
|
yield 36
|
||||||
|
|
||||||
|
n.filter = True
|
||||||
|
n.frequency = 120
|
||||||
|
synth.press((n,))
|
||||||
|
for _ in range(20):
|
||||||
|
n.frequency *= 1.0595
|
||||||
|
yield 36
|
||||||
|
synth.release_all()
|
||||||
|
yield 36
|
||||||
|
|
||||||
|
|
||||||
|
def chain(*args):
|
||||||
|
for a in args:
|
||||||
|
yield from a
|
||||||
|
|
||||||
|
|
||||||
|
# sox -r 48000 -e signed -b 16 -c 1 tune.raw tune.wav
|
||||||
|
with wave.open("fir.wav", "w") as f:
|
||||||
|
f.setnchannels(1)
|
||||||
|
f.setsampwidth(2)
|
||||||
|
f.setframerate(48000)
|
||||||
|
for n in chain(synthesize(synth)):
|
||||||
|
for i in range(n):
|
||||||
|
result, data = audiocore.get_buffer(synth)
|
||||||
|
f.writeframes(data)
|
71
tests/circuitpython-manual/synthio/note/noise.py
Normal file
71
tests/circuitpython-manual/synthio/note/noise.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(
|
||||||
|
0, f"{__file__.rpartition('/')[0] or '.'}/../../../../frozen/Adafruit_CircuitPython_Wave"
|
||||||
|
)
|
||||||
|
|
||||||
|
import random
|
||||||
|
import audiocore
|
||||||
|
import synthio
|
||||||
|
from ulab import numpy as np
|
||||||
|
import adafruit_wave as wave
|
||||||
|
|
||||||
|
random.seed(9)
|
||||||
|
SAMPLE_SIZE = 1024
|
||||||
|
VOLUME = 14700
|
||||||
|
noise = np.array(
|
||||||
|
[random.randint(-32768, 32767) for i in range(SAMPLE_SIZE)],
|
||||||
|
dtype=np.int16,
|
||||||
|
)
|
||||||
|
|
||||||
|
envelope = synthio.Envelope(
|
||||||
|
attack_time=0,
|
||||||
|
decay_time=0.2,
|
||||||
|
sustain_level=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
synth = synthio.Synthesizer(sample_rate=48000)
|
||||||
|
|
||||||
|
|
||||||
|
def randf(lo, hi):
|
||||||
|
return random.random() * (hi - lo) + lo
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize(synth):
|
||||||
|
notes = [
|
||||||
|
synthio.Note(
|
||||||
|
frequency=synthio.midi_to_hz(1 + i),
|
||||||
|
waveform=noise,
|
||||||
|
envelope=envelope,
|
||||||
|
bend_mode=synthio.BendMode.SWEEP,
|
||||||
|
bend_depth=random.choice((-1, 1)),
|
||||||
|
bend_rate=randf(4, 12),
|
||||||
|
)
|
||||||
|
for i in range(12)
|
||||||
|
]
|
||||||
|
|
||||||
|
random.seed(9)
|
||||||
|
for _ in range(16):
|
||||||
|
n = random.choice(notes)
|
||||||
|
d = random.randint(30, 60)
|
||||||
|
print(n, d)
|
||||||
|
synth.press((n,))
|
||||||
|
yield d
|
||||||
|
synth.release_all()
|
||||||
|
yield 36
|
||||||
|
|
||||||
|
|
||||||
|
def chain(*args):
|
||||||
|
for a in args:
|
||||||
|
yield from a
|
||||||
|
|
||||||
|
|
||||||
|
# sox -r 48000 -e signed -b 16 -c 1 tune.raw tune.wav
|
||||||
|
with wave.open("noise.wav", "w") as f:
|
||||||
|
f.setnchannels(1)
|
||||||
|
f.setsampwidth(2)
|
||||||
|
f.setframerate(48000)
|
||||||
|
for n in chain(synthesize(synth)):
|
||||||
|
for i in range(n):
|
||||||
|
result, data = audiocore.get_buffer(synth)
|
||||||
|
f.writeframes(data)
|
57
tests/circuitpython-manual/synthio/note/panning.py
Normal file
57
tests/circuitpython-manual/synthio/note/panning.py
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(
|
||||||
|
0, f"{__file__.rpartition('/')[0] or '.'}/../../../../frozen/Adafruit_CircuitPython_Wave"
|
||||||
|
)
|
||||||
|
|
||||||
|
import random
|
||||||
|
import audiocore
|
||||||
|
import synthio
|
||||||
|
from ulab import numpy as np
|
||||||
|
import adafruit_wave as wave
|
||||||
|
|
||||||
|
random.seed(9)
|
||||||
|
SAMPLE_SIZE = 1024
|
||||||
|
VOLUME = 14700
|
||||||
|
sine = np.array(
|
||||||
|
np.sin(np.linspace(0, 2 * np.pi, SAMPLE_SIZE, endpoint=False)) * VOLUME,
|
||||||
|
dtype=np.int16,
|
||||||
|
)
|
||||||
|
|
||||||
|
envelope = synthio.Envelope(
|
||||||
|
attack_time=0.1, decay_time=0.05, release_time=0.2, attack_level=0.8, sustain_level=0.8
|
||||||
|
)
|
||||||
|
|
||||||
|
synth = synthio.Synthesizer(sample_rate=48000, channel_count=2)
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize(synth):
|
||||||
|
n = synthio.Note(
|
||||||
|
frequency=440,
|
||||||
|
waveform=sine,
|
||||||
|
envelope=envelope,
|
||||||
|
)
|
||||||
|
|
||||||
|
print(synth, n)
|
||||||
|
synth.press((n,))
|
||||||
|
for p in range(-10, 11, 1):
|
||||||
|
n.panning = p / 10
|
||||||
|
yield 36
|
||||||
|
synth.release_all()
|
||||||
|
yield 36
|
||||||
|
|
||||||
|
|
||||||
|
def chain(*args):
|
||||||
|
for a in args:
|
||||||
|
yield from a
|
||||||
|
|
||||||
|
|
||||||
|
# sox -r 48000 -e signed -b 16 -c 1 tune.raw tune.wav
|
||||||
|
with wave.open("panning.wav", "w") as f:
|
||||||
|
f.setnchannels(2)
|
||||||
|
f.setsampwidth(2)
|
||||||
|
f.setframerate(48000)
|
||||||
|
for n in chain(synthesize(synth)):
|
||||||
|
for i in range(n):
|
||||||
|
result, data = audiocore.get_buffer(synth)
|
||||||
|
f.writeframes(data)
|
60
tests/circuitpython-manual/synthio/note/ring.py
Normal file
60
tests/circuitpython-manual/synthio/note/ring.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(
|
||||||
|
0, f"{__file__.rpartition('/')[0] or '.'}/../../../../frozen/Adafruit_CircuitPython_Wave"
|
||||||
|
)
|
||||||
|
|
||||||
|
import random
|
||||||
|
import audiocore
|
||||||
|
import synthio
|
||||||
|
from ulab import numpy as np
|
||||||
|
import adafruit_wave as wave
|
||||||
|
|
||||||
|
random.seed(9)
|
||||||
|
SAMPLE_SIZE = 1024
|
||||||
|
VOLUME = 32767
|
||||||
|
sine = np.array(
|
||||||
|
np.sin(np.linspace(0, 2 * np.pi, SAMPLE_SIZE, endpoint=False)) * VOLUME,
|
||||||
|
dtype=np.int16,
|
||||||
|
)
|
||||||
|
|
||||||
|
envelope = synthio.Envelope(
|
||||||
|
attack_time=0.1, decay_time=0.05, release_time=0.2, attack_level=0.8, sustain_level=0.8
|
||||||
|
)
|
||||||
|
|
||||||
|
synth = synthio.Synthesizer(sample_rate=48000)
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize(synth):
|
||||||
|
n = synthio.Note(
|
||||||
|
frequency=120,
|
||||||
|
waveform=sine,
|
||||||
|
ring_waveform=sine,
|
||||||
|
ring_frequency=769,
|
||||||
|
envelope=envelope,
|
||||||
|
bend_mode=synthio.BendType.VIBRATO,
|
||||||
|
bend_depth=50 / 1200,
|
||||||
|
bend_rate=7,
|
||||||
|
)
|
||||||
|
|
||||||
|
print(synth, n)
|
||||||
|
synth.press((n,))
|
||||||
|
yield 720
|
||||||
|
synth.release_all()
|
||||||
|
yield 36
|
||||||
|
|
||||||
|
|
||||||
|
def chain(*args):
|
||||||
|
for a in args:
|
||||||
|
yield from a
|
||||||
|
|
||||||
|
|
||||||
|
# sox -r 48000 -e signed -b 16 -c 1 tune.raw tune.wav
|
||||||
|
with wave.open("ring.wav", "w") as f:
|
||||||
|
f.setnchannels(1)
|
||||||
|
f.setsampwidth(2)
|
||||||
|
f.setframerate(48000)
|
||||||
|
for n in chain(synthesize(synth)):
|
||||||
|
for i in range(n):
|
||||||
|
result, data = audiocore.get_buffer(synth)
|
||||||
|
f.writeframes(data)
|
@ -1,5 +1,5 @@
|
|||||||
# Test synthio without hardware
|
# Test synthio without hardware
|
||||||
|
|
||||||
Build the uninx port then run `....../ports/unix/micropython-coverage midi2wav.py`.
|
Build the unix port then run `....../ports/unix/micropython-coverage midi2wav.py`.
|
||||||
|
|
||||||
This will create `tune.wav` as output, which you can listen to using any old audio player.
|
This will create `tune.wav` as output, which you can listen to using any old audio player.
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
# SPDX-FileCopyrightText: 2023 Guido van Rossum <guido@cwi.nl> and others.
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: PSF-2.0
|
|
||||||
|
|
||||||
import struct
|
|
||||||
|
|
||||||
|
|
||||||
def byteswap(data, sampwidth):
|
|
||||||
print(data)
|
|
||||||
raise
|
|
||||||
ch = "I" if sampwidth == 16 else "H"
|
|
@ -1,173 +0,0 @@
|
|||||||
# SPDX-FileCopyrightText: 2023 Guido van Rossum <guido@cwi.nl> and others.
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: PSF-2.0
|
|
||||||
|
|
||||||
"""Simple class to read IFF chunks.
|
|
||||||
|
|
||||||
An IFF chunk (used in formats such as AIFF, TIFF, RMFF (RealMedia File
|
|
||||||
Format)) has the following structure:
|
|
||||||
|
|
||||||
+----------------+
|
|
||||||
| ID (4 bytes) |
|
|
||||||
+----------------+
|
|
||||||
| size (4 bytes) |
|
|
||||||
+----------------+
|
|
||||||
| data |
|
|
||||||
| ... |
|
|
||||||
+----------------+
|
|
||||||
|
|
||||||
The ID is a 4-byte string which identifies the type of chunk.
|
|
||||||
|
|
||||||
The size field (a 32-bit value, encoded using big-endian byte order)
|
|
||||||
gives the size of the whole chunk, including the 8-byte header.
|
|
||||||
|
|
||||||
Usually an IFF-type file consists of one or more chunks. The proposed
|
|
||||||
usage of the Chunk class defined here is to instantiate an instance at
|
|
||||||
the start of each chunk and read from the instance until it reaches
|
|
||||||
the end, after which a new instance can be instantiated. At the end
|
|
||||||
of the file, creating a new instance will fail with an EOFError
|
|
||||||
exception.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
chunk = Chunk(file)
|
|
||||||
except EOFError:
|
|
||||||
break
|
|
||||||
chunktype = chunk.getname()
|
|
||||||
while True:
|
|
||||||
data = chunk.read(nbytes)
|
|
||||||
if not data:
|
|
||||||
pass
|
|
||||||
# do something with data
|
|
||||||
|
|
||||||
The interface is file-like. The implemented methods are:
|
|
||||||
read, close, seek, tell, isatty.
|
|
||||||
Extra methods are: skip() (called by close, skips to the end of the chunk),
|
|
||||||
getname() (returns the name (ID) of the chunk)
|
|
||||||
|
|
||||||
The __init__ method has one required argument, a file-like object
|
|
||||||
(including a chunk instance), and one optional argument, a flag which
|
|
||||||
specifies whether or not chunks are aligned on 2-byte boundaries. The
|
|
||||||
default is 1, i.e. aligned.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class Chunk:
|
|
||||||
def __init__(self, file, align=True, bigendian=True, inclheader=False):
|
|
||||||
import struct
|
|
||||||
|
|
||||||
self.closed = False
|
|
||||||
self.align = align # whether to align to word (2-byte) boundaries
|
|
||||||
if bigendian:
|
|
||||||
strflag = ">"
|
|
||||||
else:
|
|
||||||
strflag = "<"
|
|
||||||
self.file = file
|
|
||||||
self.chunkname = file.read(4)
|
|
||||||
if len(self.chunkname) < 4:
|
|
||||||
raise EOFError
|
|
||||||
try:
|
|
||||||
self.chunksize = struct.unpack_from(strflag + "L", file.read(4))[0]
|
|
||||||
except struct.error:
|
|
||||||
raise EOFError from None
|
|
||||||
if inclheader:
|
|
||||||
self.chunksize = self.chunksize - 8 # subtract header
|
|
||||||
self.size_read = 0
|
|
||||||
try:
|
|
||||||
self.offset = self.file.tell()
|
|
||||||
except (AttributeError, OSError):
|
|
||||||
self.seekable = False
|
|
||||||
else:
|
|
||||||
self.seekable = True
|
|
||||||
|
|
||||||
def getname(self):
|
|
||||||
"""Return the name (ID) of the current chunk."""
|
|
||||||
return self.chunkname
|
|
||||||
|
|
||||||
def getsize(self):
|
|
||||||
"""Return the size of the current chunk."""
|
|
||||||
return self.chunksize
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
if not self.closed:
|
|
||||||
try:
|
|
||||||
self.skip()
|
|
||||||
finally:
|
|
||||||
self.closed = True
|
|
||||||
|
|
||||||
def isatty(self):
|
|
||||||
if self.closed:
|
|
||||||
raise ValueError("I/O operation on closed file")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def seek(self, pos, whence=0):
|
|
||||||
"""Seek to specified position into the chunk.
|
|
||||||
Default position is 0 (start of chunk).
|
|
||||||
If the file is not seekable, this will result in an error.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if self.closed:
|
|
||||||
raise ValueError("I/O operation on closed file")
|
|
||||||
if not self.seekable:
|
|
||||||
raise OSError("cannot seek")
|
|
||||||
if whence == 1:
|
|
||||||
pos = pos + self.size_read
|
|
||||||
elif whence == 2:
|
|
||||||
pos = pos + self.chunksize
|
|
||||||
if pos < 0 or pos > self.chunksize:
|
|
||||||
raise RuntimeError
|
|
||||||
self.file.seek(self.offset + pos, 0)
|
|
||||||
self.size_read = pos
|
|
||||||
|
|
||||||
def tell(self):
|
|
||||||
if self.closed:
|
|
||||||
raise ValueError("I/O operation on closed file")
|
|
||||||
return self.size_read
|
|
||||||
|
|
||||||
def read(self, size=-1):
|
|
||||||
"""Read at most size bytes from the chunk.
|
|
||||||
If size is omitted or negative, read until the end
|
|
||||||
of the chunk.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if self.closed:
|
|
||||||
raise ValueError("I/O operation on closed file")
|
|
||||||
if self.size_read >= self.chunksize:
|
|
||||||
return b""
|
|
||||||
if size < 0:
|
|
||||||
size = self.chunksize - self.size_read
|
|
||||||
if size > self.chunksize - self.size_read:
|
|
||||||
size = self.chunksize - self.size_read
|
|
||||||
data = self.file.read(size)
|
|
||||||
self.size_read = self.size_read + len(data)
|
|
||||||
if self.size_read == self.chunksize and self.align and (self.chunksize & 1):
|
|
||||||
dummy = self.file.read(1)
|
|
||||||
self.size_read = self.size_read + len(dummy)
|
|
||||||
return data
|
|
||||||
|
|
||||||
def skip(self):
|
|
||||||
"""Skip the rest of the chunk.
|
|
||||||
If you are not interested in the contents of the chunk,
|
|
||||||
this method should be called so that the file points to
|
|
||||||
the start of the next chunk.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if self.closed:
|
|
||||||
raise ValueError("I/O operation on closed file")
|
|
||||||
if self.seekable:
|
|
||||||
try:
|
|
||||||
n = self.chunksize - self.size_read
|
|
||||||
# maybe fix alignment
|
|
||||||
if self.align and (self.chunksize & 1):
|
|
||||||
n = n + 1
|
|
||||||
self.file.seek(n, 1)
|
|
||||||
self.size_read = self.size_read + n
|
|
||||||
return
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
while self.size_read < self.chunksize:
|
|
||||||
n = min(8192, self.chunksize - self.size_read)
|
|
||||||
dummy = self.read(n)
|
|
||||||
if not dummy:
|
|
||||||
raise EOFError
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user