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 }