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 25struct 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 51static int 52compare_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 64BMidiStore::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 81BMidiStore::~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 92void 93BMidiStore::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 106void 107BMidiStore::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 120void 121BMidiStore::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 134void 135BMidiStore::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 148void 149BMidiStore::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 161void 162BMidiStore::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 173void 174BMidiStore::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 186void 187BMidiStore::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 200void 201BMidiStore::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 214void 215BMidiStore::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 225void 226BMidiStore::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 239status_t 240BMidiStore::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 285status_t 286BMidiStore::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 314void 315BMidiStore::SortEvents(bool force) 316{ 317 if (force || fNeedsSorting) { 318 fEvents->SortItems(compare_events); 319 fNeedsSorting = false; 320 } 321} 322 323 324uint32 325BMidiStore::CountEvents() const 326{ 327 return fEvents->CountItems(); 328} 329 330 331uint32 332BMidiStore::CurrentEvent() const 333{ 334 return fCurrentEvent; 335} 336 337 338void 339BMidiStore::SetCurrentEvent(uint32 eventNumber) 340{ 341 fCurrentEvent = eventNumber; 342} 343 344 345uint32 346BMidiStore::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 362uint32 363BMidiStore::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 374uint32 375BMidiStore::BeginTime() const 376{ 377 return fStartTime; 378} 379 380 381void 382BMidiStore::SetTempo(int32 beatsPerMinute_) 383{ 384 fBeatsPerMinute = beatsPerMinute_; 385} 386 387 388int32 389BMidiStore::Tempo() const 390{ 391 return fBeatsPerMinute; 392} 393 394 395void BMidiStore::_ReservedMidiStore1() { } 396void BMidiStore::_ReservedMidiStore2() { } 397void BMidiStore::_ReservedMidiStore3() { } 398 399 400void 401BMidiStore::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 459void 460BMidiStore::AddEvent(BMidiEvent* event) 461{ 462 fEvents->AddItem(event); 463 fNeedsSorting = true; 464} 465 466 467void 468BMidiStore::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 539BMidiEvent* 540BMidiStore::EventAt(int32 index) const 541{ 542 return (BMidiEvent*)fEvents->ItemAt(index); 543} 544 545 546uint32 547BMidiStore::GetEventTime(const BMidiEvent* event) const 548{ 549 if (event->ticks) 550 return TicksToMilliseconds(event->time); 551 552 return event->time; 553} 554 555 556uint32 557BMidiStore::TicksToMilliseconds(uint32 ticks) const 558{ 559 return ((uint64)ticks * 60000) / (fBeatsPerMinute * fTicksPerBeat); 560} 561 562 563uint32 564BMidiStore::MillisecondsToTicks(uint32 ms) const 565{ 566 return ((uint64)ms * fBeatsPerMinute * fTicksPerBeat) / 60000; 567} 568 569 570void 571BMidiStore::ReadFourCC(char* fourcc) 572{ 573 if (fFile->Read(fourcc, 4) != 4) 574 throw (status_t) B_BAD_MIDI_DATA; 575} 576 577 578void 579BMidiStore::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 588uint32 589BMidiStore::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 599void 600BMidiStore::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 613uint16 614BMidiStore::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 624void 625BMidiStore::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 636uint8 637BMidiStore::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 650uint8 651BMidiStore::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 662void 663BMidiStore::WriteByte(uint8 val) 664{ 665 if (fFile->Write(&val, 1) != 1) 666 throw (status_t) B_ERROR; 667 668 ++fByteCount; 669} 670 671 672void 673BMidiStore::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 683uint32 684BMidiStore::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 700void 701BMidiStore::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 720void 721BMidiStore::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 739void 740BMidiStore::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 845void 846BMidiStore::ReadSystemExclusive() 847{ 848 // We do not import sysex's from MIDI files. 849 850 SkipBytes(ReadVarLength()); 851} 852 853 854void 855BMidiStore::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 882void 883BMidiStore::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 973void 974BMidiStore::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