Handle streams separately in tree_add_track()
[cmus.git] / wavpack.c
blob4a3058e41d7610d8e61bce72d9a0d1605d3cb316
1 /*
2 * Copyright 2007 Johannes Weißl
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 "ip.h"
21 #include "ape.h"
22 #include "id3.h"
23 #include "xmalloc.h"
24 #include "read_wrapper.h"
25 #include "debug.h"
26 #include "buffer.h"
28 #include <wavpack/wavpack.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include <fcntl.h>
37 #define WV_CHANNEL_MAX 2
39 struct wavpack_private {
40 WavpackContext *wpc;
41 off_t len;
42 int32_t samples[CHUNK_SIZE * WV_CHANNEL_MAX];
45 /* http://www.wavpack.com/lib_use.txt */
47 static int32_t read_bytes(void *data, void *ptr, int32_t count)
49 struct input_plugin_data *ip_data = data;
50 int rc;
52 rc = read_wrapper(ip_data, ptr, count);
53 if (rc == -1) {
54 d_print("error: %s\n", strerror(errno));
55 return 0;
57 if (rc == 0) {
58 errno = 0;
59 return 0;
61 return rc;
64 static uint32_t get_pos(void *data)
66 struct input_plugin_data *ip_data = data;
68 return lseek(ip_data->fd, 0, SEEK_CUR);
71 static int set_pos_abs(void *data, uint32_t pos)
73 struct input_plugin_data *ip_data = data;
75 lseek(ip_data->fd, pos, SEEK_SET);
76 return errno;
79 static int set_pos_rel(void *data, int32_t delta, int mode)
81 struct input_plugin_data *ip_data = data;
83 lseek(ip_data->fd, delta, mode);
84 return errno;
87 static int push_back_byte(void *data, int c)
89 /* not possible? */
90 d_print("NOT POSSIBLE\n");
91 return EOF;
94 static uint32_t get_length(void *data)
96 struct input_plugin_data *ip_data = data;
97 struct wavpack_private *priv = ip_data->private;
98 return priv->len;
101 static int can_seek(void *data)
103 struct input_plugin_data *ip_data = data;
104 return !ip_data->remote;
107 static int32_t write_bytes(void *data, void *ptr, int32_t count)
109 /* we shall not write any bytes */
110 return 0;
115 * typedef struct {
116 * int32_t (*read_bytes)(void *id, void *data, int32_t bcount);
117 * uint32_t (*get_pos)(void *id);
118 * int (*set_pos_abs)(void *id, uint32_t pos);
119 * int (*set_pos_rel)(void *id, int32_t delta, int mode);
120 * int (*push_back_byte)(void *id, int c);
121 * uint32_t (*get_length)(void *id);
122 * int (*can_seek)(void *id);
124 * // this callback is for writing edited tags only
125 * int32_t (*write_bytes)(void *id, void *data, int32_t bcount);
126 * } WavpackStreamReader;
128 static WavpackStreamReader callbacks = {
129 .read_bytes = read_bytes,
130 .get_pos = get_pos,
131 .set_pos_abs = set_pos_abs,
132 .set_pos_rel = set_pos_rel,
133 .push_back_byte = push_back_byte,
134 .get_length = get_length,
135 .can_seek = can_seek,
136 .write_bytes = write_bytes
139 static int wavpack_open(struct input_plugin_data *ip_data)
141 struct wavpack_private *priv;
142 struct stat st;
143 char msg[80];
145 priv = xnew(struct wavpack_private, 1);
146 priv->wpc = NULL;
147 priv->len = 0;
148 if (!ip_data->remote && !fstat(ip_data->fd, &st))
149 priv->len = st.st_size;
150 ip_data->private = priv;
152 *msg = '\0';
154 priv->wpc = WavpackOpenFileInputEx(&callbacks, ip_data, NULL, msg,
155 OPEN_2CH_MAX | OPEN_NORMALIZE, 0);
157 if (!priv->wpc) {
158 d_print("WavpackOpenFileInputEx failed: %s\n", msg);
159 free(priv);
160 return -IP_ERROR_FILE_FORMAT;
163 ip_data->sf = sf_rate(WavpackGetSampleRate(priv->wpc))
164 | sf_channels(WavpackGetReducedChannels(priv->wpc))
165 | sf_bits(WavpackGetBitsPerSample(priv->wpc))
166 | sf_signed(1);
167 return 0;
170 static int wavpack_close(struct input_plugin_data *ip_data)
172 struct wavpack_private *priv;
174 priv = ip_data->private;
175 priv->wpc = WavpackCloseFile(priv->wpc);
176 free(priv);
177 ip_data->private = NULL;
178 return 0;
181 /* from wv_engine.cpp (C) 2006 by Peter Lemenkov <lemenkov@newmail.ru> */
182 static char *format_samples(int bps, char *dst, int32_t *src, uint32_t count)
184 int32_t temp;
186 switch (bps) {
187 case 1:
188 while (count--)
189 *dst++ = *src++ + 128;
190 break;
191 case 2:
192 while (count--) {
193 *dst++ = (char) (temp = *src++);
194 *dst++ = (char) (temp >> 8);
196 break;
197 case 3:
198 while (count--) {
199 *dst++ = (char) (temp = *src++);
200 *dst++ = (char) (temp >> 8);
201 *dst++ = (char) (temp >> 16);
203 break;
204 case 4:
205 while (count--) {
206 *dst++ = (char) (temp = *src++);
207 *dst++ = (char) (temp >> 8);
208 *dst++ = (char) (temp >> 16);
209 *dst++ = (char) (temp >> 24);
211 break;
214 return dst;
217 static int wavpack_read(struct input_plugin_data *ip_data, char *buffer, int count)
219 struct wavpack_private *priv;
220 int rc, bps, sample_count, channels;
222 priv = ip_data->private;
223 channels = sf_get_channels(ip_data->sf);
224 bps = WavpackGetBytesPerSample(priv->wpc);
226 sample_count = count / bps;
228 rc = WavpackUnpackSamples(priv->wpc, priv->samples, sample_count / channels);
229 format_samples(bps, buffer, priv->samples, rc * channels);
230 return rc * channels * bps;
233 static int wavpack_seek(struct input_plugin_data *ip_data, double offset)
235 struct wavpack_private *priv = ip_data->private;
237 if (!WavpackSeekSample(priv->wpc, WavpackGetSampleRate(priv->wpc) * offset))
238 return -IP_ERROR_INTERNAL;
239 return 0;
242 static int wavpack_read_comments(struct input_plugin_data *ip_data,
243 struct keyval **comments)
245 struct id3tag id3;
246 APETAG(ape);
247 GROWING_KEYVALS(c);
248 int fd, rc, save, i;
250 fd = open(ip_data->filename, O_RDONLY);
251 if (fd == -1)
252 return -1;
253 d_print("filename: %s\n", ip_data->filename);
255 id3_init(&id3);
256 rc = id3_read_tags(&id3, fd, ID3_V1);
257 save = errno;
258 close(fd);
259 errno = save;
260 if (rc) {
261 if (rc == -1) {
262 d_print("error: %s\n", strerror(errno));
263 return -1;
265 d_print("corrupted tag?\n");
266 goto next;
269 for (i = 0; i < NUM_ID3_KEYS; i++) {
270 char *val = id3_get_comment(&id3, i);
271 if (val)
272 comments_add(&c, id3_key_names[i], val);
275 next:
276 id3_free(&id3);
278 rc = ape_read_tags(&ape, ip_data->fd, 1);
279 if (rc < 0)
280 goto out;
282 for (i = 0; i < rc; i++) {
283 char *k, *v;
284 k = ape_get_comment(&ape, &v);
285 if (!k)
286 break;
287 comments_add(&c, k, v);
288 free(k);
291 out:
292 ape_free(&ape);
294 keyvals_terminate(&c);
295 *comments = c.keyvals;
296 return 0;
299 static int wavpack_duration(struct input_plugin_data *ip_data)
301 struct wavpack_private *priv;
302 int duration;
304 priv = ip_data->private;
305 duration = WavpackGetNumSamples(priv->wpc) /
306 WavpackGetSampleRate(priv->wpc);
308 return duration;
311 const struct input_plugin_ops ip_ops = {
312 .open = wavpack_open,
313 .close = wavpack_close,
314 .read = wavpack_read,
315 .seek = wavpack_seek,
316 .read_comments = wavpack_read_comments,
317 .duration = wavpack_duration
320 const char * const ip_extensions[] = { "wv", NULL };
321 const char * const ip_mime_types[] = { "audio/x-wavpack", NULL };