1 /* 2 * @(#)ins/Instrument.h 3.00 23 August 1999 3 * 4 * Copyright (c) 2000 Pete Goodliffe (pete@cthree.org) 5 * 6 * This file is part of TSE3 - the Trax Sequencer Engine version 3.00. 7 * 8 * This library is modifiable/redistributable under the terms of the GNU 9 * General Public License. 10 * 11 * You should have received a copy of the GNU General Public License along 12 * with this program; see the file COPYING. If not, write to the Free Software 13 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14 * 15 */ 16 17 #ifndef TSE3_INS_INSTRUMENT_H 18 #define TSE3_INS_INSTRUMENT_H 19 20 #include <list> 21 #include <utility> 22 #include <vector> 23 #include <string> 24 #include <iosfwd> 25 26 namespace TSE3 27 { 28 class Progress; 29 30 /** 31 * The @p Ins namespace contains classes that implement the Cakewalk 32 * instrument file parsing routines and provide name lookup for bank/patch 33 * and controller numbers. 34 * 35 * The facilities offered here are utilities that an application may use, 36 * and are not part of the core @ref TSE3 functionality. 37 * 38 * The @ref TSE3::Ins::CakewalkInstrumentFile class is the main entrance 39 * onto @p Ins facilities. 40 * 41 * You can get Cakewalk instrument definition files for practically 42 * every piece of MIDI hardware in existence, which is why they have been 43 * adopted by the TSE3 library. They are most easily obtained from the 44 * @p www.cakewalk.com website; follow the "Download" link and select 45 * "Instrument Definitions". 46 * 47 * @short Utility classes for MIDI instrument definitions 48 * @author Pete Goodliffe 49 * @version 3.00 50 * @see TSE3 51 */ 52 namespace Ins 53 { 54 class PatchData; 55 class ControlData; 56 class RpnData; 57 class NrpnData; 58 class NoteData; 59 60 /** 61 * Bank select values can be expressed as single 14 bit numbers 62 * (Cakewalk instrument files use this format) or as separate LSB and 63 * MSBs (the MIDI format uses this format). 64 * 65 * This function converts a 14 bit bank number into the LSB portion. 66 * If @p bank is -1, returns -1. 67 * 68 * @param bank 14 bit bank select number 69 * @return LSB value 70 * @see bankMSB 71 * @see bankFromBytes 72 */ bankToLSB(int bank)73 inline int bankToLSB(int bank) 74 { 75 return (bank < 0) ? bank : bank & 0x7f; 76 } 77 78 /** 79 * Bank select values can be expressed as single 14 bit numbers 80 * (Cakewalk instrument files use this format) or as separate LSB and 81 * MSBs (the MIDI format uses this format). 82 * 83 * This function converts a 14 bit bank number into the MSB portion. 84 * If @p bank is -1, returns -1. 85 * 86 * @param bank 14 bit bank select number 87 * @return MSB value 88 * @see bankLSB 89 * @see bankFromBytes 90 */ bankToMSB(int bank)91 inline int bankToMSB(int bank) 92 { 93 return (bank < 0) ? bank : bank >> 7; 94 } 95 96 /** 97 * Bank select values can be expressed as single 14 bit numbers 98 * (Cakewalk instrument files use this format) or as separate LSB and 99 * MSBs (the MIDI format uses this format). 100 * 101 * This function converts a bank LSB and MSB into a 14 bit bank number. 102 * If @p bank is -1, returns -1. 103 * 104 * @param bankLSB Bank LSB value 105 * @param bankMSB Bank MSB value 106 * @return bank 14 bit bank select number 107 * @see bankLSB 108 * @see bankMSB 109 */ bankFromBytes(int bankLSB,int bankMSB)110 inline int bankFromBytes(int bankLSB, int bankMSB) 111 { 112 return (bankLSB < 0 || bankMSB < 0) ? -1 : (bankMSB<<7) | bankLSB; 113 } 114 115 /** 116 * A Voice struct holds information about a voice - the bank and patch 117 * values. It is based on pair<int,int> where the first in is the 118 * bank value and the second int is the patch value. 119 * 120 * Bank values are defined to be (MSB<<7)+LSB. 121 * 122 * The value -1 denotes a wildcard - it matches any bank/patch. 123 * 124 * This is a value type. 125 * 126 * @short Instrument voice definition 127 * @author Pete Goodliffe 128 * @version 3.00 129 */ 130 struct Voice : public std::pair<int, int> 131 { 132 /** 133 * Creates a Voice with the given bank and patch values. 134 * 135 * @param bank New bank value in the form (MSB<<7)+LSB 136 * @param patch New patch value 137 */ 138 Voice(int bank, int patch); 139 140 /** 141 * Creates a Voice with the given bank and patch values. 142 * 143 * @param bankMSB Bank select MSB 144 * @param bankLSB Bank select LSB 145 * @param patch New patch value 146 */ 147 Voice(int bankMSB, int bankLSB, int patch); 148 149 /** 150 * Returns the bank value in the form (bankMSB<<7)+bankLSB. 151 */ bankVoice152 int bank() const { return first; } 153 154 /** 155 * Returns the bank MSB value. 156 */ bankMSBVoice157 int bankMSB() const { return first >> 7; } 158 159 /** 160 * Returns the bank LSB value. 161 */ bankLSBVoice162 int bankLSB() const { return first & 0x7f; } 163 164 /** 165 * Returns the patch value. 166 */ patchVoice167 int patch() const { return second; } 168 169 /** 170 * Comparison operator. Compares banks first, then patches. 171 */ 172 int operator <(const Voice &v) const; 173 }; 174 175 /** 176 * The Instrument class holds information about a specific MIDI 177 * instrument. This includes the voices it provides, control commands 178 * it understands, drum note names and so on. 179 * 180 * The Instrument class is based on the instrument definitions supplied 181 * in Cakewalk .ins instrument definition files. 182 * 183 * @short MIDI Instrument definition 184 * @author Pete Goodliffe 185 * @version 3.00 186 */ 187 class Instrument 188 { 189 public: 190 191 /** 192 * Creates an instrument with the given name from information 193 * contained in the given file. This file will be a 194 * Cakewalk .ins file. 195 * 196 * Whilst the file is being loaded, the progess can be reported 197 * via the @ref TSE3::Progess interface. 198 * 199 * @param title The title of this instrument 200 * @param file The file to take input from 201 * @param progess @ref TSE3::Progress callback, or zero for 202 * no callback 203 */ 204 Instrument(const std::string &title, 205 const std::string &filename, 206 TSE3::Progress *progess = 0); 207 208 /** 209 * Returns the title of this instrument. 210 * 211 * @return Title of this instrument 212 */ title()213 const std::string &title() const { return _title; } 214 215 /** 216 * Returns the filename of the source of the instrument 217 * definition. 218 * 219 * @return Filename of this instrument's definition 220 */ filename()221 const std::string &filename() const { return _filename; } 222 223 /** 224 * Sets the title. 225 * 226 * @param title New instrument title 227 */ 228 void setTitle(const std::string &title); 229 230 /** 231 * Returns the BankSelMethod, the values for this are defined 232 * as the BankSelMethod_XXX constants. 233 * 234 * @return Bank select method 235 */ bankSelMethod()236 int bankSelMethod() const { return _bankSelMethod; } 237 238 /** 239 * Sets the BankSelMethod. 240 * 241 * @param b New bank select method 242 */ 243 void setBankSelMethod(int b); 244 245 /** 246 * An enum type defining the Instrument's bank select method. 247 * It has the following values 248 * @li @p BankSelMethod_Normal 249 * For normal instruments: uses LSB, MSB and patch. 250 * @li @p BankSelMethod_MSB 251 * For instruments that only use MSB and patch. 252 * @li @p BankSelMethod_LSB 253 * For instruments that only use LSB and patch. 254 * @li @p BankSelMethod_Patch 255 * For instruments that only use patch. 256 */ 257 enum BankSelMethod 258 { 259 BankSelMethod_Normal = 0, 260 BankSelMethod_MSB = 1, 261 BankSelMethod_LSB = 2, 262 BankSelMethod_Patch = 3 263 }; 264 265 /** 266 * Returns the UseNotesAsControllers value. 267 * 268 * @return Whenther to use notes as controllers 269 */ useNotesAsController()270 bool useNotesAsController() const 271 { 272 return _useNotesAsControllers; 273 } 274 275 /** 276 * Sets the UseNotesAsControllers value. 277 * 278 * @param u New use notes as controllers value 279 */ 280 void setUseNotesAsControllers(bool u); 281 282 /** 283 * Returns the number of banks of patch data defined 284 * by this instrument. 285 * 286 * @return Number of banks of patches 287 */ numBanks()288 size_t numBanks() const { return banks.size(); } 289 290 /** 291 * Returns the bank number in the form: 292 * <pre> 293 * bankLSB + (bankMSB<<7) 294 * </pre> 295 * for the bank with index @p index. 296 * 297 * If you call this method with an invalid parameter, the 298 * result is undefined. 299 * 300 * @return Bank change values for bank with given index 301 */ bank(int index)302 int bank(int index) const { return banks[index]; } 303 304 /** 305 * Returns the bank number in the form: 306 * <pre> 307 * bankLSB + (bankMSB<<7) 308 * </pre> 309 * for the bank for @ref Voice @p voice. If there is no such 310 * @ref Voice defined, then -2 will be returned. 311 * 312 * @return Bank change values for bank with given index 313 */ 314 int bank(const Voice &voice) const; 315 316 /** 317 * Returns the bank LSB for the set of patches with index 318 * @p index. 319 * 320 * If you call this method with an invalid parameter, the 321 * result is undefined. 322 * 323 * @return Bank LSB value for bank with given index 324 * @see bankMSB 325 */ 326 int bankLSB(int index) const; 327 328 /** 329 * Returns the bank MSB for the set of patches with index 330 * @p index. 331 * 332 * If you call this method with an invalid parameter, the 333 * result is undefined. 334 * 335 * @return Bank MSB value for bank with given index 336 * @see bankLSB 337 */ 338 int bankMSB(int index) const; 339 340 /** 341 * Returns the @ref PatchData object for the given bank. 342 * 343 * @param index Bank index 344 * @return Pointer to @ref PatchData for bank, or 0 345 */ patch(int index)346 PatchData *patch(int index) const { return patches[index]; } 347 348 /** 349 * Returns the @ref PatchData object for the given bank, or 0 350 * if there is none. 351 * 352 * Note that this function takes the bank change number (as 353 * read from @ref bank()), not the bank index. 354 * 355 * If there is no PatchData for this bank, then zero is 356 * returned. 357 * 358 * You can specify @p bank as -1 to find the 'catch all' bank, 359 * and if your bank number is undefined, but there is a 360 * ctach all patch set, that will be returned. 361 * 362 * @param bank Bank number 363 * @return Pointer to @ref PatchData for bank, or 0 364 */ 365 PatchData *patchForBank(int bank) const; 366 367 /** 368 * Like the @ref patchForBank(int) above, but takes the 369 * LSB and MSB parameters separately. This function actually 370 * fowards responsibility onto the other version. It is 371 * provided as a convenience. 372 * 373 * If either of the LSB or MSB parameters are -1, then the 374 * overal bank value passed on is -1. 375 * 376 * @param bankLSB Bank number LSB 377 * @param bankMSB Bank number MSB 378 * @return Pointer to @ref PatchData for bank, or 0 379 */ 380 PatchData *patchForBank(int bankLSB, int bankMSB) const; 381 382 /** 383 * Returns the number of sets of @ref NoteData defined 384 * by this instrument. 385 * 386 * @return Number of patches @ref NoteData objects 387 */ numKeys()388 size_t numKeys() const { return keys.size(); } 389 390 /** 391 * Returns the @ref NoteData with the given @p index. 392 * 393 * If you call this method with an invalid parameter, the 394 * result is undefined. 395 * 396 * @return @ref NoteData for index 397 */ key(size_t index)398 NoteData *key(size_t index) const { return keys[index].second; } 399 400 /** 401 * Returns the @ref NoteData for the given @ref Voice. 402 * 403 * If there is no such @ref Voice, then zero is returned. 404 * 405 * @return @ref NoteData for index 406 */ 407 NoteData *keyForVoice(const Voice &voice) const; 408 409 /** 410 * Returns the number of drum statuses defined by this 411 * instrument. 412 * 413 * @return Number of drum statuses 414 */ numDrums()415 size_t numDrums() const { return drumFlags.size(); } 416 417 /** 418 * Returns the drum @ref Voice with the given @p index. 419 * 420 * If you call this method with an invalid parameter, the 421 * result is undefined. 422 * 423 * @return @ref Voice for index 424 */ drum(size_t index)425 Voice drum(size_t index) const { return drumFlags[index]; } 426 427 /** 428 * Returns whether the specified @p voice is defined to be 429 * a drum sound or not (this implies that note data should 430 * be opened in a "drum" editor). 431 * 432 * @return Whether voice is a drum sound 433 */ 434 bool isDrum(const Voice &voice) const; 435 436 /** 437 * Returns the @ref ControlData for this Instrument, if there 438 * is any defined, or zero if there is none. 439 * 440 * @return ControlData for this instrument 441 */ control()442 ControlData *control() const { return _control; } 443 444 /** 445 * Returns the @ref RpnData for this Instrument, if there 446 * is any defined, or zero if there is none. 447 * 448 * @return RpnData for this instrument 449 */ rpn()450 RpnData *rpn() const { return _rpn; } 451 452 /** 453 * Returns the @ref NrpnData for this Instrument, if there 454 * is any defined, or zero if there is none. 455 * 456 * @return NrpnData for this instrument 457 */ nrpn()458 NrpnData *nrpn() const { return _nrpn; } 459 460 /** 461 * Write the minimal .ins file for this instrument. 462 * 463 * @param out ostream to write output to 464 */ 465 void write(std::ostream &out); 466 467 private: 468 469 /** 470 * Loads the instrument from the given .ins file. 471 * Pre: title has been set up already. Other values have 472 * defaults. 473 */ 474 void load(std::istream &in, TSE3::Progress *progress); 475 476 /** 477 * Parses a line of the instrument definition. 478 * The istream is not used, but may be passed onto child 479 * objects. 480 */ 481 void parseLine(const std::string &line, std::istream &in); 482 483 std::string _title; 484 std::string _filename; 485 int _bankSelMethod; 486 bool _useNotesAsControllers; 487 488 std::vector<PatchData *> patches; 489 std::vector<int> banks; 490 491 std::vector<std::pair<Voice, NoteData *> > keys; 492 std::vector<Voice> drumFlags; 493 494 ControlData *_control; 495 RpnData *_rpn; 496 NrpnData *_nrpn; 497 }; 498 499 /** 500 * A base class for instrument data: many .ins file sections are based 501 * on simple lists of 0..127 values. This is a base class for such 502 * lists. 503 * 504 * @short Instrument data container class 505 * @author Pete Goodliffe 506 * @version 3.00 507 */ 508 class InstrumentData 509 { 510 public: 511 512 /** 513 * Returns the title of this data group. 514 * 515 * @return Title of the data group 516 */ title()517 const std::string &title() const { return _title; } 518 519 /** 520 * Returns the name of the item with the given @p index. 521 * the index value must be between 0 and 127, or the results 522 * are undefined. 523 * 524 * If no name has been defined for this element, returns 525 * an empty string. 526 * 527 * @return Name of the data element 528 */ name(size_t index)529 const std::string &name(size_t index) const 530 { 531 std::string *s = _names[index]; 532 return s ? *s : empty; 533 } 534 535 /** 536 * Write the .ins file subsection. 537 * 538 * @param out ostream to write output to 539 */ 540 void write(std::ostream &out) const; 541 542 protected: 543 544 /** 545 * Contructor is private since this is a base class. 546 */ 547 InstrumentData(std::string const &title, 548 std::string const &insHeading, 549 std::istream &in); 550 551 /** 552 * The .ins file heading for this section type. Derived classes 553 * <b>MUST</b> override this for load) to work. 554 * 555 * @see load 556 */ 557 const std::string insHeading; 558 559 /** 560 * Loads the subsection 'secname' from the .ins section 561 * 'insHeading'. 562 * Pre: title and insHeading have been set up. 563 */ 564 void load(const std::string &secname, std::istream &in); 565 566 std::string _title; 567 std::string *_names[128]; 568 569 static std::string empty; 570 }; 571 572 /** 573 * This class represents a group of related patches - they will have 574 * the same bank select values. 575 * 576 * @short Instrument patch data group 577 * @author Pete Goodliffe 578 * @version 3.00 579 */ 580 class PatchData : public InstrumentData 581 { 582 public: PatchData(std::string const & title,std::istream & in)583 PatchData(std::string const &title, std::istream &in) 584 : InstrumentData(title, ".Patch Names", in) {} 585 }; 586 587 /** 588 * This class represents a group of note names. 589 * 590 * @short Instrument note data group 591 * @author Pete Goodliffe 592 * @version 3.00 593 */ 594 class NoteData : public InstrumentData 595 { 596 public: NoteData(std::string const & title,std::istream & in)597 NoteData(std::string const &title, std::istream &in) 598 : InstrumentData(title, ".Note Names", in) {} 599 }; 600 601 /** 602 * This class represents a group of MIDI control change defintions. 603 * 604 * @short Instrument MIDI controller data group 605 * @author Pete Goodliffe 606 * @version 3.00 607 */ 608 class ControlData : public InstrumentData 609 { 610 public: ControlData(std::string const & title,std::istream & in)611 ControlData(std::string const &title, std::istream &in) 612 : InstrumentData(title, ".Controller Names", in) {} 613 }; 614 615 /** 616 * This class represents a group of NRPN defintions. 617 * 618 * @short Instrument NRPN data group 619 * @author Pete Goodliffe 620 * @version 3.00 621 */ 622 class NrpnData : public InstrumentData 623 { 624 public: NrpnData(std::string const & title,std::istream & in)625 NrpnData(std::string const &title, std::istream &in) 626 : InstrumentData(title, ".NRPN Names", in) {} 627 }; 628 629 /** 630 * This class represents a group of RPN defintions. 631 * 632 * @short Instrument RPN data group 633 * @author Pete Goodliffe 634 * @version 3.00 635 */ 636 class RpnData : public InstrumentData 637 { 638 public: RpnData(std::string const & title,std::istream & in)639 RpnData(std::string const &title, std::istream &in) 640 : InstrumentData(title, ".RPN Names", in) {} 641 }; 642 643 /** 644 * The class represents a Cakewalk .ins file. 645 * 646 * It provides a mechanism for listing all instruments provided by a 647 * particular .ins file, and for creating an Instrument object from it. 648 * 649 * The .ins file format is not documented. However, the documentation 650 * that ships with the TSE3 library contains a description of this 651 * format. 652 * 653 * @short Cakewalk .ins file parser 654 * @author Pete Goodliffe 655 * @version 3.00 656 * @see Instrument 657 */ 658 class CakewalkInstrumentFile 659 { 660 public: 661 662 /** 663 * Create an object for the given file. 664 * 665 * @param filename The name of the Cakewalk .ins file 666 */ 667 CakewalkInstrumentFile(const std::string &filename); 668 669 /** 670 * Returns a set of the instrument titles in the .ins file. 671 * 672 * The first time you call this method, the file will be 673 * searched. Whilst this is being done you can be informted 674 * of progress via the @ref TSE3::Progress interface. 675 * 676 * @param progress @ref TSE3::Progess callback, or zero for no 677 * callback 678 * @return List of instrument titles in the .ins file 679 */ 680 const std::list<std::string> 681 &instruments(TSE3::Progress *progress = 0); 682 683 /** 684 * Factory method that creates an Instrument object for the 685 * given instrument title from this CakewalkInstrumentFile. 686 * 687 * You can supply a @ref TSE3::Progess interface to be informed 688 * of progress. 689 * 690 * @param title The title of the instrument to create 691 * @param progress @ref TSE3::Progess callback, or zero for no 692 * callback 693 * @return New instrument object - you must delete this object 694 */ 695 Instrument *instrument(const std::string &title, 696 TSE3::Progress *progress = 0); 697 698 private: 699 700 std::string filename; 701 bool searched_yet; 702 std::list<std::string> ins; 703 }; 704 } 705 } 706 707 #endif 708