| # libkmid Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# 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 }

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