1 /*
2 * Copyright (c) 2012 Tim van der Molen <tim@kariliq.nl>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 /*
18 * Note that the WavPack API uses the term "sample" to refer to "sample per
19 * channel" (i.e. a frame).
20 */
21
22 #include <limits.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include <wavpack/wavpack.h>
28
29 #include "../siren.h"
30
31 #define IP_WAVPACK_BUFSIZE 2048 /* Buffer size, in frames */
32 #define IP_WAVPACK_ERRSTRLEN 80 /* As per the documentation */
33
34 struct ip_wavpack_ipdata {
35 WavpackContext *wpc;
36 int float_samples;
37 int32_t *buf;
38 uint32_t bufidx; /* Current sample */
39 uint32_t buflen; /* Buffer length, in samples */
40 };
41
42 static void ip_wavpack_close(struct track *);
43 static void ip_wavpack_get_metadata(struct track *);
44 static int ip_wavpack_get_position(struct track *,
45 unsigned int *);
46 static char *ip_wavpack_get_tag_item(WavpackContext *,
47 const char *);
48 static int ip_wavpack_open(struct track *);
49 static int ip_wavpack_read(struct track *,
50 struct sample_buffer *);
51 static void ip_wavpack_seek(struct track *, unsigned int);
52
53 static const char *ip_wavpack_extensions[] = { "wv", NULL };
54
55 const struct ip ip = {
56 "wavpack",
57 IP_PRIORITY_WAVPACK,
58 ip_wavpack_extensions,
59 ip_wavpack_close,
60 ip_wavpack_get_metadata,
61 ip_wavpack_get_position,
62 NULL,
63 ip_wavpack_open,
64 ip_wavpack_read,
65 ip_wavpack_seek
66 };
67
68 static void
ip_wavpack_close(struct track * t)69 ip_wavpack_close(struct track *t)
70 {
71 struct ip_wavpack_ipdata *ipd;
72
73 ipd = t->ipdata;
74 WavpackCloseFile(ipd->wpc);
75 free(ipd->buf);
76 free(ipd);
77 }
78
79 static void
ip_wavpack_get_metadata(struct track * t)80 ip_wavpack_get_metadata(struct track *t)
81 {
82 WavpackContext *wpc;
83 uint32_t nframes, rate;
84 char errstr[IP_WAVPACK_ERRSTRLEN], *val;
85
86 wpc = WavpackOpenFileInput(t->path, errstr, OPEN_TAGS, 0);
87 if (wpc == NULL) {
88 LOG_ERRX("WavpackOpenFileInput: %s: %s", t->path, errstr);
89 msg_errx("%s: Cannot open track: %s", t->path, errstr);
90 return;
91 }
92
93 t->album = ip_wavpack_get_tag_item(wpc, "album");
94 t->artist = ip_wavpack_get_tag_item(wpc, "artist");
95 t->comment = ip_wavpack_get_tag_item(wpc, "comment");
96 t->date = ip_wavpack_get_tag_item(wpc, "year");
97 t->genre = ip_wavpack_get_tag_item(wpc, "genre");
98 t->title = ip_wavpack_get_tag_item(wpc, "title");
99
100 val = ip_wavpack_get_tag_item(wpc, "track");
101 if (val != NULL) {
102 track_split_tag(val, &t->tracknumber, &t->tracktotal);
103 free(val);
104 }
105
106 /*
107 * APEv2 doesn't have a standard key for the disc number. Try the
108 * "disc" and "part" keys.
109 */
110 val = ip_wavpack_get_tag_item(wpc, "disc");
111 if (val == NULL)
112 val = ip_wavpack_get_tag_item(wpc, "part");
113 if (val != NULL) {
114 track_split_tag(val, &t->discnumber, &t->disctotal);
115 free(val);
116 }
117
118 /*
119 * APEv2 doesn't have a standard key for the album artist. Try the
120 * "albumartist" and "album artist" keys.
121 */
122 t->albumartist = ip_wavpack_get_tag_item(wpc, "albumartist");
123 if (t->albumartist == NULL)
124 t->albumartist = ip_wavpack_get_tag_item(wpc, "album artist");
125
126 nframes = WavpackGetNumSamples(wpc);
127 rate = WavpackGetSampleRate(wpc);
128 if (nframes != (uint32_t)-1 && rate != 0)
129 t->duration = nframes / rate;
130
131 WavpackCloseFile(wpc);
132 }
133
134 static int
ip_wavpack_get_position(struct track * t,unsigned int * pos)135 ip_wavpack_get_position(struct track *t, unsigned int *pos)
136 {
137 struct ip_wavpack_ipdata *ipd;
138 uint32_t curframe;
139
140 ipd = t->ipdata;
141
142 curframe = WavpackGetSampleIndex(ipd->wpc);
143 *pos = curframe / t->format.rate;
144 return 0;
145 }
146
147 /*
148 * Return the value of the APEv2 or ID3v1 tag item with the specified key.
149 */
150 static char *
ip_wavpack_get_tag_item(WavpackContext * wpc,const char * key)151 ip_wavpack_get_tag_item(WavpackContext *wpc, const char *key)
152 {
153 int len;
154 char *value;
155
156 len = WavpackGetTagItem(wpc, key, NULL, 0);
157 if (len <= 0)
158 value = NULL;
159 else {
160 if (len < INT_MAX)
161 len++;
162 value = xmalloc(len);
163 WavpackGetTagItem(wpc, key, value, len);
164 }
165
166 return value;
167 }
168
169 static int
ip_wavpack_open(struct track * t)170 ip_wavpack_open(struct track *t)
171 {
172 struct ip_wavpack_ipdata *ipd;
173 WavpackContext *wpc;
174 int float_samples;
175 char errstr[IP_WAVPACK_ERRSTRLEN];
176
177 wpc = WavpackOpenFileInput(t->path, errstr, OPEN_NORMALIZE | OPEN_WVC,
178 0);
179 if (wpc == NULL) {
180 LOG_ERRX("WavpackOpenFileInput: %s: %s", t->path, errstr);
181 msg_errx("%s: Cannot open track: %s", t->path, errstr);
182 return -1;
183 }
184
185 float_samples = WavpackGetMode(wpc) & MODE_FLOAT;
186 if (float_samples)
187 t->format.nbits = 16;
188 else
189 /*
190 * WavPack aligns samples whose bit depth is not a multiple of
191 * 8 on the MSB rather than the LSB. Therefore, determine the
192 * bit depth from the number of bytes per sample.
193 */
194 t->format.nbits = 8 * WavpackGetBytesPerSample(wpc);
195
196 t->format.nchannels = WavpackGetNumChannels(wpc);
197 t->format.rate = WavpackGetSampleRate(wpc);
198
199 ipd = xmalloc(sizeof *ipd);
200 ipd->wpc = wpc;
201 ipd->float_samples = float_samples;
202 ipd->bufidx = 0;
203 ipd->buflen = 0;
204 ipd->buf = xreallocarray(NULL,
205 IP_WAVPACK_BUFSIZE * t->format.nchannels, sizeof *ipd->buf);
206
207 t->ipdata = ipd;
208 return 0;
209 }
210
211 static int
ip_wavpack_read(struct track * t,struct sample_buffer * sb)212 ip_wavpack_read(struct track *t, struct sample_buffer *sb)
213 {
214 struct ip_wavpack_ipdata *ipd;
215 uint32_t ret;
216 float f;
217
218 ipd = t->ipdata;
219
220 for (sb->len_s = 0; sb->len_s < sb->size_s; sb->len_s++) {
221 if (ipd->bufidx == ipd->buflen) {
222 ret = WavpackUnpackSamples(ipd->wpc, ipd->buf,
223 IP_WAVPACK_BUFSIZE);
224 if (ret == 0)
225 /* EOF reached. */
226 break;
227 ipd->buflen = ret * t->format.nchannels;
228 ipd->bufidx = 0;
229 }
230
231 if (!ipd->float_samples)
232 switch (sb->nbytes) {
233 case 1:
234 sb->data1[sb->len_s] = ipd->buf[ipd->bufidx];
235 break;
236 case 2:
237 sb->data2[sb->len_s] = ipd->buf[ipd->bufidx];
238 break;
239 case 4:
240 sb->data4[sb->len_s] = ipd->buf[ipd->bufidx];
241 break;
242 }
243 else {
244 /* We assume floats use IEEE 754 representation. */
245 f = *(float *)&ipd->buf[ipd->bufidx] * -INT16_MIN;
246 if (f < INT16_MIN)
247 sb->data2[sb->len_s] = INT16_MIN;
248 else if (f > INT16_MAX)
249 sb->data2[sb->len_s] = INT16_MAX;
250 else
251 sb->data2[sb->len_s] = f;
252 }
253
254 ipd->bufidx++;
255 }
256
257 sb->len_b = sb->len_s * sb->nbytes;
258 return sb->len_s != 0;
259 }
260
261 static void
ip_wavpack_seek(struct track * t,unsigned int sec)262 ip_wavpack_seek(struct track *t, unsigned int sec)
263 {
264 struct ip_wavpack_ipdata *ipd;
265 uint32_t frame;
266
267 ipd = t->ipdata;
268 frame = sec * t->format.rate;
269 if (!WavpackSeekSample(ipd->wpc, frame)) {
270 LOG_ERRX("WavpackSeekSample: %s: %s", t->path,
271 WavpackGetErrorMessage(ipd->wpc));
272 msg_errx("Cannot seek: %s", WavpackGetErrorMessage(ipd->wpc));
273 }
274 }
275