I started playing with sound with the Raspberry Pi Pico. I'm using using an LM386 amplifier and an old 8Ω speaker:
For the sound generation code, I'm using the PWM/DMA method described in this blog post. It uses PWM sounds with 8-bit samples at 22050 Hz. I wrote a simple mixer on top of that to play multiple sounds at the same time, and an easy-to-use API for starting sounds with the option to loop, since I'll add this to the Pico Loser Corps port.
This is the schematic for the sound output:
I'm using a voltage divider with 2.2K/1K to bring the sound signal in the range 0-1V in with a 100nF capacitor in parallel to filter the unwanted high frequencies, connecting the result to the input of the LM386 using a potentiometer for volume control. The rest of the schematic was taken from the "typical application" section of the LM386 datasheet. Leaving its pins 1, 7 and 8 not connected results in a gain of 20, which is enough for my tests.
I had to change some resistors and capacitors because I didn't have
the recommended values available. The 75Ω connected to the SOUND_PIN
was meant to be a 68Ω, and the 100nF capacitor on the LM386 output was
meant to be 50nF.
I also decided to use a 1000μF capacitor on the LM386 output instead of the 250μF recommended by the datasheet to try to improve the bass. I don't think it matters too much with that crappy speaker, but it doesn't hurt.
The overall sound quality is not that great: the sample size is 8 bits, and the whole thing is mounted on a breadbord. But it's usable for now, and probably good enough for the game sound effects.
The code keeps playing a synth sound on loop, and queues a small drum sound when you press the button. For completeness, here's the schematic showing the Pico connections, including the button that triggers the drum sound:
The code is set up to mix up to 4 sounds, so if you press the button too quickly it will start ignoring presses until there are less than 4 sound playing at the same time.
The sound generation code is in audio.c
and audio.h
, this is the
API:
// init audio_init(SOUND_PIN); // play once (use the returned sound_id to control volume or stop) int sound_id = audio_play_once(samples, num_samples); // set audio volume (8.8 fixed point) audio_source_set_volume(sound_id, 3<<8); // play in loop (3rd argument says where to start the loop) sound_id = audio_play_loop(samples, num_samples, 0); // stop playing audio_source_stop(sound_id);
The mixer must be manually called at least once every 45 milliseconds
or so (this interval depends on the sound buffer size configured in
audio.h
; a larger buffer allows for more time between calls at the
expense of more latency for starting sounds):
while (1) { audio_mixer_step(); // ... other code ... sleep_ms(30); }
As usual, the code is on Github.