00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "midfile.h"
00027 #include <string.h>
00028 #include <stdio.h>
00029 #include <stdlib.h>
00030 #include <unistd.h>
00031 #include "sndcard.h"
00032 #include "midispec.h"
00033 #include "mt32togm.h"
00034 #include "sys/stat.h"
00035 #include <config.h>
00036
00037 #include <kprocess.h>
00038 #include <qfile.h>
00039
00040 int fsearch(FILE *fh,const char *text,long *ptr);
00041
00042
00043
00044 double tempoToMetronomeTempo(ulong x)
00045 {
00046 return 60/((double)x/1000000);
00047 }
00048
00049 double metronomeTempoToTempo(ulong x)
00050 {
00051 return ((double)60*x)/1000000;
00052 }
00053
00054 int uncompressFile(const char *gzname, char *tmpname)
00055
00056 {
00057 QString cmd("gzip -dc " + KProcess::quote(gzname));
00058 FILE *infile = popen( QFile::encodeName(cmd).data(), "r");
00059 if (infile==NULL) {
00060 fprintf(stderr,"ERROR : popen failed : %s\n",QFile::encodeName(cmd).data());
00061 return 1;
00062 }
00063 strcpy(tmpname, "/tmp/KMid.XXXXXXXXXX");
00064 int fd = mkstemp(tmpname);
00065 if (fd == -1)
00066 {
00067 pclose(infile);
00068 return 1;
00069 }
00070 FILE *outfile= fdopen(fd,"wb");
00071 if (outfile==NULL)
00072 {
00073 pclose(infile);
00074 return 1;
00075 }
00076 int n=getc(infile);
00077 if (n==EOF)
00078 {
00079 pclose(infile);
00080 fclose(outfile);
00081 unlink(tmpname);
00082 return 1;
00083 }
00084 fputc(n,outfile);
00085 int buf[BUFSIZ];
00086 n = fread(buf, 1, BUFSIZ, infile);
00087 while (n>0)
00088 {
00089 fwrite(buf, 1, n, outfile);
00090 n = fread(buf, 1, BUFSIZ, infile);
00091 }
00092
00093 pclose(infile);
00094
00095
00096
00097
00098 fclose(outfile);
00099 return 0;
00100 }
00101
00102 MidiTrack **readMidiFile( const char *name, MidiFileInfo *info, int &ok)
00103 {
00104 ok=1;
00105 MidiTrack **tracks;
00106
00107 struct stat buf;
00108 if (stat(name,&buf) || !S_ISREG(buf.st_mode))
00109 {
00110 fprintf(stderr,"ERROR: %s is not a regular file\n",name);
00111 ok=-6;
00112 return NULL;
00113 }
00114
00115 FILE *fh=fopen(name,"rb");
00116 if (fh==NULL)
00117 {
00118 fprintf(stderr,"ERROR: Can't open file %s\n",name);
00119 ok=-1;
00120 return NULL;
00121 }
00122 char text[4];
00123 text[0] = 0;
00124 fread(text,1,4,fh);
00125 if ((strncmp(text,"MThd",4)!=0)&&(strcmp(&name[strlen(name)-3],".gz")==0))
00126 {
00127 fclose(fh);
00128 char tempname[200];
00129 fprintf(stderr,"Trying to open zipped midi file...\n");
00130 if (uncompressFile(name,tempname)!=0)
00131 {
00132 fprintf(stderr,"ERROR: %s is not a (zipped) midi file\n",name);
00133 ok=-2;
00134 return NULL;
00135 }
00136 fh=fopen(tempname,"rb");
00137 fread(text,1,4,fh);
00138 unlink(tempname);
00139 }
00140
00141 if (strncmp(text,"MThd",4)!=0)
00142 {
00143 fseek(fh,0,SEEK_SET);
00144 long pos;
00145 if (fsearch(fh,"MThd",&pos)==0)
00146 {
00147 fclose(fh);
00148 fprintf(stderr,"ERROR: %s is not a midi file.\n",name);
00149 ok=-2;
00150 return NULL;
00151 }
00152 fseek(fh,pos,SEEK_SET);
00153 fread(text,1,4,fh);
00154 }
00155 long header_size=readLong(fh);
00156 info->format=readShort(fh);
00157 info->ntracks=readShort(fh);
00158 info->ticksPerCuarterNote=readShort(fh);
00159 if (info->ticksPerCuarterNote<0)
00160 {
00161 fprintf(stderr,"ERROR: Ticks per cuarter note is negative !\n");
00162 fprintf(stderr,"Please report this error to : larrosa@kde.org\n");
00163 fclose(fh);
00164 ok=-3;
00165 return NULL;
00166 }
00167 if (header_size>6) fseek(fh,header_size-6,SEEK_CUR);
00168 tracks=new MidiTrack*[info->ntracks];
00169 if (tracks==NULL)
00170 {
00171 fprintf(stderr,"ERROR: Not enough memory\n");
00172 fclose(fh);
00173 ok=-4;
00174 return NULL;
00175 }
00176 int i=0;
00177 while (i<info->ntracks)
00178 {
00179 fread(text,1,4,fh);
00180 if (strncmp(text,"MTrk",4)!=0)
00181 {
00182 fprintf(stderr,"ERROR: Not a well built midi file\n");
00183 fprintf(stderr,"%s",text);
00184 fclose(fh);
00185 ok=-5;
00186 return NULL;
00187 }
00188 tracks[i]=new MidiTrack(fh,info->ticksPerCuarterNote,i);
00189 if (tracks[i]==NULL)
00190 {
00191 fprintf(stderr,"ERROR: Not enough memory");
00192 fclose(fh);
00193 ok=-4;
00194 return NULL;
00195 }
00196 i++;
00197 }
00198
00199 fclose(fh);
00200
00201 return tracks;
00202
00203 }
00204
00205 void parseInfoData(MidiFileInfo *info,MidiTrack **tracks,float ratioTempo)
00206 {
00207
00208 info->ticksTotal=0;
00209 info->millisecsTotal=0.0;
00210 info->ticksPlayed=0;
00211 int i;
00212 for (i=0;i<256;i++)
00213 {
00214 info->patchesUsed[i]=0;
00215 }
00216
00217 int parsing=1;
00218 int trk,minTrk;
00219 ulong tempo=(ulong)(500000 * ratioTempo);
00220
00221 #ifdef MIDFILEDEBUG
00222 printf("Parsing 1 ...\n");
00223 #endif
00224
00225 int pgminchannel[16];
00226 for (i=0;i<16;i++)
00227 {
00228 pgminchannel[i]=0;
00229 }
00230
00231 int j;
00232 for (i=0;i<info->ntracks;i++)
00233 {
00234 tracks[i]->init();
00235 tracks[i]->changeTempo(tempo);
00236 }
00237 double prevms=0;
00238 double minTime=0;
00239 double maxTime;
00240 MidiEvent *ev=new MidiEvent;
00241 while (parsing)
00242 {
00243 prevms=minTime;
00244 trk=0;
00245 minTrk=0;
00246 maxTime=minTime + 2 * 60000L;
00247 minTime=maxTime;
00248 while (trk<info->ntracks)
00249 {
00250 if (tracks[trk]->absMsOfNextEvent()<minTime)
00251 {
00252 minTrk=trk;
00253 minTime=tracks[minTrk]->absMsOfNextEvent();
00254 }
00255 trk++;
00256 }
00257 if ((minTime==maxTime))
00258 {
00259 parsing=0;
00260 #ifdef MIDFILEDEBUG
00261 printf("END of parsing\n");
00262 #endif
00263 }
00264 else
00265 {
00266 trk=0;
00267 while (trk<info->ntracks)
00268 {
00269 tracks[trk]->currentMs(minTime);
00270 trk++;
00271 }
00272 }
00273 trk=minTrk;
00274 tracks[trk]->readEvent(ev);
00275
00276 switch (ev->command)
00277 {
00278 case (MIDI_NOTEON) :
00279 if (ev->chn!=PERCUSSION_CHANNEL)
00280 info->patchesUsed[pgminchannel[ev->chn]]++;
00281 else
00282 info->patchesUsed[ev->note+128]++;
00283 break;
00284 case (MIDI_PGM_CHANGE) :
00285 pgminchannel[ev->chn]=(ev->patch);
00286 break;
00287 case (MIDI_SYSTEM_PREFIX) :
00288 if (((ev->command|ev->chn)==META_EVENT)&&(ev->d1==ME_SET_TEMPO))
00289 {
00290 tempo=(ulong)(((ev->data[0]<<16)|(ev->data[1]<<8)|(ev->data[2])) * ratioTempo);
00291 for (j=0;j<info->ntracks;j++)
00292 {
00293 tracks[j]->changeTempo(tempo);
00294 }
00295 }
00296 break;
00297 }
00298 }
00299
00300 delete ev;
00301 info->millisecsTotal=prevms;
00302
00303 for (i=0;i<info->ntracks;i++)
00304 {
00305 tracks[i]->init();
00306 }
00307
00308 #ifdef MIDFILEDEBUG
00309 printf("info.ticksTotal = %ld \n",info->ticksTotal);
00310 printf("info.ticksPlayed= %ld \n",info->ticksPlayed);
00311 printf("info.millisecsTotal = %g \n",info->millisecsTotal);
00312 printf("info.TicksPerCN = %d \n",info->ticksPerCuarterNote);
00313 #endif
00314
00315 }
00316
00317
00318 void parsePatchesUsed(MidiTrack **tracks,MidiFileInfo *info,int gm)
00319 {
00320 int i;
00321 for (i=0;i<256;i++)
00322 {
00323 info->patchesUsed[i]=0;
00324 }
00325 int parsing=1;
00326 int trk,minTrk;
00327 ulong tempo=500000;
00328
00329 #ifdef MIDFILEDEBUG
00330 printf("Parsing for patches ...\n");
00331 #endif
00332
00333 int j;
00334 for (i=0;i<info->ntracks;i++)
00335 {
00336 tracks[i]->init();
00337 }
00338 double prevms=0;
00339 double minTime=0;
00340 double maxTime;
00341 ulong tmp;
00342 MidiEvent *ev=new MidiEvent;
00343 int pgminchannel[16];
00344 for (i=0;i<16;i++)
00345 {
00346 pgminchannel[i]=0;
00347 }
00348
00349 while (parsing)
00350 {
00351 prevms=minTime;
00352 trk=0;
00353 minTrk=0;
00354 maxTime=minTime + 2 * 60000L;
00355 minTime=maxTime;
00356 while (trk<info->ntracks)
00357 {
00358 if (tracks[trk]->absMsOfNextEvent()<minTime)
00359 {
00360 minTrk=trk;
00361 minTime=tracks[minTrk]->absMsOfNextEvent();
00362 }
00363 trk++;
00364 }
00365 if ((minTime==maxTime))
00366 {
00367 parsing=0;
00368 #ifdef MIDFILEDEBUG
00369 printf("END of parsing for patches\n");
00370 #endif
00371 }
00372 else
00373 {
00374 trk=0;
00375 while (trk<info->ntracks)
00376 {
00377 tracks[trk]->currentMs(minTime);
00378 trk++;
00379 }
00380 }
00381 trk=minTrk;
00382 tracks[trk]->readEvent(ev);
00383 switch (ev->command)
00384 {
00385 case (MIDI_NOTEON) :
00386 if (ev->chn!=PERCUSSION_CHANNEL)
00387 info->patchesUsed[pgminchannel[ev->chn]]++;
00388 else
00389 info->patchesUsed[ev->note+128]++;
00390 break;
00391 case (MIDI_PGM_CHANGE) :
00392 pgminchannel[ev->chn]=(gm==1)?(ev->patch):(MT32toGM[ev->patch]);
00393 break;
00394 case (MIDI_SYSTEM_PREFIX) :
00395 if (((ev->command|ev->chn)==META_EVENT)&&(ev->d1==ME_SET_TEMPO))
00396 {
00397 if (tempoToMetronomeTempo(tmp=((ev->data[0]<<16)|(ev->data[1]<<8)|(ev->data[2])))>=8)
00398 {
00399 tempo=tmp;
00400
00401 for (j=0;j<info->ntracks;j++)
00402 {
00403 tracks[j]->changeTempo(tempo);
00404 }
00405 }
00406 }
00407 break;
00408 }
00409 }
00410
00411 delete ev;
00412
00413 for (i=0;i<info->ntracks;i++)
00414 {
00415 tracks[i]->init();
00416 }
00417
00418 }
00419
00420 int fsearch(FILE *fh,const char *text,long *ptr)
00421
00422
00423
00424
00425 {
00426 if ((text==NULL)||(text[0]==0)) return 0;
00427 char buf[1024];
00428 char tmp[256];
00429 long pos;
00430 int l=strlen(text);
00431 int i,k,r;
00432 while (!feof(fh))
00433 {
00434 pos=ftell(fh);
00435 k=fread(buf,1,1024,fh);
00436 i=0;
00437 while (i<k)
00438 {
00439 if (buf[i]==text[0])
00440 {
00441 if (k-i>=l)
00442 r=strncmp(text,&buf[i],l);
00443 else
00444 {
00445 fseek(fh,pos+i,SEEK_SET);
00446 if (fread(tmp,1,l,fh)<(uint)l) return 0;
00447 fseek(fh,pos+k,SEEK_SET);
00448 r=strncmp(text,tmp,l);
00449 }
00450 if (r==0)
00451 {
00452 if (ptr!=NULL) *ptr=pos+i;
00453 return 1;
00454 }
00455 }
00456 i++;
00457 }
00458 }
00459 return 0;
00460 }