[continued from previous post]
On Wed, 2008-11-19 at 05:45 +0100, Jens M Andreasen wrote:
> Hmm ... I could try and open the three raw devices from the same app and
> see how that will work out, if they perhaps are drifting?
> Tomorrow! :)
>
> /j
.. which I have done now:
a) One program 'capture' opens two devices ("hw:0,0,0" and "hw:0,0,1")
and merges them into a single stereo image which is piped out on stdout.
b) another program 'playback' opens a third device ("hw:0,0") for output
and reads a stereo image from the pipe on stdin
used as: ./capture|./playback
.. and works without flaws
What does NOT work - and what might be a hint to what is wrong - is
starting jackd as either playback- or capture only (with -P or -C). Only
the duplex mode -D is running stable.
I don't get this? :-/
/* -*- mode: c; c-file-style: "ellemtel"; -*- */
/** Makefile *************************
capture: play.c
gcc -D MAIN -D CAPTURE -msse -o capture play.c -lasound
play: play.c
gcc -D MAIN -msse -o play play.c -lasound
*************************************** **/
//-- play.h -----------------------------
#ifdef __cplusplus
extern "C"
{
#else
typedef enum { false, true } bool;
#endif
#define ALSA_PCM_NEW_HW_PARAMS_API
#include <alsa/asoundlib.h>
extern snd_pcm_t* alsa_play_init (
unsigned int samplerate,
unsigned int periods,
snd_pcm_uframes_t samples, /* number of samples in each period */
bool hda, /* 16bit (false) or 32bit HD Audio (true) */
unsigned int channels, /* number of interleaved channels */
const char* device /* defaults to: "hw:0,0" */
);
extern snd_pcm_t* alsa_capture_init (
unsigned int samplerate,
unsigned int periods,
snd_pcm_uframes_t samples, /* number of samples in each period */
bool hda, /* 16bit (false) or 32bit HD Audio (true) */
unsigned int channels, /* number of interleaved channels */
const char* device /* defaults to: "hw:0,0" */
);
extern int alsa_play (
snd_pcm_t* pcm_handle,
char* buffer,
snd_pcm_uframes_t samples
);
extern int alsa_capture (
snd_pcm_t* pcm_handle,
char * buffer,
snd_pcm_uframes_t samples
);
#ifdef __cplusplus
}
#endif
// EOF play.h -----------------------------------------------------------
/* These functions here are mostly for my own convenience dealing with
* alsa, CUDA and Intel HD Audio. They may not work with your setup, but ...
*
* I have recently noticed that the ALSA project has a similar simplified
* interface in the pipe, which may be ready by he time you read this notice.
* As of late 2008 those functions still comes with a Warning though:
* - "The simple PCM API may be broken in the current release."
*
* Anyway, look up/google for: snd_spcm_init() and snd_spcm_init_duplex().
*/
#include <sched.h>
#include <unistd.h>
//#include "play.h"
#include <tmmintrin.h>
#include <xmmintrin.h>
// various information on x86 processor type
#define CPUID(f,ax,bx,cx,dx) __asm__ __volatile__ \
("cpuid": "=a" (ax), "=b" (bx), "=c" (cx), "=d" (dx) : "a" (f));
static int set_DAZ_and_FTZ(bool on)
{
int sse_level = 0;
if(on)
{
unsigned long ax, bx, cx, dx;
CPUID(0x00,ax,bx,cx,dx);
CPUID(0x01,ax,bx,cx,dx);
if (dx & 0x02000000)
{
sse_level = 1;
// set FLUSH_TO_ZERO to ON and
// set round towards zero (RZ)
_mm_setcsr(_mm_getcsr() | 0x8000|0x6000);
if (dx & 0x04000000)
{
sse_level = 2;
if (cx & 0x00000001)
{
sse_level = 3;
// set DENORMALS_ARE_ZERO to ON
_mm_setcsr(_mm_getcsr() | 0x0040);
}
// we should check for AMD K8 without SSE3 here ...
// if(AMD_K8_NO_SSE3)
// ...
}
}
} else
// clear underflow and precision flags
// and set DAZ and FTZ to OFF
// and restore round to nearest (RN)
_mm_setcsr(_mm_getcsr() & ~(0x0030|0x8000|0x0040|0x6000));
return sse_level;
}
#include <signal.h>
// sigset obsolete?
typedef void (*sighandler_t)(int);
sighandler_t sigset(int sig, sighandler_t disp);
#include <unistd.h>
static void bye(int value)
{
fprintf(stderr,"exit (%d)\n",value);
exit(value);
}
/**
* Adapted from ALSA 0.9.0 HOWTO
* by Matthias Nagorni, last update: 6 May 2002
*
*/
static snd_pcm_t* alsa_pcm_init(
unsigned int rate, unsigned int periods, snd_pcm_uframes_t periodsize,
bool hda, unsigned int channels,const char* device,bool capture)
{
set_DAZ_and_FTZ(true);
sigset(SIGINT,&bye);
sigset(SIGTERM,&bye);
// Name of the PCM device, like plughw:0,0
// The first number is the number of the soundcard,
// the second number is the number of the device.
char *pcm_name;
if(device)
pcm_name = strdup(device);
else
pcm_name = strdup("hw:0,0");
// Handle for the PCM device
snd_pcm_t *pcm_handle;
// Playback or capture stream
snd_pcm_stream_t stream;
if(capture)
stream = SND_PCM_STREAM_CAPTURE;
else
stream = SND_PCM_STREAM_PLAYBACK;
// This structure contains information about
// the hardware and can be used to specify the
// configuration to be used for the PCM stream.
snd_pcm_hw_params_t *hwparams;
// Allocate the snd_pcm_hw_params_t structure on the stack.
snd_pcm_hw_params_malloc(&hwparams);
// Open PCM. The last parameter of this function is the mode.
// If this is set to 0, the standard mode is used. Possible
// other values are SND_PCM_NONBLOCK and SND_PCM_ASYNC.
// If SND_PCM_NONBLOCK is used, read / write access to the
// PCM device will return immediately. If SND_PCM_ASYNC is
// specified, SIGIO will be emitted whenever a period has
// been completely processed by the soundcard.
if (snd_pcm_open(&pcm_handle, pcm_name, stream, 0) < 0) {
fprintf(stderr, "Error opening PCM device %s\n", pcm_name);
return(0);
}
// Init hwparams with full configuration space
if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
fprintf(stderr, "Can not configure this PCM device.\n");
return(0);
}
// Sample rate returned by
// snd_pcm_hw_params_set_rate_near
unsigned int exact_rate;
// Set access type. This can be either
// SND_PCM_ACCESS_RW_INTERLEAVED or
// SND_PCM_ACCESS_RW_NONINTERLEAVED.
// There are also access types for MMAPed
// access, but this is beyond the scope
// of this introduction.
if (snd_pcm_hw_params_set_access(pcm_handle,hwparams,
SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
fprintf(stderr, "Error setting access.\n");
return(0);
}
// Set sample format
if(hda)
{
if (snd_pcm_hw_params_set_format(pcm_handle,hwparams,
SND_PCM_FORMAT_S32_LE) < 0) {
fprintf(stderr, "Error setting HD Audio format.\n");
return(0);
}
}
else
{
if (snd_pcm_hw_params_set_format(pcm_handle,hwparams,
SND_PCM_FORMAT_S16_LE) < 0) {
fprintf(stderr, "Error setting CD/DAT format.\n");
return(0);
}
}
// 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(pcm_handle,hwparams,
&exact_rate, 0) < 0) {
fprintf(stderr, "Error setting rate.\n");
return(0);
}
if (rate != exact_rate) {
fprintf(stderr,
"The rate %d Hz is not supported by your hardware.\n"
"==> Use %d Hz instead.\n", rate, exact_rate);
return 0;
}
// Set number of channels
if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, channels) < 0) {
fprintf(stderr, "Error setting channels.\n");
return(0);
}
// Set number of periods. Periods used to be called fragments.
if (snd_pcm_hw_params_set_periods(pcm_handle, hwparams,
periods, 0) < 0) {
fprintf(stderr, "Error setting periods: %d\n",periods);
return(0);
}
// Set buffer size (in frames). The resulting latency is given by
// latency = periodsize * periods / rate
if (snd_pcm_hw_params_set_buffer_size(pcm_handle,hwparams,
periodsize * periods) < 0) {
fprintf(stderr,
"\nError setting buffersize.\n"
"Periodsize == %u not supported for %s\n\n",
(uint)periodsize,pcm_name);
return(0);
}
// Apply HW parameter settings to
// PCM device and prepare device
if (snd_pcm_hw_params(pcm_handle, hwparams) < 0) {
fprintf(stderr, "Error setting HW params.\n");
return(0);
}
// We would like to run in realtime
if((periods * periodsize) < 2048)
{
static struct sched_param schp;
memset(&schp, 0, sizeof(schp));
schp.sched_priority = 50;//sched_get_priority_max(SCHED_FIFO) * 0.9;
if (sched_setscheduler (0, SCHED_FIFO, &schp) != 0)
fprintf(stderr,"\n Sorry, can't get permission to run in realtime!\n\n");
}
else fprintf(stderr,"\n not running in realtime!\n\n");
return pcm_handle;
}
snd_pcm_t* alsa_play_init(
unsigned int rate, unsigned int periods, snd_pcm_uframes_t periodsize,
bool hda, unsigned int channels,const char* device)
{
bool capture = false;
return alsa_pcm_init(rate, periods, periodsize,
hda, channels, device,capture);
}
snd_pcm_t* alsa_capture_init(
unsigned int rate, unsigned int periods, snd_pcm_uframes_t periodsize,
bool hda, unsigned int channels,const char* device)
{
bool capture = true;
return alsa_pcm_init(rate, periods, periodsize,
hda, channels, device,capture);
}
int alsa_play(snd_pcm_t* pcm_handle, char * buffer, snd_pcm_uframes_t samples)
{
int rc = snd_pcm_writei(pcm_handle, buffer, samples);
if (rc == -EPIPE)
{
// EPIPE means underrun
//fprintf(stderr, "underrun occurred\n");
snd_pcm_prepare(pcm_handle);
}
else if (rc < 0)
{
fprintf(stderr,
"error from writei: %s\n",
snd_strerror(rc));
}
else if (rc != (int)samples)
{
fprintf(stderr,
"short write, wrote %d frames\n", rc);
}
return rc;
}
int alsa_capture(snd_pcm_t* pcm_handle, char * buffer, snd_pcm_uframes_t samples)
{
int rc = snd_pcm_readi(pcm_handle, buffer, samples);
if (rc == -EPIPE)
{
// EPIPE means overrrun
//fprintf(stderr, "overrrun occurred\n");
snd_pcm_prepare(pcm_handle);
}
else if (rc < 0)
{
fprintf(stderr,
"error from readi: %s\n",
snd_strerror(rc));
}
else if (rc != (int)samples)
{
fprintf(stderr,
"short read, read %d frames\n", rc);
}
return rc;
}
#ifdef MAIN
int main(int argc,char** argv)
{
int samplerate = 48000;
int periods = 3;
snd_pcm_uframes_t periodsize = 128;
bool hda = true;
int channels = 2;
char* device = NULL; // defaults to hw:0:0;
snd_pcm_t* pcm_handle[2];
int rc; // io error
// Use a stdio buffer large enough to hold one period
int size = periodsize * channels * (hda?4:2);
int *pcm = malloc(size);
bzero(pcm,size);
#ifdef CAPTURE
int *mix = malloc(size);
bzero(mix,size);
pcm_handle[0]= alsa_capture_init(samplerate,
periods, periodsize,
hda, channels, "hw:0,0,0");
if(!pcm_handle[0]) exit(EXIT_FAILURE);
pcm_handle[1]= alsa_capture_init(samplerate,
periods, periodsize,
hda, channels, "hw:0,0,1");
if(!pcm_handle[1]) exit(EXIT_FAILURE);
for (;;) {
int i;
alsa_capture(pcm_handle[0],(char*)pcm,periodsize);
i = 1;
alsa_capture(pcm_handle[1],(char*)mix,periodsize);
for(i = 0; i < periodsize*2; ++i)
mix[i] = (pcm[i]>>1) + (mix[i]>>1);
rc = write(1, mix, size);
if (rc == 0) {
fprintf(stderr, "end of file on input\n");
break;
} else if (rc != size) {
fprintf(stderr,
"short read: read %d bytes\n", rc);
}
}
#else // PLAYBACK
pcm_handle[0] = alsa_play_init(samplerate,
periods, periodsize,
hda, channels, device);
if(!pcm_handle[0]) exit(EXIT_FAILURE);
for (;;) {
rc = read(0, pcm, size);
if (rc == 0) {
fprintf(stderr, "end of file on input\n");
break;
} else if (rc != size) {
fprintf(stderr,
"short read: read %d bytes\n", rc);
}
alsa_play(pcm_handle[0],(char*)pcm,periodsize);
}
#endif
// This program will never get to the cleanup phase ...
snd_pcm_drain(pcm_handle[0]);
snd_pcm_close(pcm_handle[0]);
free(pcm);
return 0;
}
#endif
_______________________________________________
Linux-audio-dev mailing list
[email protected]
http://lists.linuxaudio.org/mailman/listinfo/linux-audio-dev