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) @@ -116,6 +116,9 @@ 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; + struct memory_handle *chunky_next; + struct memory_handle *file_next; + int chunky; }; /* invariant: filesize == offset + available + filerem */ @@ -138,6 +141,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 +209,40 @@ buf_ridx == buf_widx means the buffer is empty. */ +/* Reserve all space required on the buffer for a particular handle + */ +static bool finish_handle(struct memory_handle *h, bool chunkonly) { + printf("finish %d (chunky %d)\n", h->id, chunkonly); + size_t req = h->filerem; + + if (chunkonly) { + req = MIN( req, BUFFERING_DEFAULT_FILECHUNK * 2); + } + if (h->next != NULL && RINGBUF_ADD_CROSS(h->widx, req + sizeof(struct memory_handle), (unsigned)((void*)h->next-(void*)buffer)) >= 0) { + /* Not enough space */ + if (h->chunky) { + printf("complex chunky handling necessary!\n"); + return false; + } 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 +250,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) { /* gives each handle a unique id */ static int cur_handle_id = 0; @@ -232,21 +265,41 @@ mutex_lock(&llist_mutex); mutex_lock(&llist_mod_mutex); + printf("add_handle %d\n", chunky); + 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) { + if (first_chunky != NULL) { + /* 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_chunky; + while (foo != NULL) { + if (!finish_handle(foo, false)) { + mutex_unlock(&llist_mod_mutex); + mutex_unlock(&llist_mutex); + return NULL; + } + foo = foo->chunky_next; + } + } + } /* align to 4 bytes up */ new_widx = RINGBUF_ADD(buf_widx, 3) & ~3; @@ -266,7 +319,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); @@ -287,12 +340,21 @@ /* Wrap signed int is safe and 0 doesn't happen */ cur_handle_id = (cur_handle_id + 1) & BUF_HANDLE_MASK; new_handle->next = NULL; + new_handle->chunky_next = NULL; + new_handle->chunky = chunky; num_handles++; if (!first_handle) /* the new handle is the first one */ first_handle = new_handle; + if (chunky) { + if (first_chunky != NULL) { + new_handle->chunky_next = first_handle; + } + first_chunky = new_handle; + } + if (cur_handle) cur_handle->next = new_handle; @@ -613,22 +675,35 @@ 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; + size_t overlap = RINGBUF_ADD_CROSS(h->widx, copy_n, (unsigned)((void *)h->next - (void *)buffer)); + if (h->next && overlap > 0) { + if (h->chunky) { + /* OK, add a new handle to compensate */ + printf("Chunky split handle!\n"); + struct memory_handle *nh = add_handle(overlap, true, false, true); + if (nh == NULL) { + /* OK, we're in trouble */ + return false; + } + h->file_next = nh; + copy_n -= overlap; + } 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; + } } /* rc is the actual amount read */ @@ -709,6 +784,10 @@ if (!h) return; + if (h->chunky) { + printf("chunky rebuffer\n"); + } + /* 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) @@ -892,21 +971,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); if (!h) return ERR_BUFFER_FULL; @@ -944,7 +1015,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 +1080,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 +1107,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; @@ -1076,8 +1165,16 @@ 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 || h->chunky_next == NULL) { + /* access before or after buffered data. A rebuffer is needed. */ + rebuffer_handle(handle_id, newpos); + } else { + /* "chunky" handle - we can search forward in the chain */ + printf("chunky chain search\n"); + h->chunky_next->id = handle_id; + rm_handle(h); + return bufseek(handle_id, newpos); + } } else { h->ridx = RINGBUF_ADD(h->data, newpos - h->offset); @@ -1158,25 +1255,34 @@ ssize_t bufread(int handle_id, size_t size, void *dest) { const struct memory_handle *h; - size_t adjusted_size = size; + size_t rem_size = size; + size_t rsize; - h = prep_bufdata(handle_id, &adjusted_size, false); - if (!h) - return ERR_HANDLE_NOT_FOUND; + while (rem_size > 0) { + h = prep_bufdata(handle_id, &rsize, false); - 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) + return ERR_HANDLE_NOT_FOUND; + + if (rsize == 0) + break; + + if (h->ridx + rsize > 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, rsize - read); + } + else + { + memcpy(dest, &buffer[h->ridx], rsize); + } + rem_size -= rsize; + dest += rsize; } - else - { - memcpy(dest, &buffer[h->ridx], adjusted_size); - } - return adjusted_size; + return size-rem_size; } /* Update the "data" pointer to make the handle's data available to the caller. @@ -1492,6 +1598,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);