📄 audio.c
字号:
/* XMMS - Cross-platform multimedia player * Copyright (C) 1998-2001 Peter Alm, Mikael Alm, Olle Hallnas, * Thomas Nilsson and 4Front Technologies * Copyright (C) 1999-2001 Haavard Kvaalen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include "OSS.h"#include "libxmms/util.h"#include <errno.h>#define NFRAGS 32static gint fd = 0;static char *buffer;static gboolean going, prebuffer, paused, unpause, do_pause, remove_prebuffer;static gint device_buffer_used, buffer_size, prebuffer_size, blk_size;static gint rd_index = 0, wr_index = 0;static gint output_time_offset = 0;static guint64 written = 0, output_bytes = 0;static gint flush;static gint fragsize, device_buffer_size;static gchar *device_name;static pthread_t buffer_thread;static gboolean realtime, select_works;static int (*oss_convert_func)(void **data, int length);static int (*oss_stereo_convert_func)(void **data, int length, int fmt);struct format_info { union { AFormat xmms; int oss; } format; int frequency; int channels; int bps;};/* * The format of the data from the input plugin * This will never change during a song. */struct format_info input;/* * The format we get from the effect plugin. * This will be different from input if the effect plugin does * some kind of format conversion. */struct format_info effect;/* * The format of the data we actually send to the soundcard. * This might be different from effect if we need to resample or do * some other format conversion. */struct format_info output;static void oss_calc_device_buffer_used(void){ audio_buf_info buf_info; if(paused) device_buffer_used = 0; else if (!ioctl(fd, SNDCTL_DSP_GETOSPACE, &buf_info)) device_buffer_used = (buf_info.fragstotal * buf_info.fragsize) - buf_info.bytes;}static gint oss_downsample(gpointer ob, guint length, guint speed, guint espeed);static int oss_calc_bitrate(int oss_fmt, int rate, int channels){ int bitrate = rate * channels; if (oss_fmt == AFMT_U16_BE || oss_fmt == AFMT_U16_LE || oss_fmt == AFMT_S16_BE || oss_fmt == AFMT_S16_LE) bitrate *= 2; return bitrate;}static int oss_get_format(AFormat fmt){ int format = 0; switch (fmt) { case FMT_U8: format = AFMT_U8; break; case FMT_S8: format = AFMT_S8; break; case FMT_U16_LE: format = AFMT_U16_LE; break; case FMT_U16_BE: format = AFMT_U16_BE; break; case FMT_U16_NE:#ifdef WORDS_BIGENDIAN format = AFMT_U16_BE;#else format = AFMT_U16_LE;#endif break; case FMT_S16_LE: format = AFMT_S16_LE; break; case FMT_S16_BE: format = AFMT_S16_BE; break; case FMT_S16_NE:#ifdef WORDS_BIGENDIAN format = AFMT_S16_BE;#else format = AFMT_S16_LE;#endif break; } return format;}static void oss_setup_format(AFormat fmt, int rate, int nch){ effect.format.xmms = fmt; effect.frequency = rate; effect.channels = nch; effect.bps = oss_calc_bitrate(oss_get_format(fmt), rate, nch); output.format.oss = oss_get_format(fmt); output.frequency = rate; output.channels = nch; fragsize = 0; while ((1L << fragsize) < effect.bps / 25) fragsize++; fragsize--; device_buffer_size = ((1L << fragsize) * (NFRAGS + 1)); oss_set_audio_params(); output.bps = oss_calc_bitrate(output.format.oss, output.frequency, output.channels);} gint oss_get_written_time(void){ if (!going) return 0; return (written * 1000) / effect.bps;}gint oss_get_output_time(void){ guint64 bytes; if (!fd || !going) return 0; if (realtime) oss_calc_device_buffer_used(); bytes = output_bytes < device_buffer_used ? 0 : output_bytes - device_buffer_used; return output_time_offset + ((bytes * 1000) / output.bps);}static int oss_used(void){ if (realtime) return 0; else { if (wr_index >= rd_index) return wr_index - rd_index; return buffer_size - (rd_index - wr_index); }}gint oss_playing(void){ if(!going) return 0; if(realtime) oss_calc_device_buffer_used(); if (!oss_used() && (device_buffer_used - (3 * blk_size)) <= 0) return FALSE; return TRUE;}gint oss_free(void){ if (!realtime) { if (remove_prebuffer && prebuffer) { prebuffer = FALSE; remove_prebuffer = FALSE; } if (prebuffer) remove_prebuffer = TRUE; if (rd_index > wr_index) return (rd_index - wr_index) - device_buffer_size - 1; return (buffer_size - (wr_index - rd_index)) - device_buffer_size - 1; } else if (paused) return 0; else return 1000000;}static inline ssize_t write_all(int fd, const void *buf, size_t count){ ssize_t done = 0; do { ssize_t n = write(fd, buf, count - done); if (n == -1) { if (errno == EINTR) continue; else break; } done += n; } while (count > done); return done;}static void oss_write_audio(gpointer data, int length){ audio_buf_info abuf_info; AFormat new_format; int new_frequency, new_channels; EffectPlugin *ep; new_format = input.format.xmms; new_frequency = input.frequency; new_channels = input.channels; ep = get_current_effect_plugin(); if(effects_enabled() && ep && ep->query_format) { ep->query_format(&new_format,&new_frequency,&new_channels); } if (new_format != effect.format.xmms || new_frequency != effect.frequency || new_channels != effect.channels) { output_time_offset += (output_bytes * 1000) / output.bps; output_bytes = 0; close(fd); fd = open(device_name,O_WRONLY); oss_setup_format(new_format, new_frequency, new_channels); } if (effects_enabled() && ep && ep->mod_samples) length = ep->mod_samples(&data, length, input.format.xmms, input.frequency, input.channels); if (realtime && !ioctl(fd, SNDCTL_DSP_GETOSPACE, &abuf_info)) { while (abuf_info.bytes < length) { xmms_usleep(10000); if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &abuf_info)) break; } } if (oss_convert_func != NULL) length = oss_convert_func(&data, length); if (oss_stereo_convert_func != NULL) length = oss_stereo_convert_func(&data, length, output.format.oss); if (effect.frequency == output.frequency) output_bytes += write_all(fd, data, length); else output_bytes += oss_downsample(data, length, effect.frequency, output.frequency);}static void swap_endian(guint16 *data, int length){ int i; for (i = 0; i < length; i += 2, data++) *data = GUINT16_SWAP_LE_BE(*data);}#define NOT_NATIVE_ENDIAN ((IS_BIG_ENDIAN && \ (output.format.oss == AFMT_S16_LE || \ output.format.oss == AFMT_U16_LE)) || \ (!IS_BIG_ENDIAN && \ (output.format.oss == AFMT_S16_BE || \ output.format.oss == AFMT_U16_BE)))#define RESAMPLE_STEREO(sample_type) \do { \ const gint shift = sizeof (sample_type); \ gint i, in_samples, out_samples, x, delta; \ sample_type *inptr = (sample_type *)ob, *outptr; \ guint nlen = (((length >> shift) * espeed) / speed); \ if (nlen == 0) \ break; \ nlen <<= shift; \ if (NOT_NATIVE_ENDIAN) \ swap_endian(ob, length); \ if(nlen > nbuffer_size) \ { \ nbuffer = g_realloc(nbuffer, nlen); \ nbuffer_size = nlen; \ } \ outptr = (sample_type *)nbuffer; \ in_samples = length >> shift; \ out_samples = nlen >> shift; \ delta = (in_samples << 12) / out_samples; \ for (x = 0, i = 0; i < out_samples; i++) \ { \ gint x1, frac; \ x1 = (x >> 12) << 12; \ frac = x - x1; \ *outptr++ = \ (sample_type) \ ((inptr[(x1 >> 12) << 1] * \ ((1<<12) - frac) + \ inptr[((x1 >> 12) + 1) << 1] * \ frac) >> 12); \
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -