Index: apps/codecs.c =================================================================== --- apps/codecs.c (revision 22379) +++ apps/codecs.c (working copy) @@ -170,6 +170,8 @@ /* new stuff at the end, sort into place next time the API gets incompatible */ + + NULL, /* read_secbuf */ }; void codec_get_full_path(char *path, const char *codec_root_fn) Index: apps/codecs.h =================================================================== --- apps/codecs.h (revision 22379) +++ apps/codecs.h (working copy) @@ -235,6 +235,8 @@ /* new stuff at the end, sort into place next time the API gets incompatible */ + + size_t (*read_secbuf)(void *ptr, size_t size); }; /* codec header */ Index: apps/metadata/wavpack.c =================================================================== --- apps/metadata/wavpack.c (revision 22379) +++ apps/metadata/wavpack.c (working copy) @@ -29,6 +29,7 @@ #include "metadata_common.h" #include "metadata_parsers.h" #include "logf.h" +#include "buffering.h" #define ID_UNIQUE 0x3f #define ID_LARGE 0x80 @@ -52,8 +53,7 @@ bool get_wavpack_metadata(int fd, struct mp3entry* id3) { - /* Use the trackname part of the id3 structure as a temporary buffer */ - unsigned char* buf = (unsigned char *)id3->path; + unsigned char buf[MAX_PATH]; /* Use this as a temp buffer */ uint32_t totalsamples, blocksamples, flags; int i; @@ -136,6 +136,11 @@ id3->length = ((int64_t) totalsamples * 1000) / id3->frequency; id3->bitrate = filesize (fd) / (id3->length / 8); + /* Open correction file if we can */ + if (flags & HYBRID_FLAG) { + id3->has_secondary = true; + } + return true; } Index: apps/metadata.c =================================================================== --- apps/metadata.c (revision 22379) +++ apps/metadata.c (working copy) @@ -286,6 +286,7 @@ break; case AFMT_WAVPACK: + snprintf(id3->path, sizeof(id3->path), "%s", trackname); if (!get_wavpack_metadata(fd, id3)) { return false; Index: apps/metadata.h =================================================================== --- apps/metadata.h (revision 22379) +++ apps/metadata.h (working copy) @@ -246,6 +246,9 @@ /* Musicbrainz Track ID */ char* mb_track_id; + + /* Secondary file (for Wavpack hybrid) */ + bool has_secondary; }; unsigned int probe_file_format(const char *filename); Index: apps/playback.c =================================================================== --- apps/playback.c (revision 22379) +++ apps/playback.c (working copy) @@ -215,6 +215,7 @@ /* Track info structure about songs in the file buffer (A/C-) */ struct track_info { int audio_hid; /* The ID for the track's buffer handle */ + int secondary_hid; /* The ID for the track's secondary buffer handle */ int id3_hid; /* The ID for the track's metadata handle */ int codec_hid; /* The ID for the track's codec handle */ #ifdef HAVE_ALBUMART @@ -377,6 +378,13 @@ else return false; } + + if (track->secondary_hid >= 0) { + if (bufclose(track->secondary_hid)) + track->secondary_hid = -1; + else + return false; + } #ifdef HAVE_ALBUMART if (track->aa_hid >= 0) { @@ -1052,7 +1060,7 @@ if (ci.stop_codec || !playing) return 0; - + copy_n = bufread(CUR_TI->audio_hid, size, ptr); /* Nothing requested OR nothing left */ @@ -1066,6 +1074,26 @@ return copy_n; } /* codec_filebuf_callback */ +static size_t codec_secbuf_callback(void *ptr, size_t size) +{ + ssize_t copy_n; + + if (ci.stop_codec || !playing) + return 0; + + /* printf("secbuf %p %u\n", ptr, size); */ + + copy_n = bufread(CUR_TI->secondary_hid, size, ptr); + + /* Nothing requested OR nothing left */ + if (copy_n == 0) + return 0; + + bufadvance(CUR_TI->secondary_hid, copy_n); + + return copy_n; +} + static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize) { size_t copy_n = reqsize; @@ -1966,7 +1994,14 @@ else file_offset = 0; - tracks[track_widx].audio_hid = bufopen(track_id3->path, file_offset, type); + if (track_id3->has_secondary) { + char secpath[MAX_PATH]; + snprintf(secpath, MAX_PATH, "%sc", track_id3->path); + tracks[track_widx].audio_hid = bufopenchunky(track_id3->path, file_offset, type); + tracks[track_widx].secondary_hid = bufopenchunky(secpath, file_offset, type); + } else { + tracks[track_widx].audio_hid = bufopen(track_id3->path, file_offset, type); + } /* No space left, not an error */ if (tracks[track_widx].audio_hid == ERR_BUFFER_FULL) @@ -2628,6 +2663,7 @@ /* Initialize codec api. */ ci.read_filebuf = codec_filebuf_callback; + ci.read_secbuf = codec_secbuf_callback; ci.pcmbuf_insert = codec_pcmbuf_insert_callback; ci.codec_get_buffer = codec_get_buffer; ci.request_buffer = codec_request_buffer_callback; Index: apps/buffering.c =================================================================== --- apps/buffering.c (revision 22379) +++ apps/buffering.c (working copy) @@ -102,6 +102,8 @@ /* Bytes available in the buffer */ #define BUF_USED RINGBUF_SUB(buf_widx, buf_ridx) +#define BUFPOS(p) ((unsigned)((void*)p-(void*)buffer)) + /* assert(sizeof(struct memory_handle)%4==0) */ struct memory_handle { int id; /* A unique ID for the handle */ @@ -112,10 +114,14 @@ volatile size_t ridx; /* Read pointer, relative to the main buffer */ size_t widx; /* Write pointer */ size_t filesize; /* File total length */ - size_t filerem; /* Remaining bytes of file NOT in buffer */ + volatile size_t filerem; /* Remaining bytes of file NOT in buffer */ volatile size_t available; /* Available bytes to read from buffer */ size_t offset; /* Offset at which we started reading the file */ struct memory_handle *next; + int chunky; + int finished; + struct memory_handle *file_next; + volatile int invalid; }; /* invariant: filesize == offset + available + filerem */ @@ -172,6 +178,7 @@ fill at its earliest convenience */ Q_HANDLE_ADDED, /* Inform the buffering thread that a handle was added, (which means the disk is spinning) */ + Q_CLEAN_HANDLE, }; /* Buffering thread */ @@ -216,38 +223,48 @@ only potential side effect is to allocate space for the cur_handle if it returns NULL. */ -static struct memory_handle *add_handle(size_t data_size, bool can_wrap, - bool alloc_all) + +static bool finish_handle(struct memory_handle *h, bool chunky); +static bool finish_chunky_handles(); + +static struct memory_handle *add_handle_nolock(size_t data_size, bool can_wrap, + bool alloc_all, bool chunky, bool finish) { /* gives each handle a unique id */ - static int cur_handle_id = 0; size_t shift; size_t new_widx; size_t len; int overlap; - + static int cur_handle_id = 0; + if (num_handles >= BUF_MAX_HANDLES) return NULL; - - mutex_lock(&llist_mutex); - mutex_lock(&llist_mod_mutex); - - if (cur_handle && cur_handle->filerem > 0) { - /* the current handle hasn't finished buffering. We can only add - a new one if there is already enough free space to finish - the buffering. */ - size_t req = cur_handle->filerem + sizeof(struct memory_handle); - if (RINGBUF_ADD_CROSS(cur_handle->widx, req, buf_ridx) >= 0) { - /* Not enough space */ - mutex_unlock(&llist_mod_mutex); - mutex_unlock(&llist_mutex); - return NULL; - } else { - /* Allocate the remainder of the space for the current handle */ - buf_widx = RINGBUF_ADD(cur_handle->widx, cur_handle->filerem); + + if (cur_handle) { + if (!chunky && cur_handle->chunky) { + /* Uh-oh, we have to finish ALL chunky handles in the buffer */ + if (finish && !finish_chunky_handles()) { + /* Some handle didn't have enough space */ + return NULL; + } else if (!finish_handle(cur_handle, false)) { + return NULL; + } + } else if (cur_handle->filerem > 0) { + /* the current handle hasn't finished buffering. We can only add + a new one if there is already enough free space to finish + the buffering. */ + bool finishchunky = false; + if (chunky && cur_handle->chunky) { + finishchunky = true; + } + if (!finish_handle(cur_handle, finishchunky)) { + /* Not enough space */ + return NULL; + } } } + /* align to 4 bytes up */ new_widx = RINGBUF_ADD(buf_widx, 3) & ~3; @@ -268,8 +285,6 @@ overlap = RINGBUF_ADD_CROSS(buf_widx, shift + len, buf_ridx); if (overlap >= 0 && (alloc_all || (unsigned)overlap > data_size)) { /* Not enough space for required allocations */ - mutex_unlock(&llist_mod_mutex); - mutex_unlock(&llist_mutex); return NULL; } @@ -279,6 +294,8 @@ struct memory_handle *new_handle = (struct memory_handle *)(&buffer[buf_widx]); + + /*printf("Handle %u added at %u with size %u\n", cur_handle_id, buf_widx, data_size);*/ /* only advance the buffer write index of the size of the struct */ buf_widx = RINGBUF_ADD(buf_widx, sizeof(struct memory_handle)); @@ -288,6 +305,10 @@ cur_handle_id = (cur_handle_id + 1) & BUF_HANDLE_MASK; new_handle->next = NULL; num_handles++; + new_handle->chunky = chunky; + new_handle->finished = false; + new_handle->file_next = NULL; + new_handle->invalid = false; if (!first_handle) /* the new handle is the first one */ @@ -298,9 +319,22 @@ cur_handle = new_handle; + return new_handle; +} + +static struct memory_handle *add_handle(size_t data_size, bool can_wrap, + bool alloc_all, bool chunky) +{ + + mutex_lock(&llist_mutex); + mutex_lock(&llist_mod_mutex); + + struct memory_handle *ret = add_handle_nolock(data_size, can_wrap, alloc_all, chunky, false); + mutex_unlock(&llist_mod_mutex); mutex_unlock(&llist_mutex); - return new_handle; + + return ret; } /* Delete a given memory handle from the linked list @@ -310,6 +344,8 @@ if (h == NULL) return true; + /* printf("Removing handle %u at %u\n", h->id, BUFPOS(h)); */ + mutex_lock(&llist_mutex); mutex_lock(&llist_mod_mutex); @@ -353,6 +389,89 @@ return true; } +static struct memory_handle *find_handle_invalid(int handle_id) +{ + if (handle_id < 0) + return NULL; + + mutex_lock(&llist_mutex); + + struct memory_handle *m = first_handle; + while (m && (m->id != handle_id || !m->invalid)) { + m = m->next; + } + + mutex_unlock(&llist_mutex); + return m; +} + +static bool clean_handle(int handle_id) { + struct memory_handle *h; + /* printf("Cleaning handle %u\n", handle_id); */ + h = find_handle_invalid(handle_id); + while (h) { + if (!rm_handle(h)) + return false; + h = find_handle_invalid(handle_id); + } + return true; +} + +static bool finish_handle(struct memory_handle *h, bool chunky) { + if (h->finished) { + /* printf("Handle %u already finished\n", h->id); */ + return true; + } + + size_t req = h->filerem + sizeof(struct memory_handle); + if (chunky) { + req = MIN(req, 2*BUFFERING_DEFAULT_FILECHUNK+sizeof(struct memory_handle)); + } + + /*printf("Finishing%s %u of handle %u\n", chunky?" chunky":"", req, h->id);*/ + + if (RINGBUF_ADD_CROSS(h->widx, req, buf_ridx) >= 0) { + /*printf("Insufficient space to finish handle!\n");*/ + return false; + } else { + struct memory_handle *mnext = h->next; + if (mnext == NULL) + mnext = first_handle; + if (mnext != NULL) { + if (RINGBUF_ADD_CROSS(h->widx, req, BUFPOS(mnext)) >= 0) { + /*printf("Finishing handle %u at %u crosses into next handle %u at %u!\n", h->id, BUFPOS(h), mnext->id, BUFPOS(mnext));*/ + return false; + } + } + /* Allocate the remainder of the space for the current handle */ + if (h == cur_handle) { + buf_widx = RINGBUF_ADD(cur_handle->widx, req-sizeof(struct memory_handle)); + } /*else { + printf("Finishing non-current handle!\n"); + }*/ + } + + if (!h->chunky) { + h->finished = true; + } + return true; +} + +static bool finish_chunky_handles() { + /* printf("Finishing all chunky handles!\n"); */ + struct memory_handle *cur = first_handle; + while (cur != NULL) { + if (cur->chunky) { + if (!finish_handle(cur, false)) { + return false; + } + } + cur = cur->next; + } + return true; +} + + /* Return a pointer to the memory handle of given ID. NULL if the handle wasn't found */ static struct memory_handle *find_handle(int handle_id) @@ -366,11 +485,19 @@ will either be the same as the last, or the one after the last */ if (cached_handle) { + while (cached_handle && cached_handle->invalid) { + if (cached_handle->file_next == NULL) { + cached_handle = NULL; + mutex_unlock(&llist_mutex); + return find_handle(handle_id); + } + cached_handle = cached_handle->file_next; + } if (cached_handle->id == handle_id) { mutex_unlock(&llist_mutex); return cached_handle; } else if (cached_handle->next && - (cached_handle->next->id == handle_id)) { + (cached_handle->next->id == handle_id) && !cached_handle->next->invalid) { cached_handle = cached_handle->next; mutex_unlock(&llist_mutex); return cached_handle; @@ -378,7 +505,7 @@ } struct memory_handle *m = first_handle; - while (m && m->id != handle_id) { + while (m && (m->id != handle_id || m->invalid)) { m = m->next; } /* This condition can only be reached with !m or m->id == handle_id */ @@ -557,6 +684,42 @@ return data_counters.useful < (conf_watermark / 2); } +static bool extend_handle(struct memory_handle *h, size_t rem) { + mutex_lock(&llist_mutex); + mutex_lock(&llist_mod_mutex); + if (h->next == NULL) { + /* printf("CANNOT EXTEND CURRENT HANDLE!\n"); */ + mutex_unlock(&llist_mod_mutex); + mutex_unlock(&llist_mutex); + return false; + } + /* printf("Extending handle %u at %u into new handle with size %ld\n", h->id, BUFPOS(h), rem); */ + struct memory_handle *nh = add_handle_nolock(rem, true, false, true, false); + if (nh == NULL) { + return false; + } + nh->data = buf_widx; + h->file_next = nh; + nh->id = h->id; + unsigned diff = RINGBUF_SUB(BUFPOS(h->next), h->data); + h->filerem = RINGBUF_SUB(BUFPOS(h->next), h->widx); + nh->filesize = h->filesize; + nh->offset = h->offset + diff; + nh->filerem = h->filesize-nh->offset; + nh->available = 0; + nh->widx = nh->data; + nh->ridx = nh->data; + nh->fd = -1; + strcpy(nh->path, h->path); + nh->type = h->type; + /* printf("Extension for %u is at %u with data %u, offset %u, filerem %u, path %s (original offset %u, diff %u, filerem %u, filled %u)\n", h->id, BUFPOS(nh), nh->data, nh->offset, nh->filerem, nh->path, h->offset, diff, h->filerem, RINGBUF_SUB(h->widx, h->data)); */ + + mutex_unlock(&llist_mod_mutex); + mutex_unlock(&llist_mutex); + + return true; +} + /* Buffer data for the given handle. Return whether or not the buffering should continue explicitly. */ static bool buffer_handle(int handle_id) @@ -606,7 +769,7 @@ return true; } - while (h->filerem > 0) + while (h->filerem > 0 || h->file_next != NULL) { /* max amount to copy */ size_t copy_n = MIN( MIN(h->filerem, BUFFERING_DEFAULT_FILECHUNK), @@ -616,26 +779,65 @@ if (RINGBUF_ADD_CROSS(h->widx, copy_n, buf_ridx) >= 0) return false; - /* This would read into the next handle, this is broken */ - if (h->next && RINGBUF_ADD_CROSS(h->widx, copy_n, - (unsigned)((void *)h->next - (void *)buffer)) > 0) { - /* Try to recover by truncating this file */ - copy_n = RINGBUF_ADD_CROSS(h->widx, copy_n, - (unsigned)((void *)h->next - (void *)buffer)); - h->filerem -= copy_n; - h->filesize -= copy_n; - logf("buf alloc short %ld", (long)copy_n); - if (h->filerem) - continue; - else - break; + struct memory_handle *nhan = h->next; + if (nhan == NULL) + nhan = first_handle; + + if (nhan != NULL) { + unsigned diff; + long overlap; + if (h->widx == BUFPOS(nhan)) { + overlap = copy_n; + diff = 0; + } else { + overlap = RINGBUF_ADD_CROSS(h->widx, copy_n, BUFPOS(nhan)); + if (BUFPOS(nhan) > h->widx) { + diff = BUFPOS(nhan)-h->widx; + } else { + diff = buffer_len-h->widx+BUFPOS(nhan); + } + } + if (overlap > 0 && diff == 0) { + if (h->chunky && h->filerem > 0) { + if (!extend_handle(h, overlap)) { + /* printf("extend_handle failed!\n"); */ + break; + } + } + if (h->file_next != NULL) { + /* printf("no diff, moving to file_next\n"); */ + h->file_next->fd = h->fd; + h->fd = -1; + h = h->file_next; + continue; + } + /* This would read into the next handle, this is broken */ + /* DEBUGF("SHORT BY %ld FOR READ INTO%s%s HANDLE %u!\n", overlap, h->chunky?" CHUNKY":"", h->finished?" FINISHED":"", h->id); */ + /* Try to recover by truncating this file */ + copy_n = diff; + h->filerem = copy_n; + h->filesize = h->available+h->filerem; + logf("buf alloc short %ld", overlap); + if (h->filerem) + continue; + else + break; + } else if (overlap > 0) { + copy_n = diff; + } } + /* rc is the actual amount read */ int rc = read(h->fd, &buffer[h->widx], copy_n); + + /*if (rc != 0) { + printf("Read %d/%u (of %u) into handle %u at %u [%u] (next %u at %u)\n", rc, copy_n, h->filerem, h->id, h->widx, BUFPOS(h), nhan!=NULL?nhan->id:9999, nhan!=NULL?((unsigned)((void*)nhan-(void*)buffer)):0); + }*/ if (rc < 0) { + /* printf("Error reading!\n"); */ /* Some kind of filesystem error, maybe recoverable if not codec */ if (h->type == TYPE_CODEC) { logf("Partial codec"); @@ -669,6 +871,13 @@ if (!queue_empty(&buffering_queue)) break; + + if (h->filerem == 0 && h->file_next != NULL) { + /* printf("Continuing from %u at %u to %u\n", h->id, BUFPOS(h), BUFPOS(h->file_next)); */ + h->file_next->fd = h->fd; + h->fd = -1; + h = h->file_next; + } } if (h->filerem == 0) { @@ -709,6 +918,8 @@ if (!h) return; + /* printf("Rebuffering %u at %u\n", handle_id, BUFPOS(h)); */ + /* When seeking foward off of the buffer, if it is a short seek don't rebuffer the whole track, just read enough to satisfy */ if (newpos > h->offset && newpos - h->offset < BUFFERING_DEFAULT_FILECHUNK) @@ -725,7 +936,8 @@ LOGFQUEUE("buffering >| Q_RESET_HANDLE %d", handle_id); queue_send(&buffering_queue, Q_RESET_HANDLE, handle_id); - size_t next = (unsigned)((void *)h->next - (void *)buffer); + mutex_lock(&llist_mutex); + size_t next = BUFPOS(h->next); if (RINGBUF_SUB(next, h->data) < h->filesize - newpos) { /* There isn't enough space to rebuffer all of the track from its new @@ -733,6 +945,7 @@ DEBUGF("rebuffer_handle: space is needed\n"); send_event(BUFFER_EVENT_REBUFFER, &handle_id); } + mutex_unlock(&llist_mutex); /* Now we ask for a rebuffer */ LOGFQUEUE("buffering >| Q_BUFFER_HANDLE %d", handle_id); @@ -753,7 +966,13 @@ } /* rm_handle returns true unless the handle somehow persists after exit */ - return rm_handle(h); + struct memory_handle *nh = h->file_next; + bool ret = rm_handle(h); + if (nh) { + if (!close_handle(handle_id)) + ret = false; + } + return ret; } /* Free buffer space by moving the handle struct right before the useful @@ -900,13 +1119,15 @@ return value: <0 if the file cannot be opened, or one file already queued to be opened, otherwise the handle for the file in the buffer */ -int bufopen(const char *file, size_t offset, enum data_type type) +static int bufopeninternal(const char *file, size_t offset, enum data_type type, bool chunky) { + /* printf("bufopen%s %s type %d offset %u\n", chunky?"chunky":"", file!=NULL?file:"NULL", type, offset); */ + if (type == TYPE_ID3) { /* ID3 case: allocate space, init the handle and return. */ - struct memory_handle *h = add_handle(sizeof(struct mp3entry), false, true); + struct memory_handle *h = add_handle(sizeof(struct mp3entry), false, true, false); if (!h) return ERR_BUFFER_FULL; @@ -944,7 +1165,7 @@ if (adjusted_offset > size) adjusted_offset = 0; - struct memory_handle *h = add_handle(size-adjusted_offset, can_wrap, false); + struct memory_handle *h = add_handle(size-adjusted_offset, can_wrap, false, chunky); if (!h) { DEBUGF("bufopen: failed to add handle\n"); @@ -1009,6 +1230,16 @@ return h->id; } +int bufopen(const char *file, size_t offset, enum data_type type) +{ + return bufopeninternal(file, offset, type, false); +} + +int bufopenchunky(const char *file, size_t offset, enum data_type type) +{ + return bufopeninternal(file, offset, type, true); +} + /* Open a new handle from data that needs to be copied from memory. src is the source buffer from which to copy data. It can be NULL to simply reserve buffer space. @@ -1018,7 +1249,7 @@ */ int bufalloc(const void *src, size_t size, enum data_type type) { - struct memory_handle *h = add_handle(size, false, true); + struct memory_handle *h = add_handle(size, false, true, false); if (!h) return ERR_BUFFER_FULL; @@ -1070,17 +1301,40 @@ struct memory_handle *h = find_handle(handle_id); if (!h) return ERR_HANDLE_NOT_FOUND; - + if (newpos > h->filesize) { /* access beyond the end of the file */ - return ERR_INVALID_VALUE; + /* printf("Beyond end of file (%u > %u)!\n", newpos, h->filesize); */ + if (!h->chunky) + return ERR_INVALID_VALUE; + return bufseek(handle_id, h->filesize); } - else if (newpos < h->offset || h->offset + h->available < newpos) { - /* access before or after buffered data. A rebuffer is needed. */ - rebuffer_handle(handle_id, newpos); + else if (newpos < h->offset) { + /* access before buffered data. A rebuffer is needed. */ + /* printf("Before buffered data...\n"); */ + rebuffer_handle(h->id, newpos); } + else if (h->offset + h->available < newpos) { + /* access after buffered data. A rebuffer or chunky chain is needed */ + if (!h->chunky) { + rebuffer_handle(h->id, newpos); + } else { + /* printf("Seeking beyond handle %u at %u - going to next\n", h->id, BUFPOS(h)); */ + h->invalid = true; + int ret = bufseek(h->id, newpos); + if (ret == ERR_HANDLE_NOT_FOUND) { + /* printf("There was no next; rebuffer!\n"); */ + rebuffer_handle(h->id, newpos); + } else { + LOGFQUEUE("buffering >| Q_CLEAN_HANDLE %d", h->id); + queue_send(&buffering_queue, Q_CLEAN_HANDLE, h->id); + } + return ret; + } + } else { h->ridx = RINGBUF_ADD(h->data, newpos - h->offset); + /*printf("Seeked %u at %u to %u (%u in the buffer)\n", handle_id, BUFPOS(h), newpos, BUFPOS(h->ridx));*/ } return 0; } @@ -1093,6 +1347,8 @@ if (!h) return ERR_HANDLE_NOT_FOUND; + /*printf("bufadvance %u %u at %u (offset %u, ridx %u, data %u)\n", handle_id, offset, BUFPOS(h), h->offset, h->ridx, h->data);*/ + size_t newpos = h->offset + RINGBUF_SUB(h->ridx, h->data) + offset; return bufseek(handle_id, newpos); } @@ -1109,12 +1365,38 @@ return NULL; size_t avail = RINGBUF_SUB(h->widx, h->ridx); + size_t fill = RINGBUF_SUB(h->widx, h->data); if (avail == 0 && h->filerem == 0) { - /* File is finished reading */ - *size = 0; - return h; + if (!h->chunky || h->offset+fill == h->filesize) { + /* File is finished reading */ + *size = 0; + return h; + } + /*printf("%u at %u (offset %u, fill %u, filesize %u) is exhausted, moving on\n", handle_id, BUFPOS(h), h->offset, fill, h->filesize);*/ + h->invalid = true; + buf_request_buffer_handle(handle_id); + LOGFQUEUE("buffering >| Q_CLEAN_HANDLE %d", h->id); + queue_send(&buffering_queue, Q_CLEAN_HANDLE, h->id); + /* Wait for the next handle to be ready */ + do + { + /* it is not safe for a non-buffering thread to sleep while + * holding a handle */ + h = find_handle(handle_id); + if (h) + break; + sleep(1); + } + while (true); + struct memory_handle *ret = prep_bufdata(handle_id, size, guardbuf_limit); + if (ret == NULL) { + /*printf("out of data!\n");*/ + *size = 0; + return NULL; + } + return ret; } if (*size == 0 || *size > avail + h->filerem) @@ -1148,6 +1430,7 @@ } *size = MIN(*size,avail); + /*printf("prep_bufdata returning %u for %u at %u\n", *size, h->id, BUFPOS(h));*/ return h; } @@ -1158,25 +1441,39 @@ ssize_t bufread(int handle_id, size_t size, void *dest) { const struct memory_handle *h; + size_t nread = 0; size_t adjusted_size = size; - h = prep_bufdata(handle_id, &adjusted_size, false); - if (!h) - return ERR_HANDLE_NOT_FOUND; + while (nread < size) { + adjusted_size = size-nread; + h = prep_bufdata(handle_id, &adjusted_size, false); + if (!h) { + if (nread) + return nread; + return ERR_HANDLE_NOT_FOUND; + } + /*printf("prep_bufdata gave %u/%u for %u at %u\n", adjusted_size, size-nread, handle_id, BUFPOS(h));*/ + if (adjusted_size == 0) { + break; + } + nread += adjusted_size; - if (h->ridx + adjusted_size > buffer_len) - { - /* the data wraps around the end of the buffer */ - size_t read = buffer_len - h->ridx; - memcpy(dest, &buffer[h->ridx], read); - memcpy(dest+read, buffer, adjusted_size - read); + if (h->ridx + adjusted_size > buffer_len) + { + /* the data wraps around the end of the buffer */ + size_t read = buffer_len - h->ridx; + memcpy(dest, &buffer[h->ridx], read); + memcpy(dest+read, buffer, adjusted_size - read); + /*printf("Reading %u from %u at %u and %u from wrapped\n", read, handle_id, h->ridx, adjusted_size-read);*/ + } + else + { + memcpy(dest, &buffer[h->ridx], adjusted_size); + /*printf("Reading %u from %u at %u [%u] (next %u at %u)\n", adjusted_size, handle_id, h->ridx, BUFPOS(h), h->next!=NULL?h->next->id:9999, h->next!=NULL?BUFPOS(h->next):0);*/ + } } - else - { - memcpy(dest, &buffer[h->ridx], adjusted_size); - } - return adjusted_size; + return nread; } /* Update the "data" pointer to make the handle's data available to the caller. @@ -1253,6 +1550,8 @@ if (!h) return ERR_HANDLE_NOT_FOUND; + /*printf("bufcuttail %u!\n", handle_id);*/ + if (h->filerem) return ERR_HANDLE_NOT_DONE; @@ -1384,6 +1683,11 @@ LOGFQUEUE("buffering < Q_CLOSE_HANDLE %d", (int)ev.data); queue_reply(&buffering_queue, close_handle((int)ev.data)); break; + + case Q_CLEAN_HANDLE: + LOGFQUEUE("buffering < Q_CLEAN_HANDLE %d", (int)ev.data); + queue_reply(&buffering_queue, clean_handle((int)ev.data)); + break; case Q_HANDLE_ADDED: LOGFQUEUE("buffering < Q_HANDLE_ADDED %d", (int)ev.data); Index: apps/buffering.h =================================================================== --- apps/buffering.h (revision 22379) +++ apps/buffering.h (working copy) @@ -58,6 +58,7 @@ * ======================== * * bufopen : Reserve space in the buffer for a given file + * bufopenchunky : Reserve SOME space in the buffer for a given file * bufalloc : Open a new handle from data that needs to be copied from memory * bufclose : Close an open handle * bufseek : Set handle reading index, relatively to the start of the file @@ -75,6 +76,7 @@ #define BUF_MAX_HANDLES 256 int bufopen(const char *file, size_t offset, enum data_type type); +int bufopenchunky(const char *file, size_t offset, enum data_type type); int bufalloc(const void *src, size_t size, enum data_type type); bool bufclose(int handle_id); int bufseek(int handle_id, size_t newpos); Index: apps/codecs/libwavpack/words.c =================================================================== --- apps/codecs/libwavpack/words.c (revision 22379) +++ apps/codecs/libwavpack/words.c (working copy) @@ -487,6 +487,182 @@ return (flags & MONO_DATA) ? csamples : (csamples / 2); } +// Read the next word from the bitstream "wvbits" and return the value. This +// function can be used for hybrid or lossless streams, but since an +// optimized version is available for lossless this function would normally +// be used for hybrid only. If a hybrid lossless stream is being read then +// the "correction" offset is written at the specified pointer. A return value +// of WORD_EOF indicates that the end of the bitstream was reached (all 1s) or +// some other error occurred. + +int32_t get_word (WavpackStream *wps, int chan, int32_t *correction) +{ + register struct entropy_data *c = wps->w.c + chan; + uint32_t ones_count, low, mid, high; + int next8, sign; + int32_t value; + + if (correction) + *correction = 0; + + if (!(wps->w.c [0].median [0] & ~1) && !wps->w.holding_zero && !wps->w.holding_one && !(wps->w.c [1].median [0] & ~1)) { + uint32_t mask; + int cbits; + + if (wps->w.zeros_acc) { + if (--wps->w.zeros_acc) { + c->slow_level -= (c->slow_level + SLO) >> SLS; + return 0; + } + } + else { + for (cbits = 0; cbits < 33 && getbit (&wps->wvbits); ++cbits); + + if (cbits == 33) + return WORD_EOF; + + if (cbits < 2) + wps->w.zeros_acc = cbits; + else { + for (mask = 1, wps->w.zeros_acc = 0; --cbits; mask <<= 1) + if (getbit (&wps->wvbits)) + wps->w.zeros_acc |= mask; + + wps->w.zeros_acc |= mask; + } + + if (wps->w.zeros_acc) { + c->slow_level -= (c->slow_level + SLO) >> SLS; + CLEAR (wps->w.c [0].median); + CLEAR (wps->w.c [1].median); + return 0; + } + } + } + + if (wps->w.holding_zero) + ones_count = wps->w.holding_zero = 0; + else { + if (wps->wvbits.bc < 8) { + if (++(wps->wvbits.ptr) == wps->wvbits.end) + wps->wvbits.wrap (&wps->wvbits); + + next8 = (wps->wvbits.sr |= *(wps->wvbits.ptr) << wps->wvbits.bc) & 0xff; + wps->wvbits.bc += sizeof (*(wps->wvbits.ptr)) * 8; + } + else + next8 = wps->wvbits.sr & 0xff; + + if (next8 == 0xff) { + wps->wvbits.bc -= 8; + wps->wvbits.sr >>= 8; + + for (ones_count = 8; ones_count < (LIMIT_ONES + 1) && getbit (&wps->wvbits); ++ones_count); + + if (ones_count == (LIMIT_ONES + 1)) + return WORD_EOF; + + if (ones_count == LIMIT_ONES) { + uint32_t mask; + int cbits; + + for (cbits = 0; cbits < 33 && getbit (&wps->wvbits); ++cbits); + + if (cbits == 33) + return WORD_EOF; + + if (cbits < 2) + ones_count = cbits; + else { + for (mask = 1, ones_count = 0; --cbits; mask <<= 1) + if (getbit (&wps->wvbits)) + ones_count |= mask; + + ones_count |= mask; + } + + ones_count += LIMIT_ONES; + } + } + else { + wps->wvbits.bc -= (ones_count = ones_count_table [next8]) + 1; + wps->wvbits.sr >>= ones_count + 1; + } + + if (wps->w.holding_one) { + wps->w.holding_one = ones_count & 1; + ones_count = (ones_count >> 1) + 1; + } + else { + wps->w.holding_one = ones_count & 1; + ones_count >>= 1; + } + + wps->w.holding_zero = ~wps->w.holding_one & 1; + } + + if ((wps->wphdr.flags & HYBRID_FLAG) && !chan) + update_error_limit (&wps->w, wps->wphdr.flags); + + if (ones_count == 0) { + low = 0; + high = GET_MED (0) - 1; + DEC_MED0 (); + } + else { + low = GET_MED (0); + INC_MED0 (); + + if (ones_count == 1) { + high = low + GET_MED (1) - 1; + DEC_MED1 (); + } + else { + low += GET_MED (1); + INC_MED1 (); + + if (ones_count == 2) { + high = low + GET_MED (2) - 1; + DEC_MED2 (); + } + else { + low += (ones_count - 2) * GET_MED (2); + high = low + GET_MED (2) - 1; + INC_MED2 (); + } + } + } + + low &= 0x7fffffff; + high &= 0x7fffffff; + mid = (high + low + 1) >> 1; + + if (!c->error_limit) + mid = read_code (&wps->wvbits, high - low) + low; + else while (high - low > c->error_limit) { + if (getbit (&wps->wvbits)) + mid = (high + (low = mid) + 1) >> 1; + else + mid = ((high = mid - 1) + low + 1) >> 1; + } + + sign = getbit (&wps->wvbits); + + if (bs_is_open (&wps->wvcbits) && c->error_limit) { + value = read_code (&wps->wvcbits, high - low) + low; + + if (correction) + *correction = sign ? (mid - value) : (value - mid); + } + + if (wps->wphdr.flags & HYBRID_BITRATE) { + c->slow_level -= (c->slow_level + SLO) >> SLS; + c->slow_level += mylog2 (mid); + } + + return sign ? ~mid : mid; +} + // Read a single unsigned value from the specified bitstream with a value // from 0 to maxcode. If there are exactly a power of two number of possible // codes then this will read a fixed number of bits; otherwise it reads the Index: apps/codecs/libwavpack/wavpack.h =================================================================== --- apps/codecs/libwavpack/wavpack.h (revision 22379) +++ apps/codecs/libwavpack/wavpack.h (working copy) @@ -197,7 +197,7 @@ typedef struct { WavpackHeader wphdr; - Bitstream wvbits; + Bitstream wvbits, wvcbits; struct words_data w; @@ -208,6 +208,13 @@ uchar float_flags, float_shift, float_max_exp, float_norm_exp; uchar *blockbuff, *blockend; + struct { + int32_t shaping_acc [2], shaping_delta [2], error [2]; + double noise_sum, noise_ave, noise_max; + short *shaping_data, *shaping_array; + int32_t shaping_samples; + } dc; + struct decorr_pass decorr_passes [MAX_NTERMS]; } WavpackStream; @@ -235,11 +242,12 @@ int wrapper_bytes; uchar read_buffer [1024]; + uchar read_buffer2 [1024]; char error_message [80]; - read_stream infile; + read_stream infile, infile2; uint32_t total_samples, crc_errors, first_flags; - int open_flags, norm_offset, reduced_channels, lossy_blocks; + int open_flags, norm_offset, reduced_channels, lossy_blocks, wvc_flag; } WavpackContext; @@ -357,10 +365,12 @@ // unpack.c int unpack_init (WavpackContext *wpc); -int init_wv_bitstream (WavpackContext *wpc, WavpackMetadata *wpmd); +int unpack_wvc_init (WavpackContext *wpc); +int init_wv_bitstream (WavpackContext *wpc, int wvc, WavpackMetadata *wpmd); int read_decorr_terms (WavpackStream *wps, WavpackMetadata *wpmd); int read_decorr_weights (WavpackStream *wps, WavpackMetadata *wpmd); int read_decorr_samples (WavpackStream *wps, WavpackMetadata *wpmd); +int read_shaping_info (WavpackStream *wps, WavpackMetadata *wpmd); int read_float_info (WavpackStream *wps, WavpackMetadata *wpmd); int read_int32_info (WavpackStream *wps, WavpackMetadata *wpmd); int read_channel_info (WavpackContext *wpc, WavpackMetadata *wpmd); @@ -378,7 +388,7 @@ // metadata.c stuff -int read_metadata_buff (WavpackContext *wpc, WavpackMetadata *wpmd); +int read_metadata_buff (WavpackContext *wpc, read_stream infile, WavpackMetadata *wpmd, int wvc); int process_metadata (WavpackContext *wpc, WavpackMetadata *wpmd); int copy_metadata (WavpackMetadata *wpmd, uchar *buffer_start, uchar *buffer_end); void free_metadata (WavpackMetadata *wpmd); @@ -391,6 +401,7 @@ int read_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd); int32_t get_words (int32_t *buffer, int nsamples, uint32_t flags, struct words_data *w, Bitstream *bs); +int32_t get_word (WavpackStream *wps, int chan, int32_t *correction); void send_word_lossless (int32_t value, int chan, struct words_data *w, Bitstream *bs); void send_words (int32_t *buffer, int nsamples, uint32_t flags, @@ -412,6 +423,7 @@ // wputils.c WavpackContext *WavpackOpenFileInput (read_stream infile, char *error); +int WavpackOpenCorrectionFileInput (WavpackContext *wpc, read_stream infile, char *error); int WavpackGetMode (WavpackContext *wpc); Index: apps/codecs/libwavpack/wputils.c =================================================================== --- apps/codecs/libwavpack/wputils.c (revision 22379) +++ apps/codecs/libwavpack/wputils.c (working copy) @@ -16,6 +16,7 @@ // decoding. #include "wavpack.h" +#include "logf.h" #include @@ -112,6 +113,49 @@ return &wpc; } +int WavpackOpenCorrectionFileInput (WavpackContext *wpc, read_stream infile, char *error) +{ + if (wpc == NULL) + return FALSE; + WavpackStream *wps = &wpc->stream; + uint32_t bcount; + + logf("Opening correction file input: %p\n", infile); + + wpc->infile2 = infile; + wps->wphdr.block_samples = 0; + wpc->wvc_flag = 1; + + // open the source file for reading and store the size + + while (!wps->wphdr.block_samples) { + bcount = read_next_header (wpc->infile2, &wps->wphdr); + + if (bcount == (uint32_t) -1) { + strcpy_loc (error, "invalid WavPack file!"); + return FALSE; + } + + if ((wps->wphdr.flags & UNKNOWN_FLAGS) || wps->wphdr.version < MIN_STREAM_VERS || + wps->wphdr.version > MAX_STREAM_VERS) { + strcpy_loc (error, "invalid WavPack file!"); + return FALSE; + } + + if (wps->wphdr.block_samples && wps->wphdr.total_samples != (uint32_t) -1) + wpc->total_samples = wps->wphdr.total_samples; + + if (!unpack_wvc_init (wpc)) { + strcpy_loc (error, wpc->error_message [0] ? wpc->error_message : + "invalid WavPack file!"); + + return FALSE; + } + } + + return TRUE; +} + // This function obtains general information about an open file and returns // a mask with the following bit values: @@ -167,6 +211,7 @@ if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) || wps->sample_index >= wps->wphdr.block_index + wps->wphdr.block_samples) { bcount = read_next_header (wpc->infile, &wps->wphdr); + bcount = read_next_header (wpc->infile2, &wps->wphdr); if (bcount == (uint32_t) -1) break; @@ -176,9 +221,10 @@ break; } - if (!wps->wphdr.block_samples || wps->sample_index == wps->wphdr.block_index) + if (!wps->wphdr.block_samples || wps->sample_index == wps->wphdr.block_index) { if (!unpack_init (wpc)) break; + } } if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) || @@ -222,8 +268,11 @@ samples -= samples_to_unpack; if (wps->sample_index == wps->wphdr.block_index + wps->wphdr.block_samples) { - if (check_crc_error (wpc)) + int crcerr = check_crc_error(wpc); + if (crcerr) { + logf("%d CRC ERRORS!\n", crcerr); wpc->crc_errors++; + } } if (wps->sample_index == wpc->total_samples) Index: apps/codecs/libwavpack/metadata.c =================================================================== --- apps/codecs/libwavpack/metadata.c (revision 22379) +++ apps/codecs/libwavpack/metadata.c (working copy) @@ -11,15 +11,16 @@ // This module handles the metadata structure introduced in WavPack 4.0 #include "wavpack.h" +#include "logf.h" #include -int read_metadata_buff (WavpackContext *wpc, WavpackMetadata *wpmd) +int read_metadata_buff (WavpackContext *wpc, read_stream infile, WavpackMetadata *wpmd, int wvc) { uint32_t bytes_to_read; uchar tchar; - if (!wpc->infile (&wpmd->id, 1) || !wpc->infile (&tchar, 1)) + if (!infile (&wpmd->id, 1) || !infile (&tchar, 1)) return FALSE; wpmd->byte_length = tchar << 1; @@ -27,12 +28,12 @@ if (wpmd->id & ID_LARGE) { wpmd->id &= ~ID_LARGE; - if (!wpc->infile (&tchar, 1)) + if (!infile (&tchar, 1)) return FALSE; wpmd->byte_length += (int32_t) tchar << 9; - if (!wpc->infile (&tchar, 1)) + if (!infile (&tchar, 1)) return FALSE; wpmd->byte_length += (int32_t) tchar << 17; @@ -43,26 +44,33 @@ wpmd->byte_length--; } - if (!wpmd->byte_length || wpmd->id == ID_WV_BITSTREAM) { + if (!wpmd->byte_length || wpmd->id == ID_WV_BITSTREAM || wpmd->id == ID_WVC_BITSTREAM) { wpmd->data = NULL; return TRUE; } bytes_to_read = wpmd->byte_length + (wpmd->byte_length & 1); - if (bytes_to_read > sizeof (wpc->read_buffer)) { + if ((wvc && bytes_to_read > sizeof (wpc->read_buffer2)) || (!wvc && bytes_to_read > sizeof (wpc->read_buffer))) { wpmd->data = NULL; - while (bytes_to_read > sizeof (wpc->read_buffer)) - if (wpc->infile (wpc->read_buffer, sizeof (wpc->read_buffer)) == sizeof (wpc->read_buffer)) - bytes_to_read -= sizeof (wpc->read_buffer); + while ((wvc && bytes_to_read > sizeof (wpc->read_buffer2)) || (!wvc && bytes_to_read > sizeof (wpc->read_buffer))) + if ((wvc && infile (wpc->read_buffer2, sizeof (wpc->read_buffer2)) == sizeof (wpc->read_buffer2)) || + (!wvc && infile (wpc->read_buffer, sizeof (wpc->read_buffer)) == sizeof (wpc->read_buffer))) { + if (wvc) + bytes_to_read -= sizeof (wpc->read_buffer2); + else + bytes_to_read -= sizeof (wpc->read_buffer); + } else return FALSE; } - else + else if (wvc) { + wpmd->data = wpc->read_buffer2; + } else wpmd->data = wpc->read_buffer; - if (bytes_to_read && wpc->infile (wpc->read_buffer, bytes_to_read) != (int32_t) bytes_to_read) { + if (bytes_to_read && ((wvc && infile (wpc->read_buffer2, bytes_to_read) != (int32_t) bytes_to_read) || (!wvc && infile (wpc->read_buffer, bytes_to_read) != (int32_t) bytes_to_read))) { wpmd->data = NULL; return FALSE; } @@ -109,14 +117,19 @@ return read_config_info (wpc, wpmd); case ID_WV_BITSTREAM: - return init_wv_bitstream (wpc, wpmd); + return init_wv_bitstream (wpc, 0, wpmd); case ID_SHAPING_WEIGHTS: + return read_shaping_info(wps, wpmd); + case ID_WVC_BITSTREAM: + return init_wv_bitstream(wpc, 1, wpmd); + case ID_WVX_BITSTREAM: return TRUE; default: + logf("unknown meta %d\n", wpmd->id); return (wpmd->id & ID_OPTIONAL_DATA) ? TRUE : FALSE; } } Index: apps/codecs/libwavpack/unpack.c =================================================================== --- apps/codecs/libwavpack/unpack.c (revision 22379) +++ apps/codecs/libwavpack/unpack.c (working copy) @@ -14,6 +14,7 @@ // an entire buffer. #include "wavpack.h" +#include "logf.h" #include #include @@ -41,10 +42,12 @@ wps->mute_error = FALSE; wps->crc = 0xffffffff; CLEAR (wps->wvbits); + CLEAR (wps->wvcbits); CLEAR (wps->decorr_passes); + CLEAR (wps->dc); CLEAR (wps->w); - while (read_metadata_buff (wpc, &wpmd)) { + while (read_metadata_buff (wpc, wpc->infile, &wpmd, 0)) { if (!process_metadata (wpc, &wpmd)) { strcpy_loc (wpc->error_message, "invalid metadata!"); return FALSE; @@ -55,11 +58,18 @@ } if (wps->wphdr.block_samples && !bs_is_open (&wps->wvbits)) { - strcpy_loc (wpc->error_message, "invalid WavPack file!"); + if (wpc->wvc_flag) { + strcpy_loc (wpc->error_message, "can't unpack correction files alone!"); + } else { + strcpy_loc (wpc->error_message, "invalid WavPack file!"); + } return FALSE; } - if (wps->wphdr.block_samples) { + if (wpc->wvc_flag) + unpack_wvc_init(wpc); + + if (!wpc->wvc_flag && wps->wphdr.block_samples) { if ((wps->wphdr.flags & INT32_DATA) && wps->int32_sent_bits) wpc->lossy_blocks = TRUE; @@ -71,18 +81,47 @@ return TRUE; } +int unpack_wvc_init (WavpackContext *wpc) +{ + WavpackMetadata wpmd; + + logf("unpack_wvc\n"); + + while (read_metadata_buff (wpc, wpc->infile2, &wpmd, 1)) { + if (!process_metadata (wpc, &wpmd)) { + strcpy_loc (wpc->error_message, "invalid metadata!"); + return FALSE; + } + + if (wpmd.id == ID_WVC_BITSTREAM) + break; + } + + return TRUE; +} + // This function initialzes the main bitstream for audio samples, which must // be in the "wv" file. -int init_wv_bitstream (WavpackContext *wpc, WavpackMetadata *wpmd) +int init_wv_bitstream (WavpackContext *wpc, int wvc, WavpackMetadata *wpmd) { WavpackStream *wps = &wpc->stream; - if (wpmd->data) - bs_open_read (&wps->wvbits, wpmd->data, (unsigned char *) wpmd->data + wpmd->byte_length, NULL, 0); - else if (wpmd->byte_length) - bs_open_read (&wps->wvbits, wpc->read_buffer, wpc->read_buffer + sizeof (wpc->read_buffer), - wpc->infile, wpmd->byte_length + (wpmd->byte_length & 1)); + if (wpmd->data) { + if (wvc) { + bs_open_read (&wps->wvcbits, wpmd->data, (unsigned char *) wpmd->data + wpmd->byte_length, NULL, 0); + } else { + bs_open_read (&wps->wvbits, wpmd->data, (unsigned char *) wpmd->data + wpmd->byte_length, NULL, 0); + } + } else if (wpmd->byte_length) { + if (wvc) { + bs_open_read (&wps->wvcbits, wpc->read_buffer2, wpc->read_buffer2 + sizeof (wpc->read_buffer2), + wpc->infile2, wpmd->byte_length + (wpmd->byte_length & 1)); + } else { + bs_open_read (&wps->wvbits, wpc->read_buffer, wpc->read_buffer + sizeof (wpc->read_buffer), + wpc->infile, wpmd->byte_length + (wpmd->byte_length & 1)); + } + } return TRUE; } @@ -210,6 +249,50 @@ return byteptr == endptr; } +// Read the shaping weights from specified metadata block into the +// WavpackStream structure. Note that there must be two values (even +// for mono streams) and that the values are stored in the same +// manner as decorrelation weights. These would normally be read from +// the "correction" file and are used for lossless reconstruction of +// hybrid data. + +int read_shaping_info (WavpackStream *wps, WavpackMetadata *wpmd) +{ + logf("read_shaping_info\n"); + + if (wpmd->byte_length == 2) { + char *byteptr = wpmd->data; + + wps->dc.shaping_acc [0] = (int32_t) restore_weight (*byteptr++) << 16; + wps->dc.shaping_acc [1] = (int32_t) restore_weight (*byteptr++) << 16; + return TRUE; + } + else if (wpmd->byte_length >= (wps->wphdr.flags & MONO_DATA ? 4 : 8)) { + uchar *byteptr = wpmd->data; + + wps->dc.error [0] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8))); + wps->dc.shaping_acc [0] = exp2s ((short)(byteptr [2] + (byteptr [3] << 8))); + byteptr += 4; + + if (!(wps->wphdr.flags & MONO_DATA)) { + wps->dc.error [1] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8))); + wps->dc.shaping_acc [1] = exp2s ((short)(byteptr [2] + (byteptr [3] << 8))); + byteptr += 4; + } + + if (wpmd->byte_length == (wps->wphdr.flags & MONO_DATA ? 6 : 12)) { + wps->dc.shaping_delta [0] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8))); + + if (!(wps->wphdr.flags & MONO_DATA)) + wps->dc.shaping_delta [1] = exp2s ((short)(byteptr [2] + (byteptr [3] << 8))); + } + + return TRUE; + } + + return FALSE; +} + // Read the int32 data from the specified metadata into the specified stream. // This data is used for integer data that has more than 24 bits of magnitude // or, in some cases, used to eliminate redundant bits from any audio stream. @@ -265,6 +348,10 @@ wpc->config.flags |= (int32_t) *byteptr++ << 8; wpc->config.flags |= (int32_t) *byteptr++ << 16; wpc->config.flags |= (int32_t) *byteptr << 24; + + if (bytecnt >= 4 && (wpc->config.flags & CONFIG_EXTRA_MODE)) { + logf("Extra mode!\n"); + } } return TRUE; @@ -322,8 +409,10 @@ int32_t mute_limit = (1L << ((flags & MAG_MASK) >> MAG_LSB)) + 2; struct decorr_pass *dpp; int32_t *bptr, *eptr; - int tcount; + int tcount, m = 0; + logf("unpacking %ld samples\n", sample_count); + if (wps->sample_index + sample_count > wps->wphdr.block_index + wps->wphdr.block_samples) sample_count = wps->wphdr.block_index + wps->wphdr.block_samples - wps->sample_index; @@ -357,7 +446,7 @@ //////////////////// handle version 4 stereo data //////////////////////// - else { + else if (!wpc->wvc_flag && !(flags & MONO_DATA)) { eptr = buffer + (sample_count * 2); i = get_words (buffer, sample_count, flags, &wps->w, &wps->wvbits); @@ -400,13 +489,273 @@ crc = (crc * 3 + bptr [0]) * 3 + bptr [1]; } } + + /////////////////// handle hybrid lossless mono data //////////////////// + else if (wpc->wvc_flag && (flags & MONO_DATA)) { + int32_t read_word; + int32_t correction[2]; + //printf("hybrid lossless mono!\n"); + for (bptr = buffer, i = 0; i < sample_count; ++i) { + + if ((read_word = get_word (wps, 0, correction)) == WORD_EOF) + break; + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { + int32_t sam, temp; + int k; + + if (dpp->term > MAX_TERM) { + if (dpp->term & 1) + sam = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + else + sam = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + + dpp->samples_A [1] = dpp->samples_A [0]; + k = 0; + } + else { + sam = dpp->samples_A [m]; + k = (m + dpp->term) & (MAX_TERM - 1); + } + + temp = apply_weight (dpp->weight_A, sam) + read_word; + update_weight (dpp->weight_A, dpp->delta, sam, read_word); + dpp->samples_A [k] = read_word = temp; + } + + m = (m + 1) & (MAX_TERM - 1); + + if (flags & HYBRID_SHAPE) { + int shaping_weight = (wps->dc.shaping_acc [0] += wps->dc.shaping_delta [0]) >> 16; + int32_t temp = -apply_weight (shaping_weight, wps->dc.error [0]); + + if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) { + if (temp == wps->dc.error [0]) + temp = (temp < 0) ? temp + 1 : temp - 1; + + wps->dc.error [0] = temp - correction [0]; + } + else + wps->dc.error [0] = -correction [0]; + + read_word += correction [0] - temp; + } + else + read_word += correction [0]; + + crc += (crc << 1) + read_word; + +#ifdef LOSSY_MUTE + if (labs (read_word) > mute_limit) + break; +#endif + *bptr++ = read_word; + } + } + + /////////////////// handle hybrid lossless stereo data //////////////////// + + else if (wpc->wvc_flag && !(flags & MONO_DATA)) { + //printf("hybrid lossless stereo!\n"); + for (bptr = buffer, i = 0; i < sample_count; ++i) { + int32_t left, right, left2, right2; + int32_t left_c = 0, right_c = 0; + int32_t correction [2]; + + if ((left = get_word (wps, 0, correction)) == WORD_EOF || + (right = get_word (wps, 1, correction + 1)) == WORD_EOF) { + break; + } + + if (flags & CROSS_DECORR) { + left_c = left + correction [0]; + right_c = right + correction [1]; + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { + int32_t sam_A, sam_B; + + if (dpp->term > 0) { + if (dpp->term > MAX_TERM) { + if (dpp->term & 1) { + sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1]; + } + else { + sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; + } + } + else { + sam_A = dpp->samples_A [m]; + sam_B = dpp->samples_B [m]; + } + + left_c += apply_weight (dpp->weight_A, sam_A); + right_c += apply_weight (dpp->weight_B, sam_B); + } + else if (dpp->term == -1) { + left_c += apply_weight (dpp->weight_A, dpp->samples_A [0]); + right_c += apply_weight (dpp->weight_B, left_c); + } + else { + right_c += apply_weight (dpp->weight_B, dpp->samples_B [0]); + + if (dpp->term == -3) + left_c += apply_weight (dpp->weight_A, dpp->samples_A [0]); + else + left_c += apply_weight (dpp->weight_A, right_c); + } + } + + if (flags & JOINT_STEREO) + left_c += (right_c -= (left_c >> 1)); + } + + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { + int32_t sam_A, sam_B; + + if (dpp->term > 0) { + int k; + + if (dpp->term > MAX_TERM) { + if (dpp->term & 1) { + sam_A = 2 * dpp->samples_A [0] - dpp->samples_A [1]; + sam_B = 2 * dpp->samples_B [0] - dpp->samples_B [1]; + } + else { + sam_A = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; + sam_B = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; + } + + dpp->samples_A [1] = dpp->samples_A [0]; + dpp->samples_B [1] = dpp->samples_B [0]; + k = 0; + } + else { + sam_A = dpp->samples_A [m]; + sam_B = dpp->samples_B [m]; + k = (m + dpp->term) & (MAX_TERM - 1); + } + + left2 = apply_weight (dpp->weight_A, sam_A) + left; + right2 = apply_weight (dpp->weight_B, sam_B) + right; + + update_weight (dpp->weight_A, dpp->delta, sam_A, left); + update_weight (dpp->weight_B, dpp->delta, sam_B, right); + + dpp->samples_A [k] = left = left2; + dpp->samples_B [k] = right = right2; + } + else if (dpp->term == -1) { + left2 = left + apply_weight (dpp->weight_A, dpp->samples_A [0]); + update_weight_clip (dpp->weight_A, dpp->delta, dpp->samples_A [0], left); + left = left2; + right2 = right + apply_weight (dpp->weight_B, left2); + update_weight_clip (dpp->weight_B, dpp->delta, left2, right); + dpp->samples_A [0] = right = right2; + } + else { + right2 = right + apply_weight (dpp->weight_B, dpp->samples_B [0]); + update_weight_clip (dpp->weight_B, dpp->delta, dpp->samples_B [0], right); + right = right2; + + if (dpp->term == -3) { + right2 = dpp->samples_A [0]; + dpp->samples_A [0] = right; + } + + left2 = left + apply_weight (dpp->weight_A, right2); + update_weight_clip (dpp->weight_A, dpp->delta, right2, left); + dpp->samples_B [0] = left = left2; + } + } + + m = (m + 1) & (MAX_TERM - 1); + + if (!(flags & CROSS_DECORR)) { + left_c = left + correction [0]; + right_c = right + correction [1]; + + if (flags & JOINT_STEREO) + left_c += (right_c -= (left_c >> 1)); + } + + if (flags & JOINT_STEREO) + left += (right -= (left >> 1)); + + if (flags & HYBRID_SHAPE) { + int shaping_weight; + int32_t temp; + + correction [0] = left_c - left; + shaping_weight = (wps->dc.shaping_acc [0] += wps->dc.shaping_delta [0]) >> 16; + temp = -apply_weight (shaping_weight, wps->dc.error [0]); + + if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) { + if (temp == wps->dc.error [0]) + temp = (temp < 0) ? temp + 1 : temp - 1; + + wps->dc.error [0] = temp - correction [0]; + } + else + wps->dc.error [0] = -correction [0]; + + left = left_c - temp; + correction [1] = right_c - right; + shaping_weight = (wps->dc.shaping_acc [1] += wps->dc.shaping_delta [1]) >> 16; + temp = -apply_weight (shaping_weight, wps->dc.error [1]); + + if ((flags & NEW_SHAPING) && shaping_weight < 0 && temp) { + if (temp == wps->dc.error [1]) + temp = (temp < 0) ? temp + 1 : temp - 1; + + wps->dc.error [1] = temp - correction [1]; + } + else + wps->dc.error [1] = -correction [1]; + + right = right_c - temp; + } + else { + left = left_c; + right = right_c; + } + + bptr[0] = left; + bptr[1] = right; + + if (labs (bptr[0]) > mute_limit || labs (bptr[1]) > mute_limit) + break; + + crc += (crc << 3) + (bptr[0] << 1) + bptr[0] + bptr[1]; + bptr += 2; + } + } + if (i != sample_count) { memset (buffer, 0, sample_count * (flags & MONO_FLAG ? 4 : 8)); wps->mute_error = TRUE; + logf("insufficient samples returned: %ld/%ld\n", i, sample_count); i = sample_count; } + if (m) + for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) + if (dpp->term > 0 && dpp->term <= MAX_TERM) { + int32_t temp_A [MAX_TERM], temp_B [MAX_TERM]; + int k; + + memcpy(temp_A, dpp->samples_A, sizeof (dpp->samples_A)); + memcpy(temp_B, dpp->samples_B, sizeof (dpp->samples_B)); + + for (k = 0; k < MAX_TERM; k++) { + dpp->samples_A [k] = temp_A [m]; + dpp->samples_B [k] = temp_B [m]; + m = (m + 1) & (MAX_TERM - 1); + } + } + fixup_samples (wps, buffer, i); if (flags & FALSE_STEREO) { Index: apps/codecs/wavpack.c =================================================================== --- apps/codecs/wavpack.c (revision 22379) +++ apps/codecs/wavpack.c (working copy) @@ -21,6 +21,7 @@ #include "codeclib.h" #include "libwavpack/wavpack.h" +#include "logf.h" CODEC_HEADER @@ -35,6 +36,11 @@ return retval; } +static int32_t read_correction_callback (void *buffer, int32_t bytes) { + int32_t retval = ci->read_secbuf (buffer, bytes); + return retval; +} + /* this is the codec entry point */ enum codec_status codec_main(void) { @@ -64,6 +70,12 @@ goto done; } + if (ci->id3->has_secondary) { + if (!WavpackOpenCorrectionFileInput (wpc, read_correction_callback, error)) { + logf("%s\n", error); + } + } + ci->configure(DSP_SWITCH_FREQUENCY, WavpackGetSampleRate (wpc)); codec_set_replaygain(ci->id3); bps = WavpackGetBytesPerSample (wpc); @@ -82,6 +94,7 @@ ci->seek_time--; int curpos_ms = WavpackGetSampleIndex (wpc) / sr_100 * 10; int n, d, skip; + //printf("SEEK NEEDED BY WAVPACK\n"); if (ci->seek_time > curpos_ms) { n = ci->seek_time - curpos_ms; @@ -97,6 +110,11 @@ } wpc = WavpackOpenFileInput (read_callback, error); + if (ci->id3->has_secondary) { + if (!WavpackOpenCorrectionFileInput (wpc, read_correction_callback, error)) { + logf("%s\n", error); + } + } ci->seek_complete(); if (!wpc)