ALSA subdevice from tinyalsa utilities

/ ALSA

As noted on my previous post, tinyalsa code is not capable to playback/capture stream from designated subdevice. Fortunately, tinyalsa source code is easy to extend so that implementing subdevice option is straight forward.

Basics

In tinyalsa API, pcm_open is used instead of snd_pcm_open. To designate subdevice, consumer process need to call certain ioctl to /dev/snd/controlC%u before calling pcm_open. This is minimal code snipped to demonstrate it.

#include <unistd.h>   /* close */
#include <fcntl.h>      /* open */
#include <sys/ioctl.h>  /* ioctl */
#include <sound/asound.h>  /* SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE */

struct pcm *pcm_open_with_subdevice(unsigned int card, unsigned int device,
                            unsigned int subdevice, unsigned int flags, struct pcm_config *config)
{
    char fn[256];
    int fd;

    snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
    fd = open(fn, 0);
    if (fd < 0) {
        fprintf(stderr, "failed to open %s\n", fn);
        return NULL;
    }

    if (ioctl(fd, SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE, &subdevice) < 0) {
        fprintf(stderr, "failed to set subdevice %u\n", subdevice);
        close(fd);
        return NULL;
    }

    pcm = pcm_open(card, device, flags, config);

    return pcm;
}

Note that, if unexisting subdevice is passed to this implementation, this program goes into infinite loop. By default, ALSA kernel side will wait until subdevice become available: pcm_native.c:2848. To avoid this behavior, adding PCM_NONBLOCK to pcm_open flags argument enable us to go through even if subdevice is not existing. In this case, user space program probably need to handle errno to get proper playback/capture.

Download

This is preparation of patch and binary for some platform.

Yocto

tinyalsa-subdevice-2.0.0.zip contains patch and rpm binary package. This patch and binary is created based on Yocto 5.0.14 -- tinyalsa-2.0.0. Compilation of patch is done by adding it to SRC_URI variable of tinyalsa_2.0.0.bb.

root@qemux86-64:~# rpm --reinstall tinyalsa-2.0.0-r0.core2_64.rpm
root@qemux86-64:~# tinyplay --help
usage: tinyplay file.wav [options]
options:
-D | --card   <card number>         The card to receive the audio
-d | --device <device number>       The device to receive the audio
-s | --subdevice <subdevice number> The device to receive the audio
-p | --period-size <size>           The size of the PCM's period
-n | --period-count <count>         The number of PCM periods
-i | --file-type <file-type >       The type of file to read (raw or wav)
-c | --channels <count>             The amount of channels per frame
-r | --rate <rate>                  The amount of frames per second
-b | --bits <bit-count>             The number of bits in one sample
-M | --mmap                         Use memory mapped IO to play audio
root@qemux86-64:~#

Android

tinyalsa-subdevice-android13_r84.zip contains patch and prebuilt binaries. After applying patch to external/tinyalsa on your Android root, compilation will be done by just launching m command. Standalone compilation command is mm tinyplay and mm tinycap. After getting binary, binary is pushed to target via adb push and then it will be used as before.

emulator_car_x86_64:/ $ tinyplay
Usage: tinyplay file.wav [-D card] [-d device] [-s subdevice] [-p period_size] [-n n_periods]

Disclaimer

At this moment, these code is not well tested against real platform.