1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
////////////////////////////////////////////////////////////////////////////
// **** WAVPACK **** //
// Hybrid Lossless Wavefile Compressor //
// Copyright (c) 1998 - 2024 David Bryant. //
// All Rights Reserved. //
// Distributed under the BSD Software License (see license.txt) //
////////////////////////////////////////////////////////////////////////////
// dsf.c
// This module is a helper to the WavPack command-line programs to support DSF files.
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "wavpack.h"
#include "utils.h"
extern int debug_logging_mode;
#pragma pack(push,4)
typedef struct {
char ckID [4];
int64_t ckSize;
} DSFChunkHeader;
typedef struct {
char ckID [4];
int64_t ckSize;
int64_t fileSize;
int64_t metaOffset;
} DSFFileChunk;
typedef struct {
char ckID [4];
int64_t ckSize;
uint32_t formatVersion, formatID;
uint32_t chanType, numChannels, sampleRate, bitsPerSample;
int64_t sampleCount;
uint32_t blockSize, reserved;
} DSFFormatChunk;
#pragma pack(pop)
#define DSFChunkHeaderFormat "4D"
#define DSFFileChunkFormat "4DDD"
#define DSFFormatChunkFormat "4DLLLLLLDL4"
#define DSF_BLOCKSIZE 4096
static const uint16_t channel_masks [] = { 0x04, 0x03, 0x07, 0x33, 0x0f, 0x37, 0x3f };
#define NUM_CHAN_TYPES (sizeof (channel_masks) / sizeof (channel_masks [0]))
int ParseDsfHeaderConfig (FILE *infile, char *infilename, char *fourcc, WavpackContext *wpc, WavpackConfig *config)
{
int64_t infilesize, total_samples, total_blocks, leftover_samples;
DSFFileChunk file_chunk;
DSFFormatChunk format_chunk;
DSFChunkHeader chunk_header;
uint32_t bcount;
infilesize = DoGetFileSize (infile);
memcpy (&file_chunk, fourcc, 4);
if ((!DoReadFile (infile, ((char *) &file_chunk) + 4, sizeof (DSFFileChunk) - 4, &bcount) ||
bcount != sizeof (DSFFileChunk) - 4)) {
error_line ("%s is not a valid .DSF file!", infilename);
return WAVPACK_SOFT_ERROR;
}
else if (!(config->qmode & QMODE_NO_STORE_WRAPPER) &&
!WavpackAddWrapper (wpc, &file_chunk, sizeof (DSFFileChunk))) {
error_line ("%s", WavpackGetErrorMessage (wpc));
return WAVPACK_SOFT_ERROR;
}
#if 1 // this might be a little too picky...
WavpackLittleEndianToNative (&file_chunk, DSFFileChunkFormat);
if (debug_logging_mode)
error_line ("file header lengths = %lld, %lld, %lld", file_chunk.ckSize, file_chunk.fileSize, file_chunk.metaOffset);
if (infilesize && !(config->qmode & QMODE_IGNORE_LENGTH) &&
file_chunk.fileSize && file_chunk.fileSize + 1 && file_chunk.fileSize != infilesize) {
error_line ("%s is not a valid .DSF file (by total size)!", infilename);
return WAVPACK_SOFT_ERROR;
}
#endif
if (config->channel_mask || (config->qmode & QMODE_CHANS_UNASSIGNED)) {
error_line ("this DSF file already has channel order information!");
return WAVPACK_SOFT_ERROR;
}
if (!DoReadFile (infile, ((char *) &format_chunk), sizeof (DSFFormatChunk), &bcount) ||
bcount != sizeof (DSFFormatChunk) || strncmp (format_chunk.ckID, "fmt ", 4)) {
error_line ("%s is not a valid .DSF file!", infilename);
return WAVPACK_SOFT_ERROR;
}
else if (!(config->qmode & QMODE_NO_STORE_WRAPPER) &&
!WavpackAddWrapper (wpc, &format_chunk, sizeof (DSFFormatChunk))) {
error_line ("%s", WavpackGetErrorMessage (wpc));
return WAVPACK_SOFT_ERROR;
}
WavpackLittleEndianToNative (&format_chunk, DSFFormatChunkFormat);
if (format_chunk.ckSize != sizeof (DSFFormatChunk) || format_chunk.formatVersion != 1 ||
format_chunk.formatID != 0 || format_chunk.blockSize != DSF_BLOCKSIZE || format_chunk.reserved ||
format_chunk.sampleCount <= 0 || format_chunk.sampleCount > MAX_WAVPACK_SAMPLES * 8 ||
(format_chunk.bitsPerSample != 1 && format_chunk.bitsPerSample != 8) ||
format_chunk.numChannels < 1 || format_chunk.numChannels > 6 ||
format_chunk.chanType < 1 || format_chunk.chanType > NUM_CHAN_TYPES) {
error_line ("%s is not a valid .DSF file!", infilename);
return WAVPACK_SOFT_ERROR;
}
if (debug_logging_mode) {
error_line ("sampling rate = %d Hz", format_chunk.sampleRate);
error_line ("channel type = %d, channel count = %d", format_chunk.chanType, format_chunk.numChannels);
error_line ("block size = %d, bits per sample = %d", format_chunk.blockSize, format_chunk.bitsPerSample);
error_line ("sample count = %lld", format_chunk.sampleCount);
}
if (!DoReadFile (infile, ((char *) &chunk_header), sizeof (DSFChunkHeader), &bcount) ||
bcount != sizeof (DSFChunkHeader) || strncmp (chunk_header.ckID, "data", 4)) {
error_line ("%s is not a valid .DSF file!", infilename);
return WAVPACK_SOFT_ERROR;
}
else if (!(config->qmode & QMODE_NO_STORE_WRAPPER) &&
!WavpackAddWrapper (wpc, &chunk_header, sizeof (DSFChunkHeader))) {
error_line ("%s", WavpackGetErrorMessage (wpc));
return WAVPACK_SOFT_ERROR;
}
WavpackLittleEndianToNative (&chunk_header, DSFChunkHeaderFormat);
total_samples = format_chunk.sampleCount;
total_blocks = total_samples / (format_chunk.blockSize * 8);
leftover_samples = total_samples - (total_blocks * format_chunk.blockSize * 8);
if (leftover_samples)
total_blocks++;
if (debug_logging_mode) {
error_line ("leftover samples = %lld, leftover bits = %d", leftover_samples, (int)(leftover_samples % 8));
error_line ("data chunk size (specified) = %lld", chunk_header.ckSize - 12);
error_line ("data chunk size (calculated) = %lld", total_blocks * DSF_BLOCKSIZE * format_chunk.numChannels);
}
if (total_samples & 0x7)
error_line ("warning: DSF file has partial-byte leftover samples!");
if (format_chunk.sampleRate & 0x7)
error_line ("warning: DSF file has non-integer bytes/second!");
config->bits_per_sample = 8;
config->bytes_per_sample = 1;
config->num_channels = format_chunk.numChannels;
config->channel_mask = channel_masks [format_chunk.chanType - 1];
config->sample_rate = (format_chunk.sampleRate + 7) / 8;
if (format_chunk.bitsPerSample == 1)
config->qmode |= QMODE_DSD_LSB_FIRST | QMODE_DSD_IN_BLOCKS;
else
config->qmode |= QMODE_DSD_MSB_FIRST | QMODE_DSD_IN_BLOCKS;
if (!WavpackSetConfiguration64 (wpc, config, (total_samples + 7) / 8, NULL)) {
error_line ("%s: %s", infilename, WavpackGetErrorMessage (wpc));
return WAVPACK_SOFT_ERROR;
}
return WAVPACK_NO_ERROR;
}
|