react-native-tiny-wavpack-decoder 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +148 -0
- package/android/build.gradle +112 -0
- package/android/generated/java/com/tinywavpackdecoder/NativeTinyWavPackDecoderSpec.java +47 -0
- package/android/generated/jni/CMakeLists.txt +36 -0
- package/android/generated/jni/RNTinyWavPackDecoderSpec-generated.cpp +44 -0
- package/android/generated/jni/RNTinyWavPackDecoderSpec.h +31 -0
- package/android/generated/jni/react/renderer/components/RNTinyWavPackDecoderSpec/RNTinyWavPackDecoderSpecJSI-generated.cpp +46 -0
- package/android/generated/jni/react/renderer/components/RNTinyWavPackDecoderSpec/RNTinyWavPackDecoderSpecJSI.h +89 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/AndroidManifestNew.xml +2 -0
- package/android/src/main/cpp/CMakeLists.txt +54 -0
- package/android/src/main/cpp/TinyWavPackDecoderModule.cpp +118 -0
- package/android/src/main/java/com/tinywavpackdecoder/TinyWavPackDecoderModule.kt +114 -0
- package/android/src/main/java/com/tinywavpackdecoder/TinyWavPackDecoderPackage.kt +18 -0
- package/ios/TinyWavPackDecoder.h +8 -0
- package/ios/TinyWavPackDecoder.mm +83 -0
- package/ios/generated/RNTinyWavPackDecoderSpec/RNTinyWavPackDecoderSpec-generated.mm +53 -0
- package/ios/generated/RNTinyWavPackDecoderSpec/RNTinyWavPackDecoderSpec.h +69 -0
- package/ios/generated/RNTinyWavPackDecoderSpecJSI-generated.cpp +46 -0
- package/ios/generated/RNTinyWavPackDecoderSpecJSI.h +89 -0
- package/lib/module/NativeTinyWavPackDecoder.ts +19 -0
- package/lib/module/index.js +38 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/tiny-wavpack/common/TinyWavPackDecoderInterface.c +414 -0
- package/lib/module/tiny-wavpack/common/TinyWavPackDecoderInterface.h +52 -0
- package/lib/module/tiny-wavpack/common/test.c +45 -0
- package/lib/module/tiny-wavpack/common/wv2wav +0 -0
- package/lib/module/tiny-wavpack/lib/bits.c +140 -0
- package/lib/module/tiny-wavpack/lib/float.c +50 -0
- package/lib/module/tiny-wavpack/lib/license.txt +25 -0
- package/lib/module/tiny-wavpack/lib/metadata.c +105 -0
- package/lib/module/tiny-wavpack/lib/readme.txt +68 -0
- package/lib/module/tiny-wavpack/lib/unpack.c +785 -0
- package/lib/module/tiny-wavpack/lib/wavpack.h +384 -0
- package/lib/module/tiny-wavpack/lib/words.c +560 -0
- package/lib/module/tiny-wavpack/lib/wputils.c +351 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeTinyWavPackDecoder.d.ts +9 -0
- package/lib/typescript/src/NativeTinyWavPackDecoder.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +18 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/package.json +195 -0
- package/react-native-wavpack-decoder.podspec +35 -0
- package/react-native.config.js +12 -0
- package/src/NativeTinyWavPackDecoder.ts +19 -0
- package/src/index.tsx +57 -0
- package/src/tiny-wavpack/common/TinyWavPackDecoderInterface.c +414 -0
- package/src/tiny-wavpack/common/TinyWavPackDecoderInterface.h +52 -0
- package/src/tiny-wavpack/common/test.c +45 -0
- package/src/tiny-wavpack/common/wv2wav +0 -0
- package/src/tiny-wavpack/lib/bits.c +140 -0
- package/src/tiny-wavpack/lib/float.c +50 -0
- package/src/tiny-wavpack/lib/license.txt +25 -0
- package/src/tiny-wavpack/lib/metadata.c +105 -0
- package/src/tiny-wavpack/lib/readme.txt +68 -0
- package/src/tiny-wavpack/lib/unpack.c +785 -0
- package/src/tiny-wavpack/lib/wavpack.h +384 -0
- package/src/tiny-wavpack/lib/words.c +560 -0
- package/src/tiny-wavpack/lib/wputils.c +351 -0
package/src/index.tsx
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import NativeTinyWavPackDecoder from './NativeTinyWavPackDecoder';
|
|
2
|
+
import {
|
|
3
|
+
NativeModules,
|
|
4
|
+
NativeEventEmitter,
|
|
5
|
+
type EmitterSubscription,
|
|
6
|
+
} from 'react-native';
|
|
7
|
+
|
|
8
|
+
export interface TinyWavPackDecoderOptions {
|
|
9
|
+
maxSamples?: number;
|
|
10
|
+
bitsPerSample?: 8 | 16 | 24 | 32;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { TinyWavPackDecoderModule } = NativeModules;
|
|
14
|
+
const emitter = new NativeEventEmitter(TinyWavPackDecoderModule);
|
|
15
|
+
|
|
16
|
+
const TinyWavPackDecoder = {
|
|
17
|
+
decode: async (
|
|
18
|
+
inputPath: string,
|
|
19
|
+
outputPath: string,
|
|
20
|
+
options: TinyWavPackDecoderOptions = {}
|
|
21
|
+
): Promise<string> => {
|
|
22
|
+
const { maxSamples = -1, bitsPerSample = 16 } = options;
|
|
23
|
+
|
|
24
|
+
if (![8, 16, 24, 32].includes(bitsPerSample)) {
|
|
25
|
+
throw new Error('bitsPerSample must be 8, 16, 24, or 32');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return NativeTinyWavPackDecoder.decodeWavPack(
|
|
29
|
+
inputPath,
|
|
30
|
+
outputPath,
|
|
31
|
+
maxSamples,
|
|
32
|
+
bitsPerSample
|
|
33
|
+
);
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Subscribe to native progress updates
|
|
38
|
+
*/
|
|
39
|
+
addProgressListener: (
|
|
40
|
+
callback: (progress: number) => void
|
|
41
|
+
): EmitterSubscription => {
|
|
42
|
+
return emitter.addListener('onProgressUpdate', (event) => {
|
|
43
|
+
if (typeof event?.progress === 'number') {
|
|
44
|
+
callback(event.progress);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Remove all native listeners for progress updates
|
|
51
|
+
*/
|
|
52
|
+
removeAllListeners: (): void => {
|
|
53
|
+
emitter.removeAllListeners('onProgressUpdate');
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export default TinyWavPackDecoder;
|
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
#include "TinyWavPackDecoderInterface.h"
|
|
2
|
+
#include "wavpack.h"
|
|
3
|
+
#include <stdio.h>
|
|
4
|
+
#include <stdlib.h>
|
|
5
|
+
#include <string.h>
|
|
6
|
+
|
|
7
|
+
// Static file pointer for wavpack read_bytes callback
|
|
8
|
+
static FILE *currentWvFile = NULL;
|
|
9
|
+
|
|
10
|
+
// WavPack callback for reading bytes from file
|
|
11
|
+
static int read_bytes(void *data, int bcount)
|
|
12
|
+
{
|
|
13
|
+
return (int)fread(data, 1, bcount, currentWvFile);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Define error codes
|
|
17
|
+
#define ERR_FILE_OPEN_FAILED 1
|
|
18
|
+
#define ERR_WAVPACK_OPEN_FAILED 2
|
|
19
|
+
#define ERR_MEMORY_ALLOCATION_FAILED 3
|
|
20
|
+
#define ERR_INVALID_CHANNELS 4
|
|
21
|
+
#define ERR_INVALID_SAMPLES 5
|
|
22
|
+
#define ERR_DATA_OVERFLOW 6
|
|
23
|
+
#define ERR_WRITE_FAILED 7
|
|
24
|
+
#define ERR_DECODE_FAILED 8
|
|
25
|
+
#define ERR_INVALID_BPS 9
|
|
26
|
+
|
|
27
|
+
// Helper function to create and return an error result
|
|
28
|
+
static DecoderResult make_error(int code, const char *message)
|
|
29
|
+
{
|
|
30
|
+
DecoderResult result;
|
|
31
|
+
result.success = 0;
|
|
32
|
+
strncpy(result.error, message, sizeof(result.error) - 1);
|
|
33
|
+
result.error[sizeof(result.error) - 1] = '\0';
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Helper function to create and return a success result
|
|
38
|
+
static DecoderResult make_success()
|
|
39
|
+
{
|
|
40
|
+
DecoderResult result;
|
|
41
|
+
result.success = 1;
|
|
42
|
+
result.error[0] = '\0';
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Helper function to format samples to the desired bits per sample
|
|
47
|
+
static void format_samples(int bps, void *dst, int32_t *src, uint32_t samcnt, int is_float, int bitsPerSample)
|
|
48
|
+
{
|
|
49
|
+
switch (bps)
|
|
50
|
+
{
|
|
51
|
+
case 1:
|
|
52
|
+
{ // 8-bit
|
|
53
|
+
uint8_t *d = (uint8_t *)dst;
|
|
54
|
+
for (uint32_t i = 0; i < samcnt; i++)
|
|
55
|
+
{
|
|
56
|
+
if (is_float)
|
|
57
|
+
{
|
|
58
|
+
float val = *(float *)(&src[i]);
|
|
59
|
+
val = val > 1.0f ? 1.0f : (val < -1.0f ? -1.0f : val);
|
|
60
|
+
d[i] = (uint8_t)((val * 127.0f) + 128);
|
|
61
|
+
}
|
|
62
|
+
else
|
|
63
|
+
{
|
|
64
|
+
int32_t val = src[i];
|
|
65
|
+
val = val > 32767 ? 32767 : (val < -32768 ? -32768 : val);
|
|
66
|
+
d[i] = (uint8_t)((val >> (bitsPerSample <= 16 ? 0 : (bitsPerSample - 16))) + 128);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
case 2:
|
|
72
|
+
{ // 16-bit
|
|
73
|
+
int16_t *d = (int16_t *)dst;
|
|
74
|
+
for (uint32_t i = 0; i < samcnt; i++)
|
|
75
|
+
{
|
|
76
|
+
if (is_float)
|
|
77
|
+
{
|
|
78
|
+
float val = *(float *)(&src[i]);
|
|
79
|
+
val = val > 1.0f ? 1.0f : (val < -1.0f ? -1.0f : val);
|
|
80
|
+
d[i] = (int16_t)(val * 32767.0f);
|
|
81
|
+
}
|
|
82
|
+
else
|
|
83
|
+
{
|
|
84
|
+
int32_t val = src[i];
|
|
85
|
+
val = val > 32767 ? 32767 : (val < -32768 ? -32768 : val);
|
|
86
|
+
d[i] = (int16_t)(bitsPerSample <= 16 ? val : (val >> (bitsPerSample - 16)));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
case 3:
|
|
92
|
+
{ // 24-bit
|
|
93
|
+
uint8_t *d = (uint8_t *)dst;
|
|
94
|
+
for (uint32_t i = 0; i < samcnt; i++)
|
|
95
|
+
{
|
|
96
|
+
int32_t val;
|
|
97
|
+
if (is_float)
|
|
98
|
+
{
|
|
99
|
+
float fval = *(float *)(&src[i]);
|
|
100
|
+
fval = fval > 1.0f ? 1.0f : (fval < -1.0f ? -1.0f : fval);
|
|
101
|
+
val = (int32_t)(fval * 8388607.0f);
|
|
102
|
+
}
|
|
103
|
+
else
|
|
104
|
+
{
|
|
105
|
+
val = src[i];
|
|
106
|
+
val = val > 8388607 ? 8388607 : (val < -8388608 ? -8388608 : val);
|
|
107
|
+
}
|
|
108
|
+
d[i * 3] = (uint8_t)(val);
|
|
109
|
+
d[i * 3 + 1] = (uint8_t)(val >> 8);
|
|
110
|
+
d[i * 3 + 2] = (uint8_t)(val >> 16);
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
case 4:
|
|
115
|
+
{ // 32-bit
|
|
116
|
+
int32_t *d = (int32_t *)dst;
|
|
117
|
+
for (uint32_t i = 0; i < samcnt; i++)
|
|
118
|
+
{
|
|
119
|
+
if (is_float)
|
|
120
|
+
{
|
|
121
|
+
float val = *(float *)(&src[i]);
|
|
122
|
+
val = val > 1.0f ? 1.0f : (val < -1.0f ? -1.0f : val);
|
|
123
|
+
d[i] = (int32_t)(val * 2147483647.0f);
|
|
124
|
+
}
|
|
125
|
+
else
|
|
126
|
+
{
|
|
127
|
+
d[i] = src[i];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
DecoderResult decode_wavpack_to_wav(
|
|
136
|
+
const char *inputPath,
|
|
137
|
+
const char *outputPath,
|
|
138
|
+
int max_samples,
|
|
139
|
+
int force_bps,
|
|
140
|
+
ProgressCallback progress_callback,
|
|
141
|
+
void *context)
|
|
142
|
+
{
|
|
143
|
+
// Validate inputs
|
|
144
|
+
if (!inputPath || !outputPath)
|
|
145
|
+
{
|
|
146
|
+
return make_error(ERR_FILE_OPEN_FAILED, "Invalid input or output path");
|
|
147
|
+
}
|
|
148
|
+
if (max_samples < -1)
|
|
149
|
+
{
|
|
150
|
+
return make_error(ERR_INVALID_SAMPLES, "Invalid max samples");
|
|
151
|
+
}
|
|
152
|
+
if (force_bps && force_bps != 8 && force_bps != 16 && force_bps != 24 && force_bps != 32)
|
|
153
|
+
{
|
|
154
|
+
return make_error(ERR_INVALID_BPS, "Invalid bits per sample (must be 8, 16, 24, or 32)");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Buffer for WavPack error messages
|
|
158
|
+
char error[80];
|
|
159
|
+
|
|
160
|
+
// Local variable to ensure each method call has its own isolated file handle.
|
|
161
|
+
FILE *wvFile = NULL;
|
|
162
|
+
FILE *wavFile = NULL;
|
|
163
|
+
|
|
164
|
+
// Opens the .wv file in binary read mode ("rb").
|
|
165
|
+
wvFile = fopen(inputPath, "rb");
|
|
166
|
+
|
|
167
|
+
/* If it fails (e.g., file doesn’t exist), rejects the promise with an error
|
|
168
|
+
message and exits. */
|
|
169
|
+
if (!wvFile)
|
|
170
|
+
{
|
|
171
|
+
return make_error(ERR_FILE_OPEN_FAILED, "Cannot open input file");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Set static file pointer for read_bytes callback
|
|
175
|
+
currentWvFile = wvFile;
|
|
176
|
+
|
|
177
|
+
/* Creates a WavPack context using the read_bytes callback. If it fails (e.g.,
|
|
178
|
+
invalid WavPack file), rejects with the error message from error and closes
|
|
179
|
+
the file. */
|
|
180
|
+
WavpackContext *wpc = WavpackOpenFileInput(read_bytes, error);
|
|
181
|
+
if (!wpc)
|
|
182
|
+
{
|
|
183
|
+
fclose(wvFile);
|
|
184
|
+
currentWvFile = NULL;
|
|
185
|
+
return make_error(ERR_WAVPACK_OPEN_FAILED, error);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
//////////////// Extracts audio properties from the WavPack file ///////////////
|
|
189
|
+
int numChannels = WavpackGetNumChannels(wpc); // Number of channels (e.g., 1 or 2).
|
|
190
|
+
int sampleRate = WavpackGetSampleRate(wpc); // Samples per second (e.g., 44100 Hz).
|
|
191
|
+
int bitsPerSample = WavpackGetBitsPerSample(wpc); // Original bit depth (e.g., 16, 24).
|
|
192
|
+
int numSamples = WavpackGetNumSamples(wpc); // Total number of samples.
|
|
193
|
+
int flags = WavpackGetMode(wpc); // Check for float format
|
|
194
|
+
int isFloatFormat = (flags & MODE_FLOAT) != 0;
|
|
195
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
196
|
+
|
|
197
|
+
//////////// Sanity check for buffer size before memory allocation /////////////
|
|
198
|
+
// Validate channel count
|
|
199
|
+
if (numChannels <= 0 || numChannels > 100)
|
|
200
|
+
{
|
|
201
|
+
fclose(wvFile);
|
|
202
|
+
currentWvFile = NULL;
|
|
203
|
+
return make_error(ERR_INVALID_CHANNELS, "Invalid number of channels");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Validate sample count
|
|
207
|
+
if (numSamples <= 0)
|
|
208
|
+
{
|
|
209
|
+
fclose(wvFile);
|
|
210
|
+
currentWvFile = NULL;
|
|
211
|
+
return make_error(ERR_INVALID_SAMPLES, "Invalid number of samples");
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Calculate samples to decode
|
|
215
|
+
uint32_t samples_to_decode = (max_samples == -1) ? numSamples : (max_samples > numSamples ? numSamples : max_samples);
|
|
216
|
+
|
|
217
|
+
// Opens the .wav file in binary write mode ("wb")
|
|
218
|
+
wavFile = fopen(outputPath, "wb");
|
|
219
|
+
if (!wavFile)
|
|
220
|
+
{
|
|
221
|
+
// If it fails (e.g., permissions issue), make error and close the input file.
|
|
222
|
+
fclose(wvFile);
|
|
223
|
+
currentWvFile = NULL;
|
|
224
|
+
return make_error(ERR_FILE_OPEN_FAILED, "Cannot open output file");
|
|
225
|
+
}
|
|
226
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
227
|
+
|
|
228
|
+
/////////////////////////////// Write WAV Header ///////////////////////////////
|
|
229
|
+
uint32_t bytesPerSample = force_bps ? (force_bps / 8) : 2; // Default to 16-bit if not forced
|
|
230
|
+
|
|
231
|
+
// Avoid overflow: Checks if the total size of audio data exceeds 32-bit limit.
|
|
232
|
+
if ((uint64_t)samples_to_decode * numChannels * bytesPerSample > UINT32_MAX)
|
|
233
|
+
{
|
|
234
|
+
fclose(wavFile);
|
|
235
|
+
fclose(wvFile);
|
|
236
|
+
currentWvFile = NULL;
|
|
237
|
+
return make_error(ERR_DATA_OVERFLOW, "Audio data size exceeds 32-bit limit");
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
uint32_t dataSize = samples_to_decode * numChannels * bytesPerSample; // Total audio data size in bytes.
|
|
241
|
+
|
|
242
|
+
// Create and write WAV header
|
|
243
|
+
WavHeader header = {
|
|
244
|
+
/* "RIFF" identifier: Marks the start of a RIFF file. Every WAV file begins
|
|
245
|
+
with this */
|
|
246
|
+
.riff = {'R', 'I', 'F', 'F'}, // "RIFF"
|
|
247
|
+
|
|
248
|
+
/* A 32-bit unsigned integer representing the total file size minus 8 bytes
|
|
249
|
+
(the size of riff and fileSize fields). */
|
|
250
|
+
.fileSize = dataSize + 36,
|
|
251
|
+
|
|
252
|
+
/* The RIFF format identifier, a 4-byte string: Specifies that this RIFF
|
|
253
|
+
file is a WAV audio file. */
|
|
254
|
+
.wave = {'W', 'A', 'V', 'E'}, // "WAVE"
|
|
255
|
+
|
|
256
|
+
/* The format chunk identifier, a 4-byte string: Marks the start of the format
|
|
257
|
+
chunk, which describes the audio properties. The space is part of the standard. */
|
|
258
|
+
.fmt = {'f', 'm', 't', ' '}, // "fmt "
|
|
259
|
+
|
|
260
|
+
/* A 32-bit unsigned integer indicating the size of the format chunk data
|
|
261
|
+
(excluding fmt and fmtSize): Specifies how many bytes follow in the fmt chunk.
|
|
262
|
+
16 bytes (for PCM, this is fixed: audioFormat [2] + numChannels [2] + sampleRate [4] + byteRate [4] + blockAlign [2] + bitsPerSample [2]). */
|
|
263
|
+
.fmtSize = 16, // 16 for PCM
|
|
264
|
+
|
|
265
|
+
/* A 16-bit unsigned integer specifying the audio format: 1 (means uncompressed
|
|
266
|
+
PCM). This tells the player the data is raw PCM, not compressed (e.g., MP3
|
|
267
|
+
would be a different value). */
|
|
268
|
+
.audioFormat = 1,
|
|
269
|
+
|
|
270
|
+
/* A 16-bit unsigned integer for the number of audio channels. Cast from int
|
|
271
|
+
numChannels (from WavpackGetNumChannels), typically 1 (mono) or 2 (stereo). */
|
|
272
|
+
.numChannels = (uint16_t)numChannels,
|
|
273
|
+
|
|
274
|
+
/* A 32-bit unsigned integer for the sample rate in Hz. Cast from int sampleRate
|
|
275
|
+
(from WavpackGetSampleRate), e.g., 44100 Hz. Defines how many samples per
|
|
276
|
+
second the audio contains.*/
|
|
277
|
+
.sampleRate = (uint32_t)sampleRate,
|
|
278
|
+
|
|
279
|
+
/* A 32-bit unsigned integer for the average bytes per second.
|
|
280
|
+
sampleRate * numChannels * bytesPerSample (e.g., 44100 * 2 * 2 = 176400 bytes/sec for stereo 16-bit at 44.1 kHz).
|
|
281
|
+
bytesPerSample is fixed at 2 (16 bits / 8 = 2 bytes). Helps players determine
|
|
282
|
+
playback speed and buffer requirements.*/
|
|
283
|
+
.byteRate = (uint32_t)(sampleRate * numChannels * bytesPerSample),
|
|
284
|
+
|
|
285
|
+
/* A 16-bit unsigned integer for the byte size of one sample frame.
|
|
286
|
+
numChannels * bytesPerSample (e.g., 2 * 2 = 4 bytes for stereo 16-bit).
|
|
287
|
+
Specifies the alignment of sample data (how many bytes per multi-channel sample).*/
|
|
288
|
+
.blockAlign = (uint16_t)(numChannels * bytesPerSample),
|
|
289
|
+
|
|
290
|
+
/* A 16-bit unsigned integer for the bits per sample. Defines the resolution
|
|
291
|
+
of each sample (8-bit, 16-bit, 24-bit, or 32-bit).*/
|
|
292
|
+
.bitsPerSample = (uint16_t)(bytesPerSample * 8),
|
|
293
|
+
|
|
294
|
+
/* The data chunk identifier, a 4-byte string. Marks the start of the audio
|
|
295
|
+
data chunk.*/
|
|
296
|
+
.data = {'d', 'a', 't', 'a'},
|
|
297
|
+
|
|
298
|
+
/* A 32-bit unsigned integer for the size of the audio data in bytes.
|
|
299
|
+
dataSize (from numSamples * numChannels * bytesPerSample).
|
|
300
|
+
Tells the player how many bytes of raw audio data follow. */
|
|
301
|
+
.dataSize = dataSize};
|
|
302
|
+
|
|
303
|
+
// This ensures the header is written successfully before proceeding to decode and write audio data.
|
|
304
|
+
if (fwrite(&header, sizeof(WavHeader), 1, wavFile) != 1)
|
|
305
|
+
{
|
|
306
|
+
fclose(wavFile);
|
|
307
|
+
fclose(wvFile);
|
|
308
|
+
currentWvFile = NULL;
|
|
309
|
+
return make_error(ERR_WRITE_FAILED, "Failed to write WAV header");
|
|
310
|
+
}
|
|
311
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
312
|
+
|
|
313
|
+
// Allocate buffers for decoding
|
|
314
|
+
const int BUFFER_SIZE = 4096;
|
|
315
|
+
|
|
316
|
+
// For 32-bit samples from WavPack.
|
|
317
|
+
int32_t *buffer = (int32_t *)malloc(BUFFER_SIZE * numChannels * sizeof(int32_t));
|
|
318
|
+
|
|
319
|
+
// For output samples (size depends on bits per sample).
|
|
320
|
+
void *samples = malloc(BUFFER_SIZE * numChannels * bytesPerSample);
|
|
321
|
+
|
|
322
|
+
// If allocation fails, rejects and cleans up.
|
|
323
|
+
if (!buffer || !samples)
|
|
324
|
+
{
|
|
325
|
+
if (buffer)
|
|
326
|
+
free(buffer);
|
|
327
|
+
if (samples)
|
|
328
|
+
free(samples);
|
|
329
|
+
fclose(wavFile);
|
|
330
|
+
fclose(wvFile);
|
|
331
|
+
currentWvFile = NULL;
|
|
332
|
+
return make_error(ERR_MEMORY_ALLOCATION_FAILED, "Memory allocation failed");
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
///////////////////////////////// Decoding Loop ////////////////////////////////
|
|
336
|
+
int totalSamplesRead = 0;
|
|
337
|
+
|
|
338
|
+
/* Loop:
|
|
339
|
+
- Calculates how many samples to read per iteration (up to BUFFER_SIZE).
|
|
340
|
+
- WavpackUnpackSamples: Fills buffer with decoded samples.
|
|
341
|
+
- Converts samples to the desired bit depth:
|
|
342
|
+
- Float: Scales [-1.0, 1.0] to appropriate range for output bits per sample.
|
|
343
|
+
- Integer: Direct cast or shifts as needed.
|
|
344
|
+
- Writes converted samples to the .wav file.
|
|
345
|
+
- Exits if no more samples are read (samplesRead <= 0). */
|
|
346
|
+
while (totalSamplesRead < samples_to_decode)
|
|
347
|
+
{
|
|
348
|
+
int samplesToRead = (samples_to_decode - totalSamplesRead < BUFFER_SIZE) ? samples_to_decode - totalSamplesRead : BUFFER_SIZE;
|
|
349
|
+
|
|
350
|
+
int samplesRead = WavpackUnpackSamples(wpc, buffer, samplesToRead);
|
|
351
|
+
|
|
352
|
+
if (samplesRead < 0)
|
|
353
|
+
{
|
|
354
|
+
free(buffer);
|
|
355
|
+
free(samples);
|
|
356
|
+
fclose(wavFile);
|
|
357
|
+
fclose(wvFile);
|
|
358
|
+
currentWvFile = NULL;
|
|
359
|
+
return make_error(ERR_DECODE_FAILED, "Failed to unpack samples");
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/* Handle Incomplete Decoding: Rejects on negative return (error), logs a
|
|
363
|
+
warning for partial reads, and continues cleanly on zero. */
|
|
364
|
+
if (samplesRead == 0)
|
|
365
|
+
break;
|
|
366
|
+
|
|
367
|
+
// Convert samples to desired bit depth
|
|
368
|
+
format_samples(bytesPerSample, samples, buffer, samplesRead * numChannels, isFloatFormat, bitsPerSample);
|
|
369
|
+
|
|
370
|
+
// This catches write failures (e.g., disk full) during the sample writing process, allowing cleanup and error reporting.
|
|
371
|
+
if (fwrite(samples, bytesPerSample, samplesRead * numChannels, wavFile) != samplesRead * numChannels)
|
|
372
|
+
{
|
|
373
|
+
free(buffer);
|
|
374
|
+
free(samples);
|
|
375
|
+
fclose(wavFile);
|
|
376
|
+
fclose(wvFile);
|
|
377
|
+
currentWvFile = NULL;
|
|
378
|
+
return make_error(ERR_WRITE_FAILED, "Failed to write audio samples");
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
totalSamplesRead += samplesRead;
|
|
382
|
+
|
|
383
|
+
if (progress_callback)
|
|
384
|
+
{
|
|
385
|
+
progress_callback((float)totalSamplesRead / samples_to_decode, context);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
389
|
+
|
|
390
|
+
// Clean up: closes files & frees buffers.
|
|
391
|
+
free(buffer);
|
|
392
|
+
free(samples);
|
|
393
|
+
fclose(wavFile);
|
|
394
|
+
fclose(wvFile);
|
|
395
|
+
currentWvFile = NULL;
|
|
396
|
+
|
|
397
|
+
// Check for errors
|
|
398
|
+
if (WavpackGetNumErrors(wpc) > 0)
|
|
399
|
+
{
|
|
400
|
+
char err_msg[80];
|
|
401
|
+
snprintf(err_msg, sizeof(err_msg), "Decoding failed with %d CRC errors", WavpackGetNumErrors(wpc));
|
|
402
|
+
return make_error(ERR_DECODE_FAILED, err_msg);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Check if all requested samples were decoded
|
|
406
|
+
if (totalSamplesRead == samples_to_decode)
|
|
407
|
+
{
|
|
408
|
+
return make_success();
|
|
409
|
+
}
|
|
410
|
+
else
|
|
411
|
+
{
|
|
412
|
+
return make_error(ERR_DECODE_FAILED, "Failed to decode all requested samples");
|
|
413
|
+
}
|
|
414
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#ifndef TINY_WAVPACK_DECODER_INTERFACE_H
|
|
2
|
+
#define TINY_WAVPACK_DECODER_INTERFACE_H
|
|
3
|
+
|
|
4
|
+
#include <stdint.h>
|
|
5
|
+
|
|
6
|
+
#ifdef __cplusplus
|
|
7
|
+
extern "C"
|
|
8
|
+
{
|
|
9
|
+
#endif
|
|
10
|
+
|
|
11
|
+
// WAV header structure
|
|
12
|
+
typedef struct
|
|
13
|
+
{
|
|
14
|
+
char riff[4]; // "RIFF"
|
|
15
|
+
uint32_t fileSize; // File size - 8
|
|
16
|
+
char wave[4]; // "WAVE"
|
|
17
|
+
char fmt[4]; // "fmt "
|
|
18
|
+
uint32_t fmtSize; // 16 for PCM
|
|
19
|
+
uint16_t audioFormat; // 1 for PCM
|
|
20
|
+
uint16_t numChannels; // 1 or 2
|
|
21
|
+
uint32_t sampleRate; // e.g., 44100
|
|
22
|
+
uint32_t byteRate; // sampleRate * numChannels * bitsPerSample / 8
|
|
23
|
+
uint16_t blockAlign; // numChannels * bitsPerSample / 8
|
|
24
|
+
uint16_t bitsPerSample; // 8, 16, 24, or 32
|
|
25
|
+
char data[4]; // "data"
|
|
26
|
+
uint32_t dataSize; // Size of the data chunk
|
|
27
|
+
} WavHeader;
|
|
28
|
+
|
|
29
|
+
// Result struct to handle decoding results and errors
|
|
30
|
+
typedef struct
|
|
31
|
+
{
|
|
32
|
+
int success; // 1 for success, 0 for failure
|
|
33
|
+
char error[80]; // Error message if any
|
|
34
|
+
} DecoderResult;
|
|
35
|
+
|
|
36
|
+
// Callback function pointer type
|
|
37
|
+
typedef void (*ProgressCallback)(float progress, void *context);
|
|
38
|
+
|
|
39
|
+
// Core decoding function
|
|
40
|
+
DecoderResult decode_wavpack_to_wav(
|
|
41
|
+
const char *inputPath,
|
|
42
|
+
const char *outputPath,
|
|
43
|
+
int max_samples,
|
|
44
|
+
int force_bps,
|
|
45
|
+
ProgressCallback progress_callback,
|
|
46
|
+
void *context);
|
|
47
|
+
|
|
48
|
+
#ifdef __cplusplus
|
|
49
|
+
}
|
|
50
|
+
#endif
|
|
51
|
+
|
|
52
|
+
#endif // TINY_WAVPACK_DECODER_INTERFACE_H
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// #include "TinyWavPackDecoderInterface.h"
|
|
2
|
+
// #include <stdio.h>
|
|
3
|
+
// #include <stdlib.h>
|
|
4
|
+
// #include <string.h>
|
|
5
|
+
|
|
6
|
+
// int main(int argc, char *argv[]) {
|
|
7
|
+
// if (argc < 3) {
|
|
8
|
+
// printf("Usage: %s <input.wv> <output.wav> [-m max_samples] [-b bits_per_sample] [-v]\n", argv[0]);
|
|
9
|
+
// printf(" -m: max samples to decode (-1 for all, default -1)\n");
|
|
10
|
+
// printf(" -b: bits per sample (8, 16, 24, 32, default 16)\n");
|
|
11
|
+
// printf(" -v: verbose output\n");
|
|
12
|
+
// return 1;
|
|
13
|
+
// }
|
|
14
|
+
|
|
15
|
+
// const char* inputPath = argv[1];
|
|
16
|
+
// const char* outputPath = argv[2];
|
|
17
|
+
// int max_samples = -1;
|
|
18
|
+
// int force_bps = 16; // Default to 16-bit
|
|
19
|
+
// int verbose = 0;
|
|
20
|
+
|
|
21
|
+
// for (int i = 3; i < argc; i++) {
|
|
22
|
+
// if (strcmp(argv[i], "-m") == 0 && i + 1 < argc) {
|
|
23
|
+
// max_samples = atoi(argv[++i]);
|
|
24
|
+
// }
|
|
25
|
+
// else if (strcmp(argv[i], "-b") == 0 && i + 1 < argc) {
|
|
26
|
+
// force_bps = atoi(argv[++i]);
|
|
27
|
+
// }
|
|
28
|
+
// else if (strcmp(argv[i], "-v") == 0) {
|
|
29
|
+
// verbose = 1;
|
|
30
|
+
// }
|
|
31
|
+
// else {
|
|
32
|
+
// printf("Unknown option: %s\n", argv[i]);
|
|
33
|
+
// return 1;
|
|
34
|
+
// }
|
|
35
|
+
// }
|
|
36
|
+
|
|
37
|
+
// DecoderResult result = decode_wavpack_to_wav(inputPath, outputPath, max_samples, force_bps, verbose);
|
|
38
|
+
// if (result.success) {
|
|
39
|
+
// printf("Decoding successful: %s created\n", outputPath);
|
|
40
|
+
// } else {
|
|
41
|
+
// printf("Decoding failed: %s\n", result.error);
|
|
42
|
+
// }
|
|
43
|
+
|
|
44
|
+
// return result.success ? 0 : 1;
|
|
45
|
+
// }
|
|
Binary file
|