f2e6307a4057cf63e73d7ba59a66fdcc62342cf4
[platform/upstream/gstreamer.git] / gst / wavparse / gstwavparse.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20
21 #include <string.h>
22
23 #include <gstwavparse.h>
24
25 static void             gst_wavparse_class_init (GstWavParseClass *klass);
26 static void             gst_wavparse_init       (GstWavParse *wavparse);
27
28 static GstCaps*         wav_typefind            (GstBuffer *buf, gpointer private);
29
30 static void             gst_wavparse_chain      (GstPad *pad, GstBuffer *buf);
31
32 /* elementfactory information */
33 static GstElementDetails gst_wavparse_details = {
34   ".wav parser",
35   "Parser/Audio",
36   "Parse a .wav file into raw audio",
37   VERSION,
38   "Erik Walthinsen <omega@cse.ogi.edu>",
39   "(C) 1999",
40 };
41
42 GST_PADTEMPLATE_FACTORY (sink_template_factory,
43   "wavparse_sink",
44   GST_PAD_SINK,
45   GST_PAD_ALWAYS,
46   GST_CAPS_NEW (
47     "wavparse_wav",   
48     "audio/wav",  
49     NULL
50   )
51 )
52
53 GST_PADTEMPLATE_FACTORY (src_template_factory,
54   "wavparse_src",
55   GST_PAD_SRC,
56   GST_PAD_ALWAYS,
57   GST_CAPS_NEW (
58     "wavparse_raw",   
59     "audio/raw",  
60       "format",            GST_PROPS_STRING ("int"),
61        "law",              GST_PROPS_INT (0),
62        "endianness",       GST_PROPS_INT (G_BYTE_ORDER),
63        "signed",           GST_PROPS_BOOLEAN (TRUE),
64        "width",            GST_PROPS_LIST (
65                              GST_PROPS_INT (8),
66                              GST_PROPS_INT (16)
67                            ),
68        "depth",            GST_PROPS_LIST (
69                              GST_PROPS_INT (8),
70                              GST_PROPS_INT (16)
71                            ),
72        "rate",             GST_PROPS_INT_RANGE (8000, 48000), 
73        "channels",         GST_PROPS_INT_RANGE (1, 2)
74   )
75 )
76
77 /* typefactory for 'wav' */
78 static GstTypeDefinition 
79 wavdefinition = 
80 {
81   "wavparse_audio/wav",
82   "audio/wav",
83   ".wav",
84   wav_typefind,
85 };
86
87
88 /* WavParse signals and args */
89 enum {
90   /* FILL ME */
91   LAST_SIGNAL
92 };
93
94 enum {
95   ARG_0,
96   /* FILL ME */
97 };
98
99 static GstElementClass *parent_class = NULL;
100 /*static guint gst_wavparse_signals[LAST_SIGNAL] = { 0 }; */
101
102 GType
103 gst_wavparse_get_type (void) 
104 {
105   static GType wavparse_type = 0;
106
107   if (!wavparse_type) {
108     static const GTypeInfo wavparse_info = {
109       sizeof(GstWavParseClass),      NULL,
110       NULL,
111       (GClassInitFunc) gst_wavparse_class_init,
112       NULL,
113       NULL,
114       sizeof(GstWavParse),
115       0,
116       (GInstanceInitFunc) gst_wavparse_init,
117     };
118     wavparse_type = g_type_register_static (GST_TYPE_ELEMENT, "GstWavParse", &wavparse_info, 0);
119   }
120   return wavparse_type;
121 }
122
123 static void
124 gst_wavparse_class_init (GstWavParseClass *klass) 
125 {
126   GstElementClass *gstelement_class;
127
128   gstelement_class = (GstElementClass*) klass;
129
130   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
131 }
132
133 static void 
134 gst_wavparse_init (GstWavParse *wavparse) 
135 {
136   wavparse->sinkpad = gst_pad_new_from_template (GST_PADTEMPLATE_GET (sink_template_factory), "sink");
137   gst_element_add_pad (GST_ELEMENT (wavparse), wavparse->sinkpad);
138   gst_pad_set_chain_function (wavparse->sinkpad, gst_wavparse_chain);
139
140   wavparse->srcpad = gst_pad_new_from_template (GST_PADTEMPLATE_GET (src_template_factory), "src");
141   gst_element_add_pad (GST_ELEMENT (wavparse), wavparse->srcpad);
142
143   wavparse->riff = NULL;
144
145   wavparse->state = GST_WAVPARSE_UNKNOWN;
146   wavparse->riff = NULL;
147   wavparse->riff_nextlikely = 0;
148   wavparse->size = 0;
149   wavparse->bps = 0;
150 }
151
152 static GstCaps*
153 wav_typefind (GstBuffer *buf, gpointer private)
154 {
155   gchar *data = GST_BUFFER_DATA (buf);
156
157   if (strncmp (&data[0], "RIFF", 4)) return NULL;
158   if (strncmp (&data[8], "WAVE", 4)) return NULL;
159
160   return gst_caps_new ("wav_typefind", "audio/wav", NULL);
161 }
162
163
164 static void
165 gst_wavparse_chain (GstPad *pad, GstBuffer *buf)
166 {
167   GstWavParse *wavparse;
168   gboolean buffer_riffed = FALSE;       /* so we don't parse twice */
169   gchar *data;
170   gulong size;
171
172   g_return_if_fail (pad != NULL);
173   g_return_if_fail (GST_IS_PAD (pad));
174   g_return_if_fail (buf != NULL);
175   g_return_if_fail (GST_BUFFER_DATA (buf) != NULL);
176
177   wavparse = GST_WAVPARSE (gst_pad_get_parent (pad));
178   GST_DEBUG (0, "gst_wavparse_chain: got buffer in '%s'\n",
179           gst_object_get_name (GST_OBJECT (wavparse)));
180   data = (guchar *) GST_BUFFER_DATA (buf);
181   size = GST_BUFFER_SIZE (buf);
182
183   /* walk through the states in priority order */
184   /* we're in the data region */
185   if (wavparse->state == GST_WAVPARSE_DATA) {
186     /* if we're expected to see a new chunk in this buffer */
187     if ((wavparse->riff_nextlikely - GST_BUFFER_OFFSET (buf)) < GST_BUFFER_SIZE (buf)) {
188             
189       GST_BUFFER_SIZE (buf) = wavparse->riff_nextlikely - GST_BUFFER_OFFSET (buf);
190       
191       wavparse->state = GST_WAVPARSE_OTHER;
192       /* I suppose we could signal an EOF at this point, but that may be
193          premature.  We've stopped data flow, that's the main thing. */
194     } 
195     gst_pad_push (wavparse->srcpad, buf);
196     return;
197   }
198
199   if (wavparse->state == GST_WAVPARSE_OTHER) {
200     GST_DEBUG (0, "we're in unknown territory here, not passing on\n");
201     return;
202   }
203
204
205   /* here we deal with parsing out the primary state */
206   /* these are sequenced such that in the normal case each (RIFF/WAVE,
207      fmt, data) will fire in sequence, as they should */
208
209   /* we're in null state now, look for the RIFF header, start parsing */
210   if (wavparse->state == GST_WAVPARSE_UNKNOWN) {
211     gint retval;
212
213     GST_DEBUG (0, "GstWavParse: checking for RIFF format\n");
214
215     /* create a new RIFF parser */
216     wavparse->riff = gst_riff_new ();
217     
218     /* give it the current buffer to start parsing */
219     retval = gst_riff_next_buffer (wavparse->riff, buf, 0);
220     buffer_riffed = TRUE;
221     if (retval < 0) {
222       GST_DEBUG (0, "sorry, isn't RIFF\n");
223       return;
224     }
225
226     /* this has to be a file of form WAVE for us to deal with it */
227     if (wavparse->riff->form != gst_riff_fourcc_to_id ("WAVE")) {
228       GST_DEBUG (0, "sorry, isn't WAVE\n");
229       return;
230     }
231
232     /* at this point we're waiting for the 'fmt ' chunk */
233     wavparse->state = GST_WAVPARSE_CHUNK_FMT;
234   }
235
236   /* we're now looking for the 'fmt ' chunk to get the audio info */
237   if (wavparse->state == GST_WAVPARSE_CHUNK_FMT) {
238     GstRiffChunk *fmt;
239     GstWavParseFormat *format;
240
241     GST_DEBUG (0, "GstWavParse: looking for fmt chunk\n");
242
243     /* there's a good possibility we may not have parsed this buffer */
244     if (buffer_riffed == FALSE) {
245       gst_riff_next_buffer (wavparse->riff, buf, GST_BUFFER_OFFSET (buf));
246       buffer_riffed = TRUE;
247     }
248
249     /* see if the fmt chunk is available yet */
250     fmt = gst_riff_get_chunk (wavparse->riff, "fmt ");
251
252     /* if we've got something, deal with it */
253     if (fmt != NULL) {
254       GstCaps *caps;
255
256
257       /* we can gather format information now */
258       format = (GstWavParseFormat *)((guchar *) GST_BUFFER_DATA (buf) + fmt->offset);
259
260       /* set the caps on the src pad */
261       caps = GST_CAPS_NEW (
262                         "parsewav_src",
263                         "audio/raw",
264                         "format",       GST_PROPS_STRING ("int"),
265                           "law",        GST_PROPS_INT (0),              /*FIXME */
266                           "endianness", GST_PROPS_INT (G_BYTE_ORDER),
267                           "signed",     GST_PROPS_BOOLEAN (TRUE), /*FIXME */
268                           "width",      GST_PROPS_INT (format->wBitsPerSample),
269                           "depth",      GST_PROPS_INT (format->wBitsPerSample),
270                           "rate",       GST_PROPS_INT (format->dwSamplesPerSec),
271                           "channels",   GST_PROPS_INT (format->wChannels)
272                       );
273
274       gst_pad_try_set_caps (wavparse->srcpad, caps);
275
276       wavparse->bps = format->wBlockAlign;
277       GST_DEBUG (0, "frequency %d, channels %d\n",
278                  format->dwSamplesPerSec, format->wChannels); 
279
280       /* we're now looking for the data chunk */
281       wavparse->state = GST_WAVPARSE_CHUNK_DATA;
282     } else {
283       /* otherwise we just sort of give up for this buffer */
284       gst_buffer_unref (buf);
285       return;
286     }
287   }
288
289   /* now we look for the data chunk */
290   if (wavparse->state == GST_WAVPARSE_CHUNK_DATA) {
291     GstBuffer *newbuf;
292     GstRiffChunk *datachunk;
293
294     GST_DEBUG (0, "GstWavParse: looking for data chunk\n");
295
296     /* again, we might need to parse the buffer */
297     if (buffer_riffed == FALSE) {
298       gst_riff_next_buffer (wavparse->riff, buf, GST_BUFFER_OFFSET (buf));
299       buffer_riffed = TRUE;
300     }
301
302     datachunk = gst_riff_get_chunk (wavparse->riff, "data");
303
304     if (datachunk != NULL) {
305       gulong subsize;
306
307       GST_DEBUG (0, "data begins at %ld\n", datachunk->offset);
308
309       /* at this point we can ACK that we have data */
310       wavparse->state = GST_WAVPARSE_DATA;
311
312       /* now we construct a new buffer for the remainder */
313       subsize = size - datachunk->offset;
314       GST_DEBUG (0, "sending last %ld bytes along as audio\n", subsize);
315       
316       newbuf = gst_buffer_new ();
317       GST_BUFFER_DATA (newbuf) = g_malloc (subsize);
318       GST_BUFFER_SIZE (newbuf) = subsize;
319       
320       memcpy (GST_BUFFER_DATA (newbuf), GST_BUFFER_DATA (buf) + datachunk->offset, subsize);
321
322       gst_buffer_unref (buf);
323
324       gst_pad_push (wavparse->srcpad, newbuf);
325
326       /* now we're ready to go, the next buffer should start data */
327       wavparse->state = GST_WAVPARSE_DATA;
328
329       /* however, we may be expecting another chunk at some point */
330       wavparse->riff_nextlikely = gst_riff_get_nextlikely (wavparse->riff);
331     } else {
332       /* otherwise we just sort of give up for this buffer */
333       gst_buffer_unref (buf);
334       return;
335     }
336   }
337 }
338
339
340 static gboolean
341 plugin_init (GModule *module, GstPlugin *plugin)
342 {
343   GstElementFactory *factory;
344   GstTypeFactory *type;
345
346   /* create an elementfactory for the wavparse element */
347   factory = gst_elementfactory_new ("wavparse", GST_TYPE_WAVPARSE,
348                                     &gst_wavparse_details);
349   g_return_val_if_fail(factory != NULL, FALSE);
350
351   /* register src pads */
352   gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (sink_template_factory));
353   gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (src_template_factory));
354
355   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
356
357   type = gst_typefactory_new (&wavdefinition);
358   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (type));
359
360   return TRUE;
361 }
362
363 GstPluginDesc plugin_desc = {
364   GST_VERSION_MAJOR,
365   GST_VERSION_MINOR,
366   "wavparse",
367   plugin_init
368 };