Mercurial > audlegacy-plugins
view src/madplug/input.c @ 2284:d19b53359b24
cleaned up the sndfile wav plugin, currently limiting it ONLY TO WAV
PLAYBACK. if somebody is more experienced with it and wants to restore
the other formats, go ahead (maybe change the name of the plugin too?).
author | mf0102 <0102@gmx.at> |
---|---|
date | Wed, 09 Jan 2008 15:41:22 +0100 |
parents | d25cd7e7eddb |
children | 1457b35713d9 |
line wrap: on
line source
/* * 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, VFSFile *fd) { AUDDBG("f: input_init\n"); memset(info, 0, sizeof(struct mad_info_t)); // all fields are cleared to 0 --yaz 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->filename = g_strdup(url); // from input_read_replaygain() info->replaygain_album_scale = -1; info->replaygain_track_scale = -1; info->mp3gain_undo = -77; info->mp3gain_minmax = -77; if(!fd){ info->infile = aud_vfs_fopen(info->filename, "rb"); if (info->infile == NULL) { return FALSE; } } else{ AUDDBG("input_init: aud_vfs_dup\n"); info->infile = aud_vfs_dup(fd); } // obtain file size info->size = aud_vfs_fsize(info->infile); info->remote = info->size == 0 ? TRUE : FALSE; //proxy connection may result in non-zero size. if(audmad_is_remote((gchar *)url)) info->remote = TRUE; info->fileinfo_request = FALSE; AUDDBG("i: info->size = %lu\n", (long unsigned int)info->size); AUDDBG("e: input_init\n"); 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; size_t string_len = 0; gboolean is_num = TRUE; if(!string) return NULL; string_len = mad_ucs4len((id3_ucs4_t *)string); tail = (id3_ucs4_t *)string + string_len; if(BYTES(string_len + 1) > 1024) { ret = g_malloc0(BYTES(string_len + 1)); } else { ret = g_malloc0(1024); } 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 ')' 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); 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) { AUDDBG("is_num!\n"); 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); AUDDBG("genre length = %d\n", mad_ucs4len(genre)); g_free(tmp); tmp = NULL; tmp_len = mad_ucs4len(genre); memcpy(ret + BYTES(ret_len), genre, BYTES(tmp_len)); ret_len += tmp_len; *(ret + ret_len) = 0; //terminate } else { // plain text AUDDBG("plain!\n"); AUDDBG("ret_len = %d\n", ret_len); AUDDBG("end - ptr = %d\n", BYTES(end - ptr)); 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, const gchar *frame_name) { gchar *rtn0 = NULL, *rtn = NULL; const id3_ucs4_t *string_const = NULL; id3_ucs4_t *string = NULL; struct id3_frame *frame; union id3_field *field; int encoding = -1; frame = id3_tag_findframe(tag, frame_name, 0); if (!frame) return NULL; field = id3_frame_field(frame, 0); encoding = id3_field_gettextencoding(field); if (!strcmp(frame_name, ID3_FRAME_COMMENT)) field = id3_frame_field(frame, 3); else field = id3_frame_field(frame, 1); if (!field) return NULL; if (!strcmp(frame_name, ID3_FRAME_COMMENT)) string_const = id3_field_getfullstring(field); else string_const = id3_field_getstrings(field, 0); if (!string_const) return NULL; if (!strcmp(frame_name, ID3_FRAME_GENRE)) { string = mad_parse_genre(string_const); } else { string = mad_ucs4dup((id3_ucs4_t *)string_const); } if (!string) return NULL; switch (encoding) { case ID3_FIELD_TEXTENCODING_ISO_8859_1: rtn0 = (gchar *)id3_ucs4_latin1duplicate(string); rtn = aud_str_to_utf8(rtn0); g_free(rtn0); break; case ID3_FIELD_TEXTENCODING_UTF_8: default: rtn = (gchar *)id3_ucs4_utf8duplicate(string); break; } g_free((void *)string); AUDDBG("i: string = %s\n", rtn); return rtn; } static void input_set_and_free_tag(struct id3_tag *tag, Tuple *tuple, const gchar *frame, const gint nfield) { gchar *scratch = input_id3_get_string(tag, frame); aud_tuple_associate_string(tuple, nfield, NULL, scratch); aud_tuple_associate_string(tuple, -1, frame, scratch); g_free(scratch); } static void input_alloc_tag(struct mad_info_t *info) { Tuple *tuple; if (info->tuple == NULL) { tuple = aud_tuple_new(); info->tuple = tuple; aud_tuple_associate_int(info->tuple, FIELD_LENGTH, NULL, -1); } } /** * read the ID3 tag */ static void input_read_tag(struct mad_info_t *info) { gchar *string = NULL; Tuple *tuple; glong curpos = 0; AUDDBG("f: input_read_tag\n"); if (info->tuple != NULL) aud_tuple_free(info->tuple); tuple = aud_tuple_new_from_filename(info->filename); info->tuple = tuple; if(info->infile) { curpos = aud_vfs_ftell(info->infile); info->id3file = id3_file_vfsopen(info->infile, ID3_FILE_MODE_READONLY); } else { info->id3file = id3_file_open(info->filename, ID3_FILE_MODE_READONLY); } if (!info->id3file) { AUDDBG("read_tag: no id3file\n"); return; } info->tag = id3_file_tag(info->id3file); if (!info->tag) { AUDDBG("read_tag: no tag\n"); return; } input_set_and_free_tag(info->tag, tuple, ID3_FRAME_ARTIST, FIELD_ARTIST); input_set_and_free_tag(info->tag, tuple, ID3_FRAME_TITLE, FIELD_TITLE); input_set_and_free_tag(info->tag, tuple, ID3_FRAME_ALBUM, FIELD_ALBUM); input_set_and_free_tag(info->tag, tuple, ID3_FRAME_GENRE, FIELD_GENRE); input_set_and_free_tag(info->tag, tuple, ID3_FRAME_COMMENT, FIELD_COMMENT); string = input_id3_get_string(info->tag, ID3_FRAME_TRACK); if (string) { aud_tuple_associate_int(tuple, FIELD_TRACK_NUMBER, NULL, 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) { aud_tuple_associate_int(tuple, FIELD_YEAR, NULL, atoi(string)); g_free(string); string = NULL; } // length string = input_id3_get_string(info->tag, "TLEN"); if (string) { aud_tuple_associate_int(tuple, FIELD_LENGTH, NULL, atoi(string)); AUDDBG("input_read_tag: TLEN = %d\n", atoi(string)); g_free(string); string = NULL; } else aud_tuple_associate_int(tuple, FIELD_LENGTH, NULL, -1); aud_tuple_associate_string(tuple, FIELD_CODEC, NULL, "MPEG Audio (MP3)"); aud_tuple_associate_string(tuple, FIELD_QUALITY, NULL, "lossy"); info->title = aud_tuple_formatter_make_title_string(tuple, audmad_config.title_override == TRUE ? audmad_config.id3_format : aud_get_gentitle_format()); // for connection via proxy, we have to stop transfer once. I can't explain the reason. if (info->infile != NULL) { aud_vfs_fseek(info->infile, -1, SEEK_SET); // an impossible request aud_vfs_fseek(info->infile, curpos, SEEK_SET); } AUDDBG("e: input_read_tag\n"); } void input_process_remote_metadata(struct mad_info_t *info) { gboolean metadata = FALSE; if(info->remote && mad_timer_count(info->duration, MAD_UNITS_SECONDS) <= 0){ gchar *tmp = NULL; #ifdef DEBUG_INTENSIVELY AUDDBG("process_remote_meta\n"); #endif g_free(info->title); info->title = NULL; aud_tuple_disassociate(info->tuple, FIELD_TITLE, NULL); aud_tuple_disassociate(info->tuple, FIELD_ALBUM, NULL); tmp = aud_vfs_get_metadata(info->infile, "track-name"); if(tmp){ metadata = TRUE; gchar *scratch; scratch = aud_str_to_utf8(tmp); aud_tuple_associate_string(info->tuple, FIELD_TITLE, NULL, scratch); g_free(scratch); g_free(tmp); tmp = NULL; } tmp = aud_vfs_get_metadata(info->infile, "stream-name"); if(tmp){ metadata = TRUE; gchar *scratch; scratch = aud_str_to_utf8(tmp); aud_tuple_associate_string(info->tuple, FIELD_ALBUM, NULL, scratch); aud_tuple_associate_string(info->tuple, -1, "stream", scratch); g_free(scratch); g_free(tmp); tmp = NULL; } if (metadata) tmp = aud_tuple_formatter_process_string(info->tuple, "${?title:${title}}${?stream: (${stream})}"); else { gchar *realfn = g_filename_from_uri(info->filename, NULL, NULL); gchar *tmp2 = g_path_get_basename(realfn ? realfn : info->filename); // info->filename is uri. --yaz tmp = aud_str_to_utf8(tmp2); g_free(tmp2); tmp2 = NULL; g_free(realfn); realfn = NULL; // tmp = g_strdup(g_basename(info->filename)); //XXX maybe ok. --yaz } /* call set_info only if tmp is different from prev_tmp */ if ( ( ( info->prev_title != NULL ) && ( strcmp(info->prev_title,tmp) ) ) || ( info->prev_title == NULL ) ) { info->playback->set_params(info->playback, tmp, -1, // indicate the stream is unseekable info->bitrate, info->freq, info->channels); if (info->prev_title) g_free(info->prev_title); info->prev_title = g_strdup(tmp); } g_free(tmp); } } /** * 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 AUD_DEBUG gchar *tmp = g_filename_to_utf8(info->filename, -1, NULL, NULL, NULL); AUDDBG("f: input_get_info: %s, fast_scan = %s\n", tmp, fast_scan ? "TRUE" : "FALSE"); g_free(tmp); #endif /* DEBUG */ input_alloc_tag(info); input_read_tag(info); if(!info->remote) { // reduce startup delay read_replaygain(info); } /* scan mp3 file, decoding headers */ if (scan_file(info, fast_scan) == FALSE) { AUDDBG("input_get_info: scan_file failed\n"); return FALSE; } /* reset the input file to the start */ aud_vfs_fseek(info->infile, 0, SEEK_SET); info->offset = 0; /* use the filename for the title as a last resort */ if (!info->title) { char *pos = strrchr(info->filename, DIR_SEPARATOR); //XXX info->filename is uri. --yaz if (pos) info->title = g_strdup(pos + 1); else info->title = g_strdup(info->filename); //XXX info->filename is uri. --yaz } AUDDBG("e: input_get_info\n"); 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 */ // this function may be called before info->playback initialized. int input_get_data(struct mad_info_t *info, guchar * buffer, int buffer_size) { int len = 0; #ifdef DEBUG_INTENSIVELY AUDDBG ("f: input_get_data: %d\n", buffer_size); #endif /* simply read to data from the file */ len = aud_vfs_fread(buffer, 1, buffer_size, info->infile); //aud_vfs_fread returns num of elements. if(len == 0 && info->playback){ info->playback->eof = TRUE; } #ifdef DEBUG_INTENSIVELY AUDDBG ("e: input_get_data: size=%d offset=%d\n", len, info->offset); #endif info->offset += len; return len; } /** * Free up all mad_info_t related resourses. */ gboolean input_term(struct mad_info_t * info) { AUDDBG("f: input_term\n"); if (info->title) g_free(info->title); if (info->url) g_free(info->url); if (info->filename) g_free(info->filename); if (info->infile) aud_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) { aud_tuple_free(info->tuple); info->tuple = NULL; } if (info->prev_title) g_free(info->prev_title); /* set everything to zero in case it gets used again. */ memset(info, 0, sizeof(struct mad_info_t)); AUDDBG("e: input_term\n"); return TRUE; }