summaryrefslogtreecommitdiff
path: root/apps/codecs/libwavpack/wputils.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/codecs/libwavpack/wputils.c')
-rw-r--r--apps/codecs/libwavpack/wputils.c431
1 files changed, 315 insertions, 116 deletions
diff --git a/apps/codecs/libwavpack/wputils.c b/apps/codecs/libwavpack/wputils.c
index 8d58b3b4d7..7f2ab14c44 100644
--- a/apps/codecs/libwavpack/wputils.c
+++ b/apps/codecs/libwavpack/wputils.c
@@ -1,8 +1,8 @@
1//////////////////////////////////////////////////////////////////////////// 1////////////////////////////////////////////////////////////////////////////
2// **** WAVPACK **** // 2// **** WAVPACK **** //
3// Hybrid Lossless Wavefile Compressor // 3// Hybrid Lossless Wavefile Compressor //
4// Copyright (c) 1998 - 2004 Conifer Software. // 4// Copyright (c) 1998 - 2004 Conifer Software. //
5// All Rights Reserved. // 5// All Rights Reserved. //
6// Distributed under the BSD Software License (see license.txt) // 6// Distributed under the BSD Software License (see license.txt) //
7//////////////////////////////////////////////////////////////////////////// 7////////////////////////////////////////////////////////////////////////////
8 8
@@ -19,7 +19,7 @@
19 19
20#include <string.h> 20#include <string.h>
21 21
22static void strcpy_loc (char *dst, char *src) { while (*src) *dst++ = *src++; *dst = 0; } 22static void strcpy_loc (char *dst, char *src) { while ((*dst++ = *src++) != 0); }
23 23
24///////////////////////////// local table storage //////////////////////////// 24///////////////////////////// local table storage ////////////////////////////
25 25
@@ -29,7 +29,7 @@ const ulong sample_rates [] = { 6000, 8000, 9600, 11025, 12000, 16000, 22050,
29///////////////////////////// executable code //////////////////////////////// 29///////////////////////////// executable code ////////////////////////////////
30 30
31static ulong read_next_header (read_stream infile, WavpackHeader *wphdr); 31static ulong read_next_header (read_stream infile, WavpackHeader *wphdr);
32 32
33// This function reads data from the specified stream in search of a valid 33// This function reads data from the specified stream in search of a valid
34// WavPack 4.0 audio block. If this fails in 1 megabyte (or an invalid or 34// WavPack 4.0 audio block. If this fails in 1 megabyte (or an invalid or
35// unsupported WavPack block is encountered) then an appropriate message is 35// unsupported WavPack block is encountered) then an appropriate message is
@@ -62,27 +62,27 @@ WavpackContext *WavpackOpenFileInput (read_stream infile, char *error)
62 62
63 while (!wps->wphdr.block_samples) { 63 while (!wps->wphdr.block_samples) {
64 64
65 bcount = read_next_header (wpc.infile, &wps->wphdr); 65 bcount = read_next_header (wpc.infile, &wps->wphdr);
66 66
67 if (bcount == (ulong) -1) { 67 if (bcount == (ulong) -1) {
68 strcpy_loc (error, "invalid WavPack file!"); 68 strcpy_loc (error, "invalid WavPack file!");
69 return NULL; 69 return NULL;
70 } 70 }
71 71
72 if ((wps->wphdr.flags & UNKNOWN_FLAGS) || wps->wphdr.version < 0x402 || wps->wphdr.version > 0x40f) { 72 if ((wps->wphdr.flags & UNKNOWN_FLAGS) || wps->wphdr.version < 0x402 || wps->wphdr.version > 0x40f) {
73 strcpy_loc (error, "invalid WavPack file!"); 73 strcpy_loc (error, "invalid WavPack file!");
74 return NULL; 74 return NULL;
75 } 75 }
76 76
77 if (wps->wphdr.block_samples && wps->wphdr.total_samples != (ulong) -1) 77 if (wps->wphdr.block_samples && wps->wphdr.total_samples != (ulong) -1)
78 wpc.total_samples = wps->wphdr.total_samples; 78 wpc.total_samples = wps->wphdr.total_samples;
79 79
80 if (!unpack_init (&wpc)) { 80 if (!unpack_init (&wpc)) {
81 strcpy_loc (error, wpc.error_message [0] ? wpc.error_message : 81 strcpy_loc (error, wpc.error_message [0] ? wpc.error_message :
82 "invalid WavPack file!"); 82 "invalid WavPack file!");
83 83
84 return NULL; 84 return NULL;
85 } 85 }
86 } 86 }
87 87
88 wpc.config.flags &= ~0xff; 88 wpc.config.flags &= ~0xff;
@@ -91,22 +91,22 @@ WavpackContext *WavpackOpenFileInput (read_stream infile, char *error)
91 wpc.config.float_norm_exp = wps->float_norm_exp; 91 wpc.config.float_norm_exp = wps->float_norm_exp;
92 92
93 wpc.config.bits_per_sample = (wpc.config.bytes_per_sample * 8) - 93 wpc.config.bits_per_sample = (wpc.config.bytes_per_sample * 8) -
94 ((wps->wphdr.flags & SHIFT_MASK) >> SHIFT_LSB); 94 ((wps->wphdr.flags & SHIFT_MASK) >> SHIFT_LSB);
95 95
96 if (!wpc.config.sample_rate) { 96 if (!wpc.config.sample_rate) {
97 if (!wps || !wps->wphdr.block_samples || (wps->wphdr.flags & SRATE_MASK) == SRATE_MASK) 97 if (!wps || !wps->wphdr.block_samples || (wps->wphdr.flags & SRATE_MASK) == SRATE_MASK)
98 wpc.config.sample_rate = 44100; 98 wpc.config.sample_rate = 44100;
99 else 99 else
100 wpc.config.sample_rate = sample_rates [(wps->wphdr.flags & SRATE_MASK) >> SRATE_LSB]; 100 wpc.config.sample_rate = sample_rates [(wps->wphdr.flags & SRATE_MASK) >> SRATE_LSB];
101 } 101 }
102 102
103 if (!wpc.config.num_channels) { 103 if (!wpc.config.num_channels) {
104 wpc.config.num_channels = (wps->wphdr.flags & MONO_FLAG) ? 1 : 2; 104 wpc.config.num_channels = (wps->wphdr.flags & MONO_FLAG) ? 1 : 2;
105 wpc.config.channel_mask = 0x5 - wpc.config.num_channels; 105 wpc.config.channel_mask = 0x5 - wpc.config.num_channels;
106 } 106 }
107 107
108 if (!(wps->wphdr.flags & FINAL_BLOCK)) 108 if (!(wps->wphdr.flags & FINAL_BLOCK))
109 wpc.reduced_channels = (wps->wphdr.flags & MONO_FLAG) ? 1 : 2; 109 wpc.reduced_channels = (wps->wphdr.flags & MONO_FLAG) ? 1 : 2;
110 110
111 return &wpc; 111 return &wpc;
112} 112}
@@ -125,22 +125,22 @@ int WavpackGetMode (WavpackContext *wpc)
125 int mode = 0; 125 int mode = 0;
126 126
127 if (wpc) { 127 if (wpc) {
128 if (wpc->config.flags & CONFIG_HYBRID_FLAG) 128 if (wpc->config.flags & CONFIG_HYBRID_FLAG)
129 mode |= MODE_HYBRID; 129 mode |= MODE_HYBRID;
130 else if (!(wpc->config.flags & CONFIG_LOSSY_MODE)) 130 else if (!(wpc->config.flags & CONFIG_LOSSY_MODE))
131 mode |= MODE_LOSSLESS; 131 mode |= MODE_LOSSLESS;
132 132
133 if (wpc->lossy_blocks) 133 if (wpc->lossy_blocks)
134 mode &= ~MODE_LOSSLESS; 134 mode &= ~MODE_LOSSLESS;
135 135
136 if (wpc->config.flags & CONFIG_FLOAT_DATA) 136 if (wpc->config.flags & CONFIG_FLOAT_DATA)
137 mode |= MODE_FLOAT; 137 mode |= MODE_FLOAT;
138 138
139 if (wpc->config.flags & CONFIG_HIGH_FLAG) 139 if (wpc->config.flags & CONFIG_HIGH_FLAG)
140 mode |= MODE_HIGH; 140 mode |= MODE_HIGH;
141 141
142 if (wpc->config.flags & CONFIG_FAST_FLAG) 142 if (wpc->config.flags & CONFIG_FAST_FLAG)
143 mode |= MODE_FAST; 143 mode |= MODE_FAST;
144 } 144 }
145 145
146 return mode; 146 return mode;
@@ -163,70 +163,70 @@ ulong WavpackUnpackSamples (WavpackContext *wpc, long *buffer, ulong samples)
163 int num_channels = wpc->config.num_channels; 163 int num_channels = wpc->config.num_channels;
164 164
165 while (samples) { 165 while (samples) {
166 if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) || 166 if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) ||
167 wps->sample_index >= wps->wphdr.block_index + wps->wphdr.block_samples) { 167 wps->sample_index >= wps->wphdr.block_index + wps->wphdr.block_samples) {
168 bcount = read_next_header (wpc->infile, &wps->wphdr); 168 bcount = read_next_header (wpc->infile, &wps->wphdr);
169 169
170 if (bcount == (ulong) -1) 170 if (bcount == (ulong) -1)
171 break; 171 break;
172 172
173 if (wps->wphdr.version < 0x402 || wps->wphdr.version > 0x40f) { 173 if (wps->wphdr.version < 0x402 || wps->wphdr.version > 0x40f) {
174 strcpy_loc (wpc->error_message, "invalid WavPack file!"); 174 strcpy_loc (wpc->error_message, "invalid WavPack file!");
175 break; 175 break;
176 } 176 }
177 177
178 if (!wps->wphdr.block_samples || wps->sample_index == wps->wphdr.block_index) 178 if (!wps->wphdr.block_samples || wps->sample_index == wps->wphdr.block_index)
179 if (!unpack_init (wpc)) 179 if (!unpack_init (wpc))
180 break; 180 break;
181 } 181 }
182 182
183 if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) || 183 if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) ||
184 wps->sample_index >= wps->wphdr.block_index + wps->wphdr.block_samples) 184 wps->sample_index >= wps->wphdr.block_index + wps->wphdr.block_samples)
185 continue; 185 continue;
186 186
187 if (wps->sample_index < wps->wphdr.block_index) { 187 if (wps->sample_index < wps->wphdr.block_index) {
188 samples_to_unpack = wps->wphdr.block_index - wps->sample_index; 188 samples_to_unpack = wps->wphdr.block_index - wps->sample_index;
189 189
190 if (samples_to_unpack > samples) 190 if (samples_to_unpack > samples)
191 samples_to_unpack = samples; 191 samples_to_unpack = samples;
192 192
193 wps->sample_index += samples_to_unpack; 193 wps->sample_index += samples_to_unpack;
194 samples_unpacked += samples_to_unpack; 194 samples_unpacked += samples_to_unpack;
195 samples -= samples_to_unpack; 195 samples -= samples_to_unpack;
196 196
197 if (wpc->reduced_channels) 197 if (wpc->reduced_channels)
198 samples_to_unpack *= wpc->reduced_channels; 198 samples_to_unpack *= wpc->reduced_channels;
199 else 199 else
200 samples_to_unpack *= num_channels; 200 samples_to_unpack *= num_channels;
201 201
202 while (samples_to_unpack--) 202 while (samples_to_unpack--)
203 *buffer++ = 0; 203 *buffer++ = 0;
204 204
205 continue; 205 continue;
206 } 206 }
207 207
208 samples_to_unpack = wps->wphdr.block_index + wps->wphdr.block_samples - wps->sample_index; 208 samples_to_unpack = wps->wphdr.block_index + wps->wphdr.block_samples - wps->sample_index;
209 209
210 if (samples_to_unpack > samples) 210 if (samples_to_unpack > samples)
211 samples_to_unpack = samples; 211 samples_to_unpack = samples;
212 212
213 unpack_samples (wpc, buffer, samples_to_unpack); 213 unpack_samples (wpc, buffer, samples_to_unpack);
214 214
215 if (wpc->reduced_channels) 215 if (wpc->reduced_channels)
216 buffer += samples_to_unpack * wpc->reduced_channels; 216 buffer += samples_to_unpack * wpc->reduced_channels;
217 else 217 else
218 buffer += samples_to_unpack * num_channels; 218 buffer += samples_to_unpack * num_channels;
219 219
220 samples_unpacked += samples_to_unpack; 220 samples_unpacked += samples_to_unpack;
221 samples -= samples_to_unpack; 221 samples -= samples_to_unpack;
222 222
223 if (wps->sample_index == wps->wphdr.block_index + wps->wphdr.block_samples) { 223 if (wps->sample_index == wps->wphdr.block_index + wps->wphdr.block_samples) {
224 if (check_crc_error (wpc)) 224 if (check_crc_error (wpc))
225 wpc->crc_errors++; 225 wpc->crc_errors++;
226 } 226 }
227 227
228 if (wps->sample_index == wpc->total_samples) 228 if (wps->sample_index == wpc->total_samples)
229 break; 229 break;
230 } 230 }
231 231
232 return samples_unpacked; 232 return samples_unpacked;
@@ -244,7 +244,7 @@ ulong WavpackGetNumSamples (WavpackContext *wpc)
244ulong WavpackGetSampleIndex (WavpackContext *wpc) 244ulong WavpackGetSampleIndex (WavpackContext *wpc)
245{ 245{
246 if (wpc) 246 if (wpc)
247 return wpc->stream.sample_index; 247 return wpc->stream.sample_index;
248 248
249 return (ulong) -1; 249 return (ulong) -1;
250} 250}
@@ -310,9 +310,9 @@ int WavpackGetBytesPerSample (WavpackContext *wpc)
310int WavpackGetReducedChannels (WavpackContext *wpc) 310int WavpackGetReducedChannels (WavpackContext *wpc)
311{ 311{
312 if (wpc) 312 if (wpc)
313 return wpc->reduced_channels ? wpc->reduced_channels : wpc->config.num_channels; 313 return wpc->reduced_channels ? wpc->reduced_channels : wpc->config.num_channels;
314 else 314 else
315 return 2; 315 return 2;
316} 316}
317 317
318// Read from current file position until a valid 32-byte WavPack 4.0 header is 318// Read from current file position until a valid 32-byte WavPack 4.0 header is
@@ -328,29 +328,228 @@ static ulong read_next_header (read_stream infile, WavpackHeader *wphdr)
328 int bleft; 328 int bleft;
329 329
330 while (1) { 330 while (1) {
331 if (sp < ep) { 331 if (sp < ep) {
332 bleft = ep - sp; 332 bleft = ep - sp;
333 memcpy (buffer, sp, bleft); 333 memcpy (buffer, sp, bleft);
334 } 334 }
335 else 335 else
336 bleft = 0; 336 bleft = 0;
337 337
338 if (infile (buffer + bleft, sizeof (*wphdr) - bleft) != (long) sizeof (*wphdr) - bleft) 338 if (infile (buffer + bleft, sizeof (*wphdr) - bleft) != (long) sizeof (*wphdr) - bleft)
339 return -1; 339 return -1;
340 340
341 sp = buffer; 341 sp = buffer;
342 342
343 if (*sp++ == 'w' && *sp == 'v' && *++sp == 'p' && *++sp == 'k' && 343 if (*sp++ == 'w' && *sp == 'v' && *++sp == 'p' && *++sp == 'k' &&
344 !(*++sp & 1) && sp [2] < 16 && !sp [3] && sp [5] == 4 && sp [4] >= 2 && sp [4] <= 0xf) { 344 !(*++sp & 1) && sp [2] < 16 && !sp [3] && sp [5] == 4 && sp [4] >= 2 && sp [4] <= 0xf) {
345 memcpy (wphdr, buffer, sizeof (*wphdr)); 345 memcpy (wphdr, buffer, sizeof (*wphdr));
346 little_endian_to_native (wphdr, WavpackHeaderFormat); 346 little_endian_to_native (wphdr, WavpackHeaderFormat);
347 return bytes_skipped; 347 return bytes_skipped;
348 } 348 }
349 349
350 while (sp < ep && *sp != 'w') 350 while (sp < ep && *sp != 'w')
351 sp++; 351 sp++;
352 352
353 if ((bytes_skipped += sp - buffer) > 1024 * 1024) 353 if ((bytes_skipped += sp - buffer) > 1024 * 1024)
354 return -1; 354 return -1;
355 } 355 }
356} 356}
357
358// Open context for writing WavPack files. The returned context pointer is used
359// in all following calls to the library. A return value of NULL indicates
360// that memory could not be allocated for the context.
361
362WavpackContext *WavpackOpenFileOutput (void)
363{
364 CLEAR (wpc);
365 return &wpc;
366}
367
368// Set the output buffer limits. This must be done before calling
369// WavpackPackSamples(), but also may be done afterward to adjust
370// the usable buffer. Note that writing CANNOT wrap in the buffer; the
371// entire output block must fit in the buffer.
372
373void WavpackSetOutputBuffer (WavpackContext *wpc, uchar *begin, uchar *end)
374{
375 wpc->stream.blockbuff = begin;
376 wpc->stream.blockend = end;
377}
378
379// Set configuration for writing WavPack files. This must be done before
380// sending any actual samples, however it is okay to send wrapper or other
381// metadata before calling this. The "config" structure contains the following
382// required information:
383
384// config->bytes_per_sample see WavpackGetBytesPerSample() for info
385// config->bits_per_sample see WavpackGetBitsPerSample() for info
386// config->num_channels self evident
387// config->sample_rate self evident
388
389// In addition, the following fields and flags may be set:
390
391// config->flags:
392// --------------
393// o CONFIG_HYBRID_FLAG select hybrid mode (must set bitrate)
394// o CONFIG_JOINT_STEREO select joint stereo (must set override also)
395// o CONFIG_JOINT_OVERRIDE override default joint stereo selection
396// o CONFIG_HYBRID_SHAPE select hybrid noise shaping (set override &
397// shaping_weight != 0.0)
398// o CONFIG_SHAPE_OVERRIDE override default hybrid noise shaping
399// (set CONFIG_HYBRID_SHAPE and shaping_weight)
400// o CONFIG_FAST_FLAG "fast" compression mode
401// o CONFIG_HIGH_FLAG "high" compression mode
402// o CONFIG_BITRATE_KBPS hybrid bitrate is kbps, not bits / sample
403
404// config->bitrate hybrid bitrate in either bits/sample or kbps
405// config->shaping_weight hybrid noise shaping coefficient override
406// config->float_norm_exp select floating-point data (127 for +/-1.0)
407
408// If the number of samples to be written is known then it should be passed
409// here. If the duration is not known then pass -1. In the case that the size
410// is not known (or the writing is terminated early) then it is suggested that
411// the application retrieve the first block written and let the library update
412// the total samples indication. A function is provided to do this update and
413// it should be done to the "correction" file also. If this cannot be done
414// (because a pipe is being used, for instance) then a valid WavPack will still
415// be created, but when applications want to access that file they will have
416// to seek all the way to the end to determine the actual duration. Also, if
417// a RIFF header has been included then it should be updated as well or the
418// WavPack file will not be directly unpackable to a valid wav file (although
419// it will still be usable by itself). A return of FALSE indicates an error.
420
421int WavpackSetConfiguration (WavpackContext *wpc, WavpackConfig *config, ulong total_samples)
422{
423 WavpackStream *wps = &wpc->stream;
424 ulong flags = (config->bytes_per_sample - 1), shift = 0;
425 int num_chans = config->num_channels;
426 int i;
427
428 if ((wpc->config.flags & CONFIG_HYBRID_FLAG) ||
429 wpc->config.float_norm_exp ||
430 num_chans < 1 || num_chans > 2)
431 return FALSE;
432
433 wpc->total_samples = total_samples;
434 wpc->config.sample_rate = config->sample_rate;
435 wpc->config.num_channels = config->num_channels;
436 wpc->config.bits_per_sample = config->bits_per_sample;
437 wpc->config.bytes_per_sample = config->bytes_per_sample;
438 wpc->config.flags = config->flags;
439
440 shift = (config->bytes_per_sample * 8) - config->bits_per_sample;
441
442 for (i = 0; i < 15; ++i)
443 if (wpc->config.sample_rate == sample_rates [i])
444 break;
445
446 flags |= i << SRATE_LSB;
447 flags |= shift << SHIFT_LSB;
448 flags |= CROSS_DECORR;
449
450 if (!(config->flags & CONFIG_JOINT_OVERRIDE) || (config->flags & CONFIG_JOINT_STEREO))
451 flags |= JOINT_STEREO;
452
453 memcpy (wps->wphdr.ckID, "wvpk", 4);
454 wps->wphdr.ckSize = sizeof (WavpackHeader) - 8;
455 wps->wphdr.total_samples = wpc->total_samples;
456 wps->wphdr.version = 0x403;
457 wps->wphdr.flags = flags;
458
459 wps->wphdr.flags |= INITIAL_BLOCK;
460 wps->wphdr.flags |= FINAL_BLOCK;
461
462 if (num_chans == 1) {
463 wps->wphdr.flags &= ~(JOINT_STEREO | CROSS_DECORR | HYBRID_BALANCE);
464 wps->wphdr.flags |= MONO_FLAG;
465 }
466
467 pack_init (wpc);
468 return TRUE;
469}
470
471// Add wrapper (currently RIFF only) to WavPack blocks. This should be called
472// before sending any audio samples for the RIFF header or after all samples
473// have been sent for any RIFF trailer. WavpackFlushSamples() should be called
474// between sending the last samples and calling this for trailer data to make
475// sure that headers and trailers don't get mixed up in very short files. If
476// the exact contents of the RIFF header are not known because, for example,
477// the file duration is uncertain or trailing chunks are possible, simply write
478// a "dummy" header of the correct length. When all data has been written it
479// will be possible to read the first block written and update the header
480// directly. An example of this can be found in the Audition filter. A
481// return of FALSE indicates an error.
482
483void WavpackAddWrapper (WavpackContext *wpc, void *data, ulong bcount)
484{
485 wpc->wrapper_data = data;
486 wpc->wrapper_bytes = bcount;
487}
488
489// Pack the specified samples. Samples must be stored in longs in the native
490// endian format of the executing processor. The number of samples specified
491// indicates composite samples (sometimes called "frames"). So, the actual
492// number of data points would be this "sample_count" times the number of
493// channels. Note that samples are accumulated here until enough exist to
494// create a complete WavPack block (or several blocks for multichannel audio).
495// If an application wants to break a block at a specific sample, then it must
496// simply call WavpackFlushSamples() to force an early termination. Completed
497// WavPack blocks are send to the function provided in the initial call to
498// WavpackOpenFileOutput(). A return of FALSE indicates an error.
499
500ulong WavpackPackSamples (WavpackContext *wpc, long *sample_buffer, ulong sample_count)
501{
502 WavpackStream *wps = &wpc->stream;
503 ulong flags = wps->wphdr.flags;
504 ulong bcount;
505 int result;
506
507 flags &= ~MAG_MASK;
508 flags += (1 << MAG_LSB) * ((flags & BYTES_STORED) * 8 + 7);
509
510 wps->wphdr.block_index = wps->sample_index;
511 wps->wphdr.block_samples = sample_count;
512 wps->wphdr.flags = flags;
513
514 result = pack_block (wpc, sample_buffer);
515
516 if (!result) {
517 strcpy_loc (wpc->error_message, "output buffer overflowed!");
518 return 0;
519 }
520
521 bcount = ((WavpackHeader *) wps->blockbuff)->ckSize + 8;
522 native_to_little_endian ((WavpackHeader *) wps->blockbuff, WavpackHeaderFormat);
523
524 return bcount;
525}
526
527// Given the pointer to the first block written (to either a .wv or .wvc file),
528// update the block with the actual number of samples written. This should
529// be done if WavpackSetConfiguration() was called with an incorrect number
530// of samples (or -1). It is the responsibility of the application to read and
531// rewrite the block. An example of this can be found in the Audition filter.
532
533void WavpackUpdateNumSamples (WavpackContext *wpc, void *first_block)
534{
535 little_endian_to_native (wpc, WavpackHeaderFormat);
536 ((WavpackHeader *) first_block)->total_samples = WavpackGetSampleIndex (wpc);
537 native_to_little_endian (wpc, WavpackHeaderFormat);
538}
539
540// Given the pointer to the first block written to a WavPack file, this
541// function returns the location of the stored RIFF header that was originally
542// written with WavpackAddWrapper(). This would normally be used to update
543// the wav header to indicate that a different number of samples was actually
544// written or if additional RIFF chunks are written at the end of the file.
545// It is the responsibility of the application to read and rewrite the block.
546// An example of this can be found in the Audition filter.
547
548void *WavpackGetWrapperLocation (void *first_block)
549{
550 if (((uchar *) first_block) [32] == ID_RIFF_HEADER)
551 return ((uchar *) first_block) + 34;
552 else
553 return NULL;
554}
555