1 /*
2  * @(#)tse3play.cpp 3.00 30 July 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 _GNU_SOURCE
18 #define _GNU_SOURCE // Euch! I need this to get usleep to be included
19 #endif
20 
21 #include "tse3play/tse3play.h"
22 
23 #include "tse3/plt/Factory.h"
24 #include "tse3/util/MidiScheduler.h"
25 #include "tse3/TSE3MDL.h"
26 #include "tse3/TSE2MDL.h"
27 #include "tse3/MidiFile.h"
28 #include "tse3/Transport.h"
29 #include "tse3/Song.h"
30 #include "tse3/Track.h"
31 #include "tse3/TSE3.h"
32 #include "tse3/Error.h"
33 #include "tse3/Metronome.h"
34 #include <fstream>
35 #include <cstdlib>
36 
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40 
41 #ifdef TSE3_WITH_OSS
42 #include "tse3/plt/OSS.h"
43 #endif
44 
45 #ifdef TSE3_WITH_ALSA
46 #include "tse3/plt/Alsa.h"
47 #endif
48 
49 #ifdef TSE3_WITH_ARTS
50 #include "tse3/plt/Arts.h"
51 #endif
52 
53 #include <unistd.h> // for usleep
54 //#include <time.h> // for nanosleep
55 
56 using namespace TSE3;
57 using namespace TSE3_Utilities_Play;
58 
59 using std::cout;
60 using std::cerr;
61 
62 namespace
63 {
64     /**
65      * Used in VT100 terminal escape sequences, for example
66      * move cursor, change colour, etc.
67      */
68     const char escChar = char(0x1b);
69 
70     const int USE_INTERNAL_PORT    = -100;
71     const int USE_EXTERNAL_PORT    = -101;
72     const int USE_BEST_EFFORT_PORT = -101;
73 }
74 
75 /*****************************************************************************
76  * TSE3Play class: initialization
77  ****************************************************************************/
78 
79 const char *TSE3Play::_name = "tse3play";
80 
TSE3Play(int argc,char * argv[])81 TSE3Play::TSE3Play(int argc, char *argv[])
82 : verbose(0), listports(false), doplay(true), dovisual(true), doloop(false),
83   outmidiformat(1), outmidicompact(false),
84   dometronome(false),
85   midi(false), gm(false), gs(false), xg(false),
86   startClock(-1), soloTrack(-1), usleepPeriod(100),
87   dostop(true), doecho(false),
88   schedtype(Unix), patchesDir(""), port(USE_BEST_EFFORT_PORT), fastMidi(false)
89 {
90     // The list of switches understood by this program
91 
92     switches.push_back(Switch("help", "h", 0, "provide help on tse3play",
93                               &TSE3Play::handle_help));
94     switches.push_back(Switch("version", "ver", 0, "give version no",
95                               &TSE3Play::handle_version));
96     switches.push_back(Switch("verbose", "v", 0,
97                               "give verbose progress information",
98                                &TSE3Play::handle_verbose));
99     switches.push_back(Switch("list-ports", "list", 0,
100                               "list available port numbers",
101                               &TSE3Play::handle_listports));
102     switches.push_back(Switch("novisual", "nv", 0,
103                               "don't produce visual playback feedback",
104                               &TSE3Play::handle_novisual));
105     switches.push_back(Switch("noplay", "np", 0, "don't play input file",
106                               &TSE3Play::handle_noplay));
107     switches.push_back(Switch("loop", "l", 0, "set loop mode",
108                               &TSE3Play::handle_loop));
109     switches.push_back(Switch("out-midi", "omidi", 1,
110                               "convert file to MIDI\n(filename follows switch)",
111                               &TSE3Play::handle_outmidi));
112     switches.push_back(Switch("out-midi-format-0", "omidi0", 1,
113                               "specify MIDI file format as 0\n(default is 1)",
114                               &TSE3Play::handle_outmidiformat0));
115     switches.push_back(Switch("out-midi-compact", "omidicomp", 1,
116                               "specify MIDI file compaction option",
117                               &TSE3Play::handle_outmidicompact));
118     switches.push_back(Switch("out-tse3mdl", "otse3mdl", 1,
119                               "convert file to tse3mdl\n"
120                               "(filename follows switch)",
121                               &TSE3Play::handle_outtse3mdl));
122     switches.push_back(Switch("map-channel", "map", 2,
123                               "<f> <t>: map channel f to t",
124                               &TSE3Play::handle_mapchannel));
125     switches.push_back(Switch("metronome", "m", 0,
126                               "produce a metronome tick",
127                               &TSE3Play::handle_metronome));
128     switches.push_back(Switch("reset", "r", 0,
129                               "send a MIDI reset at playback start and stop",
130                               &TSE3Play::handle_reset_midi));
131     switches.push_back(Switch("gmreset", "gmr", 0,
132                               "send a GM reset at playback start and stop",
133                               &TSE3Play::handle_reset_gm));
134     switches.push_back(Switch("gsreset", "gsr", 0,
135                               "send a GS reset at playback start and stop",
136                               &TSE3Play::handle_reset_gs));
137     switches.push_back(Switch("xgreset", "xgr", 0,
138                               "send a XG reset at playback start and stop",
139                               &TSE3Play::handle_reset_xg));
140     switches.push_back(Switch("unix-scheduler", "unix", 0,
141                               "selects the best Unix scheduler device for MIDI "
142                               "output (default)",
143                               &TSE3Play::handle_unix));
144     switches.push_back(Switch("oss-scheduler", "oss", 0,
145                               "selects the OSS for MIDI output",
146                               &TSE3Play::handle_oss));
147     switches.push_back(Switch("alsa-scheduler", "alsa", 0,
148                               "selects the ALSA library for MIDI output",
149                               &TSE3Play::handle_alsa));
150     switches.push_back(Switch("arts-scheduler", "arts", 0,
151                               "selects aRts for MIDI output",
152                               &TSE3Play::handle_arts));
153     switches.push_back(Switch("stream-scheduler", "stream", 0,
154                               "selects standard output stream for MIDI output",
155                               &TSE3Play::handle_stream));
156     switches.push_back(Switch("start", "s", 1,
157                               "change start clock to specified pulse no",
158                               &TSE3Play::handle_start));
159     switches.push_back(Switch("solo-track", "solo", 1,
160                               "play with solo on given input (not MIDI) track",
161                               &TSE3Play::handle_solo));
162     switches.push_back(Switch("sleep", "sl", 1,
163                               "sets OS usecs sleep time between updates\n"
164                               "(default 100)",
165                               &TSE3Play::handle_sleep));
166     switches.push_back(Switch("no-stop", "stop", 0,
167                               "prevents tse3play from stopping at the end of "
168                               "the song",
169                               &TSE3Play::handle_nostop));
170     switches.push_back(Switch("echo", "e", 0,
171                               "enables the MIDI echo facility (soft MIDI Thru)",
172                               &TSE3Play::handle_echo));
173     switches.push_back(Switch("mute-track", "mute", 1,
174                               "mutes the specified track (first is 0)",
175                               &TSE3Play::handle_mute));
176     switches.push_back(Switch("patches-dir", "pd", 1,
177                               "specifies the directory for patch files "
178                               "(OSS only)",
179                               &TSE3Play::handle_patchesdir));
180     switches.push_back(Switch("internal", "int", 0,
181                               "force all output to first internal port",
182                               &TSE3Play::handle_internal));
183     switches.push_back(Switch("external", "ext", 0,
184                               "force all output to first external port",
185                               &TSE3Play::handle_internal));
186     switches.push_back(Switch("force-port", "port", 1,
187                               "force all output to be on specified port",
188                               &TSE3Play::handle_port));
189     switches.push_back(Switch("fast-midi", "fast", 0,
190                               "use a faster MIDI import routine",
191                               &TSE3Play::handle_fast));
192 
193     // Parse the command line
194 
195     if (argc <= 1)
196     {
197         handle_version(0, argv);
198         handle_help(0, argv);
199         exit(0);
200     }
201 
202     for (int n = 1; n < argc; n++)
203     {
204         bool done = false;
205         for(std::vector<Switch>::iterator sw = switches.begin();
206             !done && sw != switches.end();
207             sw++)
208         {
209             if (argv[n] == std::string("-") + sw->srt
210                 || argv[n] == std::string("--") + sw->lng)
211             {
212                 done = true;
213                 if (n + sw->nargs >= argc)
214                 {
215                     cerr << "Error in command format ("
216                          << argv[n] << " expects "
217                          << sw->nargs
218                          << " arguments)\n";
219                     exit(1);
220                 }
221                 (this->*(sw->handler))(n, argv);
222                 n += sw->nargs;
223             }
224         }
225         if (!done)
226         {
227             filename = argv[n];
228             if (verbose) cout << "Input filename is: " << filename << "\n";
229         }
230     }
231     if (filename == "")
232     {
233         cerr << "No filename specified.\n";
234         dovisual = false;
235         doplay   = false;
236     }
237 }
238 
239 
~TSE3Play()240 TSE3Play::~TSE3Play()
241 {
242 }
243 
244 
245 /*****************************************************************************
246  * TSE3Play class: The main program code
247  ****************************************************************************/
248 
go()249 int TSE3Play::go()
250 {
251     if (verbose)
252     {
253         cout << "\n*** tse3play (verbose mode) ***\n\n";
254         handle_version(0, 0);
255         cout << "\n";
256     }
257 
258     Playable      *playable  = 0;
259     Song          *song      = 0;
260     MidiScheduler *sch       = 0;
261     Metronome     *metronome = 0;
262     Transport     *transport = 0;
263 
264     if (doplay || midi || gm || gs || xg)
265     {
266         if (patchesDir != std::string())
267         {
268 #ifdef TSE3_WITH_OSS
269             Plt::OSSMidiScheduler::setFmPatchesDirectory(patchesDir);
270             Plt::OSSMidiScheduler::setGusPatchesDirectory(patchesDir);
271 #else
272             cout << "Patch dir specificed, but no OSS scheduler support\n";
273 #endif
274         }
275 
276         if (schedtype == Stream)
277         {
278             sch = new Util::StreamMidiScheduler();
279         }
280         else
281         {
282             using namespace Plt::UnixMidiSchedulerFactory;
283             switch (schedtype)
284             {
285                 case Arts:
286                 {
287 #ifdef TSE3_WITH_ARTS
288                     setPreferredPlatform(UnixPlatform_Arts);
289                     break;
290 #endif
291                 }
292                 case Alsa:
293                 {
294 #ifdef TSE3_WITH_ALSA
295                     setPreferredPlatform(UnixPlatform_Alsa);
296                     break;
297 #endif
298                 }
299                 case OSS:
300                 {
301 #ifdef TSE3_WITH_OSS
302                     setPreferredPlatform(UnixPlatform_OSS);
303                     break;
304 #endif
305                 }
306                 case Unix:   // Whatever comes
307                 case Stream: // Invalid case, but quietens warnings
308                 {
309                     break;
310                 }
311             }
312             MidiSchedulerFactory msf;
313             sch = msf.createScheduler();
314         }
315 
316         if (listports || verbose)
317         {
318             cout << "MidiScheduler details follow\n"
319                  << "\n  Implementation name: " << sch->implementationName()
320                  << "\n            Num ports: " << sch->numPorts();
321             std::vector<int> portNums;
322             sch->portNumbers(portNums);
323             for (size_t port = 0; port < sch->numPorts(); port++)
324             {
325                 cout << "\n  ------- Port number: " << portNums[port]
326                      << "\n                 Type: " << sch->portType(portNums[port])
327                      << "\n                 Name: " << sch->portName(portNums[port])
328                      << "\n          Is readable: ";
329                 if (sch->portReadable(portNums[port]))
330                     cout << "Yes";
331                 else
332                     cout << "No";
333                 cout << "\n         Is writeable: ";
334                 if (sch->portWriteable(portNums[port]))
335                     cout << "Yes";
336                 else
337                     cout << "No";
338                 cout << "\n         Destination:  ";
339                 if (sch->portInternal(portNums[port]))
340                     cout << "Internal";
341                 else
342                     cout << "External";
343             }
344             cout << "\n\n";
345         }
346 
347         if (port == USE_INTERNAL_PORT)
348         {
349             port = sch->defaultInternalPort();
350             if (verbose && port == MidiCommand::NoPort)
351             {
352                 cout << "No default internal port, no port forced\n";
353             }
354         }
355         else if (port == USE_EXTERNAL_PORT)
356         {
357             port = sch->defaultInternalPort();
358             if (verbose && port == MidiCommand::NoPort)
359             {
360                 cout << "No default external port, no port forced\n";
361             }
362         }
363         else if (port == USE_BEST_EFFORT_PORT)
364         {
365             port = sch->defaultInternalPort();
366             if (port == MidiCommand::NoPort)
367             {
368                 if (verbose) cout << "No default internal port\n";
369                 port = sch->defaultExternalPort();
370                 if (port == MidiCommand::NoPort && verbose)
371                     cout << "No default internal port\n";
372             }
373         }
374 
375         attachTo(sch);
376 
377         // Create the scheduler and other playback objects
378         metronome = new Metronome();
379         metronome->setStatus(Transport::Playing, dometronome);
380         transport = new Transport(metronome, sch);
381         setPanic(transport->startPanic());
382         setPanic(transport->endPanic());
383         transport->midiEcho()->filter()->setStatus(doecho);
384     }
385 
386     // Draw the on screen presence
387 
388     bool dovisual_nodeferedframe
389         = dovisual && !verbose && outmidi != "-" && outtse3mdl != "-";
390 
391     TSE3PlayVisual visual(transport, sch);
392     if (dovisual_nodeferedframe) visual.drawFrame();
393 
394     // Loading the file
395 
396     if (filename != "")
397     {
398         if (dovisual_nodeferedframe) visual.message("Loading file");
399         FileRecogniser fr(filename);
400         if (verbose) cout << "File type is: ";
401         switch (fr.type())
402         {
403             default:
404             case FileRecogniser::Type_Error:
405             {
406                 if (dovisual_nodeferedframe)
407                     visual.message("File couldn't be opened. Exiting.");
408                 if (verbose) cout << "load error - exiting\n";
409                 cerr << "Load error for \"" << filename.c_str() << "\"\n";
410                 return 1;
411             }
412             case FileRecogniser::Type_Unknown:
413             {
414                 if (dovisual_nodeferedframe)
415                     visual.message("Filetype unknown. Exiting.");
416                 if (verbose) cout << "unknown - exiting\n";
417                 cerr << "Unknown filetype for \"" << filename.c_str() << "\"\n";
418                 return 1;
419             }
420             case FileRecogniser::Type_TSE3MDL:
421             {
422                 if (dovisual_nodeferedframe)
423                     visual.message("Loading TSE3MDL file");
424                 if (verbose) cout << "TSE3MDL\n";
425                 TSE3MDL tse3mdl(_name);
426                 song     = tse3mdl.load(filename);
427                 playable = song;
428                 break;
429             }
430             case FileRecogniser::Type_TSE2MDL:
431             {
432                 if (dovisual_nodeferedframe)
433                     visual.message("Loading TSEMDL file");
434                 if (verbose) cout << "TSEMDL (legacy TSE version 2)\n";
435                 TSE2MDL fo(_name, verbose);
436                 song     = fo.load(filename);
437                 playable = song;
438                 break;
439             }
440             case FileRecogniser::Type_Midi:
441             {
442                 if (dovisual_nodeferedframe)
443                     visual.message("Loading MIDI file");
444                 if (verbose) cout << "MIDI\n";
445                 MidiFileImport *mfi = new MidiFileImport(filename, verbose);
446                 if (fastMidi && outmidi == std::string()
447                     && outtse3mdl == std::string())
448                 {
449                     playable = mfi;
450                 }
451                 else
452                 {
453                     try
454                     {
455                         song = mfi->load();
456                     }
457                     catch (const MidiFileImportError &mf)
458                     {
459                         cout << "Midi File Import was unsuccessful: "
460                              << *mf << "\n";
461                         return 1;
462                     }
463                     playable = song;
464                 }
465                 break;
466             }
467         }
468         if (verbose) cout << "File loaded.\n";
469     }
470     else
471     {
472         song     = new Song();
473         playable = song;
474     }
475 
476     if (dovisual)
477     {
478         // Don't need to do the calculation if it's not needed
479         visual.setLastClock(playable->lastClock());
480     }
481 
482     // Handle conversion facilities
483 
484     if (outmidi != std::string() && song)
485     {
486         if (dovisual_nodeferedframe)
487             visual.message("Performing MIDI output");
488         MidiFileExport mfe(outmidiformat, outmidicompact, verbose);
489         if (outmidi == "-")
490         {
491             mfe.save(std::cout, song);
492         }
493         else
494         {
495             mfe.save(outmidi, song);
496         }
497     }
498     if (outtse3mdl != std::string() && song)
499     {
500         if (dovisual_nodeferedframe)
501             visual.message("Performing TSE3MDL output");
502         TSE3MDL tse3mdl(_name);
503         if (outtse3mdl == "-")
504         {
505             tse3mdl.save(std::cout, song);
506         }
507         else
508         {
509             tse3mdl.save(outtse3mdl, song);
510         }
511     }
512 
513     // Now do playback
514 
515     if (doplay)
516     {
517         // Perform modification of Song according to command line parameters
518 
519         if (song)
520         {
521             song->setSoloTrack(soloTrack);
522             std::list<size_t>::iterator i = muteList.begin();
523             while (i != muteList.end())
524             {
525                 if (*i <= song->size())
526                     (*song)[*i]->filter()->setStatus(false);
527                 i++;
528             }
529         }
530         transport->filter()->setPort(port);
531 
532         // Handle playback facilities
533 
534         if (dovisual && !dovisual_nodeferedframe) visual.drawFrame();
535         if (dovisual)                             visual.blankFrame();
536 
537         if (verbose && !dovisual) cout << "Playing...\n";
538 
539         more_to_come = true;
540 
541         if (startClock == -1)
542         {
543             // Start from time of first event in the Playable.
544             // This may prevent a lengthy empty intro.
545             PlayableIterator *pt = playable->iterator(0);
546             startClock = (**pt).time;
547             delete pt;
548         }
549 
550         // Play loop
551         do
552         {
553             // Call the transport to do some playing
554             transport->play(playable, startClock);
555             while (more_to_come || !dostop)
556             {
557                 transport->poll();
558                 if (dovisual) visual.poll();
559                 // If we want to scan for keyboard input (in a non-platform
560                 // specific way) then do it here: if 'q' or 'Q' is pressed
561                 // then set more_to_come = false;
562                 if (usleepPeriod)
563                 {
564                     usleep(usleepPeriod);
565                     //timespec a = {0, usleepPeriod*10}, b;
566                     //nanosleep(&a, &b);
567                 }
568             }
569 
570 
571         } while (more_to_come || (doloop || !dostop));
572 
573 
574         if (dovisual) visual.blankFrame();
575         if (dovisual) visual.message("Finished playing");
576         if (verbose && !dovisual) cout << "\n\nFinished playing.\n";
577     }
578     else
579     {
580         // If a reset chosen, send it
581         if (midi || gm || gs || xg)
582         {
583             transport->play(0, 0);
584         }
585     }
586 
587     // Reset terminal
588     cout << escChar << "[0m\n";
589 
590     delete playable;
591     delete transport;
592     delete metronome;
593     delete sch;
594 
595     return 0;
596 }
597 
598 
599 /*****************************************************************************
600  * TSE3Play class: Handling command line switches
601  ****************************************************************************/
602 
handle_help(int,char * [])603 void TSE3Play::handle_help(int, char*[])
604 {
605     cout << "Usage: " << _name << " [OPTION]... [FILE]\n"
606          << "Plays and converts TSE3MDL and MIDI files.\n\n"
607          << "OPTIONs are:\n\n";
608 
609     // Work out column widths for the nicely formatted output
610     unsigned int srtsize = 0;
611     unsigned int lngsize = 0;
612     for(std::vector<Switch>::iterator n = switches.begin();
613         n != switches.end(); n++)
614     {
615         if (n->srt.size() > srtsize) srtsize = n->srt.size();
616         if (n->lng.size() > lngsize) lngsize = n->lng.size();
617     }
618     srtsize += 2;
619     lngsize += 2;
620 
621     // Produce the nicely formatted output
622     for(std::vector<Switch>::iterator n = switches.begin();
623         n != switches.end(); n++)
624     {
625         cout << "  -"  << n->srt
626              << std::string(srtsize-n->srt.size(), ' ')
627              << std::string(" --") + n->lng + " "
628              << std::string(lngsize-n->lng.size(), ' ')
629              << n->help
630              << "\n";
631     }
632 
633     cout << "\nSend bug reports to <pete@cthree.org>\n";
634     exit(0);
635 }
636 
637 
handle_version(int,char * [])638 void TSE3Play::handle_version(int, char*[])
639 {
640     cout << _name << " version " << _version/100
641          << "." << _version%100 << " built on " << __DATE__ << "\n"
642          << "TSE3 library version:   " << TSE3::TSE3_Version() << "\n"
643          << "TSE3 library copyright: " << TSE3::TSE3_Copyright() << "\n";
644 }
645 
646 
handle_verbose(int,char * [])647 void TSE3Play::handle_verbose(int, char*[])
648 {
649     verbose++;
650 }
651 
652 
handle_listports(int,char * [])653 void TSE3Play::handle_listports(int, char*[])
654 {
655     listports = true;
656 }
657 
658 
handle_novisual(int,char * [])659 void TSE3Play::handle_novisual(int, char*[])
660 {
661     dovisual = false;
662     if (verbose) cout << "Producing no visual playback feedback.\n";
663 }
664 
665 
handle_noplay(int,char * [])666 void TSE3Play::handle_noplay(int, char*[])
667 {
668     doplay = false;
669     if (verbose) cout << "Will not play input file.\n";
670 }
671 
672 
handle_loop(int,char * [])673 void TSE3Play::handle_loop(int, char*[])
674 {
675     doloop = false;
676     if (verbose) cout << "Looping playback set.\n";
677 }
678 
679 
handle_outmidi(int argpos,char * argv[])680 void TSE3Play::handle_outmidi(int argpos, char *argv[])
681 {
682     outmidi = argv[argpos+1];
683     if (verbose) cout << "Producing MIDI output in: " << outmidi << "\n";
684 }
685 
686 
handle_outmidiformat0(int,char * [])687 void TSE3Play::handle_outmidiformat0(int, char *[])
688 {
689     outmidiformat = 0;
690     if (verbose)
691        cout << "Setting output MIDI file format to: " << outmidiformat << "\n";
692 }
693 
694 
handle_outmidicompact(int,char * [])695 void TSE3Play::handle_outmidicompact(int, char*[])
696 {
697     outmidicompact = true;
698     if (verbose) cout << "Setting MIDI file compact mode\n";
699 }
700 
701 
handle_outtse3mdl(int argpos,char * argv[])702 void TSE3Play::handle_outtse3mdl(int argpos, char *argv[])
703 {
704     outtse3mdl = argv[argpos+1];
705     if (verbose) cout << "Producing tse3mdl output in: " << outtse3mdl << "\n";
706 }
707 
708 
handle_mapchannel(int argpos,char * argv[])709 void TSE3Play::handle_mapchannel(int argpos, char *argv[])
710 {
711     int from = atoi(argv[argpos+1]);
712     int to   = atoi(argv[argpos+2]);
713     if (verbose) cout << "Channel " << from << " to " << to << "\n";
714 }
715 
716 
handle_metronome(int,char * [])717 void TSE3Play::handle_metronome(int, char*[])
718 {
719     dometronome = true;
720     if (verbose) cout << "Enabling metronome\n";
721 }
722 
723 
handle_reset_midi(int,char * [])724 void TSE3Play::handle_reset_midi(int, char*[])
725 {
726     midi = true;
727     if (verbose) cout << "Enabling MIDI reset\n";
728 }
729 
730 
handle_reset_gm(int,char * [])731 void TSE3Play::handle_reset_gm(int, char*[])
732 {
733     gm = true;
734     if (verbose) cout << "Enabling GM reset\n";
735 }
736 
737 
handle_reset_gs(int,char * [])738 void TSE3Play::handle_reset_gs(int, char*[])
739 {
740     gs = true;
741     if (verbose) cout << "Enabling GS reset\n";
742 }
743 
744 
handle_reset_xg(int,char * [])745 void TSE3Play::handle_reset_xg(int, char*[])
746 {
747     xg = true;
748     if (verbose) cout << "Enabling XG reset\n";
749 }
750 
751 
handle_unix(int,char * [])752 void TSE3Play::handle_unix(int, char*[])
753 {
754     schedtype = Unix;
755     if (verbose) cout << "Selecting best Unix device for MIDI output\n";
756 }
757 
758 
handle_oss(int,char * [])759 void TSE3Play::handle_oss(int, char*[])
760 {
761 #ifdef TSE3_WITH_OSS
762     schedtype = OSS;
763     if (verbose) cout << "Selecting OSS for MIDI output\n";
764 #else
765     std::cerr << "OSS is not supported on this computer\n";
766 #endif
767 }
768 
769 
handle_alsa(int,char * [])770 void TSE3Play::handle_alsa(int, char*[])
771 {
772 #ifdef TSE3_WITH_ALSA
773     schedtype = Alsa;
774     if (verbose) cout << "Selecting ALSA for MIDI output\n";
775 #else
776     std::cerr << "ALSA is not supported on this computer\n";
777 #endif
778 }
779 
780 
handle_arts(int,char * [])781 void TSE3Play::handle_arts(int, char*[])
782 {
783 #ifdef TSE3_WITH_ARTS
784     schedtype = Arts;
785     if (verbose) cout << "Selecting aRts for MIDI output\n";
786 #else
787     std::cerr << "aRts is not supported on this computer\n";
788 #endif
789 }
790 
791 
handle_stream(int,char * [])792 void TSE3Play::handle_stream(int, char*[])
793 {
794     schedtype = Stream;
795     dovisual  = false;
796     if (verbose) cout << "Selecting standard output for MIDI output\n";
797 }
798 
799 
handle_start(int argpos,char * argv[])800 void TSE3Play::handle_start(int argpos, char *argv[])
801 {
802     startClock = atoi(argv[argpos+1]);
803     if (verbose) cout << "Start " << startClock << "\n";
804 }
805 
806 
handle_solo(int argpos,char * argv[])807 void TSE3Play::handle_solo(int argpos, char *argv[])
808 {
809     soloTrack = atoi(argv[argpos+1]);
810     if (verbose) cout << "SoloTrack " << soloTrack << "\n";
811 }
812 
813 
handle_sleep(int argpos,char * argv[])814 void TSE3Play::handle_sleep(int argpos, char *argv[])
815 {
816     usleepPeriod = atoi(argv[argpos+1]);
817     if (verbose) cout << "Sleep " << usleepPeriod << " usecs\n";
818 }
819 
820 
handle_nostop(int,char * [])821 void TSE3Play::handle_nostop(int, char*[])
822 {
823     dostop = false;
824     if (verbose) cout << "Stop at song end disabled\n";
825 }
826 
827 
handle_echo(int,char * [])828 void TSE3Play::handle_echo(int, char*[])
829 {
830     doecho = true;
831     if (verbose) cout << "MIDI echo enabled\n";
832 }
833 
834 
handle_mute(int argpos,char * argv[])835 void TSE3Play::handle_mute(int argpos, char *argv[])
836 {
837     int trackNo = atoi(argv[argpos+1]);
838     if (verbose) cout << "Muting Track " << trackNo << "\n";
839     muteList.push_back(trackNo);
840 }
841 
842 
handle_patchesdir(int argpos,char * argv[])843 void TSE3Play::handle_patchesdir(int argpos, char *argv[])
844 {
845 #ifdef TSE3_WITH_OSS
846     patchesDir = argv[argpos+1];
847     if (verbose) cout << "Patches directory is: " << patchesDir.c_str() << "\n";
848 #else
849     std::cerr << "OSS is not supported on this computer\n";
850 #endif
851 }
852 
853 
handle_internal(int,char * [])854 void TSE3Play::handle_internal(int, char *[])
855 {
856     if (verbose) cout << "Forcing output to first internal port\n";
857     port = USE_INTERNAL_PORT;
858 }
859 
860 
handle_external(int,char * [])861 void TSE3Play::handle_external(int, char *[])
862 {
863     if (verbose) cout << "Forcing output to first external port\n";
864     port = USE_EXTERNAL_PORT;
865 }
866 
867 
handle_port(int argpos,char * argv[])868 void TSE3Play::handle_port(int argpos, char *argv[])
869 {
870     port = atoi(argv[argpos+1]);
871     if (verbose) cout << "Forcing output to port " << port << "\n";
872 }
873 
874 
handle_fast(int,char * [])875 void TSE3Play::handle_fast(int, char *[])
876 {
877     fastMidi = true;
878     if (verbose) cout << "Using fast MIDI import routine\n";
879 }
880 
881 
MidiScheduler_Stopped(MidiScheduler *)882 void TSE3Play::MidiScheduler_Stopped(MidiScheduler *)
883 {
884     more_to_come = false;
885 }
886 
887 
Notifier_Deleted(MidiScheduler *)888 void TSE3Play::Notifier_Deleted(MidiScheduler *)
889 {
890     more_to_come = false;
891 }
892 
893 
setPanic(Panic * panic)894 void TSE3Play::setPanic(Panic *panic)
895 {
896     panic->setMidiReset(midi);
897     panic->setGmReset(gm);
898     panic->setGsReset(gs);
899     panic->setXgReset(xg);
900 }
901 
902 
Switch(std::string l,std::string s,int n,std::string h,handler_t hd)903 TSE3Play::Switch::Switch
904     (std::string l, std::string s, int n, std::string h, handler_t hd)
905 : lng(l), srt(s), nargs(n), help(h), handler(hd)
906 {
907     for (std::string::size_type n = 0; n < help.size(); ++n)
908     {
909         if (help[n] == '\n')
910         {
911             help.replace(n, 1, "\n                                     ");
912         }
913     }
914 }
915 
916 
917 /*****************************************************************************
918  * TSE3PlayVisual class
919  ****************************************************************************/
920 
TSE3PlayVisual(Transport * t,MidiScheduler * s)921 TSE3PlayVisual::TSE3PlayVisual(Transport *t, MidiScheduler *s)
922 : transport(t), scheduler(s), lastMsec(0), x(0), y(0),
923   lastClock(0), barPos(-1)
924 {
925     for (int chan = 0; chan < 16; chan++)
926     {
927         now[chan] = next[chan] = 0;
928     }
929     if (transport) transport->attachCallback(this);
930     if (transport) attachTo(transport);
931 }
932 
933 
~TSE3PlayVisual()934 TSE3PlayVisual::~TSE3PlayVisual()
935 {
936     if (transport) transport->detachCallback(this);
937 }
938 
939 
setLastClock(Clock lc)940 void TSE3PlayVisual::setLastClock(Clock lc)
941 {
942     lastClock = lc;
943 }
944 
945 
Transport_MidiIn(MidiCommand c)946 void TSE3PlayVisual::Transport_MidiIn(MidiCommand c)
947 {
948     Transport_MidiOut(c);
949 }
950 
951 
Transport_MidiOut(MidiCommand c)952 void TSE3PlayVisual::Transport_MidiOut(MidiCommand c)
953 {
954     if (0)
955         next[c.channel] = max;
956     if (c.status == MidiCommand_NoteOn)
957     {
958         int newval = c.data2 * max / 127;
959         if (newval > next[c.channel] && newval != now[c.channel])
960             next[c.channel] = newval;
961     }
962 }
963 
964 
drawFrame()965 void TSE3PlayVisual::drawFrame()
966 {
967     cout << escChar << "[34m"
968          << "-------------------------------\n"
969          << escChar << "[37m"
970          << "                               \n"
971          << "           tse3play            \n"
972          << "                               \n"
973          << "   (c) 2000-2 Pete Goodliffe   \n"
974          << " Incorporates TSE3 technology  \n"
975          << escChar << "[34m";
976     for (int n = 5; n <= max; n++)
977         cout << "                                 \n";
978     cout << "0-1-2-3-4-5-6-7-8-9-A-B-C-D-E-F\n"
979          << "-------------------------------\n"
980          << escChar << "[37m";
981 }
982 
983 
message(const std::string & message)984 void TSE3PlayVisual::message(const std::string &message)
985 {
986     move(0, 5);
987     cout << "                               \r";
988     move(31/2-message.size()/2, 5);
989     cout << escChar << "[33;1m"
990          << message
991          << escChar << "[37m";
992     x += message.size();
993     move(0, 0);
994     cout.flush();
995 }
996 
997 
blankFrame()998 void TSE3PlayVisual::blankFrame()
999 {
1000     for (int n = 0; n <= max; n++)
1001     {
1002         move(0, 3+n);
1003         cout << "                               \r";
1004     }
1005     move(0, 0);
1006 }
1007 
1008 
poll()1009 void TSE3PlayVisual::poll()
1010 {
1011     Clock now = scheduler->clock();
1012     cout << now / Clock::PPQN << ":" << now % Clock::PPQN << "  \r";
1013     if (1)
1014     {
1015         move(10, 0);
1016         cout << "(LA: " << transport->lookAhead();
1017         if (transport->breakUps())
1018             cout << ", BreakUps: " << transport->breakUps();
1019         cout << ")  \r";
1020         x = 0;
1021     }
1022     int msec = scheduler->msecs();
1023     if (msec > lastMsec + 100)
1024     {
1025         updateBars();
1026         lastMsec = msec;
1027     }
1028 }
1029 
1030 
updateBars()1031 void TSE3PlayVisual::updateBars()
1032 {
1033     // VU Bars
1034 
1035     for (int chan = 0; chan < 16; chan++)
1036     {
1037         if (now[chan] != next[chan])
1038         {
1039             char c   = (now[chan] > next[chan]) ? ' ' : '=';
1040             int  bot = (now[chan] > next[chan]) ? next[chan] : now[chan];
1041             int  top = (now[chan] > next[chan]) ? now[chan]  : next[chan];
1042             for (int n = bot; n <= top; n++)
1043             {
1044                 // work out colour
1045                 char *colcode = "37"; // white
1046                 if (n > max*2/3)
1047                     colcode = "31"; // red
1048                 else if (n > max/3)
1049                     colcode = "33"; // yellow
1050                 else
1051                     colcode = "32"; // green
1052                 cout << escChar << "[" << colcode << ";1m";
1053 
1054                 // print character
1055                 moveout(chan*2, 3+n, c);
1056             }
1057             now[chan] = next[chan];
1058             if (now[chan] > 0) next[chan] = now[chan]-1;
1059         }
1060     }
1061 
1062     // Song position bar
1063 
1064     if (lastClock)
1065     {
1066         int newBarPos = scheduler->clock() * 31 / lastClock;
1067         if (newBarPos != barPos && newBarPos <= 31)
1068         {
1069             cout << escChar << "[32;1m";
1070             for (; barPos < newBarPos; barPos++)
1071             {
1072                 moveout(barPos, 1, '=');
1073             }
1074         }
1075     }
1076 
1077     // Tidy up
1078 
1079     cout << escChar << "[0m";
1080     move(0, 0);
1081     cout.flush();
1082 }
1083 
1084 
move(int newx,int newy)1085 void TSE3PlayVisual::move(int newx, int newy)
1086 {
1087     if (newx < x)
1088     {
1089         cout << escChar << "[" << x-newx << "D";
1090     }
1091     else if (newx > x)
1092     {
1093         cout << escChar << "[" << newx-x << "C";
1094     }
1095     x = newx;
1096 
1097     if (newy < y)
1098     {
1099         cout << escChar << "[" << y-newy << "B";
1100     }
1101     else if (newy > y)
1102     {
1103         cout << escChar << "[" << newy-y << "A";
1104     }
1105     y = newy;
1106 }
1107 
1108 
moveout(int newx,int newy,char c)1109 void TSE3PlayVisual::moveout(int newx, int newy, char c)
1110 {
1111     move(newx, newy);
1112     cout.put(c);
1113     x++;
1114 }
1115 
1116 
Notifier_Deleted(Transport *)1117 void TSE3PlayVisual::Notifier_Deleted(Transport *)
1118 {
1119     transport = 0;
1120 }
1121 
1122