16 Replies Latest reply on Oct 6, 2021 9:22 AM by jc2048

    MP3 Playback on PI Pico

    scottiebabe

      Woo Hoo! Three cheers for Adafruit's Circuit Python.

       

      A few months ago when I tried mp3 playback in circuit python I had no success, just a few seconds of gibberish audio (not even the length of the mp3). This was the original guide I followed: https://learn.adafruit.com/mp3-playback-rp2040

       

      Whatever the issue was, they have pushed a fix into their alpha releases: https://github.com/adafruit/circuitpython/issues/4208

       

      Now, just in time for Halloween I can playback MP3 snippets on the Pico!

       

      I assume any recent CircuitPython 7.0 build will work, the one I grabbed happened to be:

      Adafruit CircuitPython 7.0.0-rc.1-70-g3e9cf8fa5

      5 days after posting this thread: https://blog.adafruit.com/2021/09/20/circuitpython-7-0-0-released/  became an official release!

       

       

      This is the sample code I ran on the Pico:

      """
      CircuitPython single MP3 playback example for Raspberry Pi Pico.
      Plays a single MP3 once.
      """
      import board
      import audiomp3
      import audiopwmio
      import time
          
      audio = audiopwmio.PWMAudioOut(board.GP2)
      decoder = audiomp3.MP3Decoder(open("sineimd_24_32.mp3", "rb"))
      #decoder = audiomp3.MP3Decoder(open("noise.mp3", "rb"))
      audio.play(decoder)
      
      while audio.playing:
          pass
      print("Done playing!")
      

      I attached the 2 MP3 files I generated in Audacity. Both MP3 are using a 24 kHz sample rate and are encoded at constant bitrate of 32kbit/s.

       

      "sineimd_24_32.mp3" : 2 Sinusoids with frequencies of 1000 Hz and 1300 Hz.

      "noise.mp3": white noise

       

      For PWM audio they are generating a 10 bit PWM. The output frequency on the Pico ends up being 125MHz/1024 ~ 122 kHz.

       

      A scope shot of the PWM output pin:

       

      Here are a few frequency spectrum plots of just the unfiltered PWM DAC pin:

      You can also see the images offset from the 24 kHz sample frequency:

       

      After a 2nd order lowpass filter the results are quite reasonable (for low-fi sound effects at least):

       

       

      I'm not sure if this is audacity or the MP3 compression specification but when you use a sample rate of 24 kHz and a bitrate of 32kbit/s the spectrum is bandlimited to about 8.2 kHz.

       

      You can see this playing back white noise:

      This actually works out quite nicely as the sampling image is restricted to being above 16 kHz, this gives a 2nd order RC reconstruction filter at least a bit of a chance to rolloff a few dB.

       

      All in all, thank you Adafruit!

       

      noise.mp3

        • Re: MP3 Playback on PI Pico
          neuromodulator

          Cool project, I've played a bit with that using FPGAs. I've never tried with MCUs, but FPGAs give you very tight timing control. I wonder if you can broadcast the audio signal.

          • Re: MP3 Playback on PI Pico
            DAB

            Nice project.

             

            The sinewave file scared my dog.

             

            DAB

            • Re: MP3 Playback on PI Pico
              bobross427

              Hey I'm new to this and can follow the wiring diagrams just fine, copy and paste just fine.

               

              I have tried a couple of the code examples for mp3 playback that seem to work good, with the intent to make a noise or rain machine, but cant get the files to play in a loop.

               

              ------- original example -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

              """

              CircuitPython multiple MP3 playback example for Raspberry Pi Pico.

              Plays two MP3 files consecutively, once time each.

              """

              import board

              import audiomp3

              import audiopwmio

               

               

              audio = audiopwmio.PWMAudioOut(board.GP0)

               

               

              mp3files = ["rain1.mp3", "rain2.mp3"]

               

               

              mp3 = open(mp3files[0], "rb")

              decoder = audiomp3.MP3Decoder(mp3)

               

               

              for filename in mp3files:

                  decoder.file = open(filename, "rb")

                  audio.play(decoder)

                  print("Playing:", filename)

                  while audio.playing:

                      pass

               

              ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

               

              Thanks if you have a working idea.

              • Re: MP3 Playback on PI Pico
                robogary

                Saweet. Did you need to load the MP3 files also to the Pico ?

                 

                Will you add a LM386 to drive a 8 ohm - ish speaker ? 

                  • Re: MP3 Playback on PI Pico
                    scottiebabe

                    I am glad you asked! One of the differences between CircuitPython and MicroPython is that with CircuitPython the Pico just shows up as a flash drive:

                    So you are left with approximately 1MB for code + data on the Pico's 2MB QSPI flash chip. This works out to be almost 4 minutes of 32 kbps mono MP3 audio. Which I think is plenty for short sound effects or jingles.

                     

                    I have been playing around with a few different ways to drive a speaker. I think the easiest way is just to use a class-d BTL amplifier breakout board. But, I realized I didn't have any of the low voltage versions that will run on 3-5V. So, in the ghostly photo above, I got my circuitpython dirty:

                     

                    >>> help()
                    Welcome to Adafruit CircuitPython 7.0.0-rc.3-6-gf9b40d417-dirty!
                    

                     

                    In order to gain register access to the RP2040 peripherals I had to compile in a custom user module which takes a register address and a value:

                    >>> import cexample
                    >>> dir(cexample)
                    ['__class__', '__name__', 'addressof', 'reg_read', 'reg_write']
                    >>> 
                    

                    I wish circuitpython would implement the mem8,mem16,mem32 functions that are available in Micropython, but they don't.

                    I followed the micropython example to include custom c code, I left the name unchanged as cexample . All that effort just to configure the GPIO drive strength to maximum .

                     

                    I then proceeded to output complement of the audio PWM on an adjacent GPIO pin. This essentially provided a class-d BTL output between the 2 GPIO pins. I think that is just so cool! I can make my own greeting card with a little 1/4 W mylar speaker a Pico and a battery. The output volume is between speaking softly and causal speech, not loud but you can hear it.

                     

                    To amp up the volume, I prototyped a discrete full bridge class-d driver:

                    I drive each of the FETs from separate GPIO pins and implemented deadtime control in a PIO state machine:

                    btl2 = """
                    .program btl2
                    .side_set 0
                        set x, 0b1100
                        wait 1 gpio 16 [0]
                        mov pins, x [7]
                        set x, 0b1010 [3]
                        mov pins, x [0]
                        set x, 0b1100
                        wait 0 gpio 16 [0]
                        mov pins, x [7]
                        set x, 0b0101 [3]
                        mov pins, x [0]
                    """
                    

                    I didn't have to modify any of the audio code, I just had the PIO watch the existing audio PWM audio output pin and transformed it into a class-d output.

                     

                    The quiescent current playing back MP3's on the pico was ~26 mA with a 5V input. The switching charge losses of my discrete bridge add another 4 mA (the pwm output is 122 kHz) to bring the operating current up to 30 mA @ 5V.

                     

                    The LM386 was a backup plan, as its output is single-ended and needs a 1.5V headroom off the rails. So when running on a 5V supply there isn't a lot of output left. I realized these MP3 modules that have been in discussion lately use a linear BTL audio amplifier designed for 5V operation:

                    The amplifier on my DFPlayer module uses a 8002 https://jarl-mie.com/01/image/tec2020/HXJ8002_Miniature_Audio_Amplifier_Datasheet.pdf which isn't as efficient as a class-d amp, but also doesn't have any switching noise to perturb your own circuits or turn you protobuild into a pirate radio station putting out fast edges on your speaker cables.

                    1 of 1 people found this helpful