x-raw-bayer -> x-bayer
[platform/upstream/gstreamer.git] / ext / wavpack / gstwavpackparse.c
1 /* GStreamer wavpack plugin
2  * Copyright (c) 2005 Arwed v. Merkatz <v.merkatz@gmx.net>
3  * Copyright (c) 2006 Tim-Philipp Müller <tim centricular net>
4  * Copyright (c) 2006 Sebastian Dröge <slomo@circular-chaos.org>
5  *
6  * gstwavpackparse.c: wavpack file parser
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 /**
25  * SECTION:element-wavpackparse
26  *
27  * WavpackParse takes raw, unframed Wavpack streams and splits them into
28  * single Wavpack chunks with information like bit depth and the position
29  * in the stream.
30  * <ulink url="http://www.wavpack.com/">Wavpack</ulink> is an open-source
31  * audio codec that features both lossless and lossy encoding.
32  *
33  * <refsect2>
34  * <title>Example launch line</title>
35  * |[
36  * gst-launch filesrc location=test.wv ! wavpackparse ! wavpackdec ! fakesink
37  * ]| This pipeline decodes the Wavpack file test.wv into raw audio buffers.
38  * </refsect2>
39  */
40
41 #ifdef HAVE_CONFIG_H
42 #include "config.h"
43 #endif
44
45 #include <gst/gst.h>
46 #include <gst/gst-i18n-plugin.h>
47
48 #include <math.h>
49 #include <string.h>
50
51 #include <wavpack/wavpack.h>
52 #include "gstwavpackparse.h"
53 #include "gstwavpackstreamreader.h"
54 #include "gstwavpackcommon.h"
55
56 GST_DEBUG_CATEGORY_STATIC (gst_wavpack_parse_debug);
57 #define GST_CAT_DEFAULT gst_wavpack_parse_debug
58
59 static inline GstWavpackParseIndexEntry *
60 gst_wavpack_parse_index_entry_new (void)
61 {
62   return g_slice_new (GstWavpackParseIndexEntry);
63 }
64
65 static inline void
66 gst_wavpack_parse_index_entry_free (GstWavpackParseIndexEntry * entry)
67 {
68   g_slice_free (GstWavpackParseIndexEntry, entry);
69 }
70
71 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
72     GST_PAD_SINK,
73     GST_PAD_ALWAYS,
74     GST_STATIC_CAPS ("audio/x-wavpack, "
75         "framed = (boolean) false; "
76         "audio/x-wavpack-correction, " "framed = (boolean) false")
77     );
78
79 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
80     GST_PAD_SRC,
81     GST_PAD_SOMETIMES,
82     GST_STATIC_CAPS ("audio/x-wavpack, "
83         "width = (int) [ 1, 32 ], "
84         "channels = (int) [ 1, 8 ], "
85         "rate = (int) [ 6000, 192000 ], " "framed = (boolean) true")
86     );
87
88 static GstStaticPadTemplate wvc_src_factory = GST_STATIC_PAD_TEMPLATE ("wvcsrc",
89     GST_PAD_SRC,
90     GST_PAD_SOMETIMES,
91     GST_STATIC_CAPS ("audio/x-wavpack-correction, " "framed = (boolean) true")
92     );
93
94 static gboolean gst_wavpack_parse_sink_activate (GstPad * sinkpad);
95
96 static gboolean
97 gst_wavpack_parse_sink_activate_pull (GstPad * sinkpad, gboolean active);
98
99 static void gst_wavpack_parse_loop (GstElement * element);
100
101 static GstStateChangeReturn gst_wavpack_parse_change_state (GstElement *
102     element, GstStateChange transition);
103 static void gst_wavpack_parse_reset (GstWavpackParse * parse);
104
105 static gint64 gst_wavpack_parse_get_upstream_length (GstWavpackParse * wvparse);
106
107 static GstBuffer *gst_wavpack_parse_pull_buffer (GstWavpackParse * wvparse,
108     gint64 offset, guint size, GstFlowReturn * flow);
109 static GstFlowReturn gst_wavpack_parse_chain (GstPad * pad, GstBuffer * buf);
110
111 GST_BOILERPLATE (GstWavpackParse, gst_wavpack_parse, GstElement,
112     GST_TYPE_ELEMENT);
113
114 static void
115 gst_wavpack_parse_base_init (gpointer klass)
116 {
117   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
118
119   gst_element_class_add_pad_template (element_class,
120       gst_static_pad_template_get (&src_factory));
121   gst_element_class_add_pad_template (element_class,
122       gst_static_pad_template_get (&wvc_src_factory));
123   gst_element_class_add_pad_template (element_class,
124       gst_static_pad_template_get (&sink_factory));
125
126   gst_element_class_set_details_simple (element_class, "Wavpack parser",
127       "Codec/Demuxer/Audio",
128       "Parses Wavpack files",
129       "Arwed v. Merkatz <v.merkatz@gmx.net>, "
130       "Sebastian Dröge <slomo@circular-chaos.org>");
131 }
132
133 static void
134 gst_wavpack_parse_finalize (GObject * object)
135 {
136   gst_wavpack_parse_reset (GST_WAVPACK_PARSE (object));
137
138   G_OBJECT_CLASS (parent_class)->finalize (object);
139 }
140
141 static void
142 gst_wavpack_parse_class_init (GstWavpackParseClass * klass)
143 {
144   GObjectClass *gobject_class;
145
146   GstElementClass *gstelement_class;
147
148   gobject_class = (GObjectClass *) klass;
149   gstelement_class = (GstElementClass *) klass;
150
151   gobject_class->finalize = gst_wavpack_parse_finalize;
152   gstelement_class->change_state =
153       GST_DEBUG_FUNCPTR (gst_wavpack_parse_change_state);
154 }
155
156 static GstWavpackParseIndexEntry *
157 gst_wavpack_parse_index_get_last_entry (GstWavpackParse * wvparse)
158 {
159   g_assert (wvparse->entries != NULL);
160
161   return wvparse->entries->data;
162 }
163
164 static GstWavpackParseIndexEntry *
165 gst_wavpack_parse_index_get_entry_from_sample (GstWavpackParse * wvparse,
166     gint64 sample_offset)
167 {
168   gint i;
169
170   GSList *node;
171
172   if (wvparse->entries == NULL)
173     return NULL;
174
175   for (node = wvparse->entries, i = 0; node; node = node->next, i++) {
176     GstWavpackParseIndexEntry *entry;
177
178     entry = node->data;
179
180     GST_LOG_OBJECT (wvparse, "Index entry %03u: sample %" G_GINT64_FORMAT " @"
181         " byte %" G_GINT64_FORMAT, i, entry->sample_offset, entry->byte_offset);
182
183     if (entry->sample_offset <= sample_offset &&
184         sample_offset < entry->sample_offset_end) {
185       GST_LOG_OBJECT (wvparse, "found match");
186       return entry;
187     }
188
189     /* as the list is sorted and we first look at the latest entry
190      * we can abort searching for an entry if the sample we want is
191      * after the latest one */
192     if (sample_offset >= entry->sample_offset_end)
193       break;
194   }
195   GST_LOG_OBJECT (wvparse, "no match in index");
196   return NULL;
197 }
198
199 static void
200 gst_wavpack_parse_index_append_entry (GstWavpackParse * wvparse,
201     gint64 byte_offset, gint64 sample_offset, gint64 num_samples)
202 {
203   GstWavpackParseIndexEntry *entry;
204
205   /* do we have this one already? */
206   if (wvparse->entries) {
207     entry = gst_wavpack_parse_index_get_last_entry (wvparse);
208     if (entry->byte_offset >= byte_offset
209         || entry->sample_offset >= sample_offset)
210       return;
211   }
212
213   GST_LOG_OBJECT (wvparse, "Adding index entry %8" G_GINT64_FORMAT " - %"
214       GST_TIME_FORMAT " @ offset 0x%08" G_GINT64_MODIFIER "x", sample_offset,
215       GST_TIME_ARGS (gst_util_uint64_scale_int (sample_offset,
216               GST_SECOND, wvparse->samplerate)), byte_offset);
217
218   entry = gst_wavpack_parse_index_entry_new ();
219   entry->byte_offset = byte_offset;
220   entry->sample_offset = sample_offset;
221   entry->sample_offset_end = sample_offset + num_samples;
222   wvparse->entries = g_slist_prepend (wvparse->entries, entry);
223 }
224
225 static void
226 gst_wavpack_parse_reset (GstWavpackParse * parse)
227 {
228   parse->total_samples = G_GINT64_CONSTANT (-1);
229   parse->samplerate = 0;
230   parse->channels = 0;
231
232   gst_segment_init (&parse->segment, GST_FORMAT_UNDEFINED);
233   parse->next_block_index = 0;
234
235   parse->current_offset = 0;
236   parse->need_newsegment = TRUE;
237   parse->discont = TRUE;
238   parse->upstream_length = -1;
239
240   if (parse->entries) {
241     g_slist_foreach (parse->entries, (GFunc) gst_wavpack_parse_index_entry_free,
242         NULL);
243     g_slist_free (parse->entries);
244     parse->entries = NULL;
245   }
246
247   if (parse->adapter) {
248     gst_adapter_clear (parse->adapter);
249     g_object_unref (parse->adapter);
250     parse->adapter = NULL;
251   }
252
253   if (parse->srcpad != NULL) {
254     gboolean res;
255
256     GST_DEBUG_OBJECT (parse, "Removing src pad");
257     res = gst_element_remove_pad (GST_ELEMENT (parse), parse->srcpad);
258     g_return_if_fail (res != FALSE);
259     gst_object_unref (parse->srcpad);
260     parse->srcpad = NULL;
261   }
262
263   g_list_foreach (parse->queued_events, (GFunc) gst_mini_object_unref, NULL);
264   g_list_free (parse->queued_events);
265   parse->queued_events = NULL;
266
267   if (parse->pending_buffer)
268     gst_buffer_unref (parse->pending_buffer);
269
270   parse->pending_buffer = NULL;
271 }
272
273 static const GstQueryType *
274 gst_wavpack_parse_get_src_query_types (GstPad * pad)
275 {
276   static const GstQueryType types[] = {
277     GST_QUERY_POSITION,
278     GST_QUERY_DURATION,
279     GST_QUERY_SEEKING,
280     0
281   };
282
283   return types;
284 }
285
286 static gboolean
287 gst_wavpack_parse_src_query (GstPad * pad, GstQuery * query)
288 {
289   GstWavpackParse *parse = GST_WAVPACK_PARSE (gst_pad_get_parent (pad));
290
291   GstFormat format;
292
293   gboolean ret = FALSE;
294
295   switch (GST_QUERY_TYPE (query)) {
296     case GST_QUERY_POSITION:{
297       gint64 cur;
298
299       guint rate;
300
301       GST_OBJECT_LOCK (parse);
302       cur = parse->segment.last_stop;
303       rate = parse->samplerate;
304       GST_OBJECT_UNLOCK (parse);
305
306       if (rate == 0) {
307         GST_DEBUG_OBJECT (parse, "haven't read header yet");
308         break;
309       }
310
311       gst_query_parse_position (query, &format, NULL);
312
313       switch (format) {
314         case GST_FORMAT_TIME:
315           cur = gst_util_uint64_scale_int (cur, GST_SECOND, rate);
316           gst_query_set_position (query, GST_FORMAT_TIME, cur);
317           ret = TRUE;
318           break;
319         case GST_FORMAT_DEFAULT:
320           gst_query_set_position (query, GST_FORMAT_DEFAULT, cur);
321           ret = TRUE;
322           break;
323         default:
324           GST_DEBUG_OBJECT (parse, "cannot handle position query in "
325               "%s format. Forwarding upstream.", gst_format_get_name (format));
326           ret = gst_pad_query_default (pad, query);
327           break;
328       }
329       break;
330     }
331     case GST_QUERY_DURATION:{
332       gint64 len;
333
334       guint rate;
335
336       GST_OBJECT_LOCK (parse);
337       rate = parse->samplerate;
338       len = parse->total_samples;
339       GST_OBJECT_UNLOCK (parse);
340
341       if (rate == 0) {
342         GST_DEBUG_OBJECT (parse, "haven't read header yet");
343         break;
344       }
345
346       gst_query_parse_duration (query, &format, NULL);
347
348       switch (format) {
349         case GST_FORMAT_TIME:
350           if (len != G_GINT64_CONSTANT (-1))
351             len = gst_util_uint64_scale_int (len, GST_SECOND, rate);
352           gst_query_set_duration (query, GST_FORMAT_TIME, len);
353           ret = TRUE;
354           break;
355         case GST_FORMAT_DEFAULT:
356           gst_query_set_duration (query, GST_FORMAT_DEFAULT, len);
357           ret = TRUE;
358           break;
359         default:
360           GST_DEBUG_OBJECT (parse, "cannot handle duration query in "
361               "%s format. Forwarding upstream.", gst_format_get_name (format));
362           ret = gst_pad_query_default (pad, query);
363           break;
364       }
365       break;
366     }
367     case GST_QUERY_SEEKING:{
368       gst_query_parse_seeking (query, &format, NULL, NULL, NULL);
369       if (format == GST_FORMAT_TIME || format == GST_FORMAT_DEFAULT) {
370         gboolean seekable;
371
372         gint64 duration = -1;
373
374         /* only fails if we didn't read the headers yet and can't say
375          * anything about our seeking capabilities */
376         if (!gst_pad_query_duration (pad, &format, &duration))
377           break;
378
379         /* can't seek in streaming mode yet */
380         GST_OBJECT_LOCK (parse);
381         seekable = (parse->adapter == NULL);
382         GST_OBJECT_UNLOCK (parse);
383
384         gst_query_set_seeking (query, format, seekable, 0, duration);
385         ret = TRUE;
386       }
387       break;
388     }
389     default:{
390       ret = gst_pad_query_default (pad, query);
391       break;
392     }
393   }
394
395   gst_object_unref (parse);
396   return ret;
397
398 }
399
400 /* returns TRUE on success, with byte_offset set to the offset of the
401  * wavpack chunk containing the sample requested. start_sample will be
402  * set to the first sample in the chunk starting at byte_offset.
403  * Scanning from the last known header offset to the wanted position
404  * when seeking forward isn't very clever, but seems fast enough in
405  * practice and has the nice side effect of populating our index
406  * table */
407 static gboolean
408 gst_wavpack_parse_scan_to_find_sample (GstWavpackParse * parse,
409     gint64 sample, gint64 * byte_offset, gint64 * start_sample)
410 {
411   GstWavpackParseIndexEntry *entry;
412
413   GstFlowReturn ret;
414
415   gint64 off = 0;
416
417   /* first, check if we have to scan at all */
418   entry = gst_wavpack_parse_index_get_entry_from_sample (parse, sample);
419   if (entry) {
420     *byte_offset = entry->byte_offset;
421     *start_sample = entry->sample_offset;
422     GST_LOG_OBJECT (parse, "Found index entry: sample %" G_GINT64_FORMAT
423         " @ offset %" G_GINT64_FORMAT, entry->sample_offset,
424         entry->byte_offset);
425     return TRUE;
426   }
427
428   GST_LOG_OBJECT (parse, "No matching entry in index, scanning file ...");
429
430   /* if we have an index, we can start scanning from the last known offset
431    * in there, after all we know our wanted sample is not in the index */
432   if (parse->entries) {
433     GstWavpackParseIndexEntry *entry;
434
435     entry = gst_wavpack_parse_index_get_last_entry (parse);
436     off = entry->byte_offset;
437   }
438
439   /* now scan forward until we find the chunk we're looking for or hit EOS */
440   do {
441     WavpackHeader header;
442
443     GstBuffer *buf;
444
445     buf = gst_wavpack_parse_pull_buffer (parse, off, sizeof (WavpackHeader),
446         &ret);
447
448     if (buf == NULL)
449       break;
450
451     gst_wavpack_read_header (&header, GST_BUFFER_DATA (buf));
452     gst_buffer_unref (buf);
453
454     if (header.flags & INITIAL_BLOCK)
455       gst_wavpack_parse_index_append_entry (parse, off, header.block_index,
456           header.block_samples);
457     else
458       continue;
459
460     if (header.block_index <= sample &&
461         sample < (header.block_index + header.block_samples)) {
462       *byte_offset = off;
463       *start_sample = header.block_index;
464       return TRUE;
465     }
466
467     off += header.ckSize + 8;
468   } while (1);
469
470   GST_DEBUG_OBJECT (parse, "scan failed: %s (off=0x%08" G_GINT64_MODIFIER "x)",
471       gst_flow_get_name (ret), off);
472
473   return FALSE;
474 }
475
476 static gboolean
477 gst_wavpack_parse_send_newsegment (GstWavpackParse * wvparse, gboolean update)
478 {
479   GstSegment *s = &wvparse->segment;
480
481   gboolean ret;
482
483   gint64 stop_time = -1;
484
485   gint64 start_time = 0;
486
487   gint64 cur_pos_time;
488
489   gint64 diff;
490
491   /* segment is in DEFAULT format, but we want to send a TIME newsegment */
492   start_time = gst_util_uint64_scale_int (s->start, GST_SECOND,
493       wvparse->samplerate);
494
495   if (s->stop != -1) {
496     stop_time = gst_util_uint64_scale_int (s->stop, GST_SECOND,
497         wvparse->samplerate);
498   }
499
500   GST_DEBUG_OBJECT (wvparse, "sending newsegment from %" GST_TIME_FORMAT
501       " to %" GST_TIME_FORMAT, GST_TIME_ARGS (start_time),
502       GST_TIME_ARGS (stop_time));
503
504   /* after a seek, s->last_stop will point to a chunk boundary, ie. from
505    * which sample we will start sending data again, while s->start will
506    * point to the sample we actually want to seek to and want to start
507    * playing right after the seek. Adjust clock-time for the difference
508    * so we start playing from start_time */
509   cur_pos_time = gst_util_uint64_scale_int (s->last_stop, GST_SECOND,
510       wvparse->samplerate);
511   diff = start_time - cur_pos_time;
512
513   ret = gst_pad_push_event (wvparse->srcpad,
514       gst_event_new_new_segment (update, s->rate, GST_FORMAT_TIME,
515           start_time, stop_time, start_time - diff));
516
517   return ret;
518 }
519
520 static gboolean
521 gst_wavpack_parse_handle_seek_event (GstWavpackParse * wvparse,
522     GstEvent * event)
523 {
524   GstSeekFlags seek_flags;
525
526   GstSeekType start_type;
527
528   GstSeekType stop_type;
529
530   GstSegment segment;
531
532   GstFormat format;
533
534   gboolean only_update;
535
536   gboolean flush, ret;
537
538   gdouble speed;
539
540   gint64 stop;
541
542   gint64 start;                 /* sample we want to seek to                  */
543
544   gint64 byte_offset;           /* byte offset the chunk we seek to starts at */
545
546   gint64 chunk_start;           /* first sample in chunk we seek to           */
547
548   guint rate;
549
550   gint64 last_stop;
551
552   if (wvparse->adapter) {
553     GST_DEBUG_OBJECT (wvparse, "seeking in streaming mode not implemented yet");
554     return FALSE;
555   }
556
557   gst_event_parse_seek (event, &speed, &format, &seek_flags, &start_type,
558       &start, &stop_type, &stop);
559
560   if (format != GST_FORMAT_DEFAULT && format != GST_FORMAT_TIME) {
561     GST_DEBUG ("seeking is only supported in TIME or DEFAULT format");
562     return FALSE;
563   }
564
565   if (speed < 0.0) {
566     GST_DEBUG ("only forward playback supported, rate %f not allowed", speed);
567     return FALSE;
568   }
569
570   GST_OBJECT_LOCK (wvparse);
571
572   rate = wvparse->samplerate;
573   if (rate == 0) {
574     GST_OBJECT_UNLOCK (wvparse);
575     GST_DEBUG ("haven't read header yet");
576     return FALSE;
577   }
578
579   /* figure out the last position we need to play. If it's configured (stop !=
580    * -1), use that, else we play until the total duration of the file */
581   if (stop == -1)
582     stop = wvparse->segment.duration;
583
584   /* convert from time to samples if necessary */
585   if (format == GST_FORMAT_TIME) {
586     if (start_type != GST_SEEK_TYPE_NONE)
587       start = gst_util_uint64_scale_int (start, rate, GST_SECOND);
588     if (stop_type != GST_SEEK_TYPE_NONE)
589       stop = gst_util_uint64_scale_int (stop, rate, GST_SECOND);
590   }
591
592   if (start < 0) {
593     GST_OBJECT_UNLOCK (wvparse);
594     GST_DEBUG_OBJECT (wvparse, "Invalid start sample %" G_GINT64_FORMAT, start);
595     return FALSE;
596   }
597
598   flush = ((seek_flags & GST_SEEK_FLAG_FLUSH) != 0);
599
600   /* operate on segment copy until we know the seek worked */
601   segment = wvparse->segment;
602
603   gst_segment_set_seek (&segment, speed, GST_FORMAT_DEFAULT,
604       seek_flags, start_type, start, stop_type, stop, &only_update);
605
606 #if 0
607   if (only_update) {
608     wvparse->segment = segment;
609     gst_wavpack_parse_send_newsegment (wvparse, TRUE);
610     goto done;
611   }
612 #endif
613
614   gst_pad_push_event (wvparse->sinkpad, gst_event_new_flush_start ());
615
616   if (flush) {
617     gst_pad_push_event (wvparse->srcpad, gst_event_new_flush_start ());
618   } else {
619     gst_pad_pause_task (wvparse->sinkpad);
620   }
621
622   GST_PAD_STREAM_LOCK (wvparse->sinkpad);
623
624   /* Save current position */
625   last_stop = wvparse->segment.last_stop;
626
627   gst_pad_push_event (wvparse->sinkpad, gst_event_new_flush_stop ());
628
629   if (flush) {
630     gst_pad_push_event (wvparse->srcpad, gst_event_new_flush_stop ());
631   }
632
633   GST_DEBUG_OBJECT (wvparse, "Performing seek to %" GST_TIME_FORMAT " sample %"
634       G_GINT64_FORMAT, GST_TIME_ARGS (segment.start * GST_SECOND / rate),
635       start);
636
637   ret = gst_wavpack_parse_scan_to_find_sample (wvparse, segment.start,
638       &byte_offset, &chunk_start);
639
640   if (ret) {
641     GST_DEBUG_OBJECT (wvparse, "new offset: %" G_GINT64_FORMAT, byte_offset);
642     wvparse->current_offset = byte_offset;
643     /* we want to send a newsegment event with the actual seek position
644      * as start, even though our first buffer might start before the
645      * configured segment. We leave it up to the decoder or sink to crop
646      * the output buffers accordingly */
647     wvparse->segment = segment;
648     wvparse->segment.last_stop = chunk_start;
649     wvparse->need_newsegment = TRUE;
650     wvparse->discont = (last_stop != chunk_start) ? TRUE : FALSE;
651
652     /* if we're doing a segment seek, post a SEGMENT_START message */
653     if (wvparse->segment.flags & GST_SEEK_FLAG_SEGMENT) {
654       gst_element_post_message (GST_ELEMENT_CAST (wvparse),
655           gst_message_new_segment_start (GST_OBJECT_CAST (wvparse),
656               wvparse->segment.format, wvparse->segment.last_stop));
657     }
658   } else {
659     GST_DEBUG_OBJECT (wvparse, "seek failed: don't know where to seek to");
660   }
661
662   GST_PAD_STREAM_UNLOCK (wvparse->sinkpad);
663   GST_OBJECT_UNLOCK (wvparse);
664
665   gst_pad_start_task (wvparse->sinkpad,
666       (GstTaskFunction) gst_wavpack_parse_loop, wvparse);
667
668   return ret;
669 }
670
671 static gboolean
672 gst_wavpack_parse_sink_event (GstPad * pad, GstEvent * event)
673 {
674   GstWavpackParse *parse;
675
676   gboolean ret = TRUE;
677
678   parse = GST_WAVPACK_PARSE (gst_pad_get_parent (pad));
679
680   switch (GST_EVENT_TYPE (event)) {
681     case GST_EVENT_FLUSH_STOP:{
682       if (parse->adapter) {
683         gst_adapter_clear (parse->adapter);
684       }
685       if (parse->pending_buffer) {
686         gst_buffer_unref (parse->pending_buffer);
687         parse->pending_buffer = NULL;
688         parse->pending_offset = 0;
689       }
690       ret = gst_pad_push_event (parse->srcpad, event);
691       break;
692     }
693     case GST_EVENT_NEWSEGMENT:{
694       parse->need_newsegment = TRUE;
695       gst_event_unref (event);
696       ret = TRUE;
697       break;
698     }
699     case GST_EVENT_EOS:{
700       if (parse->adapter) {
701         /* remove all bytes that are left in the adapter after EOS. They can't
702          * be a complete Wavpack block and we can't do anything with them */
703         gst_adapter_clear (parse->adapter);
704       }
705       if (parse->pending_buffer) {
706         gst_buffer_unref (parse->pending_buffer);
707         parse->pending_buffer = NULL;
708         parse->pending_offset = 0;
709       }
710       ret = gst_pad_push_event (parse->srcpad, event);
711       break;
712     }
713     default:{
714       /* stream lock is recursive, should be fine for all events */
715       GST_PAD_STREAM_LOCK (pad);
716       if (parse->srcpad == NULL) {
717         parse->queued_events = g_list_append (parse->queued_events, event);
718       } else {
719         ret = gst_pad_push_event (parse->srcpad, event);
720       }
721       GST_PAD_STREAM_UNLOCK (pad);
722     }
723   }
724
725
726   gst_object_unref (parse);
727   return ret;
728 }
729
730 static gboolean
731 gst_wavpack_parse_src_event (GstPad * pad, GstEvent * event)
732 {
733   GstWavpackParse *parse;
734
735   gboolean ret;
736
737   parse = GST_WAVPACK_PARSE (gst_pad_get_parent (pad));
738
739   switch (GST_EVENT_TYPE (event)) {
740     case GST_EVENT_SEEK:
741       ret = gst_wavpack_parse_handle_seek_event (parse, event);
742       break;
743     default:
744       ret = gst_pad_event_default (pad, event);
745       break;
746   }
747
748   gst_object_unref (parse);
749   return ret;
750 }
751
752 static void
753 gst_wavpack_parse_init (GstWavpackParse * parse, GstWavpackParseClass * gclass)
754 {
755   GstElementClass *klass = GST_ELEMENT_GET_CLASS (parse);
756
757   GstPadTemplate *tmpl;
758
759   tmpl = gst_element_class_get_pad_template (klass, "sink");
760   parse->sinkpad = gst_pad_new_from_template (tmpl, "sink");
761
762   gst_pad_set_activate_function (parse->sinkpad,
763       GST_DEBUG_FUNCPTR (gst_wavpack_parse_sink_activate));
764   gst_pad_set_activatepull_function (parse->sinkpad,
765       GST_DEBUG_FUNCPTR (gst_wavpack_parse_sink_activate_pull));
766   gst_pad_set_event_function (parse->sinkpad,
767       GST_DEBUG_FUNCPTR (gst_wavpack_parse_sink_event));
768   gst_pad_set_chain_function (parse->sinkpad,
769       GST_DEBUG_FUNCPTR (gst_wavpack_parse_chain));
770
771   gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad);
772
773   parse->srcpad = NULL;
774   gst_wavpack_parse_reset (parse);
775 }
776
777 static gint64
778 gst_wavpack_parse_get_upstream_length (GstWavpackParse * parse)
779 {
780   gint64 length = -1;
781
782   GstFormat format = GST_FORMAT_BYTES;
783
784   if (!gst_pad_query_peer_duration (parse->sinkpad, &format, &length)) {
785     length = -1;
786   } else {
787     GST_DEBUG ("upstream length: %" G_GINT64_FORMAT, length);
788   }
789   return length;
790 }
791
792 static GstBuffer *
793 gst_wavpack_parse_pull_buffer (GstWavpackParse * wvparse, gint64 offset,
794     guint size, GstFlowReturn * flow)
795 {
796   GstFlowReturn flow_ret;
797
798   GstBuffer *buf = NULL;
799
800   if (offset + size > wvparse->upstream_length) {
801     wvparse->upstream_length = gst_wavpack_parse_get_upstream_length (wvparse);
802     if (offset + size > wvparse->upstream_length) {
803       GST_DEBUG_OBJECT (wvparse, "EOS: %" G_GINT64_FORMAT " + %u > %"
804           G_GINT64_FORMAT, offset, size, wvparse->upstream_length);
805       flow_ret = GST_FLOW_EOS;
806       goto done;
807     }
808   }
809
810   flow_ret = gst_pad_pull_range (wvparse->sinkpad, offset, size, &buf);
811
812   if (flow_ret != GST_FLOW_OK) {
813     GST_DEBUG_OBJECT (wvparse, "pull_range (%" G_GINT64_FORMAT ", %u) "
814         "failed, flow: %s", offset, size, gst_flow_get_name (flow_ret));
815     buf = NULL;
816     goto done;
817   }
818
819   if (GST_BUFFER_SIZE (buf) < size) {
820     GST_DEBUG_OBJECT (wvparse, "Short read at offset %" G_GINT64_FORMAT
821         ", got only %u of %u bytes", offset, GST_BUFFER_SIZE (buf), size);
822     gst_buffer_unref (buf);
823     buf = NULL;
824     flow_ret = GST_FLOW_EOS;
825   }
826
827 done:
828   if (flow)
829     *flow = flow_ret;
830   return buf;
831 }
832
833 static gboolean
834 gst_wavpack_parse_create_src_pad (GstWavpackParse * wvparse, GstBuffer * buf,
835     WavpackHeader * header)
836 {
837   GstWavpackMetadata meta;
838
839   GstCaps *caps = NULL;
840
841   guchar *bufptr;
842
843   g_assert (wvparse->srcpad == NULL);
844
845   bufptr = GST_BUFFER_DATA (buf) + sizeof (WavpackHeader);
846
847   while (gst_wavpack_read_metadata (&meta, GST_BUFFER_DATA (buf), &bufptr)) {
848     switch (meta.id) {
849       case ID_WVC_BITSTREAM:{
850         caps = gst_caps_new_simple ("audio/x-wavpack-correction",
851             "framed", G_TYPE_BOOLEAN, TRUE, NULL);
852         wvparse->srcpad =
853             gst_pad_new_from_template (gst_element_class_get_pad_template
854             (GST_ELEMENT_GET_CLASS (wvparse), "wvcsrc"), "wvcsrc");
855         break;
856       }
857       case ID_WV_BITSTREAM:
858       case ID_WVX_BITSTREAM:{
859         WavpackStreamReader *stream_reader = gst_wavpack_stream_reader_new ();
860
861         WavpackContext *wpc;
862
863         gchar error_msg[80];
864
865         read_id rid;
866
867         gint channel_mask;
868
869         rid.buffer = GST_BUFFER_DATA (buf);
870         rid.length = GST_BUFFER_SIZE (buf);
871         rid.position = 0;
872
873         wpc =
874             WavpackOpenFileInputEx (stream_reader, &rid, NULL, error_msg, 0, 0);
875
876         if (!wpc)
877           return FALSE;
878
879         wvparse->samplerate = WavpackGetSampleRate (wpc);
880         wvparse->channels = WavpackGetNumChannels (wpc);
881         wvparse->total_samples =
882             (header->total_samples ==
883             0xffffffff) ? G_GINT64_CONSTANT (-1) : header->total_samples;
884
885         caps = gst_caps_new_simple ("audio/x-wavpack",
886             "width", G_TYPE_INT, WavpackGetBitsPerSample (wpc),
887             "channels", G_TYPE_INT, wvparse->channels,
888             "rate", G_TYPE_INT, wvparse->samplerate,
889             "framed", G_TYPE_BOOLEAN, TRUE, NULL);
890 #ifdef WAVPACK_OLD_API
891         channel_mask = wpc->config.channel_mask;
892 #else
893         channel_mask = WavpackGetChannelMask (wpc);
894 #endif
895         if (channel_mask == 0)
896           channel_mask =
897               gst_wavpack_get_default_channel_mask (wvparse->channels);
898
899         if (channel_mask != 0) {
900           if (!gst_wavpack_set_channel_layout (caps, channel_mask)) {
901             GST_WARNING_OBJECT (wvparse, "Failed to set channel layout");
902             gst_caps_unref (caps);
903             caps = NULL;
904             WavpackCloseFile (wpc);
905             g_free (stream_reader);
906             break;
907           }
908         }
909
910         wvparse->srcpad =
911             gst_pad_new_from_template (gst_element_class_get_pad_template
912             (GST_ELEMENT_GET_CLASS (wvparse), "src"), "src");
913         WavpackCloseFile (wpc);
914         g_free (stream_reader);
915         break;
916       }
917       default:{
918         GST_LOG_OBJECT (wvparse, "unhandled ID: 0x%02x", meta.id);
919         break;
920       }
921     }
922     if (caps != NULL)
923       break;
924   }
925
926   if (caps == NULL || wvparse->srcpad == NULL)
927     return FALSE;
928
929   GST_DEBUG_OBJECT (wvparse, "Added src pad with caps %" GST_PTR_FORMAT, caps);
930
931   gst_pad_set_query_function (wvparse->srcpad,
932       GST_DEBUG_FUNCPTR (gst_wavpack_parse_src_query));
933   gst_pad_set_query_type_function (wvparse->srcpad,
934       GST_DEBUG_FUNCPTR (gst_wavpack_parse_get_src_query_types));
935   gst_pad_set_event_function (wvparse->srcpad,
936       GST_DEBUG_FUNCPTR (gst_wavpack_parse_src_event));
937
938   gst_pad_set_caps (wvparse->srcpad, caps);
939   gst_caps_unref (caps);
940   gst_pad_use_fixed_caps (wvparse->srcpad);
941
942   gst_object_ref (wvparse->srcpad);
943   gst_pad_set_active (wvparse->srcpad, TRUE);
944   gst_element_add_pad (GST_ELEMENT (wvparse), wvparse->srcpad);
945   gst_element_no_more_pads (GST_ELEMENT (wvparse));
946
947   return TRUE;
948 }
949
950 static GstFlowReturn
951 gst_wavpack_parse_push_buffer (GstWavpackParse * wvparse, GstBuffer * buf,
952     WavpackHeader * header)
953 {
954   GstFlowReturn ret;
955   wvparse->current_offset += header->ckSize + 8;
956
957   wvparse->segment.last_stop = header->block_index;
958
959   if (wvparse->need_newsegment) {
960     if (gst_wavpack_parse_send_newsegment (wvparse, FALSE))
961       wvparse->need_newsegment = FALSE;
962   }
963
964   /* send any queued events */
965   if (wvparse->queued_events) {
966     GList *l;
967
968     for (l = wvparse->queued_events; l != NULL; l = l->next) {
969       gst_pad_push_event (wvparse->srcpad, GST_EVENT (l->data));
970     }
971     g_list_free (wvparse->queued_events);
972     wvparse->queued_events = NULL;
973   }
974
975   if (wvparse->pending_buffer == NULL) {
976     wvparse->pending_buffer = buf;
977     wvparse->pending_offset = header->block_index;
978   } else if (wvparse->pending_offset == header->block_index) {
979     wvparse->pending_buffer = gst_buffer_join (wvparse->pending_buffer, buf);
980   } else {
981     GST_ERROR ("Got incomplete block, dropping");
982     gst_buffer_unref (wvparse->pending_buffer);
983     wvparse->pending_buffer = buf;
984     wvparse->pending_offset = header->block_index;
985   }
986
987   if (!(header->flags & FINAL_BLOCK))
988     return GST_FLOW_OK;
989
990   buf = wvparse->pending_buffer;
991   wvparse->pending_buffer = NULL;
992
993   GST_BUFFER_TIMESTAMP (buf) = gst_util_uint64_scale_int (header->block_index,
994       GST_SECOND, wvparse->samplerate);
995   GST_BUFFER_DURATION (buf) = gst_util_uint64_scale_int (header->block_samples,
996       GST_SECOND, wvparse->samplerate);
997   GST_BUFFER_OFFSET (buf) = header->block_index;
998   GST_BUFFER_OFFSET_END (buf) = header->block_index + header->block_samples;
999
1000   if (wvparse->discont || wvparse->next_block_index != header->block_index) {
1001     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
1002     wvparse->discont = FALSE;
1003   }
1004
1005   wvparse->next_block_index = header->block_index + header->block_samples;
1006
1007   gst_buffer_set_caps (buf, GST_PAD_CAPS (wvparse->srcpad));
1008
1009   GST_LOG_OBJECT (wvparse, "Pushing buffer with time %" GST_TIME_FORMAT,
1010       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
1011
1012   ret = gst_pad_push (wvparse->srcpad, buf);
1013
1014   wvparse->segment.last_stop = wvparse->next_block_index;
1015
1016   return ret;
1017 }
1018
1019 static guint8 *
1020 gst_wavpack_parse_find_marker (guint8 * buf, guint size)
1021 {
1022   int i;
1023
1024   guint8 *ret = NULL;
1025
1026   if (G_UNLIKELY (size < 4))
1027     return NULL;
1028
1029   for (i = 0; i < size - 4; i++) {
1030     if (memcmp (buf + i, "wvpk", 4) == 0) {
1031       ret = buf + i;
1032       break;
1033     }
1034   }
1035   return ret;
1036 }
1037
1038 static GstFlowReturn
1039 gst_wavpack_parse_resync_loop (GstWavpackParse * parse, WavpackHeader * header)
1040 {
1041   GstFlowReturn flow_ret = GST_FLOW_EOS;
1042
1043   GstBuffer *buf = NULL;
1044
1045   /* loop until we have a frame header or reach the end of the stream */
1046   while (1) {
1047     guint8 *data, *marker;
1048
1049     guint len, size;
1050
1051     if (buf) {
1052       gst_buffer_unref (buf);
1053       buf = NULL;
1054     }
1055
1056     if (parse->upstream_length == 0 ||
1057         parse->upstream_length <= parse->current_offset) {
1058       parse->upstream_length = gst_wavpack_parse_get_upstream_length (parse);
1059       if (parse->upstream_length == 0 ||
1060           parse->upstream_length <= parse->current_offset) {
1061         break;
1062       }
1063     }
1064
1065     len = MIN (parse->upstream_length - parse->current_offset, 2048);
1066
1067     GST_LOG_OBJECT (parse, "offset: %" G_GINT64_FORMAT, parse->current_offset);
1068
1069     buf = gst_wavpack_parse_pull_buffer (parse, parse->current_offset,
1070         len, &flow_ret);
1071
1072     /* whatever the problem is, there's nothing more for us to do for now */
1073     if (flow_ret != GST_FLOW_OK)
1074       break;
1075
1076     data = GST_BUFFER_DATA (buf);
1077     size = GST_BUFFER_SIZE (buf);
1078
1079     /* not enough data for a header? */
1080     if (size < sizeof (WavpackHeader))
1081       break;
1082
1083     /* got a header right where we are at now? */
1084     if (gst_wavpack_read_header (header, data))
1085       break;
1086
1087     /* nope, let's see if we can find one */
1088     marker = gst_wavpack_parse_find_marker (data + 1, size - 1);
1089
1090     if (marker) {
1091       parse->current_offset += marker - data;
1092       /* do one more loop iteration to make sure we pull enough
1093        * data for a full header, we'll bail out then */
1094     } else {
1095       parse->current_offset += len - 4;
1096     }
1097   }
1098
1099   if (buf)
1100     gst_buffer_unref (buf);
1101
1102   return flow_ret;
1103 }
1104
1105 static void
1106 gst_wavpack_parse_loop (GstElement * element)
1107 {
1108   GstWavpackParse *parse = GST_WAVPACK_PARSE (element);
1109
1110   GstFlowReturn flow_ret;
1111   WavpackHeader header = { {0,}, 0, };
1112   GstBuffer *buf = NULL;
1113
1114   flow_ret = gst_wavpack_parse_resync_loop (parse, &header);
1115
1116   if (flow_ret != GST_FLOW_OK)
1117     goto pause;
1118
1119   GST_LOG_OBJECT (parse, "Read header at offset %" G_GINT64_FORMAT
1120       ": chunk size = %u+8", parse->current_offset, header.ckSize);
1121
1122   buf = gst_wavpack_parse_pull_buffer (parse, parse->current_offset,
1123       header.ckSize + 8, &flow_ret);
1124
1125   if (flow_ret != GST_FLOW_OK)
1126     goto pause;
1127
1128   if (parse->srcpad == NULL) {
1129     if (!gst_wavpack_parse_create_src_pad (parse, buf, &header)) {
1130       GST_ERROR_OBJECT (parse, "Failed to create src pad");
1131       flow_ret = GST_FLOW_ERROR;
1132       goto pause;
1133     }
1134   }
1135   if (header.flags & INITIAL_BLOCK)
1136     gst_wavpack_parse_index_append_entry (parse, parse->current_offset,
1137         header.block_index, header.block_samples);
1138
1139   flow_ret = gst_wavpack_parse_push_buffer (parse, buf, &header);
1140   if (flow_ret != GST_FLOW_OK)
1141     goto pause;
1142
1143   return;
1144
1145 pause:
1146   {
1147     const gchar *reason = gst_flow_get_name (flow_ret);
1148
1149     GST_LOG_OBJECT (parse, "pausing task, reason %s", reason);
1150     gst_pad_pause_task (parse->sinkpad);
1151
1152     if (flow_ret == GST_FLOW_EOS && parse->srcpad) {
1153       if (parse->segment.flags & GST_SEEK_FLAG_SEGMENT) {
1154         GstClockTime stop;
1155
1156         GST_LOG_OBJECT (parse, "Sending segment done");
1157
1158         if ((stop = parse->segment.stop) == -1)
1159           stop = parse->segment.duration;
1160
1161         gst_element_post_message (GST_ELEMENT_CAST (parse),
1162             gst_message_new_segment_done (GST_OBJECT_CAST (parse),
1163                 parse->segment.format, stop));
1164       } else {
1165         GST_LOG_OBJECT (parse, "Sending EOS, at end of stream");
1166         gst_pad_push_event (parse->srcpad, gst_event_new_eos ());
1167       }
1168     } else if (flow_ret == GST_FLOW_NOT_LINKED || flow_ret < GST_FLOW_EOS) {
1169       GST_ELEMENT_ERROR (parse, STREAM, FAILED,
1170           (_("Internal data stream error.")), ("stream stopped, reason %s",
1171               reason));
1172       if (parse->srcpad)
1173         gst_pad_push_event (parse->srcpad, gst_event_new_eos ());
1174     }
1175     return;
1176   }
1177 }
1178
1179 static gboolean
1180 gst_wavpack_parse_resync_adapter (GstAdapter * adapter)
1181 {
1182   const guint8 *buf, *marker;
1183
1184   guint avail = gst_adapter_available (adapter);
1185
1186   if (avail < 4)
1187     return FALSE;
1188
1189   /* if the marker is at the beginning don't do the expensive search */
1190   buf = gst_adapter_peek (adapter, 4);
1191   if (memcmp (buf, "wvpk", 4) == 0)
1192     return TRUE;
1193
1194   if (avail == 4)
1195     return FALSE;
1196
1197   /* search for the marker in the complete content of the adapter */
1198   buf = gst_adapter_peek (adapter, avail);
1199   if (buf && (marker = gst_wavpack_parse_find_marker ((guint8 *) buf, avail))) {
1200     gst_adapter_flush (adapter, marker - buf);
1201     return TRUE;
1202   }
1203
1204   /* flush everything except the last 4 bytes. they could contain
1205    * the start of a new marker */
1206   gst_adapter_flush (adapter, avail - 4);
1207
1208   return FALSE;
1209 }
1210
1211 static GstFlowReturn
1212 gst_wavpack_parse_chain (GstPad * pad, GstBuffer * buf)
1213 {
1214   GstWavpackParse *wvparse = GST_WAVPACK_PARSE (GST_PAD_PARENT (pad));
1215
1216   GstFlowReturn ret = GST_FLOW_OK;
1217
1218   WavpackHeader wph;
1219
1220   const guint8 *tmp_buf;
1221
1222   if (!wvparse->adapter) {
1223     wvparse->adapter = gst_adapter_new ();
1224   }
1225
1226   if (GST_BUFFER_IS_DISCONT (buf)) {
1227     gst_adapter_clear (wvparse->adapter);
1228     wvparse->discont = TRUE;
1229   }
1230
1231   gst_adapter_push (wvparse->adapter, buf);
1232
1233   if (gst_adapter_available (wvparse->adapter) < sizeof (WavpackHeader))
1234     return ret;
1235
1236   if (!gst_wavpack_parse_resync_adapter (wvparse->adapter))
1237     return ret;
1238
1239   tmp_buf = gst_adapter_peek (wvparse->adapter, sizeof (WavpackHeader));
1240   gst_wavpack_read_header (&wph, (guint8 *) tmp_buf);
1241
1242   while (gst_adapter_available (wvparse->adapter) >= wph.ckSize + 4 * 1 + 4) {
1243     GstBuffer *outbuf =
1244         gst_adapter_take_buffer (wvparse->adapter, wph.ckSize + 4 * 1 + 4);
1245
1246     if (!outbuf)
1247       return GST_FLOW_ERROR;
1248
1249     if (wvparse->srcpad == NULL) {
1250       if (!gst_wavpack_parse_create_src_pad (wvparse, outbuf, &wph)) {
1251         GST_ERROR_OBJECT (wvparse, "Failed to create src pad");
1252         ret = GST_FLOW_ERROR;
1253         break;
1254       }
1255     }
1256
1257     ret = gst_wavpack_parse_push_buffer (wvparse, outbuf, &wph);
1258
1259     if (ret != GST_FLOW_OK)
1260       break;
1261
1262     if (gst_adapter_available (wvparse->adapter) >= sizeof (WavpackHeader)) {
1263       tmp_buf = gst_adapter_peek (wvparse->adapter, sizeof (WavpackHeader));
1264
1265       if (!gst_wavpack_parse_resync_adapter (wvparse->adapter))
1266         break;
1267
1268       gst_wavpack_read_header (&wph, (guint8 *) tmp_buf);
1269     }
1270   }
1271
1272   return ret;
1273 }
1274
1275 static GstStateChangeReturn
1276 gst_wavpack_parse_change_state (GstElement * element, GstStateChange transition)
1277 {
1278   GstWavpackParse *wvparse = GST_WAVPACK_PARSE (element);
1279
1280   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1281
1282   switch (transition) {
1283     case GST_STATE_CHANGE_READY_TO_PAUSED:
1284       gst_segment_init (&wvparse->segment, GST_FORMAT_DEFAULT);
1285       wvparse->segment.last_stop = 0;
1286     default:
1287       break;
1288   }
1289
1290   if (GST_ELEMENT_CLASS (parent_class)->change_state)
1291     ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1292
1293   switch (transition) {
1294     case GST_STATE_CHANGE_PAUSED_TO_READY:
1295       gst_wavpack_parse_reset (wvparse);
1296       break;
1297     default:
1298       break;
1299   }
1300
1301   return ret;
1302 }
1303
1304 static gboolean
1305 gst_wavpack_parse_sink_activate (GstPad * sinkpad)
1306 {
1307   if (gst_pad_check_pull_range (sinkpad)) {
1308     return gst_pad_activate_pull (sinkpad, TRUE);
1309   } else {
1310     return gst_pad_activate_push (sinkpad, TRUE);
1311   }
1312 }
1313
1314 static gboolean
1315 gst_wavpack_parse_sink_activate_pull (GstPad * sinkpad, gboolean active)
1316 {
1317   gboolean result;
1318
1319   if (active) {
1320     result = gst_pad_start_task (sinkpad,
1321         (GstTaskFunction) gst_wavpack_parse_loop, GST_PAD_PARENT (sinkpad));
1322   } else {
1323     result = gst_pad_stop_task (sinkpad);
1324   }
1325
1326   return result;
1327 }
1328
1329 gboolean
1330 gst_wavpack_parse_plugin_init (GstPlugin * plugin)
1331 {
1332   if (!gst_element_register (plugin, "wavpackparse",
1333           GST_RANK_PRIMARY, GST_TYPE_WAVPACK_PARSE)) {
1334     return FALSE;
1335   }
1336
1337   GST_DEBUG_CATEGORY_INIT (gst_wavpack_parse_debug, "wavpack_parse", 0,
1338       "Wavpack file parser");
1339
1340   return TRUE;
1341 }