📄 player.c
字号:
if (fputc((ancillary->buffer << balance) | mad_bit_read(&ptr, balance), ancillary->file) == EOF) { error("ancillary", ":fputc"); return -1; } ancillary->length = 0; length -= balance; } } while (length >= 8) { int byte; byte = mad_bit_read(&ptr, 8); if (putc(byte, ancillary->file) == EOF) { error("ancillary", ":putc"); return -1; } length -= 8; } if (length) { ancillary->buffer = mad_bit_read(&ptr, length); ancillary->length = length; } if (fflush(ancillary->file) == EOF) { error("ancillary", ":fflush"); return -1; } return 0;}/* * NAME: show_id3() * DESCRIPTION: display ID3 tag information */staticvoid show_id3(struct id3_tag const *tag){ unsigned int i; struct id3_frame const *frame; id3_ucs4_t const *ucs4; id3_latin1_t *latin1; static struct { char const *id; char const *label; } const info[] = { { ID3_FRAME_TITLE, N_("Title") }, { "TIT3", 0 }, /* Subtitle */ { "TCOP", 0 }, /* Copyright */ { "TPRO", 0 }, /* Produced */ { "TCOM", N_("Composer") }, { ID3_FRAME_ARTIST, N_("Artist") }, { "TPE2", N_("Orchestra") }, { "TPE3", N_("Conductor") }, { "TEXT", N_("Lyricist") }, { ID3_FRAME_ALBUM, N_("Album") }, { ID3_FRAME_TRACK, N_("Track") }, { ID3_FRAME_YEAR, N_("Year") }, { "TPUB", N_("Publisher") }, { ID3_FRAME_GENRE, N_("Genre") }, { "TRSN", N_("Station") }, { "TENC", N_("Encoder") } }; /* text information */ for (i = 0; i < sizeof(info) / sizeof(info[0]); ++i) { union id3_field const *field; unsigned int nstrings, j; frame = id3_tag_findframe(tag, info[i].id, 0); if (frame == 0) continue; field = id3_frame_field(frame, 1); nstrings = id3_field_getnstrings(field); for (j = 0; j < nstrings; ++j) { ucs4 = id3_field_getstrings(field, j); assert(ucs4); if (strcmp(info[i].id, ID3_FRAME_GENRE) == 0) ucs4 = id3_genre_name(ucs4); latin1 = id3_ucs4_latin1duplicate(ucs4); if (latin1 == 0) goto fail; if (j == 0 && info[i].label) detail(gettext(info[i].label), 0, latin1); else { if (strcmp(info[i].id, "TCOP") == 0 || strcmp(info[i].id, "TPRO") == 0) { detail(0, "%s %s", (info[i].id[1] == 'C') ? _("Copyright (C)") : _("Produced (P)"), latin1); } else detail(0, 0, latin1); } free(latin1); } } /* comments */ i = 0; while ((frame = id3_tag_findframe(tag, ID3_FRAME_COMMENT, i++))) { ucs4 = id3_field_getstring(id3_frame_field(frame, 2)); assert(ucs4); if (*ucs4) continue; ucs4 = id3_field_getfullstring(id3_frame_field(frame, 3)); assert(ucs4); latin1 = id3_ucs4_latin1duplicate(ucs4); if (latin1 == 0) goto fail; detail(_("Comment"), 0, latin1); free(latin1); break; } if (0) { fail: error("id3", _("not enough memory to display tag")); }}/* * NAME: show_rgain() * DESCRIPTION: display Replay Gain information */staticvoid show_rgain(struct rgain const *rgain){ char const *label, *source; if (!RGAIN_VALID(rgain)) return; label = 0; switch (rgain->name) { case RGAIN_NAME_NOT_SET: break; case RGAIN_NAME_RADIO: label = _("Radio Gain"); break; case RGAIN_NAME_AUDIOPHILE: label = _("Audiophile Gain"); break; } source = rgain_originator(rgain); assert(label && source); detail(label, "%+.1f dB => %d dB SPL (%s)", RGAIN_DB(rgain), (int) RGAIN_REFERENCE, source);}/* * NAME: show_tag() * DESCRIPTION: display Xing/LAME tag information */staticvoid show_tag(struct tag const *tag){ char ident[22]; int i; memcpy(ident, tag->encoder, 21); /* separate version number from encoder name */ for (i = 0; i < 20; ++i) { if (ident[i] == 0) break; if (ident[i] >= '0' && ident[i] <= '9') { if (i > 0 && ident[i - 1] != ' ' && ident[i - 1] != 'v') { memmove(&ident[i + 1], &ident[i], 21 - i); ident[i] = ' '; } break; } } if (ident[0]) detail(_("Encoder Version"), "%s", ident); if (tag->flags & TAG_LAME) { char const *text;# if 0 detail(_("Tag Revision"), "%u", tag->lame.revision);# endif text = 0; switch (tag->lame.vbr_method) { case TAG_LAME_VBR_CONSTANT: text = _("constant"); break; case TAG_LAME_VBR_ABR: text = _("ABR"); break; case TAG_LAME_VBR_METHOD1: text = _("1 (old/rh)"); break; case TAG_LAME_VBR_METHOD2: text = _("2 (mtrh)"); break; case TAG_LAME_VBR_METHOD3: text = _("3 (mt)"); break; case TAG_LAME_VBR_METHOD4: text = _("4"); break; case TAG_LAME_VBR_CONSTANT2PASS: text = _("constant (two-pass)"); break; case TAG_LAME_VBR_ABR2PASS: text = _("ABR (two-pass)"); break; } detail(_("VBR Method"), "%s", text ? text : _("unknown")); text = 0; switch (tag->lame.vbr_method) { case TAG_LAME_VBR_CONSTANT: case TAG_LAME_VBR_CONSTANT2PASS: text = _("Bitrate"); break; case TAG_LAME_VBR_ABR: case TAG_LAME_VBR_ABR2PASS: text = _("Target Bitrate"); break; case TAG_LAME_VBR_METHOD1: case TAG_LAME_VBR_METHOD2: case TAG_LAME_VBR_METHOD3: case TAG_LAME_VBR_METHOD4: text = _("Minimum Bitrate"); break; } if (text) { detail(text, _("%u%s kbps"), tag->lame.bitrate, tag->lame.bitrate == 255 ? "+" : ""); } text = 0; switch (tag->lame.stereo_mode) { case TAG_LAME_MODE_MONO: text = _("mono"); break; case TAG_LAME_MODE_STEREO: text = _("normal"); break; case TAG_LAME_MODE_DUAL: text = _("dual channel"); break; case TAG_LAME_MODE_JOINT: text = _("joint"); break; case TAG_LAME_MODE_FORCE: text = _("force"); break; case TAG_LAME_MODE_AUTO: text = _("auto"); break; case TAG_LAME_MODE_INTENSITY: text = _("intensity"); break; case TAG_LAME_MODE_UNDEFINED: text = _("undefined"); break; } if (text) detail(_("Stereo Mode"), "%s", text); if (tag->lame.preset >= 8 && tag->lame.preset <= 320) detail(_("Preset"), _("ABR %u"), tag->lame.preset); else { text = 0; switch (tag->lame.preset) { case TAG_LAME_PRESET_NONE: text = _("none"); break; case TAG_LAME_PRESET_V9: text = _("V9"); break; case TAG_LAME_PRESET_V8: text = _("V8"); break; case TAG_LAME_PRESET_V7: text = _("V7"); break; case TAG_LAME_PRESET_V6: text = _("V6"); break; case TAG_LAME_PRESET_V5: text = _("V5"); break; case TAG_LAME_PRESET_V4: text = _("V4"); break; case TAG_LAME_PRESET_V3: text = _("V3"); break; case TAG_LAME_PRESET_V2: text = _("V2"); break; case TAG_LAME_PRESET_V1: text = _("V1"); break; case TAG_LAME_PRESET_V0: text = _("V0"); break; case TAG_LAME_PRESET_R3MIX: text = _("r3mix"); break; case TAG_LAME_PRESET_STANDARD: text = _("standard"); break; case TAG_LAME_PRESET_EXTREME: text = _("extreme"); break; case TAG_LAME_PRESET_INSANE: text = _("insane"); break; case TAG_LAME_PRESET_STANDARD_FAST: text = _("standard/fast"); break; case TAG_LAME_PRESET_EXTREME_FAST: text = _("extreme/fast"); break; case TAG_LAME_PRESET_MEDIUM: text = _("medium"); break; case TAG_LAME_PRESET_MEDIUM_FAST: text = _("medium/fast"); break; } detail(_("Preset"), "%s", text ? text : _("unknown")); } detail(_("Unwise Settings"), "%s", (tag->lame.flags & TAG_LAME_UNWISE) ? _("yes") : _("no")); detail(_("Encoding Flags"), "%s%s%s", (tag->lame.flags & TAG_LAME_NSPSYTUNE) ? "--nspsytune " : "", (tag->lame.flags & TAG_LAME_NSSAFEJOINT) ? "--nssafejoint " : "", (tag->lame.flags & (TAG_LAME_NOGAP_NEXT | TAG_LAME_NOGAP_PREV)) ? "--nogap" : ""); text = 0; switch (tag->lame.flags & (TAG_LAME_NOGAP_NEXT | TAG_LAME_NOGAP_PREV)) { case TAG_LAME_NOGAP_NEXT: text = _("following"); break; case TAG_LAME_NOGAP_PREV: text = _("preceding"); break; case TAG_LAME_NOGAP_NEXT | TAG_LAME_NOGAP_PREV: text = _("following or preceding"); break; } if (text) detail(_("No Gap"), "%s", text); text = _("Lowpass Filter"); if (tag->lame.lowpass_filter == 0) detail(text, "%s", _("unknown")); else detail(text, _("%u Hz"), tag->lame.lowpass_filter); detail(_("ATH Type"), "%u", tag->lame.ath_type); detail(_("Noise Shaping"), "%u", tag->lame.noise_shaping); switch (tag->lame.surround) { case TAG_LAME_SURROUND_NONE: text = _("none"); break; case TAG_LAME_SURROUND_DPL: text = _("DPL"); break; case TAG_LAME_SURROUND_DPL2: text = _("DPL2"); break; case TAG_LAME_SURROUND_AMBISONIC: text = _("Ambisonic"); break; default: text = _("unknown"); } detail(_("Surround"), "%s", text); detail(_("Start Delay"), _("%u samples"), tag->lame.start_delay); detail(_("End Padding"), _("%u samples"), tag->lame.end_padding); text = 0; switch (tag->lame.source_samplerate) { case TAG_LAME_SOURCE_32LOWER: text = _("32 kHz or lower"); break; case TAG_LAME_SOURCE_44_1: text = _("44.1 kHz"); break; case TAG_LAME_SOURCE_48: text = _("48 kHz"); break; case TAG_LAME_SOURCE_HIGHER48: text = _("higher than 48 kHz"); break; } if (text) detail(_("Source Rate"), "%s", text); if (tag->lame.gain != 0) detail(_("Gain"), _("%+.1f dB"), tag->lame.gain * 1.5); /* Replay Gain */ if (tag->lame.peak > 0) { double peak = mad_f_todouble(tag->lame.peak); detail(_("Peak Amplitude"), _("%.8f (%+.1f dB)"), peak, 20 * log10(peak)); } if (tag->lame.replay_gain[0].name == RGAIN_NAME_RADIO) show_rgain(&tag->lame.replay_gain[0]); if (tag->lame.replay_gain[1].name == RGAIN_NAME_AUDIOPHILE) show_rgain(&tag->lame.replay_gain[1]); detail(_("Music Length"), _("%lu bytes"), tag->lame.music_length);# if 0 detail(_("Music CRC"), "0x%04x", tag->lame.music_crc);# endif } if (tag->flags & TAG_XING) { if (tag->xing.flags & TAG_XING_FRAMES) detail(_("Audio Frames"), "%lu", tag->xing.frames); if ((tag->xing.flags & TAG_XING_BYTES) && (!(tag->flags & TAG_LAME) || tag->lame.music_length != tag->xing.bytes)) detail(_("Data Bytes"), "%lu", tag->xing.bytes); if ((tag->flags & TAG_VBR) && (tag->xing.flags & TAG_XING_SCALE)) detail(_("VBR Scale"), _("%ld/100"), 100 - tag->xing.scale); }}enum { GAIN_VOLADJ = 0x0001, GAIN_ATTAMP = 0x0002, GAIN_RELATIVE = 0x0010};/* * NAME: set_gain() * DESCRIPTION: modify player gain information */staticdouble set_gain(struct player *player, int how, double db){ double *gain_db = 0; if (how & GAIN_ATTAMP) gain_db = &player->output.attamp_db; else if (how & GAIN_VOLADJ) gain_db = &player->output.voladj_db; if (gain_db) { if (how & GAIN_RELATIVE) *gain_db += db; else *gain_db = db; } db = player->output.voladj_db + player->output.attamp_db; if (db > DB_MAX || db < DB_MIN) { db = (db > DB_MAX) ? DB_MAX : DB_MIN; player->output.attamp_db = db - player->output.voladj_db; } player->output.gain = db ? mad_f_tofixed(pow(10, db / 20)) : MAD_F_ONE; return db;}/* * NAME: use_rgain() * DESCRIPTION: select and employ a Replay Gain volume adjustment */staticvoid use_rgain(struct player *player, struct rgain *list){ struct rgain *rgain = &list[0]; if ((player->output.replay_gain & PLAYER_RGAIN_AUDIOPHILE) && list[1].name == RGAIN_NAME_AUDIOPHILE && list[1].originator != RGAIN_ORIGINATOR_UNSPECIFIED) rgain = &list[1]; if (RGAIN_VALID(rgain)) { double gain = RGAIN_DB(rgain); set_gain(player, GAIN_VOLADJ, gain); if (player->verbosity >= 0 || (player->options & PLAYER_OPTION_SHOWTAGSONLY)) { char const *source; source = rgain_originator(rgain); assert(source); detail(_("Replay Gain"), _("%+.1f dB %s adjustment (%s)"), gain, (rgain->name == RGAIN_NAME_RADIO) ? _("radio") : _("audiophile"), source); } player->output.replay_gain |= PLAYER_RGAIN_SET; }}/* * NAME: decode->filter() * DESCRIPTION: perform filtering on decoded frame */staticenum mad_flow decode_filter(void *data, struct mad_stream const *stream, struct mad_frame *frame){ struct player *player = data; /* output ancillary data */ if (player->ancillary.file && stream->anc_bitlen && write_ancillary(&player->ancillary, stream->anc_ptr, stream->anc_bitlen) == -1) return MAD_FLOW_BREAK; /* first frame accounting */ if (player->stats.absolute_framecount == 0) { if (player->input.tag.flags == 0 && tag_parse(&player->input.tag, stream) == 0) { struct tag *tag = &player->input.tag; unsigned int frame_size;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -