• 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 
40 int 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 */
44 double tempoToMetronomeTempo(ulong x)
45 {
46  return 60/((double)x/1000000);
47 }
48 
49 double metronomeTempoToTempo(ulong x)
50 {
51  return ((double)60*x)/1000000;
52 }
53 
54 int 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 
102 MidiTrack **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 
205 void 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 
318 void 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 
420 int 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.1
This website is maintained by Timothy Pearson.