Index: apps/metadata/wavpack.c =================================================================== --- apps/metadata/wavpack.c (revision 21780) +++ 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,22 @@ 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); + printf ("Attempting to open correction file %s...\n", id3->secpath); + int fd = open(id3->secpath, O_RDONLY); + if (fd < 0) { + printf("... failed.\n"); + id3->secpath[0] = '\0'; + } else { + id3->secfilesize = filesize(fd); + close(fd); + } + } else { + id3->secpath[0] = '\0'; + } + return true; } Index: apps/metadata.c =================================================================== --- apps/metadata.c (revision 21780) +++ 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 21780) +++ apps/metadata.h (working copy) @@ -164,6 +164,7 @@ struct mp3entry { char path[MAX_PATH]; + char secpath[MAX_PATH]; char* title; char* artist; char* album; @@ -191,6 +192,7 @@ avoid gaps between tracks. */ unsigned long vbr_header_pos; unsigned long filesize; /* without headers; in bytes */ + unsigned long secfilesize; /* without headers; in bytes */ unsigned long length; /* song length in ms */ unsigned long elapsed; /* ms played */ @@ -243,6 +245,7 @@ /* Musicbrainz Track ID */ char* mb_track_id; + }; unsigned int probe_file_format(const char *filename); Index: apps/playback.c =================================================================== --- apps/playback.c (revision 21780) +++ apps/playback.c (working copy) @@ -1785,6 +1785,66 @@ return true; } +bool buffer_wavpack(struct memory_handle *h, char* buffer, size_t avail, size_t *nread) { + *nread = 0; + size_t realwidx = h->widx; + while (avail > 0 && *nread < BUFFERING_DEFAULT_FILECHUNK) { + int rd; + size_t amt = h->callbackflags[1]; + if (amt == 0) + amt = BUFFERING_DEFAULT_FILECHUNK; + if (amt > avail) + amt = avail; + if (amt > BUFFERING_DEFAULT_FILECHUNK-*nread) + amt = BUFFERING_DEFAULT_FILECHUNK-*nread; + bool ff; + if ((h->callbackflags[0] || h->secfilerem == 0) && h->filerem > 0) { + ff = true; + rd = read(h->fd, &buffer[realwidx], amt); + h->filerem -= rd; + h->callbackflags[1] -= rd; + if (h->callbackflags[1] == 0 || h->filerem == 0) { + h->callbackflags[0] = false; + h->callbackflags[1] = BUFFERING_DEFAULT_FILECHUNK; + } + } else if (h->secfilerem > 0) { + ff = false; + rd = read(h->secfd, &buffer[realwidx], amt); + h->secfilerem -= rd; + h->callbackflags[1] -= rd; + if (h->callbackflags[1] == 0 || h->secfilerem == 0) { + h->callbackflags[0] = true; + h->callbackflags[1] = BUFFERING_DEFAULT_FILECHUNK; + } + } else { + return false; + } + *nread += rd; + if (h->secfilerem == 0 && h->filerem == 0) { + return false; + } + if (rd == 0) + return false; + //printf("Read %d/%d %s (%d remaining) (%d in type %d)\n", rd, amt, ff?"primary":"secondary", ff?h->filerem:h->secfilerem, h->callbackflags[1], h->callbackflags[0]); + realwidx += rd; + avail -= rd; + } + return true; +} + +#define RINGBUF_ADD(p,v) (((p)+(v))=v) ? (p)-(v) : (p)+buffer_len-(v)) +#define RINGBUF_ADD_CROSS(p1,v,p2) \ +((p1ridx, h->data); + size_t off = newpos-h->offset-h->secoffset-avail; + h->ridx = RINGBUF_ADD(h->ridx, off); + return false; +} + /* Second part of the track loading: We now have the metadata available, so we can load the codec, the album art and finally the audio data. This is called on the audio thread after the buffering thread calls the @@ -1922,7 +1982,16 @@ 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 = bufopen(track_id3->path, file_offset, type); + } else { + logf ("Opening playback secondary %s\n", track_id3->secpath); + switch (track_id3->codectype) { + case AFMT_WAVPACK: + tracks[track_widx].audio_hid = + bufopentwo(track_id3->path, file_offset, type, track_id3->secpath, 0, buffer_wavpack, seek_wavpack); + } + } /* No space left, not an error */ if (tracks[track_widx].audio_hid == ERR_BUFFER_FULL) Index: apps/buffering.c =================================================================== --- apps/buffering.c (revision 21780) +++ apps/buffering.c (working copy) @@ -85,8 +85,6 @@ /* default point to start buffer refill */ #define BUFFERING_DEFAULT_WATERMARK (1024*128) -/* amount of data to read in one read() call */ -#define BUFFERING_DEFAULT_FILECHUNK (1024*32) #define BUF_HANDLE_MASK 0x7FFFFFFF @@ -102,23 +100,6 @@ /* Bytes available in the buffer */ #define BUF_USED RINGBUF_SUB(buf_widx, buf_ridx) -/* assert(sizeof(struct memory_handle)%4==0) */ -struct memory_handle { - int id; /* A unique ID for the handle */ - enum data_type type; /* Type of data buffered with this handle */ - char path[MAX_PATH]; /* Path if data originated in a file */ - int fd; /* File descriptor to path (-1 if closed) */ - size_t data; /* Start index of the handle's data buffer */ - 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 available; /* Available bytes to read from buffer */ - size_t offset; /* Offset at which we started reading the file */ - struct memory_handle *next; -}; -/* invariant: filesize == offset + available + filerem */ - static char *buffer; static char *guard_buffer; @@ -232,11 +213,11 @@ mutex_lock(&llist_mutex); mutex_lock(&llist_mod_mutex); - if (cur_handle && cur_handle->filerem > 0) { + if (cur_handle && (cur_handle->filerem > 0 || cur_handle->secfilerem > 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); + size_t req = cur_handle->filerem + cur_handle->secfilerem + sizeof(struct memory_handle); if (RINGBUF_ADD_CROSS(cur_handle->widx, req, buf_ridx) >= 0) { /* Not enough space */ mutex_unlock(&llist_mod_mutex); @@ -244,7 +225,7 @@ return NULL; } else { /* Allocate the remainder of the space for the current handle */ - buf_widx = RINGBUF_ADD(cur_handle->widx, cur_handle->filerem); + buf_widx = RINGBUF_ADD(cur_handle->widx, cur_handle->filerem+cur_handle->secfilerem); } } @@ -287,6 +268,14 @@ /* 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->callback = NULL; + new_handle->seekcallback = NULL; + new_handle->callbackflags[0] = 1; + new_handle->callbackflags[1] = BUFFERING_DEFAULT_FILECHUNK; + new_handle->secfilerem = 0; + new_handle->secfd = -1; + new_handle->secoffset = 0; + new_handle->secfilesize = 0; num_handles++; if (!first_handle) @@ -532,7 +521,7 @@ while (m) { buffered += m->available; wasted += RINGBUF_SUB(m->ridx, m->data); - remaining += m->filerem; + remaining += m->filerem+m->secfilerem; if (m->id == base_handle_id) is_useful = true; @@ -566,12 +555,12 @@ if (!h) return true; - if (h->filerem == 0) { + if (h->filerem == 0 && h->secfilerem == 0) { /* nothing left to buffer */ return true; } - if (h->fd < 0) /* file closed, reopen */ + if (h->filerem > 0 && h->fd < 0) /* file closed, reopen */ { if (*h->path) h->fd = open(h->path, O_RDONLY); @@ -581,6 +570,7 @@ /* could not open the file, truncate it where it is */ h->filesize -= h->filerem; h->filerem = 0; + logf("TRUNCATING FILE!!!!!\n"); return true; } @@ -588,6 +578,23 @@ lseek(h->fd, h->offset, SEEK_SET); } + if (h->secfilerem > 0 && h->secfd < 0) + { + if (h->secpath[0] != '\0') + h->secfd = open(h->secpath, O_RDONLY); + + if (h->secfd < 0) + { + h->secfilesize -= h->secfilerem; + h->secfilerem = 0; + logf("TRUNCATING SECONDARY FILE %s!!!!\n", h->secpath); + return true; + } + + if (h->secoffset) + lseek(h->secfd, h->secoffset, SEEK_SET); + } + trigger_cpu_boost(); if (h->type == TYPE_ID3) @@ -606,54 +613,71 @@ return true; } - while (h->filerem > 0) + while (h->filerem > 0 || h->secfilerem > 0) { - /* max amount to copy */ - size_t copy_n = MIN( MIN(h->filerem, BUFFERING_DEFAULT_FILECHUNK), - buffer_len - h->widx); + bool ret; + if (h->callback) + { + int read; + size_t copy_n = BUFFERING_DEFAULT_FILECHUNK; + copy_n = MIN(copy_n, buffer_len-h->widx); + size_t overlap = RINGBUF_ADD_CROSS(h->widx, copy_n, (unsigned)((void*)(h->next)-(void*)(buffer))); + if (overlap > 0) + copy_n -= overlap; + ret = h->callback(h, buffer, copy_n, &read); + h->widx = RINGBUF_ADD(h->widx, read); + if (h == cur_handle) + buf_widx = h->widx; + h->available += read; + } else { + /* 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) - return false; + /* stop copying if it would overwrite the reading position */ + if (RINGBUF_ADD_CROSS(h->widx, copy_n, buf_ridx) >= 0) + return false; - /* This would read into the next handle, this is broken */ - if (h->next && RINGBUF_ADD_CROSS(h->widx, copy_n, - (unsigned)((void *)h->next - (void *)buffer)) > 0) { - /* Try to recover by truncating this file */ - copy_n = RINGBUF_ADD_CROSS(h->widx, copy_n, + /* 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; - } + 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 */ - int rc = read(h->fd, &buffer[h->widx], copy_n); + /* rc is the actual amount read */ + int rc = read(h->fd, &buffer[h->widx], copy_n); - if (rc < 0) - { - /* Some kind of filesystem error, maybe recoverable if not codec */ - if (h->type == TYPE_CODEC) { - logf("Partial codec"); + if (rc < 0) + { + /* Some kind of filesystem error, maybe recoverable if not codec */ + if (h->type == TYPE_CODEC) { + logf("Partial codec"); + break; + } + + DEBUGF("File ended %ld bytes early\n", (long)h->filerem); + h->filesize -= h->filerem; + h->filerem = 0; break; } - DEBUGF("File ended %ld bytes early\n", (long)h->filerem); - h->filesize -= h->filerem; - h->filerem = 0; - break; + /* Advance buffer */ + h->widx = RINGBUF_ADD(h->widx, rc); + h->filerem -= rc; + h->available += rc; } - /* Advance buffer */ - h->widx = RINGBUF_ADD(h->widx, rc); if (h == cur_handle) buf_widx = h->widx; - h->available += rc; - h->filerem -= rc; /* If this is a large file, see if we need to break or give the codec * more time */ @@ -671,10 +695,16 @@ break; } - if (h->filerem == 0) { + if (h->filerem == 0 && h->secfilerem == 0) { /* finished buffering the file */ - close(h->fd); - h->fd = -1; + if (h->fd >= 0) { + close(h->fd); + h->fd = -1; + } + if (h->secfd >= 0) { + close(h->secfd); + h->secfd = -1; + } send_event(BUFFER_EVENT_FINISHED, &h->id); } @@ -696,10 +726,15 @@ buf_widx = h->widx; h->available = 0; h->filerem = h->filesize - h->offset; + h->secfilerem = h->secfilesize - h->secoffset; if (h->fd >= 0) { lseek(h->fd, h->offset, SEEK_SET); } + + if (h->secfd >= 0) { + lseek(h->secfd, h->secoffset, SEEK_SET); + } } /* Seek to a nonbuffered part of a handle by rebuffering the data. */ @@ -711,7 +746,7 @@ /* 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) + if (newpos > h->offset+h->secoffset && newpos - h->offset - h->secoffset < BUFFERING_DEFAULT_FILECHUNK) { LOGFQUEUE("buffering >| Q_BUFFER_HANDLE %d", handle_id); queue_send(&buffering_queue, Q_BUFFER_HANDLE, handle_id); @@ -719,14 +754,21 @@ return; } - h->offset = newpos; + if (newpos < h->filesize) + { + h->offset = newpos; + h->secoffset = 0; + } else { + h->offset = h->filesize; + h->secoffset = newpos-h->filesize; + } /* Reset the handle to its new offset */ LOGFQUEUE("buffering >| Q_RESET_HANDLE %d", handle_id); queue_send(&buffering_queue, Q_RESET_HANDLE, handle_id); size_t next = (unsigned)((void *)h->next - (void *)buffer); - if (RINGBUF_SUB(next, h->data) < h->filesize - newpos) + if (RINGBUF_SUB(next, h->data) < h->filesize + h->secfilesize - newpos) { /* There isn't enough space to rebuffer all of the track from its new offset, so we ask the user to free some */ @@ -752,6 +794,11 @@ h->fd = -1; } + if (h->secfd >= 0) { + close(h->secfd); + h->secfd = -1; + } + /* rm_handle returns true unless the handle somehow persists after exit */ return rm_handle(h); } @@ -818,7 +865,7 @@ shrink_handle(first_handle); m = first_handle; while (queue_empty(&buffering_queue) && m) { - if (m->filerem > 0) { + if (m->filerem > 0 || m->secfilerem > 0) { if (!buffer_handle(m->id)) { m = NULL; break; @@ -892,15 +939,7 @@ 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) +static int bufopenpre(const char *file, size_t offset, enum data_type type, int *fd) { if (type == TYPE_ID3) { @@ -911,6 +950,7 @@ return ERR_BUFFER_FULL; h->fd = -1; + h->secfd = -1; h->filesize = sizeof(struct mp3entry); h->filerem = sizeof(struct mp3entry); h->offset = 0; @@ -933,11 +973,11 @@ /* Other cases: there is a little more work. */ - int fd = open(file, O_RDONLY); - if (fd < 0) + *fd = open(file, O_RDONLY); + if (*fd < 0) return ERR_FILE_ERROR; - size_t size = filesize(fd); + size_t size = filesize(*fd); bool can_wrap = type==TYPE_PACKET_AUDIO || type==TYPE_CODEC; size_t adjusted_offset = offset; @@ -948,7 +988,7 @@ if (!h) { DEBUGF("bufopen: failed to add handle\n"); - close(fd); + close(*fd); return ERR_BUFFER_FULL; } @@ -967,12 +1007,12 @@ /* Bitmap file: we load the data instead of the file */ int rc; mutex_lock(&llist_mod_mutex); /* Lock because load_bitmap yields */ - rc = load_image(fd, file); + rc = load_image(*fd, file); mutex_unlock(&llist_mod_mutex); if (rc <= 0) { rm_handle(h); - close(fd); + close(*fd); return ERR_FILE_ERROR; } h->filerem = 0; @@ -991,11 +1031,34 @@ } if (type == TYPE_CUESHEET) { - h->fd = fd; + h->fd = *fd; /* Immediately start buffering those */ LOGFQUEUE("buffering >| Q_BUFFER_HANDLE %d", h->id); queue_send(&buffering_queue, Q_BUFFER_HANDLE, h->id); - } else { + } + + 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) +{ + int fd; + + int id = bufopenpre(file, offset, type, &fd); + + if (id < 0) + return id; + + struct memory_handle *h = find_handle(id); + + if (type != TYPE_CUESHEET) { /* Other types will get buffered in the course of normal operations */ h->fd = -1; close(fd); @@ -1009,6 +1072,53 @@ return h->id; } +int bufopentwo(const char *fileone, size_t offsetone, enum data_type type, + const char *filetwo, size_t offsettwo, + bool (*callback)(struct memory_handle*, char*, size_t, size_t*), + bool (*seekcallback)(struct memory_handle*, size_t, size_t)) +{ + int fd; + + int id = bufopenpre(fileone, offsetone, type, &fd); + + if (id < 0) + return id; + + struct memory_handle *h = find_handle(id); + + h->fd = -1; + close(fd); + + fd = open(filetwo, O_RDONLY); + if (fd < 0) + return ERR_FILE_ERROR; + + size_t size = filesize(fd); + + size_t adjusted_offset = offsettwo; + if (adjusted_offset > size) + adjusted_offset = 0; + + h->secoffset = adjusted_offset; + h->secfilerem = size-adjusted_offset; + h->secfilesize = size; + h->callback = callback; + h->seekcallback = seekcallback; + strncpy(h->secpath, filetwo, MAX_PATH); + + close(fd); + h->secfd = -1; + + logf("Second file handle %d opened with offset %d size %d remaining %d\n", h->id, h->secoffset, h->secfilesize, h->secfilerem); + + LOGFQUEUE("buffering > Q_HANDLE_ADDED %d", h->id); + queue_post(&buffering_queue, Q_HANDLE_ADDED, h->id); + + logf("bufopentwo: new 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. @@ -1071,17 +1181,22 @@ if (!h) return ERR_HANDLE_NOT_FOUND; - if (newpos > h->filesize) { + if (newpos > h->filesize+h->secfilesize) { /* access beyond the end of the file */ return ERR_INVALID_VALUE; } - else if (newpos < h->offset || h->offset + h->available < newpos) { + + if (h->seekcallback) { + if (h->seekcallback(h, newpos, buffer_len)) { + LOGFQUEUE("buffering >| Q_BUFFER_HANDLE %d", handle_id); + queue_send(&buffering_queue, Q_BUFFER_HANDLE, handle_id); + } + } else if (newpos < h->offset+h->secoffset || h->offset + h->secoffset + h->available < newpos) { /* access before or after buffered data. A rebuffer is needed. */ rebuffer_handle(handle_id, newpos); + } else { + h->ridx = RINGBUF_ADD(h->data, newpos - h->offset - h->secoffset); } - else { - h->ridx = RINGBUF_ADD(h->data, newpos - h->offset); - } return 0; } @@ -1093,7 +1208,7 @@ if (!h) return ERR_HANDLE_NOT_FOUND; - size_t newpos = h->offset + RINGBUF_SUB(h->ridx, h->data) + offset; + size_t newpos = h->offset + h->secoffset + RINGBUF_SUB(h->ridx, h->data) + offset; return bufseek(handle_id, newpos); } @@ -1110,15 +1225,15 @@ size_t avail = RINGBUF_SUB(h->widx, h->ridx); - if (avail == 0 && h->filerem == 0) + if (avail == 0 && h->filerem == 0 && h->secfilerem == 0) { /* File is finished reading */ *size = 0; return h; } - if (*size == 0 || *size > avail + h->filerem) - *size = avail + h->filerem; + if (*size == 0 || (*size > avail + h->filerem + h->secfilerem)) + *size = avail + h->filerem + h->secfilerem; if (guardbuf_limit && h->type == TYPE_PACKET_AUDIO && *size > GUARD_BUFSIZE) { @@ -1129,7 +1244,7 @@ /* this ensures *size <= buffer_len - h->ridx + GUARD_BUFSIZE */ } - if (h->filerem > 0 && avail < *size) + if ((h->filerem > 0 || h->secfilerem > 0) && avail < *size) { /* Data isn't ready. Request buffering */ buf_request_buffer_handle(handle_id); @@ -1144,7 +1259,7 @@ return NULL; avail = RINGBUF_SUB(h->widx, h->ridx); } - while (h->filerem > 0 && avail < *size); + while ((h->filerem > 0 || h->secfilerem > 0) && avail < *size); } *size = MIN(*size,avail); @@ -1224,7 +1339,7 @@ if (!h) return ERR_HANDLE_NOT_FOUND; - if (h->filerem) + if (h->filerem || h->secfilerem) return ERR_HANDLE_NOT_DONE; /* We don't support tail requests of > guardbuf_size, for simplicity */ @@ -1253,7 +1368,7 @@ if (!h) return ERR_HANDLE_NOT_FOUND; - if (h->filerem) + if (h->filerem || h->secfilerem) return ERR_HANDLE_NOT_DONE; if (h->available < adjusted_size) Index: apps/buffering.h =================================================================== --- apps/buffering.h (revision 21780) +++ apps/buffering.h (working copy) @@ -25,6 +25,7 @@ #include #include #include "appevents.h" +#include "file.h" enum data_type { @@ -38,6 +39,9 @@ TYPE_UNKNOWN, }; +/* Amount of data to be retrieved in one read() call */ +#define BUFFERING_DEFAULT_FILECHUNK (1024*32) + /* Error return values */ #define ERR_HANDLE_NOT_FOUND -1 #define ERR_BUFFER_FULL -2 @@ -45,7 +49,35 @@ #define ERR_FILE_ERROR -4 #define ERR_HANDLE_NOT_DONE -5 +/* assert(sizeof(struct memory_handle)%4==0) */ +struct memory_handle { + int id; /* A unique ID for the handle */ + enum data_type type; /* Type of data buffered with this handle */ + char path[MAX_PATH]; /* Path if data originated in a file */ + int fd; /* File descriptor to path (-1 if closed) */ + size_t data; /* Start index of the handle's data buffer */ + 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 available; /* Available bytes to read from buffer */ + size_t offset; /* Offset at which we started reading the file */ + size_t secoffset; + struct memory_handle *next; + bool (*callback)(struct memory_handle*, char*, size_t, size_t*); + /* Callback for codec-specified buffering methodology */ + bool (*seekcallback)(struct memory_handle*, size_t, size_t); + /* Callback for codec-specified buffer seeking methodology */ + size_t callbackflags[2]; + /* Assorted information for use by custom buffering */ + char secpath[MAX_PATH]; + int secfd; + size_t secfilesize; + size_t secfilerem; +}; +/* invariant: filesize == offset + available + filerem */ + /* Initialise the buffering subsystem */ void buffering_init(void); @@ -75,6 +107,10 @@ #define BUF_MAX_HANDLES 256 int bufopen(const char *file, size_t offset, enum data_type type); +int bufopentwo(const char *fileone, size_t offsetone, enum data_type type, + const char *filetwo, size_t offsettwo, + bool (*callback)(struct memory_handle*, char*, size_t, size_t*), + bool (*seekcallback)(struct memory_handle*, size_t, size_t)); 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/vorbis.c =================================================================== --- apps/codecs/vorbis.c (revision 21780) +++ apps/codecs/vorbis.c (working copy) @@ -31,7 +31,7 @@ #endif /* Some standard functions and variables needed by Tremor */ - + static size_t read_handler(void *ptr, size_t size, size_t nmemb, void *datasource) { (void)datasource; Index: apps/codecs/libwavpack/words.c =================================================================== --- apps/codecs/libwavpack/words.c (revision 21780) +++ 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 21780) +++ 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 21780) +++ 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 21780) +++ 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 21780) +++ 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; + printf("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 21780) +++ apps/codecs/wavpack.c (working copy) @@ -21,20 +21,123 @@ #include "codeclib.h" #include "libwavpack/wavpack.h" +#include "logf.h" +#include "buffering.h" CODEC_HEADER +#define RINGBUF_ADD(p,v,len) (((p)+(v))<(len) ? (p)+(v) : (p)+(v)-(len)) +#define RINGBUF_SUB(p,v,len) ((p>=v) ? (p)-(v) : (p)+(len)-(v)) +#define RINGBUF_ADD_CROSS(p1,v,p2,len) \ +((p1 ci->id3->filesize) + bytes = ci->id3->filesize-readctrs[0]; + else if (bufno == 1 && bytes+readctrs[1] > ci->id3->secfilesize) + bytes = ci->id3->secfilesize-readctrs[1]; + ci->read_filebuf(&blockbufs[bufno][widxs[bufno]], bytes); + widxs[bufno] += bytes; + readctrs[bufno] += bytes; + return bytes; +} + +static void ringbuf_take (char *buf, int bufno, int32_t bytes) { + memcpy(buf, &blockbufs[bufno][ridxs[bufno]], bytes); + ridxs[bufno] += bytes; +} + +static int32_t generic_read_callback (void *buffer, int32_t bytes, int bufno, bool flag) { + char *realbuf = (char*)buffer; + int32_t retval = 0; + /*printf("Generic read %d for %ld\n", bufno, bytes);*/ + while (retval < bytes) { + int32_t bavail = ringbuf_avail(bufno); + if (bavail > 0) { + if (bavail > bytes-retval) + bavail = bytes-retval; + ringbuf_take (realbuf, bufno, bavail); + realbuf += bavail; + retval += bavail; + } + if (bytes-retval > 0 && readstateprimary != flag && !exhausted[!bufno]) { + int32_t ret = ringbuf_fill(!bufno, staterem); + if (ret == -1) { + logf("BUFFER OVERFLOW %d!!!!!!\n", !bufno); + exhausted[!bufno] = true; + return retval; + } else { + if (ret < staterem) + exhausted[!bufno] = true; + readstateprimary = !readstateprimary; + staterem = BUFFERING_DEFAULT_FILECHUNK; + } + } + if (bytes-retval > 0) { + /*printf("Finishing with %zd bytes from real read for %d\n", bytes, bufno);*/ + int32_t rb = bytes-retval; + if (rb > staterem && !exhausted[!bufno]) + rb = staterem; + if (bufno == 0 && rb+readctrs[0] > ci->id3->filesize) { + rb = ci->id3->filesize-readctrs[0]; + exhausted[0] = true; + } else if (bufno == 1 && rb+readctrs[1] > ci->id3->secfilesize) { + rb = ci->id3->secfilesize-readctrs[1]; + exhausted[1] = true; + } + int32_t nr = ci->read_filebuf ((void*)realbuf, rb); + readctrs[bufno] += nr; + retval += nr; + realbuf += nr; + staterem -= nr; + if (staterem == 0) { + staterem = BUFFERING_DEFAULT_FILECHUNK; + readstateprimary = !readstateprimary; + } + if (nr < rb) { + exhausted[bufno] = true; + break; + } + } + } + return retval; +} + static int32_t read_callback (void *buffer, int32_t bytes) { - int32_t retval = ci->read_filebuf (buffer, bytes); + int32_t ret = generic_read_callback (buffer, bytes, 0, true); ci->id3->offset = ci->curpos; - return retval; + /*if (ret < bytes) + logf("RETURNING ONLY %d instead of %d for primary!!!!\n", ret, bytes);*/ + return ret; } +static int32_t read_correction_callback (void *buffer, int32_t bytes) { + int32_t ret = generic_read_callback (buffer, bytes, 1, false); + /*if (ret < bytes) + printf("RETURNING ONLY %d instead of %d for secondary!!!!\n", ret, bytes);*/ + return ret; +} + /* this is the codec entry point */ enum codec_status codec_main(void) { @@ -64,6 +167,13 @@ goto done; } + if (ci->id3->secpath[0] != '\0') { + logf("Correction file detected, opening...\n"); + if (!WavpackOpenCorrectionFileInput (wpc, read_correction_callback, error)) { + logf("correction file open failed: %s\n", error); + } + } + ci->configure(DSP_SWITCH_FREQUENCY, WavpackGetSampleRate (wpc)); codec_set_replaygain(ci->id3); bps = WavpackGetBytesPerSample (wpc);