Index: apps/codecs.c =================================================================== --- apps/codecs.c (revision 22020) +++ 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 22020) +++ 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 22020) +++ 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) { + snprintf(id3->secpath, MAX_PATH, "%sc", id3->path); + } + return true; } Index: apps/metadata.c =================================================================== --- apps/metadata.c (revision 22020) +++ apps/metadata.c (working copy) @@ -277,6 +277,7 @@ break; case AFMT_WAVPACK: + strncpy(id3->path, trackname, sizeof(id3->path)); if (!get_wavpack_metadata(fd, id3)) { return false; Index: apps/metadata.h =================================================================== --- apps/metadata.h (revision 22020) +++ apps/metadata.h (working copy) @@ -243,6 +243,9 @@ /* Musicbrainz Track ID */ char* mb_track_id; + + /* Secondary file path */ + char secpath[MAX_PATH]; }; unsigned int probe_file_format(const char *filename); Index: apps/playback.c =================================================================== --- apps/playback.c (revision 22020) +++ apps/playback.c (working copy) @@ -216,6 +216,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 @@ -378,6 +379,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) { @@ -1067,6 +1075,24 @@ 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; + + 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 +1992,12 @@ else file_offset = 0; - tracks[track_widx].audio_hid = bufopen(track_id3->path, file_offset, type); + if (track_id3->secpath[0] != '\0') { + tracks[track_widx].audio_hid = bufopenchunky(track_id3->path, file_offset, type); + tracks[track_widx].secondary_hid = bufopenchunky(track_id3->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 +2659,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 22020) +++ apps/buffering.c (working copy) @@ -112,10 +112,12 @@ 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; + volatile struct memory_handle *next; + volatile struct memory_handle *file_next; + int chunky; }; /* invariant: filesize == offset + available + filerem */ @@ -138,6 +140,8 @@ static struct memory_handle *cur_handle; /* first memory handle in the linked list. NULL when the list is empty. */ static struct memory_handle *first_handle; +/* first "chunky" handle present */ +static struct memory_handle *first_chunky; static int num_handles; /* number of handles in the list */ @@ -204,12 +208,63 @@ buf_ridx == buf_widx means the buffer is empty. */ +static struct memory_handle *add_handle(size_t data_size, bool can_wrap, + bool alloc_all, bool chunky, int id); +/* Reserve all space required on the buffer for a particular handle + */ +static bool finish_handle(struct memory_handle *h, bool chunkonly) { + size_t req = h->filerem; + + if (chunkonly) { + req = MIN( req, BUFFERING_DEFAULT_FILECHUNK * 2); + } + + if (req == 0) { + return true; + } + + printf("finish %u of %u at %u (%s)\n", req, h->id, (unsigned)((void*)h-(void*)buffer), chunkonly?"chunkonly":"full"); + + unsigned int overlap = RINGBUF_ADD_CROSS(h->widx, req + sizeof(struct memory_handle), (unsigned)((void*)h->next-(void*)buffer)); + + if (h->next != NULL && overlap > 0) { + /* Not enough space */ + if (h->chunky) { + printf("complex chunky handling necessary!\n"); + struct memory_handle *nh = add_handle(overlap, true, false, true, h->id); + if (!nh) + return false; + h->file_next = nh; + nh->offset = h->offset+h->available+h->filerem; + nh->type = h->type; + strcpy(nh->path, h->path); + nh->data = buf_widx; + nh->widx = nh->data; + nh->ridx = nh->data; + nh->filesize = h->filesize; + nh->filerem = nh->filesize-nh->offset; + h->filerem = req-overlap; + return finish_handle(nh, chunkonly); + } else { + printf("insufficient space to finish handle!\n"); + return false; + } + } else { + /* Allocate the remainder of the space for the current handle */ + if (h->widx == buf_widx) { + buf_widx = RINGBUF_ADD(cur_handle->widx, req); + } + return true; + } +} + /* Add a new handle to the linked list and return it. It will have become the new current handle. data_size must contain the size of what will be in the handle. can_wrap tells us whether this type of data may wrap on buffer alloc_all tells us if we must immediately be able to allocate data_size + chunky tells us whether the allocation should be split across multiple handles returns a valid memory handle if all conditions for allocation are met. NULL if there memory_handle itself cannot be allocated or if the data_size cannot be allocated and alloc_all is set. This function's @@ -217,7 +272,7 @@ if it returns NULL. */ static struct memory_handle *add_handle(size_t data_size, bool can_wrap, - bool alloc_all) + bool alloc_all, bool chunky, int id) { /* gives each handle a unique id */ static int cur_handle_id = 0; @@ -233,20 +288,36 @@ 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; + if (chunky && cur_handle->chunky) { + if (!finish_handle(cur_handle, true)) { + 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); + /* 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. */ + if (!finish_handle(cur_handle, false)) { + mutex_unlock(&llist_mod_mutex); + mutex_unlock(&llist_mutex); + return NULL; + } } } + if (!chunky) { + /* uh-oh, we've got to "finish" every single chunky handle in the buffer */ + printf("finishing chunky handles necessary!\n"); + struct memory_handle *foo = first_handle; + while (foo != NULL) { + if (foo->chunky && foo->filerem > 0 && !finish_handle(foo, false)) { + mutex_unlock(&llist_mod_mutex); + mutex_unlock(&llist_mutex); + return NULL; + } + foo = foo->next; + } + } /* align to 4 bytes up */ new_widx = RINGBUF_ADD(buf_widx, 3) & ~3; @@ -266,7 +337,7 @@ /* How much space are we short in the actual ring buffer? */ overlap = RINGBUF_ADD_CROSS(buf_widx, shift + len, buf_ridx); - if (overlap >= 0 && (alloc_all || (unsigned)overlap > data_size)) { + if (overlap >= 0 && (!chunky && (alloc_all || (unsigned)overlap > data_size))) { /* Not enough space for required allocations */ mutex_unlock(&llist_mod_mutex); mutex_unlock(&llist_mutex); @@ -279,14 +350,22 @@ struct memory_handle *new_handle = (struct memory_handle *)(&buffer[buf_widx]); - + /* only advance the buffer write index of the size of the struct */ buf_widx = RINGBUF_ADD(buf_widx, sizeof(struct memory_handle)); - new_handle->id = cur_handle_id; - /* Wrap signed int is safe and 0 doesn't happen */ - cur_handle_id = (cur_handle_id + 1) & BUF_HANDLE_MASK; + if (id == 0) { + new_handle->id = cur_handle_id; + /* Wrap signed int is safe and 0 doesn't happen */ + cur_handle_id = (cur_handle_id + 1) & BUF_HANDLE_MASK; + } else { + new_handle->id = id; + } + + printf("handle %d added at %d (%s)\n", new_handle->id, (unsigned)((void*)new_handle-(void*)buffer), chunky?"chunky":"normal"); + new_handle->next = NULL; + new_handle->chunky = chunky; num_handles++; if (!first_handle) @@ -310,6 +389,8 @@ if (h == NULL) return true; + printf("removing handle %u at %u\n", h->id, (unsigned)((void*)h-(void*)buffer)); + mutex_lock(&llist_mutex); mutex_lock(&llist_mod_mutex); @@ -565,6 +646,10 @@ struct memory_handle *h = find_handle(handle_id); if (!h) return true; + while (h->filerem == 0 && h->file_next) { + h->file_next->fd = h->fd; + h = h->file_next; + } if (h->filerem == 0) { /* nothing left to buffer */ @@ -606,31 +691,76 @@ return true; } - while (h->filerem > 0) + while (h->filerem > 0 || h->file_next) { + if (h->filerem == 0 && h->file_next) { + h->file_next->fd = h->fd; + h = h->file_next; + } /* max amount to copy */ size_t copy_n = MIN( MIN(h->filerem, BUFFERING_DEFAULT_FILECHUNK), buffer_len - h->widx); /* stop copying if it would overwrite the reading position */ - if (RINGBUF_ADD_CROSS(h->widx, copy_n, buf_ridx) >= 0) + if (!h->chunky && 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; + + unsigned int noff = 0; + unsigned int overlap = 0; + if (h->next) { + noff = (unsigned)((void*)h->next - (void*)buffer); + + if (h->widx < noff && h->widx+copy_n > noff) { + overlap = h->widx+copy_n - noff; + } else if (h->widx > noff && h->widx+copy_n-buffer_len > noff) { + overlap = h->widx+copy_n-buffer_len-noff; + } } + if (h->widx == (unsigned)((void*)h->next-(void*)buffer)) { + overlap = copy_n; + } + if (h->next && overlap > 0) { + if (h->chunky) { + /* OK, add a new handle to compensate */ + printf("Chunky split handle %d (%u over)!\n", h->id, overlap); + struct memory_handle *nh = add_handle(overlap, true, false, true, h->id); + if (nh == NULL) { + /* OK, we're in trouble */ + return false; + } + h->file_next = nh; + copy_n -= overlap; + h->filerem = copy_n; + nh->filerem = overlap; + nh->filesize = h->filesize; + strcpy(nh->path, h->path); + nh->fd = -1; + nh->type = h->type; + nh->offset = h->offset+h->available+copy_n; + nh->data = buf_widx; + nh->ridx = buf_widx; + nh->widx = buf_widx; + nh->available = 0; + } else { + printf("NON-CHUNKY TRUNCATION!!!\n"); + /* 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; + } + } + + printf("Reading %u at %u into handle %u (next %u at %u, buffer ends at %u)\n", copy_n, h->widx, h->id, h->next?h->next->id:0, h->next?(unsigned)((void*)h->next-(void*)buffer):0, (unsigned)(buffer+buffer_len)); + /* rc is the actual amount read */ int rc = read(h->fd, &buffer[h->widx], copy_n); @@ -709,6 +839,16 @@ if (!h) return; + if (h->chunky) { + printf("chunky rebuffer!\n"); + struct memory_handle *cur = h->file_next; + while (cur != NULL) { + struct memory_handle *tmp = cur->file_next; + rm_handle(cur); + cur = tmp; + } + } + /* 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) @@ -752,8 +892,14 @@ h->fd = -1; } - /* rm_handle returns true unless the handle somehow persists after exit */ - return rm_handle(h); + while (h) { + struct memory_handle *next = h->file_next; + /* rm_handle returns true unless the handle somehow persists after exit */ + if (!rm_handle(h)) { + return false; + } + h = next; + } } /* Free buffer space by moving the handle struct right before the useful @@ -892,21 +1038,13 @@ management functions for all the actual handle management work. */ - -/* Reserve space in the buffer for a file. - filename: name of the file to open - offset: offset at which to start buffering the file, useful when the first - (offset-1) bytes of the file aren't needed. - 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) +int bufopeninternal(const char *file, size_t offset, enum data_type type, bool chunky) { 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, 0); if (!h) return ERR_BUFFER_FULL; @@ -944,7 +1082,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, 0); if (!h) { DEBUGF("bufopen: failed to add handle\n"); @@ -1009,6 +1147,24 @@ return h->id; } +/* Reserve space in the buffer for a file. + filename: name of the file to open + offset: offset at which to start buffering the file, useful when the first + (offset-1) bytes of the file aren't needed. + 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) +{ + return bufopeninternal(file, offset, type, false); +} + +int bufopenchunky(const char *file, size_t offset, enum data_type type) +{ + printf("bufopenchunky %s\n", file); + 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 +1174,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, 0); if (!h) return ERR_BUFFER_FULL; @@ -1071,16 +1227,44 @@ if (!h) return ERR_HANDLE_NOT_FOUND; + printf("Trying to seek %u at %u to %u\n", handle_id, (unsigned)((void*)h-(void*)buffer), newpos); + if (newpos > h->filesize) { /* access beyond the end of the file */ + printf("Access beyond file end!\n"); return ERR_INVALID_VALUE; } 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); + if (!h->chunky || newpos < h->offset) { + /* access before or after buffered data. A rebuffer is needed. */ + printf("rebuffer needed for seek\n"); + rebuffer_handle(handle_id, newpos); + } else { + /* "chunky" handle - we can search forward in the chain */ + printf("chunky chain search\n"); + if (h->file_next != NULL) { + h->file_next->fd = h->fd; + rm_handle(h); + return bufseek(handle_id, newpos); + } else { + struct memory_handle *nh = add_handle(h->filesize-newpos, true, false, true, h->id); + nh->offset = newpos; + nh->fd = h->fd; + strcpy(nh->path, h->path); + nh->data = buf_widx; + nh->widx = nh->data; + nh->ridx = nh->data; + nh->available = 0; + nh->filesize = h->filesize; + nh->type = h->type; + rm_handle(h); + return 0; + } + } } else { h->ridx = RINGBUF_ADD(h->data, newpos - h->offset); + printf("new ridx for %u (%s) is %u\n", h->id, h->path?h->path:"NFILE", h->ridx); } return 0; } @@ -1109,10 +1293,23 @@ return NULL; size_t avail = RINGBUF_SUB(h->widx, h->ridx); + while (avail == 0 && h->filerem+h->available < h->filesize-h->offset) { + avail = RINGBUF_SUB(h->widx, h->ridx); + buf_request_buffer_handle(handle_id); + do { + sleep(1); + h = find_handle(handle_id); + if (!h) + return NULL; + } while (h->file_next == NULL); + h = h->file_next; + avail = RINGBUF_SUB(h->widx, h->ridx); + } if (avail == 0 && h->filerem == 0) { /* File is finished reading */ + printf("DONE READING %u\n", handle_id); *size = 0; return h; } @@ -1136,6 +1333,7 @@ /* Wait for the data to be ready */ do { + printf("waiting for stuff to be buffered for %u\n", handle_id); sleep(1); /* it is not safe for a non-buffering thread to sleep while * holding a handle */ @@ -1145,6 +1343,7 @@ avail = RINGBUF_SUB(h->widx, h->ridx); } while (h->filerem > 0 && avail < *size); + printf("%u is now available for %u (%u requested)\n", avail, handle_id, *size); } *size = MIN(*size,avail); @@ -1157,26 +1356,68 @@ */ ssize_t bufread(int handle_id, size_t size, void *dest) { - const struct memory_handle *h; - size_t adjusted_size = size; + ssize_t nread = 0; + size_t cread = 0; + int hnum = 0; + struct memory_handle *h; - h = prep_bufdata(handle_id, &adjusted_size, false); + h = find_handle(handle_id); if (!h) return ERR_HANDLE_NOT_FOUND; - 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 (size > h->filesize-h->offset) { + size = h->filesize-h->offset; } - else - { - memcpy(dest, &buffer[h->ridx], adjusted_size); + + printf("attempting to read %u from handle %u\n", size, handle_id); + + while (nread < size) { + size_t avail = RINGBUF_SUB(h->widx, h->ridx); + + if (cread < avail) { + size_t ntor = MIN(avail-cread, size-nread); + printf("taking %u from %u for %u [%u avail] (cr %u nr %u)\n", ntor, h->ridx+cread, handle_id, avail, cread, nread); + if (h->ridx+ntor > buffer_len) { + size_t left = buffer_len-h->ridx; + memcpy(dest+nread, &buffer[h->ridx+cread], left); + nread += left; + cread += left; + ntor -= left; + memcpy(dest+nread, buffer, ntor); + } else { + memcpy(dest+nread, &buffer[h->ridx+cread], ntor); + } + cread += ntor; + nread += ntor; + } else { + if (h->filerem == 0 && h->file_next) { + printf("Moving on to chunk %u of %u\n", hnum+1, handle_id); + h = h->file_next; + hnum++; + cread = 0; + } else if (h->filesize-h->offset > h->available) { + buf_request_buffer_handle(handle_id); + sleep(1); + h = find_handle(handle_id); + if (!h) + return ERR_HANDLE_NOT_FOUND; + int i = 0; + while (i < hnum) { + printf("repositioning to chunk %u of %u\n", hnum, handle_id); + if (!h->file_next) { + printf("REPOSITIONING FAILED!\n"); + return nread; + } + h = h->file_next; + i++; + } + } else { + break; + } + } } - - return adjusted_size; + printf("Actually read %u from %u\n", nread, handle_id); + return nread; } /* Update the "data" pointer to make the handle's data available to the caller. @@ -1492,6 +1733,7 @@ buf_ridx = 0; first_handle = NULL; + first_chunky = NULL; cur_handle = NULL; cached_handle = NULL; num_handles = 0; Index: apps/buffering.h =================================================================== --- apps/buffering.h (revision 22020) +++ 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 22020) +++ 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 22020) +++ 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 22020) +++ apps/codecs/libwavpack/wputils.c (working copy) @@ -16,6 +16,7 @@ // decoding. #include "wavpack.h" +#include "logf.h" #include @@ -112,6 +113,47 @@ return &wpc; } +int WavpackOpenCorrectionFileInput (WavpackContext *wpc, read_stream infile, char *error) +{ + 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 +209,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 +219,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 +266,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 22020) +++ 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 22020) +++ 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); @@ -401,12 +490,208 @@ } } + /////////////////// handle hybrid lossless stereo data //////////////////// + + else if (wpc->wvc_flag && !(flags & MONO_DATA)) { + printf("hybrid lossless!\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 22020) +++ 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->secpath[0] != '\0') { + 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);