c++clinuxalsa

ALSA in C++ - Making the minimal working code


Topic

I would like to make the minimal working code to generate any kind of PCM sound using ALSA in C++ for a Linux computer.

Setup

I'm coding in C++ on Code::Blocks with Ubuntu 20.04.

Background

I used to make simple Arduino UNO programs doing sound processing and just needed to play raw PCM samples.

Issues

ALSA Project's Website is not very easy to understand.

I looked at c - ALSA tutorial required to find out that many links are expired.

I copy pasted the code of the minimal PCM example in C directly into a empty Code::Blocks project and I got that error:

||=== Build: Release in Test (compiler: GNU GCC Compiler) ===|
Test/main.cpp|5|fatal error: ../include/asoundlib.h: No such file or directory|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

Right in the first line of code which is #include "../include/asoundlib.h". I'm guessing that the issue could be because I have to download something or add a linker for the compiler.
But I also think it may be an issue of C to C++ conversion meaning that this works in C but not in C++.

Do I have to add a linker for the compiler or download something to make the code working?

Then I looked on ALSA library and downloaded alsa-lib-1.2.3.tar.bz2.
I got an archive that looked to have the right things but I don't know how to handle it.

Then I found usr/include/sound/asound.h on my computer. It looks to be part of ALSA but when I changed the code to use it, it spat out a bunch of errors when used.
The code looks like following now:

/*
 *  This extra small demo sends a random samples to your speakers.
 */
#include <sound/asound.h>
#include <cstdio>
static char *device = "default";            /* playback device */
unsigned char buffer[16*1024];              /* some random data */
int main(void)
{
    int err;
    unsigned int i;
    snd_pcm_t *handle;
    snd_pcm_sframes_t frames;
    for (i = 0; i < sizeof(buffer); i++)
        buffer[i] = random() & 0xff;
    if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
        printf("Playback open error: %s\n", snd_strerror(err));
        exit(EXIT_FAILURE);
    }
    if ((err = snd_pcm_set_params(handle,
                      SND_PCM_FORMAT_U8,
                      SND_PCM_ACCESS_RW_INTERLEAVED,
                      1,
                      48000,
                      1,
                      500000)) < 0) {   /* 0.5sec */
        printf("Playback open error: %s\n", snd_strerror(err));
        exit(EXIT_FAILURE);
    }
        for (i = 0; i < 16; i++) {
        frames = snd_pcm_writei(handle, buffer, sizeof(buffer));
        if (frames < 0)
            frames = snd_pcm_recover(handle, frames, 0);
        if (frames < 0) {
            printf("snd_pcm_writei failed: %s\n", snd_strerror(frames));
            break;
        }
        if (frames > 0 && frames < (long)sizeof(buffer))
            printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), frames);
    }
    /* pass the remaining samples, otherwise they're dropped in close */
    err = snd_pcm_drain(handle);
    if (err < 0)
        printf("snd_pcm_drain failed: %s\n", snd_strerror(err));
    snd_pcm_close(handle);
    return 0;
}

And the errors are like that:

||=== Build: Release in Test (compiler: GNU GCC Compiler) ===|
Test/main.cpp|6|warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]|
Test/main.cpp||In function ‘int main()’:|
Test/main.cpp|12|error: ‘snd_pcm_t’ was not declared in this scope; did you mean ‘snd_pcm_info’?|
Test/main.cpp|12|error: ‘handle’ was not declared in this scope|
Test/main.cpp|16|error: ‘SND_PCM_STREAM_PLAYBACK’ was not declared in this scope; did you mean ‘SNDRV_PCM_STREAM_PLAYBACK’?|
Test/main.cpp|16|error: ‘snd_pcm_open’ was not declared in this scope; did you mean ‘snd_pcm_info’?|
Test/main.cpp|17|error: ‘snd_strerror’ was not declared in this scope|
||error: %s\n", snd_strerror(err));|
Test/main.cpp|21|error: ‘SND_PCM_FORMAT_U8’ was not declared in this scope; did you mean ‘SNDRV_PCM_FORMAT_U8’?|
Test/main.cpp|22|error: ‘SND_PCM_ACCESS_RW_INTERLEAVED’ was not declared in this scope; did you mean ‘SNDRV_PCM_ACCESS_RW_INTERLEAVED’?|
Test/main.cpp|20|error: ‘snd_pcm_set_params’ was not declared in this scope; did you mean ‘snd_pcm_sw_params’?|
Test/main.cpp|27|error: ‘snd_strerror’ was not declared in this scope|
||error: %s\n", snd_strerror(err));|
Test/main.cpp|31|error: ‘snd_pcm_writei’ was not declared in this scope|
Test/main.cpp|33|error: ‘snd_pcm_recover’ was not declared in this scope|
Test/main.cpp|35|error: ‘snd_strerror’ was not declared in this scope|
Test/main.cpp|42|error: ‘snd_pcm_drain’ was not declared in this scope|
Test/main.cpp|44|error: ‘snd_strerror’ was not declared in this scope|
Test/main.cpp|45|error: ‘snd_pcm_close’ was not declared in this scope|
||=== Build failed: 17 error(s), 1 warning(s) (0 minute(s), 0 second(s)) ===|


Solution

  • Follow these steps:

    1. Install the ALSA development package, or make sure it is already installed. The name depends on your distribution. In my case (OpenSUSE Tumbleweed) it is alsa-devel. This will install the file asoundlib.h under the directory /usr/include/alsa.
    2. Change the line in your code from #include "../include/asoundlib.h" to #include <alsa/asoundlib.h>. Notice the angular brackets instead of quotation marks.
    3. The library which you want to link against is named libsound.so, so compile the program with a command like gcc -Wall pcm_min.c -lasound -o pcm_min
    4. Run the program: ./pcm_min