afh: Constify definition of audio format handlers.
[paraslash.git] / spx_afh.c
1 /* Copyright (C) 2010 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
2
3 /* This file is based on speexdec.c, by Jean-Marc Valin, see below. */
4
5 /* Copyright (C) 2002-2006 Jean-Marc Valin
6 File: speexdec.c
7
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
10 are met:
11
12 - Redistributions of source code must retain the above copyright
13 notice, this list of conditions and the following disclaimer.
14
15 - Redistributions in binary form must reproduce the above copyright
16 notice, this list of conditions and the following disclaimer in the
17 documentation and/or other materials provided with the distribution.
18
19 - Neither the name of the Xiph.org Foundation nor the names of its
20 contributors may be used to endorse or promote products derived from
21 this software without specific prior written permission.
22
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
27 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35 /** \file spx_afh.c Audio format handler for ogg/speex files. */
36
37 #include <ogg/ogg.h>
38 #include <regex.h>
39 #include <speex/speex.h>
40 #include <speex/speex_header.h>
41 #include <speex/speex_stereo.h>
42
43 #include "para.h"
44 #include "afh.h"
45 #include "error.h"
46 #include "portable_io.h"
47 #include "string.h"
48 #include "spx.h"
49 #include "ogg_afh_common.h"
50
51 struct private_spx_data {
52 struct spx_header_info shi;
53 };
54
55 static bool copy_if_tag_type(const char *tag, int taglen, const char *type,
56 char **p)
57 {
58 char *q = key_value_copy(tag, taglen, type);
59 if (!q)
60 return false;
61 free(*p);
62 *p = q;
63 return true;
64 }
65
66 static int spx_get_comments(unsigned char *comments, int length,
67 struct taginfo *tags)
68 {
69 char *c = (char *)comments;
70 uint32_t len, nb_fields;
71 int i;
72 char *end;
73
74 if (length < 8)
75 return -E_SPX_COMMENT;
76 end = c + length;
77 len = read_u32(c);
78 c += 4;
79 if (c + len > end)
80 return -E_SPX_COMMENT;
81 tags->comment = safe_strdup(c, len);
82
83 c += len;
84 if (c + 4 > end)
85 return -E_SPX_COMMENT;
86 nb_fields = read_u32(c);
87 PARA_DEBUG_LOG("%u comment(s)\n", nb_fields);
88 c += 4;
89 for (i = 0; i < nb_fields; i++, c += len) {
90 char *tag;
91
92 if (c + 4 > end)
93 return -E_SPX_COMMENT;
94 len = read_u32(c);
95 c += 4;
96 if (c + len > end)
97 return -E_SPX_COMMENT;
98 if (copy_if_tag_type(c, len, "author", &tags->artist))
99 continue;
100 if (copy_if_tag_type(c, len, "artist", &tags->artist))
101 continue;
102 if (copy_if_tag_type(c, len, "title", &tags->title))
103 continue;
104 if (copy_if_tag_type(c, len, "album", &tags->album))
105 continue;
106 if (copy_if_tag_type(c, len, "year", &tags->year))
107 continue;
108 if (copy_if_tag_type(c, len, "comment", &tags->comment))
109 continue;
110 tag = safe_strdup(c, len);
111 PARA_NOTICE_LOG("unrecognized comment: %s\n", tag);
112 free(tag);
113 }
114 return 1;
115 }
116
117 static int spx_packet_callback(ogg_packet *packet, int packet_num,
118 __a_unused int serial, struct afh_info *afhi,
119 void *private_data)
120 {
121 struct private_spx_data *psd = private_data;
122 int ret;
123
124 if (packet_num == 0) {
125 ret = spx_process_header(packet->packet, packet->bytes,
126 &psd->shi);
127 if (ret < 0)
128 return ret;
129 afhi->channels = psd->shi.channels;
130 afhi->frequency = psd->shi.sample_rate;
131 afhi->bitrate = psd->shi.bitrate / 1000;
132 afhi->techinfo = make_message("%s, v%d", psd->shi.mode->modeName,
133 psd->shi.mode->bitstream_version);
134 return 1;
135 }
136 if (packet_num == 1) {
137 ret = spx_get_comments(packet->packet, packet->bytes,
138 &afhi->tags);
139 if (ret < 0)
140 return ret;
141 return 0; /* header complete */
142 }
143 /* never reached */
144 return 0;
145 }
146
147 static int spx_get_file_info(char *map, size_t numbytes, __a_unused int fd,
148 struct afh_info *afhi)
149 {
150 struct private_spx_data psd;
151 struct oac_callback_info spx_callback_info = {
152 .packet_callback = spx_packet_callback,
153 .private_data = &psd,
154 };
155
156 memset(&psd, 0, sizeof(psd));
157 return oac_get_file_info(map, numbytes, afhi, &spx_callback_info);
158 }
159
160 static size_t spx_make_meta_packet(struct taginfo *tags, char **result)
161 {
162 size_t sz;
163 char *buf, *p;
164 size_t comment_len = strlen(tags->comment),
165 artist_len = strlen(tags->artist),
166 title_len = strlen(tags->title),
167 album_len = strlen(tags->album),
168 year_len = strlen(tags->year);
169 uint32_t comment_sz = comment_len,
170 artist_sz = artist_len + strlen("artist="),
171 title_sz = title_len + strlen("title="),
172 album_sz = album_len + strlen("album="),
173 year_sz = year_len + strlen("year=");
174 uint32_t num_tags;
175
176 sz = 4 /* comment length (always present) */
177 + comment_sz
178 + 4; /* number of tags */
179 num_tags = 0;
180 if (artist_len) {
181 num_tags++;
182 sz += 4 + artist_sz;
183 }
184 if (title_len) {
185 num_tags++;
186 sz += 4 + title_sz;
187 }
188 if (album_len) {
189 num_tags++;
190 sz += 4 + album_sz;
191 }
192 if (year_len) {
193 num_tags++;
194 sz += 4 + year_sz;
195 }
196 PARA_DEBUG_LOG("meta packet size: %zu bytes\n", sz);
197 /* terminating zero byte for the last sprintf() */
198 buf = p = para_malloc(sz + 1);
199 write_u32(p, comment_sz);
200 p += 4;
201 strcpy(p, tags->comment);
202 p += comment_sz;
203 write_u32(p, num_tags);
204 p += 4;
205 if (artist_len) {
206 write_u32(p, artist_sz);
207 p += 4;
208 sprintf(p, "artist=%s", tags->artist);
209 p += artist_sz;
210 }
211 if (title_len) {
212 write_u32(p, title_sz);
213 p += 4;
214 sprintf(p, "title=%s", tags->title);
215 p += title_sz;
216 }
217 if (album_len) {
218 write_u32(p, album_sz);
219 p += 4;
220 sprintf(p, "album=%s", tags->album);
221 p += album_sz;
222 }
223 if (year_len) {
224 write_u32(p, year_sz);
225 p += 4;
226 sprintf(p, "year=%s", tags->year);
227 p += year_sz;
228 }
229 assert(p == buf + sz);
230 *result = buf;
231 return sz;
232 }
233
234 static int spx_rewrite_tags(const char *map, size_t mapsize,
235 struct taginfo *tags, int output_fd,
236 __a_unused const char *filename)
237 {
238 char *meta_packet;
239 size_t meta_sz;
240 int ret;
241
242 meta_sz = spx_make_meta_packet(tags, &meta_packet);
243 ret = oac_rewrite_tags(map, mapsize, output_fd, meta_packet, meta_sz);
244 free(meta_packet);
245 return ret;
246 }
247
248 static const char * const speex_suffixes[] = {"spx", "speex", NULL};
249
250 /**
251 * The ogg/speex audio format handler.
252 *
253 * This codec is considered obsolete because the opus codec surpasses its
254 * performance in all areas. It is only compiled in if both the ogg and the
255 * speex library are installed.
256 */
257 const struct audio_format_handler spx_afh = {
258 .get_file_info = spx_get_file_info,
259 .suffixes = speex_suffixes,
260 .rewrite_tags = spx_rewrite_tags,
261 };