summaryrefslogtreecommitdiffstats
path: root/mpg123_artsplugin/mpg123/common.c
diff options
context:
space:
mode:
Diffstat (limited to 'mpg123_artsplugin/mpg123/common.c')
-rw-r--r--mpg123_artsplugin/mpg123/common.c921
1 files changed, 921 insertions, 0 deletions
diff --git a/mpg123_artsplugin/mpg123/common.c b/mpg123_artsplugin/mpg123/common.c
new file mode 100644
index 00000000..65d658fd
--- /dev/null
+++ b/mpg123_artsplugin/mpg123/common.c
@@ -0,0 +1,921 @@
+/* GPL clean */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef WIN32
+#include <time.h>
+#else
+#include <sys/time.h>
+#endif
+
+#include <fcntl.h>
+
+#ifdef READ_MMAP
+#include <sys/mman.h>
+#ifndef MAP_FAILED
+#define MAP_FAILED ( (void *) -1 )
+#endif
+#endif
+
+#include "mpg123.h"
+#include "genre.h"
+#include "common.h"
+
+int tabsel_123[2][3][16] = {
+ { {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,},
+ {0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,},
+ {0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,} },
+
+ { {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,},
+ {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,},
+ {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,} }
+};
+
+long freqs[9] = { 44100, 48000, 32000, 22050, 24000, 16000 , 11025 , 12000 , 8000 };
+
+struct bitstream_info bsi;
+
+static int bsbufend[2]= { 0,0 };
+static int bsbufold_end;
+static unsigned char bsspace[2][MAXFRAMESIZE+512]; /* MAXFRAMESIZE */
+static unsigned char *bsbuf=bsspace[1],*bsbufold;
+static int bsnum=0;
+
+static int skip_riff(struct reader *);
+static int skip_new_id3(struct reader *);
+
+unsigned char *pcm_sample;
+int pcm_point = 0;
+int audiobufsize = AUDIOBUFSIZE;
+
+static int decode_header(struct frame *fr,unsigned long newhead);
+
+void safewrite(int fd, const void *buf, size_t count) {
+ int donesofar = 0;
+ while(donesofar < count) {
+ int retval;
+ char *p = (char*) buf + donesofar;
+ retval = write(fd,(void*) p,(count-donesofar));
+ if(retval == -1) {
+ if((errno != EINTR) && (errno != EAGAIN))
+ exit(fprintf(stderr,"exception on output!\n"));
+ } else
+ donesofar += retval;
+ }
+}
+
+void audio_flush(int outmode, struct audio_info_struct *ai)
+{
+ if (pcm_point) {
+ switch (outmode) {
+#ifndef NO_DECODE_FILE
+ case DECODE_FILE:
+ safewrite (OutputDescriptor, pcm_sample, pcm_point);
+ break;
+#endif
+#ifndef NO_DECODE_AUDIO
+ case DECODE_AUDIO:
+ audio_play_samples (ai, pcm_sample, pcm_point);
+ break;
+#endif
+#ifndef NOXFERMEM
+ case DECODE_BUFFER:
+ safewrite (buffer_fd[1], pcm_sample, pcm_point);
+ break;
+#endif
+#ifndef NO_DECODE_WAV
+ case DECODE_WAV:
+ case DECODE_CDR:
+ case DECODE_AU:
+ wav_write(pcm_sample, pcm_point);
+ break;
+#endif
+ default:
+ break;
+ }
+ pcm_point = 0;
+ }
+}
+
+#if !defined(WIN32) && !defined(GENERIC)
+void (*catchsignal(int signum, void(*handler)()))()
+{
+ struct sigaction new_sa;
+ struct sigaction old_sa;
+
+#ifdef DONT_CATCH_SIGNALS
+ printf ("Not catching any signals.\n");
+ return ((void (*)()) -1);
+#endif
+
+ new_sa.sa_handler = handler;
+ sigemptyset(&new_sa.sa_mask);
+ new_sa.sa_flags = 0;
+ if (sigaction(signum, &new_sa, &old_sa) == -1)
+ return ((void (*)()) -1);
+ return (old_sa.sa_handler);
+}
+#endif
+
+void read_frame_init (struct frame *fr)
+{
+ fr->firsthead = 0;
+ fr->thishead = 0;
+ fr->freeformatsize = 0;
+}
+
+int head_check(unsigned long head)
+{
+ if( (head & 0xffe00000) != 0xffe00000)
+ return FALSE;
+ if(!((head>>17)&3))
+ return FALSE;
+ if( ((head>>12)&0xf) == 0xf)
+ return FALSE;
+ if( ((head>>10)&0x3) == 0x3 )
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * return 0: EOF or other stream error
+ * -1: giving up
+ * 1: synched
+ */
+#define MAX_INPUT_FRAMESIZE 1920
+#define SYNC_HEAD_MASK 0xffff0000
+#define SYNC_HEAD_MASK_FF 0x0000f000
+#define LOOK_AHEAD_NUM 3
+#define SCAN_LENGTH 16384
+
+#define CHECK_FOR_RIFF 0x0001
+#define CHECK_FOR_ID3_V1 0x0002
+#define CHECK_FOR_ID3_V2 0x0004
+
+int sync_stream(struct reader *rds,struct frame *fr,int flags,int *skipped)
+{
+ int i,j,l,ret;
+ unsigned long firsthead,nexthead;
+ struct frame frameInfo,nextInfo;
+ unsigned char dummybuf[MAX_INPUT_FRAMESIZE];
+ int found=0;
+ int freeformatsize=0;
+
+ for(i=0;i<SCAN_LENGTH;i++) {
+
+ readers_mark_pos(rds); /* store our current position */
+
+ if(!rds->head_read(rds,&firsthead))
+ return 0;
+
+ /* first a few simple checks */
+ if( !head_check(firsthead) || !decode_header(&frameInfo,firsthead) ) {
+
+ /* Check for RIFF Headers */
+ if( (flags & CHECK_FOR_RIFF) && firsthead == ('R'<<24)+('I'<<16)+('F'<<8)+'F') {
+ fprintf(stderr,"Found RIFF Header\n");
+ ret = skip_riff(rds);
+ if(ret > 0) { /* RIFF was OK continue with next byte */
+ *skipped += ret+4;
+ continue;
+ }
+ if(ret == 0)
+ return 0;
+ }
+
+ /* Check for old ID3 Header (or better Footer ;) */
+ if( (flags & CHECK_FOR_ID3_V1) && (firsthead>>8) == ('T'<<16)+('A'<<8)+'G') {
+ fprintf(stderr,"Found old ID3 Header\n");
+ }
+
+ /* Check for new ID3 header */
+ if( (flags & CHECK_FOR_ID3_V2) && (firsthead>>8) == ('I'<<16)+('D'<<8)+'3') {
+ if( (firsthead & 0xff) != 0xff) {
+ fprintf(stderr,"Found new ID3 Header\n");
+ ret = skip_new_id3(rds);
+ if(!ret)
+ return 0;
+ if(ret > 0) {
+ *skipped += ret+4;
+ continue;
+ }
+ }
+ }
+
+ readers_goto_mark(rds); /* reset to old mark and continue */
+ if(!rds->read_frame_body(rds,dummybuf,1))
+ return 0;
+
+ (*skipped)++;
+ continue;
+ }
+
+ found = 0;
+ freeformatsize = 0;
+
+ /*
+ * At the first free format paket we do not know the size
+ */
+ if(frameInfo.bitrate_index == 0) {
+ int maxframesize = MAX_INPUT_FRAMESIZE; /* FIXME depends on layer and sampling freq */
+
+fprintf(stderr,"Searching for next FF header\n");
+
+ if(!rds->head_read(rds,&nexthead))
+ return 0;
+
+ for(j=0;j<maxframesize;j++) {
+ if(head_check(nexthead) && (nexthead & (SYNC_HEAD_MASK|SYNC_HEAD_MASK_FF) ) == (firsthead & (SYNC_HEAD_MASK|SYNC_HEAD_MASK_FF)) &&
+ decode_header(&nextInfo,nexthead) ) {
+
+/* fprintf(stderr,"j: %d %d %d\n",j,frameInfo.padsize,nextInfo.padsize); */
+
+ freeformatsize = j - frameInfo.padsize;
+ found = 1;
+ break;
+ }
+ if(!rds->head_shift(rds,&nexthead))
+ return 0;
+ }
+ }
+ else {
+ if(!rds->read_frame_body(rds,dummybuf,frameInfo.framesize))
+ return 0;
+
+ if(!rds->head_read(rds,&nexthead))
+ return 0;
+
+/*
+fprintf(stderr,"S: %08lx %08lx %d %d %d %d\n",firsthead,nexthead, head_check(nexthead),(nexthead & SYNC_HEAD_MASK) == firsthead,(nexthead & SYNC_HEAD_MASK_FF) != 0x0,decode_header(&nextInfo,nexthead));
+*/
+
+ if( head_check(nexthead) && (nexthead & SYNC_HEAD_MASK) == (firsthead & SYNC_HEAD_MASK) &&
+ (nexthead & SYNC_HEAD_MASK_FF) != 0x0 && decode_header(&nextInfo,nexthead)) {
+ found = 1;
+ }
+ }
+
+ if(!found) {
+ readers_goto_mark(rds); /* reset to old mark and continue */
+ if(!rds->read_frame_body(rds,dummybuf,1))
+ return 0;
+ (*skipped)++;
+ continue;
+ }
+
+/*
+fprintf(stderr,"s: %08lx %08lx %d %d %d\n",firsthead,nexthead,frameInfo.framesize,nextInfo.framesize,freeformatsize);
+*/
+
+ /* check some more frames */
+ for(l=0;l<LOOK_AHEAD_NUM;l++) {
+ int size;
+
+ if( freeformatsize > 0 ) {
+ size = freeformatsize + nextInfo.padsize;
+ }
+ else
+ size = nextInfo.framesize;
+
+ /* step over data */
+ if(!rds->read_frame_body(rds,dummybuf,size))
+ return 0;
+
+ if(!rds->head_read(rds,&nexthead))
+ return 0;
+
+ if(!head_check(nexthead) ||
+ (nexthead & SYNC_HEAD_MASK) != (firsthead & SYNC_HEAD_MASK) ||
+ !decode_header(&nextInfo,nexthead) ) {
+ found = 0;
+ break;
+ }
+ if( freeformatsize > 0) {
+ if( ( nexthead & SYNC_HEAD_MASK_FF ) != 0x0) {
+ found = 0;
+ break;
+ }
+ }
+ else {
+ if( (nexthead & SYNC_HEAD_MASK_FF) == 0x0) {
+ found = 0;
+ break;
+ }
+ }
+ }
+
+ if(found)
+ break;
+
+ readers_goto_mark(rds); /* reset to old mark and continue */
+ if(!rds->read_frame_body(rds,dummybuf,1)) /* skip first byte */
+ return 0;
+ (*skipped)++;
+
+ }
+
+ if(i == SCAN_LENGTH)
+ return -1;
+
+ readers_goto_mark(rds);
+ fr->freeformatsize = freeformatsize;
+ fr->firsthead = firsthead;
+
+ return 1;
+
+}
+
+/*
+ * skips the RIFF header at the beginning
+ *
+ * returns: 0 = read-error
+ * -1/-2 = illegal RIFF header (= -2 backstep not valid)
+ * 1 = skipping succeeded
+ */
+static int skip_riff(struct reader *rds)
+{
+ unsigned long length;
+ unsigned char buf[16];
+
+ if(!rds->read_frame_body(rds,buf,16)) /* read header information */
+ return 0;
+
+ if( strncmp("WAVEfmt ",(char *)buf+4,8) ) /* check 2. signature */
+ return -1;
+
+ length = (unsigned long) buf[12] + /* decode the header length */
+ (((unsigned long) buf[13])<<8) +
+ (((unsigned long) buf[14])<<16) +
+ (((unsigned long) buf[15])<<24);
+
+ if(!rds->skip_bytes(rds,length)) /* will not store data in backbuff! */
+ return 0;
+
+ if(!rds->read_frame_body(rds,buf,8)) /* skip "data" plus length */
+ return 0;
+
+ if(strncmp("data",(char *)buf,4))
+ return -2;
+
+ return length+8+16;
+}
+
+/*
+ * skips the ID3 header at the beginning
+ *
+ * returns: 0 = read-error
+ * -1 = illegal ID3 header
+ * 1 = skipping succeeded
+ */
+static int skip_new_id3(struct reader *rds)
+{
+ unsigned long length;
+ unsigned char buf[6];
+
+ if(!rds->read_frame_body(rds,buf,6)) /* read more header information */
+ return 0;
+
+ if(buf[0] == 0xff)
+ return -1;
+
+ if( (buf[2]|buf[3]|buf[4]|buf[5]) & 0x80)
+ return -1;
+
+ length = (unsigned long) buf[2] & 0x7f;
+ length <<= 7;
+ length += (unsigned long) buf[3] & 0x7f;
+ length <<= 7;
+ length += (unsigned long) buf[4] & 0x7f;
+ length <<= 7;
+ length += (unsigned long) buf[5] & 0x7f;
+
+ if(!rds->skip_bytes(rds,length)) /* will not store data in backbuff! */
+ return 0;
+
+ return length+6;
+
+}
+
+
+
+
+
+/*****************************************************************
+ * read next frame
+ */
+int read_frame(struct reader *rds,struct frame *fr)
+{
+ unsigned long newhead,oldhead;
+ static unsigned char ssave[34];
+
+ oldhead = fr->thishead;
+
+ if (param.halfspeed) {
+ static int halfphase = 0;
+ if (halfphase--) {
+ bsi.bitindex = 0;
+ bsi.wordpointer = (unsigned char *) bsbuf;
+ if (fr->lay == 3)
+ memcpy (bsbuf, ssave, fr->sideInfoSize);
+ return 1;
+ }
+ else
+ halfphase = param.halfspeed - 1;
+ }
+
+ while(1) {
+
+ if(!rds->head_read(rds,&newhead))
+ return FALSE;
+
+/*
+ fprintf(stderr,"n %08lx",newhead);
+*/
+
+ if( !head_check(newhead) || !decode_header(fr,newhead) ) {
+ if (!param.quiet)
+ fprintf(stderr,"Illegal Audio-MPEG-Header 0x%08lx at offset 0x%lx.\n",
+ newhead,rds->tell(rds)-4);
+
+ if(param.tryresync) {
+ int try = 0;
+ readers_pushback_header(rds,newhead);
+ if(sync_stream(rds,fr,0xffff,&try) <= 0)
+ return 0;
+ if(!param.quiet)
+ fprintf (stderr, "Skipped %d bytes in input.\n", try);
+ }
+ else
+ return (0);
+ }
+ else
+ break;
+ }
+
+/*
+ fprintf(stderr,"N %08lx",newhead);
+*/
+
+ fr->header_change = 2;
+ if(oldhead) {
+ if((oldhead & 0xc00) == (fr->thishead & 0xc00)) {
+ if( (oldhead & 0xc0) == 0 && (fr->thishead & 0xc0) == 0)
+ fr->header_change = 1;
+ else if( (oldhead & 0xc0) > 0 && (fr->thishead & 0xc0) > 0)
+ fr->header_change = 1;
+ }
+ }
+
+
+ if(!fr->bitrate_index) {
+ fr->framesize = fr->freeformatsize + fr->padsize;
+ }
+
+/*
+fprintf(stderr,"Reading %d\n",fr->framesize);
+*/
+
+ /* flip/init buffer for Layer 3 */
+ /* FIXME for reentrance */
+ bsbufold = bsbuf;
+ bsbufold_end = bsbufend[bsnum];
+ bsbuf = bsspace[bsnum]+512;
+ bsnum = (bsnum + 1) & 1;
+ bsbufend[bsnum] = fr->framesize;
+
+ /* read main data into memory */
+ if(!rds->read_frame_body(rds,bsbuf,fr->framesize))
+ return 0;
+
+ {
+ /* Test */
+ static struct vbrHeader head;
+ static int vbr = 0; /* FIXME */
+ if(!vbr) {
+ getVBRHeader(&head,bsbuf,fr);
+ vbr = 1;
+ }
+ }
+
+ bsi.bitindex = 0;
+ bsi.wordpointer = (unsigned char *) bsbuf;
+
+ if (param.halfspeed && fr->lay == 3)
+ memcpy (ssave, bsbuf, fr->sideInfoSize);
+
+ return 1;
+}
+
+/*
+ * decode a header and write the information
+ * into the frame structure
+ */
+static int decode_header(struct frame *fr,unsigned long newhead)
+{
+ if(!head_check(newhead)) {
+ fprintf(stderr,"Oopps header is wrong %08lx\n",newhead);
+ return 0;
+ }
+
+ if( newhead & (1<<20) ) {
+ fr->lsf = (newhead & (1<<19)) ? 0x0 : 0x1;
+ fr->mpeg25 = 0;
+ }
+ else {
+ fr->lsf = 1;
+ fr->mpeg25 = 1;
+ }
+
+ /*
+ * CHECKME: should be add more consistency checks here ?
+ * changed layer, changed CRC bit, changed sampling frequency
+ */
+ {
+ fr->lay = 4-((newhead>>17)&3);
+ if( ((newhead>>10)&0x3) == 0x3) {
+ fprintf(stderr,"Stream error\n");
+ return 0;
+ }
+ if(fr->mpeg25) {
+ fr->sampling_frequency = 6 + ((newhead>>10)&0x3);
+ }
+ else
+ fr->sampling_frequency = ((newhead>>10)&0x3) + (fr->lsf*3);
+ fr->error_protection = ((newhead>>16)&0x1)^0x1;
+ }
+
+ fr->bitrate_index = ((newhead>>12)&0xf);
+ fr->padding = ((newhead>>9)&0x1);
+ fr->extension = ((newhead>>8)&0x1);
+ fr->mode = ((newhead>>6)&0x3);
+ fr->mode_ext = ((newhead>>4)&0x3);
+ fr->copyright = ((newhead>>3)&0x1);
+ fr->original = ((newhead>>2)&0x1);
+ fr->emphasis = newhead & 0x3;
+
+ fr->stereo = (fr->mode == MPG_MD_MONO) ? 1 : 2;
+
+ switch(fr->lay) {
+ case 1:
+ fr->framesize = (long) tabsel_123[fr->lsf][0][fr->bitrate_index] * 12000;
+ fr->framesize /= freqs[fr->sampling_frequency];
+ fr->framesize = ((fr->framesize+fr->padding)<<2)-4;
+ fr->sideInfoSize = 0;
+ fr->padsize = fr->padding << 2;
+ break;
+ case 2:
+ fr->framesize = (long) tabsel_123[fr->lsf][1][fr->bitrate_index] * 144000;
+ fr->framesize /= freqs[fr->sampling_frequency];
+ fr->framesize += fr->padding - 4;
+ fr->sideInfoSize = 0;
+ fr->padsize = fr->padding;
+ break;
+ case 3:
+ if(fr->lsf)
+ fr->sideInfoSize = (fr->stereo == 1) ? 9 : 17;
+ else
+ fr->sideInfoSize = (fr->stereo == 1) ? 17 : 32;
+ if(fr->error_protection)
+ fr->sideInfoSize += 2;
+ fr->framesize = (long) tabsel_123[fr->lsf][2][fr->bitrate_index] * 144000;
+ fr->framesize /= freqs[fr->sampling_frequency]<<(fr->lsf);
+ fr->framesize = fr->framesize + fr->padding - 4;
+ fr->padsize = fr->padding;
+ break;
+ default:
+ fprintf(stderr,"Sorry, unknown layer type.\n");
+ return (0);
+ }
+
+ if(!fr->bitrate_index) {
+ /* fprintf(stderr,"Warning, Free format not heavily tested: (head %08lx)\n",newhead); */
+ fr->framesize = 0;
+ }
+ fr->thishead = newhead;
+
+ return 1;
+}
+
+#ifdef MPG123_REMOTE
+void print_rheader(struct frame *fr)
+{
+ static char *modes[4] = { "Stereo", "Joint-Stereo", "Dual-Channel", "Single-Channel" };
+ static char *layers[4] = { "Unknown" , "I", "II", "III" };
+ static char *mpeg_type[2] = { "1.0" , "2.0" };
+
+ /* version, layer, freq, mode, channels, bitrate, BPF */
+ fprintf(stderr,"@I %s %s %ld %s %d %d %d\n",
+ mpeg_type[fr->lsf],layers[fr->lay],freqs[fr->sampling_frequency],
+ modes[fr->mode],fr->stereo,
+ tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index],
+ fr->framesize+4);
+}
+#endif
+
+void print_header(struct frame *fr)
+{
+ static char *modes[4] = { "Stereo", "Joint-Stereo", "Dual-Channel", "Single-Channel" };
+ static char *layers[4] = { "Unknown" , "I", "II", "III" };
+
+ fprintf(stderr,"MPEG %s, Layer: %s, Freq: %ld, mode: %s, modext: %d, BPF : %d\n",
+ fr->mpeg25 ? "2.5" : (fr->lsf ? "2.0" : "1.0"),
+ layers[fr->lay],freqs[fr->sampling_frequency],
+ modes[fr->mode],fr->mode_ext,fr->framesize+4);
+ fprintf(stderr,"Channels: %d, copyright: %s, original: %s, CRC: %s, emphasis: %d.\n",
+ fr->stereo,fr->copyright?"Yes":"No",
+ fr->original?"Yes":"No",fr->error_protection?"Yes":"No",
+ fr->emphasis);
+ fprintf(stderr,"Bitrate: %d Kbits/s, Extension value: %d\n",
+ tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index],fr->extension);
+}
+
+void print_header_compact(struct frame *fr)
+{
+ static char *modes[4] = { "stereo", "joint-stereo", "dual-channel", "mono" };
+ static char *layers[4] = { "Unknown" , "I", "II", "III" };
+
+ fprintf(stderr,"MPEG %s layer %s, %d kbit/s, %ld Hz %s\n",
+ fr->mpeg25 ? "2.5" : (fr->lsf ? "2.0" : "1.0"),
+ layers[fr->lay],
+ tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index],
+ freqs[fr->sampling_frequency], modes[fr->mode]);
+}
+
+void print_id3_tag(unsigned char *buf)
+{
+ struct id3tag {
+ char tag[3];
+ char title[30];
+ char artist[30];
+ char album[30];
+ char year[4];
+ char comment[30];
+ unsigned char genre;
+ };
+ struct id3tag *tag = (struct id3tag *) buf;
+ char title[31]={0,};
+ char artist[31]={0,};
+ char album[31]={0,};
+ char year[5]={0,};
+ char comment[31]={0,};
+ char genre[31]={0,};
+
+ if(param.quiet)
+ return;
+
+ strncpy(title,tag->title,30);
+ strncpy(artist,tag->artist,30);
+ strncpy(album,tag->album,30);
+ strncpy(year,tag->year,4);
+ strncpy(comment,tag->comment,30);
+
+ if ( tag->genre < sizeof(genre_table)/sizeof(*genre_table) ) {
+ strncpy(genre, genre_table[tag->genre], 30);
+ } else {
+ strncpy(genre,"Unknown",30);
+ }
+
+ fprintf(stderr,"Title : %-30s Artist: %s\n",title,artist);
+ fprintf(stderr,"Album : %-30s Year : %4s\n",album,year);
+ fprintf(stderr,"Comment: %-30s Genre : %s\n",comment,genre);
+}
+
+#if 0
+/* removed the strndup for better portability */
+/*
+ * Allocate space for a new string containing the first
+ * "num" characters of "src". The resulting string is
+ * always zero-terminated. Returns NULL if malloc fails.
+ */
+char *strndup (const char *src, int num)
+{
+ char *dst;
+
+ if (!(dst = (char *) malloc(num+1)))
+ return (NULL);
+ dst[num] = '\0';
+ return (strncpy(dst, src, num));
+}
+#endif
+
+/*
+ * Split "path" into directory and filename components.
+ *
+ * Return value is 0 if no directory was specified (i.e.
+ * "path" does not contain a '/'), OR if the directory
+ * is the same as on the previous call to this function.
+ *
+ * Return value is 1 if a directory was specified AND it
+ * is different from the previous one (if any).
+ */
+
+int split_dir_file (const char *path, char **dname, char **fname)
+{
+ static char *lastdir = NULL;
+ char *slashpos;
+
+ if ((slashpos = strrchr(path, '/'))) {
+ *fname = slashpos + 1;
+ *dname = strdup(path); /* , 1 + slashpos - path); */
+ if(!(*dname)) {
+ perror("memory");
+ exit(1);
+ }
+ (*dname)[1 + slashpos - path] = 0;
+ if (lastdir && !strcmp(lastdir, *dname)) {
+ /*** same as previous directory ***/
+ free (*dname);
+ *dname = lastdir;
+ return 0;
+ }
+ else {
+ /*** different directory ***/
+ if (lastdir)
+ free (lastdir);
+ lastdir = *dname;
+ return 1;
+ }
+ }
+ else {
+ /*** no directory specified ***/
+ if (lastdir) {
+ free (lastdir);
+ lastdir = NULL;
+ };
+ *dname = NULL;
+ *fname = (char *)path;
+ return 0;
+ }
+}
+
+void set_pointer(int ssize,long backstep)
+{
+ bsi.wordpointer = bsbuf + ssize - backstep;
+ if (backstep)
+ memcpy(bsi.wordpointer,bsbufold+bsbufold_end-backstep,backstep);
+ bsi.bitindex = 0;
+}
+
+/********************************/
+
+double compute_bpf(struct frame *fr)
+{
+ double bpf;
+
+ if(!fr->bitrate_index) {
+ return fr->freeformatsize + 4;
+ }
+
+ switch(fr->lay) {
+ case 1:
+ bpf = tabsel_123[fr->lsf][0][fr->bitrate_index];
+ bpf *= 12000.0 * 4.0;
+ bpf /= freqs[fr->sampling_frequency] <<(fr->lsf);
+ break;
+ case 2:
+ case 3:
+ bpf = tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index];
+ bpf *= 144000;
+ bpf /= freqs[fr->sampling_frequency] << (fr->lsf);
+ break;
+ default:
+ bpf = 1.0;
+ }
+
+ return bpf;
+}
+
+double compute_tpf(struct frame *fr)
+{
+ static int bs[4] = { 0,384,1152,1152 };
+ double tpf;
+
+ tpf = (double) bs[fr->lay];
+ tpf /= freqs[fr->sampling_frequency] << (fr->lsf);
+ return tpf;
+}
+
+/*
+ * Returns number of frames queued up in output buffer, i.e.
+ * offset between currently played and currently decoded frame.
+ */
+
+#ifndef NOXFERMEM
+long compute_buffer_offset(struct frame *fr)
+{
+ long bufsize;
+
+ /*
+ * buffermem->buf[0] holds output sampling rate,
+ * buffermem->buf[1] holds number of channels,
+ * buffermem->buf[2] holds audio format of output.
+ */
+
+ if(!param.usebuffer || !(bufsize=xfermem_get_usedspace(buffermem))
+ || !buffermem->buf[0] || !buffermem->buf[1])
+ return 0;
+
+ bufsize = (long)((double) bufsize / buffermem->buf[0] /
+ buffermem->buf[1] / compute_tpf(fr));
+
+ if((buffermem->buf[2] & AUDIO_FORMAT_MASK) == AUDIO_FORMAT_16)
+ return bufsize/2;
+ else
+ return bufsize;
+}
+#endif
+
+void print_stat(struct reader *rds,struct frame *fr,int no,long buffsize,struct audio_info_struct *ai)
+{
+ double bpf,tpf,tim1,tim2;
+ double dt = 0.0;
+ int sno,rno;
+ char outbuf[256];
+
+ if(!rds || !fr)
+ return;
+
+ outbuf[0] = 0;
+
+#ifndef GENERIC
+ {
+ struct timeval t;
+ fd_set serr;
+ int n,errfd = fileno(stderr);
+
+ t.tv_sec=t.tv_usec=0;
+
+ FD_ZERO(&serr);
+ FD_SET(errfd,&serr);
+ n = select(errfd+1,NULL,&serr,NULL,&t);
+ if(n <= 0)
+ return;
+ }
+#endif
+
+ bpf = compute_bpf(fr);
+ tpf = compute_tpf(fr);
+
+ if(buffsize > 0 && ai && ai->rate > 0 && ai->channels > 0) {
+ dt = (double) buffsize / ai->rate / ai->channels;
+ if( (ai->format & AUDIO_FORMAT_MASK) == AUDIO_FORMAT_16)
+ dt *= 0.5;
+ }
+
+ rno = 0;
+ sno = no;
+ if(rds->filelen >= 0) {
+ long t = rds->tell(rds);
+ rno = (int)((double)(rds->filelen-t)/bpf);
+ sno = (int)((double)t/bpf);
+ }
+
+ sprintf(outbuf+strlen(outbuf),"\rFrame# %5d [%5d], ",sno,rno);
+
+ tim1 = sno*tpf-dt;
+ tim2 = rno*tpf+dt;
+#if 0
+ tim1 = tim1 < 0 ? 0.0 : tim1;
+#endif
+ tim2 = tim2 < 0 ? 0.0 : tim2;
+
+ sprintf(outbuf+strlen(outbuf),"Time: %02u:%02u.%02u [%02u:%02u.%02u], ",
+ (unsigned int)tim1/60,
+ (unsigned int)tim1%60,
+ (unsigned int)(tim1*100)%100,
+ (unsigned int)tim2/60,
+ (unsigned int)tim2%60,
+ (unsigned int)(tim2*100)%100);
+
+ if(param.usebuffer)
+ sprintf(outbuf+strlen(outbuf),"[%8ld] ",(long)buffsize);
+ write(fileno(stderr),outbuf,strlen(outbuf));
+#if 0
+ fflush(out); /* hmm not really nec. */
+#endif
+}
+
+int get_songlen(struct reader *rds,struct frame *fr,int no)
+{
+ double tpf;
+
+ if(!fr)
+ return 0;
+
+ if(no < 0) {
+ if(!rds || rds->filelen < 0)
+ return 0;
+ no = (double) rds->filelen / compute_bpf(fr);
+ }
+
+ tpf = compute_tpf(fr);
+ return no*tpf;
+}
+
+