wav is little endian
[platform/upstream/gst-plugins-good.git] / gst / wavenc / gstwavenc.c
1 /* GStreamer
2  * Copyright (C) <2002> Iain Holmes <iain@prettypeople.org>
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 #include <gstwavenc.h>
23
24 static void     gst_wavenc_class_init   (GstWavEncClass *klass);
25 static void     gst_wavenc_init         (GstWavEnc *wavenc);
26 static void     gst_wavenc_chain        (GstPad *pad, GstBuffer *buf);
27
28 #define WAVE_FORMAT_PCM 0x0001
29
30 #define WRITE_U32(buf, x) *(buf) = (unsigned char) (x&0xff);\
31 *((buf)+1) = (unsigned char)((x>>8)&0xff);\
32 *((buf)+2) = (unsigned char)((x>>16)&0xff);\
33 *((buf)+3) = (unsigned char)((x>>24)&0xff);
34
35 #define WRITE_U16(buf, x) *(buf) = (unsigned char) (x&0xff);\
36 *((buf)+1) = (unsigned char)((x>>8)&0xff);
37
38 struct riff_struct {
39   guint8        id[4];          /* RIFF */
40   guint32       len;
41   guint8        wav_id[4];      /* WAVE */
42 };
43
44 struct chunk_struct {
45   guint8        id[4];
46   guint32       len;
47 };
48
49 struct common_struct {
50   guint16       wFormatTag;
51   guint16       wChannels;
52   guint32       dwSamplesPerSec;
53   guint32       dwAvgBytesPerSec;
54   guint16       wBlockAlign;
55   guint16       wBitsPerSample;         /* Only for PCM */
56 };
57
58 struct wave_header {
59   struct riff_struct    riff;
60   struct chunk_struct   format;
61   struct common_struct  common;
62   struct chunk_struct   data;
63 };
64
65 static GstElementDetails gst_wavenc_details = {
66   "WAV encoder",
67   "Codec/Encoder",
68   "LGPL",
69   "Encode raw audio into WAV",
70   VERSION,
71   "Iain Holmes <iain@prettypeople.org>",
72   "(C) 2002",
73 };
74
75 static GstPadTemplate *srctemplate, *sinktemplate;
76
77 GST_PAD_TEMPLATE_FACTORY (sink_factory,
78   "sink",
79   GST_PAD_SINK,
80   GST_PAD_ALWAYS,
81   GST_CAPS_NEW (
82     "wavenc_raw",
83     "audio/raw",
84       "format",   GST_PROPS_STRING ("int"),
85       "law",         GST_PROPS_INT (0),
86       "endianness",  GST_PROPS_INT (G_LITTLE_ENDIAN),
87       "signed",      GST_PROPS_BOOLEAN (TRUE),
88       "width",       GST_PROPS_INT (16),
89       "depth",       GST_PROPS_INT (16),
90       "rate",        GST_PROPS_INT_RANGE (8000, 48000),
91       "channels",    GST_PROPS_INT_RANGE (1, 2)
92   )
93 )
94
95 GST_PAD_TEMPLATE_FACTORY (src_factory,
96   "src",
97   GST_PAD_SRC,
98   GST_PAD_ALWAYS,
99   GST_CAPS_NEW (
100     "wavenc_wav",
101     "audio/x-wav",
102     NULL
103   )
104 )
105
106 static GstElementClass *parent_class = NULL;
107
108 static GType
109 gst_wavenc_get_type (void)
110 {
111   static GType type = 0;
112
113   if (type == 0) {
114     static const GTypeInfo info = {
115       sizeof (GstWavEncClass), 
116       NULL, 
117       NULL,
118       (GClassInitFunc) gst_wavenc_class_init, 
119       NULL, 
120       NULL,
121       sizeof (GstWavEnc), 
122       0, 
123       (GInstanceInitFunc) gst_wavenc_init
124     };
125
126     type = g_type_register_static (GST_TYPE_ELEMENT, "GstWavEnc", &info, 0);
127   }
128
129   return type;
130 }
131
132 static GstElementStateReturn
133 gst_wavenc_change_state (GstElement *element)
134 {
135   GstWavEnc *wavenc = GST_WAVENC (element);
136   
137   switch (GST_STATE_TRANSITION (element)) {
138     case GST_STATE_PAUSED_TO_READY:
139     case GST_STATE_READY_TO_PAUSED:
140       wavenc->setup = FALSE;
141       wavenc->flush_header = TRUE;
142       break;
143     default:
144       break;
145   }
146
147   if (parent_class->change_state) {
148     return parent_class->change_state (element);
149   }
150
151   return GST_STATE_SUCCESS;
152 }
153
154 static void
155 gst_wavenc_class_init (GstWavEncClass *klass)
156 {
157   GstElementClass *element_class;
158
159   element_class = (GstElementClass *) klass;
160   element_class->change_state = gst_wavenc_change_state;
161
162   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
163 }
164
165 static gboolean
166 gst_wavenc_setup (GstWavEnc *wavenc)
167 {
168   struct wave_header wave;
169   gint size = 0x7fffffff; /* Use a bogus size initially */
170
171   wave.common.wChannels = wavenc->channels;
172   wave.common.wBitsPerSample = wavenc->bits;
173   wave.common.dwSamplesPerSec = wavenc->rate;
174
175   memset (wavenc->header, 0, WAV_HEADER_LEN);
176
177   /* Fill out our wav-header with some information */
178   strncpy (wave.riff.id, "RIFF", 4);
179   wave.riff.len = size - 8;
180   strncpy (wave.riff.wav_id, "WAVE", 4);
181
182   strncpy (wave.format.id, "fmt ", 4);
183   wave.format.len = 16;
184
185   wave.common.wFormatTag = WAVE_FORMAT_PCM;
186   wave.common.dwAvgBytesPerSec = wave.common.wChannels * wave.common.dwSamplesPerSec * (wave.common.wBitsPerSample >> 3);
187   wave.common.wBlockAlign = wave.common.wChannels * (wave.common.wBitsPerSample >> 3);
188
189   strncpy (wave.data.id, "data", 4);
190   wave.data.len = size - 44;
191
192   strncpy (wavenc->header, wave.riff.id, 4);
193   WRITE_U32 (wavenc->header + 4, wave.riff.len);
194   strncpy (wavenc->header + 8, wave.riff.wav_id, 4);
195   strncpy (wavenc->header + 12, wave.format.id, 4);
196   WRITE_U32 (wavenc->header + 16, wave.format.len);
197   WRITE_U16 (wavenc->header + 20, wave.common.wFormatTag);
198   WRITE_U16 (wavenc->header + 22, wave.common.wChannels);
199   WRITE_U32 (wavenc->header + 24, wave.common.dwSamplesPerSec);
200   WRITE_U32 (wavenc->header + 28, wave.common.dwAvgBytesPerSec);
201   WRITE_U16 (wavenc->header + 32, wave.common.wBlockAlign);
202   WRITE_U16 (wavenc->header + 34, wave.common.wBitsPerSample);
203   strncpy (wavenc->header + 36, wave.data.id, 4);
204   WRITE_U32 (wavenc->header + 40, wave.data.len);
205
206   wavenc->setup = TRUE;
207   return TRUE;
208 }
209
210 static GstPadLinkReturn
211 gst_wavenc_sinkconnect (GstPad *pad,
212                         GstCaps *caps)
213 {
214   GstWavEnc *wavenc;
215
216   wavenc = GST_WAVENC (gst_pad_get_parent (pad));
217
218   if (!GST_CAPS_IS_FIXED (caps)) {
219     return GST_PAD_LINK_DELAYED;
220   }
221
222   gst_caps_get_int (caps, "channels", &wavenc->channels);
223   gst_caps_get_int (caps, "rate", &wavenc->rate);
224   gst_caps_get_int (caps, "depth", &wavenc->bits);
225
226   gst_wavenc_setup (wavenc);
227
228   if (wavenc->setup) {
229     return GST_PAD_LINK_OK;
230   }
231
232   return GST_PAD_LINK_REFUSED;
233 }
234
235 static void
236 gst_wavenc_init (GstWavEnc *wavenc)
237 {
238   wavenc->sinkpad = gst_pad_new_from_template (sinktemplate, "sink");
239   gst_element_add_pad (GST_ELEMENT (wavenc), wavenc->sinkpad);
240   gst_pad_set_chain_function (wavenc->sinkpad, gst_wavenc_chain);
241   gst_pad_set_link_function (wavenc->sinkpad, gst_wavenc_sinkconnect);
242
243   wavenc->srcpad = gst_pad_new_from_template (srctemplate, "src");
244   gst_element_add_pad (GST_ELEMENT (wavenc), wavenc->srcpad);
245
246   wavenc->setup = FALSE;
247   wavenc->flush_header = TRUE;
248 }
249
250 static void
251 gst_wavenc_chain (GstPad *pad,
252                   GstBuffer *buf)
253 {
254   GstWavEnc *wavenc;
255
256   wavenc = GST_WAVENC (gst_pad_get_parent (pad));
257
258   if (GST_IS_EVENT (buf)) {
259     GstEvent *event = GST_EVENT (buf);
260
261     switch (GST_EVENT_TYPE (event)) {
262       case GST_EVENT_EOS:
263         /* Should do something... */
264         gst_event_unref (event);
265
266         if (GST_PAD_IS_USABLE (wavenc->srcpad))
267           gst_pad_push (wavenc->srcpad, GST_BUFFER (gst_event_new (GST_EVENT_EOS)));
268         gst_element_set_eos (GST_ELEMENT (wavenc));
269         break;
270       default:
271         gst_pad_event_default (pad, event);
272         return;
273     }
274   } 
275   else {
276     if (!wavenc->setup) {
277       gst_buffer_unref (buf);
278       gst_element_error (GST_ELEMENT (wavenc), "encoder not initialised (input is not audio?)");
279       return;
280     }
281
282     if (GST_PAD_IS_USABLE (wavenc->srcpad)) {
283       if (wavenc->flush_header) {
284         GstBuffer *outbuf;
285
286         outbuf = gst_buffer_new_and_alloc (WAV_HEADER_LEN);
287         memcpy (GST_BUFFER_DATA (outbuf), wavenc->header, WAV_HEADER_LEN);
288         GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
289
290         gst_pad_push (wavenc->srcpad, outbuf);
291         wavenc->flush_header = FALSE;
292       }
293     
294       gst_pad_push (wavenc->srcpad, buf);
295     }
296   }
297 }
298
299 static gboolean
300 plugin_init (GModule *module,
301              GstPlugin *plugin)
302 {
303   GstElementFactory *factory;
304
305   factory = gst_element_factory_new ("wavenc", GST_TYPE_WAVENC,
306                                      &gst_wavenc_details);
307   
308   srctemplate = src_factory ();
309   gst_element_factory_add_pad_template (factory, srctemplate);
310
311   sinktemplate = sink_factory ();
312   gst_element_factory_add_pad_template (factory, sinktemplate);
313
314   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
315
316   return TRUE;
317 }
318
319 GstPluginDesc plugin_desc = {
320   GST_VERSION_MAJOR,
321   GST_VERSION_MINOR,
322   "wavenc",
323   plugin_init
324 };
325