#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "wavpack_local.h"
WavpackContext *WavpackOpenFileOutput (WavpackBlockOutput blockout, void *wv_id, void *wvc_id)
{
WavpackContext *wpc = malloc (sizeof (WavpackContext));
if (!wpc)
return NULL;
CLEAR (*wpc);
wpc->total_samples = -1;
wpc->stream_version = CUR_STREAM_VERS;
wpc->blockout = blockout;
wpc->wv_out = wv_id;
wpc->wvc_out = wvc_id;
return wpc;
}
static int add_to_metadata (WavpackContext *wpc, void *data, uint32_t bcount, unsigned char id);
void WavpackSetFileInformation (WavpackContext *wpc, char *file_extension, unsigned char file_format)
{
if (file_extension && strlen (file_extension) < sizeof (wpc->file_extension)) {
add_to_metadata (wpc, file_extension, (uint32_t) strlen (file_extension), ID_ALT_EXTENSION);
strcpy (wpc->file_extension, file_extension);
}
wpc->file_format = file_format;
}
static const struct { unsigned char a, b; } stereo_pairs [] = {
{ 1, 2 }, { 5, 6 }, { 7, 8 }, { 10, 11 }, { 13, 15 }, { 16, 18 }, { 30, 31 }, { 33, 34 }, { 35, 36 }, { 38, 39 }, { 127, 128 }, { 138, 140 }, { 141, 142 }, { 200, 201 }, { 202, 203 }, { 204, 205 }, { 206, 207 }, { 221, 222 }, };
#define NUM_STEREO_PAIRS (sizeof (stereo_pairs) / sizeof (stereo_pairs [0]))
int WavpackSetConfiguration (WavpackContext *wpc, WavpackConfig *config, uint32_t total_samples)
{
config->flags |= CONFIG_COMPATIBLE_WRITE;
if (total_samples == (uint32_t) -1)
return WavpackSetConfiguration64 (wpc, config, -1, NULL);
else
return WavpackSetConfiguration64 (wpc, config, total_samples, NULL);
}
int WavpackSetConfiguration64 (WavpackContext *wpc, WavpackConfig *config, int64_t total_samples, const unsigned char *chan_ids)
{
uint32_t flags, bps = 0;
uint32_t chan_mask = config->channel_mask;
int num_chans = config->num_channels;
int i;
if (config->sample_rate <= 0) {
strcpy (wpc->error_message, "sample rate cannot be zero or negative!");
return FALSE;
}
if (!total_samples || total_samples > MAX_WAVPACK_SAMPLES || total_samples < -1) {
strcpy (wpc->error_message, "invalid total sample count!");
return FALSE;
}
if (num_chans <= 0 || num_chans > WAVPACK_MAX_CHANS) {
strcpy (wpc->error_message, "invalid channel count!");
return FALSE;
}
if (config->block_samples && (config->block_samples < 16 || config->block_samples > 131072)) {
strcpy (wpc->error_message, "invalid custom block samples!");
return FALSE;
}
wpc->stream_version = (config->flags & CONFIG_COMPATIBLE_WRITE) ? CUR_STREAM_VERS : MAX_STREAM_VERS;
if ((config->qmode & QMODE_DSD_AUDIO) && config->bytes_per_sample == 1 && config->bits_per_sample == 8) {
#ifdef ENABLE_DSD
wpc->dsd_multiplier = 1;
flags = DSD_FLAG;
for (i = 14; i >= 0; --i)
if (config->sample_rate % sample_rates [i] == 0) {
int divisor = config->sample_rate / sample_rates [i];
if (divisor && (divisor & (divisor - 1)) == 0) {
config->sample_rate /= divisor;
wpc->dsd_multiplier = divisor;
break;
}
}
if (config->flags & CONFIG_HYBRID_FLAG) {
strcpy (wpc->error_message, "hybrid mode not available for DSD!");
return FALSE;
}
config->flags &= (CONFIG_HIGH_FLAG | CONFIG_MD5_CHECKSUM | CONFIG_PAIR_UNDEF_CHANS);
config->float_norm_exp = config->xmode = 0;
#else
strcpy (wpc->error_message, "libwavpack not configured for DSD!");
return FALSE;
#endif
}
else
flags = config->bytes_per_sample - 1;
wpc->total_samples = total_samples;
wpc->config.sample_rate = config->sample_rate;
wpc->config.num_channels = config->num_channels;
wpc->config.channel_mask = config->channel_mask;
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;
wpc->config.qmode = config->qmode;
if (config->flags & CONFIG_VERY_HIGH_FLAG)
wpc->config.flags |= CONFIG_HIGH_FLAG;
for (i = 0; i < 15; ++i)
if (wpc->config.sample_rate == sample_rates [i])
break;
flags |= i << SRATE_LSB;
if (!(flags & DSD_FLAG)) {
if (config->float_norm_exp) {
if (config->bytes_per_sample != 4 || config->bits_per_sample != 32) {
strcpy (wpc->error_message, "incorrect bits/bytes configuration for float data!");
return FALSE;
}
wpc->config.float_norm_exp = config->float_norm_exp;
wpc->config.flags |= CONFIG_FLOAT_DATA;
flags |= FLOAT_DATA;
}
else {
if (config->bytes_per_sample < 1 || config->bytes_per_sample > 4) {
strcpy (wpc->error_message, "invalid bytes per sample!");
return FALSE;
}
if (config->bits_per_sample < 1 || config->bits_per_sample > config->bytes_per_sample * 8) {
strcpy (wpc->error_message, "invalid bits per sample!");
return FALSE;
}
flags |= ((config->bytes_per_sample * 8) - config->bits_per_sample) << SHIFT_LSB;
}
if (config->flags & CONFIG_HYBRID_FLAG) {
flags |= HYBRID_FLAG | HYBRID_BITRATE | HYBRID_BALANCE;
if (!(wpc->config.flags & CONFIG_SHAPE_OVERRIDE)) {
wpc->config.flags |= CONFIG_HYBRID_SHAPE | CONFIG_AUTO_SHAPING;
flags |= HYBRID_SHAPE | NEW_SHAPING;
}
else if (wpc->config.flags & CONFIG_HYBRID_SHAPE) {
wpc->config.shaping_weight = config->shaping_weight;
flags |= HYBRID_SHAPE | NEW_SHAPING;
}
if (wpc->config.flags & (CONFIG_CROSS_DECORR | CONFIG_OPTIMIZE_WVC))
flags |= CROSS_DECORR;
if (config->flags & CONFIG_BITRATE_KBPS) {
bps = (uint32_t) floor (config->bitrate * 256000.0 / config->sample_rate / config->num_channels + 0.5);
if (bps > (64 << 8))
bps = 64 << 8;
}
else
bps = (uint32_t) floor (config->bitrate * 256.0 + 0.5);
}
else
flags |= CROSS_DECORR;
if (!(config->flags & CONFIG_JOINT_OVERRIDE) || (config->flags & CONFIG_JOINT_STEREO))
flags |= JOINT_STEREO;
if (config->flags & CONFIG_CREATE_WVC)
wpc->wvc_flag = TRUE;
}
if (chan_ids) {
int lastchan = 0, mask_copy = chan_mask;
if ((int) strlen ((char *) chan_ids) > num_chans) { strcpy (wpc->error_message, "chan_ids longer than num channels!");
return FALSE;
}
while (*chan_ids)
if (*chan_ids <= 32 && *chan_ids > lastchan && (mask_copy & (1U << (*chan_ids-1)))) {
mask_copy &= ~(1U << (*chan_ids-1));
lastchan = *chan_ids++;
}
else
break;
for (i = 0; chan_ids [i]; i++)
if (chan_ids [i] != 0xff) {
wpc->channel_identities = (unsigned char *) strdup ((char *) chan_ids);
break;
}
}
for (wpc->current_stream = 0; num_chans; wpc->current_stream++) {
WavpackStream *wps = malloc (sizeof (WavpackStream));
unsigned char left_chan_id = 0, right_chan_id = 0;
int pos, chans = 1;
wpc->streams = realloc (wpc->streams, (wpc->current_stream + 1) * sizeof (wpc->streams [0]));
wpc->streams [wpc->current_stream] = wps;
CLEAR (*wps);
if (chan_mask)
for (pos = 0; pos < 32; ++pos)
if (chan_mask & (1U << pos)) {
if (left_chan_id) {
right_chan_id = pos + 1;
break;
}
else {
chan_mask &= ~(1U << pos);
left_chan_id = pos + 1;
}
}
while (!right_chan_id && chan_ids && *chan_ids)
if (left_chan_id)
right_chan_id = *chan_ids;
else
left_chan_id = *chan_ids++;
if (!left_chan_id)
left_chan_id = right_chan_id = 0xff;
else if (!right_chan_id)
right_chan_id = 0xff;
if (num_chans >= 2) {
if ((config->flags & CONFIG_PAIR_UNDEF_CHANS) && left_chan_id == 0xff && right_chan_id == 0xff)
chans = 2;
else
for (i = 0; i < NUM_STEREO_PAIRS; ++i)
if ((left_chan_id == stereo_pairs [i].a && right_chan_id == stereo_pairs [i].b) ||
(left_chan_id == stereo_pairs [i].b && right_chan_id == stereo_pairs [i].a)) {
if (right_chan_id <= 32 && (chan_mask & (1U << (right_chan_id-1))))
chan_mask &= ~(1U << (right_chan_id-1));
else if (chan_ids && *chan_ids == right_chan_id)
chan_ids++;
chans = 2;
break;
}
}
num_chans -= chans;
if (num_chans && wpc->current_stream == NEW_MAX_STREAMS - 1)
break;
memcpy (wps->wphdr.ckID, "wvpk", 4);
wps->wphdr.ckSize = sizeof (WavpackHeader) - 8;
SET_TOTAL_SAMPLES (wps->wphdr, wpc->total_samples);
wps->wphdr.version = wpc->stream_version;
wps->wphdr.flags = flags;
wps->bits = bps;
if (!wpc->current_stream)
wps->wphdr.flags |= INITIAL_BLOCK;
if (!num_chans)
wps->wphdr.flags |= FINAL_BLOCK;
if (chans == 1) {
wps->wphdr.flags &= ~(JOINT_STEREO | CROSS_DECORR | HYBRID_BALANCE);
wps->wphdr.flags |= MONO_FLAG;
}
}
wpc->num_streams = wpc->current_stream;
wpc->current_stream = 0;
if (num_chans) {
strcpy (wpc->error_message, "too many channels!");
return FALSE;
}
if (config->flags & CONFIG_EXTRA_MODE)
wpc->config.xmode = config->xmode ? config->xmode : 1;
return TRUE;
}
int WavpackSetChannelLayout (WavpackContext *wpc, uint32_t layout_tag, const unsigned char *reorder)
{
int nchans = layout_tag & 0xff;
if ((layout_tag & 0xff00ff00) || nchans > wpc->config.num_channels)
return FALSE;
wpc->channel_layout = layout_tag;
if (wpc->channel_reordering) {
free (wpc->channel_reordering);
wpc->channel_reordering = NULL;
}
if (nchans && reorder) {
int min_index = 256, i;
for (i = 0; i < nchans; ++i)
if (reorder [i] < min_index)
min_index = reorder [i];
wpc->channel_reordering = malloc (nchans);
if (wpc->channel_reordering)
for (i = 0; i < nchans; ++i)
wpc->channel_reordering [i] = reorder [i] - min_index;
}
return TRUE;
}
static int write_metadata_block (WavpackContext *wpc);
int WavpackPackInit (WavpackContext *wpc)
{
if (wpc->metabytes > 16384) write_metadata_block (wpc);
if (wpc->dsd_multiplier) {
wpc->block_samples = (wpc->config.sample_rate % 7) ? 48000 : 44100;
if (wpc->config.flags & CONFIG_HIGH_FLAG)
wpc->block_samples /= 2;
if (wpc->config.num_channels == 1)
wpc->block_samples *= 2;
while (wpc->block_samples > 12000 && (int64_t) wpc->block_samples * wpc->config.num_channels > 300000)
wpc->block_samples /= 2;
}
else {
int divisor = (wpc->config.flags & CONFIG_HIGH_FLAG) ? 2 : 4;
while (wpc->config.sample_rate % divisor)
divisor--;
wpc->block_samples = wpc->config.sample_rate / divisor;
while (wpc->block_samples > 12000 && (int64_t) wpc->block_samples * wpc->config.num_channels > 75000)
wpc->block_samples /= 2;
while ((int64_t) wpc->block_samples * wpc->config.num_channels < 20000)
wpc->block_samples *= 2;
}
if (wpc->config.block_samples) {
if ((wpc->config.flags & CONFIG_MERGE_BLOCKS) &&
wpc->block_samples > (uint32_t) wpc->config.block_samples) {
wpc->block_boundary = wpc->config.block_samples;
wpc->block_samples /= wpc->config.block_samples;
wpc->block_samples *= wpc->config.block_samples;
}
else
wpc->block_samples = wpc->config.block_samples;
}
wpc->ave_block_samples = wpc->block_samples;
wpc->max_samples = wpc->block_samples + (wpc->block_samples >> 1);
for (wpc->current_stream = 0; wpc->current_stream < wpc->num_streams; wpc->current_stream++) {
WavpackStream *wps = wpc->streams [wpc->current_stream];
wps->sample_buffer = malloc (wpc->max_samples * (wps->wphdr.flags & MONO_FLAG ? 4 : 8));
#ifdef ENABLE_DSD
if (wps->wphdr.flags & DSD_FLAG)
pack_dsd_init (wpc);
else
#endif
pack_init (wpc);
}
return TRUE;
}
static int pack_streams (WavpackContext *wpc, uint32_t block_samples);
static int create_riff_header (WavpackContext *wpc, int64_t total_samples, void *outbuffer);
int WavpackPackSamples (WavpackContext *wpc, int32_t *sample_buffer, uint32_t sample_count)
{
int nch = wpc->config.num_channels;
while (sample_count) {
int32_t *source_pointer = sample_buffer;
unsigned int samples_to_copy;
if (!wpc->riff_header_added && !wpc->riff_header_created && !wpc->file_format) {
char riff_header [128];
if (!add_to_metadata (wpc, riff_header, create_riff_header (wpc, wpc->total_samples, riff_header), ID_RIFF_HEADER))
return FALSE;
}
if (wpc->acc_samples + sample_count > wpc->max_samples)
samples_to_copy = wpc->max_samples - wpc->acc_samples;
else
samples_to_copy = sample_count;
for (wpc->current_stream = 0; wpc->current_stream < wpc->num_streams; wpc->current_stream++) {
WavpackStream *wps = wpc->streams [wpc->current_stream];
int32_t *dptr, *sptr, cnt;
dptr = wps->sample_buffer + wpc->acc_samples * (wps->wphdr.flags & MONO_FLAG ? 1 : 2);
sptr = source_pointer;
cnt = samples_to_copy;
if (wps->wphdr.flags & MONO_FLAG) {
switch (wpc->config.bytes_per_sample) {
case 1:
while (cnt--) {
*dptr++ = (signed char) *sptr;
sptr += nch;
}
break;
case 2:
while (cnt--) {
*dptr++ = (int16_t) *sptr;
sptr += nch;
}
break;
case 3:
while (cnt--) {
*dptr++ = (int32_t)((uint32_t)*sptr << 8) >> 8;
sptr += nch;
}
break;
default:
while (cnt--) {
*dptr++ = *sptr;
sptr += nch;
}
}
source_pointer++;
}
else {
switch (wpc->config.bytes_per_sample) {
case 1:
while (cnt--) {
*dptr++ = (signed char) sptr [0];
*dptr++ = (signed char) sptr [1];
sptr += nch;
}
break;
case 2:
while (cnt--) {
*dptr++ = (int16_t) sptr [0];
*dptr++ = (int16_t) sptr [1];
sptr += nch;
}
break;
case 3:
while (cnt--) {
*dptr++ = (int32_t)((uint32_t)sptr [0] << 8) >> 8;
*dptr++ = (int32_t)((uint32_t)sptr [1] << 8) >> 8;
sptr += nch;
}
break;
default:
while (cnt--) {
*dptr++ = sptr [0];
*dptr++ = sptr [1];
sptr += nch;
}
}
source_pointer += 2;
}
}
sample_buffer += samples_to_copy * nch;
sample_count -= samples_to_copy;
if ((wpc->acc_samples += samples_to_copy) == wpc->max_samples &&
!pack_streams (wpc, wpc->block_samples))
return FALSE;
}
return TRUE;
}
int WavpackFlushSamples (WavpackContext *wpc)
{
while (wpc->acc_samples) {
uint32_t block_samples;
if (wpc->acc_samples > wpc->block_samples)
block_samples = wpc->acc_samples / 2;
else
block_samples = wpc->acc_samples;
if (!pack_streams (wpc, block_samples))
return FALSE;
}
if (wpc->metacount)
write_metadata_block (wpc);
return TRUE;
}
int WavpackAddWrapper (WavpackContext *wpc, void *data, uint32_t bcount)
{
int64_t index = WavpackGetSampleIndex64 (wpc);
unsigned char meta_id;
if (!index || index == -1) {
wpc->riff_header_added = TRUE;
meta_id = wpc->file_format ? ID_ALT_HEADER : ID_RIFF_HEADER;
}
else {
wpc->riff_trailer_bytes += bcount;
meta_id = wpc->file_format ? ID_ALT_TRAILER : ID_RIFF_TRAILER;
}
return add_to_metadata (wpc, data, bcount, meta_id);
}
int WavpackStoreMD5Sum (WavpackContext *wpc, unsigned char data [16])
{
return add_to_metadata (wpc, data, 16, (wpc->config.qmode & 0xff) ? ID_ALT_MD5_CHECKSUM : ID_MD5_CHECKSUM);
}
#pragma pack(push,4)
typedef struct {
char ckID [4];
uint64_t chunkSize64;
} CS64Chunk;
typedef struct {
uint64_t riffSize64, dataSize64, sampleCount64;
uint32_t tableLength;
} DS64Chunk;
typedef struct {
char ckID [4];
uint32_t ckSize;
char junk [28];
} JunkChunk;
#pragma pack(pop)
#define DS64ChunkFormat "DDDL"
static int create_riff_header (WavpackContext *wpc, int64_t total_samples, void *outbuffer)
{
int do_rf64 = 0, write_junk = 1;
ChunkHeader ds64hdr, datahdr, fmthdr;
char *outptr = outbuffer;
RiffChunkHeader riffhdr;
DS64Chunk ds64_chunk;
JunkChunk junkchunk;
WaveHeader wavhdr;
int64_t total_data_bytes, total_riff_bytes;
int32_t channel_mask = wpc->config.channel_mask;
int32_t sample_rate = wpc->config.sample_rate;
int bytes_per_sample = wpc->config.bytes_per_sample;
int bits_per_sample = wpc->config.bits_per_sample;
int format = (wpc->config.float_norm_exp) ? 3 : 1;
int num_channels = wpc->config.num_channels;
int wavhdrsize = 16;
wpc->riff_header_created = TRUE;
if (format == 3 && wpc->config.float_norm_exp != 127) {
strcpy (wpc->error_message, "can't create valid RIFF wav header for non-normalized floating data!");
return FALSE;
}
if (total_samples == -1)
total_samples = 0x7ffff000 / (bytes_per_sample * num_channels);
total_data_bytes = total_samples * bytes_per_sample * num_channels;
if (total_data_bytes > 0xff000000) {
write_junk = 0;
do_rf64 = 1;
}
CLEAR (wavhdr);
wavhdr.FormatTag = format;
wavhdr.NumChannels = num_channels;
wavhdr.SampleRate = sample_rate;
wavhdr.BytesPerSecond = sample_rate * num_channels * bytes_per_sample;
wavhdr.BlockAlign = bytes_per_sample * num_channels;
wavhdr.BitsPerSample = bits_per_sample;
if (num_channels > 2 || channel_mask != 0x5 - num_channels) {
wavhdrsize = sizeof (wavhdr);
wavhdr.cbSize = 22;
wavhdr.ValidBitsPerSample = bits_per_sample;
wavhdr.SubFormat = format;
wavhdr.ChannelMask = channel_mask;
wavhdr.FormatTag = 0xfffe;
wavhdr.BitsPerSample = bytes_per_sample * 8;
wavhdr.GUID [4] = 0x10;
wavhdr.GUID [6] = 0x80;
wavhdr.GUID [9] = 0xaa;
wavhdr.GUID [11] = 0x38;
wavhdr.GUID [12] = 0x9b;
wavhdr.GUID [13] = 0x71;
}
memcpy (riffhdr.ckID, do_rf64 ? "RF64" : "RIFF", sizeof (riffhdr.ckID));
memcpy (riffhdr.formType, "WAVE", sizeof (riffhdr.formType));
total_riff_bytes = sizeof (riffhdr) + wavhdrsize + sizeof (datahdr) + total_data_bytes + wpc->riff_trailer_bytes;
if (do_rf64) total_riff_bytes += sizeof (ds64hdr) + sizeof (ds64_chunk);
if (write_junk) total_riff_bytes += sizeof (junkchunk);
memcpy (fmthdr.ckID, "fmt ", sizeof (fmthdr.ckID));
memcpy (datahdr.ckID, "data", sizeof (datahdr.ckID));
fmthdr.ckSize = wavhdrsize;
if (write_junk) {
CLEAR (junkchunk);
memcpy (junkchunk.ckID, "junk", sizeof (junkchunk.ckID));
junkchunk.ckSize = sizeof (junkchunk) - 8;
WavpackNativeToLittleEndian (&junkchunk, ChunkHeaderFormat);
}
if (do_rf64) {
memcpy (ds64hdr.ckID, "ds64", sizeof (ds64hdr.ckID));
ds64hdr.ckSize = sizeof (ds64_chunk);
CLEAR (ds64_chunk);
ds64_chunk.riffSize64 = total_riff_bytes;
ds64_chunk.dataSize64 = total_data_bytes;
ds64_chunk.sampleCount64 = total_samples;
riffhdr.ckSize = (uint32_t) -1;
datahdr.ckSize = (uint32_t) -1;
WavpackNativeToLittleEndian (&ds64hdr, ChunkHeaderFormat);
WavpackNativeToLittleEndian (&ds64_chunk, DS64ChunkFormat);
}
else {
riffhdr.ckSize = (uint32_t) total_riff_bytes;
datahdr.ckSize = (uint32_t) total_data_bytes;
}
WavpackNativeToLittleEndian (&riffhdr, ChunkHeaderFormat);
WavpackNativeToLittleEndian (&fmthdr, ChunkHeaderFormat);
WavpackNativeToLittleEndian (&wavhdr, WaveHeaderFormat);
WavpackNativeToLittleEndian (&datahdr, ChunkHeaderFormat);
outptr = (char *) memcpy (outptr, &riffhdr, sizeof (riffhdr)) + sizeof (riffhdr);
if (do_rf64) {
outptr = (char *) memcpy (outptr, &ds64hdr, sizeof (ds64hdr)) + sizeof (ds64hdr);
outptr = (char *) memcpy (outptr, &ds64_chunk, sizeof (ds64_chunk)) + sizeof (ds64_chunk);
}
if (write_junk)
outptr = (char *) memcpy (outptr, &junkchunk, sizeof (junkchunk)) + sizeof (junkchunk);
outptr = (char *) memcpy (outptr, &fmthdr, sizeof (fmthdr)) + sizeof (fmthdr);
outptr = (char *) memcpy (outptr, &wavhdr, wavhdrsize) + wavhdrsize;
outptr = (char *) memcpy (outptr, &datahdr, sizeof (datahdr)) + sizeof (datahdr);
return (int)(outptr - (char *) outbuffer);
}
static int block_add_checksum (unsigned char *buffer_start, unsigned char *buffer_end, int bytes);
static int pack_streams (WavpackContext *wpc, uint32_t block_samples)
{
uint32_t max_blocksize, max_chans = 1, bcount;
unsigned char *outbuff, *outend, *out2buff, *out2end;
int result = TRUE, i;
for (i = 0; i < wpc->num_streams; i++)
if (!(wpc->streams [i]->wphdr.flags & MONO_FLAG)) {
max_chans = 2;
break;
}
max_blocksize = block_samples * max_chans * ((wpc->streams [0]->wphdr.flags & BYTES_STORED) + 1);
if ((wpc->config.flags & CONFIG_FLOAT_DATA) && !(wpc->config.flags & CONFIG_SKIP_WVX))
max_blocksize += max_blocksize; else
max_blocksize += max_blocksize >> 2;
max_blocksize += wpc->metabytes + 1024; max_blocksize += max_blocksize & 1;
out2buff = (wpc->wvc_flag) ? malloc (max_blocksize) : NULL;
out2end = (wpc->wvc_flag) ? out2buff + max_blocksize : NULL;
outbuff = malloc (max_blocksize);
outend = outbuff + max_blocksize;
for (wpc->current_stream = 0; wpc->current_stream < wpc->num_streams; wpc->current_stream++) {
WavpackStream *wps = wpc->streams [wpc->current_stream];
uint32_t flags = wps->wphdr.flags;
flags &= ~MAG_MASK;
flags += (1U << MAG_LSB) * ((flags & BYTES_STORED) * 8 + 7);
SET_BLOCK_INDEX (wps->wphdr, wps->sample_index);
wps->wphdr.block_samples = block_samples;
wps->wphdr.flags = flags;
wps->block2buff = out2buff;
wps->block2end = out2end;
wps->blockbuff = outbuff;
wps->blockend = outend;
#ifdef ENABLE_DSD
if (flags & DSD_FLAG)
result = pack_dsd_block (wpc, wps->sample_buffer);
else
#endif
result = pack_block (wpc, wps->sample_buffer);
if (result) {
result = block_add_checksum (outbuff, outend, (flags & HYBRID_FLAG) ? 2 : 4);
if (result && out2buff)
result = block_add_checksum (out2buff, out2end, 2);
}
wps->blockbuff = wps->block2buff = NULL;
if (wps->wphdr.block_samples != block_samples)
block_samples = wps->wphdr.block_samples;
if (!result) {
strcpy (wpc->error_message, "output buffer overflowed!");
break;
}
bcount = ((WavpackHeader *) outbuff)->ckSize + 8;
WavpackNativeToLittleEndian ((WavpackHeader *) outbuff, WavpackHeaderFormat);
result = wpc->blockout (wpc->wv_out, outbuff, bcount);
if (!result) {
strcpy (wpc->error_message, "can't write WavPack data, disk probably full!");
break;
}
wpc->filelen += bcount;
if (out2buff) {
bcount = ((WavpackHeader *) out2buff)->ckSize + 8;
WavpackNativeToLittleEndian ((WavpackHeader *) out2buff, WavpackHeaderFormat);
result = wpc->blockout (wpc->wvc_out, out2buff, bcount);
if (!result) {
strcpy (wpc->error_message, "can't write WavPack data, disk probably full!");
break;
}
wpc->file2len += bcount;
}
if (wpc->acc_samples != block_samples)
memmove (wps->sample_buffer, wps->sample_buffer + block_samples * (flags & MONO_FLAG ? 1 : 2),
(wpc->acc_samples - block_samples) * sizeof (int32_t) * (flags & MONO_FLAG ? 1 : 2));
}
wpc->current_stream = 0;
wpc->ave_block_samples = (wpc->ave_block_samples * 0x7 + block_samples + 0x4) >> 3;
wpc->acc_samples -= block_samples;
free (outbuff);
if (out2buff)
free (out2buff);
return result;
}
static void block_update_checksum (unsigned char *buffer_start);
void WavpackUpdateNumSamples (WavpackContext *wpc, void *first_block)
{
uint32_t wrapper_size;
if (wpc->riff_header_created && WavpackGetWrapperLocation (first_block, &wrapper_size)) {
unsigned char riff_header [128];
if (wrapper_size == create_riff_header (wpc, WavpackGetSampleIndex64 (wpc), riff_header))
memcpy (WavpackGetWrapperLocation (first_block, NULL), riff_header, wrapper_size);
}
WavpackLittleEndianToNative (first_block, WavpackHeaderFormat);
SET_TOTAL_SAMPLES (* (WavpackHeader *) first_block, WavpackGetSampleIndex64 (wpc));
block_update_checksum (first_block);
WavpackNativeToLittleEndian (first_block, WavpackHeaderFormat);
}
static void *find_metadata (void *wavpack_block, int desired_id, uint32_t *size);
void *WavpackGetWrapperLocation (void *first_block, uint32_t *size)
{
void *loc;
WavpackLittleEndianToNative (first_block, WavpackHeaderFormat);
loc = find_metadata (first_block, ID_RIFF_HEADER, size);
if (!loc)
loc = find_metadata (first_block, ID_ALT_HEADER, size);
WavpackNativeToLittleEndian (first_block, WavpackHeaderFormat);
return loc;
}
static void *find_metadata (void *wavpack_block, int desired_id, uint32_t *size)
{
WavpackHeader *wphdr = wavpack_block;
unsigned char *dp, meta_id, c1, c2;
int32_t bcount, meta_bc;
if (strncmp (wphdr->ckID, "wvpk", 4))
return NULL;
bcount = wphdr->ckSize - sizeof (WavpackHeader) + 8;
dp = (unsigned char *)(wphdr + 1);
while (bcount >= 2) {
meta_id = *dp++;
c1 = *dp++;
meta_bc = c1 << 1;
bcount -= 2;
if (meta_id & ID_LARGE) {
if (bcount < 2)
break;
c1 = *dp++;
c2 = *dp++;
meta_bc += ((uint32_t) c1 << 9) + ((uint32_t) c2 << 17);
bcount -= 2;
}
if ((meta_id & ID_UNIQUE) == desired_id) {
if ((bcount - meta_bc) >= 0) {
if (size)
*size = meta_bc - ((meta_id & ID_ODD_SIZE) ? 1 : 0);
return dp;
}
else
return NULL;
}
bcount -= meta_bc;
dp += meta_bc;
}
return NULL;
}
int copy_metadata (WavpackMetadata *wpmd, unsigned char *buffer_start, unsigned char *buffer_end)
{
uint32_t mdsize = wpmd->byte_length + (wpmd->byte_length & 1);
WavpackHeader *wphdr = (WavpackHeader *) buffer_start;
mdsize += (wpmd->byte_length > 510) ? 4 : 2;
buffer_start += wphdr->ckSize + 8;
if (buffer_start + mdsize >= buffer_end)
return FALSE;
buffer_start [0] = wpmd->id | (wpmd->byte_length & 1 ? ID_ODD_SIZE : 0);
buffer_start [1] = (wpmd->byte_length + 1) >> 1;
if (wpmd->byte_length > 510) {
buffer_start [0] |= ID_LARGE;
buffer_start [2] = (wpmd->byte_length + 1) >> 9;
buffer_start [3] = (wpmd->byte_length + 1) >> 17;
}
if (wpmd->data && wpmd->byte_length) {
memcpy (buffer_start + (wpmd->byte_length > 510 ? 4 : 2), wpmd->data, wpmd->byte_length);
if (wpmd->byte_length & 1) buffer_start [mdsize - 1] = 0;
}
wphdr->ckSize += mdsize;
return TRUE;
}
static int add_to_metadata (WavpackContext *wpc, void *data, uint32_t bcount, unsigned char id)
{
WavpackMetadata *mdp;
unsigned char *src = data;
while (bcount) {
if (wpc->metacount) {
uint32_t bc = bcount;
mdp = wpc->metadata + wpc->metacount - 1;
if (mdp->id == id) {
if (wpc->metabytes + bcount > 1000000)
bc = 1000000 - wpc->metabytes;
mdp->data = realloc (mdp->data, mdp->byte_length + bc);
memcpy ((char *) mdp->data + mdp->byte_length, src, bc);
mdp->byte_length += bc;
wpc->metabytes += bc;
bcount -= bc;
src += bc;
if (wpc->metabytes >= 1000000 && !write_metadata_block (wpc))
return FALSE;
}
}
if (bcount) {
wpc->metadata = realloc (wpc->metadata, (wpc->metacount + 1) * sizeof (WavpackMetadata));
mdp = wpc->metadata + wpc->metacount++;
mdp->byte_length = 0;
mdp->data = NULL;
mdp->id = id;
}
}
return TRUE;
}
static char *write_metadata (WavpackMetadata *wpmd, char *outdata)
{
unsigned char id = wpmd->id, wordlen [3];
wordlen [0] = (wpmd->byte_length + 1) >> 1;
wordlen [1] = (wpmd->byte_length + 1) >> 9;
wordlen [2] = (wpmd->byte_length + 1) >> 17;
if (wpmd->byte_length & 1)
id |= ID_ODD_SIZE;
if (wordlen [1] || wordlen [2])
id |= ID_LARGE;
*outdata++ = id;
*outdata++ = wordlen [0];
if (id & ID_LARGE) {
*outdata++ = wordlen [1];
*outdata++ = wordlen [2];
}
if (wpmd->data && wpmd->byte_length) {
memcpy (outdata, wpmd->data, wpmd->byte_length);
outdata += wpmd->byte_length;
if (wpmd->byte_length & 1)
*outdata++ = 0;
}
return outdata;
}
static int write_metadata_block (WavpackContext *wpc)
{
char *block_buff, *block_ptr;
WavpackHeader *wphdr;
if (wpc->metacount) {
int metacount = wpc->metacount, block_size = sizeof (WavpackHeader);
WavpackMetadata *wpmdp = wpc->metadata;
while (metacount--) {
block_size += wpmdp->byte_length + (wpmdp->byte_length & 1);
block_size += (wpmdp->byte_length > 510) ? 4 : 2;
wpmdp++;
}
wphdr = (WavpackHeader *) (block_buff = malloc (block_size + 6));
CLEAR (*wphdr);
memcpy (wphdr->ckID, "wvpk", 4);
SET_TOTAL_SAMPLES (*wphdr, wpc->total_samples);
wphdr->version = wpc->stream_version;
wphdr->ckSize = block_size - 8;
wphdr->block_samples = 0;
block_ptr = (char *)(wphdr + 1);
wpmdp = wpc->metadata;
while (wpc->metacount) {
block_ptr = write_metadata (wpmdp, block_ptr);
wpc->metabytes -= wpmdp->byte_length;
free_metadata (wpmdp++);
wpc->metacount--;
}
free (wpc->metadata);
wpc->metadata = NULL;
block_add_checksum ((unsigned char *) block_buff, (unsigned char *) block_buff + (block_size += 6), 4);
WavpackNativeToLittleEndian ((WavpackHeader *) block_buff, WavpackHeaderFormat);
if (!wpc->blockout (wpc->wv_out, block_buff, block_size)) {
free (block_buff);
strcpy (wpc->error_message, "can't write WavPack data, disk probably full!");
return FALSE;
}
free (block_buff);
}
return TRUE;
}
void free_metadata (WavpackMetadata *wpmd)
{
if (wpmd->data) {
free (wpmd->data);
wpmd->data = NULL;
}
}
static int block_add_checksum (unsigned char *buffer_start, unsigned char *buffer_end, int bytes)
{
WavpackHeader *wphdr = (WavpackHeader *) buffer_start;
#ifdef BITSTREAM_SHORTS
uint16_t *csptr = (uint16_t*) buffer_start;
#else
unsigned char *csptr = buffer_start;
#endif
int bcount = wphdr->ckSize + 8, wcount;
uint32_t csum = (uint32_t) -1;
if (bytes != 2 && bytes != 4)
return FALSE;
if (bcount < sizeof (WavpackHeader) || (bcount & 1) || buffer_start + bcount + 2 + bytes > buffer_end)
return FALSE;
wphdr->flags |= HAS_CHECKSUM;
wphdr->ckSize += 2 + bytes;
wcount = bcount >> 1;
#ifdef BITSTREAM_SHORTS
while (wcount--)
csum = (csum * 3) + *csptr++;
#else
WavpackNativeToLittleEndian ((WavpackHeader *) buffer_start, WavpackHeaderFormat);
while (wcount--) {
csum = (csum * 3) + csptr [0] + (csptr [1] << 8);
csptr += 2;
}
WavpackLittleEndianToNative ((WavpackHeader *) buffer_start, WavpackHeaderFormat);
#endif
buffer_start += bcount;
*buffer_start++ = ID_BLOCK_CHECKSUM;
*buffer_start++ = bytes >> 1;
if (bytes == 4) {
*buffer_start++ = csum;
*buffer_start++ = csum >> 8;
*buffer_start++ = csum >> 16;
*buffer_start++ = csum >> 24;
}
else {
csum ^= csum >> 16;
*buffer_start++ = csum;
*buffer_start++ = csum >> 8;
}
return TRUE;
}
static void block_update_checksum (unsigned char *buffer_start)
{
WavpackHeader *wphdr = (WavpackHeader *) buffer_start;
unsigned char *dp, meta_id, c1, c2;
uint32_t bcount, meta_bc;
if (!(wphdr->flags & HAS_CHECKSUM))
return;
bcount = wphdr->ckSize - sizeof (WavpackHeader) + 8;
dp = (unsigned char *)(wphdr + 1);
while (bcount >= 2) {
meta_id = *dp++;
c1 = *dp++;
meta_bc = c1 << 1;
bcount -= 2;
if (meta_id & ID_LARGE) {
if (bcount < 2)
return;
c1 = *dp++;
c2 = *dp++;
meta_bc += ((uint32_t) c1 << 9) + ((uint32_t) c2 << 17);
bcount -= 2;
}
if (bcount < meta_bc)
return;
if ((meta_id & ID_UNIQUE) == ID_BLOCK_CHECKSUM) {
#ifdef BITSTREAM_SHORTS
uint16_t *csptr = (uint16_t*) buffer_start;
#else
unsigned char *csptr = buffer_start;
#endif
int wcount = (int)(dp - 2 - buffer_start) >> 1;
uint32_t csum = (uint32_t) -1;
if ((meta_id & ID_ODD_SIZE) || meta_bc < 2 || meta_bc > 4)
return;
#ifdef BITSTREAM_SHORTS
while (wcount--)
csum = (csum * 3) + *csptr++;
#else
WavpackNativeToLittleEndian ((WavpackHeader *) buffer_start, WavpackHeaderFormat);
while (wcount--) {
csum = (csum * 3) + csptr [0] + (csptr [1] << 8);
csptr += 2;
}
WavpackLittleEndianToNative ((WavpackHeader *) buffer_start, WavpackHeaderFormat);
#endif
if (meta_bc == 4) {
*dp++ = csum;
*dp++ = csum >> 8;
*dp++ = csum >> 16;
*dp++ = csum >> 24;
return;
}
else {
csum ^= csum >> 16;
*dp++ = csum;
*dp++ = csum >> 8;
return;
}
}
bcount -= meta_bc;
dp += meta_bc;
}
}