summaryrefslogtreecommitdiff
path: root/plugins/mpgmad/mpgmad.c
diff options
context:
space:
mode:
authorGravatar waker <wakeroid@gmail.com>2011-04-03 22:21:37 +0200
committerGravatar waker <wakeroid@gmail.com>2011-04-03 22:21:37 +0200
commit1951aa1d1af99ee2f5af1610cb4324f0e88605b1 (patch)
tree4ab47998844fe503f2f13d9b6f0c91c97494dc5c /plugins/mpgmad/mpgmad.c
parente427b8b628b00e88dfd84d0e1ce86828fddad91b (diff)
mp3: fixed sample-accurate seeking, disabled gapless delay/padding for now
Diffstat (limited to 'plugins/mpgmad/mpgmad.c')
-rw-r--r--plugins/mpgmad/mpgmad.c140
1 files changed, 57 insertions, 83 deletions
diff --git a/plugins/mpgmad/mpgmad.c b/plugins/mpgmad/mpgmad.c
index 9b27dc31..c79050b4 100644
--- a/plugins/mpgmad/mpgmad.c
+++ b/plugins/mpgmad/mpgmad.c
@@ -84,15 +84,24 @@ typedef struct {
int bitspersample;
int channels;
float duration;
+
+ // currentsample and totalsamples are in the entire file scope (delay/padding inclusive)
int currentsample;
int totalsamples;
+
int skipsamples;
- int startoffset;
- int endoffset;
+
+ int startoffset; // in bytes (id3v2, xing/lame)
+ int endoffset; // in bytes (apev2, id3v1)
+
+ // startsample and endsample exclude delay/padding
int startsample;
int endsample;
- int startdelay;
- int enddelay;
+
+ // number of samples to skip at the start/end of file
+ int delay;
+ int padding;
+
float avg_packetlength;
int avg_samplerate;
int avg_samples_per_frame;
@@ -179,11 +188,9 @@ extract_f32 (unsigned char *buf) {
static int
cmp3_scan_stream (buffer_t *buffer, int sample) {
trace ("cmp3_scan_stream %d\n", sample);
+
int initpos = deadbeef->ftell (buffer->file);
trace ("initpos: %d\n", initpos);
-// if (sample == 0) {
-// sample = -1;
-// }
int packetlength = 0;
int nframe = 0;
int scansamples = 0;
@@ -432,7 +439,7 @@ cmp3_scan_stream (buffer_t *buffer, int sample) {
}
uint32_t nframes = extract_i32 (buf);
if (sample == 0) {
- buffer->duration = (uint64_t)nframes * (uint64_t)samples_per_frame / (uint64_t)samplerate;
+ buffer->duration = (((uint64_t)nframes * (uint64_t)samples_per_frame) - buffer->delay - buffer->padding)/ (uint64_t)samplerate;
}
trace ("xing totalsamples: %d, nframes: %d, samples_per_frame: %d\n", nframes*samples_per_frame, nframes, samples_per_frame);
if (nframes <= 0 || samples_per_frame <= 0) {
@@ -490,8 +497,8 @@ cmp3_scan_stream (buffer_t *buffer, int sample) {
// skip
deadbeef->fseek (buffer->file, 2, SEEK_CUR);
deadbeef->fread (buf, 1, 3, buffer->file);
- buffer->startdelay = (((uint32_t)buf[0]) << 4) | ((((uint32_t)buf[1]) & 0xf0)>>4);
- buffer->enddelay = ((((uint32_t)buf[1])&0x0f)<<8) | ((uint32_t)buf[2]);
+// buffer->delay = (((uint32_t)buf[0]) << 4) | ((((uint32_t)buf[1]) & 0xf0)>>4);
+// buffer->padding = ((((uint32_t)buf[1])&0x0f)<<8) | ((uint32_t)buf[2]);
// skip
deadbeef->fseek (buffer->file, 1, SEEK_CUR);
// mp3gain
@@ -503,14 +510,14 @@ cmp3_scan_stream (buffer_t *buffer, int sample) {
deadbeef->fread (buf, 1, 4, buffer->file);
// uint32_t musiclen = extract_i32 (buf);
- //trace ("lpf: %d, peaksignalamp: %f, radiogain: %d, audiophile: %d, startdelay: %d, enddelay: %d, mp3gain: %d, musiclen: %d\n", lpf, rg_peaksignalamp, rg_radio, rg_audiophile, startdelay, enddelay, mp3gain, musiclen);
+ //trace ("lpf: %d, peaksignalamp: %f, radiogain: %d, audiophile: %d, delay: %d, padding: %d, mp3gain: %d, musiclen: %d\n", lpf, rg_peaksignalamp, rg_radio, rg_audiophile, delay, padding, mp3gain, musiclen);
// skip crc
//deadbeef->fseek (buffer->file, 4, SEEK_CUR);
trace ("lame totalsamples: %d\n", buffer->totalsamples);
}
if (sample <= 0 && (flags&FRAMES_FLAG)) {
buffer->have_xing_header = 1;
- buffer->totalsamples -= buffer->enddelay + buffer->startdelay;
+// buffer->totalsamples -= buffer->delay + buffer->padding;
deadbeef->fseek (buffer->file, framepos+packetlength-4, SEEK_SET);
if (fsize >= 0) {
buffer->bitrate = (fsize - deadbeef->ftell (buffer->file))/ buffer->samplerate * 1000;
@@ -554,7 +561,7 @@ cmp3_scan_stream (buffer_t *buffer, int sample) {
buffer->avg_packetlength = packetlength;
buffer->avg_samplerate = samplerate;
buffer->avg_samples_per_frame = samples_per_frame;
- buffer->duration = buffer->nframes * samples_per_frame / samplerate;
+ buffer->duration = (buffer->nframes * samples_per_frame - buffer->delay - buffer->padding) / samplerate;
buffer->totalsamples = buffer->nframes * samples_per_frame;
trace ("totalsamples: %d, samplesperframe: %d, fsize=%lld\n", buffer->totalsamples, samples_per_frame, fsize);
// trace ("bitrate=%d, layer=%d, packetlength=%d, fsize=%d, nframes=%d, samples_per_frame=%d, samplerate=%d, duration=%f, totalsamples=%d\n", bitrate, layer, packetlength, sz, nframes, samples_per_frame, samplerate, buffer->duration, buffer->totalsamples);
@@ -595,8 +602,8 @@ cmp3_scan_stream (buffer_t *buffer, int sample) {
buffer->nframes = (fsize - buffer->startoffset - buffer->endoffset) / buffer->avg_packetlength;
if (!buffer->have_xing_header) {
- buffer->totalsamples = buffer->nframes * buffer->avg_samples_per_frame - buffer->startdelay - buffer->enddelay;
- buffer->duration = buffer->totalsamples / buffer->avg_samplerate;
+ buffer->totalsamples = buffer->nframes * buffer->avg_samples_per_frame;
+ buffer->duration = (buffer->totalsamples - buffer->delay - buffer->padding) / buffer->avg_samplerate;
}
trace ("nframes: %d, fsize: %lld, spf: %d, smp: %d, totalsamples: %d\n", buffer->nframes, fsize, buffer->avg_samples_per_frame, buffer->avg_samplerate, buffer->totalsamples);
return 0;
@@ -621,11 +628,24 @@ cmp3_scan_stream (buffer_t *buffer, int sample) {
return -1;
}
buffer->totalsamples = scansamples;
- buffer->duration = buffer->totalsamples / buffer->samplerate;
+ buffer->duration = (buffer->totalsamples - buffer->delay - buffer->padding) / buffer->samplerate;
trace ("nframes=%d, totalsamples=%d, samplerate=%d, dur=%f\n", nframe, scansamples, buffer->samplerate, buffer->duration);
return 0;
}
+int
+cmp3_seek_stream (DB_fileinfo_t *_info, int sample) {
+ mpgmad_info_t *info = (mpgmad_info_t *)_info;
+ sample += info->buffer.delay;
+ if (sample == 0) {
+ _info->readpos = 0;
+ info->buffer.currentsample = 0;
+ return 0;
+
+ }
+ return cmp3_scan_stream (&info->buffer, sample);
+}
+
static DB_fileinfo_t *
cmp3_open (uint32_t hints) {
@@ -728,17 +748,15 @@ cmp3_init (DB_fileinfo_t *_info, DB_playItem_t *it) {
info->buffer.startsample = it->startsample;
info->buffer.endsample = it->endsample;
// that comes from cue, don't calc duration, just seek and play
- plugin.seek_sample (_info, 0);
}
else {
deadbeef->pl_set_item_duration (it, info->buffer.duration);
info->buffer.startsample = 0;
- info->buffer.endsample = info->buffer.totalsamples-1;
- info->buffer.skipsamples = info->buffer.startdelay;
- info->buffer.currentsample = info->buffer.startdelay;
+ info->buffer.endsample = info->buffer.totalsamples-info->buffer.delay-info->buffer.padding;
trace ("mpgmad: seeking to %d(%xH) start offset\n", info->buffer.startoffset, info->buffer.startoffset);
deadbeef->fseek (info->buffer.file, info->buffer.startoffset, SEEK_SET);
}
+ plugin.seek_sample (_info, 0);
}
else {
deadbeef->fset_track (info->buffer.file, it);
@@ -852,14 +870,6 @@ cmp3_decode_cut (mpgmad_info_t *info, int framesize) {
trace ("mpgmad: got frame with invalid number of channels (%d)\n", info->buffer.channels);
return 1;
}
- if (info->buffer.currentsample + info->buffer.readsize / (framesize * info->buffer.channels) > info->buffer.endsample) {
- int sz = (info->buffer.endsample - info->buffer.currentsample + 1) * framesize * info->buffer.channels;
-// trace ("size truncated to %d bytes, cursample=%d, endsample=%d, totalsamples=%d\n", info->buffer.readsize, info->buffer.currentsample, info->buffer.endsample, info->buffer.totalsamples);
- if (sz <= 0) {
- return 1;
- }
- info->buffer.readsize = sz;
- }
}
return 0;
}
@@ -1103,55 +1113,30 @@ cmp3_read (DB_fileinfo_t *_info, char *bytes, int size) {
}
#endif
mpgmad_info_t *info = (mpgmad_info_t *)_info;
- info->buffer.readsize = size;
- info->buffer.out = bytes;
- cmp3_decode_int16 (info);
- info->buffer.currentsample += (size - info->buffer.readsize) / 4;
- _info->readpos = (float)(info->buffer.currentsample - info->buffer.startsample) / info->buffer.samplerate;
-#if WRITE_DUMP
- if (size - info->buffer.readsize > 0) {
- fwrite (bytes, 1, size - info->buffer.readsize, out);
- }
-#endif
- return size - info->buffer.readsize;
-}
-
-#if 0
-static int
-cmp3_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) {
-#if WRITE_DUMP
- if (!out) {
- out = fopen ("out.raw", "w+b");
+ int samplesize = _info->fmt.channels * _info->fmt.bps / 8;
+ int curr = info->buffer.currentsample - info->buffer.delay;
+ if (size / samplesize + curr > info->buffer.endsample) {
+ size = (info->buffer.endsample - curr + 1) * samplesize;
+ trace ("mp3: size truncated to %d bytes (%d samples), cursample=%d, endsample=%d\n", size, info->buffer.endsample - curr + 1, curr, info->buffer.endsample);
+ if (size <= 0) {
+ return 0;
+ }
}
-#endif
- mpgmad_info_t *info = (mpgmad_info_t *)_info;
+ int initsize = size;
info->buffer.readsize = size;
info->buffer.out = bytes;
cmp3_decode_int16 (info);
info->buffer.currentsample += (size - info->buffer.readsize) / 4;
- _info->readpos = (float)(info->buffer.currentsample - info->buffer.startsample) / info->buffer.samplerate;
+ _info->readpos = (float)(info->buffer.currentsample - info->buffer.delay - info->buffer.startsample) / info->buffer.samplerate;
#if WRITE_DUMP
if (size - info->buffer.readsize > 0) {
fwrite (bytes, 1, size - info->buffer.readsize, out);
}
#endif
- return size - info->buffer.readsize;
+ return initsize - info->buffer.readsize;
}
static int
-cmp3_read_float32 (DB_fileinfo_t *_info, char *bytes, int size) {
- mpgmad_info_t *info = (mpgmad_info_t *)_info;
-// trace ("cmp3_read_float32 readsize=%d, nchannels=%d\n", size, _info->channels);
- info->buffer.readsize = size;
- info->buffer.out = bytes;
- cmp3_decode_float32 (info);
- info->buffer.currentsample += (size - info->buffer.readsize) / 8;
- _info->readpos = (float)(info->buffer.currentsample - info->buffer.startsample) / info->buffer.samplerate;
- return size - info->buffer.readsize;
-}
-#endif
-
-static int
cmp3_seek_sample (DB_fileinfo_t *_info, int sample) {
mpgmad_info_t *info = (mpgmad_info_t *)_info;
if (!info->buffer.file) {
@@ -1174,7 +1159,6 @@ cmp3_seek_sample (DB_fileinfo_t *_info, int sample) {
// r = deadbeef->fseek (buffer.file, l, SEEK_SET);
if (!r) {
- trace ("seek successful!\n");
info->buffer.skipsamples = sample - frm * info->buffer.avg_samples_per_frame;
info->buffer.currentsample = sample;
@@ -1200,19 +1184,14 @@ cmp3_seek_sample (DB_fileinfo_t *_info, int sample) {
return 0;
}
- sample += info->buffer.startsample + info->buffer.startdelay;
+ sample += info->buffer.startsample;
+// sample += info->buffer.delay;
if (sample > info->buffer.endsample) {
trace ("seek sample %d is beyond end of track (%d)\n", sample, info->buffer.endsample);
return -1; // eof
}
// restart file, and load until we hit required pos
deadbeef->fseek (info->buffer.file, info->buffer.startoffset, SEEK_SET);
-#if 0
- int skip = deadbeef->junk_get_leading_size (info->buffer.file);
- if (skip > 0) {
- deadbeef->fseek (info->buffer.file, skip, SEEK_SET);
- }
-#endif
mad_synth_finish (&info->synth);
mad_frame_finish (&info->frame);
mad_stream_finish (&info->stream);
@@ -1220,16 +1199,9 @@ cmp3_seek_sample (DB_fileinfo_t *_info, int sample) {
info->buffer.readsize = 0;
info->buffer.decode_remaining = 0;
- if (sample == 0) {
- _info->readpos = 0;
- info->buffer.currentsample = 0;
- info->buffer.skipsamples = info->buffer.startdelay;
- return 0;
- }
-
// struct timeval tm1;
// gettimeofday (&tm1, NULL);
- if (cmp3_scan_stream (&info->buffer, sample) == -1) {
+ if (cmp3_seek_stream (_info, sample) == -1) {
trace ("failed to seek to sample %d\n", sample);
_info->readpos = 0;
return -1;
@@ -1242,7 +1214,8 @@ cmp3_seek_sample (DB_fileinfo_t *_info, int sample) {
mad_stream_options (&info->stream, MAD_OPTION_IGNORECRC);
mad_frame_init(&info->frame);
mad_synth_init(&info->synth);
- _info->readpos = (float)(info->buffer.currentsample - info->buffer.startsample) / info->buffer.samplerate;
+ trace ("seeked to %d\n", info->buffer.currentsample-info->buffer.delay);
+ _info->readpos = (float)(info->buffer.currentsample - info->buffer.delay - info->buffer.startsample) / info->buffer.samplerate;
return 0;
}
@@ -1340,7 +1313,8 @@ cmp3_insert (DB_playItem_t *after, const char *fname) {
/*int apeerr = */deadbeef->junk_apev2_read (it, fp);
/*int v2err = */deadbeef->junk_id3v2_read (it, fp);
/*int v1err = */deadbeef->junk_id3v1_read (it, fp);
- deadbeef->pl_add_meta (it, "title", NULL);
+ deadbeef->pl_set_meta_int (it, ":MP3_DELAY", buffer.delay);
+ deadbeef->pl_set_meta_int (it, ":MP3_PADDING", buffer.padding);
buffer.it = it;
cmp3_set_extra_properties (&buffer);
@@ -1350,7 +1324,7 @@ cmp3_insert (DB_playItem_t *after, const char *fname) {
deadbeef->fclose (fp);
// FIXME! bad numsamples passed to cue
- DB_playItem_t *cue_after = deadbeef->pl_insert_cue (after, it, buffer.duration*buffer.samplerate, buffer.samplerate);
+ DB_playItem_t *cue_after = deadbeef->pl_insert_cue (after, it, buffer.totalsamples-buffer.delay-buffer.padding, buffer.samplerate);
if (cue_after) {
deadbeef->pl_item_unref (it);
deadbeef->pl_item_unref (cue_after);