📄 player.c
字号:
message(""); if (command == -1) return MAD_FLOW_BREAK; if (command != KEY_PAUSE) goto again; break; case KEY_FORWARD: case KEY_CTRL('n'): case '>': player->control = PLAYER_CONTROL_NEXT; goto stop; case KEY_BACK: case KEY_CTRL('p'): case '<': { mad_timer_t threshold; mad_timer_set(&threshold, 4, 0, 0); player->control = (stopped || mad_timer_compare(player->stats.play_timer, threshold) < 0) ? PLAYER_CONTROL_PREVIOUS : PLAYER_CONTROL_REPLAY; } goto stop; case KEY_QUIT: case KEY_CTRL('c'): case 'Q': player->control = PLAYER_CONTROL_STOP; goto stop; case KEY_INFO: case '?': if (player->verbosity <= 0) { show_status(&player->stats, 0, player->input.path, 1); message("\n"); } break; case KEY_TIME: if (player->verbosity > 0) { char const *label = 0; switch (player->stats.show) { case STATS_SHOW_OVERALL: player->stats.show = STATS_SHOW_REMAINING; label = N_("[Current Time Remaining]"); break; case STATS_SHOW_REMAINING: player->stats.show = STATS_SHOW_CURRENT; label = N_("[Current Time]"); break; case STATS_SHOW_CURRENT: player->stats.show = STATS_SHOW_OVERALL; label = N_("[Overall Time]"); break; } show_status(&player->stats, 0, gettext(label), 1); } break; case KEY_GAINDECR: case KEY_GAININCR: case KEY_GAINZERO: case KEY_GAININFO: { double db; switch (command) { case KEY_GAINDECR: db = set_gain(player, GAIN_ATTAMP | GAIN_RELATIVE, -0.5); break; case KEY_GAININCR: db = set_gain(player, GAIN_ATTAMP | GAIN_RELATIVE, +0.5); break; case KEY_GAINZERO: db = set_gain(player, GAIN_ATTAMP, 0); break; default: db = set_gain(player, 0, 0); break; } if (player->verbosity > 0) { static char status[15]; sprintf(status, "%+.1f dB gain", db); show_status(&player->stats, 0, status, 1); } } break; } return flow; stop: stop_audio(player, 1); return MAD_FLOW_STOP;}# endif/* * NAME: addfilter() * DESCRIPTION: insert a filter at the beginning of the filter chain */staticint addfilter(struct player *player, filter_func_t *func, void *data){ struct filter *filter; filter = filter_new(func, data, player->output.filters); if (filter == 0) return -1; player->output.filters = filter; return 0;}/* * NAME: setup_filters() * DESCRIPTION: create output filters */staticint setup_filters(struct player *player){ /* filters must be added in reverse order */# if defined(EXPERIMENTAL) if ((player->options & PLAYER_OPTION_EXTERNALMIX) && addfilter(player, mixer_filter, stdout) == -1) return -1; if ((player->options & PLAYER_OPTION_EXPERIMENTAL) && addfilter(player, experimental_filter, 0) == -1) return -1;# endif if ((player->options & PLAYER_OPTION_FADEIN) && addfilter(player, fadein_filter, player) == -1) return -1; addfilter(player, gain_filter, &player->output.gain); if (player->output.select == PLAYER_CHANNEL_MONO && addfilter(player, mono_filter, player) == -1) return -1;# if defined(USE_TTY) if ((player->options & PLAYER_OPTION_TTYCONTROL) && addfilter(player, tty_filter, player) == -1) return -1;# endif return 0;}# if defined(USE_TTY) && !defined(_WIN32)/* * NAME: restore_tty() * DESCRIPTION: revert to previous terminal settings */staticint restore_tty(int interrupt){ struct termios tty; struct sigaction action; int result = 0; if (tcgetattr(tty_fd, &tty) == 0 && tcsetattr(tty_fd, interrupt ? TCSAFLUSH : TCSADRAIN, &save_tty) == -1) { if (!interrupt) error("tty", ":tcsetattr"); result = -1; } save_tty = tty; if (sigaction(SIGINT, 0, &action) == 0 && sigaction(SIGINT, &save_sigint, 0) == -1) { if (!interrupt) error("tty", ":sigaction(SIGINT)"); result = -1; } save_sigint = action; if (sigaction(SIGTSTP, 0, &action) == 0 && sigaction(SIGTSTP, &save_sigtstp, 0) == -1) { if (!interrupt) error("tty", ":sigaction(SIGTSTP)"); result = -1; } save_sigtstp = action; if (!interrupt) { if (close(tty_fd) == -1) { error("tty", ":close"); result = -1; } tty_fd = -1; } return result;}/* * NAME: signal_handler() * DESCRIPTION: restore tty state after software interrupt */staticvoid signal_handler(int signal){ static struct sigaction save_sigcont; /* restore tty state and previous signal actions */ restore_tty(1); /* handle SIGCONT after SIGTSTP */ switch (signal) { case SIGTSTP: { struct sigaction action; sigaction(SIGCONT, 0, &save_sigcont); action = save_sigcont; action.sa_handler = signal_handler; sigemptyset(&action.sa_mask); sigaddset(&action.sa_mask, SIGTSTP); sigaddset(&action.sa_mask, SIGINT); action.sa_flags = 0; sigaction(SIGCONT, &action, 0); } break; case SIGCONT: sigaction(SIGCONT, &save_sigcont, 0); on_same_line = 0; /* redraw status line */ break; } /* re-send signal, which is currently blocked */ kill(getpid(), signal); /* return to previous thread, which should immediately receive the signal */ return;}/* * NAME: setup_tty() * DESCRIPTION: change terminal parameters and signal handlers */staticint setup_tty(void){ struct termios tty; struct sigaction action; /* open controlling terminal */ tty_fd = open(TTY_DEVICE, O_RDONLY); if (tty_fd == -1) { error("tty", ":", TTY_DEVICE); return -1; } /* save current terminal and signal settings */ if (tcgetattr(tty_fd, &save_tty) == -1) { error("tty", ":tcgetattr"); return -1; } if (sigaction(SIGTSTP, 0, &save_sigtstp) == -1) { error("tty", ":sigaction(SIGTSTP)"); return -1; } if (sigaction(SIGINT, 0, &save_sigint) == -1) { error("tty", ":sigaction(SIGINT)"); return -1; } /* catch SIGTSTP and SIGINT so the tty state can be restored */ action = save_sigtstp; action.sa_handler = signal_handler; sigemptyset(&action.sa_mask); sigaddset(&action.sa_mask, SIGINT);# if 0 /* on some systems (Mac OS X) this remains masked upon continue (?!) */ sigaddset(&action.sa_mask, SIGCONT);# endif action.sa_flags = 0; if (sigaction(SIGTSTP, &action, 0) == -1) { error("tty", ":sigaction(SIGTSTP)"); goto fail; } action = save_sigint; action.sa_handler = signal_handler; sigemptyset(&action.sa_mask); sigaddset(&action.sa_mask, SIGTSTP); sigaddset(&action.sa_mask, SIGCONT); action.sa_flags = 0; if (sigaction(SIGINT, &action, 0) == -1) { error("tty", ":sigaction(SIGINT)"); goto fail; } /* turn off echo and canonical mode */ tty = save_tty; tty.c_lflag &= ~(ECHO | ICANON); /* set VMIN = VTIME = 0 so read() always returns immediately */ tty.c_cc[VMIN] = 0; tty.c_cc[VTIME] = 0; if (tcsetattr(tty_fd, TCSAFLUSH, &tty) == -1) { error("tty", ":tcsetattr"); goto fail; } return 0; fail: sigaction(SIGINT, &save_sigint, 0); sigaction(SIGTSTP, &save_sigtstp, 0); return -1;}# endif/* * NAME: silence() * DESCRIPTION: output silence for a period of time */staticint silence(struct player *player, mad_timer_t duration, char const *label){ union audio_control control; unsigned int nchannels, speed, nsamples; mad_fixed_t *samples; mad_timer_t unit; int result = 0; audio_control_init(&control, AUDIO_COMMAND_CONFIG); control.config.channels = 2; control.config.speed = 44100; if (player->output.command(&control) == -1) { error("audio", audio_error); return -1; } nchannels = control.config.channels; speed = control.config.speed; nsamples = speed > MAX_NSAMPLES ? MAX_NSAMPLES : speed; player->output.channels_in = nchannels; player->output.channels_out = nchannels; player->output.speed_in = speed; player->output.speed_out = speed; samples = calloc(nsamples, sizeof(mad_fixed_t)); if (samples == 0) { error("silence", _("not enough memory to allocate sample buffer")); return -1; } audio_control_init(&control, AUDIO_COMMAND_PLAY); control.play.nsamples = nsamples; control.play.samples[0] = samples; control.play.samples[1] = (nchannels == 2) ? samples : 0; control.play.mode = player->output.mode; control.play.stats = &player->stats.audio; mad_timer_set(&unit, 0, nsamples, speed); for (mad_timer_negate(&duration); mad_timer_sign(duration) < 0; mad_timer_add(&duration, unit)) { if (mad_timer_compare(unit, mad_timer_abs(duration)) > 0) { unit = mad_timer_abs(duration); control.play.nsamples = mad_timer_fraction(unit, speed); }# if defined(USE_TTY) if ((player->options & PLAYER_OPTION_TTYCONTROL) && tty_filter(player, 0) != MAD_FLOW_CONTINUE) goto fail;# endif if (player->output.command(&control) == -1) { error("audio", audio_error); goto fail; } mad_timer_add(&player->stats.global_timer, unit); if (player->verbosity > 0) show_status(&player->stats, 0, label, 0); } if (0) { fail: result = -1; } free(samples); return result;}/* * NAME: player->run() * DESCRIPTION: begin playback */int player_run(struct player *player, int argc, char const *argv[]){ int result = 0; union audio_control control; player->playlist.entries = argv; player->playlist.length = argc; /* set up terminal settings */# if defined(USE_TTY) && !defined(_WIN32) if ((player->options & PLAYER_OPTION_TTYCONTROL) && setup_tty() == -1) player->options &= ~PLAYER_OPTION_TTYCONTROL;# endif /* initialize ancillary data output file */ if (player->ancillary.path) { if (player->output.path && strcmp(player->ancillary.path, player->output.path) == 0) { error("output", _("ancillary and audio output have same path")); goto fail; } if (strcmp(player->ancillary.path, "-") == 0) player->ancillary.file = stdout; else { player->ancillary.file = fopen(player->ancillary.path, "wb"); if (player->ancillary.file == 0) { error("ancillary", ":", player->ancillary.path); goto fail; } } } /* set up filters */ if (setup_filters(player) == -1) { error("filter", _("not enough memory to allocate filters")); goto fail; } set_gain(player, 0, 0); /* initialize audio */ if (player->output.command) { audio_control_init(&control, AUDIO_COMMAND_INIT); control.init.path = player->output.path; if (player->output.command(&control) == -1) { error("audio", audio_error, control.init.path); goto fail; } if ((player->options & PLAYER_OPTION_SKIP) && mad_timer_sign(player->global_start) < 0) { player->stats.global_timer = player->global_start; if (silence(player, mad_timer_abs(player->global_start), _("lead-in")) == -1) result = -1; } } /* run playlist */ if (result == 0) result = play_all(player); /* drain and close audio */ if (player->output.command) { audio_control_init(&control, AUDIO_COMMAND_FINISH); if (player->output.command(&control) == -1) { error("audio", audio_error); goto fail; } } if (0) { fail: result = -1; } /* drain and close ancillary data output file */ if (player->ancillary.file) { if (player->ancillary.length) { if (fputc(player->ancillary.buffer << (8 - player->ancillary.length), player->ancillary.file) == EOF && result == 0) { error("ancillary", ":fputc"); result = -1; } player->ancillary.length = 0; } if (player->ancillary.file != stdout && fclose(player->ancillary.file) == EOF && result == 0) { error("ancillary", ":fclose"); result = -1; } player->ancillary.file = 0; } /* restore terminal settings */# if defined(USE_TTY) && !defined(_WIN32) if (player->options & PLAYER_OPTION_TTYCONTROL) restore_tty(0);# endif return result;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -