summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/ffap/ffap.c46
-rw-r--r--plugins/flac/flac.c138
-rw-r--r--plugins/lastfm/lastfm.c140
-rw-r--r--plugins/mpgmad/mpgmad.c617
-rw-r--r--plugins/vorbis/vorbis.c32
5 files changed, 480 insertions, 493 deletions
diff --git a/plugins/ffap/ffap.c b/plugins/ffap/ffap.c
index edd65ff6..99141fd2 100644
--- a/plugins/ffap/ffap.c
+++ b/plugins/ffap/ffap.c
@@ -38,11 +38,16 @@
#define ENABLE_DEBUG 0
+#define trace(...) { fprintf(stderr, __VA_ARGS__); }
+//#define trace(fmt,...)
+
static DB_decoder_t plugin;
static DB_functions_t *deadbeef;
-float timestart;
-float timeend;
+static float timestart;
+static float timeend;
+static int startsample;
+static int endsample;
#define PACKET_BUFFER_SIZE 100000
@@ -698,13 +703,18 @@ ffap_init(DB_playItem_t *it)
plugin.info.channels = ape_ctx.channels;
plugin.info.readpos = 0;
if (it->timeend > 0) {
+ startsample = it->startsample;
+ endsample = it->endsample;
timestart = it->timestart;
timeend = it->timeend;
- plugin.seek (0);
+ plugin.seek_sample (0);
+ trace ("start: %d/%f, end: %d/%f\n", startsample, timestart, endsample, timeend);
}
else {
timestart = 0;
timeend = it->duration;
+ startsample = 0;
+ endsample = ape_ctx.totalsamples-1;
}
return 0;
}
@@ -1635,7 +1645,7 @@ ffap_insert (DB_playItem_t *after, const char *fname) {
float duration = ape_ctx.totalsamples / (float)ape_ctx.samplerate;
DB_playItem_t *it;
- it = deadbeef->pl_insert_cue (after, fname, &plugin, "APE", duration);
+ it = deadbeef->pl_insert_cue (after, fname, &plugin, "APE", ape_ctx.totalsamples, ape_ctx.samplerate);
if (it) {
fclose (fp);
ape_free_ctx (&ape_ctx);
@@ -1707,6 +1717,31 @@ ffap_read_int16 (char *buffer, int size) {
}
static int
+ffap_seek_sample (int sample) {
+ sample += startsample;
+ trace ("seeking to %d/%d\n", sample, ape_ctx.totalsamples);
+ uint32_t newsample = sample;
+ if (newsample > ape_ctx.totalsamples) {
+ trace ("eof\n");
+ return -1;
+ }
+ int nframe = newsample / ape_ctx.blocksperframe;
+ if (nframe >= ape_ctx.totalframes) {
+ trace ("eof2\n");
+ return -1;
+ }
+ ape_ctx.currentframe = nframe;
+ ape_ctx.samplestoskip = newsample - nframe * ape_ctx.blocksperframe;
+
+ // reset decoder
+ ape_ctx.packet_remaining = 0;
+ ape_ctx.samples = 0;
+ ape_ctx.currentsample = newsample;
+ plugin.info.readpos = (float)newsample/ape_ctx.samplerate-timestart;
+ return 0;
+}
+
+static int
ffap_seek (float seconds) {
seconds += timestart;
uint32_t newsample = seconds * plugin.info.samplerate;
@@ -1724,7 +1759,7 @@ ffap_seek (float seconds) {
ape_ctx.packet_remaining = 0;
ape_ctx.samples = 0;
ape_ctx.currentsample = newsample;
- plugin.info.readpos = seconds - timestart;
+ plugin.info.readpos = (float)newsample / ape_ctx.samplerate - timestart;
return 0;
}
@@ -1745,6 +1780,7 @@ static DB_decoder_t plugin = {
.free = ffap_free,
.read_int16 = ffap_read_int16,
.seek = ffap_seek,
+ .seek_sample = ffap_seek_sample,
.insert = ffap_insert,
.exts = exts,
.id = "ffap",
diff --git a/plugins/flac/flac.c b/plugins/flac/flac.c
index 5188947e..3e688446 100644
--- a/plugins/flac/flac.c
+++ b/plugins/flac/flac.c
@@ -33,6 +33,18 @@ static char buffer[BUFFERSIZE]; // this buffer always has int32 samples
static int remaining; // bytes remaining in buffer from last read
static float timestart;
static float timeend;
+static int startsample;
+static int endsample;
+
+typedef struct {
+ DB_playItem_t *after;
+ DB_playItem_t *last;
+ const char *fname;
+ int samplerate;
+ int channels;
+ int totalsamples;
+ int bps;
+} cue_cb_data_t;
static FLAC__StreamDecoderWriteStatus
cflac_write_callback (const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const inputbuffer[], void *client_data) {
@@ -71,14 +83,11 @@ cflac_write_callback (const FLAC__StreamDecoder *decoder, const FLAC__Frame *fra
static void
cflac_metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) {
- FLAC__uint64 total_samples = metadata->data.stream_info.total_samples;
- int sample_rate = metadata->data.stream_info.sample_rate;
- int channels = metadata->data.stream_info.channels;
- int bps = metadata->data.stream_info.bits_per_sample;
- plugin.info.samplerate = sample_rate;
- plugin.info.channels = channels;
- plugin.info.bps = bps;
- plugin.info.readpos = 0;
+ cue_cb_data_t *cb = (cue_cb_data_t *)client_data;
+ cb->totalsamples = metadata->data.stream_info.total_samples;
+ cb->samplerate = metadata->data.stream_info.sample_rate;
+ cb->channels = metadata->data.stream_info.channels;
+ cb->bps = metadata->data.stream_info.bits_per_sample;
}
static void
@@ -126,24 +135,37 @@ cflac_init (DB_playItem_t *it) {
return -1;
}
FLAC__stream_decoder_set_md5_checking(decoder, 0);
- status = FLAC__stream_decoder_init_file(decoder, it->fname, cflac_write_callback, cflac_metadata_callback, cflac_error_callback, NULL);
+ cue_cb_data_t cb;
+ status = FLAC__stream_decoder_init_file(decoder, it->fname, cflac_write_callback, cflac_metadata_callback, cflac_error_callback, &cb);
if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
cflac_free ();
return -1;
}
- plugin.info.samplerate = -1;
+ //plugin.info.samplerate = -1;
if (!FLAC__stream_decoder_process_until_end_of_metadata (decoder)) {
cflac_free ();
return -1;
}
+ plugin.info.samplerate = cb.samplerate;
+ plugin.info.channels = cb.channels;
+ plugin.info.bps = cb.bps;
+ plugin.info.readpos = 0;
if (plugin.info.samplerate == -1) { // not a FLAC stream
cflac_free ();
return -1;
}
- timestart = it->timestart;
- timeend = it->timeend;
if (timeend > timestart || timeend < 0) {
- plugin.seek (0);
+ timestart = it->timestart;
+ timeend = it->timeend;
+ startsample = it->startsample;
+ endsample = it->endsample;
+ plugin.seek_sample (0);
+ }
+ else {
+ timestart = 0;
+ timeend = it->duration;
+ startsample = 0;
+ endsample = cb.totalsamples-1;
}
plugin.info.readpos = 0;
@@ -256,6 +278,17 @@ cflac_seek (float time) {
return 0;
}
+static int
+cflac_seek_sample (int sample) {
+ sample += startsample;
+ if (!FLAC__stream_decoder_seek_absolute (decoder, (FLAC__uint64)(sample))) {
+ return -1;
+ }
+ remaining = 0;
+ plugin.info.readpos = (float)sample / plugin.info.samplerate - timestart;
+ return 0;
+}
+
static FLAC__StreamDecoderWriteStatus
cflac_init_write_callback (const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const inputbuffer[], void *client_data) {
if (frame->header.blocksize == 0 || cflac_init_stop_decoding) {
@@ -264,15 +297,6 @@ cflac_init_write_callback (const FLAC__StreamDecoder *decoder, const FLAC__Frame
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
-typedef struct {
- DB_playItem_t *after;
- DB_playItem_t *last;
- const char *fname;
- int samplerate;
- int nchannels;
- float duration;
-} cue_cb_data_t;
-
static void
cflac_init_cue_metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) {
if (cflac_init_stop_decoding) {
@@ -281,67 +305,10 @@ cflac_init_cue_metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC_
cue_cb_data_t *cb = (cue_cb_data_t *)client_data;
if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
cb->samplerate = metadata->data.stream_info.sample_rate;
- cb->nchannels = metadata->data.stream_info.channels;
- cb->duration = metadata->data.stream_info.total_samples / (float)metadata->data.stream_info.sample_rate;
- }
- // {{{ disabled
-#if 0
- else if (metadata->type == FLAC__METADATA_TYPE_CUESHEET) {
- //printf ("loading embedded cuesheet!\n");
- const FLAC__StreamMetadata_CueSheet *cue = &metadata->data.cue_sheet;
- DB_playItem_t *prev = NULL;
- for (int i = 0; i < cue->num_tracks; i++) {
- FLAC__StreamMetadata_CueSheet_Track *t = &cue->tracks[i];
- if (t->type == 0 && t->num_indices > 0) {
- FLAC__StreamMetadata_CueSheet_Index *idx = t->indices;
- DB_playItem_t *it = malloc (sizeof (DB_playItem_t));
- memset (it, 0, sizeof (DB_playItem_t));
- it->decoder = &plugin;
- it->fname = strdup (cb->fname);
- it->tracknum = t->number;
- it->timestart = (float)t->offset / cb->samplerate;
- it->timeend = -1; // will be filled by next read
- if (prev) {
- prev->timeend = it->timestart;
- prev->duration = prev->timeend - prev->timestart;
- }
- it->filetype = "FLAC";
- //printf ("N: %d, t: %f, bps=%d\n", it->tracknum, it->timestart/60.f, cb->samplerate);
- DB_playItem_t *ins = deadbeef->pl_insert_item (cb->last, it);
- if (ins) {
- cb->last = ins;
- }
- else {
- deadbeef->pl_item_free (it);
- it = NULL;
- }
- prev = it;
- }
- }
- if (prev) {
- prev->timeend = cb->duration;
- prev->duration = prev->timeend - prev->timestart;
- }
- }
- else if (metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
- DB_playItem_t *it = NULL;
- if (cb->after) {
- it = cb->after->next[PL_MAIN];
- }
- else if (cb->last) {
- it = playlist_head[PL_MAIN];
- }
- if (it) {
- for (; it != cb->last->next[PL_MAIN]; it = it->next[PL_MAIN]) {
- char str[10];
- snprintf (str, 10, "%d", it->tracknum);
- pl_add_meta (it, "track", str);
- pl_add_meta (it, "title", NULL);
- }
- }
+ cb->channels = metadata->data.stream_info.channels;
+ //cb->duration = metadata->data.stream_info.total_samples / (float)metadata->data.stream_info.sample_rate;
+ cb->totalsamples = metadata->data.stream_info.total_samples;
}
-#endif
-// }}}
else if (metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
const FLAC__StreamMetadata_VorbisComment *vc = &metadata->data.vorbis_comment;
for (int i = 0; i < vc->num_comments; i++) {
@@ -351,7 +318,7 @@ cflac_init_cue_metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC_
s[c->length] = 0;
memcpy (s, c->entry, c->length);
if (!strncasecmp (s, "cuesheet=", 9)) {
- cb->last = deadbeef->pl_insert_cue_from_buffer (cb->after, cb->fname, s+9, c->length-9, &plugin, "FLAC", cb->duration);
+ cb->last = deadbeef->pl_insert_cue_from_buffer (cb->after, cb->fname, s+9, c->length-9, &plugin, "FLAC", cb->totalsamples, cb->samplerate);
}
}
}
@@ -463,9 +430,9 @@ cflac_insert (DB_playItem_t *after, const char *fname) {
}
// try external cue
- DB_playItem_t *cue_after = deadbeef->pl_insert_cue (after, fname, &plugin, "flac", cb.duration);
+ DB_playItem_t *cue_after = deadbeef->pl_insert_cue (after, fname, &plugin, "flac", cb.totalsamples, cb.samplerate);
if (cue_after) {
- cue_after->timeend = cb.duration;
+ cue_after->timeend = (float)cb.totalsamples/cb.samplerate;
cue_after->duration = cue_after->timeend - cue_after->timestart;
return cue_after;
}
@@ -527,6 +494,7 @@ static DB_decoder_t plugin = {
.read_int16 = cflac_read_int16,
.read_float32 = cflac_read_float32,
.seek = cflac_seek,
+ .seek_sample = cflac_seek_sample,
.insert = cflac_insert,
.exts = exts,
.id = "stdflac",
diff --git a/plugins/lastfm/lastfm.c b/plugins/lastfm/lastfm.c
index 030ca9a9..77ae5657 100644
--- a/plugins/lastfm/lastfm.c
+++ b/plugins/lastfm/lastfm.c
@@ -23,6 +23,9 @@
#include <curl/curl.h>
#include "../../deadbeef.h"
+//#define trace(...) { fprintf(stderr, __VA_ARGS__); }
+#define trace(fmt,...)
+
#define LFM_TESTMODE 0
#define LFM_IGNORE_RULES 0
#define LFM_NOSEND 0
@@ -69,7 +72,7 @@ lastfm_curl_res (void *ptr, size_t size, size_t nmemb, void *stream)
{
int len = size * nmemb;
if (lfm_reply_sz + len >= MAX_REPLY) {
- fprintf (stderr, "reply is too large. stopping.\n");
+ trace ("reply is too large. stopping.\n");
return 0;
}
memcpy (lfm_reply + lfm_reply_sz, ptr, len);
@@ -77,17 +80,17 @@ lastfm_curl_res (void *ptr, size_t size, size_t nmemb, void *stream)
// char s[size*nmemb+1];
// memcpy (s, ptr, size*nmemb);
// s[size*nmemb] = 0;
-// fprintf (stderr, "got from net: %s\n", s);
+// trace ("got from net: %s\n", s);
return len;
}
static int
curl_req_send (const char *req, const char *post) {
- fprintf (stderr, "sending request: %s\n", req);
+ trace ("sending request: %s\n", req);
CURL *curl;
curl = curl_easy_init ();
if (!curl) {
- fprintf (stderr, "lastfm: failed to init curl\n");
+ trace ("lastfm: failed to init curl\n");
return -1;
}
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
@@ -107,7 +110,7 @@ curl_req_send (const char *req, const char *post) {
lfm_reply[lfm_reply_sz] = 0;
}
if (status != 0) {
- fprintf (stderr, "curl request failed, err:\n%s\n", lfm_err);
+ trace ("curl request failed, err:\n%s\n", lfm_err);
}
return status;
}
@@ -148,7 +151,7 @@ auth (void) {
p++;
}
*p = 0;
- fprintf (stderr, "scrobbler auth failed, response: %s\n", lfm_reply);
+ trace ("scrobbler auth failed, response: %s\n", lfm_reply);
goto fail;
}
uint8_t *p = lfm_reply + 2;
@@ -158,7 +161,7 @@ auth (void) {
}
// get session
if (!*p) {
- fprintf (stderr, "unrecognized scrobbler reply:\n%s\n", lfm_reply);
+ trace ("unrecognized scrobbler reply:\n%s\n", lfm_reply);
goto fail;
}
uint8_t *end = p+1;
@@ -166,12 +169,12 @@ auth (void) {
end++;
}
if (end-p > 32) {
- fprintf (stderr, "scrobbler session id is invalid length. probably plugin needs fixing.\n");
+ trace ("scrobbler session id is invalid length. probably plugin needs fixing.\n");
goto fail;
}
strncpy (lfm_sess, p, 32);
lfm_sess[32] = 0;
- fprintf (stderr, "obtained scrobbler session: %s\n", lfm_sess);
+ trace ("obtained scrobbler session: %s\n", lfm_sess);
p = end;
// skip whitespace
while (*p && *p < 0x20) {
@@ -179,7 +182,7 @@ auth (void) {
}
// get nowplaying url
if (!*p) {
- fprintf (stderr, "unrecognized scrobbler reply:\n%s\n", lfm_reply);
+ trace ("unrecognized scrobbler reply:\n%s\n", lfm_reply);
goto fail;
}
end = p+1;
@@ -187,12 +190,12 @@ auth (void) {
end++;
}
if (end - p > sizeof (lfm_nowplaying_url)-1) {
- fprintf (stderr, "scrobbler nowplaying url is too long:\n", lfm_reply);
+ trace ("scrobbler nowplaying url is too long:\n", lfm_reply);
goto fail;
}
strncpy (lfm_nowplaying_url, p, end-p);
lfm_nowplaying_url[end-p] = 0;
- fprintf (stderr, "obtained scrobbler nowplaying url: %s\n", lfm_nowplaying_url);
+ trace ("obtained scrobbler nowplaying url: %s\n", lfm_nowplaying_url);
p = end;
// skip whitespace
while (*p && *p < 0x20) {
@@ -200,7 +203,7 @@ auth (void) {
}
// get submission url
if (!*p) {
- fprintf (stderr, "unrecognized scrobbler reply:\n%s\n", lfm_reply);
+ trace ("unrecognized scrobbler reply:\n%s\n", lfm_reply);
goto fail;
}
end = p+1;
@@ -208,12 +211,12 @@ auth (void) {
end++;
}
if (end - p > sizeof (lfm_submission_url)-1) {
- fprintf (stderr, "scrobbler submission url is too long:\n", lfm_reply);
+ trace ("scrobbler submission url is too long:\n", lfm_reply);
goto fail;
}
strncpy (lfm_submission_url, p, end-p);
lfm_submission_url[end-p] = 0;
- fprintf (stderr, "obtained scrobbler submission url: %s\n", lfm_submission_url);
+ trace ("obtained scrobbler submission url: %s\n", lfm_submission_url);
p = end;
}
else {
@@ -261,18 +264,18 @@ static int
lfm_uri_encode (char *out, int outl, const char *str) {
int l = outl;
static const char echars[] = " ;/?:@=#&";
- //fprintf (stderr, "lfm_uri_encode %p %d %s\n", out, outl, str);
+ //trace ("lfm_uri_encode %p %d %s\n", out, outl, str);
while (*str) {
if (outl <= 1) {
- //fprintf (stderr, "no space left for 1 byte in buffer\n");
+ //trace ("no space left for 1 byte in buffer\n");
return -1;
}
if (strchr (echars, *str)) {
if (outl <= 3) {
- //fprintf (stderr, "no space left for 3 bytes in the buffer\n");
+ //trace ("no space left for 3 bytes in the buffer\n");
return -1;
}
- //fprintf (stderr, "adding escaped value for %c\n", *str);
+ //trace ("adding escaped value for %c\n", *str);
snprintf (out, outl, "%%%02x", (int)*str);
outl -= 3;
str++;
@@ -325,7 +328,7 @@ lfm_add_keyvalue_uri_encoded (char **out, int *outl, const char *key, const char
static int
lfm_format_uri (int subm, DB_playItem_t *song, char *out, int outl) {
if (subm > 50) {
- fprintf (stderr, "lastfm: it's only allowed to send up to 50 submissions at once (got idx=%d)\n", subm);
+ trace ("lastfm: it's only allowed to send up to 50 submissions at once (got idx=%d)\n", subm);
return -1;
}
int sz = outl;
@@ -353,37 +356,37 @@ lfm_format_uri (int subm, DB_playItem_t *song, char *out, int outl) {
}
if (lfm_fetch_song_info (song, &a, &t, &b, &l, &n, &m) == 0) {
-// fprintf (stderr, "playtime: %f\nartist: %s\ntitle: %s\nalbum: %s\nduration: %f\ntracknum: %s\n---\n", song->playtime, a, t, b, l, n);
+// trace ("playtime: %f\nartist: %s\ntitle: %s\nalbum: %s\nduration: %f\ntracknum: %s\n---\n", song->playtime, a, t, b, l, n);
}
else {
-// fprintf (stderr, "file %s doesn't have enough tags to submit to last.fm\n", song->fname);
+// trace ("file %s doesn't have enough tags to submit to last.fm\n", song->fname);
return -1;
}
if (lfm_add_keyvalue_uri_encoded (&out, &outl, ka, a) < 0) {
-// fprintf (stderr, "failed to add %s=%s\n", ka, a);
+// trace ("failed to add %s=%s\n", ka, a);
return -1;
}
if (lfm_add_keyvalue_uri_encoded (&out, &outl, kt, t) < 0) {
-// fprintf (stderr, "failed to add %s=%s\n", kt, t);
+// trace ("failed to add %s=%s\n", kt, t);
return -1;
}
if (lfm_add_keyvalue_uri_encoded (&out, &outl, kb, b) < 0) {
-// fprintf (stderr, "failed to add %s=%s\n", kb, b);
+// trace ("failed to add %s=%s\n", kb, b);
return -1;
}
if (lfm_add_keyvalue_uri_encoded (&out, &outl, kn, n) < 0) {
-// fprintf (stderr, "failed to add %s=%s\n", kn, n);
+// trace ("failed to add %s=%s\n", kn, n);
return -1;
}
if (lfm_add_keyvalue_uri_encoded (&out, &outl, km, m) < 0) {
-// fprintf (stderr, "failed to add %s=%s\n", km, m);
+// trace ("failed to add %s=%s\n", km, m);
return -1;
}
int processed;
processed = snprintf (out, outl, "%s=%d&", kl, (int)l);
if (processed > outl) {
-// fprintf (stderr, "failed to add %s=%d\n", kl, (int)l);
+// trace ("failed to add %s=%d\n", kl, (int)l);
return -1;
}
out += processed;
@@ -391,7 +394,7 @@ lfm_format_uri (int subm, DB_playItem_t *song, char *out, int outl) {
if (subm >= 0) {
processed = snprintf (out, outl, "i[%d]=%d&o[%d]=P&r[%d]=&", subm, (int)song->started_timestamp, subm, subm);
if (processed > outl) {
-// fprintf (stderr, "failed to add i[%d]=%d&o[%d]=P&r[%d]=&\n", subm, (int)song->started_timestamp, subm, subm);
+// trace ("failed to add i[%d]=%d&o[%d]=P&r[%d]=&\n", subm, (int)song->started_timestamp, subm, subm);
return -1;
}
out += processed;
@@ -407,7 +410,7 @@ lastfm_songstarted (DB_event_song_t *ev, uintptr_t data) {
if (lfm_format_uri (-1, ev->song, lfm_nowplaying, sizeof (lfm_nowplaying)) < 0) {
lfm_nowplaying[0] = 0;
}
-// fprintf (stderr, "%s\n", lfm_nowplaying);
+// trace ("%s\n", lfm_nowplaying);
deadbeef->mutex_unlock (lfm_mutex);
if (lfm_nowplaying[0]) {
deadbeef->cond_signal (lfm_cond);
@@ -418,29 +421,34 @@ lastfm_songstarted (DB_event_song_t *ev, uintptr_t data) {
static int
lastfm_songfinished (DB_event_song_t *ev, uintptr_t data) {
+ trace ("lfm songfinished\n");
#if !LFM_IGNORE_RULES
// check submission rules
+ // duration must be >= 30 sec
+ if (ev->song->duration < 30) {
+ trace ("song duration is %f seconds. not eligible for submission\n", ev->song->duration);
+ return 0;
+ }
// must be played for >=240sec of half the total time
if (ev->song->playtime < 240 && ev->song->playtime < ev->song->duration/2) {
+ trace ("song playtime=%f seconds. not eligible for submission\n", ev->song->playtime);
return 0;
}
- // duration must be >= 30 sec
- if (ev->song->duration < 30) {
- return 0;
- }
#endif
if (!deadbeef->pl_find_meta (ev->song, "artist")
|| !deadbeef->pl_find_meta (ev->song, "title")
// || !deadbeef->pl_find_meta (ev->song, "album")
) {
+ trace ("lfm: not enough metadata for submission, artist=%s, title=%s, album=%s\n", deadbeef->pl_find_meta (ev->song, "artist"), deadbeef->pl_find_meta (ev->song, "title"), deadbeef->pl_find_meta (ev->song, "album"));
return 0;
}
deadbeef->mutex_lock (lfm_mutex);
// find free place in queue
for (int i = 0; i < LFM_SUBMISSION_QUEUE_SIZE; i++) {
if (!lfm_subm_queue[i]) {
+ trace ("lfm: song is now in queue for submission\n");
lfm_subm_queue[i] = deadbeef->pl_item_alloc ();
deadbeef->pl_item_copy (lfm_subm_queue[i], ev->song);
break;
@@ -455,11 +463,11 @@ lastfm_songfinished (DB_event_song_t *ev, uintptr_t data) {
static void
lfm_send_nowplaying (void) {
if (auth () < 0) {
- fprintf (stderr, "auth failed! nowplaying cancelled.\n");
+ trace ("auth failed! nowplaying cancelled.\n");
lfm_nowplaying[0] = 0;
return;
}
- fprintf (stderr, "auth successful! setting nowplaying\n");
+ trace ("auth successful! setting nowplaying\n");
char s[100];
snprintf (s, sizeof (s), "s=%s&", lfm_sess);
int l = strlen (lfm_nowplaying);
@@ -469,23 +477,23 @@ lfm_send_nowplaying (void) {
int status = curl_req_send (lfm_nowplaying_url, lfm_nowplaying);
if (!status) {
if (strncmp (lfm_reply, "OK", 2)) {
- fprintf (stderr, "nowplaying failed, response:\n%s\n", lfm_reply);
+ trace ("nowplaying failed, response:\n%s\n", lfm_reply);
if (!strncmp (lfm_reply, "BADSESSION", 7)) {
- fprintf (stderr, "got badsession; trying to restore session...\n");
+ trace ("got badsession; trying to restore session...\n");
lfm_sess[0] = 0;
curl_req_cleanup ();
if (auth () < 0) {
- fprintf (stderr, "fail!\n");
+ trace ("fail!\n");
break; // total fail
}
- fprintf (stderr, "success! retrying send nowplaying...\n");
+ trace ("success! retrying send nowplaying...\n");
snprintf (s, sizeof (s), "s=%s&", lfm_sess);
strcpy (lfm_nowplaying+l, s);
continue; // retry with new session
}
}
else {
- fprintf (stderr, "nowplaying success! response:\n%s\n", lfm_reply);
+ trace ("nowplaying success! response:\n%s\n", lfm_reply);
}
}
curl_req_cleanup ();
@@ -497,6 +505,7 @@ lfm_send_nowplaying (void) {
static void
lfm_send_submissions (void) {
+ trace ("lfm_send_submissions\n");
int i;
char req[1024*50];
int idx = 0;
@@ -508,6 +517,7 @@ lfm_send_submissions (void) {
if (lfm_subm_queue[i]) {
res = lfm_format_uri (idx, lfm_subm_queue[i], r, len);
if (res < 0) {
+ trace ("lfm: failed to format uri\n");
return;
}
len -= res;
@@ -526,28 +536,28 @@ lfm_send_submissions (void) {
if (res > len) {
return;
}
- fprintf (stderr, "submission req string:\n%s\n", req);
+ trace ("submission req string:\n%s\n", req);
#if !LFM_NOSEND
for (int attempts = 2; attempts > 0; attempts--) {
int status = curl_req_send (lfm_submission_url, req);
if (!status) {
if (strncmp (lfm_reply, "OK", 2)) {
- fprintf (stderr, "submission failed, response:\n%s\n", lfm_reply);
+ trace ("submission failed, response:\n%s\n", lfm_reply);
if (!strncmp (lfm_reply, "BADSESSION", 7)) {
- fprintf (stderr, "got badsession; trying to restore session...\n");
+ trace ("got badsession; trying to restore session...\n");
lfm_sess[0] = 0;
curl_req_cleanup ();
if (auth () < 0) {
- fprintf (stderr, "fail!\n");
+ trace ("fail!\n");
break; // total fail
}
- fprintf (stderr, "success! retrying send nowplaying...\n");
+ trace ("success! retrying send nowplaying...\n");
res = snprintf (r, len, "s=%s&", lfm_sess);
continue; // retry with new session
}
}
else {
- fprintf (stderr, "submission successful, response:\n%s\n", lfm_reply);
+ trace ("submission successful, response:\n%s\n", lfm_reply);
deadbeef->mutex_lock (lfm_mutex);
for (i = 0; i < LFM_SUBMISSION_QUEUE_SIZE; i++) {
if (lfm_subm_queue[i]) {
@@ -561,19 +571,29 @@ lfm_send_submissions (void) {
curl_req_cleanup ();
break;
}
+#else
+ trace ("submission successful (NOSEND=1):\n");
+ deadbeef->mutex_lock (lfm_mutex);
+ for (i = 0; i < LFM_SUBMISSION_QUEUE_SIZE; i++) {
+ if (lfm_subm_queue[i]) {
+ deadbeef->pl_item_free (lfm_subm_queue[i]);
+ lfm_subm_queue[i] = NULL;
+ }
+ }
+ deadbeef->mutex_unlock (lfm_mutex);
#endif
}
static void
lfm_thread (uintptr_t ctx) {
- //fprintf (stderr, "lfm_thread started\n");
+ //trace ("lfm_thread started\n");
for (;;) {
deadbeef->cond_wait (lfm_cond, lfm_mutex);
- fprintf (stderr, "cond signalled!\n");
+ trace ("cond signalled!\n");
if (lfm_stopthread) {
deadbeef->mutex_unlock (lfm_mutex);
deadbeef->cond_signal (lfm_cond);
- fprintf (stderr, "lfm_thread end\n");
+ trace ("lfm_thread end\n");
return;
}
deadbeef->mutex_unlock (lfm_mutex);
@@ -615,9 +635,9 @@ auth_v2 (void) {
if (end) {
*end = 0;
snprintf (msg, sizeof (msg), "http://www.last.fm/api/auth/?api_key=%s&token=%s", LASTFM_API_KEY, token);
- fprintf (stderr, "Dear user. Please visit this URL and authenticate deadbeef. Thanks.\n");
+ trace ("Dear user. Please visit this URL and authenticate deadbeef. Thanks.\n");
- fprintf (stderr, "%s\n", msg);
+ trace ("%s\n", msg);
strncpy (lfm_token, token, 32);
lfm_token[32] = 0;
}
@@ -645,16 +665,16 @@ auth_v2 (void) {
*end = 0;
char config[1024];
snprintf (config, sizeof (config), "%s/.config/deadbeef/lastfmv2", getenv ("HOME"));
- fprintf (stderr, "got session key %s\n", sess);
+ trace ("got session key %s\n", sess);
FILE *fp = fopen (config, "w+b");
if (!fp) {
- fprintf (stderr, "lastfm: failed to write config file %s\n", config);
+ trace ("lastfm: failed to write config file %s\n", config);
curl_req_cleanup ();
return -1;
}
if (fwrite (sess, 1, 32, fp) != 32) {
fclose (fp);
- fprintf (stderr, "lastfm: failed to write config file %s\n", config);
+ trace ("lastfm: failed to write config file %s\n", config);
curl_req_cleanup ();
return -1;
}
@@ -662,7 +682,7 @@ auth_v2 (void) {
strcpy (lfm_sess, sess);
}
}
-// fprintf (stderr, "reply: %s\n", lfm_reply);
+// trace ("reply: %s\n", lfm_reply);
}
curl_req_cleanup ();
if (lfm_sess[0]) {
@@ -708,16 +728,16 @@ lastfm_start (void) {
snprintf (config, 1024, "%s/lastfm", deadbeef->get_config_dir ());
FILE *fp = fopen (config, "rt");
if (!fp) {
- fprintf (stderr, "lastfm: failed open %s\n", config);
+ trace ("lastfm: failed open %s\n", config);
return -1;
}
if (!fgets (lfm_user, 50, fp)) {
- fprintf (stderr, "lastfm: failed to read login from %s\n", config);
+ trace ("lastfm: failed to read login from %s\n", config);
fclose (fp);
return -1;
}
if (!fgets (lfm_pass, 50, fp)) {
- fprintf (stderr, "lastfm: failed to read pass from %s\n", config);
+ trace ("lastfm: failed to read pass from %s\n", config);
fclose (fp);
return -1;
}
@@ -745,7 +765,7 @@ lastfm_start (void) {
static int
lastfm_stop (void) {
- //fprintf (stderr, "lastfm_stop\n");
+ //trace ("lastfm_stop\n");
deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_SONGSTARTED, DB_CALLBACK (lastfm_songstarted), 0);
deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_SONGFINISHED, DB_CALLBACK (lastfm_songfinished), 0);
lfm_stopthread = 1;
diff --git a/plugins/mpgmad/mpgmad.c b/plugins/mpgmad/mpgmad.c
index 74847f37..46f0bbd6 100644
--- a/plugins/mpgmad/mpgmad.c
+++ b/plugins/mpgmad/mpgmad.c
@@ -22,6 +22,9 @@
#include <stdlib.h>
#include "../../deadbeef.h"
+#define trace(...) { fprintf(stderr, __VA_ARGS__); }
+//#define trace(fmt,...)
+
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
@@ -44,6 +47,12 @@ static DB_functions_t *deadbeef;
#define LAME_VBR3 5
#define LAME_VBR4 6
+// xing header flags
+#define FRAMES_FLAG 0x0001
+#define BYTES_FLAG 0x0002
+#define TOC_FLAG 0x0004
+#define VBR_SCALE_FLAG 0x0008
+
typedef struct {
FILE *file;
@@ -51,9 +60,8 @@ typedef struct {
float timestart;
float timeend;
- // input buffer, for MPEG data
- // FIXME: this should go away if reading happens per-frame
- mad_timer_t timer;
+// // input buffer, for MPEG data
+// // FIXME: this should go away if reading happens per-frame
char input[READBUFFER];
int remaining;
@@ -77,25 +85,15 @@ typedef struct {
int bitspersample;
int channels;
float duration;
+ int currentsample;
+ int totalsamples;
+ int skipsamples;
int startoffset;
int endoffset;
-#if 0
- // only for xing/lame header
- uint32_t frames;
- uint32_t flags;
- uint32_t vbr_scale;
- uint32_t bytes;
- uint8_t lame_revision;
- uint8_t vbrmethod;
- uint8_t vbrbitrate;
- uint8_t mp3gain;
- float peak_signal_amp;
- uint16_t radio_replay_gain;
- uint16_t audiophile_replay_gain;
- uint16_t delay_start;
- uint16_t delay_end;
- uint32_t musiclength;
-#endif
+ int startsample;
+ int endsample;
+ int startdelay;
+ int enddelay;
} buffer_t;
static buffer_t buffer;
@@ -106,101 +104,6 @@ static struct mad_synth synth;
static int
cmp3_decode (void);
-static int
-cmp3_scan_stream (buffer_t *buffer, float position);
-
-static int
-cmp3_init (DB_playItem_t *it) {
- memset (&buffer, 0, sizeof (buffer));
- buffer.file = fopen (it->fname, "rb");
- if (!buffer.file) {
- return -1;
- }
- buffer.startoffset = it->startoffset;
- buffer.endoffset = it->endoffset;
- buffer.remaining = 0;
- //buffer.output = NULL;
- buffer.readsize = 0;
- buffer.cachefill = 0;
- buffer.cachepos = 0;
- plugin.info.readpos = 0;
- mad_timer_reset(&buffer.timer);
-
-// fseek (buffer.file, buffer.startoffset, SEEK_SET);
- if (it->timeend > 0) {
- buffer.timestart = it->timestart;
- buffer.timeend = it->timeend;
- // that comes from cue, don't calc duration, just seek and play
- cmp3_scan_stream (&buffer, it->timestart);
- mad_timer_reset(&buffer.timer);
- }
- else {
- it->duration = cmp3_scan_stream (&buffer, -1); // scan entire stream, calc duration
- buffer.timestart = 0;
- buffer.timeend = it->duration;
- fseek (buffer.file, buffer.startoffset, SEEK_SET);
- }
- if (buffer.samplerate == 0) {
- //fprintf (stderr, "bad mpeg file: %f\n", it->fname);
- fclose (buffer.file);
- return -1;
- }
- plugin.info.bps = buffer.bitspersample;
- plugin.info.samplerate = buffer.samplerate;
- plugin.info.channels = buffer.channels;
-
- mad_stream_init(&stream);
- mad_frame_init(&frame);
- mad_synth_init(&synth);
-
- return 0;
-}
-
-/****************************************************************************
- * Converts a sample from libmad's fixed point number format to a signed *
- * short (16 bits). *
- ****************************************************************************/
-static inline int16_t
-MadFixedToSshort(mad_fixed_t Fixed)
-{
- /* A fixed point number is formed of the following bit pattern:
- *
- * SWWWFFFFFFFFFFFFFFFFFFFFFFFFFFFF
- * MSB LSB
- * S ==> Sign (0 is positive, 1 is negative)
- * W ==> Whole part bits
- * F ==> Fractional part bits
- *
- * This pattern contains MAD_F_FRACBITS fractional bits, one
- * should alway use this macro when working on the bits of a fixed
- * point number. It is not guaranteed to be constant over the
- * different platforms supported by libmad.
- *
- * The signed short value is formed, after clipping, by the least
- * significant whole part bit, followed by the 15 most significant
- * fractional part bits. Warning: this is a quick and dirty way to
- * compute the 16-bit number, madplay includes much better
- * algorithms.
- */
-
- /* Clipping */
- if(Fixed>=MAD_F_ONE)
- return(32767);
- if(Fixed<=-MAD_F_ONE)
- return(-32768);
-
- /* Conversion. */
- Fixed=Fixed>>(MAD_F_FRACBITS-15);
- return((signed short)Fixed);
-}
-
-static inline float
-MadFixedToFloat (mad_fixed_t Fixed) {
- return (float)((Fixed) / (float)(1L << MAD_F_FRACBITS));
-}
-
-#define MadErrorString(x) mad_stream_errorstr(x)
-
static uint32_t
extract_i32 (unsigned char *buf)
{
@@ -262,41 +165,31 @@ extract_f32 (unsigned char *buf) {
return f;
}
-// function scans file and calculates duration
-// if position >= 0 -- scanning is stopped after duration is greater than position
+// sample=-1: scan entire stream, calculate precise duration
+// sample=0: read headers/tags, calculate approximate duration
+// sample>0: seek to the frame with the sample, update skipsamples
+// return value: -1 on error
static int
-cmp3_scan_stream (buffer_t *buffer, float position) {
+cmp3_scan_stream (buffer_t *buffer, int sample) {
int nframe = 0;
- int nskipped = 0;
- float duration = 0;
- int nreads = 0;
- int nseeks = 0;
-
- int pos = ftell (buffer->file);
- if (pos <= 0) {
- // try to skip id3v2
- int skip = deadbeef->junk_get_leading_size (buffer->file);
- if (skip > 0) {
- fseek (buffer->file, skip, SEEK_SET);
- }
+ int got_xing_header = 0;
+ buffer->duration = 0;
+ int scansamples = 0;
+ buffer->currentsample = 0;
+ buffer->skipsamples = 0;
+
+ if (sample <= 0) {
+ buffer->totalsamples = 0;
}
for (;;) {
- if (position >= 0 && duration > position) {
- // set decoder timer
- buffer->timer.seconds = (int)duration;
- buffer->timer.fraction = (int)((duration - (float)buffer->timer.seconds)*MAD_TIMER_RESOLUTION);
- return duration;
- }
uint32_t hdr;
uint8_t sync;
- pos = ftell (buffer->file);
+ size_t pos = ftell (buffer->file);
if (fread (&sync, 1, 1, buffer->file) != 1) {
break; // eof
}
- nreads++;
if (sync != 0xff) {
- nskipped++;
continue; // not an mpeg frame
}
else {
@@ -304,9 +197,7 @@ cmp3_scan_stream (buffer_t *buffer, float position) {
if (fread (&sync, 1, 1, buffer->file) != 1) {
break; // eof
}
- nreads++;
if ((sync >> 5) != 7) {
- nskipped++;
continue;
}
}
@@ -316,14 +207,11 @@ cmp3_scan_stream (buffer_t *buffer, float position) {
if (fread (&sync, 1, 1, buffer->file) != 1) {
break; // eof
}
- nreads++;
hdr |= sync << 8;
if (fread (&sync, 1, 1, buffer->file) != 1) {
break; // eof
}
- nreads++;
hdr |= sync;
- nskipped = 0;
// parse header
@@ -434,7 +322,7 @@ cmp3_scan_stream (buffer_t *buffer, float position) {
continue;
}
- if (position != 0 || nframe == 0/* || buffer->version != ver || buffer->layer != layer*/)
+ if (sample != 0 || nframe == 0)
{
buffer->version = ver;
buffer->layer = layer;
@@ -446,8 +334,11 @@ cmp3_scan_stream (buffer_t *buffer, float position) {
buffer->bitspersample = 16;
//fprintf (stderr, "frame %d(@%d) mpeg v%d layer %d bitrate %d samplerate %d packetlength %d framedur %f channels %d\n", nframe, pos, ver, layer, bitrate, samplerate, packetlength, dur, nchannels);
}
- // try to read xing/info tag
- if (position == 0) {
+ // try to read xing/info tag (only on initial scans)
+ if (sample <= 0 && !got_xing_header)
+ {
+ size_t framepos = ftell (buffer->file);
+ trace ("trying to read xing header\n");
if (ver == 1) {
fseek (buffer->file, 32, SEEK_CUR);
}
@@ -467,50 +358,217 @@ cmp3_scan_stream (buffer_t *buffer, float position) {
}
if (!strncmp (xing, magic, 4) || !strncmp (info, magic, 4)) {
+ trace ("xing/info frame found\n");
// read flags
uint32_t flags;
- char buf[4];
+ uint8_t buf[4];
if (fread (buf, 1, 4, buffer->file) != 4) {
return -1; // EOF
}
flags = extract_i32 (buf);
- if (flags & 0x01) {
+ if (flags & FRAMES_FLAG) {
// read number of frames
if (fread (buf, 1, 4, buffer->file) != 4) {
return -1; // EOF
}
uint32_t nframes = extract_i32 (buf);
buffer->duration = (float)nframes * (float)samples_per_frame / (float)samplerate;
+ buffer->totalsamples = nframes * samples_per_frame;
+ trace ("xing totalsamples: %d\n", buffer->totalsamples);
+ buffer->samplerate = samplerate;
+ }
+ if (flags & BYTES_FLAG) {
+ fseek (buffer->file, 4, SEEK_CUR);
+ }
+ if (flags & TOC_FLAG) {
+ fseek (buffer->file, 100, SEEK_CUR);
+ }
+ if (flags & VBR_SCALE_FLAG) {
+ fseek (buffer->file, 4, SEEK_CUR);
+ }
+ // lame header
+ if (fread (buf, 1, 4, buffer->file) != 4) {
+ return -1; // EOF
+ }
+ trace ("tell=%x, %c%c%c%c\n", ftell(buffer->file), buf[0], buf[1], buf[2], buf[3]);
+ if (!memcmp (buf, "LAME", 4)) {
+ trace ("lame header found\n");
+ fseek (buffer->file, 6, SEEK_CUR);
+
+ // FIXME: that can be optimized by single read
+ uint8_t lpf;
+ fread (&lpf, 1, 1, buffer->file);
+ //3 floats: replay gain
+ fread (buf, 1, 4, buffer->file);
+ float rg_peaksignalamp = extract_f32 (buf);
+ fread (buf, 1, 2, buffer->file);
+ uint16_t rg_radio = extract_i16 (buf);
+ fread (buf, 1, 2, buffer->file);
+ uint16_t rg_audiophile = extract_i16 (buf);
+ // skip
+ fseek (buffer->file, 2, SEEK_CUR);
+ fread (buf, 1, 3, buffer->file);
+ uint32_t startdelay = (((uint32_t)buf[0]) << 4) | ((((uint32_t)buf[1]) & 0xf0)>>4);
+ uint32_t enddelay = ((((uint32_t)buf[1])&0x0f)<<8) | ((uint32_t)buf[2]);
+ // skip
+ fseek (buffer->file, 1, SEEK_CUR);
+ // mp3gain
+ uint8_t mp3gain;
+ fread (&mp3gain, 1, 1, buffer->file);
+ // skip
+ fseek (buffer->file, 2, SEEK_CUR);
+ // musiclen
+ 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);
+ // skip crc
+ //fseek (buffer->file, 4, SEEK_CUR);
+ buffer->startdelay = startdelay;
+ buffer->enddelay = enddelay;
+ }
+ if (sample <= 0 && (flags&FRAMES_FLAG)) {
+ buffer->totalsamples -= buffer->enddelay;
+ fseek (buffer->file, framepos+packetlength-4, SEEK_SET);
return 0;
}
}
- // xing header failed, calculate based on file size
- fseek (buffer->file, 0, SEEK_END);
- int sz = ftell (buffer->file) - buffer->startoffset - buffer->endoffset;
- int nframes = sz / packetlength;
- buffer->duration = nframes * samples_per_frame / samplerate;
+ if (sample == 0) {
+ // xing header failed, calculate based on file size
+ fseek (buffer->file, 0, SEEK_END);
+ int sz = ftell (buffer->file) - buffer->startoffset - buffer->endoffset;
+ int nframes = sz / packetlength;
+ buffer->duration = nframes * samples_per_frame / samplerate;
+ buffer->totalsamples = nframes * samples_per_frame;
+ buffer->samplerate = samplerate;
+
+ if (sample == 0) {
+ fseek (buffer->file, framepos+packetlength-4, SEEK_SET);
+ return 0;
+ }
+ }
+ fseek (buffer->file, framepos+packetlength-4, SEEK_SET);
+ got_xing_header = 1;
+ }
+ if (sample >= 0 && scansamples + samples_per_frame >= sample) {
+ fseek (buffer->file, -4, SEEK_CUR);
+ buffer->currentsample = sample;
+ buffer->skipsamples = scansamples + samples_per_frame - sample;
return 0;
}
+ scansamples += samples_per_frame;
- duration += dur;
+ buffer->duration += dur;
nframe++;
if (packetlength > 0) {
fseek (buffer->file, packetlength-4, SEEK_CUR);
- nseeks++;
}
}
- if (position >= 0 && duration >= position) {
- // set decoder timer
- buffer->timer.seconds = (int)duration;
- buffer->timer.fraction = (int)((duration - (float)buffer->timer.seconds)*MAD_TIMER_RESOLUTION);
- }
if (nframe == 0) {
return -1;
}
- return duration;
+ buffer->totalsamples = scansamples;
+ buffer->duration = buffer->totalsamples / buffer->samplerate;
+ return 0;
+}
+
+
+static int
+cmp3_init (DB_playItem_t *it) {
+ memset (&buffer, 0, sizeof (buffer));
+ buffer.file = fopen (it->fname, "rb");
+ if (!buffer.file) {
+ return -1;
+ }
+ int skip = deadbeef->junk_get_leading_size (buffer.file);
+ if (skip > 0) {
+ fseek(buffer.file, skip, SEEK_SET);
+ }
+ buffer.remaining = 0;
+ //buffer.output = NULL;
+ buffer.readsize = 0;
+ buffer.cachefill = 0;
+ buffer.cachepos = 0;
+ plugin.info.readpos = 0;
+
+ cmp3_scan_stream (&buffer, -1); // scan entire stream, calc duration
+ if (it->timeend > 0) {
+ buffer.timestart = it->timestart;
+ buffer.timeend = it->timeend;
+ buffer.startsample = it->startsample;
+ buffer.endsample = it->endsample;
+ // that comes from cue, don't calc duration, just seek and play
+ plugin.seek_sample (0);
+ }
+ else {
+ it->duration = buffer.duration;
+ buffer.timestart = 0;
+ buffer.timeend = it->duration;
+ buffer.startsample = 0;
+ buffer.endsample = it->duration * buffer.samplerate - 1;
+ fseek (buffer.file, buffer.startoffset, SEEK_SET);
+ }
+ if (buffer.samplerate == 0) {
+ //fprintf (stderr, "bad mpeg file: %f\n", it->fname);
+ fclose (buffer.file);
+ return -1;
+ }
+ plugin.info.bps = buffer.bitspersample;
+ plugin.info.samplerate = buffer.samplerate;
+ plugin.info.channels = buffer.channels;
+
+ mad_stream_init(&stream);
+ mad_frame_init(&frame);
+ mad_synth_init(&synth);
+
+ return 0;
}
+/****************************************************************************
+ * Converts a sample from libmad's fixed point number format to a signed *
+ * short (16 bits). *
+ ****************************************************************************/
+static inline int16_t
+MadFixedToSshort(mad_fixed_t Fixed)
+{
+ /* A fixed point number is formed of the following bit pattern:
+ *
+ * SWWWFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+ * MSB LSB
+ * S ==> Sign (0 is positive, 1 is negative)
+ * W ==> Whole part bits
+ * F ==> Fractional part bits
+ *
+ * This pattern contains MAD_F_FRACBITS fractional bits, one
+ * should alway use this macro when working on the bits of a fixed
+ * point number. It is not guaranteed to be constant over the
+ * different platforms supported by libmad.
+ *
+ * The signed short value is formed, after clipping, by the least
+ * significant whole part bit, followed by the 15 most significant
+ * fractional part bits. Warning: this is a quick and dirty way to
+ * compute the 16-bit number, madplay includes much better
+ * algorithms.
+ */
+
+ /* Clipping */
+ if(Fixed>=MAD_F_ONE)
+ return(32767);
+ if(Fixed<=-MAD_F_ONE)
+ return(-32768);
+
+ /* Conversion. */
+ Fixed=Fixed>>(MAD_F_FRACBITS-15);
+ return((signed short)Fixed);
+}
+
+static inline float
+MadFixedToFloat (mad_fixed_t Fixed) {
+ return (float)((Fixed) / (float)(1L << MAD_F_FRACBITS));
+}
+
+#define MadErrorString(x) mad_stream_errorstr(x)
+
static int
cmp3_decode (void) {
int eof = 0;
@@ -519,6 +577,7 @@ cmp3_decode (void) {
if (eof) {
break;
}
+ // FIXME: read single frame here
// read more MPEG data if needed
if(stream.buffer==NULL || stream.error==MAD_ERROR_BUFLEN) {
// copy part of last frame to beginning
@@ -543,28 +602,31 @@ cmp3_decode (void) {
}
bytesread += buffer.remaining;
mad_stream_buffer(&stream,buffer.input,bytesread);
- stream.error=0;
+ if (stream.buffer==NULL) {
+ // check sync bits
+ if (bytes[0] != 0xff || (bytes[1]&(3<<5)) != (3<<5)) {
+ trace ("mpgmad: read didn't start at frame boundary!\ncmp3_scan_stream is broken\n");
+ }
+ }
}
+ stream.error=0;
// decode next frame
if(mad_frame_decode(&frame,&stream))
{
if(MAD_RECOVERABLE(stream.error))
{
- if(stream.error!=MAD_ERROR_LOSTSYNC)
- {
- //fprintf(stderr,"recoverable frame level error (%s)\n",
- // MadErrorString(&stream));
- //fflush(stderr);
+ if(stream.error!=MAD_ERROR_LOSTSYNC) {
+ trace ("mpgmad: recoverable frame level error (%s)\n", MadErrorString(&stream));
}
continue;
}
else {
- if(stream.error==MAD_ERROR_BUFLEN)
+ if(stream.error==MAD_ERROR_BUFLEN) {
continue;
+ }
else
{
- //fprintf(stderr,"unrecoverable frame level error (%s).\n",
- // MadErrorString(&stream));
+ trace ("mpgmad: unrecoverable frame level error (%s).\n", MadErrorString(&stream));
break;
}
}
@@ -572,23 +634,26 @@ cmp3_decode (void) {
plugin.info.samplerate = frame.header.samplerate;
plugin.info.channels = MAD_NCHANNELS(&frame.header);
-
- mad_timer_add(&buffer.timer,frame.header.duration);
mad_synth_frame(&synth,&frame);
-// char *cache = &buffer.cache[(buffer.cachefill + buffer.cachepos) & CACHE_MASK];
int cachepos = (buffer.cachefill + buffer.cachepos) & CACHE_MASK;
- int i;
- for(i=0;i<synth.pcm.length;i++)
+ int len = synth.pcm.length;
+ if (buffer.currentsample + len >= buffer.totalsamples) {
+ len = buffer.totalsamples - buffer.currentsample;
+ }
+ int i = min (synth.pcm.length, buffer.skipsamples);
+ buffer.skipsamples -= i;
+ buffer.currentsample += len-i;
+ for(;i<len;i++)
{
if (buffer.cachefill >= CACHE_SIZE) {
printf ("cache overflow!\n");
break;
}
- if (buffer.cachefill >= CACHE_SIZE - sizeof (mad_fixed_t)) {
+// if (buffer.cachefill >= CACHE_SIZE - sizeof (mad_fixed_t)) {
// printf ("readsize=%d, pcm.length=%d(%d)\n", buffer.readsize, synth.pcm.length, i);
- }
+// }
assert (buffer.cachefill < CACHE_SIZE - sizeof (mad_fixed_t));
memcpy (buffer.cache+cachepos, &synth.pcm.samples[0][i], sizeof (mad_fixed_t));
cachepos = (cachepos + sizeof (mad_fixed_t)) & CACHE_MASK;
@@ -596,7 +661,7 @@ cmp3_decode (void) {
buffer.readsize -= sizeof (mad_fixed_t);
if (MAD_NCHANNELS(&frame.header) == 2) {
if (buffer.cachefill >= CACHE_SIZE - sizeof (mad_fixed_t)) {
- printf ("readsize=%d, pcm.length=%d(%d), cachefill=%d, cachepos=%d(%d)\n", buffer.readsize, synth.pcm.length, i, buffer.cachefill, buffer.cachepos, cachepos);
+ trace ("readsize=%d, pcm.length=%d(%d), cachefill=%d, cachepos=%d(%d)\n", buffer.readsize, synth.pcm.length, i, buffer.cachefill, buffer.cachepos, cachepos);
}
assert (buffer.cachefill < CACHE_SIZE - sizeof (mad_fixed_t));
memcpy (buffer.cache+cachepos, &synth.pcm.samples[1][i], sizeof (mad_fixed_t));
@@ -605,8 +670,10 @@ cmp3_decode (void) {
buffer.readsize -= sizeof (mad_fixed_t);
}
}
- //printf ("readsize at end of frame: %d\n", buffer.readsize);
- if (buffer.readsize <= 0 || eof) {
+ if (buffer.currentsample > buffer.totalsamples) {
+ trace ("mpgmad: warning: extra samples were read after end of stream\n");
+ }
+ if (buffer.readsize <= 0 || eof || buffer.currentsample >= buffer.totalsamples) {
break;
}
}
@@ -636,7 +703,7 @@ cmp3_read (char *bytes, int size) {
if (buffer.cachefill < size) {
buffer.readsize = (size - buffer.cachefill);
cmp3_decode ();
- plugin.info.readpos = (float)buffer.timer.seconds + (float)buffer.timer.fraction / MAD_TIMER_RESOLUTION;
+ plugin.info.readpos = (float)buffer.currentsample / buffer.samplerate - buffer.timestart;
}
if (buffer.cachefill > 0) {
int sz = min (size, buffer.cachefill);
@@ -684,7 +751,7 @@ cmp3_read_float32 (char *bytes, int size) {
buffer.readsize = (size - buffer.cachefill);
//printf ("decoding %d bytes using read_float32\n", buffer.readsize);
cmp3_decode ();
- plugin.info.readpos = (float)buffer.timer.seconds + (float)buffer.timer.fraction / MAD_TIMER_RESOLUTION;
+ plugin.info.readpos = (float)buffer.currentsample / buffer.samplerate - buffer.timestart;
}
if (buffer.cachefill > 0) {
int sz = min (size, buffer.cachefill);
@@ -720,177 +787,51 @@ cmp3_read_float32 (char *bytes, int size) {
}
static int
-cmp3_seek (float time) {
- time += buffer.timestart;
+cmp3_seek_sample (int sample) {
if (!buffer.file) {
return -1;
}
+ sample += buffer.startsample + buffer.startdelay;
+ if (sample >= buffer.totalsamples) {
+ return -1; // eof
+ }
// restart file, and load until we hit required pos
+ fseek (buffer.file, 0, SEEK_SET);
+ int skip = deadbeef->junk_get_leading_size (buffer.file);
+ if (skip > 0) {
+ fseek(buffer.file, skip, SEEK_SET);
+ }
mad_synth_finish (&synth);
mad_frame_finish (&frame);
mad_stream_finish (&stream);
- fseek(buffer.file, buffer.startoffset, SEEK_SET);
- mad_stream_init(&stream);
- mad_frame_init(&frame);
- mad_synth_init(&synth);
- mad_timer_reset(&buffer.timer);
+ buffer.remaining = 0;
+ buffer.readsize = 0;
+ buffer.cachefill = 0;
+ buffer.cachepos = 0;
- if (time == 0) {
+ if (sample == 0) {
plugin.info.readpos = 0;
+ buffer.currentsample = 0;
+ buffer.skipsamples = buffer.startdelay;
return 0;
}
- if (cmp3_scan_stream (&buffer, time) == -1) {
+ if (cmp3_scan_stream (&buffer, sample) == -1) {
plugin.info.readpos = 0;
return -1;
}
- // fixup timer
- plugin.info.readpos = (float)buffer.timer.seconds + (float)buffer.timer.fraction / MAD_TIMER_RESOLUTION;
- plugin.info.readpos -= buffer.timestart;
- buffer.timer.seconds = (int)plugin.info.readpos;
- buffer.timer.fraction = (plugin.info.readpos - buffer.timer.seconds) * MAD_TIMER_RESOLUTION;
+ mad_stream_init(&stream);
+ mad_frame_init(&frame);
+ mad_synth_init(&synth);
+ plugin.info.readpos = (float)buffer.currentsample / buffer.samplerate - buffer.timestart;
return 0;
}
-// {{{ separate xing/lame header reader (unused)
-#if 0
-// read xing/lame header
-// based on Xing example code
-#define FRAMES_FLAG 0x0001
-#define BYTES_FLAG 0x0002
-#define TOC_FLAG 0x0004
-#define VBR_SCALE_FLAG 0x0008
-
-#define FRAMES_AND_BYTES (FRAMES_FLAG | BYTES_FLAG)
-int
-cmp3_read_info_tag (buffer_t *buffer, DB_playItem_t *it, FILE *fp) {
- rewind (fp);
- int h_id, h_mode, h_sr_index, h_ly_index;
- static int sr_table[4] = { 44100, 48000, 32000, -1 };
- uint8_t header[1024];
- if (fread (header, 1, 1024, fp) != 1024) {
- return -1;
- }
- uint8_t *buf = header;
-
- // check sync
- if (buf[0] != 0xff || (buf[1]&(3<<5)) != (3<<5)) {
- printf ("sync bits not set, not a mpeg frame\n");
- return -1;
- }
-
- // get selected MPEG header data
- h_id = (buf[1] >> 3) & 1;
- h_sr_index = (buf[2] >> 2) & 3;
- h_mode = (buf[3] >> 6) & 3;
- h_ly_index = (buf[1] >> 1) & 3;
-
- static int ltbl[] = { -1, 3, 2, 1 };
- int layer = ltbl[h_ly_index];
- printf ("layer%d\n", layer);
- if (layer < 0) {
- printf ("invalid layer\n");
- return -1;
- }
-
- // determine offset of header
- if (h_id) { // mpeg1
- if (h_mode != 3) {
- buf += (32+4);
- }
- else {
- buf += (17+4);
- }
- }
- else { // mpeg2
- if (h_mode != 3) {
- buf += (17+4);
- }
- else {
- buf += (9+4);
- }
- }
-
- int is_vbr = 0;
- if (!strncmp (buf, "Xing", 4)) {
- is_vbr = 1;
- }
- else if (!strncmp (buf, "Info", 4)) {
- is_vbr = 0;
- }
- else {
- return -1; // no xing header found
- }
-
- buf+=4;
-
- if (h_id) {
- buffer->version = 1;
- }
- else {
- buffer->version = 2;
- }
-
- buffer->samplerate = sr_table[h_sr_index];
- if (!h_id) {
- buffer->samplerate >>= 1;
- }
-
- uint32_t flags, frames, bytes, vbr_scale;
- // get flags
- flags = extract_i32 (buf);
- buf+=4;
-
- if (flags & FRAMES_FLAG) {
- frames = extract_i32(buf);
- buf+=4;
- }
- if (flags & BYTES_FLAG) {
- bytes = extract_i32(buf);
- buf+=4;
- }
-
- if (flags & TOC_FLAG) {
- // read toc
- buf+=100;
- }
-
- vbr_scale = -1;
- if (flags & VBR_SCALE_FLAG) {
- vbr_scale = extract_i32(buf);
- buf+=4;
- }
-
- // check for lame header
- if( buf[0] != 'L' ) return 0;
- if( buf[1] != 'A' ) return 0;
- if( buf[2] != 'M' ) return 0;
- if( buf[3] != 'E' ) return 0;
- printf ("found LAME header\n");
- buf += 20;
- buf += 140;
-
- uint8_t vbrmethod = header[0xa5];
- if (vbrmethod & 0xf0) {
- buffer->lame_revision = 1;
- }
- else {
- buffer->lame_revision = 0;
- }
- buffer->vbrmethod = vbrmethod & 0x0f;
-
- buffer->peak_signal_amp = extract_f32 (&header[0xa7]);
- buffer->radio_replay_gain = extract_i16 (&header[0xab]);
- buffer->audiophile_replay_gain = extract_i16 (&header[0xad]);
- buffer->vbrbitrate = header[0xb0];
- buffer->delay_start = (((uint16_t)header[0xb1]) << 4) | (((uint16_t)header[0xb2])>>4);
- buffer->delay_end = ((((uint16_t)header[0xb2]) & 0x0f) << 8) | ((uint16_t)header[0xb3]);
- buffer->mp3gain = header[0xb5];
- buffer->musiclength = extract_i32 (&header[0xb8]);
- return 0;
+static int
+cmp3_seek (float time) {
+ int sample = time * buffer.samplerate;
+ return cmp3_seek_sample (sample);
}
-#endif
-// }}}
static const char *filetypes[] = {
"MPEG 1.0 layer I", "MPEG 1.0 layer II", "MPEG 1.0 layer III", "MPEG 2.0 layer I", "MPEG 2.0 layer II", "MPEG 2.0 layer III", "MPEG 2.5 layer I", "MPEG 2.5 layer II", "MPEG 2.5 layer III", NULL
@@ -952,7 +893,8 @@ cmp3_insert (DB_playItem_t *after, const char *fname) {
break;
}
}
- DB_playItem_t *cue_after = deadbeef->pl_insert_cue (after, fname, &plugin, ftype, buffer.duration);
+ // FIXME! bad numsamples passed to cue
+ DB_playItem_t *cue_after = deadbeef->pl_insert_cue (after, fname, &plugin, ftype, buffer.duration*buffer.samplerate, buffer.samplerate);
if (cue_after) {
fclose (fp);
return cue_after;
@@ -974,7 +916,6 @@ cmp3_insert (DB_playItem_t *after, const char *fname) {
}
int apeerr = deadbeef->junk_read_ape (it, fp);
deadbeef->pl_add_meta (it, "title", NULL);
- it->startoffset = buffer.startoffset;
it->duration = buffer.duration;
it->filetype = ftype;
@@ -1000,10 +941,10 @@ static DB_decoder_t plugin = {
.plugin.website = "http://deadbeef.sf.net",
.init = cmp3_init,
.free = cmp3_free,
-// .read = cmp3_read,
.read_int16 = cmp3_read,
.read_float32 = cmp3_read_float32,
.seek = cmp3_seek,
+ .seek_sample = cmp3_seek_sample,
.insert = cmp3_insert,
.exts = exts,
.id = "stdmpg",
diff --git a/plugins/vorbis/vorbis.c b/plugins/vorbis/vorbis.c
index 6daba52a..cb271e69 100644
--- a/plugins/vorbis/vorbis.c
+++ b/plugins/vorbis/vorbis.c
@@ -34,14 +34,13 @@ static vorbis_info *vi;
static int cur_bit_stream;
static float timestart;
static float timeend;
+static int startsample;
+static int endsample;
static void
cvorbis_free (void);
static int
-cvorbis_seek (float time);
-
-static int
cvorbis_init (DB_playItem_t *it) {
file = NULL;
vi = NULL;
@@ -67,11 +66,15 @@ cvorbis_init (DB_playItem_t *it) {
if (it->timeend > 0) {
timestart = it->timestart;
timeend = it->timeend;
- cvorbis_seek (0);
+ startsample = it->startsample;
+ endsample = it->endsample;
+ plugin.seek_sample (0);
}
else {
timestart = 0;
timeend = it->duration;
+ startsample = 0;
+ endsample = ov_pcm_total (&vorbis_file, -1)-1;
}
return 0;
}
@@ -138,6 +141,23 @@ cvorbis_seek (float time) {
return 0;
}
+static int
+cvorbis_seek_sample (int sample) {
+ if (!file) {
+ return -1;
+ }
+ sample += startsample;
+ int res = ov_pcm_seek (&vorbis_file, sample);
+ if (res != 0 && res != OV_ENOSEEK)
+ return -1;
+ int tell = ov_pcm_tell (&vorbis_file);
+ if (tell != sample) {
+ fprintf (stderr, "oggvorbis: failed to do sample-accurate seek (%d->%d)\n", sample, tell);
+ }
+ plugin.info.readpos = ov_time_tell(&vorbis_file) - timestart;
+ return 0;
+}
+
static DB_playItem_t *
cvorbis_insert (DB_playItem_t *after, const char *fname) {
// check for validity
@@ -153,7 +173,8 @@ cvorbis_insert (DB_playItem_t *after, const char *fname) {
return NULL;
}
float duration = ov_time_total (&vorbis_file, -1);
- DB_playItem_t *cue_after = deadbeef->pl_insert_cue (after, fname, &plugin, "OggVorbis", duration);
+ int totalsamples = ov_pcm_total (&vorbis_file, -1);
+ DB_playItem_t *cue_after = deadbeef->pl_insert_cue (after, fname, &plugin, "OggVorbis", totalsamples, vi->rate);
if (cue_after) {
ov_clear (&vorbis_file);
return cue_after;
@@ -213,6 +234,7 @@ static DB_decoder_t plugin = {
// vorbisfile can't output float32
// .read_float32 = cvorbis_read_float32,
.seek = cvorbis_seek,
+ .seek_sample = cvorbis_seek_sample,
.insert = cvorbis_insert,
.exts = exts,
.id = "stdogg",