//////////////////////////////////////////////////////////////////////////// // **** WAVPACK **** // // Hybrid Lossless Wavefile Compressor // // Copyright (c) 1998 - 2019 David Bryant. // // All Rights Reserved. // // Distributed under the BSD Software License (see license.txt) // //////////////////////////////////////////////////////////////////////////// // open_raw.c // This code provides the ability to decode WavPack frames directly from // memory for use in a streaming application. It can handle full blocks // or the headerless block data provided by Matroska and the DirectShow // WavPack splitter. For information about how Matroska stores WavPack, // see: https://www.matroska.org/technical/specs/codecid/wavpack.html #include #include #include "wavpack_local.h" typedef struct { unsigned char *sptr, *dptr, *eptr, free_required; } RawSegment; typedef struct { RawSegment *segments; int num_segments, curr_segment; unsigned char ungetc_char, ungetc_flag; } WavpackRawContext; static int32_t raw_read_bytes (void *id, void *data, int32_t bcount) { WavpackRawContext *rcxt = id; unsigned char *outptr = data; while (bcount) { if (rcxt->ungetc_flag) { *outptr++ = rcxt->ungetc_char; rcxt->ungetc_flag = 0; bcount--; } else if (rcxt->curr_segment < rcxt->num_segments) { RawSegment *segptr = rcxt->segments + rcxt->curr_segment; int bytes_to_copy = (int)(segptr->eptr - segptr->dptr); if (bytes_to_copy > bcount) bytes_to_copy = bcount; memcpy (outptr, segptr->dptr, bytes_to_copy); outptr += bytes_to_copy; bcount -= bytes_to_copy; if ((segptr->dptr += bytes_to_copy) == segptr->eptr) rcxt->curr_segment++; } else break; } return (int32_t)(outptr - (unsigned char *) data); } static int32_t raw_write_bytes (void *id, void *data, int32_t bcount) { return 0; } static int64_t raw_get_pos (void *id) { return 0; } static int raw_set_pos_abs (void *id, int64_t pos) { return 0; } static int raw_set_pos_rel (void *id, int64_t delta, int mode) { return 0; } static int raw_push_back_byte (void *id, int c) { WavpackRawContext *rcxt = id; rcxt->ungetc_char = c; rcxt->ungetc_flag = 1; return c; } static int64_t raw_get_length (void *id) { return 0; } static int raw_can_seek (void *id) { return 0; } static int raw_close_stream (void *id) { WavpackRawContext *rcxt = id; int i; if (rcxt) { for (i = 0; i < rcxt->num_segments; ++i) if (rcxt->segments [i].sptr && rcxt->segments [i].free_required) free (rcxt->segments [i].sptr); if (rcxt->segments) free (rcxt->segments); free (rcxt); } return 0; } static WavpackStreamReader64 raw_reader = { raw_read_bytes, raw_write_bytes, raw_get_pos, raw_set_pos_abs, raw_set_pos_rel, raw_push_back_byte, raw_get_length, raw_can_seek, NULL, raw_close_stream }; // This function is similar to WavpackOpenFileInput() except that instead of // providing a filename to open, the caller provides pointers to buffered // WavPack frames (both standard and, optionally, correction data). It // decodes only a single frame. Note that in this context, a "frame" is a // collection of WavPack blocks that represent all the channels present. In // the case of mono or [most] stereo streams, this is the same thing, but // for multichannel streams each frame consists of several WavPack blocks // (which can contain only 1 or 2 channels). WavpackContext *WavpackOpenRawDecoder ( void *main_data, int32_t main_size, void *corr_data, int32_t corr_size, int16_t version, char *error, int flags, int norm_offset) { WavpackRawContext *raw_wv = NULL, *raw_wvc = NULL; // if the WavPack data does not contain headers we assume Matroska-style storage // and recreate the missing headers if (strncmp (main_data, "wvpk", 4)) { uint32_t multiple_blocks = 0, block_size, block_samples = 0, wphdr_flags, crc; uint32_t main_bytes = main_size, corr_bytes = corr_size; unsigned char *mcp = main_data; unsigned char *ccp = corr_data; int msi = 0, csi = 0; raw_wv = malloc (sizeof (WavpackRawContext)); memset (raw_wv, 0, sizeof (WavpackRawContext)); if (corr_data && corr_size) { raw_wvc = malloc (sizeof (WavpackRawContext)); memset (raw_wvc, 0, sizeof (WavpackRawContext)); } while (main_bytes >= 12) { if (!msi) { block_samples = *mcp++; block_samples += *mcp++ << 8; block_samples += *mcp++ << 16; block_samples += *mcp++ << 24; main_bytes -= 4; } wphdr_flags = *mcp++; wphdr_flags += *mcp++ << 8; wphdr_flags += *mcp++ << 16; wphdr_flags += *mcp++ << 24; main_bytes -= 4; // if the first block does not have the FINAL_BLOCK flag set, // then there are multiple blocks if (!msi && !(wphdr_flags & FINAL_BLOCK)) multiple_blocks = 1; crc = *mcp++; crc += *mcp++ << 8; crc += *mcp++ << 16; crc += *mcp++ << 24; main_bytes -= 4; if (multiple_blocks) { block_size = *mcp++; block_size += *mcp++ << 8; block_size += *mcp++ << 16; block_size += *mcp++ << 24; main_bytes -= 4; } else block_size = main_bytes; if (block_size > main_bytes) { if (error) strcpy (error, "main block overran available data!"); raw_close_stream (raw_wv); raw_close_stream (raw_wvc); return NULL; } else { WavpackHeader *wphdr = malloc (sizeof (WavpackHeader)); memset (wphdr, 0, sizeof (WavpackHeader)); memcpy (wphdr->ckID, "wvpk", 4); wphdr->ckSize = sizeof (WavpackHeader) - 8 + block_size; SET_TOTAL_SAMPLES (*wphdr, block_samples); wphdr->block_samples = block_samples; wphdr->version = version; wphdr->flags = wphdr_flags; wphdr->crc = crc; WavpackLittleEndianToNative (wphdr, WavpackHeaderFormat); raw_wv->num_segments += 2; raw_wv->segments = realloc (raw_wv->segments, sizeof (RawSegment) * raw_wv->num_segments); raw_wv->segments [msi].dptr = raw_wv->segments [msi].sptr = (unsigned char *) wphdr; raw_wv->segments [msi].eptr = raw_wv->segments [msi].dptr + sizeof (WavpackHeader); raw_wv->segments [msi++].free_required = 1; raw_wv->segments [msi].dptr = raw_wv->segments [msi].sptr = mcp; raw_wv->segments [msi].eptr = raw_wv->segments [msi].dptr + block_size; raw_wv->segments [msi++].free_required = 0; main_bytes -= block_size; mcp += block_size; } if (corr_data && corr_bytes >= 4) { crc = *ccp++; crc += *ccp++ << 8; crc += *ccp++ << 16; crc += *ccp++ << 24; corr_bytes -= 4; if (multiple_blocks) { block_size = *ccp++; block_size += *ccp++ << 8; block_size += *ccp++ << 16; block_size += *ccp++ << 24; corr_bytes -= 4; } else block_size = corr_bytes; if (block_size > corr_bytes) { if (error) strcpy (error, "correction block overran available data!"); raw_close_stream (raw_wv); raw_close_stream (raw_wvc); return NULL; } else { WavpackHeader *wphdr = malloc (sizeof (WavpackHeader)); memset (wphdr, 0, sizeof (WavpackHeader)); memcpy (wphdr->ckID, "wvpk", 4); wphdr->ckSize = sizeof (WavpackHeader) - 8 + block_size; SET_TOTAL_SAMPLES (*wphdr, block_samples); wphdr->block_samples = block_samples; wphdr->version = version; wphdr->flags = wphdr_flags; wphdr->crc = crc; WavpackLittleEndianToNative (wphdr, WavpackHeaderFormat); raw_wvc->num_segments += 2; raw_wvc->segments = realloc (raw_wvc->segments, sizeof (RawSegment) * raw_wvc->num_segments); raw_wvc->segments [csi].dptr = raw_wvc->segments [csi].sptr = (unsigned char *) wphdr; raw_wvc->segments [csi].eptr = raw_wvc->segments [csi].dptr + sizeof (WavpackHeader); raw_wvc->segments [csi++].free_required = 1; raw_wvc->segments [csi].dptr = raw_wvc->segments [csi].sptr = ccp; raw_wvc->segments [csi].eptr = raw_wvc->segments [csi].dptr + block_size; raw_wvc->segments [csi++].free_required = 0; corr_bytes -= block_size; ccp += block_size; } } } if (main_bytes || (corr_data && corr_bytes)) { if (error) strcpy (error, "leftover multiblock data!"); raw_close_stream (raw_wv); raw_close_stream (raw_wvc); return NULL; } } else { // the case of WavPack blocks with headers is much easier... if (main_data) { raw_wv = malloc (sizeof (WavpackRawContext)); memset (raw_wv, 0, sizeof (WavpackRawContext)); raw_wv->num_segments = 1; raw_wv->segments = malloc (sizeof (RawSegment) * raw_wv->num_segments); raw_wv->segments [0].dptr = raw_wv->segments [0].sptr = main_data; raw_wv->segments [0].eptr = raw_wv->segments [0].dptr + main_size; raw_wv->segments [0].free_required = 0; } if (corr_data && corr_size) { raw_wvc = malloc (sizeof (WavpackRawContext)); memset (raw_wvc, 0, sizeof (WavpackRawContext)); raw_wvc->num_segments = 1; raw_wvc->segments = malloc (sizeof (RawSegment) * raw_wvc->num_segments); raw_wvc->segments [0].dptr = raw_wvc->segments [0].sptr = corr_data; raw_wvc->segments [0].eptr = raw_wvc->segments [0].dptr + corr_size; raw_wvc->segments [0].free_required = 0; } } return WavpackOpenFileInputEx64 (&raw_reader, raw_wv, raw_wvc, error, flags | OPEN_STREAMING | OPEN_NO_CHECKSUM, norm_offset); } // Return the number of samples represented by the current (and in the raw case, only) frame. uint32_t WavpackGetNumSamplesInFrame (WavpackContext *wpc) { if (wpc && wpc->streams && wpc->streams [0]) return wpc->streams [0]->wphdr.block_samples; else return -1; }