All pastes #497195 Raw Edit

Stuff

public text v1 · immutable
·published 2007-05-19 18:07 UTC
rendered paste body
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
#include <id3tag.h>
#include <mad.h>
#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<end; )
			*name++ = *s++;

		*name = '\0';
	}

	if (dir) {
		for(s=path; s<p; )
			*dir++ = *s++;

		*dir = '\0';
	}
}

id3_utf8_t *utf8_id3tag_findframe(struct id3_tag *tag, const char *const frameid,
         unsigned short index)
 {
         const struct id3_frame  *frame = NULL;
         const union id3_field   *field = NULL;
         const id3_ucs4_t        *ucs4 = NULL;
         unsigned int            nstrings, i, mp3gid;
 
         if ((frame = id3_tag_findframe (tag, frameid, index))) {
                 field = id3_frame_field(frame, 1);
                 nstrings = id3_field_getnstrings(field);
                 for (i = 0; i < nstrings; ++i) {
                         if ((ucs4 = id3_field_getstrings(field, i))) {
                                 if (strcmp(frameid, ID3_FRAME_GENRE) == 0) {
                                         /* Some encoders encaps id nb in brackets and the
                                         lib doesn't handle this properly. This is ugly */
                                         if ((ucs4[0] == '(') && (isdigit(ucs4[1]))) {
                                                 mp3gid = id3_ucs4_getnumber(ucs4+1);
                                                 ucs4 = id3_genre_index(mp3gid);
                                         }
                                         else
                                                 ucs4 = id3_genre_name(ucs4);
                                 }
                                 return(id3_ucs4_utf8duplicate(ucs4));
                                 /* id3_ucs4_utf8duplicate() allocates memory.
                                 Don't forget to free() after the function call. */
                         }
                 }
         }
         return NULL;
 }



int qxmms_test() {
	if(xmms_remote_get_version(XMMS_SESSION)) {
		return 1;
	} else {
		return 0;
	}
}


void go_mad(const char *filename) {

	FILE *in = fopen(filename, "r");

	if(in) {
		struct stat             filestat;
	 
	         struct mad_stream       madstream;
	         struct mad_header       madheader;
	         struct mad_frame        madframe;
	         struct xing             xing = { .frames = 0, .bytes = 0 };
	         
	         unsigned char           madinput_buffer[INPUT_BUFFER_SIZE];
	         size_t                  madread_size, remaining;
	         mad_timer_t             madduration = mad_timer_zero;   /* total play time */
	 
	         unsigned short          has_xing = 0;
	         unsigned long           tagsize = 0, data_used = 0, frames = 0;
	
	
		signed char filetype = 0;
		unsigned char flags = 0;
		unsigned long size = 0, mtime = 0, bitrate = 0;
		unsigned short freq = 0, length = 0;
		
	
		fstat(fileno(in), &filestat);

		filetype = FT_MP3;
		flags &= ~EF_VBR;
		 
		size = filestat.st_size;
		mtime = filestat.st_mtime;

		/* Now it's time for mad operations to find out the bitrate, length & samplerate! */
                 /* initialisation of the MAD subsystem */
                 mad_stream_init(&madstream);
                 mad_header_init(&madheader);
                 mad_frame_init(&madframe);
 
                 while (1) {
                         /* find out how much we have to read from the file */
                         remaining = madstream.bufend - madstream.next_frame;
                         memcpy(madinput_buffer, madstream.this_frame, remaining);
                         madread_size = fread(madinput_buffer + remaining, 1, INPUT_BUFFER_SIZE - remaining, in);
 
                         if (madread_size <= 0) {
                                 printf("DBG: maderror madread_size <= 0 on %s", filename);
                                 break;
                                 /* TODO traitement eventuel erreur lecture */
                         }
 
                         mad_stream_buffer(&madstream, madinput_buffer, madread_size + remaining);
 
                         while (1) {
                                 if (mad_header_decode(&madheader, &madstream) == -1) {
                                         if (madstream.error == MAD_ERROR_BUFLEN)
                                                 break;
                                         if (!MAD_RECOVERABLE(madstream.error)) {
                                                 /* TODO traitement eventuel erreur recoverable */
                                                 printf("DBG: maderror unrecoverable header decode on %s", filename);
                                                 break;
                                         }
                                         if (madstream.error == MAD_ERROR_LOSTSYNC) {
                                                 /* ignore LOSTSYNC due to ID3 tags */
                                                 tagsize = id3_tag_query(madstream.this_frame, madstream.bufend - madstream.this_frame);
                                                 if (tagsize > 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);
}