📄 alsa_a.c
字号:
total_bytes = frag_size * frags; ctl->cmsg(CMSG_INFO, VERB_VERBOSE, "Requested buffer size %d, fragment size %d", total_bytes, frag_size); if ((tmp = snd_pcm_hw_params_set_buffer_size_near(handle, pinfo, total_bytes >> sample_shift)) < 0) { ctl->cmsg(CMSG_WARNING, VERB_NORMAL, "ALSA pcm '%s' can't set buffer size %d", alsa_device_name(), total_bytes); snd_pcm_close(handle); return -1; } if ((tmp = snd_pcm_hw_params_set_period_size_near(handle, pinfo, frag_size >> sample_shift, 0)) < 0) { ctl->cmsg(CMSG_WARNING, VERB_NORMAL, "ALSA pcm '%s' can't set period size %d", alsa_device_name(), frag_size); snd_pcm_close(handle); return -1; } if (snd_pcm_hw_params(handle, pinfo) < 0) { snd_output_t *log; ctl->cmsg(CMSG_WARNING, VERB_NORMAL, "ALSA pcm '%s' can't set hw_params", alsa_device_name()); snd_output_stdio_attach(&log, stderr, 0); snd_pcm_hw_params_dump(pinfo, log); snd_pcm_close(handle); return -1; } total_bytes = snd_pcm_hw_params_get_buffer_size(pinfo) << sample_shift; frag_size = snd_pcm_hw_params_get_period_size(pinfo, NULL) << sample_shift; ctl->cmsg(CMSG_INFO, VERB_VERBOSE, "ALSA pcm '%s' set buffer size %d, period size %d bytes", alsa_device_name(), total_bytes, frag_size); tmp = snd_pcm_hw_params_get_rate(pinfo, NULL); if (tmp > 0 && tmp != dpm.rate) { dpm.rate = tmp; ret_val = 1; } if (orig_rate != dpm.rate) { ctl->cmsg(CMSG_WARNING, VERB_VERBOSE, "Output rate adjusted to %d Hz (requested %d Hz)", dpm.rate, orig_rate); } snd_pcm_sw_params_current(handle, swpinfo); snd_pcm_sw_params_set_start_threshold(handle, swpinfo, total_bytes >> sample_shift); snd_pcm_sw_params_set_stop_threshold(handle, swpinfo, total_bytes >> sample_shift); tmp = snd_pcm_prepare(handle); if (tmp < 0) { ctl->cmsg(CMSG_WARNING, VERB_NORMAL, "unable to prepare channel\n"); snd_pcm_close(handle); return -1; } pfds = snd_pcm_poll_descriptors_count(handle); if (pfds > 1) { ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "too many poll descriptors: %s", alsa_device_name()); close_output (); return -1; } else if (pfds == 1) { struct pollfd pfd; if (snd_pcm_poll_descriptors(handle, &pfd, 1) >= 0) dpm.fd = pfd.fd; else dpm.fd = -1; } else dpm.fd = -1; output_counter = 0; return ret_val;}static void close_output(void){ if (handle) { int ret = snd_pcm_close (handle); if (ret < 0) error_report (ret); handle = NULL; } dpm.fd = -1;}static int output_data(char *buf, int32 nbytes){ int n; int nframes, shift; if (! handle) return -1; nframes = nbytes; shift = 0; if (!(dpm.encoding & PE_MONO)) shift++; if (dpm.encoding & PE_16BIT) shift++; nframes >>= shift; while (nframes > 0) { n = snd_pcm_writei(handle, buf, nframes); if (n == -EAGAIN || (n >= 0 && n < nframes)) { snd_pcm_wait(handle, 1000); } else if (n == -EPIPE) { snd_pcm_status_t *status; snd_pcm_status_alloca(&status); if (snd_pcm_status(handle, status) < 0) { ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "%s: cannot get status", alsa_device_name()); return -1; } ctl->cmsg(CMSG_INFO, VERB_DEBUG, "%s: underrun at %ld", alsa_device_name(), output_counter << sample_shift); snd_pcm_prepare(handle); } else if (n < 0) { ctl->cmsg(CMSG_WARNING, VERB_DEBUG, "%s: %s", alsa_device_name(), (n < 0) ? snd_strerror(n) : "write error"); return -1; } if (n > 0) { nframes -= n; buf += n << shift; output_counter += n; } } return 0;}static int acntl(int request, void *arg){ snd_pcm_status_t *status; snd_pcm_sframes_t delay; if (handle == NULL) return -1; switch (request) { case PM_REQ_GETFRAGSIZ: if (frag_size == 0) return -1; *((int *)arg) = frag_size; return 0; case PM_REQ_GETQSIZ: if (total_bytes == -1) return -1; *((int *)arg) = total_bytes; return 0; case PM_REQ_GETFILLABLE: if (total_bytes == -1) return -1; snd_pcm_status_alloca(&status); if (snd_pcm_status(handle, status) < 0) return -1; *((int *)arg) = snd_pcm_status_get_avail(status); return 0; case PM_REQ_GETFILLED: if (total_bytes == -1) return -1; if (snd_pcm_delay(handle, &delay) < 0) return -1; *((int *)arg) = delay; return 0; case PM_REQ_GETSAMPLES: if (total_bytes == -1) return -1; if (snd_pcm_delay(handle, &delay) < 0) return -1; *((int *)arg) = output_counter - delay; return 0; case PM_REQ_DISCARD: if (snd_pcm_drop(handle) < 0) return -1; if (snd_pcm_prepare(handle) < 0) return -1; output_counter = 0; return 0; case PM_REQ_FLUSH: if (snd_pcm_drain(handle) < 0) return -1; if (snd_pcm_prepare(handle) < 0) return -1; output_counter = 0; return 0; } return -1;}/* end ALSA API 0.9.x */#elif ALSA_LIB == 5/*================================================================ * ALSA API version 0.5.x *================================================================*//*return value == 0 sucess == 1 warning == -1 fails */static int set_playback_info (snd_pcm_t* handle__, int32* encoding__, int32* rate__, const int32 extra_param[5]){ int ret_val = 0; const int32 orig_rate = *rate__; int tmp; snd_pcm_channel_info_t pinfo; snd_pcm_channel_params_t pparams; snd_pcm_channel_setup_t psetup; memset (&pinfo, 0, sizeof (pinfo)); memset (&pparams, 0, sizeof (pparams)); pinfo.channel = SND_PCM_CHANNEL_PLAYBACK; tmp = snd_pcm_channel_info (handle__, &pinfo); if (tmp < 0) { error_report (tmp); return -1; } /*check sample bit*/ if (!(pinfo.formats & ~(SND_PCM_FMT_S8 | SND_PCM_FMT_U8))) *encoding__ &= ~PE_16BIT; /*force 8bit samples*/ if (!(pinfo.formats & ~(SND_PCM_FMT_S16 | SND_PCM_FMT_U16))) *encoding__ |= PE_16BIT; /*force 16bit samples*/ /*check rate*/ if (pinfo.min_rate > *rate__) *rate__ = pinfo.min_rate; if (pinfo.max_rate < *rate__) *rate__ = pinfo.max_rate; pparams.format.rate = *rate__; /*check channels*/ if ((*encoding__ & PE_MONO) != 0 && pinfo.min_voices > 1) *encoding__ &= ~PE_MONO; if ((*encoding__ & PE_MONO) == 0 && pinfo.max_voices < 2) *encoding__ |= PE_MONO; if ((*encoding__ & PE_MONO) != 0) pparams.format.voices = 1; /*mono*/ else pparams.format.voices = 2; /*stereo*/ /*check format*/ if ((*encoding__ & PE_16BIT) != 0) { /*16bit*/ if ((pinfo.formats & SND_PCM_FMT_S16_LE) != 0) { pparams.format.format = SND_PCM_SFMT_S16_LE; *encoding__ |= PE_SIGNED; } else if ((pinfo.formats & SND_PCM_FMT_U16_LE) != 0) { pparams.format.format = SND_PCM_SFMT_U16_LE; *encoding__ &= ~PE_SIGNED; } else if ((pinfo.formats & SND_PCM_FMT_S16_BE) != 0) { pparams.format.format = SND_PCM_SFMT_S16_BE; *encoding__ |= PE_SIGNED; } else if ((pinfo.formats & SND_PCM_FMT_U16_BE) != 0) { pparams.format.format = SND_PCM_SFMT_U16_BE; *encoding__ &= ~PE_SIGNED; } else { ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s doesn't support 16 bit sample width", alsa_device_name()); return -1; } } else { /*8bit*/ if ((pinfo.formats & SND_PCM_FMT_U8) != 0) { pparams.format.format = SND_PCM_SFMT_U8; *encoding__ &= ~PE_SIGNED; }#if 0 else if ((pinfo.formats & SND_PCM_FMT_S8) != 0) { pcm_format.format = SND_PCM_SFMT_U16_LE; *encoding__ |= PE_SIGNED; }#endif else { ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s doesn't support 8 bit sample width", alsa_device_name()); return -1; } } sample_shift = 0; if (!(dpm.encoding & PE_MONO)) sample_shift++; if (dpm.encoding & PE_16BIT) sample_shift++; /* Set buffer fragment size (in extra_param[1]) */ if (extra_param[1] != 0) tmp = extra_param[1]; else tmp = audio_buffer_size << sample_shift; /* Set buffer fragments (in extra_param[0]) */ pparams.buf.block.frag_size = tmp; pparams.buf.block.frags_max = (extra_param[0] == 0) ? -1 : extra_param[0]; pparams.buf.block.frags_min = 1; pparams.mode = SND_PCM_MODE_BLOCK; pparams.channel = SND_PCM_CHANNEL_PLAYBACK; pparams.start_mode = SND_PCM_START_DATA; /* .. should be START_FULL */ pparams.stop_mode = SND_PCM_STOP_STOP; pparams.format.interleave = 1; snd_pcm_channel_flush (handle__, SND_PCM_CHANNEL_PLAYBACK); tmp = snd_pcm_channel_params (handle__, &pparams); if (tmp < 0) { ctl->cmsg(CMSG_WARNING, VERB_NORMAL, "%s doesn't support buffer fragments" ":request size=%d, max=%d, min=%d\n", alsa_device_name(), pparams.buf.block.frag_size, pparams.buf.block.frags_max, pparams.buf.block.frags_min); return -1; } tmp = snd_pcm_channel_prepare (handle__, SND_PCM_CHANNEL_PLAYBACK); if (tmp < 0) { ctl->cmsg(CMSG_WARNING, VERB_NORMAL, "unable to prepare channel\n"); return -1; } memset (&psetup, 0, sizeof(psetup)); psetup.channel = SND_PCM_CHANNEL_PLAYBACK; tmp = snd_pcm_channel_setup (handle__, &psetup); if (tmp == 0) { if(psetup.format.rate != orig_rate) { ctl->cmsg(CMSG_WARNING, VERB_VERBOSE, "Output rate adjusted to %d Hz (requested %d Hz)", psetup.format.rate, orig_rate); dpm.rate = psetup.format.rate; ret_val = 1; } frag_size = psetup.buf.block.frag_size; total_bytes = frag_size * psetup.buf.block.frags; } else { frag_size = 0; total_bytes = -1; /* snd_pcm_playback_status fails */ } return ret_val;}static int detect(void){ snd_pcm_t *pcm; if (snd_pcm_open(&pcm, card, device, SND_PCM_OPEN_PLAYBACK | SND_PCM_OPEN_NONBLOCK) < 0) return 0; snd_pcm_close(pcm); return 1; /* found */}static int open_output(void){ int tmp, warnings=0; int ret; tmp = check_sound_cards (&card, &device, dpm.extra_param); if (tmp < 0) return -1; /* Open the audio device */ ret = snd_pcm_open (&handle, card, device, SND_PCM_OPEN_PLAYBACK | SND_PCM_OPEN_NONBLOCK); if (ret < 0) { ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: %s", alsa_device_name(), snd_strerror (ret)); return -1; } snd_pcm_nonblock_mode(handle, 0); /* set back to blocking mode */ /* They can't mean these */ dpm.encoding &= ~(PE_ULAW|PE_ALAW|PE_BYTESWAP); warnings = set_playback_info (handle, &dpm.encoding, &dpm.rate, dpm.extra_param); if (warnings < 0) { close_output (); return -1; } dpm.fd = snd_pcm_file_descriptor (handle, SND_PCM_CHANNEL_PLAYBACK); output_counter = 0; return warnings;}static void close_output(void){ int ret; if (handle == NULL) return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -