Pico 2 W running the Audio Library

ghostintranslation

Well-known member
I wanted to use the Pico 2 for music, when there isn't need for as much power as Teensy 4 can provide. So after some minor edits to the Audio lib classes that I needed, here it is:


In this video I am sending MIDI over USB, and the sketch is running a 16 voices polyphonic synth, there are a few mixers and each voice has a sine and an envelope. So quite a few objects but I'm sure it can handle even more. The audio output is I2S sent to a PCM5100A (I'm using the audio expander from my Motherboard project) handled with the Pico's library, which I have some troubles with at the moment and is cracking occasionally, there might be a better way to do it.

Anyway, I'm not aware of others having done that yet so I thought I'd share. I'm thinking of pushing it to GitHub sometime soon.
 
Would love to see the code at some point :) Are you using both cores to get 16-note polyphony? And does the Pico 2 have the same issue as the original Pico, that you can't hit exactly 44.1kHz?

Not entirely sure how the Cortex M33 compares with the M7, but for the same cost as a Teensy 4.1 I could get 6 Pico 2 boards, so 12 CPUs each running at 150MHz ... and over 3MB of RAM. It all starts to look very interesting :cool:
 
I'll publish the code as soon as I have time to clean up my project because I initially started making my own half baked library before switching to this and I need to separate the 2.

But to answer the questions, in this demo I'm using only one core, and I have AUDIO_SAMPLE_RATE and AUDIO_SAMPLE_RATE_EXACT at 48000 and AUDIO_BLOCK_SAMPLES at 256.

I only use a few classes/files also so I only edited those, it's not the entire library that I reworked, yet. Modifications are minor really, removed conditionals for Teensy LC for example or some keywords that wouldn't work on Pico, and changed the timer that calls all the ojects update methods at AUDIO_SAMPLE_RATE / AUDIO_BLOCK_SAMPLES period...

Yes price is also a motivation.

And Pico 2 W features Wifi with ability to do an access point and serve a web page to configure any of the synth settings for example. I'm not expecting to run audio at the same time as Wifi though but opens up a few possibilities still.
 
Sounds good (see what I did there?!), looking forward to seeing the code. It’d be interesting to come up with a hacky Python tool to massage the Teensy version to the Pico one, for full coverage and maintenance purposes.

Ah yes, 48kHz makes sense. Presumably the change to AUDIO_BLOCK_SAMPLES is to reduce overhead? It’d be interesting to have a like-for-like comparison of CPU load, to see how much difference the Teensy’s speed, cache, TCM etc. make.

The second core is a wonderful thing … you could run audio on one and WiFi on the other, for example.

Did you solve the “occasional cracking” issue? I had one such recently, needed an extra 100R on MCLK near the Teensy, the one on the audio adaptor was too far away.
 
The cracking in this case is caused by the I2S library, or rather how I use it probably, it's not a hardware thing, and it's not also wrong signals sent to the output. I know it's not hardware because I'm using the same PCB I'm using for my Motherboard and Drone module which work fine. And I know it's not wrong signals because I could hear it even by sending only "1" as samples values in place of the objects signals.

I'm not sure that I'm doing the best with even using the I2S library at all, maybe there is a better way to do I2S than this. I could reduce it on that video already compared to my original test by increasing the buffers, which might imply the sending is maybe blocking too long or taking too much time I'm not sure.
 
Not sure I'm fully following what you've eliminated :unsure:

Have to assume you're right about your hardware, as long as you're happy it works with the wire lengths - looking back at your video, they're not a lot shorter than the ones that gave me issues...

So one has to assume it is somehow "wrong signals", e.g. inconsistent MCLK / BCLK / LRCLK, or DMA feeding the DOUT not keeping up. But I don't understand your process for "sending only '1' as samples values", so it's hard to comment. Presumably you have some hybrid of the Teensy AudioOutputI2S and the Pico I²S library? Do you have an oscilloscope you can use to look at the signals?
 
Yes I am not using the Teensy's AudioOutputI2S because I2S is totally different for the Pico.

By '1' I meant that I only sent samples of values 1 in place of actual signals coming from the sines and envelopes, to eliminate anything related to the audio objects. So with just the I2S and the hardware timer I use to call the updates I could still hear the clicks.

Might be more clear once I publish the code later this week hopefully.
 
So with just the I2S and the hardware timer I use to call the updates I could still hear the clicks.

Might be more clear once I publish the code later this week hopefully.
That's probably something to do with it - Teensy paces the I²S directly from the SAI hardware (2 interrupts to half-fill the DMA buffer per audio update; every other interrupt triggers the audio update). If the Pico is using a separate timer then I guess it's possible things get out of sync if somehow the timebases are running at fractionally different speeds.

I'll wait for the code - I've got some hardware here I can spin up fairly quickly :)
 
Well it didn't take me long so here it is:

You should be able to place it in your Arduino/libraries folder and then open the example under File/examples/pico-audio/PolySynth
To run the example you need to select the Pico 2 W board (I only tried with this one) and USB stack Adafruit TinyUSB, then send MIDI over USB to play the synth.

In the code you'll see:
  • AudioOutputI2S.h is where my implementation of the I2S object is. If you want to experience more cracking you can comment i2s.setBuffers in this file.
  • AudioStream.cpp line 421 is where I set a hardware timer to orchestrate everything.
 
In my IDE, MIDI.h goes to:

@file MIDI.h
* Project Arduino MIDI Library
* @brief MIDI Library for the Arduino
* @author Francois Best, lathoub
* @date 24/02/11

which indeed you need actally
 
Found it, thanks: here's how to find it in the Library Manager, for the benefit of the Future Reader
1741193785098.png


...so now the problems are:
  • arm_math.h o_O I tried just copying the Teensy4 one from cores, which then needs core_cmInstr.h, which then clashes with a bunch of stuff from the Pico libraries - clearly not what you did
  • an error message which looks unrelated:
    Code:
    E:\<redacted>\Arduino\libraries\pico-audio\examples\PolySynth\PolySynth.ino: In function 'void setup()':
    PolySynth:77:22: error: 'class Adafruit_USBD_Device' has no member named 'isInitialized'
       77 |   if (!TinyUSBDevice.isInitialized()) {
          |                      ^~~~~~~~~~~~~
Any thoughts?
 
ok I forgot to push files I had in my original test. Added a arm_math folder. I can't remember exactly if it was any special I think it is just the official library that I downloaded.
 
Right, after a bit of a tussle, I'm now officially beating myself about the head for being a bit of an idiot :eek:
  • it's kind of important to set the board to a Pico 2, not an old-fashioned RP2040-based Pico
  • I was using an old version of the Arduino Pico package (3.6.2); updating to 4.4.4 makes things way better
Looks like you only need to put the header files in the arm_math folder, which saves a ton of compile time; those headers need to be moved out of the libraries/pico-audio/arm_math/src folder into libraries/arm_math.

It compiles OK for a Pico 2 (no WiFi) ... which I don't have, so I've ordered some. There will now be a short pause while Digikey do their thing.

I'll be back...
 
Yes Pico 2 is the 2350, I'm not sure how much work it would require for the 2040, but also if it works fine like that on 2350 it's fine to not support 2040. The Pico 2 W will work for sure, it's the same chip + an extra chip for the Wifi.

Good to know about the Arduino Pico version, something to add to the Readme.

Oh I just noticed, I actually had also Arduino_CMSIS-DSP library installed which is that <arm_math.h>. I'm not sure what's the best approach for that, to leave external dependencies to be installed separately or copy them... probably easier when we have them all in the repo. It's not as easy to manage as with Composer for Php or Npm for JS.

Are you saying that now you can play the PolySynth example alright?
 
I think the RP2040 doesn’t have the DSP instructions that the RP2350 does; it might be as simple as fixing up the conditional compilation to make it look like a Teensy LC or 3.x, but not sure. Not going there right now!

Looks like the math library is in the Pi Pico board toolchain, but not the header files. Or maybe it isn’t, but the minimal port so far hasn’t needed more than the headers. Using filters or FFT might give a link error. Again, TBD…

Can’t play it until I get my Pico 2 :( But I can prepare for its arrival, and getting your demo compiled is a big part of that. I have a couple of ideas on how to fix the cracking, but not 100% sure so will report (either way) when I’ve given at least one of them a go. And do you a PR if successful, of course.
 
I can see why you thought that, it was a bit ambiguous…

Just finished wiring a cheapo PCM5102 breakout, will test with a Teensy 4 tomorrow (getting late here in the UK). First time using these, but I’ve done my research on the jumper gotchas so should work. Then I’ll be fairly confident that’s not a weak link when the Pico 2 arrives.
 
If that can help here is the schematics of the board I'm using with the PCM5100A:

it has also a TL072 op amp that you can ignore because it's to amplify to Eurorack levels.

and on the Pico I forgot to mention, default pins are 20, 21, 22 but you can set any you want as long as pWs = pBCLK+1. And pWS corresponds to LRCK on the DAC and pDOUT goes to DIN on the DAC.
 
OK, I've done a pull request which seems to fix the issue - see what you think. CAUTION! I've removed the *0.1 when transferring data into the I²S transmit buffer, so the output will be much louder.

The basic scheme is to attach a callback to the I²S buffer transfers, trigger a low priority user interrupt, and execute the audio update() in the user ISR. This is very similar to the way the existing Teensyduino library works. It may seem over-complicated, but does mean we're in control of the interrupt priority used for update(), while the timing is still controlled by the hardware.

See what you think - more in the PR comment.
 
It works well for me!

I had reduced the volume because I thought I had saturation, maybe it had to do with how I was doing the I2S and the int26 maybe? But now the volume is loud but clear so it's good too.

One thing I see you commented, the SAMPLE_BITS_DEPTH, which you explained what you did, I just failed to mention that before, I had thought of using constants so that I would be able to switch to 32 bits maybe in place of 16.

Also I just tested and I can set the AUDIO_SAMPLE_RATE to 96000 and it works fine.

So did you receive your Pico 2 or did you coded blindly??

Unless you want to add anything to the PR I'm good to merge it.
 
Oops, sorry, I had to move those constants / macros, it wouldn’t compile otherwise.

Great, glad it’s working for you. I don’t want to add anything else at this point, though no doubt you’ll want to do a bit of tidying up - I tried to keep my changes minimal so you could see what was important.

Yes, got the Pico 2, and had a good idea of where I wanted to make changes, so it was quite quick to implement. Good to hear 96k works too 🥳

I might look at making a Python script to port the Teensy audio objects to Pico, but it probably won’t be soon. Keep us posted how this goes, even if it’s a bit OT in a Teensy forum:eek:
 
No problem I thought those constants needed to be explained, I will probably remove them when I clean up, it was from my initial trial with my own classes and now it would be difficult to use that with Teensy's Audio classes.

Thanks for the work, it's merged.

I think my next step will be to try replicate what my Motherboard does, inputs and outputs etc... and add more of the Audio lib.
 
I think much of the Audio library might work nearly unchanged if it’s provided with macros that make it think it’s being compiled for a Teensy 4.x. You can find a non-comprehensive list at https://github.com/TeensyUser/doc/wiki/Macros.

If that sort of works, I think an RP2040 might work if the macros for a Teensy LC are defined, since both are a Cortex M0+ core.
 
Back
Top