I started playing with sound with the Raspberry Pi Pico. I'm using using an LM386 amplifier and an old 8Ω speaker:

Photo of the Pico, the LM386 amplifier and the speaker
Photo of the Pico, the LM386 amplifier and the 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:

Sound output schematic
Sound output schematic

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:

Pico connections schematic
Pico connections schematic

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.