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