📄 player.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: player.c,v 1.68 2004/02/17 02:26:43 rob Exp $ */# ifdef HAVE_CONFIG_H# include "config.h"# endif# include "global.h"# include <stdio.h># include <stdarg.h># include <stdlib.h># ifdef HAVE_SYS_TYPES_H# include <sys/types.h># endif# include <sys/stat.h># ifdef HAVE_FCNTL_H# include <fcntl.h># endif# ifdef HAVE_UNISTD_H# include <unistd.h># endif# include <string.h># ifdef HAVE_ERRNO_H# include <errno.h># endif# include <time.h># include <locale.h># include <math.h># ifdef HAVE_TERMIOS_H# include <termios.h># endif# ifdef _WIN32# include <windows.h># endif# include <signal.h># ifdef HAVE_ASSERT_H# include <assert.h># endif# if defined(HAVE_MMAP)# include <sys/mman.h># endif# if !defined(O_BINARY)# define O_BINARY 0# endif# include <mad.h># include <id3tag.h># include "gettext.h"# include "player.h"# include "audio.h"# include "resample.h"# include "filter.h"# include "tag.h"# define MPEG_BUFSZ 40000 /* 2.5 s at 128 kbps; 1 s at 320 kbps */# define FREQ_TOLERANCE 2 /* percent sampling frequency tolerance */# define TTY_DEVICE "/dev/tty"# define KEY_CTRL(key) ((key) & 0x1f)enum { KEY_PAUSE = 'p', KEY_STOP = 's', KEY_FORWARD = 'f', KEY_BACK = 'b', KEY_TIME = 't', KEY_QUIT = 'q', KEY_INFO = 'i', KEY_GAINDECR = '-', KEY_GAININCR = '+', KEY_GAINZERO = '_', KEY_GAININFO = '='};static int on_same_line;# if defined(USE_TTY) && !defined(_WIN32)static int tty_fd = -1;static struct termios save_tty;static struct sigaction save_sigtstp, save_sigint;# endif/* * NAME: player_init() * DESCRIPTION: initialize player structure */void player_init(struct player *player){ player->verbosity = 0; player->options = 0; player->repeat = 1; player->control = PLAYER_CONTROL_DEFAULT; player->playlist.entries = 0; player->playlist.length = 0; player->playlist.current = 0; player->global_start = mad_timer_zero; player->global_stop = mad_timer_zero; player->fade_in = mad_timer_zero; player->fade_out = mad_timer_zero; player->gap = mad_timer_zero; player->input.path = 0; player->input.fd = -1;# if defined(HAVE_MMAP) player->input.fdm = 0;# endif player->input.data = 0; player->input.length = 0; player->input.eof = 0; tag_init(&player->input.tag); player->output.mode = AUDIO_MODE_DITHER; player->output.voladj_db = 0; player->output.attamp_db = 0; player->output.gain = MAD_F_ONE; player->output.replay_gain = 0; player->output.filters = 0; player->output.channels_in = 0; player->output.channels_out = 0; player->output.select = PLAYER_CHANNEL_DEFAULT; player->output.speed_in = 0; player->output.speed_out = 0; player->output.speed_request = 0; player->output.precision_in = 0; player->output.precision_out = 0; player->output.path = 0; player->output.command = 0; /* player->output.resample */ player->output.resampled = 0; player->ancillary.path = 0; player->ancillary.file = 0; player->ancillary.buffer = 0; player->ancillary.length = 0; player->stats.show = STATS_SHOW_OVERALL; player->stats.label = 0; player->stats.total_bytes = 0; player->stats.total_time = mad_timer_zero; player->stats.global_timer = mad_timer_zero; player->stats.absolute_timer = mad_timer_zero; player->stats.play_timer = mad_timer_zero; player->stats.global_framecount = 0; player->stats.absolute_framecount = 0; player->stats.play_framecount = 0; player->stats.error_frame = -1; player->stats.mute_frame = 0; player->stats.vbr = 0; player->stats.bitrate = 0; player->stats.vbr_frames = 0; player->stats.vbr_rate = 0; player->stats.nsecs = 0; player->stats.audio.clipped_samples = 0; player->stats.audio.peak_clipping = 0; player->stats.audio.peak_sample = 0;}/* * NAME: player_finish() * DESCRIPTION: destroy a player structure */void player_finish(struct player *player){ if (player->output.filters) filter_free(player->output.filters); if (player->output.resampled) { resample_finish(&player->output.resample[0]); resample_finish(&player->output.resample[1]); free(player->output.resampled); player->output.resampled = 0; }}/* * NAME: message() * DESCRIPTION: show a message, possibly overwriting a previous w/o newline */staticint message(char const *format, ...){ int len, newline, result; va_list args; len = strlen(format); newline = (len > 0 && format[len - 1] == '\n'); if (on_same_line && newline && len > 1) fputc('\n', stderr); va_start(args, format); result = vfprintf(stderr, format, args); va_end(args); if (on_same_line && !newline && result < on_same_line) { unsigned int i; i = on_same_line - result; while (i--) putc(' ', stderr); } on_same_line = newline ? 0 : result; if (!newline) { fputc('\r', stderr); fflush(stderr); } return result;}/* * NAME: detail() * DESCRIPTION: show right-aligned label and line-wrap corresponding text */staticvoid detail(char const *label, char const *format, ...){ char const spaces[] = " "; va_list args;# define LINEWRAP (80 - sizeof(spaces) - 2 - 2) if (on_same_line) message("\n"); if (label) { unsigned int len; len = strlen(label); assert(len < sizeof(spaces)); fprintf(stderr, "%s%s: ", &spaces[len], label); } else fprintf(stderr, "%s ", spaces); va_start(args, format); if (format) { vfprintf(stderr, format, args); fputc('\n', stderr); } else { char *ptr, *newline, *linebreak; /* N.B. this argument must be mutable! */ ptr = va_arg(args, char *); do { newline = strchr(ptr, '\n'); if (newline) *newline = 0; if (strlen(ptr) > LINEWRAP) { linebreak = ptr + LINEWRAP; while (linebreak > ptr && *linebreak != ' ') --linebreak; if (*linebreak == ' ') { if (newline) *newline = '\n'; *(newline = linebreak) = 0; } } fprintf(stderr, "%s\n", ptr); if (newline) { ptr = newline + 1; fprintf(stderr, "%s ", spaces); } } while (newline); } va_end(args);}/* * NAME: error() * DESCRIPTION: show an error using proper interaction with message() */staticvoid error(char const *id, char const *format, ...){ int err; va_list args; err = errno; if (on_same_line) message("\n"); if (id) fprintf(stderr, "%s: ", id); va_start(args, format); if (*format == ':') { if (format[1] == 0) { format = va_arg(args, char const *); errno = err; perror(format); } else { errno = err; perror(format + 1); } } else { vfprintf(stderr, format, args); fputc('\n', stderr); } va_end(args);}# if defined(HAVE_MMAP)/* * NAME: map_file() * DESCRIPTION: map the contents of a file into memory */staticvoid *map_file(int fd, unsigned long length){ void *fdm; fdm = mmap(0, length, PROT_READ, MAP_SHARED, fd, 0); if (fdm == MAP_FAILED) return 0;# if defined(HAVE_MADVISE) madvise(fdm, length, MADV_SEQUENTIAL);# endif return fdm;}/* * NAME: unmap_file() * DESCRIPTION: undo a file mapping */staticint unmap_file(void *fdm, unsigned long length){ if (munmap(fdm, length) == -1) return -1; return 0;}/* * NAME: decode->input_mmap() * DESCRIPTION: (re)fill decoder input buffer from a memory map */staticenum mad_flow decode_input_mmap(void *data, struct mad_stream *stream){ struct player *player = data; struct input *input = &player->input; if (input->eof) return MAD_FLOW_STOP; if (stream->next_frame) { struct stat stat; unsigned long posn, left; if (fstat(input->fd, &stat) == -1) return MAD_FLOW_BREAK; posn = stream->next_frame - input->fdm; /* check for file size change and update map */ if (stat.st_size > input->length) { if (unmap_file(input->fdm, input->length) == -1) { input->fdm = 0; input->data = 0; return MAD_FLOW_BREAK; } player->stats.total_bytes += stat.st_size - input->length; input->length = stat.st_size; input->fdm = map_file(input->fd, input->length); if (input->fdm == 0) { input->data = 0; return MAD_FLOW_BREAK; } mad_stream_buffer(stream, input->fdm + posn, input->length - posn); return MAD_FLOW_CONTINUE; } /* end of memory map; append MAD_BUFFER_GUARD zero bytes */ left = input->length - posn; input->data = malloc(left + MAD_BUFFER_GUARD); if (input->data == 0) return MAD_FLOW_BREAK; input->eof = 1; memcpy(input->data, input->fdm + posn, left); memset(input->data + left, 0, MAD_BUFFER_GUARD); mad_stream_buffer(stream, input->data, left + MAD_BUFFER_GUARD); return MAD_FLOW_CONTINUE; } /* first call */ mad_stream_buffer(stream, input->fdm, input->length); return MAD_FLOW_CONTINUE;}# endif/* * NAME: decode->input_read() * DESCRIPTION: (re)fill decoder input buffer by reading a file descriptor */staticenum mad_flow decode_input_read(void *data, struct mad_stream *stream){ struct player *player = data; struct input *input = &player->input; int len; if (input->eof) return MAD_FLOW_STOP; if (stream->next_frame) { memmove(input->data, stream->next_frame, input->length = &input->data[input->length] - stream->next_frame); } do { len = read(input->fd, input->data + input->length, MPEG_BUFSZ - input->length); } while (len == -1 && errno == EINTR); if (len == -1) { error("input", ":read"); return MAD_FLOW_BREAK; } else if (len == 0) { input->eof = 1; assert(MPEG_BUFSZ - input->length >= MAD_BUFFER_GUARD); while (len < MAD_BUFFER_GUARD) input->data[input->length + len++] = 0; } mad_stream_buffer(stream, input->data, input->length += len); return MAD_FLOW_CONTINUE;}/* * NAME: decode->header() * DESCRIPTION: decide whether to continue decoding based on header */staticenum mad_flow decode_header(void *data, struct mad_header const *header){ struct player *player = data; if ((player->options & PLAYER_OPTION_TIMED) && mad_timer_compare(player->stats.global_timer, player->global_stop) > 0) return MAD_FLOW_STOP; /* accounting (except first frame) */ if (player->stats.absolute_framecount) { ++player->stats.absolute_framecount; mad_timer_add(&player->stats.absolute_timer, header->duration); ++player->stats.global_framecount; mad_timer_add(&player->stats.global_timer, header->duration); if ((player->options & PLAYER_OPTION_SKIP) && mad_timer_compare(player->stats.global_timer, player->global_start) < 0) return MAD_FLOW_IGNORE; } return MAD_FLOW_CONTINUE;}/* * NAME: write_ancillary() * DESCRIPTION: pack ancillary data into octets and output */staticint write_ancillary(struct ancillary *ancillary, struct mad_bitptr ptr, unsigned int length){ if (ancillary->length) { unsigned int balance; balance = 8 - ancillary->length; if (balance > length) { ancillary->buffer = (ancillary->buffer << length) | mad_bit_read(&ptr, length); ancillary->length += length; return 0; } else { if (fputc((ancillary->buffer << balance) | mad_bit_read(&ptr, balance), ancillary->file) == EOF) { error("ancillary", ":fputc"); return -1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -