1 //
2 // Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
3 // Creation Date: Mon Feb 16 12:26:32 PST 2015 Adapted from binasc program.
4 // Last Modified: Sat Apr 21 10:52:19 PDT 2018 Removed using namespace std;
5 // Filename: midifile/src/Binasc.cpp
6 // Syntax: C++11
7 // vim: ts=3 noexpandtab
8 //
9 // description: Interface to convert bytes between binary and ASCII forms.
10 //
11
12 #include "Binasc.h"
13
14 #include <sstream>
15 #include <stdlib.h>
16
17
18 namespace smf {
19
20 //////////////////////////////
21 //
22 // Binasc::Binasc -- Constructor: set the default option values.
23 //
24
Binasc(void)25 Binasc::Binasc(void) {
26 m_bytesQ = 1; // for printing HEX bytes when converting to ASCII
27 m_commentsQ = 0; // for printing text comments when converting to ASCII
28 m_midiQ = 0; // for printing ASCII as parsed MIDI file.
29 m_maxLineLength = 75;
30 m_maxLineBytes = 25;
31 }
32
33
34
35 //////////////////////////////
36 //
37 // Binasc::~Binasc -- Destructor.
38 //
39
~Binasc()40 Binasc::~Binasc() {
41 // do nothing
42 }
43
44
45
46 //////////////////////////////
47 //
48 // Binasc::setLineLength -- Set the maximum length of a line when converting
49 // binary content into ASCII bytes. If the input size is less than one,
50 // set to the default value of 75 characters per line.
51 //
52
setLineLength(int length)53 int Binasc::setLineLength(int length) {
54 if (length < 1) {
55 m_maxLineLength = 75;
56 } else {
57 m_maxLineLength = length;
58 }
59 return m_maxLineLength;
60 }
61
62
63
64 //////////////////////////////
65 //
66 // Binasc::getLineLength -- Set the maximum length of a line when converting
67 // binary content into ASCII bytes.
68 //
69
getLineLength(void)70 int Binasc::getLineLength(void) {
71 return m_maxLineLength;
72 }
73
74
75
76 //////////////////////////////
77 //
78 // Binasc::setLineBytes -- Set the maximum number of hex bytes in ASCII output.
79 // If the input size is less than one, set to the default value of 25
80 // hex bytes per line.
81 //
82
setLineBytes(int length)83 int Binasc::setLineBytes(int length) {
84 if (length < 1) {
85 m_maxLineBytes = 25;
86 } else {
87 m_maxLineBytes = length;
88 }
89 return m_maxLineBytes;
90 }
91
92
93
94 //////////////////////////////
95 //
96 // Binasc::getLineBytes -- Get the maximum number of hex bytes in ASCII output.
97 //
98
getLineBytes(void)99 int Binasc::getLineBytes(void) {
100 return m_maxLineLength;
101 }
102
103
104
105 //////////////////////////////
106 //
107 // Binasc::setComments -- Display or not display printable characters
108 // as comments when converting binary files to ASCII byte codes.
109 //
110
setComments(int state)111 void Binasc::setComments(int state) {
112 m_commentsQ = state ? 1 : 0;
113 }
114
115
setCommentsOn(void)116 void Binasc::setCommentsOn(void) {
117 setComments(true);
118 }
119
120
setCommentsOff(void)121 void Binasc::setCommentsOff(void) {
122 setComments(false);
123 }
124
125
126
127 //////////////////////////////
128 //
129 // Binasc::getComments -- Get the comment display style for
130 // showing comments in ASCII output;
131 //
132
getComments(void)133 int Binasc::getComments(void) {
134 return m_commentsQ;
135 }
136
137
138
139 //////////////////////////////
140 //
141 // Binasc::setBytes -- Display or not display hex codes (only
142 // print ASCII printable characters).
143 //
144
setBytes(int state)145 void Binasc::setBytes(int state) {
146 m_bytesQ = state ? 1 : 0;
147 }
148
149
setBytesOn(void)150 void Binasc::setBytesOn(void) {
151 setBytes(true);
152 }
153
154
setBytesOff(void)155 void Binasc::setBytesOff(void) {
156 setBytes(false);
157 }
158
159
160 //////////////////////////////
161 //
162 // Binasc::getBytes -- Get hex byte display status.
163 //
164
getBytes(void)165 int Binasc::getBytes(void) {
166 return m_bytesQ;
167 }
168
169
170 //////////////////////////////
171 //
172 // Binasc::setMidi -- Display or not display parsed MIDI data.
173 //
174
setMidi(int state)175 void Binasc::setMidi(int state) {
176 m_midiQ = state ? 1 : 0;
177 }
178
179
setMidiOn(void)180 void Binasc::setMidiOn(void) {
181 setMidi(true);
182 }
183
184
setMidiOff(void)185 void Binasc::setMidiOff(void) {
186 setMidi(false);
187 }
188
189
190
191 //////////////////////////////
192 //
193 // Binasc::getMidi -- Get the MIDI file printing style option state.
194 //
195
getMidi(void)196 int Binasc::getMidi(void) {
197 return m_midiQ;
198 }
199
200
201
202 //////////////////////////////
203 //
204 // Binasc::writeToBinary -- Convert an ASCII representation of bytes into
205 // the binary file that it describes. Returns 0 if there was a problem
206 // otherwise returns 1.
207 //
208
writeToBinary(const std::string & outfile,const std::string & infile)209 int Binasc::writeToBinary(const std::string& outfile,
210 const std::string& infile) {
211 std::ifstream input;
212 input.open(infile.c_str());
213 if (!input.is_open()) {
214 std::cerr << "Cannot open " << infile
215 << " for reading in binasc." << std::endl;
216 return 0;
217 }
218
219 std::ofstream output;
220 output.open(outfile.c_str());
221 if (!output.is_open()) {
222 std::cerr << "Cannot open " << outfile
223 << " for reading in binasc." << std::endl;
224 return 0;
225 }
226
227 int status = writeToBinary(output, input);
228 input.close();
229 output.close();
230 return status;
231 }
232
233
writeToBinary(const std::string & outfile,std::istream & input)234 int Binasc::writeToBinary(const std::string& outfile, std::istream& input) {
235 std::ofstream output;
236 output.open(outfile.c_str());
237 if (!output.is_open()) {
238 std::cerr << "Cannot open " << outfile
239 << " for reading in binasc." << std::endl;
240 return 0;
241 }
242
243 int status = writeToBinary(output, input);
244 output.close();
245 return status;
246 }
247
248
writeToBinary(std::ostream & out,const std::string & infile)249 int Binasc::writeToBinary(std::ostream& out, const std::string& infile) {
250 std::ifstream input;
251 input.open(infile.c_str());
252 if (!input.is_open()) {
253 std::cerr << "Cannot open " << infile
254 << " for reading in binasc." << std::endl;
255 return 0;
256 }
257
258 int status = writeToBinary(out, input);
259 input.close();
260 return status;
261 }
262
263
writeToBinary(std::ostream & out,std::istream & input)264 int Binasc::writeToBinary(std::ostream& out, std::istream& input) {
265 char inputLine[1024] = {0}; // current line being processed
266 int lineNum = 0; // current line number
267
268 input.getline(inputLine, 1024, '\n');
269 lineNum++;
270 while (!input.eof()) {
271 int status = processLine(out, inputLine, lineNum);
272 if (!status) {
273 return 0;
274 }
275 input.getline(inputLine, 1024, '\n');
276 lineNum++;
277 }
278 return 1;
279 }
280
281
282
283 //////////////////////////////
284 //
285 // Binasc::readFromBinary -- convert an ASCII representation of bytes into
286 // the binary file that it describes.
287 //
288
readFromBinary(const std::string & outfile,const std::string & infile)289 int Binasc::readFromBinary(const std::string& outfile, const std::string& infile) {
290 std::ifstream input;
291 input.open(infile.c_str());
292 if (!input.is_open()) {
293 std::cerr << "Cannot open " << infile
294 << " for reading in binasc." << std::endl;
295 return 0;
296 }
297
298 std::ofstream output;
299 output.open(outfile.c_str());
300 if (!output.is_open()) {
301 std::cerr << "Cannot open " << outfile
302 << " for reading in binasc." << std::endl;
303 return 0;
304 }
305
306 int status = readFromBinary(output, input);
307 input.close();
308 output.close();
309 return status;
310 }
311
312
readFromBinary(const std::string & outfile,std::istream & input)313 int Binasc::readFromBinary(const std::string& outfile, std::istream& input) {
314 std::ofstream output;
315 output.open(outfile.c_str());
316 if (!output.is_open()) {
317 std::cerr << "Cannot open " << outfile
318 << " for reading in binasc." << std::endl;
319 return 0;
320 }
321
322 int status = readFromBinary(output, input);
323 output.close();
324 return status;
325 }
326
327
readFromBinary(std::ostream & out,const std::string & infile)328 int Binasc::readFromBinary(std::ostream& out, const std::string& infile) {
329 std::ifstream input;
330 input.open(infile.c_str());
331 if (!input.is_open()) {
332 std::cerr << "Cannot open " << infile
333 << " for reading in binasc." << std::endl;
334 return 0;
335 }
336
337 int status = readFromBinary(out, input);
338 input.close();
339 return status;
340 }
341
342
readFromBinary(std::ostream & out,std::istream & input)343 int Binasc::readFromBinary(std::ostream& out, std::istream& input) {
344 int status;
345 if (m_midiQ) {
346 status = outputStyleMidi(out, input);
347 } else if (!m_bytesQ) {
348 status = outputStyleAscii(out, input);
349 } else if (m_bytesQ && m_commentsQ) {
350 status = outputStyleBoth(out, input);
351 } else {
352 status = outputStyleBinary(out, input);
353 }
354 return status;
355 }
356
357
358
359 ///////////////////////////////////////////////////////////////////////////
360 //
361 // protected functions --
362 //
363
364 //////////////////////////////
365 //
366 // Binasc::outputStyleAscii -- read an input file and output bytes in ascii
367 // form, not displaying any blank lines. Output words are not
368 // broken unless they are longer than 75 characters.
369 //
370
outputStyleAscii(std::ostream & out,std::istream & input)371 int Binasc::outputStyleAscii(std::ostream& out, std::istream& input) {
372 uchar outputWord[256] = {0}; // storage for current word
373 int index = 0; // current length of word
374 int lineCount = 0; // current length of line
375 int type = 0; // 0=space, 1=printable
376 uchar ch; // current input byte
377
378 ch = input.get();
379 while (!input.eof()) {
380 int lastType = type;
381 type = (isprint(ch) && !isspace(ch)) ? 1 : 0;
382
383 if ((type == 1) && (lastType == 0)) {
384 // start of a new word. check where to put old word
385 if (index + lineCount >= m_maxLineLength) { // put on next line
386 outputWord[index] = '\0';
387 out << '\n' << outputWord;
388 lineCount = index;
389 index = 0;
390 } else { // put on current line
391 outputWord[index] = '\0';
392 if (lineCount != 0) {
393 out << ' ';
394 lineCount++;
395 }
396 out << outputWord;
397 lineCount += index;
398 index = 0;
399 }
400 }
401 if (type == 1) {
402 outputWord[index++] = ch;
403 }
404 ch = input.get();
405 }
406
407 if (index != 0) {
408 out << std::endl;
409 }
410
411 return 1;
412 }
413
414
415
416 //////////////////////////////
417 //
418 // Binasc::outputStyleBinary -- read an input binary file and output bytes
419 // in ascii form, hexadecimal numbers only.
420 //
421
outputStyleBinary(std::ostream & out,std::istream & input)422 int Binasc::outputStyleBinary(std::ostream& out, std::istream& input) {
423 int currentByte = 0; // current byte output in line
424 uchar ch; // current input byte
425
426 ch = input.get();
427 if (input.eof()) {
428 std::cerr << "End of the file right away!" << std::endl;
429 return 0;
430 }
431
432 while (!input.eof()) {
433 if (ch < 0x10) {
434 out << '0';
435 }
436 out << std::hex << (int)ch << ' ';
437 currentByte++;
438 if (currentByte >= m_maxLineBytes) {
439 out << '\n';
440 currentByte = 0;
441 }
442 ch = input.get();
443 }
444
445 if (currentByte != 0) {
446 out << std::endl;
447 }
448
449 return 1;
450 }
451
452
453
454 //////////////////////////////
455 //
456 // Binasc::outputStyleBoth -- read an input file and output bytes in ASCII
457 // form with both hexadecimal numbers and ascii representation
458 //
459
outputStyleBoth(std::ostream & out,std::istream & input)460 int Binasc::outputStyleBoth(std::ostream& out, std::istream& input) {
461 uchar asciiLine[256] = {0}; // storage for output line
462 int currentByte = 0; // current byte output in line
463 int index = 0; // current character in asciiLine
464 uchar ch; // current input byte
465
466 ch = input.get();
467 while (!input.eof()) {
468 if (index == 0) {
469 asciiLine[index++] = ';';
470 out << ' ';
471 }
472 if (ch < 0x10) {
473 out << '0';
474 }
475 out << std::hex << (int)ch << ' ';
476 currentByte++;
477
478 asciiLine[index++] = ' ';
479 if (isprint(ch)) {
480 asciiLine[index++] = ch;
481 } else {
482 asciiLine[index++] = ' ';
483 }
484 asciiLine[index++] = ' ';
485
486 if (currentByte >= m_maxLineBytes) {
487 out << '\n';
488 asciiLine[index] = '\0';
489 out << asciiLine << "\n\n";
490 currentByte = 0;
491 index = 0;
492 }
493 ch = input.get();
494 }
495
496 if (currentByte != 0) {
497 out << '\n';
498 asciiLine[index] = '\0';
499 out << asciiLine << '\n' << std::endl;
500 }
501
502 return 1;
503 }
504
505
506
507 ///////////////////////////////
508 //
509 // processLine -- read a line of input and output any specified bytes
510 //
511
processLine(std::ostream & out,const std::string & input,int lineCount)512 int Binasc::processLine(std::ostream& out, const std::string& input,
513 int lineCount) {
514 int status = 1;
515 int i = 0;
516 int length = (int)input.size();
517 std::string word;
518 while (i<length) {
519 if ((input[i] == ';') || (input[i] == '#') || (input[i] == '/')) {
520 // comment to end of line, so ignore
521 return 1;
522 } else if ((input[i] == ' ') || (input[i] == '\n')
523 || (input[i] == '\t')) {
524 // ignore whitespace
525 i++;
526 continue;
527 } else if (input[i] == '+') {
528 i = getWord(word, input, " \n\t", i);
529 status = processAsciiWord(out, word, lineCount);
530 } else if (input[i] == '"') {
531 i = getWord(word, input, "\"", i);
532 status = processStringWord(out, word, lineCount);
533 } else if (input[i] == 'v') {
534 i = getWord(word, input, " \n\t", i);
535 status = processVlvWord(out, word, lineCount);
536 } else if (input[i] == 'p') {
537 i = getWord(word, input, " \n\t", i);
538 status = processMidiPitchBendWord(out, word, lineCount);
539 } else if (input[i] == 't') {
540 i = getWord(word, input, " \n\t", i);
541 status = processMidiTempoWord(out, word, lineCount);
542 } else {
543 i = getWord(word, input, " \n\t", i);
544 if (word.find('\'') != std::string::npos) {
545 status = processDecimalWord(out, word, lineCount);
546 } else if ((word.find(',') != std::string::npos)
547 || (word.size() > 2)) {
548 status = processBinaryWord(out, word, lineCount);
549 } else {
550 status = processHexWord(out, word, lineCount);
551 }
552 }
553
554 if (status == 0) {
555 return 0;
556 }
557
558 }
559
560 return 1;
561 }
562
563
564
565 //////////////////////////////
566 //
567 // Binasc::getWord -- extract a sub string, stopping at any of the given
568 // terminator characters.
569 //
570
getWord(std::string & word,const std::string & input,const std::string & terminators,int index)571 int Binasc::getWord(std::string& word, const std::string& input,
572 const std::string& terminators, int index) {
573 word.resize(0);
574 int i = index;
575 int escape = 0;
576 int ecount = 0;
577 if (terminators.find('"') != std::string::npos) {
578 escape = 1;
579 }
580 while (i < (int)input.size()) {
581 if (escape && input[i] == '\"') {
582 ecount++;
583 i++;
584 if (ecount >= 2) {
585 break;
586 }
587 }
588 if (escape && (i<(int)input.size()-1) && (input[i] == '\\')
589 && (input[i+1] == '"')) {
590 word.push_back(input[i+1]);
591 i += 2;
592 } else if (terminators.find(input[i]) == std::string::npos) {
593 word.push_back(input[i]);
594 i++;
595 } else {
596 i++;
597 return i;
598 }
599 }
600 return i;
601 }
602
603
604
605 ///////////////////////////////
606 //
607 // Binasc::getVLV -- read a Variable-Length Value from the file
608 //
609
getVLV(std::istream & infile,int & trackbytes)610 int Binasc::getVLV(std::istream& infile, int& trackbytes) {
611 int output = 0;
612 uchar ch = 0;
613 infile.read((char*)&ch, 1);
614 trackbytes++;
615 output = (output << 7) | (0x7f & ch);
616 while (ch >= 0x80) {
617 infile.read((char*)&ch, 1);
618 trackbytes++;
619 output = (output << 7) | (0x7f & ch);
620 }
621 return output;
622 }
623
624
625
626 //////////////////////////////
627 //
628 // Binasc::readMidiEvent -- Read a delta time and then a MIDI message
629 // (or meta message). Returns 1 if not end-of-track meta message;
630 // 0 otherwise.
631 //
632
readMidiEvent(std::ostream & out,std::istream & infile,int & trackbytes,int & command)633 int Binasc::readMidiEvent(std::ostream& out, std::istream& infile,
634 int& trackbytes, int& command) {
635
636 // Read and print Variable Length Value for delta ticks
637 int vlv = getVLV(infile, trackbytes);
638
639 std::stringstream output;
640
641 output << "v" << std::dec << vlv << "\t";
642
643 std::string comment;
644
645 int status = 1;
646 uchar ch = 0;
647 char byte1, byte2;
648 infile.read((char*)&ch, 1);
649 trackbytes++;
650 if (ch < 0x80) {
651 // running status: command byte is previous one in data stream
652 output << " ";
653 } else {
654 // midi command byte
655 output << std::hex << (int)ch;
656 command = ch;
657 infile.read((char*)&ch, 1);
658 trackbytes++;
659 }
660 byte1 = ch;
661 switch (command & 0xf0) {
662 case 0x80: // note-off: 2 bytes
663 output << " '" << std::dec << (int)byte1;
664 infile.read((char*)&ch, 1);
665 trackbytes++;
666 byte2 = ch;
667 output << " '" << std::dec << (int)byte2;
668 if (m_commentsQ) {
669 comment += "note-off " + keyToPitchName(byte1);
670 }
671 break;
672 case 0x90: // note-on: 2 bytes
673 output << " '" << std::dec << (int)byte1;
674 infile.read((char*)&ch, 1);
675 trackbytes++;
676 byte2 = ch;
677 output << " '" << std::dec << (int)byte2;
678 if (m_commentsQ) {
679 if (byte2 == 0) {
680 comment += "note-off " + keyToPitchName(byte1);
681 } else {
682 comment += "note-on " + keyToPitchName(byte1);
683 }
684 }
685 break;
686 case 0xA0: // aftertouch: 2 bytes
687 output << " '" << std::dec << (int)byte1;
688 infile.read((char*)&ch, 1);
689 trackbytes++;
690 byte2 = ch;
691 output << " '" << std::dec << (int)byte2;
692 if (m_commentsQ) {
693 comment += "after-touch";
694 }
695 break;
696 case 0xB0: // continuous controller: 2 bytes
697 output << " '" << std::dec << (int)byte1;
698 infile.read((char*)&ch, 1);
699 trackbytes++;
700 byte2 = ch;
701 output << " '" << std::dec << (int)byte2;
702 if (m_commentsQ) {
703 comment += "controller";
704 }
705 break;
706 case 0xE0: // pitch-bend: 2 bytes
707 output << " '" << std::dec << (int)byte1;
708 infile.read((char*)&ch, 1);
709 trackbytes++;
710 byte2 = ch;
711 output << " '" << std::dec << (int)byte2;
712 if (m_commentsQ) {
713 comment += "pitch-bend";
714 }
715 break;
716 case 0xC0: // patch change: 1 bytes
717 output << " '" << std::dec << (int)byte1;
718 if (m_commentsQ) {
719 output << "\t";
720 comment += "patch-change";
721 }
722 break;
723 case 0xD0: // channel pressure: 1 bytes
724 output << " '" << std::dec << (int)byte1;
725 if (m_commentsQ) {
726 comment += "channel pressure";
727 }
728 break;
729 case 0xF0: // various system bytes: variable bytes
730 switch (command) {
731 case 0xf0:
732 break;
733 case 0xf7:
734 // Read the first byte which is either 0xf0 or 0xf7.
735 // Then a VLV byte count for the number of bytes
736 // that remain in the message will follow.
737 // Then read that number of bytes.
738 {
739 infile.putback(byte1);
740 trackbytes--;
741 int length = getVLV(infile, trackbytes);
742 output << " v" << std::dec << length;
743 for (int i=0; i<length; i++) {
744 infile.read((char*)&ch, 1);
745 trackbytes++;
746 if (ch < 0x10) {
747 output << " 0" << std::hex << (int)ch;
748 } else {
749 output << " " << std::hex << (int)ch;
750 }
751 }
752 }
753 break;
754 case 0xf1:
755 break;
756 case 0xf2:
757 break;
758 case 0xf3:
759 break;
760 case 0xf4:
761 break;
762 case 0xf5:
763 break;
764 case 0xf6:
765 break;
766 case 0xf8:
767 break;
768 case 0xf9:
769 break;
770 case 0xfa:
771 break;
772 case 0xfb:
773 break;
774 case 0xfc:
775 break;
776 case 0xfd:
777 break;
778 case 0xfe:
779 std::cerr << "Error command not yet handled" << std::endl;
780 return 0;
781 break;
782 case 0xff: // meta message
783 {
784 int metatype = ch;
785 output << " " << std::hex << metatype;
786 int length = getVLV(infile, trackbytes);
787 output << " v" << std::dec << length;
788 switch (metatype) {
789
790 case 0x00: // sequence number
791 // display two-byte big-endian decimal value.
792 {
793 infile.read((char*)&ch, 1);
794 trackbytes++;
795 int number = ch;
796 infile.read((char*)&ch, 1);
797 trackbytes++;
798 number = (number << 8) | ch;
799 output << " 2'" << number;
800 }
801 break;
802
803 case 0x20: // MIDI channel prefix
804 case 0x21: // MIDI port
805 // display single-byte decimal number
806 infile.read((char*)&ch, 1);
807 trackbytes++;
808 output << " '" << (int)ch;
809 break;
810
811 case 0x51: // Tempo
812 // display tempo as "t" word.
813 {
814 int number = 0;
815 infile.read((char*)&ch, 1);
816 trackbytes++;
817 number = (number << 8) | ch;
818 infile.read((char*)&ch, 1);
819 trackbytes++;
820 number = (number << 8) | ch;
821 infile.read((char*)&ch, 1);
822 trackbytes++;
823 number = (number << 8) | ch;
824 double tempo = 1000000.0 / number * 60.0;
825 output << " t" << tempo;
826 }
827 break;
828
829 case 0x54: // SMPTE offset
830 infile.read((char*)&ch, 1);
831 trackbytes++;
832 output << " '" << (int)ch; // hour
833 infile.read((char*)&ch, 1);
834 trackbytes++;
835 output << " '" << (int)ch; // minutes
836 infile.read((char*)&ch, 1);
837 trackbytes++;
838 output << " '" << (int)ch; // seconds
839 infile.read((char*)&ch, 1);
840 trackbytes++;
841 output << " '" << (int)ch; // frames
842 infile.read((char*)&ch, 1);
843 trackbytes++;
844 output << " '" << (int)ch; // subframes
845 break;
846
847 case 0x58: // time signature
848 infile.read((char*)&ch, 1);
849 trackbytes++;
850 output << " '" << (int)ch; // numerator
851 infile.read((char*)&ch, 1);
852 trackbytes++;
853 output << " '" << (int)ch; // denominator power
854 infile.read((char*)&ch, 1);
855 trackbytes++;
856 output << " '" << (int)ch; // clocks per beat
857 infile.read((char*)&ch, 1);
858 trackbytes++;
859 output << " '" << (int)ch; // 32nd notes per beat
860 break;
861
862 case 0x59: // key signature
863 infile.read((char*)&ch, 1);
864 trackbytes++;
865 output << " '" << (int)ch; // accidentals
866 infile.read((char*)&ch, 1);
867 trackbytes++;
868 output << " '" << (int)ch; // mode
869 break;
870
871 case 0x01: // text
872 case 0x02: // copyright
873 case 0x03: // track name
874 case 0x04: // instrument name
875 case 0x05: // lyric
876 case 0x06: // marker
877 case 0x07: // cue point
878 case 0x08: // program name
879 case 0x09: // device name
880 output << " \"";
881 for (int i=0; i<length; i++) {
882 infile.read((char*)&ch, 1);
883 trackbytes++;
884 if (ch == '"') {
885 output << '\\';
886 }
887 output << (char)ch;
888 }
889 output << "\"";
890 break;
891 default:
892 for (int i=0; i<length; i++) {
893 infile.read((char*)&ch, 1);
894 trackbytes++;
895 output << " ";
896 if (ch < 0x10) {
897 output << "0";
898 }
899 output << std::hex << (int)ch;
900 }
901 }
902 switch (metatype) {
903 case 0x00: comment += "sequence number"; break;
904 case 0x01: comment += "text"; break;
905 case 0x02: comment += "copyright notice"; break;
906 case 0x03: comment += "track name"; break;
907 case 0x04: comment += "instrument name"; break;
908 case 0x05: comment += "lyric"; break;
909 case 0x06: comment += "marker"; break;
910 case 0x07: comment += "cue point"; break;
911 case 0x08: comment += "program name"; break;
912 case 0x09: comment += "device name"; break;
913 case 0x20: comment += "MIDI channel prefix"; break;
914 case 0x21: comment += "MIDI port"; break;
915 case 0x51: comment += "tempo"; break;
916 case 0x54: comment += "SMPTE offset"; break;
917 case 0x58: comment += "time signature"; break;
918 case 0x59: comment += "key signature"; break;
919 case 0x7f: comment += "system exclusive"; break;
920 case 0x2f:
921 status = 0;
922 comment += "end-of-track";
923 break;
924 default:
925 comment += "meta-message";
926 }
927 }
928 break;
929
930 }
931 break;
932 }
933
934 out << output.str();
935 if (m_commentsQ) {
936 out << "\t; " << comment;
937 }
938
939 return status;
940 }
941
942
943
944 /////////////////////////////
945 //
946 // Binasc::keyToPitchName -- Convert a MIDI key number to scientific
947 // pitch notation.
948 //
949
keyToPitchName(int key)950 std::string Binasc::keyToPitchName(int key) {
951 int pc = key % 12;
952 int octave = key / 12 - 1;
953 std::stringstream output;
954 switch (pc) {
955 case 0: output << "C"; break;
956 case 1: output << "C#"; break;
957 case 2: output << "D"; break;
958 case 3: output << "D#"; break;
959 case 4: output << "E"; break;
960 case 5: output << "F"; break;
961 case 6: output << "F#"; break;
962 case 7: output << "G"; break;
963 case 8: output << "G#"; break;
964 case 9: output << "A"; break;
965 case 10: output << "A#"; break;
966 case 11: output << "B"; break;
967 }
968 output << octave;
969 return output.str().c_str();
970 }
971
972
973
974 //////////////////////////////
975 //
976 // Binasc::outputStyleMidi -- Read an input file and output bytes parsed
977 // as a MIDI file (return false if not a MIDI file).
978 //
979
outputStyleMidi(std::ostream & out,std::istream & input)980 int Binasc::outputStyleMidi(std::ostream& out, std::istream& input) {
981 uchar ch = 0; // current input byte
982 std::stringstream tempout;
983 input.read((char*)&ch, 1);
984
985 if (input.eof()) {
986 std::cerr << "End of the file right away!" << std::endl;
987 return 0;
988 }
989
990 // Read the MIDI file header:
991
992 // The first four bytes must be the characters "MThd"
993 if (ch != 'M') { std::cerr << "Not a MIDI file M" << std::endl; return 0; }
994 input.read((char*)&ch, 1);
995 if (ch != 'T') { std::cerr << "Not a MIDI file T" << std::endl; return 0; }
996 input.read((char*)&ch, 1);
997 if (ch != 'h') { std::cerr << "Not a MIDI file h" << std::endl; return 0; }
998 input.read((char*)&ch, 1);
999 if (ch != 'd') { std::cerr << "Not a MIDI file d" << std::endl; return 0; }
1000 tempout << "\"MThd\"";
1001 if (m_commentsQ) {
1002 tempout << "\t\t\t; MIDI header chunk marker";
1003 }
1004 tempout << std::endl;
1005
1006 // The next four bytes are a big-endian byte count for the header
1007 // which should nearly always be "6".
1008 int headersize = 0;
1009 input.read((char*)&ch, 1); headersize = (headersize << 8) | ch;
1010 input.read((char*)&ch, 1); headersize = (headersize << 8) | ch;
1011 input.read((char*)&ch, 1); headersize = (headersize << 8) | ch;
1012 input.read((char*)&ch, 1); headersize = (headersize << 8) | ch;
1013 tempout << "4'" << headersize;
1014 if (m_commentsQ) {
1015 tempout << "\t\t\t; bytes to follow in header chunk";
1016 }
1017 tempout << std::endl;
1018
1019 // First number in header is two-byte file type.
1020 int filetype = 0;
1021 input.read((char*)&ch, 1);
1022 filetype = (filetype << 8) | ch;
1023 input.read((char*)&ch, 1);
1024 filetype = (filetype << 8) | ch;
1025 tempout << "2'" << filetype;
1026 if (m_commentsQ) {
1027 tempout << "\t\t\t; file format: Type-" << filetype << " (";
1028 switch (filetype) {
1029 case 0: tempout << "single track"; break;
1030 case 1: tempout << "multitrack"; break;
1031 case 2: tempout << "multisegment"; break;
1032 default: tempout << "unknown"; break;
1033 }
1034 tempout << ")";
1035 }
1036 tempout << std::endl;
1037
1038 // Second number in header is two-byte trackcount.
1039 int trackcount = 0;
1040 input.read((char*)&ch, 1);
1041 trackcount = (trackcount << 8) | ch;
1042 input.read((char*)&ch, 1);
1043 trackcount = (trackcount << 8) | ch;
1044 tempout << "2'" << trackcount;
1045 if (m_commentsQ) {
1046 tempout << "\t\t\t; number of tracks";
1047 }
1048 tempout << std::endl;
1049
1050 // Third number is divisions. This can be one of two types:
1051 // regular: top bit is 0: number of ticks per quarter note
1052 // SMPTE: top bit is 1: first byte is negative frames, second is
1053 // ticks per frame.
1054 uchar byte1 = 0;
1055 uchar byte2 = 0;
1056 input.read((char*)&byte1, 1);
1057 input.read((char*)&byte2, 1);
1058 if (byte1 & 0x80) {
1059 // SMPTE divisions
1060 tempout << "'-" << 0xff - (ulong)byte1 + 1;
1061 if (m_commentsQ) {
1062 tempout << "\t\t\t; SMPTE frames/second";
1063 }
1064 tempout << std::endl;
1065 tempout << "'" << std::dec << (long)byte2;
1066 if (m_commentsQ) {
1067 tempout << "\t\t\t; subframes per frame";
1068 }
1069 tempout << std::endl;
1070 } else {
1071 // regular divisions
1072 int divisions = 0;
1073 divisions = (divisions << 8) | byte1;
1074 divisions = (divisions << 8) | byte2;
1075 tempout << "2'" << divisions;
1076 if (m_commentsQ) {
1077 tempout << "\t\t\t; ticks per quarter note";
1078 }
1079 tempout << std::endl;
1080 }
1081
1082 // Print any strange bytes in header:
1083 int i;
1084 for (i=0; i<headersize - 6; i++) {
1085 input.read((char*)&ch, 1);
1086 if (ch < 0x10) {
1087 tempout << '0';
1088 }
1089 tempout << std::hex << (int)ch;
1090 }
1091 if (headersize - 6 > 0) {
1092 tempout << "\t\t\t; unknown header bytes";
1093 tempout << std::endl;
1094 }
1095
1096 for (i=0; i<trackcount; i++) {
1097 tempout << "\n;;; TRACK "
1098 << i << " ----------------------------------" << std::endl;
1099
1100 input.read((char*)&ch, 1);
1101 // The first four bytes of a track must be the characters "MTrk"
1102 if (ch != 'M') { std::cerr << "Not a MIDI file M2" << std::endl; return 0; }
1103 input.read((char*)&ch, 1);
1104 if (ch != 'T') { std::cerr << "Not a MIDI file T2" << std::endl; return 0; }
1105 input.read((char*)&ch, 1);
1106 if (ch != 'r') { std::cerr << "Not a MIDI file r" << std::endl; return 0; }
1107 input.read((char*)&ch, 1);
1108 if (ch != 'k') { std::cerr << "Not a MIDI file k" << std::endl; return 0; }
1109 tempout << "\"MTrk\"";
1110 if (m_commentsQ) {
1111 tempout << "\t\t\t; MIDI track chunk marker";
1112 }
1113 tempout << std::endl;
1114
1115 // The next four bytes are a big-endian byte count for the track
1116 int tracksize = 0;
1117 input.read((char*)&ch, 1); tracksize = (tracksize << 8) | ch;
1118 input.read((char*)&ch, 1); tracksize = (tracksize << 8) | ch;
1119 input.read((char*)&ch, 1); tracksize = (tracksize << 8) | ch;
1120 input.read((char*)&ch, 1); tracksize = (tracksize << 8) | ch;
1121 tempout << "4'" << tracksize;
1122 if (m_commentsQ) {
1123 tempout << "\t\t\t; bytes to follow in track chunk";
1124 }
1125 tempout << std::endl;
1126
1127 int trackbytes = 0;
1128 int command = 0;
1129
1130 // process MIDI events until the end of the track
1131 while (readMidiEvent(tempout, input, trackbytes, command)) {
1132 tempout << "\n";
1133 };
1134 tempout << "\n";
1135
1136 if (trackbytes != tracksize) {
1137 tempout << "; TRACK SIZE ERROR, ACTUAL SIZE: " << trackbytes << std::endl;
1138 }
1139 }
1140
1141 // print #define definitions if requested.
1142
1143
1144 // print main content of MIDI file parsing:
1145 out << tempout.str();
1146 return 1;
1147 }
1148
1149
1150
1151 //////////////////////////////
1152 //
1153 // Binasc::processDecimalWord -- interprets a decimal word into
1154 // constituent bytes
1155 //
1156
processDecimalWord(std::ostream & out,const std::string & word,int lineNum)1157 int Binasc::processDecimalWord(std::ostream& out, const std::string& word,
1158 int lineNum) {
1159 int length = (int)word.size(); // length of ascii binary number
1160 int byteCount = -1; // number of bytes to output
1161 int quoteIndex = -1; // index of decimal specifier
1162 int signIndex = -1; // index of any sign for number
1163 int periodIndex = -1; // index of period for floating point
1164 int endianIndex = -1; // index of little endian specifier
1165 int i = 0;
1166
1167 // make sure that all characters are valid
1168 for (i=0; i<length; i++) {
1169 switch (word[i]) {
1170 case '\'':
1171 if (quoteIndex != -1) {
1172 std::cerr << "Error on line " << lineNum << " at token: " << word
1173 << std::endl;
1174 std::cerr << "extra quote in decimal number" << std::endl;
1175 return 0;
1176 } else {
1177 quoteIndex = i;
1178 }
1179 break;
1180 case '-':
1181 if (signIndex != -1) {
1182 std::cerr << "Error on line " << lineNum << " at token: " << word
1183 << std::endl;
1184 std::cerr << "cannot have more than two minus signs in number"
1185 << std::endl;
1186 return 0;
1187 } else {
1188 signIndex = i;
1189 }
1190 if (i == 0 || word[i-1] != '\'') {
1191 std::cerr << "Error on line " << lineNum << " at token: " << word
1192 << std::endl;
1193 std::cerr << "minus sign must immediately follow quote mark" << std::endl;
1194 return 0;
1195 }
1196 break;
1197 case '.':
1198 if (quoteIndex == -1) {
1199 std::cerr << "Error on line " << lineNum << " at token: " << word
1200 << std::endl;
1201 std::cerr << "cannot have decimal marker before quote" << std::endl;
1202 return 0;
1203 }
1204 if (periodIndex != -1) {
1205 std::cerr << "Error on line " << lineNum << " at token: " << word
1206 << std::endl;
1207 std::cerr << "extra period in decimal number" << std::endl;
1208 return 0;
1209 } else {
1210 periodIndex = i;
1211 }
1212 break;
1213 case 'u':
1214 case 'U':
1215 if (quoteIndex != -1) {
1216 std::cerr << "Error on line " << lineNum << " at token: " << word
1217 << std::endl;
1218 std::cerr << "cannot have endian specified after quote" << std::endl;
1219 return 0;
1220 }
1221 if (endianIndex != -1) {
1222 std::cerr << "Error on line " << lineNum << " at token: " << word
1223 << std::endl;
1224 std::cerr << "extra \"u\" in decimal number" << std::endl;
1225 return 0;
1226 } else {
1227 endianIndex = i;
1228 }
1229 break;
1230 case '8':
1231 case '1': case '2': case '3': case '4':
1232 if (quoteIndex == -1 && byteCount != -1) {
1233 std::cerr << "Error on line " << lineNum << " at token: " << word
1234 << std::endl;
1235 std::cerr << "invalid byte specificaton before quote in "
1236 << "decimal number" << std::endl;
1237 return 0;
1238 } else if (quoteIndex == -1) {
1239 byteCount = word[i] - '0';
1240 }
1241 break;
1242 case '0': case '5': case '6': case '7': case '9':
1243 if (quoteIndex == -1) {
1244 std::cerr << "Error on line " << lineNum << " at token: " << word
1245 << std::endl;
1246 std::cerr << "cannot have numbers before quote in decimal number"
1247 << std::endl;
1248 return 0;
1249 }
1250 break;
1251 default:
1252 std::cerr << "Error on line " << lineNum << " at token: " << word
1253 << std::endl;
1254 std::cerr << "Invalid character in decimal number"
1255 " (character number " << i <<")" << std::endl;
1256 return 0;
1257 }
1258 }
1259
1260 // there must be a quote character to indicate a decimal number
1261 // and there must be a decimal number after the quote
1262 if (quoteIndex == -1) {
1263 std::cerr << "Error on line " << lineNum << " at token: " << word
1264 << std::endl;
1265 std::cerr << "there must be a quote to signify a decimal number" << std::endl;
1266 return 0;
1267 } else if (quoteIndex == length - 1) {
1268 std::cerr << "Error on line " << lineNum << " at token: " << word
1269 << std::endl;
1270 std::cerr << "there must be a decimal number after the quote" << std::endl;
1271 return 0;
1272 }
1273
1274 // 8 byte decimal output can only occur if reading a double number
1275 if (periodIndex == -1 && byteCount == 8) {
1276 std::cerr << "Error on line " << lineNum << " at token: " << word
1277 << std::endl;
1278 std::cerr << "only floating-point numbers can use 8 bytes" << std::endl;
1279 return 0;
1280 }
1281
1282 // default size for floating point numbers is 4 bytes
1283 if (periodIndex != -1) {
1284 if (byteCount == -1) {
1285 byteCount = 4;
1286 }
1287 }
1288
1289 // process any floating point numbers possibilities
1290 if (periodIndex != -1) {
1291 double doubleOutput = atof(&word[quoteIndex+1]);
1292 float floatOutput = (float)doubleOutput;
1293 switch (byteCount) {
1294 case 4:
1295 if (endianIndex == -1) {
1296 writeBigEndianFloat(out, floatOutput);
1297 } else {
1298 writeLittleEndianFloat(out, floatOutput);
1299 }
1300 return 1;
1301 break;
1302 case 8:
1303 if (endianIndex == -1) {
1304 writeBigEndianDouble(out, doubleOutput);
1305 } else {
1306 writeLittleEndianDouble(out, doubleOutput);
1307 }
1308 return 1;
1309 break;
1310 default:
1311 std::cerr << "Error on line " << lineNum << " at token: " << word
1312 << std::endl;
1313 std::cerr << "floating-point numbers can be only 4 or 8 bytes" << std::endl;
1314 return 0;
1315 }
1316 }
1317
1318 // process any integer decimal number possibilities
1319
1320 // default integer size is one byte, if size is not specified, then
1321 // the number must be in the one byte range and cannot overflow
1322 // the byte if the size of the decimal number is not specified
1323 if (byteCount == -1) {
1324 if (signIndex != -1) {
1325 long tempLong = atoi(&word[quoteIndex + 1]);
1326 if (tempLong > 127 || tempLong < -128) {
1327 std::cerr << "Error on line " << lineNum << " at token: " << word
1328 << std::endl;
1329 std::cerr << "Decimal number out of range from -128 to 127" << std::endl;
1330 return 0;
1331 }
1332 char charOutput = (char)tempLong;
1333 out << charOutput;
1334 return 1;
1335 } else {
1336 ulong tempLong = (ulong)atoi(&word[quoteIndex + 1]);
1337 uchar ucharOutput = (uchar)tempLong;
1338 if (tempLong > 255) { // || (tempLong < 0)) {
1339 std::cerr << "Error on line " << lineNum << " at token: " << word
1340 << std::endl;
1341 std::cerr << "Decimal number out of range from 0 to 255" << std::endl;
1342 return 0;
1343 }
1344 out << ucharOutput;
1345 return 1;
1346 }
1347 }
1348
1349 // left with an integer number with a specified number of bytes
1350 switch (byteCount) {
1351 case 1:
1352 if (signIndex != -1) {
1353 long tempLong = atoi(&word[quoteIndex + 1]);
1354 char charOutput = (char)tempLong;
1355 out << charOutput;
1356 return 1;
1357 } else {
1358 ulong tempLong = (ulong)atoi(&word[quoteIndex + 1]);
1359 uchar ucharOutput = (uchar)tempLong;
1360 out << ucharOutput;
1361 return 1;
1362 }
1363 break;
1364 case 2:
1365 if (signIndex != -1) {
1366 long tempLong = atoi(&word[quoteIndex + 1]);
1367 short shortOutput = (short)tempLong;
1368 if (endianIndex == -1) {
1369 writeBigEndianShort(out, shortOutput);
1370 } else {
1371 writeLittleEndianShort(out, shortOutput);
1372 }
1373 return 1;
1374 } else {
1375 ulong tempLong = (ulong)atoi(&word[quoteIndex + 1]);
1376 ushort ushortOutput = (ushort)tempLong;
1377 if (endianIndex == -1) {
1378 writeBigEndianUShort(out, ushortOutput);
1379 } else {
1380 writeLittleEndianUShort(out, ushortOutput);
1381 }
1382 return 1;
1383 }
1384 break;
1385 case 3:
1386 {
1387 if (signIndex != -1) {
1388 std::cerr << "Error on line " << lineNum << " at token: " << word
1389 << std::endl;
1390 std::cerr << "negative decimal numbers cannot be stored in 3 bytes"
1391 << std::endl;
1392 return 0;
1393 }
1394 ulong tempLong = (ulong)atoi(&word[quoteIndex + 1]);
1395 uchar byte1 = (uchar)((tempLong & 0x00ff0000) >> 16);
1396 uchar byte2 = (uchar)((tempLong & 0x0000ff00) >> 8);
1397 uchar byte3 = (uchar)((tempLong & 0x000000ff));
1398 if (endianIndex == -1) {
1399 out << byte1;
1400 out << byte2;
1401 out << byte3;
1402 } else {
1403 out << byte3;
1404 out << byte2;
1405 out << byte1;
1406 }
1407 return 1;
1408 }
1409 break;
1410 case 4:
1411 if (signIndex != -1) {
1412 long tempLong = atoi(&word[quoteIndex + 1]);
1413 if (endianIndex == -1) {
1414 writeBigEndianLong(out, tempLong);
1415 } else {
1416 writeLittleEndianLong(out, tempLong);
1417 }
1418 return 1;
1419 } else {
1420 ulong tempuLong = (ulong)atoi(&word[quoteIndex + 1]);
1421 if (endianIndex == -1) {
1422 writeBigEndianULong(out, tempuLong);
1423 } else {
1424 writeLittleEndianULong(out, tempuLong);
1425 }
1426 return 1;
1427 }
1428 break;
1429 default:
1430 std::cerr << "Error on line " << lineNum << " at token: " << word
1431 << std::endl;
1432 std::cerr << "invalid byte count specification for decimal number" << std::endl;
1433 return 0;
1434 }
1435
1436 return 1;
1437 }
1438
1439
1440
1441 //////////////////////////////
1442 //
1443 // Binasc::processHexWord -- interprets a hexadecimal word and converts into
1444 // its binary byte form.
1445 //
1446
processHexWord(std::ostream & out,const std::string & word,int lineNum)1447 int Binasc::processHexWord(std::ostream& out, const std::string& word,
1448 int lineNum) {
1449 int length = (int)word.size();
1450 uchar outputByte;
1451
1452 if (length > 2) {
1453 std::cerr << "Error on line " << lineNum << " at token: " << word << std::endl;
1454 std::cerr << "Size of hexadecimal number is too large. Max is ff." << std::endl;
1455 return 0;
1456 }
1457
1458 if (!isxdigit(word[0]) || (length == 2 && !isxdigit(word[1]))) {
1459 std::cerr << "Error on line " << lineNum << " at token: " << word << std::endl;
1460 std::cerr << "Invalid character in hexadecimal number." << std::endl;
1461 return 0;
1462 }
1463
1464 outputByte = (uchar)strtol(word.c_str(), (char**)NULL, 16);
1465 out << outputByte;
1466 return 1;
1467 }
1468
1469
1470
1471 //////////////////////////////
1472 //
1473 // Binasc::processStringWord -- interprets a binary word into
1474 // its constituent byte
1475 //
1476
processStringWord(std::ostream & out,const std::string & word,int)1477 int Binasc::processStringWord(std::ostream& out, const std::string& word,
1478 int /* lineNum */) {
1479 out << word;
1480 return 1;
1481 }
1482
1483
1484
1485 //////////////////////////////
1486 //
1487 // Binasc::processAsciiWord -- interprets a binary word into
1488 // its constituent byte
1489 //
1490
processAsciiWord(std::ostream & out,const std::string & word,int lineNum)1491 int Binasc::processAsciiWord(std::ostream& out, const std::string& word,
1492 int lineNum) {
1493 int length = (int)word.size();
1494 uchar outputByte;
1495
1496 if (word[0] != '+') {
1497 std::cerr << "Error on line " << lineNum << " at token: " << word << std::endl;
1498 std::cerr << "character byte must start with \'+\' sign: " << std::endl;
1499 return 0;
1500 }
1501
1502 if (length > 2) {
1503 std::cerr << "Error on line " << lineNum << " at token: " << word << std::endl;
1504 std::cerr << "character byte word is too long -- specify only one character"
1505 << std::endl;
1506 return 0;
1507 }
1508
1509 if (length == 2) {
1510 outputByte = (uchar)word[1];
1511 } else {
1512 outputByte = ' ';
1513 }
1514 out << outputByte;
1515 return 1;
1516 }
1517
1518
1519
1520 //////////////////////////////
1521 //
1522 // Binasc::processBinaryWord -- interprets a binary word into
1523 // its constituent byte
1524 //
1525
processBinaryWord(std::ostream & out,const std::string & word,int lineNum)1526 int Binasc::processBinaryWord(std::ostream& out, const std::string& word,
1527 int lineNum) {
1528 int length = (int)word.size(); // length of ascii binary number
1529 int commaIndex = -1; // index location of comma in number
1530 int leftDigits = -1; // number of digits to left of comma
1531 int rightDigits = -1; // number of digits to right of comma
1532 int i = 0;
1533
1534 // make sure that all characters are valid
1535 for (i=0; i<length; i++) {
1536 if (word [i] == ',') {
1537 if (commaIndex != -1) {
1538 std::cerr << "Error on line " << lineNum << " at token: " << word
1539 << std::endl;
1540 std::cerr << "extra comma in binary number" << std::endl;
1541 return 0;
1542 } else {
1543 commaIndex = i;
1544 }
1545 } else if (!(word[i] == '1' || word[i] == '0')) {
1546 std::cerr << "Error on line " << lineNum << " at token: " << word
1547 << std::endl;
1548 std::cerr << "Invalid character in binary number"
1549 " (character is " << word[i] <<")" << std::endl;
1550 return 0;
1551 }
1552 }
1553
1554 // comma cannot start or end number
1555 if (commaIndex == 0) {
1556 std::cerr << "Error on line " << lineNum << " at token: " << word
1557 << std::endl;
1558 std::cerr << "cannot start binary number with a comma" << std::endl;
1559 return 0;
1560 } else if (commaIndex == length - 1 ) {
1561 std::cerr << "Error on line " << lineNum << " at token: " << word
1562 << std::endl;
1563 std::cerr << "cannot end binary number with a comma" << std::endl;
1564 return 0;
1565 }
1566
1567 // figure out how many digits there are in binary number
1568 // number must be able to fit into one byte.
1569 if (commaIndex != -1) {
1570 leftDigits = commaIndex;
1571 rightDigits = length - commaIndex - 1;
1572 } else if (length > 8) {
1573 std::cerr << "Error on line " << lineNum << " at token: " << word
1574 << std::endl;
1575 std::cerr << "too many digits in binary number" << std::endl;
1576 return 0;
1577 }
1578 // if there is a comma, then there cannot be more than 4 digits on a side
1579 if (leftDigits > 4) {
1580 std::cerr << "Error on line " << lineNum << " at token: " << word
1581 << std::endl;
1582 std::cerr << "too many digits to left of comma" << std::endl;
1583 return 0;
1584 }
1585 if (rightDigits > 4) {
1586 std::cerr << "Error on line " << lineNum << " at token: " << word
1587 << std::endl;
1588 std::cerr << "too many digits to right of comma" << std::endl;
1589 return 0;
1590 }
1591
1592 // OK, we have a valid binary number, so calculate the byte
1593
1594 uchar output = 0;
1595
1596 // if no comma in binary number
1597 if (commaIndex == -1) {
1598 for (i=0; i<length; i++) {
1599 output = output << 1;
1600 output |= word[i] - '0';
1601 }
1602 }
1603 // if comma in binary number
1604 else {
1605 for (i=0; i<leftDigits; i++) {
1606 output = output << 1;
1607 output |= word[i] - '0';
1608 }
1609 output = output << (4-rightDigits);
1610 for (i=0+commaIndex+1; i<rightDigits+commaIndex+1; i++) {
1611 output = output << 1;
1612 output |= word[i] - '0';
1613 }
1614 }
1615
1616 // send the byte to the output
1617 out << output;
1618 return 1;
1619 }
1620
1621
1622
1623 //////////////////////////////
1624 //
1625 // Binasc::processVlvWord -- print a number in Variable Length Value form.
1626 // The int is split into 7-bit groupings, the MSB's that are zero
1627 // are dropped. A continuation bit is added as the MSbit to each
1628 // 7-bit grouping. The continuation bit is "1" if there is another
1629 // byte in the VLV; "0" for the last byte. VLVs are always
1630 // big-endian. The input word starts with the character "v" followed
1631 // without space by an integer.
1632 //
1633
processVlvWord(std::ostream & out,const std::string & word,int lineNum)1634 int Binasc::processVlvWord(std::ostream& out, const std::string& word,
1635 int lineNum) {
1636 if (word.size() < 2) {
1637 std::cerr << "Error on line: " << lineNum
1638 << ": 'v' needs to be followed immediately by a decimal digit"
1639 << std::endl;
1640 return 0;
1641 }
1642 if (!isdigit(word[1])) {
1643 std::cerr << "Error on line: " << lineNum
1644 << ": 'v' needs to be followed immediately by a decimal digit"
1645 << std::endl;
1646 return 0;
1647 }
1648 ulong value = atoi(&word[1]);
1649
1650 uchar byte[5];
1651 byte[0] = (value >> 28) & 0x7f;
1652 byte[1] = (value >> 21) & 0x7f;
1653 byte[2] = (value >> 14) & 0x7f;
1654 byte[3] = (value >> 7) & 0x7f;
1655 byte[4] = (value >> 0) & 0x7f;
1656
1657 int i;
1658 int flag = 0;
1659 for (i=0; i<4; i++) {
1660 if (byte[i] != 0) {
1661 flag = 1;
1662 }
1663 if (flag) {
1664 byte[i] |= 0x80;
1665 }
1666 }
1667
1668 for (i=0; i<5; i++) {
1669 if (byte[i] >= 0x80 || i == 4) {
1670 out << byte[i];
1671 }
1672 }
1673
1674 return 1;
1675 }
1676
1677
1678
1679 ////////////////////////////
1680 //
1681 // Binasc::processMidiTempoWord -- convert a floating point tempo into
1682 // a three-byte number of microseconds per beat per minute value.
1683 //
1684
processMidiTempoWord(std::ostream & out,const std::string & word,int lineNum)1685 int Binasc::processMidiTempoWord(std::ostream& out, const std::string& word,
1686 int lineNum) {
1687 if (word.size() < 2) {
1688 std::cerr << "Error on line: " << lineNum
1689 << ": 't' needs to be followed immediately by "
1690 << "a floating-point number" << std::endl;
1691 return 0;
1692 }
1693 if (!(isdigit(word[1]) || word[1] == '.' || word[1] == '-'
1694 || word[1] == '+')) {
1695 std::cerr << "Error on line: " << lineNum
1696 << ": 't' needs to be followed immediately by "
1697 << "a floating-point number" << std::endl;
1698 return 0;
1699 }
1700 double value = strtod(&word[1], NULL);
1701
1702 if (value < 0.0) {
1703 value = -value;
1704 }
1705
1706 int intval = int(60.0 * 1000000.0 / value + 0.5);
1707
1708 uchar byte0 = intval & 0xff;
1709 uchar byte1 = (intval >> 8) & 0xff;
1710 uchar byte2 = (intval >> 16) & 0xff;
1711 out << byte2 << byte1 << byte0;
1712 return 1;
1713 }
1714
1715
1716
1717 ////////////////////////////
1718 //
1719 // Binasc::processMidiPitchBendWord -- convert a floating point number in
1720 // the range from +1.0 to -1.0 into a 14-point integer with -1.0 mapping
1721 // to 0 and +1.0 mapping to 2^15-1. This integer will be packed into
1722 // two bytes, with the LSB coming first and containing the bottom
1723 // 7-bits of the 14-bit value, then the MSB coming second and containing
1724 // the top 7-bits of the 14-bit value.
1725
processMidiPitchBendWord(std::ostream & out,const std::string & word,int lineNum)1726 int Binasc::processMidiPitchBendWord(std::ostream& out, const std::string& word,
1727 int lineNum) {
1728 if (word.size() < 2) {
1729 std::cerr << "Error on line: " << lineNum
1730 << ": 'p' needs to be followed immediately by "
1731 << "a floating-point number" << std::endl;
1732 return 0;
1733 }
1734 if (!(isdigit(word[1]) || word[1] == '.' || word[1] == '-'
1735 || word[1] == '+')) {
1736 std::cerr << "Error on line: " << lineNum
1737 << ": 'p' needs to be followed immediately by "
1738 << "a floating-point number" << std::endl;
1739 return 0;
1740 }
1741 double value = strtod(&word[1], NULL);
1742
1743 if (value > 1.0) {
1744 value = 1.0;
1745 }
1746 if (value < -1.0) {
1747 value = -1.0;
1748 }
1749
1750 int intval = (int)(((1 << 13)-0.5) * (value + 1.0) + 0.5);
1751 uchar LSB = intval & 0x7f;
1752 uchar MSB = (intval >> 7) & 0x7f;
1753 out << LSB << MSB;
1754 return 1;
1755 }
1756
1757
1758
1759 ///////////////////////////////////////////////////////////////////////////
1760 //
1761 // Ordered byte writing functions --
1762 //
1763
1764 //////////////////////////////
1765 //
1766 // Binasc::writeLittleEndianUShort --
1767 //
1768
writeLittleEndianUShort(std::ostream & out,ushort value)1769 std::ostream& Binasc::writeLittleEndianUShort(std::ostream& out, ushort value) {
1770 union { char bytes[2]; ushort us; } data;
1771 data.us = value;
1772 out << data.bytes[0];
1773 out << data.bytes[1];
1774 return out;
1775 }
1776
1777
1778
1779 //////////////////////////////
1780 //
1781 // Binasc::writeBigEndianUShort --
1782 //
1783
writeBigEndianUShort(std::ostream & out,ushort value)1784 std::ostream& Binasc::writeBigEndianUShort(std::ostream& out, ushort value) {
1785 union { char bytes[2]; ushort us; } data;
1786 data.us = value;
1787 out << data.bytes[1];
1788 out << data.bytes[0];
1789 return out;
1790 }
1791
1792
1793
1794 //////////////////////////////
1795 //
1796 // Binasc::writeLittleEndianShort --
1797 //
1798
writeLittleEndianShort(std::ostream & out,short value)1799 std::ostream& Binasc::writeLittleEndianShort(std::ostream& out, short value) {
1800 union { char bytes[2]; short s; } data;
1801 data.s = value;
1802 out << data.bytes[0];
1803 out << data.bytes[1];
1804 return out;
1805 }
1806
1807
1808
1809 //////////////////////////////
1810 //
1811 // writeBigEndianShort --
1812 //
1813
writeBigEndianShort(std::ostream & out,short value)1814 std::ostream& Binasc::writeBigEndianShort(std::ostream& out, short value) {
1815 union { char bytes[2]; short s; } data;
1816 data.s = value;
1817 out << data.bytes[1];
1818 out << data.bytes[0];
1819 return out;
1820 }
1821
1822
1823
1824 //////////////////////////////
1825 //
1826 // Binasc::writeLittleEndianULong --
1827 //
1828
writeLittleEndianULong(std::ostream & out,ulong value)1829 std::ostream& Binasc::writeLittleEndianULong(std::ostream& out, ulong value) {
1830 union { char bytes[4]; ulong ul; } data;
1831 data.ul = value;
1832 out << data.bytes[0];
1833 out << data.bytes[1];
1834 out << data.bytes[2];
1835 out << data.bytes[3];
1836 return out;
1837 }
1838
1839
1840
1841 //////////////////////////////
1842 //
1843 // Binasc::writeBigEndianULong --
1844 //
1845
writeBigEndianULong(std::ostream & out,ulong value)1846 std::ostream& Binasc::writeBigEndianULong(std::ostream& out, ulong value) {
1847 union { char bytes[4]; long ul; } data;
1848 data.ul = value;
1849 out << data.bytes[3];
1850 out << data.bytes[2];
1851 out << data.bytes[1];
1852 out << data.bytes[0];
1853 return out;
1854 }
1855
1856
1857
1858 //////////////////////////////
1859 //
1860 // Binasc::writeLittleEndianLong --
1861 //
1862
writeLittleEndianLong(std::ostream & out,long value)1863 std::ostream& Binasc::writeLittleEndianLong(std::ostream& out, long value) {
1864 union { char bytes[4]; long l; } data;
1865 data.l = value;
1866 out << data.bytes[0];
1867 out << data.bytes[1];
1868 out << data.bytes[2];
1869 out << data.bytes[3];
1870 return out;
1871 }
1872
1873
1874
1875 //////////////////////////////
1876 //
1877 // Binasc::writeBigEndianLong --
1878 //
1879
writeBigEndianLong(std::ostream & out,long value)1880 std::ostream& Binasc::writeBigEndianLong(std::ostream& out, long value) {
1881 union { char bytes[4]; long l; } data;
1882 data.l = value;
1883 out << data.bytes[3];
1884 out << data.bytes[2];
1885 out << data.bytes[1];
1886 out << data.bytes[0];
1887 return out;
1888
1889 }
1890
1891
1892
1893 //////////////////////////////
1894 //
1895 // Binasc::writeBigEndianFloat --
1896 //
1897
writeBigEndianFloat(std::ostream & out,float value)1898 std::ostream& Binasc::writeBigEndianFloat(std::ostream& out, float value) {
1899 union { char bytes[4]; float f; } data;
1900 data.f = value;
1901 out << data.bytes[3];
1902 out << data.bytes[2];
1903 out << data.bytes[1];
1904 out << data.bytes[0];
1905 return out;
1906 }
1907
1908
1909
1910 //////////////////////////////
1911 //
1912 // Binasc::writeLittleEndianFloat --
1913 //
1914
writeLittleEndianFloat(std::ostream & out,float value)1915 std::ostream& Binasc::writeLittleEndianFloat(std::ostream& out, float value) {
1916 union { char bytes[4]; float f; } data;
1917 data.f = value;
1918 out << data.bytes[0];
1919 out << data.bytes[1];
1920 out << data.bytes[2];
1921 out << data.bytes[3];
1922 return out;
1923 }
1924
1925
1926
1927 //////////////////////////////
1928 //
1929 // Binasc::writeBigEndianDouble --
1930 //
1931
writeBigEndianDouble(std::ostream & out,double value)1932 std::ostream& Binasc::writeBigEndianDouble(std::ostream& out, double value) {
1933 union { char bytes[8]; double d; } data;
1934 data.d = value;
1935 out << data.bytes[7];
1936 out << data.bytes[6];
1937 out << data.bytes[5];
1938 out << data.bytes[4];
1939 out << data.bytes[3];
1940 out << data.bytes[2];
1941 out << data.bytes[1];
1942 out << data.bytes[0];
1943 return out;
1944 }
1945
1946
1947
1948 //////////////////////////////
1949 //
1950 // Binasc::writeLittleEndianDouble --
1951 //
1952
writeLittleEndianDouble(std::ostream & out,double value)1953 std::ostream& Binasc::writeLittleEndianDouble(std::ostream& out, double value) {
1954 union { char bytes[8]; double d; } data;
1955 data.d = value;
1956 out << data.bytes[0];
1957 out << data.bytes[1];
1958 out << data.bytes[2];
1959 out << data.bytes[3];
1960 out << data.bytes[4];
1961 out << data.bytes[5];
1962 out << data.bytes[6];
1963 out << data.bytes[7];
1964 return out;
1965 }
1966
1967
1968 } // end namespace smf
1969
1970
1971
1972