📄 ao_sun.c
字号:
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <fcntl.h>#include <errno.h>#include <sys/ioctl.h>#include <sys/time.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/audioio.h>#ifdef AUDIO_SWFEATURE_MIXER /* solaris8 or newer? */# define HAVE_SYS_MIXER_H 1#endif#if HAVE_SYS_MIXER_H# include <sys/mixer.h>#endif#ifdef __svr4__#include <stropts.h>#endif#include "config.h"#include "mixer.h"#include "audio_out.h"#include "audio_out_internal.h"#include "libaf/af_format.h"#include "mp_msg.h"#include "help_mp.h"static ao_info_t info = { "Sun audio output", "sun", "Juergen Keil", ""};LIBAO_EXTERN(sun)/* These defines are missing on NetBSD */#ifndef AUDIO_PRECISION_8#define AUDIO_PRECISION_8 8#define AUDIO_PRECISION_16 16#endif#ifndef AUDIO_CHANNELS_MONO#define AUDIO_CHANNELS_MONO 1#define AUDIO_CHANNELS_STEREO 2#endifstatic char *sun_mixer_device = NULL;static char *audio_dev = NULL;static int queued_bursts = 0;static int queued_samples = 0;static int bytes_per_sample = 0;static int byte_per_sec = 0;static int audio_fd = -1;static enum { RTSC_UNKNOWN = 0, RTSC_ENABLED, RTSC_DISABLED} enable_sample_timing;extern int verbose;// convert an OSS audio format specification into a sun audio encodingstatic int af2sunfmt(int format){ switch (format){ case AF_FORMAT_MU_LAW: return AUDIO_ENCODING_ULAW; case AF_FORMAT_A_LAW: return AUDIO_ENCODING_ALAW; case AF_FORMAT_S16_NE: return AUDIO_ENCODING_LINEAR;#ifdef AUDIO_ENCODING_LINEAR8 // Missing on SunOS 5.5.1... case AF_FORMAT_U8: return AUDIO_ENCODING_LINEAR8;#endif case AF_FORMAT_S8: return AUDIO_ENCODING_LINEAR;#ifdef AUDIO_ENCODING_DVI // Missing on NetBSD... case AF_FORMAT_IMA_ADPCM: return AUDIO_ENCODING_DVI;#endif default: return AUDIO_ENCODING_NONE; }}// try to figure out, if the soundcard driver provides usable (precise)// sample counter informationstatic int realtime_samplecounter_available(char *dev){ int fd = -1; audio_info_t info; int rtsc_ok = RTSC_DISABLED; int len; void *silence = NULL; struct timeval start, end; struct timespec delay; int usec_delay; unsigned last_samplecnt; unsigned increment; unsigned min_increment; len = 44100 * 4 / 4; /* amount of data for 0.25sec of 44.1khz, stereo, * 16bit. 44kbyte can be sent to all supported * sun audio devices without blocking in the * "write" below. */ silence = calloc(1, len); if (silence == NULL) goto error; if ((fd = open(dev, O_WRONLY)) < 0) goto error; AUDIO_INITINFO(&info); info.play.sample_rate = 44100; info.play.channels = AUDIO_CHANNELS_STEREO; info.play.precision = AUDIO_PRECISION_16; info.play.encoding = AUDIO_ENCODING_LINEAR; info.play.samples = 0; if (ioctl(fd, AUDIO_SETINFO, &info)) { if (verbose>0) mp_msg(MSGT_AO, MSGL_ERR, MSGTR_AO_SUN_RtscSetinfoFailed); goto error; } if (write(fd, silence, len) != len) { if (verbose>0) mp_msg(MSGT_AO, MSGL_ERR, MSGTR_AO_SUN_RtscWriteFailed); goto error; } if (ioctl(fd, AUDIO_GETINFO, &info)) { if (verbose>0) perror("rtsc: GETINFO1"); goto error; } last_samplecnt = info.play.samples; min_increment = ~0; gettimeofday(&start, NULL); for (;;) { delay.tv_sec = 0; delay.tv_nsec = 10000000; nanosleep(&delay, NULL); gettimeofday(&end, NULL); usec_delay = (end.tv_sec - start.tv_sec) * 1000000 + end.tv_usec - start.tv_usec; // stop monitoring sample counter after 0.2 seconds if (usec_delay > 200000) break; if (ioctl(fd, AUDIO_GETINFO, &info)) { if (verbose>0) perror("rtsc: GETINFO2 failed"); goto error; } if (info.play.samples < last_samplecnt) { if (verbose>0) printf("rtsc: %d > %d?\n", last_samplecnt, info.play.samples); goto error; } if ((increment = info.play.samples - last_samplecnt) > 0) { if (verbose>0) printf("ao_sun: sample counter increment: %d\n", increment); if (increment < min_increment) { min_increment = increment; if (min_increment < 2000) break; // looks good } } last_samplecnt = info.play.samples; } /* * For 44.1kkz, stereo, 16-bit format we would send sound data in 16kbytes * chunks (== 4096 samples) to the audio device. If we see a minimum * sample counter increment from the soundcard driver of less than * 2000 samples, we assume that the driver provides a useable realtime * sample counter in the AUDIO_INFO play.samples field. Timing based * on sample counts should be much more accurate than counting whole * 16kbyte chunks. */ if (min_increment < 2000) rtsc_ok = RTSC_ENABLED; if (verbose>0) printf("ao_sun: minimum sample counter increment per 10msec interval: %d\n" "\t%susing sample counter based timing code\n", min_increment, rtsc_ok == RTSC_ENABLED ? "" : "not "); error: if (silence != NULL) free(silence); if (fd >= 0) {#ifdef __svr4__ // remove the 0 bytes from the above measurement from the // audio driver's STREAMS queue ioctl(fd, I_FLUSH, FLUSHW);#endif //ioctl(fd, AUDIO_DRAIN, 0); close(fd); } return rtsc_ok;}// match the requested sample rate |sample_rate| against the// sample rates supported by the audio device |dev|. Return// a supported sample rate, if that sample rate is close to// (< 1% difference) the requested rate; return 0 otherwise.#define MAX_RATE_ERR 1static unsignedfind_close_samplerate_match(int dev, unsigned sample_rate){#if HAVE_SYS_MIXER_H am_sample_rates_t *sr; unsigned i, num, err, best_err, best_rate; for (num = 16; num < 1024; num *= 2) { sr = malloc(AUDIO_MIXER_SAMP_RATES_STRUCT_SIZE(num)); if (!sr) return 0; sr->type = AUDIO_PLAY; sr->flags = 0; sr->num_samp_rates = num; if (ioctl(dev, AUDIO_MIXER_GET_SAMPLE_RATES, sr)) { free(sr); return 0; } if (sr->num_samp_rates <= num) break; free(sr); } if (sr->flags & MIXER_SR_LIMITS) { /* * HW can playback any rate between * sr->samp_rates[0] .. sr->samp_rates[1] */ free(sr); return 0; } else { /* HW supports fixed sample rates only */ best_err = 65535; best_rate = 0; for (i = 0; i < sr->num_samp_rates; i++) { err = abs(sr->samp_rates[i] - sample_rate); if (err == 0) { /* * exact supported sample rate match, no need to * retry something else */ best_rate = 0; break; } if (err < best_err) { best_err = err; best_rate = sr->samp_rates[i]; } } free(sr); if (best_rate > 0 && (100/MAX_RATE_ERR)*best_err < sample_rate) { /* found a supported sample rate with <1% error? */ return best_rate; } return 0; }#else /* old audioio driver, cannot return list of supported rates */ /* XXX: hardcoded sample rates */ unsigned i, err; unsigned audiocs_rates[] = { 5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050, 27420, 32000, 33075, 37800, 44100, 48000, 0 }; for (i = 0; audiocs_rates[i]; i++) { err = abs(audiocs_rates[i] - sample_rate); if (err == 0) { /* * exact supported sample rate match, no need to * retry something elise */ return 0; } if ((100/MAX_RATE_ERR)*err < audiocs_rates[i]) { /* <1% error? */ return audiocs_rates[i]; } } return 0;#endif}// return the highest sample rate supported by audio device |dev|.static unsignedfind_highest_samplerate(int dev){#if HAVE_SYS_MIXER_H am_sample_rates_t *sr; unsigned i, num, max_rate; for (num = 16; num < 1024; num *= 2) { sr = malloc(AUDIO_MIXER_SAMP_RATES_STRUCT_SIZE(num)); if (!sr) return 0; sr->type = AUDIO_PLAY; sr->flags = 0; sr->num_samp_rates = num; if (ioctl(dev, AUDIO_MIXER_GET_SAMPLE_RATES, sr)) { free(sr); return 0; } if (sr->num_samp_rates <= num) break; free(sr); } if (sr->flags & MIXER_SR_LIMITS) { /* * HW can playback any rate between * sr->samp_rates[0] .. sr->samp_rates[1] */ max_rate = sr->samp_rates[1]; } else { /* HW supports fixed sample rates only */ max_rate = 0; for (i = 0; i < sr->num_samp_rates; i++) { if (sr->samp_rates[i] > max_rate) max_rate = sr->samp_rates[i]; } } free(sr); return max_rate;#else /* old audioio driver, cannot return list of supported rates */ return 44100; /* should be supported even on old ISA SB cards */#endif}static void setup_device_paths(){ if (audio_dev == NULL) { if ((audio_dev = getenv("AUDIODEV")) == NULL) audio_dev = "/dev/audio"; } if (sun_mixer_device == NULL) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -