/* GStreamer Wavpack plugin * Copyright (c) 2005 Arwed v. Merkatz * Copyright (c) 1998 - 2005 Conifer Software * Copyright (c) 2006 Sebastian Dröge * * gstwavpackcommon.c: common helper functions * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "gstwavpackcommon.h" #include #include #include GST_DEBUG_CATEGORY_EXTERN (wavpack_debug); #define GST_CAT_DEFAULT wavpack_debug gboolean gst_wavpack_read_header (WavpackHeader * header, guint8 * buf) { g_memmove (header, buf, sizeof (WavpackHeader)); #ifndef WAVPACK_OLD_API WavpackLittleEndianToNative (header, WavpackHeaderFormat); #else little_endian_to_native (header, WavpackHeaderFormat); #endif return (memcmp (header->ckID, "wvpk", 4) == 0); } /* inspired by the original one in wavpack */ gboolean gst_wavpack_read_metadata (GstWavpackMetadata * wpmd, guint8 * header_data, guint8 ** p_data) { WavpackHeader hdr; guint8 *end; gst_wavpack_read_header (&hdr, header_data); end = header_data + hdr.ckSize + 8; if (end - *p_data < 2) return FALSE; wpmd->id = GST_READ_UINT8 (*p_data); wpmd->byte_length = 2 * (guint) GST_READ_UINT8 (*p_data + 1); *p_data += 2; if ((wpmd->id & ID_LARGE) == ID_LARGE) { guint extra; wpmd->id &= ~ID_LARGE; if (end - *p_data < 2) return FALSE; extra = GST_READ_UINT16_LE (*p_data); wpmd->byte_length += (extra << 9); *p_data += 2; } if ((wpmd->id & ID_ODD_SIZE) == ID_ODD_SIZE) { wpmd->id &= ~ID_ODD_SIZE; --wpmd->byte_length; } if (wpmd->byte_length > 0) { if (end - *p_data < wpmd->byte_length + (wpmd->byte_length & 1)) { wpmd->data = NULL; return FALSE; } wpmd->data = *p_data; *p_data += wpmd->byte_length + (wpmd->byte_length & 1); } else { wpmd->data = NULL; } return TRUE; } gint gst_wavpack_get_default_channel_mask (gint nchannels) { gint channel_mask = 0; /* Set the default channel mask for the given number of channels. * It's the same as for WAVE_FORMAT_EXTENDED: * http://www.microsoft.com/whdc/device/audio/multichaud.mspx */ switch (nchannels) { case 11: channel_mask |= 0x00400; channel_mask |= 0x00200; case 9: channel_mask |= 0x00100; case 8: channel_mask |= 0x00080; channel_mask |= 0x00040; case 6: channel_mask |= 0x00020; channel_mask |= 0x00010; case 4: channel_mask |= 0x00008; case 3: channel_mask |= 0x00004; case 2: channel_mask |= 0x00002; channel_mask |= 0x00001; break; case 1: /* For mono use front center */ channel_mask |= 0x00004; break; } return channel_mask; } static const struct { const guint32 ms_mask; const GstAudioChannelPosition gst_pos; } layout_mapping[] = { { 0x00001, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT}, { 0x00002, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, { 0x00004, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}, { 0x00008, GST_AUDIO_CHANNEL_POSITION_LFE}, { 0x00010, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT}, { 0x00020, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, { 0x00040, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER}, { 0x00080, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, { 0x00100, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, { 0x00200, GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT}, { 0x00400, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}, { 0x00800, GST_AUDIO_CHANNEL_POSITION_INVALID}, /* TOP_CENTER */ { 0x01000, GST_AUDIO_CHANNEL_POSITION_INVALID}, /* TOP_FRONT_LEFT */ { 0x02000, GST_AUDIO_CHANNEL_POSITION_INVALID}, /* TOP_FRONT_CENTER */ { 0x04000, GST_AUDIO_CHANNEL_POSITION_INVALID}, /* TOP_FRONT_RIGHT */ { 0x08000, GST_AUDIO_CHANNEL_POSITION_INVALID}, /* TOP_BACK_LEFT */ { 0x10000, GST_AUDIO_CHANNEL_POSITION_INVALID}, /* TOP_BACK_CENTER */ { 0x20000, GST_AUDIO_CHANNEL_POSITION_INVALID} /* TOP_BACK_RIGHT */ }; #define MAX_CHANNEL_POSITIONS G_N_ELEMENTS (layout_mapping) gboolean gst_wavpack_set_channel_layout (GstCaps * caps, gint layout) { GstAudioChannelPosition pos[MAX_CHANNEL_POSITIONS]; GstStructure *s; gint num_channels, i, p; s = gst_caps_get_structure (caps, 0); if (!gst_structure_get_int (s, "channels", &num_channels)) g_return_val_if_reached (FALSE); if (num_channels == 1 && layout == 0x00004) { pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_MONO; gst_audio_set_channel_positions (s, pos); return TRUE; } p = 0; for (i = 0; i < MAX_CHANNEL_POSITIONS; ++i) { if ((layout & layout_mapping[i].ms_mask) != 0) { if (p >= num_channels) { GST_WARNING ("More bits set in the channel layout map than there " "are channels! Broken file"); return FALSE; } if (layout_mapping[i].gst_pos == GST_AUDIO_CHANNEL_POSITION_INVALID) { GST_WARNING ("Unsupported channel position (mask 0x%08x) in channel " "layout map - ignoring those channels", layout_mapping[i].ms_mask); /* what to do? just ignore it and let downstream deal with a channel * layout that has INVALID positions in it for now ... */ } pos[p] = layout_mapping[i].gst_pos; ++p; } } if (p != num_channels) { GST_WARNING ("Only %d bits set in the channel layout map, but there are " "supposed to be %d channels! Broken file", p, num_channels); return FALSE; } gst_audio_set_channel_positions (s, pos); return TRUE; } GstAudioChannelPosition * gst_wavpack_get_default_channel_positions (gint nchannels) { GstAudioChannelPosition *pos = g_new (GstAudioChannelPosition, nchannels); gint i; if (nchannels == 1) { pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER; return pos; } for (i = 0; i < nchannels; i++) pos[i] = layout_mapping[i].gst_pos; return pos; } gint gst_wavpack_get_channel_mask_from_positions (GstAudioChannelPosition * pos, gint nchannels) { gint channel_mask = 0; gint i, j; if (nchannels == 1 && pos[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_MONO) { channel_mask = 0x00000004; return channel_mask; } /* FIXME: not exactly efficient but otherwise we need an inverse * mapping table too */ for (i = 0; i < nchannels; i++) { for (j = 0; j < MAX_CHANNEL_POSITIONS; j++) { if (pos[i] == layout_mapping[j].gst_pos) { channel_mask |= layout_mapping[j].ms_mask; break; } } } return channel_mask; } gboolean gst_wavpack_set_channel_mapping (GstAudioChannelPosition * pos, gint nchannels, gint8 * channel_mapping) { gint i, j; gboolean ret = TRUE; for (i = 0; i < nchannels; i++) { for (j = 0; j < MAX_CHANNEL_POSITIONS; j++) { if (pos[i] == layout_mapping[j].gst_pos) { channel_mapping[i] = j; ret &= (i == j); break; } } } return !ret; }