0
|
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 }
|