comparison src/midifile.c @ 0:785057719d9b

Import.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 05 Aug 2013 12:25:43 +0300
parents
children 4df6d9714314
comparison
equal deleted inserted replaced
-1:000000000000 0:785057719d9b
1 /*
2 * midiFile.c - A general purpose midi file handling library. This code
3 * can read and write MIDI files in formats 0 and 1.
4 * Version 1.4
5 *
6 * AUTHOR: Steven Goodwin (StevenGoodwin@gmail.com)
7 * Copyright 1998-2010, Steven Goodwin
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License,or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #ifndef __APPLE__
28 #include <malloc.h>
29 #endif
30 #include "midifile.h"
31
32 /*
33 ** Internal Data Structures
34 */
35 typedef struct {
36 BYTE note, chn;
37 BYTE valid, p2;
38 DWORD end_pos;
39 } MIDI_LAST_NOTE;
40
41 typedef struct {
42 BYTE *ptr;
43 BYTE *pBase;
44 BYTE *pEnd;
45
46 DWORD pos;
47 DWORD dt;
48 /* For Reading MIDI Files */
49 DWORD sz; /* size of whole iTrack */
50 /* For Writing MIDI Files */
51 DWORD iBlockSize; /* max size of track */
52 BYTE iDefaultChannel; /* use for write only */
53 BYTE last_status; /* used for running status */
54
55 MIDI_LAST_NOTE LastNote[MAX_TRACK_POLYPHONY];
56 } MIDI_FILE_TRACK;
57
58 typedef struct {
59 DWORD iHeaderSize;
60 /**/
61 WORD iVersion; /* 0, 1 or 2 */
62 WORD iNumTracks; /* number of tracks... (will be 1 for MIDI type 0) */
63 WORD PPQN; /* pulses per quarter note */
64 } MIDI_HEADER;
65
66 typedef struct {
67 FILE *pFile;
68 BOOL bOpenForWriting;
69
70 MIDI_HEADER Header;
71 BYTE *ptr; /* to whole data block */
72 DWORD file_sz;
73
74 MIDI_FILE_TRACK Track[MAX_MIDI_TRACKS];
75 } _MIDI_FILE;
76
77
78 /*
79 ** Internal Functions
80 */
81 #define DT_DEF 32 /* assume maximum delta-time + msg is no more than 32 bytes */
82 #define SWAP_WORD(w) (WORD)(((w)>>8)|((w)<<8))
83 #define SWAP_DWORD(d) (DWORD)((d)>>24)|(((d)>>8)&0xff00)|(((d)<<8)&0xff0000)|(((d)<<24))
84
85 #define _VAR_CAST _MIDI_FILE *pMF = (_MIDI_FILE *)_pMF
86 #define IsFilePtrValid(pMF) (pMF)
87 #define IsTrackValid(_x) (_midiValidateTrack(pMF, _x))
88 #define IsChannelValid(_x) ((_x)>=1 && (_x)<=16)
89 #define IsNoteValid(_x) ((_x)>=0 && (_x)<128)
90 #define IsMessageValid(_x) ((_x)>=msgNoteOff && (_x)<=msgMetaEvent)
91
92
93 static BOOL _midiValidateTrack(const _MIDI_FILE *pMF, int iTrack)
94 {
95 if (!IsFilePtrValid(pMF)) return FALSE;
96
97 if (pMF->bOpenForWriting)
98 {
99 if (iTrack < 0 || iTrack >= MAX_MIDI_TRACKS)
100 return FALSE;
101 }
102 else /* open for reading */
103 {
104 if (!pMF->ptr)
105 return FALSE;
106
107 if (iTrack < 0 || iTrack>=pMF->Header.iNumTracks)
108 return FALSE;
109 }
110
111 return TRUE;
112 }
113
114 static BYTE *_midiWriteVarLen(BYTE *ptr, int n)
115 {
116 register long buffer;
117 register long value=n;
118
119 buffer = value & 0x7f;
120 while ((value >>= 7) > 0)
121 {
122 buffer <<= 8;
123 buffer |= 0x80;
124 buffer += (value & 0x7f);
125 }
126
127 while (TRUE)
128 {
129 *ptr++ = (BYTE)buffer;
130 if (buffer & 0x80)
131 buffer >>= 8;
132 else
133 break;
134 }
135
136 return(ptr);
137 }
138
139 /* Return a ptr to valid block of memory to store a message
140 ** of up to sz_reqd bytes
141 */
142 static BYTE *_midiGetPtr(_MIDI_FILE *pMF, int iTrack, int sz_reqd)
143 {
144 const DWORD mem_sz_inc = 8092; /* arbitary */
145 BYTE *ptr;
146 int curr_offset;
147 MIDI_FILE_TRACK *pTrack = &pMF->Track[iTrack];
148
149 ptr = pTrack->ptr;
150 if (ptr == NULL || ptr+sz_reqd > pTrack->pEnd) /* need more RAM! */
151 {
152 curr_offset = ptr-pTrack->pBase;
153 if ((ptr = (BYTE *)realloc(pTrack->pBase, mem_sz_inc+pTrack->iBlockSize)))
154 {
155 pTrack->pBase = ptr;
156 pTrack->iBlockSize += mem_sz_inc;
157 pTrack->pEnd = ptr+pTrack->iBlockSize;
158 /* Move new ptr to continue data entry: */
159 pTrack->ptr = ptr+curr_offset;
160 ptr += curr_offset;
161 }
162 else
163 {
164 /* NO MEMORY LEFT */
165 return NULL;
166 }
167 }
168
169 return ptr;
170 }
171
172
173 static int _midiGetLength(int ppqn, int iNoteLen, BOOL bOverride)
174 {
175 int length = ppqn;
176
177 if (bOverride)
178 {
179 length = iNoteLen;
180 }
181 else
182 {
183 switch(iNoteLen)
184 {
185 case MIDI_NOTE_DOTTED_MINIM:
186 length *= 3;
187 break;
188
189 case MIDI_NOTE_DOTTED_CROCHET:
190 length *= 3;
191 length /= 2;
192 break;
193
194 case MIDI_NOTE_DOTTED_QUAVER:
195 length *= 3;
196 length /= 4;
197 break;
198
199 case MIDI_NOTE_DOTTED_SEMIQUAVER:
200 length *= 3;
201 length /= 8;
202 break;
203
204 case MIDI_NOTE_DOTTED_SEMIDEMIQUAVER:
205 length *= 3;
206 length /= 16;
207 break;
208
209 case MIDI_NOTE_BREVE:
210 length *= 4;
211 break;
212
213 case MIDI_NOTE_MINIM:
214 length *= 2;
215 break;
216
217 case MIDI_NOTE_QUAVER:
218 length /= 2;
219 break;
220
221 case MIDI_NOTE_SEMIQUAVER:
222 length /= 4;
223 break;
224
225 case MIDI_NOTE_SEMIDEMIQUAVER:
226 length /= 8;
227 break;
228
229 case MIDI_NOTE_TRIPLE_CROCHET:
230 length *= 2;
231 length /= 3;
232 break;
233 }
234 }
235
236 return length;
237 }
238
239 /*
240 ** midiFile* Functions
241 */
242 MIDI_FILE *midiFileCreate(const char *pFilename, BOOL bOverwriteIfExists)
243 {
244 _MIDI_FILE *pMF = (_MIDI_FILE *)malloc(sizeof(_MIDI_FILE));
245 int i;
246
247 if (!pMF) return NULL;
248
249 if (!bOverwriteIfExists)
250 {
251 if ((pMF->pFile = fopen(pFilename, "r")))
252 {
253 fclose(pMF->pFile);
254 free(pMF);
255 return NULL;
256 }
257 }
258
259 if ((pMF->pFile = fopen(pFilename, "wb+")))
260 {/*empty*/}
261 else
262 {
263 free((void *)pMF);
264 return NULL;
265 }
266
267 pMF->bOpenForWriting = TRUE;
268 pMF->Header.PPQN = MIDI_PPQN_DEFAULT;
269 pMF->Header.iVersion = MIDI_VERSION_DEFAULT;
270
271 for(i=0;i<MAX_MIDI_TRACKS;++i)
272 {
273 pMF->Track[i].pos = 0;
274 pMF->Track[i].ptr = NULL;
275 pMF->Track[i].pBase = NULL;
276 pMF->Track[i].pEnd = NULL;
277 pMF->Track[i].iBlockSize = 0;
278 pMF->Track[i].dt = 0;
279 pMF->Track[i].iDefaultChannel = (BYTE)(i & 0xf);
280
281 memset(pMF->Track[i].LastNote, '\0', sizeof(pMF->Track[i].LastNote));
282 }
283
284 return (MIDI_FILE *)pMF;
285 }
286
287 int midiFileSetTracksDefaultChannel(MIDI_FILE *_pMF, int iTrack, int iChannel)
288 {
289 int prev;
290
291 _VAR_CAST;
292 if (!IsFilePtrValid(pMF)) return 0;
293 if (!IsTrackValid(iTrack)) return 0;
294 if (!IsChannelValid(iChannel)) return 0;
295
296 /* For programmer each, iChannel is between 1 & 16 - but MIDI uses
297 ** 0-15. Thus, the fudge factor of 1 :)
298 */
299 prev = pMF->Track[iTrack].iDefaultChannel+1;
300 pMF->Track[iTrack].iDefaultChannel = (BYTE)(iChannel-1);
301 return prev;
302 }
303
304 int midiFileGetTracksDefaultChannel(const MIDI_FILE *_pMF, int iTrack)
305 {
306 _VAR_CAST;
307 if (!IsFilePtrValid(pMF)) return 0;
308 if (!IsTrackValid(iTrack)) return 0;
309
310 return pMF->Track[iTrack].iDefaultChannel+1;
311 }
312
313 int midiFileSetPPQN(MIDI_FILE *_pMF, int PPQN)
314 {
315 int prev;
316
317 _VAR_CAST;
318 if (!IsFilePtrValid(pMF)) return MIDI_PPQN_DEFAULT;
319 prev = pMF->Header.PPQN;
320 pMF->Header.PPQN = (WORD)PPQN;
321 return prev;
322 }
323
324 int midiFileGetPPQN(const MIDI_FILE *_pMF)
325 {
326 _VAR_CAST;
327 if (!IsFilePtrValid(pMF)) return MIDI_PPQN_DEFAULT;
328 return (int)pMF->Header.PPQN;
329 }
330
331 int midiFileSetVersion(MIDI_FILE *_pMF, int iVersion)
332 {
333 int prev;
334
335 _VAR_CAST;
336 if (!IsFilePtrValid(pMF)) return MIDI_VERSION_DEFAULT;
337 if (iVersion<0 || iVersion>2) return MIDI_VERSION_DEFAULT;
338 prev = pMF->Header.iVersion;
339 pMF->Header.iVersion = (WORD)iVersion;
340 return prev;
341 }
342
343 int midiFileGetVersion(const MIDI_FILE *_pMF)
344 {
345 _VAR_CAST;
346 if (!IsFilePtrValid(pMF)) return MIDI_VERSION_DEFAULT;
347 return pMF->Header.iVersion;
348 }
349
350 MIDI_FILE *midiFileOpen(const char *pFilename)
351 {
352 FILE *fp = fopen(pFilename, "rb");
353 _MIDI_FILE *pMF = NULL;
354 BYTE *ptr;
355 BOOL bValidFile=FALSE;
356 long size;
357
358 if (fp)
359 {
360 if ((pMF = (_MIDI_FILE *)malloc(sizeof(_MIDI_FILE))))
361 {
362 fseek(fp, 0L, SEEK_END);
363 size = ftell(fp);
364 if ((pMF->ptr = (BYTE *)malloc(size)))
365 {
366 fseek(fp, 0L, SEEK_SET);
367 fread(pMF->ptr, sizeof(BYTE), size, fp);
368 /* Is this a valid MIDI file ? */
369 ptr = pMF->ptr;
370 if (*(ptr+0) == 'M' && *(ptr+1) == 'T' &&
371 *(ptr+2) == 'h' && *(ptr+3) == 'd')
372 {
373 DWORD dwData;
374 WORD wData;
375 int i;
376
377 dwData = *((DWORD *)(ptr+4));
378 pMF->Header.iHeaderSize = SWAP_DWORD(dwData);
379
380 wData = *((WORD *)(ptr+8));
381 pMF->Header.iVersion = (WORD)SWAP_WORD(wData);
382
383 wData = *((WORD *)(ptr+10));
384 pMF->Header.iNumTracks = (WORD)SWAP_WORD(wData);
385
386 wData = *((WORD *)(ptr+12));
387 pMF->Header.PPQN = (WORD)SWAP_WORD(wData);
388
389 ptr += pMF->Header.iHeaderSize+8;
390 /*
391 ** Get all tracks
392 */
393 for(i=0;i<MAX_MIDI_TRACKS;++i)
394 {
395 pMF->Track[i].pos = 0;
396 pMF->Track[i].last_status = 0;
397 }
398
399 for(i=0;i<pMF->Header.iNumTracks;++i)
400 {
401 pMF->Track[i].pBase = ptr;
402 pMF->Track[i].ptr = ptr+8;
403 dwData = *((DWORD *)(ptr+4));
404 pMF->Track[i].sz = SWAP_DWORD(dwData);
405 pMF->Track[i].pEnd = ptr+pMF->Track[i].sz+8;
406 ptr += pMF->Track[i].sz+8;
407 }
408
409 pMF->bOpenForWriting = FALSE;
410 pMF->pFile = NULL;
411 bValidFile = TRUE;
412 }
413 }
414 }
415
416 fclose(fp);
417 }
418
419 if (!bValidFile)
420 {
421 if (pMF) free((void *)pMF);
422 return NULL;
423 }
424
425 return (MIDI_FILE *)pMF;
426 }
427
428 typedef struct {
429 int iIdx;
430 int iEndPos;
431 } MIDI_END_POINT;
432
433 static int qs_cmp_pEndPoints(const void *e1, const void *e2)
434 {
435 MIDI_END_POINT *p1 = (MIDI_END_POINT *)e1;
436 MIDI_END_POINT *p2 = (MIDI_END_POINT *)e2;
437
438 return p1->iEndPos-p2->iEndPos;
439 }
440
441 BOOL midiFileFlushTrack(MIDI_FILE *_pMF, int iTrack, BOOL bFlushToEnd, DWORD dwEndTimePos)
442 {
443 int sz;
444 BYTE *ptr;
445 MIDI_END_POINT *pEndPoints;
446 int num, i, mx_pts;
447 BOOL bNoChanges = TRUE;
448
449 _VAR_CAST;
450 if (!IsFilePtrValid(pMF)) return FALSE;
451 if (!_midiValidateTrack(pMF, iTrack)) return FALSE;
452 sz = sizeof(pMF->Track[0].LastNote)/sizeof(pMF->Track[0].LastNote[0]);
453
454 /*
455 ** Flush all
456 */
457 pEndPoints = (MIDI_END_POINT *)malloc(sz * sizeof(MIDI_END_POINT));
458 mx_pts = 0;
459 for(i=0;i<sz;++i)
460 if (pMF->Track[iTrack].LastNote[i].valid)
461 {
462 pEndPoints[mx_pts].iIdx = i;
463 pEndPoints[mx_pts].iEndPos = pMF->Track[iTrack].LastNote[i].end_pos;
464 mx_pts++;
465 }
466
467 if (bFlushToEnd)
468 {
469 if (mx_pts)
470 dwEndTimePos = pEndPoints[mx_pts-1].iEndPos;
471 else
472 dwEndTimePos = pMF->Track[iTrack].pos;
473 }
474
475 if (mx_pts)
476 {
477 /* Sort, smallest first, and add the note off msgs */
478 qsort(pEndPoints, mx_pts, sizeof(MIDI_END_POINT), qs_cmp_pEndPoints);
479
480 i = 0;
481 while ((dwEndTimePos >= (DWORD)pEndPoints[i].iEndPos || bFlushToEnd) && i<mx_pts)
482 {
483 ptr = _midiGetPtr(pMF, iTrack, DT_DEF);
484 if (!ptr)
485 return FALSE;
486
487 num = pEndPoints[i].iIdx; /* get 'LastNote' index */
488
489 ptr = _midiWriteVarLen(ptr, pMF->Track[iTrack].LastNote[num].end_pos - pMF->Track[iTrack].pos);
490 /* msgNoteOn msgNoteOff */
491 *ptr++ = (BYTE)(msgNoteOff | pMF->Track[iTrack].LastNote[num].chn);
492 *ptr++ = pMF->Track[iTrack].LastNote[num].note;
493 *ptr++ = 0;
494
495 pMF->Track[iTrack].LastNote[num].valid = FALSE;
496 pMF->Track[iTrack].pos = pMF->Track[iTrack].LastNote[num].end_pos;
497
498 pMF->Track[iTrack].ptr = ptr;
499
500 ++i;
501 bNoChanges = FALSE;
502 }
503 }
504
505 free((void *)pEndPoints);
506 /*
507 ** Re-calc current position
508 */
509 pMF->Track[iTrack].dt = dwEndTimePos - pMF->Track[iTrack].pos;
510
511 return TRUE;
512 }
513
514 BOOL midiFileSyncTracks(MIDI_FILE *_pMF, int iTrack1, int iTrack2)
515 {
516 int p1, p2;
517
518 _VAR_CAST;
519 if (!IsFilePtrValid(pMF)) return FALSE;
520 if (!IsTrackValid(iTrack1)) return FALSE;
521 if (!IsTrackValid(iTrack2)) return FALSE;
522
523 p1 = pMF->Track[iTrack1].pos + pMF->Track[iTrack1].dt;
524 p2 = pMF->Track[iTrack2].pos + pMF->Track[iTrack2].dt;
525
526 if (p1 < p2) midiTrackIncTime(pMF, iTrack1, p2-p1, TRUE);
527 else if (p2 < p1) midiTrackIncTime(pMF, iTrack2, p1-p2, TRUE);
528
529 return TRUE;
530 }
531
532
533 BOOL midiFileClose(MIDI_FILE *_pMF)
534 {
535 _VAR_CAST;
536 if (!IsFilePtrValid(pMF)) return FALSE;
537
538 if (pMF->bOpenForWriting)
539 {
540 WORD iNumTracks = 0;
541 WORD wTest = 256;
542 BOOL bSwap = FALSE;
543 int i;
544
545 /* Intel processor style-endians need byte swap :( */
546 if (*((BYTE *)&wTest) == 0)
547 bSwap = TRUE;
548
549 /* Flush our buffers */
550 for(i=0;i<MAX_MIDI_TRACKS;++i)
551 {
552 if (pMF->Track[i].ptr)
553 {
554 midiSongAddEndSequence(pMF, i);
555 midiFileFlushTrack(pMF, i, TRUE, 0);
556 iNumTracks++;
557 }
558 }
559 /*
560 ** Header
561 */
562 {
563 const BYTE mthd[4] = {'M', 'T', 'h', 'd'};
564 DWORD dwData;
565 WORD wData;
566 WORD version, PPQN;
567
568 fwrite(mthd, sizeof(BYTE), 4, pMF->pFile);
569 dwData = 6;
570 if (bSwap) dwData = SWAP_DWORD(dwData);
571 fwrite(&dwData, sizeof(DWORD), 1, pMF->pFile);
572
573 wData = (WORD)(iNumTracks==1?pMF->Header.iVersion:1);
574 if (bSwap) version = SWAP_WORD(wData); else version = (WORD)wData;
575 if (bSwap) iNumTracks = SWAP_WORD(iNumTracks);
576 wData = pMF->Header.PPQN;
577 if (bSwap) PPQN = SWAP_WORD(wData); else PPQN = wData;
578 fwrite(&version, sizeof(WORD), 1, pMF->pFile);
579 fwrite(&iNumTracks, sizeof(WORD), 1, pMF->pFile);
580 fwrite(&PPQN, sizeof(WORD), 1, pMF->pFile);
581 }
582 /*
583 ** Track data
584 */
585 for(i=0;i<MAX_MIDI_TRACKS;++i)
586 if (pMF->Track[i].ptr)
587 {
588 const BYTE mtrk[4] = {'M', 'T', 'r', 'k'};
589 DWORD sz, dwData;
590
591 /* Write track header */
592 fwrite(&mtrk, sizeof(BYTE), 4, pMF->pFile);
593
594 /* Write data size */
595 sz = dwData = (int)(pMF->Track[i].ptr - pMF->Track[i].pBase);
596 if (bSwap) sz = SWAP_DWORD(sz);
597 fwrite(&sz, sizeof(DWORD), 1, pMF->pFile);
598
599 /* Write data */
600 fwrite(pMF->Track[i].pBase, sizeof(BYTE), dwData, pMF->pFile);
601
602 /* Free memory */
603 free((void *)pMF->Track[i].pBase);
604 }
605
606 }
607
608 if (pMF->pFile)
609 return fclose(pMF->pFile)?FALSE:TRUE;
610 free((void *)pMF);
611 return TRUE;
612 }
613
614
615 /*
616 ** midiSong* Functions
617 */
618 BOOL midiSongAddSMPTEOffset(MIDI_FILE *_pMF, int iTrack, int iHours, int iMins, int iSecs, int iFrames, int iFFrames)
619 {
620 static BYTE tmp[] = {msgMetaEvent, metaSMPTEOffset, 0x05, 0,0,0,0,0};
621
622 _VAR_CAST;
623 if (!IsFilePtrValid(pMF)) return FALSE;
624 if (!IsTrackValid(iTrack)) return FALSE;
625
626 if (iMins<0 || iMins>59) iMins=0;
627 if (iSecs<0 || iSecs>59) iSecs=0;
628 if (iFrames<0 || iFrames>24) iFrames=0;
629
630 tmp[3] = (BYTE)iHours;
631 tmp[4] = (BYTE)iMins;
632 tmp[5] = (BYTE)iSecs;
633 tmp[6] = (BYTE)iFrames;
634 tmp[7] = (BYTE)iFFrames;
635 return midiTrackAddRaw(pMF, iTrack, sizeof(tmp), tmp, FALSE, 0);
636 }
637
638
639 BOOL midiSongAddSimpleTimeSig(MIDI_FILE *_pMF, int iTrack, int iNom, int iDenom)
640 {
641 return midiSongAddTimeSig(_pMF, iTrack, iNom, iDenom, 24, 8);
642 }
643
644 BOOL midiSongAddTimeSig(MIDI_FILE *_pMF, int iTrack, int iNom, int iDenom, int iClockInMetroTick, int iNotated32nds)
645 {
646 static BYTE tmp[] = {msgMetaEvent, metaTimeSig, 0x04, 0,0,0,0};
647
648 _VAR_CAST;
649 if (!IsFilePtrValid(pMF)) return FALSE;
650 if (!IsTrackValid(iTrack)) return FALSE;
651
652 tmp[3] = (BYTE)iNom;
653 tmp[4] = (BYTE)(MIDI_NOTE_MINIM/iDenom);
654 tmp[5] = (BYTE)iClockInMetroTick;
655 tmp[6] = (BYTE)iNotated32nds;
656 return midiTrackAddRaw(pMF, iTrack, sizeof(tmp), tmp, FALSE, 0);
657 }
658
659 BOOL midiSongAddKeySig(MIDI_FILE *_pMF, int iTrack, tMIDI_KEYSIG iKey)
660 {
661 static BYTE tmp[] = {msgMetaEvent, metaKeySig, 0x02, 0, 0};
662
663 _VAR_CAST;
664 if (!IsFilePtrValid(pMF)) return FALSE;
665 if (!IsTrackValid(iTrack)) return FALSE;
666
667 tmp[3] = (BYTE)((iKey&keyMaskKey)*((iKey&keyMaskNeg)?-1:1));
668 tmp[4] = (BYTE)((iKey&keyMaskMin)?1:0);
669 return midiTrackAddRaw(pMF, iTrack, sizeof(tmp), tmp, FALSE, 0);
670 }
671
672 BOOL midiSongAddTempo(MIDI_FILE *_pMF, int iTrack, int iTempo)
673 {
674 static BYTE tmp[] = {msgMetaEvent, metaSetTempo, 0x03, 0,0,0};
675 int us; /* micro-seconds per qn */
676
677 _VAR_CAST;
678 if (!IsFilePtrValid(pMF)) return FALSE;
679 if (!IsTrackValid(iTrack)) return FALSE;
680
681 us = 60000000L/iTempo;
682 tmp[3] = (BYTE)((us>>16)&0xff);
683 tmp[4] = (BYTE)((us>>8)&0xff);
684 tmp[5] = (BYTE)((us>>0)&0xff);
685 return midiTrackAddRaw(pMF, iTrack, sizeof(tmp), tmp, FALSE, 0);
686 }
687
688 BOOL midiSongAddMIDIPort(MIDI_FILE *_pMF, int iTrack, int iPort)
689 {
690 static BYTE tmp[] = {msgMetaEvent, metaMIDIPort, 1, 0};
691
692 _VAR_CAST;
693 if (!IsFilePtrValid(pMF)) return FALSE;
694 if (!IsTrackValid(iTrack)) return FALSE;
695 tmp[3] = (BYTE)iPort;
696 return midiTrackAddRaw(pMF, iTrack, sizeof(tmp), tmp, FALSE, 0);
697 }
698
699 BOOL midiSongAddEndSequence(MIDI_FILE *_pMF, int iTrack)
700 {
701 static BYTE tmp[] = {msgMetaEvent, metaEndSequence, 0};
702
703 _VAR_CAST;
704 if (!IsFilePtrValid(pMF)) return FALSE;
705 if (!IsTrackValid(iTrack)) return FALSE;
706
707 return midiTrackAddRaw(pMF, iTrack, sizeof(tmp), tmp, FALSE, 0);
708 }
709
710
711 /*
712 ** midiTrack* Functions
713 */
714 BOOL midiTrackAddRaw(MIDI_FILE *_pMF, int iTrack, int data_sz, const BYTE *pData, BOOL bMovePtr, int dt)
715 {
716 MIDI_FILE_TRACK *pTrk;
717 BYTE *ptr;
718 int dtime;
719
720 _VAR_CAST;
721 if (!IsFilePtrValid(pMF)) return FALSE;
722 if (!IsTrackValid(iTrack)) return FALSE;
723
724 pTrk = &pMF->Track[iTrack];
725 ptr = _midiGetPtr(pMF, iTrack, data_sz+DT_DEF);
726 if (!ptr)
727 return FALSE;
728
729 dtime = pTrk->dt;
730 if (bMovePtr)
731 dtime += dt;
732
733 ptr = _midiWriteVarLen(ptr, dtime);
734 memcpy(ptr, pData, data_sz);
735
736 pTrk->pos += dtime;
737 pTrk->dt = 0;
738 pTrk->ptr = ptr+data_sz;
739
740 return TRUE;
741 }
742
743
744 BOOL midiTrackIncTime(MIDI_FILE *_pMF, int iTrack, int iDeltaTime, BOOL bOverridePPQN)
745 {
746 DWORD will_end_at;
747
748 _VAR_CAST;
749 if (!IsFilePtrValid(pMF)) return FALSE;
750 if (!IsTrackValid(iTrack)) return FALSE;
751
752 will_end_at = _midiGetLength(pMF->Header.PPQN, iDeltaTime, bOverridePPQN);
753 will_end_at += pMF->Track[iTrack].pos + pMF->Track[iTrack].dt;
754
755 midiFileFlushTrack(pMF, iTrack, FALSE, will_end_at);
756
757 return TRUE;
758 }
759
760 BOOL midiTrackAddText(MIDI_FILE *_pMF, int iTrack, tMIDI_TEXT iType, const char *pTxt)
761 {
762 BYTE *ptr;
763 int sz;
764
765 _VAR_CAST;
766 if (!IsFilePtrValid(pMF)) return FALSE;
767 if (!IsTrackValid(iTrack)) return FALSE;
768
769 sz = strlen(pTxt);
770 if ((ptr = _midiGetPtr(pMF, iTrack, sz+DT_DEF)))
771 {
772 *ptr++ = 0; /* delta-time=0 */
773 *ptr++ = msgMetaEvent;
774 *ptr++ = (BYTE)iType;
775 ptr = _midiWriteVarLen((BYTE *)ptr, sz);
776 strcpy((char *)ptr, pTxt);
777 pMF->Track[iTrack].ptr = ptr+sz;
778 return TRUE;
779 }
780 else
781 {
782 return FALSE;
783 }
784 }
785
786 BOOL midiTrackSetKeyPressure(MIDI_FILE *pMF, int iTrack, int iNote, int iAftertouch)
787 {
788 return midiTrackAddMsg(pMF, iTrack, msgNoteKeyPressure, iNote, iAftertouch);
789 }
790
791 BOOL midiTrackAddControlChange(MIDI_FILE *pMF, int iTrack, tMIDI_CC iCCType, int iParam)
792 {
793 return midiTrackAddMsg(pMF, iTrack, msgControlChange, iCCType, iParam);
794 }
795
796 BOOL midiTrackAddProgramChange(MIDI_FILE *pMF, int iTrack, int iInstrPatch)
797 {
798 return midiTrackAddMsg(pMF, iTrack, msgSetProgram, iInstrPatch, 0);
799 }
800
801 BOOL midiTrackChangeKeyPressure(MIDI_FILE *pMF, int iTrack, int iDeltaPressure)
802 {
803 return midiTrackAddMsg(pMF, iTrack, msgChangePressure, iDeltaPressure&0x7f, 0);
804 }
805
806 BOOL midiTrackSetPitchWheel(MIDI_FILE *pMF, int iTrack, int iWheelPos)
807 {
808 WORD wheel = (WORD)iWheelPos;
809
810 /* bitshift 7 instead of eight because we're dealing with 7 bit numbers */
811 wheel += MIDI_WHEEL_CENTRE;
812 return midiTrackAddMsg(pMF, iTrack, msgSetPitchWheel, wheel&0x7f, (wheel>>7)&0x7f);
813 }
814
815 BOOL midiTrackAddMsg(MIDI_FILE *_pMF, int iTrack, tMIDI_MSG iMsg, int iParam1, int iParam2)
816 {
817 BYTE *ptr;
818 BYTE data[3];
819 int sz;
820
821 _VAR_CAST;
822 if (!IsFilePtrValid(pMF)) return FALSE;
823 if (!IsTrackValid(iTrack)) return FALSE;
824 if (!IsMessageValid(iMsg)) return FALSE;
825
826 ptr = _midiGetPtr(pMF, iTrack, DT_DEF);
827 if (!ptr)
828 return FALSE;
829
830 data[0] = (BYTE)(iMsg | pMF->Track[iTrack].iDefaultChannel);
831 data[1] = (BYTE)(iParam1 & 0x7f);
832 data[2] = (BYTE)(iParam2 & 0x7f);
833 /*
834 ** Is this msg a single, or double BYTE, prm?
835 */
836 switch(iMsg)
837 {
838 case msgSetProgram: /* only one byte required for these msgs */
839 case msgChangePressure:
840 sz = 2;
841 break;
842
843 default: /* double byte messages */
844 sz = 3;
845 break;
846 }
847
848 return midiTrackAddRaw(pMF, iTrack, sz, data, FALSE, 0);
849
850 }
851
852 BOOL midiTrackAddNote(MIDI_FILE *_pMF, int iTrack, int iNote, int iLength, int iVol, BOOL bAutoInc, BOOL bOverrideLength)
853 {
854 MIDI_FILE_TRACK *pTrk;
855 BYTE *ptr;
856 BOOL bSuccess = FALSE;
857 int i, chn;
858
859 _VAR_CAST;
860 if (!IsFilePtrValid(pMF)) return FALSE;
861 if (!IsTrackValid(iTrack)) return FALSE;
862 if (!IsNoteValid(iNote)) return FALSE;
863
864 pTrk = &pMF->Track[iTrack];
865 ptr = _midiGetPtr(pMF, iTrack, DT_DEF);
866 if (!ptr)
867 return FALSE;
868
869 chn = pTrk->iDefaultChannel;
870 iLength = _midiGetLength(pMF->Header.PPQN, iLength, bOverrideLength);
871
872 for(i=0;i<sizeof(pTrk->LastNote)/sizeof(pTrk->LastNote[0]);++i)
873 if (pTrk->LastNote[i].valid == FALSE)
874 {
875 pTrk->LastNote[i].note = (BYTE)iNote;
876 pTrk->LastNote[i].chn = (BYTE)chn;
877 pTrk->LastNote[i].end_pos = pTrk->pos+pTrk->dt+iLength;
878 pTrk->LastNote[i].valid = TRUE;
879 bSuccess = TRUE;
880
881 ptr = _midiWriteVarLen(ptr, pTrk->dt); /* delta-time */
882 *ptr++ = (BYTE)(msgNoteOn | chn);
883 *ptr++ = (BYTE)iNote;
884 *ptr++ = (BYTE)iVol;
885 break;
886 }
887
888 if (!bSuccess)
889 return FALSE;
890
891 pTrk->ptr = ptr;
892
893 pTrk->pos += pTrk->dt;
894 pTrk->dt = 0;
895
896 if (bAutoInc)
897 return midiTrackIncTime(pMF, iTrack, iLength, bOverrideLength);
898
899 return TRUE;
900 }
901
902 BOOL midiTrackAddRest(MIDI_FILE *_pMF, int iTrack, int iLength, BOOL bOverridePPQN)
903 {
904 _VAR_CAST;
905 if (!IsFilePtrValid(pMF)) return FALSE;
906 if (!IsTrackValid(iTrack)) return FALSE;
907
908 iLength = _midiGetLength(pMF->Header.PPQN, iLength, bOverridePPQN);
909 return midiTrackIncTime(pMF, iTrack, iLength, bOverridePPQN);
910 }
911
912 int midiTrackGetEndPos(MIDI_FILE *_pMF, int iTrack)
913 {
914 _VAR_CAST;
915 if (!IsFilePtrValid(pMF)) return FALSE;
916 if (!IsTrackValid(iTrack)) return FALSE;
917
918 return pMF->Track[iTrack].pos;
919 }
920
921 /*
922 ** midiRead* Functions
923 */
924 static BYTE *_midiReadVarLen(BYTE *ptr, DWORD *num)
925 {
926 register DWORD value;
927 register BYTE c;
928
929 if ((value = *ptr++) & 0x80)
930 {
931 value &= 0x7f;
932 do
933 {
934 value = (value << 7) + ((c = *ptr++) & 0x7f);
935 } while (c & 0x80);
936 }
937 *num = value;
938 return(ptr);
939 }
940
941
942 static BOOL _midiReadTrackCopyData(MIDI_MSG *pMsg, BYTE *ptr, DWORD sz, BOOL bCopyPtrData)
943 {
944 if (sz > pMsg->data_sz)
945 {
946 pMsg->data = (BYTE *)realloc(pMsg->data, sz);
947 pMsg->data_sz = sz;
948 }
949
950 if (!pMsg->data)
951 return FALSE;
952
953 if (bCopyPtrData && ptr)
954 memcpy(pMsg->data, ptr, sz);
955
956 return TRUE;
957 }
958
959 int midiReadGetNumTracks(const MIDI_FILE *_pMF)
960 {
961 _VAR_CAST;
962 return pMF->Header.iNumTracks;
963 }
964
965 BOOL midiReadGetNextMessage(const MIDI_FILE *_pMF, int iTrack, MIDI_MSG *pMsg)
966 {
967 MIDI_FILE_TRACK *pTrack;
968 BYTE *bptr, *pMsgDataPtr;
969 int sz;
970
971 _VAR_CAST;
972 if (!IsTrackValid(iTrack)) return FALSE;
973
974 pTrack = &pMF->Track[iTrack];
975 /* FIXME: Check if there is data on this track first!!! */
976 if (pTrack->ptr >= pTrack->pEnd)
977 return FALSE;
978
979 pTrack->ptr = _midiReadVarLen(pTrack->ptr, &pMsg->dt);
980 pTrack->pos += pMsg->dt;
981
982 pMsg->dwAbsPos = pTrack->pos;
983
984 if (*pTrack->ptr & 0x80) /* Is this is sys message */
985 {
986 pMsg->iType = (tMIDI_MSG)((*pTrack->ptr) & 0xf0);
987 pMsgDataPtr = pTrack->ptr+1;
988
989 /* SysEx & Meta events don't carry channel info, but something
990 ** important in their lower bits that we must keep */
991 if (pMsg->iType == 0xf0)
992 pMsg->iType = (tMIDI_MSG)(*pTrack->ptr);
993 }
994 else /* just data - so use the last msg type */
995 {
996 pMsg->iType = pMsg->iLastMsgType;
997 pMsgDataPtr = pTrack->ptr;
998 }
999
1000 pMsg->iLastMsgType = (tMIDI_MSG)pMsg->iType;
1001 pMsg->iLastMsgChnl = (BYTE)((*pTrack->ptr) & 0x0f)+1;
1002
1003 switch(pMsg->iType)
1004 {
1005 case msgNoteOn:
1006 pMsg->MsgData.NoteOn.iChannel = pMsg->iLastMsgChnl;
1007 pMsg->MsgData.NoteOn.iNote = *(pMsgDataPtr);
1008 pMsg->MsgData.NoteOn.iVolume = *(pMsgDataPtr+1);
1009 pMsg->iMsgSize = 3;
1010 break;
1011
1012 case msgNoteOff:
1013 pMsg->MsgData.NoteOff.iChannel = pMsg->iLastMsgChnl;
1014 pMsg->MsgData.NoteOff.iNote = *(pMsgDataPtr);
1015 pMsg->iMsgSize = 3;
1016 break;
1017
1018 case msgNoteKeyPressure:
1019 pMsg->MsgData.NoteKeyPressure.iChannel = pMsg->iLastMsgChnl;
1020 pMsg->MsgData.NoteKeyPressure.iNote = *(pMsgDataPtr);
1021 pMsg->MsgData.NoteKeyPressure.iPressure = *(pMsgDataPtr+1);
1022 pMsg->iMsgSize = 3;
1023 break;
1024
1025 case msgSetParameter:
1026 pMsg->MsgData.NoteParameter.iChannel = pMsg->iLastMsgChnl;
1027 pMsg->MsgData.NoteParameter.iControl = (tMIDI_CC)*(pMsgDataPtr);
1028 pMsg->MsgData.NoteParameter.iParam = *(pMsgDataPtr+1);
1029 pMsg->iMsgSize = 3;
1030 break;
1031
1032 case msgSetProgram:
1033 pMsg->MsgData.ChangeProgram.iChannel = pMsg->iLastMsgChnl;
1034 pMsg->MsgData.ChangeProgram.iProgram = *(pMsgDataPtr);
1035 pMsg->iMsgSize = 2;
1036 break;
1037
1038 case msgChangePressure:
1039 pMsg->MsgData.ChangePressure.iChannel = pMsg->iLastMsgChnl;
1040 pMsg->MsgData.ChangePressure.iPressure = *(pMsgDataPtr);
1041 pMsg->iMsgSize = 2;
1042 break;
1043
1044 case msgSetPitchWheel:
1045 pMsg->MsgData.PitchWheel.iChannel = pMsg->iLastMsgChnl;
1046 pMsg->MsgData.PitchWheel.iPitch = *(pMsgDataPtr) | (*(pMsgDataPtr+1) << 7);
1047 pMsg->MsgData.PitchWheel.iPitch -= MIDI_WHEEL_CENTRE;
1048 pMsg->iMsgSize = 3;
1049 break;
1050
1051 case msgMetaEvent:
1052 /* We can use 'pTrack->ptr' from now on, since meta events
1053 ** always have bit 7 set */
1054 bptr = pTrack->ptr;
1055 pMsg->MsgData.MetaEvent.iType = (tMIDI_META)*(pTrack->ptr+1);
1056 pTrack->ptr = _midiReadVarLen(pTrack->ptr+2, &pMsg->iMsgSize);
1057 sz = (pTrack->ptr-bptr)+pMsg->iMsgSize;
1058
1059 if (_midiReadTrackCopyData(pMsg, pTrack->ptr, sz, FALSE) == FALSE)
1060 return FALSE;
1061
1062 /* Now copy the data...*/
1063 memcpy(pMsg->data, bptr, sz);
1064
1065 /* Now place it in a neat structure */
1066 switch(pMsg->MsgData.MetaEvent.iType)
1067 {
1068 case metaMIDIPort:
1069 pMsg->MsgData.MetaEvent.Data.iMIDIPort = *(pTrack->ptr+0);
1070 break;
1071 case metaSequenceNumber:
1072 pMsg->MsgData.MetaEvent.Data.iSequenceNumber = *(pTrack->ptr+0);
1073 break;
1074 case metaTextEvent:
1075 case metaCopyright:
1076 case metaTrackName:
1077 case metaInstrument:
1078 case metaLyric:
1079 case metaMarker:
1080 case metaCuePoint:
1081 /* TODO - Add NULL terminator ??? */
1082 pMsg->MsgData.MetaEvent.Data.Text.pData = pTrack->ptr;
1083 break;
1084 case metaEndSequence:
1085 /* NO DATA */
1086 break;
1087 case metaSetTempo:
1088 {
1089 DWORD us = ((*(pTrack->ptr+0))<<16)|((*(pTrack->ptr+1))<<8)|(*(pTrack->ptr+2));
1090 pMsg->MsgData.MetaEvent.Data.Tempo.iBPM = 60000000L/us;
1091 }
1092 break;
1093 case metaSMPTEOffset:
1094 pMsg->MsgData.MetaEvent.Data.SMPTE.iHours = *(pTrack->ptr+0);
1095 pMsg->MsgData.MetaEvent.Data.SMPTE.iMins= *(pTrack->ptr+1);
1096 pMsg->MsgData.MetaEvent.Data.SMPTE.iSecs = *(pTrack->ptr+2);
1097 pMsg->MsgData.MetaEvent.Data.SMPTE.iFrames = *(pTrack->ptr+3);
1098 pMsg->MsgData.MetaEvent.Data.SMPTE.iFF = *(pTrack->ptr+4);
1099 break;
1100 case metaTimeSig:
1101 pMsg->MsgData.MetaEvent.Data.TimeSig.iNom = *(pTrack->ptr+0);
1102 pMsg->MsgData.MetaEvent.Data.TimeSig.iDenom = *(pTrack->ptr+1) * MIDI_NOTE_MINIM;
1103 /* TODO: Variations without 24 & 8 */
1104 break;
1105 case metaKeySig:
1106 if (*pTrack->ptr & 0x80)
1107 {
1108 /* Do some trendy sign extending in reverse :) */
1109 pMsg->MsgData.MetaEvent.Data.KeySig.iKey = ((256-*pTrack->ptr)&keyMaskKey);
1110 pMsg->MsgData.MetaEvent.Data.KeySig.iKey |= keyMaskNeg;
1111 }
1112 else
1113 {
1114 pMsg->MsgData.MetaEvent.Data.KeySig.iKey = (tMIDI_KEYSIG)(*pTrack->ptr&keyMaskKey);
1115 }
1116 if (*(pTrack->ptr+1))
1117 pMsg->MsgData.MetaEvent.Data.KeySig.iKey |= keyMaskMin;
1118 break;
1119 case metaSequencerSpecific:
1120 pMsg->MsgData.MetaEvent.Data.Sequencer.iSize = pMsg->iMsgSize;
1121 pMsg->MsgData.MetaEvent.Data.Sequencer.pData = pTrack->ptr;
1122 break;
1123 }
1124
1125 pTrack->ptr += pMsg->iMsgSize;
1126 pMsg->iMsgSize = sz;
1127 break;
1128
1129 case msgSysEx1:
1130 case msgSysEx2:
1131 bptr = pTrack->ptr;
1132 pTrack->ptr = _midiReadVarLen(pTrack->ptr+1, &pMsg->iMsgSize);
1133 sz = (pTrack->ptr-bptr)+pMsg->iMsgSize;
1134
1135 if (_midiReadTrackCopyData(pMsg, pTrack->ptr, sz, FALSE) == FALSE)
1136 return FALSE;
1137
1138 /* Now copy the data... */
1139 memcpy(pMsg->data, bptr, sz);
1140 pTrack->ptr += pMsg->iMsgSize;
1141 pMsg->iMsgSize = sz;
1142 pMsg->MsgData.SysEx.pData = pMsg->data;
1143 pMsg->MsgData.SysEx.iSize = sz;
1144 break;
1145 }
1146 /*
1147 ** Standard MIDI messages use a common copy routine
1148 */
1149 pMsg->bImpliedMsg = FALSE;
1150 if ((pMsg->iType&0xf0) != 0xf0)
1151 {
1152 if (*pTrack->ptr & 0x80)
1153 {
1154 }
1155 else
1156 {
1157 pMsg->bImpliedMsg = TRUE;
1158 pMsg->iImpliedMsg = pMsg->iLastMsgType;
1159 pMsg->iMsgSize--;
1160 }
1161 _midiReadTrackCopyData(pMsg, pTrack->ptr, pMsg->iMsgSize, TRUE);
1162 pTrack->ptr+=pMsg->iMsgSize;
1163 }
1164 return TRUE;
1165 }
1166
1167 void midiReadInitMessage(MIDI_MSG *pMsg)
1168 {
1169 pMsg->data = NULL;
1170 pMsg->data_sz = 0;
1171 pMsg->bImpliedMsg = FALSE;
1172 }
1173
1174 void midiReadFreeMessage(MIDI_MSG *pMsg)
1175 {
1176 if (pMsg->data) free((void *)pMsg->data);
1177 pMsg->data = NULL;
1178 }