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 } else 430 break; 431 } 432 433 if (firstEvent) { 434 fStartTime = B_NOW; 435 baseTime = fStartTime; 436 } else if (resetTime) 437 baseTime = B_NOW; 438 439 if (firstEvent || resetTime) { 440 timeAdjust = baseTime - GetEventTime(event); 441 SprayEvent(event, baseTime); 442 firstEvent = false; 443 resetTime = false; 444 } else 445 SprayEvent(event, GetEventTime(event) + timeAdjust); 446 447 ++fCurrentEvent; 448 } 449 450 fFinished = true; 451 fPaused = false; 452 453 if (fHookFunc != NULL) 454 (*fHookFunc)(fHookArg); 455 } 456 457 458 void 459 BMidiStore::AddEvent(BMidiEvent* event) 460 { 461 fEvents->AddItem(event); 462 fNeedsSorting = true; 463 } 464 465 466 void 467 BMidiStore::SprayEvent(const BMidiEvent* event, uint32 time) 468 { 469 uchar byte1 = event->byte1; 470 uchar byte2 = event->byte2; 471 uchar byte3 = event->byte3; 472 473 switch (byte1 & 0xF0) { 474 case B_NOTE_OFF: 475 SprayNoteOff((byte1 & 0x0F) + 1, byte2, byte3, time); 476 return; 477 478 case B_NOTE_ON: 479 SprayNoteOn((byte1 & 0x0F) + 1, byte2, byte3, time); 480 return; 481 482 case B_KEY_PRESSURE: 483 SprayKeyPressure((byte1 & 0x0F) + 1, byte2, byte3, time); 484 return; 485 486 case B_CONTROL_CHANGE: 487 SprayControlChange((byte1 & 0x0F) + 1, byte2, byte3, time); 488 return; 489 490 case B_PROGRAM_CHANGE: 491 SprayProgramChange((byte1 & 0x0F) + 1, byte2, time); 492 return; 493 494 case B_CHANNEL_PRESSURE: 495 SprayChannelPressure((byte1 & 0x0F) + 1, byte2, time); 496 return; 497 498 case B_PITCH_BEND: 499 SprayPitchBend((byte1 & 0x0F) + 1, byte2, byte3, time); 500 return; 501 502 case 0xF0: 503 switch (byte1) { 504 case B_SYS_EX_START: 505 SpraySystemExclusive(event->data, event->length, time); 506 return; 507 508 case B_MIDI_TIME_CODE: 509 case B_SONG_POSITION: 510 case B_SONG_SELECT: 511 case B_CABLE_MESSAGE: 512 case B_TUNE_REQUEST: 513 case B_SYS_EX_END: 514 SpraySystemCommon(byte1, byte2, byte3, time); 515 return; 516 517 case B_TIMING_CLOCK: 518 case B_START: 519 case B_CONTINUE: 520 case B_STOP: 521 case B_ACTIVE_SENSING: 522 SpraySystemRealTime(byte1, time); 523 return; 524 525 case B_SYSTEM_RESET: 526 if (byte2 == 0x51 && byte3 == 0x03) { 527 SprayTempoChange(event->tempo, time); 528 fBeatsPerMinute = event->tempo; 529 } else 530 SpraySystemRealTime(byte1, time); 531 return; 532 } 533 return; 534 } 535 } 536 537 538 BMidiEvent* 539 BMidiStore::EventAt(int32 index) const 540 { 541 return (BMidiEvent*)fEvents->ItemAt(index); 542 } 543 544 545 uint32 546 BMidiStore::GetEventTime(const BMidiEvent* event) const 547 { 548 if (event->ticks) 549 return TicksToMilliseconds(event->time); 550 551 return event->time; 552 } 553 554 555 uint32 556 BMidiStore::TicksToMilliseconds(uint32 ticks) const 557 { 558 return ((uint64)ticks * 60000) / (fBeatsPerMinute * fTicksPerBeat); 559 } 560 561 562 uint32 563 BMidiStore::MillisecondsToTicks(uint32 ms) const 564 { 565 return ((uint64)ms * fBeatsPerMinute * fTicksPerBeat) / 60000; 566 } 567 568 569 void 570 BMidiStore::ReadFourCC(char* fourcc) 571 { 572 if (fFile->Read(fourcc, 4) != 4) 573 throw (status_t) B_BAD_MIDI_DATA; 574 } 575 576 577 void 578 BMidiStore::WriteFourCC(char a, char b, char c, char d) 579 { 580 char fourcc[4] = { a, b, c, d }; 581 582 if (fFile->Write(fourcc, 4) != 4) 583 throw (status_t) B_ERROR; 584 } 585 586 587 uint32 588 BMidiStore::Read32Bit() 589 { 590 uint8 buf[4]; 591 if (fFile->Read(buf, 4) != 4) 592 throw (status_t) B_BAD_MIDI_DATA; 593 594 return (buf[0] << 24L) | (buf[1] << 16L) | (buf[2] << 8L) | buf[3]; 595 } 596 597 598 void 599 BMidiStore::Write32Bit(uint32 val) 600 { 601 uint8 buf[4]; 602 buf[0] = (val >> 24) & 0xFF; 603 buf[1] = (val >> 16) & 0xFF; 604 buf[2] = (val >> 8) & 0xFF; 605 buf[3] = val & 0xFF; 606 607 if (fFile->Write(buf, 4) != 4) 608 throw (status_t) B_ERROR; 609 } 610 611 612 uint16 613 BMidiStore::Read16Bit() 614 { 615 uint8 buf[2]; 616 if (fFile->Read(buf, 2) != 2) 617 throw (status_t) B_BAD_MIDI_DATA; 618 619 return (buf[0] << 8) | buf[1]; 620 } 621 622 623 void 624 BMidiStore::Write16Bit(uint16 val) 625 { 626 uint8 buf[2]; 627 buf[0] = (val >> 8) & 0xFF; 628 buf[1] = val & 0xFF; 629 630 if (fFile->Write(buf, 2) != 2) 631 throw (status_t) B_ERROR; 632 } 633 634 635 uint8 636 BMidiStore::PeekByte() 637 { 638 uint8 buf; 639 if (fFile->Read(&buf, 1) != 1) 640 throw (status_t) B_BAD_MIDI_DATA; 641 642 if (fFile->Seek(-1, SEEK_CUR) < 0) 643 throw (status_t) B_ERROR; 644 645 return buf; 646 } 647 648 649 uint8 650 BMidiStore::NextByte() 651 { 652 uint8 buf; 653 if (fFile->Read(&buf, 1) != 1) 654 throw (status_t) B_BAD_MIDI_DATA; 655 656 --fByteCount; 657 return buf; 658 } 659 660 661 void 662 BMidiStore::WriteByte(uint8 val) 663 { 664 if (fFile->Write(&val, 1) != 1) 665 throw (status_t) B_ERROR; 666 667 ++fByteCount; 668 } 669 670 671 void 672 BMidiStore::SkipBytes(uint32 length) 673 { 674 if (fFile->Seek(length, SEEK_CUR) < 0) { 675 throw (status_t) B_BAD_MIDI_DATA; 676 } 677 678 fByteCount -= length; 679 } 680 681 682 uint32 683 BMidiStore::ReadVarLength() 684 { 685 uint32 val; 686 uint8 byte; 687 688 if ((val = NextByte()) & 0x80) { 689 val &= 0x7F; 690 do { 691 val = (val << 7) + ((byte = NextByte()) & 0x7F); 692 } while (byte & 0x80); 693 } 694 695 return val; 696 } 697 698 699 void 700 BMidiStore::WriteVarLength(uint32 val) 701 { 702 uint32 buffer = val & 0x7F; 703 704 while ((val >>= 7) != 0) { 705 buffer <<= 8; 706 buffer |= ((val & 0x7F) | 0x80); 707 } 708 709 while (true) { 710 WriteByte(buffer); 711 if (buffer & 0x80) 712 buffer >>= 8; 713 else 714 break; 715 } 716 } 717 718 719 void 720 BMidiStore::ReadChunk() 721 { 722 char fourcc[4]; 723 ReadFourCC(fourcc); 724 725 fByteCount = Read32Bit(); 726 727 if (strncmp(fourcc, "MTrk", 4) == 0) 728 ReadTrack(); 729 else { 730 TRACE(("Skipping '%c%c%c%c' chunk (%lu bytes)", 731 fourcc[0], fourcc[1], fourcc[2], fourcc[3], fByteCount)) 732 733 SkipBytes(fByteCount); 734 } 735 } 736 737 738 void 739 BMidiStore::ReadTrack() 740 { 741 uint8 status = 0; 742 uint8 data1; 743 uint8 data2; 744 BMidiEvent* event; 745 746 fTotalTicks = 0; 747 748 while (fByteCount > 0) { 749 uint32 ticks = ReadVarLength(); 750 fTotalTicks += ticks; 751 752 if (PeekByte() & 0x80) 753 status = NextByte(); 754 755 switch (status & 0xF0) { 756 case B_NOTE_OFF: 757 case B_NOTE_ON: 758 case B_KEY_PRESSURE: 759 case B_CONTROL_CHANGE: 760 case B_PITCH_BEND: 761 data1 = NextByte(); 762 data2 = NextByte(); 763 event = new BMidiEvent; 764 event->time = fTotalTicks; 765 event->ticks = true; 766 event->byte1 = status; 767 event->byte2 = data1; 768 event->byte3 = data2; 769 AddEvent(event); 770 break; 771 772 case B_PROGRAM_CHANGE: 773 case B_CHANNEL_PRESSURE: 774 data1 = NextByte(); 775 event = new BMidiEvent; 776 event->time = fTotalTicks; 777 event->ticks = true; 778 event->byte1 = status; 779 event->byte2 = data1; 780 AddEvent(event); 781 782 if ((status & 0xF0) == B_PROGRAM_CHANGE) 783 fInstruments[data1] = true; 784 break; 785 786 case 0xF0: 787 switch (status) { 788 case B_SYS_EX_START: 789 ReadSystemExclusive(); 790 break; 791 792 case B_TUNE_REQUEST: 793 case B_SYS_EX_END: 794 case B_TIMING_CLOCK: 795 case B_START: 796 case B_CONTINUE: 797 case B_STOP: 798 case B_ACTIVE_SENSING: 799 event = new BMidiEvent; 800 event->time = fTotalTicks; 801 event->ticks = true; 802 event->byte1 = status; 803 AddEvent(event); 804 break; 805 806 case B_MIDI_TIME_CODE: 807 case B_SONG_SELECT: 808 case B_CABLE_MESSAGE: 809 data1 = NextByte(); 810 event = new BMidiEvent; 811 event->time = fTotalTicks; 812 event->ticks = true; 813 event->byte1 = status; 814 event->byte2 = data1; 815 AddEvent(event); 816 break; 817 818 case B_SONG_POSITION: 819 data1 = NextByte(); 820 data2 = NextByte(); 821 event = new BMidiEvent; 822 event->time = fTotalTicks; 823 event->ticks = true; 824 event->byte1 = status; 825 event->byte2 = data1; 826 event->byte3 = data2; 827 AddEvent(event); 828 break; 829 830 case B_SYSTEM_RESET: 831 ReadMetaEvent(); 832 break; 833 } 834 break; 835 } 836 837 event = NULL; 838 } 839 840 ++fCurrTrack; 841 } 842 843 844 void 845 BMidiStore::ReadSystemExclusive() 846 { 847 // We do not import sysex's from MIDI files. 848 849 SkipBytes(ReadVarLength()); 850 } 851 852 853 void 854 BMidiStore::ReadMetaEvent() 855 { 856 // We only import the Tempo Change meta event. 857 858 uint8 type = NextByte(); 859 uint32 length = ReadVarLength(); 860 861 if (type == 0x51 && length == 3) { 862 uchar data[3]; 863 data[0] = NextByte(); 864 data[1] = NextByte(); 865 data[2] = NextByte(); 866 uint32 val = (data[0] << 16) | (data[1] << 8) | data[2]; 867 868 BMidiEvent* event = new BMidiEvent; 869 event->time = fTotalTicks; 870 event->ticks = true; 871 event->byte1 = 0xFF; 872 event->byte2 = 0x51; 873 event->byte3 = 0x03; 874 event->tempo = 60000000 / val; 875 AddEvent(event); 876 } else 877 SkipBytes(length); 878 } 879 880 881 void 882 BMidiStore::WriteTrack() 883 { 884 WriteFourCC('M', 'T', 'r', 'k'); 885 off_t lengthPos = fFile->Position(); 886 Write32Bit(0); 887 888 fByteCount = 0; 889 uint32 oldTime = 0; 890 uint32 newTime; 891 892 for (uint32 t = 0; t < CountEvents(); ++t) { 893 BMidiEvent* event = EventAt(t); 894 895 if (event->ticks) 896 newTime = event->time; 897 else 898 newTime = MillisecondsToTicks(event->time); 899 900 if (t == 0) 901 WriteVarLength(0); 902 else 903 WriteVarLength(newTime - oldTime); 904 905 oldTime = newTime; 906 907 switch (event->byte1 & 0xF0) { 908 case B_NOTE_OFF: 909 case B_NOTE_ON: 910 case B_KEY_PRESSURE: 911 case B_CONTROL_CHANGE: 912 case B_PITCH_BEND: 913 WriteByte(event->byte1); 914 WriteByte(event->byte2); 915 WriteByte(event->byte3); 916 break; 917 918 case B_PROGRAM_CHANGE: 919 case B_CHANNEL_PRESSURE: 920 WriteByte(event->byte1); 921 WriteByte(event->byte2); 922 break; 923 924 case 0xF0: 925 switch (event->byte1) { 926 case B_SYS_EX_START: 927 // We do not export sysex's. 928 break; 929 930 case B_TUNE_REQUEST: 931 case B_SYS_EX_END: 932 case B_TIMING_CLOCK: 933 case B_START: 934 case B_CONTINUE: 935 case B_STOP: 936 case B_ACTIVE_SENSING: 937 WriteByte(event->byte1); 938 break; 939 940 case B_MIDI_TIME_CODE: 941 case B_SONG_SELECT: 942 case B_CABLE_MESSAGE: 943 WriteByte(event->byte1); 944 WriteByte(event->byte2); 945 break; 946 947 case B_SONG_POSITION: 948 WriteByte(event->byte1); 949 WriteByte(event->byte2); 950 WriteByte(event->byte3); 951 break; 952 953 case B_SYSTEM_RESET: 954 WriteMetaEvent(event); 955 break; 956 } 957 break; 958 } 959 } 960 961 WriteVarLength(0); 962 WriteByte(0xFF); // the end-of-track 963 WriteByte(0x2F); // marker is required 964 WriteByte(0x00); 965 966 fFile->Seek(lengthPos, SEEK_SET); 967 Write32Bit(fByteCount); 968 fFile->Seek(0, SEEK_END); 969 } 970 971 972 void 973 BMidiStore::WriteMetaEvent(BMidiEvent* event) 974 { 975 // We only export the Tempo Change meta event. 976 977 if (event->byte2 == 0x51 && event->byte3 == 0x03) { 978 uint32 val = 60000000 / event->tempo; 979 980 WriteByte(0xFF); 981 WriteByte(0x51); 982 WriteByte(0x03); 983 WriteByte(val >> 16); 984 WriteByte(val >> 8); 985 WriteByte(val); 986 } 987 } 988 989