To handle decoding and the overhead of moving bits around, it uses a dual-buffer DMA approach. It reads data from an SD card, running the decoding routine on the main CPU. The decoded samples are written into one of two buffers. While one buffer is being written to, the other is read via DMA and sent to the PWM buffer (more on PWM audio here: https://x.com/beala/status/1826147833168408738). Because DMA is used, data transfer can occur in parallel with decoding, without involving the main processor. When the bits have all been written to the PWM buffer, an interrupt triggers a buffer swap, and the process repeats.
I'm leaving out many details. Tuning the pwm speed to achieve correct playback speed was quite an adventure. And when I needed to increase the clock speed to play higher sample rates, this caused I/O errors with the SD card that I never managed to solve. Although I learned a lot from this process, I ultimately opted to use an MP3 module in my final build: https://store-usa.arduino.cc/products/dfplayer-a-mini-mp3-pl...
This is probably routine for an experienced embedded engineer, but it was quite a learning experience for me!
> And when I needed to increase the clock speed to play higher sample rates, this caused I/O errors with the SD card that I never managed to solve.
Presumably if a RP2040 could almost do it, a RP2350 could do it no problem. Unless you were limited by PWM speed. Even then you might be able to accept less resolution and filter and dither.
I've got music-player on my short list of projects as well. I anticipate using a Teensy though. I'm pretty sure there are good libraries to handle everything you would need.
I feel like the flaws of the Pico 2/RP2350 should be advertised better. The Pico is great. I waisted half of an extremely frustrating day with a Pico 2 before suspecting the board itself was the problem, and confirming it with very specific searches that brought up threads about the issue. The internal pull-downs don't work.
Maybe it's my fault for not making it to page 1357 (!) of the datasheet, where the issue is described as "RP2350-E9".
This is unfortunately not emphasized with many breakout boards. It pays off to skim errata sections at the start of a project. All hardware has errata, and it ranges from incorrect details and minor malfunctions all the way through to broken peripherals and all manner of critical malfunctions.
To be clear, this isn't a problem with the breakout board. The issue is inside the microcontroller itself, which, iiuc, is why there is such a long lead time on fixing it.
While I agree that reading the errata absolutely should be on the "mandatory TODO before you start" list for any project (esp with new/unfamiliar hardware), one should be equally aware that manufacturers aren't always transparent about the defects in their products, sometimes failing to note that entire subsystems, frankly, don't work.
Something is just not right in the Raspberry Pi design team if at this point ESP32 have less unpleasant surprises. New RP micros always turn out have disappointing bugs that make them just shy of considering using for actual embedded projects.
The RP2040 can output 24 bit (or technically 32-bit), 48kHz stereo audio over I2S. Combine that with a DAC like the PCM1502 (which has plenty of dev boards) and you've got yourself a high quality audio setup.
I have a Rust crate for outputting I2S data, or retrieving it from something like a mic or ADC. You can hook it up to DMA and make your audio go brrrr
I think schematics are easier to read than looking at a (fritzing?) breadboard layout.
Discrete resistors aren't necessary for the buttons if you enable the internal pullups on the RP2040.
I did a lot of research for building my own little iPod Nano sized player, which should also have the ability to be controlled by a wired headphone remote.
So here are my results up until now:
- The best device I found is a LicheeRV Nano[1] - also used in NanoKVM.
- It has enough CPU+RAM to support more complex formats like FLAC while still being very small.
- It has a full featured MIPI 31-pin / 6-pin touch display port, although I could not find any available display smaller than 7" to order without hassle (there is an unavailable official 3" though[4]).
- It also has USB-C, where it should be possible to use one of these external USB-C to audiojack adapters with mic support.
- Some of these adapters should support headset control buttons out of the box (via kernel usbsound) - on my notebook this works flawlessly.
- There also is a full featured configurable buildroot[2], so, less work to get started with an optimized build.
- Combining a TP4057 battery charger and a battery gauge (MAX17043) you should be able to support a 1200mah battery, read out battery status via I2C and recharge within a < 3" 3D printed case.
- All you would have to do is create a custom buildroot and a nice user interface via LVGL, they already have a music player sample on there samples listing[3]. Unfortunately this is where the workload is huge ;)
For headphone audio it's plenty to just PWM, inductance of the headphones and mechanical low pass characteristic of the speaker itself will mostly do the rest.
If you want to smooth the things out because you target a line input, simple LC filter will work just fine here.
Indeed. fast PWM is a great approach and fits in well with the analog conversion side effects.
I still find the principal question of "if I need additional components anyway, should I just get the more complex ones" interesting. It's located somewhere between quality, price, the definition of simple and optimal, as well as one of ones personal (or this week's) perspective on the art of the craft.
The projects is not a discrete player that basically pushes SD card data via a few digital standard ICs out as PWM, it uses a pretty sophisticated MCU after all. So, is a I2S DAC the logical solution or the fast PWM, I still find it hard to decide, especially when i have to order (even the simple or
mechanical) parts anyway, or should I have a few I2S DACs in the drawer with the Pico these days.
Not sure if I can bring that point across. It's more about what are your default parts these days. I always have a few i2c OLED displays in that drawer, and the project uses one, too, thirty years ago it might have been resistors, LEDs and a few logic ICs instead. So I wonder, would I go discrete (or "simple") for that particular part of the solution for a particular reason or not. And also, why do I still have no I2S DACs stocked in that drawer, while I have display, sensors etc.
So maybe it's just about audio being a somehow ignored interface in (my) embedded tinkering. Or it's more general and about a modernized selection of stocked parts and using those more naturally while still being aware of the simpler solutions. I use pretty complex (but inexpensive) I2C sensors these days, after all.
Ok, many words on that small part of the overall decision making...
If you want to do a similar audio player I can recommend the DY-SV5W module family, this is essentially a micro-SD playback chip with a headphone amp and a 5W (mono) amplifier, the whole thing can be powered from 5V/USB. Really great for art projects where you just need a single track to play/loop on the press of a button and can't be bothered to program.
It has thre DIP switches that set the mode, you can use them to set the playback modes, one of which is serial control, meaning you can easily beef this up using a microcontroller.
Similarly, the dfplayer mini is fine if you want some sound to scream back at you. Works on button press, but also rx/TX for track management from micro SD card. Small amplifier built in.
I built motion activated playback speakers - good for hiding them in the bushes on halloween.
I've got a Pico 2 (2035) playing mono 22kHz MP3 using built-in 12-bit PWM, sounds pretty good. This would probably work on the 2040. However, as this is C code I'm not sure if this ports to MicroPython easily.
Was giving this thought as well and realized this is more in line with building a “crystal radio” type project than aiming for a marketable product. It’s a case study of a learning process, which is pretty spiffy to me.
Isn't there some way you could calculate the signal density you'd need in any of PDM/PWM/PCM to roughly equate to the quality of signal reproduction of a given tape media, given known tape surface/speed/magnetic density?
Anyway, you could do the work to work out the bit depth/signal rate you'd need to equate to reproduction of a given fluxdensity/recording rate. I would bet the numbers for classic PCM/PDM devices line up surprisingly close to certain tape systems.
On the flipside, once you know those numbers, you could also calculate what the expected analog equivalents could be, sample existing analog media/playback systems and use those to systematically characterize their quality, instead of going by earfeel, as it were.
I wanted to create an mp3 player (not just wav) using a pico and found this repository very instructive: https://github.com/ikjordan/picosounds
To handle decoding and the overhead of moving bits around, it uses a dual-buffer DMA approach. It reads data from an SD card, running the decoding routine on the main CPU. The decoded samples are written into one of two buffers. While one buffer is being written to, the other is read via DMA and sent to the PWM buffer (more on PWM audio here: https://x.com/beala/status/1826147833168408738). Because DMA is used, data transfer can occur in parallel with decoding, without involving the main processor. When the bits have all been written to the PWM buffer, an interrupt triggers a buffer swap, and the process repeats.
I'm leaving out many details. Tuning the pwm speed to achieve correct playback speed was quite an adventure. And when I needed to increase the clock speed to play higher sample rates, this caused I/O errors with the SD card that I never managed to solve. Although I learned a lot from this process, I ultimately opted to use an MP3 module in my final build: https://store-usa.arduino.cc/products/dfplayer-a-mini-mp3-pl...
This is probably routine for an experienced embedded engineer, but it was quite a learning experience for me!
> And when I needed to increase the clock speed to play higher sample rates, this caused I/O errors with the SD card that I never managed to solve.
Presumably if a RP2040 could almost do it, a RP2350 could do it no problem. Unless you were limited by PWM speed. Even then you might be able to accept less resolution and filter and dither.
I've got music-player on my short list of projects as well. I anticipate using a Teensy though. I'm pretty sure there are good libraries to handle everything you would need.
I feel like the flaws of the Pico 2/RP2350 should be advertised better. The Pico is great. I waisted half of an extremely frustrating day with a Pico 2 before suspecting the board itself was the problem, and confirming it with very specific searches that brought up threads about the issue. The internal pull-downs don't work.
Maybe it's my fault for not making it to page 1357 (!) of the datasheet, where the issue is described as "RP2350-E9".
https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.p...
What, "Increased leakage current on Bank 0 GPIO when pad input is enabled"? How does that relate to the pulldowns?
> The internal pull-downs don't work.
They don't work at all? How the heck did something that important get past testing?
Guess I'm not moving on from the RP2040 anytime soon...
Yeah, it's an A0 flaw. Word on the streets is that it was from modifying the pads to be 5V tolerant.
This is unfortunately not emphasized with many breakout boards. It pays off to skim errata sections at the start of a project. All hardware has errata, and it ranges from incorrect details and minor malfunctions all the way through to broken peripherals and all manner of critical malfunctions.
To be clear, this isn't a problem with the breakout board. The issue is inside the microcontroller itself, which, iiuc, is why there is such a long lead time on fixing it.
While I agree that reading the errata absolutely should be on the "mandatory TODO before you start" list for any project (esp with new/unfamiliar hardware), one should be equally aware that manufacturers aren't always transparent about the defects in their products, sometimes failing to note that entire subsystems, frankly, don't work.
Something is just not right in the Raspberry Pi design team if at this point ESP32 have less unpleasant surprises. New RP micros always turn out have disappointing bugs that make them just shy of considering using for actual embedded projects.
The RP2040 can output 24 bit (or technically 32-bit), 48kHz stereo audio over I2S. Combine that with a DAC like the PCM1502 (which has plenty of dev boards) and you've got yourself a high quality audio setup.
I have a Rust crate for outputting I2S data, or retrieving it from something like a mic or ADC. You can hook it up to DMA and make your audio go brrrr
https://github.com/bschwind/rp2040-i2s
I should probably also link an example usage, playing noise to help with sleeping:
https://github.com/bschwind/sleep-machine/blob/main/src/main...
I once built a player with a MAX98357 I2S Amp that could drive a small speaker, it was fun.
I think schematics are easier to read than looking at a (fritzing?) breadboard layout. Discrete resistors aren't necessary for the buttons if you enable the internal pullups on the RP2040.
This. Also still waiting for AI to be able to read schematics, its going to make my life so much easier.
I'm fairly sure it can read them now.
I did a lot of research for building my own little iPod Nano sized player, which should also have the ability to be controlled by a wired headphone remote.
So here are my results up until now:
- The best device I found is a LicheeRV Nano[1] - also used in NanoKVM.
- It has enough CPU+RAM to support more complex formats like FLAC while still being very small.
- It has a full featured MIPI 31-pin / 6-pin touch display port, although I could not find any available display smaller than 7" to order without hassle (there is an unavailable official 3" though[4]).
- It also has USB-C, where it should be possible to use one of these external USB-C to audiojack adapters with mic support.
- Some of these adapters should support headset control buttons out of the box (via kernel usbsound) - on my notebook this works flawlessly.
- There also is a full featured configurable buildroot[2], so, less work to get started with an optimized build.
- Combining a TP4057 battery charger and a battery gauge (MAX17043) you should be able to support a 1200mah battery, read out battery status via I2C and recharge within a < 3" 3D printed case.
- All you would have to do is create a custom buildroot and a nice user interface via LVGL, they already have a music player sample on there samples listing[3]. Unfortunately this is where the workload is huge ;)
1: https://wiki.sipeed.com/hardware/en/lichee/RV_Nano/1_intro.h...
2: https://github.com/sipeed/LicheeRV-Nano-Build
3: https://github.com/lvgl/lv_demos/tree/master/src/lv_demo_mus...
4: https://aliexpress.com/item/1005006519668532.html
Due to the missing DAC on the Pico this always runs into that fully discrete vs. resistor ladder/network vs. external dac (i2s or other) decision.
Great that the author chose one and finished (and published) the project instead of stopping at that annoying junction :)
For headphone audio it's plenty to just PWM, inductance of the headphones and mechanical low pass characteristic of the speaker itself will mostly do the rest.
If you want to smooth the things out because you target a line input, simple LC filter will work just fine here.
Like this: https://shorturl.at/z3eYG (link to Falstad's Analog Filter Tool)
If you want to get better bit depth, you should use PDM instead of PWM.
Indeed. fast PWM is a great approach and fits in well with the analog conversion side effects.
I still find the principal question of "if I need additional components anyway, should I just get the more complex ones" interesting. It's located somewhere between quality, price, the definition of simple and optimal, as well as one of ones personal (or this week's) perspective on the art of the craft.
The projects is not a discrete player that basically pushes SD card data via a few digital standard ICs out as PWM, it uses a pretty sophisticated MCU after all. So, is a I2S DAC the logical solution or the fast PWM, I still find it hard to decide, especially when i have to order (even the simple or mechanical) parts anyway, or should I have a few I2S DACs in the drawer with the Pico these days.
Not sure if I can bring that point across. It's more about what are your default parts these days. I always have a few i2c OLED displays in that drawer, and the project uses one, too, thirty years ago it might have been resistors, LEDs and a few logic ICs instead. So I wonder, would I go discrete (or "simple") for that particular part of the solution for a particular reason or not. And also, why do I still have no I2S DACs stocked in that drawer, while I have display, sensors etc.
So maybe it's just about audio being a somehow ignored interface in (my) embedded tinkering. Or it's more general and about a modernized selection of stocked parts and using those more naturally while still being aware of the simpler solutions. I use pretty complex (but inexpensive) I2C sensors these days, after all.
Ok, many words on that small part of the overall decision making...
Last week YouTube recommended a video about a Logic Analyzer using the Pi Pico. Had no idea it was that powerful.
Plug for Shareplay, which turns a raspberry pi into an airplay server so you can connect non-Airplay speakers:
https://github.com/mikebrady/shairport-sync
Not on a Pico though.
You could connect airplay to snapcast[0] then use an embedded snapcast player, like mine[1], I do this on an ESP32 with 320KiB of SRAM
[0]: https://github.com/badaix/snapcast
[1]: https://github.com/davidventura/esp-snapcast
If you want to do a similar audio player I can recommend the DY-SV5W module family, this is essentially a micro-SD playback chip with a headphone amp and a 5W (mono) amplifier, the whole thing can be powered from 5V/USB. Really great for art projects where you just need a single track to play/loop on the press of a button and can't be bothered to program.
It has thre DIP switches that set the mode, you can use them to set the playback modes, one of which is serial control, meaning you can easily beef this up using a microcontroller.
That looks fantastic, thank you!
Thanks for this, wasn't exactly looking for something like this but it fits a bunch of projects I have in mind!
Similarly, the dfplayer mini is fine if you want some sound to scream back at you. Works on button press, but also rx/TX for track management from micro SD card. Small amplifier built in.
I built motion activated playback speakers - good for hiding them in the bushes on halloween.
Am I understanding that this project is limited to play 8kHz samples wave-files, ie only up to 4kHz real world? Is that even tape-quality?
Not to talk down anyone’s hobby project, but that kinda limits the appeal for most other people I would think?
I've got a Pico 2 (2035) playing mono 22kHz MP3 using built-in 12-bit PWM, sounds pretty good. This would probably work on the 2040. However, as this is C code I'm not sure if this ports to MicroPython easily.
That's the (lowest) sample rate on telephone networks, so no. Way worse than tape.
Was giving this thought as well and realized this is more in line with building a “crystal radio” type project than aiming for a marketable product. It’s a case study of a learning process, which is pretty spiffy to me.
Analog tape can sound pretty damn good, depending on the total surface area used and how fast it moves past the record/play heads.
Isn't there some way you could calculate the signal density you'd need in any of PDM/PWM/PCM to roughly equate to the quality of signal reproduction of a given tape media, given known tape surface/speed/magnetic density?
It turns out there is https://www.electricity-magnetism.org/magnetic-storage-devic... -- its a bit more complicated than what I'm more familiar with, which is film-grain-to-digital-equivalent resolution.
Anyway, you could do the work to work out the bit depth/signal rate you'd need to equate to reproduction of a given fluxdensity/recording rate. I would bet the numbers for classic PCM/PDM devices line up surprisingly close to certain tape systems.
On the flipside, once you know those numbers, you could also calculate what the expected analog equivalents could be, sample existing analog media/playback systems and use those to systematically characterize their quality, instead of going by earfeel, as it were.
[dead]