diff --git a/data/nvm.toml b/data/nvm.toml index 6b678f15e3..8bca037b05 160000 --- a/data/nvm.toml +++ b/data/nvm.toml @@ -1 +1 @@ -Subproject commit 6b678f15e378edce820f2ffdef3286b3e55449e7 +Subproject commit 8bca037b052a4a4dc46a56a25a1b802652ee3f47 diff --git a/extmod/modasyncio.c b/extmod/modasyncio.c index 4672f6940c..b0af32f70f 100644 --- a/extmod/modasyncio.c +++ b/extmod/modasyncio.c @@ -160,11 +160,6 @@ static const mp_rom_map_elem_t task_queue_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_push), MP_ROM_PTR(&task_queue_push_obj) }, { MP_ROM_QSTR(MP_QSTR_pop), MP_ROM_PTR(&task_queue_pop_obj) }, { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&task_queue_remove_obj) }, - - // CIRCUITPY-CHANGE: Remove these in CircuitPython 10.0.0 - { MP_ROM_QSTR(MP_QSTR_push_head), MP_ROM_PTR(&task_queue_push_obj) }, - { MP_ROM_QSTR(MP_QSTR_push_sorted), MP_ROM_PTR(&task_queue_push_obj) }, - { MP_ROM_QSTR(MP_QSTR_pop_head), MP_ROM_PTR(&task_queue_pop_obj) }, }; static MP_DEFINE_CONST_DICT(task_queue_locals_dict, task_queue_locals_dict_table); diff --git a/frozen/Adafruit_CircuitPython_Bitmap_Font b/frozen/Adafruit_CircuitPython_Bitmap_Font index 51b4032f68..1ba6e0d0fa 160000 --- a/frozen/Adafruit_CircuitPython_Bitmap_Font +++ b/frozen/Adafruit_CircuitPython_Bitmap_Font @@ -1 +1 @@ -Subproject commit 51b4032f6809e5b4177d95ef5d70520352081eb8 +Subproject commit 1ba6e0d0fa1fc0512692615a7c95281fdc0671b0 diff --git a/frozen/Adafruit_CircuitPython_Display_Text b/frozen/Adafruit_CircuitPython_Display_Text index 46340b3772..f7971b6cf1 160000 --- a/frozen/Adafruit_CircuitPython_Display_Text +++ b/frozen/Adafruit_CircuitPython_Display_Text @@ -1 +1 @@ -Subproject commit 46340b3772b03a2acc584cbcb30ad801c902dc42 +Subproject commit f7971b6cf1d2f1c88a84561cdb6fb9419073c120 diff --git a/frozen/Adafruit_CircuitPython_ST7789 b/frozen/Adafruit_CircuitPython_ST7789 index ea32f33ada..43a67c2672 160000 --- a/frozen/Adafruit_CircuitPython_ST7789 +++ b/frozen/Adafruit_CircuitPython_ST7789 @@ -1 +1 @@ -Subproject commit ea32f33ada7ad6da33cead40a2d7393a9d60c029 +Subproject commit 43a67c2672796324c4465ff41ef1d14bd4883db3 diff --git a/frozen/Adafruit_CircuitPython_asyncio b/frozen/Adafruit_CircuitPython_asyncio index b74b18efd3..24705c799e 160000 --- a/frozen/Adafruit_CircuitPython_asyncio +++ b/frozen/Adafruit_CircuitPython_asyncio @@ -1 +1 @@ -Subproject commit b74b18efd391e830d165125738441d2710485028 +Subproject commit 24705c799e7df85fa6f0094e196788d3c8c99c87 diff --git a/locale/ID.po b/locale/ID.po index 82995f794e..65b150b054 100644 --- a/locale/ID.po +++ b/locale/ID.po @@ -114,6 +114,10 @@ msgstr "%q berisi pin duplikat" msgid "%q failure: %d" msgstr "%q gagal: %d" +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" msgstr "%q dalam %q harus bertipe %q, bukan %q" @@ -236,7 +240,8 @@ msgid "%q must be of type %q, %q, or %q, not %q" msgstr "%q harus bertipe %q, %q, atau %q, bukan %q" #: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/synthio/Note.c shared-module/synthio/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c msgid "%q must be of type %q, not %q" msgstr "%q harus bertipe %q, %q, atau %q, bukan %q" @@ -2636,6 +2641,7 @@ msgid "bits must be 32 or less" msgstr "" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c #: shared-bindings/audiofilters/Distortion.c #: shared-bindings/audiofilters/Filter.c shared-bindings/audiomixer/Mixer.c diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 695f3bf8cc..486d4eb149 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -102,6 +102,10 @@ msgstr "" msgid "%q failure: %d" msgstr "" +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" msgstr "" @@ -224,7 +228,8 @@ msgid "%q must be of type %q, %q, or %q, not %q" msgstr "" #: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/synthio/Note.c shared-module/synthio/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c msgid "%q must be of type %q, not %q" msgstr "" @@ -2587,6 +2592,7 @@ msgid "bits must be 32 or less" msgstr "" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c #: shared-bindings/audiofilters/Distortion.c #: shared-bindings/audiofilters/Filter.c shared-bindings/audiomixer/Mixer.c diff --git a/locale/cs.po b/locale/cs.po index b403d2253c..986d0bd115 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -113,6 +113,10 @@ msgstr "%q obsahuje duplicitní piny" msgid "%q failure: %d" msgstr "%q: selhání %d" +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" msgstr "%q v %q musí být typu %q, ne %q" @@ -235,7 +239,8 @@ msgid "%q must be of type %q, %q, or %q, not %q" msgstr "" #: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/synthio/Note.c shared-module/synthio/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c msgid "%q must be of type %q, not %q" msgstr "%q musí být typu %q, ne %q" @@ -2616,6 +2621,7 @@ msgid "bits must be 32 or less" msgstr "počet bitů nesmí přesáhnout 32" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c #: shared-bindings/audiofilters/Distortion.c #: shared-bindings/audiofilters/Filter.c shared-bindings/audiomixer/Mixer.c diff --git a/locale/de_DE.po b/locale/de_DE.po index f956325f32..eede1a4cbc 100644 --- a/locale/de_DE.po +++ b/locale/de_DE.po @@ -115,6 +115,10 @@ msgstr "%q enthält doppelte Pins" msgid "%q failure: %d" msgstr "%q Fehler: %d" +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" msgstr "%q in %q muss von Typ %q sein, nicht %q" @@ -238,7 +242,8 @@ msgid "%q must be of type %q, %q, or %q, not %q" msgstr "%q muss vom Typ %q, %q oder %q sein, und nicht %q" #: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/synthio/Note.c shared-module/synthio/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c msgid "%q must be of type %q, not %q" msgstr "%q muss von Typ %q sein, nicht %q" @@ -2648,6 +2653,7 @@ msgid "bits must be 32 or less" msgstr "bits müssen 32 oder kleiner sein" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c #: shared-bindings/audiofilters/Distortion.c #: shared-bindings/audiofilters/Filter.c shared-bindings/audiomixer/Mixer.c diff --git a/locale/el.po b/locale/el.po index d0bb53f619..cb845563c6 100644 --- a/locale/el.po +++ b/locale/el.po @@ -117,6 +117,10 @@ msgstr "%q περιέχει διπλότυπα pins" msgid "%q failure: %d" msgstr "%q αποτυχία: %d" +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" msgstr "%q στο %q πρέπει να είναι τύπου %q, όχι %q" @@ -239,7 +243,8 @@ msgid "%q must be of type %q, %q, or %q, not %q" msgstr "" #: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/synthio/Note.c shared-module/synthio/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c msgid "%q must be of type %q, not %q" msgstr "%q πρέπει να είναι τύπου %q, όχι %q" @@ -2615,6 +2620,7 @@ msgid "bits must be 32 or less" msgstr "" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c #: shared-bindings/audiofilters/Distortion.c #: shared-bindings/audiofilters/Filter.c shared-bindings/audiomixer/Mixer.c diff --git a/locale/en_GB.po b/locale/en_GB.po index 5f3c583b6c..fc869eb5ec 100644 --- a/locale/en_GB.po +++ b/locale/en_GB.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2025-03-06 13:42+0000\n" +"PO-Revision-Date: 2025-04-19 15:42+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: none\n" "Language: en_GB\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.10.3-dev\n" +"X-Generator: Weblate 5.11.1-dev\n" #: main.c msgid "" @@ -115,6 +115,10 @@ msgstr "%q contains duplicate pins" msgid "%q failure: %d" msgstr "%q failure: %d" +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" msgstr "%q in %q must be of type %q, not %q" @@ -237,7 +241,8 @@ msgid "%q must be of type %q, %q, or %q, not %q" msgstr "%q must be of type %q, %q, or %q, not %q" #: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/synthio/Note.c shared-module/synthio/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c msgid "%q must be of type %q, not %q" msgstr "%q must be of type %q, not %q" @@ -805,7 +810,7 @@ msgstr "Cannot record to a file" #: shared-module/storage/__init__.c msgid "Cannot remount path when visible via USB." -msgstr "" +msgstr "Cannot remount path when visible via USB." #: shared-bindings/digitalio/DigitalInOut.c msgid "Cannot set value when direction is input." @@ -1391,7 +1396,7 @@ msgstr "MITM security is not supported" #: ports/stm/common-hal/sdioio/SDCard.c #, c-format msgid "MMC/SDIO Clock Error %x" -msgstr "" +msgstr "MMC/SDIO Clock Error %x" #: shared-bindings/is31fl3741/IS31FL3741.c msgid "Mapping must be a tuple" @@ -1581,7 +1586,7 @@ msgstr "No pulldown on pin; 1Mohm recommended" #: shared-module/touchio/TouchIn.c msgid "No pullup on pin; 1Mohm recommended" -msgstr "" +msgstr "No pullup on pin; 1Mohm recommended" #: py/moderrno.c msgid "No space left on device" @@ -2620,6 +2625,7 @@ msgid "bits must be 32 or less" msgstr "bits must be 32 or less" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c #: shared-bindings/audiofilters/Distortion.c #: shared-bindings/audiofilters/Filter.c shared-bindings/audiomixer/Mixer.c diff --git a/locale/es.po b/locale/es.po index eda3a5e238..cc4db0a453 100644 --- a/locale/es.po +++ b/locale/es.po @@ -117,6 +117,10 @@ msgstr "%q contiene pines duplicados" msgid "%q failure: %d" msgstr "%q fallo: %d" +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" msgstr "%q en %q debe ser del tipo %q, no %q" @@ -239,7 +243,8 @@ msgid "%q must be of type %q, %q, or %q, not %q" msgstr "%q debe ser de tipo %q, %q, o %q, no %q" #: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/synthio/Note.c shared-module/synthio/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c msgid "%q must be of type %q, not %q" msgstr "%q debe ser del tipo %q, y no %q" @@ -2657,6 +2662,7 @@ msgid "bits must be 32 or less" msgstr "los bits deben ser 32 o menos" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c #: shared-bindings/audiofilters/Distortion.c #: shared-bindings/audiofilters/Filter.c shared-bindings/audiomixer/Mixer.c diff --git a/locale/fil.po b/locale/fil.po index 18501fac50..1ce93c219e 100644 --- a/locale/fil.po +++ b/locale/fil.po @@ -105,6 +105,10 @@ msgstr "" msgid "%q failure: %d" msgstr "" +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" msgstr "" @@ -227,7 +231,8 @@ msgid "%q must be of type %q, %q, or %q, not %q" msgstr "" #: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/synthio/Note.c shared-module/synthio/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c msgid "%q must be of type %q, not %q" msgstr "" @@ -2606,6 +2611,7 @@ msgid "bits must be 32 or less" msgstr "" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c #: shared-bindings/audiofilters/Distortion.c #: shared-bindings/audiofilters/Filter.c shared-bindings/audiomixer/Mixer.c diff --git a/locale/fr.po b/locale/fr.po index e6cc7f32f1..58ce43ee79 100644 --- a/locale/fr.po +++ b/locale/fr.po @@ -116,6 +116,10 @@ msgstr "%q contient des broches en double" msgid "%q failure: %d" msgstr "Échec de %q : %d" +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" msgstr "%q dans %q doit être de type %q, pas %q" @@ -238,7 +242,8 @@ msgid "%q must be of type %q, %q, or %q, not %q" msgstr "%q doit être de type %q, %q ou %q, pas %q" #: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/synthio/Note.c shared-module/synthio/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c msgid "%q must be of type %q, not %q" msgstr "%q doit être de type %q, pas %q" @@ -2667,6 +2672,7 @@ msgid "bits must be 32 or less" msgstr "les bits doivent être 32 ou moins" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c #: shared-bindings/audiofilters/Distortion.c #: shared-bindings/audiofilters/Filter.c shared-bindings/audiomixer/Mixer.c diff --git a/locale/hi.po b/locale/hi.po index 416707ca9b..5db2fbadaa 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -104,6 +104,10 @@ msgstr "" msgid "%q failure: %d" msgstr "" +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" msgstr "" @@ -226,7 +230,8 @@ msgid "%q must be of type %q, %q, or %q, not %q" msgstr "" #: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/synthio/Note.c shared-module/synthio/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c msgid "%q must be of type %q, not %q" msgstr "" @@ -2589,6 +2594,7 @@ msgid "bits must be 32 or less" msgstr "" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c #: shared-bindings/audiofilters/Distortion.c #: shared-bindings/audiofilters/Filter.c shared-bindings/audiomixer/Mixer.c diff --git a/locale/it_IT.po b/locale/it_IT.po index 4380876a27..5aaef58772 100644 --- a/locale/it_IT.po +++ b/locale/it_IT.po @@ -106,6 +106,10 @@ msgstr "" msgid "%q failure: %d" msgstr "%q fallito: %d" +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" msgstr "" @@ -228,7 +232,8 @@ msgid "%q must be of type %q, %q, or %q, not %q" msgstr "" #: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/synthio/Note.c shared-module/synthio/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c msgid "%q must be of type %q, not %q" msgstr "" @@ -2604,6 +2609,7 @@ msgid "bits must be 32 or less" msgstr "" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c #: shared-bindings/audiofilters/Distortion.c #: shared-bindings/audiofilters/Filter.c shared-bindings/audiomixer/Mixer.c diff --git a/locale/ja.po b/locale/ja.po index 1b82401bb4..b1d26e7518 100644 --- a/locale/ja.po +++ b/locale/ja.po @@ -115,6 +115,10 @@ msgstr "%q に重複するピンがあります" msgid "%q failure: %d" msgstr "%q 失敗: %d" +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" msgstr "%q 内の %q は %q 型である必要があり、 %q 型は使用できません" @@ -239,7 +243,8 @@ msgstr "" "%q は %q 型や %q 型または %q 型である必要があります、 %q 型は使用できません" #: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/synthio/Note.c shared-module/synthio/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c msgid "%q must be of type %q, not %q" msgstr "%q は %q 型である必要があります、 %q 型は使用できません" @@ -2612,6 +2617,7 @@ msgid "bits must be 32 or less" msgstr "" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c #: shared-bindings/audiofilters/Distortion.c #: shared-bindings/audiofilters/Filter.c shared-bindings/audiomixer/Mixer.c diff --git a/locale/ko.po b/locale/ko.po index 001b8bcb2c..b7a74fada6 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -115,6 +115,10 @@ msgstr "%q에 중복된 핀이 포함" msgid "%q failure: %d" msgstr "%q 실패: %d" +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" msgstr "%q의 %q는 %q가 아니라 %q 유형이어야 합니다" @@ -243,7 +247,8 @@ msgid "%q must be of type %q, %q, or %q, not %q" msgstr "" #: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/synthio/Note.c shared-module/synthio/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c msgid "%q must be of type %q, not %q" msgstr "%q는 %q가 아니라 %q 유형이어야 합니다" @@ -2665,6 +2670,7 @@ msgid "bits must be 32 or less" msgstr "" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c #: shared-bindings/audiofilters/Distortion.c #: shared-bindings/audiofilters/Filter.c shared-bindings/audiomixer/Mixer.c diff --git a/locale/nl.po b/locale/nl.po index 44b9e8a7ae..24402572c4 100644 --- a/locale/nl.po +++ b/locale/nl.po @@ -102,6 +102,10 @@ msgstr "" msgid "%q failure: %d" msgstr "%q fout: %d" +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" msgstr "" @@ -224,7 +228,8 @@ msgid "%q must be of type %q, %q, or %q, not %q" msgstr "" #: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/synthio/Note.c shared-module/synthio/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c msgid "%q must be of type %q, not %q" msgstr "" @@ -2605,6 +2610,7 @@ msgid "bits must be 32 or less" msgstr "" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c #: shared-bindings/audiofilters/Distortion.c #: shared-bindings/audiofilters/Filter.c shared-bindings/audiomixer/Mixer.c diff --git a/locale/pl.po b/locale/pl.po index df0bb07daa..020f8bc655 100644 --- a/locale/pl.po +++ b/locale/pl.po @@ -110,6 +110,10 @@ msgstr "%q zawiera zduplikowane piny" msgid "%q failure: %d" msgstr "%q niepowodzenie: %d" +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" msgstr "%q w %q musi być typu %q, a nie %q" @@ -232,7 +236,8 @@ msgid "%q must be of type %q, %q, or %q, not %q" msgstr "" #: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/synthio/Note.c shared-module/synthio/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c msgid "%q must be of type %q, not %q" msgstr "%q musi być typu %q, a nie %q" @@ -2617,6 +2622,7 @@ msgid "bits must be 32 or less" msgstr "" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c #: shared-bindings/audiofilters/Distortion.c #: shared-bindings/audiofilters/Filter.c shared-bindings/audiomixer/Mixer.c diff --git a/locale/pt_BR.po b/locale/pt_BR.po index 30188c144a..829533f8b9 100644 --- a/locale/pt_BR.po +++ b/locale/pt_BR.po @@ -115,6 +115,10 @@ msgstr "%q contém pinos duplicados" msgid "%q failure: %d" msgstr "%q falha: %d" +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" msgstr "%q em %q deve ser do tipo %q e não %q" @@ -237,7 +241,8 @@ msgid "%q must be of type %q, %q, or %q, not %q" msgstr "%q deve ser do tipo %q, %q ou %q e não %q" #: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/synthio/Note.c shared-module/synthio/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c msgid "%q must be of type %q, not %q" msgstr "%q deve ser do tipo %q e não %q" @@ -2663,6 +2668,7 @@ msgid "bits must be 32 or less" msgstr "bits deve ser 32 ou menos" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c #: shared-bindings/audiofilters/Distortion.c #: shared-bindings/audiofilters/Filter.c shared-bindings/audiomixer/Mixer.c diff --git a/locale/ru.po b/locale/ru.po index 231b2087b9..ab806c300f 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -117,6 +117,10 @@ msgstr "%q содержит пины дупликаты" msgid "%q failure: %d" msgstr "%q сбой: %d" +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" msgstr "%q в %q должно быть типа %q, а не %q" @@ -239,7 +243,8 @@ msgid "%q must be of type %q, %q, or %q, not %q" msgstr "%q должен иметь тип %q, %q или %q, а не %q" #: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/synthio/Note.c shared-module/synthio/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c msgid "%q must be of type %q, not %q" msgstr "%q должно быть типа %q, а не %q" @@ -2659,6 +2664,7 @@ msgid "bits must be 32 or less" msgstr "биты должны быть 32 или менее" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c #: shared-bindings/audiofilters/Distortion.c #: shared-bindings/audiofilters/Filter.c shared-bindings/audiomixer/Mixer.c diff --git a/locale/sv.po b/locale/sv.po index c3165a92a5..929056ae86 100644 --- a/locale/sv.po +++ b/locale/sv.po @@ -114,6 +114,10 @@ msgstr "%q innehåller dubblettstift" msgid "%q failure: %d" msgstr "%q-fel: %d" +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" msgstr "%q i %q måste vara av typen %q, inte %q" @@ -238,7 +242,8 @@ msgid "%q must be of type %q, %q, or %q, not %q" msgstr "%q måste vara av typen %q, %q, eller %q, inte %q" #: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/synthio/Note.c shared-module/synthio/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c msgid "%q must be of type %q, not %q" msgstr "%q måste vara av typen %q, inte %q" @@ -2631,6 +2636,7 @@ msgid "bits must be 32 or less" msgstr "bits måste vara 32 eller färre" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c #: shared-bindings/audiofilters/Distortion.c #: shared-bindings/audiofilters/Filter.c shared-bindings/audiomixer/Mixer.c diff --git a/locale/tr.po b/locale/tr.po index 3cd634a720..3df4ec78c8 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -115,6 +115,10 @@ msgstr "%q yinelenen pinler içeriyor" msgid "%q failure: %d" msgstr "%q hata: %d" +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" msgstr "" @@ -237,7 +241,8 @@ msgid "%q must be of type %q, %q, or %q, not %q" msgstr "" #: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/synthio/Note.c shared-module/synthio/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c msgid "%q must be of type %q, not %q" msgstr "" @@ -2611,6 +2616,7 @@ msgid "bits must be 32 or less" msgstr "" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c #: shared-bindings/audiofilters/Distortion.c #: shared-bindings/audiofilters/Filter.c shared-bindings/audiomixer/Mixer.c diff --git a/locale/zh_Latn_pinyin.po b/locale/zh_Latn_pinyin.po index a463ffbc89..643e0c9b77 100644 --- a/locale/zh_Latn_pinyin.po +++ b/locale/zh_Latn_pinyin.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: circuitpython-cn\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-04 12:55-0600\n" -"PO-Revision-Date: 2025-02-24 05:02+0000\n" +"PO-Revision-Date: 2025-04-10 22:01+0000\n" "Last-Translator: hexthat \n" "Language-Team: Chinese Hanyu Pinyin\n" "Language: zh_Latn_pinyin\n" @@ -15,7 +15,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 5.10.1-dev\n" +"X-Generator: Weblate 5.11-dev\n" #: main.c msgid "" @@ -116,6 +116,10 @@ msgstr "%q bāo hán chóng fù de yǐn jiǎo" msgid "%q failure: %d" msgstr "%q Shībài: %d" +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" msgstr "%q zhōng de %q bì xū shì %q lèi xíng, ér bù shì %q" @@ -239,7 +243,8 @@ msgid "%q must be of type %q, %q, or %q, not %q" msgstr "%q bìxūshì %q, %q huò %q lèixíng, érbùshì %q" #: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/synthio/Note.c shared-module/synthio/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c msgid "%q must be of type %q, not %q" msgstr "%q de lèi xíng bì xū shì %q, ér bù shì %q" @@ -810,7 +815,7 @@ msgstr "Wúfǎ jìlù dào wénjiàn" #: shared-module/storage/__init__.c msgid "Cannot remount path when visible via USB." -msgstr "" +msgstr "tōngguò USB kějiàn shí wúfǎ chóngxīn guàzǎi lùjìng." #: shared-bindings/digitalio/DigitalInOut.c msgid "Cannot set value when direction is input." @@ -1061,7 +1066,7 @@ msgstr "Wúfǎ shìfàng mutex, err 0x%04x" #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" -msgstr "" +msgstr "wúfǎ shèzhì zhǔjīmíng" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "Failed to start async audio" @@ -1405,7 +1410,7 @@ msgstr "bù zhīchí MITM ānquánxìng" #: ports/stm/common-hal/sdioio/SDCard.c #, c-format msgid "MMC/SDIO Clock Error %x" -msgstr "" +msgstr "MMC/SDIO shízhōngcuòwù %x" #: shared-bindings/is31fl3741/IS31FL3741.c msgid "Mapping must be a tuple" @@ -1595,7 +1600,7 @@ msgstr "Yǐn jiǎo shàng méiyǒu xiàlā; 1Mohm tuījiàn" #: shared-module/touchio/TouchIn.c msgid "No pullup on pin; 1Mohm recommended" -msgstr "" +msgstr "Yǐn jiǎo shàng wú shàng lā diànzǔ; jiànyì shǐyòng 1Mohm" #: py/moderrno.c msgid "No space left on device" @@ -2638,6 +2643,7 @@ msgid "bits must be 32 or less" msgstr "wèi bì xū shì 32 huò gèng shǎo" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c #: shared-bindings/audiodelays/PitchShift.c #: shared-bindings/audiofilters/Distortion.c #: shared-bindings/audiofilters/Filter.c shared-bindings/audiomixer/Mixer.c diff --git a/main.c b/main.c index 0ea389635f..109cdfdf25 100644 --- a/main.c +++ b/main.c @@ -903,12 +903,8 @@ static void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { #endif } - port_post_boot_py(true); - cleanup_after_vm(_exec_result.exception); _exec_result.exception = NULL; - - port_post_boot_py(false); } static int run_repl(safe_mode_t safe_mode) { diff --git a/ports/atmel-samd/common-hal/alarm/touch/TouchAlarm.c b/ports/atmel-samd/common-hal/alarm/touch/TouchAlarm.c index 44396fa8e8..3c1fbb5ba7 100644 --- a/ports/atmel-samd/common-hal/alarm/touch/TouchAlarm.c +++ b/ports/atmel-samd/common-hal/alarm/touch/TouchAlarm.c @@ -7,6 +7,6 @@ #include "shared-bindings/alarm/touch/TouchAlarm.h" #include "shared-bindings/microcontroller/__init__.h" -void common_hal_alarm_touch_touchalarm_construct(alarm_touch_touchalarm_obj_t *self, const mcu_pin_obj_t *pin) { +NORETURN void common_hal_alarm_touch_touchalarm_construct(alarm_touch_touchalarm_obj_t *self, const mcu_pin_obj_t *pin) { mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_TouchAlarm); } diff --git a/ports/atmel-samd/mpconfigport.h b/ports/atmel-samd/mpconfigport.h index eca28dbd9b..3eafe0acfa 100644 --- a/ports/atmel-samd/mpconfigport.h +++ b/ports/atmel-samd/mpconfigport.h @@ -100,6 +100,11 @@ #define CIRCUITPY_DEFAULT_STACK_SIZE 3584 #endif +#ifndef CIRCUITPY_PYSTACK_SIZE +// Default for most boards is 2048 starting with CircuitPython 10, but on SAMD21, keep it at previous lower value. +#define CIRCUITPY_PYSTACK_SIZE 1536 +#endif + #ifndef SAMD21_BOD33_LEVEL // Set brownout detection to ~2.7V. Default from factory is 1.7V, // which is too low for proper operation of external SPI flash chips diff --git a/ports/atmel-samd/supervisor/port.c b/ports/atmel-samd/supervisor/port.c index 8f5115789d..03c2c2543a 100644 --- a/ports/atmel-samd/supervisor/port.c +++ b/ports/atmel-samd/supervisor/port.c @@ -686,7 +686,7 @@ void port_idle_until_interrupt(void) { /** * \brief Default interrupt handler for unused IRQs. */ -__attribute__((used)) void HardFault_Handler(void) { +__attribute__((used)) NORETURN void HardFault_Handler(void) { #ifdef ENABLE_MICRO_TRACE_BUFFER // Turn off the micro trace buffer so we don't fill it up in the infinite // loop below. diff --git a/ports/broadcom/common-hal/microcontroller/__init__.c b/ports/broadcom/common-hal/microcontroller/__init__.c index b0ed1fcb9e..279be26f91 100644 --- a/ports/broadcom/common-hal/microcontroller/__init__.c +++ b/ports/broadcom/common-hal/microcontroller/__init__.c @@ -11,6 +11,7 @@ #include "common-hal/microcontroller/__init__.h" #include "peripherals/broadcom/defines.h" #include "peripherals/broadcom/interrupts.h" +#include "supervisor/port.h" #include "mphalport.h" @@ -39,6 +40,7 @@ void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) { } void common_hal_mcu_reset(void) { + reset_cpu(); } // The singleton microcontroller.Processor object, bound to microcontroller.cpu diff --git a/ports/espressif/supervisor/port.c b/ports/espressif/supervisor/port.c index c05cab92d3..c99b1ca87d 100644 --- a/ports/espressif/supervisor/port.c +++ b/ports/espressif/supervisor/port.c @@ -324,8 +324,12 @@ void port_free(void *ptr) { heap_caps_free(ptr); } -void *port_realloc(void *ptr, size_t size) { - return heap_caps_realloc(ptr, size, MALLOC_CAP_8BIT); +void *port_realloc(void *ptr, size_t size, bool dma_capable) { + size_t caps = MALLOC_CAP_8BIT; + if (dma_capable) { + caps |= MALLOC_CAP_DMA; + } + return heap_caps_realloc(ptr, size, caps); } size_t port_heap_get_largest_free_size(void) { @@ -493,11 +497,17 @@ void port_idle_until_interrupt(void) { } } -void port_post_boot_py(bool heap_valid) { - if (!heap_valid && filesystem_present()) { +#if CIRCUITPY_WIFI +void port_boot_info(void) { + uint8_t mac[6]; + esp_wifi_get_mac(ESP_IF_WIFI_STA, mac); + mp_printf(&mp_plat_print, "MAC"); + for (int i = 0; i < 6; i++) { + mp_printf(&mp_plat_print, ":%02X", mac[i]); } + mp_printf(&mp_plat_print, "\n"); } - +#endif // Wrap main in app_main that the IDF expects. extern void main(void); diff --git a/ports/nordic/boards/TG-Watch/mpconfigboard.h b/ports/nordic/boards/TG-Watch/mpconfigboard.h index 8ebb88f6f8..4016b1ea84 100644 --- a/ports/nordic/boards/TG-Watch/mpconfigboard.h +++ b/ports/nordic/boards/TG-Watch/mpconfigboard.h @@ -13,7 +13,7 @@ #define MICROPY_HW_MCU_NAME "nRF52840" // TG-Gui requires a deeper call stack than normal CircuitPython, this is intentional overkill -#define CIRCUITPY_PYSTACK_SIZE 8192 // 1536 is the normal size, (32 bytes/frame * 48 frames) +#define CIRCUITPY_PYSTACK_SIZE 8192 // the board has a 32mhz crystal but NOT a 32khz one #define BOARD_HAS_32KHZ_XTAL 0 diff --git a/ports/raspberrypi/audio_dma.c b/ports/raspberrypi/audio_dma.c index c17ec10229..1d9642a16e 100644 --- a/ports/raspberrypi/audio_dma.c +++ b/ports/raspberrypi/audio_dma.c @@ -227,12 +227,7 @@ audio_dma_result audio_dma_setup_playback( max_buffer_length /= dma->sample_spacing; } - dma->buffer[0] = (uint8_t *)m_realloc(dma->buffer[0], - #if MICROPY_MALLOC_USES_ALLOCATED_SIZE - dma->buffer_length[0], // Old size - #endif - max_buffer_length); - + dma->buffer[0] = (uint8_t *)port_realloc(dma->buffer[0], max_buffer_length, true); dma->buffer_length[0] = max_buffer_length; if (dma->buffer[0] == NULL) { @@ -240,12 +235,7 @@ audio_dma_result audio_dma_setup_playback( } if (!single_buffer) { - dma->buffer[1] = (uint8_t *)m_realloc(dma->buffer[1], - #if MICROPY_MALLOC_USES_ALLOCATED_SIZE - dma->buffer_length[1], // Old size - #endif - max_buffer_length); - + dma->buffer[1] = (uint8_t *)port_realloc(dma->buffer[1], max_buffer_length, true); dma->buffer_length[1] = max_buffer_length; if (dma->buffer[1] == NULL) { @@ -439,21 +429,11 @@ void audio_dma_init(audio_dma_t *dma) { } void audio_dma_deinit(audio_dma_t *dma) { - #if MICROPY_MALLOC_USES_ALLOCATED_SIZE - m_free(dma->buffer[0], dma->buffer_length[0]); - #else - m_free(dma->buffer[0]); - #endif - + port_free(dma->buffer[0]); dma->buffer[0] = NULL; dma->buffer_length[0] = 0; - #if MICROPY_MALLOC_USES_ALLOCATED_SIZE - m_free(dma->buffer[1], dma->buffer_length[1]); - #else - m_free(dma->buffer[1]); - #endif - + port_free(dma->buffer[1]); dma->buffer[1] = NULL; dma->buffer_length[1] = 0; } diff --git a/ports/raspberrypi/audio_dma.h b/ports/raspberrypi/audio_dma.h index 48cc024fda..e4636501c6 100644 --- a/ports/raspberrypi/audio_dma.h +++ b/ports/raspberrypi/audio_dma.h @@ -20,7 +20,7 @@ typedef enum { typedef struct { mp_obj_t sample; - uint8_t *buffer[2]; + uint8_t *buffer[2]; // Allocated through port_malloc so they are dma-able size_t buffer_length[2]; uint32_t channels_to_load_mask; uint32_t output_register_address; diff --git a/ports/raspberrypi/mpconfigport.h b/ports/raspberrypi/mpconfigport.h index 1181517fdf..aa20cd90b8 100644 --- a/ports/raspberrypi/mpconfigport.h +++ b/ports/raspberrypi/mpconfigport.h @@ -33,6 +33,11 @@ #define CIRCUITPY_PROCESSOR_COUNT (2) +// For many RP2 boards BOOTSEL is not connected to a GPIO pin. +#ifndef CIRCUITPY_BOOT_BUTTON +#define CIRCUITPY_BOOT_BUTTON_NO_GPIO (1) +#endif + #if CIRCUITPY_USB_HOST #define CIRCUITPY_USB_HOST_INSTANCE 1 #endif diff --git a/ports/raspberrypi/supervisor/port.c b/ports/raspberrypi/supervisor/port.c index 7514b4e6ad..60d43e78ad 100644 --- a/ports/raspberrypi/supervisor/port.c +++ b/ports/raspberrypi/supervisor/port.c @@ -4,6 +4,7 @@ // // SPDX-License-Identifier: MIT +#include #include #include @@ -56,6 +57,13 @@ #include "RP2350.h" // CMSIS #endif +#if CIRCUITPY_BOOT_BUTTON_NO_GPIO +#include "hardware/gpio.h" +#include "hardware/sync.h" +#include "hardware/structs/ioqspi.h" +#include "hardware/structs/sio.h" +#endif + #include "supervisor/shared/serial.h" #include "tusb.h" @@ -89,8 +97,7 @@ extern uint32_t _ld_itcm_size; extern uint32_t _ld_itcm_flash_copy; static tlsf_t _heap = NULL; -static pool_t _ram_pool = NULL; -static pool_t _psram_pool = NULL; +static tlsf_t _psram_heap = NULL; static size_t _psram_size = 0; #ifdef CIRCUITPY_PSRAM_CHIP_SELECT @@ -245,10 +252,9 @@ static void _port_heap_init(void) { uint32_t *heap_bottom = port_heap_get_bottom(); uint32_t *heap_top = port_heap_get_top(); size_t size = (heap_top - heap_bottom) * sizeof(uint32_t); - _heap = tlsf_create_with_pool(heap_bottom, size, 64 * 1024 * 1024); - _ram_pool = tlsf_get_pool(_heap); + _heap = tlsf_create_with_pool(heap_bottom, size, size); if (_psram_size > 0) { - _psram_pool = tlsf_add_pool(_heap, (void *)0x11000000, _psram_size); + _psram_heap = tlsf_create_with_pool((void *)0x11000000, _psram_size, _psram_size); } } @@ -257,15 +263,31 @@ void port_heap_init(void) { } void *port_malloc(size_t size, bool dma_capable) { + if (!dma_capable && _psram_size > 0) { + void *block = tlsf_malloc(_psram_heap, size); + if (block) { + return block; + } + } void *block = tlsf_malloc(_heap, size); return block; } void port_free(void *ptr) { - tlsf_free(_heap, ptr); + if (((size_t)ptr) < SRAM_BASE) { + tlsf_free(_psram_heap, ptr); + } else { + tlsf_free(_heap, ptr); + } } -void *port_realloc(void *ptr, size_t size) { +void *port_realloc(void *ptr, size_t size, bool dma_capable) { + if (_psram_size > 0 && ((ptr != NULL && ((size_t)ptr) < SRAM_BASE) || (ptr == NULL && !dma_capable))) { + void *block = tlsf_realloc(_psram_heap, ptr, size); + if (block) { + return block; + } + } return tlsf_realloc(_heap, ptr, size); } @@ -279,12 +301,13 @@ static bool max_size_walker(void *ptr, size_t size, int used, void *user) { size_t port_heap_get_largest_free_size(void) { size_t max_size = 0; - tlsf_walk_pool(_ram_pool, max_size_walker, &max_size); - if (_psram_pool != NULL) { - tlsf_walk_pool(_psram_pool, max_size_walker, &max_size); + tlsf_walk_pool(tlsf_get_pool(_heap), max_size_walker, &max_size); + max_size = tlsf_fit_size(_heap, max_size); + if (_psram_heap != NULL) { + tlsf_walk_pool(tlsf_get_pool(_psram_heap), max_size_walker, &max_size); + max_size = tlsf_fit_size(_psram_heap, max_size); } - // IDF does this. Not sure why. - return tlsf_fit_size(_heap, max_size); + return max_size; } safe_mode_t port_init(void) { @@ -549,7 +572,7 @@ void port_idle_until_interrupt(void) { /** * \brief Default interrupt handler for unused IRQs. */ -extern void isr_hardfault(void); // provide a prototype to avoid a missing-prototypes diagnostic +extern NORETURN void isr_hardfault(void); // provide a prototype to avoid a missing-prototypes diagnostic __attribute__((used)) void __not_in_flash_func(isr_hardfault)(void) { // Only safe mode from core 0 which is running CircuitPython. Core 1 faulting // should not be fatal to CP. (Fingers crossed.) @@ -576,3 +599,53 @@ void port_boot_info(void) { mp_printf(&mp_plat_print, "\n"); #endif } + +#if CIRCUITPY_BOOT_BUTTON_NO_GPIO +bool __no_inline_not_in_flash_func(port_boot_button_pressed)(void) { + // Sense the state of the boot button. Because this function + // disables flash, it cannot be safely called once the second + // core has been started. When the BOOTSEL button is sensed as + // pressed, return is delayed until the button is released and + // a delay has passed in order to debounce the button. + const uint32_t CS_PIN_INDEX = 1; + #if defined(PICO_RP2040) + const uint32_t CS_BIT = 1u << 1; + #else + const uint32_t CS_BIT = SIO_GPIO_HI_IN_QSPI_CSN_BITS; + #endif + uint32_t int_state = save_and_disable_interrupts(); + // Wait for any outstanding XIP activity to finish. Flash + // must be quiescent before disabling the chip select. Since + // there's no XIP busy indication we can test, we delay a + // generous 5 ms to allow any XIP activity to finish. + busy_wait_us(5000); + // Float the flash chip select pin. The line will HI-Z due to + // the external 10K pull-up resistor. + hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl, + GPIO_OVERRIDE_LOW << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB, + IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS); + // Delay 100 us to allow the CS line to stabilize. If BOOTSEL is + // pressed, the line will be pulled low by the button and its + // 1K external resistor to ground. + busy_wait_us(100); + bool button_pressed = !(sio_hw->gpio_hi_in & CS_BIT); + // Wait for the button to be released. + if (button_pressed) { + while (!(sio_hw->gpio_hi_in & CS_BIT)) { + tight_loop_contents(); + } + // Wait for 50 ms to debounce the button. + busy_wait_us(50000); + } + // Restore the flash chip select pin to its original state. + hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl, + GPIO_OVERRIDE_NORMAL << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB, + IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS); + // Delay 5 ms to allow the flash chip to re-enable and for the + // flash CS pin to stabilize. + busy_wait_us(5000); + // Restore the interrupt state. + restore_interrupts(int_state); + return button_pressed; +} +#endif diff --git a/ports/stm/supervisor/port.c b/ports/stm/supervisor/port.c index 5f29eaaf96..4aa8c04412 100644 --- a/ports/stm/supervisor/port.c +++ b/ports/stm/supervisor/port.c @@ -227,7 +227,7 @@ void port_free(void *ptr) { tlsf_free(_heap, ptr); } -void *port_realloc(void *ptr, size_t size) { +void *port_realloc(void *ptr, size_t size, bool dma_capable) { return tlsf_realloc(_heap, ptr, size); } #endif diff --git a/ports/unix/variants/coverage/mpconfigvariant.mk b/ports/unix/variants/coverage/mpconfigvariant.mk index 835fe70569..e18645409f 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.mk +++ b/ports/unix/variants/coverage/mpconfigvariant.mk @@ -36,6 +36,7 @@ SRC_BITMAP := \ shared-bindings/audiodelays/Echo.c \ shared-bindings/audiodelays/Chorus.c \ shared-bindings/audiodelays/PitchShift.c \ + shared-bindings/audiodelays/MultiTapDelay.c \ shared-bindings/audiodelays/__init__.c \ shared-bindings/audiofilters/Distortion.c \ shared-bindings/audiofilters/Filter.c \ @@ -80,6 +81,7 @@ SRC_BITMAP := \ shared-module/audiodelays/Echo.c \ shared-module/audiodelays/Chorus.c \ shared-module/audiodelays/PitchShift.c \ + shared-module/audiodelays/MultiTapDelay.c \ shared-module/audiodelays/__init__.c \ shared-module/audiofilters/Distortion.c \ shared-module/audiofilters/Filter.c \ diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index e6ebd97b3b..905b281f7e 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -300,7 +300,6 @@ async def build_circuitpython(): portdir / "common-hal/microcontroller/Pin.c", portdir / "common-hal/microcontroller/Processor.c", portdir / "common-hal/os/__init__.c", - "supervisor/stub/misc.c", "shared/readline/readline.c", "shared/runtime/context_manager_helpers.c", "shared/runtime/pyexec.c", diff --git a/ports/zephyr-cp/supervisor/port.c b/ports/zephyr-cp/supervisor/port.c index c1d6c913d6..860584c2b1 100644 --- a/ports/zephyr-cp/supervisor/port.c +++ b/ports/zephyr-cp/supervisor/port.c @@ -151,7 +151,7 @@ void port_free(void *ptr) { tlsf_free(heap, ptr); } -void *port_realloc(void *ptr, size_t size) { +void *port_realloc(void *ptr, size_t size, bool dma_capable) { return tlsf_realloc(heap, ptr, size); } diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 7eb55e09de..46fc6d6157 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -667,6 +667,7 @@ SRC_SHARED_MODULE_ALL = \ audiodelays/Echo.c \ audiodelays/Chorus.c \ audiodelays/PitchShift.c \ + audiodelays/MultiTapDelay.c \ audiodelays/__init__.c \ audiofilters/Distortion.c \ audiofilters/Filter.c \ diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 42e6f1841a..e52a68982a 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -344,6 +344,7 @@ typedef long mp_off_t; #define CIRCUITPY_CONSOLE_UART (1) #ifndef CIRCUITPY_CONSOLE_UART_BAUDRATE #define CIRCUITPY_CONSOLE_UART_BAUDRATE (115200) +#endif #if !defined(CIRCUITPY_CONSOLE_UART_PRINTF) #define CIRCUITPY_CONSOLE_UART_PRINTF(...) mp_printf(&console_uart_print, __VA_ARGS__) #endif @@ -353,7 +354,6 @@ typedef long mp_off_t; #if !defined(CIRCUITPY_CONSOLE_UART_TIMESTAMP) #define CIRCUITPY_CONSOLE_UART_TIMESTAMP (0) #endif -#endif #else #define CIRCUITPY_CONSOLE_UART (0) #define CIRCUITPY_CONSOLE_UART_PRINTF(...) (void)0 @@ -452,7 +452,7 @@ void background_callback_run_all(void); #endif #ifndef CIRCUITPY_PYSTACK_SIZE -#define CIRCUITPY_PYSTACK_SIZE 1536 +#define CIRCUITPY_PYSTACK_SIZE 2048 #endif // The VM heap starts at this size and doubles in size as needed until it runs @@ -635,6 +635,15 @@ void background_callback_run_all(void); #define CIRCUITPY_SAVES_PARTITION_SIZE 0 #endif +// Boards that have a boot button connected to a GPIO pin should set +// CIRCUITPY_BOOT_BUTTON_NO_GPIO to 1. +#ifndef CIRCUITPY_BOOT_BUTTON_NO_GPIO +#define CIRCUITPY_BOOT_BUTTON_NO_GPIO (0) +#endif +#if defined(CIRCUITPY_BOOT_BUTTON) && CIRCUITPY_BOOT_BUTTON_NO_GPIO +#error "CIRCUITPY_BOOT_BUTTON and CIRCUITPY_BOOT_BUTTON_NO_GPIO are mutually exclusive" +#endif + #if defined(__GNUC__) && !defined(__ZEPHYR__) #if __GNUC__ < CIRCUITPY_MIN_GCC_VERSION // (the 3 level scheme here is required to get expansion & stringization diff --git a/shared-bindings/alarm/time/TimeAlarm.c b/shared-bindings/alarm/time/TimeAlarm.c index 0277c22fc4..49d49c89a8 100644 --- a/shared-bindings/alarm/time/TimeAlarm.c +++ b/shared-bindings/alarm/time/TimeAlarm.c @@ -14,7 +14,7 @@ #include "shared-bindings/time/__init__.h" #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE -mp_obj_t MP_WEAK rtc_get_time_source_time(void) { +NORETURN mp_obj_t MP_WEAK rtc_get_time_source_time(void) { mp_raise_RuntimeError(MP_ERROR_TEXT("RTC is not supported on this board")); } #endif diff --git a/shared-bindings/audiodelays/Echo.c b/shared-bindings/audiodelays/Echo.c index 71e5330555..5ae849b19a 100644 --- a/shared-bindings/audiodelays/Echo.c +++ b/shared-bindings/audiodelays/Echo.c @@ -17,9 +17,6 @@ #include "shared-bindings/util.h" #include "shared-module/synthio/block.h" -#define DECAY_DEFAULT 0.7f -#define MIX_DEFAULT 0.5f - //| class Echo: //| """An Echo effect""" //| @@ -28,7 +25,7 @@ //| max_delay_ms: int = 500, //| delay_ms: synthio.BlockInput = 250.0, //| decay: synthio.BlockInput = 0.7, -//| mix: synthio.BlockInput = 0.5, +//| mix: synthio.BlockInput = 0.25, //| buffer_size: int = 512, //| sample_rate: int = 8000, //| bits_per_sample: int = 16, @@ -163,7 +160,7 @@ MP_PROPERTY_GETSET(audiodelays_echo_delay_ms_obj, (mp_obj_t)&audiodelays_echo_set_delay_ms_obj); //| decay: synthio.BlockInput -//| """The rate the echo decays between 0 and 1 where 1 is forever and 0 is no echo.""" +//| """The rate the echo fades between 0 and 1 where 0 is instant and 1 is never.""" static mp_obj_t audiodelays_echo_obj_get_decay(mp_obj_t self_in) { return common_hal_audiodelays_echo_get_decay(self_in); } @@ -181,7 +178,7 @@ MP_PROPERTY_GETSET(audiodelays_echo_decay_obj, (mp_obj_t)&audiodelays_echo_set_decay_obj); //| mix: synthio.BlockInput -//| """The rate the echo mix between 0 and 1 where 0 is only sample and 1 is all effect.""" +//| """The rate the echo mix between 0 and 1 where 0 is only sample, 0.5 is an equal mix of the sample and the effect and 1 is all effect.""" static mp_obj_t audiodelays_echo_obj_get_mix(mp_obj_t self_in) { return common_hal_audiodelays_echo_get_mix(self_in); } diff --git a/shared-bindings/audiodelays/MultiTapDelay.c b/shared-bindings/audiodelays/MultiTapDelay.c new file mode 100644 index 0000000000..5b057eaf80 --- /dev/null +++ b/shared-bindings/audiodelays/MultiTapDelay.c @@ -0,0 +1,306 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Cooper Dalrymple +// +// SPDX-License-Identifier: MIT + +#include + +#include "shared-bindings/audiodelays/MultiTapDelay.h" +#include "shared-bindings/audiocore/__init__.h" +#include "shared-module/audiodelays/MultiTapDelay.h" + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/util.h" +#include "shared-module/synthio/block.h" + +//| class MultiTapDelay: +//| """A delay with multiple buffer positions to create a rhythmic effect.""" +//| +//| def __init__( +//| self, +//| max_delay_ms: int = 500, +//| delay_ms: synthio.BlockInput = 250.0, +//| decay: synthio.BlockInput = 0.7, +//| mix: synthio.BlockInput = 0.25, +//| taps: Optional[Tuple[float|Tuple[float, float], ...]] = None, +//| buffer_size: int = 512, +//| sample_rate: int = 8000, +//| bits_per_sample: int = 16, +//| samples_signed: bool = True, +//| channel_count: int = 1, +//| ) -> None: +//| """Create a delay effect where you hear the original sample play back at varying times, or "taps". +//| These tap positions and levels can be used to create rhythmic effects. +//| The timing of the delay can be changed at runtime with the delay_ms parameter but the delay can +//| never exceed the max_delay_ms parameter. The maximum delay you can set is limited by available +//| memory. +//| +//| Each time the delay plays back the volume is reduced by the decay setting (delay * decay). +//| +//| The mix parameter allows you to change how much of the unchanged sample passes through to +//| the output to how much of the effect audio you hear as the output. +//| +//| :param int max_delay_ms: The maximum time the delay can be in milliseconds. +//| :param float delay_ms: The current time of the delay in milliseconds. Must be less than max_delay_ms. +//| :param synthio.BlockInput decay: The rate the delay fades. 0.0 = instant; 1.0 = never. +//| :param synthio.BlockInput mix: The mix as a ratio of the sample (0.0) to the effect (1.0). +//| :param tuple taps: The positions and levels to tap into the delay buffer. +//| :param int buffer_size: The total size in bytes of each of the two playback buffers to use. +//| :param int sample_rate: The sample rate to be used. +//| :param int channel_count: The number of channels the source samples contain. 1 = mono; 2 = stereo. +//| :param int bits_per_sample: The bits per sample of the effect. +//| :param bool samples_signed: Effect is signed (True) or unsigned (False). +//| +//| Playing adding a multi-tap delay to a synth:: +//| +//| import time +//| import board +//| import audiobusio +//| import synthio +//| import audiodelays +//| +//| audio = audiobusio.I2SOut(bit_clock=board.GP20, word_select=board.GP21, data=board.GP22) +//| synth = synthio.Synthesizer(channel_count=1, sample_rate=44100) +//| effect = audiodelays.MultiTapDelay(max_delay_ms=500, delay_ms=500, decay=0.65, mix=0.5, taps=((2/3, 0.7), 1), buffer_size=1024, channel_count=1, sample_rate=44100) +//| effect.play(synth) +//| audio.play(effect) +//| +//| note = synthio.Note(261) +//| while True: +//| synth.press(note) +//| time.sleep(0.05) +//| synth.release(note) +//| time.sleep(5)""" +//| ... +//| +static mp_obj_t audiodelays_multi_tap_delay_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_max_delay_ms, ARG_delay_ms, ARG_decay, ARG_mix, ARG_taps, ARG_buffer_size, ARG_sample_rate, ARG_bits_per_sample, ARG_samples_signed, ARG_channel_count, }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_max_delay_ms, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 500 } }, + { MP_QSTR_delay_ms, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(250) } }, + { MP_QSTR_decay, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_mix, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_taps, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + { MP_QSTR_buffer_size, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 512} }, + { MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 8000} }, + { MP_QSTR_bits_per_sample, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 16} }, + { MP_QSTR_samples_signed, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + { MP_QSTR_channel_count, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1 } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_int_t max_delay_ms = mp_arg_validate_int_range(args[ARG_max_delay_ms].u_int, 1, 4000, MP_QSTR_max_delay_ms); + + mp_int_t channel_count = mp_arg_validate_int_range(args[ARG_channel_count].u_int, 1, 2, MP_QSTR_channel_count); + mp_int_t sample_rate = mp_arg_validate_int_min(args[ARG_sample_rate].u_int, 1, MP_QSTR_sample_rate); + mp_int_t bits_per_sample = args[ARG_bits_per_sample].u_int; + if (bits_per_sample != 8 && bits_per_sample != 16) { + mp_raise_ValueError(MP_ERROR_TEXT("bits_per_sample must be 8 or 16")); + } + + audiodelays_multi_tap_delay_obj_t *self = mp_obj_malloc(audiodelays_multi_tap_delay_obj_t, &audiodelays_multi_tap_delay_type); + common_hal_audiodelays_multi_tap_delay_construct(self, max_delay_ms, args[ARG_delay_ms].u_obj, args[ARG_decay].u_obj, args[ARG_mix].u_obj, args[ARG_taps].u_obj, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitialises the MultiTapDelay.""" +//| ... +//| +static mp_obj_t audiodelays_multi_tap_delay_deinit(mp_obj_t self_in) { + audiodelays_multi_tap_delay_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiodelays_multi_tap_delay_deinit(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_multi_tap_delay_deinit_obj, audiodelays_multi_tap_delay_deinit); + +static void check_for_deinit(audiodelays_multi_tap_delay_obj_t *self) { + audiosample_check_for_deinit(&self->base); +} + +//| def __enter__(self) -> MultiTapDelay: +//| """No-op used by Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +// Provided by context manager helper. + + +//| delay_ms: float +//| """Time to delay the incoming signal in milliseconds. Must be less than max_delay_ms.""" +//| +static mp_obj_t audiodelays_multi_tap_delay_obj_get_delay_ms(mp_obj_t self_in) { + audiodelays_multi_tap_delay_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_float(common_hal_audiodelays_multi_tap_delay_get_delay_ms(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_multi_tap_delay_get_delay_ms_obj, audiodelays_multi_tap_delay_obj_get_delay_ms); + +static mp_obj_t audiodelays_multi_tap_delay_obj_set_delay_ms(mp_obj_t self_in, mp_obj_t delay_ms_in) { + audiodelays_multi_tap_delay_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiodelays_multi_tap_delay_set_delay_ms(self, delay_ms_in); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiodelays_multi_tap_delay_set_delay_ms_obj, audiodelays_multi_tap_delay_obj_set_delay_ms); + +MP_PROPERTY_GETSET(audiodelays_multi_tap_delay_delay_ms_obj, + (mp_obj_t)&audiodelays_multi_tap_delay_get_delay_ms_obj, + (mp_obj_t)&audiodelays_multi_tap_delay_set_delay_ms_obj); + +//| decay: synthio.BlockInput +//| """The rate the echo fades between 0 and 1 where 0 is instant and 1 is never.""" +static mp_obj_t audiodelays_multi_tap_delay_obj_get_decay(mp_obj_t self_in) { + return common_hal_audiodelays_multi_tap_delay_get_decay(self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_multi_tap_delay_get_decay_obj, audiodelays_multi_tap_delay_obj_get_decay); + +static mp_obj_t audiodelays_multi_tap_delay_obj_set_decay(mp_obj_t self_in, mp_obj_t decay_in) { + audiodelays_multi_tap_delay_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiodelays_multi_tap_delay_set_decay(self, decay_in); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiodelays_multi_tap_delay_set_decay_obj, audiodelays_multi_tap_delay_obj_set_decay); + +MP_PROPERTY_GETSET(audiodelays_multi_tap_delay_decay_obj, + (mp_obj_t)&audiodelays_multi_tap_delay_get_decay_obj, + (mp_obj_t)&audiodelays_multi_tap_delay_set_decay_obj); + +//| mix: synthio.BlockInput +//| """The mix of the effect between 0 and 1 where 0 is only sample, 0.5 is an equal mix of the sample and the effect and 1 is all effect.""" +static mp_obj_t audiodelays_multi_tap_delay_obj_get_mix(mp_obj_t self_in) { + return common_hal_audiodelays_multi_tap_delay_get_mix(self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_multi_tap_delay_get_mix_obj, audiodelays_multi_tap_delay_obj_get_mix); + +static mp_obj_t audiodelays_multi_tap_delay_obj_set_mix(mp_obj_t self_in, mp_obj_t mix_in) { + audiodelays_multi_tap_delay_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiodelays_multi_tap_delay_set_mix(self, mix_in); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiodelays_multi_tap_delay_set_mix_obj, audiodelays_multi_tap_delay_obj_set_mix); + +MP_PROPERTY_GETSET(audiodelays_multi_tap_delay_mix_obj, + (mp_obj_t)&audiodelays_multi_tap_delay_get_mix_obj, + (mp_obj_t)&audiodelays_multi_tap_delay_set_mix_obj); + +//| taps: Tuple[float|int|Tuple[float|int, float|int], ...] +//| """The position or position and level of delay taps. +//| The position is a number from 0 (start) to 1 (end) as a relative position in the delay buffer. +//| The level is a number from 0 (silence) to 1 (full volume). +//| If only a float or integer is provided as an element of the tuple, the level is assumed to be 1. +//| When retrieving the value of this property, the level will always be included.""" +//| +static mp_obj_t audiodelays_multi_tap_delay_obj_get_taps(mp_obj_t self_in) { + return common_hal_audiodelays_multi_tap_delay_get_taps(self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_multi_tap_delay_get_taps_obj, audiodelays_multi_tap_delay_obj_get_taps); + +static mp_obj_t audiodelays_multi_tap_delay_obj_set_taps(mp_obj_t self_in, mp_obj_t taps_in) { + audiodelays_multi_tap_delay_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiodelays_multi_tap_delay_set_taps(self, taps_in); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiodelays_multi_tap_delay_set_taps_obj, audiodelays_multi_tap_delay_obj_set_taps); + +MP_PROPERTY_GETSET(audiodelays_multi_tap_delay_taps_obj, + (mp_obj_t)&audiodelays_multi_tap_delay_get_taps_obj, + (mp_obj_t)&audiodelays_multi_tap_delay_set_taps_obj); + + + +//| playing: bool +//| """True when the effect is playing a sample. (read-only)""" +//| +static mp_obj_t audiodelays_multi_tap_delay_obj_get_playing(mp_obj_t self_in) { + audiodelays_multi_tap_delay_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_audiodelays_multi_tap_delay_get_playing(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_multi_tap_delay_get_playing_obj, audiodelays_multi_tap_delay_obj_get_playing); + +MP_PROPERTY_GETTER(audiodelays_multi_tap_delay_playing_obj, + (mp_obj_t)&audiodelays_multi_tap_delay_get_playing_obj); + +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| """Plays the sample once when loop=False and continuously when loop=True. +//| Does not block. Use `playing` to block. +//| +//| The sample must match the encoding settings given in the constructor.""" +//| ... +//| +static mp_obj_t audiodelays_multi_tap_delay_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_sample, ARG_loop }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED, {} }, + { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + audiodelays_multi_tap_delay_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + + mp_obj_t sample = args[ARG_sample].u_obj; + common_hal_audiodelays_multi_tap_delay_play(self, sample, args[ARG_loop].u_bool); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audiodelays_multi_tap_delay_play_obj, 1, audiodelays_multi_tap_delay_obj_play); + +//| def stop(self) -> None: +//| """Stops playback of the sample. The echo continues playing.""" +//| ... +//| +//| +static mp_obj_t audiodelays_multi_tap_delay_obj_stop(mp_obj_t self_in) { + audiodelays_multi_tap_delay_obj_t *self = MP_OBJ_TO_PTR(self_in); + + common_hal_audiodelays_multi_tap_delay_stop(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_multi_tap_delay_stop_obj, audiodelays_multi_tap_delay_obj_stop); + +static const mp_rom_map_elem_t audiodelays_multi_tap_delay_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audiodelays_multi_tap_delay_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&default___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audiodelays_multi_tap_delay_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audiodelays_multi_tap_delay_stop_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiodelays_multi_tap_delay_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_delay_ms), MP_ROM_PTR(&audiodelays_multi_tap_delay_delay_ms_obj) }, + { MP_ROM_QSTR(MP_QSTR_decay), MP_ROM_PTR(&audiodelays_multi_tap_delay_decay_obj) }, + { MP_ROM_QSTR(MP_QSTR_mix), MP_ROM_PTR(&audiodelays_multi_tap_delay_mix_obj) }, + { MP_ROM_QSTR(MP_QSTR_taps), MP_ROM_PTR(&audiodelays_multi_tap_delay_taps_obj) }, + AUDIOSAMPLE_FIELDS, +}; +static MP_DEFINE_CONST_DICT(audiodelays_multi_tap_delay_locals_dict, audiodelays_multi_tap_delay_locals_dict_table); + +static const audiosample_p_t audiodelays_multi_tap_delay_proto = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_audiosample) + .reset_buffer = (audiosample_reset_buffer_fun)audiodelays_multi_tap_delay_reset_buffer, + .get_buffer = (audiosample_get_buffer_fun)audiodelays_multi_tap_delay_get_buffer, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + audiodelays_multi_tap_delay_type, + MP_QSTR_MultiTapDelay, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, audiodelays_multi_tap_delay_make_new, + locals_dict, &audiodelays_multi_tap_delay_locals_dict, + protocol, &audiodelays_multi_tap_delay_proto + ); diff --git a/shared-bindings/audiodelays/MultiTapDelay.h b/shared-bindings/audiodelays/MultiTapDelay.h new file mode 100644 index 0000000000..6684c16b63 --- /dev/null +++ b/shared-bindings/audiodelays/MultiTapDelay.h @@ -0,0 +1,34 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Cooper Dalrymple +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-module/audiodelays/MultiTapDelay.h" + +extern const mp_obj_type_t audiodelays_multi_tap_delay_type; + +void common_hal_audiodelays_multi_tap_delay_construct(audiodelays_multi_tap_delay_obj_t *self, uint32_t max_delay_ms, + mp_obj_t delay_ms, mp_obj_t decay, mp_obj_t mix, mp_obj_t taps, + uint32_t buffer_size, uint8_t bits_per_sample, bool samples_signed, + uint8_t channel_count, uint32_t sample_rate); + +void common_hal_audiodelays_multi_tap_delay_deinit(audiodelays_multi_tap_delay_obj_t *self); + +mp_float_t common_hal_audiodelays_multi_tap_delay_get_delay_ms(audiodelays_multi_tap_delay_obj_t *self); +void common_hal_audiodelays_multi_tap_delay_set_delay_ms(audiodelays_multi_tap_delay_obj_t *self, mp_obj_t delay_ms); + +mp_obj_t common_hal_audiodelays_multi_tap_delay_get_decay(audiodelays_multi_tap_delay_obj_t *self); +void common_hal_audiodelays_multi_tap_delay_set_decay(audiodelays_multi_tap_delay_obj_t *self, mp_obj_t decay); + +mp_obj_t common_hal_audiodelays_multi_tap_delay_get_mix(audiodelays_multi_tap_delay_obj_t *self); +void common_hal_audiodelays_multi_tap_delay_set_mix(audiodelays_multi_tap_delay_obj_t *self, mp_obj_t mix); + +mp_obj_t common_hal_audiodelays_multi_tap_delay_get_taps(audiodelays_multi_tap_delay_obj_t *self); +void common_hal_audiodelays_multi_tap_delay_set_taps(audiodelays_multi_tap_delay_obj_t *self, mp_obj_t taps); + +bool common_hal_audiodelays_multi_tap_delay_get_playing(audiodelays_multi_tap_delay_obj_t *self); +void common_hal_audiodelays_multi_tap_delay_play(audiodelays_multi_tap_delay_obj_t *self, mp_obj_t sample, bool loop); +void common_hal_audiodelays_multi_tap_delay_stop(audiodelays_multi_tap_delay_obj_t *self); diff --git a/shared-bindings/audiodelays/__init__.c b/shared-bindings/audiodelays/__init__.c index 89b0fdb338..e93052eabf 100644 --- a/shared-bindings/audiodelays/__init__.c +++ b/shared-bindings/audiodelays/__init__.c @@ -13,6 +13,7 @@ #include "shared-bindings/audiodelays/Echo.h" #include "shared-bindings/audiodelays/Chorus.h" #include "shared-bindings/audiodelays/PitchShift.h" +#include "shared-bindings/audiodelays/MultiTapDelay.h" //| """Support for audio delay effects //| @@ -25,6 +26,7 @@ static const mp_rom_map_elem_t audiodelays_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_Echo), MP_ROM_PTR(&audiodelays_echo_type) }, { MP_ROM_QSTR(MP_QSTR_Chorus), MP_ROM_PTR(&audiodelays_chorus_type) }, { MP_ROM_QSTR(MP_QSTR_PitchShift), MP_ROM_PTR(&audiodelays_pitch_shift_type) }, + { MP_ROM_QSTR(MP_QSTR_MultiTapDelay), MP_ROM_PTR(&audiodelays_multi_tap_delay_type) }, }; static MP_DEFINE_CONST_DICT(audiodelays_module_globals, audiodelays_module_globals_table); diff --git a/shared-bindings/audiofilters/Filter.c b/shared-bindings/audiofilters/Filter.c index 075ec3bdcc..2a4887a4d4 100644 --- a/shared-bindings/audiofilters/Filter.c +++ b/shared-bindings/audiofilters/Filter.c @@ -17,8 +17,6 @@ #include "shared-bindings/util.h" #include "shared-module/synthio/block.h" -#define MIX_DEFAULT 1.0f - //| class Filter: //| """A Filter effect""" //| diff --git a/shared-bindings/bitmaptools/__init__.c b/shared-bindings/bitmaptools/__init__.c index 9d655c3f90..709e458211 100644 --- a/shared-bindings/bitmaptools/__init__.c +++ b/shared-bindings/bitmaptools/__init__.c @@ -691,7 +691,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_draw_polygon_obj, 0, bitmaptools_obj_draw //| available in the destination bitmap. //| //| If x1 or y1 are not specified, they are taken as 0. If x2 or y2 -//| are not specified, or are given as -1, they are taken as the width +//| are not specified, or are given as None, they are taken as the width //| and height of the image. //| //| The coordinates affected by the blit are ``x1 <= x < x2`` and ``y1 <= y < y2``. @@ -719,7 +719,7 @@ static mp_obj_t bitmaptools_arrayblit(size_t n_args, const mp_obj_t *pos_args, m static const mp_arg_t allowed_args[] = { { MP_QSTR_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - ALLOWED_ARGS_X1_Y1_X2_Y2(0, MP_ARG_REQUIRED), + ALLOWED_ARGS_X1_Y1_X2_Y2(0, 0), { MP_QSTR_skip_index, MP_ARG_OBJ, {.u_obj = mp_const_none } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; diff --git a/shared-bindings/bitmaptools/__init__.h b/shared-bindings/bitmaptools/__init__.h index 2d9b22e216..21116b8bd4 100644 --- a/shared-bindings/bitmaptools/__init__.h +++ b/shared-bindings/bitmaptools/__init__.h @@ -80,8 +80,8 @@ typedef struct { #define ARGS_X1_Y1_X2_Y2 ARG_x1, ARG_y1, ARG_x2, ARG_y2 #define ALLOWED_ARGS_X1_Y1_X2_Y2(if_required1, if_required2) \ {MP_QSTR_x1, if_required1 | MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0)}}, \ - {MP_QSTR_y1, if_required2 | MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0)}}, \ - {MP_QSTR_x2, if_required1 | MP_ARG_OBJ, {.u_obj = MP_ROM_NONE}}, \ + {MP_QSTR_y1, if_required1 | MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0)}}, \ + {MP_QSTR_x2, if_required2 | MP_ARG_OBJ, {.u_obj = MP_ROM_NONE}}, \ {MP_QSTR_y2, if_required2 | MP_ARG_OBJ, {.u_obj = MP_ROM_NONE}} bitmaptools_rect_t bitmaptools_validate_coord_range_pair(const mp_arg_val_t in[4], int width, int height); diff --git a/shared-bindings/microcontroller/__init__.h b/shared-bindings/microcontroller/__init__.h index a94cca7b69..2a4a973278 100644 --- a/shared-bindings/microcontroller/__init__.h +++ b/shared-bindings/microcontroller/__init__.h @@ -20,7 +20,7 @@ extern void common_hal_mcu_disable_interrupts(void); extern void common_hal_mcu_enable_interrupts(void); extern void common_hal_mcu_on_next_reset(mcu_runmode_t runmode); -extern void common_hal_mcu_reset(void); +NORETURN extern void common_hal_mcu_reset(void); extern const mp_obj_dict_t mcu_pin_globals; diff --git a/shared-bindings/storage/__init__.h b/shared-bindings/storage/__init__.h index 7ab5fc97bd..ed3e8d44e4 100644 --- a/shared-bindings/storage/__init__.h +++ b/shared-bindings/storage/__init__.h @@ -16,7 +16,7 @@ void common_hal_storage_umount_path(const char *path); void common_hal_storage_umount_object(mp_obj_t vfs_obj); void common_hal_storage_remount(const char *path, bool readonly, bool disable_concurrent_write_protection); mp_obj_t common_hal_storage_getmount(const char *path); -void common_hal_storage_erase_filesystem(bool extended); +NORETURN void common_hal_storage_erase_filesystem(bool extended); bool common_hal_storage_disable_usb_drive(void); bool common_hal_storage_enable_usb_drive(void); diff --git a/shared-bindings/time/__init__.c b/shared-bindings/time/__init__.c index 504cd07d90..be936b6d01 100644 --- a/shared-bindings/time/__init__.c +++ b/shared-bindings/time/__init__.c @@ -172,7 +172,7 @@ MP_DEFINE_CONST_FUN_OBJ_0(time_not_implemented_obj, time_not_implemented); #endif #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE -mp_obj_t MP_WEAK rtc_get_time_source_time(void) { +NORETURN mp_obj_t MP_WEAK rtc_get_time_source_time(void) { mp_raise_RuntimeError(MP_ERROR_TEXT("RTC is not supported on this board")); } diff --git a/shared-bindings/util.h b/shared-bindings/util.h index f1e4dca13a..1d9fa0e62b 100644 --- a/shared-bindings/util.h +++ b/shared-bindings/util.h @@ -9,6 +9,6 @@ #include "py/mpprint.h" #include "py/runtime.h" -void raise_deinited_error(void); +NORETURN void raise_deinited_error(void); void properties_print_helper(const mp_print_t *print, mp_obj_t self_in, const mp_arg_t *properties, size_t n_properties); void properties_construct_helper(mp_obj_t self_in, const mp_arg_t *args, const mp_arg_val_t *vals, size_t n_properties); diff --git a/shared-module/audiodelays/Echo.c b/shared-module/audiodelays/Echo.c index 968c3bbddb..f4e15b80aa 100644 --- a/shared-module/audiodelays/Echo.c +++ b/shared-module/audiodelays/Echo.c @@ -71,7 +71,7 @@ void common_hal_audiodelays_echo_construct(audiodelays_echo_obj_t *self, uint32_ synthio_block_assign_slot(delay_ms, &self->delay_ms, MP_QSTR_delay_ms); if (mix == MP_OBJ_NULL) { - mix = mp_obj_new_float(MICROPY_FLOAT_CONST(0.5)); + mix = mp_obj_new_float(MICROPY_FLOAT_CONST(0.25)); } synthio_block_assign_slot(mix, &self->mix, MP_QSTR_mix); @@ -268,7 +268,7 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t * // get the effect values we need from the BlockInput. These may change at run time so you need to do bounds checking if required shared_bindings_synthio_lfo_tick(self->base.sample_rate, n / self->base.channel_count); - mp_float_t mix = synthio_block_slot_get_limited(&self->mix, MICROPY_FLOAT_CONST(0.0), MICROPY_FLOAT_CONST(1.0)); + mp_float_t mix = synthio_block_slot_get_limited(&self->mix, MICROPY_FLOAT_CONST(0.0), MICROPY_FLOAT_CONST(1.0)) * MICROPY_FLOAT_CONST(2.0); mp_float_t decay = synthio_block_slot_get_limited(&self->decay, MICROPY_FLOAT_CONST(0.0), MICROPY_FLOAT_CONST(1.0)); mp_float_t f_delay_ms = synthio_block_slot_get(&self->delay_ms); @@ -323,7 +323,7 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t * echo_buffer[self->echo_buffer_write_pos++] = word; } - word = (int16_t)(echo * mix); + word = (int16_t)(echo * MIN(mix, MICROPY_FLOAT_CONST(1.0))); if (MP_LIKELY(self->base.bits_per_sample == 16)) { word_buffer[i] = word; @@ -414,16 +414,17 @@ audioio_get_buffer_result_t audiodelays_echo_get_buffer(audiodelays_echo_obj_t * } } - word = echo + sample_word; + word = (int32_t)((sample_word * MIN(MICROPY_FLOAT_CONST(2.0) - mix, MICROPY_FLOAT_CONST(1.0))) + + (echo * MIN(mix, MICROPY_FLOAT_CONST(1.0)))); word = synthio_mix_down_sample(word, SYNTHIO_MIX_DOWN_SCALE(2)); if (MP_LIKELY(self->base.bits_per_sample == 16)) { - word_buffer[i] = (int16_t)((sample_word * (MICROPY_FLOAT_CONST(1.0) - mix)) + (word * mix)); + word_buffer[i] = (int16_t)word; if (!self->base.samples_signed) { word_buffer[i] ^= 0x8000; } } else { - int8_t mixed = (int16_t)((sample_word * (MICROPY_FLOAT_CONST(1.0) - mix)) + (word * mix)); + int8_t mixed = (int16_t)word; if (self->base.samples_signed) { hword_buffer[i] = mixed; } else { diff --git a/shared-module/audiodelays/MultiTapDelay.c b/shared-module/audiodelays/MultiTapDelay.c new file mode 100644 index 0000000000..7b702ecf11 --- /dev/null +++ b/shared-module/audiodelays/MultiTapDelay.c @@ -0,0 +1,478 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Cooper Dalrymple +// +// SPDX-License-Identifier: MIT +#include "shared-bindings/audiodelays/MultiTapDelay.h" +#include "shared-bindings/audiocore/__init__.h" + +#include +#include "py/runtime.h" +#include + +void common_hal_audiodelays_multi_tap_delay_construct(audiodelays_multi_tap_delay_obj_t *self, uint32_t max_delay_ms, + mp_obj_t delay_ms, mp_obj_t decay, mp_obj_t mix, mp_obj_t taps, + uint32_t buffer_size, uint8_t bits_per_sample, + bool samples_signed, uint8_t channel_count, uint32_t sample_rate) { + + // Basic settings every effect and audio sample has + // These are the effects values, not the source sample(s) + self->base.bits_per_sample = bits_per_sample; // Most common is 16, but 8 is also supported in many places + self->base.samples_signed = samples_signed; // Are the samples we provide signed (common is true) + self->base.channel_count = channel_count; // Channels can be 1 for mono or 2 for stereo + self->base.sample_rate = sample_rate; // Sample rate for the effect, this generally needs to match all audio objects + self->base.single_buffer = false; + self->base.max_buffer_length = buffer_size; + + // To smooth things out as CircuitPython is doing other tasks most audio objects have a buffer + // A double buffer is set up here so the audio output can use DMA on buffer 1 while we + // write to and create buffer 2. + // This buffer is what is passed to the audio component that plays the effect. + // Samples are set sequentially. For stereo audio they are passed L/R/L/R/... + self->buffer_len = buffer_size; // in bytes + + self->buffer[0] = m_malloc_maybe(self->buffer_len); + if (self->buffer[0] == NULL) { + common_hal_audiodelays_multi_tap_delay_deinit(self); + m_malloc_fail(self->buffer_len); + } + memset(self->buffer[0], 0, self->buffer_len); + + self->buffer[1] = m_malloc_maybe(self->buffer_len); + if (self->buffer[1] == NULL) { + common_hal_audiodelays_multi_tap_delay_deinit(self); + m_malloc_fail(self->buffer_len); + } + memset(self->buffer[1], 0, self->buffer_len); + + self->last_buf_idx = 1; // Which buffer to use first, toggle between 0 and 1 + + // Initialize other values most effects will need. + self->sample = NULL; // The current playing sample + self->sample_remaining_buffer = NULL; // Pointer to the start of the sample buffer we have not played + self->sample_buffer_length = 0; // How many samples do we have left to play (these may be 16 bit!) + self->loop = false; // When the sample is done do we loop to the start again or stop (e.g. in a wav file) + self->more_data = false; // Is there still more data to read from the sample or did we finish + + // The below section sets up the multi-tap delay effect's starting values. For a different effect this section will change + + // If we did not receive a BlockInput we need to create a default float value + if (decay == MP_OBJ_NULL) { + decay = mp_obj_new_float(MICROPY_FLOAT_CONST(0.7)); + } + synthio_block_assign_slot(decay, &self->decay, MP_QSTR_decay); + + if (mix == MP_OBJ_NULL) { + mix = mp_obj_new_float(MICROPY_FLOAT_CONST(0.25)); + } + synthio_block_assign_slot(mix, &self->mix, MP_QSTR_mix); + + // Allocate the delay buffer for the max possible delay, delay is always 16-bit + self->max_delay_ms = max_delay_ms; + self->max_delay_buffer_len = (uint32_t)(self->base.sample_rate / MICROPY_FLOAT_CONST(1000.0) * max_delay_ms) * (self->base.channel_count * sizeof(uint16_t)); // bytes + self->delay_buffer = m_malloc_maybe(self->max_delay_buffer_len); + if (self->delay_buffer == NULL) { + common_hal_audiodelays_multi_tap_delay_deinit(self); + m_malloc_fail(self->max_delay_buffer_len); + } + memset(self->delay_buffer, 0, self->max_delay_buffer_len); + + // calculate the length of a single sample in milliseconds + self->sample_ms = MICROPY_FLOAT_CONST(1000.0) / self->base.sample_rate; + + // calculate everything needed for the current delay + common_hal_audiodelays_multi_tap_delay_set_delay_ms(self, delay_ms); + self->delay_buffer_pos = 0; + self->delay_buffer_right_pos = 0; + + // Initialize our tap values + self->tap_positions = NULL; + self->tap_levels = NULL; + self->tap_offsets = NULL; + self->tap_len = 0; + common_hal_audiodelays_multi_tap_delay_set_taps(self, taps); +} + +void common_hal_audiodelays_multi_tap_delay_deinit(audiodelays_multi_tap_delay_obj_t *self) { + audiosample_mark_deinit(&self->base); + self->delay_buffer = NULL; + self->buffer[0] = NULL; + self->buffer[1] = NULL; + + self->tap_positions = NULL; + self->tap_levels = NULL; + self->tap_offsets = NULL; +} + +mp_float_t common_hal_audiodelays_multi_tap_delay_get_delay_ms(audiodelays_multi_tap_delay_obj_t *self) { + return self->delay_ms; +} + +void common_hal_audiodelays_multi_tap_delay_set_delay_ms(audiodelays_multi_tap_delay_obj_t *self, mp_obj_t delay_ms) { + self->delay_ms = mp_obj_get_float(delay_ms); + + // Require that delay is at least 1 sample long + self->delay_ms = MAX(self->delay_ms, self->sample_ms); + + // Calculate the current delay buffer length in bytes + self->delay_buffer_len = (uint32_t)(self->base.sample_rate / MICROPY_FLOAT_CONST(1000.0) * self->delay_ms) * (self->base.channel_count * sizeof(uint16_t)); + + // Limit to valid range + if (self->delay_buffer_len > self->max_delay_buffer_len) { + self->delay_buffer_len = self->max_delay_buffer_len; + } else if (self->delay_buffer_len < self->buffer_len) { + // If the delay buffer is smaller than our audio buffer, weird things happen + self->delay_buffer_len = self->buffer_len; + } + + // Clear the now unused part of the buffer or some weird artifacts appear + memset(self->delay_buffer + self->delay_buffer_len, 0, self->max_delay_buffer_len - self->delay_buffer_len); + + // Update tap offsets if we have any + recalculate_tap_offsets(self); +} + +mp_obj_t common_hal_audiodelays_multi_tap_delay_get_decay(audiodelays_multi_tap_delay_obj_t *self) { + return self->decay.obj; +} + +void common_hal_audiodelays_multi_tap_delay_set_decay(audiodelays_multi_tap_delay_obj_t *self, mp_obj_t decay) { + synthio_block_assign_slot(decay, &self->decay, MP_QSTR_decay); +} + +mp_obj_t common_hal_audiodelays_multi_tap_delay_get_mix(audiodelays_multi_tap_delay_obj_t *self) { + return self->mix.obj; +} + +void common_hal_audiodelays_multi_tap_delay_set_mix(audiodelays_multi_tap_delay_obj_t *self, mp_obj_t mix) { + synthio_block_assign_slot(mix, &self->mix, MP_QSTR_mix); +} + +mp_obj_t common_hal_audiodelays_multi_tap_delay_get_taps(audiodelays_multi_tap_delay_obj_t *self) { + if (!self->tap_len) { + return mp_const_none; + } else { + mp_obj_tuple_t *taps = mp_obj_new_tuple(self->tap_len, NULL); + for (size_t i = 0; i < self->tap_len; i++) { + mp_obj_tuple_t *pair = mp_obj_new_tuple(2, NULL); + pair->items[0] = mp_obj_new_float(self->tap_positions[i]); + pair->items[1] = mp_obj_new_float(self->tap_levels[i]); + taps->items[i] = pair; + } + return taps; + } +} + +void validate_tap_value(mp_obj_t item, qstr arg_name) { + if (mp_obj_is_small_int(item)) { + mp_arg_validate_int_range(mp_obj_get_int(item), 0, 1, arg_name); + } else { + mp_arg_validate_obj_float_range(item, 0, 1, arg_name); + } +} + +mp_float_t get_tap_value(mp_obj_t item) { + mp_float_t value; + if (mp_obj_is_small_int(item)) { + value = (mp_float_t)mp_obj_get_int(item); + } else { + value = mp_obj_float_get(item); + } + return value; +} + +void common_hal_audiodelays_multi_tap_delay_set_taps(audiodelays_multi_tap_delay_obj_t *self, mp_obj_t taps_in) { + if (taps_in != mp_const_none && !MP_OBJ_TYPE_HAS_SLOT(mp_obj_get_type(taps_in), iter)) { + mp_raise_TypeError_varg( + MP_ERROR_TEXT("%q must be of type %q, not %q"), + MP_QSTR_taps, MP_QSTR_iterable, mp_obj_get_type(taps_in)->name); + } + + size_t len, i; + mp_obj_t *items; + + if (taps_in == mp_const_none) { + len = 0; + items = NULL; + } else { + // convert object to tuple if it wasn't before + taps_in = MP_OBJ_TYPE_GET_SLOT(&mp_type_tuple, make_new)( + &mp_type_tuple, 1, 0, &taps_in); + + mp_obj_tuple_get(taps_in, &len, &items); + mp_arg_validate_length_min(len, 1, MP_QSTR_items); + + for (i = 0; i < len; i++) { + mp_obj_t item = items[i]; + if (mp_obj_is_tuple_compatible(item)) { + size_t len1; + mp_obj_t *items1; + mp_obj_tuple_get(item, &len1, &items1); + mp_arg_validate_length(len1, 2, MP_QSTR_items); + + for (size_t j = 0; j < len1; j++) { + validate_tap_value(items1[j], j ? MP_QSTR_level : MP_QSTR_position); + } + } else if (mp_obj_is_float(item) || mp_obj_is_small_int(item)) { + validate_tap_value(item, MP_QSTR_position); + } else { + mp_raise_TypeError_varg( + MP_ERROR_TEXT("%q in %q must be of type %q or %q, not %q"), + MP_QSTR_object, + MP_QSTR_taps, + MP_QSTR_iterable, + MP_QSTR_float, + mp_obj_get_type(item)->name); + } + + } + } + + self->tap_positions = m_renew(mp_float_t, + self->tap_positions, + self->tap_len, + len); + self->tap_levels = m_renew(mp_float_t, + self->tap_levels, + self->tap_len, + len); + self->tap_offsets = m_renew(uint32_t, + self->tap_offsets, + self->tap_len, + len); + self->tap_len = len; + + for (i = 0; i < len; i++) { + mp_obj_t item = items[i]; + if (mp_obj_is_tuple_compatible(item)) { + size_t len1; + mp_obj_t *items1; + mp_obj_tuple_get(item, &len1, &items1); + + self->tap_positions[i] = get_tap_value(items1[0]); + self->tap_levels[i] = get_tap_value(items1[1]); + } else { + self->tap_positions[i] = get_tap_value(item); + self->tap_levels[i] = MICROPY_FLOAT_CONST(1.0); + } + } + + recalculate_tap_offsets(self); +} + +void recalculate_tap_offsets(audiodelays_multi_tap_delay_obj_t *self) { + if (!self->tap_len) { + return; + } + + uint32_t delay_buffer_len = self->delay_buffer_len / self->base.channel_count / sizeof(uint16_t); + for (size_t i = 0; i < self->tap_len; i++) { + self->tap_offsets[i] = (uint32_t)(delay_buffer_len * self->tap_positions[i]); + } +} + +void audiodelays_multi_tap_delay_reset_buffer(audiodelays_multi_tap_delay_obj_t *self, + bool single_channel_output, + uint8_t channel) { + + memset(self->buffer[0], 0, self->buffer_len); + memset(self->buffer[1], 0, self->buffer_len); + memset(self->delay_buffer, 0, self->max_delay_buffer_len); +} + +bool common_hal_audiodelays_multi_tap_delay_get_playing(audiodelays_multi_tap_delay_obj_t *self) { + return self->sample != NULL; +} + +void common_hal_audiodelays_multi_tap_delay_play(audiodelays_multi_tap_delay_obj_t *self, mp_obj_t sample, bool loop) { + audiosample_must_match(&self->base, sample); + + self->sample = sample; + self->loop = loop; + + audiosample_reset_buffer(self->sample, false, 0); + audioio_get_buffer_result_t result = audiosample_get_buffer(self->sample, false, 0, (uint8_t **)&self->sample_remaining_buffer, &self->sample_buffer_length); + + // Track remaining sample length in terms of bytes per sample + self->sample_buffer_length /= (self->base.bits_per_sample / 8); + // Store if we have more data in the sample to retrieve + self->more_data = result == GET_BUFFER_MORE_DATA; + + return; +} + +void common_hal_audiodelays_multi_tap_delay_stop(audiodelays_multi_tap_delay_obj_t *self) { + // When the sample is set to stop playing do any cleanup here + // For delay we clear the sample but the delay continues until the object reading our effect stops + self->sample = NULL; + return; +} + +audioio_get_buffer_result_t audiodelays_multi_tap_delay_get_buffer(audiodelays_multi_tap_delay_obj_t *self, bool single_channel_output, uint8_t channel, + uint8_t **buffer, uint32_t *buffer_length) { + + if (!single_channel_output) { + channel = 0; + } + + // Switch our buffers to the other buffer + self->last_buf_idx = !self->last_buf_idx; + + // If we are using 16 bit samples we need a 16 bit pointer, 8 bit needs an 8 bit pointer + int16_t *word_buffer = (int16_t *)self->buffer[self->last_buf_idx]; + int8_t *hword_buffer = self->buffer[self->last_buf_idx]; + uint32_t length = self->buffer_len / (self->base.bits_per_sample / 8); + + // The delay buffer is always stored as a 16-bit value internally + int16_t *delay_buffer = (int16_t *)self->delay_buffer; + uint32_t delay_buffer_len = self->delay_buffer_len / self->base.channel_count / sizeof(uint16_t); + + uint32_t delay_buffer_pos = self->delay_buffer_pos; + if (single_channel_output && channel == 1) { + delay_buffer_pos = self->delay_buffer_right_pos; + } + + int32_t mix_down_scale = SYNTHIO_MIX_DOWN_SCALE(self->tap_len); + + // Loop over the entire length of our buffer to fill it, this may require several calls to get data from the sample + while (length != 0) { + // Check if there is no more sample to play, we will either load more data, reset the sample if loop is on or clear the sample + if (self->sample_buffer_length == 0) { + if (!self->more_data) { // The sample has indicated it has no more data to play + if (self->loop && self->sample) { // If we are supposed to loop reset the sample to the start + audiosample_reset_buffer(self->sample, false, 0); + } else { // If we were not supposed to loop the sample, stop playing it but we still need to play the delay + self->sample = NULL; + } + } + if (self->sample) { + // Load another sample buffer to play + audioio_get_buffer_result_t result = audiosample_get_buffer(self->sample, false, 0, (uint8_t **)&self->sample_remaining_buffer, &self->sample_buffer_length); + // Track length in terms of words. + self->sample_buffer_length /= (self->base.bits_per_sample / 8); + self->more_data = result == GET_BUFFER_MORE_DATA; + } + } + + // Determine how many bytes we can process to our buffer, the less of the sample we have left and our buffer remaining + uint32_t n; + if (self->sample == NULL) { + n = MIN(length, SYNTHIO_MAX_DUR * self->base.channel_count); + } else { + n = MIN(MIN(self->sample_buffer_length, length), SYNTHIO_MAX_DUR * self->base.channel_count); + } + + // get the effect values we need from the BlockInput. These may change at run time so you need to do bounds checking if required + shared_bindings_synthio_lfo_tick(self->base.sample_rate, n / self->base.channel_count); + mp_float_t mix = synthio_block_slot_get_limited(&self->mix, MICROPY_FLOAT_CONST(0.0), MICROPY_FLOAT_CONST(1.0)) * MICROPY_FLOAT_CONST(2.0); + mp_float_t decay = synthio_block_slot_get_limited(&self->decay, MICROPY_FLOAT_CONST(0.0), MICROPY_FLOAT_CONST(1.0)); + + int16_t *sample_src = NULL; + int8_t *sample_hsrc = NULL; + if (self->sample != NULL) { + // we have a sample to play and delay + sample_src = (int16_t *)self->sample_remaining_buffer; // for 16-bit samples + sample_hsrc = (int8_t *)self->sample_remaining_buffer; // for 8-bit samples + } + + for (uint32_t i = 0; i < n; i++) { + uint32_t delay_buffer_offset = delay_buffer_len * ((single_channel_output && channel == 1) || (!single_channel_output && (i % self->base.channel_count) == 1)); + + int32_t sample_word = 0; + if (self->sample != NULL) { + if (MP_LIKELY(self->base.bits_per_sample == 16)) { + sample_word = sample_src[i]; + } else { + if (self->base.samples_signed) { + sample_word = sample_hsrc[i]; + } else { + // Be careful here changing from an 8 bit unsigned to signed into a 32-bit signed + sample_word = (int8_t)(((uint8_t)sample_hsrc[i]) ^ 0x80); + } + } + } + + // Pull words from delay buffer at tap positions, apply level and mix down + int32_t word = 0; + int32_t delay_word; + if (self->tap_len) { + size_t tap_pos; + for (size_t j = 0; j < self->tap_len; j++) { + tap_pos = (delay_buffer_pos + delay_buffer_len - self->tap_offsets[j]) % delay_buffer_len; + delay_word = delay_buffer[tap_pos + delay_buffer_offset]; + word += (int32_t)(delay_word * self->tap_levels[j]); + } + + if (self->tap_len > 1) { + word = synthio_mix_down_sample(word, mix_down_scale); + } + } + + // Update delay buffer with sample and decay + delay_word = delay_buffer[delay_buffer_pos + delay_buffer_offset]; + + // If no taps are provided, use as standard delay + if (!self->tap_len) { + word = delay_word; + } + + // Apply decay and add sample + delay_word = (int32_t)(delay_word * decay) + sample_word; + + if (MP_LIKELY(self->base.bits_per_sample == 16)) { + delay_word = synthio_mix_down_sample(delay_word, SYNTHIO_MIX_DOWN_SCALE(2)); + delay_buffer[delay_buffer_pos + delay_buffer_offset] = (int16_t)delay_word; + } else { + // Do not have mix_down for 8 bit so just hard cap samples into 1 byte + delay_word = MIN(MAX(delay_word, -128), 127); + delay_buffer[delay_buffer_pos + delay_buffer_offset] = (int8_t)delay_word; + } + + // Mix sample with tap output + word = (int32_t)((sample_word * MIN(MICROPY_FLOAT_CONST(2.0) - mix, MICROPY_FLOAT_CONST(1.0))) + + (word * MIN(mix, MICROPY_FLOAT_CONST(1.0)))); + word = synthio_mix_down_sample(word, SYNTHIO_MIX_DOWN_SCALE(2)); + + if (MP_LIKELY(self->base.bits_per_sample == 16)) { + word_buffer[i] = (int16_t)word; + if (!self->base.samples_signed) { + word_buffer[i] ^= 0x8000; + } + } else { + int8_t mixed = (int16_t)word; + if (self->base.samples_signed) { + hword_buffer[i] = mixed; + } else { + hword_buffer[i] = (uint8_t)mixed ^ 0x80; + } + } + + if ((self->base.channel_count == 1 || single_channel_output || (!single_channel_output && (i % self->base.channel_count) == 1)) + && ++delay_buffer_pos >= delay_buffer_len) { + delay_buffer_pos = 0; + } + } + + // Update the remaining length and the buffer positions based on how much we wrote into our buffer + length -= n; + word_buffer += n; + hword_buffer += n; + if (self->sample != NULL) { + self->sample_remaining_buffer += (n * (self->base.bits_per_sample / 8)); + self->sample_buffer_length -= n; + } + } + + if (single_channel_output && channel == 1) { + self->delay_buffer_right_pos = delay_buffer_pos; + } else { + self->delay_buffer_pos = delay_buffer_pos; + } + + // Finally pass our buffer and length to the calling audio function + *buffer = (uint8_t *)self->buffer[self->last_buf_idx]; + *buffer_length = self->buffer_len; + + // MultiTapDelay always returns more data but some effects may return GET_BUFFER_DONE or GET_BUFFER_ERROR (see audiocore/__init__.h) + return GET_BUFFER_MORE_DATA; +} diff --git a/shared-module/audiodelays/MultiTapDelay.h b/shared-module/audiodelays/MultiTapDelay.h new file mode 100644 index 0000000000..ebe310b05f --- /dev/null +++ b/shared-module/audiodelays/MultiTapDelay.h @@ -0,0 +1,60 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Cooper Dalrymple +// +// SPDX-License-Identifier: MIT +#pragma once + +#include "py/obj.h" + +#include "shared-module/audiocore/__init__.h" +#include "shared-module/synthio/__init__.h" +#include "shared-module/synthio/block.h" + +extern const mp_obj_type_t audiodelays_multi_tap_delay_type; + +typedef struct { + audiosample_base_t base; + uint32_t max_delay_ms; + mp_float_t delay_ms; + mp_float_t sample_ms; + synthio_block_slot_t decay; + synthio_block_slot_t mix; + + mp_float_t *tap_positions; + mp_float_t *tap_levels; + uint32_t *tap_offsets; + size_t tap_len; + + int8_t *buffer[2]; + uint8_t last_buf_idx; + uint32_t buffer_len; // max buffer in bytes + + uint8_t *sample_remaining_buffer; + uint32_t sample_buffer_length; + + bool loop; + bool more_data; + + int8_t *delay_buffer; + uint32_t delay_buffer_len; // bytes + uint32_t max_delay_buffer_len; // bytes + uint32_t delay_buffer_pos; + uint32_t delay_buffer_right_pos; + + mp_obj_t sample; +} audiodelays_multi_tap_delay_obj_t; + +void validate_tap_value(mp_obj_t item, qstr arg_name); +mp_float_t get_tap_value(mp_obj_t item); +void recalculate_tap_offsets(audiodelays_multi_tap_delay_obj_t *self); + +void audiodelays_multi_tap_delay_reset_buffer(audiodelays_multi_tap_delay_obj_t *self, + bool single_channel_output, + uint8_t channel); + +audioio_get_buffer_result_t audiodelays_multi_tap_delay_get_buffer(audiodelays_multi_tap_delay_obj_t *self, + bool single_channel_output, + uint8_t channel, + uint8_t **buffer, + uint32_t *buffer_length); // length in bytes diff --git a/shared-module/bitmapfilter/__init__.c b/shared-module/bitmapfilter/__init__.c index cdce674628..2a54ab7443 100644 --- a/shared-module/bitmapfilter/__init__.c +++ b/shared-module/bitmapfilter/__init__.c @@ -21,7 +21,7 @@ #include #define port_free free #define port_malloc(sz, hint) (malloc(sz)) -#define port_realloc realloc +#define port_realloc(ptr, size, dma_capable) realloc(ptr, size) #else #include "supervisor/port_heap.h" #endif @@ -48,7 +48,7 @@ static void *scratchpad_alloc(size_t sz) { } else { if (scratchpad) { if (sz > scratchpad_size) { - void *tmp = port_realloc(scratchpad, sz); + void *tmp = port_realloc(scratchpad, sz, false); if (!tmp) { port_free(scratchpad); scratchpad = NULL; diff --git a/supervisor/port.h b/supervisor/port.h index 29c071515c..cc49538218 100644 --- a/supervisor/port.h +++ b/supervisor/port.h @@ -95,12 +95,6 @@ void port_wake_main_task_from_isr(void); // CircuitPython task when others are done. void port_yield(void); -// Some ports need special handling just after completing boot.py execution. -// This function is called once while boot.py's VM is still valid, and -// then a second time after the VM is finalized. -// A default weak implementation is provided that does nothing. -void port_post_boot_py(bool heap_valid); - // Some ports want to add information to boot_out.txt. // A default weak implementation is provided that does nothing. void port_boot_info(void); @@ -108,3 +102,8 @@ void port_boot_info(void); // Some ports want to mark additional pointers as gc roots. // A default weak implementation is provided that does nothing. void port_gc_collect(void); + +// Most ports that implement CIRCUITPY_BOOT_BUTTON use a generic version of +// this function to sense the button. Ports that need to can override this +// function to provide their own implementation. +bool port_boot_button_pressed(void); diff --git a/supervisor/port_heap.h b/supervisor/port_heap.h index f035cc2409..07a3c884e2 100644 --- a/supervisor/port_heap.h +++ b/supervisor/port_heap.h @@ -24,6 +24,6 @@ void *port_malloc(size_t size, bool dma_capable); void port_free(void *ptr); -void *port_realloc(void *ptr, size_t size); +void *port_realloc(void *ptr, size_t size, bool dma_capable); size_t port_heap_get_largest_free_size(void); diff --git a/supervisor/shared/bluetooth/bluetooth.c b/supervisor/shared/bluetooth/bluetooth.c index 15e472148f..73c05139ee 100644 --- a/supervisor/shared/bluetooth/bluetooth.c +++ b/supervisor/shared/bluetooth/bluetooth.c @@ -10,16 +10,13 @@ #include "shared-bindings/_bleio/__init__.h" #include "shared-bindings/_bleio/Adapter.h" -#if defined(CIRCUITPY_BOOT_BUTTON) -#include "shared-bindings/digitalio/DigitalInOut.h" -#include "shared-bindings/time/__init__.h" -#endif #include "shared-bindings/microcontroller/Processor.h" #include "shared-bindings/microcontroller/ResetReason.h" #include "shared-module/storage/__init__.h" #include "common-hal/_bleio/__init__.h" +#include "supervisor/port.h" #include "supervisor/shared/serial.h" #include "supervisor/shared/status_leds.h" #include "supervisor/shared/tick.h" @@ -238,18 +235,10 @@ void supervisor_bluetooth_init(void) { new_status_color(BLACK); } #endif - // Init the boot button every time in case it is used for LEDs. - #ifdef CIRCUITPY_BOOT_BUTTON - digitalio_digitalinout_obj_t boot_button; - common_hal_digitalio_digitalinout_construct(&boot_button, CIRCUITPY_BOOT_BUTTON); - common_hal_digitalio_digitalinout_switch_to_input(&boot_button, PULL_UP); - common_hal_time_delay_ms(1); - bool button_pressed = !common_hal_digitalio_digitalinout_get_value(&boot_button); - common_hal_digitalio_digitalinout_deinit(&boot_button); - if (button_pressed) { + if (port_boot_button_pressed()) { boot_in_discovery_mode = true; + break; } - #endif diff = supervisor_ticks_ms64() - start_ticks; } if (boot_in_discovery_mode) { diff --git a/supervisor/shared/external_flash/common_commands.h b/supervisor/shared/external_flash/common_commands.h index 539f679d76..f2853e6b50 100644 --- a/supervisor/shared/external_flash/common_commands.h +++ b/supervisor/shared/external_flash/common_commands.h @@ -23,3 +23,4 @@ #define CMD_ENABLE_RESET 0x66 #define CMD_RESET 0x99 #define CMD_WAKE 0xab +#define CMD_GLOBAL_BLOCK_PROTECTION_UNLOCK 0x98 diff --git a/supervisor/shared/external_flash/device.h b/supervisor/shared/external_flash/device.h index 869f7b7a24..d35a84d9c3 100644 --- a/supervisor/shared/external_flash/device.h +++ b/supervisor/shared/external_flash/device.h @@ -24,8 +24,12 @@ typedef struct { // status register. uint8_t quad_enable_bit_mask; + // Device has sector-level write protection bool has_sector_protection : 1; + // Device uses global block protection lock instead of status register bits to enable sector writes + bool use_global_block_protection_lock : 1; + // Supports the 0x0b fast read command with 8 dummy cycles. bool supports_fast_read : 1; diff --git a/supervisor/shared/external_flash/devices.h.jinja b/supervisor/shared/external_flash/devices.h.jinja index 3859559d2b..3d24b674fe 100644 --- a/supervisor/shared/external_flash/devices.h.jinja +++ b/supervisor/shared/external_flash/devices.h.jinja @@ -17,6 +17,7 @@ .max_clock_speed_mhz = {{ device.max_clock_speed_mhz }}, \ .quad_enable_bit_mask = {{ device.quad_enable_bit_mask }}, \ .has_sector_protection = {{ device.has_sector_protection | lower() }}, \ + .use_global_block_protection_lock = {{ device.use_global_block_protection_lock | lower() }}, \ .supports_fast_read = {{ device.supports_fast_read | lower() }}, \ .supports_qspi = {{ device["6b_quad_read"] | lower() }}, \ .supports_qspi_writes = {{ device["32_qspi_write"] | lower() }}, \ diff --git a/supervisor/shared/external_flash/external_flash.c b/supervisor/shared/external_flash/external_flash.c index b029286fa4..abf232c0d1 100644 --- a/supervisor/shared/external_flash/external_flash.c +++ b/supervisor/shared/external_flash/external_flash.c @@ -268,8 +268,12 @@ void supervisor_flash_init(void) { write_enable(); // Turn off sector protection - uint8_t data[1] = {0x00}; - spi_flash_write_command(CMD_WRITE_STATUS_BYTE1, data, 1); + if (flash_device->use_global_block_protection_lock) { + spi_flash_command(CMD_GLOBAL_BLOCK_PROTECTION_UNLOCK); + } else { + uint8_t data[1] = {0x00}; + spi_flash_write_command(CMD_WRITE_STATUS_BYTE1, data, 1); + } } // Turn off writes in case this is a microcontroller only reset. diff --git a/supervisor/shared/port.c b/supervisor/shared/port.c index 72bb45b8a2..3b9f0c8718 100644 --- a/supervisor/shared/port.c +++ b/supervisor/shared/port.c @@ -12,6 +12,11 @@ #include "lib/tlsf/tlsf.h" +#ifdef CIRCUITPY_BOOT_BUTTON +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/time/__init__.h" +#endif + static tlsf_t heap; MP_WEAK void port_wake_main_task(void) { @@ -42,7 +47,7 @@ MP_WEAK void port_free(void *ptr) { tlsf_free(heap, ptr); } -MP_WEAK void *port_realloc(void *ptr, size_t size) { +MP_WEAK void *port_realloc(void *ptr, size_t size, bool dma_capable) { return tlsf_realloc(heap, ptr, size); } @@ -60,3 +65,18 @@ MP_WEAK size_t port_heap_get_largest_free_size(void) { // IDF does this. Not sure why. return tlsf_fit_size(heap, max_size); } + +MP_WEAK bool port_boot_button_pressed(void) { + #if defined(CIRCUITPY_BOOT_BUTTON) + // Init/deinit the boot button every time in case it is used for LEDs. + digitalio_digitalinout_obj_t boot_button; + common_hal_digitalio_digitalinout_construct(&boot_button, CIRCUITPY_BOOT_BUTTON); + common_hal_digitalio_digitalinout_switch_to_input(&boot_button, PULL_UP); + common_hal_time_delay_ms(1); + bool button_pressed = !common_hal_digitalio_digitalinout_get_value(&boot_button); + common_hal_digitalio_digitalinout_deinit(&boot_button); + return button_pressed; + #else + return false; + #endif +} diff --git a/supervisor/shared/safe_mode.c b/supervisor/shared/safe_mode.c index 85c49451bc..5f24618f7f 100644 --- a/supervisor/shared/safe_mode.c +++ b/supervisor/shared/safe_mode.c @@ -8,10 +8,6 @@ #include "mphalport.h" -#if defined(CIRCUITPY_BOOT_BUTTON) -#include "shared-bindings/digitalio/DigitalInOut.h" -#include "shared-bindings/time/__init__.h" -#endif #include "shared-bindings/microcontroller/Processor.h" #include "shared-bindings/microcontroller/ResetReason.h" @@ -78,19 +74,10 @@ safe_mode_t wait_for_safe_mode_reset(void) { new_status_color(BLACK); } #endif - // Init the boot button every time in case it is used for LEDs. - #ifdef CIRCUITPY_BOOT_BUTTON - digitalio_digitalinout_obj_t boot_button; - common_hal_digitalio_digitalinout_construct(&boot_button, CIRCUITPY_BOOT_BUTTON); - common_hal_digitalio_digitalinout_switch_to_input(&boot_button, PULL_UP); - common_hal_time_delay_ms(1); - bool button_pressed = !common_hal_digitalio_digitalinout_get_value(&boot_button); - common_hal_digitalio_digitalinout_deinit(&boot_button); - if (button_pressed) { + if (port_boot_button_pressed()) { boot_in_safe_mode = true; break; } - #endif diff = supervisor_ticks_ms64() - start_ticks; } #if CIRCUITPY_STATUS_LED @@ -142,7 +129,7 @@ void print_safe_mode_message(safe_mode_t reason) { case SAFE_MODE_USER: #if defined(BOARD_USER_SAFE_MODE_ACTION) message = BOARD_USER_SAFE_MODE_ACTION; - #elif defined(CIRCUITPY_BOOT_BUTTON) + #elif defined(CIRCUITPY_BOOT_BUTTON) || CIRCUITPY_BOOT_BUTTON_NO_GPIO message = MP_ERROR_TEXT("You pressed the BOOT button at start up"); #else message = MP_ERROR_TEXT("You pressed the reset button during boot."); diff --git a/supervisor/stub/misc.c b/supervisor/stub/misc.c deleted file mode 100644 index c0a2a87f9f..0000000000 --- a/supervisor/stub/misc.c +++ /dev/null @@ -1,13 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2022 Jeff Epler for Adafruit Industries -// -// SPDX-License-Identifier: MIT -#include "stdbool.h" - -#include "supervisor/port.h" -#include "py/mpconfig.h" - - -MP_WEAK void port_post_boot_py(bool heap_valid) { -} diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index cb71374938..0ecc6ea3ac 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -19,7 +19,6 @@ SRC_SUPERVISOR = \ supervisor/shared/traceback.c \ supervisor/shared/translate/translate.c \ supervisor/shared/workflow.c \ - supervisor/stub/misc.c \ # For tlsf CFLAGS += -D_DEBUG=0