dwhinham / mt32-pi

๐ŸŽน๐ŸŽถ A baremetal kernel that turns your Raspberry Pi 3 or later into a Roland MT-32 emulator and SoundFont synthesizer based on Circle, Munt, and FluidSynth.
https://twitter.com/d0pefish
GNU General Public License v3.0
1.28k stars 81 forks source link

๐Ÿ› [BUG] No sound via Adafruit I2S Audio Bonnet (UDA1334A) #233

Closed htamas2 closed 2 years ago

htamas2 commented 2 years ago

Hardware and software

Bug description

If I set output_device to hdmi, everything works: I can play on a usb midi keyboard or via rtp-midi, using either fluidsynth or munt. However, if I set output_device to i2s, there is no sound output.

The same configuration is supported by Circle out of the box. It works flawlessly in circle/sample/29-miniorgan or in minisynth. If I write a minimal kernel with a class that extends CI2SSoundBaseDevice and overrides GetChunk(u32*, unsigned) to output a square wave, I can hear the sound. Incidentally it also works in Raspbian.

Steps to reproduce

  1. Install mt32-pi v0.11.0 to a fresh sd card (or build from git HEAD)
  2. Update mt32-pi.cfg with output_device = i2s, default_synth = soundfont
  3. Boot the pi from the sd card
  4. Play notes on the midi keyboard, hear nothing

Expected behavior

There should be sound.

Configuration file (changes from default)

[system]
verbose = on
default_synth = soundfont

[audio]
output_device = i2s

[network]
mode = wifi
ftp = on
ftp_password = ********

Additional information

In CMT32Pi::AudioTask(), IntBuffer still contains valid sample data which is then sent to CSoundBaseDevice::Write(const void *, size_t). Compare this to the working Circle demo code that doesn't call CSoundBaseDevice::Write but overrides CI2SSoundBaseDevice::GetChunk to fill the buffer on an IRQ. I wonder if that makes any difference or is just a consequence of mt32-pi being multithreaded.

dwhinham commented 2 years ago

The GetChunk() versus Write() stuff shouldn't affect anything at all, it just determines when the audio data is passed to the driver (interrupt context versus not). Munt/Fluid's rendering is too intensive to run inside an interrupt without wrecking performance/timing of other tasks, hence why GetChunk() is not used.

I have other UDA1334A DACs that are working fine here, so there's not much to suggest other than:

htamas2 commented 2 years ago

Thanks for the quick reply. You are right, the mute pin was the culprit. When probing for the Pisound in CMT32Pi::Initialize, pin 16 (used for Pisound's oversampling ratio) is driven low which mutes the sound.

dwhinham commented 2 years ago

Thankyou for testing, and great catch with the Pisound probing - I'd forgotten about this device.

I've attempted a fix, which you can test by grabbing the kernels package from this build run and just replacing the kernels: https://github.com/dwhinham/mt32-pi/actions/runs/1723643561

I've basically just added a destructor to the Pisound driver so that when it's deleted, the affected GPIO pins are reset back to being inputs in pull-down mode (as they should be on power-on, according to Broadcom docs).

If you can test this when you get a chance I'll merge it for the next release.

Thanks!

htamas2 commented 2 years ago

I can confirm it works great. Thank you.

dwhinham commented 2 years ago

Excellent, thanks for the quick test and again for the report. This will probably improve compatibility with some other DAC accessories that have a mute pin too.

This fix is now on the develop branch and will be part of the next release.