Search code examples
clinuxaudioalsalibsndfile

Dependency Issues with ALSA (libao and sndfile)


I have written a small program to play PCM audio files using a reference from github with slight modifications for my application. The program is compiling fine, but I am having a few runtime issues regarding dependencies and ALSA devices.

The errors I am receiving are:

ERROR: Failed to load plugin /usr/lib/x86_64-linux-gnu/ao/plugins-4/libsndio.so => dlopen() failed
ERROR: Failed to load plugin /usr/lib/x86_64-linux-gnu/ao/plugins-4/libnas.so => dlopen() failed
ao_alsa WARNING: Unable to open surround playback.  Trying default device...
ALSA lib pcm_dmix.c:1089:(snd_pcm_dmix_open) unable to open slave
ao_alsa ERROR: Unable to open ALSA device 'default' for playback => No such file or directory

I have been researching many different solutions and none seem to be working, including adding a line to the alsa.conf file in /etc/modprobe.d (however, these solutions suggest adding a line in reference to intel when I am using an AMD setup.

Here is the full code:

/*
 *
 * ao_example.c
 *
 *     Written by Stan Seibert - July 2001
 *
 * Legal Terms:
 *
 *     This source file is released into the public domain.  It is
 *     distributed without any warranty; without even the implied
 *     warranty * of merchantability or fitness for a particular
 *     purpose.
 *
 * Function:
 *
 *     This program opens the default driver and plays a 440 Hz tone for
 *     one second.
 *
 * Compilation command line (for Linux systems):
 *
 *     gcc -o ao_example ao_example.c -lao -ldl -lm -lsndfile
 *
 */

#include <stdio.h>
#include <string.h>
#include <ao/ao.h>
#include <math.h>
#include <sndfile.h>

#define BUF_SIZE 4096

static void clean(ao_device *device, SNDFILE *file){
    ao_close(device);
    sf_close(file);
    ao_shutdown();
}

int main(int argc, char **argv)
{
    ao_device *device;
    ao_sample_format format;
    unsigned long count;
    int default_driver;
    short *buffer;
    int buf_size;
    int sample;
    int i;
    //FILE *fp;
    SF_INFO sfinfo;

    if (argc != 2){
        printf("usage: %s <filename>\n", argv[0]);
        exit(1);
    }

    /* -- Initialize -- */

    fprintf(stderr, "libao example program\n");

    SNDFILE *fp = sf_open(argv[1], SFM_READ, &sfinfo);

    // fp = fopen(argv[1], "rb");
    if (fp == NULL){
        printf("Cannot open %s.\n", argv[1]);
        exit(1);
    }

    printf("samples: %d\n", sfinfo.frames);
    printf("sample rate: %d\n", sfinfo.samplerate);
    printf("Channels: %d\n", sfinfo.channels);

    ao_initialize();

    /* -- Setup for default driver -- */

    default_driver = ao_default_driver_id();

    switch(sfinfo.format & SF_FORMAT_SUBMASK){
        case SF_FORMAT_PCM_16:
            format.bits = 16;
            break;
        case SF_FORMAT_PCM_24:
            format.bits = 24;
            break;
        case SF_FORMAT_PCM_32:
            format.bits = 32;
            break;
        case SF_FORMAT_PCM_S8:
            format.bits = 8;
            break;
        case SF_FORMAT_PCM_U8:
            format.bits = 8;
            break;
        default:
            format.bits = 24;
            break;
    }

    format.channels = sfinfo.channels;
    format.rate = sfinfo.samplerate;
    format.byte_format = AO_FMT_LITTLE;
    // format.byte_format = AO_FMT_NATIVE;
    format.matrix = 0;

    // memset(&format, 0, sizeof(format));
    // format.bits = 24;
    // format.channels = 16;
    // format.rate = 48000;
    // format.byte_format = AO_FMT_LITTLE;

    /* -- Open driver -- */
    device = ao_open_live(default_driver, &format, NULL /* no options */);

    if (device == NULL) {
        fprintf(stderr, "Error opening device.\n");
        return 1;
    }
    
    // fseek(fp, 0, SEEK_END);
    // count = ftell(fp);
    // fseek(fp, 0, SEEK_SET);

    // // printf("count: %ld\n", count);

    buffer = calloc(BUF_SIZE, sizeof(short));

    while(1){
        int read = sf_read_short(fp, buffer, BUF_SIZE);

        if (ao_play(device, (char *) buffer, (uint_32)(read * sizeof(short))) == 0){
            printf("ao_play: failed\n");
            clean(device, fp);
            break;
        }
    }

    clean(device, fp);

  return 0;
}

I hope someone else has had the same trouble and can shed light on the solution. Thank you.


Solution

  • To summarise the discussion:

    The problem does not seem to lie within the code per se but in the libao configuration.

    According to the libao documentation, the library tries to determine a default driver as follows:

    In the absence of configuration files to explicit identify a default driver, the library will try to detect a suitable default driver. It does this by testing every available live output driver (using ao_plugin_test()) and finding the driver with the highest priority (see the ao_info struct) that works. Drivers with priority 0, such as the null and file output drivers, are never selected as the default.

    The error messages indicate that several drivers including nas, sndio and alsa are tried. Choosing the driver manually with ao_driver_id(...) instead of using ao_default_driver_id() fixes the issue.

    Further problems with opening the device with ao_open_live(...) can be investigated by getting the corresponding error number with printf("errno %d\n", errno);. The output can be interpreted as follows:

    AO_ENODRIVER (1) - No driver corresponds to driver_id.
    AO_ENOTLIVE (3) - This driver is not a live output device.
    AO_EBADOPTION (4) - A valid option key has an invalid value.
    AO_EOPENDEVICE (5) - Cannot open the device (for example, if /dev/dsp cannot be opened for writing).
    AO_EFAIL (100) - Any other cause of failure.
    

    Besides that debugging can be enabled in the ~/.libao configuration file to get further information.