2016-08-28 20:03:54 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// **** WAVPACK **** //
|
|
|
|
// Hybrid Lossless Wavefile Compressor //
|
|
|
|
// Copyright (c) 1998 - 2013 Conifer Software. //
|
|
|
|
// All Rights Reserved. //
|
|
|
|
// Distributed under the BSD Software License (see license.txt) //
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// common_utils.c
|
|
|
|
|
|
|
|
// This module provides a lot of the trivial WavPack API functions and several
|
|
|
|
// functions that are common to both reading and writing WavPack files (like
|
|
|
|
// WavpackCloseFile()). Functions here are restricted to those that have few
|
2020-03-22 07:15:45 +00:00
|
|
|
// external dependencies and this is done so that applications that statically
|
2016-08-28 20:03:54 +00:00
|
|
|
// link to the WavPack library (like the command-line utilities on Windows)
|
|
|
|
// do not need to include the entire library image if they only use a subset
|
|
|
|
// of it. This module will be loaded for ANY WavPack application.
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
#include "wavpack_local.h"
|
|
|
|
|
|
|
|
#ifndef LIBWAVPACK_VERSION_STRING
|
|
|
|
#include "wavpack_version.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
///////////////////////////// local table storage ////////////////////////////
|
|
|
|
|
|
|
|
const uint32_t sample_rates [] = { 6000, 8000, 9600, 11025, 12000, 16000, 22050,
|
|
|
|
24000, 32000, 44100, 48000, 64000, 88200, 96000, 192000 };
|
|
|
|
|
|
|
|
///////////////////////////// executable code ////////////////////////////////
|
|
|
|
|
|
|
|
// This function obtains general information about an open input file and
|
|
|
|
// returns a mask with the following bit values:
|
|
|
|
|
|
|
|
// MODE_WVC: a .wvc file has been found and will be used for lossless
|
|
|
|
// MODE_LOSSLESS: file is lossless (either pure or hybrid)
|
|
|
|
// MODE_HYBRID: file is hybrid mode (either lossy or lossless)
|
|
|
|
// MODE_FLOAT: audio data is 32-bit ieee floating point
|
2020-03-22 07:15:45 +00:00
|
|
|
// MODE_VALID_TAG: file contains a valid ID3v1 or APEv2 tag
|
2016-08-28 20:03:54 +00:00
|
|
|
// MODE_HIGH: file was created in "high" mode (information only)
|
|
|
|
// MODE_FAST: file was created in "fast" mode (information only)
|
|
|
|
// MODE_EXTRA: file was created using "extra" mode (information only)
|
|
|
|
// MODE_APETAG: file contains a valid APEv2 tag
|
|
|
|
// MODE_SFX: file was created as a "self-extracting" executable
|
|
|
|
// MODE_VERY_HIGH: file was created in the "very high" mode (or in
|
|
|
|
// the "high" mode prior to 4.4)
|
|
|
|
// MODE_MD5: file contains an MD5 checksum
|
|
|
|
// MODE_XMODE: level used for extra mode (1-6, 0=unknown)
|
|
|
|
// MODE_DNS: dynamic noise shaping
|
|
|
|
|
|
|
|
int WavpackGetMode (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
int mode = 0;
|
|
|
|
|
|
|
|
if (wpc) {
|
|
|
|
if (wpc->config.flags & CONFIG_HYBRID_FLAG)
|
|
|
|
mode |= MODE_HYBRID;
|
|
|
|
else if (!(wpc->config.flags & CONFIG_LOSSY_MODE))
|
|
|
|
mode |= MODE_LOSSLESS;
|
|
|
|
|
|
|
|
if (wpc->wvc_flag)
|
|
|
|
mode |= (MODE_LOSSLESS | MODE_WVC);
|
|
|
|
|
|
|
|
if (wpc->lossy_blocks)
|
|
|
|
mode &= ~MODE_LOSSLESS;
|
|
|
|
|
|
|
|
if (wpc->config.flags & CONFIG_FLOAT_DATA)
|
|
|
|
mode |= MODE_FLOAT;
|
|
|
|
|
|
|
|
if (wpc->config.flags & (CONFIG_HIGH_FLAG | CONFIG_VERY_HIGH_FLAG)) {
|
|
|
|
mode |= MODE_HIGH;
|
|
|
|
|
|
|
|
if ((wpc->config.flags & CONFIG_VERY_HIGH_FLAG) ||
|
|
|
|
(wpc->streams && wpc->streams [0] && wpc->streams [0]->wphdr.version < 0x405))
|
|
|
|
mode |= MODE_VERY_HIGH;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wpc->config.flags & CONFIG_FAST_FLAG)
|
|
|
|
mode |= MODE_FAST;
|
|
|
|
|
|
|
|
if (wpc->config.flags & CONFIG_EXTRA_MODE)
|
|
|
|
mode |= (MODE_EXTRA | (wpc->config.xmode << 12));
|
|
|
|
|
|
|
|
if (wpc->config.flags & CONFIG_CREATE_EXE)
|
|
|
|
mode |= MODE_SFX;
|
|
|
|
|
|
|
|
if (wpc->config.flags & CONFIG_MD5_CHECKSUM)
|
|
|
|
mode |= MODE_MD5;
|
|
|
|
|
|
|
|
if ((wpc->config.flags & CONFIG_HYBRID_FLAG) && (wpc->config.flags & CONFIG_DYNAMIC_SHAPING) &&
|
|
|
|
wpc->streams && wpc->streams [0] && wpc->streams [0]->wphdr.version >= 0x407)
|
|
|
|
mode |= MODE_DNS;
|
|
|
|
|
|
|
|
#ifndef NO_TAGS
|
|
|
|
if (valid_tag (&wpc->m_tag)) {
|
|
|
|
mode |= MODE_VALID_TAG;
|
|
|
|
|
|
|
|
if (valid_tag (&wpc->m_tag) == 'A')
|
|
|
|
mode |= MODE_APETAG;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
mode |= (wpc->config.qmode << 16) & 0xFF0000;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This function obtains information about specific file features that were
|
|
|
|
// added for version 5.0, specifically qualifications added to support CAF
|
|
|
|
// and DSD files. Except for indicating the presence of DSD data, these
|
|
|
|
// bits are meant to simply indicate the format of the data in the original
|
|
|
|
// source file and do NOT indicate how the library will return the data to
|
|
|
|
// the appication (which is always the same). This means that in general an
|
|
|
|
// application that simply wants to play or process the audio data need not
|
|
|
|
// be concerned about these. If the file is DSD audio, then either of the
|
|
|
|
// QMDOE_DSD_LSB_FIRST or QMODE_DSD_MSB_FIRST bits will be set (but the
|
|
|
|
// DSD audio is always returned to the caller MSB first).
|
|
|
|
|
|
|
|
// QMODE_BIG_ENDIAN 0x1 // big-endian data format (opposite of WAV format)
|
|
|
|
// QMODE_SIGNED_BYTES 0x2 // 8-bit audio data is signed (opposite of WAV format)
|
|
|
|
// QMODE_UNSIGNED_WORDS 0x4 // audio data (other than 8-bit) is unsigned (opposite of WAV format)
|
|
|
|
// QMODE_REORDERED_CHANS 0x8 // source channels were not Microsoft order, so they were reordered
|
|
|
|
// QMODE_DSD_LSB_FIRST 0x10 // DSD bytes, LSB first (most Sony .dsf files)
|
|
|
|
// QMODE_DSD_MSB_FIRST 0x20 // DSD bytes, MSB first (Philips .dff files)
|
|
|
|
// QMODE_DSD_IN_BLOCKS 0x40 // DSD data is blocked by channels (Sony .dsf only)
|
|
|
|
|
|
|
|
int WavpackGetQualifyMode (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
return wpc->config.qmode & 0xFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This function returns a pointer to a string describing the last error
|
|
|
|
// generated by WavPack.
|
|
|
|
|
|
|
|
char *WavpackGetErrorMessage (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
return wpc->error_message;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get total number of samples contained in the WavPack file, or -1 if unknown
|
|
|
|
|
|
|
|
uint32_t WavpackGetNumSamples (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
return (uint32_t) WavpackGetNumSamples64 (wpc);
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t WavpackGetNumSamples64 (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
return wpc ? wpc->total_samples : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the current sample index position, or -1 if unknown
|
|
|
|
|
|
|
|
uint32_t WavpackGetSampleIndex (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
return (uint32_t) WavpackGetSampleIndex64 (wpc);
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t WavpackGetSampleIndex64 (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
if (wpc) {
|
2020-03-22 07:15:45 +00:00
|
|
|
#ifdef ENABLE_LEGACY
|
2016-08-28 20:03:54 +00:00
|
|
|
if (wpc->stream3)
|
|
|
|
return get_sample_index3 (wpc);
|
|
|
|
else if (wpc->streams && wpc->streams [0])
|
|
|
|
return wpc->streams [0]->sample_index;
|
|
|
|
#else
|
|
|
|
if (wpc->streams && wpc->streams [0])
|
|
|
|
return wpc->streams [0]->sample_index;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the number of errors encountered so far
|
|
|
|
|
|
|
|
int WavpackGetNumErrors (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
return wpc ? wpc->crc_errors : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// return TRUE if any uncorrected lossy blocks were actually written or read
|
|
|
|
|
|
|
|
int WavpackLossyBlocks (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
return wpc ? wpc->lossy_blocks : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate the progress through the file as a double from 0.0 (for begin)
|
|
|
|
// to 1.0 (for done). A return value of -1.0 indicates that the progress is
|
|
|
|
// unknown.
|
|
|
|
|
|
|
|
double WavpackGetProgress (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
if (wpc && wpc->total_samples != -1 && wpc->total_samples != 0)
|
|
|
|
return (double) WavpackGetSampleIndex64 (wpc) / wpc->total_samples;
|
|
|
|
else
|
|
|
|
return -1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the total size of the WavPack file(s) in bytes.
|
|
|
|
|
|
|
|
uint32_t WavpackGetFileSize (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
return (uint32_t) (wpc ? wpc->filelen + wpc->file2len : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t WavpackGetFileSize64 (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
return wpc ? wpc->filelen + wpc->file2len : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate the ratio of the specified WavPack file size to the size of the
|
|
|
|
// original audio data as a double greater than 0.0 and (usually) smaller than
|
|
|
|
// 1.0. A value greater than 1.0 represents "negative" compression and a
|
|
|
|
// return value of 0.0 indicates that the ratio cannot be determined.
|
|
|
|
|
|
|
|
double WavpackGetRatio (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
if (wpc && wpc->total_samples != -1 && wpc->filelen) {
|
|
|
|
double output_size = (double) wpc->total_samples * wpc->config.num_channels *
|
|
|
|
wpc->config.bytes_per_sample;
|
|
|
|
double input_size = (double) wpc->filelen + wpc->file2len;
|
|
|
|
|
|
|
|
if (output_size >= 1.0 && input_size >= 1.0)
|
|
|
|
return input_size / output_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate the average bitrate of the WavPack file in bits per second. A
|
|
|
|
// return of 0.0 indicates that the bitrate cannot be determined. An option is
|
|
|
|
// provided to use (or not use) any attendant .wvc file.
|
|
|
|
|
|
|
|
double WavpackGetAverageBitrate (WavpackContext *wpc, int count_wvc)
|
|
|
|
{
|
|
|
|
if (wpc && wpc->total_samples != -1 && wpc->filelen) {
|
|
|
|
double output_time = (double) wpc->total_samples / WavpackGetSampleRate (wpc);
|
|
|
|
double input_size = (double) wpc->filelen + (count_wvc ? wpc->file2len : 0);
|
|
|
|
|
|
|
|
if (output_time >= 0.1 && input_size >= 1.0)
|
|
|
|
return input_size * 8.0 / output_time;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate the bitrate of the current WavPack file block in bits per second.
|
|
|
|
// This can be used for an "instant" bit display and gets updated from about
|
|
|
|
// 1 to 4 times per second. A return of 0.0 indicates that the bitrate cannot
|
|
|
|
// be determined.
|
|
|
|
|
|
|
|
double WavpackGetInstantBitrate (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
if (wpc && wpc->stream3)
|
|
|
|
return WavpackGetAverageBitrate (wpc, TRUE);
|
|
|
|
|
|
|
|
if (wpc && wpc->streams && wpc->streams [0] && wpc->streams [0]->wphdr.block_samples) {
|
|
|
|
double output_time = (double) wpc->streams [0]->wphdr.block_samples / WavpackGetSampleRate (wpc);
|
|
|
|
double input_size = 0;
|
|
|
|
int si;
|
|
|
|
|
|
|
|
for (si = 0; si < wpc->num_streams; ++si) {
|
|
|
|
if (wpc->streams [si]->blockbuff)
|
|
|
|
input_size += ((WavpackHeader *) wpc->streams [si]->blockbuff)->ckSize;
|
|
|
|
|
|
|
|
if (wpc->streams [si]->block2buff)
|
|
|
|
input_size += ((WavpackHeader *) wpc->streams [si]->block2buff)->ckSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (output_time > 0.0 && input_size >= 1.0)
|
|
|
|
return input_size * 8.0 / output_time;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This function allows retrieving the Core Audio File channel layout, many of which do not
|
|
|
|
// conform to the Microsoft ordering standard that WavPack requires internally (at least for
|
|
|
|
// those channels present in the "channel mask"). In addition to the layout tag, this function
|
|
|
|
// returns the reordering string (if stored in the file) to allow the unpacker to reorder the
|
|
|
|
// channels back to the specified layout (if it wants to restore the CAF order). The number of
|
|
|
|
// channels in the layout is determined from the lower nybble of the layout word (and should
|
|
|
|
// probably match the number of channels in the file), and if a reorder string is requested
|
|
|
|
// then that much space must be allocated. Note that all the reordering is actually done
|
|
|
|
// outside of this library, and that if reordering is done then the appropriate qmode bit
|
|
|
|
// will be set.
|
2020-03-22 07:15:45 +00:00
|
|
|
//
|
|
|
|
// Note: Normally this function would not be used by an application unless it specifically
|
|
|
|
// wanted to restore a non-standard channel order (to check an MD5, for example) or obtain
|
|
|
|
// the Core Audio channel layout ID. For simple file decoding for playback, the channel_mask
|
|
|
|
// should provide all the information required unless there are non-Microsoft channels
|
|
|
|
// involved, in which case WavpackGetChannelIdentities() will provide the identities of
|
|
|
|
// the other channels (if they are known).
|
2016-08-28 20:03:54 +00:00
|
|
|
|
|
|
|
uint32_t WavpackGetChannelLayout (WavpackContext *wpc, unsigned char *reorder)
|
|
|
|
{
|
|
|
|
if ((wpc->channel_layout & 0xff) && wpc->channel_reordering && reorder)
|
|
|
|
memcpy (reorder, wpc->channel_reordering, wpc->channel_layout & 0xff);
|
|
|
|
|
|
|
|
return wpc->channel_layout;
|
|
|
|
}
|
|
|
|
|
2020-03-22 07:15:45 +00:00
|
|
|
// This function provides the identities of ALL the channels in the file, including the
|
|
|
|
// standard Microsoft channels (which come first, in order, and are numbered 1-18) and also
|
|
|
|
// any non-Microsoft channels (which can be in any order and have values from 33-254). The
|
|
|
|
// value 0x00 is invalid and 0xFF indicates an "unknown" or "unnassigned" channel. The
|
|
|
|
// string is NULL terminated so the caller must supply enough space for the number
|
|
|
|
// of channels indicated by WavpackGetNumChannels(), plus one.
|
|
|
|
//
|
|
|
|
// Note that this function returns the actual order of the channels in the Wavpack file
|
|
|
|
// (i.e., the order returned by WavpackUnpackSamples()). If the file includes a "reordering"
|
|
|
|
// string because the source file was not in Microsoft order that is NOT taken into account
|
|
|
|
// here and really only needs to be considered if doing an MD5 verification or if it's
|
|
|
|
// required to restore the original order/file (like wvunpack does).
|
|
|
|
|
|
|
|
void WavpackGetChannelIdentities (WavpackContext *wpc, unsigned char *identities)
|
|
|
|
{
|
|
|
|
int num_channels = wpc->config.num_channels, index = 1;
|
|
|
|
uint32_t channel_mask = wpc->config.channel_mask;
|
|
|
|
unsigned char *src = wpc->channel_identities;
|
|
|
|
|
|
|
|
while (num_channels--) {
|
|
|
|
if (channel_mask) {
|
|
|
|
while (!(channel_mask & 1)) {
|
|
|
|
channel_mask >>= 1;
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
|
|
|
|
*identities++ = index++;
|
|
|
|
channel_mask >>= 1;
|
|
|
|
}
|
|
|
|
else if (src && *src)
|
|
|
|
*identities++ = *src++;
|
|
|
|
else
|
|
|
|
*identities++ = 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
*identities = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// For local use only. Install a callback to be executed when WavpackCloseFile() is called,
|
|
|
|
// usually used to dump some statistics accumulated during encode or decode.
|
|
|
|
|
|
|
|
void install_close_callback (WavpackContext *wpc, void cb_func (void *wpc))
|
|
|
|
{
|
|
|
|
wpc->close_callback = cb_func;
|
|
|
|
}
|
|
|
|
|
2016-08-28 20:03:54 +00:00
|
|
|
// Close the specified WavPack file and release all resources used by it.
|
|
|
|
// Returns NULL.
|
|
|
|
|
|
|
|
WavpackContext *WavpackCloseFile (WavpackContext *wpc)
|
|
|
|
{
|
2020-03-22 07:15:45 +00:00
|
|
|
if (wpc->close_callback)
|
|
|
|
wpc->close_callback (wpc);
|
|
|
|
|
2016-08-28 20:03:54 +00:00
|
|
|
if (wpc->streams) {
|
|
|
|
free_streams (wpc);
|
|
|
|
|
|
|
|
if (wpc->streams [0])
|
|
|
|
free (wpc->streams [0]);
|
|
|
|
|
|
|
|
free (wpc->streams);
|
|
|
|
}
|
|
|
|
|
2020-03-22 07:15:45 +00:00
|
|
|
#ifdef ENABLE_LEGACY
|
2016-08-28 20:03:54 +00:00
|
|
|
if (wpc->stream3)
|
|
|
|
free_stream3 (wpc);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (wpc->reader && wpc->reader->close && wpc->wv_in)
|
|
|
|
wpc->reader->close (wpc->wv_in);
|
|
|
|
|
|
|
|
if (wpc->reader && wpc->reader->close && wpc->wvc_in)
|
|
|
|
wpc->reader->close (wpc->wvc_in);
|
|
|
|
|
|
|
|
WavpackFreeWrapper (wpc);
|
|
|
|
|
2020-03-22 07:15:45 +00:00
|
|
|
if (wpc->metadata) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < wpc->metacount; ++i)
|
|
|
|
if (wpc->metadata [i].data)
|
|
|
|
free (wpc->metadata [i].data);
|
|
|
|
|
|
|
|
free (wpc->metadata);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wpc->channel_identities)
|
|
|
|
free (wpc->channel_identities);
|
|
|
|
|
2016-08-28 20:03:54 +00:00
|
|
|
if (wpc->channel_reordering)
|
|
|
|
free (wpc->channel_reordering);
|
|
|
|
|
|
|
|
#ifndef NO_TAGS
|
|
|
|
free_tag (&wpc->m_tag);
|
|
|
|
#endif
|
|
|
|
|
2020-03-22 07:15:45 +00:00
|
|
|
#ifdef ENABLE_DSD
|
2016-08-28 20:03:54 +00:00
|
|
|
if (wpc->decimation_context)
|
|
|
|
decimate_dsd_destroy (wpc->decimation_context);
|
2020-03-22 07:15:45 +00:00
|
|
|
#endif
|
2016-08-28 20:03:54 +00:00
|
|
|
|
|
|
|
free (wpc);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// These routines are used to access (and free) header and trailer data that
|
|
|
|
// was retrieved from the Wavpack file. The header will be available before
|
|
|
|
// the samples are decoded and the trailer will be available after all samples
|
|
|
|
// have been read.
|
|
|
|
|
|
|
|
uint32_t WavpackGetWrapperBytes (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
return wpc ? wpc->wrapper_bytes : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char *WavpackGetWrapperData (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
return wpc ? wpc->wrapper_data : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WavpackFreeWrapper (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
if (wpc && wpc->wrapper_data) {
|
|
|
|
free (wpc->wrapper_data);
|
|
|
|
wpc->wrapper_data = NULL;
|
|
|
|
wpc->wrapper_bytes = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the sample rate of the specified WavPack file
|
|
|
|
|
|
|
|
uint32_t WavpackGetSampleRate (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
return wpc ? (wpc->dsd_multiplier ? wpc->config.sample_rate * wpc->dsd_multiplier : wpc->config.sample_rate) : 44100;
|
|
|
|
}
|
|
|
|
|
2020-03-22 07:15:45 +00:00
|
|
|
// Returns the native sample rate of the specified WavPack file
|
|
|
|
// (provides the native rate for DSD files rather than the "byte" rate that's used for
|
|
|
|
// seeking, duration, etc. and would generally be used just for user facing reports)
|
|
|
|
|
|
|
|
uint32_t WavpackGetNativeSampleRate (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
return wpc ? (wpc->dsd_multiplier ? wpc->config.sample_rate * wpc->dsd_multiplier * 8 : wpc->config.sample_rate) : 44100;
|
|
|
|
}
|
|
|
|
|
2016-08-28 20:03:54 +00:00
|
|
|
// Returns the number of channels of the specified WavPack file. Note that
|
|
|
|
// this is the actual number of channels contained in the file even if the
|
|
|
|
// OPEN_2CH_MAX flag was specified when the file was opened.
|
|
|
|
|
|
|
|
int WavpackGetNumChannels (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
return wpc ? wpc->config.num_channels : 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the standard Microsoft channel mask for the specified WavPack
|
|
|
|
// file. A value of zero indicates that there is no speaker assignment
|
|
|
|
// information.
|
|
|
|
|
|
|
|
int WavpackGetChannelMask (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
return wpc ? wpc->config.channel_mask : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the normalization value for floating point data (valid only
|
|
|
|
// if floating point data is present). A value of 127 indicates that
|
|
|
|
// the floating point range is +/- 1.0. Higher values indicate a
|
|
|
|
// larger floating point range.
|
|
|
|
|
|
|
|
int WavpackGetFloatNormExp (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
return wpc->config.float_norm_exp;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the actual number of valid bits per sample contained in the
|
|
|
|
// original file, which may or may not be a multiple of 8. Floating data
|
|
|
|
// always has 32 bits, integers may be from 1 to 32 bits each. 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.
|
|
|
|
|
|
|
|
int WavpackGetBitsPerSample (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
return wpc ? wpc->config.bits_per_sample : 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.
|
|
|
|
|
|
|
|
int WavpackGetBytesPerSample (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
return wpc ? wpc->config.bytes_per_sample : 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the OPEN_2CH_MAX flag is specified when opening the file, this function
|
|
|
|
// will return the actual number of channels decoded from the file (which may
|
|
|
|
// or may not be less than the actual number of channels, but will always be
|
|
|
|
// 1 or 2). Normally, this will be the front left and right channels of a
|
|
|
|
// multichannel file.
|
|
|
|
|
|
|
|
int WavpackGetReducedChannels (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
if (wpc)
|
|
|
|
return wpc->reduced_channels ? wpc->reduced_channels : wpc->config.num_channels;
|
|
|
|
else
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Free all memory allocated for raw WavPack blocks (for all allocated streams)
|
2020-03-22 07:15:45 +00:00
|
|
|
// and free all additional streams. This does not free the default stream ([0])
|
2016-08-28 20:03:54 +00:00
|
|
|
// which is always kept around.
|
|
|
|
|
|
|
|
void free_streams (WavpackContext *wpc)
|
|
|
|
{
|
|
|
|
int si = wpc->num_streams;
|
|
|
|
|
|
|
|
while (si--) {
|
|
|
|
if (wpc->streams [si]->blockbuff) {
|
|
|
|
free (wpc->streams [si]->blockbuff);
|
|
|
|
wpc->streams [si]->blockbuff = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wpc->streams [si]->block2buff) {
|
|
|
|
free (wpc->streams [si]->block2buff);
|
|
|
|
wpc->streams [si]->block2buff = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wpc->streams [si]->sample_buffer) {
|
|
|
|
free (wpc->streams [si]->sample_buffer);
|
|
|
|
wpc->streams [si]->sample_buffer = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wpc->streams [si]->dc.shaping_data) {
|
|
|
|
free (wpc->streams [si]->dc.shaping_data);
|
|
|
|
wpc->streams [si]->dc.shaping_data = NULL;
|
|
|
|
}
|
|
|
|
|
2020-03-22 07:15:45 +00:00
|
|
|
#ifdef ENABLE_DSD
|
|
|
|
free_dsd_tables (wpc->streams [si]);
|
|
|
|
#endif
|
2016-08-28 20:03:54 +00:00
|
|
|
|
|
|
|
if (si) {
|
|
|
|
wpc->num_streams--;
|
|
|
|
free (wpc->streams [si]);
|
|
|
|
wpc->streams [si] = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
wpc->current_stream = 0;
|
|
|
|
}
|
|
|
|
|
2020-03-22 07:15:45 +00:00
|
|
|
void free_dsd_tables (WavpackStream *wps)
|
|
|
|
{
|
|
|
|
if (wps->dsd.probabilities) {
|
|
|
|
free (wps->dsd.probabilities);
|
|
|
|
wps->dsd.probabilities = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wps->dsd.summed_probabilities) {
|
|
|
|
free (wps->dsd.summed_probabilities);
|
|
|
|
wps->dsd.summed_probabilities = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wps->dsd.lookup_buffer) {
|
|
|
|
free (wps->dsd.lookup_buffer);
|
|
|
|
wps->dsd.lookup_buffer = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wps->dsd.value_lookup) {
|
|
|
|
free (wps->dsd.value_lookup);
|
|
|
|
wps->dsd.value_lookup = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wps->dsd.ptable) {
|
|
|
|
free (wps->dsd.ptable);
|
|
|
|
wps->dsd.ptable = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-28 20:03:54 +00:00
|
|
|
void WavpackFloatNormalize (int32_t *values, int32_t num_values, int delta_exp)
|
|
|
|
{
|
|
|
|
f32 *fvalues = (f32 *) values;
|
|
|
|
int exp;
|
|
|
|
|
|
|
|
if (!delta_exp)
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (num_values--) {
|
|
|
|
if ((exp = get_exponent (*fvalues)) == 0 || exp + delta_exp <= 0)
|
|
|
|
*fvalues = 0;
|
|
|
|
else if (exp == 255 || (exp += delta_exp) >= 255) {
|
|
|
|
set_exponent (*fvalues, 255);
|
|
|
|
set_mantissa (*fvalues, 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
set_exponent (*fvalues, exp);
|
|
|
|
|
|
|
|
fvalues++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WavpackLittleEndianToNative (void *data, char *format)
|
|
|
|
{
|
|
|
|
unsigned char *cp = (unsigned char *) data;
|
|
|
|
int64_t temp;
|
|
|
|
|
|
|
|
while (*format) {
|
|
|
|
switch (*format) {
|
|
|
|
case 'D':
|
|
|
|
temp = cp [0] + ((int64_t) cp [1] << 8) + ((int64_t) cp [2] << 16) + ((int64_t) cp [3] << 24) +
|
|
|
|
((int64_t) cp [4] << 32) + ((int64_t) cp [5] << 40) + ((int64_t) cp [6] << 48) + ((int64_t) cp [7] << 56);
|
|
|
|
* (int64_t *) cp = temp;
|
|
|
|
cp += 8;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'L':
|
|
|
|
temp = cp [0] + ((int32_t) cp [1] << 8) + ((int32_t) cp [2] << 16) + ((int32_t) cp [3] << 24);
|
|
|
|
* (int32_t *) cp = (int32_t) temp;
|
|
|
|
cp += 4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'S':
|
|
|
|
temp = cp [0] + (cp [1] << 8);
|
|
|
|
* (int16_t *) cp = (int16_t) temp;
|
|
|
|
cp += 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (isdigit (*format))
|
|
|
|
cp += *format - '0';
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
format++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WavpackNativeToLittleEndian (void *data, char *format)
|
|
|
|
{
|
|
|
|
unsigned char *cp = (unsigned char *) data;
|
|
|
|
int64_t temp;
|
|
|
|
|
|
|
|
while (*format) {
|
|
|
|
switch (*format) {
|
|
|
|
case 'D':
|
|
|
|
temp = * (int64_t *) cp;
|
|
|
|
*cp++ = (unsigned char) temp;
|
|
|
|
*cp++ = (unsigned char) (temp >> 8);
|
|
|
|
*cp++ = (unsigned char) (temp >> 16);
|
|
|
|
*cp++ = (unsigned char) (temp >> 24);
|
|
|
|
*cp++ = (unsigned char) (temp >> 32);
|
|
|
|
*cp++ = (unsigned char) (temp >> 40);
|
|
|
|
*cp++ = (unsigned char) (temp >> 48);
|
|
|
|
*cp++ = (unsigned char) (temp >> 56);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'L':
|
|
|
|
temp = * (int32_t *) cp;
|
|
|
|
*cp++ = (unsigned char) temp;
|
|
|
|
*cp++ = (unsigned char) (temp >> 8);
|
|
|
|
*cp++ = (unsigned char) (temp >> 16);
|
|
|
|
*cp++ = (unsigned char) (temp >> 24);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'S':
|
|
|
|
temp = * (int16_t *) cp;
|
|
|
|
*cp++ = (unsigned char) temp;
|
|
|
|
*cp++ = (unsigned char) (temp >> 8);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (isdigit (*format))
|
|
|
|
cp += *format - '0';
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
format++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WavpackBigEndianToNative (void *data, char *format)
|
|
|
|
{
|
|
|
|
unsigned char *cp = (unsigned char *) data;
|
|
|
|
int64_t temp;
|
|
|
|
|
|
|
|
while (*format) {
|
|
|
|
switch (*format) {
|
|
|
|
case 'D':
|
|
|
|
temp = cp [7] + ((int64_t) cp [6] << 8) + ((int64_t) cp [5] << 16) + ((int64_t) cp [4] << 24) +
|
|
|
|
((int64_t) cp [3] << 32) + ((int64_t) cp [2] << 40) + ((int64_t) cp [1] << 48) + ((int64_t) cp [0] << 56);
|
|
|
|
* (int64_t *) cp = temp;
|
|
|
|
cp += 8;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'L':
|
|
|
|
temp = cp [3] + ((int32_t) cp [2] << 8) + ((int32_t) cp [1] << 16) + ((int32_t) cp [0] << 24);
|
|
|
|
* (int32_t *) cp = (int32_t) temp;
|
|
|
|
cp += 4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'S':
|
|
|
|
temp = cp [1] + (cp [0] << 8);
|
|
|
|
* (int16_t *) cp = (int16_t) temp;
|
|
|
|
cp += 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (isdigit (*format))
|
|
|
|
cp += *format - '0';
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
format++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WavpackNativeToBigEndian (void *data, char *format)
|
|
|
|
{
|
|
|
|
unsigned char *cp = (unsigned char *) data;
|
|
|
|
int64_t temp;
|
|
|
|
|
|
|
|
while (*format) {
|
|
|
|
switch (*format) {
|
|
|
|
case 'D':
|
|
|
|
temp = * (int64_t *) cp;
|
|
|
|
*cp++ = (unsigned char) (temp >> 56);
|
|
|
|
*cp++ = (unsigned char) (temp >> 48);
|
|
|
|
*cp++ = (unsigned char) (temp >> 40);
|
|
|
|
*cp++ = (unsigned char) (temp >> 32);
|
|
|
|
*cp++ = (unsigned char) (temp >> 24);
|
|
|
|
*cp++ = (unsigned char) (temp >> 16);
|
|
|
|
*cp++ = (unsigned char) (temp >> 8);
|
|
|
|
*cp++ = (unsigned char) temp;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'L':
|
|
|
|
temp = * (int32_t *) cp;
|
|
|
|
*cp++ = (unsigned char) (temp >> 24);
|
|
|
|
*cp++ = (unsigned char) (temp >> 16);
|
|
|
|
*cp++ = (unsigned char) (temp >> 8);
|
|
|
|
*cp++ = (unsigned char) temp;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'S':
|
|
|
|
temp = * (int16_t *) cp;
|
|
|
|
*cp++ = (unsigned char) (temp >> 8);
|
|
|
|
*cp++ = (unsigned char) temp;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (isdigit (*format))
|
|
|
|
cp += *format - '0';
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
format++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t WavpackGetLibraryVersion (void)
|
|
|
|
{
|
|
|
|
return (LIBWAVPACK_MAJOR<<16)
|
|
|
|
|(LIBWAVPACK_MINOR<<8)
|
|
|
|
|(LIBWAVPACK_MICRO<<0);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *WavpackGetLibraryVersionString (void)
|
|
|
|
{
|
|
|
|
return LIBWAVPACK_VERSION_STRING;
|
|
|
|
}
|
|
|
|
|