/* mp3chunk.c by Michael Thorpe 2008-10-22 */ #include #include #include #include #include #include #include #include #include struct mpegheader { unsigned int frame :11; unsigned int version : 2; unsigned int layer : 2; unsigned int checksum : 1; unsigned int bitrate : 4; unsigned int samprate : 2; unsigned int padding : 1; unsigned int private : 1; unsigned int chanmode : 2; unsigned int modeext : 2; unsigned int copyright : 1; unsigned int original : 1; unsigned int emphasis : 2; }; char *versions[4]={"2.5","unknown","2","1"}; char *layers[4]={"unknown","III","II","I"}; int bitrates[5][16]={ {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,-1}, {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,-1}, {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,-1}, {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,-1}, {0,32,64,96,128,160,192,224,256,288,320,352,384,416,488,-1} }; char *samprates[4][4]={ {"11.025","12","8","-1"}, {"0","0","0","0"}, {"22.05","24","16","-1"}, {"44.1","48","32","-1"} }; int samplerates[4][4]={ {11025,12000,8000,-1}, {0,0,0,0}, {22050,24000,16000,-1}, {44100,48000,32000,-1} }; char *chanmodes[4]={"stereo","joint stereo","dual channel","single channel"}; int bands[4]={4,8,12,16}; char *emphases[4]={"no","50/15 ms","unknown","CCIT J.17"}; #define BUF_LEN 4096 int decimal_positions=0; unsigned char *buf,bufpooka; size_t buflen,bufpos; int bufin,bufout; int buf_init(int fin,int fout) { buf=(unsigned char *)malloc(BUF_LEN); if(!buf) return(-1); bufpooka=0; buflen=0; bufpos=0; bufin=fin; bufout=fout; return(0); } unsigned char *buf_clear(void) { buflen=0; return(buf); } /* FIXME: We should probably try to use page I/O */ int buf_add(size_t len) { int i; if(buflen+len>BUF_LEN) return(-1); while(len && 0<(i=read(bufin,buf+buflen,len))) { buflen+=i; bufpos+=i; len-=i; } if(len) return(-1); return(0); } int buf_skip(size_t len,int verbose) { int i; if(verbose) fprintf(stderr,"Skipping %u bytes of crap...\n",buflen+len); if(buflen>0) bufpooka=buf[buflen-1]; while(len && 0<(i=read(bufin,buf,((len>BUF_LEN)?BUF_LEN:len)))) { len-=i; bufpos+=i; } buflen=0; if(len) return(-1); return(0); } int buf_put(void) { int i=0,j=0; if(buflen>0) bufpooka=buf[buflen-1]; if(bufout==-1) { buflen=0; return(0); } while(buflen>j && 0<(i=write(bufout,buf+j,buflen-j))) j+=i; buflen=0; if(i<0) return(-1); return(0); } void buf_uninit(void) { free(buf); } size_t buf_getpos(void) { return(bufpos); } void buf_unput(unsigned char c) { if(buflen+1>BUF_LEN) return; memmove(buf+1,buf,buflen); buf[0]=c; buflen++; } struct mpegheader fillheader(unsigned char *data) { struct mpegheader header; header.frame=(data[0]<<3)+(data[1]>>5); header.version=(data[1]>>3)&3; header.layer=(data[1]>>1)&3; header.checksum=data[1]&1; header.bitrate=(data[2]>>4)&15; header.samprate=(data[2]>>2)&3; header.padding=(data[2]>>1)&1; header.private=data[2]&1; header.chanmode=(data[3]>>6)&3; header.modeext=(data[3]>>4)&3; header.copyright=(data[3]>>3)&1; header.original=(data[3]>>2)&1; header.emphasis=data[3]&3; return(header); } size_t printtype(int verbose,int blockzeros,double *duration) { unsigned char *data; struct mpegheader header; int grabbed=10,i,j,*bitrate; data=buf_clear(); if(buf_add(grabbed)) return(0); if(verbose>1) { size_t pos; pos=buf_getpos(); if(decimal_positions) printf("%08lu: ",(unsigned long)pos-grabbed); else printf("%8.8lX: ",(unsigned long)pos-grabbed); } if(data[0]=='I' && data[1]=='D' && data[2]=='3') { if(verbose) { printf("ID3v%d",data[3]); if(data[4]) printf(".%d",data[4]); printf(" header"); } if(data[6]&128 || data[7]&128 || data[8]&128 || data[9]&128) { if(verbose) printf(" (invalid size)\n"); return(0); } i=(data[6]<<21)+(data[7]<<14)+(data[8]<<7)+data[9]; if(verbose) printf(" (%d bytes)\n",i+grabbed); if(buf_skip(i,verbose==1)) return(0); return(i+grabbed); } else if(data[0]=='R' && data[1]=='I' && data[2]=='F' && data[3]=='F') { grabbed+=30; if(buf_add(30)) return(0); i=data[16]+(data[17]<<8); if(buf_skip(i,verbose==1)) return(0); if(verbose) printf("RIFF (%d bytes)\n",i+grabbed); return(i+grabbed); } else if(data[0]=='T' && data[1]=='A' && data[2]=='G') { if(verbose) printf("TAG (128 bytes)\n"); if(buf_skip(118,verbose==1)) return(0); return(128); } else if(data[0]=='\0' && data[1]=='T' && data[2]=='A' && data[3]=='G') { if(verbose) { printf("null byte; TAG (128 bytes)\n"); } if(buf_skip(119,verbose==1)) return(0); return(129); } else { if(data[0]==0xFF && data[1]==0xF2 && data[2]==0x6C) { if(verbose) printf("<0x6C->0x72>"); data[2]=0x72; } header=fillheader(data); if(data[1]==0xFF && 0xE0<=data[2] && data[2]<0xFF) { if(verbose) printf(""); grabbed--; /* This isn't _correct_, but it works */ header=fillheader(data+1); } if(header.frame != 2047 && bufpooka==0xFF && data[0]>=0xE0) { if(verbose) printf(""); buf_unput(bufpooka); grabbed++; /* This isn't _correct_, but it works */ if(data[0]==0xFF && data[1]==0xF2 && data[2]==0x6C) { if(verbose) printf("<0x6C->0x72>"); data[2]=0x72; } header=fillheader(data); } if(header.frame != 2047 && data[2]==0xFF && data[3]>=0xE0) { if(verbose) printf(""); grabbed-=2; /* This isn't _correct_, but it works */ header=fillheader(data+2); } if(header.frame != 2047) { if(verbose) printf("(unknown header)\n"); return(0); } if(verbose) printf("MPEG%s Layer %s",versions[header.version],layers[header.layer]); bitrate=bitrates[0]; if(header.layer==3) bitrate=bitrates[1]; if(header.version==3 && header.layer) bitrate=bitrates[header.layer+1]; if(verbose) { printf(", %dkbps, %skHz",bitrate[header.bitrate],samprates[header.version][header.samprate]); if(!header.checksum) printf(", checksum"); if(header.padding) printf(", padded"); if(header.private) printf(", private"); printf(", %s",chanmodes[header.chanmode]); if(header.chanmode==1) { if(header.layer==1) { if(header.modeext&2) printf(" (intensity stereo)"); if(header.modeext&2) printf(" (MS stereo)"); } else { printf(" (bands %d to 31)",bands[header.modeext]); } } if(header.copyright) printf(", copyrighted"); if(header.original) printf(", original"); printf(", %s emphasis",emphases[header.emphasis]); } i=((header.layer==3)?12:144); if(verbose>2) printf("\ni=%d*%d*1000/%d+%d",i,bitrate[header.bitrate],samplerates[header.version][header.samprate],header.padding); i=i*bitrate[header.bitrate]*1000/samplerates[header.version][header.samprate]+header.padding; if(verbose>2) printf("=%d",i); if(buf_add(i-grabbed)) { if(verbose) putchar('\n'); fprintf(stderr,"Failed to grab %d more bytes\n",i-grabbed); return(0); } if(duration) { if(header.layer==3) { *duration+=(double)384.0/(double)samplerates[header.version][header.samprate]; } else { *duration+=(double)1152.0/(double)samplerates[header.version][header.samprate]; } } j=4; if(blockzeros) { for(;j2) { done: printf("usage: %s [-dllvvz] []\n",argv[0]); return(1); } if(argv[o][0]=='-' && !argv[o][1]) i=0; else { i=open(argv[o],O_RDONLY); if(i==-1) { perror("open"); return(2); } } if(argc<=++o) o=-1; else if(argv[o][0]=='-' && !argv[o][1]) o=1; else { o=open(argv[o],O_WRONLY|O_CREAT|O_NOCTTY|O_APPEND,0666); if(o==-1) { perror("open"); return(3); } } if(buf_init(i,o)) { fprintf(stderr,"Unable to initialize I/O subroutines\n"); return(-1); } duration=0.0; while(printtype(v,z,l?&duration:0)) ; buf_uninit(); if(l) { if(l==1) duration+=.5; if(duration>=3600) printf("%d:%02d",(int)(duration/3600),(int)(duration/60)%60); else printf("%d",(int)(duration/60)%60); printf(":%02d",(int)duration%60); if(l>1) printf(".%02d",(int)(duration*100)%100); putchar('\n'); } if(i) close(i); if(o>1) close(o); return(0); }