Mercurial > audlegacy-plugins
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/demac/plugin.c Thu Nov 22 02:54:06 2007 +0300 @@ -0,0 +1,408 @@ +/* + * Audacious Monkey's Audio plugin + * + * Copyright (C) Eugene Zagidullin 2007 + * + * Used some code from libdemac and example application: + * Copyright (C) Dave Chapman 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA + * + */ + +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE +#endif + +#include "config.h" + +#include <ctype.h> +#include <stdio.h> + +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <assert.h> + +#include <glib.h> +#include <gtk/gtk.h> +#include <glib/gprintf.h> + +#include <audacious/i18n.h> + +#include <audacious/plugin.h> +#include <audacious/main.h> +#include <audacious/output.h> +#include <audacious/vfs.h> +#include <audacious/util.h> + +#include "ape.h" +#include "apev2.h" + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#define MAX_BYTESPERSAMPLE 2 // currently only 16bps supported + +static GThread *pb_thread; +static gpointer demac_decode_loop(InputPlayback *pb); +static Tuple *demac_get_tuple(char *filename); +static Tuple *demac_probe_for_tuple (gchar *uri, VFSFile *vfd); + +static GMutex *demac_mutex; +static unsigned long seek_to_msec=(unsigned long)-1; /* -1 == not needed */ + +static InputPlugin demac_ip; + +#ifdef DEBUG +# include "crc.c" +#endif + +static gboolean demac_probe_vfs(char *filename, VFSFile* input_file) { + APEContext *ctx; + + ctx = calloc(sizeof(APEContext), 1); + if(ape_read_header(ctx, input_file, 1) < 0) { + free(ctx); + aud_vfs_rewind(input_file); /* Do we really need it? */ + return FALSE; + } + + free(ctx); + + aud_vfs_rewind(input_file); /* Do we really need it? */ + + return TRUE; +} + +static void demac_play(InputPlayback *pb) { + pb->playing = 1; + pb->eof = 0; + pb->error = FALSE; + pb_thread = g_thread_self(); + pb->set_pb_ready(pb); + + demac_decode_loop(pb); +} + +static void demac_do_mseek(APEContext *ctx, unsigned long msec) { + if(ctx->seektable) { + unsigned int framecount = msec * ((unsigned long long)ctx->totalframes - 1L) / ctx->duration; + ctx->currentframe = framecount; + } +} + +gpointer demac_decode_loop(InputPlayback *pb) { + VFSFile *vfd; + uint8_t *frame_buf = NULL; + guint8 *wav = NULL; + int wav_buffer_size; + gchar *title = NULL; + int audio_opened = 0; + int playing; + unsigned long local_seek_to; + APEContext *ctx = NULL; + APEDecoderContext *dec = NULL; + int decoded_bytes; + int pkt_size, bytes_used; +#ifdef DEBUG + uint32_t frame_crc; +#endif + + if ((vfd = aud_vfs_fopen(pb->filename, "r")) == NULL) { +#ifdef DEBUG + fprintf(stderr, "** demac: plugin.c: Error opening URI: %s\n", pb->filename); +#endif + pb->error = TRUE; + goto cleanup; + } + + ctx = calloc(sizeof(APEContext), 1); + if(ape_read_header(ctx, vfd, 0) < 0) { + pb->error = TRUE; +#ifdef DEBUG + fprintf(stderr, "** demac: plugin.c: Cannot parse APE header or unsupported format: %s\n", pb->filename); +#endif + goto cleanup; + } + + dec = calloc(sizeof(APEDecoderContext), 1); + if(ape_decode_init(dec, ctx) < 0) { + pb->error = TRUE; +#ifdef DEBUG + fprintf(stderr, "** demac: plugin.c: Error initializing decoder\n"); +#endif + goto cleanup; + } + + frame_buf = malloc(ctx->max_packet_size); + +#ifdef DEBUG + fprintf(stderr, "** demac: plugin.c: Duration: %u msec\n", ctx->duration); +#endif + + if(!pb->output->open_audio(FMT_S16_LE, ctx->samplerate, ctx->channels)) { + pb->error = TRUE; +#ifdef DEBUG + fprintf(stderr, "** demac: plugin.c: Cannot open audio.\n"); +#endif + goto cleanup; + } + + audio_opened = 1; + + Tuple *tpl = demac_probe_for_tuple (pb->filename, vfd); + title = aud_tuple_formatter_make_title_string(tpl, aud_get_gentitle_format()); + pb->set_params(pb, title, ctx->duration, -1, ctx->samplerate, ctx->channels); + aud_tuple_free(tpl); + + /* begin decoding */ + wav_buffer_size = ctx->blocksperframe * MAX_BYTESPERSAMPLE * ctx->channels; + wav = malloc(wav_buffer_size); + + g_mutex_lock(demac_mutex); + playing = pb->playing; + g_mutex_unlock(demac_mutex); + + while (playing && (ctx->currentframe < ctx->totalframes)) + { + g_mutex_lock(demac_mutex); + playing = pb->playing; + local_seek_to = seek_to_msec; + g_mutex_unlock(demac_mutex); + + /* do seeking */ + if (local_seek_to != -1) { + demac_do_mseek(ctx, local_seek_to); + pb->output->flush(local_seek_to); + + local_seek_to = -1; + g_mutex_lock(demac_mutex); + seek_to_msec = -1; + g_mutex_unlock(demac_mutex); + + /* reset decoder */ + dec->samples = 0; + } + + ape_read_packet(ctx, vfd, frame_buf, &pkt_size); +#ifdef DEBUG + assert(pkt_size <= ctx->max_packet_size); + frame_crc = ape_initcrc(); +#endif + bytes_used = 0; + +/*#ifdef DEBUG + fprintf(stderr, "frame %d, %d samples, offset %d, size %d\n", ctx->currentframe-1, + *((uint32_t*)frame_buf), *((uint32_t*)(frame_buf+4)), pkt_size); +#endif*/ + + /* Decode the frame a chunk at a time */ + while (playing && (bytes_used != pkt_size) && (local_seek_to == -1)) + { + g_mutex_lock(demac_mutex); + playing = pb->playing; + local_seek_to = seek_to_msec; + g_mutex_unlock(demac_mutex); + + decoded_bytes = wav_buffer_size; + bytes_used = ape_decode_frame(dec, wav, &decoded_bytes, frame_buf, pkt_size); + if(bytes_used < 0) { + /* skip frame */ + dec->samples = 0; + break; + } + + if(local_seek_to != -1) break; + + /* Write audio data */ + pb->pass_audio(pb, FMT_S16_LE, ctx->channels, decoded_bytes, wav, &playing); +#if DEBUG + frame_crc = ape_updatecrc(wav, decoded_bytes, frame_crc); +#endif + + } + +#if DEBUG + frame_crc = ape_finishcrc(frame_crc); + + if (dec->CRC != frame_crc) { + fprintf(stderr, "** demac: plugin.c: CRC error in frame %d\n", ctx->currentframe-1); + } +#endif + } + +cleanup: + + pb->eof = TRUE; + pb->playing = 0; + + if(title) g_free(title); + if(audio_opened) pb->output->close_audio(); + + if(dec) {ape_decode_close(dec); free(dec);} + if(wav) free(wav); + if(frame_buf) free(frame_buf); + if(ctx) {ape_read_close(ctx); free(ctx);} + if(vfd) aud_vfs_fclose(vfd); + +#ifdef DEBUG + fprintf(stderr, "** demac: plugin.c: decoding loop finished\n"); +#endif + + return NULL; +} + +static void demac_stop(InputPlayback *pb) +{ + g_mutex_lock(demac_mutex); + int playing = pb->playing; + g_mutex_unlock(demac_mutex); + + if (playing) { + g_mutex_lock(demac_mutex); + pb->playing = 0; + g_mutex_unlock(demac_mutex); +#ifdef DEBUG + fprintf(stderr, "** demac: plugin.c: waiting for thread finished\n"); +#endif + //g_thread_join(pb->thread); + /* pb->thread is useless if input plugin initialized from **terrible** cue-sheet plugin */ + g_thread_join(pb_thread); +#ifdef DEBUG + fprintf(stderr, "** demac: plugin.c: thread finished\n"); +#endif + } +} + +static void demac_pause(InputPlayback *pb, short paused) { + pb->output->pause(paused); +} + +Tuple *demac_probe_for_tuple (gchar *uri, VFSFile *vfd) { +#ifdef DEBUG + fprintf(stderr, "** demac: plugin.c: demac_probe_for_tuple()\n"); +#endif + Tuple *tpl = aud_tuple_new_from_filename(uri); + gchar codec_string[32]; + + GHashTable *tag = NULL; + gchar *item; + if ((tag = parse_apev2_tag(vfd)) != NULL) { + if((item = g_hash_table_lookup (tag, "Artist")) != NULL) aud_tuple_associate_string(tpl, FIELD_ARTIST, NULL, item); + if((item = g_hash_table_lookup (tag, "Title")) != NULL) aud_tuple_associate_string(tpl, FIELD_TITLE, NULL, item); + if((item = g_hash_table_lookup (tag, "Album")) != NULL) aud_tuple_associate_string(tpl, FIELD_ALBUM, NULL, item); + if((item = g_hash_table_lookup (tag, "Comment")) != NULL) aud_tuple_associate_string(tpl, FIELD_COMMENT, NULL, item); + if((item = g_hash_table_lookup (tag, "Genre")) != NULL) aud_tuple_associate_string(tpl, FIELD_GENRE, NULL, item); + if((item = g_hash_table_lookup (tag, "Track")) != NULL) aud_tuple_associate_int(tpl, FIELD_TRACK_NUMBER, NULL, atoi(item)); + if((item = g_hash_table_lookup (tag, "Year")) != NULL) aud_tuple_associate_int(tpl, FIELD_YEAR, NULL, atoi(item)); + } + + APEContext *ctx = calloc(sizeof(APEContext), 1); + aud_vfs_rewind(vfd); + ape_read_header(ctx, vfd, 1); + aud_tuple_associate_int(tpl, FIELD_LENGTH, NULL, ctx->duration); + ape_read_close(ctx); + free(ctx); + + if (tag) g_hash_table_remove_all(tag); + + g_sprintf(codec_string, "Monkey's Audio v%4.2f", (float)ctx->fileversion/1000.0); +#ifdef DEBUG + fprintf(stderr, "** demac: plugin.c: Codec: %s\n", codec_string); +#endif + aud_tuple_associate_string(tpl, FIELD_CODEC, NULL, codec_string); + aud_tuple_associate_string(tpl, FIELD_QUALITY, NULL, "lossless"); + return tpl; +} + +Tuple *demac_get_tuple(char *filename) { +#ifdef DEBUG + fprintf(stderr, "** demac: plugin.c: demac_get_tuple()\n"); +#endif + VFSFile *vfd; + + if ((vfd = aud_vfs_fopen(filename, "r")) == NULL) { + return NULL; + } + + Tuple *tpl = demac_probe_for_tuple(filename, vfd); + aud_vfs_fclose(vfd); + return tpl; +} + +static void demac_mseek (InputPlayback *pb, gulong millisecond) { + g_mutex_lock(demac_mutex); + seek_to_msec = millisecond; + g_mutex_unlock(demac_mutex); +#ifdef DEBUG + fprintf(stderr, "** demac: plugin.c: seeking to %u msec\n", millisecond); +#endif +} + +static void demac_seek (InputPlayback *pb, gint time) { + demac_mseek(pb, (unsigned long)time*1000); +} + +static void demac_init() { + /* Found in Freedesktop shared-mime-info */ + aud_mime_set_plugin("audio/x-ape", &demac_ip); + demac_mutex = g_mutex_new(); +} + +static void demac_cleanup() { + g_mutex_free(demac_mutex); +} + +static void demac_about(void) { + static GtkWidget *about_window = NULL; + + if (about_window) + gdk_window_raise(about_window->window); + else { + about_window = audacious_info_dialog(_("About Monkey's Audio Plugin"), + _("Copyright (C) 2007 Eugene Zagidullin <e.asphyx@gmail.com>\n" + "Based on ffape decoder, Copyright (C) 2007 Benjamin Zores\n" + "ffape itself based on libdemac by Dave Chapman\n\n" + "ffape is a part of FFmpeg project, http://ffmpeg.mplayerhq.hu/"), + _("Ok"), FALSE, NULL, NULL); + g_signal_connect(G_OBJECT(about_window), "destroy", + G_CALLBACK(gtk_widget_destroyed), &about_window); + } +} + +static gchar *fmts[] = { "ape", NULL }; + +static InputPlugin demac_ip = { + .description = "Monkey's Audio Plugin", + .init = demac_init, + .about = demac_about, + .play_file = demac_play, + .stop = demac_stop, + .pause = demac_pause, + .seek = demac_seek, + .cleanup = demac_cleanup, + .get_song_tuple = demac_get_tuple, + .is_our_file_from_vfs = demac_probe_vfs, + .vfs_extensions = fmts, + .mseek = demac_mseek, + .probe_for_tuple = demac_probe_for_tuple, +}; + +InputPlugin *demac_iplist[] = { &demac_ip, NULL }; +DECLARE_PLUGIN(demac, NULL, NULL, demac_iplist, NULL, NULL, NULL, NULL, NULL); + + /* vim:foldmethod=syntax: */
