--- /dev/null
+/*
+ * 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;
+}