📄 player.c
字号:
len = read(input->fd, allocated + count, tagsize - count); while (len == -1 && errno == EINTR); if (len == -1) { error("id3", ":read"); goto fail; } if (len == 0) { error("id3", _("EOF while reading tag data")); goto fail; } count += len; } data = allocated; } tag = id3_tag_parse(data, tagsize); fail: if (allocated) free(allocated); return tag;}/* * NAME: decode->error() * DESCRIPTION: handle a decoding error */staticenum mad_flow decode_error(void *data, struct mad_stream *stream, struct mad_frame *frame){ struct player *player = data; signed long tagsize; switch (stream->error) { case MAD_ERROR_BADDATAPTR: return MAD_FLOW_CONTINUE; case MAD_ERROR_LOSTSYNC: tagsize = id3_tag_query(stream->this_frame, stream->bufend - stream->this_frame); if (tagsize > 0) { if (player->options & PLAYER_OPTION_STREAMID3) { struct id3_tag *tag; tag = get_id3(stream, tagsize, &player->input); if (tag) { process_id3(tag, player); id3_tag_delete(tag); } } else mad_stream_skip(stream, tagsize); if (player->stats.total_bytes >= tagsize) player->stats.total_bytes -= tagsize; return MAD_FLOW_CONTINUE; } /* fall through */ default: if (player->verbosity >= -1 && !(player->options & PLAYER_OPTION_SHOWTAGSONLY) && ((stream->error == MAD_ERROR_LOSTSYNC && !player->input.eof) || stream->sync) && player->stats.global_framecount != player->stats.error_frame) { error("error", _("frame %lu: %s"), player->stats.absolute_framecount, mad_stream_errorstr(stream)); player->stats.error_frame = player->stats.global_framecount; } } if (stream->error == MAD_ERROR_BADCRC) { if (player->stats.global_framecount == player->stats.mute_frame) mad_frame_mute(frame); player->stats.mute_frame = player->stats.global_framecount + 1; return MAD_FLOW_IGNORE; } return MAD_FLOW_CONTINUE;}/* * NAME: decode() * DESCRIPTION: decode and output audio for an open file */staticint decode(struct player *player){ struct stat stat; struct mad_decoder decoder; int options, result; if (fstat(player->input.fd, &stat) == -1) { error("decode", ":fstat"); return -1; } if (S_ISREG(stat.st_mode)) player->stats.total_bytes = stat.st_size; tag_init(&player->input.tag); /* prepare input buffers */# if defined(HAVE_MMAP) if (S_ISREG(stat.st_mode) && stat.st_size > 0) { player->input.length = stat.st_size; player->input.fdm = map_file(player->input.fd, player->input.length); if (player->input.fdm == 0 && player->verbosity >= 0) error("decode", ":mmap"); player->input.data = player->input.fdm; }# endif if (player->input.data == 0) { player->input.data = malloc(MPEG_BUFSZ); if (player->input.data == 0) { error("decode", _("not enough memory to allocate input buffer")); return -1; } player->input.length = 0; } player->input.eof = 0; /* reset statistics */ player->stats.absolute_timer = mad_timer_zero; player->stats.play_timer = mad_timer_zero; player->stats.absolute_framecount = 0; player->stats.play_framecount = 0; player->stats.error_frame = -1; player->stats.vbr = 0; player->stats.bitrate = 0; player->stats.vbr_frames = 0; player->stats.vbr_rate = 0; player->stats.audio.clipped_samples = 0; player->stats.audio.peak_clipping = 0; player->stats.audio.peak_sample = 0; mad_decoder_init(&decoder, player,# if defined(HAVE_MMAP) player->input.fdm ? decode_input_mmap :# endif decode_input_read, decode_header, decode_filter, player->output.command ? decode_output : 0, decode_error, 0); options = 0; if (player->options & PLAYER_OPTION_DOWNSAMPLE) options |= MAD_OPTION_HALFSAMPLERATE; if (player->options & PLAYER_OPTION_IGNORECRC) options |= MAD_OPTION_IGNORECRC; mad_decoder_options(&decoder, options); result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC); mad_decoder_finish(&decoder);# if defined(HAVE_MMAP) if (player->input.fdm) { if (unmap_file(player->input.fdm, player->input.length) == -1) { error("decode", ":munmap"); result = -1; } player->input.fdm = 0; if (!player->input.eof) player->input.data = 0; }# endif if (player->input.data) { free(player->input.data); player->input.data = 0; } tag_finish(&player->input.tag); return result;}/* * NAME: play_one() * DESCRIPTION: open and play a single file */staticint play_one(struct player *player){ char const *file = player->playlist.entries[player->playlist.current]; int result; if (strcmp(file, "-") == 0) { if (isatty(STDIN_FILENO)) { error(0, "%s: %s", _("stdin"), _("is a tty")); return -1; } player->input.path = _("stdin"); player->input.fd = STDIN_FILENO; } else { player->input.path = file; player->input.fd = open(file, O_RDONLY | O_BINARY); if (player->input.fd == -1) { error(0, ":", file); return -1; } } if (player->verbosity >= 0 && player->playlist.length > 1) message(">> %s\n", player->input.path); /* reset file information */ player->stats.total_bytes = 0; player->stats.total_time = mad_timer_zero; if (!(player->options & PLAYER_OPTION_IGNOREVOLADJ)) set_gain(player, GAIN_VOLADJ, 0); player->output.replay_gain &= ~PLAYER_RGAIN_SET; /* try reading ID3 tag information now (else read later from stream) */ { int fd; struct id3_file *file; player->options &= ~PLAYER_OPTION_STREAMID3; fd = dup(player->input.fd); file = id3_file_fdopen(fd, ID3_FILE_MODE_READONLY); if (file == 0) { close(fd); player->options |= PLAYER_OPTION_STREAMID3; } else { process_id3(id3_file_tag(file), player); id3_file_close(file); } } result = decode(player); if (result == 0 && player->verbosity >= 0 && !(player->options & PLAYER_OPTION_SHOWTAGSONLY)) { char time_str[19], db_str[7]; char const *peak_str; mad_fixed_t peak; mad_timer_string(player->stats.play_timer, time_str, "%lu:%02u:%02u.%1u", MAD_UNITS_HOURS, MAD_UNITS_DECISECONDS, 0);# if defined(HAVE_LOCALECONV) { char *point; point = strchr(time_str, '.'); if (point) *point = *localeconv()->decimal_point; }# endif peak = MAD_F_ONE + player->stats.audio.peak_clipping; if (peak == MAD_F_ONE) peak = player->stats.audio.peak_sample; if (peak == 0) peak_str = "-inf"; else { sprintf(db_str, "%+.1f", 20 * log10(mad_f_todouble(peak))); peak_str = db_str; } message("%lu %s (%s), %s dB %s, %lu %s\n", player->stats.play_framecount, player->stats.play_framecount == 1 ? _("frame decoded") : _("frames decoded"), time_str, peak_str, _("peak amplitude"), player->stats.audio.clipped_samples, player->stats.audio.clipped_samples == 1 ? _("clipped sample") : _("clipped samples")); } if (player->input.fd != STDIN_FILENO && close(player->input.fd) == -1 && result == 0) { error(0, ":", player->input.path); result = -1; } return result;}/* * NAME: play_all() * DESCRIPTION: run the player's playlist */staticint play_all(struct player *player){ int count, i, j, result = 0; struct playlist *playlist = &player->playlist; char const *tmp; /* set up playlist */ count = playlist->length; if (player->options & PLAYER_OPTION_SHUFFLE) { srand(time(0)); /* initial shuffle */ for (i = 0; i < count; ++i) { j = rand() % count; tmp = playlist->entries[i]; playlist->entries[i] = playlist->entries[j]; playlist->entries[j] = tmp; } } /* run playlist */ while (count && (player->repeat == -1 || player->repeat--)) { while (playlist->current < playlist->length) { i = playlist->current; if (playlist->entries[i] == 0) { ++playlist->current; continue; } player->control = PLAYER_CONTROL_DEFAULT; if (play_one(player) == -1) { playlist->entries[i] = 0; --count; result = -1; } if ((player->options & PLAYER_OPTION_TIMED) && mad_timer_compare(player->stats.global_timer, player->global_stop) > 0) { count = 0; break; } switch (player->control) { case PLAYER_CONTROL_DEFAULT: if ((player->options & PLAYER_OPTION_SHUFFLE) && player->repeat && ++i < playlist->length) { /* pick something from the next half only */ j = (i + rand() % ((playlist->length + 1) / 2)) % playlist->length; tmp = playlist->entries[i]; playlist->entries[i] = playlist->entries[j]; playlist->entries[j] = tmp; } /* fall through */ case PLAYER_CONTROL_NEXT: ++playlist->current; break; case PLAYER_CONTROL_PREVIOUS: do { if (playlist->current-- == 0) playlist->current = playlist->length; } while (playlist->current < playlist->length && playlist->entries[playlist->current] == 0); break; case PLAYER_CONTROL_REPLAY: break; case PLAYER_CONTROL_STOP: playlist->current = playlist->length; count = 0; break; } } playlist->current = 0; } return result;}/* * NAME: stop_audio() * DESCRIPTION: stop playing the audio device immediately */staticint stop_audio(struct player *player, int flush){ int result = 0; if (player->output.command) { union audio_control control; audio_control_init(&control, AUDIO_COMMAND_STOP); control.stop.flush = flush; result = player->output.command(&control); } return result;}# if defined(USE_TTY)/* * NAME: readkey() * DESCRIPTION: read a keypress from the keyboard */staticint readkey(int blocking){# if !defined(_WIN32) unsigned char key; ssize_t count; if (!blocking) { /* tty_fd should be a tty in noncanonical mode with VMIN = VTIME = 0 */ count = read(tty_fd, &key, 1); if (count == -1 && errno != EINTR) { error("tty", ":read"); return -1; } return (count == 1) ? key : 0; } else { struct termios tty, save_tty; if (tcgetattr(tty_fd, &tty) == -1) { error("tty", ":tcgetattr"); return -1; } save_tty = tty; /* change terminal temporarily to get a blocking read() */ tty.c_cc[VMIN] = 1; if (tcsetattr(tty_fd, TCSANOW, &tty) == -1) { error("tty", ":tcsetattr"); return -1; } do count = read(tty_fd, &key, 1); while (count == -1 && errno == EINTR); if (count == -1) error("tty", ":read"); if (tcsetattr(tty_fd, TCSANOW, &save_tty) == -1) { error("tty", ":tcsetattr"); return -1; } if (count == -1) return -1; return (count == 1) ? key : 0; }# elif defined(_WIN32) HANDLE console; INPUT_RECORD input; DWORD count; console = GetStdHandle(STD_INPUT_HANDLE); do { if (GetNumberOfConsoleInputEvents(console, &count) == 0) { error("tty", "GetNumberOfConsoleInputEvents() failed"); return -1; } if (count == 0) { if (!blocking) return 0; else { /* this is necessary to keep Windows from hanging (!) */ Sleep(500); switch (WaitForSingleObject(console, INFINITE)) { case WAIT_ABANDONED: case WAIT_OBJECT_0: continue; case WAIT_TIMEOUT: default: /* ? */ case WAIT_FAILED: error("tty", "WaitForSingleObject() failed"); return -1; } } } if (ReadConsoleInput(console, &input, 1, &count) == 0 || count != 1) { error("tty", "ReadConsoleInput() failed"); return -1; } } while (input.EventType != KEY_EVENT || !input.Event.KeyEvent.bKeyDown || input.Event.KeyEvent.uChar.AsciiChar == 0); return (unsigned char) input.Event.KeyEvent.uChar.AsciiChar;# endif return blocking ? -1 : 0;}/* * NAME: tty_filter() * DESCRIPTION: process TTY commands */staticenum mad_flow tty_filter(void *data, struct mad_frame *frame){ struct player *player = data; enum mad_flow flow = MAD_FLOW_CONTINUE; int command, stopped = 0; command = readkey(0); if (command == -1) return MAD_FLOW_BREAK; again: switch (command) { case KEY_STOP: stopped = 1; player->control = PLAYER_CONTROL_REPLAY; flow = MAD_FLOW_STOP; /* fall through */ case KEY_PAUSE: stop_audio(player, stopped); message(" --%s--", stopped ? _("Stopped") : _("Paused")); command = readkey(1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -