Beispiel #1
0
static int
laudio_alsa_start(uint64_t cur_pos, uint64_t next_pkt)
{
  int ret;

  ret = snd_pcm_prepare(hdl);
  if (ret < 0)
    {
      DPRINTF(E_LOG, L_LAUDIO, "Could not prepare PCM device: %s\n", snd_strerror(ret));

      return -1;
    }

  DPRINTF(E_DBG, L_LAUDIO, "Start local audio curpos %" PRIu64 ", next_pkt %" PRIu64 "\n", cur_pos, next_pkt);
  DPRINTF(E_DBG, L_LAUDIO, "PCM will start after %d samples (%d packets)\n", pcm_buf_threshold, pcm_buf_threshold / AIRTUNES_V2_PACKET_SAMPLES);

  /* Make pcm_pos the rtptime of the packet containing cur_pos */
  pcm_pos = next_pkt;
  while (pcm_pos > cur_pos)
    pcm_pos -= AIRTUNES_V2_PACKET_SAMPLES;

  pcm_start_pos = next_pkt + pcm_buf_threshold;

  /* Compensate threshold, as it's taken into account by snd_pcm_delay() */
  //pcm_pos += pcm_buf_threshold;

  DPRINTF(E_DBG, L_LAUDIO, "PCM pos %" PRIu64 ", start pos %" PRIu64 "\n", pcm_pos, pcm_start_pos);

  pcm_pkt_head = NULL;
  pcm_pkt_tail = NULL;

  pcm_last_error = 0;
  pcm_recovery = 0;

  ret = laudio_alsa_set_start_threshold(pcm_buf_threshold);
  if (ret < 0)
    {
      DPRINTF(E_LOG, L_LAUDIO, "Could not set PCM start threshold for local audio start\n");

      return -1;
    }

  update_status(LAUDIO_STARTED);

  return 0;
}
Beispiel #2
0
static void
laudio_alsa_write(uint8_t *buf, uint64_t rtptime)
{
  struct pcm_packet *pkt;
  snd_pcm_sframes_t nsamp;
  int ret;

  pkt = (struct pcm_packet *)malloc(sizeof(struct pcm_packet));
  if (!pkt)
    {
      DPRINTF(E_LOG, L_LAUDIO, "Out of memory for PCM pkt\n");

      update_status(LAUDIO_FAILED);
      return;
    }

  memcpy(pkt->samples, buf, sizeof(pkt->samples));

  pkt->rtptime = rtptime;
  pkt->offset = 0;
  pkt->next = NULL;

  if (pcm_pkt_tail)
    {
      pcm_pkt_tail->next = pkt;
      pcm_pkt_tail = pkt;
    }
  else
    {
      pcm_pkt_head = pkt;
      pcm_pkt_tail = pkt;
    }

  if (pcm_pos < pcm_pkt_head->rtptime)
    {
      pcm_pos += AIRTUNES_V2_PACKET_SAMPLES;

      return;
    }
  else if ((pcm_status != LAUDIO_RUNNING) && (pcm_pos + pcm_buf_threshold >= pcm_start_pos))
    {
      /* Kill threshold */
      ret = laudio_alsa_set_start_threshold(0);
      if (ret < 0)
	DPRINTF(E_WARN, L_LAUDIO, "Couldn't set PCM start threshold to 0 for output start\n");

      update_status(LAUDIO_RUNNING);
    }

  pkt = pcm_pkt_head;

  while (pkt)
    {
      if (pcm_recovery)
	{
	  ret = laudio_alsa_xrun_recover(0);
	  if ((ret == 2) && (pcm_recovery < 10))
	    return;
	  else
	    {
	      if (ret == 2)
		DPRINTF(E_LOG, L_LAUDIO, "Couldn't recover PCM device after 10 tries, aborting\n");

	      update_status(LAUDIO_FAILED);
	      return;
	    }
	}

      nsamp = snd_pcm_writei(hdl, pkt->samples + pkt->offset, BTOS(sizeof(pkt->samples) - pkt->offset));
      if ((nsamp == -EPIPE) || (nsamp == -ESTRPIPE))
	{
	  ret = laudio_alsa_xrun_recover(nsamp);
	  if ((ret < 0) || (ret == 1))
	    {
	      if (ret < 0)
		DPRINTF(E_LOG, L_LAUDIO, "PCM write error: %s\n", snd_strerror(ret));

	      update_status(LAUDIO_FAILED);
	      return;
	    }
	  else if (ret != 0)
	    return;

	  continue;
	}
      else if (nsamp < 0)
	{
	  DPRINTF(E_LOG, L_LAUDIO, "PCM write error: %s\n", snd_strerror(nsamp));

	  update_status(LAUDIO_FAILED);
	  return;
	}

      pcm_pos += nsamp;

      pkt->offset += STOB(nsamp);
      if (pkt->offset == sizeof(pkt->samples))
	{
	  pcm_pkt_head = pkt->next;

	  if (pkt == pcm_pkt_tail)
	    pcm_pkt_tail = NULL;

	  free(pkt);

	  pkt = pcm_pkt_head;
	}

      /* Don't let ALSA fill up the buffer too much */
// Disabled - seems to cause buffer underruns
//      if (nsamp == AIRTUNES_V2_PACKET_SAMPLES)
//	return;
    }
}
Beispiel #3
0
static int
laudio_alsa_start(uint64_t cur_pos, uint64_t next_pkt)
{
  snd_output_t *output;
  char *debug_pcm_cfg;
  int ret;

  ret = snd_pcm_prepare(hdl);
  if (ret < 0)
    {
      DPRINTF(E_LOG, L_LAUDIO, "Could not prepare PCM device: %s\n", snd_strerror(ret));

      return -1;
    }

  DPRINTF(E_DBG, L_LAUDIO, "Start local audio curpos %" PRIu64 ", next_pkt %" PRIu64 "\n", cur_pos, next_pkt);

  /* Make pcm_pos the rtptime of the packet containing cur_pos */
  pcm_pos = next_pkt;
  while (pcm_pos > cur_pos)
    pcm_pos -= AIRTUNES_V2_PACKET_SAMPLES;

  pcm_start_pos = next_pkt + pcm_period_size;

  /* Compensate period size, otherwise get_pos won't be correct */
  pcm_pos += pcm_period_size;

  DPRINTF(E_DBG, L_LAUDIO, "PCM pos %" PRIu64 ", start pos %" PRIu64 "\n", pcm_pos, pcm_start_pos);

  pcm_pkt_head = NULL;
  pcm_pkt_tail = NULL;

  pcm_last_error = 0;
  pcm_recovery = 0;

  // alsa doesn't actually seem to wait for this threshold?
  ret = laudio_alsa_set_start_threshold(pcm_buf_threshold);
  if (ret < 0)
    {
      DPRINTF(E_LOG, L_LAUDIO, "Could not set PCM start threshold for local audio start\n");

      return -1;
    }

  // Dump PCM config data for E_DBG logging
  ret = snd_output_buffer_open(&output);
  if (ret == 0)
    {
      if (snd_pcm_dump_setup(hdl, output) == 0)
	{
	  snd_output_buffer_string(output, &debug_pcm_cfg);
	  DPRINTF(E_DBG, L_LAUDIO, "Dump of sound device config:\n%s\n", debug_pcm_cfg);
	}

      snd_output_close(output);
    }

  update_status(LAUDIO_STARTED);

  return 0;
}