1/* FluidSynth - A Software Synthesizer 2 * 3 * Copyright (C) 2003 Peter Hanappe and others. 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Library General Public License 7 * as published by the Free Software Foundation; either version 2 of 8 * the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, but 11 * WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Library General Public License for more details. 14 * 15 * You should have received a copy of the GNU Library General Public 16 * License along with this library; if not, write to the Free 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 18 * 02111-1307, USA 19 */ 20 21#include "fluid_midi.h" 22#include "fluid_sys.h" 23#include "fluid_synth.h" 24#include "fluid_settings.h" 25 26/* all outgoing user messages are stored in a global text buffer */ 27#define MIDI_MESSAGE_LENGTH 1024 28char midi_message_buffer[MIDI_MESSAGE_LENGTH]; 29 30 31/* Taken from Nagano Daisuke's USB-MIDI driver */ 32 33static int remains_f0f6[] = { 34 0, /** 0xF0 **/ 35 2, /** 0XF1 **/ 36 3, /** 0XF2 **/ 37 2, /** 0XF3 **/ 38 2, /** 0XF4 (Undefined by MIDI Spec, and subject to change) **/ 39 2, /** 0XF5 (Undefined by MIDI Spec, and subject to change) **/ 40 1 /** 0XF6 **/ 41}; 42 43static int remains_80e0[] = { 44 3, /** 0x8X Note Off **/ 45 3, /** 0x9X Note On **/ 46 3, /** 0xAX Poly-key pressure **/ 47 3, /** 0xBX Control Change **/ 48 2, /** 0xCX Program Change **/ 49 2, /** 0xDX Channel pressure **/ 50 3 /** 0xEX PitchBend Change **/ 51}; 52 53 54/*************************************************************** 55 * 56 * MIDIFILE 57 */ 58 59/** 60 * Open a MIDI file and return a new MIDI file handle. 61 * @internal 62 * @param filename Path of file to open. 63 * @return New MIDI file handle or NULL on error. 64 */ 65fluid_midi_file* new_fluid_midi_file(char* filename) 66{ 67 fluid_midi_file* mf; 68 69 mf = FLUID_NEW(fluid_midi_file); 70 if (mf == NULL) { 71 FLUID_LOG(FLUID_ERR, "Out of memory"); 72 return NULL; 73 } 74 FLUID_MEMSET(mf, 0, sizeof(fluid_midi_file)); 75 76 mf->c = -1; 77 mf->running_status = -1; 78 mf->fp = FLUID_FOPEN(filename, "rb"); 79 80 if (mf->fp == NULL) { 81 FLUID_LOG(FLUID_ERR, "Couldn't open the MIDI file"); 82 FLUID_FREE(mf); 83 return NULL; 84 } 85 86 if (fluid_midi_file_read_mthd(mf) != FLUID_OK) { 87 FLUID_FREE(mf); 88 return NULL; 89 } 90 return mf; 91} 92 93/** 94 * Delete a MIDI file handle. 95 * @internal 96 * @param mf MIDI file handle to close and free. 97 */ 98void delete_fluid_midi_file(fluid_midi_file* mf) 99{ 100 if (mf == NULL) { 101 return; 102 } 103 if (mf->fp != NULL) { 104 FLUID_FCLOSE(mf->fp); 105 } 106 FLUID_FREE(mf); 107 return; 108} 109 110/* 111 * Get the next byte in a MIDI file. 112 */ 113int fluid_midi_file_getc(fluid_midi_file* mf) 114{ 115 unsigned char c; 116 int n; 117 if (mf->c >= 0) { 118 c = mf->c; 119 mf->c = -1; 120 } else { 121 n = FLUID_FREAD(&c, 1, 1, mf->fp); 122 mf->trackpos++; 123 } 124 return (int) c; 125} 126 127/* 128 * fluid_midi_file_push 129 */ 130int fluid_midi_file_push(fluid_midi_file* mf, int c) 131{ 132 mf->c = c; 133 return FLUID_OK; 134} 135 136/* 137 * fluid_midi_file_read 138 */ 139int fluid_midi_file_read(fluid_midi_file* mf, void* buf, int len) 140{ 141 int num = FLUID_FREAD(buf, 1, len, mf->fp); 142 mf->trackpos += num; 143#if DEBUG 144 if (num != len) { 145 FLUID_LOG(FLUID_DBG, "Coulnd't read the requested number of bytes"); 146 } 147#endif 148 return (num != len)? FLUID_FAILED : FLUID_OK; 149} 150 151/* 152 * fluid_midi_file_skip 153 */ 154int fluid_midi_file_skip(fluid_midi_file* mf, int skip) 155{ 156 int err = FLUID_FSEEK(mf->fp, skip, SEEK_CUR); 157 if (err) { 158 FLUID_LOG(FLUID_ERR, "Failed to seek position in file"); 159 return FLUID_FAILED; 160 } 161 return FLUID_OK; 162} 163 164/* 165 * fluid_midi_file_read_mthd 166 */ 167int fluid_midi_file_read_mthd(fluid_midi_file* mf) 168{ 169 char mthd[15]; 170 if (fluid_midi_file_read(mf, mthd, 14) != FLUID_OK) { 171 return FLUID_FAILED; 172 } 173 if ((FLUID_STRNCMP(mthd, "MThd", 4) != 0) || (mthd[7] != 6) || (mthd[9] > 2)) { 174 FLUID_LOG(FLUID_ERR, "Doesn't look like a MIDI file: invalid MThd header"); 175 return FLUID_FAILED; 176 } 177 mf->type = mthd[9]; 178 mf->ntracks = (unsigned) mthd[11]; 179 mf->ntracks += (unsigned int) (mthd[10]) << 16; 180 if((mthd[12]) < 0){ 181 mf->uses_smpte = 1; 182 mf->smpte_fps = -mthd[12]; 183 mf->smpte_res = (unsigned) mthd[13]; 184 FLUID_LOG(FLUID_ERR, "File uses SMPTE timing -- Not implemented yet"); 185 return FLUID_FAILED; 186 } else { 187 mf->uses_smpte = 0; 188 mf->division = (mthd[12] << 8) | (mthd[13] & 0xff); 189 FLUID_LOG(FLUID_DBG, "Division=%d", mf->division); 190 } 191 return FLUID_OK; 192} 193 194/* 195 * fluid_midi_file_load_tracks 196 */ 197int fluid_midi_file_load_tracks(fluid_midi_file* mf, fluid_player_t* player) 198{ 199 int i; 200 for (i = 0; i < mf->ntracks; i++) { 201 if (fluid_midi_file_read_track(mf, player, i) != FLUID_OK) { 202 return FLUID_FAILED; 203 } 204 } 205 return FLUID_OK; 206} 207 208/* 209 * fluid_isasciistring 210 */ 211int fluid_isasciistring(char* s) 212{ 213 int i; 214 int len = (int) FLUID_STRLEN(s); 215 for (i = 0; i < len; i++) { 216 if (!fluid_isascii(s[i])) { 217 return 0; 218 } 219 } 220 return 1; 221} 222 223/* 224 * fluid_getlength 225 */ 226long fluid_getlength(unsigned char *s) 227{ 228 long i = 0; 229 i = s[3] | (s[2]<<8) | (s[1]<<16) | (s[0]<<24); 230 return i; 231} 232 233/* 234 * fluid_midi_file_read_tracklen 235 */ 236int fluid_midi_file_read_tracklen(fluid_midi_file* mf) 237{ 238 unsigned char length[5]; 239 if (fluid_midi_file_read(mf, length, 4) != FLUID_OK) { 240 return FLUID_FAILED; 241 } 242 mf->tracklen = fluid_getlength(length); 243 mf->trackpos = 0; 244 mf->eot = 0; 245 return FLUID_OK; 246} 247 248/* 249 * fluid_midi_file_eot 250 */ 251int fluid_midi_file_eot(fluid_midi_file* mf) 252{ 253#if DEBUG 254 if (mf->trackpos > mf->tracklen) { 255 printf("track overrun: %d > %d\n", mf->trackpos, mf->tracklen); 256 } 257#endif 258 return mf->eot || (mf->trackpos >= mf->tracklen); 259} 260 261/* 262 * fluid_midi_file_read_track 263 */ 264int fluid_midi_file_read_track(fluid_midi_file* mf, fluid_player_t* player, int num) 265{ 266 fluid_track_t* track; 267 unsigned char id[5], length[5]; 268 int found_track = 0; 269 int skip; 270 271 if (fluid_midi_file_read(mf, id, 4) != FLUID_OK) { 272 return FLUID_FAILED; 273 } 274 id[4]='\0'; 275 mf->dtime = 0; 276 277 while (!found_track){ 278 279 if (fluid_isasciistring((char*) id) == 0) { 280 FLUID_LOG(FLUID_ERR, "An non-ascii track header found, currupt file"); 281 return FLUID_FAILED; 282 283 } else if (strcmp((char*) id, "MTrk") == 0) { 284 285 found_track = 1; 286 287 if (fluid_midi_file_read_tracklen(mf) != FLUID_OK) { 288 return FLUID_FAILED; 289 } 290 291 track = new_fluid_track(num); 292 if (track == NULL) { 293 FLUID_LOG(FLUID_ERR, "Out of memory"); 294 return FLUID_FAILED; 295 } 296 297 while (!fluid_midi_file_eot(mf)) { 298 if (fluid_midi_file_read_event(mf, track) != FLUID_OK) { 299 return FLUID_FAILED; 300 } 301 } 302 303 fluid_player_add_track(player, track); 304 305 } else { 306 found_track = 0; 307 if (fluid_midi_file_read(mf, length, 4) != FLUID_OK) { 308 return FLUID_FAILED; 309 } 310 skip = fluid_getlength(length); 311/* fseek(mf->fp, skip, SEEK_CUR); */ 312 if (fluid_midi_file_skip(mf, skip) != FLUID_OK) { 313 return FLUID_FAILED; 314 } 315 } 316 } 317 if (feof(mf->fp)) { 318 FLUID_LOG(FLUID_ERR, "Unexpected end of file"); 319 return FLUID_FAILED; 320 } 321 return FLUID_OK; 322} 323 324/* 325 * fluid_midi_file_read_varlen 326 */ 327int fluid_midi_file_read_varlen(fluid_midi_file* mf) 328{ 329 int i; 330 int c; 331 mf->varlen = 0; 332 for (i = 0;;i++) { 333 if (i == 4) { 334 FLUID_LOG(FLUID_ERR, "Invalid variable length number"); 335 return FLUID_FAILED; 336 } 337 c = fluid_midi_file_getc(mf); 338 if (c < 0) { 339 FLUID_LOG(FLUID_ERR, "Unexpected end of file"); 340 return FLUID_FAILED; 341 } 342 if (c & 0x80){ 343 mf->varlen |= (int) (c & 0x7F); 344 mf->varlen <<= 7; 345 } else { 346 mf->varlen += c; 347 break; 348 } 349 } 350 return FLUID_OK; 351} 352 353/* 354 * fluid_midi_file_read_event 355 */ 356int fluid_midi_file_read_event(fluid_midi_file* mf, fluid_track_t* track) 357{ 358 int status; 359 int type; 360 int tempo; 361 unsigned char* metadata = NULL; 362 unsigned char* dyn_buf = NULL; 363 unsigned char static_buf[256]; 364 int nominator, denominator, clocks, notes, sf, mi; 365 fluid_midi_event_t* evt; 366 int channel = 0; 367 int param1 = 0; 368 int param2 = 0; 369 370 /* read the delta-time of the event */ 371 if (fluid_midi_file_read_varlen(mf) != FLUID_OK) { 372 return FLUID_FAILED; 373 } 374 mf->dtime += mf->varlen; 375 376 /* read the status byte */ 377 status = fluid_midi_file_getc(mf); 378 if (status < 0) { 379 FLUID_LOG(FLUID_ERR, "Unexpected end of file"); 380 return FLUID_FAILED; 381 } 382 383 /* not a valid status byte: use the running status instead */ 384 if ((status & 0x80) == 0) { 385 if ((mf->running_status & 0x80) == 0) { 386 FLUID_LOG(FLUID_ERR, "Undefined status and invalid running status"); 387 return FLUID_FAILED; 388 } 389 fluid_midi_file_push(mf, status); 390 status = mf->running_status; 391 } 392 393 /* check what message we have */ 394 if (status & 0x80) { 395 mf->running_status = status; 396 397 if ((status == MIDI_SYSEX) || (status == MIDI_EOX)) { /* system exclusif */ 398 /* 399 * Sysex messages are not handled yet 400 */ 401 /* read the length of the message */ 402 if (fluid_midi_file_read_varlen(mf) != FLUID_OK) { 403 return FLUID_FAILED; 404 } 405 406 if (mf->varlen) { 407 408 if (mf->varlen < 255) { 409 metadata = &static_buf[0]; 410 } else { 411 FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__, __LINE__, mf->varlen); 412 dyn_buf = FLUID_MALLOC(mf->varlen + 1); 413 if (dyn_buf == NULL) { 414 FLUID_LOG(FLUID_PANIC, "Out of memory"); 415 return FLUID_FAILED; 416 } 417 metadata = dyn_buf; 418 } 419 420 /* read the data of the message */ 421 if (fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) { 422 if (dyn_buf) { 423 FLUID_FREE(dyn_buf); 424 } 425 return FLUID_FAILED; 426 } 427 428 if (dyn_buf) { 429 FLUID_LOG(FLUID_DBG, "%s: %d: free metadata", __FILE__, __LINE__); 430 FLUID_FREE(dyn_buf); 431 } 432 } 433 434 return FLUID_OK; 435 436 } else if (status == MIDI_META_EVENT) { /* meta events */ 437 438 int result = FLUID_OK; 439 440 /* get the type of the meta message */ 441 type = fluid_midi_file_getc(mf); 442 if (type < 0) { 443 FLUID_LOG(FLUID_ERR, "Unexpected end of file"); 444 return FLUID_FAILED; 445 } 446 447 /* get the length of the data part */ 448 if (fluid_midi_file_read_varlen(mf) != FLUID_OK) { 449 return FLUID_FAILED; 450 } 451 452 if (mf->varlen < 255) { 453 metadata = &static_buf[0]; 454 } else { 455 FLUID_LOG(FLUID_DBG, "%s: %d: alloc metadata, len = %d", __FILE__, __LINE__, mf->varlen); 456 dyn_buf = FLUID_MALLOC(mf->varlen + 1); 457 if (dyn_buf == NULL) { 458 FLUID_LOG(FLUID_PANIC, "Out of memory"); 459 return FLUID_FAILED; 460 } 461 metadata = dyn_buf; 462 } 463 464 /* read the data */ 465 if (mf->varlen) 466 { 467 if (fluid_midi_file_read(mf, metadata, mf->varlen) != FLUID_OK) { 468 if (dyn_buf) { 469 FLUID_FREE(dyn_buf); 470 } 471 return FLUID_FAILED; 472 } 473 } 474 475 /* handle meta data */ 476 switch (type) { 477 478 case MIDI_COPYRIGHT: 479 metadata[mf->varlen] = 0; 480 break; 481 482 case MIDI_TRACK_NAME: 483 metadata[mf->varlen] = 0; 484 fluid_track_set_name(track, (char*) metadata); 485 break; 486 487 case MIDI_INST_NAME: 488 metadata[mf->varlen] = 0; 489 break; 490 491 case MIDI_LYRIC: 492 break; 493 494 case MIDI_MARKER: 495 break; 496 497 case MIDI_CUE_POINT: 498 break; /* don't care much for text events */ 499 500 case MIDI_EOT: 501 if (mf->varlen != 0) { 502 FLUID_LOG(FLUID_ERR, "Invalid length for EndOfTrack event"); 503 result = FLUID_FAILED; 504 break; 505 } 506 mf->eot = 1; 507 break; 508 509 case MIDI_SET_TEMPO: 510 if (mf->varlen != 3) { 511 FLUID_LOG(FLUID_ERR, "Invalid length for SetTempo meta event"); 512 result = FLUID_FAILED; 513 break; 514 } 515 tempo = (metadata[0] << 16) + (metadata[1] << 8) + metadata[2]; 516 evt = new_fluid_midi_event(); 517 if (evt == NULL) { 518 FLUID_LOG(FLUID_ERR, "Out of memory"); 519 result = FLUID_FAILED; 520 break; 521 } 522 evt->dtime = mf->dtime; 523 evt->type = MIDI_SET_TEMPO; 524 evt->channel = 0; 525 evt->param1 = tempo; 526 evt->param2 = 0; 527 fluid_track_add_event(track, evt); 528 mf->dtime = 0; 529 break; 530 531 case MIDI_SMPTE_OFFSET: 532 if (mf->varlen != 5) { 533 FLUID_LOG(FLUID_ERR, "Invalid length for SMPTE Offset meta event"); 534 result = FLUID_FAILED; 535 break; 536 } 537 break; /* we don't use smtp */ 538 539 case MIDI_TIME_SIGNATURE: 540 if (mf->varlen != 4) { 541 FLUID_LOG(FLUID_ERR, "Invalid length for TimeSignature meta event"); 542 result = FLUID_FAILED; 543 break; 544 } 545 nominator = metadata[0]; 546 denominator = pow(2.0, (double) metadata[1]); 547 clocks = metadata[2]; 548 notes = metadata[3]; 549 550 FLUID_LOG(FLUID_DBG, "signature=%d/%d, metronome=%d, 32nd-notes=%d", 551 nominator, denominator, clocks, notes); 552 553 break; 554 555 case MIDI_KEY_SIGNATURE: 556 if (mf->varlen != 2) { 557 FLUID_LOG(FLUID_ERR, "Invalid length for KeySignature meta event"); 558 result = FLUID_FAILED; 559 break; 560 } 561 sf = metadata[0]; 562 mi = metadata[1]; 563 break; 564 565 case MIDI_SEQUENCER_EVENT: 566 break; 567 568 default: 569 break; 570 } 571 572 if (dyn_buf) { 573 FLUID_LOG(FLUID_DBG, "%s: %d: free metadata", __FILE__, __LINE__); 574 FLUID_FREE(dyn_buf); 575 } 576 577 return result; 578 579 } else { /* channel messages */ 580 581 type = status & 0xf0; 582 channel = status & 0x0f; 583 584 /* all channel message have at least 1 byte of associated data */ 585 if ((param1 = fluid_midi_file_getc(mf)) < 0) { 586 FLUID_LOG(FLUID_ERR, "Unexpected end of file"); 587 return FLUID_FAILED; 588 } 589 590 switch (type) { 591 592 case NOTE_ON: 593 if ((param2 = fluid_midi_file_getc(mf)) < 0) { 594 FLUID_LOG(FLUID_ERR, "Unexpected end of file"); 595 return FLUID_FAILED; 596 } 597 break; 598 599 case NOTE_OFF: 600 if ((param2 = fluid_midi_file_getc(mf)) < 0) { 601 FLUID_LOG(FLUID_ERR, "Unexpected end of file"); 602 return FLUID_FAILED; 603 } 604 break; 605 606 case KEY_PRESSURE: 607 if ((param2 = fluid_midi_file_getc(mf)) < 0) { 608 FLUID_LOG(FLUID_ERR, "Unexpected end of file"); 609 return FLUID_FAILED; 610 } 611 break; 612 613 case CONTROL_CHANGE: 614 if ((param2 = fluid_midi_file_getc(mf)) < 0) { 615 FLUID_LOG(FLUID_ERR, "Unexpected end of file"); 616 return FLUID_FAILED; 617 } 618 break; 619 620 case PROGRAM_CHANGE: 621 break; 622 623 case CHANNEL_PRESSURE: 624 break; 625 626 case PITCH_BEND: 627 if ((param2 = fluid_midi_file_getc(mf)) < 0) { 628 FLUID_LOG(FLUID_ERR, "Unexpected end of file"); 629 return FLUID_FAILED; 630 } 631 632 param1 = ((param2 & 0x7f) << 7) | (param1 & 0x7f); 633 param2 = 0; 634 break; 635 636 default: 637 /* Can't possibly happen !? */ 638 FLUID_LOG(FLUID_ERR, "Unrecognized MIDI event"); 639 return FLUID_FAILED; 640 } 641 evt = new_fluid_midi_event(); 642 if (evt == NULL) { 643 FLUID_LOG(FLUID_ERR, "Out of memory"); 644 return FLUID_FAILED; 645 } 646 evt->dtime = mf->dtime; 647 evt->type = type; 648 evt->channel = channel; 649 evt->param1 = param1; 650 evt->param2 = param2; 651 fluid_track_add_event(track, evt); 652 mf->dtime = 0; 653 } 654 } 655 return FLUID_OK; 656} 657 658/* 659 * fluid_midi_file_get_division 660 */ 661int fluid_midi_file_get_division(fluid_midi_file* midifile) 662{ 663 return midifile->division; 664} 665 666/****************************************************** 667 * 668 * fluid_track_t 669 */ 670 671/** 672 * Create a MIDI event structure. 673 * @return New MIDI event structure or NULL when out of memory. 674 */ 675fluid_midi_event_t* new_fluid_midi_event() 676{ 677 fluid_midi_event_t* evt; 678 evt = FLUID_NEW(fluid_midi_event_t); 679 if (evt == NULL) { 680 FLUID_LOG(FLUID_ERR, "Out of memory"); 681 return NULL; 682 } 683 evt->dtime = 0; 684 evt->type = 0; 685 evt->channel = 0; 686 evt->param1 = 0; 687 evt->param2 = 0; 688 evt->next = NULL; 689 return evt; 690} 691 692/** 693 * Delete MIDI event structure. 694 * @param evt MIDI event structure 695 * @return Always returns 0 696 */ 697int delete_fluid_midi_event(fluid_midi_event_t* evt) 698{ 699 fluid_midi_event_t *temp; 700 701 while (evt) 702 { 703 temp = evt->next; 704 FLUID_FREE(evt); 705 evt = temp; 706 } 707 return FLUID_OK; 708} 709 710/** 711 * Get the event type field of a MIDI event structure. 712 * DOCME - Event type enum appears to be internal (fluid_midi.h) 713 * @param evt MIDI event structure 714 * @return Event type field 715 */ 716int fluid_midi_event_get_type(fluid_midi_event_t* evt) 717{ 718 return evt->type; 719} 720 721/** 722 * Set the event type field of a MIDI event structure. 723 * DOCME - Event type enum appears to be internal (fluid_midi.h) 724 * @param evt MIDI event structure 725 * @param type Event type field 726 * @return Always returns 0 727 */ 728int fluid_midi_event_set_type(fluid_midi_event_t* evt, int type) 729{ 730 evt->type = type; 731 return FLUID_OK; 732} 733 734/** 735 * Get the channel field of a MIDI event structure. 736 * @param evt MIDI event structure 737 * @return Channel field 738 */ 739int fluid_midi_event_get_channel(fluid_midi_event_t* evt) 740{ 741 return evt->channel; 742} 743 744/** 745 * Set the channel field of a MIDI event structure. 746 * @param evt MIDI event structure 747 * @param chan MIDI channel field 748 * @return Always returns 0 749 */ 750int fluid_midi_event_set_channel(fluid_midi_event_t* evt, int chan) 751{ 752 evt->channel = chan; 753 return FLUID_OK; 754} 755 756/** 757 * Get the key field of a MIDI event structure. 758 * @param evt MIDI event structure 759 * @return MIDI note number (0-127) 760 */ 761int fluid_midi_event_get_key(fluid_midi_event_t* evt) 762{ 763 return evt->param1; 764} 765 766/** 767 * Set the key field of a MIDI event structure. 768 * @param evt MIDI event structure 769 * @param v MIDI note number (0-127) 770 * @return Always returns 0 771 */ 772int fluid_midi_event_set_key(fluid_midi_event_t* evt, int v) 773{ 774 evt->param1 = v; 775 return FLUID_OK; 776} 777 778/** 779 * Get the velocity field of a MIDI event structure. 780 * @param evt MIDI event structure 781 * @return MIDI velocity number (0-127) 782 */ 783int fluid_midi_event_get_velocity(fluid_midi_event_t* evt) 784{ 785 return evt->param2; 786} 787 788/** 789 * Set the velocity field of a MIDI event structure. 790 * @param evt MIDI event structure 791 * @param v MIDI velocity value 792 * @return Always returns 0 793 */ 794int fluid_midi_event_set_velocity(fluid_midi_event_t* evt, int v) 795{ 796 evt->param2 = v; 797 return FLUID_OK; 798} 799 800/** 801 * Get the control number of a MIDI event structure. 802 * @param evt MIDI event structure 803 * @return MIDI control number 804 */ 805int fluid_midi_event_get_control(fluid_midi_event_t* evt) 806{ 807 return evt->param1; 808} 809 810/** 811 * Set the control field of a MIDI event structure. 812 * @param evt MIDI event structure 813 * @param v MIDI control number 814 * @return Always returns 0 815 */ 816int fluid_midi_event_set_control(fluid_midi_event_t* evt, int v) 817{ 818 evt->param1 = v; 819 return FLUID_OK; 820} 821 822/** 823 * Get the value field from a MIDI event structure. 824 * @param evt MIDI event structure 825 * @return Value field 826 */ 827int fluid_midi_event_get_value(fluid_midi_event_t* evt) 828{ 829 return evt->param2; 830} 831 832/** 833 * Set the value field of a MIDI event structure. 834 * @param evt MIDI event structure 835 * @param v Value to assign 836 * @return Always returns 0 837 */ 838int fluid_midi_event_set_value(fluid_midi_event_t* evt, int v) 839{ 840 evt->param2 = v; 841 return FLUID_OK; 842} 843 844/** 845 * Get the program field of a MIDI event structure. 846 * @param evt MIDI event structure 847 * @return MIDI program number (0-127) 848 */ 849int fluid_midi_event_get_program(fluid_midi_event_t* evt) 850{ 851 return evt->param1; 852} 853 854/** 855 * Set the program field of a MIDI event structure. 856 * @param evt MIDI event structure 857 * @param val MIDI program number (0-127) 858 * @return Always returns 0 859 */ 860int fluid_midi_event_set_program(fluid_midi_event_t* evt, int val) 861{ 862 evt->param1 = val; 863 return FLUID_OK; 864} 865 866/** 867 * Get the pitch field of a MIDI event structure. 868 * @param evt MIDI event structure 869 * @return Pitch value (DOCME units?) 870 */ 871int fluid_midi_event_get_pitch(fluid_midi_event_t* evt) 872{ 873 return evt->param1; 874} 875 876/** 877 * Set the pitch field of a MIDI event structure. 878 * @param evt MIDI event structure 879 * @param val Pitch value (DOCME units?) 880 * @return Always returns 0 881 */ 882int fluid_midi_event_set_pitch(fluid_midi_event_t* evt, int val) 883{ 884 evt->param1 = val; 885 return FLUID_OK; 886} 887 888/* 889 * fluid_midi_event_get_param1 890 */ 891/* int fluid_midi_event_get_param1(fluid_midi_event_t* evt) */ 892/* { */ 893/* return evt->param1; */ 894/* } */ 895 896/* 897 * fluid_midi_event_set_param1 898 */ 899/* int fluid_midi_event_set_param1(fluid_midi_event_t* evt, int v) */ 900/* { */ 901/* evt->param1 = v; */ 902/* return FLUID_OK; */ 903/* } */ 904 905/* 906 * fluid_midi_event_get_param2 907 */ 908/* int fluid_midi_event_get_param2(fluid_midi_event_t* evt) */ 909/* { */ 910/* return evt->param2; */ 911/* } */ 912 913/* 914 * fluid_midi_event_set_param2 915 */ 916/* int fluid_midi_event_set_param2(fluid_midi_event_t* evt, int v) */ 917/* { */ 918/* evt->param2 = v; */ 919/* return FLUID_OK; */ 920/* } */ 921 922/****************************************************** 923 * 924 * fluid_track_t 925 */ 926 927/* 928 * new_fluid_track 929 */ 930fluid_track_t* new_fluid_track(int num) 931{ 932 fluid_track_t* track; 933 track = FLUID_NEW(fluid_track_t); 934 if (track == NULL) { 935 return NULL; 936 } 937 track->name = NULL; 938 track->num = num; 939 track->first = NULL; 940 track->cur = NULL; 941 track->last = NULL; 942 track->ticks = 0; 943 return track; 944} 945 946/* 947 * delete_fluid_track 948 */ 949int delete_fluid_track(fluid_track_t* track) 950{ 951 if (track->name != NULL) { 952 FLUID_FREE(track->name); 953 } 954 if (track->first != NULL) { 955 delete_fluid_midi_event(track->first); 956 } 957 FLUID_FREE(track); 958 return FLUID_OK; 959} 960 961/* 962 * fluid_track_set_name 963 */ 964int fluid_track_set_name(fluid_track_t* track, char* name) 965{ 966 int len; 967 if (track->name != NULL) { 968 FLUID_FREE(track->name); 969 } 970 if (name == NULL) { 971 track->name = NULL; 972 return FLUID_OK; 973 } 974 len = FLUID_STRLEN(name); 975 track->name = FLUID_MALLOC(len + 1); 976 if (track->name == NULL) { 977 FLUID_LOG(FLUID_ERR, "Out of memory"); 978 return FLUID_FAILED; 979 } 980 FLUID_STRCPY(track->name, name); 981 return FLUID_OK; 982} 983 984/* 985 * fluid_track_get_name 986 */ 987char* fluid_track_get_name(fluid_track_t* track) 988{ 989 return track->name; 990} 991 992/* 993 * fluid_track_get_duration 994 */ 995int fluid_track_get_duration(fluid_track_t* track) 996{ 997 int time = 0; 998 fluid_midi_event_t* evt = track->first; 999 while (evt != NULL) { 1000 time += evt->dtime; 1001 evt = evt->next; 1002 } 1003 return time; 1004} 1005 1006/* 1007 * fluid_track_count_events 1008 */ 1009int fluid_track_count_events(fluid_track_t* track, int* on, int* off) 1010{ 1011 fluid_midi_event_t* evt = track->first; 1012 while (evt != NULL) { 1013 if (evt->type == NOTE_ON) { 1014 (*on)++; 1015 } else if (evt->type == NOTE_OFF) { 1016 (*off)++; 1017 } 1018 evt = evt->next; 1019 } 1020 return FLUID_OK; 1021} 1022 1023/* 1024 * fluid_track_add_event 1025 */ 1026int fluid_track_add_event(fluid_track_t* track, fluid_midi_event_t* evt) 1027{ 1028 evt->next = NULL; 1029 if (track->first == NULL) { 1030 track->first = evt; 1031 track->cur = evt; 1032 track->last = evt; 1033 } else { 1034 track->last->next = evt; 1035 track->last = evt; 1036 } 1037 return FLUID_OK; 1038} 1039 1040/* 1041 * fluid_track_first_event 1042 */ 1043fluid_midi_event_t* fluid_track_first_event(fluid_track_t* track) 1044{ 1045 track->cur = track->first; 1046 return track->cur; 1047} 1048 1049/* 1050 * fluid_track_next_event 1051 */ 1052fluid_midi_event_t* fluid_track_next_event(fluid_track_t* track) 1053{ 1054 if (track->cur != NULL) { 1055 track->cur = track->cur->next; 1056 } 1057 return track->cur; 1058} 1059 1060/* 1061 * fluid_track_reset 1062 */ 1063int 1064fluid_track_reset(fluid_track_t* track) 1065{ 1066 track->ticks = 0; 1067 track->cur = track->first; 1068 return FLUID_OK; 1069} 1070 1071/* 1072 * fluid_track_send_events 1073 */ 1074int 1075fluid_track_send_events(fluid_track_t* track, 1076 fluid_synth_t* synth, 1077 fluid_player_t* player, 1078 unsigned int ticks) 1079{ 1080 int status = FLUID_OK; 1081 fluid_midi_event_t* event; 1082 1083 while (1) { 1084 1085 event = track->cur; 1086 if (event == NULL) { 1087 return status; 1088 } 1089 1090/* printf("track=%02d\tticks=%05u\ttrack=%05u\tdtime=%05u\tnext=%05u\n", */ 1091/* track->num, */ 1092/* ticks, */ 1093/* track->ticks, */ 1094/* event->dtime, */ 1095/* track->ticks + event->dtime); */ 1096 1097 if (track->ticks + event->dtime > ticks) { 1098 return status; 1099 } 1100 1101 1102 track->ticks += event->dtime; 1103 status = fluid_midi_send_event(synth, player, event); 1104 fluid_track_next_event(track); 1105 1106 } 1107 return status; 1108} 1109 1110/****************************************************** 1111 * 1112 * fluid_player 1113 */ 1114 1115/** 1116 * Create a new MIDI player. 1117 * @param synth Fluid synthesizer instance to create player for 1118 * @return New MIDI player instance or NULL on error (out of memory) 1119 */ 1120fluid_player_t* new_fluid_player(fluid_synth_t* synth) 1121{ 1122 int i; 1123 fluid_player_t* player; 1124 player = FLUID_NEW(fluid_player_t); 1125 if (player == NULL) { 1126 FLUID_LOG(FLUID_ERR, "Out of memory"); 1127 return NULL; 1128 } 1129 player->status = FLUID_PLAYER_READY; 1130 player->loop = 0; 1131 player->ntracks = 0; 1132 for (i = 0; i < MAX_NUMBER_OF_TRACKS; i++) { 1133 player->track[i] = NULL; 1134 } 1135 player->synth = synth; 1136 player->timer = NULL; 1137 player->playlist = NULL; 1138 player->current_file = NULL; 1139 player->division = 0; 1140 player->send_program_change = 1; 1141 player->miditempo = 480000; 1142 player->deltatime = 4.0; 1143 return player; 1144} 1145 1146/** 1147 * Delete a MIDI player instance. 1148 * @param player MIDI player instance 1149 * @return Always returns 0 1150 */ 1151int delete_fluid_player(fluid_player_t* player) 1152{ 1153 if (player == NULL) { 1154 return FLUID_OK; 1155 } 1156 fluid_player_stop(player); 1157 fluid_player_reset(player); 1158 FLUID_FREE(player); 1159 return FLUID_OK; 1160} 1161 1162int fluid_player_reset(fluid_player_t* player) 1163{ 1164 int i; 1165 1166 for (i = 0; i < MAX_NUMBER_OF_TRACKS; i++) { 1167 if (player->track[i] != NULL) { 1168 delete_fluid_track(player->track[i]); 1169 player->track[i] = NULL; 1170 } 1171 } 1172 player->current_file = NULL; 1173 player->status = FLUID_PLAYER_READY; 1174 player->loop = 0; 1175 player->ntracks = 0; 1176 player->division = 0; 1177 player->send_program_change = 1; 1178 player->miditempo = 480000; 1179 player->deltatime = 4.0; 1180 return 0; 1181} 1182 1183/* 1184 * fluid_player_add_track 1185 */ 1186int fluid_player_add_track(fluid_player_t* player, fluid_track_t* track) 1187{ 1188 if (player->ntracks < MAX_NUMBER_OF_TRACKS) { 1189 player->track[player->ntracks++] = track; 1190 return FLUID_OK; 1191 } else { 1192 return FLUID_FAILED; 1193 } 1194} 1195 1196/* 1197 * fluid_player_count_tracks 1198 */ 1199int fluid_player_count_tracks(fluid_player_t* player) 1200{ 1201 return player->ntracks; 1202} 1203 1204/* 1205 * fluid_player_get_track 1206 */ 1207fluid_track_t* fluid_player_get_track(fluid_player_t* player, int i) 1208{ 1209 if ((i >= 0) && (i < MAX_NUMBER_OF_TRACKS)) { 1210 return player->track[i]; 1211 } else { 1212 return NULL; 1213 } 1214} 1215 1216int fluid_player_add(fluid_player_t* player, char* midifile) 1217{ 1218 char *s = FLUID_STRDUP(midifile); 1219 player->playlist = fluid_list_append(player->playlist, s); 1220 return 0; 1221} 1222 1223/* 1224 * fluid_player_load 1225 */ 1226int fluid_player_load(fluid_player_t* player, char *filename) 1227{ 1228 fluid_midi_file* midifile; 1229 1230 midifile = new_fluid_midi_file(filename); 1231 if (midifile == NULL) { 1232 return FLUID_FAILED; 1233 } 1234 player->division = fluid_midi_file_get_division(midifile); 1235 1236 /*FLUID_LOG(FLUID_DBG, "quarter note division=%d\n", player->division); */ 1237 1238 if (fluid_midi_file_load_tracks(midifile, player) != FLUID_OK){ 1239 return FLUID_FAILED; 1240 } 1241 delete_fluid_midi_file(midifile); 1242 return FLUID_OK; 1243} 1244 1245/* 1246 * fluid_player_callback 1247 */ 1248int fluid_player_callback(void* data, unsigned int msec) 1249{ 1250 int i; 1251 int status = FLUID_PLAYER_DONE; 1252 fluid_player_t* player; 1253 fluid_synth_t* synth; 1254 player = (fluid_player_t*) data; 1255 synth = player->synth; 1256 1257 /* Load the next file if necessary */ 1258 while (player->current_file == NULL) { 1259 1260 if (player->playlist == NULL) { 1261 return 0; 1262 } 1263 1264 fluid_player_reset(player); 1265 1266 player->current_file = fluid_list_get(player->playlist); 1267 player->playlist = fluid_list_next(player->playlist); 1268 1269 FLUID_LOG(FLUID_DBG, "%s: %d: Loading midifile %s", __FILE__, __LINE__, player->current_file); 1270 1271 if (fluid_player_load(player, player->current_file) == FLUID_OK) { 1272 1273 player->begin_msec = msec; 1274 player->start_msec = msec; 1275 player->start_ticks = 0; 1276 player->cur_ticks = 0; 1277 1278 for (i = 0; i < player->ntracks; i++) { 1279 if (player->track[i] != NULL) { 1280 fluid_track_reset(player->track[i]); 1281 } 1282 } 1283 1284 } else { 1285 player->current_file = NULL; 1286 } 1287 } 1288 1289 player->cur_msec = msec; 1290 player->cur_ticks = (player->start_ticks + 1291 (int) ((double) (player->cur_msec - player->start_msec) / player->deltatime)); 1292 1293 for (i = 0; i < player->ntracks; i++) { 1294 if (!fluid_track_eot(player->track[i])) { 1295 status = FLUID_PLAYER_PLAYING; 1296 if (fluid_track_send_events(player->track[i], synth, player, player->cur_ticks) != FLUID_OK) { 1297 /* */ 1298 } 1299 } 1300 } 1301 1302 player->status = status; 1303 1304 if (player->status == FLUID_PLAYER_DONE) { 1305 FLUID_LOG(FLUID_DBG, "%s: %d: Duration=%.3f sec", 1306 __FILE__, __LINE__, (msec - player->begin_msec) / 1000.0); 1307 player->current_file = NULL; 1308 } 1309 1310 return 1; 1311} 1312 1313/** 1314 * Activates play mode for a MIDI player if not already playing. 1315 * @param player MIDI player instance 1316 * @return 0 on success, -1 on failure 1317 */ 1318int fluid_player_play(fluid_player_t* player) 1319{ 1320 if (player->status == FLUID_PLAYER_PLAYING) { 1321 return FLUID_OK; 1322 } 1323 1324 if (player->playlist == NULL) { 1325 return FLUID_OK; 1326 } 1327 1328 player->status = FLUID_PLAYER_PLAYING; 1329 1330 player->timer = new_fluid_timer((int) player->deltatime, fluid_player_callback, 1331 (void*) player, 1, 0); 1332 if (player->timer == NULL) { 1333 return FLUID_FAILED; 1334 } 1335 return FLUID_OK; 1336} 1337 1338/** 1339 * Stops a MIDI player. 1340 * @param player MIDI player instance 1341 * @return Always returns 0 1342 */ 1343int fluid_player_stop(fluid_player_t* player) 1344{ 1345 if (player->timer != NULL) { 1346 delete_fluid_timer(player->timer); 1347 } 1348 player->status = FLUID_PLAYER_DONE; 1349 player->timer = NULL; 1350 return FLUID_OK; 1351} 1352 1353/* FIXME - Looping seems to not actually be implemented? */ 1354 1355/** 1356 * Enable looping of a MIDI player (DOCME - Does this actually work?) 1357 * @param player MIDI player instance 1358 * @param loop Value for looping (DOCME - What would this value be, boolean/time index?) 1359 * @return Always returns 0 1360 */ 1361int fluid_player_set_loop(fluid_player_t* player, int loop) 1362{ 1363 player->loop = loop; 1364 return FLUID_OK; 1365} 1366 1367/** 1368 * Set the tempo of a MIDI player. 1369 * @param player MIDI player instance 1370 * @param tempo Tempo to set playback speed to (DOCME - Units?) 1371 * @return Always returns 0 1372 * 1373 */ 1374int fluid_player_set_midi_tempo(fluid_player_t* player, int tempo) 1375{ 1376 player->miditempo = tempo; 1377 player->deltatime = (double) tempo / player->division / 1000.0; /* in milliseconds */ 1378 player->start_msec = player->cur_msec; 1379 player->start_ticks = player->cur_ticks; 1380 1381 FLUID_LOG(FLUID_DBG,"tempo=%d, tick time=%f msec, cur time=%d msec, cur tick=%d", 1382 tempo, player->deltatime, player->cur_msec, player->cur_ticks); 1383 1384 return FLUID_OK; 1385} 1386 1387/** 1388 * Set the tempo of a MIDI player in beats per minute. 1389 * @param player MIDI player instance 1390 * @param bpm Tempo in beats per minute 1391 * @return Always returns 0 1392 */ 1393int fluid_player_set_bpm(fluid_player_t* player, int bpm) 1394{ 1395 return fluid_player_set_midi_tempo(player, (int)((double) 60 * 1e6 / bpm)); 1396} 1397 1398/** 1399 * Wait for a MIDI player to terminate (when done playing). 1400 * @param player MIDI player instance 1401 * @return 0 on success, -1 otherwise 1402 * 1403 */ 1404int fluid_player_join(fluid_player_t* player) 1405{ 1406 return player->timer? fluid_timer_join(player->timer) : FLUID_OK; 1407} 1408 1409/************************************************************************ 1410 * MIDI PARSER 1411 * 1412 */ 1413 1414/* 1415 * new_fluid_midi_parser 1416 */ 1417fluid_midi_parser_t* new_fluid_midi_parser() 1418{ 1419 fluid_midi_parser_t* parser; 1420 parser = FLUID_NEW(fluid_midi_parser_t); 1421 if (parser == NULL) { 1422 FLUID_LOG(FLUID_ERR, "Out of memory"); 1423 return NULL; 1424 } 1425 parser->status = 0; /* As long as the status is 0, the parser won't do anything -> no need to initialize all the fields. */ 1426 return parser; 1427} 1428 1429/* 1430 * delete_fluid_midi_parser 1431 */ 1432int delete_fluid_midi_parser(fluid_midi_parser_t* parser) 1433{ 1434 FLUID_FREE(parser); 1435 return FLUID_OK; 1436} 1437 1438/* 1439 * fluid_midi_parser_parse 1440 * 1441 * Purpose: 1442 * The MIDI byte stream is fed into the parser, one byte at a time. 1443 * As soon as the parser has recognized an event, it will return it. 1444 * Otherwise it returns NULL. 1445 */ 1446fluid_midi_event_t* fluid_midi_parser_parse(fluid_midi_parser_t* parser, unsigned char c) 1447{ 1448 /*********************************************************************/ 1449 /* 'Process' system real-time messages */ 1450 /*********************************************************************/ 1451 /* There are not too many real-time messages that are of interest here. 1452 * They can occur anywhere, even in the middle of a noteon message! 1453 * Real-time range: 0xF8 .. 0xFF 1454 * Note: Real-time does not affect (running) status. 1455 */ 1456 if (c >= 0xF8){ 1457 if (c == MIDI_SYSTEM_RESET){ 1458 parser->event.type = c; 1459 parser->status = 0; /* clear the status */ 1460 return &parser->event; 1461 }; 1462 return NULL; 1463 }; 1464 1465 /*********************************************************************/ 1466 /* 'Process' system common messages (again, just skip them) */ 1467 /*********************************************************************/ 1468 /* There are no system common messages that are of interest here. 1469 * System common range: 0xF0 .. 0xF7 1470 */ 1471 1472 if (c > 0xF0){ 1473 /* MIDI specs say: To ignore a non-real-time message, just discard all data up to 1474 * the next status byte. 1475 * And our parser will ignore data that is received without a valid status. 1476 * Note: system common cancels running status. */ 1477 parser->status = 0; 1478 return NULL; 1479 }; 1480 1481 /*********************************************************************/ 1482 /* Process voice category messages: */ 1483 /*********************************************************************/ 1484 /* Now that we have handled realtime and system common messages, only 1485 * voice messages are left. 1486 * Only a status byte has bit # 7 set. 1487 * So no matter the status of the parser (in case we have lost sync), 1488 * as soon as a byte >= 0x80 comes in, we are dealing with a status byte 1489 * and start a new event. 1490 */ 1491 1492 if (c & 0x80){ 1493 parser->channel = c & 0x0F; 1494 parser->status = c & 0xF0; 1495 /* The event consumes x bytes of data... (subtract 1 for the status byte) */ 1496 parser->nr_bytes_total=fluid_midi_event_length(parser->status)-1; 1497 /* of which we have read 0 at this time. */ 1498 parser->nr_bytes = 0; 1499 return NULL; 1500 }; 1501 1502 /*********************************************************************/ 1503 /* Process data */ 1504 /*********************************************************************/ 1505 /* If we made it this far, then the received char belongs to the data 1506 * of the last event. */ 1507 if (parser->status == 0){ 1508 /* We are not interested in the event currently received. 1509 * Discard the data. */ 1510 return NULL; 1511 }; 1512 1513 /* Store the first couple of bytes */ 1514 if (parser->nr_bytes < FLUID_MIDI_PARSER_MAX_PAR){ 1515 parser->p[parser->nr_bytes]=c; 1516 }; 1517 parser->nr_bytes++; 1518 1519 /* Do we still need more data to get this event complete? */ 1520 if (parser->nr_bytes < parser->nr_bytes_total){ 1521 return NULL; 1522 }; 1523 1524 /*********************************************************************/ 1525 /* Send the event */ 1526 /*********************************************************************/ 1527 /* The event is ready-to-go. 1528 * About 'running status': 1529 * The MIDI protocol has a built-in compression mechanism. If several similar events are sent 1530 * in-a-row, for example note-ons, then the event type is only sent once. For this case, 1531 * the last event type (status) is remembered. 1532 * We simply keep the status as it is, just reset 1533 * the parameter counter. If another status byte comes in, it will overwrite the status. */ 1534 parser->event.type = parser->status; 1535 parser->event.channel = parser->channel; 1536 parser->nr_bytes = 0; /* Related to running status! */ 1537 switch (parser->status){ 1538 case NOTE_OFF: 1539 case NOTE_ON: 1540 case KEY_PRESSURE: 1541 case CONTROL_CHANGE: 1542 case PROGRAM_CHANGE: 1543 case CHANNEL_PRESSURE: 1544 parser->event.param1 = parser->p[0]; /* For example key number */ 1545 parser->event.param2 = parser->p[1]; /* For example velocity */ 1546 break; 1547 case PITCH_BEND: 1548 /* Pitch-bend is transmitted with 14-bit precision. */ 1549 parser->event.param1 = ((parser->p[1] << 7) | parser->p[0]); /* Note: '|' does here the same as '+' (no common bits), but might be faster */ 1550 break; 1551 default: 1552 /* Unlikely */ 1553 return NULL; 1554 }; 1555 return &parser->event; 1556}; 1557 1558/* Purpose: 1559 * Returns the length of the MIDI message starting with c. 1560 * Taken from Nagano Daisuke's USB-MIDI driver */ 1561int fluid_midi_event_length(unsigned char event){ 1562 if ( event < 0xf0 ) { 1563 return remains_80e0[((event-0x80)>>4)&0x0f]; 1564 } else if ( event < 0xf7 ) { 1565 return remains_f0f6[event-0xf0]; 1566 } else { 1567 return 1; 1568 } 1569} 1570 1571/************************************************************************ 1572 * fluid_midi_send_event 1573 * 1574 * This is a utility function that doesn't really belong to any class 1575 * or structure. It is called by fluid_midi_track and fluid_midi_device. 1576 */ 1577int fluid_midi_send_event(fluid_synth_t* synth, fluid_player_t* player, fluid_midi_event_t* event) 1578{ 1579 switch (event->type) { 1580 case NOTE_ON: 1581 if (fluid_synth_noteon(synth, event->channel, event->param1, event->param2) != FLUID_OK) { 1582 return FLUID_FAILED; 1583 } 1584 break; 1585 case NOTE_OFF: 1586 if (fluid_synth_noteoff(synth, event->channel, event->param1) != FLUID_OK) { 1587 return FLUID_FAILED; 1588 } 1589 break; 1590 case CONTROL_CHANGE: 1591 if (fluid_synth_cc(synth, event->channel, event->param1, event->param2) != FLUID_OK) { 1592 return FLUID_FAILED; 1593 } 1594 break; 1595 case MIDI_SET_TEMPO: 1596 if (player != NULL) { 1597 if (fluid_player_set_midi_tempo(player, event->param1) != FLUID_OK) { 1598 return FLUID_FAILED; 1599 } 1600 } 1601 break; 1602 case PROGRAM_CHANGE: 1603 if (fluid_synth_program_change(synth, event->channel, event->param1) != FLUID_OK) { 1604 return FLUID_FAILED; 1605 } 1606 break; 1607 case PITCH_BEND: 1608 if (fluid_synth_pitch_bend(synth, event->channel, event->param1) != FLUID_OK) { 1609 return FLUID_FAILED; 1610 } 1611 break; 1612 default: 1613 break; 1614 } 1615 return FLUID_OK; 1616} 1617