Add support for the speex codec.
[paraslash.git] / spx_afh.c
diff --git a/spx_afh.c b/spx_afh.c
new file mode 100644 (file)
index 0000000..472c2c8
--- /dev/null
+++ b/spx_afh.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2010 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/* This file is based on speexdec.c, by Jean-Marc Valin, see below. */
+
+/* Copyright (C) 2002-2006 Jean-Marc Valin
+   File: speexdec.c
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+   
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   
+   - Neither the name of the Xiph.org Foundation nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+   
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/** \file spx_afh.c Audio format handler for ogg/speex files. */
+
+#include <stdbool.h>
+#include <ogg/ogg.h>
+#include <regex.h>
+#include <speex/speex.h>
+#include <speex/speex_header.h>
+#include <speex/speex_stereo.h>
+
+#include "para.h"
+#include "afh.h"
+#include "error.h"
+#include "portable_io.h"
+#include "string.h"
+#include "spx.h"
+#include "ogg_afh_common.h"
+
+struct private_spx_data {
+       struct spx_header_info shi;
+};
+
+
+static char *copy_comment(const char *src, int len)
+{
+       char *p = para_malloc(len + 1);
+
+       if (len > 0)
+               memcpy(p, src, len);
+       p[len] = '\0';
+       PARA_DEBUG_LOG("%s\n", p);
+       return p;
+}
+
+static bool copy_if_tag_type(const char *tag, int taglen, const char *type,
+               char **p)
+{
+       int len = strlen(type);
+
+       if (taglen <= len)
+               return false;
+       if (strncasecmp(tag, type, len))
+               return false;
+       if (tag[len] != '=')
+               return false;
+       free(*p);
+       *p = copy_comment(tag + len + 1, taglen - len - 1);
+       return true;
+}
+
+static int spx_get_comments(unsigned char *comments, int length,
+               struct taginfo *tags)
+{
+       char *c = (char *)comments;
+       uint32_t len, nb_fields;
+       int i;
+       char *end;
+
+       if (length < 8)
+               return -E_SPX_COMMENT;
+       end = c + length;
+       len = read_u32(c);
+       c += 4;
+       if (c + len > end)
+               return -E_SPX_COMMENT;
+       tags->comment = copy_comment(c, len);
+
+       c += len;
+       if (c + 4 > end)
+               return -E_SPX_COMMENT;
+       nb_fields = read_u32(c);
+       PARA_DEBUG_LOG("%d comment(s)\n", nb_fields);
+       c += 4;
+       for (i = 0; i < nb_fields; i++, c += len) {
+               char *tag;
+
+               if (c + 4 > end)
+                       return -E_SPX_COMMENT;
+               len = read_u32(c);
+               c += 4;
+               if (c + len > end)
+                       return -E_SPX_COMMENT;
+               if (copy_if_tag_type(c, len, "author", &tags->artist))
+                       continue;
+               if (copy_if_tag_type(c, len, "artist", &tags->artist))
+                       continue;
+               if (copy_if_tag_type(c, len, "title", &tags->title))
+                       continue;
+               if (copy_if_tag_type(c, len, "album", &tags->album))
+                       continue;
+               if (copy_if_tag_type(c, len, "year", &tags->year))
+                       continue;
+               if (copy_if_tag_type(c, len, "comment", &tags->comment))
+                       continue;
+               tag = copy_comment(c, len);
+               PARA_NOTICE_LOG("unrecognized comment: %s\n", tag);
+               free(tag);
+       }
+       return 1;
+}
+
+static const char* speex_suffixes[] = {"spx", "speex", NULL};
+
+static int spx_packet_callback(ogg_packet *packet, int packet_num,
+               struct afh_info *afhi, void *private_data)
+{
+       struct private_spx_data *psd = private_data;
+       int ret;
+
+       if (packet_num == 0) {
+               ret = spx_process_header(packet->packet, packet->bytes,
+                       &psd->shi);
+               if (ret < 0)
+                       return ret;
+               afhi->channels = psd->shi.channels;
+               afhi->frequency = psd->shi.sample_rate;
+               afhi->bitrate = psd->shi.bitrate / 1000;
+               afhi->techinfo = make_message("%s, v%d", psd->shi.mode->modeName,
+                       psd->shi.mode->bitstream_version);
+               return 1;
+       }
+       if (packet_num == 1) {
+               ret = spx_get_comments(packet->packet, packet->bytes,
+                       &afhi->tags);
+               if (ret < 0)
+                       return ret;
+               return 0; /* header complete */
+       }
+       /* never reached */
+       return 0;
+}
+
+static int spx_get_file_info(char *map, size_t numbytes, __a_unused int fd,
+               struct afh_info *afhi)
+{
+       struct private_spx_data psd;
+       struct ogg_afh_callback_info spx_callback_info = {
+               .packet_callback = spx_packet_callback,
+               .private_data = &psd,
+       };
+
+       memset(&psd, 0, sizeof(psd));
+       return ogg_get_file_info(map, numbytes, afhi, &spx_callback_info);
+}
+
+/**
+ * The init function of the ogg/speex audio format handler.
+ *
+ * \param afh Pointer to the struct to initialize.
+ */
+void spx_afh_init(struct audio_format_handler *afh)
+{
+       afh->get_file_info = spx_get_file_info,
+       afh->suffixes = speex_suffixes;
+}