Initial support for FLAC (the free lossless audio codec).
[paraslash.git] / spx_afh.c
1 /*
2 * Copyright (C) 2010-2011 Andre Noll <maan@systemlinux.org>
3 *
4 * Licensed under the GPL v2. For licencing details see COPYING.
5 */
6
7 /* This file is based on speexdec.c, by Jean-Marc Valin, see below. */
8
9 /* Copyright (C) 2002-2006 Jean-Marc Valin
10 File: speexdec.c
11
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions
14 are met:
15
16 - Redistributions of source code must retain the above copyright
17 notice, this list of conditions and the following disclaimer.
18
19 - Redistributions in binary form must reproduce the above copyright
20 notice, this list of conditions and the following disclaimer in the
21 documentation and/or other materials provided with the distribution.
22
23 - Neither the name of the Xiph.org Foundation nor the names of its
24 contributors may be used to endorse or promote products derived from
25 this software without specific prior written permission.
26
27 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
31 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
32 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
33 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
34 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
35 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
36 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
37 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 */
39 /** \file spx_afh.c Audio format handler for ogg/speex files. */
40
41 #include <stdbool.h>
42 #include <ogg/ogg.h>
43 #include <regex.h>
44 #include <speex/speex.h>
45 #include <speex/speex_header.h>
46 #include <speex/speex_stereo.h>
47
48 #include "para.h"
49 #include "afh.h"
50 #include "error.h"
51 #include "portable_io.h"
52 #include "string.h"
53 #include "spx.h"
54 #include "ogg_afh_common.h"
55
56 struct private_spx_data {
57 struct spx_header_info shi;
58 };
59
60 static bool copy_if_tag_type(const char *tag, int taglen, const char *type,
61 char **p)
62 {
63 char *q = key_value_copy(tag, taglen, type);
64 if (!q)
65 return false;
66 free(*p);
67 *p = q;
68 return true;
69 }
70
71 static int spx_get_comments(unsigned char *comments, int length,
72 struct taginfo *tags)
73 {
74 char *c = (char *)comments;
75 uint32_t len, nb_fields;
76 int i;
77 char *end;
78
79 if (length < 8)
80 return -E_SPX_COMMENT;
81 end = c + length;
82 len = read_u32(c);
83 c += 4;
84 if (c + len > end)
85 return -E_SPX_COMMENT;
86 tags->comment = safe_strdup(c, len);
87
88 c += len;
89 if (c + 4 > end)
90 return -E_SPX_COMMENT;
91 nb_fields = read_u32(c);
92 PARA_DEBUG_LOG("%d comment(s)\n", nb_fields);
93 c += 4;
94 for (i = 0; i < nb_fields; i++, c += len) {
95 char *tag;
96
97 if (c + 4 > end)
98 return -E_SPX_COMMENT;
99 len = read_u32(c);
100 c += 4;
101 if (c + len > end)
102 return -E_SPX_COMMENT;
103 if (copy_if_tag_type(c, len, "author", &tags->artist))
104 continue;
105 if (copy_if_tag_type(c, len, "artist", &tags->artist))
106 continue;
107 if (copy_if_tag_type(c, len, "title", &tags->title))
108 continue;
109 if (copy_if_tag_type(c, len, "album", &tags->album))
110 continue;
111 if (copy_if_tag_type(c, len, "year", &tags->year))
112 continue;
113 if (copy_if_tag_type(c, len, "comment", &tags->comment))
114 continue;
115 tag = safe_strdup(c, len);
116 PARA_NOTICE_LOG("unrecognized comment: %s\n", tag);
117 free(tag);
118 }
119 return 1;
120 }
121
122 static const char* speex_suffixes[] = {"spx", "speex", NULL};
123
124 static int spx_packet_callback(ogg_packet *packet, int packet_num,
125 __a_unused int serial, struct afh_info *afhi,
126 void *private_data)
127 {
128 struct private_spx_data *psd = private_data;
129 int ret;
130
131 if (packet_num == 0) {
132 ret = spx_process_header(packet->packet, packet->bytes,
133 &psd->shi);
134 if (ret < 0)
135 return ret;
136 afhi->channels = psd->shi.channels;
137 afhi->frequency = psd->shi.sample_rate;
138 afhi->bitrate = psd->shi.bitrate / 1000;
139 afhi->techinfo = make_message("%s, v%d", psd->shi.mode->modeName,
140 psd->shi.mode->bitstream_version);
141 return 1;
142 }
143 if (packet_num == 1) {
144 ret = spx_get_comments(packet->packet, packet->bytes,
145 &afhi->tags);
146 if (ret < 0)
147 return ret;
148 return 0; /* header complete */
149 }
150 /* never reached */
151 return 0;
152 }
153
154 static int spx_get_file_info(char *map, size_t numbytes, __a_unused int fd,
155 struct afh_info *afhi)
156 {
157 struct private_spx_data psd;
158 struct ogg_afh_callback_info spx_callback_info = {
159 .packet_callback = spx_packet_callback,
160 .private_data = &psd,
161 };
162
163 memset(&psd, 0, sizeof(psd));
164 return ogg_get_file_info(map, numbytes, afhi, &spx_callback_info);
165 }
166
167 /**
168 * The init function of the ogg/speex audio format handler.
169 *
170 * \param afh Pointer to the struct to initialize.
171 */
172 void spx_afh_init(struct audio_format_handler *afh)
173 {
174 afh->get_file_info = spx_get_file_info,
175 afh->suffixes = speex_suffixes;
176 }