1 /* 2 * Copyright 2005-2006, Haiku. 3 * 4 * Copyright (c) 2002-2004 Matthijs Hollemans 5 * Copyright (c) 2002 Jerome Leveque 6 * Copyright (c) 2002 Paul Stadler 7 * Distributed under the terms of the MIT License. 8 * 9 * Authors: 10 * Matthijs Hollemans 11 * Jérôme Leveque 12 * Paul Stadler 13 */ 14 15 #include <File.h> 16 #include <List.h> 17 #include <MidiDefs.h> 18 #include <MidiStore.h> 19 #include <stdlib.h> 20 #include <string.h> 21 22 #include "debug.h" 23 24 25 struct BMidiEvent { 26 BMidiEvent() 27 { 28 byte1 = 0; 29 byte2 = 0; 30 byte3 = 0; 31 data = NULL; 32 length = 0; 33 } 34 35 ~BMidiEvent() 36 { 37 free(data); 38 } 39 40 uint32 time; // either ticks or milliseconds 41 bool ticks; // event is from MIDI file 42 uchar byte1; 43 uchar byte2; 44 uchar byte3; 45 void* data; // sysex data 46 size_t length; // sysex data size 47 int32 tempo; // beats per minute 48 }; 49 50 51 static int 52 compare_events(const void* event1, const void* event2) 53 { 54 BMidiEvent* e1 = *((BMidiEvent**) event1); 55 BMidiEvent* e2 = *((BMidiEvent**) event2); 56 57 return (e1->time - e2->time); 58 } 59 60 61 // #pragma mark - 62 63 64 BMidiStore::BMidiStore() 65 { 66 fEvents = new BList; 67 fCurrentEvent = 0; 68 fStartTime = 0; 69 fNeedsSorting = false; 70 fBeatsPerMinute = 60; 71 fTicksPerBeat = 240; 72 fFile = NULL; 73 fHookFunc = NULL; 74 fLooping = false; 75 fPaused = false; 76 fFinished = false; 77 fInstruments = new bool[128]; 78 } 79 80 81 BMidiStore::~BMidiStore() 82 { 83 for (int32 t = 0; t < fEvents->CountItems(); ++t) { 84 delete EventAt(t); 85 } 86 87 delete fEvents; 88 delete[] fInstruments; 89 } 90 91 92 void 93 BMidiStore::NoteOff(uchar channel, uchar note, uchar velocity, 94 uint32 time) 95 { 96 BMidiEvent* event = new BMidiEvent; 97 event->time = time; 98 event->ticks = false; 99 event->byte1 = B_NOTE_OFF | (channel - 1); 100 event->byte2 = note; 101 event->byte3 = velocity; 102 AddEvent(event); 103 } 104 105 106 void 107 BMidiStore::NoteOn(uchar channel, uchar note, 108 uchar velocity, uint32 time) 109 { 110 BMidiEvent* event = new BMidiEvent; 111 event->time = time; 112 event->ticks = false; 113 event->byte1 = B_NOTE_ON | (channel - 1); 114 event->byte2 = note; 115 event->byte3 = velocity; 116 AddEvent(event); 117 } 118 119 120 void 121 BMidiStore::KeyPressure(uchar channel, uchar note, 122 uchar pressure, uint32 time) 123 { 124 BMidiEvent* event = new BMidiEvent; 125 event->time = time; 126 event->ticks = false; 127 event->byte1 = B_KEY_PRESSURE | (channel - 1); 128 event->byte2 = note; 129 event->byte3 = pressure; 130 AddEvent(event); 131 } 132 133 134 void 135 BMidiStore::ControlChange(uchar channel, uchar controlNumber, 136 uchar controlValue, uint32 time) 137 { 138 BMidiEvent* event = new BMidiEvent; 139 event->time = time; 140 event->ticks = false; 141 event->byte1 = B_CONTROL_CHANGE | (channel - 1); 142 event->byte2 = controlNumber; 143 event->byte3 = controlValue; 144 AddEvent(event); 145 } 146 147 148 void 149 BMidiStore::ProgramChange(uchar channel, uchar programNumber, 150 uint32 time) 151 { 152 BMidiEvent* event = new BMidiEvent; 153 event->time = time; 154 event->ticks = false; 155 event->byte1 = B_PROGRAM_CHANGE | (channel - 1); 156 event->byte2 = programNumber; 157 AddEvent(event); 158 } 159 160 161 void 162 BMidiStore::ChannelPressure(uchar channel, uchar pressure, uint32 time) 163 { 164 BMidiEvent* event = new BMidiEvent; 165 event->time = time; 166 event->ticks = false; 167 event->byte1 = B_CHANNEL_PRESSURE | (channel - 1); 168 event->byte2 = pressure; 169 AddEvent(event); 170 } 171 172 173 void 174 BMidiStore::PitchBend(uchar channel, uchar lsb, uchar msb, uint32 time) 175 { 176 BMidiEvent* event = new BMidiEvent; 177 event->time = time; 178 event->ticks = false; 179 event->byte1 = B_PITCH_BEND | (channel - 1); 180 event->byte2 = lsb; 181 event->byte3 = msb; 182 AddEvent(event); 183 } 184 185 186 void 187 BMidiStore::SystemExclusive(void* data, size_t length, uint32 time) 188 { 189 BMidiEvent* event = new BMidiEvent; 190 event->time = time; 191 event->ticks = false; 192 event->byte1 = B_SYS_EX_START; 193 event->data = malloc(length); 194 event->length = length; 195 memcpy(event->data, data, length); 196 AddEvent(event); 197 } 198 199 200 void 201 BMidiStore::SystemCommon(uchar status, uchar data1, 202 uchar data2, uint32 time) 203 { 204 BMidiEvent* event = new BMidiEvent; 205 event->time = time; 206 event->ticks = false; 207 event->byte1 = status; 208 event->byte2 = data1; 209 event->byte3 = data2; 210 AddEvent(event); 211 } 212 213 214 void 215 BMidiStore::SystemRealTime(uchar status, uint32 time) 216 { 217 BMidiEvent* event = new BMidiEvent; 218 event->time = time; 219 event->ticks = false; 220 event->byte1 = status; 221 AddEvent(event); 222 } 223 224 225 void 226 BMidiStore::TempoChange(int32 beatsPerMinute, uint32 time) 227 { 228 BMidiEvent* event = new BMidiEvent; 229 event->time = time; 230 event->ticks = false; 231 event->byte1 = 0xFF; 232 event->byte2 = 0x51; 233 event->byte3 = 0x03; 234 event->tempo = beatsPerMinute; 235 AddEvent(event); 236 } 237 238 239 status_t 240 BMidiStore::Import(const entry_ref* ref) 241 { 242 memset(fInstruments, 0, 128 * sizeof(bool)); 243 244 try { 245 fFile = new BFile(ref, B_READ_ONLY); 246 if (fFile->InitCheck() != B_OK) 247 throw fFile->InitCheck(); 248 249 char fourcc[4]; 250 ReadFourCC(fourcc); 251 if (strncmp(fourcc, "MThd", 4) != 0) 252 throw (status_t) B_BAD_MIDI_DATA; 253 254 if (Read32Bit() != 6) 255 throw (status_t) B_BAD_MIDI_DATA; 256 257 fFormat = Read16Bit(); 258 fNumTracks = Read16Bit(); 259 fTicksPerBeat = Read16Bit(); 260 261 if (fTicksPerBeat & 0x8000) { 262 // we don't support SMPTE time codes, 263 // only ticks per quarter note 264 fTicksPerBeat = 240; 265 } 266 267 fCurrTrack = 0; 268 while (fCurrTrack < fNumTracks) { 269 ReadChunk(); 270 } 271 } catch (status_t e) { 272 delete fFile; 273 fFile = NULL; 274 return e; 275 } 276 277 SortEvents(true); 278 279 delete fFile; 280 fFile = NULL; 281 return B_OK; 282 } 283 284 285 status_t 286 BMidiStore::Export(const entry_ref* ref, int32 format) 287 { 288 try { 289 fFile = new BFile(ref, B_READ_WRITE); 290 if (fFile->InitCheck() != B_OK) 291 throw fFile->InitCheck(); 292 293 SortEvents(true); 294 295 WriteFourCC('M', 'T', 'h', 'd'); 296 Write32Bit(6); 297 Write16Bit(0); // we do only format 0 298 Write16Bit(1); 299 Write16Bit(fTicksPerBeat); 300 301 WriteTrack(); 302 } catch (status_t e) { 303 delete fFile; 304 fFile = NULL; 305 return e; 306 } 307 308 delete fFile; 309 fFile = NULL; 310 return B_OK; 311 } 312 313 314 void 315 BMidiStore::SortEvents(bool force) 316 { 317 if (force || fNeedsSorting) { 318 fEvents->SortItems(compare_events); 319 fNeedsSorting = false; 320 } 321 } 322 323 324 uint32 325 BMidiStore::CountEvents() const 326 { 327 return fEvents->CountItems(); 328 } 329 330 331 uint32 332 BMidiStore::CurrentEvent() const 333 { 334 return fCurrentEvent; 335 } 336 337 338 void 339 BMidiStore::SetCurrentEvent(uint32 eventNumber) 340 { 341 fCurrentEvent = eventNumber; 342 } 343 344 345 uint32 346 BMidiStore::DeltaOfEvent(uint32 eventNumber) const 347 { 348 // Even though the BeBook says that the delta is the time span between 349 // an event and the first event in the list, this doesn't appear to be 350 // true for events that were captured from other BMidi objects such as 351 // BMidiPort. For those events, we return the absolute timestamp. The 352 // BeBook is correct for events from MIDI files, though. 353 354 BMidiEvent* event = EventAt(eventNumber); 355 if (event != NULL) 356 return GetEventTime(event); 357 358 return 0; 359 } 360 361 362 uint32 363 BMidiStore::EventAtDelta(uint32 time) const 364 { 365 for (int32 t = 0; t < fEvents->CountItems(); ++t) { 366 if (GetEventTime(EventAt(t)) >= time) 367 return t; 368 } 369 370 return 0; 371 } 372 373 374 uint32 375 BMidiStore::BeginTime() const 376 { 377 return fStartTime; 378 } 379 380 381 void 382 BMidiStore::SetTempo(int32 beatsPerMinute_) 383 { 384 fBeatsPerMinute = beatsPerMinute_; 385 } 386 387 388 int32 389 BMidiStore::Tempo() const 390 { 391 return fBeatsPerMinute; 392 } 393 394 395 void BMidiStore::_ReservedMidiStore1() { } 396 void BMidiStore::_ReservedMidiStore2() { } 397 void BMidiStore::_ReservedMidiStore3() { } 398 399 400 void 401 BMidiStore::Run() 402 { 403 // This rather compilicated Run() loop is not only used by BMidiStore 404 // but also by BMidiSynthFile. The "paused", "finished", and "looping" 405 // flags, and the "stop hook" are especially provided for the latter. 406 407 fPaused = false; 408 fFinished = false; 409 410 int32 timeAdjust = 0; 411 uint32 baseTime = 0; 412 bool firstEvent = true; 413 bool resetTime = false; 414 415 while (KeepRunning()) { 416 if (fPaused) { 417 resetTime = true; 418 snooze(100000); 419 continue; 420 } 421 422 BMidiEvent* event = EventAt(fCurrentEvent); 423 424 if (event == NULL) { 425 // no more events 426 if (fLooping) { 427 resetTime = true; 428 fCurrentEvent = 0; 429 continue; 430 } 431 break; 432 } 433 434 if (firstEvent) { 435 fStartTime = B_NOW; 436 baseTime = fStartTime; 437 } else if (resetTime) 438 baseTime = B_NOW; 439 440 if (firstEvent || resetTime) { 441 timeAdjust = baseTime - GetEventTime(event); 442 SprayEvent(event, baseTime); 443 firstEvent = false; 444 resetTime = false; 445 } else 446 SprayEvent(event, GetEventTime(event) + timeAdjust); 447 448 ++fCurrentEvent; 449 } 450 451 fFinished = true; 452 fPaused = false; 453 454 if (fHookFunc != NULL) 455 (*fHookFunc)(fHookArg); 456 } 457 458 459 void 460 BMidiStore::AddEvent(BMidiEvent* event) 461 { 462 fEvents->AddItem(event); 463 fNeedsSorting = true; 464 } 465 466 467 void 468 BMidiStore::SprayEvent(const BMidiEvent* event, uint32 time) 469 { 470 uchar byte1 = event->byte1; 471 uchar byte2 = event->byte2; 472 uchar byte3 = event->byte3; 473 474 switch (byte1 & 0xF0) { 475 case B_NOTE_OFF: 476 SprayNoteOff((byte1 & 0x0F) + 1, byte2, byte3, time); 477 return; 478 479 case B_NOTE_ON: 480 SprayNoteOn((byte1 & 0x0F) + 1, byte2, byte3, time); 481 return; 482 483 case B_KEY_PRESSURE: 484 SprayKeyPressure((byte1 & 0x0F) + 1, byte2, byte3, time); 485 return; 486 487 case B_CONTROL_CHANGE: 488 SprayControlChange((byte1 & 0x0F) + 1, byte2, byte3, time); 489 return; 490 491 case B_PROGRAM_CHANGE: 492 SprayProgramChange((byte1 & 0x0F) + 1, byte2, time); 493 return; 494 495 case B_CHANNEL_PRESSURE: 496 SprayChannelPressure((byte1 & 0x0F) + 1, byte2, time); 497 return; 498 499 case B_PITCH_BEND: 500 SprayPitchBend((byte1 & 0x0F) + 1, byte2, byte3, time); 501 return; 502 503 case 0xF0: 504 switch (byte1) { 505 case B_SYS_EX_START: 506 SpraySystemExclusive(event->data, event->length, time); 507 return; 508 509 case B_MIDI_TIME_CODE: 510 case B_SONG_POSITION: 511 case B_SONG_SELECT: 512 case B_CABLE_MESSAGE: 513 case B_TUNE_REQUEST: 514 case B_SYS_EX_END: 515 SpraySystemCommon(byte1, byte2, byte3, time); 516 return; 517 518 case B_TIMING_CLOCK: 519 case B_START: 520 case B_CONTINUE: 521 case B_STOP: 522 case B_ACTIVE_SENSING: 523 SpraySystemRealTime(byte1, time); 524 return; 525 526 case B_SYSTEM_RESET: 527 if (byte2 == 0x51 && byte3 == 0x03) { 528 SprayTempoChange(event->tempo, time); 529 fBeatsPerMinute = event->tempo; 530 } else 531 SpraySystemRealTime(byte1, time); 532 return; 533 } 534 return; 535 } 536 } 537 538 539 BMidiEvent* 540 BMidiStore::EventAt(int32 index) const 541 { 542 return (BMidiEvent*)fEvents->ItemAt(index); 543 } 544 545 546 uint32 547 BMidiStore::GetEventTime(const BMidiEvent* event) const 548 { 549 if (event->ticks) 550 return TicksToMilliseconds(event->time); 551 552 return event->time; 553 } 554 555 556 uint32 557 BMidiStore::TicksToMilliseconds(uint32 ticks) const 558 { 559 return ((uint64)ticks * 60000) / (fBeatsPerMinute * fTicksPerBeat); 560 } 561 562 563 uint32 564 BMidiStore::MillisecondsToTicks(uint32 ms) const 565 { 566 return ((uint64)ms * fBeatsPerMinute * fTicksPerBeat) / 60000; 567 } 568 569 570 void 571 BMidiStore::ReadFourCC(char* fourcc) 572 { 573 if (fFile->Read(fourcc, 4) != 4) 574 throw (status_t) B_BAD_MIDI_DATA; 575 } 576 577 578 void 579 BMidiStore::WriteFourCC(char a, char b, char c, char d) 580 { 581 char fourcc[4] = { a, b, c, d }; 582 583 if (fFile->Write(fourcc, 4) != 4) 584 throw (status_t) B_ERROR; 585 } 586 587 588 uint32 589 BMidiStore::Read32Bit() 590 { 591 uint8 buf[4]; 592 if (fFile->Read(buf, 4) != 4) 593 throw (status_t) B_BAD_MIDI_DATA; 594 595 return (buf[0] << 24L) | (buf[1] << 16L) | (buf[2] << 8L) | buf[3]; 596 } 597 598 599 void 600 BMidiStore::Write32Bit(uint32 val) 601 { 602 uint8 buf[4]; 603 buf[0] = (val >> 24) & 0xFF; 604 buf[1] = (val >> 16) & 0xFF; 605 buf[2] = (val >> 8) & 0xFF; 606 buf[3] = val & 0xFF; 607 608 if (fFile->Write(buf, 4) != 4) 609 throw (status_t) B_ERROR; 610 } 611 612 613 uint16 614 BMidiStore::Read16Bit() 615 { 616 uint8 buf[2]; 617 if (fFile->Read(buf, 2) != 2) 618 throw (status_t) B_BAD_MIDI_DATA; 619 620 return (buf[0] << 8) | buf[1]; 621 } 622 623 624 void 625 BMidiStore::Write16Bit(uint16 val) 626 { 627 uint8 buf[2]; 628 buf[0] = (val >> 8) & 0xFF; 629 buf[1] = val & 0xFF; 630 631 if (fFile->Write(buf, 2) != 2) 632 throw (status_t) B_ERROR; 633 } 634 635 636 uint8 637 BMidiStore::PeekByte() 638 { 639 uint8 buf; 640 if (fFile->Read(&buf, 1) != 1) 641 throw (status_t) B_BAD_MIDI_DATA; 642 643 if (fFile->Seek(-1, SEEK_CUR) < 0) 644 throw (status_t) B_ERROR; 645 646 return buf; 647 } 648 649 650 uint8 651 BMidiStore::NextByte() 652 { 653 uint8 buf; 654 if (fFile->Read(&buf, 1) != 1) 655 throw (status_t) B_BAD_MIDI_DATA; 656 657 --fByteCount; 658 return buf; 659 } 660 661 662 void 663 BMidiStore::WriteByte(uint8 val) 664 { 665 if (fFile->Write(&val, 1) != 1) 666 throw (status_t) B_ERROR; 667 668 ++fByteCount; 669 } 670 671 672 void 673 BMidiStore::SkipBytes(uint32 length) 674 { 675 if (fFile->Seek(length, SEEK_CUR) < 0) { 676 throw (status_t) B_BAD_MIDI_DATA; 677 } 678 679 fByteCount -= length; 680 } 681 682 683 uint32 684 BMidiStore::ReadVarLength() 685 { 686 uint32 val; 687 uint8 byte; 688 689 if ((val = NextByte()) & 0x80) { 690 val &= 0x7F; 691 do { 692 val = (val << 7) + ((byte = NextByte()) & 0x7F); 693 } while (byte & 0x80); 694 } 695 696 return val; 697 } 698 699 700 void 701 BMidiStore::WriteVarLength(uint32 val) 702 { 703 uint32 buffer = val & 0x7F; 704 705 while ((val >>= 7) != 0) { 706 buffer <<= 8; 707 buffer |= ((val & 0x7F) | 0x80); 708 } 709 710 while (true) { 711 WriteByte(buffer); 712 if (buffer & 0x80) 713 buffer >>= 8; 714 else 715 break; 716 } 717 } 718 719 720 void 721 BMidiStore::ReadChunk() 722 { 723 char fourcc[4]; 724 ReadFourCC(fourcc); 725 726 fByteCount = Read32Bit(); 727 728 if (strncmp(fourcc, "MTrk", 4) == 0) 729 ReadTrack(); 730 else { 731 TRACE(("Skipping '%c%c%c%c' chunk (%" B_PRIu32 " bytes)", 732 fourcc[0], fourcc[1], fourcc[2], fourcc[3], fByteCount)) 733 734 SkipBytes(fByteCount); 735 } 736 } 737 738 739 void 740 BMidiStore::ReadTrack() 741 { 742 uint8 status = 0; 743 uint8 data1; 744 uint8 data2; 745 BMidiEvent* event; 746 747 fTotalTicks = 0; 748 749 while (fByteCount > 0) { 750 uint32 ticks = ReadVarLength(); 751 fTotalTicks += ticks; 752 753 if (PeekByte() & 0x80) 754 status = NextByte(); 755 756 switch (status & 0xF0) { 757 case B_NOTE_OFF: 758 case B_NOTE_ON: 759 case B_KEY_PRESSURE: 760 case B_CONTROL_CHANGE: 761 case B_PITCH_BEND: 762 data1 = NextByte(); 763 data2 = NextByte(); 764 event = new BMidiEvent; 765 event->time = fTotalTicks; 766 event->ticks = true; 767 event->byte1 = status; 768 event->byte2 = data1; 769 event->byte3 = data2; 770 AddEvent(event); 771 break; 772 773 case B_PROGRAM_CHANGE: 774 case B_CHANNEL_PRESSURE: 775 data1 = NextByte(); 776 event = new BMidiEvent; 777 event->time = fTotalTicks; 778 event->ticks = true; 779 event->byte1 = status; 780 event->byte2 = data1; 781 AddEvent(event); 782 783 if ((status & 0xF0) == B_PROGRAM_CHANGE) 784 fInstruments[data1] = true; 785 break; 786 787 case 0xF0: 788 switch (status) { 789 case B_SYS_EX_START: 790 ReadSystemExclusive(); 791 break; 792 793 case B_TUNE_REQUEST: 794 case B_SYS_EX_END: 795 case B_TIMING_CLOCK: 796 case B_START: 797 case B_CONTINUE: 798 case B_STOP: 799 case B_ACTIVE_SENSING: 800 event = new BMidiEvent; 801 event->time = fTotalTicks; 802 event->ticks = true; 803 event->byte1 = status; 804 AddEvent(event); 805 break; 806 807 case B_MIDI_TIME_CODE: 808 case B_SONG_SELECT: 809 case B_CABLE_MESSAGE: 810 data1 = NextByte(); 811 event = new BMidiEvent; 812 event->time = fTotalTicks; 813 event->ticks = true; 814 event->byte1 = status; 815 event->byte2 = data1; 816 AddEvent(event); 817 break; 818 819 case B_SONG_POSITION: 820 data1 = NextByte(); 821 data2 = NextByte(); 822 event = new BMidiEvent; 823 event->time = fTotalTicks; 824 event->ticks = true; 825 event->byte1 = status; 826 event->byte2 = data1; 827 event->byte3 = data2; 828 AddEvent(event); 829 break; 830 831 case B_SYSTEM_RESET: 832 ReadMetaEvent(); 833 break; 834 } 835 break; 836 } 837 838 event = NULL; 839 } 840 841 ++fCurrTrack; 842 } 843 844 845 void 846 BMidiStore::ReadSystemExclusive() 847 { 848 // We do not import sysex's from MIDI files. 849 850 SkipBytes(ReadVarLength()); 851 } 852 853 854 void 855 BMidiStore::ReadMetaEvent() 856 { 857 // We only import the Tempo Change meta event. 858 859 uint8 type = NextByte(); 860 uint32 length = ReadVarLength(); 861 862 if (type == 0x51 && length == 3) { 863 uchar data[3]; 864 data[0] = NextByte(); 865 data[1] = NextByte(); 866 data[2] = NextByte(); 867 uint32 val = (data[0] << 16) | (data[1] << 8) | data[2]; 868 869 BMidiEvent* event = new BMidiEvent; 870 event->time = fTotalTicks; 871 event->ticks = true; 872 event->byte1 = 0xFF; 873 event->byte2 = 0x51; 874 event->byte3 = 0x03; 875 event->tempo = 60000000 / val; 876 AddEvent(event); 877 } else 878 SkipBytes(length); 879 } 880 881 882 void 883 BMidiStore::WriteTrack() 884 { 885 WriteFourCC('M', 'T', 'r', 'k'); 886 off_t lengthPos = fFile->Position(); 887 Write32Bit(0); 888 889 fByteCount = 0; 890 uint32 oldTime = 0; 891 uint32 newTime; 892 893 for (uint32 t = 0; t < CountEvents(); ++t) { 894 BMidiEvent* event = EventAt(t); 895 896 if (event->ticks) 897 newTime = event->time; 898 else 899 newTime = MillisecondsToTicks(event->time); 900 901 if (t == 0) 902 WriteVarLength(0); 903 else 904 WriteVarLength(newTime - oldTime); 905 906 oldTime = newTime; 907 908 switch (event->byte1 & 0xF0) { 909 case B_NOTE_OFF: 910 case B_NOTE_ON: 911 case B_KEY_PRESSURE: 912 case B_CONTROL_CHANGE: 913 case B_PITCH_BEND: 914 WriteByte(event->byte1); 915 WriteByte(event->byte2); 916 WriteByte(event->byte3); 917 break; 918 919 case B_PROGRAM_CHANGE: 920 case B_CHANNEL_PRESSURE: 921 WriteByte(event->byte1); 922 WriteByte(event->byte2); 923 break; 924 925 case 0xF0: 926 switch (event->byte1) { 927 case B_SYS_EX_START: 928 // We do not export sysex's. 929 break; 930 931 case B_TUNE_REQUEST: 932 case B_SYS_EX_END: 933 case B_TIMING_CLOCK: 934 case B_START: 935 case B_CONTINUE: 936 case B_STOP: 937 case B_ACTIVE_SENSING: 938 WriteByte(event->byte1); 939 break; 940 941 case B_MIDI_TIME_CODE: 942 case B_SONG_SELECT: 943 case B_CABLE_MESSAGE: 944 WriteByte(event->byte1); 945 WriteByte(event->byte2); 946 break; 947 948 case B_SONG_POSITION: 949 WriteByte(event->byte1); 950 WriteByte(event->byte2); 951 WriteByte(event->byte3); 952 break; 953 954 case B_SYSTEM_RESET: 955 WriteMetaEvent(event); 956 break; 957 } 958 break; 959 } 960 } 961 962 WriteVarLength(0); 963 WriteByte(0xFF); // the end-of-track 964 WriteByte(0x2F); // marker is required 965 WriteByte(0x00); 966 967 fFile->Seek(lengthPos, SEEK_SET); 968 Write32Bit(fByteCount); 969 fFile->Seek(0, SEEK_END); 970 } 971 972 973 void 974 BMidiStore::WriteMetaEvent(BMidiEvent* event) 975 { 976 // We only export the Tempo Change meta event. 977 978 if (event->byte2 == 0x51 && event->byte3 == 0x03) { 979 uint32 val = 60000000 / event->tempo; 980 981 WriteByte(0xFF); 982 WriteByte(0x51); 983 WriteByte(0x03); 984 WriteByte(val >> 16); 985 WriteByte(val >> 8); 986 WriteByte(val); 987 } 988 } 989 990