diff --git a/locale/ID.po b/locale/ID.po index bd12a5c943..2b8f5a91cf 100644 --- a/locale/ID.po +++ b/locale/ID.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-07-25 22:48-0700\n" +"POT-Creation-Date: 2019-07-31 16:30-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -255,6 +255,7 @@ msgstr "Semua timer untuk pin ini sedang digunakan" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c #: ports/atmel-samd/common-hal/pulseio/PulseOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #: ports/nrf/common-hal/pulseio/PulseOut.c shared-bindings/pulseio/PWMOut.c #: shared-module/_pew/PewPew.c msgid "All timers in use" @@ -329,6 +330,11 @@ msgstr "" msgid "Buffer incorrect size. Should be %d bytes." msgstr "" +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c +#, c-format +msgid "Buffer length %d too big. It must be less than %d" +msgstr "" + #: shared-bindings/bitbangio/I2C.c shared-bindings/busio/I2C.c msgid "Buffer must be at least length 1" msgstr "" @@ -951,6 +957,7 @@ msgid "Not connected" msgstr "Tidak dapat menyambungkan ke AP" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c msgid "Not playing" msgstr "" @@ -1080,6 +1087,7 @@ msgid "Sample rate must be positive" msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #, c-format msgid "Sample rate too high. It must be less than %d" msgstr "Nilai sampel terlalu tinggi. Nilai harus kurang dari %d" diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 1b135822fd..b872fa74d1 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-07-25 22:48-0700\n" +"POT-Creation-Date: 2019-07-31 16:30-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -253,6 +253,7 @@ msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c #: ports/atmel-samd/common-hal/pulseio/PulseOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #: ports/nrf/common-hal/pulseio/PulseOut.c shared-bindings/pulseio/PWMOut.c #: shared-module/_pew/PewPew.c msgid "All timers in use" @@ -325,6 +326,11 @@ msgstr "" msgid "Buffer incorrect size. Should be %d bytes." msgstr "" +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c +#, c-format +msgid "Buffer length %d too big. It must be less than %d" +msgstr "" + #: shared-bindings/bitbangio/I2C.c shared-bindings/busio/I2C.c msgid "Buffer must be at least length 1" msgstr "" @@ -935,6 +941,7 @@ msgid "Not connected" msgstr "" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c msgid "Not playing" msgstr "" @@ -1060,6 +1067,7 @@ msgid "Sample rate must be positive" msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #, c-format msgid "Sample rate too high. It must be less than %d" msgstr "" diff --git a/locale/de_DE.po b/locale/de_DE.po index 7ad69e1ddf..9d1b5fec72 100644 --- a/locale/de_DE.po +++ b/locale/de_DE.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-07-25 22:48-0700\n" +"POT-Creation-Date: 2019-07-31 16:30-0500\n" "PO-Revision-Date: 2018-07-27 11:55-0700\n" "Last-Translator: Pascal Deneaux\n" "Language-Team: Sebastian Plamauer, Pascal Deneaux\n" @@ -255,6 +255,7 @@ msgstr "Alle timer für diesen Pin werden bereits benutzt" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c #: ports/atmel-samd/common-hal/pulseio/PulseOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #: ports/nrf/common-hal/pulseio/PulseOut.c shared-bindings/pulseio/PWMOut.c #: shared-module/_pew/PewPew.c msgid "All timers in use" @@ -329,6 +330,11 @@ msgstr "Die Helligkeit ist nicht einstellbar" msgid "Buffer incorrect size. Should be %d bytes." msgstr "Der Puffergröße ist inkorrekt. Sie sollte %d bytes haben." +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c +#, c-format +msgid "Buffer length %d too big. It must be less than %d" +msgstr "" + #: shared-bindings/bitbangio/I2C.c shared-bindings/busio/I2C.c msgid "Buffer must be at least length 1" msgstr "Der Puffer muss eine Mindestenslänge von 1 haben" @@ -948,6 +954,7 @@ msgid "Not connected" msgstr "Nicht verbunden" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c msgid "Not playing" msgstr "Spielt nicht" @@ -1077,6 +1084,7 @@ msgid "Sample rate must be positive" msgstr "Abtastrate muss positiv sein" #: ports/atmel-samd/common-hal/audioio/AudioOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #, c-format msgid "Sample rate too high. It must be less than %d" msgstr "Abtastrate zu hoch. Wert muss unter %d liegen" diff --git a/locale/en_US.po b/locale/en_US.po index 49699283c6..e98c863fd0 100644 --- a/locale/en_US.po +++ b/locale/en_US.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-07-25 22:48-0700\n" +"POT-Creation-Date: 2019-07-31 16:30-0500\n" "PO-Revision-Date: 2018-07-27 11:55-0700\n" "Last-Translator: \n" "Language-Team: \n" @@ -253,6 +253,7 @@ msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c #: ports/atmel-samd/common-hal/pulseio/PulseOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #: ports/nrf/common-hal/pulseio/PulseOut.c shared-bindings/pulseio/PWMOut.c #: shared-module/_pew/PewPew.c msgid "All timers in use" @@ -325,6 +326,11 @@ msgstr "" msgid "Buffer incorrect size. Should be %d bytes." msgstr "" +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c +#, c-format +msgid "Buffer length %d too big. It must be less than %d" +msgstr "" + #: shared-bindings/bitbangio/I2C.c shared-bindings/busio/I2C.c msgid "Buffer must be at least length 1" msgstr "" @@ -935,6 +941,7 @@ msgid "Not connected" msgstr "" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c msgid "Not playing" msgstr "" @@ -1060,6 +1067,7 @@ msgid "Sample rate must be positive" msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #, c-format msgid "Sample rate too high. It must be less than %d" msgstr "" diff --git a/locale/en_x_pirate.po b/locale/en_x_pirate.po index 885abe6818..bf603384fd 100644 --- a/locale/en_x_pirate.po +++ b/locale/en_x_pirate.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-07-25 22:48-0700\n" +"POT-Creation-Date: 2019-07-31 16:30-0500\n" "PO-Revision-Date: 2018-07-27 11:55-0700\n" "Last-Translator: \n" "Language-Team: @sommersoft, @MrCertainly\n" @@ -255,6 +255,7 @@ msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c #: ports/atmel-samd/common-hal/pulseio/PulseOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #: ports/nrf/common-hal/pulseio/PulseOut.c shared-bindings/pulseio/PWMOut.c #: shared-module/_pew/PewPew.c msgid "All timers in use" @@ -329,6 +330,11 @@ msgstr "" msgid "Buffer incorrect size. Should be %d bytes." msgstr "" +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c +#, c-format +msgid "Buffer length %d too big. It must be less than %d" +msgstr "" + #: shared-bindings/bitbangio/I2C.c shared-bindings/busio/I2C.c msgid "Buffer must be at least length 1" msgstr "" @@ -939,6 +945,7 @@ msgid "Not connected" msgstr "" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c msgid "Not playing" msgstr "" @@ -1064,6 +1071,7 @@ msgid "Sample rate must be positive" msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #, c-format msgid "Sample rate too high. It must be less than %d" msgstr "" diff --git a/locale/es.po b/locale/es.po index 772570a31e..0e24fc835f 100644 --- a/locale/es.po +++ b/locale/es.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-07-25 22:48-0700\n" +"POT-Creation-Date: 2019-07-31 16:30-0500\n" "PO-Revision-Date: 2018-08-24 22:56-0500\n" "Last-Translator: \n" "Language-Team: \n" @@ -257,6 +257,7 @@ msgstr "Todos los timers para este pin están siendo utilizados" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c #: ports/atmel-samd/common-hal/pulseio/PulseOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #: ports/nrf/common-hal/pulseio/PulseOut.c shared-bindings/pulseio/PWMOut.c #: shared-module/_pew/PewPew.c msgid "All timers in use" @@ -333,6 +334,11 @@ msgstr "El brillo no se puede ajustar" msgid "Buffer incorrect size. Should be %d bytes." msgstr "Tamaño de buffer incorrecto. Debe ser de %d bytes." +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c +#, c-format +msgid "Buffer length %d too big. It must be less than %d" +msgstr "" + #: shared-bindings/bitbangio/I2C.c shared-bindings/busio/I2C.c msgid "Buffer must be at least length 1" msgstr "Buffer debe ser de longitud 1 como minimo" @@ -950,6 +956,7 @@ msgid "Not connected" msgstr "No conectado" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c msgid "Not playing" msgstr "No reproduciendo" @@ -1088,6 +1095,7 @@ msgid "Sample rate must be positive" msgstr "Sample rate debe ser positivo" #: ports/atmel-samd/common-hal/audioio/AudioOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #, c-format msgid "Sample rate too high. It must be less than %d" msgstr "Frecuencia de muestreo demasiado alta. Debe ser menor a %d" diff --git a/locale/fil.po b/locale/fil.po index 8e4dbd9fd0..ac010aee6b 100644 --- a/locale/fil.po +++ b/locale/fil.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-07-25 22:48-0700\n" +"POT-Creation-Date: 2019-07-31 16:30-0500\n" "PO-Revision-Date: 2018-12-20 22:15-0800\n" "Last-Translator: Timothy \n" "Language-Team: fil\n" @@ -257,6 +257,7 @@ msgstr "Lahat ng timers para sa pin na ito ay ginagamit" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c #: ports/atmel-samd/common-hal/pulseio/PulseOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #: ports/nrf/common-hal/pulseio/PulseOut.c shared-bindings/pulseio/PWMOut.c #: shared-module/_pew/PewPew.c msgid "All timers in use" @@ -331,6 +332,11 @@ msgstr "" msgid "Buffer incorrect size. Should be %d bytes." msgstr "Mali ang size ng buffer. Dapat %d bytes." +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c +#, c-format +msgid "Buffer length %d too big. It must be less than %d" +msgstr "" + #: shared-bindings/bitbangio/I2C.c shared-bindings/busio/I2C.c msgid "Buffer must be at least length 1" msgstr "Buffer dapat ay hindi baba sa 1 na haba" @@ -960,6 +966,7 @@ msgid "Not connected" msgstr "Hindi maka connect sa AP" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c msgid "Not playing" msgstr "Hindi playing" @@ -1093,6 +1100,7 @@ msgid "Sample rate must be positive" msgstr "Sample rate ay dapat positibo" #: ports/atmel-samd/common-hal/audioio/AudioOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #, c-format msgid "Sample rate too high. It must be less than %d" msgstr "Sample rate ay masyadong mataas. Ito ay dapat hindi hiigit sa %d" diff --git a/locale/fr.po b/locale/fr.po index 5bb51b622b..1a2bb90b37 100644 --- a/locale/fr.po +++ b/locale/fr.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-07-25 22:48-0700\n" +"POT-Creation-Date: 2019-07-31 16:30-0500\n" "PO-Revision-Date: 2019-04-14 20:05+0100\n" "Last-Translator: Pierrick Couturier \n" "Language-Team: fr\n" @@ -260,6 +260,7 @@ msgstr "Tous les timers pour cette broche sont utilisés" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c #: ports/atmel-samd/common-hal/pulseio/PulseOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #: ports/nrf/common-hal/pulseio/PulseOut.c shared-bindings/pulseio/PWMOut.c #: shared-module/_pew/PewPew.c msgid "All timers in use" @@ -336,6 +337,11 @@ msgstr "Luminosité non-ajustable" msgid "Buffer incorrect size. Should be %d bytes." msgstr "Tampon de taille incorrect. Devrait être de %d octets." +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c +#, c-format +msgid "Buffer length %d too big. It must be less than %d" +msgstr "" + #: shared-bindings/bitbangio/I2C.c shared-bindings/busio/I2C.c msgid "Buffer must be at least length 1" msgstr "Le tampon doit être de longueur au moins 1" @@ -969,6 +975,7 @@ msgid "Not connected" msgstr "Non connecté" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c msgid "Not playing" msgstr "Ne joue pas" @@ -1110,6 +1117,7 @@ msgid "Sample rate must be positive" msgstr "Le taux d'échantillonage doit être positif" #: ports/atmel-samd/common-hal/audioio/AudioOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #, c-format msgid "Sample rate too high. It must be less than %d" msgstr "Taux d'échantillonage trop élevé. Doit être inf. à %d" diff --git a/locale/it_IT.po b/locale/it_IT.po index 2f8cfdc1e7..ccf3958467 100644 --- a/locale/it_IT.po +++ b/locale/it_IT.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-07-25 22:48-0700\n" +"POT-Creation-Date: 2019-07-31 16:30-0500\n" "PO-Revision-Date: 2018-10-02 16:27+0200\n" "Last-Translator: Enrico Paganin \n" "Language-Team: \n" @@ -256,6 +256,7 @@ msgstr "Tutti i timer per questo pin sono in uso" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c #: ports/atmel-samd/common-hal/pulseio/PulseOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #: ports/nrf/common-hal/pulseio/PulseOut.c shared-bindings/pulseio/PWMOut.c #: shared-module/_pew/PewPew.c msgid "All timers in use" @@ -331,6 +332,11 @@ msgstr "Illiminazione non è regolabile" msgid "Buffer incorrect size. Should be %d bytes." msgstr "Buffer di lunghezza non valida. Dovrebbe essere di %d bytes." +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c +#, c-format +msgid "Buffer length %d too big. It must be less than %d" +msgstr "" + #: shared-bindings/bitbangio/I2C.c shared-bindings/busio/I2C.c msgid "Buffer must be at least length 1" msgstr "Il buffer deve essere lungo almeno 1" @@ -959,6 +965,7 @@ msgid "Not connected" msgstr "Impossible connettersi all'AP" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c msgid "Not playing" msgstr "In pausa" @@ -1098,6 +1105,7 @@ msgid "Sample rate must be positive" msgstr "STA deve essere attiva" #: ports/atmel-samd/common-hal/audioio/AudioOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #, c-format msgid "Sample rate too high. It must be less than %d" msgstr "" diff --git a/locale/pl.po b/locale/pl.po index 83e408a3dc..3a8b7c5306 100644 --- a/locale/pl.po +++ b/locale/pl.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-07-25 22:48-0700\n" +"POT-Creation-Date: 2019-07-31 16:30-0500\n" "PO-Revision-Date: 2019-03-19 18:37-0700\n" "Last-Translator: Radomir Dopieralski \n" "Language-Team: pl\n" @@ -254,6 +254,7 @@ msgstr "Wszystkie timery tej nóżki w użyciu" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c #: ports/atmel-samd/common-hal/pulseio/PulseOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #: ports/nrf/common-hal/pulseio/PulseOut.c shared-bindings/pulseio/PWMOut.c #: shared-module/_pew/PewPew.c msgid "All timers in use" @@ -328,6 +329,11 @@ msgstr "Jasność nie jest regulowana" msgid "Buffer incorrect size. Should be %d bytes." msgstr "Zła wielkość bufora. Powinno być %d bajtów." +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c +#, c-format +msgid "Buffer length %d too big. It must be less than %d" +msgstr "" + #: shared-bindings/bitbangio/I2C.c shared-bindings/busio/I2C.c msgid "Buffer must be at least length 1" msgstr "Bufor musi mieć długość 1 lub więcej" @@ -945,6 +951,7 @@ msgid "Not connected" msgstr "Nie podłączono" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c msgid "Not playing" msgstr "Nic nie jest odtwarzane" @@ -1070,6 +1077,7 @@ msgid "Sample rate must be positive" msgstr "Częstotliwość próbkowania musi być dodatnia" #: ports/atmel-samd/common-hal/audioio/AudioOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #, c-format msgid "Sample rate too high. It must be less than %d" msgstr "Zbyt wysoka częstotliwość próbkowania. Musi być mniejsza niż %d" diff --git a/locale/pt_BR.po b/locale/pt_BR.po index d3a8fe50a7..0bc5d06136 100644 --- a/locale/pt_BR.po +++ b/locale/pt_BR.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-07-25 22:48-0700\n" +"POT-Creation-Date: 2019-07-31 16:30-0500\n" "PO-Revision-Date: 2018-10-02 21:14-0000\n" "Last-Translator: \n" "Language-Team: \n" @@ -256,6 +256,7 @@ msgstr "Todos os temporizadores para este pino estão em uso" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c #: ports/atmel-samd/common-hal/pulseio/PulseOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #: ports/nrf/common-hal/pulseio/PulseOut.c shared-bindings/pulseio/PWMOut.c #: shared-module/_pew/PewPew.c msgid "All timers in use" @@ -328,6 +329,11 @@ msgstr "" msgid "Buffer incorrect size. Should be %d bytes." msgstr "Buffer de tamanho incorreto. Deve ser %d bytes." +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c +#, c-format +msgid "Buffer length %d too big. It must be less than %d" +msgstr "" + #: shared-bindings/bitbangio/I2C.c shared-bindings/busio/I2C.c msgid "Buffer must be at least length 1" msgstr "" @@ -951,6 +957,7 @@ msgid "Not connected" msgstr "Não é possível conectar-se ao AP" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c msgid "Not playing" msgstr "" @@ -1080,6 +1087,7 @@ msgid "Sample rate must be positive" msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #, c-format msgid "Sample rate too high. It must be less than %d" msgstr "Taxa de amostragem muito alta. Deve ser menor que %d" diff --git a/locale/zh_Latn_pinyin.po b/locale/zh_Latn_pinyin.po index 055dd54612..74eed5231f 100644 --- a/locale/zh_Latn_pinyin.po +++ b/locale/zh_Latn_pinyin.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: circuitpython-cn\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-07-25 22:48-0700\n" +"POT-Creation-Date: 2019-07-31 16:30-0500\n" "PO-Revision-Date: 2019-04-13 10:10-0700\n" "Last-Translator: hexthat\n" "Language-Team: Chinese Hanyu Pinyin\n" @@ -255,6 +255,7 @@ msgstr "Cǐ yǐn jiǎo de suǒyǒu jìshí qì zhèngzài shǐyòng" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c #: ports/atmel-samd/common-hal/pulseio/PulseOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #: ports/nrf/common-hal/pulseio/PulseOut.c shared-bindings/pulseio/PWMOut.c #: shared-module/_pew/PewPew.c msgid "All timers in use" @@ -329,6 +330,11 @@ msgstr "Liàngdù wúfǎ tiáozhěng" msgid "Buffer incorrect size. Should be %d bytes." msgstr "Huǎnchōng qū dàxiǎo bù zhèngquè. Yīnggāi shì %d zì jié." +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c +#, c-format +msgid "Buffer length %d too big. It must be less than %d" +msgstr "" + #: shared-bindings/bitbangio/I2C.c shared-bindings/busio/I2C.c msgid "Buffer must be at least length 1" msgstr "Huǎnchōng qū bìxū zhìshǎo chángdù 1" @@ -945,6 +951,7 @@ msgid "Not connected" msgstr "Wèi liánjiē" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c msgid "Not playing" msgstr "Wèi bòfàng" @@ -1075,6 +1082,7 @@ msgid "Sample rate must be positive" msgstr "Cǎiyàng lǜ bìxū wèi zhèng shù" #: ports/atmel-samd/common-hal/audioio/AudioOut.c +#: ports/nrf/common-hal/audiopwmio/PWMAudioOut.c #, c-format msgid "Sample rate too high. It must be less than %d" msgstr "Cǎiyàng lǜ tài gāo. Tā bìxū xiǎoyú %d" @@ -1128,8 +1136,9 @@ msgstr "" msgid "" "The `microcontroller` module was used to boot into safe mode. Press reset to " "exit safe mode.\n" -msgstr "“Wēi kòngzhì qì” mókuài yòng yú qǐdòng ānquán móshì. Àn chóng zhì kě " -"tuìchū ānquán móshì.\n" +msgstr "" +"“Wēi kòngzhì qì” mókuài yòng yú qǐdòng ānquán móshì. Àn chóng zhì kě tuìchū " +"ānquán móshì.\n" #: supervisor/shared/safe_mode.c msgid "" diff --git a/ports/nrf/background.c b/ports/nrf/background.c index 9c4f3ab27e..453bc96dfb 100644 --- a/ports/nrf/background.c +++ b/ports/nrf/background.c @@ -33,6 +33,10 @@ #include "shared-module/displayio/__init__.h" #endif +#if CIRCUITPY_AUDIOPWMIO +#include "common-hal/audiopwmio/PWMAudioOut.h" +#endif + static bool running_background_tasks = false; void background_tasks_reset(void) { @@ -47,6 +51,9 @@ void run_background_tasks(void) { running_background_tasks = true; filesystem_background(); usb_background(); +#if CIRCUITPY_AUDIOPWMIO + audiopwmout_background(); +#endif #if CIRCUITPY_DISPLAYIO displayio_refresh_displays(); diff --git a/ports/nrf/common-hal/audiopwmio/PWMAudioOut.c b/ports/nrf/common-hal/audiopwmio/PWMAudioOut.c new file mode 100644 index 0000000000..0321b751ca --- /dev/null +++ b/ports/nrf/common-hal/audiopwmio/PWMAudioOut.c @@ -0,0 +1,316 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +#include "extmod/vfs_fat.h" +#include "py/gc.h" +#include "py/mperrno.h" +#include "py/runtime.h" +#include "common-hal/audiopwmio/PWMAudioOut.h" +#include "common-hal/pulseio/PWMOut.h" +#include "shared-bindings/audiopwmio/PWMAudioOut.h" +#include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "supervisor/shared/translate.h" + +// TODO: This should be the same size as PWMOut.c:pwms[], but there's no trivial way to accomplish that +STATIC audiopwmio_pwmaudioout_obj_t* active_audio[4]; + +#define F_TARGET (62500) +#define F_PWM (16000000) +// return the REFRESH value, store the TOP value in an out-parameter +// Tested for key values (worst relative error = 0.224% = 3.84 cents) +// 8000: top = 250 refresh = 7 [ 8000.0] +// 22050: top = 242 refresh = 2 [22038.5] +// 24000: top = 222 refresh = 2 [24024.0] +// 44100: top = 181 refresh = 1 [44198.8] +// 48000: top = 167 refresh = 1 [47904.1] +STATIC uint32_t calculate_pwm_parameters(uint32_t sample_rate, uint32_t *top_out) { + // the desired frequency is the closest integer multiple of sample_rate not less than F_TARGET + uint32_t desired_frequency = (F_TARGET + sample_rate - 1) / sample_rate * sample_rate; + // The top value is the PWM frequency divided by the desired frequency (round to nearest) + uint32_t top = (F_PWM + desired_frequency/2) / desired_frequency; + // The actual frequency is the PWM frequency divided by the top value (round to nearest) + uint32_t actual_frequency = (F_PWM + top/2) / top; + // The multiplier is the actual frequency divided by the sample rate (round to nearest) + uint32_t multiplier = (actual_frequency + sample_rate/2) / sample_rate; + *top_out = top; + return multiplier - 1; +} + +STATIC void activate_audiopwmout_obj(audiopwmio_pwmaudioout_obj_t *self) { + for(size_t i=0; i < MP_ARRAY_SIZE(active_audio); i++) { + if(!active_audio[i]) { + active_audio[i] = self; + break; + } + } +} +STATIC void deactivate_audiopwmout_obj(audiopwmio_pwmaudioout_obj_t *self) { + for(size_t i=0; i < MP_ARRAY_SIZE(active_audio); i++) { + if(active_audio[i] == self) + active_audio[i] = NULL; + } +} + +void audiopwmout_reset() { + for(size_t i=0; i < MP_ARRAY_SIZE(active_audio); i++) + active_audio[i] = NULL; +} + +STATIC void fill_buffers(audiopwmio_pwmaudioout_obj_t *self, int buf) { + self->pwm->EVENTS_SEQSTARTED[1-buf] = 0; + uint16_t *dev_buffer = self->buffers[buf]; + uint8_t *buffer; + uint32_t buffer_length; + audioio_get_buffer_result_t get_buffer_result = + audiosample_get_buffer(self->sample, false, 0, + &buffer, &buffer_length); + if (get_buffer_result == GET_BUFFER_ERROR) { + common_hal_audiopwmio_pwmaudioout_stop(self); + return; + } + uint32_t num_samples = buffer_length / self->bytes_per_sample / self->spacing; + + if(self->bytes_per_sample == 1) { + uint8_t offset = self->signed_to_unsigned ? 0x80 : 0; + uint16_t scale = self->scale; + for(uint32_t i=0; ispacing; i++) { + uint8_t rawval = (*buffer++ + offset); + uint16_t val = (uint16_t)(((uint32_t)rawval * (uint32_t)scale) >> 8); + *dev_buffer++ = val; + if(self->spacing == 1) + *dev_buffer++ = val; + } + } else { + uint16_t offset = self->signed_to_unsigned ? 0x8000 : 0; + uint16_t scale = self->scale; + uint16_t *buffer16 = (uint16_t*)buffer; + for(uint32_t i=0; ispacing; i++) { + uint16_t rawval = (*buffer16++ + offset); + uint16_t val = (uint16_t)((rawval * (uint32_t)scale) >> 16); + *dev_buffer++ = val; + if(self->spacing == 1) + *dev_buffer++ = val; + } + } + self->pwm->SEQ[buf].PTR = (intptr_t)self->buffers[buf]; + self->pwm->SEQ[buf].CNT = num_samples*2; + + if (self->loop && get_buffer_result == GET_BUFFER_DONE) { + audiosample_reset_buffer(self->sample, false, 0); + } else if(get_buffer_result == GET_BUFFER_DONE) { + self->pwm->SHORTS = NRF_PWM_SHORT_SEQEND0_STOP_MASK | NRF_PWM_SHORT_SEQEND1_STOP_MASK; + self->stopping = true; + } +} + +STATIC void audiopwmout_background_obj(audiopwmio_pwmaudioout_obj_t *self) { + if(!common_hal_audiopwmio_pwmaudioout_get_playing(self)) + return; + if(self->stopping) { + bool stopped = + (self->pwm->EVENTS_SEQEND[0] || !self->pwm->EVENTS_SEQSTARTED[0]) && + (self->pwm->EVENTS_SEQEND[1] || !self->pwm->EVENTS_SEQSTARTED[1]); + if(stopped) + self->pwm->TASKS_STOP = 1; + } else if(!self->paused && !self->single_buffer) { + if(self->pwm->EVENTS_SEQSTARTED[0]) fill_buffers(self, 1); + if(self->pwm->EVENTS_SEQSTARTED[1]) fill_buffers(self, 0); + } +} + +void audiopwmout_background() { + for(size_t i=0; i < MP_ARRAY_SIZE(active_audio); i++) { + if(!active_audio[i]) continue; + audiopwmout_background_obj(active_audio[i]); + } +} + +void common_hal_audiopwmio_pwmaudioout_construct(audiopwmio_pwmaudioout_obj_t* self, + const mcu_pin_obj_t* left_channel, const mcu_pin_obj_t* right_channel, uint16_t quiescent_value) { + assert_pin_free(left_channel); + assert_pin_free(right_channel); + self->pwm = pwmout_allocate(256, PWM_PRESCALER_PRESCALER_DIV_1, true, NULL, NULL); + if(!self->pwm) { + mp_raise_RuntimeError(translate("All timers in use")); + } + + self->pwm->PRESCALER = PWM_PRESCALER_PRESCALER_DIV_1; + // two uint16_t values per sample when Grouped + // n.b. SEQ[#].CNT "counts" are 2 per sample (left and right channels) + self->pwm->DECODER = PWM_DECODER_LOAD_Grouped; + + // we use channels 0 and 2 because these are GROUPED; it lets us save half + // the space for sample data (no additional optimization is possible for + // single channel) + self->pwm->PSEL.OUT[0] = self->left_channel_number = left_channel->number; + claim_pin(left_channel); + + if(right_channel) + { + self->pwm->PSEL.OUT[2] = self->right_channel_number = right_channel->number; + claim_pin(right_channel); + } + + self->quiescent_value = quiescent_value >> 8; + + self->pwm->ENABLE = 1; + // TODO: Ramp from 0 to quiescent value +} + +bool common_hal_audiopwmio_pwmaudioout_deinited(audiopwmio_pwmaudioout_obj_t* self) { + return !self->pwm; +} + +void common_hal_audiopwmio_pwmaudioout_deinit(audiopwmio_pwmaudioout_obj_t* self) { + if (common_hal_audiopwmio_pwmaudioout_deinited(self)) { + return; + } + // TODO: ramp the pwm down from quiescent value to 0 + self->pwm->ENABLE = 0; + + if(self->left_channel_number) + reset_pin_number(self->left_channel_number); + if(self->right_channel_number) + reset_pin_number(self->right_channel_number); + + pwmout_free_channel(self->pwm, 0); + pwmout_free_channel(self->pwm, 2); + + self->pwm = NULL; + + m_free(self->buffers[0]); + self->buffers[0] = NULL; + + m_free(self->buffers[1]); + self->buffers[1] = NULL; +} + +void common_hal_audiopwmio_pwmaudioout_play(audiopwmio_pwmaudioout_obj_t* self, mp_obj_t sample, bool loop) { + if (common_hal_audiopwmio_pwmaudioout_get_playing(self)) { + common_hal_audiopwmio_pwmaudioout_stop(self); + } + self->sample = sample; + self->loop = loop; + + uint32_t sample_rate = audiosample_sample_rate(sample); + uint32_t max_sample_rate = 62500; + if (sample_rate > max_sample_rate) { + mp_raise_ValueError_varg(translate("Sample rate too high. It must be less than %d"), max_sample_rate); + } + self->bytes_per_sample = audiosample_bits_per_sample(sample) / 8; + + uint32_t max_buffer_length; + audiosample_get_buffer_structure(sample, /* single channel */ false, + &self->single_buffer, &self->signed_to_unsigned, &max_buffer_length, + &self->spacing); + if(max_buffer_length > UINT16_MAX) { + mp_raise_ValueError_varg(translate("Buffer length %d too big. It must be less than %d"), max_buffer_length, UINT16_MAX); + } + self->buffer_length = (uint16_t)max_buffer_length; + self->buffers[0] = m_malloc(self->buffer_length * 2 * sizeof(uint16_t), false); + if(!self->single_buffer) + self->buffers[1] = m_malloc(self->buffer_length * 2 * sizeof(uint16_t), false); + + + uint32_t top; + self->pwm->SEQ[0].REFRESH = self->pwm->SEQ[1].REFRESH = calculate_pwm_parameters(sample_rate, &top); + self->scale = top-1; + self->pwm->COUNTERTOP = top; + + self->pwm->LOOP = 1; + audiosample_reset_buffer(self->sample, false, 0); + activate_audiopwmout_obj(self); + fill_buffers(self, 0); + self->pwm->SEQ[1].PTR = self->pwm->SEQ[0].PTR; + self->pwm->SEQ[1].CNT = self->pwm->SEQ[0].CNT; + self->pwm->EVENTS_SEQSTARTED[0] = 0; + self->pwm->EVENTS_SEQSTARTED[1] = 0; + self->pwm->EVENTS_STOPPED = 0; + self->pwm->SHORTS = NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK; + self->pwm->TASKS_SEQSTART[0] = 1; + self->playing = true; + self->stopping = false; + self->paused = false; +} + +void common_hal_audiopwmio_pwmaudioout_stop(audiopwmio_pwmaudioout_obj_t* self) { + deactivate_audiopwmout_obj(self); + self->pwm->TASKS_STOP = 1; + self->stopping = false; + self->paused = false; + + m_free(self->buffers[0]); + self->buffers[0] = NULL; + + m_free(self->buffers[1]); + self->buffers[1] = NULL; +} + +bool common_hal_audiopwmio_pwmaudioout_get_playing(audiopwmio_pwmaudioout_obj_t* self) { + if(!self->paused && self->pwm->EVENTS_STOPPED) { + self->playing = false; + self->pwm->EVENTS_STOPPED = 0; + } + return self->playing; +} + +/* pause/resume present difficulties for the NRF PWM audio module. + * + * A PWM sequence can be stopped in its tracks by sending a TASKS_STOP event, + * but there's no way to pick up the sequence where it was stopped; you could + * start at the start of one of the two sequences, but especially for "single buffer" + * sample, this seems undesirable. + * + * Or, you can stop at the end of a sequence so that you don't duplicate anything + * when restarting, but again this is unsatisfactory for a "single buffer" sample. + * + * For now, I've taken the coward's way and left these methods unimplemented. + * Perhaps the way forward is to divide even "single buffer" samples into tasks of + * only a few ms long, so that they can be stopped/restarted quickly enough that it + * feels instant. (This also saves on memory, for long in-memory "single buffer" + * samples, since we have to locally take a resampled copy!) + */ +void common_hal_audiopwmio_pwmaudioout_pause(audiopwmio_pwmaudioout_obj_t* self) { + self->paused = true; + self->pwm->SHORTS = NRF_PWM_SHORT_SEQEND1_STOP_MASK; +} + +void common_hal_audiopwmio_pwmaudioout_resume(audiopwmio_pwmaudioout_obj_t* self) { + self->paused = false; + self->pwm->SHORTS = NRF_PWM_SHORT_LOOPSDONE_SEQSTART0_MASK; + if (self->pwm->EVENTS_STOPPED) { + self->pwm->EVENTS_STOPPED = 0; + self->pwm->TASKS_SEQSTART[0] = 1; + } +} + +bool common_hal_audiopwmio_pwmaudioout_get_paused(audiopwmio_pwmaudioout_obj_t* self) { + return self->paused; +} diff --git a/ports/nrf/common-hal/audiopwmio/PWMAudioOut.h b/ports/nrf/common-hal/audiopwmio/PWMAudioOut.h new file mode 100644 index 0000000000..8deff5d340 --- /dev/null +++ b/ports/nrf/common-hal/audiopwmio/PWMAudioOut.h @@ -0,0 +1,59 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_AUDIOPWM_AUDIOOUT_H +#define MICROPY_INCLUDED_NRF_COMMON_HAL_AUDIOPWM_AUDIOOUT_H + +#include "common-hal/microcontroller/Pin.h" + +typedef struct { + mp_obj_base_t base; + mp_obj_t *sample; + NRF_PWM_Type *pwm; + uint16_t *buffers[2]; + + uint16_t buffer_length; + uint16_t quiescent_value; + uint16_t scale; + + uint8_t left_channel_number; + uint8_t right_channel_number; + uint8_t spacing; + uint8_t bytes_per_sample; + + bool playing; + bool stopping; + bool paused; + bool loop; + bool signed_to_unsigned; + bool single_buffer; +} audiopwmio_pwmaudioout_obj_t; + +void audiopwmout_reset(void); + +void audiopwmout_background(void); + +#endif diff --git a/ports/nrf/common-hal/audiopwmio/__init__.c b/ports/nrf/common-hal/audiopwmio/__init__.c new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ports/nrf/common-hal/pulseio/PWMOut.c b/ports/nrf/common-hal/pulseio/PWMOut.c index 5233c9adc6..b827470d3d 100644 --- a/ports/nrf/common-hal/pulseio/PWMOut.c +++ b/ports/nrf/common-hal/pulseio/PWMOut.c @@ -57,6 +57,12 @@ STATIC uint16_t pwm_seq[MP_ARRAY_SIZE(pwms)][CHANNELS_PER_PWM]; static uint8_t never_reset_pwm[MP_ARRAY_SIZE(pwms)]; +STATIC int pwm_idx(NRF_PWM_Type *pwm) { + for(size_t i=0; i < MP_ARRAY_SIZE(pwms); i++) + if(pwms[i] == pwm) return i; + return -1; +} + void common_hal_pulseio_pwmout_never_reset(pulseio_pwmout_obj_t *self) { for(size_t i=0; i < MP_ARRAY_SIZE(pwms); i++) { NRF_PWM_Type* pwm = pwms[i]; @@ -133,6 +139,57 @@ bool convert_frequency(uint32_t frequency, uint16_t *countertop, nrf_pwm_clk_t * return false; } +NRF_PWM_Type *pwmout_allocate(uint16_t countertop, nrf_pwm_clk_t base_clock, + bool variable_frequency, int8_t *channel_out, bool *pwm_already_in_use_out) { + for (size_t pwm_index = 0; pwm_index < MP_ARRAY_SIZE(pwms); pwm_index++) { + NRF_PWM_Type *pwm = pwms[pwm_index]; + bool pwm_already_in_use = pwm->ENABLE & SPIM_ENABLE_ENABLE_Msk; + if (pwm_already_in_use) { + if (variable_frequency) { + // Variable frequency requires exclusive use of a PWM, so try the next one. + continue; + } + + // PWM is in use, but see if it's set to the same frequency we need. If so, + // look for a free channel. + if (pwm->COUNTERTOP == countertop && pwm->PRESCALER == base_clock) { + for (size_t chan = 0; chan < CHANNELS_PER_PWM; chan++) { + if (pwm->PSEL.OUT[chan] == 0xFFFFFFFF) { + // Channel is free. + if(channel_out) + *channel_out = chan; + if(pwm_already_in_use_out) + *pwm_already_in_use_out = pwm_already_in_use; + return pwm; + } + } + } + } else { + // PWM not yet in use, so we can start to use it. Use channel 0. + if(channel_out) + *channel_out = 0; + if(pwm_already_in_use_out) + *pwm_already_in_use_out = pwm_already_in_use; + return pwm; + } + } + return NULL; +} + +void pwmout_free_channel(NRF_PWM_Type *pwm, int8_t channel) { + // Disconnect pin from channel. + pwm->PSEL.OUT[channel] = 0xFFFFFFFF; + + for(int i=0; i < CHANNELS_PER_PWM; i++) { + if (pwm->PSEL.OUT[i] != 0xFFFFFFFF) { + // Some channel is still being used, so don't disable. + return; + } + } + + nrf_pwm_disable(pwm); +} + pwmout_result_t common_hal_pulseio_pwmout_construct(pulseio_pwmout_obj_t* self, const mcu_pin_obj_t* pin, uint16_t duty, @@ -148,48 +205,16 @@ pwmout_result_t common_hal_pulseio_pwmout_construct(pulseio_pwmout_obj_t* self, return PWMOUT_INVALID_FREQUENCY; } - self->pwm = NULL; - self->channel = CHANNELS_PER_PWM; // out-of-range value. + int8_t channel; bool pwm_already_in_use; - NRF_PWM_Type* pwm; - size_t pwm_index = 0; - for (; pwm_index < MP_ARRAY_SIZE(pwms); pwm_index++) { - pwm = pwms[pwm_index]; - pwm_already_in_use = pwm->ENABLE & SPIM_ENABLE_ENABLE_Msk; - if (pwm_already_in_use) { - if (variable_frequency) { - // Variable frequency requires exclusive use of a PWM, so try the next one. - continue; - } - - // PWM is in use, but see if it's set to the same frequency we need. If so, - // look for a free channel. - if (pwm->COUNTERTOP == countertop && pwm->PRESCALER == base_clock) { - for (size_t chan = 0; chan < CHANNELS_PER_PWM; chan++) { - if (pwm->PSEL.OUT[chan] == 0xFFFFFFFF) { - // Channel is free. - self->pwm = pwm; - self->channel = chan; - break; - } - } - // Did we find a channel? If not, loop and check the next pwm. - if (self->pwm != NULL) { - break; - } - } - } else { - // PWM not yet in use, so we can start to use it. Use channel 0. - self->pwm = pwm; - self->channel = 0; - break; - } - } + self->pwm = pwmout_allocate(countertop, base_clock, variable_frequency, + &channel, &pwm_already_in_use); if (self->pwm == NULL) { return PWMOUT_ALL_TIMERS_IN_USE; } + self->channel = channel; self->pin_number = pin->number; claim_pin(pin); @@ -200,17 +225,17 @@ pwmout_result_t common_hal_pulseio_pwmout_construct(pulseio_pwmout_obj_t* self, nrf_gpio_cfg_output(self->pin_number); // disable before mapping pin channel - nrf_pwm_disable(pwm); + nrf_pwm_disable(self->pwm); if (!pwm_already_in_use) { - reset_single_pwmout(pwm_index); - nrf_pwm_configure(pwm, base_clock, NRF_PWM_MODE_UP, countertop); + reset_single_pwmout(pwm_idx(self->pwm)); + nrf_pwm_configure(self->pwm, base_clock, NRF_PWM_MODE_UP, countertop); } // Connect channel to pin, without disturbing other channels. - pwm->PSEL.OUT[self->channel] = pin->number; + self->pwm->PSEL.OUT[self->channel] = pin->number; - nrf_pwm_enable(pwm); + nrf_pwm_enable(self->pwm); common_hal_pulseio_pwmout_set_duty_cycle(self, duty); return PWMOUT_OK; @@ -230,17 +255,7 @@ void common_hal_pulseio_pwmout_deinit(pulseio_pwmout_obj_t* self) { NRF_PWM_Type* pwm = self->pwm; self->pwm = NULL; - // Disconnect pin from channel. - pwm->PSEL.OUT[self->channel] = 0xFFFFFFFF; - - for(int i=0; i < CHANNELS_PER_PWM; i++) { - if (self->pwm->PSEL.OUT[i] != 0xFFFFFFFF) { - // Some channel is still being used, so don't disable. - return; - } - } - - nrf_pwm_disable(pwm); + pwmout_free_channel(pwm, self->channel); } void common_hal_pulseio_pwmout_set_duty_cycle(pulseio_pwmout_obj_t* self, uint16_t duty_cycle) { diff --git a/ports/nrf/common-hal/pulseio/PWMOut.h b/ports/nrf/common-hal/pulseio/PWMOut.h index a4e58dc1a5..b6798cb685 100644 --- a/ports/nrf/common-hal/pulseio/PWMOut.h +++ b/ports/nrf/common-hal/pulseio/PWMOut.h @@ -41,5 +41,8 @@ typedef struct { } pulseio_pwmout_obj_t; void pwmout_reset(void); +NRF_PWM_Type *pwmout_allocate(uint16_t countertop, nrf_pwm_clk_t base_clock, + bool variable_frequency, int8_t *channel_out, bool *pwm_already_in_use_out); +void pwmout_free_channel(NRF_PWM_Type *pwm, int8_t channel); #endif // MICROPY_INCLUDED_NRF_COMMON_HAL_PULSEIO_PWMOUT_H diff --git a/ports/nrf/mpconfigport.mk b/ports/nrf/mpconfigport.mk index 83166708a6..57879bbaae 100644 --- a/ports/nrf/mpconfigport.mk +++ b/ports/nrf/mpconfigport.mk @@ -10,8 +10,10 @@ USB_SERIAL_NUMBER_LENGTH = 16 # All nRF ports have longints. LONGINT_IMPL = MPZ -# No DAC, so no regular audio. +# Audio via PWM +CIRCUITPY_AUDIOCORE = 1 CIRCUITPY_AUDIOIO = 0 +CIRCUITPY_AUDIOPWMIO = 1 # No I2S yet. CIRCUITPY_AUDIOBUSIO = 0 diff --git a/ports/nrf/supervisor/port.c b/ports/nrf/supervisor/port.c index 8fbf75498f..03f7876247 100644 --- a/ports/nrf/supervisor/port.c +++ b/ports/nrf/supervisor/port.c @@ -50,6 +50,10 @@ #include "shared-bindings/rtc/__init__.h" +#ifdef CIRCUITPY_AUDIOPWMIO +#include "common-hal/audiopwmio/PWMAudioOut.h" +#endif + static void power_warning_handler(void) { reset_into_safe_mode(BROWNOUT); } @@ -94,6 +98,10 @@ void reset_port(void) { spi_reset(); uart_reset(); +#ifdef CIRCUITPY_AUDIOPWMIO + audiopwmout_reset(); +#endif + #if CIRCUITPY_PULSEIO pwmout_reset(); pulseout_reset(); diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index bdefc18cc0..a6dde61dac 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -108,6 +108,9 @@ endif ifeq ($(CIRCUITPY_AUDIOIO),1) SRC_PATTERNS += audioio/% endif +ifeq ($(CIRCUITPY_AUDIOPWMIO),1) +SRC_PATTERNS += audiopwmio/% +endif ifeq ($(CIRCUITPY_AUDIOCORE),1) SRC_PATTERNS += audiocore/% endif @@ -223,6 +226,8 @@ $(filter $(SRC_PATTERNS), \ audiobusio/__init__.c \ audiobusio/I2SOut.c \ audiobusio/PDMIn.c \ + audiopwmio/__init__.c \ + audiopwmio/PWMAudioOut.c \ audioio/__init__.c \ audioio/AudioOut.c \ bleio/__init__.c \ @@ -303,6 +308,7 @@ $(filter $(SRC_PATTERNS), \ _stage/Layer.c \ _stage/Text.c \ _stage/__init__.c \ + audiopwmio/__init__.c \ audioio/__init__.c \ audiocore/__init__.c \ audiocore/Mixer.c \ diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 3440eb052a..d88d6538e0 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -244,6 +244,13 @@ extern const struct _mp_obj_module_t audioio_module; #define AUDIOIO_MODULE #endif +#if CIRCUITPY_AUDIOPWMIO +#define AUDIOPWMIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_audiopwmio), (mp_obj_t)&audiopwmio_module }, +extern const struct _mp_obj_module_t audiopwmio_module; +#else +#define AUDIOPWMIO_MODULE +#endif + #if CIRCUITPY_BITBANGIO #define BITBANGIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_bitbangio), (mp_obj_t)&bitbangio_module }, extern const struct _mp_obj_module_t bitbangio_module; @@ -573,6 +580,7 @@ extern const struct _mp_obj_module_t ustack_module; AUDIOBUSIO_MODULE \ AUDIOCORE_MODULE \ AUDIOIO_MODULE \ + AUDIOPWMIO_MODULE \ BITBANGIO_MODULE \ BLEIO_MODULE \ BOARD_MODULE \ diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 772a1c3e1f..a408cd7acc 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -74,9 +74,18 @@ CIRCUITPY_AUDIOIO = $(CIRCUITPY_FULL_BUILD) endif CFLAGS += -DCIRCUITPY_AUDIOIO=$(CIRCUITPY_AUDIOIO) +ifndef CIRCUITPY_AUDIOPWMIO +CIRCUITPY_AUDIOPWMIO = 0 +endif +CFLAGS += -DCIRCUITPY_AUDIOPWMIO=$(CIRCUITPY_AUDIOPWMIO) + ifndef CIRCUITPY_AUDIOCORE +ifeq ($(CIRCUITPY_AUDIOPWMIO),1) +CIRCUITPY_AUDIOCORE = $(CIRCUITPY_AUDIOPWMIO) +else CIRCUITPY_AUDIOCORE = $(CIRCUITPY_AUDIOIO) endif +endif CFLAGS += -DCIRCUITPY_AUDIOCORE=$(CIRCUITPY_AUDIOCORE) ifndef CIRCUITPY_BITBANGIO diff --git a/shared-bindings/audiobusio/I2SOut.c b/shared-bindings/audiobusio/I2SOut.c index 980f113929..9bce9c7609 100644 --- a/shared-bindings/audiobusio/I2SOut.c +++ b/shared-bindings/audiobusio/I2SOut.c @@ -56,7 +56,7 @@ //| using `UDA1334 Breakout `_:: //| //| import audiobusio -//| import audioio +//| import audiocore //| import board //| import array //| import time @@ -68,7 +68,7 @@ //| for i in range(length): //| sine_wave[i] = int(math.sin(math.pi * 2 * i / 18) * (2 ** 15) + 2 ** 15) //| -//| sine_wave = audiobusio.RawSample(sine_wave, sample_rate=8000) +//| sine_wave = audiocore.RawSample(sine_wave, sample_rate=8000) //| i2s = audiobusio.I2SOut(board.D1, board.D0, board.D9) //| i2s.play(sine_wave, loop=True) //| time.sleep(1) @@ -78,12 +78,13 @@ //| //| import board //| import audioio +//| import audiocore //| import audiobusio //| import digitalio //| //| //| f = open("cplay-5.1-16bit-16khz.wav", "rb") -//| wav = audioio.WaveFile(f) +//| wav = audiocore.WaveFile(f) //| //| a = audiobusio.I2SOut(board.D1, board.D0, board.D9) //| @@ -163,7 +164,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audiobusio_i2sout___exit___obj, 4, 4, //| Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| Sample must be an `audioio.WaveFile` or `audioio.RawSample`. +//| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, or `audiocore.Mixer`. //| //| The sample itself should consist of 8 bit or 16 bit samples. //| diff --git a/shared-bindings/audiocore/Mixer.c b/shared-bindings/audiocore/Mixer.c index b2f0a3fdac..9ab720434b 100644 --- a/shared-bindings/audiocore/Mixer.c +++ b/shared-bindings/audiocore/Mixer.c @@ -36,7 +36,7 @@ #include "shared-bindings/util.h" #include "supervisor/shared/translate.h" -//| .. currentmodule:: audioio +//| .. currentmodule:: audiocore //| //| :class:`Mixer` -- Mixes one or more audio samples together //| =========================================================== @@ -54,15 +54,16 @@ //| //| import board //| import audioio +//| import audiocore //| import digitalio //| //| # Required for CircuitPlayground Express //| speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) //| speaker_enable.switch_to_output(value=True) //| -//| music = audioio.WaveFile(open("cplay-5.1-16bit-16khz.wav", "rb")) -//| drum = audioio.WaveFile(open("drum.wav", "rb")) -//| mixer = audioio.Mixer(voice_count=2, sample_rate=16000, channel_count=1, bits_per_sample=16, samples_signed=True) +//| music = audiocore.WaveFile(open("cplay-5.1-16bit-16khz.wav", "rb")) +//| drum = audiocore.WaveFile(open("drum.wav", "rb")) +//| mixer = audiocore.Mixer(voice_count=2, sample_rate=16000, channel_count=1, bits_per_sample=16, samples_signed=True) //| a = audioio.AudioOut(board.A0) //| //| print("playing") @@ -151,7 +152,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_mixer___exit___obj, 4, 4, aud //| Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| Sample must be an `audioio.WaveFile`, `audioio.Mixer` or `audioio.RawSample`. +//| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, or `audiocore.Mixer`. //| //| The sample must match the Mixer's encoding settings given in the constructor. //| diff --git a/shared-bindings/audiocore/RawSample.c b/shared-bindings/audiocore/RawSample.c index 912b48bfbb..07f8c683f2 100644 --- a/shared-bindings/audiocore/RawSample.c +++ b/shared-bindings/audiocore/RawSample.c @@ -35,7 +35,7 @@ #include "shared-bindings/audiocore/RawSample.h" #include "supervisor/shared/translate.h" -//| .. currentmodule:: audioio +//| .. currentmodule:: audiocore //| //| :class:`RawSample` -- A raw audio sample buffer //| ======================================================== @@ -55,6 +55,7 @@ //| //| Simple 8ksps 440 Hz sin wave:: //| +//| import audiocore //| import audioio //| import board //| import array @@ -68,7 +69,7 @@ //| sine_wave[i] = int(math.sin(math.pi * 2 * i / 18) * (2 ** 15)) //| //| dac = audioio.AudioOut(board.SPEAKER) -//| sine_wave = audioio.RawSample(sine_wave) +//| sine_wave = audiocore.RawSample(sine_wave) //| dac.play(sine_wave, loop=True) //| time.sleep(1) //| dac.stop() diff --git a/shared-bindings/audiocore/WaveFile.c b/shared-bindings/audiocore/WaveFile.c index 4c1993b7c4..edb190d3ab 100644 --- a/shared-bindings/audiocore/WaveFile.c +++ b/shared-bindings/audiocore/WaveFile.c @@ -33,7 +33,7 @@ #include "shared-bindings/util.h" #include "supervisor/shared/translate.h" -//| .. currentmodule:: audioio +//| .. currentmodule:: audiocore //| //| :class:`WaveFile` -- Load a wave file for audio playback //| ======================================================== @@ -50,6 +50,7 @@ //| Playing a wave file from flash:: //| //| import board +//| import audiocore //| import audioio //| import digitalio //| @@ -58,7 +59,7 @@ //| speaker_enable.switch_to_output(value=True) //| //| data = open("cplay-5.1-16bit-16khz.wav", "rb") -//| wav = audioio.WaveFile(data) +//| wav = audiocore.WaveFile(data) //| a = audioio.AudioOut(board.A0) //| //| print("playing") diff --git a/shared-bindings/audioio/AudioOut.c b/shared-bindings/audioio/AudioOut.c index 79df66f7dd..af2fce48e3 100644 --- a/shared-bindings/audioio/AudioOut.c +++ b/shared-bindings/audioio/AudioOut.c @@ -55,6 +55,7 @@ //| //| Simple 8ksps 440 Hz sin wave:: //| +//| import audiocore //| import audioio //| import board //| import array @@ -68,7 +69,7 @@ //| sine_wave[i] = int(math.sin(math.pi * 2 * i / 18) * (2 ** 15) + 2 ** 15) //| //| dac = audioio.AudioOut(board.SPEAKER) -//| sine_wave = audioio.RawSample(sine_wave, sample_rate=8000) +//| sine_wave = audiocore.RawSample(sine_wave, sample_rate=8000) //| dac.play(sine_wave, loop=True) //| time.sleep(1) //| dac.stop() @@ -84,7 +85,7 @@ //| speaker_enable.switch_to_output(value=True) //| //| data = open("cplay-5.1-16bit-16khz.wav", "rb") -//| wav = audioio.WaveFile(data) +//| wav = audiocore.WaveFile(data) //| a = audioio.AudioOut(board.A0) //| //| print("playing") @@ -162,7 +163,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audioio_audioout___exit___obj, 4, 4, //| Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| Sample must be an `audioio.WaveFile` or `audioio.RawSample`. +//| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, or `audiocore.Mixer`. //| //| The sample itself should consist of 16 bit samples. Microcontrollers with a lower output //| resolution will use the highest order bits to output. For example, the SAMD21 has a 10 bit diff --git a/shared-bindings/audiopwmio/PWMAudioOut.c b/shared-bindings/audiopwmio/PWMAudioOut.c new file mode 100644 index 0000000000..92543d1128 --- /dev/null +++ b/shared-bindings/audiopwmio/PWMAudioOut.c @@ -0,0 +1,294 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "lib/utils/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/audiopwmio/PWMAudioOut.h" +#include "shared-bindings/audiocore/RawSample.h" +#include "shared-bindings/util.h" +#include "supervisor/shared/translate.h" + +//| .. currentmodule:: audiopwmio +//| +//| :class:`PWMAudioOut` -- Output an analog audio signal +//| ======================================================== +//| +//| AudioOut can be used to output an analog audio signal on a given pin. +//| +//| .. class:: PWMAudioOut(left_channel, *, right_channel=None, quiescent_value=0x8000) +//| +//| Create a PWMAudioOut object associated with the given pin(s). This allows you to +//| play audio signals out on the given pin(s). In contrast to mod:`audioio`, +//| the pin(s) specified are digital pins, and are driven with a device-dependent PWM +//| signal. +//| +//| :param ~microcontroller.Pin left_channel: The pin to output the left channel to +//| :param ~microcontroller.Pin right_channel: The pin to output the right channel to +//| :param int quiescent_value: The output value when no signal is present. Samples should start +//| and end with this value to prevent audible popping. +//| +//| Simple 8ksps 440 Hz sin wave:: +//| +//| import audiocore +//| import audiopwmio +//| import board +//| import array +//| import time +//| import math +//| +//| # Generate one period of sine wav. +//| length = 8000 // 440 +//| sine_wave = array.array("H", [0] * length) +//| for i in range(length): +//| sine_wave[i] = int(math.sin(math.pi * 2 * i / 18) * (2 ** 15) + 2 ** 15) +//| +//| dac = audiopwmio.PWMAudioOut(board.SPEAKER) +//| sine_wave = audiocore.RawSample(sine_wave, sample_rate=8000) +//| dac.play(sine_wave, loop=True) +//| time.sleep(1) +//| dac.stop() +//| +//| Playing a wave file from flash:: +//| +//| import board +//| import audiocore +//| import audiopwmio +//| import digitalio +//| +//| # Required for CircuitPlayground Express +//| speaker_enable = digitalio.DigitalInOut(board.SPEAKER_ENABLE) +//| speaker_enable.switch_to_output(value=True) +//| +//| data = open("cplay-5.1-16bit-16khz.wav", "rb") +//| wav = audiocore.WaveFile(data) +//| a = audiopwmio.PWMAudioOut(board.SPEAKER) +//| +//| print("playing") +//| a.play(wav) +//| while a.playing: +//| pass +//| print("stopped") +//| +STATIC mp_obj_t audiopwmio_pwmaudioout_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_left_channel, ARG_right_channel, ARG_quiescent_value }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_left_channel, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_right_channel, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_quiescent_value, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x8000} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t left_channel_obj = args[ARG_left_channel].u_obj; + assert_pin(left_channel_obj, false); + const mcu_pin_obj_t *left_channel_pin = MP_OBJ_TO_PTR(left_channel_obj); + + mp_obj_t right_channel_obj = args[ARG_right_channel].u_obj; + const mcu_pin_obj_t *right_channel_pin = NULL; + if (right_channel_obj != mp_const_none) { + assert_pin(right_channel_obj, false); + right_channel_pin = MP_OBJ_TO_PTR(right_channel_obj); + } + + // create AudioOut object from the given pin + audiopwmio_pwmaudioout_obj_t *self = m_new_obj(audiopwmio_pwmaudioout_obj_t); + self->base.type = &audiopwmio_pwmaudioout_type; + common_hal_audiopwmio_pwmaudioout_construct(self, left_channel_pin, right_channel_pin, args[ARG_quiescent_value].u_int); + + return MP_OBJ_FROM_PTR(self); +} + +//| .. method:: deinit() +//| +//| Deinitialises the PWMAudioOut and releases any hardware resources for reuse. +//| +STATIC mp_obj_t audiopwmio_pwmaudioout_deinit(mp_obj_t self_in) { + audiopwmio_pwmaudioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiopwmio_pwmaudioout_deinit(self); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(audiopwmio_pwmaudioout_deinit_obj, audiopwmio_pwmaudioout_deinit); + +STATIC void check_for_deinit(audiopwmio_pwmaudioout_obj_t *self) { + if (common_hal_audiopwmio_pwmaudioout_deinited(self)) { + raise_deinited_error(); + } +} +//| .. method:: __enter__() +//| +//| No-op used by Context Managers. +//| +// Provided by context manager helper. + +//| .. method:: __exit__() +//| +//| Automatically deinitializes the hardware when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info. +//| +STATIC mp_obj_t audiopwmio_pwmaudioout_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_audiopwmio_pwmaudioout_deinit(args[0]); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(audiopwmio_pwmaudioout___exit___obj, 4, 4, audiopwmio_pwmaudioout_obj___exit__); + + +//| .. method:: play(sample, *, loop=False) +//| +//| Plays the sample once when loop=False and continuously when loop=True. +//| Does not block. Use `playing` to block. +//| +//| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, or `audiocore.Mixer`. +//| +//| The sample itself should consist of 16 bit samples. Microcontrollers with a lower output +//| resolution will use the highest order bits to output. For example, the SAMD21 has a 10 bit +//| DAC that ignores the lowest 6 bits when playing 16 bit samples. +//| +STATIC mp_obj_t audiopwmio_pwmaudioout_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} }, + }; + audiopwmio_pwmaudioout_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_audiopwmio_pwmaudioout_play(self, sample, args[ARG_loop].u_bool); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(audiopwmio_pwmaudioout_play_obj, 1, audiopwmio_pwmaudioout_obj_play); + +//| .. method:: stop() +//| +//| Stops playback and resets to the start of the sample. +//| +STATIC mp_obj_t audiopwmio_pwmaudioout_obj_stop(mp_obj_t self_in) { + audiopwmio_pwmaudioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_audiopwmio_pwmaudioout_stop(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(audiopwmio_pwmaudioout_stop_obj, audiopwmio_pwmaudioout_obj_stop); + +//| .. attribute:: playing +//| +//| True when an audio sample is being output even if `paused`. (read-only) +//| +STATIC mp_obj_t audiopwmio_pwmaudioout_obj_get_playing(mp_obj_t self_in) { + audiopwmio_pwmaudioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_audiopwmio_pwmaudioout_get_playing(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiopwmio_pwmaudioout_get_playing_obj, audiopwmio_pwmaudioout_obj_get_playing); + +const mp_obj_property_t audiopwmio_pwmaudioout_playing_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audiopwmio_pwmaudioout_get_playing_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +//| .. method:: pause() +//| +//| Stops playback temporarily while remembering the position. Use `resume` to resume playback. +//| +STATIC mp_obj_t audiopwmio_pwmaudioout_obj_pause(mp_obj_t self_in) { + audiopwmio_pwmaudioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + if (!common_hal_audiopwmio_pwmaudioout_get_playing(self)) { + mp_raise_RuntimeError(translate("Not playing")); + } + common_hal_audiopwmio_pwmaudioout_pause(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(audiopwmio_pwmaudioout_pause_obj, audiopwmio_pwmaudioout_obj_pause); + +//| .. method:: resume() +//| +//| Resumes sample playback after :py:func:`pause`. +//| +STATIC mp_obj_t audiopwmio_pwmaudioout_obj_resume(mp_obj_t self_in) { + audiopwmio_pwmaudioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + if (common_hal_audiopwmio_pwmaudioout_get_paused(self)) { + common_hal_audiopwmio_pwmaudioout_resume(self); + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(audiopwmio_pwmaudioout_resume_obj, audiopwmio_pwmaudioout_obj_resume); + +//| .. attribute:: paused +//| +//| True when playback is paused. (read-only) +//| +STATIC mp_obj_t audiopwmio_pwmaudioout_obj_get_paused(mp_obj_t self_in) { + audiopwmio_pwmaudioout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_audiopwmio_pwmaudioout_get_paused(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiopwmio_pwmaudioout_get_paused_obj, audiopwmio_pwmaudioout_obj_get_paused); + +const mp_obj_property_t audiopwmio_pwmaudioout_paused_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&audiopwmio_pwmaudioout_get_paused_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj}, +}; + +STATIC const mp_rom_map_elem_t audiopwmio_pwmaudioout_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audiopwmio_pwmaudioout_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&audiopwmio_pwmaudioout___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&audiopwmio_pwmaudioout_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&audiopwmio_pwmaudioout_stop_obj) }, + { MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&audiopwmio_pwmaudioout_pause_obj) }, + { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&audiopwmio_pwmaudioout_resume_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiopwmio_pwmaudioout_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_paused), MP_ROM_PTR(&audiopwmio_pwmaudioout_paused_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(audiopwmio_pwmaudioout_locals_dict, audiopwmio_pwmaudioout_locals_dict_table); + +const mp_obj_type_t audiopwmio_pwmaudioout_type = { + { &mp_type_type }, + .name = MP_QSTR_PWMAudioOut, + .make_new = audiopwmio_pwmaudioout_make_new, + .locals_dict = (mp_obj_dict_t*)&audiopwmio_pwmaudioout_locals_dict, +}; diff --git a/shared-bindings/audiopwmio/PWMAudioOut.h b/shared-bindings/audiopwmio/PWMAudioOut.h new file mode 100644 index 0000000000..22afc70d38 --- /dev/null +++ b/shared-bindings/audiopwmio/PWMAudioOut.h @@ -0,0 +1,49 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOPWMIO_AUDIOOUT_H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOPWMIO_AUDIOOUT_H + +#include "common-hal/audiopwmio/PWMAudioOut.h" +#include "common-hal/microcontroller/Pin.h" +#include "shared-bindings/audiocore/RawSample.h" + +extern const mp_obj_type_t audiopwmio_pwmaudioout_type; + +// left_channel will always be non-NULL but right_channel may be for mono output. +void common_hal_audiopwmio_pwmaudioout_construct(audiopwmio_pwmaudioout_obj_t* self, + const mcu_pin_obj_t* left_channel, const mcu_pin_obj_t* right_channel, uint16_t default_value); + +void common_hal_audiopwmio_pwmaudioout_deinit(audiopwmio_pwmaudioout_obj_t* self); +bool common_hal_audiopwmio_pwmaudioout_deinited(audiopwmio_pwmaudioout_obj_t* self); +void common_hal_audiopwmio_pwmaudioout_play(audiopwmio_pwmaudioout_obj_t* self, mp_obj_t sample, bool loop); +void common_hal_audiopwmio_pwmaudioout_stop(audiopwmio_pwmaudioout_obj_t* self); +bool common_hal_audiopwmio_pwmaudioout_get_playing(audiopwmio_pwmaudioout_obj_t* self); +void common_hal_audiopwmio_pwmaudioout_pause(audiopwmio_pwmaudioout_obj_t* self); +void common_hal_audiopwmio_pwmaudioout_resume(audiopwmio_pwmaudioout_obj_t* self); +bool common_hal_audiopwmio_pwmaudioout_get_paused(audiopwmio_pwmaudioout_obj_t* self); + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOPWMIO_AUDIOOUT_H diff --git a/shared-bindings/audiopwmio/__init__.c b/shared-bindings/audiopwmio/__init__.c new file mode 100644 index 0000000000..8a2b202b36 --- /dev/null +++ b/shared-bindings/audiopwmio/__init__.c @@ -0,0 +1,71 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/audiopwmio/__init__.h" +#include "shared-bindings/audiopwmio/PWMAudioOut.h" + +//| :mod:`audiopwmio` --- Support for audio input and output +//| ======================================================== +//| +//| .. module:: audiopwmio +//| :synopsis: Support for audio output via digital PWM +//| :platform: NRF52 +//| +//| The `audiopwmio` module contains classes to provide access to audio IO. +//| +//| Libraries +//| +//| .. toctree:: +//| :maxdepth: 3 +//| +//| PWMAudioOut +//| +//| All classes change hardware state and should be deinitialized when they +//| are no longer needed if the program continues after use. To do so, either +//| call :py:meth:`!deinit` or use a context manager. See +//| :ref:`lifetime-and-contextmanagers` for more info. +//| +//| Since CircuitPython 5, `Mixer`, `RawSample` and `WaveFile` are moved +//| to :mod:`audiocore`. +//| + +STATIC const mp_rom_map_elem_t audiopwmio_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audiopwmio) }, + { MP_ROM_QSTR(MP_QSTR_PWMAudioOut), MP_ROM_PTR(&audiopwmio_pwmaudioout_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(audiopwmio_module_globals, audiopwmio_module_globals_table); + +const mp_obj_module_t audiopwmio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&audiopwmio_module_globals, +}; diff --git a/shared-bindings/audiopwmio/__init__.h b/shared-bindings/audiopwmio/__init__.h new file mode 100644 index 0000000000..e4b7067d11 --- /dev/null +++ b/shared-bindings/audiopwmio/__init__.h @@ -0,0 +1,34 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO___INIT___H +#define MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO___INIT___H + +#include "py/obj.h" + +// Nothing now. + +#endif // MICROPY_INCLUDED_SHARED_BINDINGS_AUDIOIO___INIT___H diff --git a/shared-module/audiocore/WaveFile.h b/shared-module/audiocore/WaveFile.h index 97dd6a46f2..3eecbf15ce 100644 --- a/shared-module/audiocore/WaveFile.h +++ b/shared-module/audiocore/WaveFile.h @@ -27,6 +27,7 @@ #ifndef MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_WAVEFILE_H #define MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_WAVEFILE_H +#include "extmod/vfs_fat.h" #include "py/obj.h" #include "shared-module/audiocore/__init__.h" diff --git a/shared-module/audiopwmio/__init__.c b/shared-module/audiopwmio/__init__.c new file mode 100644 index 0000000000..e69de29bb2 diff --git a/shared-module/audiopwmio/__init__.h b/shared-module/audiopwmio/__init__.h new file mode 100644 index 0000000000..964e4aafe2 --- /dev/null +++ b/shared-module/audiopwmio/__init__.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_MODULE_AUDIOPWMIO__INIT__H +#define MICROPY_INCLUDED_SHARED_MODULE_AUDIOPWMIO__INIT__H + +#include "shared-module/audiocore/__init__.h" + +#endif // MICROPY_INCLUDED_SHARED_MODULE_AUDIOPWMIO__INIT__H