Merge branch 'main' into efr32-doc-fixes
This commit is contained in:
commit
46ddacddde
@ -27,7 +27,11 @@ This project has a bunch of git submodules. You will need to update them regula
|
|||||||
|
|
||||||
In the root folder of the CircuitPython repository, execute the following:
|
In the root folder of the CircuitPython repository, execute the following:
|
||||||
|
|
||||||
make fetch-submodules
|
make fetch-all-submodules
|
||||||
|
|
||||||
|
Or, in the ports directory for the particular port you are building, do:
|
||||||
|
|
||||||
|
make fetch-port-submodules
|
||||||
|
|
||||||
### Required Python Packages
|
### Required Python Packages
|
||||||
|
|
||||||
|
26
Makefile
26
Makefile
@ -61,7 +61,6 @@ TRANSLATE_SOURCES_EXC = -path "ports/*/build-*" \
|
|||||||
|
|
||||||
help:
|
help:
|
||||||
@echo "Please use \`make <target>' where <target> is one of"
|
@echo "Please use \`make <target>' where <target> is one of"
|
||||||
@echo " fetch-submodules to fetch dependencies from submodules, run this right after you clone the repo"
|
|
||||||
@echo " html to make standalone HTML files"
|
@echo " html to make standalone HTML files"
|
||||||
@echo " dirhtml to make HTML files named index.html in directories"
|
@echo " dirhtml to make HTML files named index.html in directories"
|
||||||
@echo " singlehtml to make a single large HTML file"
|
@echo " singlehtml to make a single large HTML file"
|
||||||
@ -84,6 +83,8 @@ help:
|
|||||||
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
|
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
|
||||||
@echo " linkcheck to check all external links for integrity"
|
@echo " linkcheck to check all external links for integrity"
|
||||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||||
|
@echo " fetch-all-submodules to fetch submodules for all ports"
|
||||||
|
@echo " remove-all-submodules remove all submodules, including files and .git/ data"
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BUILDDIR)/*
|
rm -rf $(BUILDDIR)/*
|
||||||
@ -324,25 +325,12 @@ clean-stm:
|
|||||||
$(MAKE) -C ports/stm BOARD=feather_stm32f405_express clean
|
$(MAKE) -C ports/stm BOARD=feather_stm32f405_express clean
|
||||||
|
|
||||||
|
|
||||||
# If available, do blobless partial clones of submodules to save time and space.
|
.PHONY: fetch-all-submodules
|
||||||
# A blobless partial clone lazily fetches data as needed, but has all the metadata available (tags, etc.)
|
fetch-all-submodules:
|
||||||
# so it does not have the idiosyncrasies of a shallow clone.
|
tools/fetch-submodules.sh
|
||||||
#
|
|
||||||
# If not available, do a fetch that will fail, and then fix it up with a second fetch.
|
|
||||||
# (Only works for git servers that allow sha fetches.)
|
|
||||||
.PHONY: fetch-submodules
|
|
||||||
fetch-submodules:
|
|
||||||
git submodule sync
|
|
||||||
#####################################################################################
|
|
||||||
# NOTE: Ideally, use git version 2.36.0 or later, to do partial clones of submodules.
|
|
||||||
# If an older git is used, submodules will be cloned with a shallow clone of depth 1.
|
|
||||||
# You will see a git usage message first if the git version is too old to do
|
|
||||||
# clones of submodules.
|
|
||||||
#####################################################################################
|
|
||||||
git submodule update --init --filter=blob:none || git submodule update --init -N --depth 1 || git submodule foreach 'git fetch --tags --depth 1 origin $$sha1 && git checkout -q $$sha1' || echo 'make fetch-submodules FAILED'
|
|
||||||
|
|
||||||
.PHONY: remove-submodules
|
.PHONY: remove-all-submodules
|
||||||
remove-submodules:
|
remove-all-submodules:
|
||||||
git submodule deinit -f --all
|
git submodule deinit -f --all
|
||||||
rm -rf .git/modules/*
|
rm -rf .git/modules/*
|
||||||
|
|
||||||
|
@ -363,11 +363,15 @@ SRC_QSTR_PREPROCESSOR += peripherals/samd/$(PERIPHERALS_CHIP_FAMILY)/clocks.c
|
|||||||
|
|
||||||
all: $(BUILD)/firmware.bin $(BUILD)/firmware.uf2
|
all: $(BUILD)/firmware.bin $(BUILD)/firmware.uf2
|
||||||
|
|
||||||
|
ifeq ($(VALID_BOARD),)
|
||||||
|
$(BUILD)/firmware.elf: invalid-board
|
||||||
|
else
|
||||||
$(BUILD)/firmware.elf: $(OBJ) $(GENERATED_LD_FILE)
|
$(BUILD)/firmware.elf: $(OBJ) $(GENERATED_LD_FILE)
|
||||||
$(STEPECHO) "LINK $@"
|
$(STEPECHO) "LINK $@"
|
||||||
$(Q)echo $(OBJ) > $(BUILD)/firmware.objs
|
$(Q)echo $(OBJ) > $(BUILD)/firmware.objs
|
||||||
$(Q)$(CC) -o $@ $(LDFLAGS) @$(BUILD)/firmware.objs -Wl,--print-memory-usage -Wl,--start-group $(LIBS) -Wl,--end-group
|
$(Q)$(CC) -o $@ $(LDFLAGS) @$(BUILD)/firmware.objs -Wl,--print-memory-usage -Wl,--start-group $(LIBS) -Wl,--end-group
|
||||||
$(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $(GENERATED_LD_FILE) $(BUILD)
|
$(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $(GENERATED_LD_FILE) $(BUILD)
|
||||||
|
endif
|
||||||
|
|
||||||
$(BUILD)/firmware.bin: $(BUILD)/firmware.elf
|
$(BUILD)/firmware.bin: $(BUILD)/firmware.elf
|
||||||
$(STEPECHO) "Create $@"
|
$(STEPECHO) "Create $@"
|
||||||
|
@ -142,10 +142,14 @@ all: $(BUILD)/firmware.kernel$(SUFFIX).img $(BUILD)/firmware.disk.img.zip
|
|||||||
%.o: %.c
|
%.o: %.c
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
ifeq ($(VALID_BOARD),)
|
||||||
|
$(BUILD)/kernel$(SUFFIX).elf: invalid-board
|
||||||
|
else
|
||||||
$(BUILD)/kernel$(SUFFIX).elf: $(OBJ)
|
$(BUILD)/kernel$(SUFFIX).elf: $(OBJ)
|
||||||
$(STEPECHO) "LINK $@"
|
$(STEPECHO) "LINK $@"
|
||||||
$(Q)echo $(OBJ) > $(BUILD)/firmware.objs
|
$(Q)echo $(OBJ) > $(BUILD)/firmware.objs
|
||||||
$(Q)$(CC) -o $@ $(LDFLAGS) @$(BUILD)/firmware.objs -Wl,--start-group $(LIBS) -Wl,--end-group
|
$(Q)$(CC) -o $@ $(LDFLAGS) @$(BUILD)/firmware.objs -Wl,--start-group $(LIBS) -Wl,--end-group
|
||||||
|
endif
|
||||||
|
|
||||||
$(BUILD)/kernel$(SUFFIX).img: $(BUILD)/kernel$(SUFFIX).elf
|
$(BUILD)/kernel$(SUFFIX).img: $(BUILD)/kernel$(SUFFIX).elf
|
||||||
$(STEPECHO) "Create $@"
|
$(STEPECHO) "Create $@"
|
||||||
|
@ -183,9 +183,13 @@ $(BUILD)/firmware.elf: $(BUILD)/libmpy.a
|
|||||||
$(MKSPK):
|
$(MKSPK):
|
||||||
$(MAKE) -C mkspk
|
$(MAKE) -C mkspk
|
||||||
|
|
||||||
|
ifeq ($(VALID_BOARD),)
|
||||||
|
$(BUILD)/firmware.spk: invalid-board
|
||||||
|
else
|
||||||
$(BUILD)/firmware.spk: $(BUILD)/firmware.elf $(MKSPK)
|
$(BUILD)/firmware.spk: $(BUILD)/firmware.elf $(MKSPK)
|
||||||
$(ECHO) "Creating $@"
|
$(ECHO) "Creating $@"
|
||||||
$(MKSPK) -c 2 $(BUILD)/firmware.elf nuttx $(BUILD)/firmware.spk
|
$(MKSPK) -c 2 $(BUILD)/firmware.elf nuttx $(BUILD)/firmware.spk
|
||||||
|
endif
|
||||||
|
|
||||||
flash: $(BUILD)/firmware.spk
|
flash: $(BUILD)/firmware.spk
|
||||||
$(ECHO) "Writing $< to the board"
|
$(ECHO) "Writing $< to the board"
|
||||||
|
@ -411,6 +411,7 @@ ESP_IDF_COMPONENTS_EXPANDED += $(BUILD)/esp-idf/esp-idf/esp32-camera/libesp32-ca
|
|||||||
#$(error $(ESP_IDF_COMPONENTS_EXPANDED))
|
#$(error $(ESP_IDF_COMPONENTS_EXPANDED))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq ($(VALID_BOARD),)
|
||||||
# BOOTLOADER_OFFSET is determined by chip type, based on the ROM bootloader, and is not changeable.
|
# BOOTLOADER_OFFSET is determined by chip type, based on the ROM bootloader, and is not changeable.
|
||||||
ifeq ($(IDF_TARGET),esp32)
|
ifeq ($(IDF_TARGET),esp32)
|
||||||
BOOTLOADER_OFFSET = 0x1000
|
BOOTLOADER_OFFSET = 0x1000
|
||||||
@ -423,6 +424,7 @@ BOOTLOADER_OFFSET = 0x1000
|
|||||||
else
|
else
|
||||||
$(error unknown IDF_TARGET $(IDF_TARGET))
|
$(error unknown IDF_TARGET $(IDF_TARGET))
|
||||||
endif
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
IDF_CMAKE_TARGETS = \
|
IDF_CMAKE_TARGETS = \
|
||||||
bootloader/bootloader.bin \
|
bootloader/bootloader.bin \
|
||||||
@ -457,8 +459,12 @@ $(BUILD)/circuitpython-firmware.bin: $(BUILD)/firmware.elf | tools/build_memory_
|
|||||||
$(Q)esptool.py --chip $(IDF_TARGET) elf2image $(FLASH_FLAGS) --elf-sha256-offset 0xb0 -o $@ $^
|
$(Q)esptool.py --chip $(IDF_TARGET) elf2image $(FLASH_FLAGS) --elf-sha256-offset 0xb0 -o $@ $^
|
||||||
$(Q)$(PYTHON) tools/build_memory_info.py $< $(BUILD)/esp-idf/sdkconfig $@ $(BUILD)
|
$(Q)$(PYTHON) tools/build_memory_info.py $< $(BUILD)/esp-idf/sdkconfig $@ $(BUILD)
|
||||||
|
|
||||||
|
ifeq ($(VALID_BOARD),)
|
||||||
|
$(BUILD)/firmware.bin: invalid-board
|
||||||
|
else
|
||||||
$(BUILD)/firmware.bin: $(BUILD)/circuitpython-firmware.bin | esp-idf-stamp
|
$(BUILD)/firmware.bin: $(BUILD)/circuitpython-firmware.bin | esp-idf-stamp
|
||||||
$(Q)$(PYTHON) ../../tools/join_bins.py $@ $(BOOTLOADER_OFFSET) $(BUILD)/esp-idf/bootloader/bootloader.bin $(PARTITION_TABLE_OFFSET) $(BUILD)/esp-idf/partition_table/partition-table.bin $(FIRMWARE_OFFSET) $(BUILD)/circuitpython-firmware.bin
|
$(Q)$(PYTHON) ../../tools/join_bins.py $@ $(BOOTLOADER_OFFSET) $(BUILD)/esp-idf/bootloader/bootloader.bin $(PARTITION_TABLE_OFFSET) $(BUILD)/esp-idf/partition_table/partition-table.bin $(FIRMWARE_OFFSET) $(BUILD)/circuitpython-firmware.bin
|
||||||
|
endif
|
||||||
|
|
||||||
UF2_FAMILY_ID_esp32s2 = 0xbfdd4eee
|
UF2_FAMILY_ID_esp32s2 = 0xbfdd4eee
|
||||||
UF2_FAMILY_ID_esp32s3 = 0xc47e5767
|
UF2_FAMILY_ID_esp32s3 = 0xc47e5767
|
||||||
|
@ -133,10 +133,14 @@ SRC_QSTR_PREPROCESSOR +=
|
|||||||
|
|
||||||
all: $(BUILD)/firmware.bin $(BUILD)/firmware.dfu
|
all: $(BUILD)/firmware.bin $(BUILD)/firmware.dfu
|
||||||
|
|
||||||
|
ifeq ($(VALID_BOARD),)
|
||||||
|
$(BUILD)/firmware.elf: invalid-board
|
||||||
|
else
|
||||||
$(BUILD)/firmware.elf: $(OBJ)
|
$(BUILD)/firmware.elf: $(OBJ)
|
||||||
$(STEPECHO) "LINK $@"
|
$(STEPECHO) "LINK $@"
|
||||||
$(Q)$(CC) -o $@ $(LDFLAGS) $^ -Wl,--start-group $(LIBS) -Wl,--end-group
|
$(Q)$(CC) -o $@ $(LDFLAGS) $^ -Wl,--start-group $(LIBS) -Wl,--end-group
|
||||||
$(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $(LD_FILE) $(BUILD)
|
$(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $(LD_FILE) $(BUILD)
|
||||||
|
endif
|
||||||
|
|
||||||
$(BUILD)/firmware.bin: $(BUILD)/firmware.elf
|
$(BUILD)/firmware.bin: $(BUILD)/firmware.elf
|
||||||
$(STEPECHO) "Create $@"
|
$(STEPECHO) "Create $@"
|
||||||
|
@ -200,9 +200,13 @@ SRC_QSTR += $(SRC_C) $(SRC_SUPERVISOR) $(SRC_COMMON_HAL_EXPANDED) $(SRC_SHARED_M
|
|||||||
|
|
||||||
all: $(BUILD)/firmware.bin $(BUILD)/firmware.uf2 $(BUILD)/firmware.hex
|
all: $(BUILD)/firmware.bin $(BUILD)/firmware.uf2 $(BUILD)/firmware.hex
|
||||||
|
|
||||||
|
ifeq ($(VALID_BOARD),)
|
||||||
|
$(BUILD)/firmware.elf: invalid-board
|
||||||
|
else
|
||||||
$(BUILD)/firmware.elf: $(OBJ) $(LD_FILES)
|
$(BUILD)/firmware.elf: $(OBJ) $(LD_FILES)
|
||||||
$(STEPECHO) "LINK $@"
|
$(STEPECHO) "LINK $@"
|
||||||
$(Q)$(CC) -o $@ $(LDFLAGS) $(filter-out %.ld, $^) -Wl,--print-memory-usage -Wl,--start-group $(LIBS) -Wl,--end-group
|
$(Q)$(CC) -o $@ $(LDFLAGS) $(filter-out %.ld, $^) -Wl,--print-memory-usage -Wl,--start-group $(LIBS) -Wl,--end-group
|
||||||
|
endif
|
||||||
|
|
||||||
# -R excludes sections from the output files.
|
# -R excludes sections from the output files.
|
||||||
$(BUILD)/firmware.bin: $(BUILD)/firmware.elf
|
$(BUILD)/firmware.bin: $(BUILD)/firmware.elf
|
||||||
|
@ -208,11 +208,15 @@ UF2_FAMILY_ID_nrf52833 = 0x621E937A
|
|||||||
|
|
||||||
all: $(BUILD)/firmware.bin $(BUILD)/firmware.uf2 $(BUILD)/firmware.combined.hex
|
all: $(BUILD)/firmware.bin $(BUILD)/firmware.uf2 $(BUILD)/firmware.combined.hex
|
||||||
|
|
||||||
|
ifeq ($(VALID_BOARD),)
|
||||||
|
$(BUILD)/firmware.elf: invalid-board
|
||||||
|
else
|
||||||
$(BUILD)/firmware.elf: $(OBJ) $(GENERATED_LD_FILE)
|
$(BUILD)/firmware.elf: $(OBJ) $(GENERATED_LD_FILE)
|
||||||
$(STEPECHO) "LINK $@"
|
$(STEPECHO) "LINK $@"
|
||||||
$(Q)echo $(OBJ) > $(BUILD)/firmware.objs
|
$(Q)echo $(OBJ) > $(BUILD)/firmware.objs
|
||||||
$(Q)$(CC) -o $@ $(LDFLAGS) @$(BUILD)/firmware.objs -Wl,--start-group $(LIBS) -Wl,--end-group
|
$(Q)$(CC) -o $@ $(LDFLAGS) @$(BUILD)/firmware.objs -Wl,--start-group $(LIBS) -Wl,--end-group
|
||||||
$(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $(GENERATED_LD_FILE) $(BUILD)
|
$(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $(GENERATED_LD_FILE) $(BUILD)
|
||||||
|
endif
|
||||||
|
|
||||||
$(BUILD)/firmware.bin: $(BUILD)/firmware.elf
|
$(BUILD)/firmware.bin: $(BUILD)/firmware.elf
|
||||||
$(STEPECHO) "Create $@"
|
$(STEPECHO) "Create $@"
|
||||||
|
@ -31,8 +31,6 @@
|
|||||||
#define MICROPY_HW_MCU_NAME "nRF52833"
|
#define MICROPY_HW_MCU_NAME "nRF52833"
|
||||||
|
|
||||||
#define MICROPY_HW_LED_STATUS (&pin_P0_13)
|
#define MICROPY_HW_LED_STATUS (&pin_P0_13)
|
||||||
#define MICROPY_HW_LED_TX (&pin_P0_14)
|
|
||||||
#define MICROPY_HW_LED_RX (&pin_P0_15)
|
|
||||||
|
|
||||||
#define CIRCUITPY_INTERNAL_NVM_SIZE 0
|
#define CIRCUITPY_INTERNAL_NVM_SIZE 0
|
||||||
#define CIRCUITPY_INTERNAL_FLASH_FILESYSTEM_SIZE (60 * 1024)
|
#define CIRCUITPY_INTERNAL_FLASH_FILESYSTEM_SIZE (60 * 1024)
|
||||||
|
@ -45,10 +45,14 @@ STATIC const mp_rom_map_elem_t board_module_globals_table[] = {
|
|||||||
{ MP_ROM_QSTR(MP_QSTR_LED3), MP_ROM_PTR(&pin_P0_15) },
|
{ MP_ROM_QSTR(MP_QSTR_LED3), MP_ROM_PTR(&pin_P0_15) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_LED4), MP_ROM_PTR(&pin_P0_16) },
|
{ MP_ROM_QSTR(MP_QSTR_LED4), MP_ROM_PTR(&pin_P0_16) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_BUTTON1), MP_ROM_PTR(&pin_P0_11) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_BUTTON1_DEFAULT), MP_ROM_PTR(&pin_P0_11) },
|
{ MP_ROM_QSTR(MP_QSTR_BUTTON1_DEFAULT), MP_ROM_PTR(&pin_P0_11) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_BUTTON1_OPTIONAL), MP_ROM_PTR(&pin_P1_07) },
|
{ MP_ROM_QSTR(MP_QSTR_BUTTON1_OPTIONAL), MP_ROM_PTR(&pin_P1_07) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_BUTTON2), MP_ROM_PTR(&pin_P0_12) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_BUTTON2_DEFAULT), MP_ROM_PTR(&pin_P0_12) },
|
{ MP_ROM_QSTR(MP_QSTR_BUTTON2_DEFAULT), MP_ROM_PTR(&pin_P0_12) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_BUTTON2_OPTIONAL), MP_ROM_PTR(&pin_P1_08) },
|
{ MP_ROM_QSTR(MP_QSTR_BUTTON2_OPTIONAL), MP_ROM_PTR(&pin_P1_08) },
|
||||||
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_BUTTON3), MP_ROM_PTR(&pin_P0_24) },
|
{ MP_ROM_QSTR(MP_QSTR_BUTTON3), MP_ROM_PTR(&pin_P0_24) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_BUTTON4), MP_ROM_PTR(&pin_P0_25) },
|
{ MP_ROM_QSTR(MP_QSTR_BUTTON4), MP_ROM_PTR(&pin_P0_25) },
|
||||||
};
|
};
|
||||||
|
@ -434,11 +434,15 @@ endif
|
|||||||
|
|
||||||
LINKER_SCRIPTS += -Wl,-T,link.ld
|
LINKER_SCRIPTS += -Wl,-T,link.ld
|
||||||
|
|
||||||
|
ifeq ($(VALID_BOARD),)
|
||||||
|
$(BUILD)/firmware.elf: invalid-board
|
||||||
|
else
|
||||||
$(BUILD)/firmware.elf: $(OBJ) $(BOARD_LD) link.ld
|
$(BUILD)/firmware.elf: $(OBJ) $(BOARD_LD) link.ld
|
||||||
$(STEPECHO) "LINK $@"
|
$(STEPECHO) "LINK $@"
|
||||||
$(Q)echo $(OBJ) > $(BUILD)/firmware.objs
|
$(Q)echo $(OBJ) > $(BUILD)/firmware.objs
|
||||||
$(Q)echo $(PICO_LDFLAGS) > $(BUILD)/firmware.ldflags
|
$(Q)echo $(PICO_LDFLAGS) > $(BUILD)/firmware.ldflags
|
||||||
$(Q)$(CC) -o $@ $(CFLAGS) @$(BUILD)/firmware.ldflags $(LINKER_SCRIPTS) -Wl,--print-memory-usage -Wl,-Map=$@.map -Wl,-cref -Wl,--gc-sections @$(BUILD)/firmware.objs -Wl,-lc
|
$(Q)$(CC) -o $@ $(CFLAGS) @$(BUILD)/firmware.ldflags $(LINKER_SCRIPTS) -Wl,--print-memory-usage -Wl,-Map=$@.map -Wl,-cref -Wl,--gc-sections @$(BUILD)/firmware.objs -Wl,-lc
|
||||||
|
endif
|
||||||
|
|
||||||
$(BUILD)/firmware.bin: $(BUILD)/firmware.elf
|
$(BUILD)/firmware.bin: $(BUILD)/firmware.elf
|
||||||
$(STEPECHO) "Create $@"
|
$(STEPECHO) "Create $@"
|
||||||
|
@ -206,9 +206,9 @@ const uint8_t display_start_sequence[] = {
|
|||||||
// Look up tables for voltage sequence for pixel transition
|
// Look up tables for voltage sequence for pixel transition
|
||||||
// Common voltage
|
// Common voltage
|
||||||
LUT_VCOM, 44,
|
LUT_VCOM, 44,
|
||||||
0x00, 0x16, 0x16, 0x0d, 0x00, 0x01,
|
0x00, 0x64, 0x64, 0x37, 0x00, 0x01,
|
||||||
0x00, 0x23, 0x23, 0x00, 0x00, 0x02,
|
0x00, 0x8c, 0x8c, 0x00, 0x00, 0x04,
|
||||||
0x00, 0x16, 0x16, 0x0d, 0x00, 0x01,
|
0x00, 0x64, 0x64, 0x37, 0x00, 0x01,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
@ -217,9 +217,9 @@ const uint8_t display_start_sequence[] = {
|
|||||||
|
|
||||||
// White to white
|
// White to white
|
||||||
LUT_WW, 42,
|
LUT_WW, 42,
|
||||||
0x54, 0x16, 0x16, 0x0d, 0x00, 0x01,
|
0x54, 0x64, 0x64, 0x37, 0x00, 0x01,
|
||||||
0x60, 0x23, 0x23, 0x00, 0x00, 0x02,
|
0x60, 0x8c, 0x8c, 0x00, 0x00, 0x04,
|
||||||
0xa8, 0x16, 0x16, 0x0d, 0x00, 0x01,
|
0xa8, 0x64, 0x64, 0x37, 0x00, 0x01,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
@ -227,9 +227,9 @@ const uint8_t display_start_sequence[] = {
|
|||||||
|
|
||||||
// Black to white
|
// Black to white
|
||||||
LUT_BW, 42,
|
LUT_BW, 42,
|
||||||
0x54, 0x16, 0x16, 0x0d, 0x00, 0x01,
|
0x54, 0x64, 0x64, 0x37, 0x00, 0x01,
|
||||||
0x60, 0x23, 0x23, 0x00, 0x00, 0x02,
|
0x60, 0x8c, 0x8c, 0x00, 0x00, 0x04,
|
||||||
0xa8, 0x16, 0x16, 0x0d, 0x00, 0x01,
|
0xa8, 0x64, 0x64, 0x37, 0x00, 0x01,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
@ -237,9 +237,9 @@ const uint8_t display_start_sequence[] = {
|
|||||||
|
|
||||||
// White to black
|
// White to black
|
||||||
LUT_WB, 42,
|
LUT_WB, 42,
|
||||||
0xa8, 0x16, 0x16, 0x0d, 0x00, 0x01,
|
0xa8, 0x64, 0x64, 0x37, 0x00, 0x01,
|
||||||
0x60, 0x23, 0x23, 0x00, 0x00, 0x02,
|
0x60, 0x8c, 0x8c, 0x00, 0x00, 0x04,
|
||||||
0x54, 0x16, 0x16, 0x0d, 0x00, 0x01,
|
0x54, 0x64, 0x64, 0x37, 0x00, 0x01,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
@ -247,9 +247,9 @@ const uint8_t display_start_sequence[] = {
|
|||||||
|
|
||||||
// Black to black
|
// Black to black
|
||||||
LUT_BB, 42,
|
LUT_BB, 42,
|
||||||
0xa8, 0x16, 0x16, 0x0d, 0x00, 0x01,
|
0xa8, 0x64, 0x64, 0x37, 0x00, 0x01,
|
||||||
0x60, 0x23, 0x23, 0x00, 0x00, 0x02,
|
0x60, 0x8c, 0x8c, 0x00, 0x00, 0x04,
|
||||||
0x54, 0x16, 0x16, 0x0d, 0x00, 0x01,
|
0x54, 0x64, 0x64, 0x37, 0x00, 0x01,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
@ -137,9 +137,13 @@ MCU_SECTIONS = $^ $@
|
|||||||
# Default goal
|
# Default goal
|
||||||
all: $(OUTPUT_DIR)/firmware.bin
|
all: $(OUTPUT_DIR)/firmware.bin
|
||||||
|
|
||||||
|
ifeq ($(VALID_BOARD),)
|
||||||
|
$(BUILD)/firmware.bin: invalid-board
|
||||||
|
else
|
||||||
$(OUTPUT_DIR)/firmware.bin: $(SILABS_BUILD)/$(PROJECTNAME).Makefile $(OUTPUT_DIR)/firmware.hex
|
$(OUTPUT_DIR)/firmware.bin: $(SILABS_BUILD)/$(PROJECTNAME).Makefile $(OUTPUT_DIR)/firmware.hex
|
||||||
+@$(MAKE) --no-print-directory $(OUTPUT_DIR)/firmware.out
|
+@$(MAKE) --no-print-directory $(OUTPUT_DIR)/firmware.out
|
||||||
@echo 'Done.'
|
@echo 'Done.'
|
||||||
|
endif
|
||||||
|
|
||||||
$(SILABS_BUILD)/$(PROJECTNAME).Makefile:
|
$(SILABS_BUILD)/$(PROJECTNAME).Makefile:
|
||||||
+@$(MAKE) --no-print-directory slc-generate
|
+@$(MAKE) --no-print-directory slc-generate
|
||||||
|
@ -48,9 +48,9 @@ Ensure your clone of CircuitPython is ready to build by following the [guide on
|
|||||||
|
|
||||||
Clone the source code of CircuitPython from GitHub:
|
Clone the source code of CircuitPython from GitHub:
|
||||||
|
|
||||||
git clone https://github.com/adafruit/circuitpython.git
|
$ git clone https://github.com/SiliconLabs/circuitpython.git
|
||||||
cd circuitpython
|
$ cd circuitpython/ports/silabs
|
||||||
make fetch-submodules
|
$ make fetch-port-submodules
|
||||||
|
|
||||||
Checkout the branch or tag you want to build. For example:
|
Checkout the branch or tag you want to build. For example:
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ INC += -I../../supervisor/shared/usb
|
|||||||
#Debugging/Optimization
|
#Debugging/Optimization
|
||||||
ifeq ($(DEBUG), 1)
|
ifeq ($(DEBUG), 1)
|
||||||
CFLAGS += -ggdb3
|
CFLAGS += -ggdb3
|
||||||
# You may want to enable these flags to make setting breakpoints easier.
|
# You may want to enable these flags to make setting breakpoints easier.
|
||||||
CFLAGS += -fno-inline -fno-ipa-sra
|
CFLAGS += -fno-inline -fno-ipa-sra
|
||||||
else
|
else
|
||||||
CFLAGS += -DNDEBUG
|
CFLAGS += -DNDEBUG
|
||||||
@ -264,11 +264,15 @@ endif
|
|||||||
|
|
||||||
all: $(BUILD)/firmware.bin $(BUILD)/firmware.uf2
|
all: $(BUILD)/firmware.bin $(BUILD)/firmware.uf2
|
||||||
|
|
||||||
|
ifeq ($(VALID_BOARD),)
|
||||||
|
$(BUILD)/firmware.elf: invalid-board
|
||||||
|
else
|
||||||
$(BUILD)/firmware.elf: $(OBJ)
|
$(BUILD)/firmware.elf: $(OBJ)
|
||||||
$(STEPECHO) "LINK $@"
|
$(STEPECHO) "LINK $@"
|
||||||
$(Q)echo $^ > $(BUILD)/firmware.objs
|
$(Q)echo $^ > $(BUILD)/firmware.objs
|
||||||
$(Q)$(CC) -o $@ $(LDFLAGS) @$(BUILD)/firmware.objs -Wl,--start-group $(LIBS) -Wl,--end-group
|
$(Q)$(CC) -o $@ $(LDFLAGS) @$(BUILD)/firmware.objs -Wl,--start-group $(LIBS) -Wl,--end-group
|
||||||
$(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $(LD_FILE) $(BUILD)
|
$(Q)$(SIZE) $@ | $(PYTHON) $(TOP)/tools/build_memory_info.py $(LD_FILE) $(BUILD)
|
||||||
|
endif
|
||||||
|
|
||||||
$(BUILD)/firmware.bin: $(BUILD)/firmware.elf
|
$(BUILD)/firmware.bin: $(BUILD)/firmware.elf
|
||||||
$(STEPECHO) "Create $@"
|
$(STEPECHO) "Create $@"
|
||||||
|
@ -45,6 +45,7 @@ SRC_BITMAP := \
|
|||||||
shared-bindings/synthio/MidiTrack.c \
|
shared-bindings/synthio/MidiTrack.c \
|
||||||
shared-bindings/synthio/LFO.c \
|
shared-bindings/synthio/LFO.c \
|
||||||
shared-bindings/synthio/Note.c \
|
shared-bindings/synthio/Note.c \
|
||||||
|
shared-bindings/synthio/Biquad.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 \
|
||||||
@ -70,6 +71,7 @@ SRC_BITMAP := \
|
|||||||
shared-module/synthio/MidiTrack.c \
|
shared-module/synthio/MidiTrack.c \
|
||||||
shared-module/synthio/LFO.c \
|
shared-module/synthio/LFO.c \
|
||||||
shared-module/synthio/Note.c \
|
shared-module/synthio/Note.c \
|
||||||
|
shared-module/synthio/Biquad.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 \
|
||||||
|
@ -650,6 +650,7 @@ SRC_SHARED_MODULE_ALL = \
|
|||||||
struct/__init__.c \
|
struct/__init__.c \
|
||||||
supervisor/__init__.c \
|
supervisor/__init__.c \
|
||||||
supervisor/StatusBar.c \
|
supervisor/StatusBar.c \
|
||||||
|
synthio/Biquad.c \
|
||||||
synthio/LFO.c \
|
synthio/LFO.c \
|
||||||
synthio/Math.c \
|
synthio/Math.c \
|
||||||
synthio/MidiTrack.c \
|
synthio/MidiTrack.c \
|
||||||
@ -829,3 +830,15 @@ check-release-needs-clean-build:
|
|||||||
|
|
||||||
# Ignore these errors
|
# Ignore these errors
|
||||||
$(BUILD)/lib/libm/kf_rem_pio2.o: CFLAGS += -Wno-maybe-uninitialized
|
$(BUILD)/lib/libm/kf_rem_pio2.o: CFLAGS += -Wno-maybe-uninitialized
|
||||||
|
|
||||||
|
# Fetch only submodules needed for this particular port.
|
||||||
|
.PHONY: fetch-port-submodules
|
||||||
|
fetch-port-submodules:
|
||||||
|
$(TOP)/tools/fetch-submodules.sh data extmod frozen lib tools ports/$(shell basename $(CURDIR))
|
||||||
|
|
||||||
|
.PHONY: invalid-board
|
||||||
|
invalid-board:
|
||||||
|
$(Q)if [ -z "$(BOARD)" ] ; then echo "ERROR: No BOARD specified" ; else echo "ERROR: Invalid BOARD $(BOARD) specified"; fi && \
|
||||||
|
echo "Valid boards:" && \
|
||||||
|
printf '%s\n' $(ALL_BOARDS_IN_PORT) | column -xc $$(tput cols || echo 80) 1>&2 && \
|
||||||
|
false
|
||||||
|
@ -24,22 +24,10 @@
|
|||||||
|
|
||||||
# Common Makefile items that can be shared across CircuitPython ports.
|
# Common Makefile items that can be shared across CircuitPython ports.
|
||||||
|
|
||||||
# Select the board to build for.
|
ALL_BOARDS_IN_PORT := $(patsubst boards/%/mpconfigboard.mk,%,$(wildcard boards/*/mpconfigboard.mk))
|
||||||
define show_board_error
|
# An incorrect BOARD might have been specified, so check against the list.
|
||||||
$(info Valid boards:)
|
# There is deliberately no space after the :=
|
||||||
$(shell printf '%s\n' $(patsubst boards/%/mpconfigboard.mk,%,$(wildcard boards/*/mpconfigboard.mk)) | column -xc $$(tput cols || echo 80) 1>&2)
|
VALID_BOARD :=$(filter $(BOARD),$(ALL_BOARDS_IN_PORT))
|
||||||
$(error Rerun with $(MAKE) BOARD=<board>)
|
|
||||||
endef
|
|
||||||
|
|
||||||
ifeq ($(BOARD),)
|
|
||||||
$(info No BOARD specified)
|
|
||||||
$(call show_board_error)
|
|
||||||
else
|
|
||||||
ifeq ($(wildcard boards/$(BOARD)/.),)
|
|
||||||
$(info Invalid BOARD specified)
|
|
||||||
$(call show_board_error)
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
# If the flash PORT is not given, use the default /dev/tty.SLAB_USBtoUART.
|
# If the flash PORT is not given, use the default /dev/tty.SLAB_USBtoUART.
|
||||||
PORT ?= /dev/tty.SLAB_USBtoUART
|
PORT ?= /dev/tty.SLAB_USBtoUART
|
||||||
@ -47,16 +35,22 @@ PORT ?= /dev/tty.SLAB_USBtoUART
|
|||||||
# If the build directory is not given, make it reflect the board name.
|
# If the build directory is not given, make it reflect the board name.
|
||||||
BUILD ?= build-$(BOARD)
|
BUILD ?= build-$(BOARD)
|
||||||
|
|
||||||
|
# First makefile with targets. Defines the default target.
|
||||||
include ../../py/mkenv.mk
|
include ../../py/mkenv.mk
|
||||||
|
|
||||||
# Board-specific
|
# Board-specific. Skip if the rule requested is not board-specific.
|
||||||
|
ifneq ($(VALID_BOARD),)
|
||||||
include boards/$(BOARD)/mpconfigboard.mk
|
include boards/$(BOARD)/mpconfigboard.mk
|
||||||
|
endif
|
||||||
|
|
||||||
# Port-specific
|
# Port-specific
|
||||||
include mpconfigport.mk
|
include mpconfigport.mk
|
||||||
|
|
||||||
|
# Also board-specific. Skip if the rule requested is not board-specific.
|
||||||
|
ifneq ($(VALID_BOARD),)
|
||||||
# CircuitPython-specific
|
# CircuitPython-specific
|
||||||
include $(TOP)/py/circuitpy_mpconfig.mk
|
include $(TOP)/py/circuitpy_mpconfig.mk
|
||||||
|
endif
|
||||||
|
|
||||||
# qstr definitions (must come before including py.mk)
|
# qstr definitions (must come before including py.mk)
|
||||||
QSTR_DEFS = qstrdefsport.h
|
QSTR_DEFS = qstrdefsport.h
|
||||||
|
@ -59,8 +59,8 @@ extern void common_hal_mcu_enable_interrupts(void);
|
|||||||
//
|
//
|
||||||
// default is 128; consider raising to reduce fragmentation.
|
// default is 128; consider raising to reduce fragmentation.
|
||||||
#define MICROPY_ALLOC_PARSE_CHUNK_INIT (16)
|
#define MICROPY_ALLOC_PARSE_CHUNK_INIT (16)
|
||||||
// default is 512.
|
// default is 512. Longest path in .py bundle as of June 6th, 2023 is 73 characters.
|
||||||
#define MICROPY_ALLOC_PATH_MAX (256)
|
#define MICROPY_ALLOC_PATH_MAX (96)
|
||||||
#define MICROPY_CAN_OVERRIDE_BUILTINS (1)
|
#define MICROPY_CAN_OVERRIDE_BUILTINS (1)
|
||||||
#define MICROPY_COMP_CONST (1)
|
#define MICROPY_COMP_CONST (1)
|
||||||
#define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (1)
|
#define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (1)
|
||||||
|
10
py/runtime.c
10
py/runtime.c
@ -201,7 +201,7 @@ mp_obj_t MICROPY_WRAP_MP_LOAD_GLOBAL(mp_load_global)(qstr qst) {
|
|||||||
return elem->value;
|
return elem->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
mp_obj_t mp_load_build_class(void) {
|
mp_obj_t __attribute__((noinline)) mp_load_build_class(void) {
|
||||||
DEBUG_OP_printf("load_build_class\n");
|
DEBUG_OP_printf("load_build_class\n");
|
||||||
#if MICROPY_CAN_OVERRIDE_BUILTINS
|
#if MICROPY_CAN_OVERRIDE_BUILTINS
|
||||||
if (MP_STATE_VM(mp_module_builtins_override_dict) != NULL) {
|
if (MP_STATE_VM(mp_module_builtins_override_dict) != NULL) {
|
||||||
@ -858,7 +858,7 @@ mp_obj_t mp_call_method_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ob
|
|||||||
}
|
}
|
||||||
|
|
||||||
// unpacked items are stored in reverse order into the array pointed to by items
|
// unpacked items are stored in reverse order into the array pointed to by items
|
||||||
void mp_unpack_sequence(mp_obj_t seq_in, size_t num, mp_obj_t *items) {
|
void __attribute__((noinline,)) mp_unpack_sequence(mp_obj_t seq_in, size_t num, mp_obj_t *items) {
|
||||||
size_t seq_len;
|
size_t seq_len;
|
||||||
if (mp_obj_is_type(seq_in, &mp_type_tuple) || mp_obj_is_type(seq_in, &mp_type_list)) {
|
if (mp_obj_is_type(seq_in, &mp_type_tuple) || mp_obj_is_type(seq_in, &mp_type_list)) {
|
||||||
mp_obj_t *seq_items;
|
mp_obj_t *seq_items;
|
||||||
@ -905,7 +905,7 @@ too_long:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// unpacked items are stored in reverse order into the array pointed to by items
|
// unpacked items are stored in reverse order into the array pointed to by items
|
||||||
void mp_unpack_ex(mp_obj_t seq_in, size_t num_in, mp_obj_t *items) {
|
void __attribute__((noinline)) mp_unpack_ex(mp_obj_t seq_in, size_t num_in, mp_obj_t *items) {
|
||||||
size_t num_left = num_in & 0xff;
|
size_t num_left = num_in & 0xff;
|
||||||
size_t num_right = (num_in >> 8) & 0xff;
|
size_t num_right = (num_in >> 8) & 0xff;
|
||||||
DEBUG_OP_printf("unpack ex " UINT_FMT " " UINT_FMT "\n", num_left, num_right);
|
DEBUG_OP_printf("unpack ex " UINT_FMT " " UINT_FMT "\n", num_left, num_right);
|
||||||
@ -1482,7 +1482,7 @@ mp_obj_t mp_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level) {
|
|||||||
return mp_builtin___import__(5, args);
|
return mp_builtin___import__(5, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
mp_obj_t mp_import_from(mp_obj_t module, qstr name) {
|
mp_obj_t __attribute__((noinline,)) mp_import_from(mp_obj_t module, qstr name) {
|
||||||
DEBUG_printf("import from %p %s\n", module, qstr_str(name));
|
DEBUG_printf("import from %p %s\n", module, qstr_str(name));
|
||||||
|
|
||||||
mp_obj_t dest[2];
|
mp_obj_t dest[2];
|
||||||
@ -1528,7 +1528,7 @@ mp_obj_t mp_import_from(mp_obj_t module, qstr name) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void mp_import_all(mp_obj_t module) {
|
void __attribute__((noinline)) mp_import_all(mp_obj_t module) {
|
||||||
DEBUG_printf("import all %p\n", module);
|
DEBUG_printf("import all %p\n", module);
|
||||||
|
|
||||||
// TODO: Support __all__
|
// TODO: Support __all__
|
||||||
|
110
shared-bindings/synthio/Biquad.c
Normal file
110
shared-bindings/synthio/Biquad.c
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the MicroPython project, http://micropython.org/
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 Artyom Skrobov
|
||||||
|
*
|
||||||
|
* 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 <string.h>
|
||||||
|
|
||||||
|
#include "py/enum.h"
|
||||||
|
#include "py/mperrno.h"
|
||||||
|
#include "py/obj.h"
|
||||||
|
#include "py/objnamedtuple.h"
|
||||||
|
#include "py/runtime.h"
|
||||||
|
|
||||||
|
#include "shared-bindings/synthio/__init__.h"
|
||||||
|
#include "shared-bindings/synthio/LFO.h"
|
||||||
|
#include "shared-bindings/synthio/Math.h"
|
||||||
|
#include "shared-bindings/synthio/MidiTrack.h"
|
||||||
|
#include "shared-bindings/synthio/Note.h"
|
||||||
|
#include "shared-bindings/synthio/Synthesizer.h"
|
||||||
|
|
||||||
|
#include "shared-module/synthio/LFO.h"
|
||||||
|
|
||||||
|
#define default_attack_time (MICROPY_FLOAT_CONST(0.1))
|
||||||
|
#define default_decay_time (MICROPY_FLOAT_CONST(0.05))
|
||||||
|
#define default_release_time (MICROPY_FLOAT_CONST(0.2))
|
||||||
|
#define default_attack_level (MICROPY_FLOAT_CONST(1.))
|
||||||
|
#define default_sustain_level (MICROPY_FLOAT_CONST(0.8))
|
||||||
|
|
||||||
|
static const mp_arg_t biquad_properties[] = {
|
||||||
|
{ MP_QSTR_a1, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_ROM_NONE} },
|
||||||
|
{ MP_QSTR_a2, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_ROM_NONE} },
|
||||||
|
{ MP_QSTR_b0, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_ROM_NONE} },
|
||||||
|
{ MP_QSTR_b1, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_ROM_NONE} },
|
||||||
|
{ MP_QSTR_b2, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_ROM_NONE} },
|
||||||
|
};
|
||||||
|
|
||||||
|
//| class Biquad:
|
||||||
|
//| def __init__(self, b0: float, b1: float, b2: float, a1: float, a2: float) -> None:
|
||||||
|
//| """Construct a normalized biquad filter object.
|
||||||
|
//|
|
||||||
|
//| This implements the "direct form 1" biquad filter, where each coefficient
|
||||||
|
//| has been pre-divided by a0.
|
||||||
|
//|
|
||||||
|
//| Biquad objects are usually constructed via one of the related methods on a `Synthesizer` object
|
||||||
|
//| rather than directly from coefficients.
|
||||||
|
//|
|
||||||
|
//| https://github.com/WebAudio/Audio-EQ-Cookbook/blob/main/Audio-EQ-Cookbook.txt
|
||||||
|
//| """
|
||||||
|
//|
|
||||||
|
STATIC mp_obj_t synthio_biquad_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(biquad_properties)];
|
||||||
|
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(biquad_properties), biquad_properties, args);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < MP_ARRAY_SIZE(biquad_properties); i++) {
|
||||||
|
args[i].u_obj = mp_obj_new_float(mp_arg_validate_type_float(args[i].u_obj, biquad_properties[i].qst));
|
||||||
|
}
|
||||||
|
|
||||||
|
MP_STATIC_ASSERT(sizeof(mp_arg_val_t) == sizeof(mp_obj_t));
|
||||||
|
return namedtuple_make_new(type_in, MP_ARRAY_SIZE(args), 0, &args[0].u_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mp_obj_namedtuple_type_t synthio_biquad_type_obj = {
|
||||||
|
.base = {
|
||||||
|
.base = {
|
||||||
|
.type = &mp_type_type
|
||||||
|
},
|
||||||
|
.flags = MP_TYPE_FLAG_EXTENDED,
|
||||||
|
.name = MP_QSTR_Biquad,
|
||||||
|
.print = namedtuple_print,
|
||||||
|
.parent = &mp_type_tuple,
|
||||||
|
.make_new = synthio_biquad_make_new,
|
||||||
|
.attr = namedtuple_attr,
|
||||||
|
MP_TYPE_EXTENDED_FIELDS(
|
||||||
|
.unary_op = mp_obj_tuple_unary_op,
|
||||||
|
.binary_op = mp_obj_tuple_binary_op,
|
||||||
|
.subscr = mp_obj_tuple_subscr,
|
||||||
|
.getiter = mp_obj_tuple_getiter,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
.n_fields = 5,
|
||||||
|
.fields = {
|
||||||
|
MP_QSTR_a1,
|
||||||
|
MP_QSTR_a2,
|
||||||
|
MP_QSTR_b0,
|
||||||
|
MP_QSTR_b1,
|
||||||
|
MP_QSTR_b2,
|
||||||
|
},
|
||||||
|
};
|
9
shared-bindings/synthio/Biquad.h
Normal file
9
shared-bindings/synthio/Biquad.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "py/obj.h"
|
||||||
|
#include "py/objnamedtuple.h"
|
||||||
|
|
||||||
|
extern const mp_obj_namedtuple_type_t synthio_biquad_type_obj;
|
||||||
|
mp_obj_t common_hal_synthio_new_lpf(mp_float_t w0, mp_float_t Q);
|
||||||
|
mp_obj_t common_hal_synthio_new_hpf(mp_float_t w0, mp_float_t Q);
|
||||||
|
mp_obj_t common_hal_synthio_new_bpf(mp_float_t w0, mp_float_t Q);
|
@ -41,7 +41,7 @@ static const mp_arg_t note_properties[] = {
|
|||||||
{ MP_QSTR_bend, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(0) } },
|
{ MP_QSTR_bend, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(0) } },
|
||||||
{ MP_QSTR_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },
|
{ 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_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_filter, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },
|
||||||
{ MP_QSTR_ring_frequency, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } },
|
{ MP_QSTR_ring_frequency, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } },
|
||||||
{ MP_QSTR_ring_bend, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } },
|
{ MP_QSTR_ring_bend, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } },
|
||||||
{ MP_QSTR_ring_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },
|
{ MP_QSTR_ring_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },
|
||||||
@ -56,6 +56,7 @@ static const mp_arg_t note_properties[] = {
|
|||||||
//| envelope: Optional[Envelope] = None,
|
//| envelope: Optional[Envelope] = None,
|
||||||
//| amplitude: BlockInput = 0.0,
|
//| amplitude: BlockInput = 0.0,
|
||||||
//| bend: BlockInput = 0.0,
|
//| bend: BlockInput = 0.0,
|
||||||
|
//| filter: Optional[Biquad] = None,
|
||||||
//| ring_frequency: float = 0.0,
|
//| ring_frequency: float = 0.0,
|
||||||
//| ring_bend: float = 0.0,
|
//| ring_bend: float = 0.0,
|
||||||
//| ring_waveform: Optional[ReadableBuffer] = 0.0,
|
//| ring_waveform: Optional[ReadableBuffer] = 0.0,
|
||||||
@ -97,17 +98,21 @@ MP_PROPERTY_GETSET(synthio_note_frequency_obj,
|
|||||||
(mp_obj_t)&synthio_note_get_frequency_obj,
|
(mp_obj_t)&synthio_note_get_frequency_obj,
|
||||||
(mp_obj_t)&synthio_note_set_frequency_obj);
|
(mp_obj_t)&synthio_note_set_frequency_obj);
|
||||||
|
|
||||||
//| filter: bool
|
//| filter: Optional[Biquad]
|
||||||
//| """True if the note should be processed via the synthesizer's FIR filter."""
|
//| """If not None, the output of this Note is filtered according to the provided coefficients.
|
||||||
|
//|
|
||||||
|
//| Construct an appropriate filter by calling a filter-making method on the
|
||||||
|
//| `Synthesizer` object where you plan to play the note, as filter coefficients depend
|
||||||
|
//| on the sample rate"""
|
||||||
STATIC mp_obj_t synthio_note_get_filter(mp_obj_t self_in) {
|
STATIC mp_obj_t synthio_note_get_filter(mp_obj_t self_in) {
|
||||||
synthio_note_obj_t *self = MP_OBJ_TO_PTR(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));
|
return common_hal_synthio_note_get_filter_obj(self);
|
||||||
}
|
}
|
||||||
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_filter_obj, synthio_note_get_filter);
|
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) {
|
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);
|
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
common_hal_synthio_note_set_filter(self, mp_obj_is_true(arg));
|
common_hal_synthio_note_set_filter(self, arg);
|
||||||
return mp_const_none;
|
return mp_const_none;
|
||||||
}
|
}
|
||||||
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_filter_obj, synthio_note_set_filter);
|
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_filter_obj, synthio_note_set_filter);
|
||||||
|
@ -9,8 +9,8 @@ typedef enum synthio_bend_mode_e synthio_bend_mode_t;
|
|||||||
mp_float_t common_hal_synthio_note_get_frequency(synthio_note_obj_t *self);
|
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);
|
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);
|
mp_obj_t common_hal_synthio_note_get_filter_obj(synthio_note_obj_t *self);
|
||||||
void common_hal_synthio_note_set_filter(synthio_note_obj_t *self, bool value);
|
void common_hal_synthio_note_set_filter(synthio_note_obj_t *self, mp_obj_t biquad);
|
||||||
|
|
||||||
mp_obj_t common_hal_synthio_note_get_panning(synthio_note_obj_t *self);
|
mp_obj_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_obj_t value);
|
void common_hal_synthio_note_set_panning(synthio_note_obj_t *self, mp_obj_t value);
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
#include "py/objproperty.h"
|
#include "py/objproperty.h"
|
||||||
#include "py/runtime.h"
|
#include "py/runtime.h"
|
||||||
#include "shared-bindings/util.h"
|
#include "shared-bindings/util.h"
|
||||||
|
#include "shared-bindings/synthio/Biquad.h"
|
||||||
#include "shared-bindings/synthio/Synthesizer.h"
|
#include "shared-bindings/synthio/Synthesizer.h"
|
||||||
#include "shared-bindings/synthio/LFO.h"
|
#include "shared-bindings/synthio/LFO.h"
|
||||||
#include "shared-bindings/synthio/__init__.h"
|
#include "shared-bindings/synthio/__init__.h"
|
||||||
@ -52,7 +53,6 @@
|
|||||||
//| channel_count: int = 1,
|
//| channel_count: int = 1,
|
||||||
//| waveform: Optional[ReadableBuffer] = None,
|
//| waveform: Optional[ReadableBuffer] = None,
|
||||||
//| envelope: Optional[Envelope] = None,
|
//| envelope: Optional[Envelope] = None,
|
||||||
//| filter: Optional[ReadableBuffer] = None,
|
|
||||||
//| ) -> None:
|
//| ) -> None:
|
||||||
//| """Create a synthesizer object.
|
//| """Create a synthesizer object.
|
||||||
//|
|
//|
|
||||||
@ -65,17 +65,15 @@
|
|||||||
//| :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 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_channel_count, ARG_waveform, ARG_envelope, ARG_filter };
|
enum { ARG_sample_rate, ARG_channel_count, ARG_waveform, ARG_envelope };
|
||||||
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_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);
|
||||||
@ -87,7 +85,6 @@ STATIC mp_obj_t synthio_synthesizer_make_new(const mp_obj_type_t *type, size_t n
|
|||||||
args[ARG_sample_rate].u_int,
|
args[ARG_sample_rate].u_int,
|
||||||
args[ARG_channel_count].u_int,
|
args[ARG_channel_count].u_int,
|
||||||
args[ARG_waveform].u_obj,
|
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);
|
||||||
@ -292,6 +289,114 @@ MP_PROPERTY_GETTER(synthio_synthesizer_blocks_obj,
|
|||||||
//| """Maximum polyphony of the synthesizer (read-only class property)"""
|
//| """Maximum polyphony of the synthesizer (read-only class property)"""
|
||||||
//|
|
//|
|
||||||
|
|
||||||
|
//| def low_pass_filter(cls, frequency: float, q_factor: float = 0.7071067811865475) -> Biquad:
|
||||||
|
//| """Construct a low-pass filter with the given parameters.
|
||||||
|
//|
|
||||||
|
//| ``frequency``, called f0 in the cookbook, is the corner frequency in Hz
|
||||||
|
//| of the filter.
|
||||||
|
//|
|
||||||
|
//| ``q_factor``, called ``Q`` in the cookbook. Controls how peaked the response will be at the cutoff frequency. A large value makes the response more peaked.
|
||||||
|
//| """
|
||||||
|
|
||||||
|
enum passfilter_arg_e { ARG_f0, ARG_Q };
|
||||||
|
|
||||||
|
// M_PI is not part of the math.h standard and may not be defined
|
||||||
|
// And by defining our own we can ensure it uses the correct const format.
|
||||||
|
#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846)
|
||||||
|
|
||||||
|
static const mp_arg_t passfilter_properties[] = {
|
||||||
|
{ MP_QSTR_frequency, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_ROM_NONE} },
|
||||||
|
{ MP_QSTR_Q, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL } },
|
||||||
|
};
|
||||||
|
|
||||||
|
STATIC mp_obj_t synthio_synthesizer_lpf(size_t n_pos, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||||
|
mp_arg_val_t args[MP_ARRAY_SIZE(passfilter_properties)];
|
||||||
|
|
||||||
|
mp_obj_t self_in = pos_args[0];
|
||||||
|
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
|
||||||
|
mp_arg_parse_all(n_pos - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(passfilter_properties), passfilter_properties, args);
|
||||||
|
|
||||||
|
mp_float_t f0 = mp_arg_validate_type_float(args[ARG_f0].u_obj, MP_QSTR_f0);
|
||||||
|
mp_float_t Q =
|
||||||
|
args[ARG_Q].u_obj == MP_OBJ_NULL ? MICROPY_FLOAT_CONST(0.7071067811865475) :
|
||||||
|
mp_arg_validate_type_float(args[ARG_Q].u_obj, MP_QSTR_Q);
|
||||||
|
|
||||||
|
mp_float_t w0 = f0 / self->synth.sample_rate * 2 * MP_PI;
|
||||||
|
|
||||||
|
return common_hal_synthio_new_lpf(w0, Q);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_KW(synthio_synthesizer_lpf_fun_obj, 1, synthio_synthesizer_lpf);
|
||||||
|
|
||||||
|
//| def high_pass_filter(
|
||||||
|
//| cls, frequency: float, q_factor: float = 0.7071067811865475
|
||||||
|
//| ) -> Biquad:
|
||||||
|
//| """Construct a high-pass filter with the given parameters.
|
||||||
|
//|
|
||||||
|
//| ``frequency``, called f0 in the cookbook, is the corner frequency in Hz
|
||||||
|
//| of the filter.
|
||||||
|
//|
|
||||||
|
//| ``q_factor``, called ``Q`` in the cookbook. Controls how peaked the response will be at the cutoff frequency. A large value makes the response more peaked.
|
||||||
|
//| """
|
||||||
|
|
||||||
|
STATIC mp_obj_t synthio_synthesizer_hpf(size_t n_pos, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||||
|
mp_arg_val_t args[MP_ARRAY_SIZE(passfilter_properties)];
|
||||||
|
|
||||||
|
mp_obj_t self_in = pos_args[0];
|
||||||
|
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
|
||||||
|
mp_arg_parse_all(n_pos - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(passfilter_properties), passfilter_properties, args);
|
||||||
|
|
||||||
|
mp_float_t f0 = mp_arg_validate_type_float(args[ARG_f0].u_obj, MP_QSTR_f0);
|
||||||
|
mp_float_t Q =
|
||||||
|
args[ARG_Q].u_obj == MP_OBJ_NULL ? MICROPY_FLOAT_CONST(0.7071067811865475) :
|
||||||
|
mp_arg_validate_type_float(args[ARG_Q].u_obj, MP_QSTR_Q);
|
||||||
|
|
||||||
|
mp_float_t w0 = f0 / self->synth.sample_rate * 2 * MP_PI;
|
||||||
|
|
||||||
|
return common_hal_synthio_new_hpf(w0, Q);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//| def band_pass_filter(
|
||||||
|
//| cls, frequency: float, q_factor: float = 0.7071067811865475
|
||||||
|
//| ) -> Biquad:
|
||||||
|
//| """Construct a band-pass filter with the given parameters.
|
||||||
|
//|
|
||||||
|
//| ``frequency``, called f0 in the cookbook, is the center frequency in Hz
|
||||||
|
//| of the filter.
|
||||||
|
//|
|
||||||
|
//| ``q_factor``, called ``Q`` in the cookbook. Controls how peaked the response will be at the cutoff frequency. A large value makes the response more peaked.
|
||||||
|
//|
|
||||||
|
//| The coefficients are scaled such that the filter has a 0dB peak gain.
|
||||||
|
//| """
|
||||||
|
//|
|
||||||
|
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_KW(synthio_synthesizer_hpf_fun_obj, 1, synthio_synthesizer_hpf);
|
||||||
|
|
||||||
|
STATIC mp_obj_t synthio_synthesizer_bpf(size_t n_pos, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||||
|
mp_arg_val_t args[MP_ARRAY_SIZE(passfilter_properties)];
|
||||||
|
|
||||||
|
mp_obj_t self_in = pos_args[0];
|
||||||
|
synthio_synthesizer_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||||
|
|
||||||
|
mp_arg_parse_all(n_pos - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(passfilter_properties), passfilter_properties, args);
|
||||||
|
|
||||||
|
mp_float_t f0 = mp_arg_validate_type_float(args[ARG_f0].u_obj, MP_QSTR_f0);
|
||||||
|
mp_float_t Q =
|
||||||
|
args[ARG_Q].u_obj == MP_OBJ_NULL ? MICROPY_FLOAT_CONST(0.7071067811865475) :
|
||||||
|
mp_arg_validate_type_float(args[ARG_Q].u_obj, MP_QSTR_Q);
|
||||||
|
|
||||||
|
mp_float_t w0 = f0 / self->synth.sample_rate * 2 * MP_PI;
|
||||||
|
|
||||||
|
return common_hal_synthio_new_bpf(w0, Q);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MP_DEFINE_CONST_FUN_OBJ_KW(synthio_synthesizer_bpf_fun_obj, 1, synthio_synthesizer_bpf);
|
||||||
|
|
||||||
STATIC const mp_rom_map_elem_t synthio_synthesizer_locals_dict_table[] = {
|
STATIC const mp_rom_map_elem_t synthio_synthesizer_locals_dict_table[] = {
|
||||||
// Methods
|
// Methods
|
||||||
{ MP_ROM_QSTR(MP_QSTR_press), MP_ROM_PTR(&synthio_synthesizer_press_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_press), MP_ROM_PTR(&synthio_synthesizer_press_obj) },
|
||||||
@ -304,6 +409,9 @@ STATIC const mp_rom_map_elem_t synthio_synthesizer_locals_dict_table[] = {
|
|||||||
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
|
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&synthio_synthesizer___exit___obj) },
|
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&synthio_synthesizer___exit___obj) },
|
||||||
|
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_low_pass_filter), MP_ROM_PTR(&synthio_synthesizer_lpf_fun_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_high_pass_filter), MP_ROM_PTR(&synthio_synthesizer_hpf_fun_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_band_pass_filter), MP_ROM_PTR(&synthio_synthesizer_bpf_fun_obj) },
|
||||||
// Properties
|
// Properties
|
||||||
{ MP_ROM_QSTR(MP_QSTR_envelope), MP_ROM_PTR(&synthio_synthesizer_envelope_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_envelope), MP_ROM_PTR(&synthio_synthesizer_envelope_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&synthio_synthesizer_sample_rate_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_sample_rate), MP_ROM_PTR(&synthio_synthesizer_sample_rate_obj) },
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
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, int channel_count, mp_obj_t waveform_obj, mp_obj_t filter_obj,
|
uint32_t sample_rate, int channel_count, mp_obj_t waveform_obj,
|
||||||
mp_obj_t envelope_obj);
|
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);
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include "extmod/vfs_posix.h"
|
#include "extmod/vfs_posix.h"
|
||||||
|
|
||||||
#include "shared-bindings/synthio/__init__.h"
|
#include "shared-bindings/synthio/__init__.h"
|
||||||
|
#include "shared-bindings/synthio/Biquad.h"
|
||||||
#include "shared-bindings/synthio/LFO.h"
|
#include "shared-bindings/synthio/LFO.h"
|
||||||
#include "shared-bindings/synthio/Math.h"
|
#include "shared-bindings/synthio/Math.h"
|
||||||
#include "shared-bindings/synthio/MidiTrack.h"
|
#include "shared-bindings/synthio/MidiTrack.h"
|
||||||
@ -310,6 +311,7 @@ MP_DEFINE_CONST_FUN_OBJ_VAR(synthio_lfo_tick_obj, 1, synthio_lfo_tick);
|
|||||||
|
|
||||||
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_Biquad), MP_ROM_PTR(&synthio_biquad_type_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_Math), MP_ROM_PTR(&synthio_math_type) },
|
{ MP_ROM_QSTR(MP_QSTR_Math), MP_ROM_PTR(&synthio_math_type) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_MathOperation), MP_ROM_PTR(&synthio_math_operation_type) },
|
{ MP_ROM_QSTR(MP_QSTR_MathOperation), MP_ROM_PTR(&synthio_math_operation_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) },
|
||||||
|
143
shared-module/synthio/Biquad.c
Normal file
143
shared-module/synthio/Biquad.c
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* 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 "shared-bindings/synthio/Biquad.h"
|
||||||
|
#include "shared-module/synthio/Biquad.h"
|
||||||
|
|
||||||
|
mp_obj_t common_hal_synthio_new_lpf(mp_float_t w0, mp_float_t Q) {
|
||||||
|
mp_float_t s = MICROPY_FLOAT_C_FUN(sin)(w0);
|
||||||
|
mp_float_t c = MICROPY_FLOAT_C_FUN(cos)(w0);
|
||||||
|
mp_float_t alpha = s / (2 * Q);
|
||||||
|
mp_float_t a0 = 1 + alpha;
|
||||||
|
mp_float_t a1 = -2 * c;
|
||||||
|
mp_float_t a2 = 1 - alpha;
|
||||||
|
mp_float_t b0 = (1 - c) / 2;
|
||||||
|
mp_float_t b1 = 1 - c;
|
||||||
|
mp_float_t b2 = (1 - c) / 2;
|
||||||
|
|
||||||
|
mp_obj_t out_args[] = {
|
||||||
|
mp_obj_new_float(a1 / a0),
|
||||||
|
mp_obj_new_float(a2 / a0),
|
||||||
|
mp_obj_new_float(b0 / a0),
|
||||||
|
mp_obj_new_float(b1 / a0),
|
||||||
|
mp_obj_new_float(b2 / a0),
|
||||||
|
};
|
||||||
|
|
||||||
|
return namedtuple_make_new((const mp_obj_type_t *)&synthio_biquad_type_obj, MP_ARRAY_SIZE(out_args), 0, out_args);
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_obj_t common_hal_synthio_new_hpf(mp_float_t w0, mp_float_t Q) {
|
||||||
|
mp_float_t s = MICROPY_FLOAT_C_FUN(sin)(w0);
|
||||||
|
mp_float_t c = MICROPY_FLOAT_C_FUN(cos)(w0);
|
||||||
|
mp_float_t alpha = s / (2 * Q);
|
||||||
|
mp_float_t a0 = 1 + alpha;
|
||||||
|
mp_float_t a1 = -2 * c;
|
||||||
|
mp_float_t a2 = 1 - alpha;
|
||||||
|
mp_float_t b0 = (1 + c) / 2;
|
||||||
|
mp_float_t b1 = -(1 + c);
|
||||||
|
mp_float_t b2 = (1 + c) / 2;
|
||||||
|
|
||||||
|
mp_obj_t out_args[] = {
|
||||||
|
mp_obj_new_float(a1 / a0),
|
||||||
|
mp_obj_new_float(a2 / a0),
|
||||||
|
mp_obj_new_float(b0 / a0),
|
||||||
|
mp_obj_new_float(b1 / a0),
|
||||||
|
mp_obj_new_float(b2 / a0),
|
||||||
|
};
|
||||||
|
|
||||||
|
return namedtuple_make_new((const mp_obj_type_t *)&synthio_biquad_type_obj, MP_ARRAY_SIZE(out_args), 0, out_args);
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_obj_t common_hal_synthio_new_bpf(mp_float_t w0, mp_float_t Q) {
|
||||||
|
mp_float_t s = MICROPY_FLOAT_C_FUN(sin)(w0);
|
||||||
|
mp_float_t c = MICROPY_FLOAT_C_FUN(cos)(w0);
|
||||||
|
mp_float_t alpha = s / (2 * Q);
|
||||||
|
mp_float_t a0 = 1 + alpha;
|
||||||
|
mp_float_t a1 = -2 * c;
|
||||||
|
mp_float_t a2 = 1 - alpha;
|
||||||
|
mp_float_t b0 = alpha;
|
||||||
|
mp_float_t b1 = 0;
|
||||||
|
mp_float_t b2 = -alpha;
|
||||||
|
|
||||||
|
mp_obj_t out_args[] = {
|
||||||
|
mp_obj_new_float(a1 / a0),
|
||||||
|
mp_obj_new_float(a2 / a0),
|
||||||
|
mp_obj_new_float(b0 / a0),
|
||||||
|
mp_obj_new_float(b1 / a0),
|
||||||
|
mp_obj_new_float(b2 / a0),
|
||||||
|
};
|
||||||
|
|
||||||
|
return namedtuple_make_new((const mp_obj_type_t *)&synthio_biquad_type_obj, MP_ARRAY_SIZE(out_args), 0, out_args);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BIQUAD_SHIFT (15)
|
||||||
|
STATIC int32_t biquad_scale_arg_obj(mp_obj_t arg) {
|
||||||
|
return (int32_t)MICROPY_FLOAT_C_FUN(round)(MICROPY_FLOAT_C_FUN(ldexp)(mp_obj_get_float(arg), BIQUAD_SHIFT));
|
||||||
|
}
|
||||||
|
void synthio_biquad_filter_assign(biquad_filter_state *st, mp_obj_t biquad_obj) {
|
||||||
|
if (biquad_obj != mp_const_none) {
|
||||||
|
mp_arg_validate_type(biquad_obj, (const mp_obj_type_t *)&synthio_biquad_type_obj, MP_QSTR_filter);
|
||||||
|
mp_obj_tuple_t *biquad = (mp_obj_tuple_t *)MP_OBJ_TO_PTR(biquad_obj);
|
||||||
|
st->a1 = biquad_scale_arg_obj(biquad->items[0]);
|
||||||
|
st->a2 = biquad_scale_arg_obj(biquad->items[1]);
|
||||||
|
st->b0 = biquad_scale_arg_obj(biquad->items[2]);
|
||||||
|
st->b1 = biquad_scale_arg_obj(biquad->items[3]);
|
||||||
|
st->b2 = biquad_scale_arg_obj(biquad->items[4]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void synthio_biquad_filter_reset(biquad_filter_state *st) {
|
||||||
|
memset(&st->x, 0, 4 * sizeof(int16_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
void synthio_biquad_filter_samples(biquad_filter_state *st, int32_t *buffer, size_t n_samples) {
|
||||||
|
int32_t a1 = st->a1;
|
||||||
|
int32_t a2 = st->a2;
|
||||||
|
int32_t b0 = st->b0;
|
||||||
|
int32_t b1 = st->b1;
|
||||||
|
int32_t b2 = st->b2;
|
||||||
|
|
||||||
|
int32_t x0 = st->x[0];
|
||||||
|
int32_t x1 = st->x[1];
|
||||||
|
int32_t y0 = st->y[0];
|
||||||
|
int32_t y1 = st->y[1];
|
||||||
|
|
||||||
|
for (size_t n = n_samples; n; --n, ++buffer) {
|
||||||
|
int32_t input = *buffer;
|
||||||
|
int32_t output = (b0 * input + b1 * x0 + b2 * x1 - a1 * y0 - a2 * y1 + (1 << (BIQUAD_SHIFT - 1))) >> BIQUAD_SHIFT;
|
||||||
|
|
||||||
|
x1 = x0;
|
||||||
|
x0 = input;
|
||||||
|
y1 = y0;
|
||||||
|
y0 = output;
|
||||||
|
*buffer = output;
|
||||||
|
}
|
||||||
|
st->x[0] = x0;
|
||||||
|
st->x[1] = x1;
|
||||||
|
st->y[0] = y0;
|
||||||
|
st->y[1] = y1;
|
||||||
|
}
|
38
shared-module/synthio/Biquad.h
Normal file
38
shared-module/synthio/Biquad.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "py/obj.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int32_t a1, a2, b0, b1, b2;
|
||||||
|
int32_t x[2], y[2];
|
||||||
|
} biquad_filter_state;
|
||||||
|
|
||||||
|
void synthio_biquad_filter_assign(biquad_filter_state *st, mp_obj_t biquad_obj);
|
||||||
|
void synthio_biquad_filter_reset(biquad_filter_state *st);
|
||||||
|
void synthio_biquad_filter_samples(biquad_filter_state *st, int32_t *buffer, size_t n_samples);
|
@ -122,7 +122,7 @@ void common_hal_synthio_miditrack_construct(synthio_miditrack_obj_t *self,
|
|||||||
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, 1, waveform_obj, mp_const_none, envelope_obj);
|
synthio_synth_init(&self->synth, sample_rate, 1, waveform_obj, envelope_obj);
|
||||||
|
|
||||||
start_parse(self);
|
start_parse(self);
|
||||||
}
|
}
|
||||||
|
@ -40,12 +40,13 @@ void common_hal_synthio_note_set_frequency(synthio_note_obj_t *self, mp_float_t
|
|||||||
self->frequency_scaled = synthio_frequency_convert_float_to_scaled(val);
|
self->frequency_scaled = synthio_frequency_convert_float_to_scaled(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool common_hal_synthio_note_get_filter(synthio_note_obj_t *self) {
|
mp_obj_t common_hal_synthio_note_get_filter_obj(synthio_note_obj_t *self) {
|
||||||
return self->filter;
|
return self->filter_obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
void common_hal_synthio_note_set_filter(synthio_note_obj_t *self, bool value_in) {
|
void common_hal_synthio_note_set_filter(synthio_note_obj_t *self, mp_obj_t filter_in) {
|
||||||
self->filter = value_in;
|
synthio_biquad_filter_assign(&self->filter_state, filter_in);
|
||||||
|
self->filter_obj = filter_in;
|
||||||
}
|
}
|
||||||
|
|
||||||
mp_float_t common_hal_synthio_note_get_ring_frequency(synthio_note_obj_t *self) {
|
mp_float_t common_hal_synthio_note_get_ring_frequency(synthio_note_obj_t *self) {
|
||||||
@ -147,6 +148,7 @@ void synthio_note_recalculate(synthio_note_obj_t *self, int32_t sample_rate) {
|
|||||||
|
|
||||||
void synthio_note_start(synthio_note_obj_t *self, int32_t sample_rate) {
|
void synthio_note_start(synthio_note_obj_t *self, int32_t sample_rate) {
|
||||||
synthio_note_recalculate(self, sample_rate);
|
synthio_note_recalculate(self, sample_rate);
|
||||||
|
synthio_biquad_filter_reset(&self->filter_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform a pitch bend operation
|
// Perform a pitch bend operation
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "shared-module/synthio/__init__.h"
|
#include "shared-module/synthio/__init__.h"
|
||||||
|
#include "shared-module/synthio/Biquad.h"
|
||||||
#include "shared-module/synthio/LFO.h"
|
#include "shared-module/synthio/LFO.h"
|
||||||
#include "shared-bindings/synthio/__init__.h"
|
#include "shared-bindings/synthio/__init__.h"
|
||||||
|
|
||||||
@ -37,12 +38,14 @@ typedef struct synthio_note_obj {
|
|||||||
|
|
||||||
mp_float_t frequency, ring_frequency;
|
mp_float_t frequency, ring_frequency;
|
||||||
mp_obj_t waveform_obj, envelope_obj, ring_waveform_obj;
|
mp_obj_t waveform_obj, envelope_obj, ring_waveform_obj;
|
||||||
|
mp_obj_t filter_obj;
|
||||||
|
|
||||||
|
biquad_filter_state filter_state;
|
||||||
|
|
||||||
int32_t sample_rate;
|
int32_t sample_rate;
|
||||||
|
|
||||||
int32_t frequency_scaled;
|
int32_t frequency_scaled;
|
||||||
int32_t ring_frequency_scaled, ring_frequency_bent;
|
int32_t ring_frequency_scaled, ring_frequency_bent;
|
||||||
bool filter;
|
|
||||||
|
|
||||||
mp_buffer_info_t waveform_buf;
|
mp_buffer_info_t waveform_buf;
|
||||||
mp_buffer_info_t ring_waveform_buf;
|
mp_buffer_info_t ring_waveform_buf;
|
||||||
|
@ -33,10 +33,10 @@
|
|||||||
|
|
||||||
|
|
||||||
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, int channel_count, mp_obj_t waveform_obj, mp_obj_t filter_obj,
|
uint32_t sample_rate, int channel_count, mp_obj_t waveform_obj,
|
||||||
mp_obj_t envelope_obj) {
|
mp_obj_t envelope_obj) {
|
||||||
|
|
||||||
synthio_synth_init(&self->synth, sample_rate, channel_count, waveform_obj, filter_obj, envelope_obj);
|
synthio_synth_init(&self->synth, sample_rate, channel_count, waveform_obj, envelope_obj);
|
||||||
self->blocks = mp_obj_new_list(0, NULL);
|
self->blocks = mp_obj_new_list(0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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/Biquad.h"
|
||||||
#include "shared-module/synthio/Note.h"
|
#include "shared-module/synthio/Note.h"
|
||||||
#include "py/runtime.h"
|
#include "py/runtime.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
@ -171,24 +172,11 @@ int16_t mix_down_sample(int32_t sample) {
|
|||||||
return sample;
|
return sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void synth_note_into_buffer(synthio_synth_t *synth, int chan, int32_t *out_buffer32, int16_t dur) {
|
static bool synth_note_into_buffer(synthio_synth_t *synth, int chan, int32_t *out_buffer32, int16_t dur, uint16_t loudness[2]) {
|
||||||
mp_obj_t note_obj = synth->span.note_obj[chan];
|
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;
|
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;
|
uint32_t dds_rate;
|
||||||
const int16_t *waveform = synth->waveform_bufinfo.buf;
|
const int16_t *waveform = synth->waveform_bufinfo.buf;
|
||||||
@ -227,33 +215,37 @@ static void synth_note_into_buffer(synthio_synth_t *synth, int chan, int32_t *ou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int synth_chan = synth->channel_count;
|
uint32_t lim = waveform_length << SYNTHIO_FREQUENCY_SHIFT;
|
||||||
if (ring_dds_rate) {
|
uint32_t accum = synth->accum[chan];
|
||||||
uint32_t lim = waveform_length << SYNTHIO_FREQUENCY_SHIFT;
|
|
||||||
uint32_t accum = synth->accum[chan];
|
|
||||||
|
|
||||||
if (dds_rate > lim / 2) {
|
if (dds_rate > lim / 2) {
|
||||||
// beyond nyquist, can't play note
|
// beyond nyquist, can't play note
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// can happen if note waveform gets set mid-note, but the expensive modulo is usually avoided
|
// can happen if note waveform gets set mid-note, but the expensive modulo is usually avoided
|
||||||
|
if (accum > lim) {
|
||||||
|
accum %= lim;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
if (accum > lim) {
|
||||||
accum %= lim;
|
accum -= lim;
|
||||||
}
|
}
|
||||||
|
int16_t idx = accum >> SYNTHIO_FREQUENCY_SHIFT;
|
||||||
|
out_buffer32[i] = waveform[idx];
|
||||||
|
}
|
||||||
|
synth->accum[chan] = accum;
|
||||||
|
|
||||||
int32_t ring_buffer[dur];
|
if (ring_dds_rate) {
|
||||||
// first, fill with waveform
|
if (ring_dds_rate > lim / 2) {
|
||||||
for (uint16_t i = 0; i < dur; i++) {
|
// beyond nyquist, can't play ring (but did synth main sound so
|
||||||
accum += dds_rate;
|
// return true)
|
||||||
// because dds_rate is low enough, the subtraction is guaranteed to go back into range, no expensive modulo needed
|
return true;
|
||||||
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
|
// now modulate by ring and accumulate
|
||||||
accum = synth->ring_accum[chan];
|
accum = synth->ring_accum[chan];
|
||||||
@ -264,82 +256,43 @@ static void synth_note_into_buffer(synthio_synth_t *synth, int chan, int32_t *ou
|
|||||||
accum %= lim;
|
accum %= lim;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint16_t i = 0, j = 0; i < dur; i++) {
|
for (uint16_t i = 0; i < dur; i++) {
|
||||||
accum += ring_dds_rate;
|
accum += ring_dds_rate;
|
||||||
// because dds_rate is low enough, the subtraction is guaranteed to go back into range, no expensive modulo needed
|
// because dds_rate is low enough, the subtraction is guaranteed to go back into range, no expensive modulo needed
|
||||||
if (accum > lim) {
|
if (accum > lim) {
|
||||||
accum -= lim;
|
accum -= lim;
|
||||||
}
|
}
|
||||||
int16_t idx = accum >> SYNTHIO_FREQUENCY_SHIFT;
|
int16_t idx = accum >> SYNTHIO_FREQUENCY_SHIFT;
|
||||||
int16_t wi = (ring_waveform[idx] * ring_buffer[i]) / 32768;
|
int16_t wi = (ring_waveform[idx] * out_buffer32[i]) / 32768;
|
||||||
for (int c = 0; c < synth_chan; c++) {
|
out_buffer32[i] = wi;
|
||||||
out_buffer32[j] += (wi * loudness[c]) / 32768;
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
synth->ring_accum[chan] = accum;
|
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;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
STATIC void run_fir(synthio_synth_t *synth, int32_t *out_buffer32, uint16_t dur) {
|
STATIC mp_obj_t synthio_synth_get_note_filter(mp_obj_t note_obj) {
|
||||||
int16_t *coeff = (int16_t *)synth->filter_bufinfo.buf;
|
|
||||||
size_t fir_len = synth->filter_bufinfo.len;
|
|
||||||
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) {
|
if (note_obj == mp_const_none) {
|
||||||
return false;
|
return mp_const_none;
|
||||||
}
|
}
|
||||||
if (!mp_obj_is_small_int(note_obj)) {
|
if (!mp_obj_is_small_int(note_obj)) {
|
||||||
synthio_note_obj_t *note = MP_OBJ_TO_PTR(note_obj);
|
synthio_note_obj_t *note = MP_OBJ_TO_PTR(note_obj);
|
||||||
return note->filter;
|
return note->filter_obj;
|
||||||
|
}
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
|
||||||
|
STATIC void sum_with_loudness(int32_t *out_buffer32, int32_t *tmp_buffer32, uint16_t loudness[2], size_t dur, int synth_chan) {
|
||||||
|
if (synth_chan == 1) {
|
||||||
|
for (size_t i = 0; i < dur; i++) {
|
||||||
|
*out_buffer32++ += (*tmp_buffer32++ *loudness[0]) >> 16;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (size_t i = 0; i < dur; i++) {
|
||||||
|
*out_buffer32++ += (*tmp_buffer32 * loudness[0]) >> 16;
|
||||||
|
*out_buffer32++ += (*tmp_buffer32++ *loudness[1]) >> 16;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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) {
|
||||||
@ -359,37 +312,44 @@ void synthio_synth_synthesize(synthio_synth_t *synth, uint8_t **bufptr, uint32_t
|
|||||||
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;
|
||||||
|
|
||||||
int32_t out_buffer32[dur * synth->channel_count];
|
int32_t out_buffer32[SYNTHIO_MAX_DUR * synth->channel_count];
|
||||||
|
int32_t tmp_buffer32[SYNTHIO_MAX_DUR];
|
||||||
if (synth->filter_buffer) {
|
memset(out_buffer32, 0, synth->channel_count * dur * sizeof(int32_t));
|
||||||
int32_t *filter_start = &synth->filter_buffer[synth->filter_bufinfo.len * synth->channel_count];
|
|
||||||
memset(filter_start, 0, dur * synth->channel_count * sizeof(int32_t));
|
|
||||||
|
|
||||||
for (int chan = 0; chan < CIRCUITPY_SYNTHIO_MAX_CHANNELS; chan++) {
|
|
||||||
mp_obj_t note_obj = synth->span.note_obj[chan];
|
|
||||||
if (!synthio_synth_get_note_filtered(note_obj)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
synth_note_into_buffer(synth, chan, filter_start, dur);
|
|
||||||
}
|
|
||||||
|
|
||||||
run_fir(synth, out_buffer32, dur);
|
|
||||||
} else {
|
|
||||||
memset(out_buffer32, 0, sizeof(out_buffer32));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int chan = 0; chan < CIRCUITPY_SYNTHIO_MAX_CHANNELS; chan++) {
|
for (int chan = 0; chan < CIRCUITPY_SYNTHIO_MAX_CHANNELS; chan++) {
|
||||||
mp_obj_t note_obj = synth->span.note_obj[chan];
|
mp_obj_t note_obj = synth->span.note_obj[chan];
|
||||||
if (synth->filter_buffer && synthio_synth_get_note_filtered(note_obj)) {
|
if (note_obj == SYNTHIO_SILENCE) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
synth_note_into_buffer(synth, chan, out_buffer32, dur);
|
|
||||||
|
if (synth->envelope_state[chan].level == 0) {
|
||||||
|
// note is truly finished, but we only just noticed
|
||||||
|
synth->span.note_obj[chan] = SYNTHIO_SILENCE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t loudness[2] = {synth->envelope_state[chan].level,synth->envelope_state[chan].level};
|
||||||
|
|
||||||
|
if (!synth_note_into_buffer(synth, chan, tmp_buffer32, dur, loudness)) {
|
||||||
|
// for some other reason, such as being above nyquist, note
|
||||||
|
// couldn't be synthed, so don't filter or sum it in
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_obj_t filter_obj = synthio_synth_get_note_filter(note_obj);
|
||||||
|
if (filter_obj != mp_const_none) {
|
||||||
|
synthio_note_obj_t *note = MP_OBJ_TO_PTR(note_obj);
|
||||||
|
synthio_biquad_filter_samples(¬e->filter_state, tmp_buffer32, dur);
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjust loudness by envelope
|
||||||
|
sum_with_loudness(out_buffer32, tmp_buffer32, loudness, dur, synth->channel_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t *out_buffer16 = (int16_t *)(void *)synth->buffers[synth->buffer_index];
|
int16_t *out_buffer16 = (int16_t *)(void *)synth->buffers[synth->buffer_index];
|
||||||
|
|
||||||
// mix down audio
|
// mix down audio
|
||||||
for (size_t i = 0; i < MP_ARRAY_SIZE(out_buffer32); i++) {
|
for (size_t i = 0; i < dur * synth->channel_count; i++) {
|
||||||
int32_t sample = out_buffer32[i];
|
int32_t sample = out_buffer32[i];
|
||||||
out_buffer16[i] = mix_down_sample(sample);
|
out_buffer16[i] = mix_down_sample(sample);
|
||||||
}
|
}
|
||||||
@ -419,7 +379,6 @@ bool synthio_synth_deinited(synthio_synth_t *synth) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void synthio_synth_deinit(synthio_synth_t *synth) {
|
void synthio_synth_deinit(synthio_synth_t *synth) {
|
||||||
synth->filter_buffer = NULL;
|
|
||||||
synth->buffers[0] = NULL;
|
synth->buffers[0] = NULL;
|
||||||
synth->buffers[1] = NULL;
|
synth->buffers[1] = NULL;
|
||||||
}
|
}
|
||||||
@ -433,17 +392,12 @@ 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, int channel_count, mp_obj_t waveform_obj, mp_obj_t filter_obj, 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 envelope_obj) {
|
||||||
synthio_synth_parse_waveform(&synth->waveform_bufinfo, waveform_obj);
|
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);
|
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->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 + SYNTHIO_MAX_DUR) * channel_count * sizeof(int32_t);
|
|
||||||
synth->filter_buffer = m_malloc(synth->filter_buffer_length, false);
|
|
||||||
}
|
|
||||||
synth->channel_count = channel_count;
|
synth->channel_count = channel_count;
|
||||||
synth->other_channel = -1;
|
synth->other_channel = -1;
|
||||||
synth->waveform_obj = waveform_obj;
|
synth->waveform_obj = waveform_obj;
|
||||||
@ -483,11 +437,6 @@ void synthio_synth_parse_waveform(mp_buffer_info_t *bufinfo_waveform, mp_obj_t w
|
|||||||
parse_common(bufinfo_waveform, waveform_obj, MP_QSTR_waveform, 16384);
|
parse_common(bufinfo_waveform, waveform_obj, MP_QSTR_waveform, 16384);
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
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_obj[i] == note) {
|
if (synth->span.note_obj[i] == note) {
|
||||||
|
@ -64,12 +64,11 @@ typedef struct synthio_synth {
|
|||||||
uint32_t sample_rate;
|
uint32_t sample_rate;
|
||||||
uint32_t total_envelope;
|
uint32_t total_envelope;
|
||||||
int16_t *buffers[2];
|
int16_t *buffers[2];
|
||||||
int32_t *filter_buffer;
|
|
||||||
uint8_t channel_count;
|
uint8_t channel_count;
|
||||||
uint16_t buffer_length, filter_buffer_length;
|
uint16_t 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;
|
||||||
mp_buffer_info_t waveform_bufinfo, filter_bufinfo;
|
mp_buffer_info_t waveform_bufinfo;
|
||||||
synthio_envelope_definition_t global_envelope_definition;
|
synthio_envelope_definition_t global_envelope_definition;
|
||||||
mp_obj_t waveform_obj, filter_obj, envelope_obj;
|
mp_obj_t waveform_obj, filter_obj, envelope_obj;
|
||||||
synthio_midi_span_t span;
|
synthio_midi_span_t span;
|
||||||
@ -91,7 +90,7 @@ typedef struct {
|
|||||||
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, int channel_count, mp_obj_t waveform_obj, mp_obj_t filter_obj, mp_obj_t envelope);
|
void synthio_synth_init(synthio_synth_t *synth, uint32_t sample_rate, int channel_count, mp_obj_t waveform_obj, 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);
|
||||||
|
61
tests/circuitpython-manual/synthio/note/biquad.py
Normal file
61
tests/circuitpython-manual/synthio/note/biquad.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
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.15, decay_time=0, release_time=0.08, attack_level=1.0, sustain_level=1.0
|
||||||
|
)
|
||||||
|
|
||||||
|
SAMPLE_SIZE = 1024
|
||||||
|
VOLUME = 14700
|
||||||
|
sine = np.array(
|
||||||
|
np.sin(np.linspace(0, 2 * np.pi, SAMPLE_SIZE, endpoint=False)) * VOLUME,
|
||||||
|
dtype=np.int16,
|
||||||
|
)
|
||||||
|
noise = np.array([random.randint(-VOLUME, VOLUME) for i in range(SAMPLE_SIZE)], dtype=np.int16)
|
||||||
|
bend_out = np.linspace(0, 32767, num=SAMPLE_SIZE, endpoint=True, dtype=np.int16)
|
||||||
|
|
||||||
|
|
||||||
|
def synthesize(synth):
|
||||||
|
for waveform in (sine, None, noise):
|
||||||
|
for biquad in (
|
||||||
|
None,
|
||||||
|
synth.low_pass_filter(120),
|
||||||
|
):
|
||||||
|
for midi_note in range(24, 90, 3):
|
||||||
|
n = synthio.Note(
|
||||||
|
frequency=synthio.midi_to_hz(midi_note),
|
||||||
|
envelope=envelope,
|
||||||
|
filter=biquad,
|
||||||
|
waveform=waveform,
|
||||||
|
bend=synthio.LFO(bend_out, once=True, rate=1 / 2, scale=5),
|
||||||
|
)
|
||||||
|
|
||||||
|
synth.press(n)
|
||||||
|
print(n.frequency)
|
||||||
|
yield 24
|
||||||
|
synth.release_all()
|
||||||
|
yield 16
|
||||||
|
yield 24
|
||||||
|
yield 48
|
||||||
|
|
||||||
|
|
||||||
|
with wave.open("biquad.wav", "w") as f:
|
||||||
|
f.setnchannels(1)
|
||||||
|
f.setsampwidth(2)
|
||||||
|
f.setframerate(48000)
|
||||||
|
synth = synthio.Synthesizer(sample_rate=48000)
|
||||||
|
for n in synthesize(synth):
|
||||||
|
for i in range(n):
|
||||||
|
result, data = audiocore.get_buffer(synth)
|
||||||
|
f.writeframes(data)
|
@ -1,53 +0,0 @@
|
|||||||
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
|
|
||||||
import mkfilter
|
|
||||||
|
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|
||||||
SAMPLE_SIZE = 1024
|
|
||||||
bend_out = np.linspace(0, 32767, num=SAMPLE_SIZE, endpoint=True, dtype=np.int16)
|
|
||||||
|
|
||||||
filter_rectangular = mkfilter.LPF(48000, 800, 13)
|
|
||||||
filter_rectangular_big = mkfilter.LPF(48000, 800, 59)
|
|
||||||
filter_blackman = mkfilter.LPF(48000, 800, 59, win=mkfilter.blackman)
|
|
||||||
print(filter_blackman)
|
|
||||||
|
|
||||||
|
|
||||||
def synthesize(synth):
|
|
||||||
n = synthio.Note(
|
|
||||||
frequency=120,
|
|
||||||
envelope=envelope,
|
|
||||||
filter=True,
|
|
||||||
bend=synthio.LFO(bend_out, once=True, rate=1 / 2, scale=5),
|
|
||||||
)
|
|
||||||
|
|
||||||
synth.press(n)
|
|
||||||
print(synth, n)
|
|
||||||
yield 2 * 48000 // 256
|
|
||||||
synth.release_all()
|
|
||||||
yield 36
|
|
||||||
|
|
||||||
|
|
||||||
with wave.open("fir.wav", "w") as f:
|
|
||||||
f.setnchannels(1)
|
|
||||||
f.setsampwidth(2)
|
|
||||||
f.setframerate(48000)
|
|
||||||
for filter_coeffs in [None, filter_rectangular, filter_rectangular_big, filter_blackman]:
|
|
||||||
synth = synthio.Synthesizer(sample_rate=48000, filter=filter_coeffs)
|
|
||||||
for n in synthesize(synth):
|
|
||||||
for i in range(n):
|
|
||||||
result, data = audiocore.get_buffer(synth)
|
|
||||||
f.writeframes(data)
|
|
@ -1,105 +0,0 @@
|
|||||||
try:
|
|
||||||
from ulab import numpy as np
|
|
||||||
except ImportError:
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
|
|
||||||
def lpf(fS, f, N, win=lambda N: 1):
|
|
||||||
if not (N & 1):
|
|
||||||
raise ValueError("filter length must be odd")
|
|
||||||
h = np.sinc(2 * f / fS * (np.arange(N) - (N - 1) / 2))
|
|
||||||
h = h * win(N)
|
|
||||||
return h * (1 / np.sum(h))
|
|
||||||
|
|
||||||
|
|
||||||
def hpf(fS, f, N, win=lambda N: 1):
|
|
||||||
if not (N & 1):
|
|
||||||
raise ValueError("filter length must be odd")
|
|
||||||
h = -lpf(fS, f, N)
|
|
||||||
h = h * win(N)
|
|
||||||
h[(N - 1) // 2] += 1
|
|
||||||
return h
|
|
||||||
|
|
||||||
|
|
||||||
def brf(fS, fL, NL, fH, NH, win=lambda N: 1):
|
|
||||||
hlpf = lpf(fS, fL, NL, win)
|
|
||||||
hhpf = hpf(fS, fH, NH, win)
|
|
||||||
|
|
||||||
if NH > NL:
|
|
||||||
h = hhpf
|
|
||||||
h[(NH - NL) // 2 : (NH - NL) // 2 + NL] += hlpf
|
|
||||||
else:
|
|
||||||
h = hlpf
|
|
||||||
h[(NL - NH) // 2 : (NL - NH) // 2 + NH] += hhpf
|
|
||||||
|
|
||||||
return h
|
|
||||||
|
|
||||||
|
|
||||||
def bpf(fS, fL, NL, fH, NH, win=lambda N: 1):
|
|
||||||
hlpf = lpf(fS, fL, NL, win)
|
|
||||||
hhpf = hpf(fS, fH, NH, win)
|
|
||||||
return np.convolve(hlpf, hhpf)
|
|
||||||
|
|
||||||
|
|
||||||
def blackman(M):
|
|
||||||
n = np.arange(1 - M, M, 2)
|
|
||||||
return 0.42 + 0.5 * np.cos(np.pi * n / (M - 1)) + 0.08 * np.cos(2.0 * np.pi * n / (M - 1))
|
|
||||||
|
|
||||||
|
|
||||||
def tosynthio(coeffs):
|
|
||||||
result = np.array(coeffs * 32767, dtype=np.int16)
|
|
||||||
return trim_zeros(result)
|
|
||||||
|
|
||||||
|
|
||||||
def trim_zeros(arr):
|
|
||||||
i = 0
|
|
||||||
j = len(arr) - 1
|
|
||||||
while i < len(arr) and arr[i] == 0:
|
|
||||||
i += 1
|
|
||||||
while j > i and arr[j] == 0:
|
|
||||||
j -= 1
|
|
||||||
return arr[i : j + 1]
|
|
||||||
|
|
||||||
|
|
||||||
# fiiir.com uses factor 4.6 for blackman window, 0.91 for rectangular
|
|
||||||
def ntaps(fS, fB, factor=4.6):
|
|
||||||
b = fB / fS
|
|
||||||
return round(factor / b) | 1
|
|
||||||
|
|
||||||
|
|
||||||
def LPF(*args, **kw):
|
|
||||||
return tosynthio(lpf(*args, **kw))
|
|
||||||
|
|
||||||
|
|
||||||
def HPF(*args, **kw):
|
|
||||||
return tosynthio(hpf(*args, **kw))
|
|
||||||
|
|
||||||
|
|
||||||
def BRF(*args, **kw):
|
|
||||||
return tosynthio(brf(*args, **kw))
|
|
||||||
|
|
||||||
|
|
||||||
def BPF(*args, **kw):
|
|
||||||
return tosynthio(bpf(*args, **kw))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print("lpf(24000, 2040, 13) # 1920Hz transition window")
|
|
||||||
print(list(lpf(24000, 2040, 13)))
|
|
||||||
|
|
||||||
print("hpf(24000, 9600, 13) # 960Hz transition window")
|
|
||||||
print(list(hpf(24000, 9600, 23)))
|
|
||||||
|
|
||||||
print("bpf(24000, 1200, 11, 3960, 15) # 2400Hz, 1600Hz transition windows")
|
|
||||||
print(list(bpf(24000, 1200, 11, 3960, 15)))
|
|
||||||
|
|
||||||
print("brf(24000, 960, 19, 2400, 13) # 1200, 1800Hz transition windows")
|
|
||||||
brf_tst = brf(24000, 960, 19, 2400, 13)
|
|
||||||
print(brf_tst)
|
|
||||||
|
|
||||||
print("brf(24000, 960, 13, 2400, 19) # 1200, 1800Hz transition windows")
|
|
||||||
brf_tst = brf(24000, 960, 13, 2400, 19)
|
|
||||||
print(brf_tst)
|
|
||||||
|
|
||||||
print("lpf(1, 0.1, 59, blackman) # 1920Hz transition window, blackman")
|
|
||||||
print(lpf(1, 0.1, 59, blackman))
|
|
@ -23,18 +23,17 @@ envelope = synthio.Envelope(
|
|||||||
)
|
)
|
||||||
|
|
||||||
synth = synthio.Synthesizer(sample_rate=48000)
|
synth = synthio.Synthesizer(sample_rate=48000)
|
||||||
|
bend_out = np.linspace(0, 32767, num=SAMPLE_SIZE, endpoint=True, dtype=np.int16)
|
||||||
|
|
||||||
|
|
||||||
def synthesize(synth):
|
def synthesize(synth):
|
||||||
n = synthio.Note(
|
n = synthio.Note(
|
||||||
frequency=120,
|
frequency=440,
|
||||||
waveform=sine,
|
waveform=sine,
|
||||||
ring_waveform=sine,
|
ring_waveform=sine,
|
||||||
ring_frequency=769,
|
ring_frequency=769,
|
||||||
envelope=envelope,
|
envelope=envelope,
|
||||||
bend_mode=synthio.BendType.VIBRATO,
|
bend=synthio.LFO(bend_out, scale=50 / 1200, rate=7),
|
||||||
bend_depth=50 / 1200,
|
|
||||||
bend_rate=7,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
print(synth, n)
|
print(synth, n)
|
||||||
@ -43,6 +42,13 @@ def synthesize(synth):
|
|||||||
synth.release_all()
|
synth.release_all()
|
||||||
yield 36
|
yield 36
|
||||||
|
|
||||||
|
n.ring_frequency = 0
|
||||||
|
print(synth, n)
|
||||||
|
synth.press((n,))
|
||||||
|
yield 720
|
||||||
|
synth.release_all()
|
||||||
|
yield 36
|
||||||
|
|
||||||
|
|
||||||
def chain(*args):
|
def chain(*args):
|
||||||
for a in args:
|
for a in args:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
(0, 1, 512, 1)
|
(0, 1, 512, 1)
|
||||||
1 [-16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, -16383, 16383, 16383]
|
1 [-16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 16383, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, -16384, 16383, 16383]
|
||||||
(0, 1, 512, 1)
|
(0, 1, 512, 1)
|
||||||
1 [0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0, 0, 0, 0, 0, -16383, -16383, -16383, -16383, -16383, -16383, 0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0, 0, 0, 0, 0, -16383, -16383, -16383, -16383, -16383, -16383, 0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0, 0, 0, 0, 0, -16383, -16383, -16383, -16383, -16383, -16383, 0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0, 0, 0, 0, 0, 0, -16383, -16383, -16383, -16383, -16383, -16383, 0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0, 0, 0, 0, 0, -16383, -16383, -16383, -16383, -16383, -16383, 0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0, 0, 0, 0, 0, -16383, -16383, -16383, -16383, -16383, -16383, 0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0, 0, 0, 0, 0, -16383, -16383, -16383, -16383, -16383, -16383, 0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0, 0, 0, 0, 0, -16383, -16383, -16383, -16383, -16383, -16383, 0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0, 0, 0, 0, 0, -16383, -16383, -16383, -16383, -16383, -16383, 0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0, 0, 0, 0, 0, -16383, -16383, -16383, -16383, -16383, -16383, 0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0]
|
1 [0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0, 0, 0, 0, 0, -16384, -16384, -16384, -16384, -16384, -16384, 0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0, 0, 0, 0, 0, -16384, -16384, -16384, -16384, -16384, -16384, 0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0, 0, 0, 0, 0, -16384, -16384, -16384, -16384, -16384, -16384, 0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0, 0, 0, 0, 0, 0, -16384, -16384, -16384, -16384, -16384, -16384, 0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0, 0, 0, 0, 0, -16384, -16384, -16384, -16384, -16384, -16384, 0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0, 0, 0, 0, 0, -16384, -16384, -16384, -16384, -16384, -16384, 0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0, 0, 0, 0, 0, -16384, -16384, -16384, -16384, -16384, -16384, 0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0, 0, 0, 0, 0, -16384, -16384, -16384, -16384, -16384, -16384, 0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0, 0, 0, 0, 0, -16384, -16384, -16384, -16384, -16384, -16384, 0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0, 0, 0, 0, 0, -16384, -16384, -16384, -16384, -16384, -16384, 0, 0, 0, 0, 0, 0, 16383, 16383, 16383, 16383, 16383, 16383, 0, 0]
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,30 +1,30 @@
|
|||||||
()
|
()
|
||||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
(80,)
|
(80,)
|
||||||
[-16383, -16383, -16383, -16383, 16383, 16383, 16383, 16383, 16383, -16383, -16383, -16383, -16383, -16383, 16383, 16383, 16383, 16383, 16383, -16383, -16383, -16383, -16383, -16383]
|
[-16384, -16384, -16384, -16384, 16383, 16383, 16383, 16383, 16383, -16384, -16384, -16384, -16384, -16384, 16383, 16383, 16383, 16383, 16383, -16384, -16384, -16384, -16384, -16384]
|
||||||
(80, 91)
|
(80, 91)
|
||||||
[0, 0, 28045, 28045, 0, -28046, -28046, 0, 28045, 28045, 0, 0, 28045, 0, 0, -28046, -28046, 0, 28045, 28045, 0, 0, 28045, 0]
|
[-1, -1, 28045, 28045, -1, -28046, -28046, -1, 28045, 28045, -1, -1, 28045, -1, -1, -28046, -28046, -1, 28045, 28045, -1, -1, 28045, -1]
|
||||||
(91,)
|
(91,)
|
||||||
[-28046, 0, 0, 28045, 0, 0, 28045, 28045, 0, -28046, -28046, 0, 28045, 28045, 0, 0, 28045, 0, 0, -28046, -28046, -28046, 28045, 28045]
|
[-28046, -1, -1, 28045, -1, -1, 28045, 28045, -1, -28046, -28046, -1, 28045, 28045, -1, -1, 28045, -1, -1, -28046, -28046, -28046, 28045, 28045]
|
||||||
(-5242, 5242)
|
(-5243, 5242)
|
||||||
(-10485, 10484)
|
(-10485, 10484)
|
||||||
(-15727, 15727)
|
(-15728, 15727)
|
||||||
(-16383, 16383)
|
(-16384, 16383)
|
||||||
(-14286, 14286)
|
(-14287, 14286)
|
||||||
(-13106, 13106)
|
(-13107, 13106)
|
||||||
(-13106, 13106)
|
(-13107, 13106)
|
||||||
(-13106, 13106)
|
(-13107, 13106)
|
||||||
(-13106, 13106)
|
(-13107, 13106)
|
||||||
(-13106, 13106)
|
(-13107, 13106)
|
||||||
(-13106, 13106)
|
(-13107, 13106)
|
||||||
(-13106, 13106)
|
(-13107, 13106)
|
||||||
(-13106, 13106)
|
(-13107, 13106)
|
||||||
(-11009, 11009)
|
(-11010, 11009)
|
||||||
(-8912, 8912)
|
(-8913, 8912)
|
||||||
(-6815, 6815)
|
(-6816, 6815)
|
||||||
(-4718, 4718)
|
(-4719, 4718)
|
||||||
(-2621, 2621)
|
(-2622, 2621)
|
||||||
(-524, 524)
|
(-525, 524)
|
||||||
(0, 0)
|
(0, 0)
|
||||||
(0, 0)
|
(0, 0)
|
||||||
(0, 0)
|
(0, 0)
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
()
|
()
|
||||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
(Note(frequency=830.6076004423605, panning=0.0, amplitude=1.0, bend=0.0, waveform=None, envelope=None, filter=True, ring_frequency=0.0, ring_bend=0.0, ring_waveform=None),)
|
(Note(frequency=830.6076004423605, panning=0.0, amplitude=1.0, bend=0.0, waveform=None, envelope=None, filter=None, ring_frequency=0.0, ring_bend=0.0, ring_waveform=None),)
|
||||||
[-16383, -16383, -16383, -16383, 16382, 16382, 16382, 16382, 16382, -16383, -16383, -16383, -16383, -16383, 16382, 16382, 16382, 16382, 16382, -16383, -16383, -16383, -16383, -16383]
|
[-16383, -16383, -16383, -16383, 16382, 16382, 16382, 16382, 16382, -16383, -16383, -16383, -16383, -16383, 16382, 16382, 16382, 16382, 16382, -16383, -16383, -16383, -16383, -16383]
|
||||||
(Note(frequency=830.6076004423605, panning=0.0, amplitude=1.0, bend=0.0, waveform=None, envelope=None, filter=True, ring_frequency=0.0, ring_bend=0.0, ring_waveform=None), Note(frequency=830.6076004423605, panning=0.0, amplitude=1.0, bend=0.0, waveform=None, envelope=None, filter=True, ring_frequency=0.0, ring_bend=0.0, ring_waveform=None))
|
(Note(frequency=830.6076004423605, panning=0.0, amplitude=1.0, bend=0.0, waveform=None, envelope=None, filter=None, ring_frequency=0.0, ring_bend=0.0, ring_waveform=None), Note(frequency=830.6076004423605, panning=0.0, amplitude=1.0, bend=0.0, waveform=None, envelope=None, filter=None, ring_frequency=0.0, ring_bend=0.0, ring_waveform=None))
|
||||||
[-1, -1, -1, -1, -1, -1, -1, -1, 28045, -1, -1, -1, -1, -28046, -1, -1, -1, -1, 28045, -1, -1, -1, -1, -28046]
|
[-1, -1, -1, -1, -1, -1, -1, -1, 28045, -1, -1, -1, -1, -28046, -1, -1, -1, -1, 28045, -1, -1, -1, -1, -28046]
|
||||||
(Note(frequency=830.6076004423605, panning=0.0, amplitude=1.0, bend=0.0, waveform=None, envelope=None, filter=True, ring_frequency=0.0, ring_bend=0.0, ring_waveform=None),)
|
(Note(frequency=830.6076004423605, panning=0.0, amplitude=1.0, bend=0.0, waveform=None, envelope=None, filter=None, ring_frequency=0.0, ring_bend=0.0, ring_waveform=None),)
|
||||||
[-1, -1, -1, 28045, -1, -1, -1, -1, -1, -1, -1, -1, 28045, -1, -1, -1, -1, -28046, -1, -1, -1, -1, 28045, -1]
|
[-1, -1, -1, 28045, -1, -1, -1, -1, -1, -1, -1, -1, 28045, -1, -1, -1, -1, -28046, -1, -1, -1, -1, 28045, -1]
|
||||||
(-5242, 5241)
|
(-5242, 5241)
|
||||||
(-10484, 10484)
|
(-10485, 10484)
|
||||||
(-15727, 15726)
|
(-15727, 15726)
|
||||||
(-16383, 16382)
|
(-16383, 16382)
|
||||||
(-14286, 14285)
|
(-14286, 14285)
|
||||||
|
12
tests/circuitpython/synthio_biquad.py
Normal file
12
tests/circuitpython/synthio_biquad.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from synthio import Synthesizer
|
||||||
|
|
||||||
|
s = Synthesizer(sample_rate=48000)
|
||||||
|
|
||||||
|
|
||||||
|
def print_filter(x):
|
||||||
|
print(" ".join(f"{v:.4g}" for v in x))
|
||||||
|
|
||||||
|
|
||||||
|
print_filter(s.low_pass_filter(330))
|
||||||
|
print_filter(s.high_pass_filter(330))
|
||||||
|
print_filter(s.band_pass_filter(330))
|
3
tests/circuitpython/synthio_biquad.py.exp
Normal file
3
tests/circuitpython/synthio_biquad.py.exp
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
-1.939 0.9407 0.0004526 0.0009052 0.0004526
|
||||||
|
-1.939 0.9407 0.9699 -1.94 0.9699
|
||||||
|
-1.939 0.9407 0.02963 0 -0.02963
|
23
tools/fetch-submodules.sh
Executable file
23
tools/fetch-submodules.sh
Executable file
@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# Pass the directories of submodules to fetch as command-line arguments. For example:
|
||||||
|
# ./fetch-submodules.sh lib tools
|
||||||
|
# If no arguments are passed, all submodules will be fetched.
|
||||||
|
# This script handles being called at other than the top level by making the paths
|
||||||
|
# for the submodules absolute.
|
||||||
|
|
||||||
|
TOP=$(git rev-parse --show-toplevel)
|
||||||
|
|
||||||
|
git submodule sync --quiet
|
||||||
|
# Prefix all the args with the absolute path to the top of the repo.
|
||||||
|
abs_submodules=""
|
||||||
|
for d in "$@"; do
|
||||||
|
abs_submodules="${abs_submodules} ${TOP}/${d}"
|
||||||
|
done
|
||||||
|
echo ${abs_submodules}
|
||||||
|
|
||||||
|
# Fetch submodules as partial clones if possible. If that fails due to an older version of git,
|
||||||
|
# do a shallow init and fetch tags.
|
||||||
|
git submodule update --init --filter=blob:none ${abs_submodules} || \
|
||||||
|
git submodule update --init --depth 1 ${abs_submodules} && \
|
||||||
|
git submodule foreach 'git fetch --tags --depth 1' || \
|
||||||
|
echo "ERROR: fetch-submodules.sh FAILED"
|
64
tools/gdb-stack-size.py
Normal file
64
tools/gdb-stack-size.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
"""Source this file into gdb `source ../../tools/gdb-stack-size.py` then run
|
||||||
|
`stack-size` to print a backtrace with each frame size next to it."""
|
||||||
|
|
||||||
|
|
||||||
|
class StackSize(gdb.Command):
|
||||||
|
def __init__(self):
|
||||||
|
super(StackSize, self).__init__("stack-size", gdb.COMMAND_USER)
|
||||||
|
|
||||||
|
def invoke(self, arg, from_tty):
|
||||||
|
frame = gdb.newest_frame()
|
||||||
|
total_size = 0
|
||||||
|
while frame:
|
||||||
|
sp = frame.read_register("sp")
|
||||||
|
frame_up = frame.older()
|
||||||
|
if not frame_up:
|
||||||
|
break
|
||||||
|
f = frame.function()
|
||||||
|
l = frame.level()
|
||||||
|
if l < 10:
|
||||||
|
l = "#" + str(l) + " "
|
||||||
|
else:
|
||||||
|
l = "#" + str(l)
|
||||||
|
size = frame_up.read_register("sp") - sp
|
||||||
|
total_size += size
|
||||||
|
print(l, sp, frame.type(), f, " " * (40 - len(str(f))), size)
|
||||||
|
# print(dir(f))
|
||||||
|
# Tweak this if for more detail for a specific function.
|
||||||
|
if False and f.name == "mp_execute_bytecode":
|
||||||
|
b = frame.block()
|
||||||
|
prev_b = None
|
||||||
|
while not b.is_static:
|
||||||
|
print(" block", hex(b.start), hex(b.end), b.function)
|
||||||
|
for sym in b:
|
||||||
|
if not sym.needs_frame:
|
||||||
|
continue
|
||||||
|
v = sym.value(frame)
|
||||||
|
print(" ", sym.addr_class, v.address, sym.type.sizeof, sym, sym.type, v)
|
||||||
|
prev_b = b
|
||||||
|
b = b.superblock
|
||||||
|
|
||||||
|
if b.function == f:
|
||||||
|
break
|
||||||
|
b = prev_b
|
||||||
|
print("pc scan", hex(b.start), hex(b.end))
|
||||||
|
seen = set()
|
||||||
|
for pc in range(b.start, b.end, 2):
|
||||||
|
b = gdb.block_for_pc(pc)
|
||||||
|
r = (b.start, b.end)
|
||||||
|
if r in seen:
|
||||||
|
continue
|
||||||
|
seen.add(r)
|
||||||
|
print(" ", hex(pc), hex(b.start), hex(b.end), b.function)
|
||||||
|
for sym in b:
|
||||||
|
if not sym.needs_frame:
|
||||||
|
continue
|
||||||
|
# if sym.type.sizeof <= 4:
|
||||||
|
# continue
|
||||||
|
v = sym.value(frame)
|
||||||
|
print(" ", sym.addr_class, v.address, sym.type.sizeof, sym, sym.type, v)
|
||||||
|
frame = frame_up
|
||||||
|
print("total size:", total_size)
|
||||||
|
|
||||||
|
|
||||||
|
StackSize()
|
28
tools/stack-loc-to-pc.py
Normal file
28
tools/stack-loc-to-pc.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
"""Prints the pcs that access each stack location in a function. Useful for finding
|
||||||
|
infrequently used stack space.
|
||||||
|
|
||||||
|
Pipe in disassembly like so:
|
||||||
|
|
||||||
|
arm-none-eabi-objdump --disassemble=mp_execute_bytecode build-metro_m0_express/firmware.elf | python ../../tools/stack-loc-to-pc.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
|
||||||
|
offset = re.compile(r"sp, #(\d+)")
|
||||||
|
|
||||||
|
offsets = {}
|
||||||
|
for line in sys.stdin:
|
||||||
|
if "sp" in line:
|
||||||
|
m = offset.search(line)
|
||||||
|
o = int(m.groups()[0])
|
||||||
|
pc = line.split(":")[0]
|
||||||
|
if o not in offsets:
|
||||||
|
offsets[o] = []
|
||||||
|
offsets[o].append(pc.strip())
|
||||||
|
|
||||||
|
print("Offset", "Size", "PCs", sep="\t")
|
||||||
|
last_o = 0
|
||||||
|
for o in sorted(offsets):
|
||||||
|
print(o, o - last_o, offsets[o], sep="\t")
|
||||||
|
last_o = o
|
Loading…
x
Reference in New Issue
Block a user