static void snd_pcm_file_dump(snd_pcm_t *pcm, snd_output_t *out) { snd_pcm_file_t *file = pcm->private_data; if (file->fname) snd_output_printf(out, "File PCM (file=%s)\n", file->fname); else snd_output_printf(out, "File PCM (fd=%d)\n", file->fd); if (pcm->setup) { snd_output_printf(out, "Its setup is:\n"); snd_pcm_dump_setup(pcm, out); } snd_output_printf(out, "Slave: "); snd_pcm_dump(file->gen.slave, out); }
int snd_pcm_dump(snd_pcm_t *pcm, snd_output_t *out) { char *name; int err = snd_card_get_name(pcm->card, &name); if (err < 0) return err; snd_output_printf(out, "Hardware PCM card %d '%s' device %d subdevice %d\n", pcm->card, name, pcm->device, pcm->subdevice); if (pcm->setup) { snd_output_printf(out, "Its setup is:\n"); snd_pcm_dump_setup(pcm, out); } free(name); return 0; }
static int setparams(snd_pcm_t *phandle, snd_pcm_t *chandle, snd_pcm_format_t format, int latency, int allow_resample, struct final_params *negotiated) { int i; unsigned ratep, ratec = 0; unsigned ratemin = 32000, ratemax = 96000, val; int err, channels = 2; snd_pcm_hw_params_t *p_hwparams, *c_hwparams; snd_pcm_sw_params_t *p_swparams, *c_swparams; snd_pcm_uframes_t c_size, p_psize, c_psize; /* Our latency is 2 periods (in usecs) */ unsigned int c_periods = 2, p_periods; unsigned int c_periodtime, p_periodtime; const unsigned int prefered_rates[] = { 44100, 48000, 32000 }; snd_pcm_hw_params_alloca(&p_hwparams); snd_pcm_hw_params_alloca(&c_hwparams); snd_pcm_sw_params_alloca(&p_swparams); snd_pcm_sw_params_alloca(&c_swparams); if (setparams_stream(chandle, c_hwparams, format, &channels, "capture")) return 1; if (setparams_stream(phandle, p_hwparams, format, &channels, "playback")) return 1; if (allow_resample) { err = snd_pcm_hw_params_set_rate_resample(chandle, c_hwparams, 1); if (err < 0) { fprintf(error_fp, "alsa: Resample setup failed: %s\n", snd_strerror(err)); return 1; } else if (verbose) fprintf(error_fp, "alsa: Resample enabled.\n"); } err = snd_pcm_hw_params_get_rate_min(c_hwparams, &ratemin, 0); if (err >= 0 && verbose) fprintf(error_fp, "alsa: Capture min rate is %d\n", ratemin); err = snd_pcm_hw_params_get_rate_max(c_hwparams, &ratemax, 0); if (err >= 0 && verbose) fprintf(error_fp, "alsa: Capture max rate is %u\n", ratemax); err = snd_pcm_hw_params_get_rate_min(p_hwparams, &val, 0); if (err >= 0) { if (verbose) fprintf(error_fp, "alsa: Playback min rate is %u\n", val); if (val > ratemin) ratemin = val; } err = snd_pcm_hw_params_get_rate_max(p_hwparams, &val, 0); if (err >= 0) { if (verbose) fprintf(error_fp, "alsa: Playback max rate is %u\n", val); if (val < ratemax) ratemax = val; } if (verbose) fprintf(error_fp, "alsa: Will search a common rate between %u and %u\n", ratemin, ratemax); /* First try a set of common rates */ err = -1; for (i = 0; i < ARRAY_SIZE(prefered_rates); i++) { if (prefered_rates[i] < ratemin || prefered_rates[i] > ratemax) continue; ratep = ratec = prefered_rates[i]; err = alsa_try_rate(phandle, chandle, p_hwparams, c_hwparams, allow_resample, &ratep, &ratec); if (err == 0) break; } if (err != 0) { if (ratemin >= 44100) { for (i = ratemin; i <= ratemax; i += 100) { ratep = ratec = i; err = alsa_try_rate(phandle, chandle, p_hwparams, c_hwparams, allow_resample, &ratep, &ratec); if (err == 0) break; } } else { for (i = ratemax; i >= ratemin; i -= 100) { ratep = ratec = i; err = alsa_try_rate(phandle, chandle, p_hwparams, c_hwparams, allow_resample, &ratep, &ratec); if (err == 0) break; } } } if (err < 0) { fprintf(error_fp, "alsa: Failed to set a supported rate: %s\n", snd_strerror(err)); return 1; } if (ratep != ratec) { if (verbose || allow_resample) fprintf(error_fp, "alsa: Couldn't find a rate that it is supported by both playback and capture\n"); return 2; } if (verbose) fprintf(error_fp, "alsa: Using Rate %d\n", ratec); /* Negotiate period parameters */ c_periodtime = latency * 1000 / c_periods; getparams_periods(chandle, c_hwparams, &c_periodtime, &c_periods, "capture"); p_periods = c_periods * 2; p_periodtime = c_periodtime; getparams_periods(phandle, p_hwparams, &p_periodtime, &p_periods, "playback"); c_periods = p_periods / 2; /* * Some playback devices support a very limited periodtime range. If the user needs to * use a higher latency to avoid overrun/underrun, use an alternate algorithm of incresing * the number of periods, to archive the needed latency */ if (p_periodtime < c_periodtime) { c_periodtime = p_periodtime; c_periods = round (latency * 1000.0 / c_periodtime + 0.5); getparams_periods(chandle, c_hwparams, &c_periodtime, &c_periods, "capture"); p_periods = c_periods * 2; p_periodtime = c_periodtime; getparams_periods(phandle, p_hwparams, &p_periodtime, &p_periods, "playback"); c_periods = p_periods / 2; } if (setparams_periods(chandle, c_hwparams, &c_periodtime, &c_periods, "capture")) return 1; /* Note we use twice as much periods for the playback buffer, since we will get a period size near the requested time and we don't want it to end up smaller then the capture buffer as then we could end up blocking on writing to it. Note we will configure the playback dev to start playing as soon as it has 2 capture periods worth of data, so this won't influence latency */ if (setparams_periods(phandle, p_hwparams, &p_periodtime, &p_periods, "playback")) return 1; snd_pcm_hw_params_get_period_size(p_hwparams, &p_psize, NULL); snd_pcm_hw_params_get_period_size(c_hwparams, &c_psize, NULL); snd_pcm_hw_params_get_buffer_size(c_hwparams, &c_size); latency = c_periods * c_psize; if (setparams_set(phandle, p_hwparams, p_swparams, latency, "playback")) return 1; if (setparams_set(chandle, c_hwparams, c_swparams, c_psize, "capture")) return 1; if ((err = snd_pcm_prepare(phandle)) < 0) { fprintf(error_fp, "alsa: Prepare error: %s\n", snd_strerror(err)); return 1; } if (verbose) { fprintf(error_fp, "alsa: Negociated configuration:\n"); snd_pcm_dump_setup(phandle, output); snd_pcm_dump_setup(chandle, output); fprintf(error_fp, "alsa: Parameters are %iHz, %s, %i channels\n", ratep, snd_pcm_format_name(format), channels); fprintf(error_fp, "alsa: Set bitrate to %u%s, buffer size is %u\n", ratec, allow_resample ? " with resample enabled at playback": "", (unsigned int)c_size); } negotiated->bufsize = c_size; negotiated->rate = ratep; negotiated->channels = channels; negotiated->latency = latency; return 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; }