Mercurial > hg > forks > bilotrip-mj12
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 } |