//
// Programmer:    Tom M. <tom.mbrt@gmail.com>
// Creation Date: Sat Nov 05 14:51:00 PST 2016
// Last Modified: Sat Nov 05 14:51:00 PST 2016
// Filename:      tools/mid2beep.cpp
// URL:           https://github.com/craigsapp/midifile/blob/master/tools/midi2beep.cpp
// Syntax:        C++11
// vim:           ts=3
//
// Description:   Linux-only: Play a monophonic midi file on the PC Speaker (i.e. the
//                midifile contains only a single track on a single channel playing
//                a single note at a time).
//

#include "MidiFile.h"
#include "Options.h"

#include <cmath>
#include <iostream>
#include <signal.h>
#include <unistd.h> // usleep()

#ifdef __APPLE__
	#include <sys/uio.h>
#else
	#include <sys/io.h>
#endif

using namespace std;
using namespace smf;


void beepOff   (int);
void beep      (int fre, int usDuration);


///////////////////////////////////////////////////////////////////////////

int main(int argc, char** argv) {
	signal(SIGINT, beepOff);

	Options options;
	options.process(argc, argv);
	MidiFile midifile;
	if (options.getArgCount() == 1) {
		midifile.read(options.getArg(1));
	}
	else {
		cout << "usage: " << argv[0] << " file.mid" << endl;
		return -1;
	}

	int k= iopl(3);

	printf("\n Result iopl: %d \n", k);
	if (k<0) {
		cerr << " iopl() " << endl;
		return k;
	}

	midifile.linkNotePairs();
	midifile.joinTracks();
	midifile.doTimeAnalysis();

	midifile.absoluteTicks();

	double lastNoteFinished = 0.0;
	for (int track=0; track < midifile.getTrackCount(); track++) {
		for (int i=0; i<midifile[track].size(); i++) {
			MidiEvent* mev = &midifile[track][i];
			if (!mev->isNoteOn() || mev->getLinkedEvent() == NULL) {
				continue;
			}

			// pause, silence
			int silence = static_cast<int>((midifile.getTimeInSeconds(mev->tick) - lastNoteFinished) * 1000 * 1000);
			if(silence >0) {
				usleep(silence);
			}

			double duration = mev->getDurationInSeconds();

			int halfTonesFromA4 = mev->getKeyNumber() - 69; // 69 == A4 == 440Hz
			int frq = 440 * pow(2, halfTonesFromA4/12.0);

			// play note
			beep(frq, static_cast<int>(duration*1000*1000));

			MidiEvent* off = mev->getLinkedEvent();
			lastNoteFinished = midifile.getTimeInSeconds(off->tick);

		}
	}

	return 0;
}


///////////////////////////////////////////////////////////////////////////

//////////////////////////////
//
// beepOff --

void beepOff(int) {
	int r = inb(0x61);
	outb(r|3, 0x61);
	outb(r & 0xFC, 0x61);

	exit(0);
}



//////////////////////////////
//
// beep --
//

void beep(int fre, int usDuration) {
	if (fre > 0) {
		int ff = 1193180/fre;
		outb( 0xB6,            0x43);
		outb( ff & 0xff,       0x42);
		outb((ff >> 8) & 0xff, 0x42);
	}

	int r = inb(0x61);
	if(fre > 0) outb(r|3, 0x61);
	usleep(usDuration);
	outb(r & 0xFC, 0x61);
}



