raspberry-piraspberry-pi3android-things

PCM5122 DAC with Android Things


I have a Raspberry Pi 3B and Suptronics X920 Expansion Board which uses PCM5122 DAC. So I'm having trouble playing sounds through that board.

The config file is default except for the display configuration part:

kernel=u-boot-dtok.bin
framebuffer_depth=16

# Prevent the firmware from loading HAT overlays now that we handle pin muxing.
# ourselves. See:
# https://www.raspberrypi.org/documentation/configuration/device-tree.md#part3.4
dtoverlay=

dtparam=i2c_arm=on
dtparam=spi=on
dtparam=audio=on

# pwm and I2S are mutually-exclusive since they share hardware clocks.
dtoverlay=pwm-2chan-with-clk,pin=18,func=2,pin2=13,func2=4
dtoverlay=generic-i2s

start_x=1

# Tell U-boot to always use the "serial0" interface for the console, which is
# set to whichever uart (uart0 or uart1) is set to the header pins. This doesn't
# interfere with the uart selected for Bluetooth.
dtoverlay=chosen-serial0

# Enable skip-init on the UART interfaces, so U-Boot doesn't attempt to
# re-initialize them.
dtoverlay=rpi-uart-skip-init

# Add pin devices to the system for use by the runtime pin configuration driver.
dtoverlay=runtimepinconfig
dtoverlay=uart1
dtoverlay=bcm2710-rpi-3-b-spi0-pin-reorder

# Tell the I2S driver to use the cprman clock.
dtoverlay=bcm2710-rpi-3-b-i2s-use-cprman

# Uncomment to disable serial port on headers, use GPIO14 and GPIO15
# as gpios and to allow the core_freq to change at runtime.
enable_uart=1
core_freq=400

# Support official RPi display.
dtoverlay=i2c-rtc,ds3231
dtoverlay=rpi-ft5406
hdmi_force_hotplug=1

# Set framebuffer to support RGBA colors.
framebuffer_swap=0

# Waveshare display settings
max_usb_current=1
hdmi_group=2
hdmi_mode=87
hdmi_cvt 1024 600 60 6 0 0 0
hdmi_drive=1

This is the code for playing a sound file:

fun playSound(file: File) {
    val audioEncoding = AudioFormat.ENCODING_PCM_16BIT
    val sampleRate = 16000

    val audioOutputFormat = AudioFormat.Builder()
            .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
            .setEncoding(audioEncoding)
            .setSampleRate(16000)
            .build()

    val audioOutputBufferSize = AudioTrack.getMinBufferSize(sampleRate, audioOutputFormat.channelMask, audioEncoding)

    val audioOutputDevice = findAudioDevice(AudioManager.GET_DEVICES_OUTPUTS, AudioDeviceInfo.TYPE_BUS)

    val audioTrack = AudioTrack.Builder()
            .setAudioFormat(audioOutputFormat)
            .setBufferSizeInBytes(audioOutputBufferSize)
            .setTransferMode(AudioTrack.MODE_STREAM)
            .build()

    audioTrack.preferredDevice = audioOutputDevice

    val buffer = ByteArray(audioOutputBufferSize)

    audioTrack.play()
    audioTrack.setVolume(1f)

    val stream = file.inputStream().buffered()
    try {
        while (stream.read(buffer) > 0) {
            val out = audioTrack.write(buffer, 0, buffer.size, AudioTrack.WRITE_BLOCKING)
            d { "audioTrack.write = $out" }
        }
    } catch (error: Throwable) {
        e(error) { "Error playing audio $file" }
    } finally {
        stream.close()
    }

    audioTrack.stop()
    audioTrack.release()
}

private fun findAudioDevice(deviceFlag: Int, deviceType: Int): AudioDeviceInfo? {
    val manager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
    val adis = manager.getDevices(deviceFlag)
    for (adi in adis) {
        if (adi.type == deviceType) {
            return adi
        }
    }
    return null
}

I've tested the code with a regular Raspberry Pi audio output (which is AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) and it works ok. But with AudioDeviceInfo.TYPE_BUS it just produces no sound without any errors.

I tried various config options like dtoverlay=hifiberry or dtoverlay=hifiberry-dacplus with no luck.

Please help.


Solution

  • It looks like you might be using some of the code for the Google Assistant sample, and you are correct to assume that TYPE_BUS is what you need to enable the audio routes to use the I2S bus instead of the built-in audio jack.

    However, that is likely not the whole story. The DAC likely requires additional configuration commands and/or external triggers. Looking at a similar HAT with the same DAC, for example, there is an I2C bus connection as well for DAC setup commands. Our Assistant sample uses the VoiceHAT driver to accomplish the additional triggering required by the DAC on that peripheral.

    In Raspbian, the driver you enable via dtoverlay likely takes care of both pieces. Here, your code will need to manage the setup bits manually. Look at how the VoiceHAT driver is used in the Assistant sample as an example of this.

    Also, make sure you are not enabling any of the I2S pins as either GPIO or PWM, as this will disable the audio route per the documentation.

    Side Note: Android Things does not support making kernel changes via config.txt, so adding drivers there is expected not to have any effect.