/* Start playback (implies song load) */ int sndmp3_start(const char *fn, int loop) { /* Can't start again if already playing */ if (sndmp3_status == STATUS_PLAYING) return -1; /* Initialize MP3 engine */ if (fn) { if (xing_init(fn) < 0) return -1; /* Set looping status */ sndmp3_loop = loop; } /* Wait for player thread to be ready */ while (sndmp3_status != STATUS_READY) thd_pass(); /* Tell it to start */ if (fn) sndmp3_status = STATUS_STARTING; else sndmp3_status = STATUS_REINIT; sem_signal(sndmp3_halt_sem); return 0; }
/* Called on the first frame decoded. Returns true if this frame * should be ignored. */ bool RageSoundReader_MP3::handle_first_frame() { bool ret = false; /* Check for a XING tag. */ xing_init( &mad->xingtag ); if( xing_parse(&mad->xingtag, mad->Stream.anc_ptr, mad->Stream.anc_bitlen) == 0 ) { /* * "Info" tags are written by some tools. They're just Xing tags, but for * CBR files. * * However, DWI's decoder, BASS, doesn't understand this, and treats it as a * corrupt frame, outputting a frame of silence. Let's ignore the tag, so * it'll be treated as an invalid frame, so we match DWI sync. * * The information in it isn't very useful to us. The TOC is less accurate * then computing it ourself (low resolution). The file length computation * might be wrong, if the tag is incorrect, and we can compute that accurately * ourself for CBR files. */ if( mad->xingtag.type == xing::INFO ) return false; mad->header_bytes = max( mad->header_bytes, get_this_frame_byte(mad) ); mad->has_xing = true; mad_timer_t tm = mad->Frame.header.duration; /* XXX: does this include the Xing header itself? */ mad_timer_multiply( &tm, mad->xingtag.frames ); mad->length = mad_timer_count( tm, MAD_UNITS_MILLISECONDS ); /* XXX: an id3v2 footer tag would throw this off a little. This also assumes * the Xing tag is the last header; it always is, I think. */ int bytes = mad->filesize - mad->header_bytes; mad->bitrate = (int)(bytes * 8 / (mad->length/1000.f)); if( mad->xingtag.type == xing::XING ) ret = 1; } /* If there's no Xing tag, mad->length will be filled in by _open. */ return ret; }
static int count_time_internal (struct mp3_data *data) { struct xing xing; unsigned long bitrate = 0; int has_xing = 0; int is_vbr = 0; int num_frames = 0; mad_timer_t duration = mad_timer_zero; struct mad_header header; int good_header = 0; /* Have we decoded any header? */ mad_header_init (&header); xing_init (&xing); /* There are three ways of calculating the length of an mp3: 1) Constant bitrate: One frame can provide the information needed: # of frames and duration. Just see how long it is and do the division. 2) Variable bitrate: Xing tag. It provides the number of frames. Each frame has the same number of samples, so just use that. 3) All: Count up the frames and duration of each frames by decoding each one. We do this if we've no other choice, i.e. if it's a VBR file with no Xing tag. */ while (1) { /* Fill the input buffer if needed */ if (data->stream.buffer == NULL || data->stream.error == MAD_ERROR_BUFLEN) { if (!fill_buff(data)) break; } if (mad_header_decode(&header, &data->stream) == -1) { if (MAD_RECOVERABLE(data->stream.error)) continue; else if (data->stream.error == MAD_ERROR_BUFLEN) continue; else { debug ("Can't decode header: %s", mad_stream_errorstr( &data->stream)); break; } } good_header = 1; /* Limit xing testing to the first frame header */ if (!num_frames++) { if (xing_parse(&xing, data->stream.anc_ptr, data->stream.anc_bitlen) != -1) { is_vbr = 1; debug ("Has XING header"); if (xing.flags & XING_FRAMES) { has_xing = 1; num_frames = xing.frames; break; } debug ("XING header doesn't contain number of " "frames."); } } /* Test the first n frames to see if this is a VBR file */ if (!is_vbr && !(num_frames > 20)) { if (bitrate && header.bitrate != bitrate) { debug ("Detected VBR after %d frames", num_frames); is_vbr = 1; } else bitrate = header.bitrate; } /* We have to assume it's not a VBR file if it hasn't already * been marked as one and we've checked n frames for different * bitrates */ else if (!is_vbr) { debug ("Fixed rate MP3"); break; } mad_timer_add (&duration, header.duration); } if (!good_header) return -1; if (!is_vbr) { /* time in seconds */ double time = (data->size * 8.0) / (header.bitrate); double timefrac = (double)time - ((long)(time)); /* samples per frame */ long nsamples = 32 * MAD_NSBSAMPLES(&header); /* samplerate is a constant */ num_frames = (long) (time * header.samplerate / nsamples); /* the average bitrate is the constant bitrate */ data->avg_bitrate = bitrate; mad_timer_set(&duration, (long)time, (long)(timefrac*100), 100); } else if (has_xing) { mad_timer_multiply (&header.duration, num_frames); duration = header.duration; } else { /* the durations have been added up, and the number of frames counted. We do nothing here. */ debug ("Counted duration by counting frames durations in " "VBR file."); } if (data->avg_bitrate == -1 && mad_timer_count(duration, MAD_UNITS_SECONDS) > 0) { data->avg_bitrate = data->size / mad_timer_count(duration, MAD_UNITS_SECONDS) * 8; } mad_header_finish(&header); debug ("MP3 time: %ld", mad_timer_count (duration, MAD_UNITS_SECONDS)); return mad_timer_count (duration, MAD_UNITS_SECONDS); }
/* Call this function as a thread to handle playback. Playback will stop and this thread will return when you call sndmp3_shutdown(). */ static void sndmp3_thread() { int sj; stream_hnd = snd_stream_alloc(NULL, SND_STREAM_BUFFER_MAX); assert( stream_hnd != -1 ); /* Main command loop */ while(sndmp3_status != STATUS_QUIT) { switch(sndmp3_status) { case STATUS_INIT: sndmp3_status = STATUS_READY; break; case STATUS_READY: printf("sndserver: waiting on semaphore\r\n"); sem_wait(sndmp3_halt_sem); printf("sndserver: released from semaphore\r\n"); break; case STATUS_STARTING: /* Initialize streaming driver */ if (snd_stream_reinit(stream_hnd, xing_callback) < 0) { sndmp3_status = STATUS_READY; } else { snd_stream_start(stream_hnd, decinfo.samprate, decinfo.channels - 1); sndmp3_status = STATUS_PLAYING; } break; case STATUS_REINIT: /* Re-initialize streaming driver */ snd_stream_reinit(stream_hnd, NULL); sndmp3_status = STATUS_READY; break; case STATUS_PLAYING: { sj = jiffies; if (snd_stream_poll(stream_hnd) < 0) { if (sndmp3_loop) { printf("sndserver: restarting '%s'\r\n", mp3_last_fn); if (xing_init(mp3_last_fn) < 0) { sndmp3_status = STATUS_STOPPING; mp3_last_fn[0] = 0; } } else { printf("sndserver: not restarting\r\n"); snd_stream_stop(stream_hnd); sndmp3_status = STATUS_READY; mp3_last_fn[0] = 0; } // stream_start(); } else thd_sleep(50); break; } case STATUS_STOPPING: snd_stream_stop(stream_hnd); sndmp3_status = STATUS_READY; break; } } /* Done: clean up */ xing_shutdown(); snd_stream_stop(stream_hnd); snd_stream_destroy(stream_hnd); sndmp3_status = STATUS_ZOMBIE; }
static int mad_open(input_object *obj, const char *path) { struct mad_local_data *data; char *p; int mode; if (!obj) return 0; obj->local_data = malloc(sizeof(struct mad_local_data)); if (!obj->local_data) { puts("failed to allocate local data"); return 0; } data = (struct mad_local_data *)obj->local_data; memset(data, 0, sizeof(struct mad_local_data)); if ((data->mad_fd = reader_open(path, &reader_status, obj)) == NULL) { fprintf(stderr, "mad_open(obj, %s) failed\n", path); free(obj->local_data); obj->local_data = NULL; return 0; } obj->flags = 0; if (strncasecmp(path, "http://", 7) == 0) { obj->flags |= P_STREAMBASED; strcpy(data->sinfo.status, "Prebuffering"); } else { obj->flags |= P_FILEBASED; } if (!reader_seekable(data->mad_fd)) { data->seekable = 0; } else { obj->flags |= P_SEEK; obj->flags |= P_PERFECTSEEK; data->seekable = 1; } obj->flags |= P_REENTRANT; mad_init_decoder(data); memset(&data->xing, 0, sizeof(struct xing)); xing_init (&data->xing); data->mad_init = 1; fill_buffer(data, -1); //alsaplayer_error("initial bytes_avail = %d", data->bytes_avail); if (obj->flags & P_PERFECTSEEK) { data->offset = find_initial_frame(data->mad_map, data->bytes_avail < STREAM_BUFFER_SIZE ? data->bytes_avail : STREAM_BUFFER_SIZE); } else { data->offset = 0; } data->highest_frame = 0; if (data->offset < 0) { //fprintf(stderr, "mad_open() couldn't find valid MPEG header\n"); data->offset = 0; } //alsaplayer_error("data->offset = %d", data->offset); if (data->offset > data->bytes_avail) { data->seekable = 1; //alsaplayer_error("Need to refill buffer (data->offset = %d)", data->offset); fill_buffer(data, 0); mad_stream_buffer(&data->stream, data->mad_map, data->bytes_avail); } else { mad_stream_buffer(&data->stream, data->mad_map + data->offset, data->bytes_avail - data->offset); data->bytes_avail -= data->offset; } first_frame: if ((mad_header_decode(&data->frame.header, &data->stream) == -1)) { switch (data->stream.error) { case MAD_ERROR_BUFLEN: return 0; case MAD_ERROR_LOSTSYNC: case MAD_ERROR_BADEMPHASIS: case MAD_ERROR_BADBITRATE: case MAD_ERROR_BADLAYER: case MAD_ERROR_BADSAMPLERATE: //alsaplayer_error("Error %x (frame %d)", data->stream.error, data->current_frame); data->bytes_avail-=(data->stream.next_frame - data->stream.this_frame); goto first_frame; break; case MAD_ERROR_BADBITALLOC: return 0; case MAD_ERROR_BADCRC: alsaplayer_error("MAD_ERROR_BADCRC: %s", error_str(data->stream.error, data->str)); case MAD_ERROR_BADBIGVALUES: case MAD_ERROR_BADDATAPTR: break; default: alsaplayer_error("ERROR: %s", error_str(data->stream.error, data->str)); alsaplayer_error("No valid frame found at start (pos: %d, error: 0x%x --> %x %x %x %x) (%s)", data->offset, data->stream.error, data->stream.this_frame[0], data->stream.this_frame[1], data->stream.this_frame[2], data->stream.this_frame[3],path); return 0; } } mad_frame_decode(&data->frame, &data->stream); /* alsaplayer_error("xing parsing...%x %x %x %x (%x %d)", data->stream.this_frame[0], data->stream.this_frame[1], data->stream.this_frame[2], data->stream.this_frame[3], data->stream.anc_ptr, data->stream.anc_bitlen); */ if (xing_parse(&data->xing, data->stream.anc_ptr, data->stream.anc_bitlen) == 0) { // We use the xing data later on } mode = (data->frame.header.mode == MAD_MODE_SINGLE_CHANNEL) ? 1 : 2; data->samplerate = data->frame.header.samplerate; data->bitrate = data->frame.header.bitrate; mad_synth_frame (&data->synth, &data->frame); { struct mad_pcm *pcm = &data->synth.pcm; obj->nr_channels = pcm->channels; //alsaplayer_error("nr_channels = %d", obj->nr_channels); } //alsaplayer_error("Initial: %d, %d, %d", data->samplerate, data->bitrate, obj->nr_channels); /* Calculate some values */ data->bytes_avail = data->stream.bufend - data->stream.next_frame; { int64_t time; int64_t samples; int64_t frames; long oldpos = reader_tell(data->mad_fd); reader_seek(data->mad_fd, 0, SEEK_END); data->filesize = reader_tell(data->mad_fd); data->filesize -= data->offset; reader_seek(data->mad_fd, oldpos, SEEK_SET); if (data->bitrate) time = (data->filesize * 8) / (data->bitrate); else time = 0; samples = 32 * MAD_NSBSAMPLES(&data->frame.header); obj->frame_size = (int) samples << 2; /* Assume 16-bit stereo */ frames = data->samplerate * (time+1) / samples; if (data->xing.flags & XING_FRAMES) { obj->nr_frames = data->xing.frames; } else { obj->nr_frames = (int) frames; } obj->nr_tracks = 1; } /* Determine if nr_frames makes sense */ if (!(obj->flags & P_SEEK) && (obj->flags & P_STREAMBASED)) { obj->nr_frames = -1; } /* Allocate frame index */ if (!data->seekable || obj->nr_frames > 1000000 || (data->frames = (ssize_t *)malloc((obj->nr_frames + FRAME_RESERVE) * sizeof(ssize_t))) == NULL) { data->seekable = 0; // Given really } else { data->seekable = 1; data->frames[0] = 0; } data->mad_init = 1; p = strrchr(path, '/'); if (p) { strcpy(data->filename, ++p); } else { strcpy(data->filename, path); } strcpy(data->path, path); data->parse_id3 = prefs_get_bool(ap_prefs, "mad", "parse_id3", 1); return 1; }
/* * 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; xing_init(&player->input.xing); player->output.mode = AUDIO_MODE_DITHER; player->output.attenuation = MAD_F_ONE; 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.precision_in = 0; player->output.precision_out = 0; player->output.path = 0; player->output.command = 0; /* player->output.resample */ player->output.resampled = 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; }