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