Index: apps/codecs.c =================================================================== --- apps/codecs.c (revision 21547) +++ apps/codecs.c (working copy) @@ -171,6 +171,9 @@ /* new stuff at the end, sort into place next time the API gets incompatible */ + + NULL, /* advance_secondary_buffer */ + NULL, /* read_secondarybuf */ }; void codec_get_full_path(char *path, const char *codec_root_fn) Index: apps/codecs.h =================================================================== --- apps/codecs.h (revision 21547) +++ apps/codecs.h (working copy) @@ -236,6 +236,12 @@ /* new stuff at the end, sort into place next time the API gets incompatible */ + + /* Advance secondary buffer position by amount of bytes. */ + void (*advance_secondary_buffer)(int handle, size_t amount); + /* Read next amount bytes from secondary buffer to . + Will return number of bytes read or 0 if end of file. */ + size_t (*read_secondarybuf)(int handle, void *ptr, size_t size); }; /* codec header */ Index: apps/metadata/wavpack.c =================================================================== --- apps/metadata/wavpack.c (revision 21547) +++ 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,17 @@ 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(buf, MAX_PATH, "%sc", id3->path); + logf ("Attempting to open correction file %s...\n", buf); + id3->sechandle = bufopensecondary(buf, 0, TYPE_PACKET_AUDIO); + if (id3->sechandle < 0) { + logf("Opening Wavpack correction file %s failed!", buf); + id3->sechandle = 0; + } + } + return true; } Index: apps/metadata.c =================================================================== --- apps/metadata.c (revision 21547) +++ apps/metadata.c (working copy) @@ -274,6 +274,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 21547) +++ apps/metadata.h (working copy) @@ -242,6 +242,9 @@ /* Musicbrainz Track ID */ char* mb_track_id; + + /* Secondary filehandle */ + int sechandle; }; unsigned int probe_file_format(const char *filename); Index: apps/playback.c =================================================================== --- apps/playback.c (revision 21547) +++ apps/playback.c (working copy) @@ -1024,6 +1024,24 @@ } /* copy up-to size bytes into ptr and return the actual size copied */ +static size_t codec_secondarybuf_callback(int handle, void *ptr, size_t size) { + ssize_t copy_n; + + if (ci.stop_codec || !playing) + return 0; + + copy_n = bufread(handle, size, ptr); + + /* Nothing requested OR nothing left */ + if (copy_n == 0) + return 0; + + bufadvance(handle, copy_n); + + return copy_n; +} + +/* copy up-to size bytes into ptr and return the actual size copied */ static size_t codec_filebuf_callback(void *ptr, size_t size) { ssize_t copy_n; @@ -1089,6 +1107,11 @@ codec_set_offset_callback(ci.curpos); } +static void codec_advance_secondary_buffer_callback(int handle, size_t amount) +{ + bufadvance(handle, amount); +} + static void codec_advance_buffer_loc_callback(void *ptr) { size_t amount = buf_get_offset(CUR_TI->audio_hid, ptr); @@ -2587,6 +2610,8 @@ ci.discard_codec = codec_discard_codec_callback; ci.dsp = (struct dsp_config *)dsp_configure(NULL, DSP_MYDSP, CODEC_IDX_AUDIO); + ci.advance_secondary_buffer = codec_advance_secondary_buffer_callback; + ci.read_secondarybuf = codec_secondarybuf_callback; thistrack_id3 = &mp3entry_buf[0]; othertrack_id3 = &mp3entry_buf[1]; Index: apps/buffering.c =================================================================== --- apps/buffering.c (revision 21547) +++ apps/buffering.c (working copy) @@ -93,9 +93,11 @@ /* Ring buffer helper macros */ /* Buffer pointer (p) plus value (v), wrapped if necessary */ -#define RINGBUF_ADD(p,v) (((p)+(v))=v) ? (p)-(v) : (p)+buffer_len-(v)) +#define RINGBUF_SUB2(p,v,b,l) ((p>=v) ? (p)-(v)+(b) : (p)+(l)-(v)) +#define RINGBUF_SUB(p,v) RINGBUF_SUB2(p, v, 0, buffer_len) /* How far value (v) plus buffer pointer (p1) will cross buffer pointer (p2) */ #define RINGBUF_ADD_CROSS(p1,v,p2) \ ((p1id = cur_handle_id; + /* Wrap signed int is safe and 0 doesn't happen */ + cur_handle_id = (cur_handle_id + 1) & BUF_HANDLE_MASK; + new_handle->next = next; + new_handle->data = loc+sizeof(struct memory_handle); + new_handle->ridx = new_handle->data; + new_handle->widx = new_handle->data; + num_handles++; + if (prev != NULL) + prev->next = new_handle; + + if (!first_handle) + /* the new handle is the first one */ + first_handle = new_handle; + + if (next == NULL) { + cur_handle = new_handle; + buf_widx = new_handle->data; + } + + printf("add_handle_at %d with prev %d and next %d\n", new_handle, prev, next); + return new_handle; +} + /* 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 returns a valid memory handle if all conditions for allocation are met. - NULL if there memory_handle itself cannot be allocated or if the + NULL if the memory_handle itself cannot be allocated or if the data_size cannot be allocated and alloc_all is set. This function's only potential side effect is to allocate space for the cur_handle if it returns NULL. @@ -219,12 +256,11 @@ static struct memory_handle *add_handle(size_t data_size, bool can_wrap, bool alloc_all) { - /* gives each handle a unique id */ - static int cur_handle_id = 0; size_t shift; size_t new_widx; size_t len; int overlap; + struct memory_handle *new_handle; if (num_handles >= BUF_MAX_HANDLES) return NULL; @@ -232,13 +268,37 @@ mutex_lock(&llist_mutex); mutex_lock(&llist_mod_mutex); - if (cur_handle && cur_handle->filerem > 0) { + if (data_size < buffer_len / 2) { + /* Our data size is "small" - try to fit it in between other handles */ + struct memory_handle *cur = first_handle; + while (cur != NULL) { + size_t req = cur->filerem + sizeof(struct memory_handle); + if (req < buffer_len / 2) { + size_t foo = RINGBUF_ADD(cur->widx, req); + if (cur->next != NULL && + RINGBUF_ADD_CROSS(foo, data_size, + (unsigned)((void*)cur->next-(void*)buffer)) < -3) { + /* We might fit our data between this handle and the next (remember alignment) */ + foo = RINGBUF_ADD(foo, 3) & ~3; + if (foo > cur->next+cur->next->filerem) { + new_handle = add_handle_at(foo, cur, cur->next); + mutex_unlock(&llist_mod_mutex); + mutex_unlock(&llist_mutex); + return new_handle; + } + } + } + cur = cur->next; + } + printf("Nowhere to fit new handle of size %d!\n", data_size); + } else 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. */ + printf("buffering finish of %d forced\n", cur_handle); size_t req = cur_handle->filerem + sizeof(struct memory_handle); if (RINGBUF_ADD_CROSS(cur_handle->widx, req, buf_ridx) >= 0) { - /* Not enough space */ + /* Not enough space between here and start of next handle */ mutex_unlock(&llist_mod_mutex); mutex_unlock(&llist_mutex); return NULL; @@ -277,27 +337,13 @@ * initialize the struct */ buf_widx = new_widx; - struct memory_handle *new_handle = - (struct memory_handle *)(&buffer[buf_widx]); - + new_handle = add_handle_at(buf_widx, + buf_widx>(unsigned)((void*)cur_handle-(void*)buffer)?cur_handle:NULL, + buf_widx<(unsigned)((void*)first_handle-(void*)buffer)?first_handle:NULL); + /* 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; - new_handle->next = NULL; - num_handles++; - - if (!first_handle) - /* the new handle is the first one */ - first_handle = new_handle; - - if (cur_handle) - cur_handle->next = new_handle; - - cur_handle = new_handle; - mutex_unlock(&llist_mod_mutex); mutex_unlock(&llist_mutex); return new_handle; @@ -456,39 +502,64 @@ } } + /* Check for safety */ + if (newpos > buffer_len) + return false; + dest = (struct memory_handle *)(&buffer[newpos]); - - if (src == first_handle) { + if (dest < first_handle) { + (*h)->next = first_handle; first_handle = dest; buf_ridx = newpos; + } else if (dest > cur_handle) { + cur_handle->next = dest; + (*h)->next = NULL; + cur_handle = dest; } else { + /* Reinsert handle in order */ struct memory_handle *m = first_handle; while (m && m->next != src) { m = m->next; } if (m && m->next == src) { + m->next = m->next->next; + } else { + mutex_unlock(&llist_mod_mutex); + mutex_unlock(&llist_mutex); + return false; + } + m = first_handle; + while (m && m->next < dest) { + m = m->next; + } + if (m && m->next > dest) { m->next = dest; + (*h)->next = m->next; } else { mutex_unlock(&llist_mod_mutex); mutex_unlock(&llist_mutex); return false; } } + + if (cur_handle == src) { + struct memory_handle *m = first_handle; + while (m->next) { + m = m->next; + } + cur_handle = m; + } - /* Update the cache to prevent it from keeping the old location of h */ if (src == cached_handle) cached_handle = dest; - /* the cur_handle pointer might need updating */ - if (src == cur_handle) - cur_handle = dest; - if (overlap > 0) { size_t first_part = size_to_move - overlap; memmove(dest, src, first_part); memmove(buffer, (const char *)src + first_part, overlap); } else { + printf("Moving %d into %d from %d for delta %d (originally %d) in buffer %d size %d\n", size_to_move, dest, src, final_delta, *delta, buffer, buffer_len); memmove(dest, src, size_to_move); } @@ -879,13 +950,14 @@ MAIN BUFFERING API CALLS ======================== -bufopen : Request the opening of a new handle for a file -bufalloc : Open a new handle for data other than a file. -bufclose : Close an open handle -bufseek : Set the read pointer in a handle -bufadvance : Move the read pointer in a handle -bufread : Copy data from a handle into a given buffer -bufgetdata : Give a pointer to the handle's data +bufopen : Request the opening of a new handle for a file +bufopensecondary : Open a file in the space not occupied by the normal ones +bufalloc : Open a new handle for data other than a file. +bufclose : Close an open handle +bufseek : Set the read pointer in a handle +bufadvance : Move the read pointer in a handle +bufread : Copy data from a handle into a given buffer +bufgetdata : Give a pointer to the handle's data These functions are exported, to allow interaction with the buffer. They take care of the content of the structs, and rely on the linked list @@ -914,15 +986,13 @@ h->filesize = sizeof(struct mp3entry); h->filerem = sizeof(struct mp3entry); h->offset = 0; - h->data = buf_widx; - h->ridx = buf_widx; - h->widx = buf_widx; h->available = 0; h->type = type; strncpy(h->path, file, MAX_PATH); - buf_widx += sizeof(struct mp3entry); /* safe because the handle - can't wrap */ + /*if (h->data == buf_widx) + buf_widx += sizeof(struct mp3entry);*/ /* safe because the handle + can't wrap */ /* Inform the buffering thread that we added a handle */ LOGFQUEUE("buffering > Q_HANDLE_ADDED %d", h->id); @@ -954,9 +1024,6 @@ strncpy(h->path, file, MAX_PATH); h->offset = adjusted_offset; - h->ridx = buf_widx; - h->widx = buf_widx; - h->data = buf_widx; h->available = 0; h->filerem = 0; h->type = type; @@ -978,8 +1045,9 @@ h->filerem = 0; h->filesize = rc; h->available = rc; - h->widx = buf_widx + rc; /* safe because the data doesn't wrap */ - buf_widx += rc; /* safe too */ + h->widx += rc; /* safe because the data doesn't wrap */ + /*if (h->data == buf_widx) + buf_widx += rc;*/ /* safe too */ } else #endif @@ -987,7 +1055,6 @@ h->filerem = size - adjusted_offset; h->filesize = size; h->available = 0; - h->widx = buf_widx; } if (type == TYPE_CUESHEET) { @@ -1009,6 +1076,51 @@ return h->id; } +/* Reserve space in the buffer for another 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 current buffer layout + is bad feng shui, otherwise the handle for the file in the buffer +*/ +int bufopensecondary(const char *file, size_t offset, enum data_type type) { + int fd = open(file, O_RDONLY); + if (fd < 0) + return ERR_FILE_ERROR; + + size_t size = filesize(fd); + size_t adjusted_offset = offset; + if (adjusted_offset > size) + adjusted_offset = 0; + + struct memory_handle *h = add_handle(size-adjusted_offset, true, false); + if (!h) + { + DEBUGF("bufopen: failed to add secondary handle\n"); + close(fd); + return ERR_BUFFER_FULL; + } + + strncpy(h->path, file, MAX_PATH); + h->offset = adjusted_offset; + h->available = 0; + h->filerem = 0; + h->type = type; + h->filerem = size - adjusted_offset; + h->filesize = size; + h->available = 0; + + h->fd = -1; + close(fd); + + /* Inform the buffering thread that we added a handle */ + LOGFQUEUE("buffering > Q_HANDLE_ADDED %d", h->id); + queue_post(&buffering_queue, Q_HANDLE_ADDED, h->id); + + logf("bufopen: new secondary hdl %d", h->id); + return h->id; +} + /* 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. @@ -1026,10 +1138,10 @@ if (src) { if (type == TYPE_ID3 && size == sizeof(struct mp3entry)) { /* specially take care of struct mp3entry */ - copy_mp3entry((struct mp3entry *)&buffer[buf_widx], + copy_mp3entry((struct mp3entry *)(buffer+h->data), (const struct mp3entry *)src); } else { - memcpy(&buffer[buf_widx], src, size); + memcpy(buffer+h->data, src, size); } } @@ -1038,13 +1150,11 @@ h->filesize = size; h->filerem = 0; h->offset = 0; - h->ridx = buf_widx; - h->widx = buf_widx + size; /* this is safe because the data doesn't wrap */ - h->data = buf_widx; + h->widx += size; /* this is safe because the data doesn't wrap */ h->available = size; h->type = type; - buf_widx += size; /* safe too */ + /*buf_widx += size;*/ /* safe too */ logf("bufalloc: new hdl %d", h->id); return h->id; @@ -1093,7 +1203,8 @@ if (!h) return ERR_HANDLE_NOT_FOUND; - size_t newpos = h->offset + RINGBUF_SUB(h->ridx, h->data) + offset; + size_t newpos; + newpos = h->offset + RINGBUF_SUB(h->ridx, h->data) + offset; return bufseek(handle_id, newpos); } Index: apps/buffering.h =================================================================== --- apps/buffering.h (revision 21547) +++ apps/buffering.h (working copy) @@ -83,8 +83,8 @@ ssize_t bufgetdata(int handle_id, size_t size, void **data); ssize_t bufgettail(int handle_id, size_t size, void **data); ssize_t bufcuttail(int handle_id, size_t size); +int bufopensecondary(const char *file, size_t offset, enum data_type type); - /*************************************************************************** * SECONDARY FUNCTIONS * =================== Index: apps/codecs/libwavpack/words.c =================================================================== --- apps/codecs/libwavpack/words.c (revision 21547) +++ 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 21547) +++ 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 21547) +++ 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 21547) +++ 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 21547) +++ 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,207 @@ } } + /////////////////// handle hybrid lossless stereo data //////////////////// + + else if (wpc->wvc_flag && !(flags & MONO_DATA)) { + 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 21547) +++ 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_secondarybuf (ci->id3->sechandle, 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->sechandle) { + 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);