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