Mercurial > audlegacy-plugins
diff src/Input/cue/cuesheet.c @ 0:13389e613d67 trunk
[svn] - initial import of audacious-plugins tree (lots to do)
| author | nenolod |
|---|---|
| date | Mon, 18 Sep 2006 01:11:49 -0700 |
| parents | |
| children | 54f9e753b511 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Input/cue/cuesheet.c Mon Sep 18 01:11:49 2006 -0700 @@ -0,0 +1,448 @@ +/* Audacious: An advanced media player. + * cuesheet.c: Support cuesheets as a media container. + * + * Copyright (C) 2006 William Pitcock <nenolod -at- nenolod.net>. + * + * This file was hacked out of of xmms-cueinfo, + * Copyright (C) 2003 Oskar Liljeblad + * + * This software is copyrighted work licensed under the terms of the + * GNU General Public License. Please consult the file "COPYING" for + * details. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <audacious/plugin.h> +#include <audacious/output.h> +#include <audacious/playlist.h> +#include <libaudacious/vfs.h> + +#define MAX_CUE_LINE_LENGTH 1000 +#define MAX_CUE_TRACKS 1000 + +static void cache_cue_file(gchar *f); +static void free_cue_info(void); +static void fix_cue_argument(char *line); +static gboolean is_our_file(gchar *filespec); +static void play(gchar *uri); +static void play_cue_uri(gchar *uri); +static gint get_time(void); +static void seek(gint time); +static void stop(void); +static TitleInput *get_tuple(gchar *uri); +static TitleInput *get_tuple_uri(gchar *uri); + +static gint watchdog_func(gpointer unused); + +static gchar *cue_performer = NULL; +static gchar *cue_title = NULL; +static gchar *cue_file = NULL; +static gint last_cue_track = 0; +static gint cur_cue_track = 0; +static struct { + gchar *performer; + gchar *title; + gint index; +} cue_tracks[MAX_CUE_TRACKS]; +static gint timeout_tag = 0; +static gint finetune_seek = 0; + +static InputPlugin *real_ip = NULL; + +InputPlugin cue_ip = +{ + NULL, /* handle */ + NULL, /* filename */ + NULL, /* description */ + NULL, /* init */ + NULL, /* about */ + NULL, /* configure */ + is_our_file, + NULL, /* audio cd */ + play, + stop, + NULL, + seek, + NULL, /* set eq */ + get_time, + NULL, + NULL, + NULL, /* cleanup */ + NULL, + NULL, + NULL, + NULL, + NULL, /* XXX get_song_info iface */ + NULL, + NULL, + get_tuple, + NULL +}; + +static gboolean is_our_file(gchar *filename) +{ + gchar *ext; + gboolean ret = FALSE; + + /* is it a cue:// URI? */ + if (!strncasecmp(filename, "cue://", 6)) + return TRUE; + + ext = strrchr(filename, '.'); + + if(!ext) + return FALSE; + + if (!strncasecmp(ext, ".cue", 4)) + { + gint i; + ret = -1; + + /* add the files, build cue urls, etc. */ + cache_cue_file(filename); + + for (i = 0; i < last_cue_track; i++) + { + gchar _buf[65535]; + + g_snprintf(_buf, 65535, "cue://%s?%d", filename, i); + playlist_add_url(_buf); + } + + free_cue_info(); + } + + return ret; +} + +static gint get_time(void) +{ + return get_output_time(); +} + +static void play(gchar *uri) +{ + /* this isn't a cue:// uri? */ + if (strncasecmp("cue://", uri, 6)) + { + gchar *tmp = g_strdup_printf("cue://%s?0", uri); + play_cue_uri(tmp); + g_free(tmp); + return; + } + + play_cue_uri(uri); +} + +static TitleInput *get_tuple(gchar *uri) +{ + TitleInput *ret; + + /* this isn't a cue:// uri? */ + if (strncasecmp("cue://", uri, 6)) + { + gchar *tmp = g_strdup_printf("cue://%s?0", uri); + ret = get_tuple_uri(tmp); + g_free(tmp); + return ret; + } + + return get_tuple_uri(uri); +} + +static TitleInput *get_tuple_uri(gchar *uri) +{ + gchar *path2 = g_strdup(uri + 6); + gchar *_path = strchr(path2, '?'); + gint track = 0; + InputPlugin *dec; + TitleInput *phys_tuple, *out; + + if (_path != NULL && *_path == '?') + { + *_path = '\0'; + _path++; + track = atoi(_path); + } + + cache_cue_file(path2); + + dec = input_check_file(cue_file, FALSE); + + if (dec == NULL) + return NULL; + + phys_tuple = dec->get_song_tuple(cue_file); + + out = bmp_title_input_new(); + + out->genre = g_strdup(phys_tuple->genre); + out->album_name = g_strdup(phys_tuple->album_name); + out->file_path = g_strdup(phys_tuple->file_path); + out->file_name = g_strdup(phys_tuple->file_name); + out->file_ext = g_strdup(phys_tuple->file_ext); + out->length = phys_tuple->length; + + bmp_title_input_free(phys_tuple); + + out->track_name = g_strdup(cue_tracks[track].title); + out->performer = g_strdup(cue_tracks[track].performer); + + return out; +} + +static void seek(gint time) +{ + if (real_ip != NULL) + real_ip->seek(time); +} + +static void stop(void) +{ + if (real_ip != NULL) + real_ip->stop(); + + gtk_timeout_remove(timeout_tag); + free_cue_info(); + + real_ip->set_info = cue_ip.set_info; + real_ip->output = NULL; + real_ip = NULL; +} + +static void set_info_override(gchar * unused, gint length, gint rate, gint freq, gint nch) +{ + gchar *title; + + /* annoying. */ + if (playlist_position->tuple == NULL) + { + gint pos = playlist_get_position(); + playlist_get_tuple(pos); + } + + title = g_strdup(playlist_position->title); + + cue_ip.set_info(title, length, rate, freq, nch); +} + +static void play_cue_uri(gchar *uri) +{ + gchar *path2 = g_strdup(uri + 6); + gchar *_path = strchr(path2, '?'); + gint track = 0; + + if (_path != NULL && *_path == '?') + { + *_path = '\0'; + _path++; + track = atoi(_path); + } + + cache_cue_file(path2); + + real_ip = input_check_file(cue_file, FALSE); + + if (real_ip != NULL) + { + real_ip->set_info = set_info_override; + real_ip->output = cue_ip.output; + real_ip->play_file(cue_file); + real_ip->seek(finetune_seek ? finetune_seek / 1000 : cue_tracks[track].index / 1000); + } + + finetune_seek = 0; + + cur_cue_track = track; + + timeout_tag = gtk_timeout_add(100, watchdog_func, NULL); +} + +InputPlugin *get_iplugin_info(void) +{ + cue_ip.description = g_strdup_printf("Cuesheet Container Plugin"); + return &cue_ip; +} + +/******************************************************* watchdog */ + +/* + * This is fairly hard to explain. + * + * Basically we loop until we have reached the correct track. + * Then we set a finetune adjustment to make sure we stay in the + * right place. + * + * I used to recurse here (it was prettier), but that didn't work + * as well as I was hoping. + * + * Anyhow, yeah. The logic here isn't great, but it works, so I'm + * cool with it. + * + * - nenolod + */ +static gint watchdog_func(gpointer unused) +{ + gint time = get_output_time(); + gboolean dir = FALSE; + + while (time < cue_tracks[cur_cue_track].index) + { + cur_cue_track--; + if (!(time < cue_tracks[cur_cue_track].index)) + finetune_seek = time; + playlist_prev(); + dir = TRUE; + time = get_output_time(); + g_usleep(10000); + } + + while (dir == FALSE && cur_cue_track != last_cue_track && (time > cue_tracks[cur_cue_track + 1].index)) + { + cur_cue_track++; + if (!(time > cue_tracks[cur_cue_track].index)) + finetune_seek = time; + playlist_next(); + time = get_output_time(); + g_usleep(10000); + } + + return TRUE; +} + +/******************************************************** cuefile */ + +static void free_cue_info(void) +{ + g_free(cue_performer); + cue_performer = NULL; + g_free(cue_title); + cue_title = NULL; + g_free(cue_file); + cue_file = NULL; + for (; last_cue_track > 0; last_cue_track--) { + g_free(cue_tracks[last_cue_track-1].performer); + g_free(cue_tracks[last_cue_track-1].title); + } +} + +static void cache_cue_file(char *f) +{ + VFSFile *file = vfs_fopen(f, "rb"); + gchar line[MAX_CUE_LINE_LENGTH+1]; + + if(!file) + return; + + while (TRUE) { + gint p; + gint q; + + if (vfs_fgets(line, MAX_CUE_LINE_LENGTH+1, file) == NULL) + return; + + for (p = 0; line[p] && isspace((int) line[p]); p++); + if (!line[p]) + continue; + for (q = p; line[q] && !isspace((int) line[q]); q++); + if (!line[q]) + continue; + line[q] = '\0'; + for (q++; line[q] && isspace((int) line[q]); q++); + + if (strcasecmp(line+p, "PERFORMER") == 0) { + fix_cue_argument(line+q); + if (last_cue_track == 0) { + if (!g_utf8_validate(line + q, -1, NULL)) { + cue_performer = g_locale_to_utf8 (line + q, -1, NULL, NULL, NULL); + } else + cue_performer = g_strdup(line+q); + } else { + if (!g_utf8_validate(line + q, -1, NULL)) { + cue_tracks[last_cue_track-1].performer = g_locale_to_utf8 (line + q, -1, NULL, NULL, NULL); + } else + cue_tracks[last_cue_track-1].performer = g_strdup(line+q); + } + } + else if (strcasecmp(line+p, "FILE") == 0) { + gchar *tmp = g_path_get_dirname(f); + fix_cue_argument(line+q); + cue_file = g_strdup_printf("%s/%s", tmp, line+q); /* XXX: yaz might need to UTF validate this?? -nenolod */ + g_free(tmp); + } + else if (strcasecmp(line+p, "TITLE") == 0) { + fix_cue_argument(line+q); + if (last_cue_track == 0) { + if (!g_utf8_validate(line + q, -1, NULL)) { + cue_title = g_locale_to_utf8 (line + q, -1, NULL, NULL, NULL); + } else + cue_title = g_strdup(line+q); + } else { + if (!g_utf8_validate(line + q, -1, NULL)) { + cue_tracks[last_cue_track-1].title = g_locale_to_utf8 (line + q, -1, NULL, NULL, NULL); + } else + cue_tracks[last_cue_track-1].title = g_strdup(line+q); + } + } + else if (strcasecmp(line+p, "TRACK") == 0) { + gint track; + + fix_cue_argument(line+q); + for (p = q; line[p] && isdigit((int) line[p]); p++); + line[p] = '\0'; + for (; line[q] && line[q] == '0'; q++); + if (!line[q]) + continue; + track = atoi(line+q); + if (track >= MAX_CUE_TRACKS) + continue; + last_cue_track = track; + cue_tracks[last_cue_track-1].index = 0; + cue_tracks[last_cue_track-1].performer = NULL; + cue_tracks[last_cue_track-1].title = NULL; + } + else if (strcasecmp(line+p, "INDEX") == 0) { + for (p = q; line[p] && !isspace((int) line[p]); p++); + if (!line[p]) + continue; + for (p++; line[p] && isspace((int) line[p]); p++); + for (q = p; line[q] && !isspace((int) line[q]); q++); + if (q-p >= 8 && line[p+2] == ':' && line[p+5] == ':') { + cue_tracks[last_cue_track-1].index = + ((line[p+0]-'0')*10 + (line[p+1]-'0')) * 60000 + + ((line[p+3]-'0')*10 + (line[p+4]-'0')) * 1000 + + ((line[p+6]-'0')*10 + (line[p+7]-'0')) * 10; + } + } + } + + vfs_fclose(file); +} + +static void fix_cue_argument(char *line) +{ + if (line[0] == '"') { + gchar *l2; + for (l2 = line+1; *l2 && *l2 != '"'; l2++) + *(l2-1) = *l2; + *(l2-1) = *l2; + for (; *line && *line != '"'; line++) { + if (*line == '\\' && *(line+1)) { + for (l2 = line+1; *l2 && *l2 != '"'; l2++) + *(l2-1) = *l2; + *(l2-1) = *l2; + } + } + *line = '\0'; + } + else { + for (; *line && *line != '\r' && *line != '\n'; line++); + *line = '\0'; + } +}
