Merge branch 'master' into 0.11
[platform/upstream/gst-plugins-good.git] / ext / wavpack / gstwavpackcommon.c
1 /* GStreamer Wavpack plugin
2  * Copyright (c) 2005 Arwed v. Merkatz <v.merkatz@gmx.net>
3  * Copyright (c) 1998 - 2005 Conifer Software
4  * Copyright (c) 2006 Sebastian Dröge <slomo@circular-chaos.org>
5  *
6  * gstwavpackcommon.c: common helper functions
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 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "gstwavpackcommon.h"
29 #include <string.h>
30
31 #include <gst/gst.h>
32
33 GST_DEBUG_CATEGORY_EXTERN (wavpack_debug);
34 #define GST_CAT_DEFAULT wavpack_debug
35
36 gboolean
37 gst_wavpack_read_header (WavpackHeader * header, guint8 * buf)
38 {
39   g_memmove (header, buf, sizeof (WavpackHeader));
40
41 #ifndef WAVPACK_OLD_API
42   WavpackLittleEndianToNative (header, (char *) WavpackHeaderFormat);
43 #else
44   little_endian_to_native (header, WavpackHeaderFormat);
45 #endif
46
47   return (memcmp (header->ckID, "wvpk", 4) == 0);
48 }
49
50 /* inspired by the original one in wavpack */
51 gboolean
52 gst_wavpack_read_metadata (GstWavpackMetadata * wpmd, guint8 * header_data,
53     guint8 ** p_data)
54 {
55   WavpackHeader hdr;
56   guint8 *end;
57
58   gst_wavpack_read_header (&hdr, header_data);
59   end = header_data + hdr.ckSize + 8;
60
61   if (end - *p_data < 2)
62     return FALSE;
63
64   wpmd->id = GST_READ_UINT8 (*p_data);
65   wpmd->byte_length = 2 * (guint) GST_READ_UINT8 (*p_data + 1);
66
67   *p_data += 2;
68
69   if ((wpmd->id & ID_LARGE) == ID_LARGE) {
70     guint extra;
71
72     wpmd->id &= ~ID_LARGE;
73
74     if (end - *p_data < 2)
75       return FALSE;
76
77     extra = GST_READ_UINT16_LE (*p_data);
78     wpmd->byte_length += (extra << 9);
79     *p_data += 2;
80   }
81
82   if ((wpmd->id & ID_ODD_SIZE) == ID_ODD_SIZE) {
83     wpmd->id &= ~ID_ODD_SIZE;
84     --wpmd->byte_length;
85   }
86
87   if (wpmd->byte_length > 0) {
88     if (end - *p_data < wpmd->byte_length + (wpmd->byte_length & 1)) {
89       wpmd->data = NULL;
90       return FALSE;
91     }
92
93     wpmd->data = *p_data;
94     *p_data += wpmd->byte_length + (wpmd->byte_length & 1);
95   } else {
96     wpmd->data = NULL;
97   }
98
99   return TRUE;
100 }
101
102 gint
103 gst_wavpack_get_default_channel_mask (gint nchannels)
104 {
105   gint channel_mask = 0;
106
107   /* Set the default channel mask for the given number of channels.
108    * It's the same as for WAVE_FORMAT_EXTENDED:
109    * http://www.microsoft.com/whdc/device/audio/multichaud.mspx
110    */
111   switch (nchannels) {
112     case 11:
113       channel_mask |= 0x00400;
114       channel_mask |= 0x00200;
115     case 9:
116       channel_mask |= 0x00100;
117     case 8:
118       channel_mask |= 0x00080;
119       channel_mask |= 0x00040;
120     case 6:
121       channel_mask |= 0x00020;
122       channel_mask |= 0x00010;
123     case 4:
124       channel_mask |= 0x00008;
125     case 3:
126       channel_mask |= 0x00004;
127     case 2:
128       channel_mask |= 0x00002;
129       channel_mask |= 0x00001;
130       break;
131     case 1:
132       /* For mono use front center */
133       channel_mask |= 0x00004;
134       break;
135   }
136
137   return channel_mask;
138 }
139
140 static const struct
141 {
142   const guint32 ms_mask;
143   const GstAudioChannelPosition gst_pos;
144 } layout_mapping[] = {
145   {
146   0x00001, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT}, {
147   0x00002, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
148   0x00004, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}, {
149   0x00008, GST_AUDIO_CHANNEL_POSITION_LFE1}, {
150   0x00010, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT}, {
151   0x00020, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
152   0x00040, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER}, {
153   0x00080, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, {
154   0x00100, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, {
155   0x00200, GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT}, {
156   0x00400, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}, {
157   0x00800, GST_AUDIO_CHANNEL_POSITION_TOP_CENTER}, {
158   0x01000, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT}, {
159   0x02000, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER}, {
160   0x04000, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT}, {
161   0x08000, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT}, {
162   0x10000, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER}, {
163   0x20000, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT}
164 };
165
166 #define MAX_CHANNEL_POSITIONS G_N_ELEMENTS (layout_mapping)
167
168 gboolean
169 gst_wavpack_get_channel_positions (gint num_channels, gint layout,
170     GstAudioChannelPosition * pos)
171 {
172   gint i, p;
173
174   if (num_channels == 1 && layout == 0x00004) {
175     pos[0] = GST_AUDIO_CHANNEL_POSITION_MONO;
176     return TRUE;
177   }
178
179   p = 0;
180   for (i = 0; i < MAX_CHANNEL_POSITIONS; ++i) {
181     if ((layout & layout_mapping[i].ms_mask) != 0) {
182       if (p >= num_channels) {
183         GST_WARNING ("More bits set in the channel layout map than there "
184             "are channels! Broken file");
185         return FALSE;
186       }
187       if (layout_mapping[i].gst_pos == GST_AUDIO_CHANNEL_POSITION_INVALID) {
188         GST_WARNING ("Unsupported channel position (mask 0x%08x) in channel "
189             "layout map - ignoring those channels", layout_mapping[i].ms_mask);
190         /* what to do? just ignore it and let downstream deal with a channel
191          * layout that has INVALID positions in it for now ... */
192       }
193       pos[p] = layout_mapping[i].gst_pos;
194       ++p;
195     }
196   }
197
198   if (p != num_channels) {
199     GST_WARNING ("Only %d bits set in the channel layout map, but there are "
200         "supposed to be %d channels! Broken file", p, num_channels);
201     return FALSE;
202   }
203
204   return TRUE;
205 }
206
207 GstAudioChannelPosition *
208 gst_wavpack_get_default_channel_positions (gint nchannels)
209 {
210   GstAudioChannelPosition *pos = g_new (GstAudioChannelPosition, nchannels);
211   gint i;
212
213   if (nchannels == 1) {
214     pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
215     return pos;
216   }
217
218   for (i = 0; i < nchannels; i++)
219     pos[i] = layout_mapping[i].gst_pos;
220
221   return pos;
222 }
223
224 gint
225 gst_wavpack_get_channel_mask_from_positions (GstAudioChannelPosition * pos,
226     gint nchannels)
227 {
228   gint channel_mask = 0;
229   gint i, j;
230
231   if (nchannels == 1 && pos[0] == GST_AUDIO_CHANNEL_POSITION_MONO) {
232     channel_mask = 0x00000004;
233     return channel_mask;
234   }
235
236   /* FIXME: not exactly efficient but otherwise we need an inverse
237    * mapping table too */
238   for (i = 0; i < nchannels; i++) {
239     for (j = 0; j < MAX_CHANNEL_POSITIONS; j++) {
240       if (pos[i] == layout_mapping[j].gst_pos) {
241         channel_mask |= layout_mapping[j].ms_mask;
242         break;
243       }
244     }
245   }
246
247   return channel_mask;
248 }
249
250 gboolean
251 gst_wavpack_set_channel_mapping (GstAudioChannelPosition * pos, gint nchannels,
252     gint8 * channel_mapping)
253 {
254   gint i, j;
255   gboolean ret = TRUE;
256
257   for (i = 0; i < nchannels; i++) {
258     for (j = 0; j < MAX_CHANNEL_POSITIONS; j++) {
259       if (pos[i] == layout_mapping[j].gst_pos) {
260         channel_mapping[i] = j;
261         ret &= (i == j);
262         break;
263       }
264     }
265   }
266
267   return !ret;
268 }