📄 player.c
字号:
if (player->options & PLAYER_OPTION_SHOWTAGSONLY) { if (player->verbosity > 0) show_tag(tag); } else { if ((tag->flags & TAG_LAME) && (player->output.replay_gain & PLAYER_RGAIN_ENABLED) && !(player->output.replay_gain & PLAYER_RGAIN_SET)) use_rgain(player, tag->lame.replay_gain); } if ((tag->flags & TAG_XING) && (tag->xing.flags & TAG_XING_FRAMES)) { player->stats.total_time = frame->header.duration; mad_timer_multiply(&player->stats.total_time, tag->xing.frames); } /* total stream byte size adjustment */ frame_size = stream->next_frame - stream->this_frame; if (player->stats.total_bytes == 0) { if ((tag->flags & TAG_XING) && (tag->xing.flags & TAG_XING_BYTES) && tag->xing.bytes > frame_size) player->stats.total_bytes = tag->xing.bytes - frame_size; } else if (player->stats.total_bytes >= stream->next_frame - stream->this_frame) player->stats.total_bytes -= frame_size; return (player->options & PLAYER_OPTION_SHOWTAGSONLY) ? MAD_FLOW_STOP : MAD_FLOW_IGNORE; } else if (player->options & PLAYER_OPTION_SHOWTAGSONLY) return MAD_FLOW_STOP; ++player->stats.absolute_framecount; mad_timer_add(&player->stats.absolute_timer, frame->header.duration); ++player->stats.global_framecount; mad_timer_add(&player->stats.global_timer, frame->header.duration); if ((player->options & PLAYER_OPTION_SKIP) && mad_timer_compare(player->stats.global_timer, player->global_start) < 0) return MAD_FLOW_IGNORE; } /* run the filter chain */ return filter_run(player->output.filters, frame);}/* * NAME: process_id3() * DESCRIPTION: display and process ID3 tag information */staticvoid process_id3(struct id3_tag const *tag, struct player *player){ struct id3_frame const *frame; /* display the tag */ if (player->verbosity >= 0 || (player->options & PLAYER_OPTION_SHOWTAGSONLY)) show_id3(tag); /* * The following is based on information from the * ID3 tag version 2.4.0 Native Frames informal standard. */ /* length information */ frame = id3_tag_findframe(tag, "TLEN", 0); if (frame) { union id3_field const *field; unsigned int nstrings; field = id3_frame_field(frame, 1); nstrings = id3_field_getnstrings(field); if (nstrings > 0) { id3_latin1_t *latin1; latin1 = id3_ucs4_latin1duplicate(id3_field_getstrings(field, 0)); if (latin1) { signed long ms; /* * "The 'Length' frame contains the length of the audio file * in milliseconds, represented as a numeric string." */ ms = atol(latin1); if (ms > 0) mad_timer_set(&player->stats.total_time, 0, ms, 1000); free(latin1); } } } /* relative volume adjustment information */ if ((player->options & PLAYER_OPTION_SHOWTAGSONLY) || !(player->options & PLAYER_OPTION_IGNOREVOLADJ)) { frame = id3_tag_findframe(tag, "RVA2", 0); if (frame) { id3_latin1_t const *id; id3_byte_t const *data; id3_length_t length; enum { CHANNEL_OTHER = 0x00, CHANNEL_MASTER_VOLUME = 0x01, CHANNEL_FRONT_RIGHT = 0x02, CHANNEL_FRONT_LEFT = 0x03, CHANNEL_BACK_RIGHT = 0x04, CHANNEL_BACK_LEFT = 0x05, CHANNEL_FRONT_CENTRE = 0x06, CHANNEL_BACK_CENTRE = 0x07, CHANNEL_SUBWOOFER = 0x08 }; id = id3_field_getlatin1(id3_frame_field(frame, 0)); data = id3_field_getbinarydata(id3_frame_field(frame, 1), &length); assert(id && data); /* * "The 'identification' string is used to identify the situation * and/or device where this adjustment should apply. The following is * then repeated for every channel * * Type of channel $xx * Volume adjustment $xx xx * Bits representing peak $xx * Peak volume $xx (xx ...)" */ while (length >= 4) { unsigned int peak_bytes; peak_bytes = (data[3] + 7) / 8; if (4 + peak_bytes > length) break; if (data[0] == CHANNEL_MASTER_VOLUME) { signed int voladj_fixed; double voladj_float; /* * "The volume adjustment is encoded as a fixed point decibel * value, 16 bit signed integer representing (adjustment*512), * giving +/- 64 dB with a precision of 0.001953125 dB." */ voladj_fixed = (data[1] << 8) | (data[2] << 0); voladj_fixed |= -(voladj_fixed & 0x8000); voladj_float = (double) voladj_fixed / 512; set_gain(player, GAIN_VOLADJ, voladj_float); if (player->verbosity >= 0) { detail(_("Relative Volume"), _("%+.1f dB adjustment (%s)"), voladj_float, id); } break; } data += 4 + peak_bytes; length -= 4 + peak_bytes; } } } /* Replay Gain */ if ((player->options & PLAYER_OPTION_SHOWTAGSONLY) || ((player->output.replay_gain & PLAYER_RGAIN_ENABLED) && !(player->output.replay_gain & PLAYER_RGAIN_SET))) { frame = id3_tag_findframe(tag, "RGAD", 0); if (frame) { id3_byte_t const *data; id3_length_t length; data = id3_field_getbinarydata(id3_frame_field(frame, 0), &length); assert(data); /* * Peak Amplitude $xx $xx $xx $xx * Radio Replay Gain Adjustment $xx $xx * Audiophile Replay Gain Adjustment $xx $xx */ if (length >= 8) { struct mad_bitptr ptr; mad_fixed_t peak; struct rgain rgain[2]; mad_bit_init(&ptr, data); peak = mad_bit_read(&ptr, 32) << 5; rgain_parse(&rgain[0], &ptr); rgain_parse(&rgain[1], &ptr); use_rgain(player, rgain); mad_bit_finish(&ptr); } } }}/* * NAME: show_status() * DESCRIPTION: generate and output stream statistics */staticvoid show_status(struct stats *stats, struct mad_header const *header, char const *label, int now){ signed long seconds; static char const *const layer_str[3] = { N_("I"), N_("II"), N_("III") }; static char const *const mode_str[4] = { N_("single channel"), N_("dual channel"), N_("joint stereo"), N_("stereo") }; if (header) { unsigned int bitrate; bitrate = header->bitrate / 1000; stats->vbr_rate += bitrate; stats->vbr_frames++; stats->vbr += (stats->bitrate && stats->bitrate != bitrate) ? 128 : -1; if (stats->vbr < 0) stats->vbr = 0; else if (stats->vbr > 512) stats->vbr = 512; stats->bitrate = bitrate; } seconds = mad_timer_count(stats->global_timer, MAD_UNITS_SECONDS); if (seconds != stats->nsecs || !on_same_line || now) { mad_timer_t timer; char time_str[18]; char const *joint_str = ""; stats->nsecs = seconds; switch (stats->show) { case STATS_SHOW_OVERALL: timer = stats->global_timer; break; case STATS_SHOW_CURRENT: timer = stats->absolute_timer; break; case STATS_SHOW_REMAINING: timer = stats->total_time; if (mad_timer_sign(timer) == 0 && stats->total_bytes) { unsigned long rate; /* estimate based on size and bitrate */ rate = stats->vbr ? stats->vbr_rate * 125 / stats->vbr_frames : stats->bitrate * 125UL; mad_timer_set(&timer, 0, stats->total_bytes, rate); } mad_timer_negate(&timer); mad_timer_add(&timer, stats->absolute_timer); break; } mad_timer_string(timer, time_str, " %02lu:%02u:%02u", MAD_UNITS_HOURS, 0, 0); if (mad_timer_sign(timer) < 0) time_str[0] = '-'; if (label || stats->label) { message("%s %s", time_str, label ? label : stats->label); stats->label = now ? label : 0; } else if (header) { if (header->mode == MAD_MODE_JOINT_STEREO) { switch (header->flags & (MAD_FLAG_MS_STEREO | MAD_FLAG_I_STEREO)) { case 0: joint_str = _(" (LR)"); break; case MAD_FLAG_MS_STEREO: joint_str = _(" (MS)"); break; case MAD_FLAG_I_STEREO: joint_str = _(" (I)"); break; case (MAD_FLAG_MS_STEREO | MAD_FLAG_I_STEREO): joint_str = _(" (MS+I)"); break; } } message(_("%s Layer %s, %s%u kbps%s, %u Hz, %s%s, %s"), time_str, gettext(layer_str[header->layer - 1]), stats->vbr ? _("VBR (avg ") : "", stats->vbr ? ((stats->vbr_rate * 2) / stats->vbr_frames + 1) / 2 : stats->bitrate, stats->vbr ? _(")") : "", header->samplerate, gettext(mode_str[header->mode]), joint_str, (header->flags & MAD_FLAG_PROTECTION) ? _("CRC") : _("no CRC")); } else message("%s", time_str); }}/* * NAME: decode->output() * DESCRIPTION: configure audio module and output decoded samples */staticenum mad_flow decode_output(void *data, struct mad_header const *header, struct mad_pcm *pcm){ struct player *player = data; struct output *output = &player->output; mad_fixed_t const *ch1, *ch2; unsigned int nchannels; union audio_control control; ch1 = pcm->samples[0]; ch2 = pcm->samples[1]; switch (nchannels = pcm->channels) { case 1: ch2 = 0; if (output->select == PLAYER_CHANNEL_STEREO) { ch2 = ch1; nchannels = 2; } break; case 2: switch (output->select) { case PLAYER_CHANNEL_RIGHT: ch1 = ch2; /* fall through */ case PLAYER_CHANNEL_LEFT: ch2 = 0; nchannels = 1; /* fall through */ case PLAYER_CHANNEL_STEREO: break; default: if (header->mode == MAD_MODE_DUAL_CHANNEL) { if (output->select == PLAYER_CHANNEL_DEFAULT) { if (player->verbosity >= -1) { error("output", _("no channel selected for dual channel; using first")); } output->select = -PLAYER_CHANNEL_LEFT; } ch2 = 0; nchannels = 1; } } } if (output->channels_in != nchannels || output->speed_in != pcm->samplerate) { unsigned int speed_request; if (player->verbosity >= 1 && pcm->samplerate != header->samplerate) { error("output", _("decoded sample frequency %u Hz"), pcm->samplerate); } speed_request = output->speed_request ? output->speed_request : pcm->samplerate; audio_control_init(&control, AUDIO_COMMAND_CONFIG); control.config.channels = nchannels; control.config.speed = speed_request; control.config.precision = output->precision_in; if (output->command(&control) == -1) { error("output", audio_error); return MAD_FLOW_BREAK; } output->channels_in = nchannels; output->speed_in = pcm->samplerate; output->channels_out = control.config.channels; output->speed_out = control.config.speed; output->precision_out = control.config.precision; if (player->verbosity >= -1 && output->channels_in != output->channels_out) { if (output->channels_in == 1) error("output", _("mono output not available; forcing stereo")); else { error("output", _("stereo output not available; using first channel " "(use -m to mix)")); } } if (player->verbosity >= -1 && output->precision_in && output->precision_in != output->precision_out) { error("output", _("bit depth %u not available; using %u"), output->precision_in, output->precision_out); } if (player->verbosity >= -1 && speed_request != output->speed_out) { error("output", _("sample frequency %u Hz not available; using %u Hz"), speed_request, output->speed_out); } /* check whether resampling is necessary */ if (abs(output->speed_out - output->speed_in) < (long) FREQ_TOLERANCE * output->speed_in / 100) { if (output->resampled) { resample_finish(&output->resample[0]); resample_finish(&output->resample[1]); free(output->resampled); output->resampled = 0; } } else { if (output->resampled) { resample_finish(&output->resample[0]); resample_finish(&output->resample[1]); } else { output->resampled = malloc(sizeof(*output->resampled)); if (output->resampled == 0) { error("output", _("not enough memory to allocate resampling buffer")); output->speed_in = 0; return MAD_FLOW_BREAK; } } if (resample_init(&output->resample[0], output->speed_in, output->speed_out) == -1 || resample_init(&output->resample[1], output->speed_in, output->speed_out) == -1) { error("output", _("cannot resample %u Hz to %u Hz"), output->speed_in, output->speed_out); free(output->resampled); output->resampled = 0; output->speed_in = 0; return MAD_FLOW_BREAK; } else if (player->verbosity >= -1) { error("output", _("resampling %u Hz to %u Hz"), output->speed_in, output->speed_out); } } } audio_control_init(&control, AUDIO_COMMAND_PLAY); if (output->channels_in != output->channels_out) ch2 = (output->channels_out == 2) ? ch1 : 0; if (output->resampled) { control.play.nsamples = resample_block(&output->resample[0], pcm->length, ch1, (*output->resampled)[0]); control.play.samples[0] = (*output->resampled)[0]; if (ch2 == ch1) control.play.samples[1] = control.play.samples[0]; else if (ch2) { resample_block(&output->resample[1], pcm->length, ch2, (*output->resampled)[1]); control.play.samples[1] = (*output->resampled)[1]; } else control.play.samples[1] = 0; } else { control.play.nsamples = pcm->length; control.play.samples[0] = ch1; control.play.samples[1] = ch2; } control.play.mode = output->mode; control.play.stats = &player->stats.audio; if (output->command(&control) == -1) { error("output", audio_error); return MAD_FLOW_BREAK; } ++player->stats.play_framecount; mad_timer_add(&player->stats.play_timer, header->duration); if (player->verbosity > 0) show_status(&player->stats, header, 0, 0); return MAD_FLOW_CONTINUE;}/* * NAME: get_id3() * DESCRIPTION: read and parse an ID3 tag from a stream */staticstruct id3_tag *get_id3(struct mad_stream *stream, id3_length_t tagsize, struct input *input){ struct id3_tag *tag = 0; id3_length_t count; id3_byte_t const *data; id3_byte_t *allocated = 0; count = stream->bufend - stream->this_frame; if (tagsize <= count) { data = stream->this_frame; mad_stream_skip(stream, tagsize); } else { allocated = malloc(tagsize); if (allocated == 0) { error("id3", _("not enough memory to allocate tag data buffer")); goto fail; } memcpy(allocated, stream->this_frame, count); mad_stream_skip(stream, count); while (count < tagsize) { int len; do
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -