1 /*
2  * @(#)Instrument.h 3.00 3 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 #include "tse3/ins/Instrument.h"
18 
19 #include "tse3/Progress.h"
20 
21 #include <fstream>
22 #include <iostream>
23 #include <sstream>
24 #include <algorithm>
25 
26 using namespace TSE3;
27 using namespace TSE3::Ins;
28 
29 namespace
30 {
31     /**
32      * The .ins file comment divider. We use this when we write .ins files.
33      *
34      * This is the standard divider from one of Cakewalk's files.
35      */
36     const char *ins_divider = "; ----------------------------------"
37                               "------------------------------------\n";
38 
39     /**
40      * .ins files come from Windows/MS-DOS. They have ASCII 13 at the ends of
41      * the lines. We need to strip them out before we handle the line. That's
42      * what this we functon does.
43      *
44      * We also need to strip comments from lines. So we do that here too.
45      */
clean_string(std::string & str)46     void clean_string(std::string &str)
47     {
48         if (str[str.length()-1] == 13) // for empty lines?
49         {
50             str = str.substr(0, str.length()-1);
51         }
52         if (str.find(';') != str.npos)
53         {
54             str = str.substr(0,str.find(';'));
55         }
56     }
57 }
58 
59 
60 /******************************************************************************
61  * Voice class
62  *****************************************************************************/
63 
Voice(int bank,int patch)64 Voice::Voice(int bank, int patch)
65 {
66     first  = bank;
67     second = patch;
68 }
69 
70 
Voice(int bankMSB,int bankLSB,int patch)71 Voice::Voice(int bankMSB, int bankLSB, int patch)
72 {
73     first  = (bankMSB << 7) + bankLSB;
74     second = patch;
75 }
76 
77 
operator <(const Voice & v) const78 int Voice::operator <(const Voice &v) const
79 {
80     if (first != v.first)
81     {
82         return first < v.first;
83     }
84     else
85     {
86         return second < v.second;
87     }
88 }
89 
90 
91 /******************************************************************************
92  * Instrument class
93  *****************************************************************************/
94 
Instrument(const std::string & title,const std::string & filename,TSE3::Progress * p)95 Instrument::Instrument(const std::string &title, const std::string &filename,
96                        TSE3::Progress *p)
97 : _title(title), _filename(filename),
98   _bankSelMethod(0), _useNotesAsControllers(false),
99   _control(0), _rpn(0), _nrpn(0)
100 {
101     std::ifstream in(filename.c_str());
102     if (in.good())
103     {
104         load(in, p);
105     }
106 }
107 
108 
setTitle(const std::string & n)109 void Instrument::setTitle(const std::string &n)
110 {
111     _title = n;
112 }
113 
114 
setBankSelMethod(int b)115 void Instrument::setBankSelMethod(int b)
116 {
117     if (b <= BankSelMethod_Normal || b >= BankSelMethod_Patch)
118         return;
119     _bankSelMethod = b;
120 }
121 
122 
setUseNotesAsControllers(bool u)123 void Instrument::setUseNotesAsControllers(bool u)
124 {
125     _useNotesAsControllers = u;
126 }
127 
128 
bankLSB(int index) const129 int Instrument::bankLSB(int index) const
130 {
131     return banks[index] == -1 ? -1 : banks[index] & 0xff;
132 }
133 
134 
bankMSB(int index) const135 int Instrument::bankMSB(int index) const
136 {
137     return banks[index] == -1 ? -1 : banks[index] >> 7;
138 }
139 
140 
bank(const Voice &) const141 int Instrument::bank(const Voice &/*voice*/) const
142 {
143     return -2;
144     // XXX
145 #warning This is not implemented
146 }
147 
148 
patchForBank(int bank) const149 PatchData *Instrument::patchForBank(int bank) const
150 {
151     std::vector<int>::const_iterator i
152         = std::find(banks.begin(), banks.end(), bank);
153 
154     if (i == banks.end() && bank != -1)
155     {
156         // Ok, so that failed. Perhaps there's a catch-all patch set?
157         i = std::find(banks.begin(), banks.end(), -1);
158     }
159 
160     if (i != banks.end())
161     {
162         return patches[i-banks.begin()];
163     }
164     else
165     {
166         return 0;
167     }
168 }
169 
170 
patchForBank(int bankLSB,int bankMSB) const171 PatchData *Instrument::patchForBank(int bankLSB, int bankMSB) const
172 {
173     return patchForBank(bankFromBytes(bankLSB, bankMSB));
174 }
175 
176 
keyForVoice(const Voice & voice) const177 NoteData *Instrument::keyForVoice(const Voice &voice) const
178 {
179     std::vector<std::pair<Voice, NoteData*> >::const_iterator i = keys.begin();
180     while (i != keys.end() && i->first != voice) i++;
181 
182     if (i != keys.end())
183     {
184         return i->second;
185     }
186     else
187     {
188         return 0;
189     }
190 }
191 
192 
isDrum(const Voice & voice) const193 bool Instrument::isDrum(const Voice &voice) const
194 {
195     return
196         std::find(drumFlags.begin(), drumFlags.end(), voice) != drumFlags.end();
197 }
198 
199 
200 /******************************************************************************
201  * Instrument class saving/loading
202  *****************************************************************************/
203 
load(std::istream & in,TSE3::Progress * progress)204 void Instrument::load(std::istream &in, TSE3::Progress *progress)
205 {
206     if (progress)
207     {
208         progress->progressRange(0, 100);
209         progress->progress(0);
210     }
211 
212     // 1. Find the ".Instrument Definitions" line (0% - 10%)
213 
214     in.seekg(0, std::ios::beg);
215     std::string line;
216     while (!in.eof() && line != ".Instrument Definitions")
217     {
218         std::getline(in, line);
219         clean_string(line);
220     }
221     if (line != ".Instrument Definitions") return;
222     if (progress)
223     {
224         progress->progress(10);
225     }
226 
227     // 2. Find the instrument definition section (10% - 20%)
228 
229     std::string matchstr = std::string("[") + _title + std::string("]");
230     while (!in.eof() && line != matchstr)
231     {
232         std::getline(in, line);
233         clean_string(line);
234     }
235     if (progress)
236     {
237         progress->progress(20);
238     }
239 
240     // 3. Parse each of the bits in it (20% - 100%)
241 
242     std::streampos defnFilePos = in.tellg();
243     std::streampos defnEndPos  = defnFilePos;
244     if (progress)
245     {
246         // Find how big this section is for %age
247         while (!in.eof() && line.size() != 0)
248         {
249             std::getline(in, line);
250             clean_string(line);
251             if (line[0] == '[') line ="";
252         }
253         defnEndPos = in.tellg();
254         in.seekg(defnFilePos, std::ios::beg);
255     }
256 
257     line = " ";
258     while (!in.eof() && line.size() != 0)
259     {
260         if (progress)
261         {
262             progress->progress(20
263                                + ((in.tellg()-defnFilePos) * 80
264                                   / (defnEndPos - defnFilePos)));
265         }
266         std::getline(in, line);
267         clean_string(line);
268         if (line[0] != '[')
269         {
270             parseLine(line, in);
271         }
272         else
273         {
274             line = "";
275         }
276     }
277     if (progress)
278     {
279         progress->progress(100);
280     }
281 }
282 
283 
parseLine(const std::string & line,std::istream & in)284 void Instrument::parseLine(const std::string &line, std::istream &in)
285 {
286     if (line == "UseNotesAsControllers=1")
287     {
288         _useNotesAsControllers = true;
289     }
290     else if (line.substr(0,8) == "Control=")
291     {
292         std::string title(line.substr(8));
293         delete _control;
294         _control = new ControlData(title, in);
295     }
296     else if (line.substr(0,4) == "RPN=")
297     {
298         std::string title(line.substr(4));
299         delete _rpn;
300         _rpn = new RpnData(title, in);
301     }
302     else if (line.substr(0,5) == "NRPN=")
303     {
304         std::string title(line.substr(5));
305         delete _nrpn;
306         _nrpn = new NrpnData(title, in);
307     }
308     else if (line.substr(0,14) == "BankSelMethod=")
309     {
310         std::istringstream si(line.c_str()+14);
311         si >> _bankSelMethod;
312     }
313     else if (line.substr(0,6) == "Patch[")
314     {
315         std::string bank_s = line.substr(6, line.find(']')-6);
316         int bank = -1;
317         if (bank_s != "*")
318         {
319             std::istringstream si(line.c_str()+6);
320             si >> bank;
321         }
322         std::string title(line.substr(line.find('=')+1));
323         banks.push_back(bank);
324         patches.push_back(new PatchData(title, in));
325     }
326     else if (line.substr(0,4) == "Key[")
327     {
328         std::string bank_s(line.substr(4,line.find(',')-4));
329         int a = line.find(',')+1;
330         std::string patch_s(line.substr(a, line.find(']')-a));
331         int bank = -1, patch = -1;
332         if (bank_s != "*")
333         {
334             std::istringstream si(bank_s);
335             si >> bank;
336         }
337         if (patch_s != "*")
338         {
339             std::istringstream si(patch_s);
340             si >> patch;
341         }
342         std::string title(line.substr(line.find('=')+1));
343 
344         // This has been split onto a separate line to keep gcc happy
345         std::pair<Voice, NoteData *> pr
346             (Voice(bank,patch), new NoteData(title, in));
347         keys.push_back(pr);
348     }
349     else if (line.substr(0,5) == "Drum[")
350     {
351         std::string bank_s(line.substr(5,line.find(',')-5));
352         int a = line.find(',')+1;
353         std::string patch_s(line.substr(a, line.find(']')-a));
354         int bank = -1, patch = -1;
355         if (bank_s != "*")
356         {
357             std::istringstream si(bank_s);
358             si >> bank;
359         }
360         if (patch_s != "*")
361         {
362             std::istringstream si(patch_s);
363             si >> patch;
364         }
365         std::string title(line.substr(line.find('=')+1));
366         drumFlags.push_back(Voice(bank,patch));
367     }
368 }
369 
370 
write(std::ostream & out)371 void Instrument::write(std::ostream &out)
372 {
373     out << "\n"
374         << ins_divider
375         << "; Instrument definition file save by TSE3 library\n"
376         << "; Defines the " << _title << " instrument only\n"
377         << "; Pete Goodliffe\n\n";
378 
379     out << ins_divider << "\n.Patch Names\n\n";
380     {
381         std::vector<PatchData*>::iterator ip = patches.begin();
382         while (ip != patches.end())
383         {
384             (*ip)->write(out);
385             ++ip;
386         }
387     }
388 
389     out << ins_divider << "\n.Note Names\n\n";
390     {
391         std::vector<std::pair<Voice,NoteData*> >::iterator i = keys.begin();
392         while (i != keys.end())
393         {
394             (*i).second->write(out);
395             ++i;
396         }
397     }
398 
399     out << ins_divider << "\n.Controller Names\n\n";
400     if (_control) _control->write(out);
401 
402     out << ins_divider << "\n.RPN Names\n\n";
403     // hmm?
404 
405     out << ins_divider << "\n.NRPN Names\n\n";
406     if (_nrpn) _nrpn->write(out);
407 
408     out << ins_divider << "\n.Instrument Definitions\n\n";
409     out << "[" << _title << "]\n";
410     if (_useNotesAsControllers) out << "UseNotesAsControllers=1\n";
411     if (_control)               out << "Control=" << _control->title() << "\n";
412     if (_nrpn)                  out << "NRPN=" << _nrpn->title() << "\n";
413     if (_bankSelMethod)         out << "BankSelMethod=" << _bankSelMethod
414                                     << "\n";
415     {
416         std::vector<PatchData*>::iterator ip = patches.begin();
417         std::vector<int>::iterator        ib = banks.begin();
418         while (ip != patches.end())
419         {
420             out << "Patch[";
421             if (*ib == -1)
422                 out << "*";
423             else
424                 out << *ib;
425             out << "]=" << (*ip)->title() << "\n";
426             ++ip;
427             ++ib;
428         }
429     }
430     {
431         std::vector<std::pair<Voice,NoteData*> >::iterator i = keys.begin();
432         while (i != keys.end())
433         {
434             out << "Key[";
435             if ((*i).first.bank() == -1)
436                 out << "*";
437             else
438                 out << (*i).first.bank();
439             out << ",";
440             if ((*i).first.patch() == -1)
441                 out << "*";
442             else
443                 out << (*i).first.patch();
444             out << "]=" << (*i).second->title() << "\n";
445             ++i;
446         }
447     }
448     {
449         std::vector<Voice>::iterator i = drumFlags.begin();
450         while (i != drumFlags.end())
451         {
452             out << "Drum[";
453             if ((*i).bank() == -1)
454                 out << "*";
455             else
456                 out << (*i).bank();
457             out << ",";
458             if ((*i).patch() == -1)
459                 out << "*";
460             else
461                 out << (*i).patch();
462             out << "]=1\n";
463             ++i;
464         }
465     }
466     out << "\n";
467 }
468 
469 
470 /******************************************************************************
471  * InstrumentData class
472  *****************************************************************************/
473 
474 std::string InstrumentData::empty;
475 
InstrumentData(std::string const & title,std::string const & insHeading,std::istream & in)476 InstrumentData::InstrumentData(std::string const &title,
477                                std::string const &insHeading,
478                                std::istream &in)
479 : insHeading(insHeading), _title(title)
480 {
481     for (int n = 0; n < 128; ++n) _names[n] = 0;
482     load(_title, in);
483 }
484 
485 
load(const std::string & secname,std::istream & in)486 void InstrumentData::load(const std::string &secname, std::istream &in)
487 {
488     //out << "Loading ["<<secname<<"] from "<<insHeading<<"\n";
489     std::streampos fpos = in.tellg();
490     in.seekg(0, std::ios::beg);
491     std::string line;
492     bool         success = false;
493     while (!in.eof() && line != insHeading)
494     {
495         std::getline(in, line);
496         clean_string(line);
497     }
498     if (line == insHeading)
499     {
500         line = "";
501         std::string matchstr = "[" + secname + "]";
502         while (!in.eof()
503                && line != matchstr
504                && (line.size() == 0 || line[0] != '.'))
505         {
506             std::getline(in, line);
507             clean_string(line);
508         }
509 
510         if (line == matchstr)
511         {
512             line    = "";
513             success = true;
514             while (!in.eof()
515                    && (line.size() == 0 || (line[0] != '.' && line[0] != '[')))
516             {
517                 std::getline(in, line);
518                 clean_string(line);
519                 if (line.substr(0, 7) == "BasedOn") load(line.substr(8), in);
520                 if (line.find('=') != line.npos)
521                 {
522                     int no = 0;
523                     {
524                         std::istringstream si(line);
525                         si >> no;
526                     }
527                     std::string title = line.substr(line.find('=')+1);
528                     delete _names[no];
529                     _names[no] = new std::string(title);
530                 }
531             }
532         }
533     }
534     in.seekg(fpos, std::ios::beg);
535     if (!success)
536     {
537         std::cerr << "TSE3: Failed to load data [" << secname.c_str()
538                   << "] from instrument file section "<< insHeading << "\n";
539     }
540 }
541 
542 
write(std::ostream & out) const543 void InstrumentData::write(std::ostream &out) const
544 {
545     out << "[" << _title << "]\n";
546     for (int n = 0; n < 128; ++n)
547         if (_names[n]) out << n << "=" << *(_names[n])   << "\n";
548     out << "\n";
549 }
550 
551 
552 /******************************************************************************
553  * CakewalkInstrumentFile class
554  *****************************************************************************/
555 
CakewalkInstrumentFile(const std::string & filename)556 CakewalkInstrumentFile::CakewalkInstrumentFile(const std::string &filename)
557 : filename(filename), searched_yet(false)
558 {
559 }
560 
561 
instruments(TSE3::Progress * progress)562 const std::list<std::string> &CakewalkInstrumentFile::instruments
563     (TSE3::Progress *progress)
564 {
565     if (!searched_yet)
566     {
567         size_t progressCount = 0;
568         searched_yet = true;
569         std::ifstream in(filename.c_str());
570         if (!in.good())
571         {
572             return ins;
573         }
574         if (progress)
575         {
576             in.seekg(0, std::ios::end);
577             progress->progressRange(0, in.tellg());
578             in.seekg(0, std::ios::beg);
579         }
580         std::string line;
581         while (!in.eof() && line != ".Instrument Definitions")
582         {
583             std::getline(in, line);
584             clean_string(line);
585             if (progress && !(progressCount%20))
586             {
587                 progress->progress(in.tellg());
588             }
589             progressCount++;
590         }
591         if (line != ".Instrument Definitions") return ins;
592         while (!in.eof())
593         {
594             std::getline(in, line);
595             clean_string(line);
596             if (line.size() && line[0] == '[')
597             {
598                 ins.push_back(line.substr(1,line.size()-2));
599             }
600             if (progress && !(progressCount%20))
601             {
602                 progress->progress(in.tellg());
603             }
604             progressCount++;
605         }
606     }
607     return ins;
608 }
609 
610 
instrument(const std::string & title,TSE3::Progress * p)611 Instrument *CakewalkInstrumentFile::instrument(const std::string &title,
612                                                TSE3::Progress *p)
613 {
614     return new Instrument(title, filename, p);
615 }
616