/*
** WavPackUtils.java
**
** Copyright (c) 2008 Peter McQuillan
**
** All Rights Reserved.
**
** Distributed under the BSD Software License (see license.txt)
**
*/
package com.wavpack.encoder;
public class WavPackUtils {
///////////////////////////// local table storage ////////////////////////////
static long[] sample_rates =
{
6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200,
96000, 192000
};
// This function returns a pointer to a string describing the last error
// generated by WavPack.
static String WavpackGetErrorMessage(WavpackContext wpc) {
return wpc.error_message;
}
// Set configuration for writing WavPack files. This must be done before
// sending any actual samples. The "config" structure contains the following
// required information:
// config.bytes_per_sample see WavpackGetBytesPerSample() for info
// config.bits_per_sample see WavpackGetBitsPerSample() for info
// config.num_channels self evident
// config.sample_rate self evident
// In addition, the following fields and flags may be set:
// config->flags:
// --------------
// o CONFIG_HYBRID_FLAG select hybrid mode (must set bitrate)
// o CONFIG_JOINT_STEREO select joint stereo (must set override also)
// o CONFIG_JOINT_OVERRIDE override default joint stereo selection
// o CONFIG_HYBRID_SHAPE select hybrid noise shaping (set override &
// shaping_weight != 0)
// o CONFIG_SHAPE_OVERRIDE override default hybrid noise shaping
// (set CONFIG_HYBRID_SHAPE and shaping_weight)
// o CONFIG_FAST_FLAG "fast" compression mode
// o CONFIG_HIGH_FLAG "high" compression mode
// o CONFIG_VERY_HIGH_FLAG "very high" compression mode
// o CONFIG_CREATE_WVC create correction file
// o CONFIG_OPTIMIZE_WVC maximize bybrid compression (-cc option)
// config->bitrate hybrid bitrate in bits/sample (scaled up 2^8)
// config->shaping_weight hybrid noise shaping coefficient (scaled up 2^10)
// config->block_samples force samples per WavPack block (0 = use deflt)
// If the number of samples to be written is known then it should be passed
// here. If the duration is not known then pass -1. In the case that the size
// is not known (or the writing is terminated early) then it is suggested that
// the application retrieve the first block written and let the library update
// the total samples indication. A function is provided to do this update and
// it should be done to the "correction" file also. If this cannot be done
// (because a pipe is being used, for instance) then a valid WavPack will still
// be created, but when applications want to access that file they will have
// to seek all the way to the end to determine the actual duration. A return of
// FALSE indicates an error.
public static int WavpackSetConfiguration(WavpackContext wpc, WavpackConfig config, long total_samples) {
long flags = (config.bytes_per_sample - 1);
WavpackStream wps = wpc.stream;
int bps = 0;
int shift;
int i;
if (config.num_channels > 2) {
wpc.error_message = "too many channels!";
return Defines.FALSE;
}
wpc.total_samples = total_samples;
wpc.config.sample_rate = config.sample_rate;
wpc.config.num_channels = config.num_channels;
wpc.config.bits_per_sample = config.bits_per_sample;
wpc.config.bytes_per_sample = config.bytes_per_sample;
wpc.config.block_samples = config.block_samples;
wpc.config.flags = config.flags;
if ((wpc.config.flags & Defines.CONFIG_VERY_HIGH_FLAG) > 0) {
wpc.config.flags |= Defines.CONFIG_HIGH_FLAG;
}
shift = (config.bytes_per_sample * 8) - config.bits_per_sample;
for (i = 0; i < 15; ++i) {
if (wpc.config.sample_rate == sample_rates[i]) {
break;
}
}
flags |= (i << Defines.SRATE_LSB);
flags |= (shift << Defines.SHIFT_LSB);
if ((config.flags & Defines.CONFIG_HYBRID_FLAG) != 0) {
flags |= (Defines.HYBRID_FLAG | Defines.HYBRID_BITRATE | Defines.HYBRID_BALANCE);
if (((wpc.config.flags & Defines.CONFIG_SHAPE_OVERRIDE) != 0) &&
((wpc.config.flags & Defines.CONFIG_HYBRID_SHAPE) != 0) &&
(config.shaping_weight != 0)) {
wpc.config.shaping_weight = config.shaping_weight;
flags |= (Defines.HYBRID_SHAPE | Defines.NEW_SHAPING);
}
if ((wpc.config.flags & Defines.CONFIG_OPTIMIZE_WVC) != 0) {
flags |= Defines.CROSS_DECORR;
}
bps = config.bitrate;
} else {
flags |= Defines.CROSS_DECORR;
}
if (((config.flags & Defines.CONFIG_JOINT_OVERRIDE) == 0) ||
((config.flags & Defines.CONFIG_JOINT_STEREO) != 0)) {
flags |= Defines.JOINT_STEREO;
}
if ((config.flags & Defines.CONFIG_CREATE_WVC) != 0) {
wpc.wvc_flag = Defines.TRUE;
}
wpc.stream_version = Defines.CUR_STREAM_VERS;
wps.wphdr.ckID[0] = 'w';
wps.wphdr.ckID[1] = 'v';
wps.wphdr.ckID[2] = 'p';
wps.wphdr.ckID[3] = 'k';
// 32 is the size of the WavPack header
wps.wphdr.ckSize = 32 - 8;
wps.wphdr.total_samples = wpc.total_samples;
wps.wphdr.version = wpc.stream_version;
wps.wphdr.flags = flags | Defines.INITIAL_BLOCK | Defines.FINAL_BLOCK;
wps.bits = bps;
if (config.num_channels == 1) {
wps.wphdr.flags &= ~(Defines.JOINT_STEREO | Defines.CROSS_DECORR |
Defines.HYBRID_BALANCE);
wps.wphdr.flags |= Defines.MONO_FLAG;
}
return Defines.TRUE;
}
// Prepare to actually pack samples by determining the size of the WavPack
// blocks and initializing the stream. Call after WavpackSetConfiguration()
// and before WavpackPackSamples(). A return of FALSE indicates an error.
public static int WavpackPackInit(WavpackContext wpc) {
if (wpc.config.block_samples > 0) {
wpc.block_samples = wpc.config.block_samples;
} else {
if ((wpc.config.flags & Defines.CONFIG_HIGH_FLAG) > 0) {
wpc.block_samples = wpc.config.sample_rate;
} else if ((wpc.config.sample_rate % 2) == 0) {
wpc.block_samples = wpc.config.sample_rate / 2;
} else {
wpc.block_samples = wpc.config.sample_rate;
}
while ((wpc.block_samples * wpc.config.num_channels) > 150000) {
wpc.block_samples /= 2;
}
while ((wpc.block_samples * wpc.config.num_channels) < 40000) {
wpc.block_samples *= 2;
}
}
PackUtils.pack_init(wpc);
return Defines.TRUE;
}
// Pack the specified samples. Samples must be stored in longs in the native
// endian format of the executing processor. The number of samples specified
// indicates composite samples (sometimes called "frames"). So, the actual
// number of data points would be this "sample_count" times the number of
// channels. Note that samples are immediately packed into the block(s)
// currently being built. If the predetermined number of sample per block
// is reached, or the block being built is approaching overflow, then the
// block will be completed and written. If an application wants to break a
// block at a specific sample, then it must simply call WavpackFlushSamples()
// to force an early termination. Completed WavPack blocks are send to the
// function provided in the initial call to WavpackOpenFileOutput(). A
// return of FALSE indicates an error.
public static int WavpackPackSamples(WavpackContext wpc, long[] sample_buffer, long sample_count) {
WavpackStream wps = wpc.stream;
long flags = wps.wphdr.flags;
if ((flags & Defines.SHIFT_MASK) != 0) {
int shift = (int) ((flags & Defines.SHIFT_MASK) >> Defines.SHIFT_LSB);
long cnt = sample_count;
int ptrIndex = 0;
if ((flags & (Defines.MONO_FLAG | Defines.FALSE_STEREO)) != 0) {
while (cnt > 0) {
sample_buffer[ptrIndex] = sample_buffer[ptrIndex] >>> shift; // was >>
ptrIndex++;
cnt--;
}
} else {
while (cnt > 0) {
sample_buffer[ptrIndex] = sample_buffer[ptrIndex] >>> shift; // was >>
ptrIndex++;
sample_buffer[ptrIndex] = sample_buffer[ptrIndex] >>> shift; // was >>
ptrIndex++;
cnt--;
}
}
}
while (sample_count > 0) {
long samples_to_pack;
long samples_packed;
if (wpc.acc_samples == 0) {
flags &= ~Defines.MAG_MASK;
flags += ((1L << Defines.MAG_LSB) * (((flags & Defines.BYTES_STORED) * 8) + 7));
wps.wphdr.block_index = wps.sample_index;
wps.wphdr.flags = flags;
PackUtils.pack_start_block(wpc);
}
if ((wpc.acc_samples + sample_count) > wpc.block_samples) {
samples_to_pack = wpc.block_samples - wpc.acc_samples;
} else {
samples_to_pack = sample_count;
}
samples_packed = PackUtils.pack_samples(wpc, sample_buffer, samples_to_pack);
sample_count -= samples_packed;
if (((wpc.acc_samples += samples_packed) == wpc.block_samples) ||
(samples_packed != samples_to_pack)) {
if (finish_block(wpc) == 0) {
return Defines.FALSE;
}
}
}
return Defines.TRUE;
}
// Flush all accumulated samples into WavPack blocks. This is normally called
// after all samples have been sent to WavpackPackSamples(), but can also be
// called to terminate a WavPack block at a specific sample (in other words it
// is possible to continue after this operation). A return of FALSE indicates
// an error.
public static int WavpackFlushSamples(WavpackContext wpc) {
if ((wpc.acc_samples != 0) && (finish_block(wpc) == 0)) {
return Defines.FALSE;
}
return Defines.TRUE;
}
static int finish_block(WavpackContext wpc) {
WavpackStream wps = wpc.stream;
long bcount;
int result = 0;
result = PackUtils.pack_finish_block(wpc);
wpc.acc_samples = 0;
if (result == 0) {
wpc.error_message = "output buffer overflowed!";
return result;
}
bcount = (wps.blockbuff[4] & 0xff) + ((wps.blockbuff[5] & 0xff) << 8) +
((wps.blockbuff[6] & 0xff) << 16) + ((wps.blockbuff[7] & 0xff) << 24) + 8;
try {
wpc.outfile.write(wps.blockbuff, 0, (int) bcount);
if (wpc.first_block_size == -1)
wpc.first_block_size = (int) bcount;
}
catch (Exception e) {
result = Defines.FALSE;
}
if (result == 0) {
wpc.error_message = "can't write WavPack data, disk probably full!";
return result;
}
wpc.filelen += bcount;
if (wps.block2buff[0] == 'w') // if starts with w then has a WavPack header i.e. it is defined
{
bcount = (wps.block2buff[4] & 0xff) + ((wps.block2buff[5] & 0xff) << 8) +
((wps.block2buff[6] & 0xff) << 16) + ((wps.block2buff[7] & 0xff) << 24) + 8;
try {
wpc.correction_outfile.write(wps.block2buff, 0, (int) bcount);
}
catch (Exception e) {
result = Defines.FALSE;
}
if (result == 0) {
wpc.error_message = "can't write WavPack data, disk probably full!";
return result;
}
wpc.file2len += bcount;
}
return result;
}
// Get total number of samples contained in the WavPack file, or -1 if unknown
static long WavpackGetNumSamples(WavpackContext wpc) {
if (null != wpc) {
return (wpc.total_samples);
} else {
return (long) -1;
}
}
// Get the current sample index position, or -1 if unknown
static long WavpackGetSampleIndex(WavpackContext wpc) {
if (null != wpc) {
return wpc.stream.sample_index;
}
return (long) -1;
}
// Returns the sample rate of the specified WavPack file
static long WavpackGetSampleRate(WavpackContext wpc) {
if (null != wpc) {
return (wpc.config.sample_rate);
} else {
return (long) 44100;
}
}
// Returns the number of channels of the specified WavPack file.
public static int WavpackGetNumChannels(WavpackContext wpc) {
if (null != wpc) {
return (wpc.config.num_channels);
} else {
return 2;
}
}
// Returns the actual number of valid bits per sample contained in the
// original file from 1 to 24, and which may or may not be a multiple
// of 8. When this value is not a multiple of 8, then the "extra" bits
// are located in the LSBs of the results. That is, values are right
// justified when unpacked into ints, but are left justified in the
// number of bytes used by the original data.
static int WavpackGetBitsPerSample(WavpackContext wpc) {
if (null != wpc) {
return (wpc.config.bits_per_sample);
} else {
return 16;
}
}
// Returns the number of bytes used for each sample (1 to 4) in the original
// file. This is required information for the user of this module because the
// audio data is returned in the LOWER bytes of the long buffer and must be
// left-shifted 8, 16, or 24 bits if normalized longs are required.
public static int WavpackGetBytesPerSample(WavpackContext wpc) {
if (null != wpc) {
return (wpc.config.bytes_per_sample);
} else {
return 2;
}
}
}