📄 audio.c
字号:
/* * madplay - MPEG audio decoder and player * Copyright (C) 2000-2004 Robert Leslie * * 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 * * $Id: audio.c,v 1.36 2004/01/23 09:41:31 rob Exp $ */# ifdef HAVE_CONFIG_H# include "config.h"# endif# include "global.h"# include <string.h># include <mad.h># include "audio.h"char const *audio_error;static struct audio_dither left_dither, right_dither;# if defined(_MSC_VER)# pragma warning(disable: 4550) /* expression evaluates to a function which is missing an argument list */# endif/* * NAME: audio_output() * DESCRIPTION: choose an audio output module from a specifier pathname */audio_ctlfunc_t *audio_output(char const **path){ char const *ext; int i; struct map { char const *name; audio_ctlfunc_t *module; }; struct map const prefixes[] = { { "cdda", audio_cdda }, { "aiff", audio_aiff }, { "wave", audio_wave }, { "wav", audio_wave }, { "snd", audio_snd }, { "au", audio_snd }, { "raw", audio_raw }, { "pcm", audio_raw }, { "hex", audio_hex },# if defined(HAVE_LIBESD) { "esd", audio_esd },# endif# if defined(HAVE_LIBAUDIO) { "nas", audio_nas },# endif { "null", audio_null }, { "nul", audio_null } }; struct map const extensions[] = { { "cdr", audio_cdda }, { "cda", audio_cdda }, { "cdda", audio_cdda }, { "aif", audio_aiff }, { "aiff", audio_aiff }, { "wav", audio_wave }, { "snd", audio_snd }, { "au", audio_snd }, { "raw", audio_raw }, { "pcm", audio_raw }, { "out", audio_raw }, { "bin", audio_raw }, { "hex", audio_hex }, { "txt", audio_hex } }; if (path == 0) return AUDIO_DEFAULT; /* check for prefix specifier */ ext = strchr(*path, ':'); if (ext) { char const *type; type = *path; *path = ext + 1; for (i = 0; i < sizeof(prefixes) / sizeof(prefixes[0]); ++i) { if (strncasecmp(type, prefixes[i].name, ext - type) == 0 && strlen(prefixes[i].name) == ext - type) return prefixes[i].module; } *path = type; return 0; } if (strcmp(*path, "/dev/null") == 0) return audio_null; if (strncmp(*path, "/dev/", 5) == 0) return AUDIO_DEFAULT; /* check for file extension specifier */ ext = strrchr(*path, '.'); if (ext) { ++ext; for (i = 0; i < sizeof(extensions) / sizeof(extensions[0]); ++i) { if (strcasecmp(ext, extensions[i].name) == 0) return extensions[i].module; } } return 0;}/* * NAME: audio_control_init() * DESCRIPTION: initialize an audio control structure */void audio_control_init(union audio_control *control, enum audio_command command){ switch (control->command = command) { case AUDIO_COMMAND_INIT: control->init.path = 0; break; case AUDIO_COMMAND_CONFIG: control->config.channels = 0; control->config.speed = 0; control->config.precision = 0; break; case AUDIO_COMMAND_PLAY: control->play.nsamples = 0; control->play.samples[0] = 0; control->play.samples[1] = 0; control->play.mode = AUDIO_MODE_DITHER; control->play.stats = 0; break; case AUDIO_COMMAND_STOP: control->stop.flush = 0; break; case AUDIO_COMMAND_FINISH: break; }}/* * NAME: clip() * DESCRIPTION: gather signal statistics while clipping */static inlinevoid clip(mad_fixed_t *sample, struct audio_stats *stats){ enum { MIN = -MAD_F_ONE, MAX = MAD_F_ONE - 1 }; if (*sample >= stats->peak_sample) { if (*sample > MAX) { ++stats->clipped_samples; if (*sample - MAX > stats->peak_clipping) stats->peak_clipping = *sample - MAX; *sample = MAX; } stats->peak_sample = *sample; } else if (*sample < -stats->peak_sample) { if (*sample < MIN) { ++stats->clipped_samples; if (MIN - *sample > stats->peak_clipping) stats->peak_clipping = MIN - *sample; *sample = MIN; } stats->peak_sample = -*sample; }}/* * NAME: audio_linear_round() * DESCRIPTION: generic linear sample quantize routine */# if defined(_MSC_VER)extern /* needed to satisfy bizarre MSVC++ interaction with inline */# endifinlinesigned long audio_linear_round(unsigned int bits, mad_fixed_t sample, struct audio_stats *stats){ /* round */ sample += (1L << (MAD_F_FRACBITS - bits)); /* clip */ clip(&sample, stats); /* quantize and scale */ return sample >> (MAD_F_FRACBITS + 1 - bits);}/* * NAME: prng() * DESCRIPTION: 32-bit pseudo-random number generator */static inlineunsigned long prng(unsigned long state){ return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;}/* * NAME: audio_linear_dither() * DESCRIPTION: generic linear sample quantize and dither routine */# if defined(_MSC_VER)extern /* needed to satisfy bizarre MSVC++ interaction with inline */# endifinlinesigned long audio_linear_dither(unsigned int bits, mad_fixed_t sample, struct audio_dither *dither, struct audio_stats *stats){ unsigned int scalebits; mad_fixed_t output, mask, random; enum { MIN = -MAD_F_ONE, MAX = MAD_F_ONE - 1 }; /* noise shape */ sample += dither->error[0] - dither->error[1] + dither->error[2]; dither->error[2] = dither->error[1]; dither->error[1] = dither->error[0] / 2; /* bias */ output = sample + (1L << (MAD_F_FRACBITS + 1 - bits - 1)); scalebits = MAD_F_FRACBITS + 1 - bits; mask = (1L << scalebits) - 1; /* dither */ random = prng(dither->random); output += (random & mask) - (dither->random & mask); dither->random = random; /* clip */ if (output >= stats->peak_sample) { if (output > MAX) { ++stats->clipped_samples; if (output - MAX > stats->peak_clipping) stats->peak_clipping = output - MAX; output = MAX; if (sample > MAX) sample = MAX; } stats->peak_sample = output; } else if (output < -stats->peak_sample) { if (output < MIN) { ++stats->clipped_samples; if (MIN - output > stats->peak_clipping) stats->peak_clipping = MIN - output; output = MIN; if (sample < MIN) sample = MIN; } stats->peak_sample = -output; } /* quantize */ output &= ~mask; /* error feedback */ dither->error[0] = sample - output; /* scale */ return output >> scalebits;}/* * NAME: audio_pcm_u8() * DESCRIPTION: write a block of unsigned 8-bit PCM samples */unsigned int audio_pcm_u8(unsigned char *data, unsigned int nsamples, mad_fixed_t const *left, mad_fixed_t const *right, enum audio_mode mode, struct audio_stats *stats){ unsigned int len; len = nsamples; if (right) { /* stereo */ switch (mode) { case AUDIO_MODE_ROUND: while (len--) { data[0] = audio_linear_round(8, *left++, stats) ^ 0x80; data[1] = audio_linear_round(8, *right++, stats) ^ 0x80; data += 2; } break; case AUDIO_MODE_DITHER: while (len--) { data[0] = audio_linear_dither(8, *left++, &left_dither, stats) ^ 0x80; data[1] = audio_linear_dither(8, *right++, &right_dither, stats) ^ 0x80; data += 2; } break; default: return 0; } return nsamples * 2; } else { /* mono */ switch (mode) { case AUDIO_MODE_ROUND: while (len--) *data++ = audio_linear_round(8, *left++, stats) ^ 0x80; break; case AUDIO_MODE_DITHER: while (len--) *data++ = audio_linear_dither(8, *left++, &left_dither, stats) ^ 0x80; break; default: return 0; } return nsamples; }}/* * NAME: audio_pcm_s8() * DESCRIPTION: write a block of signed 8-bit PCM samples */unsigned int audio_pcm_s8(unsigned char *data, unsigned int nsamples, mad_fixed_t const *left, mad_fixed_t const *right, enum audio_mode mode, struct audio_stats *stats){ unsigned int len; len = nsamples; if (right) { /* stereo */ switch (mode) { case AUDIO_MODE_ROUND: while (len--) { data[0] = audio_linear_round(8, *left++, stats); data[1] = audio_linear_round(8, *right++, stats); data += 2; } break; case AUDIO_MODE_DITHER: while (len--) { data[0] = audio_linear_dither(8, *left++, &left_dither, stats); data[1] = audio_linear_dither(8, *right++, &right_dither, stats); data += 2; } break; default: return 0; } return nsamples * 2; } else { /* mono */ switch (mode) { case AUDIO_MODE_ROUND: while (len--) *data++ = audio_linear_round(8, *left++, stats); break; case AUDIO_MODE_DITHER: while (len--) *data++ = audio_linear_dither(8, *left++, &left_dither, stats); break; default: return 0; } return nsamples; }}/* * NAME: audio_pcm_s16le() * DESCRIPTION: write a block of signed 16-bit little-endian PCM samples */unsigned int audio_pcm_s16le(unsigned char *data, unsigned int nsamples, mad_fixed_t const *left, mad_fixed_t const *right, enum audio_mode mode, struct audio_stats *stats){ unsigned int len; register signed int sample0, sample1; len = nsamples; if (right) { /* stereo */ switch (mode) { case AUDIO_MODE_ROUND: while (len--) { sample0 = audio_linear_round(16, *left++, stats); sample1 = audio_linear_round(16, *right++, stats); data[0] = sample0 >> 0; data[1] = sample0 >> 8; data[2] = sample1 >> 0; data[3] = sample1 >> 8; data += 4; } break; case AUDIO_MODE_DITHER: while (len--) { sample0 = audio_linear_dither(16, *left++, &left_dither, stats); sample1 = audio_linear_dither(16, *right++, &right_dither, stats); data[0] = sample0 >> 0; data[1] = sample0 >> 8; data[2] = sample1 >> 0; data[3] = sample1 >> 8; data += 4; } break; default: return 0; } return nsamples * 2 * 2; } else { /* mono */ switch (mode) { case AUDIO_MODE_ROUND: while (len--) { sample0 = audio_linear_round(16, *left++, stats); data[0] = sample0 >> 0; data[1] = sample0 >> 8; data += 2; } break; case AUDIO_MODE_DITHER: while (len--) { sample0 = audio_linear_dither(16, *left++, &left_dither, stats); data[0] = sample0 >> 0; data[1] = sample0 >> 8; data += 2; } break; default: return 0; } return nsamples * 2; }}/* * NAME: audio_pcm_s16be() * DESCRIPTION: write a block of signed 16-bit big-endian PCM samples */unsigned int audio_pcm_s16be(unsigned char *data, unsigned int nsamples, mad_fixed_t const *left, mad_fixed_t const *right, enum audio_mode mode, struct audio_stats *stats){ unsigned int len; register signed int sample0, sample1; len = nsamples; if (right) { /* stereo */ switch (mode) { case AUDIO_MODE_ROUND: while (len--) { sample0 = audio_linear_round(16, *left++, stats); sample1 = audio_linear_round(16, *right++, stats); data[0] = sample0 >> 8; data[1] = sample0 >> 0; data[2] = sample1 >> 8; data[3] = sample1 >> 0; data += 4; } break; case AUDIO_MODE_DITHER: while (len--) { sample0 = audio_linear_dither(16, *left++, &left_dither, stats); sample1 = audio_linear_dither(16, *right++, &right_dither, stats); data[0] = sample0 >> 8; data[1] = sample0 >> 0; data[2] = sample1 >> 8; data[3] = sample1 >> 0; data += 4; } break; default: return 0; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -