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" through 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 libkmid Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Sep 29 09:41:37 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003