1 ////////////////////////////////////////////////////////////////////////////
2 // **** WAVPACK **** //
3 // Hybrid Lossless Wavefile Compressor //
4 // Copyright (c) 1998 - 2013 Conifer Software. //
5 // All Rights Reserved. //
6 // Distributed under the BSD Software License (see license.txt) //
7 ////////////////////////////////////////////////////////////////////////////
8
9 // unpack_seek.c
10
11 // This module provides the high-level API for unpacking audio data from
12 // a specific sample index (i.e., seeking).
13
14 #ifndef NO_SEEKING
15
16 #include <stdlib.h>
17 #include <string.h>
18
19 #include "wavpack_local.h"
20
21 ///////////////////////////// executable code ////////////////////////////////
22
23 static int64_t find_sample (WavpackContext *wpc, void *infile, int64_t header_pos, int64_t sample);
24
25 // Seek to the specified sample index, returning TRUE on success. Note that
26 // files generated with version 4.0 or newer will seek almost immediately.
27 // Older files can take quite long if required to seek through unplayed
28 // portions of the file, but will create a seek map so that reverse seeks
29 // (or forward seeks to already scanned areas) will be very fast. After a
30 // FALSE return the file should not be accessed again (other than to close
31 // it); this is a fatal error.
32
WavpackSeekSample(WavpackContext * wpc,uint32_t sample)33 int WavpackSeekSample (WavpackContext *wpc, uint32_t sample)
34 {
35 return WavpackSeekSample64 (wpc, sample);
36 }
37
WavpackSeekSample64(WavpackContext * wpc,int64_t sample)38 int WavpackSeekSample64 (WavpackContext *wpc, int64_t sample)
39 {
40 WavpackStream *wps = wpc->streams ? wpc->streams [wpc->current_stream = 0] : NULL;
41 uint32_t bcount, samples_to_skip, samples_to_decode = 0;
42 int32_t *buffer;
43
44 if (wpc->total_samples == -1 || sample >= wpc->total_samples ||
45 !wpc->reader->can_seek (wpc->wv_in) || (wpc->open_flags & OPEN_STREAMING) ||
46 (wpc->wvc_flag && !wpc->reader->can_seek (wpc->wvc_in)))
47 return FALSE;
48
49 #ifdef ENABLE_LEGACY
50 if (wpc->stream3)
51 return seek_sample3 (wpc, (uint32_t) sample);
52 #endif
53
54 #ifdef ENABLE_DSD
55 if (wpc->decimation_context) { // the decimation code needs some context to be sample accurate
56 if (sample < 16) {
57 samples_to_decode = (uint32_t) sample;
58 sample = 0;
59 }
60 else {
61 samples_to_decode = 16;
62 sample -= 16;
63 }
64 }
65 #endif
66
67 if (!wps->wphdr.block_samples || !(wps->wphdr.flags & INITIAL_BLOCK) || sample < GET_BLOCK_INDEX (wps->wphdr) ||
68 sample >= GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples) {
69
70 free_streams (wpc);
71 wpc->filepos = find_sample (wpc, wpc->wv_in, wpc->filepos, sample);
72
73 if (wpc->filepos == -1)
74 return FALSE;
75
76 if (wpc->wvc_flag) {
77 wpc->file2pos = find_sample (wpc, wpc->wvc_in, 0, sample);
78
79 if (wpc->file2pos == -1)
80 return FALSE;
81 }
82 }
83
84 if (!wps->blockbuff) {
85 wpc->reader->set_pos_abs (wpc->wv_in, wpc->filepos);
86 wpc->reader->read_bytes (wpc->wv_in, &wps->wphdr, sizeof (WavpackHeader));
87 WavpackLittleEndianToNative (&wps->wphdr, WavpackHeaderFormat);
88
89 if ((wps->wphdr.ckSize & 1) || wps->wphdr.ckSize < 24 || wps->wphdr.ckSize >= 1024 * 1024) {
90 free_streams (wpc);
91 return FALSE;
92 }
93
94 wps->blockbuff = (unsigned char *)malloc (wps->wphdr.ckSize + 8);
95 memcpy (wps->blockbuff, &wps->wphdr, sizeof (WavpackHeader));
96
97 if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + sizeof (WavpackHeader), wps->wphdr.ckSize - 24) !=
98 wps->wphdr.ckSize - 24) {
99 free_streams (wpc);
100 return FALSE;
101 }
102
103 // render corrupt blocks harmless
104 if (!WavpackVerifySingleBlock (wps->blockbuff, !(wpc->open_flags & OPEN_NO_CHECKSUM))) {
105 wps->wphdr.ckSize = sizeof (WavpackHeader) - 8;
106 wps->wphdr.block_samples = 0;
107 memcpy (wps->blockbuff, &wps->wphdr, 32);
108 }
109
110 SET_BLOCK_INDEX (wps->wphdr, GET_BLOCK_INDEX (wps->wphdr) - wpc->initial_index);
111 memcpy (wps->blockbuff, &wps->wphdr, sizeof (WavpackHeader));
112 wps->init_done = FALSE;
113
114 if (wpc->wvc_flag) {
115 wpc->reader->set_pos_abs (wpc->wvc_in, wpc->file2pos);
116 wpc->reader->read_bytes (wpc->wvc_in, &wps->wphdr, sizeof (WavpackHeader));
117 WavpackLittleEndianToNative (&wps->wphdr, WavpackHeaderFormat);
118
119 if ((wps->wphdr.ckSize & 1) || wps->wphdr.ckSize < 24 || wps->wphdr.ckSize >= 1024 * 1024) {
120 free_streams (wpc);
121 return FALSE;
122 }
123
124 wps->block2buff = (unsigned char *)malloc (wps->wphdr.ckSize + 8);
125 memcpy (wps->block2buff, &wps->wphdr, sizeof (WavpackHeader));
126
127 if (wpc->reader->read_bytes (wpc->wvc_in, wps->block2buff + sizeof (WavpackHeader), wps->wphdr.ckSize - 24) !=
128 wps->wphdr.ckSize - 24) {
129 free_streams (wpc);
130 return FALSE;
131 }
132
133 // render corrupt blocks harmless
134 if (!WavpackVerifySingleBlock (wps->block2buff, !(wpc->open_flags & OPEN_NO_CHECKSUM))) {
135 wps->wphdr.ckSize = sizeof (WavpackHeader) - 8;
136 wps->wphdr.block_samples = 0;
137 memcpy (wps->block2buff, &wps->wphdr, 32);
138 }
139
140 SET_BLOCK_INDEX (wps->wphdr, GET_BLOCK_INDEX (wps->wphdr) - wpc->initial_index);
141 memcpy (wps->block2buff, &wps->wphdr, sizeof (WavpackHeader));
142 }
143
144 if (!wps->init_done && !unpack_init (wpc)) {
145 free_streams (wpc);
146 return FALSE;
147 }
148
149 wps->init_done = TRUE;
150 }
151
152 while (!wpc->reduced_channels && !(wps->wphdr.flags & FINAL_BLOCK)) {
153 if (++wpc->current_stream == wpc->num_streams) {
154
155 if (wpc->num_streams == wpc->max_streams) {
156 free_streams (wpc);
157 return FALSE;
158 }
159
160 wpc->streams = (WavpackStream **)realloc (wpc->streams, (wpc->num_streams + 1) * sizeof (wpc->streams [0]));
161 wps = wpc->streams [wpc->num_streams++] = (WavpackStream *)malloc (sizeof (WavpackStream));
162 CLEAR (*wps);
163 bcount = read_next_header (wpc->reader, wpc->wv_in, &wps->wphdr);
164
165 if (bcount == (uint32_t) -1) {
166 free_streams (wpc);
167 return FALSE;
168 }
169
170 wps->blockbuff = (unsigned char *)malloc (wps->wphdr.ckSize + 8);
171 memcpy (wps->blockbuff, &wps->wphdr, 32);
172
173 if (wpc->reader->read_bytes (wpc->wv_in, wps->blockbuff + 32, wps->wphdr.ckSize - 24) !=
174 wps->wphdr.ckSize - 24) {
175 free_streams (wpc);
176 return FALSE;
177 }
178
179 // render corrupt blocks harmless
180 if (!WavpackVerifySingleBlock (wps->blockbuff, !(wpc->open_flags & OPEN_NO_CHECKSUM))) {
181 wps->wphdr.ckSize = sizeof (WavpackHeader) - 8;
182 wps->wphdr.block_samples = 0;
183 memcpy (wps->blockbuff, &wps->wphdr, 32);
184 }
185
186 wps->init_done = FALSE;
187
188 if (wpc->wvc_flag && !read_wvc_block (wpc)) {
189 free_streams (wpc);
190 return FALSE;
191 }
192
193 if (!wps->init_done && !unpack_init (wpc)) {
194 free_streams (wpc);
195 return FALSE;
196 }
197
198 wps->init_done = TRUE;
199 }
200 else
201 wps = wpc->streams [wpc->current_stream];
202 }
203
204 if (sample < wps->sample_index) {
205 for (wpc->current_stream = 0; wpc->current_stream < wpc->num_streams; wpc->current_stream++)
206 if (!unpack_init (wpc))
207 return FALSE;
208 else
209 wpc->streams [wpc->current_stream]->init_done = TRUE;
210 }
211
212 samples_to_skip = (uint32_t) (sample - wps->sample_index);
213
214 if (samples_to_skip > 131072) {
215 free_streams (wpc);
216 return FALSE;
217 }
218
219 if (samples_to_skip) {
220 buffer = (int32_t *)malloc (samples_to_skip * 8);
221
222 for (wpc->current_stream = 0; wpc->current_stream < wpc->num_streams; wpc->current_stream++)
223 #ifdef ENABLE_DSD
224 if (wpc->streams [wpc->current_stream]->wphdr.flags & DSD_FLAG)
225 unpack_dsd_samples (wpc, buffer, samples_to_skip);
226 else
227 #endif
228 unpack_samples (wpc, buffer, samples_to_skip);
229
230 free (buffer);
231 }
232
233 wpc->current_stream = 0;
234
235 #ifdef ENABLE_DSD
236 if (wpc->decimation_context)
237 decimate_dsd_reset (wpc->decimation_context);
238
239 if (samples_to_decode) {
240 buffer = (int32_t *)calloc (1, samples_to_decode * wpc->config.num_channels * 4);
241
242 if (buffer) {
243 WavpackUnpackSamples (wpc, buffer, samples_to_decode);
244 free (buffer);
245 }
246 }
247 #endif
248
249 return TRUE;
250 }
251
252 // Find a valid WavPack header, searching either from the current file position
253 // (or from the specified position if not -1) and store it (endian corrected)
254 // at the specified pointer. The return value is the exact file position of the
255 // header, although we may have actually read past it. Because this function
256 // is used for seeking to a specific audio sample, it only considers blocks
257 // that contain audio samples for the initial stream to be valid.
258
259 #define BUFSIZE 4096
260
find_header(WavpackStreamReader64 * reader,void * id,int64_t filepos,WavpackHeader * wphdr)261 static int64_t find_header (WavpackStreamReader64 *reader, void *id, int64_t filepos, WavpackHeader *wphdr)
262 {
263 unsigned char *buffer = (unsigned char *)malloc (BUFSIZE), *sp = buffer, *ep = buffer;
264
265 if (filepos != (uint32_t) -1 && reader->set_pos_abs (id, filepos)) {
266 free (buffer);
267 return -1;
268 }
269
270 while (1) {
271 int bleft;
272
273 if (sp < ep) {
274 bleft = (int)(ep - sp);
275 memmove (buffer, sp, bleft);
276 ep -= (sp - buffer);
277 sp = buffer;
278 }
279 else {
280 if (sp > ep)
281 if (reader->set_pos_rel (id, (int32_t)(sp - ep), SEEK_CUR)) {
282 free (buffer);
283 return -1;
284 }
285
286 sp = ep = buffer;
287 bleft = 0;
288 }
289
290 ep += reader->read_bytes (id, ep, BUFSIZE - bleft);
291
292 if (ep - sp < 32) {
293 free (buffer);
294 return -1;
295 }
296
297 while (sp + 32 <= ep)
298 if (*sp++ == 'w' && *sp == 'v' && *++sp == 'p' && *++sp == 'k' &&
299 !(*++sp & 1) && sp [2] < 16 && !sp [3] && (sp [2] || sp [1] || *sp >= 24) && sp [5] == 4 &&
300 sp [4] >= (MIN_STREAM_VERS & 0xff) && sp [4] <= (MAX_STREAM_VERS & 0xff) && sp [18] < 3 && !sp [19]) {
301 memcpy (wphdr, sp - 4, sizeof (*wphdr));
302 WavpackLittleEndianToNative (wphdr, WavpackHeaderFormat);
303
304 if (wphdr->block_samples && (wphdr->flags & INITIAL_BLOCK)) {
305 free (buffer);
306 return reader->get_pos (id) - (ep - sp + 4);
307 }
308
309 if (wphdr->ckSize > 1024)
310 sp += wphdr->ckSize - 1024;
311 }
312 }
313 }
314
315 // Find the WavPack block that contains the specified sample. If "header_pos"
316 // is zero, then no information is assumed except the total number of samples
317 // in the file and its size in bytes. If "header_pos" is non-zero then we
318 // assume that it is the file position of the valid header image contained in
319 // the first stream and we can limit our search to either the portion above
320 // or below that point. If a .wvc file is being used, then this must be called
321 // for that file also.
322
find_sample(WavpackContext * wpc,void * infile,int64_t header_pos,int64_t sample)323 static int64_t find_sample (WavpackContext *wpc, void *infile, int64_t header_pos, int64_t sample)
324 {
325 WavpackStream *wps = wpc->streams [wpc->current_stream];
326 int64_t file_pos1 = 0, file_pos2 = wpc->reader->get_length (infile);
327 int64_t sample_pos1 = 0, sample_pos2 = wpc->total_samples;
328 double ratio = 0.96;
329 int file_skip = 0;
330
331 if (sample >= wpc->total_samples)
332 return -1;
333
334 if (header_pos && wps->wphdr.block_samples) {
335 if (GET_BLOCK_INDEX (wps->wphdr) > sample) {
336 sample_pos2 = GET_BLOCK_INDEX (wps->wphdr);
337 file_pos2 = header_pos;
338 }
339 else if (GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples <= sample) {
340 sample_pos1 = GET_BLOCK_INDEX (wps->wphdr);
341 file_pos1 = header_pos;
342 }
343 else
344 return header_pos;
345 }
346
347 while (1) {
348 double bytes_per_sample;
349 int64_t seek_pos;
350
351 bytes_per_sample = (double) file_pos2 - file_pos1;
352 bytes_per_sample /= sample_pos2 - sample_pos1;
353 seek_pos = file_pos1 + (file_skip ? 32 : 0);
354 seek_pos += (int64_t)(bytes_per_sample * (sample - sample_pos1) * ratio);
355 seek_pos = find_header (wpc->reader, infile, seek_pos, &wps->wphdr);
356
357 if (seek_pos != (int64_t) -1)
358 SET_BLOCK_INDEX (wps->wphdr, GET_BLOCK_INDEX (wps->wphdr) - wpc->initial_index);
359
360 if (seek_pos == (int64_t) -1 || seek_pos >= file_pos2) {
361 if (ratio > 0.0) {
362 if ((ratio -= 0.24) < 0.0)
363 ratio = 0.0;
364 }
365 else
366 return -1;
367 }
368 else if (GET_BLOCK_INDEX (wps->wphdr) > sample) {
369 sample_pos2 = GET_BLOCK_INDEX (wps->wphdr);
370 file_pos2 = seek_pos;
371 }
372 else if (GET_BLOCK_INDEX (wps->wphdr) + wps->wphdr.block_samples <= sample) {
373
374 if (seek_pos == file_pos1)
375 file_skip = 1;
376 else {
377 sample_pos1 = GET_BLOCK_INDEX (wps->wphdr);
378 file_pos1 = seek_pos;
379 }
380 }
381 else
382 return seek_pos;
383 }
384 }
385
386 #endif
387
388