diff --git a/src/common/wavpack_common.cpp b/src/common/wavpack_common.cpp index c3ea7dd24..d2f70886b 100644 --- a/src/common/wavpack_common.cpp +++ b/src/common/wavpack_common.cpp @@ -93,52 +93,66 @@ read_next_header(mm_io_c *mm_io, int32_t wv_parse_frame(mm_io_c *mm_io, wavpack_header_t &wphdr, - wavpack_meta_t &meta) { + wavpack_meta_t &meta, + bool read_blocked_frames, + bool keep_initial_position) { uint32_t bcount; + uint64_t first_data_pos = mm_io->getFilePointer(); + bool can_leave = !read_blocked_frames; - // read next WavPack header - bcount = read_next_header(mm_io, &wphdr); + do { + // read next WavPack header + bcount = read_next_header(mm_io, &wphdr); - if (bcount == (uint32_t) -1) { - return -1; - } - - // if there's audio samples in there... - if (wphdr.block_samples) { - meta.sample_rate = (wphdr.flags & WV_SRATE_MASK) >> WV_SRATE_LSB; - - if (meta.sample_rate == 15) - mxwarn("wavpack_reader: unknown sample rate!\n"); - else - meta.sample_rate = sample_rates[meta.sample_rate]; - - if (wphdr.flags & WV_INT32_DATA || wphdr.flags & WV_FLOAT_DATA) - meta.bits_per_sample = 32; - else - meta.bits_per_sample = ((wphdr.flags & WV_BYTES_STORED) + 1) << 3; - - meta.samples_per_block = wphdr.block_samples; - - if (wphdr.flags & WV_INITIAL_BLOCK) { - meta.channel_count = (wphdr.flags & WV_MONO_FLAG) ? 1 : 2; - if (wphdr.flags & WV_FINAL_BLOCK) { - mxverb(3, "wavpack_reader: %s block: %s, %d bytes\n", - (wphdr.flags & WV_MONO_FLAG) ? "mono" : "stereo", - (wphdr.flags & WV_HYBRID_FLAG) ? "hybrid" : "lossless", - wphdr.ck_size + 8); - } - } else { - meta.channel_count += (wphdr.flags & WV_MONO_FLAG) ? 1 : 2; - - if (wphdr.flags & WV_FINAL_BLOCK) { - mxverb(2, "wavpack_reader: %d chans: %s, %d bytes\n", - meta.channel_count, - (wphdr.flags & WV_HYBRID_FLAG) ? "hybrid" : "lossless", - wphdr.ck_size + 8); - } + if (bcount == (uint32_t) -1) { + return -1; } - } else - mxwarn("wavpack_reader: non-audio block found\n"); + + // if there's audio samples in there... + if (wphdr.block_samples) { + meta.channel_count += (wphdr.flags & WV_MONO_FLAG) ? 1 : 2; + if (wphdr.flags & WV_INITIAL_BLOCK) { + meta.sample_rate = (wphdr.flags & WV_SRATE_MASK) >> WV_SRATE_LSB; + + if (meta.sample_rate == 15) + mxwarn("wavpack_reader: unknown sample rate!\n"); + else + meta.sample_rate = sample_rates[meta.sample_rate]; + + if (wphdr.flags & WV_INT32_DATA || wphdr.flags & WV_FLOAT_DATA) + meta.bits_per_sample = 32; + else + meta.bits_per_sample = ((wphdr.flags & WV_BYTES_STORED) + 1) << 3; + + meta.samples_per_block = wphdr.block_samples; + + first_data_pos = mm_io->getFilePointer(); + meta.channel_count = (wphdr.flags & WV_MONO_FLAG) ? 1 : 2; + if (wphdr.flags & WV_FINAL_BLOCK) { + can_leave = true; + mxverb(3, "wavpack_reader: %s block: %s, %d bytes\n", + (wphdr.flags & WV_MONO_FLAG) ? "mono" : "stereo", + (wphdr.flags & WV_HYBRID_FLAG) ? "hybrid" : "lossless", + wphdr.ck_size + 8); + } + } else { + if (wphdr.flags & WV_FINAL_BLOCK) { + can_leave = true; + mxverb(2, "wavpack_reader: %d chans, mode: %s, %d bytes\n", + meta.channel_count, + (wphdr.flags & WV_HYBRID_FLAG) ? "hybrid" : "lossless", + wphdr.ck_size + 8); + } + } + } else + mxwarn("wavpack_reader: non-audio block found\n"); + if (!can_leave) { + mm_io->skip(wphdr.ck_size - sizeof(wavpack_header_t) + 8); + } + } while (!can_leave); + + if (keep_initial_position) + mm_io->setFilePointer(first_data_pos); return wphdr.ck_size - sizeof(wavpack_header_t) + 8; } diff --git a/src/common/wavpack_common.h b/src/common/wavpack_common.h index f58528b74..a91e04315 100644 --- a/src/common/wavpack_common.h +++ b/src/common/wavpack_common.h @@ -83,6 +83,8 @@ typedef struct { // encountered int32_t MTX_DLL_API wv_parse_frame(mm_io_c *mm_io, wavpack_header_t &header, - wavpack_meta_t &meta); + wavpack_meta_t &meta, + bool read_blocked_frames, + bool keep_initial_position); #endif // __WAVPACK_COMMON_H diff --git a/src/input/r_wavpack.cpp b/src/input/r_wavpack.cpp index 5efa34590..a4091c0ec 100644 --- a/src/input/r_wavpack.cpp +++ b/src/input/r_wavpack.cpp @@ -56,7 +56,7 @@ wavpack_reader_c::wavpack_reader_c(track_info_c *nti) mm_io = new mm_file_io_c(ti->fname); size = mm_io->get_size(); - packet_size = wv_parse_frame(mm_io, header, meta); + packet_size = wv_parse_frame(mm_io, header, meta, true, true); if (packet_size < 0) mxerror(FMT_FN "The file header was not read correctly.\n", ti->fname.c_str()); @@ -73,11 +73,12 @@ wavpack_reader_c::wavpack_reader_c(track_info_c *nti) try { if (header.flags & WV_HYBRID_FLAG) { mm_io_correc = new mm_file_io_c(ti->fname + "c"); - packet_size = wv_parse_frame(mm_io_correc, header_correc, meta_correc); + packet_size = wv_parse_frame(mm_io_correc, header_correc, meta_correc, + true, true); if (packet_size < 0) mxerror(FMT_FN "The correction file header was not read correctly.\n", ti->fname.c_str()); - mm_io_correc->setFilePointer(mm_io->getFilePointer() - + mm_io_correc->setFilePointer(mm_io_correc->getFilePointer() - sizeof(wavpack_header_t), seek_beginning); meta.has_correction = true; } @@ -112,13 +113,30 @@ wavpack_reader_c::read(generic_packetizer_c *ptzr, bool force) { wavpack_header_t dummy_header, dummy_header_correc; wavpack_meta_t dummy_meta; - int32_t data_size = wv_parse_frame(mm_io, dummy_header, dummy_meta); + uint64_t initial_position = mm_io->getFilePointer(); + uint8_t *chunk, *databuffer; + + // determine the final data size + int32_t data_size = 0, block_size; + int extra_frames_number = -1; + + dummy_meta.channel_count = 0; + while (dummy_meta.channel_count < meta.channel_count) { + extra_frames_number++; + block_size = wv_parse_frame(mm_io, dummy_header, dummy_meta, false, false); + if (block_size == -1) + return FILE_STATUS_DONE; + data_size += block_size; + mm_io->skip(block_size); + } if (data_size < 0) return FILE_STATUS_DONE; - uint8_t *chunk = (uint8_t *)safemalloc(data_size + sizeof(wavpack_header_t) - - WV_KEEP_HEADER_POSITION); + data_size += sizeof(wavpack_header_t) - WV_KEEP_HEADER_POSITION; + if (extra_frames_number) + data_size += (extra_frames_number * 12) + 4; + chunk = (uint8_t *)safemalloc(data_size); // keep the header minus the ID & size (all found in Matroska) put_uint16_le(chunk, dummy_header.version); @@ -127,15 +145,28 @@ wavpack_reader_c::read(generic_packetizer_c *ptzr, put_uint32_le(&chunk[4], dummy_header.total_samples); put_uint32_le(&chunk[8], dummy_header.block_index); put_uint32_le(&chunk[12], dummy_header.block_samples); - put_uint32_le(&chunk[16], dummy_header.flags); - put_uint32_le(&chunk[20], dummy_header.crc); - if (mm_io->read(&chunk[sizeof(wavpack_header_t) - WV_KEEP_HEADER_POSITION], - data_size) < 0) - return FILE_STATUS_DONE; + mm_io->setFilePointer(initial_position); - memory_c mem(chunk, data_size + sizeof(wavpack_header_t) - - WV_KEEP_HEADER_POSITION, true); + dummy_meta.channel_count = 0; + databuffer = &chunk[16]; + while (dummy_meta.channel_count < meta.channel_count) { + block_size = wv_parse_frame(mm_io, dummy_header, dummy_meta, false, false); + put_uint32_le(databuffer, dummy_header.flags); + databuffer += 4; + put_uint32_le(databuffer, dummy_header.crc); + databuffer += 4; + if (meta.channel_count > 2) { + // not stored for the last block + put_uint32_le(databuffer, block_size); + databuffer += 4; + } + if (mm_io->read(databuffer, block_size) < 0) + return FILE_STATUS_DONE; + databuffer += block_size; + } + + memory_c mem(chunk, data_size, true); // find the if there is a correction file data corresponding memories_c mems; @@ -143,8 +174,21 @@ wavpack_reader_c::read(generic_packetizer_c *ptzr, if (mm_io_correc) { do { - data_size = wv_parse_frame(mm_io_correc, dummy_header_correc, - dummy_meta); + initial_position = mm_io_correc->getFilePointer(); + // determine the final data size + data_size = 0; + extra_frames_number = 0; + dummy_meta.channel_count = 0; + while (dummy_meta.channel_count < meta_correc.channel_count) { + extra_frames_number++; + block_size = wv_parse_frame(mm_io_correc, dummy_header_correc, + dummy_meta, false, false); + if (block_size == -1) + return FILE_STATUS_DONE; + data_size += block_size; + mm_io_correc->skip(block_size); + } + // no more correction to be found if (data_size < 0) { delete mm_io_correc; @@ -155,21 +199,39 @@ wavpack_reader_c::read(generic_packetizer_c *ptzr, } while (dummy_header_correc.block_samples < dummy_header.block_samples); if (dummy_header_correc.block_samples == dummy_header.block_samples) { + uint8_t *chunk_correc; - uint8_t *chunk_correc = (uint8_t *)safemalloc(data_size + 4); + mm_io_correc->setFilePointer(initial_position); + + if (meta.channel_count > 2) + data_size += extra_frames_number * 8; + else + data_size += 4; + chunk_correc = (uint8_t *)safemalloc(data_size); // only keep the CRC in the header - put_uint32_le(chunk_correc, dummy_header_correc.crc); + dummy_meta.channel_count = 0; + databuffer = chunk_correc; + while (dummy_meta.channel_count < meta_correc.channel_count) { + block_size = wv_parse_frame(mm_io_correc, dummy_header_correc, + dummy_meta, false, false); + put_uint32_le(databuffer, dummy_header_correc.crc); + databuffer += 4; + if (meta_correc.channel_count > 2) { + // not stored for the last block + put_uint32_le(databuffer, block_size); + databuffer += 4; + } + if (mm_io_correc->read(databuffer, block_size) < 0) { + delete mm_io_correc; + mm_io_correc = NULL; + } + databuffer += block_size; + } - if (mm_io_correc->read(&chunk_correc[4], data_size) < 0) { - delete mm_io_correc; - mm_io_correc = NULL; - } - else { - memory_c mem_correc(chunk_correc, data_size + 4, true); - mems.push_back(&mem_correc); - PTZR0->process(mems); - } + memory_c mem_correc(chunk_correc, data_size, true); + mems.push_back(&mem_correc); + PTZR0->process(mems); } }