comparison src/demac/plugin.c @ 2182:cc5e9ec110a4

Added initial version of Monkey's Audio plugin
author Eugene Zagidullin <e.asphyx@gmail.com>
date Thu, 22 Nov 2007 02:54:06 +0300
parents
children 549009824758
comparison
equal deleted inserted replaced
2181:bbb631ab78e9 2182:cc5e9ec110a4
1 /*
2 * Audacious Monkey's Audio plugin
3 *
4 * Copyright (C) Eugene Zagidullin 2007
5 *
6 * Used some code from libdemac and example application:
7 * Copyright (C) Dave Chapman 2007
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
22 *
23 */
24
25 #ifndef _XOPEN_SOURCE
26 #define _XOPEN_SOURCE
27 #endif
28
29 #include "config.h"
30
31 #include <ctype.h>
32 #include <stdio.h>
33
34 #include <stdlib.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <assert.h>
38
39 #include <glib.h>
40 #include <gtk/gtk.h>
41 #include <glib/gprintf.h>
42
43 #include <audacious/i18n.h>
44
45 #include <audacious/plugin.h>
46 #include <audacious/main.h>
47 #include <audacious/output.h>
48 #include <audacious/vfs.h>
49 #include <audacious/util.h>
50
51 #include "ape.h"
52 #include "apev2.h"
53
54 #ifndef MIN
55 #define MIN(a,b) ((a) < (b) ? (a) : (b))
56 #endif
57
58 #define MAX_BYTESPERSAMPLE 2 // currently only 16bps supported
59
60 static GThread *pb_thread;
61 static gpointer demac_decode_loop(InputPlayback *pb);
62 static Tuple *demac_get_tuple(char *filename);
63 static Tuple *demac_probe_for_tuple (gchar *uri, VFSFile *vfd);
64
65 static GMutex *demac_mutex;
66 static unsigned long seek_to_msec=(unsigned long)-1; /* -1 == not needed */
67
68 static InputPlugin demac_ip;
69
70 #ifdef DEBUG
71 # include "crc.c"
72 #endif
73
74 static gboolean demac_probe_vfs(char *filename, VFSFile* input_file) {
75 APEContext *ctx;
76
77 ctx = calloc(sizeof(APEContext), 1);
78 if(ape_read_header(ctx, input_file, 1) < 0) {
79 free(ctx);
80 aud_vfs_rewind(input_file); /* Do we really need it? */
81 return FALSE;
82 }
83
84 free(ctx);
85
86 aud_vfs_rewind(input_file); /* Do we really need it? */
87
88 return TRUE;
89 }
90
91 static void demac_play(InputPlayback *pb) {
92 pb->playing = 1;
93 pb->eof = 0;
94 pb->error = FALSE;
95 pb_thread = g_thread_self();
96 pb->set_pb_ready(pb);
97
98 demac_decode_loop(pb);
99 }
100
101 static void demac_do_mseek(APEContext *ctx, unsigned long msec) {
102 if(ctx->seektable) {
103 unsigned int framecount = msec * ((unsigned long long)ctx->totalframes - 1L) / ctx->duration;
104 ctx->currentframe = framecount;
105 }
106 }
107
108 gpointer demac_decode_loop(InputPlayback *pb) {
109 VFSFile *vfd;
110 uint8_t *frame_buf = NULL;
111 guint8 *wav = NULL;
112 int wav_buffer_size;
113 gchar *title = NULL;
114 int audio_opened = 0;
115 int playing;
116 unsigned long local_seek_to;
117 APEContext *ctx = NULL;
118 APEDecoderContext *dec = NULL;
119 int decoded_bytes;
120 int pkt_size, bytes_used;
121 #ifdef DEBUG
122 uint32_t frame_crc;
123 #endif
124
125 if ((vfd = aud_vfs_fopen(pb->filename, "r")) == NULL) {
126 #ifdef DEBUG
127 fprintf(stderr, "** demac: plugin.c: Error opening URI: %s\n", pb->filename);
128 #endif
129 pb->error = TRUE;
130 goto cleanup;
131 }
132
133 ctx = calloc(sizeof(APEContext), 1);
134 if(ape_read_header(ctx, vfd, 0) < 0) {
135 pb->error = TRUE;
136 #ifdef DEBUG
137 fprintf(stderr, "** demac: plugin.c: Cannot parse APE header or unsupported format: %s\n", pb->filename);
138 #endif
139 goto cleanup;
140 }
141
142 dec = calloc(sizeof(APEDecoderContext), 1);
143 if(ape_decode_init(dec, ctx) < 0) {
144 pb->error = TRUE;
145 #ifdef DEBUG
146 fprintf(stderr, "** demac: plugin.c: Error initializing decoder\n");
147 #endif
148 goto cleanup;
149 }
150
151 frame_buf = malloc(ctx->max_packet_size);
152
153 #ifdef DEBUG
154 fprintf(stderr, "** demac: plugin.c: Duration: %u msec\n", ctx->duration);
155 #endif
156
157 if(!pb->output->open_audio(FMT_S16_LE, ctx->samplerate, ctx->channels)) {
158 pb->error = TRUE;
159 #ifdef DEBUG
160 fprintf(stderr, "** demac: plugin.c: Cannot open audio.\n");
161 #endif
162 goto cleanup;
163 }
164
165 audio_opened = 1;
166
167 Tuple *tpl = demac_probe_for_tuple (pb->filename, vfd);
168 title = aud_tuple_formatter_make_title_string(tpl, aud_get_gentitle_format());
169 pb->set_params(pb, title, ctx->duration, -1, ctx->samplerate, ctx->channels);
170 aud_tuple_free(tpl);
171
172 /* begin decoding */
173 wav_buffer_size = ctx->blocksperframe * MAX_BYTESPERSAMPLE * ctx->channels;
174 wav = malloc(wav_buffer_size);
175
176 g_mutex_lock(demac_mutex);
177 playing = pb->playing;
178 g_mutex_unlock(demac_mutex);
179
180 while (playing && (ctx->currentframe < ctx->totalframes))
181 {
182 g_mutex_lock(demac_mutex);
183 playing = pb->playing;
184 local_seek_to = seek_to_msec;
185 g_mutex_unlock(demac_mutex);
186
187 /* do seeking */
188 if (local_seek_to != -1) {
189 demac_do_mseek(ctx, local_seek_to);
190 pb->output->flush(local_seek_to);
191
192 local_seek_to = -1;
193 g_mutex_lock(demac_mutex);
194 seek_to_msec = -1;
195 g_mutex_unlock(demac_mutex);
196
197 /* reset decoder */
198 dec->samples = 0;
199 }
200
201 ape_read_packet(ctx, vfd, frame_buf, &pkt_size);
202 #ifdef DEBUG
203 assert(pkt_size <= ctx->max_packet_size);
204 frame_crc = ape_initcrc();
205 #endif
206 bytes_used = 0;
207
208 /*#ifdef DEBUG
209 fprintf(stderr, "frame %d, %d samples, offset %d, size %d\n", ctx->currentframe-1,
210 *((uint32_t*)frame_buf), *((uint32_t*)(frame_buf+4)), pkt_size);
211 #endif*/
212
213 /* Decode the frame a chunk at a time */
214 while (playing && (bytes_used != pkt_size) && (local_seek_to == -1))
215 {
216 g_mutex_lock(demac_mutex);
217 playing = pb->playing;
218 local_seek_to = seek_to_msec;
219 g_mutex_unlock(demac_mutex);
220
221 decoded_bytes = wav_buffer_size;
222 bytes_used = ape_decode_frame(dec, wav, &decoded_bytes, frame_buf, pkt_size);
223 if(bytes_used < 0) {
224 /* skip frame */
225 dec->samples = 0;
226 break;
227 }
228
229 if(local_seek_to != -1) break;
230
231 /* Write audio data */
232 pb->pass_audio(pb, FMT_S16_LE, ctx->channels, decoded_bytes, wav, &playing);
233 #if DEBUG
234 frame_crc = ape_updatecrc(wav, decoded_bytes, frame_crc);
235 #endif
236
237 }
238
239 #if DEBUG
240 frame_crc = ape_finishcrc(frame_crc);
241
242 if (dec->CRC != frame_crc) {
243 fprintf(stderr, "** demac: plugin.c: CRC error in frame %d\n", ctx->currentframe-1);
244 }
245 #endif
246 }
247
248 cleanup:
249
250 pb->eof = TRUE;
251 pb->playing = 0;
252
253 if(title) g_free(title);
254 if(audio_opened) pb->output->close_audio();
255
256 if(dec) {ape_decode_close(dec); free(dec);}
257 if(wav) free(wav);
258 if(frame_buf) free(frame_buf);
259 if(ctx) {ape_read_close(ctx); free(ctx);}
260 if(vfd) aud_vfs_fclose(vfd);
261
262 #ifdef DEBUG
263 fprintf(stderr, "** demac: plugin.c: decoding loop finished\n");
264 #endif
265
266 return NULL;
267 }
268
269 static void demac_stop(InputPlayback *pb)
270 {
271 g_mutex_lock(demac_mutex);
272 int playing = pb->playing;
273 g_mutex_unlock(demac_mutex);
274
275 if (playing) {
276 g_mutex_lock(demac_mutex);
277 pb->playing = 0;
278 g_mutex_unlock(demac_mutex);
279 #ifdef DEBUG
280 fprintf(stderr, "** demac: plugin.c: waiting for thread finished\n");
281 #endif
282 //g_thread_join(pb->thread);
283 /* pb->thread is useless if input plugin initialized from **terrible** cue-sheet plugin */
284 g_thread_join(pb_thread);
285 #ifdef DEBUG
286 fprintf(stderr, "** demac: plugin.c: thread finished\n");
287 #endif
288 }
289 }
290
291 static void demac_pause(InputPlayback *pb, short paused) {
292 pb->output->pause(paused);
293 }
294
295 Tuple *demac_probe_for_tuple (gchar *uri, VFSFile *vfd) {
296 #ifdef DEBUG
297 fprintf(stderr, "** demac: plugin.c: demac_probe_for_tuple()\n");
298 #endif
299 Tuple *tpl = aud_tuple_new_from_filename(uri);
300 gchar codec_string[32];
301
302 GHashTable *tag = NULL;
303 gchar *item;
304 if ((tag = parse_apev2_tag(vfd)) != NULL) {
305 if((item = g_hash_table_lookup (tag, "Artist")) != NULL) aud_tuple_associate_string(tpl, FIELD_ARTIST, NULL, item);
306 if((item = g_hash_table_lookup (tag, "Title")) != NULL) aud_tuple_associate_string(tpl, FIELD_TITLE, NULL, item);
307 if((item = g_hash_table_lookup (tag, "Album")) != NULL) aud_tuple_associate_string(tpl, FIELD_ALBUM, NULL, item);
308 if((item = g_hash_table_lookup (tag, "Comment")) != NULL) aud_tuple_associate_string(tpl, FIELD_COMMENT, NULL, item);
309 if((item = g_hash_table_lookup (tag, "Genre")) != NULL) aud_tuple_associate_string(tpl, FIELD_GENRE, NULL, item);
310 if((item = g_hash_table_lookup (tag, "Track")) != NULL) aud_tuple_associate_int(tpl, FIELD_TRACK_NUMBER, NULL, atoi(item));
311 if((item = g_hash_table_lookup (tag, "Year")) != NULL) aud_tuple_associate_int(tpl, FIELD_YEAR, NULL, atoi(item));
312 }
313
314 APEContext *ctx = calloc(sizeof(APEContext), 1);
315 aud_vfs_rewind(vfd);
316 ape_read_header(ctx, vfd, 1);
317 aud_tuple_associate_int(tpl, FIELD_LENGTH, NULL, ctx->duration);
318 ape_read_close(ctx);
319 free(ctx);
320
321 if (tag) g_hash_table_remove_all(tag);
322
323 g_sprintf(codec_string, "Monkey's Audio v%4.2f", (float)ctx->fileversion/1000.0);
324 #ifdef DEBUG
325 fprintf(stderr, "** demac: plugin.c: Codec: %s\n", codec_string);
326 #endif
327 aud_tuple_associate_string(tpl, FIELD_CODEC, NULL, codec_string);
328 aud_tuple_associate_string(tpl, FIELD_QUALITY, NULL, "lossless");
329 return tpl;
330 }
331
332 Tuple *demac_get_tuple(char *filename) {
333 #ifdef DEBUG
334 fprintf(stderr, "** demac: plugin.c: demac_get_tuple()\n");
335 #endif
336 VFSFile *vfd;
337
338 if ((vfd = aud_vfs_fopen(filename, "r")) == NULL) {
339 return NULL;
340 }
341
342 Tuple *tpl = demac_probe_for_tuple(filename, vfd);
343 aud_vfs_fclose(vfd);
344 return tpl;
345 }
346
347 static void demac_mseek (InputPlayback *pb, gulong millisecond) {
348 g_mutex_lock(demac_mutex);
349 seek_to_msec = millisecond;
350 g_mutex_unlock(demac_mutex);
351 #ifdef DEBUG
352 fprintf(stderr, "** demac: plugin.c: seeking to %u msec\n", millisecond);
353 #endif
354 }
355
356 static void demac_seek (InputPlayback *pb, gint time) {
357 demac_mseek(pb, (unsigned long)time*1000);
358 }
359
360 static void demac_init() {
361 /* Found in Freedesktop shared-mime-info */
362 aud_mime_set_plugin("audio/x-ape", &demac_ip);
363 demac_mutex = g_mutex_new();
364 }
365
366 static void demac_cleanup() {
367 g_mutex_free(demac_mutex);
368 }
369
370 static void demac_about(void) {
371 static GtkWidget *about_window = NULL;
372
373 if (about_window)
374 gdk_window_raise(about_window->window);
375 else {
376 about_window = audacious_info_dialog(_("About Monkey's Audio Plugin"),
377 _("Copyright (C) 2007 Eugene Zagidullin <e.asphyx@gmail.com>\n"
378 "Based on ffape decoder, Copyright (C) 2007 Benjamin Zores\n"
379 "ffape itself based on libdemac by Dave Chapman\n\n"
380 "ffape is a part of FFmpeg project, http://ffmpeg.mplayerhq.hu/"),
381 _("Ok"), FALSE, NULL, NULL);
382 g_signal_connect(G_OBJECT(about_window), "destroy",
383 G_CALLBACK(gtk_widget_destroyed), &about_window);
384 }
385 }
386
387 static gchar *fmts[] = { "ape", NULL };
388
389 static InputPlugin demac_ip = {
390 .description = "Monkey's Audio Plugin",
391 .init = demac_init,
392 .about = demac_about,
393 .play_file = demac_play,
394 .stop = demac_stop,
395 .pause = demac_pause,
396 .seek = demac_seek,
397 .cleanup = demac_cleanup,
398 .get_song_tuple = demac_get_tuple,
399 .is_our_file_from_vfs = demac_probe_vfs,
400 .vfs_extensions = fmts,
401 .mseek = demac_mseek,
402 .probe_for_tuple = demac_probe_for_tuple,
403 };
404
405 InputPlugin *demac_iplist[] = { &demac_ip, NULL };
406 DECLARE_PLUGIN(demac, NULL, NULL, demac_iplist, NULL, NULL, NULL, NULL, NULL);
407
408 /* vim:foldmethod=syntax: */