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 }