diff --git a/apps/buffering.c b/apps/buffering.c index 9a52d60b3f..ed723d23aa 100644 --- a/apps/buffering.c +++ b/apps/buffering.c @@ -1440,6 +1440,62 @@ ssize_t bufgetdata(int handle_id, size_t size, void **data) return size; } +ssize_t bufgettail(int handle_id, size_t size, void **data) +{ + if (thread_self() != buffering_thread_id) + return ERR_WRONG_THREAD; /* only from buffering thread */ + + /* We don't support tail requests of > guardbuf_size, for simplicity */ + if (size > GUARD_BUFSIZE) + return ERR_INVALID_VALUE; + + const struct memory_handle *h = find_handle(handle_id); + if (!h) + return ERR_HANDLE_NOT_FOUND; + + if (h->end >= h->filesize) { + size_t tidx = ringbuf_sub_empty(h->widx, size); + + if (tidx + size > buffer_len) { + size_t copy_n = tidx + size - buffer_len; + memcpy(guard_buffer, ringbuf_ptr(0), copy_n); + } + + *data = ringbuf_ptr(tidx); + } + else { + size = ERR_HANDLE_NOT_DONE; + } + + return size; +} + +ssize_t bufcuttail(int handle_id, size_t size) +{ + if (thread_self() != buffering_thread_id) + return ERR_WRONG_THREAD; /* only from buffering thread */ + + struct memory_handle *h = find_handle(handle_id); + if (!h) + return ERR_HANDLE_NOT_FOUND; + + if (h->end >= h->filesize) { + /* Cannot trim to before read position */ + size_t available = h->end - MAX(h->start, h->pos); + if (available < size) + size = available; + + h->widx = ringbuf_sub_empty(h->widx, size); + h->filesize -= size; + h->end -= size; + } else { + size = ERR_HANDLE_NOT_DONE; + } + + return size; +} + + /* SECONDARY EXPORTED FUNCTIONS ============================ diff --git a/apps/buffering.h b/apps/buffering.h index bc47d6b1a1..1a75d865ae 100644 --- a/apps/buffering.h +++ b/apps/buffering.h @@ -46,7 +46,8 @@ enum data_type { #define ERR_FILE_ERROR -4 #define ERR_HANDLE_NOT_DONE -5 #define ERR_UNSUPPORTED_TYPE -6 -#define ERR_BITMAP_TOO_LARGE -7 +#define ERR_WRONG_THREAD -7 +#define ERR_BITMAP_TOO_LARGE -8 /* Initialise the buffering subsystem */ void buffering_init(void) INIT_ATTR; @@ -67,6 +68,8 @@ bool buffering_reset(char *buf, size_t buflen); * bufftell : Return the handle's file read position * bufread : Copy data from a handle to a buffer * bufgetdata: Obtain a pointer for linear access to a "size" amount of data + * bufgettail: Out-of-band get the last size bytes of a handle. + * bufcuttail: Out-of-band remove the trailing 'size' bytes of a handle. * * NOTE: bufread and bufgetdata will block the caller until the requested * amount of data is ready (unless EOF is reached). @@ -82,6 +85,8 @@ int bufadvance(int handle_id, off_t offset); off_t bufftell(int handle_id); ssize_t bufread(int handle_id, size_t size, void *dest); ssize_t bufgetdata(int handle_id, size_t size, void **data); +ssize_t bufgettail(int handle_id, size_t size, void **data); +ssize_t bufcuttail(int handle_id, size_t size); /*************************************************************************** * SECONDARY FUNCTIONS diff --git a/apps/playback.c b/apps/playback.c index 381c474415..03d7340b1b 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -1255,7 +1255,7 @@ void allocate_playback_log(void) playback_log_handle = core_alloc(PLAYBACK_LOG_BUFSZ); if (playback_log_handle > 0) { - DEBUGF("%s Allocated %d bytes\n", __func__, PLAYBACK_LOG_BUFSZ); + DEBUGF("%s Allocated %d bytes\n", __func__, PLAYBACK_LOG_BUFSZ); char *buf = core_get_data(playback_log_handle); buf[0] = '\0'; } @@ -3748,6 +3748,11 @@ static void buffer_event_finished_callback(unsigned short id, void *ev_data) break; case TYPE_PACKET_AUDIO: + /* Strip any useless trailing tags that are left. + Note this is needed to prevent playback noise at the + end of MP3 files with ID3v1 or APEv2 tags! */ + strip_tags(hid); + /* Fall-through */ case TYPE_ATOMIC_AUDIO: LOGFQUEUE("buffering > audio Q_AUDIO_HANDLE_FINISHED: %d", hid); audio_queue_post(Q_AUDIO_HANDLE_FINISHED, hid); diff --git a/lib/rbcodec/metadata/metadata.c b/lib/rbcodec/metadata/metadata.c index 8cae7156b7..3eca2ca062 100644 --- a/lib/rbcodec/metadata/metadata.c +++ b/lib/rbcodec/metadata/metadata.c @@ -499,6 +499,44 @@ bool get_metadata(struct mp3entry* id3, int fd, const char* trackname) return get_metadata_ex(id3, fd, trackname, 0); } +#ifndef __PCTOOL__ +void strip_tags(int handle_id) +{ + static const unsigned char tag[] = "TAG"; + static const unsigned char apetag[] = "APETAGEX"; + size_t len, version; + void *tail; + + if (bufgettail(handle_id, 128, &tail) != 128) + return; + + if (memcmp(tail, tag, 3) == 0) + { + /* Skip id3v1 tag */ + logf("Cutting off ID3v1 tag"); + bufcuttail(handle_id, 128); + } + + /* Get a new tail, as the old one may have been cut */ + if (bufgettail(handle_id, 32, &tail) != 32) + return; + + /* Check for APE tag (look for the APE tag footer) */ + if (memcmp(tail, apetag, 8) != 0) + return; + + /* Read the version and length from the footer */ + version = get_long_le(&((unsigned char *)tail)[8]); + len = get_long_le(&((unsigned char *)tail)[12]); + if (version == 2000) + len += 32; /* APEv2 has a 32 byte header */ + + /* Skip APE tag */ + logf("Cutting off APE tag (%ldB)", len); + bufcuttail(handle_id, len); +} +#endif /* ! __PCTOOL__ */ + #define MOVE_ENTRY(x) if (x) x += offset; void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig) diff --git a/lib/rbcodec/metadata/metadata.h b/lib/rbcodec/metadata/metadata.h index a48afee004..505154de1c 100644 --- a/lib/rbcodec/metadata/metadata.h +++ b/lib/rbcodec/metadata/metadata.h @@ -338,6 +338,7 @@ void wipe_mp3entry(struct mp3entry *id3); void fill_metadata_from_path(struct mp3entry *id3, const char *trackname); int get_audio_base_codec_type(int type); const char * get_codec_string(int type); +void strip_tags(int handle_id); bool rbcodec_format_is_atomic(int afmt); bool format_buffers_with_offset(int afmt);