//////////////////////////////////////////////////////////////////////////// // **** WAVPACK **** // // Hybrid Lossless Wavefile Compressor // // Copyright (c) 1998 - 2005 Conifer Software. // // All Rights Reserved. // // Distributed under the BSD Software License (see license.txt) // //////////////////////////////////////////////////////////////////////////// // pack.c // This module actually handles the compression of the audio data, except for // the entropy coding which is handled by the words? modules. For efficiency, // the conversion is isolated to tight loops that handle an entire buffer. #include "wavpack.h" #include // This flag provides faster encoding speed at the expense of more code. The // improvement applies to 16-bit stereo lossless only. //////////////////////////////// local tables /////////////////////////////// // These two tables specify the characteristics of the decorrelation filters. // Each term represents one layer of the sequential filter, where positive // values indicate the relative sample involved from the same channel (1=prev), // 17 & 18 are special functions using the previous 2 samples, and negative // values indicate cross channel decorrelation (in stereo only). static const char default_terms [] = { 18,18,2,3,-2,0 }; static const char high_terms [] = { 18,18,2,3,-2,18,2,4,7,5,3,6,0 }; static const char fast_terms [] = { 17,17,0 }; ///////////////////////////// executable code //////////////////////////////// // This function initializes everything required to pack WavPack bitstreams // and must be called BEFORE any other function in this module. void pack_init (WavpackContext *wpc) { WavpackStream *wps = &wpc->stream; ulong flags = wps->wphdr.flags; struct decorr_pass *dpp; const char *term_string; int ti; wps->sample_index = 0; CLEAR (wps->decorr_passes); if (wpc->config.flags & CONFIG_HIGH_FLAG) term_string = high_terms; else if (wpc->config.flags & CONFIG_FAST_FLAG) term_string = fast_terms; else term_string = default_terms; for (dpp = wps->decorr_passes, ti = 0; term_string [ti]; ti++) if (term_string [ti] >= 0 || (flags & CROSS_DECORR)) { dpp->term = term_string [ti]; dpp++->delta = 2; } else if (!(flags & MONO_FLAG)) { dpp->term = -3; dpp++->delta = 2; } wps->num_terms = dpp - wps->decorr_passes; init_words (wps); } // Allocate room for and copy the decorrelation terms from the decorr_passes // array into the specified metadata structure. Both the actual term id and // the delta are packed into single characters. static void write_decorr_terms (WavpackStream *wps, WavpackMetadata *wpmd) { int tcount = wps->num_terms; struct decorr_pass *dpp; char *byteptr; byteptr = wpmd->data = wpmd->temp_data; wpmd->id = ID_DECORR_TERMS; for (dpp = wps->decorr_passes; tcount--; ++dpp) *byteptr++ = ((dpp->term + 5) & 0x1f) | ((dpp->delta << 5) & 0xe0); wpmd->byte_length = byteptr - (char *) wpmd->data; } // Allocate room for and copy the decorrelation term weights from the // decorr_passes array into the specified metadata structure. The weights // range +/-1024, but are rounded and truncated to fit in signed chars for // metadata storage. Weights are separate for the two channels static void write_decorr_weights (WavpackStream *wps, WavpackMetadata *wpmd) { int tcount = wps->num_terms; struct decorr_pass *dpp; char *byteptr; byteptr = wpmd->data = wpmd->temp_data; wpmd->id = ID_DECORR_WEIGHTS; for (dpp = wps->decorr_passes; tcount--; ++dpp) { dpp->weight_A = restore_weight (*byteptr++ = store_weight (dpp->weight_A)); if (!(wps->wphdr.flags & MONO_FLAG)) dpp->weight_B = restore_weight (*byteptr++ = store_weight (dpp->weight_B)); } wpmd->byte_length = byteptr - (char *) wpmd->data; } // Allocate room for and copy the decorrelation samples from the decorr_passes // array into the specified metadata structure. The samples are signed 32-bit // values, but are converted to signed log2 values for storage in metadata. // Values are stored for both channels and are specified from the first term // with unspecified samples set to zero. The number of samples stored varies // with the actual term value, so those must obviously be specified before // these in the metadata list. Any number of terms can have their samples // specified from no terms to all the terms, however I have found that // sending more than the first term's samples is a waste. The "wcount" // variable can be set to the number of terms to have their samples stored. static void write_decorr_samples (WavpackStream *wps, WavpackMetadata *wpmd) { int tcount = wps->num_terms, wcount = 1, temp; struct decorr_pass *dpp; uchar *byteptr; byteptr = wpmd->data = wpmd->temp_data; wpmd->id = ID_DECORR_SAMPLES; for (dpp = wps->decorr_passes; tcount--; ++dpp) if (wcount) { if (dpp->term > MAX_TERM) { dpp->samples_A [0] = exp2s (temp = log2s (dpp->samples_A [0])); *byteptr++ = temp; *byteptr++ = temp >> 8; dpp->samples_A [1] = exp2s (temp = log2s (dpp->samples_A [1])); *byteptr++ = temp; *byteptr++ = temp >> 8; if (!(wps->wphdr.flags & MONO_FLAG)) { dpp->samples_B [0] = exp2s (temp = log2s (dpp->samples_B [0])); *byteptr++ = temp; *byteptr++ = temp >> 8; dpp->samples_B [1] = exp2s (temp = log2s (dpp->samples_B [1])); *byteptr++ = temp; *byteptr++ = temp >> 8; } } else if (dpp->term < 0) { dpp->samples_A [0] = exp2s (temp = log2s (dpp->samples_A [0])); *byteptr++ = temp; *byteptr++ = temp >> 8; dpp->samples_B [0] = exp2s (temp = log2s (dpp->samples_B [0])); *byteptr++ = temp; *byteptr++ = temp >> 8; } else { int m = 0, cnt = dpp->term; while (cnt--) { dpp->samples_A [m] = exp2s (temp = log2s (dpp->samples_A [m])); *byteptr++ = temp; *byteptr++ = temp >> 8; if (!(wps->wphdr.flags & MONO_FLAG)) { dpp->samples_B [m] = exp2s (temp = log2s (dpp->samples_B [m])); *byteptr++ = temp; *byteptr++ = temp >> 8; } m++; } } wcount--; } else { CLEAR (dpp->samples_A); CLEAR (dpp->samples_B); } wpmd->byte_length = byteptr - (uchar *) wpmd->data; } // Allocate room for and copy the configuration information into the specified // metadata structure. Currently, we just store the upper 3 bytes of // config.flags and only in the first block of audio data. Note that this is // for informational purposes not required for playback or decoding (like // whether high or fast mode was specified). static void write_config_info (WavpackContext *wpc, WavpackMetadata *wpmd) { char *byteptr; byteptr = wpmd->data = wpmd->temp_data; wpmd->id = ID_CONFIG_BLOCK; *byteptr++ = (char) (wpc->config.flags >> 8); *byteptr++ = (char) (wpc->config.flags >> 16); *byteptr++ = (char) (wpc->config.flags >> 24); wpmd->byte_length = byteptr - (char *) wpmd->data; } // Pack an entire block of samples (either mono or stereo) into a completed // WavPack block. It is assumed that there is sufficient space for the // completed block at "wps->blockbuff" and that "wps->blockend" points to the // end of the available space. A return value of FALSE indicates an error. // Any unsent metadata is transmitted first, then required metadata for this // block is sent, and finally the compressed integer data is sent. If a "wpx" // stream is required for floating point data or large integer data, then this // must be handled outside this function. To find out how much data was written // the caller must look at the ckSize field of the written WavpackHeader, NOT // the one in the WavpackStream. int pack_start_block (WavpackContext *wpc) { WavpackStream *wps = &wpc->stream; WavpackMetadata wpmd; memcpy (wps->blockbuff, &wps->wphdr, sizeof (WavpackHeader)); ((WavpackHeader *) wps->blockbuff)->ckSize = sizeof (WavpackHeader) - 8; ((WavpackHeader *) wps->blockbuff)->block_index = wps->sample_index; ((WavpackHeader *) wps->blockbuff)->block_samples = 0; ((WavpackHeader *) wps->blockbuff)->crc = 0xffffffff; if (wpc->wrapper_bytes) { wpmd.id = ID_RIFF_HEADER; wpmd.byte_length = wpc->wrapper_bytes; wpmd.data = wpc->wrapper_data; copy_metadata (&wpmd, wps->blockbuff, wps->blockend); free_metadata (&wpmd); wpc->wrapper_data = NULL; wpc->wrapper_bytes = 0; } write_decorr_terms (wps, &wpmd); copy_metadata (&wpmd, wps->blockbuff, wps->blockend); free_metadata (&wpmd); write_decorr_weights (wps, &wpmd); copy_metadata (&wpmd, wps->blockbuff, wps->blockend); free_metadata (&wpmd); write_decorr_samples (wps, &wpmd); copy_metadata (&wpmd, wps->blockbuff, wps->blockend); free_metadata (&wpmd); write_entropy_vars (wps, &wpmd); copy_metadata (&wpmd, wps->blockbuff, wps->blockend); free_metadata (&wpmd); if ((wps->wphdr.flags & INITIAL_BLOCK) && !wps->sample_index) { write_config_info (wpc, &wpmd); copy_metadata (&wpmd, wps->blockbuff, wps->blockend); free_metadata (&wpmd); } bs_open_write (&wps->wvbits, wps->blockbuff + ((WavpackHeader *) wps->blockbuff)->ckSize + 12, wps->blockend); return TRUE; } static void decorr_stereo_pass (struct decorr_pass *dpp, long *bptr, long *eptr, int m); static void decorr_stereo_pass_18 (struct decorr_pass *dpp, long *bptr, long *eptr); static void decorr_stereo_pass_17 (struct decorr_pass *dpp, long *bptr, long *eptr); static void decorr_stereo_pass_m2 (struct decorr_pass *dpp, long *bptr, long *eptr); int pack_samples (WavpackContext *wpc, long *buffer, ulong sample_count) { WavpackStream *wps = &wpc->stream; ulong flags = wps->wphdr.flags; struct decorr_pass *dpp; long *bptr, *eptr; int tcount, m; ulong crc; if (!sample_count) return TRUE; eptr = buffer + sample_count * ((flags & MONO_FLAG) ? 1 : 2); m = ((WavpackHeader *) wps->blockbuff)->block_samples & (MAX_TERM - 1); crc = ((WavpackHeader *) wps->blockbuff)->crc; /////////////////////// handle lossless mono mode ///////////////////////// if (!(flags & HYBRID_FLAG) && (flags & MONO_FLAG)) for (bptr = buffer; bptr < eptr;) { long code; crc = crc * 3 + (code = *bptr); for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) { long sam; if (dpp->term > MAX_TERM) { if (dpp->term & 1) sam = 2 * dpp->samples_A [0] - dpp->samples_A [1]; else sam = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; dpp->samples_A [1] = dpp->samples_A [0]; dpp->samples_A [0] = code; } else { sam = dpp->samples_A [m]; dpp->samples_A [(m + dpp->term) & (MAX_TERM - 1)] = code; } code -= apply_weight_i (dpp->weight_A, sam); update_weight (dpp->weight_A, 2, sam, code); } m = (m + 1) & (MAX_TERM - 1); *bptr++ = code; } //////////////////// handle the lossless stereo mode ////////////////////// else if (!(flags & HYBRID_FLAG) && !(flags & MONO_FLAG)) { if (flags & JOINT_STEREO) for (bptr = buffer; bptr < eptr; bptr += 2) { crc = crc * 9 + (bptr [0] * 3) + bptr [1]; bptr [1] += ((bptr [0] -= bptr [1]) >> 1); } else for (bptr = buffer; bptr < eptr; bptr += 2) crc = crc * 9 + (bptr [0] * 3) + bptr [1]; for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount-- ; dpp++) { if (dpp->term == 17) decorr_stereo_pass_17 (dpp, buffer, eptr); else if (dpp->term == 18) decorr_stereo_pass_18 (dpp, buffer, eptr); else if (dpp->term >= 1 && dpp->term <= 7) decorr_stereo_pass (dpp, buffer, eptr, m); else if (dpp->term == -2) decorr_stereo_pass_m2 (dpp, buffer, eptr); } } send_words (buffer, sample_count, flags, &wps->w, &wps->wvbits); ((WavpackHeader *) wps->blockbuff)->crc = crc; ((WavpackHeader *) wps->blockbuff)->block_samples += sample_count; wps->sample_index += sample_count; return TRUE; } static void decorr_stereo_pass (struct decorr_pass *dpp, long *bptr, long *eptr, int m) { int k = (m + dpp->term) & (MAX_TERM - 1); long sam; while (bptr < eptr) { dpp->samples_A [k] = bptr [0]; bptr [0] -= apply_weight_i (dpp->weight_A, (sam = dpp->samples_A [m])); update_weight (dpp->weight_A, 2, sam, bptr [0]); bptr++; dpp->samples_B [k] = bptr [0]; bptr [0] -= apply_weight_i (dpp->weight_B, (sam = dpp->samples_B [m])); update_weight (dpp->weight_B, 2, sam, bptr [0]); bptr++; m = (m + 1) & (MAX_TERM - 1); k = (k + 1) & (MAX_TERM - 1); } } static void decorr_stereo_pass_18 (struct decorr_pass *dpp, long *bptr, long *eptr) { long sam; while (bptr < eptr) { sam = (3 * dpp->samples_A [0] - dpp->samples_A [1]) >> 1; dpp->samples_A [1] = dpp->samples_A [0]; dpp->samples_A [0] = bptr [0]; bptr [0] -= apply_weight_i (dpp->weight_A, sam); update_weight (dpp->weight_A, 2, sam, bptr [0]); bptr++; sam = (3 * dpp->samples_B [0] - dpp->samples_B [1]) >> 1; dpp->samples_B [1] = dpp->samples_B [0]; dpp->samples_B [0] = bptr [0]; bptr [0] -= apply_weight_i (dpp->weight_B, sam); update_weight (dpp->weight_B, 2, sam, bptr [0]); bptr++; } } static void decorr_stereo_pass_m2 (struct decorr_pass *dpp, long *bptr, long *eptr) { long sam_A, sam_B; for (; bptr < eptr; bptr += 2) { sam_A = bptr [1]; sam_B = dpp->samples_B [0]; dpp->samples_B [0] = bptr [0]; bptr [0] -= apply_weight_i (dpp->weight_A, sam_A); update_weight_clip (dpp->weight_A, 2, sam_A, bptr [0]); bptr [1] -= apply_weight_i (dpp->weight_B, sam_B); update_weight_clip (dpp->weight_B, 2, sam_B, bptr [1]); } } static void decorr_stereo_pass_17 (struct decorr_pass *dpp, long *bptr, long *eptr) { long sam; while (bptr < eptr) { sam = 2 * dpp->samples_A [0] - dpp->samples_A [1]; dpp->samples_A [1] = dpp->samples_A [0]; dpp->samples_A [0] = bptr [0]; bptr [0] -= apply_weight_i (dpp->weight_A, sam); update_weight (dpp->weight_A, 2, sam, bptr [0]); bptr++; sam = 2 * dpp->samples_B [0] - dpp->samples_B [1]; dpp->samples_B [1] = dpp->samples_B [0]; dpp->samples_B [0] = bptr [0]; bptr [0] -= apply_weight_i (dpp->weight_B, sam); update_weight (dpp->weight_B, 2, sam, bptr [0]); bptr++; } } int pack_finish_block (WavpackContext *wpc) { WavpackStream *wps = &wpc->stream; struct decorr_pass *dpp; ulong data_count; int tcount, m; m = ((WavpackHeader *) wps->blockbuff)->block_samples & (MAX_TERM - 1); if (m) for (tcount = wps->num_terms, dpp = wps->decorr_passes; tcount--; dpp++) if (dpp->term > 0 && dpp->term <= MAX_TERM) { long 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); } } flush_word (&wps->w, &wps->wvbits); data_count = bs_close_write (&wps->wvbits); if (data_count) { if (data_count != (ulong) -1) { uchar *cptr = wps->blockbuff + ((WavpackHeader *) wps->blockbuff)->ckSize + 8; *cptr++ = ID_WV_BITSTREAM | ID_LARGE; *cptr++ = data_count >> 1; *cptr++ = data_count >> 9; *cptr++ = data_count >> 17; ((WavpackHeader *) wps->blockbuff)->ckSize += data_count + 4; } else return FALSE; } return TRUE; }