Paul Davis wrote:
On Mon, 2006-01-30 at 20:05 +0000, James Courtier-Dutton wrote:
[EMAIL PROTECTED] wrote:
Has anyone managed to successfully get jackd talking to dmix?

My dmix setup works fix (i run VoIP stuff, artsd, and other assorted junk through it daily).

jackd works great when connect to hw:0,0 or to plughw:0,0 (with the warning), but if I point it at my dmix'd "default" it sits there with 100% CPU usage, and doesn't otherwise operate.

Does anyone have this combination of jack and dmix working?

It is due to the fact that the alsa programming in jackd has been implemented wrongly. The poll revents are not handled correctly. This results is OK operation when using hw:0,0, but likely to fail for more exotic alsa configurations like dmix.

i will happily take a patch for this. or is it just the MMAP_COMPLEX
patch and nothing more?

--p


Paul,

If I get a moment, I will try to get a patch together for jackd this weekend. It is just a matter of adding a few lines of code, so hopefully won't take long.

If you want to make a head start, you need to add
snd_pcm_poll_descriptors_revents() calls.

I attach some GPL code from another project that should illustrate the method to use.

James
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <alsa/asoundlib.h>
#include <inttypes.h>
#include <time.h>

#if 0
#define ALSA_LOG
#define ALSA_LOG_BUFFERS
#endif
#if 0
#define ALSA_PLAYBACK_LOG
#define ALSA_CAPTURE_LOG
#endif
#define BUFFER_TIME               2000*1000


typedef struct alsa_driver_s {
	snd_pcm_t	*audio_fd;
	int		 capabilities;
	int		 open_mode;
	int		 has_pause_resume;
	int		 is_paused;
	int32_t		 output_sample_rate, input_sample_rate;
	double		 sample_rate_factor;
	uint32_t	 num_channels;
	uint32_t	 bits_per_sample;
	uint32_t	 bytes_per_frame;
	uint32_t	 bytes_in_buffer;      /* number of bytes writen to audio hardware   */
	int16_t		*app_buffer_y1;
	int16_t		*app_buffer_y2;
	int		*app_buffer_offset;
	int		 app_buffer_length;
	double		*Tsec;
	double		*tbuf;
	int		*ibuf;
	int		*ndsec;
	int		*tx_ok;
	int		 tx_starting;
	int		 tx_offset;
	int		*tr_period;
	int		*nwave;
	int		*nmode;
	int		*transmitting;

	snd_pcm_uframes_t  buffer_size;
	snd_pcm_uframes_t  period_size;
	int32_t		 mmap; 
} alsa_driver_t;

alsa_driver_t alsa_driver_playback;
alsa_driver_t alsa_driver_capture;
void *alsa_capture_buffers[2];
void *alsa_playback_buffers[2];

static snd_output_t *jcd_out;

/*
 * open the audio device for writing to
 */
static int ao_alsa_open(alsa_driver_t *this_gen, int32_t *input_rate, snd_pcm_stream_t direction ) {
  alsa_driver_t        *this = (alsa_driver_t *) this_gen;
  char                 *pcm_device;
  snd_pcm_hw_params_t  *params;
  snd_pcm_sw_params_t  *swparams;
  snd_pcm_access_mask_t *mask;
  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;
  snd_pcm_format_t      format;
  uint32_t              buffer_time=BUFFER_TIME;
  snd_pcm_uframes_t     buffer_time_to_size;
  int                   err, dir;
  int                 open_mode=1; /* NONBLOCK */
  /* int                   open_mode=0;  BLOCK */
  int32_t            rate=*input_rate;
  this->input_sample_rate=*input_rate;

  snd_pcm_hw_params_alloca(&params);
  snd_pcm_sw_params_alloca(&swparams);
  err = snd_output_stdio_attach(&jcd_out, stdout, 0);
  
  this->num_channels = 2;
  pcm_device="default";
#ifdef ALSA_LOG
  printf("audio_alsa_out: Audio Device name = %s\n",pcm_device);
  printf("audio_alsa_out: Number of channels = %d\n",this->num_channels);
#endif

  if (this->audio_fd) {
    printf("audio_alsa_out:Already open...WHY!");
    snd_pcm_close (this->audio_fd);
    this->audio_fd = NULL;
  }

  this->bytes_in_buffer        = 0;
  /*
   * open audio device
   */
  err=snd_pcm_open(&this->audio_fd, pcm_device, direction, open_mode);      
  if(err <0 ) {                                                           
    printf ("audio_alsa_out: snd_pcm_open() of %s failed: %s\n", pcm_device, snd_strerror(err));               
    printf ("audio_alsa_out: >>> check if another program already uses PCM <<<\n");
    return 0;
  }
  /* printf ("audio_alsa_out: snd_pcm_open() opened %s\n", pcm_device); */ 
  /* We wanted non blocking open but now put it back to normal */
  //snd_pcm_nonblock(this->audio_fd, 0);
  snd_pcm_nonblock(this->audio_fd, 1);
  /*
   * configure audio device
   */
  err = snd_pcm_hw_params_any(this->audio_fd, params);
  if (err < 0) {
    printf ("audio_alsa_out: broken configuration for this PCM: no configurations available: %s\n"),
	     snd_strerror(err);
    goto close;
  }
  /* set interleaved access */
  if (this->mmap != 0) {
    mask = alloca(snd_pcm_access_mask_sizeof());
    snd_pcm_access_mask_none(mask);
    snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
    snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
    snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_COMPLEX);
    err = snd_pcm_hw_params_set_access_mask(this->audio_fd, params, mask);
    if (err < 0) {
      printf ( "audio_alsa_out: mmap not availiable, falling back to compatiblity mode\n");
      this->mmap=0;
      err = snd_pcm_hw_params_set_access(this->audio_fd, params,
                                     SND_PCM_ACCESS_RW_NONINTERLEAVED);
    }
  } else {
    err = snd_pcm_hw_params_set_access(this->audio_fd, params,
                                     SND_PCM_ACCESS_RW_NONINTERLEAVED);
  }
      
  if (err < 0) {
    printf ( "audio_alsa_out: access type not available: %s\n", snd_strerror(err));
    goto close;
  }
  /* set the sample format S16 */
  /* ALSA automatically appends _LE or _BE depending on the CPU */
  format = SND_PCM_FORMAT_S16;
  err = snd_pcm_hw_params_set_format(this->audio_fd, params, format );
  if (err < 0) {
    printf ( "audio_alsa_out: sample format non available: %s\n", snd_strerror(err));
    goto close;
  }
  /* set the number of channels */
  err = snd_pcm_hw_params_set_channels(this->audio_fd, params, this->num_channels);
  if (err < 0) {
    printf ( "audio_alsa_out: Cannot set number of channels to %d (err=%d:%s)\n", 
	     this->num_channels, err, snd_strerror(err));
    goto close;
  }
#if SND_LIB_VERSION >= 0x010009
  /* Restrict a configuration space to contain only real hardware rates */
  err = snd_pcm_hw_params_set_rate_resample(this->audio_fd, params, 0);
#endif
  /* set the stream rate [Hz] */
  dir=0;
  err = snd_pcm_hw_params_set_rate_near(this->audio_fd, params, &rate, &dir);
  if (err < 0) {
    printf ( "audio_alsa_out: rate not available: %s\n", snd_strerror(err));
    goto close;
  }
  this->output_sample_rate = (uint32_t)rate;
  if (this->input_sample_rate != this->output_sample_rate) {
    printf ( "audio_alsa_out: audio rate : %d requested, %d provided by device/sec\n",
	     this->input_sample_rate, this->output_sample_rate);
  }
  buffer_time_to_size = ( (uint64_t)buffer_time * rate) / 1000000;
  err = snd_pcm_hw_params_get_buffer_size_min(params, &buffer_size_min);
  err = snd_pcm_hw_params_get_buffer_size_max(params, &buffer_size_max);
  dir=0;
  err = snd_pcm_hw_params_get_period_size_min(params, &period_size_min,&dir);
  dir=0;
  err = snd_pcm_hw_params_get_period_size_max(params, &period_size_max,&dir);
#ifdef ALSA_LOG_BUFFERS
  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);
  printf("Buffer time size %lu\n",buffer_time_to_size);
#endif
  this->buffer_size = buffer_time_to_size;
  if (buffer_size_max < this->buffer_size)
	this->buffer_size = buffer_size_max;
  if (buffer_size_min > this->buffer_size)
	this->buffer_size = buffer_size_min;
  this->period_size = this->buffer_size/8;
  if (this->period_size > 2048)
	this->period_size = 2048;
  this->buffer_size = this->period_size*8;
#ifdef ALSA_LOG_BUFFERS
  printf("To choose buffer_size = %ld\n",this->buffer_size);
  printf("To choose period_size = %ld\n",this->period_size);
#endif

#if 0
  /* Set period to buffer size ratios at 8 periods to 1 buffer */
  dir=-1;
  periods=8;
  err = snd_pcm_hw_params_set_periods_near(this->audio_fd, params, &periods ,&dir);
  if (err < 0) {
    xprintf (this->class->xine, XINE_VERBOSITY_DEBUG, 
	     "audio_alsa_out: unable to set any periods: %s\n", snd_strerror(err));
    goto close;
  }
  /* set the ring-buffer time [us] (large enough for x us|y samples ...) */
  dir=0;
  err = snd_pcm_hw_params_set_buffer_time_near(this->audio_fd, params, &buffer_time, &dir);
  if (err < 0) {
    xprintf (this->class->xine, XINE_VERBOSITY_DEBUG, 
	     "audio_alsa_out: buffer time not available: %s\n", snd_strerror(err));
    goto close;
  }
#endif
#if 1
  /* set the period time [us] (interrupt every x us|y samples ...) */
  dir=0;
  err = snd_pcm_hw_params_set_period_size_near(this->audio_fd, params, &(this->period_size), &dir);
  if (err < 0) {
    printf ( "audio_alsa_out: period time not available: %s\n", snd_strerror(err));
    goto close;
  }
#endif
  dir=0;
  err = snd_pcm_hw_params_get_period_size(params, &(this->period_size), &dir);

  dir=0;
  err = snd_pcm_hw_params_set_buffer_size_near(this->audio_fd, params, &(this->buffer_size));
  if (err < 0) {
    printf ( "audio_alsa_out: buffer time not available: %s\n", snd_strerror(err));
    goto close;
  }
  err = snd_pcm_hw_params_get_buffer_size(params, &(this->buffer_size));
#ifdef ALSA_LOG_BUFFERS
  printf("was set period_size = %ld\n",this->period_size);
  printf("was set buffer_size = %ld\n",this->buffer_size);
#endif
  if (2*this->period_size > this->buffer_size) {
    printf ( "audio_alsa_out: buffer to small, could not use\n");
    goto close;
  }
  
  /* write the parameters to device */
  err = snd_pcm_hw_params(this->audio_fd, params);
  if (err < 0) {
    printf ( "audio_alsa_out: pcm hw_params failed: %s\n", snd_strerror(err));
    goto close;
  }
  /* Check for pause/resume support */
  this->has_pause_resume = ( snd_pcm_hw_params_can_pause (params)
			    && snd_pcm_hw_params_can_resume (params) );
  //  printf( "audio_alsa_out:open pause_resume=%d\n", this->has_pause_resume);
  this->sample_rate_factor = (double) this->output_sample_rate / (double) this->input_sample_rate;
  this->bytes_per_frame = snd_pcm_frames_to_bytes (this->audio_fd, 1);
  /*
   * audio buffer size handling
   */
  /* Copy current parameters into swparams */
  err = snd_pcm_sw_params_current(this->audio_fd, swparams);
  if (err < 0) {
    printf ( "audio_alsa_out: Unable to determine current swparams: %s\n", snd_strerror(err));
    goto close;
  }
  /* align all transfers to 1 sample */
  err = snd_pcm_sw_params_set_xfer_align(this->audio_fd, swparams, 1);
  if (err < 0) {
    printf ( "audio_alsa_out: Unable to set transfer alignment: %s\n", snd_strerror(err));
    goto close;
  }
  /* allow the transfer when at least period_size samples can be processed */
  err = snd_pcm_sw_params_set_avail_min(this->audio_fd, swparams, this->period_size);
  if (err < 0) {
    printf ( "audio_alsa_out: Unable to set available min: %s\n", snd_strerror(err));
    goto close;
  }
  if (direction == SND_PCM_STREAM_PLAYBACK) {
  	/* start the transfer when the buffer contains at least period_size samples */
	err = snd_pcm_sw_params_set_start_threshold(this->audio_fd, swparams, this->buffer_size);
  } else {
	err = snd_pcm_sw_params_set_start_threshold(this->audio_fd, swparams, -1);
  }
  if (err < 0) {
    printf ( "audio_alsa_out: Unable to set start threshold: %s\n", snd_strerror(err));
    goto close;
  }

  if (direction == SND_PCM_STREAM_PLAYBACK) {
        /* never stop the transfer, even on xruns */
  	err = snd_pcm_sw_params_set_stop_threshold(this->audio_fd, swparams, this->buffer_size);
  } else {
  	err = snd_pcm_sw_params_set_stop_threshold(this->audio_fd, swparams, this->buffer_size);
  }
  if (err < 0) {
    printf ( "audio_alsa_out: Unable to set stop threshold: %s\n", snd_strerror(err));
    goto close;
  }

  /* Install swparams into current parameters */
  err = snd_pcm_sw_params(this->audio_fd, swparams);
  if (err < 0) {
    printf ( "audio_alsa_out: Unable to set swparams: %s\n", snd_strerror(err));
    goto close;
  }
#ifdef ALSA_LOG
  snd_pcm_dump_setup(this->audio_fd, jcd_out); 
  snd_pcm_sw_params_dump(swparams, jcd_out);
#endif
  
  return this->output_sample_rate;

close:
  snd_pcm_close (this->audio_fd);
  this->audio_fd=NULL;
  return 0;
}

int16_t zero_buffer[65536];

int playback_callback(alsa_driver_t *alsa_driver_playback) {
	alsa_driver_t *this = alsa_driver_playback;
	int result;
	struct timeval tv;
	double stime;
	int nsec;
	int i,n;
	static int ic;
	snd_pcm_sframes_t delay;	
	static short int n2;
	int16_t b0[2048];

//	printf("playback callback\n");
	snd_pcm_delay(this->audio_fd, &delay);
	gettimeofday(&tv, NULL);
	stime = (double) tv.tv_sec + ((double)tv.tv_usec / 1000000.0) +
		*(this->ndsec) * 0.1;
	stime = stime + ((double)delay / (double)(this->output_sample_rate));
	*(this->Tsec) = stime;
	//printf("PLAY:TIME, %lf, %ld, %ld, %d\n", stime, delay, this->output_sample_rate, *this->ndsec);
	if(!(this->tx_starting) && (*(this->tx_ok)) ) {
		nsec = (int)stime;
		n = nsec / *(this->tr_period);  
		ic = (int)(stime - *(this->tr_period) * n) * this->output_sample_rate;
		ic = ic % *(this->nwave);
		this->tx_offset = ic;
	}
	this->tx_starting = *(this->tx_ok);
	*(this->transmitting) = *(this->tx_ok);
	if(*(this->tx_ok)) {
	  /*
		alsa_playback_buffers[0] = this->app_buffer_y1 + this->tx_offset;
		alsa_playback_buffers[1] = this->app_buffer_y1 + this->tx_offset;
	  */
	  alsa_playback_buffers[0] = b0;
	  alsa_playback_buffers[1] = b0;
	  for(i=0; i<this->period_size; i++) {
	    n2=this->app_buffer_y1[ic];
	    addnoise_(&n2);
	    b0[i]=n2;
	    ic++;
	    if(ic>=*this->nwave) {
	      if(*this->nmode==2) {
		*this->tx_ok=0;
		ic--;
	      }
	      else
		ic = ic % *this->nwave;       //Wrap buffer pointer
	    }
	  }
	} else {
		alsa_playback_buffers[0] = zero_buffer;
		alsa_playback_buffers[1] = zero_buffer;
	}
	result = snd_pcm_writen(this->audio_fd, alsa_playback_buffers, this->period_size);
	this->tx_offset += this->period_size;
	if (result != this->period_size) {
		printf("Playback write failed. Expected %d samples, sent only %d\n", this->period_size, result);
#ifdef ALSA_PLAYBACK_LOG
		snd_pcm_status_t *pcm_stat;
		snd_pcm_status_alloca(&pcm_stat);
		snd_pcm_status(this->audio_fd, pcm_stat);
		snd_pcm_status_dump(pcm_stat, jcd_out);
#endif
	}
  	fivehztx_();                             //Call fortran routine
}

int capture_callback(alsa_driver_t *alsa_driver_capture) {
	alsa_driver_t *this = alsa_driver_capture;
	int result;
	struct timeval tv;
	double stime;
	int ib;
	snd_pcm_sframes_t delay;	
#ifdef ALSA_CAPTURE_LOG
	printf("capture callback %d samples\n", this->period_size);
#endif
#ifdef ALSA_CAPTURE_LOG
	snd_pcm_status_t *pcm_stat;
	snd_pcm_status_alloca(&pcm_stat);
	snd_pcm_status(this->audio_fd, pcm_stat);
        snd_pcm_status_dump(pcm_stat, jcd_out);
#endif
	snd_pcm_delay(this->audio_fd, &delay);
	gettimeofday(&tv, NULL);
	stime = (double) tv.tv_sec + ((double)tv.tv_usec / 1000000.0) +
		*(this->ndsec) * 0.1;
	stime = stime - ((double)delay / (double)(this->output_sample_rate));
	*(this->Tsec) = stime;
	ib=*(this->ibuf);
	this->tbuf[ib] = stime;
	//printf("CAP:TIME, %d, %lf, %ld, %ld, %d\n",ib, stime, delay, this->output_sample_rate, *this->ndsec);
	ib++;
	if(ib>=1024)
		ib = 0;
	*(this->ibuf) = ib;

	alsa_capture_buffers[0]=this->app_buffer_y1 + *(this->app_buffer_offset);
	alsa_capture_buffers[1]=this->app_buffer_y2 + *(this->app_buffer_offset);
	result = snd_pcm_readn(this->audio_fd, alsa_capture_buffers, this->period_size);
	*(this->app_buffer_offset) += this->period_size;
	if ( *(this->app_buffer_offset) >= this->app_buffer_length )
		*(this->app_buffer_offset)=0;  /* FIXME: implement proper wrapping */
#ifdef ALSA_CAPTURE_LOG
	printf("result=%d\n",result);
	snd_pcm_status(this->audio_fd, pcm_stat);
        snd_pcm_status_dump(pcm_stat, jcd_out);
#endif
	fivehz_();                             //Call fortran routine
}

int playback_xrun(alsa_driver_t *alsa_driver_playback) {
	alsa_driver_t *this = alsa_driver_playback;
	snd_pcm_status_t *pcm_stat;
	snd_pcm_status_alloca(&pcm_stat);
	printf("playback xrun\n");
	snd_pcm_status(this->audio_fd, pcm_stat);
        snd_pcm_status_dump(pcm_stat, jcd_out);
	snd_pcm_prepare(this->audio_fd);
}

int capture_xrun(alsa_driver_t *alsa_driver_capture) {
	alsa_driver_t *this = alsa_driver_capture;
	snd_pcm_status_t *pcm_stat;
	snd_pcm_status_alloca(&pcm_stat);
	printf("capture xrun\n");
	snd_pcm_status(this->audio_fd, pcm_stat);
        snd_pcm_status_dump(pcm_stat, jcd_out);
}

void ao_alsa_loop(void *iarg) {
	int playback_nfds;
	int capture_nfds;
	struct pollfd *pfd;
	int nfds;
	int capture_index;
	unsigned short playback_revents;
	unsigned short capture_revents;
	playback_nfds = snd_pcm_poll_descriptors_count (
				alsa_driver_playback.audio_fd);
	capture_nfds = snd_pcm_poll_descriptors_count (
				alsa_driver_capture.audio_fd);
	pfd = (struct pollfd *) malloc (sizeof (struct pollfd) * 
		(playback_nfds + capture_nfds));
	
	nfds=0;	
	snd_pcm_poll_descriptors (alsa_driver_playback.audio_fd,
		&pfd[0],
		playback_nfds);
	nfds += playback_nfds;
	snd_pcm_poll_descriptors (alsa_driver_capture.audio_fd,
		&pfd[nfds],
		capture_nfds);
	capture_index = nfds;
	nfds += capture_nfds;
	while(1) {
		if (poll (pfd, nfds, 100000) < 0) {
			printf("poll failed\n");
			return;
		}
		snd_pcm_poll_descriptors_revents(alsa_driver_playback.audio_fd, &pfd[0], playback_nfds, &playback_revents);
		snd_pcm_poll_descriptors_revents(alsa_driver_capture.audio_fd, &pfd[capture_index], capture_nfds, &capture_revents);
		//if ((playback_revents & POLLERR) || ((capture_revents) & POLLERR)) {
		if (((capture_revents) & POLLERR)) {
			printf("pollerr\n");
			capture_xrun(&alsa_driver_capture);
			return;
		}
		if (((playback_revents) & POLLERR)) {
			printf("pollerr\n");
			playback_xrun(&alsa_driver_capture);
			return;
		}
		if (playback_revents & POLLOUT) {
			playback_callback(&alsa_driver_playback);
		}
		if (capture_revents & POLLIN) {
			capture_callback(&alsa_driver_capture);
		}
	}
		
	return;
}

extern void decode1_(int *iarg);

int start_threads_(int *ndevin, int *ndevout, short y1[], short y2[],
	int *nbuflen, int *iwrite, short iwave[],
	int *nwave, int *nfsample, int *nsamperbuf,
	int *TRPeriod, int *TxOK, int *ndebug,
	int *Transmitting, double *Tsec, int *ngo, int *nmode,
	double tbuf[], int *ibuf, int *ndsec)
{
  pthread_t thread1,thread2;
  int iret1,iret2;
  int iarg1 = 1,iarg2 = 2;
  //int32_t rate=11025;
  int32_t rate=*nfsample;
  alsa_driver_capture.app_buffer_y1 = y1;
  alsa_driver_capture.app_buffer_y2 = y2;
  alsa_driver_capture.app_buffer_offset = iwrite;
  alsa_driver_capture.app_buffer_length = *nbuflen;
  alsa_driver_capture.Tsec = Tsec;
  alsa_driver_capture.tbuf = tbuf;
  alsa_driver_capture.ibuf = ibuf;
  alsa_driver_capture.ndsec = ndsec;
  alsa_driver_playback.Tsec = Tsec;
  alsa_driver_playback.app_buffer_y1 = iwave;
  alsa_driver_playback.tx_ok = TxOK;
  alsa_driver_playback.tr_period = TRPeriod;
  alsa_driver_playback.nwave = nwave;
  alsa_driver_playback.nmode = nmode;
  alsa_driver_playback.transmitting = Transmitting;
  alsa_driver_playback.ndsec = ndsec;
  //  printf("start_threads: creating thread for decode1\n");
  iret1 = pthread_create(&thread1,NULL,decode1_,&iarg1);
/* Open audio card. */
  printf("Using ALSA sound.\n");
  ao_alsa_open(&alsa_driver_playback, &rate, SND_PCM_STREAM_PLAYBACK);
  ao_alsa_open(&alsa_driver_capture, &rate, SND_PCM_STREAM_CAPTURE);

/*
 * Start audio io thread
 */
  iret2 = pthread_create(&thread2, NULL, ao_alsa_loop, NULL);
  snd_pcm_prepare(alsa_driver_capture.audio_fd);
  snd_pcm_start(alsa_driver_capture.audio_fd);
  snd_pcm_prepare(alsa_driver_playback.audio_fd);
  //snd_pcm_start(alsa_driver_playback.audio_fd);
}

Reply via email to