Mercurial > audlegacy-plugins
diff src/madplug/input.c @ 610:862190d39e00 trunk
[svn] - add madplug. It is not yet hooked up, I'll do that later.
| author | nenolod |
|---|---|
| date | Mon, 05 Feb 2007 12:28:01 -0800 |
| parents | |
| children | 3f7a52adfe0e |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug/input.c Mon Feb 05 12:28:01 2007 -0800 @@ -0,0 +1,518 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa + * + * Portions derived from xmms-mad: + * Copyright (C) 2001-2002 Sam Clegg - See COPYING + * + * 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; under version 2 of the License. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_ASSERT_H +#include <assert.h> +#endif /* HAVE_ASSERT_H */ + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif /* HAVE_SYS_TYPES_H */ + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif /* HAVE_SYS_SOCKET_H */ + +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif /* HAVE_NETINET_IN_H */ + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif /* HAVE_ARPA_INET_H */ + +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif /* HAVE_NETDB_H */ + +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif /* HAVE_SYS_STAT_H */ + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif /* HAVE_SYS_TIME_H */ + +#include <fcntl.h> +#include <errno.h> +#include <audacious/util.h> + +#include "input.h" +#include "replaygain.h" + +#define DIR_SEPARATOR '/' +#define HEADER_SIZE 256 +#define LINE_LENGTH 256 + +extern gboolean scan_file(struct mad_info_t *info, gboolean fast); + +/** + * init the mad_info_t struct. + */ +gboolean input_init(struct mad_info_t * info, const char *url) +{ +#ifdef DEBUG + g_message("f: input_init"); +#endif + memset(info, 0, sizeof(struct mad_info_t)); + + info->fmt = FMT_S16_LE; + info->channels = -1; + info->mpeg_layer = -1; + info->size = -1; + info->freq = -1; + info->seek = -1; + info->duration = mad_timer_zero; + info->pos = mad_timer_zero; + info->url = g_strdup(url); + info->current_frame = 0; + info->frames = 0; + info->bitrate = 0; + info->vbr = 0; + info->mode = 0; + info->title = 0; + info->offset = 0; + + info->replaygain_album_str = 0; + info->replaygain_track_str = 0; + info->replaygain_album_peak_str = 0; + info->replaygain_track_peak_str = 0; + info->mp3gain_undo_str = 0; + info->mp3gain_minmax_str = 0; + + info->tuple = NULL; + info->filename = g_strdup(url); + + info->infile = vfs_fopen(info->filename, "rb"); + if (info->infile == NULL) + return FALSE; + + // obtain file size + vfs_fseek(info->infile, 0, SEEK_END); + info->size = vfs_ftell(info->infile); + vfs_fseek(info->infile, 0, SEEK_SET); + info->remote = info->size == 0 ? TRUE : FALSE; + +#ifdef DEBUG + g_message("i: info->size == %lu", info->size); + g_message("e: input_init"); +#endif + return TRUE; +} + +/* return length in letters */ +size_t mad_ucs4len(id3_ucs4_t *ucs) +{ + id3_ucs4_t *ptr = ucs; + size_t len = 0; + + while(*ptr++ != 0) + len++; + + return len; +} + +/* duplicate id3_ucs4_t string. new string will be terminated with 0. */ +id3_ucs4_t *mad_ucs4dup(id3_ucs4_t *org) +{ + id3_ucs4_t *new = NULL; + size_t len = mad_ucs4len(org); + + new = g_malloc0((len + 1) * sizeof(id3_ucs4_t)); + memcpy(new, org, len * sizeof(id3_ucs4_t)); + *(new + len) = 0; //terminate + + return new; +} + +#define BYTES(x) ((x) * sizeof(id3_ucs4_t)) + +id3_ucs4_t *mad_parse_genre(const id3_ucs4_t *string) +{ + id3_ucs4_t *ret = NULL; + id3_ucs4_t *tmp = NULL; + id3_ucs4_t *genre = NULL; + id3_ucs4_t *ptr, *end, *tail, *tp; + size_t ret_len = 0; //num of ucs4 char! + size_t tmp_len = 0; + gboolean is_num = TRUE; + + tail = (id3_ucs4_t *)string + mad_ucs4len((id3_ucs4_t *)string); + + ret = g_malloc0(1024); // realloc() is too picky + + for(ptr = (id3_ucs4_t *)string; *ptr != 0 && ptr <= tail; ptr++) { + if(*ptr == '(') { + if(*(++ptr) == '(') { // escaped text like: ((something) + for(end = ptr; *end != ')' && *end != 0;) { // copy "(something)" + end++; + } + end++; //include trailing ')' +// ret = g_realloc(ret, BYTES(end - ptr + 1)); + memcpy(ret, ptr, BYTES(end - ptr)); + ret_len += (end - ptr); + *(ret + ret_len) = 0; //terminate + ptr = end + 1; + } + else { + // reference to an id3v1 genre code + for(end = ptr; *end != ')' && *end != 0;) { + end++; + } + + tmp = g_malloc0(BYTES(end - ptr + 1)); + memcpy(tmp, ptr, BYTES(end - ptr)); + *(tmp + (end - ptr)) = 0; //terminate + ptr += end - ptr; + + genre = (id3_ucs4_t *)id3_genre_name((const id3_ucs4_t *)tmp); + + g_free(tmp); + tmp = NULL; + + tmp_len = mad_ucs4len(genre); + +// ret = g_realloc(ret, BYTES(ret_len + tmp_len + 1)); + memcpy(ret + BYTES(ret_len), genre, BYTES(tmp_len)); + + ret_len += tmp_len; + *(ret + ret_len) = 0; //terminate + } + } + else { + for(end = ptr; *end != '(' && *end != 0; ) { + end++; + } + // scan string to determine whether a genre code number or not + tp = ptr; + is_num = TRUE; + while(tp < end) { + if(*tp < '0' || *tp > '9') { // anything else than number appears. + is_num = FALSE; + break; + } + tp++; + } + if(is_num) { +#ifdef DEBUG + printf("is_num!\n"); +#endif + tmp = g_malloc0(BYTES(end - ptr + 1)); + memcpy(tmp, ptr, BYTES(end - ptr)); + *(tmp + (end - ptr)) = 0; //terminate + ptr += end - ptr; + + genre = (id3_ucs4_t *)id3_genre_name((const id3_ucs4_t *)tmp); +#ifdef DEBUG + printf("genre length = %d\n", mad_ucs4len(genre)); +#endif + g_free(tmp); + tmp = NULL; + + tmp_len = mad_ucs4len(genre); + +// ret = g_realloc(ret, BYTES(ret_len + tmp_len + 1)); + memcpy(ret + BYTES(ret_len), genre, BYTES(tmp_len)); + + ret_len += tmp_len; + *(ret + ret_len) = 0; //terminate + } + else { // plain text +// ret = g_realloc(ret, BYTES(end - ptr + 1)); +#ifdef DEBUG + printf("plain!\n"); + printf("ret_len = %d\n", ret_len); +#endif + memcpy(ret + BYTES(ret_len), ptr, BYTES(end - ptr)); + ret_len = ret_len + (end - ptr); + *(ret + ret_len) = 0; //terminate + ptr += (end - ptr); + } + } + } + return ret; +} + + +gchar *input_id3_get_string(struct id3_tag * tag, char *frame_name) +{ + gchar *rtn; + const id3_ucs4_t *string_const; + id3_ucs4_t *string; + struct id3_frame *frame; + union id3_field *field; + + frame = id3_tag_findframe(tag, frame_name, 0); + if (!frame) + return NULL; + + if (frame_name == ID3_FRAME_COMMENT) + field = id3_frame_field(frame, 3); + else + field = id3_frame_field(frame, 1); + + if (!field) + return NULL; + + if (frame_name == ID3_FRAME_COMMENT) + string_const = id3_field_getfullstring(field); + else + string_const = id3_field_getstrings(field, 0); + + if (!string_const) + return NULL; + + string = mad_ucs4dup((id3_ucs4_t *)string_const); + + if (frame_name == ID3_FRAME_GENRE) { + id3_ucs4_t *string2 = NULL; + string2 = mad_parse_genre(string); + g_free((void *)string); + string = string2; + } + + { + id3_utf8_t *string2 = id3_ucs4_utf8duplicate(string); + rtn = str_to_utf8(string2); + g_free(string2); + } + +#ifdef DEBUG + g_print("i: string = %s\n", rtn); +#endif + return rtn; +} + +/** + * read the ID3 tag + */ +static void input_read_tag(struct mad_info_t *info) +{ + gchar *string = NULL; + TitleInput *title_input; + + if (info->tuple == NULL) { + title_input = bmp_title_input_new(); + info->tuple = title_input; + } + else + title_input = info->tuple; + + info->id3file = id3_file_open(info->filename, ID3_FILE_MODE_READONLY); + if (!info->id3file) { + return; + } + + info->tag = id3_file_tag(info->id3file); + if (!info->tag) { + return; + } + + title_input->performer = + input_id3_get_string(info->tag, ID3_FRAME_ARTIST); + title_input->track_name = + input_id3_get_string(info->tag, ID3_FRAME_TITLE); + title_input->album_name = + input_id3_get_string(info->tag, ID3_FRAME_ALBUM); + title_input->genre = input_id3_get_string(info->tag, ID3_FRAME_GENRE); + title_input->comment = + input_id3_get_string(info->tag, ID3_FRAME_COMMENT); + string = input_id3_get_string(info->tag, ID3_FRAME_TRACK); + if (string) { + title_input->track_number = atoi(string); + g_free(string); + string = NULL; + } + // year + string = NULL; + string = input_id3_get_string(info->tag, ID3_FRAME_YEAR); //TDRC + if (!string) + string = input_id3_get_string(info->tag, "TYER"); + + if (string) { + title_input->year = atoi(string); + g_free(string); + string = NULL; + } + + title_input->file_name = g_strdup(g_basename(info->filename)); + title_input->file_path = g_path_get_dirname(info->filename); + if ((string = strrchr(title_input->file_name, '.'))) { + title_input->file_ext = string + 1; + *string = '\0'; // make filename end at dot. + } + + info->title = xmms_get_titlestring(audmad_config.title_override == TRUE ? + audmad_config.id3_format : xmms_get_gentitle_format(), title_input); +} + +/** + * Retrieve meta-information about URL. + * For local files this means ID3 tag etc. + */ +gboolean input_get_info(struct mad_info_t *info, gboolean fast_scan) +{ +#ifdef DEBUG + g_message("f: input_get_info: %s", info->title); +#endif /* DEBUG */ + input_read_tag(info); + input_read_replaygain(info); + + /* scan mp3 file, decoding headers unless fast_scan is set */ + if (scan_file(info, fast_scan) == FALSE) + return FALSE; + + /* reset the input file to the start */ + vfs_rewind(info->infile); + info->offset = 0; + + if (info->remote) + { + gchar *stream_name = vfs_get_metadata(info->infile, "stream-name"); + gchar *track_name = vfs_get_metadata(info->infile, "track-name"); + gchar *tmp = NULL; + + g_free(info->title); + g_free(info->tuple->track_name); + g_free(info->tuple->album_name); + + info->title = g_strdup(track_name); + info->tuple->track_name = g_strdup(track_name); + info->tuple->album_name = g_strdup(stream_name); + tmp = g_strdup_printf("%s (%s)", track_name, stream_name); + mad_plugin->set_info(tmp, + -1, // indicates the stream is unseekable + info->bitrate, info->freq, info->channels); + g_free(tmp); g_free(stream_name); g_free(track_name); + } + + /* use the filename for the title as a last resort */ + if (!info->title) + { + char *pos = strrchr(info->filename, DIR_SEPARATOR); + if (pos) + info->title = g_strdup(pos + 1); + else + info->title = g_strdup(info->filename); + } + +#ifdef DEBUG + g_message("e: input_get_info"); +#endif /* DEBUG */ + return TRUE; +} + + +/** + * Read data from the source given my madinfo into the buffer + * provided. Return the number of bytes read. + * @return 0 on EOF + * @return -1 on error + */ +int +input_get_data(struct mad_info_t *madinfo, guchar * buffer, + int buffer_size) +{ + int len = 0; +#ifdef DEBUG +// g_message ("f: input_get_data: %d", buffer_size); +#endif + + /* simply read to data from the file */ + len = vfs_fread(buffer, 1, buffer_size, madinfo->infile); + + if (len == 0 && madinfo->playback) + madinfo->playback->eof = TRUE; + + if (madinfo->remote) + { + gchar *stream_name = vfs_get_metadata(madinfo->infile, "stream-name"); + gchar *track_name = vfs_get_metadata(madinfo->infile, "track-name"); + gchar *tmp = NULL; + + g_free(madinfo->title); + g_free(madinfo->tuple->track_name); + g_free(madinfo->tuple->album_name); + + madinfo->title = g_strdup(track_name); + madinfo->tuple->track_name = g_strdup(track_name); + madinfo->tuple->album_name = g_strdup(stream_name); + tmp = g_strdup_printf("%s (%s)", track_name, stream_name); + mad_plugin->set_info(tmp, + -1, // indicates the stream is unseekable + madinfo->bitrate, madinfo->freq, madinfo->channels); + g_free(tmp); g_free(stream_name); g_free(track_name); + } + +#ifdef DEBUG +// g_message ("e: input_get_data: size=%d offset=%d", len, madinfo->offset); +#endif + madinfo->offset += len; + return len; +} + +/** + * Free up all mad_info_t related resourses. + */ +gboolean input_term(struct mad_info_t * info) +{ +#ifdef DEBUG + g_message("f: input_term"); +#endif + + if (info->title) + g_free(info->title); + if (info->url) + g_free(info->url); + if (info->filename) + g_free(info->filename); + if (info->infile) + vfs_fclose(info->infile); + if (info->id3file) + id3_file_close(info->id3file); + + if (info->replaygain_album_str) + g_free(info->replaygain_album_str); + if (info->replaygain_track_str) + g_free(info->replaygain_track_str); + if (info->replaygain_album_peak_str) + g_free(info->replaygain_album_peak_str); + if (info->replaygain_track_peak_str) + g_free(info->replaygain_track_peak_str); + if (info->mp3gain_undo_str) + g_free(info->mp3gain_undo_str); + if (info->mp3gain_minmax_str) + g_free(info->mp3gain_minmax_str); + + if (info->tuple) { + bmp_title_input_free(info->tuple); + info->tuple = NULL; + } + + /* set everything to zero in case it gets used again. */ + memset(info, 0, sizeof(struct mad_info_t)); +#ifdef DEBUG + g_message("e: input_term"); +#endif + return TRUE; +}
