Mercurial > audlegacy-plugins
view src/madplug/decoder.c @ 972:cf7021ca4e7b trunk
[svn] Add lastfm:// transport, an abstract VFS class which derives from curl
to provide lastfm radio support. Written by majeru with some cleanups
by me. Most last.fm metadata support isn't yet implemented, however, and
will need to be done by majeru. ;)
| author | nenolod |
|---|---|
| date | Sun, 22 Apr 2007 04:16:08 -0700 |
| parents | b2fbf53360cf |
| children | 6ba4a4bfd127 |
line wrap: on
line source
/* * mad plugin for audacious * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa * * Portions derived from xmms-mad: * Copyright (C) 2001-2002 Sam Clegg - See COPYING * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; under version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <math.h> #include <assert.h> #include <pthread.h> #include <signal.h> #include <audacious/plugin.h> #include <audacious/output.h> #include <audacious/util.h> #include <sys/time.h> #include "plugin.h" #include "input.h" #define BUFFER_SIZE 16*1024 #define N_AVERAGE_FRAMES 10 extern int triangular_dither_noise(int nbits); /** * Scale PCM data */ static inline signed int scale(mad_fixed_t sample, struct mad_info_t *file_info) { /* replayGain by SamKR */ gdouble scale = -1; if (audmad_config.replaygain.enable) { if (file_info->has_replaygain) { scale = file_info->replaygain_track_scale; if (file_info->replaygain_album_scale != -1 && (scale == -1 || !audmad_config.replaygain.track_mode)) { scale = file_info->replaygain_album_scale; } } if (scale == -1) scale = audmad_config.replaygain.default_scale; } if (scale == -1) scale = 1.0; if (audmad_config.pregain_scale != 1) scale = scale * audmad_config.pregain_scale; /* hard-limit (clipping-prevention) */ if (audmad_config.hard_limit) { /* convert to double before computation, to avoid mad_fixed_t wrapping */ double x = mad_f_todouble(sample) * scale; static const double k = 0.5; // -6dBFS if (x > k) { x = tanh((x - k) / (1 - k)) * (1 - k) + k; } else if (x < -k) { x = tanh((x + k) / (1 - k)) * (1 - k) - k; } sample = x * (MAD_F_ONE); } else sample *= scale; int n_bits_to_loose = MAD_F_FRACBITS + 1 - 16; /* round */ /* add half of the bits_to_loose range to round */ sample += (1L << (n_bits_to_loose - 1)); #ifdef DEBUG_DITHER mad_fixed_t no_dither = sample; #endif /* dither one bit of actual output */ if (audmad_config.dither) { int dither = triangular_dither_noise(n_bits_to_loose + 1); sample += dither; } /* clip */ /* make sure we are between -1 and 1 */ if (sample >= MAD_F_ONE) { sample = MAD_F_ONE - 1; } else if (sample < -MAD_F_ONE) { sample = -MAD_F_ONE; } /* quantize */ /* * Turn our mad_fixed_t into an integer. * Shift all but 16-bits of the fractional part * off the right hand side and shift an extra place * to get the sign bit. */ sample >>= n_bits_to_loose; #ifdef DEBUG_DITHER static int n_zeros = 0; no_dither >>= n_bits_to_loose; if (no_dither - sample == 0) n_zeros++; else { g_message("dither: %d %d", n_zeros, no_dither - sample); n_zeros = 0; } #endif return sample; } void write_output(struct mad_info_t *info, struct mad_pcm *pcm, struct mad_header *header) { unsigned int nsamples; mad_fixed_t const *left_ch, *right_ch; char *output; int olen = 0; int pos = 0; nsamples = pcm->length; left_ch = pcm->samples[0]; right_ch = pcm->samples[1]; olen = nsamples * MAD_NCHANNELS(header) * 2; output = (char *) g_malloc(olen * sizeof(char)); while (nsamples--) { signed int sample; /* output sample(s) in 16-bit signed little-endian PCM */ sample = scale(*left_ch++, info); output[pos++] = (sample >> 0) & 0xff; output[pos++] = (sample >> 8) & 0xff; if (MAD_NCHANNELS(header) == 2) { sample = scale(*right_ch++, info); output[pos++] = (sample >> 0) & 0xff; output[pos++] = (sample >> 8) & 0xff; } } assert(pos == olen); if (!info->playback->playing) return; produce_audio(info->playback->output->written_time(), FMT_S16_LE, MAD_NCHANNELS(header), olen, output, &(info->playback->playing)); if (!info->playback->playing) return; g_free(output); } /** * Decode all headers in the file and fill in stats * @return FALSE if scan failed. */ gboolean scan_file(struct mad_info_t * info, gboolean fast) { struct mad_stream stream; struct mad_header header; int remainder = 0; int data_used = 0; int len = 0; int tagsize = 0; unsigned char buffer[BUFFER_SIZE]; struct mad_frame frame; /* to read xing data */ gboolean has_xing = FALSE; int bitrate_frames = 0; guint xing_bitrate = 0; mad_stream_init(&stream); mad_header_init(&header); mad_frame_init(&frame); xing_init(&info->xing); info->bitrate = 0; info->pos = mad_timer_zero; info->duration = mad_timer_zero; // should be cleared before loop, if we use it as break condition. #ifdef DEBUG g_message("f: scan_file"); g_message("scan_file frames = %d", info->frames); #endif /* DEBUG */ while (1) { remainder = stream.bufend - stream.next_frame; /* if (remainder >= BUFFER_SIZE) { printf("oh dear.. remainder = %d\n", remainder); } */ memcpy(buffer, stream.this_frame, remainder); len = input_get_data(info, buffer + remainder, BUFFER_SIZE - remainder); if (len <= 0) { #ifdef DEBUG g_message("scan_file: len <= 0! len = %d", len); #endif break; } mad_stream_buffer(&stream, buffer, len + remainder); while (!fast || (fast && info->frames < N_AVERAGE_FRAMES)) { if (mad_header_decode(&header, &stream) == -1) { if (stream.error == MAD_ERROR_BUFLEN) { break; } if (!MAD_RECOVERABLE(stream.error)) { #ifdef DEBUG g_message("(fatal) error decoding header %d: %s", info->frames, mad_stream_errorstr(&stream)); g_message("remainder = %d", remainder); g_message("len = %d", len); #endif /* DEBUG */ break; } if (stream.error == MAD_ERROR_LOSTSYNC) { /* ignore LOSTSYNC due to ID3 tags */ tagsize = id3_tag_query(stream.this_frame, stream.bufend - stream.this_frame); if (tagsize > 0) { #ifdef DEBUG g_message("skipping id3_tag: %d", tagsize); #endif /* DEBUG */ mad_stream_skip(&stream, tagsize); continue; } } #ifdef DEBUG g_message("(recovered) error decoding header %d: %s", info->frames, mad_stream_errorstr(&stream)); g_message("remainder = %d", remainder); g_message("len = %d", len); #endif /* DEBUG */ continue; } info->frames++; #ifdef DEBUG #ifdef DEBUG_INTENSIVELY g_message("duration = %lu", mad_timer_count(header.duration, MAD_UNITS_MILLISECONDS)); g_message("size = %d", stream.next_frame - stream.this_frame); #endif #endif if(info->tuple->length == -1) mad_timer_add(&info->duration, header.duration); else { info->duration.seconds = info->tuple->length / 1000; info->duration.fraction = info->tuple->length % 1000; } data_used += stream.next_frame - stream.this_frame; if (info->frames == 1) { /* most of these *should* remain constant */ info->bitrate = header.bitrate; info->freq = header.samplerate; info->channels = MAD_NCHANNELS(&header); info->mpeg_layer = header.layer; info->mode = header.mode; if (audmad_config.use_xing) { frame.header = header; if (mad_frame_decode(&frame, &stream) == -1) { #ifdef DEBUG g_message("xing frame decode failed"); #endif goto no_xing; } if (xing_parse (&info->xing, stream.anc_ptr, stream.anc_bitlen) == 0) { #ifdef DEBUG g_message("found xing header"); #endif /* DEBUG */ has_xing = TRUE; info->vbr = TRUE; /* otherwise xing header would have been 'Info' */ info->frames = info->xing.frames; if(info->tuple->length == -1) mad_timer_multiply(&info->duration, info->frames); else { info->duration.seconds = info->tuple->length / 1000; info->duration.fraction = info->tuple->length % 1000; } xing_bitrate = 8.0 * info->xing.bytes / mad_timer_count(info->duration, MAD_UNITS_SECONDS); #ifdef DEBUG g_message("xing: bitrate = %d", xing_bitrate); #endif continue; } } } else { /* perhaps we have a VRB file */ if (info->bitrate != header.bitrate) info->vbr = TRUE; if (info->vbr) { info->bitrate += header.bitrate; bitrate_frames++; } /* check for changin layer/samplerate/channels */ if (info->mpeg_layer != header.layer) g_warning("layer varies!!"); if (info->freq != header.samplerate) g_warning("samplerate varies!!"); if (info->channels != MAD_NCHANNELS(&header)) g_warning("number of channels varies!!"); } no_xing: if (fast && info->frames >= N_AVERAGE_FRAMES) { float frame_size = ((double) data_used) / N_AVERAGE_FRAMES; #ifdef DEBUG g_message("info->frames = %d info->size = %d tagsize = %d frame_size = %lf", info->frames, info->size, tagsize, frame_size); #endif if(info->size != 0) info->frames = (info->size - tagsize) / frame_size; #ifdef DEBUG g_message("info->frames = %d", info->frames); #endif if(info->tuple->length == -1) { info->duration.seconds /= N_AVERAGE_FRAMES; info->duration.fraction /= N_AVERAGE_FRAMES; mad_timer_multiply(&info->duration, info->frames); } else { info->duration.seconds = info->tuple->length / 1000; info->duration.fraction = info->tuple->length % 1000; } #ifdef DEBUG g_message("using fast playtime calculation"); g_message("data used = %d [tagsize=%d framesize=%f]", data_used, tagsize, frame_size); g_message("frames = %d, frequency = %d, channels = %d", info->frames, info->freq, info->channels); long millis = mad_timer_count(info->duration, MAD_UNITS_MILLISECONDS); g_message("duration = %lu:%lu", millis / 1000 / 60, (millis / 1000) % 60); #endif /* DEBUG */ break; } } if (stream.error != MAD_ERROR_BUFLEN) break; } if (info->vbr && xing_bitrate == 0) { info->bitrate = info->bitrate / bitrate_frames; #ifdef DEBUG g_message("info->bitrate = %d", info->bitrate); #endif } mad_frame_finish(&frame); mad_header_finish(&header); mad_stream_finish(&stream); xing_finish(&info->xing); #ifdef DEBUG g_message("scan_file: info->frames = %d", info->frames); g_message("e: scan_file"); #endif /* DEBUG */ return (info->frames != 0 || info->remote == TRUE); } // sanity check for audio open parameters static gboolean check_audio_param(struct mad_info_t *info) { if(info->fmt < FMT_U8 || info->fmt > FMT_S16_NE) return FALSE; if(info->freq < 0) // not sure about maximum frequency. --yaz return FALSE; if(info->channels < 1 || info->channels > 2) return FALSE; return TRUE; } gpointer decode_loop(gpointer arg) { unsigned char buffer[BUFFER_SIZE]; int len; gboolean seek_skip = FALSE; int remainder = 0; gint tlen; unsigned int iteration = 0; /* mad structs */ struct mad_stream stream; struct mad_frame frame; struct mad_synth synth; /* track info is passed in as thread argument */ struct mad_info_t *info = (struct mad_info_t *) arg; #ifdef DEBUG g_message("f: decode"); #endif /* DEBUG */ /* init mad stuff */ mad_frame_init(&frame); mad_stream_init(&stream); mad_stream_options(&stream, MAD_OPTION_IGNORECRC); mad_synth_init(&synth); if(!info->playback){ #ifdef DEBUG g_message("decode: playback == NULL"); #endif return NULL; } #ifdef DEBUG g_message("decode: fmt = %d freq = %d channels = %d", info->fmt, info->freq, info->channels); #endif if(check_audio_param(info) == FALSE) return NULL; if (!info->playback->output->open_audio(info->fmt, info->freq, info->channels)) { g_mutex_lock(pb_mutex); info->playback->error = TRUE; info->playback->eof = 1; g_mutex_unlock(pb_mutex); g_message("failed to open audio output: %s", info->playback->output->description); return NULL; } /* set mainwin title */ if (info->title) g_free(info->title); info->title = xmms_get_titlestring(audmad_config.title_override == TRUE ? audmad_config.id3_format : xmms_get_gentitle_format(), info->tuple); tlen = (gint) mad_timer_count(info->duration, MAD_UNITS_MILLISECONDS), mad_plugin->set_info(info->title, (tlen == 0 || info->size <= 0) ? -1 : tlen, info->bitrate, info->freq, info->channels); #ifdef DEBUG g_message("decode: tlen = %d", tlen); #endif /* main loop */ do { if (!info->playback->playing) { #ifdef DEBUG g_message("decode: stop signaled"); #endif /* DEBUG */ break; } if (seek_skip) remainder = 0; else { remainder = stream.bufend - stream.next_frame; memcpy(buffer, stream.this_frame, remainder); } len = input_get_data(info, buffer + remainder, BUFFER_SIZE - remainder); input_process_remote_metadata(info); if (len <= 0) { #ifdef DEBUG g_message("finished decoding"); #endif /* DEBUG */ break; } len += remainder; if (len < MAD_BUFFER_GUARD) { int i; for (i = len; i < MAD_BUFFER_GUARD; i++) buffer[i] = 0; len = MAD_BUFFER_GUARD; } mad_stream_buffer(&stream, buffer, len); if (seek_skip) { #ifdef DEBUG g_message("skipping: %d", seek_skip); #endif int skip = 2; do { if (mad_frame_decode(&frame, &stream) == 0) { mad_timer_add(&info->pos, frame.header.duration); if (--skip == 0) mad_synth_frame(&synth, &frame); } else if (!MAD_RECOVERABLE(stream.error)) { g_mutex_lock(pb_mutex); info->playback->error = TRUE; info->playback->eof = 1; g_mutex_unlock(pb_mutex); break; } } while (skip); seek_skip = FALSE; } while (info->playback->playing) { if (info->seek != -1 && info->size > 0) { #ifdef DEBUG g_message("seeking: %ld", info->seek); #endif int new_position; gulong milliseconds = mad_timer_count(info->duration, MAD_UNITS_MILLISECONDS); if (info->seek >= milliseconds) info->seek = milliseconds; mad_timer_set(&info->pos, 0, info->seek, 1000); // in millisecond new_position = ((double) info->seek / (double) milliseconds) * info->size; if(new_position < 0) new_position = 0; #ifdef DEBUG g_message("seeking to: %d bytes", new_position); #endif if (vfs_fseek(info->infile, new_position, SEEK_SET) == -1) audmad_error("failed to seek to: %d", new_position); mad_frame_mute(&frame); mad_synth_mute(&synth); stream.error = MAD_ERROR_BUFLEN; info->playback->output->flush(mad_timer_count(info->pos, MAD_UNITS_MILLISECONDS)); stream.sync = 0; info->seek = -1; seek_skip = TRUE; break; } if (mad_header_decode(&frame.header, &stream) == -1) { if (!MAD_RECOVERABLE(stream.error)) { break; } if (stream.error == MAD_ERROR_LOSTSYNC) { /* ignore LOSTSYNC due to ID3 tags */ int tagsize = id3_tag_query(stream.this_frame, stream.bufend - stream.this_frame); if (tagsize > 0) { mad_stream_skip(&stream, tagsize); continue; } } #ifdef DEBUG g_message("(recovered) error decoding header %d: %s", info->current_frame, mad_stream_errorstr(&stream)); #endif /* DEBUG */ continue; } info->bitrate = frame.header.bitrate; if (!audmad_config.show_avg_vbr_bitrate && info->vbr && (iteration % 40 == 0)) { #ifdef DEBUG #ifdef DEBUG_INTENSIVELY g_message("decode vbr tlen = %d", tlen); #endif #endif mad_plugin->set_info(info->title, (tlen == 0 || info->size <= 0) ? -1 : tlen, info->bitrate, info->freq, info->channels); } iteration++; if (mad_frame_decode(&frame, &stream) == -1) { if (!MAD_RECOVERABLE(stream.error)) break; #ifdef DEBUG g_message("(recovered) error decoding frame %d: %s", info->current_frame, mad_stream_errorstr(&stream)); #endif /* DEBUG */ } info->current_frame++; if (info->freq != frame.header.samplerate || info->channels != (guint) MAD_NCHANNELS(&frame.header)) { #ifdef DEBUG g_message("change in audio type detected"); g_message("old: frequency = %d, channels = %d", info->freq, info->channels); g_message("new: frequency = %d, channels = %d", frame.header.samplerate, (guint) MAD_NCHANNELS(&frame.header)); #endif /* DEBUG */ info->freq = frame.header.samplerate; info->channels = MAD_NCHANNELS(&frame.header); if(audmad_config.force_reopen_audio && check_audio_param(info)) { gint current_time = info->playback->output->output_time(); #ifdef DEBUG g_message("re-opening audio due to change in audio type"); #endif info->playback->output->close_audio(); if (!info->playback->output->open_audio(info->fmt, info->freq, info->channels)) { g_mutex_lock(pb_mutex); info->playback->error = TRUE; info->playback->eof = 1; g_mutex_unlock(pb_mutex); g_message("failed to re-open audio output: %s", info->playback->output->description); return NULL; } // restore time and advance 0.5sec info->seek = current_time + 500; } } if (!info->playback->playing) break; mad_synth_frame(&synth, &frame); mad_stream_sync(&stream); write_output(info, &synth.pcm, &frame.header); mad_timer_add(&info->pos, frame.header.duration); } } while (stream.error == MAD_ERROR_BUFLEN); /* free mad stuff */ mad_frame_finish(&frame); mad_stream_finish(&stream); mad_synth_finish(&synth); if (info->playback->playing) { GTimeVal sleeptime; info->playback->output->buffer_free(); info->playback->output->buffer_free(); while (info->playback->output->buffer_playing()) { #ifdef DEBUG g_message("f: buffer_playing=%d", info->playback->output->buffer_playing()); #endif g_get_current_time(&sleeptime); g_time_val_add(&sleeptime, 500000); g_mutex_lock(mad_mutex); g_cond_timed_wait(mad_cond, mad_mutex, &sleeptime); g_mutex_unlock(mad_mutex); if (!info->playback->playing) { break; } } } #ifdef DEBUG g_message("e: decode"); #endif /* DEBUG */ bmp_title_input_free(info->tuple); info->tuple = NULL; info->playback->output->close_audio(); g_mutex_lock(mad_mutex); info->playback->playing = 0; g_mutex_unlock(mad_mutex); g_thread_exit(0); return NULL; /* dummy */ }
