Alsa lib 32 bits - c

I've been trying to use the ALSA lib for a while and I don't understand how I should use it.
I took an example program and I've tried to modify it to use float (32bits) instead of unsigned char (8bits). But now when I'm running it, I have a segmentation fault in the second loop.
Here is my code :
#include <alsa/asoundlib.h>
snd_pcm_t *create_pcm(const char* name, snd_pcm_stream_t mode, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int nbChannel, unsigned int rate, int softSample, unsigned int latency)
{
int err;
snd_pcm_t *handle;
if ((err = snd_pcm_open(&handle, name, mode, 0)) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_set_params(handle,
format,
access,
nbChannel,
rate,
softSample,
latency)) < 0) { /* 0.5sec */
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
return handle;
}
int main(void)
{
unsigned int i;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
float buffer[16*1024]; /* some random data */
handle = create_pcm("default", // name of the device used by the sound card
SND_PCM_STREAM_PLAYBACK, // to use the device in output
SND_PCM_FORMAT_FLOAT, // use the device with 32bit depth (float)
SND_PCM_ACCESS_RW_INTERLEAVED,
1, // use 1 channel
48000, // use 48000 Hz (dvd quality)
1, // soft resample ON
500000); // 0.5s of latency
// building random data
for(i = 0; i < sizeof(buffer); i++)
buffer[i] = i % 255; // random();
for (i = 0; i < 16; i++) {
frames = snd_pcm_writei(handle, buffer, sizeof(buffer)); // segmentation fault
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);
}
snd_pcm_close(handle);
return 0;
}
How to use this lib with 32bits?
I've tried this format and others like little endian or big endian.. The only one that doesn't crash is SND_PCM_FORMAT_FLOAT but it's making the error :
ALSA lib pcm.c:8507:(snd_pcm_set_params) Sample format not available for PLAYBACK: Invalid argument
Playback open error: Invalid argument
Thanks in advance.
P.S.: Linux, Ubuntu 19.10 64bits

The segmentation fault may already occur when you write into buffer:
for(i = 0; i < sizeof(buffer); i++)
buffer[i] = i % 255; // random();
sizeof(buffer) will give you the size in bytes not the number of elements. They are only equal for char (and unsigned char) since sizeof(char) is 1. You most likely want to iterate over the elements:
for(i = 0; i < sizeof buffer/sizeof *buffer; i++)
buffer[i] = i % 255; // random();

It was indeed a problem of condition in my loop and my snd_pcm_writei()
Here is the code without errors thanks to #Osiris :
#include <alsa/asoundlib.h>
snd_pcm_t *create_pcm(const char* name, snd_pcm_stream_t mode, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int nbChannel, unsigned int rate, int softSample, unsigned int latency)
{
int err;
snd_pcm_t *handle;
if ((err = snd_pcm_open(&handle, name, mode, 0)) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_set_params(handle,
format,
access,
nbChannel,
rate,
softSample,
latency)) < 0) { /* 0.5sec */
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
return handle;
}
int main(void)
{
unsigned int i;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
float buffer[16*1024]; /* some random data */
handle = create_pcm("default", // name of the device used by the sound card
SND_PCM_STREAM_PLAYBACK, // to use the device in output
SND_PCM_FORMAT_FLOAT, // use the device with 32bit depth (float)
SND_PCM_ACCESS_RW_INTERLEAVED,
1, // use 1 channel
48000, // use 48000 Hz (dvd quality)
1, // soft resample ON
500000); // 0.5s of latency
// building random data
for(i = 0; i < sizeof(buffer) / sizeof(*buffer); i++)
buffer[i] = i % 0xffffffff; // random();
for (i = 0; i < 16; i++) {
frames = snd_pcm_writei(handle, buffer, sizeof(buffer) / sizeof(*buffer)); // segmentation fault
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) / sizeof(*buffer)))
printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), frames);
}
snd_pcm_close(handle);
return 0;
}

Related

Alsa plays audio get from CAN FD

I am trying to use Alsa library to reproduce the audio I get from my CAN FD communication, into my headphones. I don't quite understand how to properly configure Alsa's parameters, in order to be able to listen to the sound I get from the CAN FD.
static char *device = "plughw:0,0"; /* playback device */
static snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; /* sample format */
static unsigned int rate = 16000; /* stream rate */
static unsigned int channels = 1; /* count of channels */
static unsigned int buffer_time = 40000; /* ring buffer length in us */
static unsigned int period_time = 120000; /* period time in us */
static int resample = 1; /* enable alsa-lib resampling */
static int period_event = 0; /* produce poll event after each period */
int size;
while (1) {
do {
nbytes = read(s, &frame, sizeof(struct canfd_frame));
} while (nbytes == 0);
for (x = 0; x < 64; x = x + 2) {
buffer[a] = ((uint32_t) frame.data[x] << 8)
| ((uint32_t) (frame.data[x + 1]));
a++;
}
//err=snd_pcm_writei(handle,buffer,32);
//printf("Datos = %d\n", err);
memcpy(total1 + i * 32, buffer, 32 * sizeof(uint32_t));
i++;
a = 0;
if (i == 500) {
buffer_length=16000;
ptr = total1;
while(buffer_length > 0){
err = snd_pcm_writei(handle, ptr, 16000);
printf("Datos = %d\n", err);
snd_pcm_avail_delay(handle, &availp, &delayp);
//printf("available frames =%ld delay = %ld z = %d\n", availp, delayp, z);
if (err == -EAGAIN)
continue;
if(err < 0){
err=snd_pcm_recover(handle, err, 1);
}
else{
ptr += err * channels;
buffer_length -= err;
z++;
}
if(err<0){
printf("snd_pcm_writei failed: %s\n", snd_strerror(err));
break;
}
}
i = 0;
}
This is a part of my code, I don't thinks posting the whole code is worth. I don't understand which values should I give to buffer_time, period_time and how to be able to listen to what a I get through the CAN FD in real time. I am using snd_pcm_writei, inserting a buffer I fill with some samples I get from the CAN FD. I don't know which size should I give to the buffer and to the "frames" variable, another one that I don't quite understand, eventhough I have read some about it.
Any idea how should I configure my system? (buffer_time, period_time, buffer_size, frame,...)
I have tried using different buffer and frame sizes, but I don't think I understand how it works properly. How can I calculate the size of the frame and buffer of the snd_pcm_writei(), in order to listen in Real Time to the audio?
Should I use two differente threads? One to create the buffer with the CAN FD information and the other one to handle the buffer and the audio output?
Thanks in advance,
Ander.
I have finally managed to hear my self through the headphones. I have changed my configuration posted on my previous in order to sincronize it with the data I get from the CAN FD. I will post part of my code down here in case somebody needs an example. The most important part having to handle buffers like these is to handle the time to fill and the time to communicate it. Handling the time and configuring the Alsa parameters accordingly makes easier to handle the buffers.
static char *device = "plughw:0,0"; /* playback device */
static snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; /* sample format */
static unsigned int rate = 22000; /* stream rate */
static unsigned int channels = 1; /* count of channels */
static unsigned int buffer_time = 1000; /* ring buffer length in us */
static unsigned int period_time = 10000; /* period time in us */
static int resample = 1; /* enable alsa-lib resampling */
static int period_event = 0; /* produce poll event after each period */
int size;
static snd_pcm_sframes_t buffer_size;
static snd_pcm_sframes_t period_size;
static snd_output_t *output = NULL;
snd_pcm_sframes_t delayp;
snd_pcm_sframes_t availp;
snd_pcm_uframes_t frames;
static void write_loop(snd_pcm_t *handle) {
uint32_t *buffer = malloc(16000 * sizeof(uint32_t));
uint32_t *total1 = malloc(16000 * sizeof(uint32_t)); // array to hold the result
while (1) {
do {
nbytes = read(s, &frame, sizeof(struct canfd_frame));
} while (nbytes == 0);
for (x = 0; x < 64;x = x + 2) {
buffer[a] = ((uint32_t) frame.data[x] << 8)
| ((uint32_t) (frame.data[x + 1]));
//buffer[a]=frame.data[x];
a++;
}
i++;
if (i == 250) {
memcpy(total1, buffer, 16000 * sizeof(uint32_t));
//printf("Address = %lu \n",(unsigned long)total1);
flag = 1;
buffer_length = 16000;
i = 0;
a = 0;
}
if (flag == 1) {
while(buffer_length > 0) {
snd_pcm_prepare(handle);
err = snd_pcm_writei(handle, total1, buffer_length);
//printf("Datos = %d\n", err);
snd_pcm_avail_delay(handle, &availp, &delayp);
//printf("available frames =%ld delay = %ld\n",availp,delayp);
if (err == -EAGAIN)
continue;
if (err < 0) {
err = snd_pcm_recover(handle, err, 1);
} else {
ptr += err * channels;
buffer_length -= err;
z++;
}
if (err < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror(err));
break;
}
}
flag = 0;
}
}
}

ALSA 'snd_pcm_writei' behavior in blocking mode

I slightly modified the demo taken from ALSA Project website in order to test it on my laptop's sound card (Intel PCH ALC3227 Analog, Ubuntu 18.04), which requires 2 channels and 16 bit integers. I also doubled the latency (1 s), switched off resampling and made the demo lasts longer. This is the code (runtime error checking not pasted for sake of synthesis)
#include <alsa/asoundlib.h>
#include <stdlib.h>
static char *device = "hw:1,0"; /* playback device */
snd_output_t *output = NULL;
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] = (unsigned char) (rand() & 0xff);
snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)
snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE,
SND_PCM_ACCESS_RW_INTERLEAVED, 2, 48000, 0, 1E6);
// Print actual buffer size
snd_pcm_hw_params_t *hw_params;
snd_pcm_hw_params_malloc(&hw_params);
snd_pcm_hw_params_current(handle, hw_params);
snd_pcm_uframes_t bufferSize;
snd_pcm_hw_params_get_buffer_size(hw_params, &bufferSize);
printf("ALSA buffer size = %li\n", bufferSize);
// playback
for (i = 0; i < 256; ++i) {
frames = snd_pcm_writei(handle, buffer, sizeof(buffer) / 4);
if (frames < 0)
frames = snd_pcm_recover(handle, (int) frames, 0);
if (frames < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror((int) frames));
break;
}
if (frames > 0 && frames < (long) sizeof(buffer) / 4)
printf("Short write (expected %li, wrote %li)\n",
(long) sizeof(buffer) / 4, frames);
}
snd_pcm_hw_params_free(hw_params);
snd_pcm_close(handle);
return (0);
}
Audio works but could someone explain me why I sometimes get output like the following
ALSA buffer size = 16384
Short write (expected 4096, wrote 9)
Short write (expected 4096, wrote 4080)
indicating that less frames than expected have been written by snd_pcm_writei? According to the ALSA docs, I understand that a signal has to be occurred, but I don't get the reason and which signal is.
I also tried to halve the buffer's size, but the result is pretty the same.
A short read is reported when an error happens, but some frames were already written successfully.
You are supposed to call the same function again, with the remaining buffer; if the error was not transient, it will be reported then.
(This example code is wrong; it just ignores that the remaining part of the buffer was not written.)

decode FLAC file with avcodec_decode_audio4 not work well

I use FFmpeg to decode my flac file and write it to pcm file, then use GoldenWave to play it with pcm signed 16bit, little endian, mono and the total play time is ok.
I doubt i write the 2 channel file in one place, but i don't know how to get every signal channel and write it to pcm file.
any help? thank you.
while (av_read_frame(fmt_ctx, &pkt) >= 0) {
AVPacket orig_pkt = pkt;
do {
ret = decode_packet(&got_frame, 0);
if (ret < 0)
break;
pkt.data += ret;
pkt.size -= ret;
} while (pkt.size > 0);
av_free_packet(&orig_pkt);
}
pkt.data = NULL;
pkt.size = 0;
do {
decode_packet(&got_frame, 1);
LOG("flush cached frames");
} while (got_frame);
static int decode_packet(int *got_frame, int cached)
{
int ret = 0;
int decoded = pkt.size;
*got_frame = 0;
if (pkt.stream_index == audio_stream_idx) {
ret = avcodec_decode_audio4(audio_dec_ctx, frame, got_frame, &pkt);
if (ret < 0) {
LOG("Error decoding audio frame (%s)\n", av_err2str(ret));
return ret;
}
decoded = FFMIN(ret, pkt.size);
if (*got_frame) {
size_t unpadded_linesize = frame->nb_samples * av_get_bytes_per_sample(audio_dec_ctx->sample_fmt);
//decode packet nb_samples:4608, xx:2, unpadded_linesize: 9216
LOG("decode packet nb_samples:%d, xx:%d, unpadded_linesize: %d",
frame->nb_samples, av_get_bytes_per_sample(audio_dec_ctx->sample_fmt), unpadded_linesize);
fwrite(frame->extended_data[0], 1, unpadded_linesize, audio_dst_file);
//int nb_sample = frame->nb_samples;
//fwrite(frame->extended_data[0], 1, nb_sample, audio_dst_file);
//fwrite(frame->extended_data[0] + nb_sample, 1, nb_sample, audio_dst_file);
}
}
if (*got_frame && api_mode == API_MODE_NEW_API_REF_COUNT)
av_frame_unref(frame);
return decoded;
}
You didn't describe the problem you're having, but from what you're writing, I see two problems:
you're not checking the raw audio format of the frame, see frame->format (or audio_dec_ctx->sample_fmt). You're writing it as if it were AV_SAMPLE_FMT_S16, but you're not checking that it is
your unpadded_linesize is not multiplied by the number of channels (see e.g. frame->channels)

Sending sine wave values from array to audio output

Im trying to get a c program to send a sine wave tone to the audio output of my raspberry pi using ALSA
Im generating the buffer of sine wave values but when i send them it doesnt sound like a sine - but like low-mid frequency noise?
I have a dumped the values from the array into a .csv and plotted in excel to verify the sine wave is good
Just wondering have a got stuff fundamentally incorrect in this program, if someone could spot something id be very grateful, thanks
edit: final working code below!!!
#include <alsa/asoundlib.h>
#include <alsa/pcm.h>
#include <math.h>
#define BUFFER_LEN 48000
static char *device = "default"; //soundcard
snd_output_t *output = NULL;
float buffer [BUFFER_LEN];
int main(void)
{
int err;
int j,k;
int f = 440; //frequency
int fs = 48000; //sampling frequency
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
// ERROR HANDLING
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_FLOAT,
SND_PCM_ACCESS_RW_INTERLEAVED,
1,
48000,
1,
500000)) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
// SINE WAVE
printf("Sine tone at %dHz ",f);
for (k=0; k<BUFFER_LEN; k++){
buffer[k] = (sin(2*M_PI*f/fs*k)); //sine wave value generation
}
for (j=0; j<5; j++){
frames = snd_pcm_writei(handle, buffer, BUFFER_LEN); //sending values to sound driver
}
snd_pcm_close(handle);
return 0;
}
You have configured the sample format SND_PCM_FORMAT_U8, but the actuall buffer contains 32-bit floating-point samples.
Use SND_PCM_FORMAT_FLOAT, or define the buffer as an array of unsigned char.
Furthermore, you have confused the loop to initialize the buffer and the loop to play the data, and many bytes/frames numbers, and fs is wrong; you need to use something like this:
for (i = 0; i < BUFFER_LEN; i++)
buffer [i] = sin(2*M_PI*f/48000*i); // sine wave value generation
for (i = 0; i < 10 * 48000 / BUFFER_LEN; i++) { // 10 seconds
frames = snd_pcm_writei(handle, buffer, BUFFER_LEN);
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 < BUFFER_LEN)
printf("Short write (expected %li, wrote %li)\n", BUFFER_LEN, frames);
}

Warning/error from ALSA's pcm_min.c example. Possible problem?

When I compile ALSA's pcm_min.c example with
gcc -Wall -lasound pcm_min.c -o pcm_min
Everything is fine, but running it, I get the white noise as expected, but I also get this warning/error:
Short write (expected 16384, wrote 7616)
Which comes from the last if-statement.
#include <alsa/asoundlib.h>
static char *device = "default"; /* playback device */
snd_output_t *output = NULL;
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(err));
break;
}
if (frames > 0 && frames < (long)sizeof(buffer))
printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), frames);
}
snd_pcm_close(handle);
return 0;
}
Can someone see why this warning/error occur?
Hugs,
Louise
The snd_pcm_writei() function might return less than sizeof(buffer) when there's either a signal received or an underrun. In your case, it seems that you're mixing bytes and frames. The last parameter of the call is the number of frames that you have in your buffer. Since you're passing the number of bytes in your buffer instead, you're seeing an underrun.
I was also having some problems with this example. I modified it a bit and now it works.
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
static char *device = "default"; /* playback device */
snd_output_t *output = NULL;
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;
snd_pcm_uframes_t bufferSize, periodSize;
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_S16_LE,
SND_PCM_ACCESS_RW_INTERLEAVED,
1, //channels
44100, //sample rate
1, //allow resampling
500000) //required latency in us
) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_prepare(handle)) < 0) {
printf("Pcm prepare error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_get_params( handle, &bufferSize, &periodSize )) < 0) {
printf("Pcm get params error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
printf("Buffer size:%d, Period size:%d\n", (int)bufferSize, (int)periodSize);
for (i = 0; i < 16; i++) {
frames = snd_pcm_writei(handle, buffer, periodSize);
if (frames < 0)
frames = snd_pcm_recover(handle, frames, 0);
if (frames < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror(err));
break;
}
if (frames > 0 && frames < (long)periodSize)
printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), frames);
}
snd_pcm_close(handle);
return 0;
}

Resources