i have a test app that works successfully with hw:0,1 on gnto if i link it dynamically:
dmx-gnto jutz # ./atestminhw1
Playback device is hw:0,1
Stream parameters are 48000Hz, S16_LE, 6 channels
Rate set to 48000Hz (requested 48000Hz)
Buffer size range from 6 to 5461
Period size range from 3 to 2730
Using max buffer size 5460
Periods = 4
was set period_size = 1365
was set buffer_size = 5460
Channel: 0 Freq: 220.000000
but it fails to find hw:0,1 if i link it statically, is that expected behavior?
dmx-gnto jutz # ./atestminhw1
Playback device is hw:0,1
Stream parameters are 48000Hz, S16_LE, 6 channels
ALSA lib pcm.c:2109:(snd_pcm_open_conf) symbol _snd_pcm_hw_open is not defined inside (null)
Failed: No such device or address
this line succeeds with a dynamic linking and fails with
if(0 > (iErr = snd_pcm_open(&pPcm, gs_pcNameDev, SND_PCM_STREAM_PLAYBACK, 0))) goto fin;
here is my static link arguments:
gcc -g -static -o atestminhw1 atestmin.c -lasound -lm -lpthread -ldl
here is my dynamic link arguments:
gcc -g -o atestminhw1 atestmin.c -lasound
and, for completeness, here is my lame test app, derived from other test apps that come with the source:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>
#include <errno.h>
#include <getopt.h>
#include <inttypes.h>
#include <ctype.h>
#include <byteswap.h>
#define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API
#include <alsa/asoundlib.h>
#include <sys/time.h>
#include <math.h>
#define MAX_CHANNELS 16
static char *gs_pcNameDev = "hw:0,1"; /* playback device */
//static char *gs_pcNameDev = "plughw"; /* playback device */
//static char *gs_pcNameDev = "default"; /* playback device */
static snd_pcm_format_t gs_SndPcmFmt = SND_PCM_FORMAT_S16; /* sample format */
static unsigned int gs_uRate = 48000; /* stream rate */
static unsigned int gs_uCntChan = 6; /* count of channels */
static unsigned int gs_uTimBuf = 0; /* ring buffer length in us */
static unsigned int gs_uTimPer = 0; /* period time in us */
static unsigned int gs_uCntPer = 4; /* number of periods */
static double gs_dFreq = 440; /* sinusoidal wave frequency in Hz */
static snd_pcm_uframes_t gs_uSizBuf;
static snd_pcm_uframes_t gs_uSizPer;
static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, snd_pcm_access_t access)
{
unsigned int uRate;
int iErr;
snd_pcm_uframes_t period_size_min;
snd_pcm_uframes_t period_size_max;
snd_pcm_uframes_t buffer_size_min;
snd_pcm_uframes_t buffer_size_max;
/* choose all parameters */
if (0 > (iErr = snd_pcm_hw_params_any(handle, params)))
{
printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(iErr));
return iErr;
}
/* set the interleaved read/write format */
if (0 > (iErr = snd_pcm_hw_params_set_access(handle, params, access)))
{
printf("Access type not available for playback: %s\n", snd_strerror(iErr));
return iErr;
}
/* set the sample format */
if (0 > (iErr = snd_pcm_hw_params_set_format(handle, params, gs_SndPcmFmt)))
{
printf("Sample format not available for playback: %s\n", snd_strerror(iErr));
return iErr;
}
/* set the count of channels */
if (0 > (iErr = snd_pcm_hw_params_set_channels(handle, params, gs_uCntChan)))
{
printf("Channels count (%i) not available for playbacks: %s\n", gs_uCntChan, snd_strerror(iErr));
return iErr;
}
/* set the stream rate */
uRate = gs_uRate;
if (0 > (iErr = snd_pcm_hw_params_set_rate(handle, params, gs_uRate, 0)))
{
printf("Rate %iHz not available for playback: %s\n", gs_uRate, snd_strerror(iErr));
return iErr;
}
if (uRate != gs_uRate)
{
printf("Rate doesn't match (requested %iHz, get %iHz, err %d)\n", gs_uRate, uRate, iErr);
return -EINVAL;
}
printf("Rate set to %iHz (requested %iHz)\n", uRate, gs_uRate);
/* set the buffer time */
iErr = snd_pcm_hw_params_get_buffer_size_min(params, &buffer_size_min);
iErr = snd_pcm_hw_params_get_buffer_size_max(params, &buffer_size_max);
iErr = snd_pcm_hw_params_get_period_size_min(params, &period_size_min, NULL);
iErr = snd_pcm_hw_params_get_period_size_max(params, &period_size_max, NULL);
printf("Buffer size range from %lu to %lu\n",buffer_size_min, buffer_size_max);
printf("Period size range from %lu to %lu\n",period_size_min, period_size_max);
if (gs_uTimPer > 0)
{
printf("Requested period time %u us\n", gs_uTimPer);
if (0 > (iErr = snd_pcm_hw_params_set_period_time_near(handle, params, &gs_uTimPer, NULL)))
{
printf("Unable to set period time %u us for playback: %s\n",
gs_uTimPer, snd_strerror(iErr));
return iErr;
}
}
if (gs_uTimBuf > 0)
{
printf("Requested buffer time %u us\n", gs_uTimBuf);
if (0 > (iErr = snd_pcm_hw_params_set_buffer_time_near(handle, params, &gs_uTimBuf, NULL)))
{
printf("Unable to set buffer time %u us for playback: %s\n",
gs_uTimBuf, snd_strerror(iErr));
return iErr;
}
}
if (! gs_uTimBuf && ! gs_uTimPer)
{
gs_uSizBuf = buffer_size_max;
if (! gs_uTimPer) gs_uSizBuf = (gs_uSizBuf / gs_uCntPer) * gs_uCntPer;
printf("Using max buffer size %lu\n", gs_uSizBuf);
if (0 > (iErr = snd_pcm_hw_params_set_buffer_size_near(handle, params, &gs_uSizBuf)))
{
printf("Unable to set buffer size %lu for playback: %s\n",
gs_uSizBuf, snd_strerror(iErr));
return iErr;
}
}
if (! gs_uTimBuf || ! gs_uTimPer)
{
printf("Periods = %u\n", gs_uCntPer);
if (0 > (iErr = snd_pcm_hw_params_set_periods_near(handle, params, &gs_uCntPer, NULL)))
{
printf("Unable to set nperiods %u for playback: %s\n",
gs_uCntPer, snd_strerror(iErr));
return iErr;
}
}
snd_pcm_hw_params_get_buffer_size(params, &gs_uSizBuf);
snd_pcm_hw_params_get_period_size(params, &gs_uSizPer, NULL);
printf("was set period_size = %lu\n",gs_uSizPer);
printf("was set buffer_size = %lu\n",gs_uSizBuf);
if (2*gs_uSizPer > gs_uSizBuf) {
printf("buffer to small, could not use\n");
return iErr;
}
/* write the parameters to device */
iErr = snd_pcm_hw_params(handle, params);
if (iErr < 0) {
printf("Unable to set hw params for playback: %s\n", snd_strerror(iErr));
return iErr;
}
return 0;
}
static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams)
{
int iErr;
/* get the current swparams */
if (0 > (iErr = snd_pcm_sw_params_current(handle, swparams)))
{
printf("Unable to determine current swparams for playback: %s\n", snd_strerror(iErr));
return iErr;
}
/* start the transfer when a buffer is full */
if (0 > (iErr = snd_pcm_sw_params_set_start_threshold(handle, swparams, gs_uSizBuf)))
{
printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(iErr));
return iErr;
}
/* allow the transfer when at least period_size frames can be processed */
if (0 > (iErr = snd_pcm_sw_params_set_avail_min(handle, swparams, gs_uSizPer)))
{
printf("Unable to set avail min for playback: %s\n", snd_strerror(iErr));
return iErr;
}
/* align all transfers to 1 sample */
if (0 > (iErr = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1)))
{
printf("Unable to set transfer align for playback: %s\n", snd_strerror(iErr));
return iErr;
}
/* write the parameters to the playback device */
if (0 > (iErr = snd_pcm_sw_params(handle, swparams)))
{
printf("Unable to set sw params for playback: %s\n", snd_strerror(iErr));
return iErr;
}
return 0;
}
static void generate_sine(uint8_t *pu8Frames, int iChannel, int iCntFrames, double *pdPhase, float fFreq)
{
double dPhase = *pdPhase;
double dPhaseMax = 1.0 / fFreq;
double dSizStep = 1.0 / (double)gs_uRate;
double dRes;
int iIdxChan;
int32_t i32Res;
int16_t *pi16Samp = (int16_t*) pu8Frames;
while (iCntFrames-- > 0)
{
for(iIdxChan=0;iIdxChan<gs_uCntChan;iIdxChan++)
if (iIdxChan==iChannel)
{
dRes = (sin((dPhase * 2 * M_PI) / dPhaseMax - M_PI)) * 0x03fffffff; /* Don't use MAX volume */
i32Res = dRes;
*pi16Samp++ = i32Res >>16;
}
else *pi16Samp++ = 0;
dPhase += dSizStep;
if (dPhase >= dPhaseMax)
dPhase -= dPhaseMax;
}
*pdPhase = dPhase;
}
/*
* Underrun and suspend recovery
*/
static int xrun_recovery(snd_pcm_t *pPcm, int iErr)
{
if (iErr == -EPIPE) { /* under-run */
iErr = snd_pcm_prepare(pPcm);
if (iErr < 0)
printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(iErr));
return 0;
}
else if (iErr == -ESTRPIPE) {
while ((iErr = snd_pcm_resume(pPcm)) == -EAGAIN)
sleep(1); /* wait until the suspend flag is released */
if (iErr < 0) {
iErr = snd_pcm_prepare(pPcm);
if (iErr < 0)
printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(iErr));
}
return 0;
}
return iErr;
}
static int write_loop(snd_pcm_t *pPcm, int iChan, int iNumPer, uint8_t *pu8Frames)
{
double phase = 0;
int iErr, iIdxPer, iCntFrames;
uint8_t *pu8FramesLocal;
float fFreq = gs_dFreq*(iChan+1)/2; // provide an audible chan change cue
printf("Channel: %d Freq: %f\n", iChan, fFreq);
for(iIdxPer = 0; iIdxPer < iNumPer; iIdxPer++)
{
iCntFrames = gs_uSizPer;
pu8FramesLocal = pu8Frames;
generate_sine(pu8FramesLocal, iChan, gs_uSizPer, &phase, fFreq);
while (0 < iCntFrames)
{
if (-EAGAIN == (iErr = snd_pcm_writei(pPcm, pu8FramesLocal, iCntFrames)))
continue;
if (0 > iErr)
{
printf("Write error: %d,%s\n", iErr, snd_strerror(iErr));
if (0 > xrun_recovery(pPcm, iErr))
{
printf("xrun_recovery failed: %d,%s\n", iErr, snd_strerror(iErr));
return -1;
}
break; // skip one period
}
pu8FramesLocal += snd_pcm_frames_to_bytes(pPcm, iErr);
iCntFrames -= iErr;
}
}
if (gs_uSizBuf > iIdxPer * gs_uSizPer)
{
snd_pcm_drain(pPcm);
snd_pcm_prepare(pPcm );
}
return 0;
}
int main(int argc, char *argv[])
{
int iErr;
int iIdxChan;
uint8_t *pu8Frames = NULL;
snd_pcm_t *pPcm = NULL;
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;
snd_pcm_hw_params_alloca(&hwparams);
snd_pcm_sw_params_alloca(&swparams);
printf("Playback device is %s\n", gs_pcNameDev);
printf("Stream parameters are %iHz, %s, %i channels\n", gs_uRate, snd_pcm_format_name(gs_SndPcmFmt), gs_uCntChan);
if(0 > (iErr = snd_pcm_open(&pPcm, gs_pcNameDev, SND_PCM_STREAM_PLAYBACK, 0))) goto fin;
if(0 > (iErr = set_hwparams(pPcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED))) goto fin;
if(0 > (iErr = set_swparams(pPcm, swparams))) goto fin;
if(!(pu8Frames = malloc(snd_pcm_frames_to_bytes(pPcm, gs_uSizPer)))) goto fin;
while(1)
for(iIdxChan = 0; iIdxChan < gs_uCntChan; iIdxChan++)
if (0> (iErr = write_loop(pPcm, iIdxChan, (gs_uRate*3)/gs_uSizPer, pu8Frames)))
goto fin;
iErr = EXIT_SUCCESS;
fin:
if(iErr) printf("Failed: %s\n",snd_strerror(iErr));
if(pu8Frames) free(pu8Frames);
if(pPcm) snd_pcm_close(pPcm);
exit(iErr);
}