Reputation: 323
I would like to make the minimal working code to generate any kind of PCM sound using ALSA
in C++
for a Linux
computer.
I'm coding in C++
on Code::Blocks
with Ubuntu 20.04
.
I used to make simple Arduino UNO
programs doing sound processing and just needed to play raw PCM samples.
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)) ===|
Upvotes: 0
Views: 6847
Reputation: 1
Then I found
usr/include/sound/asound.h
on my computer. It looks to be part of ALSA
This header isn't part of alsa-lib, it's part of the kernel.
I would like to make the minimal working code to generate any kind of PCM sound using ALSA in C++ for a Linux computer.
If it is only PCM you're after, then you can forget about asoundlib altogether. Give a try to https://gitlab.com/bztsrc/nanoalsa This is a single header library (just a header, no library needed). It also does not need alsa headers or anything else, just "sound/asound.h" for the ioctl structs and enums.
Using nanoalsa is as simple as:
#define ALSA_IMPLEMENTATION /* get the single header library */
#include "nanoalsa.h"
alsa_t ctx; /* internal state (few bytes only) */
alsa_open(&ctx, 0, 0, SNDRV_PCM_FORMAT_S16_LE, 44100, 2); /* specify requested characteristics */
alsa_write(&ctx, buf, numframes); /* play the audio */
alsa_close(&ctx); /* free resources */
The only con is, it only supports sample formats that your sound card supports, it does not convert samples. (On standard AC97 chips it is likely that the format you've tried, SNDRV_PCM_FORMAT_U8
mono channel will fail, you should use SNDRV_PCM_FORMAT_S16
stereo instead.)
Cheers,
bzt
Upvotes: 0
Reputation: 517
Follow these steps:
#include "../include/asoundlib.h"
to #include <alsa/asoundlib.h>
. Notice the angular brackets instead of quotation marks.gcc -Wall pcm_min.c -lasound -o pcm_min
./pcm_min
Upvotes: 2