libkmid Library API Documentation

midfile.cc

00001 /**************************************************************************
00002 
00003     midfile.cc - function which reads a midi file,and creates the track classes
00004     This file is part of LibKMid 0.9.5
00005     Copyright (C) 1997,98,99,2000  Antonio Larrosa Jimenez
00006     LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libkmid.html
00007 
00008     This library is free software; you can redistribute it and/or
00009     modify it under the terms of the GNU Library General Public
00010     License as published by the Free Software Foundation; either
00011     version 2 of the License, or (at your option) any later version.
00012  
00013     This library is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016     Library General Public License for more details.
00017  
00018     You should have received a copy of the GNU Library General Public License
00019     along with this library; see the file COPYING.LIB.  If not, write to
00020     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00021     Boston, MA 02111-1307, USA.
00022 
00023     Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
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 /* This function gives the metronome tempo, from a tempo data as found in
00043    a midi file */
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   // Returns 0 if OK, 1 if error (tmpname not set)
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   //if (pclose(infile) != 0) fprintf(stderr,"Error : pclose failed\n");
00096   // Is it right for pclose to always fail ?
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             //                   printf("setTempo %ld\n",tempo);
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   // Search for "text" throught the fh file and then returns :
00422   // text MUST BE smaller than 256 characters
00423   //   0 if not was found 
00424   //   1 if it was found and in ptr (if !=NULL) the position where text begins.
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 }
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:28:29 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001