1 /** 2 This file is a port of some old C code I had. 3 4 I'll eventually refactor it into something more D-like 5 */ 6 module arsd.midi; 7 8 9 import core.stdc.stdio; 10 import core.stdc.stdlib; 11 12 /* NOTE: MIDI files are BIG ENDIAN! */ 13 14 struct MidiChunk { 15 int timeStamp; // this is encoded my way. real file is msb == 1, more ubytes 16 ubyte event; // Event << 8 | channel is how it is actually stored 17 18 // for channel events 19 ubyte channel; // see above - it is stored with event! 20 // channel == track btw 21 ubyte param1; // pitch (usually) 22 ubyte param2; // volume - not necessarily present 23 24 ubyte status; // event << 4 | channel 25 26 // for meta events (event = f, channel = f 27 ubyte type; 28 int length; // stored as variable length 29 ubyte* data; // only allocated if event == 255 30 31 MidiChunk* next; // next in the track 32 33 // This stuff is just for playing help and such 34 // It is only set if you call recalculateMidiAbsolutes, and probably 35 // not maintained if you do edits 36 int track; 37 uint absoluteTime; 38 uint absoluteTimeInMilliSeconds; // for convenience 39 int absoluteWait; 40 MidiChunk* nextAbsolute; 41 } 42 43 /* 44 Meta event 45 timeStamp = 0 46 event = 255 47 channel = event 48 param1 param2 = not in gile 49 length = variable 50 data[length] 51 */ 52 53 struct MidiTrack { 54 // MTrk header 55 int lengthInBytes; 56 MidiChunk* chunks; // linked list 57 // the linked list should NOT hold the track ending chunk 58 // just hold a null instead 59 } 60 61 struct Midi { 62 // headers go here 63 short type; 64 short numTracks; 65 short speed; 66 MidiTrack* tracks; /* Array of numTracks size */ 67 68 // only set if you call recalculateMidiAbsolutes 69 MidiChunk* firstAbsolute; 70 } 71 72 73 enum MIDI_EVENT_NOTE_OFF = 0x08; 74 enum MIDI_EVENT_NOTE_ON = 0x09; 75 enum MIDI_EVENT_NOTE_AFTERTOUCH = 0x0a; 76 enum MIDI_EVENT_CONTROLLER = 0x0b; 77 enum MIDI_EVENT_PROGRAM_CHANGE = 0x0c;// only one param 78 enum MIDI_EVENT_CHANNEL_AFTERTOUCH = 0x0d;// only one param 79 enum MIDI_EVENT_PITCH_BEND = 0x0e; 80 81 82 83 /* 84 static char[][] instrumentNames = { 85 "", // 0 is nothing 86 // Piano: 87 "Acoustic Grand Piano", 88 "Bright Acoustic Piano", 89 "Electric Grand Piano", 90 "Honky-tonk Piano", 91 "Electric Piano 1", 92 "Electric Piano 2", 93 "Harpsichord", 94 "Clavinet", 95 96 // Chromatic Percussion: 97 "Celesta", 98 "Glockenspiel", 99 "Music Box", 100 "Vibraphone", 101 "Marimba", 102 "Xylophone", 103 "Tubular Bells", 104 "Dulcimer", 105 106 // Organ: 107 "Drawbar Organ", 108 "Percussive Organ", 109 "Rock Organ", 110 "Church Organ", 111 "Reed Organ", 112 "Accordion", 113 "Harmonica", 114 "Tango Accordion", 115 116 // Guitar: 117 "Acoustic Guitar (nylon)", 118 "Acoustic Guitar (steel)", 119 "Electric Guitar (jazz)", 120 "Electric Guitar (clean)", 121 "Electric Guitar (muted)", 122 "Overdriven Guitar", 123 "Distortion Guitar", 124 "Guitar harmonics", 125 126 // Bass: 127 "Acoustic Bass", 128 "Electric Bass (finger)", 129 "Electric Bass (pick)", 130 "Fretless Bass", 131 "Slap Bass 1", 132 "Slap Bass 2", 133 "Synth Bass 1", 134 "Synth Bass 2", 135 136 // Strings: 137 "Violin", 138 "Viola", 139 "Cello", 140 "Contrabass", 141 "Tremolo Strings", 142 "Pizzicato Strings", 143 "Orchestral Harp", 144 "Timpani", 145 146 // Strings (continued): 147 "String Ensemble 1", 148 "String Ensemble 2", 149 "Synth Strings 1", 150 "Synth Strings 2", 151 "Choir Aahs", 152 "Voice Oohs", 153 "Synth Voice", 154 "Orchestra Hit", 155 156 // Brass: 157 "Trumpet", 158 "Trombone", 159 "Tuba", 160 "Muted Trumpet", 161 "French Horn", 162 "Brass Section", 163 "Synth Brass 1", 164 "Synth Brass 2", 165 166 // Reed: 167 "Soprano Sax", 168 "Alto Sax", 169 "Tenor Sax", 170 "Baritone Sax", 171 "Oboe", 172 "English Horn", 173 "Bassoon", 174 "Clarinet", 175 176 // Pipe: 177 "Piccolo", 178 "Flute", 179 "Recorder", 180 "Pan Flute", 181 "Blown Bottle", 182 "Shakuhachi", 183 "Whistle", 184 "Ocarina", 185 186 // Synth Lead: 187 "Lead 1 (square)", 188 "Lead 2 (sawtooth)", 189 "Lead 3 (calliope)", 190 "Lead 4 (chiff)", 191 "Lead 5 (charang)", 192 "Lead 6 (voice)", 193 "Lead 7 (fifths)", 194 "Lead 8 (bass + lead)", 195 196 // Synth Pad: 197 "Pad 1 (new age)", 198 "Pad 2 (warm)", 199 "Pad 3 (polysynth)", 200 "Pad 4 (choir)", 201 "Pad 5 (bowed)", 202 "Pad 6 (metallic)", 203 "Pad 7 (halo)", 204 "Pad 8 (sweep)", 205 206 // Synth Effects: 207 "FX 1 (rain)", 208 "FX 2 (soundtrack)", 209 "FX 3 (crystal)", 210 "FX 4 (atmosphere)", 211 "FX 5 (brightness)", 212 "FX 6 (goblins)", 213 "FX 7 (echoes)", 214 "FX 8 (sci-fi)", 215 216 // Ethnic: 217 "Sitar", 218 "Banjo", 219 "Shamisen", 220 "Koto", 221 "Kalimba", 222 "Bag pipe", 223 "Fiddle", 224 "Shanai", 225 226 // Percussive: 227 "Tinkle Bell", 228 "Agogo", 229 "Steel Drums", 230 "Woodblock", 231 "Taiko Drum", 232 "Melodic Tom", 233 "Synth Drum", 234 235 // Sound effects: 236 "Reverse Cymbal", 237 "Guitar Fret Noise", 238 "Breath Noise", 239 "Seashore", 240 "Bird Tweet", 241 "Telephone Ring", 242 "Helicopter", 243 "Applause", 244 "Gunshot" 245 }; 246 */ 247 248 249 int addMidiTrack(Midi* mid){ 250 int trackNum; 251 MidiTrack* tracks; 252 tracks = cast(MidiTrack*) realloc(mid.tracks, MidiTrack.sizeof * (mid.numTracks + 1)); 253 if(tracks is null) 254 return -1; 255 256 mid.tracks = tracks; 257 trackNum = mid.numTracks; 258 mid.numTracks++; 259 260 mid.tracks[trackNum].lengthInBytes = 0; 261 mid.tracks[trackNum].chunks = null; 262 263 return trackNum; 264 } 265 266 int addMidiEvent(Midi* mid, int track, int deltatime, int event, int channel, int value1, int value2){ 267 int length = 2; 268 MidiChunk* c; 269 MidiChunk* current, previous; 270 if(track >= mid.numTracks) 271 return -1; 272 273 c = cast(MidiChunk*) malloc(MidiChunk.sizeof); 274 if(c is null) 275 return -1; 276 277 c.timeStamp = deltatime; 278 c.event = cast(ubyte) event; 279 c.channel = cast(ubyte) channel; 280 c.param1 = cast(ubyte) value1; 281 c.param2 = cast(ubyte) value2; 282 283 c.status = cast(ubyte) ((event << 4) | channel); 284 285 c.type = 0; 286 c.length = 0; 287 c.data = null; 288 289 c.next = null; 290 291 292 previous = null; 293 current = mid.tracks[track].chunks; 294 while(current != null){ 295 previous = current; 296 current = current . next; 297 } 298 299 if(previous){ 300 previous.next = c; 301 } else { 302 mid.tracks[track].chunks = c; 303 } 304 305 length += getvldLength(deltatime); 306 if(event != MIDI_EVENT_CHANNEL_AFTERTOUCH && 307 event != MIDI_EVENT_PROGRAM_CHANGE) 308 length++; // param2 309 mid.tracks[track].lengthInBytes += length; 310 311 return 0; 312 } 313 314 int addMidiMetaEvent(Midi* mid, int track, int dt, int type, int length, ubyte* data){ 315 int len = 2; 316 int a; 317 MidiChunk* c; 318 MidiChunk* current, previous; 319 320 if(track >= mid.numTracks) 321 return -1; 322 323 c = cast(MidiChunk*) malloc(MidiChunk.sizeof); 324 if(c == null) 325 return -1; 326 327 c.timeStamp = dt; 328 c.event = 0xff; 329 c.channel = 0; 330 c.param1 = 0; 331 c.param2 = 0; 332 333 c.type = cast(ubyte) type; 334 c.length = length; 335 // copy data in 336 c.data = cast(typeof(c.data)) malloc(length); 337 if(c.data == null){ 338 free(c); 339 return -1; 340 } 341 for(a = 0; a < length; a++) 342 c.data[a] = data[a]; 343 344 345 c.next = null; 346 347 348 previous = null; 349 current = mid.tracks[track].chunks; 350 while(current != null){ 351 previous = current; 352 current = current . next; 353 } 354 355 if(previous){ 356 previous.next = c; 357 } else { 358 mid.tracks[track].chunks = c; 359 } 360 361 len += getvldLength(dt); 362 len += length; 363 364 mid.tracks[track].lengthInBytes += len; 365 366 return 0; 367 } 368 369 int createMidi(Midi** midWhere){ 370 Midi* mid; 371 372 mid = cast(Midi*) malloc(Midi.sizeof); 373 if(mid == null) 374 return 1; 375 376 mid.type = 1; 377 mid.numTracks = 0; 378 mid.speed = 0x80; // 128 ticks per quarter note - potential FIXME 379 mid.tracks = null; 380 381 *midWhere = mid; 382 return 0; 383 } 384 385 void freeChunkList(MidiChunk* c){ 386 if(c == null) 387 return; 388 freeChunkList(c.next); 389 if(c.event == 255) 390 free(c.data); 391 free(c); 392 } 393 394 void freeMidi(Midi** mid){ 395 int a; 396 Midi* m = *mid; 397 398 for(a = 0; a < m.numTracks; a++) 399 freeChunkList(m.tracks[a].chunks); 400 free(m.tracks); 401 free(m); 402 *mid = null; 403 } 404 405 // FIXME: these fail on big endian machines 406 void write4(int v, FILE* fp){ 407 fputc(*(cast(ubyte*)&v + 3), fp); 408 fputc(*(cast(ubyte*)&v + 2), fp); 409 fputc(*(cast(ubyte*)&v + 1), fp); 410 fputc(*(cast(ubyte*)&v + 0), fp); 411 } 412 413 void write2(short v, FILE* fp){ 414 fputc(*(cast(ubyte*)&v + 1), fp); 415 fputc(*(cast(ubyte*)&v + 0), fp); 416 } 417 418 void writevld(uint v, FILE* fp){ 419 uint omg = v; 420 ubyte a; 421 ubyte[4] ubytes; 422 int c = 0; 423 more: 424 a = cast(ubyte) (omg&(~(1 << 7))); 425 omg >>= 7; 426 if(omg){ 427 ubytes[c++] = a; 428 goto more; 429 } 430 431 ubytes[c] = a; 432 433 for(; c >= 0; c--) 434 fputc(ubytes[c] | (c ? (1<<7):0), fp); 435 } 436 437 438 int read4(FILE* fp){ 439 int v; 440 441 *(cast(ubyte*)&v + 3) = cast(ubyte) fgetc(fp); 442 *(cast(ubyte*)&v + 2) = cast(ubyte) fgetc(fp); 443 *(cast(ubyte*)&v + 1) = cast(ubyte) fgetc(fp); 444 *(cast(ubyte*)&v + 0) = cast(ubyte) fgetc(fp); 445 446 return v; 447 } 448 449 short read2(FILE* fp){ 450 short v; 451 *(cast(ubyte*)&v + 1) = cast(ubyte) fgetc(fp); 452 *(cast(ubyte*)&v + 0) = cast(ubyte) fgetc(fp); 453 454 return v; 455 } 456 457 uint readvld(FILE* fp){ 458 uint omg = 0; 459 ubyte a; 460 more: 461 a = cast(ubyte) fgetc(fp); 462 if(a & (1<<7)){ 463 a &= ~(1<<7); 464 omg <<= 7; 465 omg |= a; 466 goto more; 467 } 468 469 omg <<= 7; 470 omg |= a; 471 472 return omg; 473 } 474 475 476 477 int getvldLength(uint v){ 478 int count = 0; 479 uint omg = v; 480 ubyte a; 481 more: 482 a = omg&((1 << 7)-1); // 483 omg >>= 7; 484 if(omg){ 485 a &= 1<<7; 486 count++; 487 goto more; 488 } 489 count++; 490 return count; 491 } 492 493 // END: big endian fixme 494 495 int loadMidi(Midi** midWhere, const char* filename){ 496 int error = 0; 497 FILE* fp; 498 Midi* mid = null; 499 int runningStatus; 500 int t, a; 501 int numtrk; 502 503 int timestamp; 504 int event; 505 int channel; 506 int param1; 507 int type; 508 int length; 509 int param2; 510 ubyte* data; 511 512 int done; 513 514 fp = fopen(filename, "rb"); 515 if(fp == null){ 516 fprintf(stderr, "Cannot load file %s.\n", filename); 517 error = 1; 518 goto cleanup1; 519 } 520 521 522 if(fgetc(fp) != 'M') goto badfile; 523 if(fgetc(fp) != 'T') goto badfile; 524 if(fgetc(fp) != 'h') goto badfile; 525 if(fgetc(fp) != 'd') goto badfile; 526 if(read4(fp) != 6) goto badfile; 527 528 if(createMidi(&mid) != 0){ 529 fprintf(stderr, "Could not allocate struct\n"); 530 error = 3; 531 goto cleanup3; 532 } 533 534 mid.type = read2(fp); 535 numtrk = read2(fp); 536 mid.speed = read2(fp); 537 538 for(t = 0; t < numtrk; t++){ 539 if(fgetc(fp) != 'M') goto badfile; 540 if(fgetc(fp) != 'T') goto badfile; 541 if(fgetc(fp) != 'r') goto badfile; 542 if(fgetc(fp) != 'k') goto badfile; 543 544 if(addMidiTrack(mid) < 0){ 545 fprintf(stderr, "add midi track failed \n"); 546 error = 3; 547 goto cleanup3; 548 } 549 550 // mid.tracks[t].lengthInBytes = read4(fp) - 4; 551 read4(fp); // ignoring it for now FIXME? 552 553 done = 0; 554 do{ 555 timestamp = readvld(fp); 556 event = fgetc(fp); 557 if(event == 0xff){ 558 type = fgetc(fp); 559 length = readvld(fp); 560 561 // potential optimization for malloc 562 if(length){ 563 data = cast(typeof(data)) malloc(length); 564 for(a = 0; a < length; a++) 565 data[a] = cast(ubyte) fgetc(fp); 566 } else 567 data = null; 568 569 570 if(type == 0x2f){ 571 done = 1; 572 } else { 573 // add the event to the list here 574 // FIXME: error check 575 addMidiMetaEvent(mid, t, timestamp, 576 type, length, data); 577 } 578 if(data) 579 free(data); 580 } else { 581 if(event < 0x80){ 582 param1 = event; 583 event = runningStatus; 584 } else { 585 runningStatus = event; 586 param1 = fgetc(fp); 587 } 588 589 channel = event&0x0f; 590 event = event >> 4; 591 if(event != MIDI_EVENT_PROGRAM_CHANGE 592 && event != MIDI_EVENT_CHANNEL_AFTERTOUCH) 593 param2 = fgetc(fp); 594 595 // add the event 596 // FIXME: error check 597 addMidiEvent(mid, t, timestamp, event, channel, param1, param2); 598 } 599 } while(!done); 600 } 601 602 goto success; 603 badfile: 604 fprintf(stderr, "The file is not in the right format. %c\n", fgetc(fp)); 605 error = 2; 606 cleanup3: 607 if(mid != null) 608 freeMidi(&mid); 609 success: 610 fclose(fp); 611 *midWhere = mid; 612 cleanup1: 613 return error; 614 } 615 616 617 618 619 620 621 int saveMidi(Midi* mid, char* filename){ 622 int error = 0; 623 FILE* fp; 624 int t, a; 625 int runningStatus = -1; 626 int status; 627 628 fp = fopen(filename, "wb"); 629 if(fp == null){ 630 fprintf(stderr, "Unable to open midi file (%s) for writing.\n", filename); 631 error = 1; 632 goto cleanup1; 633 } 634 635 fputc('M', fp); 636 fputc('T', fp); 637 fputc('h', fp); 638 fputc('d', fp); 639 640 write4(6, fp); 641 write2(mid.type, fp); 642 write2(mid.numTracks, fp); 643 write2(mid.speed, fp); 644 645 for(t = 0; t < mid.numTracks; t++){ 646 fputc('M', fp); 647 fputc('T', fp); 648 fputc('r', fp); 649 fputc('k', fp); 650 651 runningStatus = -1; 652 653 write4(mid.tracks[t].lengthInBytes + 4, fp); 654 MidiChunk* current; 655 current = mid.tracks[t].chunks; 656 while(current != null){ 657 writevld(current.timeStamp, fp); 658 if(current.event == 0xff){ 659 fputc(current.event, fp); 660 fputc(current.type, fp); 661 writevld(current.length, fp); 662 for(a = 0; a < current.length; a++) 663 fputc(current.data[a], fp); 664 } else { 665 // FIXME: add support for writing running status 666 status = current.event << 4 | current.channel; 667 668 // if(status != runningStatus){ 669 runningStatus = status; 670 fputc(status, fp); 671 // } 672 673 fputc(current.param1, fp); 674 if(current.event != MIDI_EVENT_PROGRAM_CHANGE 675 &¤t.event != MIDI_EVENT_CHANNEL_AFTERTOUCH) 676 fputc(current.param2, fp); 677 } 678 current = current.next; 679 } 680 /* the end of track chunk */ 681 fputc(0, fp); 682 fputc(0xff, fp); 683 fputc(0x2f, fp); 684 fputc(0x00, fp); 685 } 686 /* cleanup2:*/ 687 fclose(fp); 688 cleanup1: 689 return error; 690 } 691 692 693 int removeMidiTrack(Midi* m, int track){ 694 int a; 695 if(track >= m.numTracks) 696 return -1; 697 698 for(a = track; a < m.numTracks-1; a++){ 699 m.tracks[a] = m.tracks[a+1]; 700 } 701 702 m.numTracks--; 703 704 return 0; 705 } 706 707 void printMidiEvent(MidiChunk* c){ 708 int e = c.event; 709 printf("%d %s %d %d\n", c.timeStamp, 710 e == MIDI_EVENT_NOTE_OFF ? "Note off".ptr 711 :e == MIDI_EVENT_NOTE_ON ? "Note on".ptr 712 :e == MIDI_EVENT_PROGRAM_CHANGE ? "Program change".ptr 713 :e == MIDI_EVENT_NOTE_AFTERTOUCH ? "Aftertouch".ptr 714 : "I dunno".ptr 715 , c.param1, c.param2); 716 } 717 718 MidiChunk* getTrackNameChunk(Midi* m, int track){ 719 MidiChunk* c; 720 721 if(track >= m.numTracks) 722 return null; 723 724 c = m.tracks[track].chunks; 725 while(c){ 726 if(c.event == 0xff && c.type == 3) 727 return c; 728 729 c = c.next; 730 } 731 732 return c; 733 } 734 735 int getMidiTempo(Midi* m){ 736 int a; 737 MidiChunk* c; 738 for(a = 0; a < m.numTracks; a++){ 739 c = m.tracks[a].chunks; 740 while(c){ 741 if(c.event == 0xff) 742 if(c.type == 0x51){ 743 int p = 0; 744 p |= cast(int)(c.data[0]) << 16; 745 p |= cast(int)(c.data[1]) << 8; 746 p |= cast(int)(c.data[2]) << 0; 747 748 return 60000000 / p; 749 } 750 c = c.next; 751 } 752 } 753 754 return 120; 755 } 756 757 int getTempoFromTempoEvent(MidiChunk* c){ 758 int tempo = -1; 759 if(c.event == 0xff && c.type == 0x51){ 760 int p = 0; 761 p |= cast(int)(c.data[0]) << 16; 762 p |= cast(int)(c.data[1]) << 8; 763 p |= cast(int)(c.data[2]) << 0; 764 tempo = 60000000 / p; 765 } 766 return tempo; 767 } 768 769 // returns milliseconds to wait given the params 770 int getMidiWaitTime(Midi* mid, int timeStamp, int tempo){ 771 return (timeStamp * 60000) / (tempo * mid.speed); 772 } 773 774 // sets absolute values and links up, useful for playing or editing 775 // but remember you must recalculate them yourself if you change anything 776 // Returns the final absolute time in seconds 777 int recalculateMidiAbsolutes(Midi* mid){ 778 MidiChunk*[128] c; 779 int[128] trackWaits; 780 int playing; 781 int waited; 782 int minWait = 100000; 783 int a; 784 uint absoluteTime = 0; 785 int tempo = 120; 786 uint absoluteTimeInMilliSeconds = 0; 787 int t; 788 int timeOfLastEvent = 0; 789 790 MidiChunk* absoulteCurrent; 791 792 mid.firstAbsolute = null; 793 absoulteCurrent = null; 794 795 playing = mid.numTracks; 796 for(a = 0; a < mid.numTracks; a++){ 797 c[a] = mid.tracks[a].chunks; 798 if(c[a]){ 799 trackWaits[a] = c[a].timeStamp; 800 if(trackWaits[a] < minWait) 801 minWait = trackWaits[a]; 802 } else 803 playing--; 804 } 805 806 while(playing){ 807 waited = minWait; 808 minWait = 1000000; 809 absoluteTime += waited; 810 absoluteTimeInMilliSeconds += getMidiWaitTime(mid, waited, tempo); 811 for(a = 0; a < mid.numTracks; a++){ 812 if(!c[a]) 813 continue; 814 trackWaits[a] -= waited; 815 if(trackWaits[a] == 0){ 816 817 t = getTempoFromTempoEvent(c[a]); 818 if(t != -1) 819 tempo = t; 820 821 // append it to the list 822 if(absoulteCurrent == null){ 823 mid.firstAbsolute = c[a]; 824 absoulteCurrent = c[a]; 825 } else { 826 absoulteCurrent.nextAbsolute = c[a]; 827 absoulteCurrent = absoulteCurrent.nextAbsolute; 828 } 829 absoulteCurrent.nextAbsolute = null; 830 absoulteCurrent.absoluteTime = absoluteTime; 831 absoulteCurrent.absoluteTimeInMilliSeconds = absoluteTimeInMilliSeconds; 832 absoulteCurrent.track = a; 833 absoulteCurrent.absoluteWait = absoluteTime - timeOfLastEvent; 834 835 timeOfLastEvent = absoluteTime; 836 c[a] = c[a].next; 837 if(c[a] == null){ 838 playing --; 839 trackWaits[a] = 1000000; 840 } 841 else 842 trackWaits[a] = c[a].timeStamp; 843 } 844 if(trackWaits[a] < minWait ) 845 minWait = trackWaits[a]; 846 } 847 } 848 849 850 return absoluteTimeInMilliSeconds / 1000; 851 } 852 853 // returns approximate seconds 854 int getMidiLength(Midi* mid){ 855 return recalculateMidiAbsolutes(mid); 856 } 857 858 859 860 861 862 863 import arsd.simpleaudio; 864 865 struct PlayingMidi { 866 ushort channelMask; /* The channels that will be played */ 867 int[128] playtracks; 868 869 // Callbacks 870 // onPlayedNote. Args: this, note, midi ticks waited since last message 871 // This is intended for tablature creation 872 void function(void*, int, int) onPlayedNote; 873 // onMidiEvent. Args: this, event being executed 874 // This can be used to print it or whatever 875 // If you return 1, it skips the event. Return 0 for normal operation 876 int function(void*, MidiChunk*) onMidiEvent; 877 878 Midi* mid; 879 MidiOutput* dev; 880 881 int transpose; 882 float tempoMultiplier; 883 884 /* This stuff is state for the midi in progress */ 885 int tempo; 886 887 MidiChunk* current; 888 889 int wait; 890 } 891 892 893 // the main loop for the first time 894 int resetPlayingMidi(PlayingMidi* pmid){ 895 pmid.current = pmid.mid.firstAbsolute; 896 pmid.tempo = 120; 897 pmid.wait = 0; 898 if(pmid.current) 899 return getMidiWaitTime(pmid.mid, pmid.current.absoluteWait, cast(int) (pmid.tempo * pmid.tempoMultiplier)); 900 return 0; 901 } 902 903 void setPlayingMidiDefaults(PlayingMidi* pmid){ 904 int a; 905 pmid.channelMask =0xffff; 906 for(a = 0; a < 128; a++) 907 pmid.playtracks[a] = 1; 908 909 pmid.onPlayedNote = null; 910 pmid.onMidiEvent = null; 911 912 pmid.mid = null; 913 pmid.dev = null; 914 915 pmid.transpose = 0; 916 pmid.tempoMultiplier = 1.0; 917 918 } 919 920 921 void seekPlayingMidi(PlayingMidi* pmid, int sec){ 922 pmid.dev.silenceAllNotes(); 923 pmid.dev.reset(); 924 925 pmid.current = pmid.mid.firstAbsolute; 926 while(pmid.current){ 927 if(pmid.current.absoluteTimeInMilliSeconds >= sec * 1000) 928 break; 929 pmid.current = pmid.current.next; 930 } 931 } 932 933 934 // This is the main loop. Returns how many milliseconds to wait before 935 // calling it again. If zero, then the song is over. 936 int advancePlayingMidi(PlayingMidi* pmid){ 937 MidiChunk* c; 938 if(pmid.current == null) 939 return 0; 940 more: 941 c = pmid.current; 942 pmid.wait += c.absoluteWait; 943 944 if(pmid.onMidiEvent){ 945 if(pmid.onMidiEvent(pmid, c)) 946 goto skip; 947 } 948 949 if(c.event != 0xff){ 950 if(pmid.playtracks[c.track]){ 951 if(pmid.channelMask & (1 << c.channel)){ 952 int note = c.param1; 953 if(c.event == MIDI_EVENT_NOTE_ON 954 || c.event == MIDI_EVENT_NOTE_AFTERTOUCH 955 || c.event == MIDI_EVENT_NOTE_OFF){ 956 note += pmid.transpose; 957 //skipCounter = SKIP_MAX; 958 } 959 960 if(pmid.dev) 961 pmid.dev.writeMidiMessage(c.status, note, c.param2); 962 if(pmid.onPlayedNote) 963 if(c.event == MIDI_EVENT_NOTE_ON 964 && c.param2 != 0){ 965 pmid.onPlayedNote(pmid, 966 note, 967 (pmid.wait * 4) / (pmid.mid.speed)); 968 pmid.wait = 0; 969 } 970 } 971 } 972 } else { 973 if(c.type == 0x51) 974 pmid.tempo = getTempoFromTempoEvent(c); 975 } 976 977 skip: 978 pmid.current = pmid.current.nextAbsolute; 979 if(pmid.current) 980 if(pmid.current.absoluteWait == 0) 981 goto more; 982 else 983 return getMidiWaitTime( 984 pmid.mid, 985 pmid.current.absoluteWait, 986 cast(int) (pmid.tempo * pmid.tempoMultiplier)); 987 else return 0; 988 } 989 990 991 992 993 version(MidiDemo) { 994 995 996 997 998 999 MidiOutput* globaldev; 1000 1001 version(Windows) 1002 import core.sys.windows.windows; 1003 else { 1004 import core.sys.posix.unistd; 1005 void Sleep(int ms){ 1006 usleep(ms*1000); 1007 } 1008 1009 import core.stdc.signal; 1010 // FIXME: this sucks. 1011 extern(C) 1012 alias fuckyou = void function(int) @nogc nothrow @system; 1013 extern(C) 1014 void sigint(){ 1015 if(globaldev){ 1016 globaldev.silenceAllNotes(); 1017 globaldev.reset(); 1018 destroy(*globaldev); 1019 } 1020 exit(1); 1021 } 1022 } 1023 1024 enum SKIP_MAX = 3000; // allow no more than about 3 seconds of silence 1025 // if the -k option is set 1026 1027 // Potential FIXME: it doesn't support more than 128 tracks. 1028 1029 void awesome(void* midiptr, int note, int wait) { 1030 printf("%d %d ", wait, note); 1031 fflush(stdout); 1032 } 1033 1034 // FIXME: add support for displaying lyrics 1035 extern(C) int main(int argc, char** argv){ 1036 int a, b; 1037 1038 PlayingMidi pmid; 1039 1040 int tempo = 120; 1041 Midi* mid; 1042 MidiOutput midiout = MidiOutput(0); 1043 MidiChunk*[128] c; 1044 1045 int minWait = 10000, waited; 1046 int playing; 1047 1048 int wait = 0; 1049 int num; 1050 1051 char* filename = null; 1052 1053 int verbose = 0; 1054 float tempoMultiplier = 1; 1055 int transpose = 0; 1056 int displayinfo = 0; 1057 int play = 1; 1058 int tracing = 0; 1059 int skip = 0; 1060 int[128] playtracks; 1061 int skipCounter = SKIP_MAX; 1062 1063 ushort channelMask = 0xffff; 1064 1065 int sleepTime = 0; 1066 1067 version(Posix) { 1068 signal(SIGINT, cast(fuckyou) &sigint); 1069 } 1070 1071 for(a = 0; a< 128; a++) 1072 playtracks[a] = 1; 1073 1074 1075 for(a = 1; a < argc; a++){ 1076 if(argv[a][0] == '-') 1077 switch(argv[a][1]){ 1078 case 't': 1079 for(b = 0; b< 128; b++) 1080 playtracks[b] = 0; 1081 num = 0; 1082 b = 0; 1083 a++; 1084 if(a == argc){ 1085 printf("%s: option %s requires an argument\n", argv[0], argv[a-1]); 1086 return 1; 1087 } 1088 for(b = 0; argv[a][b]; b++){ 1089 if(argv[a][b] == ','){ 1090 playtracks[num] = 1; 1091 num = 0; 1092 continue; 1093 } 1094 num *= 10; 1095 num += argv[a][b] - '0'; 1096 } 1097 playtracks[num] = 1; 1098 break; 1099 case 's': 1100 a++; 1101 if(a == argc){ 1102 printf("%s: option %s requires an argument\n", argv[0], argv[a-1]); 1103 return 1; 1104 } 1105 tempoMultiplier = atof(argv[a]); 1106 break; 1107 case 'i': // FIXME 1108 displayinfo = 1; 1109 // tracks, guesstimated length 1110 break; 1111 // -o loop to from 1112 // -b begin at 1113 // -e end at 1114 case 'l': 1115 tracing = 1; 1116 break; 1117 case 'n': 1118 play = 0; 1119 break; 1120 case 'k': 1121 skip = 1; 1122 break; 1123 case 'c': 1124 channelMask = 0; 1125 // channels 1126 num = 0; 1127 b = 0; 1128 a++; 1129 if(a == argc){ 1130 printf("%s: option %s requires an argument\n", argv[0], argv[a-1]); 1131 return 1; 1132 } 1133 for(b = 0; argv[a][b]; b++){ 1134 if(argv[a][b] == ','){ 1135 channelMask |= (1 << num); 1136 num = 0; 1137 continue; 1138 } 1139 num *= 10; 1140 num += argv[a][b] - '0'; 1141 } 1142 channelMask |= (1 << num); 1143 break; 1144 case 'r': 1145 a++; 1146 if(a == argc){ 1147 printf("%s: option %s requires an argument\n", argv[0], argv[a-1]); 1148 return 1; 1149 } 1150 transpose = atoi(argv[a]); 1151 break; 1152 case 'v': 1153 verbose = 1; 1154 break; 1155 case 'h': 1156 printf("Usage: %s [options...] file\n", argv[0]); 1157 printf(" Options:\n"); 1158 printf(" -t comma separated list of tracks to play (default: all)\n"); 1159 printf(" -s tempo (speed) multiplier (default: 1.0)\n"); 1160 printf(" -i file info (track list)\n"); 1161 printf(" -l list notes as they are played (in the format totablature expects)\n"); 1162 printf(" -n no sound; don't actually play the midi\n"); 1163 printf(" -c comma separated list of channels to play (default: all)\n"); 1164 printf(" -r transpose notes by amount (default: 0)\n"); 1165 printf(" -k skip long sections of silence (good for playing single tracks)\n"); 1166 1167 printf(" -v verbose; list all events except note on / note off\n"); 1168 printf(" -h shows this help screen\n"); 1169 1170 return 0; 1171 break; 1172 default: 1173 printf("%s: unknown command line option: %s\n", argv[0], argv[1]); 1174 return 1; 1175 } 1176 else 1177 filename = argv[a]; 1178 } 1179 1180 if(filename == null){ 1181 printf("%s: no file given. Try %s -h for help.\n", argv[0], argv[0]); 1182 return 1; 1183 } 1184 1185 1186 1187 1188 1189 1190 1191 loadMidi(&mid, filename); 1192 if(mid == null){ 1193 printf("%s: unable to read file %s\n", argv[0], filename); 1194 return 1; 1195 } 1196 1197 if(displayinfo){ 1198 int len = getMidiLength(mid); 1199 printf("File: %s\n", filename); 1200 printf("Ticks per quarter note: %d\n", mid.speed); 1201 printf("Initial tempo: %d\n", getMidiTempo(mid)); 1202 printf("Length: %d:%d\n", len / 60, len%60); 1203 printf("Tracks:\n"); 1204 for(a = 0; a < mid.numTracks; a++){ 1205 c[0] = getTrackNameChunk(mid, a); 1206 if(c[0] != null){ 1207 printf("%d: ", a); 1208 for(b = 0; b < c[0].length; b++) 1209 fputc(c[0].data[b], stdout); 1210 printf("\n"); 1211 } 1212 } 1213 1214 freeMidi(&mid); 1215 return 0; 1216 } 1217 1218 1219 1220 if(play){ 1221 globaldev = &midiout; 1222 } else 1223 globaldev = null; 1224 1225 1226 recalculateMidiAbsolutes(mid); 1227 setPlayingMidiDefaults(&pmid); 1228 1229 if(tracing) 1230 pmid.onPlayedNote = &awesome; 1231 pmid.mid = mid; 1232 pmid.dev = &midiout; 1233 1234 for(a = 0; a < 127; a++) 1235 pmid.playtracks[a] = playtracks[a]; 1236 1237 pmid.channelMask = channelMask; 1238 pmid.transpose = transpose; 1239 pmid.tempoMultiplier = tempoMultiplier; 1240 1241 1242 sleepTime = resetPlayingMidi(&pmid); 1243 do { 1244 //printf("%d\n", sleepTime); 1245 if(play) { 1246 if(skip && sleepTime > 1000) 1247 sleepTime = 1000; 1248 Sleep(sleepTime); 1249 } 1250 sleepTime = advancePlayingMidi(&pmid); 1251 } while(sleepTime); 1252 1253 1254 /* 1255 playing = mid.numTracks; 1256 1257 // prepare! 1258 for(a = 0; a < mid.numTracks; a++){ 1259 c[a] = mid.tracks[a].chunks; 1260 if(c[a]){ 1261 trackWaits[a] = c[a].timeStamp; 1262 if(trackWaits[a] < minWait) 1263 minWait = trackWaits[a]; 1264 } else 1265 playing--; 1266 } 1267 1268 while(playing){ 1269 if(play && (!skip || skipCounter > 100)){ 1270 Sleep(getMidiWaitTime(mid, minWait, (int)(tempo * tempoMultiplier))); 1271 if(skip) 1272 skipCounter -= getMidiWaitTime(mid, minWait, (int)(tempo*tempoMultiplier)); 1273 } 1274 waited = minWait; 1275 minWait = 1000000; 1276 wait += waited; 1277 for(a = 0; a < mid.numTracks; a++){ 1278 if(!c[a]) 1279 continue; 1280 trackWaits[a] -= waited; 1281 if(trackWaits[a] == 0){ 1282 if(c[a].event != 0xff){ 1283 if(playtracks[a]){ 1284 if(playchannels[c[a].channel]){ 1285 int note = c[a].param1; 1286 if(c[a].event == MIDI_EVENT_NOTE_ON 1287 || c[a].event == MIDI_EVENT_NOTE_AFTERTOUCH 1288 || c[a].event == MIDI_EVENT_NOTE_OFF){ 1289 note += transpose; 1290 skipCounter = SKIP_MAX; 1291 } 1292 1293 if(play) 1294 writeMidiMessage(dev, c[a].status, note, c[a].param2); 1295 if(tracing) 1296 if(c[a].event == MIDI_EVENT_NOTE_ON 1297 && c[a].param2 != 0){ 1298 printf("%d %d ", 1299 (wait * 4) / (mid.speed), 1300 note); 1301 fflush(stdout); 1302 wait = 0; 1303 } 1304 } 1305 } 1306 // data output: 1307 // waittime note 1308 // waittime is in 1/16 notes 1309 } else { 1310 if(c[a].type == 0x51){ 1311 tempo = getTempoFromTempoEvent(c[a]); 1312 if(verbose) 1313 printf("Tempo change: %d\n", tempo); 1314 } 1315 } 1316 c[a] = c[a].next; 1317 if(c[a] == null){ 1318 playing --; 1319 trackWaits[a] = 1000000; 1320 } 1321 else 1322 trackWaits[a] = c[a].timeStamp; 1323 } 1324 1325 if(trackWaits[a] < minWait ) 1326 minWait = trackWaits[a]; 1327 } 1328 } 1329 */ 1330 1331 freeMidi(&mid); 1332 1333 return 0; 1334 } 1335 }