#include #include #include #include #include #include #include "qxmms.h" #define INPUT_BUFFER_SIZE 16*1024 #define GENRE_MAX 0x94 #define MAD_AVERAGE_FRAMES 10 #define DO_FAST_CALC 1 #define XING_MAGIC ( ('X' << 24) | ('i' << 16) | ('n' << 8) | 'g' ) #define EF_VBR (1 << 0) struct xing { long flags; /* valid fields (see below) */ unsigned long frames; /* total number of frames */ unsigned long bytes; /* total number of bytes */ unsigned char toc[100]; /* 100-point seek table */ long scale; /* ?? */ }; enum { XING_FRAMES = 0x00000001L, XING_BYTES = 0x00000002L, XING_TOC = 0x00000004L, XING_SCALE = 0x00000008L }; enum { FT_MP3, FT_OGG, FT_FLAC, FT_MP4, FT_MAX }; static short xing_parse(struct xing *xing, struct mad_bitptr ptr, unsigned short bitlen) { if (bitlen < 64 || mad_bit_read(&ptr, 32) != XING_MAGIC) goto fail; xing->flags = mad_bit_read(&ptr, 32); bitlen -= 64; if (xing->flags & XING_FRAMES) { if (bitlen < 32) goto fail; xing->frames = mad_bit_read(&ptr, 32); bitlen -= 32; } if (xing->flags & XING_BYTES) { if (bitlen < 32) goto fail; xing->bytes = mad_bit_read(&ptr, 32); bitlen -= 32; } if (xing->flags & XING_TOC) { register unsigned short i; if (bitlen < 800) goto fail; for (i = 0; i < 100; ++i) xing->toc[i] = mad_bit_read(&ptr, 8); bitlen -= 800; } if (xing->flags & XING_SCALE) { if (bitlen < 32) goto fail; xing->scale = mad_bit_read(&ptr, 32); bitlen -= 32; } return 0; fail: xing->flags = 0; return -1; } char *strrev( char *s ) { char *p1, *p2; char c; if( s != NULL ) { p1 = s; p2 = s + strlen(s) - 1; while( p1 < p2 ) { c = *p1; *p1 = *p2; *p2 = c; p1++; p2--; } } return s; } void _splitpath(const char* path, char* drv, char* dir, char* name, char* ext) { const char* end; /* end of processed string */ const char* p; /* search pointer */ const char* s; /* copy pointer */ /* extract drive name */ if (path[0] && path[1]==':') { if (drv) { *drv++ = *path++; *drv++ = *path++; *drv = '\0'; } } else if (drv) *drv = '\0'; /* search for end of string or stream separator */ for(end=path; *end && *end!=':'; ) end++; /* search for begin of file extension */ for(p=end; p>path && *--p!='\\' && *p!='/'; ) if (*p == '.') { end = p; break; } if (ext) for(s=end; (*ext=*s++); ) ext++; /* search for end of directory name */ for(p=end; p>path; ) if (*--p=='\\' || *p=='/') { p++; break; } if (name) { for(s=p; s 0) { mad_stream_skip(&madstream, tagsize); continue; } } continue; } frames++; /* count the number of frames for average length calculation */ mad_timer_add(&madduration, madheader.duration); /* sum frame duration */ data_used += madstream.next_frame - madstream.this_frame; if (frames == 1) { /* the first frame should give us pretty much everything we need, unless... */ freq = madheader.samplerate; bitrate = madheader.bitrate; /* ...we have a VBR file. See if it has XING headers first */ madframe.header = madheader; if (mad_frame_decode(&madframe, &madstream) == -1) { if (!MAD_RECOVERABLE(madstream.error)) { /* TODO traitement eventuel erreur recoverable */ printf("DBG: maderror unrecoverable frame decode on %s", filename); break; } } if (xing_parse(&xing, madstream.anc_ptr, madstream.anc_bitlen) == 0) { /* found xing header */ has_xing = 1; flags |= EF_VBR; /* Get the total number of frames and find out the average bitrate */ frames = xing.frames; mad_timer_multiply(&madduration, frames); bitrate = 8 * xing.bytes / mad_timer_count(madduration, MAD_UNITS_SECONDS); break; } } else { /* Maybe we have a VBR file without xing header */ if (bitrate != madheader.bitrate) flags |= EF_VBR; if ((flags & EF_VBR)) bitrate += madheader.bitrate; /* we'll do an average, XXX overflow when no FAST_CALC? */ } /* We can either scan the whole file to get precise duration & bitrate, or just check a few frames and guess the whole story, if DO_FAST_CALC is set */ if (DO_FAST_CALC && frames >= MAD_AVERAGE_FRAMES) { float frame_size = ((double)data_used) / MAD_AVERAGE_FRAMES; frames = (size - tagsize) / frame_size; madduration.seconds /= MAD_AVERAGE_FRAMES; madduration.fraction /= MAD_AVERAGE_FRAMES; mad_timer_multiply(&madduration, frames); break; } } if (madstream.error != MAD_ERROR_BUFLEN) break; } if ((flags & EF_VBR) && !has_xing) bitrate = bitrate / frames * 1000; if (!length) /* remember, length can sometimes be encoded in ID3 tags */ length = mad_timer_count(madduration, MAD_UNITS_SECONDS); /* MAD subsystem exit */ mad_frame_finish(&madframe); mad_header_finish(&madheader); mad_stream_finish(&madstream); /* convert the bitrate to bytes */ // bitrate *= 1.024; printf("@["); printf( (flags & EF_VBR) ? "VBR~%ld" : "CBR=%ld", bitrate / 1000); printf("kb/s]"); fclose(in); } } int qxmms_process(int argc, char **args) { if(!qxmms_test()) { printf("XMMS does not seem to be running."); } //. query xmms. qxmms(&q); char dir[FILENAME_MAX]; _splitpath(q.file, NULL, dir, NULL, NULL); int n = 0; char *p; if((p = strchr(dir, '/')) != NULL) { n++; while( (p = strchr(++p, '/')) != NULL) n++; } dir[strlen(dir) - 1] = '\0'; // remove trailing '/' if(n > 1) { strrev(dir); *strchr(dir, '/') = '\0'; strrev(dir); } id3_utf8_t *utf8; struct id3_tag *tag; struct id3_file *file = id3_file_open(q.file, ID3_FILE_MODE_READONLY); if(file) { if( (tag = id3_file_tag(file)) && tag->frames) { struct id3_frame *frame; if ((utf8 = utf8_id3tag_findframe(tag, ID3_FRAME_GENRE, 0))) { char *p = (char *)utf8; printf("[%s]", p); free(utf8); } printf ("[%s]", dir); if ((utf8 = utf8_id3tag_findframe(tag, ID3_FRAME_ARTIST, 0))) { char *p = (char *)utf8; printf("[%s -", p); free(utf8); } if ((utf8 = utf8_id3tag_findframe(tag, ID3_FRAME_TITLE, 0))) { char *p = (char *)utf8; printf(" %s]", p); free(utf8); } } id3_file_close(file); go_mad(q.file); qxmms_print_time(); } } void qxmms_play() { xmms_remote_play(XMMS_SESSION); } void qxmms_stop() { xmms_remote_stop(XMMS_SESSION); } void qxmms_pause() { xmms_remote_pause(XMMS_SESSION); } void qxmms_next() { xmms_remote_playlist_next(XMMS_SESSION); } void qxmms_prev() { xmms_remote_playlist_prev(XMMS_SESSION); } void qxmms_addfile(char *string) { xmms_remote_playlist_add_url_string(XMMS_SESSION,string); } void qxmms_set_volume(char *value) { int vol,intval; xmms_remote_get_volume(XMMS_SESSION, &vol, &vol); if(!strcmp("down",value)) { intval = vol - 5; } else if(!strcmp("up",value)) { intval = vol + 5; } else { intval = atoi(value); } xmms_remote_set_volume(0, intval, intval); } void qxmms_jump_pos(char *charvalue) { int value = atoi(charvalue); xmms_remote_set_playlist_pos(XMMS_SESSION,(value - 1)); } void qxmms_seek(char *input) { char *subcmd; int cmdlen = strlen(input); int curtime = xmms_remote_get_output_time(XMMS_SESSION); int inc = 5; int min,sec,newtime; if(input) { if(!strncmp(input,"back",4)) { subcmd = strstr(input," "); if(subcmd) { subcmd++; inc = atoi(subcmd); } xmms_remote_jump_to_time(XMMS_SESSION,curtime - (inc*1000)); } else if(!strncmp(input,"forward",7)) { subcmd = strstr(input," "); if(subcmd) { subcmd++; inc = atoi(subcmd); } xmms_remote_jump_to_time(XMMS_SESSION,curtime + (inc*1000)); } else { strtok(input,":"); subcmd = &input[strlen(input) + 1]; xmms_remote_jump_to_time(XMMS_SESSION,(((atoi(input) * 60) + atoi(subcmd)) * 1000)); } } } void qxmms_skin(char *input) { xmms_remote_set_skin(XMMS_SESSION, input); }