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