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; }
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; } }
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; }