📄 audio.c
字号:
type *ptr = data; \ for (i = 0; i < length; i += 2) \ { \ *ptr = *ptr * alsa_cfg.vol.left / 100; \ ptr++; \ *ptr = *ptr * alsa_cfg.vol.right / 100; \ ptr++; \ } \} while (0)#define MONO_ADJUST8(type) \do { \ type *ptr = data; \ for (i = 0; i < length; i += 4) \ { \ *ptr = *ptr * vol / 100; \ ptr++; \ } \} while (0)#define VOLUME_ADJUST8(type) \do { \ if (channels == 2) \ STEREO_ADJUST8(type); \ else \ MONO_ADJUST8(type); \} while (0) static void volume_adjust(void* data, int length, AFormat fmt, int channels){ int i, vol; if ((alsa_cfg.vol.left == 100 && alsa_cfg.vol.right == 100) || (channels == 1 && (alsa_cfg.vol.left == 100 || alsa_cfg.vol.right == 100))) return; vol = MAX(alsa_cfg.vol.left, alsa_cfg.vol.right); switch (fmt) { case FMT_S16_LE: VOLUME_ADJUST(gint16, GINT16, LE); break; case FMT_U16_LE: VOLUME_ADJUST(guint16, GUINT16, LE); break; case FMT_S16_BE: VOLUME_ADJUST(gint16, GINT16, BE); break; case FMT_U16_BE: VOLUME_ADJUST(guint16, GUINT16, BE); break; case FMT_S8: VOLUME_ADJUST8(gint8); break; case FMT_U8: VOLUME_ADJUST8(guint8); break; default: g_warning("volue_adjust(): unhandled format: %d", fmt); break; }}void alsa_write(gpointer data, int length){ EffectPlugin *ep; if (paused) return; force_start = FALSE; if (effects_enabled() && (ep = get_current_effect_plugin())) { int new_freq = inputf->rate; int new_chn = inputf->channels; AFormat f = inputf->xmms_format; if (ep->query_format) { ep->query_format(&f, &new_freq, &new_chn); if (f != effectf->xmms_format || new_freq != effectf->rate || new_chn != effectf->channels) { debug("Changing audio format for effect plugin"); g_free(effectf); effectf = snd_format_from_xmms(f, new_freq, new_chn); alsa_reopen(effectf); } } length = ep->mod_samples(&data, length, inputf->xmms_format, inputf->rate, inputf->channels); } else if (effectf) { g_free(effectf); effectf = NULL; effectf = snd_format_from_xmms(inputf->xmms_format, inputf->rate, inputf->channels); alsa_reopen(inputf); } if (alsa_convert_func != NULL) length = alsa_convert_func(convertb, &data, length); if (alsa_stereo_convert_func != NULL) length = alsa_stereo_convert_func(convertb, &data, length); if (alsa_frequency_convert_func != NULL) length = alsa_frequency_convert_func(convertb, &data, length, effectf->rate, outputf->rate); if (alsa_cfg.soft_volume) volume_adjust(data, length, outputf->xmms_format, outputf->channels); if (mmap) alsa_mmap_audio(data, length); else alsa_write_audio(data, length);}static void alsa_write_audio(gpointer data, int length){ snd_pcm_sframes_t written_frames; while (length > 0) { int frames = snd_pcm_bytes_to_frames(alsa_pcm, length); written_frames = snd_pcm_writei(alsa_pcm, data, frames); if (written_frames > 0) { int written = snd_pcm_frames_to_bytes(alsa_pcm, written_frames); alsa_total_written += written; length -= written; data = (char*) data + written; } else if (written_frames == -EPIPE) xrun_recover(); else { g_warning("alsa_write_audio(): write error: %s", snd_strerror(-written_frames)); break; } }}static void alsa_mmap_audio(char *data, int length){ int cnt = 0, err; snd_pcm_uframes_t offset, frames, frame; const snd_pcm_channel_area_t *chan_areas = areas; int channel_offset = 0, channel; ssize_t sample_size, offset_bytes, step; alsa_get_avail(); while (length > 0) { frames = snd_pcm_bytes_to_frames(alsa_pcm, length); if ((err = snd_pcm_mmap_begin(alsa_pcm, &chan_areas, &offset, &frames) < 0)) g_warning("alsa_mmap_audio(): snd_pcm_mmap_begin() " "failed: %s", snd_strerror(-err)); cnt = snd_pcm_frames_to_bytes(alsa_pcm, frames); sample_size = snd_pcm_samples_to_bytes(alsa_pcm, 1); step = chan_areas[0].step / 8; offset_bytes = offset * step; for (frame = 0; frame < frames; frame++) { for (channel = 0; channel < outputf->channels; channel++) { char *ptr = chan_areas[channel].addr; memcpy(ptr + chan_areas[channel].first / 8 + offset_bytes, data + channel_offset, sample_size); channel_offset += sample_size; } offset_bytes += step; } err = snd_pcm_mmap_commit(alsa_pcm, offset, frames); if (err == -EPIPE) xrun_recover(); else if (err < 0) g_warning("alsa_mmap_audio(): snd_pcm_mmap_commit() " "failed: %s", snd_strerror(-err)); else if (err != frames) g_warning("alsa_mmap_audio(): snd_pcm_mmap_commit " "returned %d, expected %d", err, (int)frames); alsa_total_written += cnt; length -= cnt; }}int alsa_open(AFormat fmt, int rate, int nch){ debug("Opening device"); inputf = snd_format_from_xmms(fmt, rate, nch); effectf = snd_format_from_xmms(fmt, rate, nch); if (alsa_cfg.debug) snd_output_stdio_attach(&logs, stdout, 0); mmap = alsa_cfg.mmap; if (alsa_setup(inputf) < 0) { alsa_close(); return 0; } alsa_setup_mixer(); convertb = xmms_convert_buffers_new(); alsa_total_written = 0; going = TRUE; paused = FALSE; force_start = FALSE; snd_pcm_prepare(alsa_pcm); return 1;}static struct snd_format * snd_format_from_xmms(AFormat fmt, int rate, int channels){ struct snd_format *f = g_malloc(sizeof(struct snd_format)); int i; f->xmms_format = fmt; f->format = SND_PCM_FORMAT_UNKNOWN; for (i = 0; i < sizeof(format_table) / sizeof(format_table[0]); i++) if (format_table[i].xmms == fmt) { f->format = format_table[i].alsa; break; } /* Get rid of _NE */ for (i = 0; i < sizeof(format_table) / sizeof(format_table[0]); i++) if (format_table[i].alsa == f->format) { f->xmms_format = format_table[i].xmms; break; } f->rate = rate; f->channels = channels; return f;}static int format_from_alsa(snd_pcm_format_t fmt){ int i; for (i = 0; i < sizeof(format_table) / sizeof(format_table[0]); i++) if (format_table[i].alsa == fmt) return format_table[i].xmms; g_warning("Unsupported format: %s", snd_pcm_format_name(fmt)); return -1;}static int alsa_setup(struct snd_format *f){ int err; snd_pcm_hw_params_t *hwparams; snd_pcm_sw_params_t *swparams; int alsa_buffer_time, bits_per_sample; unsigned int alsa_period_time; snd_pcm_uframes_t alsa_buffer_size, alsa_period_size; debug("alsa_setup"); alsa_convert_func = NULL; alsa_stereo_convert_func = NULL; alsa_frequency_convert_func = NULL; outputf = snd_format_from_xmms(effectf->xmms_format, effectf->rate, effectf->channels); debug("Opening device: %s", alsa_cfg.pcm_device); /* FIXME: Can snd_pcm_open() return EAGAIN? */ if ((err = snd_pcm_open(&alsa_pcm, alsa_cfg.pcm_device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) { g_warning("alsa_setup(): Failed to open pcm device (%s): %s", alsa_cfg.pcm_device, snd_strerror(-err)); alsa_pcm = NULL; return -1; } snd_pcm_nonblock(alsa_pcm, FALSE); if (alsa_cfg.debug) { snd_pcm_info_t *info; int alsa_card, alsa_device, alsa_subdevice; snd_pcm_info_alloca(&info); snd_pcm_info(alsa_pcm, info); alsa_card = snd_pcm_info_get_card(info); alsa_device = snd_pcm_info_get_device(info); alsa_subdevice = snd_pcm_info_get_subdevice(info); printf("Card %i, Device %i, Subdevice %i\n", alsa_card, alsa_device, alsa_subdevice); } snd_pcm_hw_params_alloca(&hwparams); if ((err = snd_pcm_hw_params_any(alsa_pcm, hwparams)) < 0) { g_warning("alsa_setup(): No configuration available for " "playback: %s", snd_strerror(-err)); return -1; } if (mmap && (err = snd_pcm_hw_params_set_access(alsa_pcm, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0) { g_message("alsa_setup(): Cannot set mmap'ed mode: %s. " "falling back to direct write", snd_strerror(-err)); mmap = 0; } if (!mmap && (err = snd_pcm_hw_params_set_access(alsa_pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { g_warning("alsa_setup(): Cannot set direct write mode: %s", snd_strerror(-err)); return -1; } if ((err = snd_pcm_hw_params_set_format(alsa_pcm, hwparams, outputf->format)) < 0) { /* * Try if one of these format work (one of them should work * on almost all soundcards) */ snd_pcm_format_t formats[] = {SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S16_BE, SND_PCM_FORMAT_U8}; int i; for (i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) { if (snd_pcm_hw_params_set_format(alsa_pcm, hwparams, formats[i]) == 0) { outputf->format = formats[i]; break; } } if (outputf->format != effectf->format) { outputf->xmms_format = format_from_alsa(outputf->format); debug("Converting format from %d to %d", effectf->xmms_format, outputf->xmms_format); if (outputf->xmms_format < 0) return -1; alsa_convert_func = xmms_convert_get_func(outputf->xmms_format, effectf->xmms_format); if (alsa_convert_func == NULL) return -1; } else { g_warning("alsa_setup(): Sample format not " "available for playback: %s", snd_strerror(-err)); return -1; } } snd_pcm_hw_params_set_channels_near(alsa_pcm, hwparams, &outputf->channels); if (outputf->channels != effectf->channels) { debug("Converting channels from %d to %d", effectf->channels, outputf->channels); alsa_stereo_convert_func = xmms_convert_get_channel_func(outputf->xmms_format, outputf->channels, effectf->channels); if (alsa_stereo_convert_func == NULL) return -1; } snd_pcm_hw_params_set_rate_near(alsa_pcm, hwparams, &outputf->rate, 0); if (outputf->rate == 0) { g_warning("alsa_setup(): No usable samplerate available."); return -1; } if (outputf->rate != effectf->rate) { debug("Converting samplerate from %d to %d", effectf->rate, outputf->rate); alsa_frequency_convert_func = xmms_convert_get_frequency_func(outputf->xmms_format, outputf->channels); if (alsa_frequency_convert_func == NULL) return -1; } alsa_buffer_time = alsa_cfg.buffer_time * 1000; if ((err = snd_pcm_hw_params_set_buffer_time_near(alsa_pcm, hwparams, &alsa_buffer_time, 0)) < 0) { g_warning("alsa_setup(): Set buffer time failed: %s.", snd_strerror(-err)); return -1; } alsa_period_time = alsa_cfg.period_time * 1000; if ((err = snd_pcm_hw_params_set_period_time_near(alsa_pcm, hwparams, &alsa_period_time, 0)) < 0) { g_warning("alsa_setup(): Set period time failed: %s.", snd_strerror(-err)); return -1; } if (snd_pcm_hw_params(alsa_pcm, hwparams) < 0) { if (alsa_cfg.debug) snd_pcm_hw_params_dump(hwparams, logs); g_warning("alsa_setup(): Unable to install hw params"); return -1; } if ((err = snd_pcm_hw_params_get_buffer_size(hwparams, &alsa_buffer_size)) < 0) { g_warning("alsa_setup(): snd_pcm_hw_params_get_buffer_size() " "failed: %s", snd_strerror(-err)); return -1; } if ((err = snd_pcm_hw_params_get_period_size(hwparams, &alsa_period_size, 0)) < 0) { g_warning("alsa_setup(): snd_pcm_hw_params_get_period_size() " "failed: %s", snd_strerror(-err)); return -1; } alsa_can_pause = snd_pcm_hw_params_can_pause(hwparams); snd_pcm_sw_params_alloca(&swparams); snd_pcm_sw_params_current(alsa_pcm, swparams); /* This has effect for non-mmap only */ if ((err = snd_pcm_sw_params_set_start_threshold(alsa_pcm, swparams, alsa_buffer_size - alsa_period_size) < 0)) g_warning("alsa_setup(): setting start " "threshold failed: %s", snd_strerror(-err)); if (snd_pcm_sw_params(alsa_pcm, swparams) < 0) { g_warning("alsa_setup(): Unable to install sw params"); return -1; } if (alsa_cfg.debug) { snd_pcm_sw_params_dump(swparams, logs); snd_pcm_dump(alsa_pcm, logs); } bits_per_sample = snd_pcm_format_physical_width(outputf->format); alsa_bps = (outputf->rate * bits_per_sample * outputf->channels) >> 3; if (mmap) { int chn; buffer = g_malloc(alsa_period_size * bits_per_sample / 8 * outputf->channels); areas = g_malloc0(outputf->channels * sizeof(snd_pcm_channel_area_t)); for (chn = 0; chn < outputf->channels; chn++) { areas[chn].addr = buffer; areas[chn].first = chn * bits_per_sample; areas[chn].step = outputf->channels * bits_per_sample; } } debug("Device setup: buffer time: %i, size: %i.", alsa_buffer_time, snd_pcm_frames_to_bytes(alsa_pcm, alsa_buffer_size)); debug("bits per sample: %i; frame size: %i; Bps: %i", bits_per_sample, snd_pcm_frames_to_bytes(alsa_pcm, 1), alsa_bps); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -