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