Playing audio information in a Pi Pico and not using a DAC
The Raspberry Pico is all of a sudden turning into my most well-liked chip for embedded improvement. It is effectively made, sturdy {hardware}, with a ton of options that seem designed with smartness and keenness (the state machines driving the GPIOs are a killer characteristic!). Its foremost weak point, the dearth of connectivity, is now resolved by the W variant. The knowledge sheet is great and paperwork each facet of the chip. Moreover, it’s effectively supported by MicroPython (which I’m utilizing quite a bit), and the C SDK atmosphere is respectable, even when stuffed with ineffective complexities like as we speak trend calls for: a cmake construct system that in flip generates a Makefile, information to outline this and that (used libraries, debug outputs, …), and generally an enormous overkill for the purpose of compiling tiny packages for tiny gadgets. No, it’s worse than that: all this complexity to generate packages for a FIXED {hardware} with a hard and fast set of options (if not for the W / non-W variant). Enough with the rant about how a lot as we speak software program sucks, nevertheless it should be remembered.
One of the cool issues one desires to do with an MCU like that, is producing some sound. The most evident means to do that is utilizing the built-in PWM characteristic of the chip. The GPIOs could be configured to simply alterante between zero and one on the desired frequency, like that:
from machine import Pin, PWM
pwm = PWM(Pin(1))
pwm.freq(400)
pwm.duty_u16(1000)
Assuming you related a piezo to GND and pin 1 of your Pico, you’ll hear a sq. wave sound at 400hz of frequency. Now, there are little sounds as horrible to listen to as sq. waves. Maybe we are able to do higher. I’ll skip all of the intermediate steps right here, like producing a sin wave, and immediately soar to taking part in a wav file. Once you see how to try this, you’ll be able to simply generate your individual different waves (sin, noise, envelops for such waveforms and so forth).
Now you’re possible asking your self: how can I generate the complicated wave kinds to play a wav file, if the Pico can solely swap the pin excessive or low? A correct non sq. waveform consists of various ranges, so I would want a DAC! Fortunately we are able to do all this and not using a DAC in any respect, only a single pin of our Pico.
### How complicated sound era works
I don’t wish to cowl an excessive amount of background right here. But all it is advisable to know is that, for those who don’t wish to generate a trivial sq. wave, that simply alternates between a minimal and most stage of output, you will have to have intermediate steps, like that:
S0: #
S1: ####
S2: ######
S3: #######
S4: ########
And so forth, the place S0 is the primary pattern, S1, the second pattern, …
Each pattern length is dependent upon the sampling frequency, that’s what number of instances each second we alter (when taking part in) or pattern (when recording) the audio wave. This implies that to play a fancy sound, we want the flexibility of our Pico pin to output completely different voltages.
There is a trick to do that with the Pico simply utilizing PWM, that’s to make use of a sq. wave with a really excessive frequency, however with a special responsibility cycle for the completely different voltages we wish to generate. So we set a really very excessive frequency output:
pwm.freq(100000)
Then, if we wish to produce the S0 pattern, we set the responsibility cycle (whose worth is between 0 and 65535) to a small worth. If we wish to produce the S1 pattern, we use the next worth, and so forth. In sequence we might wish to do one thing like that:
pwm.duty_u16(3000) # S0
pwm.duty_u16(12000) # S1
pwm.duty_u16(18000) # S2
pwm.duty_u16(21000) # S3
pwm.duty_u16(24000) # S4
The responsibility cycle is how a lot time the pin is ready to 1 versus how a lot time the pin is ready to 0. An obligation cycle of 65535 means 100% of time pin excessive. 0% means on a regular basis low. All this, whereas preserving the set alternating frequency. So if we zoom like if we’ve an oscilloscope, we are able to see what occurs throughout S2 and S3 pattern era:
S2:
######################
#
#
#
#
######################
#
#
#
#
While S3 can be like:
######################
######################
#
#
#
######################
######################
#
#
#
The pin goes up and down with the identical frequency, however within the case of S3 it stays up extra. This will produce the next common voltage. This permits us to approximate our wave.
### Convert and play a WAV file
In order to play a wav file, we’ve to transform it right into a uncooked format that’s simple to learn utilizing MicroPython. I downloaded a wav file saying “Oh no!” from SoundCloud. So my conversion will seem like this:
ffmpeg -i ohno.wav -ar 24000 -acodec pcm_u8 -f u8 output.uncooked
Note that we transformed the file to eight bit audio (256 completely different output ranges per pattern). Anyway our PWM trick shouldn’t be going to approximate the completely different ranges so effectively, and we’re useful resource constrained. You can strive with 16 bit as effectively, however I bought respectable outcomes like this.
Then, add the output.uncooked file on the machine through mpremote:
mpremote cp output.uncooked :
Now write a file known as “play.py” or as you want, with this content material:
from machine import Pin, PWM
pwm = PWM(Pin(1))
pwm.freq(100000)
f = open("output.uncooked","rb")
buf = bytearray(4096)
whereas f.readinto(buf) > 0:
for pattern in buf:
pwm.duty_u16(pattern<8)
x=1
x=1
x=1
x=1
x=1
f.shut()
What we’re doing right here is simply getting the file, 4096 samples per iteration, then “taking part in” it by setting completely different PWM responsibility cycles one after the opposite, in response to the samples values. The downside is, in our PCM file we’ve 24000 samples per second (see ffmpeg command line). How can make sure that it matches the MicroPython pace? effectively, certainly it’s not an ideal match, so I added “x=1” statements to delay it a bit to kinda match the pitch that appeared appropriate.
Oh, and in case you are questioning what the pattern<8 factor is, that is simply to rescale a 8 bit pattern to the total 16 bit precision wanted to set the PWM responsibility cycle.
The draw back of all that is that it’ll take your program busy whereas taking part in. I didn’t take a look at it but, however MicroPython helps threading, so to have a thread taking part in the audio might be the best way to go.
Comments
HI-FI News
through http://antirez.com
March 6, 2024 at 12:48PM
-
Product on saleAudiophile Vinyl Records Cleaning BundleOriginal price was: €44.95.€34.95Current price is: €34.95. excl. VAT
-
Product on saleEasy Start Vinyl Records Cleaning KitOriginal price was: €39.90.€29.90Current price is: €29.90. excl. VAT
-
Vinyl Records Cleaner Easy Groove Concentrate€19.95 excl. VAT
-
Easy Groove Super Set€199.00 excl. VAT
-
Easy Groove Enzycaster – vinyl records prewash cleaner€25.00 excl. VAT
-
Easy Groove Spray&Wipe vinyl records cleaner€19.95 excl. VAT