[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

Reply via email to