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