Why does ALSA snd_pcm_hw_params_get_buffer_size react to the size of local stack frame allocations? - alsa

Distro: Debian 9.11
Compiler: gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
ALSA version: Advanced Linux Sound Architecture Driver Version k4.9.0-11-amd64.
Please consider the following minimal reproducible example (now updated, with more error handling):
/**
* Trying to set some parameters for ALSA.
*
* Code from: http://equalarea.com/paul/alsa-audio.html
*
*/
#include <alsa/asoundlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#define PCM_DEVICE "default"
int main(int argc, char **argv)
{
unsigned int err;
snd_pcm_t *pcm_handle;
snd_pcm_hw_params_t *params;
if((err = snd_pcm_open(&pcm_handle, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
printf("Error: Can't open \"%s\" PCM device. %s\n", PCM_DEVICE, snd_strerror(err));
return 1;
}
snd_pcm_hw_params_alloca(&params);
snd_pcm_hw_params_any(pcm_handle, params);
if((err = snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
printf("Error: Can't set interleaved mode. %s\n", snd_strerror(err));
return 1;
}
if((err = snd_pcm_hw_params_set_format(pcm_handle, params, SND_PCM_FORMAT_FLOAT)) < 0) {
printf("Error: Can't set format. %s\n", snd_strerror(err));
return 1;
}
int channels = 2;
if((err = snd_pcm_hw_params_set_channels(pcm_handle, params, channels)) < 0) {
printf("Error: Can't set channels number. %s\n", snd_strerror(err));
return 1;
}
int rate = 44100;
if((err = snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, 0)) < 0) {
printf("Error: Can't set rate. %s\n", snd_strerror(err));
return 1;
}
printf("PCM name: '%s'\n", snd_pcm_name(pcm_handle));
printf("PCM state: %s\n", snd_pcm_state_name(snd_pcm_state(pcm_handle)));
int ch;
if((err = snd_pcm_hw_params_get_channels(params, &ch)) < 0) {
printf("Error: Unable to get the number of channels: %s\n", snd_strerror(err));
return 1;
}
if(ch > 1) {
printf("Channels: %i, stereo\n", ch);
}
else if(ch == 1) {
printf("Channels: %i, mono\n", ch);
}
else {
printf("Error: Unknown number of channels (%d).\n", ch);
return 1;
}
if((err = snd_pcm_hw_params_get_rate(params, &ch, 0)) < 0) {
printf("Error: Unable to get the rate: %s\n", snd_strerror(err));
return 1;
}
printf("Rate: %d bps\n", ch);
int dir;
snd_pcm_uframes_t period_size = 1024;
printf("Attempting to set the period size to %d\n", period_size);
if((err = snd_pcm_hw_params_set_period_size_near(pcm_handle, params, &period_size, &dir)) < 0) {
printf("Error: Unable to set the period size: %s\n", snd_strerror(err));
return 1;
}
printf("Attempting to get the period size.\n");
if((err = snd_pcm_hw_params_get_period_size(params, &period_size, 0)) < 0) {
printf("Error: Unable to get the period size: %s\n", snd_strerror(err));
return 1;
}
printf("Period size is now: %zd\n", period_size);
int period_time = -1;
printf("Attempting to get the period time.\n");
if((err = snd_pcm_hw_params_get_period_time(params, &period_time, NULL)) < 0) {
printf("Error: Unable to get the period time: %s\n", snd_strerror(err));
return 1;
}
printf("Period time: %d\n", period_time);
int buffer_size = 4096;
printf("Attempting to set the buffer size to: %d\n", buffer_size);
if((err = snd_pcm_hw_params_set_buffer_size(pcm_handle, params, buffer_size)) < 0) {
printf("Error: Unable to set the buffer size: %s\n", snd_strerror(err));
return 1;
}
printf("Finalizing hw params.\n");
if((err = snd_pcm_hw_params(pcm_handle, params)) < 0) {
printf("Error: Can't set harware parameters. %s\n", snd_strerror(err));
return 1;
}
snd_pcm_uframes_t temp;
printf("Attempting to get the buffer size.\n");
if((err = snd_pcm_hw_params_get_buffer_size(params, &temp)) < 0) {
printf("Error: Unable to get the buffer size: %s\n", snd_strerror(err));
return 1;
}
printf("Buffer size is now: %d\n", temp);
if((err = snd_pcm_drain(pcm_handle)) < 0) {
printf("Error: Unable to drain the pcm handle: %s\n", snd_strerror(err));
return 1;
}
if((err = snd_pcm_close(pcm_handle)) < 0) {
printf("Error: Unable to close the pcm handle: %s\n", snd_strerror(err));
return 1;
}
return 0;
}
When char test[5] exists, I get this output:
PCM name: 'default'
PCM state: OPEN
Channels: 2, stereo
Rate: 44100 bps
Attempting to set the period size to 1024
Attempting to get the period size.
Period size is now: 1024
Attempting to get the period time.
Period time: -1
Attempting to set the buffer size to: 4096
Finalizing hw params.
Attempting to get the buffer size.
Buffer size is now: 523774
Note the "Buffer size is now: 523774". The expected output is given below:
However, if I change test[5] to test[4] or less (or remove the statement altogether), recompile, and run, I get:
PCM name: 'default'
PCM state: OPEN
Channels: 2, stereo
Rate: 44100 bps
Attempting to set frames to 1024
Frames is now: 1024
Attempting to set the buffer size to: 4096
Buffer size is now: 4096
I must be abusing the ALSA API, be doing something else weird I don't realize, or the ALSA API is broken.
Why does it react to this trivial stack allocation of 5 bytes?
Note that a higher value than 5 also produces this problem, such as a word aligned number of 32. I don't think has to do with stack alignment or something of that sort.
Compiler notes, how to reproduce:
Copy/paste the minimal example into broken_alsa.c
Then issue:
$ gcc broken_alsa.c -lasound
$ ./a.out
Remove the char temp[5] (or set it to 4), and it should work.

When calling snd_pcm_hw_params_set_period_size_near(), initialize dir to zero.

Related

SDL audio capture callbacks slower than playback

SDL capture audio callbacks seem to be called once for every 12 playback callbacks. Am I doing something wrong? This feels like an SDL or PulseAudio bug.
The program below prints "Reading audio..." once every ~12 "Writing audio..." prints. Tested via running the command directly, through gdb, and through Valgrind.
I've tried this in both C and Golang (using github.com/veandco/go-sdl2/sdl), on the same machine.
C code:
// A test program to copy audio in (microphone) to audio out (speaker) via SDL.
//
// Compile: cc inout.c -o inout -lSDL2
// Run: ./inout
#include <SDL2/SDL.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#define BUF_SIZE 1024
// Stereo float32 samples.
static uint8_t saved[BUF_SIZE*2*sizeof(float)];
// Copies audio callback data into the saved buffer.
void audioReader(void* udata, uint8_t* buf, int len) {
fprintf(stderr, "Reading audio: %d -> %ld bytes\n", len, sizeof(saved));
memcpy(saved, buf, len);
}
// Copies saved audio data into the callback buffer.
void audioWriter(void* udata, uint8_t* buf, int len) {
fprintf(stderr, "Writing audio: %ld -> %d bytes\n", sizeof(saved), len);
memcpy(buf, saved, len);
}
// List all devices of the given type, and return the name of the first or NULL.
// Caller must free the returned pointer.
char* ChooseDevice(int is_capture) {
int dev_cnt = SDL_GetNumAudioDevices(is_capture);
if (dev_cnt < 1) {
fprintf(stderr, "No %s devices: %s\n", is_capture ? "capture" : "playback", SDL_GetError());
return NULL;
}
printf("%s devices:\n", is_capture ? "capture" : "playback");
char* dev_name = NULL;
for (int i = 0; i < dev_cnt; i++) {
printf("%c %s\n", !dev_name ? '*' : ' ', SDL_GetAudioDeviceName(i, is_capture));
if (!dev_name) {
const char* tmp = SDL_GetAudioDeviceName(i, is_capture);
dev_name = malloc(strlen(tmp)+1);
strcpy(dev_name, tmp);
}
}
if (!dev_name) {
fprintf(stderr, "No %s devices\n", is_capture ? "capture" : "playback");
}
return dev_name;
}
// Opens and unpauses the first device of the given type, returning its ID, or
// returns 0.
SDL_AudioDeviceID OpenDevice(int is_capture) {
char* dev_name = ChooseDevice(is_capture);
if (!dev_name) return 0;
SDL_AudioSpec spec;
SDL_memset(&spec, 0, sizeof(spec));
spec.freq = 48000;
spec.format = AUDIO_F32;
spec.channels = 2;
spec.samples = BUF_SIZE;
spec.callback = is_capture ? audioReader : audioWriter;
SDL_AudioDeviceID dev_id = SDL_OpenAudioDevice(dev_name, is_capture, &spec, NULL, 0);
if (dev_id == 0) {
fprintf(stderr, "Failed to open %s device %s: %s\n", is_capture ? "input" : "output", dev_name, SDL_GetError());
return 0;
}
free(dev_name);
SDL_PauseAudioDevice(dev_id, SDL_FALSE);
return dev_id;
}
int main(int argc, char** argv) {
SDL_memset(saved, 0, sizeof(saved));
if (SDL_Init(SDL_INIT_AUDIO) < 0) {
fprintf(stderr, "Failed to initialize SDL audio: %s\n", SDL_GetError());
return 1;
}
SDL_AudioDeviceID in_dev_id = OpenDevice(/* is_capture = */ SDL_TRUE);
if (in_dev_id == 0) return 1;
SDL_AudioDeviceID out_dev_id = OpenDevice(/* is_capture = */ SDL_FALSE);
if (out_dev_id == 0) return 1;
SDL_Delay(10000); // 10 seconds
SDL_CloseAudioDevice(in_dev_id);
SDL_CloseAudioDevice(out_dev_id);
SDL_Quit();
return 0;
}

How to set the period size in ALSA, revisited

This appears to answer my problem: How to set periods and buffer size in ALSA? but I have an example which doesn't work.
frames = 1024;
int dir;
snd_pcm_hw_params_set_period_size_near(pcm_handle, params, &frames, &dir);
snd_pcm_hw_params(pcm_handle, params);
snd_pcm_hw_params_get_period_size(params, &frames, 0);
printf("Frames: %zd\n", frames);
Regardless of frames being a high or a low number, when I fetch what it actually was set to with snd_pcm_hw_params_get_period_size(), it always shows me 512 frames.
I understand that the exact number I'm trying to set might not be available. However, shouldn't it set it to the nearest value of what I'm requesting?
I'm expecting to see it at least get increased when I request a higher number, or get lowered when I request a lower number, not remain exactly the same.
hw_params.c output for the "hw" device:
Device: hw (type: HW)
Access types: MMAP_INTERLEAVED RW_INTERLEAVED
Formats: S16_LE S32_LE
Channels: 2 4 6 8
Sample rates: 44100 48000 96000 192000
Interrupt interval: 20-5944309 us
Buffer size: 41-11888617 us
hw_params.c output for the "default" device:
Device: default (type: IOPLUG)
Access types: RW_INTERLEAVED
Formats: U8 S16_LE S16_BE S24_LE S24_BE S32_LE S32_BE FLOAT_LE FLOAT_BE MU_LAW A_LAW S24_3LE S24_3BE
Channels: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
Sample rates: 1-192000
Interrupt interval: 5-4294967295 us
Buffer size: 15-4294967295 us
Minimal reproducible example where I attempt to set both the period of the buffer size:
/**
* Attempting to set some parameters.
*
* Using the template from: http://equalarea.com/paul/alsa-audio.html
*
**/
#include <alsa/asoundlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#define PCM_DEVICE "default"
int main(int argc, char **argv)
{
unsigned int pcm;
snd_pcm_t *pcm_handle;
snd_pcm_hw_params_t *params;
/* Open the PCM device in playback mode */
if(pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0) < 0) {
printf("ERROR: Can't open \"%s\" PCM device. %s\n", PCM_DEVICE, snd_strerror(pcm));
return 1;
}
/* Allocate parameters object and fill it with default values*/
snd_pcm_hw_params_alloca(&params);
snd_pcm_hw_params_any(pcm_handle, params);
/* Set parameters */
if(pcm = snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
printf("ERROR: Can't set interleaved mode. %s\n", snd_strerror(pcm));
return 1;
}
if(pcm = snd_pcm_hw_params_set_format(pcm_handle, params, SND_PCM_FORMAT_FLOAT) < 0) {
printf("ERROR: Can't set format. %s\n", snd_strerror(pcm));
return 1;
}
int channels = 2;
if(pcm = snd_pcm_hw_params_set_channels(pcm_handle, params, channels) < 0) {
printf("ERROR: Can't set channels number. %s\n", snd_strerror(pcm));
return 1;
}
int rate = 44100;
if(pcm = snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, 0) < 0) {
printf("ERROR: Can't set rate. %s\n", snd_strerror(pcm));
return 1;
}
/* Write parameters */
if(pcm = snd_pcm_hw_params(pcm_handle, params) < 0) {
printf("ERROR: Can't set harware parameters. %s\n", snd_strerror(pcm));
return 1;
}
printf("PCM name: '%s'\n", snd_pcm_name(pcm_handle));
printf("PCM state: %s\n", snd_pcm_state_name(snd_pcm_state(pcm_handle)));
int ch;
snd_pcm_hw_params_get_channels(params, &ch);
if(ch > 1) {
printf("Channels: %i, stereo\n", ch);
}
else if(ch == 1) {
printf("Channels: %i, mono\n", ch);
}
snd_pcm_hw_params_get_rate(params, &ch, 0);
printf("Rate: %d bps\n", ch);
int dir;
snd_pcm_uframes_t frames = 1024;
printf("Attempting to set frames to %d\n", frames);
snd_pcm_hw_params_set_period_size_near(pcm_handle, params, &frames, &dir);
snd_pcm_hw_params(pcm_handle, params);
snd_pcm_hw_params_get_period_size(params, &frames, 0);
printf("Frames is now: %zd\n", frames);
snd_pcm_hw_params_get_period_time(params, &ch, NULL);
int buffer_size = 52430;
printf("Attempting to set the buffer size to: %d\n", buffer_size);
snd_pcm_hw_params_set_buffer_size(pcm_handle, params, buffer_size);
snd_pcm_hw_params(pcm_handle, params);
snd_pcm_uframes_t temp;
snd_pcm_hw_params_get_buffer_size(params, &temp);
printf("Buffer size is now: %d\n", temp);
return 0;
}
Compile with: gcc set_params_fail.c -lasound
Output:
PCM name: 'default'
PCM state: PREPARED
Channels: 2, stereo
Rate: 44100 bps
Attempting to set frames to 1024
Frames is now: 512
Attempting to set the buffer size to: 52430
Buffer size is now: 524288
After calling snd_pcm_hw_params(), all parameters are fixed. You have to call snd_pcm_hw_params_set_period_size_near() before the first (and only) call to snd_pcm_hw_params().

Alsa library configuration

I am using Alsa library to find Maximum value of Sound samples from stereo output.i am using S32_LE pcm Format. From my python code , i can able to acheive max values instantaneously. But from C Alsa Lib ,instant values are not able to get. Please help me to solve this . I have attached my python script as well as c code for your reference.
Python Code:
#!/usr/bin/env python
import alsaaudio, time, audioop, math
card = 'sysdefault:CARD=Default'
inp=alsaaudio.PCM(alsaaudio.PCM_CAPTURE,alsaaudio.PCM_NORMAL,card)
inp.setchannels(1)
inp.setrate(64000)
inp.setformat(alsaaudio.PCM_FORMAT_S32_LE)
inp.setperiodsize(1024)
val=0
loop=0
myarr=[]
count=math.pow(2,24)
while True:
l,data=inp.read()
if l:
amplitude=audioop.max(data,4)
val=int(amplitude)
val=val>>8
if val > 0:
db=(20* math.log(val/count))+120
C Code:
#include <alsa/asoundlib.h>
#include <stdio.h>
#include <math.h>
#define PCM_DEVICE "default"
int max=0,min=0;
int rate, channels, seconds;
int buff_size, loops;
unsigned int pcm, tmp, dir;
int main(int argc, char **argv) {
int FS = (int)(pow(2,24));
snd_pcm_t *pcm_handle;
int32_t value,result,i;
snd_pcm_hw_params_t *params;
snd_pcm_uframes_t frames;
int32_t *buff;
if (argc < 4) {
printf("Usage: %s <sample_rate> <channels> <seconds>\n",
argv[0]);
return -1;
}
rate = atoi(argv[1]);
channels = atoi(argv[2]);
seconds = atoi(argv[3]);
/* Open the PCM device in playback mode */
if (pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE,
SND_PCM_STREAM_CAPTURE, 0) < 0)
printf("ERROR: Can't open \"%s\" PCM device. %s\n",
PCM_DEVICE, snd_strerror(pcm));
/* Allocate parameters object and fill it with default values*/
snd_pcm_hw_params_alloca(&params);
snd_pcm_hw_params_any(pcm_handle, params);
/* Set parameters */
if (pcm = snd_pcm_hw_params_set_access(pcm_handle, params,
SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
printf("ERROR: Can't set interleaved mode. %s\n", snd_strerror(pcm));
if (pcm = snd_pcm_hw_params_set_format(pcm_handle, params,
SND_PCM_FORMAT_S32_LE) < 0)
printf("ERROR: Can't set format. %s\n", snd_strerror(pcm));
if (pcm = snd_pcm_hw_params_set_channels(pcm_handle, params, channels) < 0)
printf("ERROR: Can't set channels number. %s\n", snd_strerror(pcm));
if (pcm = snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, 0) < 0)
printf("ERROR: Can't set rate. %s\n", snd_strerror(pcm));
/* Write parameters */
if (pcm = snd_pcm_hw_params(pcm_handle, params) < 0)
printf("ERROR: Can't set harware parameters. %s\n", snd_strerror(pcm));
/* Resume information */
printf("PCM name: '%s'\n", snd_pcm_name(pcm_handle));
printf("PCM state: %s\n", snd_pcm_state_name(snd_pcm_state(pcm_handle)));
snd_pcm_hw_params_get_channels(params, &tmp);
printf("channels: %i ", tmp);
if (tmp == 1)
printf("(mono)\n");
else if (tmp == 2)
printf("(stereo)\n");
snd_pcm_hw_params_get_rate(params, &tmp, 0);
printf("rate: %d bps\n", tmp);
printf("seconds: %d\n", seconds);
/* Allocate buffer to hold single period */
snd_pcm_hw_params_get_period_size(params, &frames, 0);
buff_size = frames*4*channels /* 2 -> sample size */;
buff = malloc(buff_size);
printf("Buff Size %d\n ",frames);
snd_pcm_hw_params_get_period_time(params, &tmp, NULL);
printf("seconds: %d\n", tmp);
// for (loops =1000000/ tmp; loops < (seconds * 1000000)/tmp; loops++){
while(1) {
if (pcm = snd_pcm_readi(pcm_handle, buff, frames) == -EPIPE) {
printf("XRUN.\n");
snd_pcm_prepare(pcm_handle);
} else if (pcm < 0) {
printf("ERROR. Can't write to PCM device. %s\n", snd_strerror(pcm));
}
for(i=0;i<sizeof(buff);i=i+2){
value= buff[i];
value=value>>8;
// if(max<value)
// max=value;
// if(min > value)
// min=value;
if(value < 0 ) value*=-1;
if (result<value) result=value;
}
// if(max<0)
// max=max* -1;
//double mymax=log((double)((double)max/(double)FS));
// int mymax1=20 * log(max/FS);
// double mymax=(double)20*(log((value/(double)FS)));
// printf("MAx %ld",max);
printf("%ld \n ",result);
// double mymax = (double)20.000 * (log((double)((double)max/(double)FS)));
//printf("%ld \n ",mymax);
result=0;
value=0;
memset(buff,0,sizeof(buff));
// }
//snd_pcm_drain(pcm_handle);
snd_pcm_close(pcm_handle);
}
return 0;
}
Regards
Raji
Alsa code is working fine . i can able to getsound variation
Here i have attached my working Alsa lib c code .
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <alsa/asoundlib.h>
#define PCM_DEVICE "default"
double db;
int main()
{
int i,var,min=0,max=0;
int err;
int32_t buffer[1024];
int32_t value;int loop=0;
int buffer_frames =128;
int FS = (int)(pow(2,24));
unsigned int rate = 64000;
snd_pcm_t *capture_handle;
snd_pcm_hw_params_t *hw_params;
snd_pcm_format_t format = SND_PCM_FORMAT_S32_LE;
if ((err = snd_pcm_open (&capture_handle,PCM_DEVICE, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
printf ("cannot open audio device %s (%s)\n",
PCM_DEVICE,
snd_strerror (err));
exit (1);
}
if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
printf ("cannot allocate hardware parameter structure (%s)\n",
snd_strerror (err));
exit (1);
}
if ((err = snd_pcm_hw_params_any (capture_handle, hw_params)) < 0) {
printf ("cannot initialize hardware parameter structure (%s)\n",
snd_strerror (err));
exit (1);
}
if ((err = snd_pcm_hw_params_set_access (capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
printf ("cannot set access type (%s)\n",
snd_strerror (err));
exit (1);
}
if ((err = snd_pcm_hw_params_set_format (capture_handle, hw_params, format)) < 0) {
printf ("cannot set sample format (%s)\n",
snd_strerror (err));
exit (1);
}
if ((err = snd_pcm_hw_params_set_rate_near (capture_handle, hw_params, &rate, 0)) < 0) {
printf ("cannot set sample rate (%s)\n",
snd_strerror (err));
exit (1);
}
if ((err = snd_pcm_hw_params_set_channels (capture_handle, hw_params, 2)) < 0) {
printf ("cannot set channel count (%s)\n",
snd_strerror (err));
exit (1);
}
if ((err = snd_pcm_hw_params (capture_handle, hw_params)) < 0) {
printf ("cannot set parameters (%s)\n",
snd_strerror (err));
exit (1);
}
snd_pcm_hw_params_free (hw_params);
if ((err = snd_pcm_prepare (capture_handle)) < 0) {
printf ("cannot prepare audio interface for use (%s)\n",
snd_strerror (err));
exit (1);
}
while(1){
if ((err = snd_pcm_readi (capture_handle, buffer, buffer_frames)) != buffer_frames) {
printf ("read from audio interface failed (%s)\n",
err, snd_strerror (err));
break;
}
for(var=0;var<128;var=var+2){
value=buffer[var];
value= abs(value>>8);
if(max<value)
max=value;
if(min > value)
min=value;
}
db = ((double)20.000 * log((double)((double)max/(double)FS)))+120;
printf("%lf\n",db);
memset(buffer,0,sizeof(buffer));
max=0;min=0; value=0;
sleep(0.5);
}
snd_pcm_close (capture_handle);
exit (0);
}

How to properly set up ALSA device

Edit: This question is different than the proposed duplicate because I'm asking How do you set the period/buffer size that will work with multiple targets each with different sound hardware?.
I have created some code that attempts to set up ALSA before playback of an OGG file. The code below works on one embedded Linux platform, but on another it fails with the following output:
Error setting buffersize.
Playback open error: Operation not permitted
I've included only the code that demonstrates the issue. setup_alsa() is not complete and won't completely configure an alsa device.
#include <alsa/asoundlib.h>
char *buffer;
static char *device = "default";
snd_pcm_uframes_t periodsize = 8192; /* Periodsize (bytes) */
int setup_alsa(snd_pcm_t *handle)
{
int rc;
int dir = 0;
snd_pcm_uframes_t periods; /* Number of fragments/periods */
snd_pcm_hw_params_t *params;
snd_pcm_sw_params_t *sw_params;
int rate = 44100;
int exact_rate;
int i = 0;
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_alloca(&params);
/* Fill it in with default values. */
if (snd_pcm_hw_params_any(handle, params) < 0)
{
fprintf(stderr, "Can not configure this PCM device.\n");
snd_pcm_close(handle);
return(-1);
}
/* Set number of periods. Periods used to be called fragments. */
periods = 4;
if ( snd_pcm_hw_params_set_periods(handle, params, periods, 0) < 0 )
{
fprintf(stderr, "Error setting periods.\n");
snd_pcm_close(handle);
return(-1);
}
/* Set buffer size (in frames). The resulting latency is given by */
/* latency = periodsize * periods / (rate * bytes_per_frame) */
if (snd_pcm_hw_params_set_buffer_size(handle, params, (periodsize * periods)>>2) < 0)
{
fprintf(stderr, "Error setting buffersize.\n");
snd_pcm_close(handle);
return(-1);
}
/* Write the parameters to the driver */
rc = snd_pcm_hw_params(handle, params);
if (rc < 0)
{
fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
snd_pcm_close(handle);
return -1;
}
snd_pcm_hw_params_free(params);
What is the normal way to setup ALSA that doesn't require a specific buffer/period size be set that provides smooth audio playback?**
As it turns out, I can program my ALSA setup routine to let ALSA determine what the nearest working period/buffer size is by using snd_pcm_hw_params_set_buffer_size_near() instead of snd_pcm_hw_params_set_buffer_size().
The following code now works on both platforms:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <vorbis/vorbisfile.h>
#include <alsa/asoundlib.h>
char *buffer;
//static char *device = "default";
static char *device = "plughw:0,0";
snd_pcm_uframes_t periodsize = 4096; /* Periodsize (bytes) */
int setup_alsa(snd_pcm_t *handle)
{
int rc;
int dir = 0;
snd_pcm_uframes_t periods; /* Number of fragments/periods */
snd_pcm_hw_params_t *params;
snd_pcm_sw_params_t *sw_params;
int rate = 44100;
int exact_rate;
int i = 0;
/* Allocate a hardware parameters object. */
snd_pcm_hw_params_malloc(&params);
/* Fill it in with default values. */
if (snd_pcm_hw_params_any(handle, params) < 0)
{
fprintf(stderr, "Can not configure this PCM device.\n");
snd_pcm_close(handle);
return(-1);
}
/* Set the desired hardware parameters. */
/* Non-Interleaved mode */
snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_NONINTERLEAVED);
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
/* 44100 bits/second sampling rate (CD quality) */
/* Set sample rate. If the exact rate is not supported */
/* by the hardware, use nearest possible rate. */
exact_rate = rate;
if (snd_pcm_hw_params_set_rate_near(handle, params, &exact_rate, 0) < 0)
{
fprintf(stderr, "Error setting rate.\n");
snd_pcm_close(handle);
return(-1);
}
if (rate != exact_rate)
{
fprintf(stderr, "The rate %d Hz is not supported by your hardware.\n==> Using %d Hz instead.\n", rate, exact_rate);
}
/* Set number of channels to 1 */
if( snd_pcm_hw_params_set_channels(handle, params, 1 ) < 0 )
{
fprintf(stderr, "Error setting channels.\n");
snd_pcm_close(handle);
return(-1);
}
/* Set number of periods. Periods used to be called fragments. */
periods = 4;
if ( snd_pcm_hw_params_set_periods(handle, params, periods, 0) < 0 )
{
fprintf(stderr, "Error setting periods.\n");
snd_pcm_close(handle);
return(-1);
}
snd_pcm_uframes_t size = (periodsize * periods) >> 2;
if( (rc = snd_pcm_hw_params_set_buffer_size_near( handle, params, &size )) < 0)
{
fprintf(stderr, "Error setting buffersize: [%s]\n", snd_strerror(rc) );
snd_pcm_close(handle);
return(-1);
}
else
{
printf("Buffer size = %lu\n", (unsigned long)size);
}
/* Write the parameters to the driver */
rc = snd_pcm_hw_params(handle, params);
if (rc < 0)
{
fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
snd_pcm_close(handle);
return -1;
}
snd_pcm_hw_params_free(params);
/* Allocate a software parameters object. */
rc = snd_pcm_sw_params_malloc(&sw_params);
if( rc < 0 )
{
fprintf (stderr, "cannot allocate software parameters structure (%s)\n", snd_strerror(rc) );
return(-1);
}
rc = snd_pcm_sw_params_current(handle, sw_params);
if( rc < 0 )
{
fprintf (stderr, "cannot initialize software parameters structure (%s)\n", snd_strerror(rc) );
return(-1);
}
if((rc = snd_pcm_sw_params_set_avail_min(handle, sw_params, 1024)) < 0)
{
fprintf (stderr, "cannot set minimum available count (%s)\n", snd_strerror (rc));
return(-1);
}
rc = snd_pcm_sw_params_set_start_threshold(handle, sw_params, 1);
if( rc < 0 )
{
fprintf(stderr, "Error setting start threshold\n");
snd_pcm_close(handle);
return -1;
}
if((rc = snd_pcm_sw_params(handle, sw_params)) < 0)
{
fprintf (stderr, "cannot set software parameters (%s)\n", snd_strerror (rc));
return(-1);
}
snd_pcm_sw_params_free(sw_params);
return 0;
}
/* copied from libvorbis source */
int ov_fopen(const char *path, OggVorbis_File *vf)
{
int ret = 0;
FILE *f = fopen(path, "rb");
if( f )
{
ret = ov_open(f, vf, NULL, 0);
if( ret )
{
fclose(f);
}
}
else
{
ret = -1;
}
return( ret );
}
int main(int argc, char *argv[])
{
// sample rate * bytes per sample * channel count * seconds
//int bufferSize = 44100 * 2 * 1 * 2;
int err;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
buffer = (char *) malloc( periodsize );
if( buffer )
{
if((err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0)
{
printf("Playback open error #1: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if(err = setup_alsa(handle))
{
printf("Playback open error #2: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
OggVorbis_File vf;
int eof = 0;
int current_section;
err = ov_fopen(argv[1], &vf);
if(err != 0)
{
perror("Error opening file");
}
else
{
vorbis_info *vi = ov_info(&vf, -1);
fprintf(stderr, "Bitstream is %d channel, %ldHz\n", vi->channels, vi->rate);
fprintf(stderr, "Encoded by: %s\n\n", ov_comment(&vf, -1)->vendor);
while(!eof)
{
long ret = ov_read(&vf, buffer, periodsize, 0, 2, 1, &current_section);
if(ret == 0)
{
/* EOF */
eof = 1;
}
else if(ret < 0)
{
/* error in the stream. */
fprintf( stderr, "ov_read error %l", ret );
}
else
{
frames = snd_pcm_writen(handle, (void *)&buffer, ret/2);
if(frames < 0)
{
printf("snd_pcm_writen failed: %s\n", snd_strerror(frames));
if( frames == -EPIPE )
{
snd_pcm_prepare(handle);
//frames = snd_pcm_writen(handle, (void *)&buffer, ret/2);
}
else
{
break;
}
}
}
}
ov_clear(&vf);
}
free( buffer );
snd_pcm_drain(handle);
snd_pcm_close(handle);
}
return 0;
}

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