• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • libtdemid
 

libtdemid

  • libtdemid
midfile.cpp
1/**************************************************************************
2
3 midfile.cpp - function which reads a midi file,and creates the track classes
4 This file is part of LibKMid 0.9.5
5 Copyright (C) 1997,98,99,2000 Antonio Larrosa Jimenez
6 LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libtdemid.html
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA.
22
23 Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>
24
25***************************************************************************/
26#include "midfile.h"
27#include <string.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <unistd.h>
31#include "sndcard.h"
32#include "midispec.h"
33#include "mt32togm.h"
34#include "sys/stat.h"
35#include <config.h>
36
37#include <tdeprocess.h>
38#include <tqfile.h>
39
40int fsearch(FILE *fh,const char *text,long *ptr);
41
42/* This function gives the metronome tempo, from a tempo data as found in
43 a midi file */
44double tempoToMetronomeTempo(ulong x)
45{
46 return 60/((double)x/1000000);
47}
48
49double metronomeTempoToTempo(ulong x)
50{
51 return ((double)60*x)/1000000;
52}
53
54int uncompressFile(const char *gzname, char *tmpname)
55 // Returns 0 if OK, 1 if error (tmpname not set)
56{
57 TQString cmd("gzip -dc " + TDEProcess::quote(gzname));
58 FILE *infile = popen( TQFile::encodeName(cmd).data(), "r");
59 if (infile==NULL) {
60 fprintf(stderr,"ERROR : popen failed : %s\n",TQFile::encodeName(cmd).data());
61 return 1;
62 }
63 strcpy(tmpname, "/tmp/KMid.XXXXXXXXXX");
64 int fd = mkstemp(tmpname);
65 if (fd == -1)
66 {
67 pclose(infile);
68 return 1;
69 }
70 FILE *outfile= fdopen(fd,"wb");
71 if (outfile==NULL)
72 {
73 pclose(infile);
74 return 1;
75 }
76 int n=getc(infile);
77 if (n==EOF)
78 {
79 pclose(infile);
80 fclose(outfile);
81 unlink(tmpname);
82 return 1;
83 }
84 fputc(n,outfile);
85 int buf[BUFSIZ];
86 n = fread(buf, 1, BUFSIZ, infile);
87 while (n>0)
88 {
89 fwrite(buf, 1, n, outfile);
90 n = fread(buf, 1, BUFSIZ, infile);
91 }
92
93 pclose(infile);
94
95 //if (pclose(infile) != 0) fprintf(stderr,"Error : pclose failed\n");
96 // Is it right for pclose to always fail ?
97
98 fclose(outfile);
99 return 0;
100}
101
102MidiTrack **readMidiFile( const char *name, MidiFileInfo *info, int &ok)
103{
104 ok=1;
105 MidiTrack **tracks;
106
107 struct stat buf;
108 if (stat(name,&buf) || !S_ISREG(buf.st_mode))
109 {
110 fprintf(stderr,"ERROR: %s is not a regular file\n",name);
111 ok=-6;
112 return NULL;
113 }
114
115 FILE *fh=fopen(name,"rb");
116 if (fh==NULL)
117 {
118 fprintf(stderr,"ERROR: Can't open file %s\n",name);
119 ok=-1;
120 return NULL;
121 }
122 char text[4];
123 text[0] = 0;
124 fread(text,1,4,fh);
125 if ((strncmp(text,"MThd",4)!=0)&&(strcmp(&name[strlen(name)-3],".gz")==0))
126 {
127 fclose(fh);
128 char tempname[200];
129 fprintf(stderr,"Trying to open zipped midi file...\n");
130 if (uncompressFile(name,tempname)!=0)
131 {
132 fprintf(stderr,"ERROR: %s is not a (zipped) midi file\n",name);
133 ok=-2;
134 return NULL;
135 }
136 fh=fopen(tempname,"rb");
137 fread(text,1,4,fh);
138 unlink(tempname);
139 }
140
141 if (strncmp(text,"MThd",4)!=0)
142 {
143 fseek(fh,0,SEEK_SET);
144 long pos;
145 if (fsearch(fh,"MThd",&pos)==0)
146 {
147 fclose(fh);
148 fprintf(stderr,"ERROR: %s is not a midi file.\n",name);
149 ok=-2;
150 return NULL;
151 }
152 fseek(fh,pos,SEEK_SET);
153 fread(text,1,4,fh);
154 }
155 long header_size=readLong(fh);
156 info->format=readShort(fh);
157 info->ntracks=readShort(fh);
158 info->ticksPerCuarterNote=readShort(fh);
159 if (info->ticksPerCuarterNote<0)
160 {
161 fprintf(stderr,"ERROR: Ticks per cuarter note is negative !\n");
162 fprintf(stderr,"Please report this error to : larrosa@kde.org\n");
163 fclose(fh);
164 ok=-3;
165 return NULL;
166 }
167 if (header_size>6) fseek(fh,header_size-6,SEEK_CUR);
168 tracks=new MidiTrack*[info->ntracks];
169 if (tracks==NULL)
170 {
171 fprintf(stderr,"ERROR: Not enough memory\n");
172 fclose(fh);
173 ok=-4;
174 return NULL;
175 }
176 int i=0;
177 while (i<info->ntracks)
178 {
179 fread(text,1,4,fh);
180 if (strncmp(text,"MTrk",4)!=0)
181 {
182 fprintf(stderr,"ERROR: Not a well built midi file\n");
183 fprintf(stderr,"%s",text);
184 fclose(fh);
185 ok=-5;
186 return NULL;
187 }
188 tracks[i]=new MidiTrack(fh,info->ticksPerCuarterNote,i);
189 if (tracks[i]==NULL)
190 {
191 fprintf(stderr,"ERROR: Not enough memory");
192 fclose(fh);
193 ok=-4;
194 return NULL;
195 }
196 i++;
197 }
198
199 fclose(fh);
200
201 return tracks;
202
203}
204
205void parseInfoData(MidiFileInfo *info,MidiTrack **tracks,float ratioTempo)
206{
207
208 info->ticksTotal=0;
209 info->millisecsTotal=0.0;
210 info->ticksPlayed=0;
211 int i;
212 for (i=0;i<256;i++)
213 {
214 info->patchesUsed[i]=0;
215 }
216
217 int parsing=1;
218 int trk,minTrk;
219 ulong tempo=(ulong)(500000 * ratioTempo);
220
221#ifdef MIDFILEDEBUG
222 printf("Parsing 1 ...\n");
223#endif
224
225 int pgminchannel[16];
226 for (i=0;i<16;i++)
227 {
228 pgminchannel[i]=0;
229 }
230
231 int j;
232 for (i=0;i<info->ntracks;i++)
233 {
234 tracks[i]->init();
235 tracks[i]->changeTempo(tempo);
236 }
237 double prevms=0;
238 double minTime=0;
239 double maxTime;
240 MidiEvent *ev=new MidiEvent;
241 while (parsing)
242 {
243 prevms=minTime;
244 trk=0;
245 minTrk=0;
246 maxTime=minTime + 2 * 60000L;
247 minTime=maxTime;
248 while (trk<info->ntracks)
249 {
250 if (tracks[trk]->absMsOfNextEvent()<minTime)
251 {
252 minTrk=trk;
253 minTime=tracks[minTrk]->absMsOfNextEvent();
254 }
255 trk++;
256 }
257 if ((minTime==maxTime))
258 {
259 parsing=0;
260#ifdef MIDFILEDEBUG
261 printf("END of parsing\n");
262#endif
263 }
264 else
265 {
266 trk=0;
267 while (trk<info->ntracks)
268 {
269 tracks[trk]->currentMs(minTime);
270 trk++;
271 }
272 }
273 trk=minTrk;
274 tracks[trk]->readEvent(ev);
275
276 switch (ev->command)
277 {
278 case (MIDI_NOTEON) :
279 if (ev->chn!=PERCUSSION_CHANNEL)
280 info->patchesUsed[pgminchannel[ev->chn]]++;
281 else
282 info->patchesUsed[ev->note+128]++;
283 break;
284 case (MIDI_PGM_CHANGE) :
285 pgminchannel[ev->chn]=(ev->patch);
286 break;
287 case (MIDI_SYSTEM_PREFIX) :
288 if (((ev->command|ev->chn)==META_EVENT)&&(ev->d1==ME_SET_TEMPO))
289 {
290 tempo=(ulong)(((ev->data[0]<<16)|(ev->data[1]<<8)|(ev->data[2])) * ratioTempo);
291 for (j=0;j<info->ntracks;j++)
292 {
293 tracks[j]->changeTempo(tempo);
294 }
295 }
296 break;
297 }
298 }
299
300 delete ev;
301 info->millisecsTotal=prevms;
302
303 for (i=0;i<info->ntracks;i++)
304 {
305 tracks[i]->init();
306 }
307
308#ifdef MIDFILEDEBUG
309 printf("info.ticksTotal = %ld \n",info->ticksTotal);
310 printf("info.ticksPlayed= %ld \n",info->ticksPlayed);
311 printf("info.millisecsTotal = %g \n",info->millisecsTotal);
312 printf("info.TicksPerCN = %d \n",info->ticksPerCuarterNote);
313#endif
314
315}
316
317
318void parsePatchesUsed(MidiTrack **tracks,MidiFileInfo *info,int gm)
319{
320 int i;
321 for (i=0;i<256;i++)
322 {
323 info->patchesUsed[i]=0;
324 }
325 int parsing=1;
326 int trk,minTrk;
327 ulong tempo=500000;
328
329#ifdef MIDFILEDEBUG
330 printf("Parsing for patches ...\n");
331#endif
332
333 int j;
334 for (i=0;i<info->ntracks;i++)
335 {
336 tracks[i]->init();
337 }
338 double prevms=0;
339 double minTime=0;
340 double maxTime;
341 ulong tmp;
342 MidiEvent *ev=new MidiEvent;
343 int pgminchannel[16];
344 for (i=0;i<16;i++)
345 {
346 pgminchannel[i]=0;
347 }
348
349 while (parsing)
350 {
351 prevms=minTime;
352 trk=0;
353 minTrk=0;
354 maxTime=minTime + 2 * 60000L;
355 minTime=maxTime;
356 while (trk<info->ntracks)
357 {
358 if (tracks[trk]->absMsOfNextEvent()<minTime)
359 {
360 minTrk=trk;
361 minTime=tracks[minTrk]->absMsOfNextEvent();
362 }
363 trk++;
364 }
365 if ((minTime==maxTime))
366 {
367 parsing=0;
368#ifdef MIDFILEDEBUG
369 printf("END of parsing for patches\n");
370#endif
371 }
372 else
373 {
374 trk=0;
375 while (trk<info->ntracks)
376 {
377 tracks[trk]->currentMs(minTime);
378 trk++;
379 }
380 }
381 trk=minTrk;
382 tracks[trk]->readEvent(ev);
383 switch (ev->command)
384 {
385 case (MIDI_NOTEON) :
386 if (ev->chn!=PERCUSSION_CHANNEL)
387 info->patchesUsed[pgminchannel[ev->chn]]++;
388 else
389 info->patchesUsed[ev->note+128]++;
390 break;
391 case (MIDI_PGM_CHANGE) :
392 pgminchannel[ev->chn]=(gm==1)?(ev->patch):(MT32toGM[ev->patch]);
393 break;
394 case (MIDI_SYSTEM_PREFIX) :
395 if (((ev->command|ev->chn)==META_EVENT)&&(ev->d1==ME_SET_TEMPO))
396 {
397 if (tempoToMetronomeTempo(tmp=((ev->data[0]<<16)|(ev->data[1]<<8)|(ev->data[2])))>=8)
398 {
399 tempo=tmp;
400 // printf("setTempo %ld\n",tempo);
401 for (j=0;j<info->ntracks;j++)
402 {
403 tracks[j]->changeTempo(tempo);
404 }
405 }
406 }
407 break;
408 }
409 }
410
411 delete ev;
412
413 for (i=0;i<info->ntracks;i++)
414 {
415 tracks[i]->init();
416 }
417
418}
419
420int fsearch(FILE *fh,const char *text,long *ptr)
421 // Search for "text" through the fh file and then returns :
422 // text MUST BE smaller than 256 characters
423 // 0 if not was found
424 // 1 if it was found and in ptr (if !=NULL) the position where text begins.
425{
426 if ((text==NULL)||(text[0]==0)) return 0;
427 char buf[1024];
428 char tmp[256];
429 long pos;
430 int l=strlen(text);
431 int i,k,r;
432 while (!feof(fh))
433 {
434 pos=ftell(fh);
435 k=fread(buf,1,1024,fh);
436 i=0;
437 while (i<k)
438 {
439 if (buf[i]==text[0])
440 {
441 if (k-i>=l)
442 r=strncmp(text,&buf[i],l);
443 else
444 {
445 fseek(fh,pos+i,SEEK_SET);
446 if (fread(tmp,1,l,fh)<(uint)l) return 0;
447 fseek(fh,pos+k,SEEK_SET);
448 r=strncmp(text,tmp,l);
449 }
450 if (r==0)
451 {
452 if (ptr!=NULL) *ptr=pos+i;
453 return 1;
454 }
455 }
456 i++;
457 }
458 }
459 return 0;
460}
MidiTrack
Stores a MIDI track.
Definition: track.h:127
MidiTrack::readEvent
void readEvent(MidiEvent *ev)
Reads the event at the iterator position, and puts it on the structure pointed to by ev.
Definition: track.cpp:190
MidiTrack::init
void init(void)
Initializes the iterator.
Definition: track.cpp:519
MidiTrack::currentMs
int currentMs(double ms)
Returns the current millisecond which the iterator is at.
Definition: track.cpp:174
MidiTrack::changeTempo
void changeTempo(ulong t)
Change the tempo of the song.
Definition: track.cpp:544
MidiTrack::absMsOfNextEvent
double absMsOfNextEvent(void)
Returns the absolute number of milliseconds of the next event.
Definition: track.h:212
TDEProcess::quote
static TQString quote(const TQString &arg)
MidiEvent
An structure that represents a MIDI event.
Definition: track.h:38
MidiEvent::command
uchar command
MIDI Command.
Definition: track.h:44
MidiEvent::chn
uchar chn
Channel.
Definition: track.h:49
MidiEvent::note
uchar note
Note.
Definition: track.h:54
MidiEvent::patch
uchar patch
Patch (if command was a change patch command)
Definition: track.h:64
MidiEvent::d1
uchar d1
Data 1.
Definition: track.h:74
MidiEvent::data
uchar * data
The data for commands like text, sysex, etc.
Definition: track.h:109
MidiFileInfo
Contains all the information about a MIDI file.
Definition: midfile.h:40
MidiFileInfo::format
int format
Format of MIDI file.
Definition: midfile.h:44
MidiFileInfo::ticksTotal
ulong ticksTotal
Total number of MIDI ticks.
Definition: midfile.h:59
MidiFileInfo::millisecsTotal
double millisecsTotal
Total number of milliseconds.
Definition: midfile.h:64
MidiFileInfo::ntracks
int ntracks
Number of tracks.
Definition: midfile.h:49
MidiFileInfo::patchesUsed
int patchesUsed[256]
Patches used in the MIDI file.
Definition: midfile.h:77
MidiFileInfo::ticksPerCuarterNote
int ticksPerCuarterNote
Ticks per cuarter note.
Definition: midfile.h:54

libtdemid

Skip menu "libtdemid"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

libtdemid

Skip menu "libtdemid"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdeioslave
  •   http
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for libtdemid by doxygen 1.9.4
This website is maintained by Timothy Pearson.