Mercurial > audlegacy-plugins
annotate src/wavpack/libwavpack.cxx @ 130:16e2c64d8b2b trunk
[svn] - provide a complete tuple (fixes albumart and such; path was missing.)
author | nenolod |
---|---|
date | Fri, 27 Oct 2006 11:58:34 -0700 |
parents | 2e77e3fdd3c1 |
children | 7b7660c9f31c |
rev | line source |
---|---|
109 | 1 #include <assert.h> |
2 #include <string.h> | |
3 #include <stdio.h> | |
4 #include <stdlib.h> | |
5 #include <unistd.h> | |
6 #include <wavpack/wputils.h> | |
7 extern "C" { | |
8 #include <audacious/plugin.h> | |
111 | 9 #include <audacious/output.h> |
109 | 10 #include <audacious/configdb.h> |
11 #include <audacious/titlestring.h> | |
12 #include <audacious/util.h> | |
13 } | |
14 #include <glib.h> | |
15 #include <gtk/gtk.h> | |
16 #include <iconv.h> | |
17 #include <math.h> | |
18 #include "tags.h" | |
19 #ifndef M_LN10 | |
20 #define M_LN10 2.3025850929940456840179914546843642 | |
21 #endif | |
22 | |
112 | 23 #ifdef DEBUG |
24 # define DBG(format, args...) fprintf(stderr, format, ## args) | |
25 #else | |
26 # define DBG(format, args...) | |
27 #endif | |
28 | |
109 | 29 #define BUFFER_SIZE 256 // read buffer size, in samples |
30 | |
31 extern "C" InputPlugin * get_iplugin_info(void); | |
32 static void wv_load_config(); | |
33 static int wv_is_our_file(char *); | |
34 static void wv_play(char *); | |
35 static void wv_stop(void); | |
36 static void wv_pause(short); | |
37 static void wv_seek(int); | |
38 static int wv_get_time(void); | |
39 static void wv_get_song_info(char *, char **, int *); | |
40 static char *generate_title(const char *, WavpackContext *ctx); | |
41 static double isSeek; | |
42 static short paused; | |
43 static bool killDecodeThread; | |
44 static bool AudioError; | |
45 static GThread *thread_handle; | |
114 | 46 static TitleInput *wv_get_song_tuple(char *); |
109 | 47 |
48 // in ui.cpp | |
49 void wv_configure(); | |
50 void wv_about_box(void); | |
51 void wv_file_info_box(char *); | |
52 extern gboolean clipPreventionEnabled; | |
53 extern gboolean dynBitrateEnabled; | |
54 extern gboolean replaygainEnabled; | |
55 extern gboolean albumReplaygainEnabled; | |
56 extern gboolean openedAudio; | |
57 | |
58 InputPlugin mod = { | |
59 NULL, //handle | |
60 NULL, //filename | |
61 NULL, | |
62 wv_load_config, | |
63 wv_about_box, | |
64 wv_configure, | |
65 wv_is_our_file, | |
66 NULL, //no use | |
67 wv_play, | |
68 wv_stop, | |
69 wv_pause, | |
70 wv_seek, | |
71 NULL, //set eq | |
72 wv_get_time, | |
73 NULL, //get volume | |
74 NULL, //set volume | |
75 NULL, //cleanup | |
76 NULL, //obsolete | |
77 NULL, //add_vis | |
78 NULL, | |
79 NULL, | |
80 wv_get_song_info, | |
81 wv_file_info_box, //info box | |
82 NULL, //output | |
114 | 83 wv_get_song_tuple, |
109 | 84 }; |
85 | |
86 class WavpackDecoder | |
87 { | |
88 public: | |
89 InputPlugin *mod; | |
90 int32_t *input; | |
91 int16_t *output; | |
92 int sample_rate; | |
93 int num_channels; | |
94 WavpackContext *ctx; | |
95 char error_buff[4096]; // TODO: fixme! | |
96 | |
97 WavpackDecoder(InputPlugin *mod) : mod(mod) | |
98 { | |
99 ctx = NULL; | |
100 input = NULL; | |
101 output = NULL; | |
102 } | |
103 | |
104 ~WavpackDecoder() | |
105 { | |
106 if (input != NULL) { | |
107 free(input); | |
108 input = NULL; | |
109 } | |
110 if (output != NULL) { | |
111 free(output); | |
112 output = NULL; | |
113 } | |
114 if (ctx != NULL) { | |
115 WavpackCloseFile(ctx); | |
116 ctx = NULL; | |
117 } | |
118 } | |
119 | |
120 bool attach(const char *filename) | |
121 { | |
122 ctx = WavpackOpenFileInput(filename, error_buff, OPEN_TAGS | OPEN_WVC, 0); | |
123 | |
124 if (ctx == NULL) { | |
125 return false; | |
126 } | |
127 | |
128 sample_rate = WavpackGetSampleRate(ctx); | |
129 num_channels = WavpackGetNumChannels(ctx); | |
130 input = (int32_t *)calloc(BUFFER_SIZE, num_channels * sizeof(int32_t)); | |
131 output = (int16_t *)calloc(BUFFER_SIZE, num_channels * sizeof(int16_t)); | |
132 mod->set_info(generate_title(filename, ctx), | |
133 (int) (WavpackGetNumSamples(ctx) / sample_rate) * 1000, | |
134 (int) WavpackGetAverageBitrate(ctx, num_channels), | |
135 (int) sample_rate, num_channels); | |
136 return true; | |
137 } | |
138 | |
139 bool open_audio() | |
140 { | |
112 | 141 return mod->output->open_audio(FMT_S16_NE, sample_rate, num_channels); |
109 | 142 } |
143 | |
144 void process_buffer(size_t num_samples) | |
145 { | |
146 for (int i = 0; i < num_samples * num_channels; i++) { | |
147 output[i] = input[i]; | |
148 } | |
112 | 149 produce_audio(mod->output->output_time(), FMT_S16_NE, |
111 | 150 num_channels, |
151 num_samples * num_channels * sizeof(int16_t), | |
152 output, | |
153 NULL); | |
109 | 154 } |
155 }; | |
156 | |
157 extern "C" InputPlugin * | |
158 get_iplugin_info(void) | |
159 { | |
160 mod.description = | |
161 g_strdup_printf(("Wavpack Decoder Plugin %s"), VERSION); | |
162 return &mod; | |
163 } | |
164 | |
165 static int | |
166 wv_is_our_file(char *filename) | |
167 { | |
168 char *ext; | |
169 | |
170 ext = strrchr(filename, '.'); | |
171 if (ext) { | |
172 if (!strcasecmp(ext, ".wv")) { | |
173 return TRUE; | |
174 } | |
175 } | |
176 return FALSE; | |
177 } | |
178 | |
179 void | |
180 load_tag(ape_tag *tag, WavpackContext *ctx) | |
181 { | |
182 memset(tag, 0, sizeof(ape_tag)); | |
183 WavpackGetTagItem(ctx, "Album", tag->album, sizeof(tag->album)); | |
184 WavpackGetTagItem(ctx, "Artist", tag->artist, sizeof(tag->artist)); | |
185 WavpackGetTagItem(ctx, "Comment", tag->comment, sizeof(tag->comment)); | |
186 WavpackGetTagItem(ctx, "Genre", tag->genre, sizeof(tag->genre)); | |
187 WavpackGetTagItem(ctx, "Title", tag->title, sizeof(tag->title)); | |
188 WavpackGetTagItem(ctx, "Track", tag->track, sizeof(tag->track)); | |
189 WavpackGetTagItem(ctx, "Year", tag->year, sizeof(tag->year)); | |
190 } | |
191 | |
192 static char * | |
193 convertUTF8toLocale(char *utf8) | |
194 { | |
195 // note - opens a new iconv descriptor for each call | |
196 // will have to find a way to reuse the descriptor if this turns | |
197 // out to be too slow | |
198 iconv_t idesc = iconv_open("", "UTF-8"); | |
199 if (idesc == (iconv_t) -1) { | |
200 perror("iconv_open failed"); | |
201 return g_strdup(utf8); | |
202 } | |
203 | |
204 size_t in_left = strlen(utf8); | |
205 size_t out_left = 2 * in_left + 1; | |
206 char *buf = (char *)g_malloc(out_left); | |
207 char *in = utf8; | |
208 char *out = buf; | |
209 | |
210 memset(buf, 0, out_left); | |
211 size_t err = iconv(idesc, &in, &in_left, &out, &out_left); | |
212 iconv_close(idesc); | |
213 return buf; | |
214 } | |
215 | |
216 static void * | |
217 end_thread() | |
218 { | |
219 return 0; | |
220 } | |
221 | |
222 static void * | |
223 DecodeThread(void *a) | |
224 { | |
225 ape_tag tag; | |
226 char *filename = (char *) a; | |
227 int bps_updateCounter = 0; | |
228 int bps; | |
229 int i; | |
230 WavpackDecoder d(&mod); | |
231 | |
232 if (!d.attach(filename)) { | |
233 printf("wavpack: Error opening file: \"%s\"\n", filename); | |
234 killDecodeThread = true; | |
235 return end_thread(); | |
236 } | |
237 bps = WavpackGetBytesPerSample(d.ctx) * d.num_channels; | |
238 DBG("reading %s at %d rate with %d channels\n", filename, d.sample_rate, d.num_channels); | |
239 | |
240 if (!d.open_audio()) { | |
241 DBG("error opening xmms audio channel\n"); | |
242 killDecodeThread = true; | |
243 AudioError = true; | |
244 openedAudio = false; | |
245 } | |
246 else { | |
247 DBG("opened xmms audio channel\n"); | |
248 openedAudio = true; | |
249 } | |
250 unsigned status; | |
251 char *display = generate_title(filename, d.ctx); | |
252 int length = (int) (1000 * WavpackGetNumSamples(d.ctx)); | |
253 | |
254 while (!killDecodeThread) { | |
255 if (isSeek != -1) { | |
256 DBG("seeking to position %d\n", isSeek); | |
257 WavpackSeekSample(d.ctx, isSeek * d.sample_rate); | |
258 isSeek = -1; | |
259 } | |
260 if (paused == 0 | |
261 && (mod.output->buffer_free() >= | |
262 (1152 * 2 * | |
263 (16 / 8)) << (mod.output->buffer_playing()? 1 : 0))) { | |
264 status = | |
265 WavpackUnpackSamples(d.ctx, d.input, BUFFER_SIZE); | |
266 if (status == (unsigned) (-1)) { | |
267 printf("wavpack: Error decoding file.\n"); | |
268 break; | |
269 } | |
270 else if (status == 0) { | |
271 killDecodeThread = true; | |
272 break; | |
273 } | |
274 else { | |
275 d.process_buffer(status); | |
276 } | |
277 } | |
278 else { | |
279 xmms_usleep(10000); | |
280 } | |
281 } | |
282 return end_thread(); | |
283 } | |
284 | |
285 static void | |
286 wv_play(char *filename) | |
287 { | |
288 paused = 0; | |
289 isSeek = -1; | |
290 killDecodeThread = false; | |
291 AudioError = false; | |
292 thread_handle = g_thread_create(DecodeThread, (void *) filename, TRUE, NULL); | |
293 return; | |
294 } | |
295 | |
114 | 296 static TitleInput * |
297 tuple_from_WavpackContext(const char *fn, WavpackContext *ctx) | |
298 { | |
299 ape_tag tag; | |
300 TitleInput *ti; | |
301 int sample_rate = WavpackGetSampleRate(ctx); | |
302 | |
303 ti = bmp_title_input_new(); | |
304 | |
130
16e2c64d8b2b
[svn] - provide a complete tuple (fixes albumart and such; path was missing.)
nenolod
parents:
115
diff
changeset
|
305 ti->file_name = g_path_get_basename(fn); |
16e2c64d8b2b
[svn] - provide a complete tuple (fixes albumart and such; path was missing.)
nenolod
parents:
115
diff
changeset
|
306 ti->file_path = g_path_get_dirname(fn); |
114 | 307 ti->file_ext = "wv"; |
308 | |
309 load_tag(&tag, ctx); | |
310 | |
115
2e77e3fdd3c1
[svn] - make sure the tuple data is copied, not referenced (oops)
nenolod
parents:
114
diff
changeset
|
311 ti->track_name = g_strdup(tag.title); |
2e77e3fdd3c1
[svn] - make sure the tuple data is copied, not referenced (oops)
nenolod
parents:
114
diff
changeset
|
312 ti->performer = g_strdup(tag.artist); |
2e77e3fdd3c1
[svn] - make sure the tuple data is copied, not referenced (oops)
nenolod
parents:
114
diff
changeset
|
313 ti->album_name = g_strdup(tag.album); |
2e77e3fdd3c1
[svn] - make sure the tuple data is copied, not referenced (oops)
nenolod
parents:
114
diff
changeset
|
314 ti->date = g_strdup(tag.year); |
114 | 315 ti->track_number = atoi(tag.track); |
316 if (ti->track_number < 0) | |
317 ti->track_number = 0; | |
318 ti->year = atoi(tag.year); | |
319 if (ti->year < 0) | |
320 ti->year = 0; | |
115
2e77e3fdd3c1
[svn] - make sure the tuple data is copied, not referenced (oops)
nenolod
parents:
114
diff
changeset
|
321 ti->genre = g_strdup(tag.genre); |
2e77e3fdd3c1
[svn] - make sure the tuple data is copied, not referenced (oops)
nenolod
parents:
114
diff
changeset
|
322 ti->comment = g_strdup(tag.comment); |
114 | 323 ti->length = (int)(WavpackGetNumSamples(ctx) / sample_rate) * 1000; |
324 | |
325 return ti; | |
326 } | |
327 | |
109 | 328 static char * |
329 generate_title(const char *fn, WavpackContext *ctx) | |
330 { | |
331 static char *displaytitle = NULL; | |
332 TitleInput *ti; | |
333 | |
114 | 334 ti = tuple_from_WavpackContext(fn, ctx); |
109 | 335 |
336 displaytitle = xmms_get_titlestring(xmms_get_gentitle_format(), ti); | |
115
2e77e3fdd3c1
[svn] - make sure the tuple data is copied, not referenced (oops)
nenolod
parents:
114
diff
changeset
|
337 if (!displaytitle || *displaytitle == '\0') |
2e77e3fdd3c1
[svn] - make sure the tuple data is copied, not referenced (oops)
nenolod
parents:
114
diff
changeset
|
338 displaytitle = g_strdup(fn); |
114 | 339 |
340 bmp_title_input_free(ti); | |
109 | 341 |
342 return displaytitle; | |
343 } | |
344 | |
114 | 345 static TitleInput * |
346 wv_get_song_tuple(char *filename) | |
347 { | |
348 TitleInput *ti; | |
349 char error_buff[4096]; // TODO: fixme! | |
350 WavpackContext *ctx = WavpackOpenFileInput(filename, error_buff, OPEN_TAGS | OPEN_WVC, 0); | |
351 | |
352 if (ctx == NULL) { | |
353 printf("wavpack: Error opening file: \"%s: %s\"\n", filename, error_buff); | |
354 return NULL; | |
355 } | |
356 | |
357 ti = tuple_from_WavpackContext(filename, ctx); | |
358 | |
359 WavpackCloseFile(ctx); | |
360 | |
361 return ti; | |
362 } | |
363 | |
109 | 364 static void |
365 wv_get_song_info(char *filename, char **title, int *length) | |
366 { | |
367 assert(filename != NULL); | |
368 char error_buff[4096]; // TODO: fixme! | |
369 WavpackContext *ctx = WavpackOpenFileInput(filename, error_buff, OPEN_TAGS | OPEN_WVC, 0); | |
370 if (ctx == NULL) { | |
371 printf("wavpack: Error opening file: \"%s: %s\"\n", filename, error_buff); | |
372 return; | |
373 } | |
374 int sample_rate = WavpackGetSampleRate(ctx); | |
375 int num_channels = WavpackGetNumChannels(ctx); | |
376 DBG("reading %s at %d rate with %d channels\n", filename, sample_rate, num_channels); | |
377 | |
378 *length = (int)(WavpackGetNumSamples(ctx) / sample_rate) * 1000, | |
379 *title = generate_title(filename, ctx); | |
380 DBG("title for %s = %s\n", filename, *title); | |
381 WavpackCloseFile(ctx); | |
382 } | |
383 | |
384 static int | |
385 wv_get_time(void) | |
386 { | |
387 if (!mod.output) | |
388 return -1; | |
389 if (AudioError) | |
390 return -2; | |
391 if (killDecodeThread && !mod.output->buffer_playing()) | |
392 return -1; | |
393 return mod.output->output_time(); | |
394 } | |
395 | |
396 | |
397 static void | |
398 wv_seek(int sec) | |
399 { | |
400 isSeek = sec; | |
401 mod.output->flush((int) (1000 * isSeek)); | |
402 } | |
403 | |
404 static void | |
405 wv_pause(short pause) | |
406 { | |
407 mod.output->pause(paused = pause); | |
408 } | |
409 | |
410 static void | |
411 wv_stop(void) | |
412 { | |
413 killDecodeThread = true; | |
414 if (thread_handle != 0) { | |
415 g_thread_join(thread_handle); | |
416 if (openedAudio) { | |
417 mod.output->buffer_free(); | |
418 mod.output->close_audio(); | |
419 } | |
420 openedAudio = false; | |
421 if (AudioError) | |
422 printf("Could not open Audio\n"); | |
423 } | |
424 | |
425 } | |
426 | |
427 static void | |
428 wv_load_config() | |
429 { | |
430 ConfigDb *cfg; | |
431 | |
432 cfg = bmp_cfg_db_open(); | |
433 | |
434 bmp_cfg_db_get_bool(cfg, "wavpack", "clip_prevention", | |
435 &clipPreventionEnabled); | |
436 bmp_cfg_db_get_bool(cfg, "wavpack", "album_replaygain", | |
437 &albumReplaygainEnabled); | |
438 bmp_cfg_db_get_bool(cfg, "wavpack", "dyn_bitrate", &dynBitrateEnabled); | |
439 bmp_cfg_db_get_bool(cfg, "wavpack", "replaygain", &replaygainEnabled); | |
440 bmp_cfg_db_close(cfg); | |
441 | |
442 openedAudio = false; | |
443 } |