add :refresh command
[cmus.git] / vorbis.c
blobd716d257632626fdbb615f9de98c14e84306d690
1 /*
2 * Copyright 2004-2005 Timo Hirvonen
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
20 #include <config.h>
21 #include <ip.h>
22 #include <xmalloc.h>
23 #include <read_wrapper.h>
24 #include <debug.h>
26 #ifdef CONFIG_TREMOR
27 #include <tremor/ivorbisfile.h>
28 #else
29 #include <vorbis/vorbisfile.h>
30 #endif
32 #include <errno.h>
33 #include <string.h>
35 #include <sys/types.h>
36 #include <unistd.h>
38 struct vorbis_private {
39 OggVorbis_File vf;
40 int current_section;
43 /* http://www.xiph.org/vorbis/doc/vorbisfile/callbacks.html */
45 static size_t read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
47 struct input_plugin_data *ip_data = datasource;
48 int rc;
50 rc = read_wrapper(ip_data, ptr, size * nmemb);
51 if (rc == -1) {
52 d_print("error: %s\n", strerror(errno));
53 return 0;
55 if (rc == 0) {
56 errno = 0;
57 return 0;
59 return rc / size;
62 static int seek_func(void *datasource, ogg_int64_t offset, int whence)
64 struct input_plugin_data *ip_data = datasource;
65 int rc;
67 rc = lseek(ip_data->fd, offset, whence);
68 if (rc == -1)
69 return -1;
70 return 0;
73 static int close_func(void *datasource)
75 struct input_plugin_data *ip_data = datasource;
76 int rc;
78 rc = close(ip_data->fd);
79 ip_data->fd = -1;
80 return rc;
83 static long tell_func(void *datasource)
85 struct input_plugin_data *ip_data = datasource;
86 int rc;
88 rc = lseek(ip_data->fd, 0, SEEK_CUR);
89 return rc;
93 * typedef struct {
94 * size_t (*read_func) (void *ptr, size_t size, size_t nmemb, void *datasource);
95 * int (*seek_func) (void *datasource, ogg_int64_t offset, int whence);
96 * int (*close_func) (void *datasource);
97 * long (*tell_func) (void *datasource);
98 * } ov_callbacks;
100 static ov_callbacks callbacks = {
101 .read_func = read_func,
102 .seek_func = seek_func,
103 .close_func = close_func,
104 .tell_func = tell_func
107 static int vorbis_open(struct input_plugin_data *ip_data)
109 struct vorbis_private *priv;
110 vorbis_info *vi;
111 int rc;
113 priv = xnew(struct vorbis_private, 1);
114 priv->current_section = 0;
115 memset(&priv->vf, 0, sizeof(priv->vf));
117 rc = ov_open_callbacks(ip_data, &priv->vf, NULL, 0, callbacks);
118 if (rc != 0) {
119 d_print("ov_open failed: %d\n", rc);
120 free(priv);
121 return -IP_ERROR_FILE_FORMAT;
123 ip_data->private = priv;
125 vi = ov_info(&priv->vf, -1);
126 ip_data->sf = sf_rate(vi->rate) | sf_channels(vi->channels) | sf_bits(16) | sf_signed(1);
127 return 0;
130 static int vorbis_close(struct input_plugin_data *ip_data)
132 struct vorbis_private *priv;
133 int rc;
135 priv = ip_data->private;
136 /* this closes ip_data->fd! */
137 rc = ov_clear(&priv->vf);
138 ip_data->fd = -1;
139 if (rc)
140 d_print("ov_clear returned %d\n", rc);
141 free(priv);
142 ip_data->private = NULL;
143 return 0;
147 * OV_HOLE
148 * indicates there was an interruption in the data.
149 * (one of: garbage between pages, loss of sync followed by recapture,
150 * or a corrupt page)
151 * OV_EBADLINK
152 * indicates that an invalid stream section was supplied to libvorbisfile,
153 * or the requested link is corrupt.
155 * indicates EOF
157 * indicates actual number of bytes read. ov_read() will decode at most
158 * one vorbis packet per invocation, so the value returned will generally
159 * be less than length.
161 static int vorbis_read(struct input_plugin_data *ip_data, char *buffer, int count)
163 struct vorbis_private *priv;
164 int rc;
166 priv = ip_data->private;
167 #ifdef CONFIG_TREMOR
168 /* Tremor can only handle signed 16 bit data */
169 rc = ov_read(&priv->vf, buffer, count, &priv->current_section);
170 #else
171 rc = ov_read(&priv->vf, buffer, count, 0, 2, 1, &priv->current_section);
172 #endif
173 switch (rc) {
174 case OV_HOLE:
175 errno = EAGAIN;
176 return -1;
177 case OV_EBADLINK:
178 errno = EINVAL;
179 return -1;
180 case OV_EINVAL:
181 errno = EINVAL;
182 return -1;
183 case 0:
184 if (errno) {
185 d_print("error: %s\n", strerror(errno));
186 return -1;
187 /* return -IP_ERROR_INTERNAL; */
189 /* EOF */
190 return 0;
191 default:
192 if (rc < 0) {
193 d_print("error: %d\n", rc);
194 rc = -IP_ERROR_FILE_FORMAT;
196 return rc;
200 static int vorbis_seek(struct input_plugin_data *ip_data, double offset)
202 struct vorbis_private *priv;
203 int rc;
205 priv = ip_data->private;
207 #ifdef CONFIG_TREMOR
208 rc = ov_time_seek(&priv->vf, offset * 1000);
209 #else
210 rc = ov_time_seek(&priv->vf, offset);
211 #endif
212 switch (rc) {
213 case OV_ENOSEEK:
214 return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
215 case OV_EINVAL:
216 return -IP_ERROR_INTERNAL;
217 case OV_EREAD:
218 return -IP_ERROR_INTERNAL;
219 case OV_EFAULT:
220 return -IP_ERROR_INTERNAL;
221 case OV_EBADLINK:
222 return -IP_ERROR_INTERNAL;
224 return 0;
227 static int vorbis_read_comments(struct input_plugin_data *ip_data,
228 struct keyval **comments)
230 struct keyval *c;
231 struct vorbis_private *priv;
232 vorbis_comment *vc;
233 int i, s, d;
235 priv = ip_data->private;
236 vc = ov_comment(&priv->vf, -1);
237 if (vc == NULL) {
238 d_print("vc == NULL\n");
239 *comments = xnew0(struct keyval, 1);
240 return 0;
242 c = xnew0(struct keyval, vc->comments + 1);
243 for (s = 0, d = 0; s < vc->comments; s++) {
244 const char *str = vc->user_comments[s];
245 char *key, *val;
247 for (i = 0; str[i]; i++) {
248 if (str[i] == '=')
249 break;
251 if (str[i] != '=') {
252 d_print("invalid comment: '%s' ('=' expected)\n", str);
253 continue;
255 key = xstrndup(str, i);
256 if (!is_interesting_key(key)) {
257 free(key);
258 continue;
261 val = xstrdup(str + i + 1);
262 if (!strcasecmp(key, "tracknumber") || !strcasecmp(key, "discnumber"))
263 fix_track_or_disc(val);
264 c[d].key = key;
265 c[d].val = val;
266 d++;
268 *comments = c;
269 return 0;
272 static int vorbis_duration(struct input_plugin_data *ip_data)
274 struct vorbis_private *priv;
275 int duration;
277 priv = ip_data->private;
278 duration = ov_time_total(&priv->vf, -1);
279 if (duration == OV_EINVAL)
280 return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
281 #ifdef CONFIG_TREMOR
282 duration = (duration + 500) / 1000;
283 #endif
284 return duration;
287 const struct input_plugin_ops ip_ops = {
288 .open = vorbis_open,
289 .close = vorbis_close,
290 .read = vorbis_read,
291 .seek = vorbis_seek,
292 .read_comments = vorbis_read_comments,
293 .duration = vorbis_duration
296 const char * const ip_extensions[] = { "ogg", NULL };
297 const char * const ip_mime_types[] = { "application/ogg", "audio/x-ogg", NULL };