# GNU Solfege - ear training for GNOME
# Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005 Tom Cato Amundsen
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

import sys, os, tempfile, signal
import mfutils

def ms_win_kill(pid):
    import win32api
    handle = win32api.OpenProcess(1, 0, pid)
    return (0 != win32api.TerminateProcess(handle, 0))

class MidiFileSynth:
    NUM_CHANNELS = 16
    def __init__(self, cmd, verbose_init):
        self.m_type_major = "Midifile"
        self.m_cmd = cmd
        self.m_tmpfilename = tempfile.mktemp()
        self.grab_tmp_file(self.m_tmpfilename)
        self.m_pidfile = tempfile.mktemp()
        self.grab_tmp_file(self.m_pidfile)
        if sys.platform == 'win32':
            self.play_track = self._play_track_system
        else:
            self.play_track = self._play_track_fork_execl
        if verbose_init:
            print "Solfege will use an external midiplayer program."
            print "cmdline:", self.m_cmd
            print "tmpfile:", self.m_tmpfilename
        self.__child_pid = None
    def grab_tmp_file(self, fn):
        try:
            # create the file so nobody else takes it
            fd = os.open(fn, os.O_CREAT|os.O_EXCL|os.O_TRUNC|os.O_RDWR, 0600)
            os.close(fd)
        except IOError, e:
            print "trouble creating tmp file. This should *not* happen."
            print "Please complaint to bug-solfege@gnu.org"
            print "Exception:", e
            raise "Shit"
    def close(self):
        os.remove(self.m_tmpfilename)
    def join_cmd_and_filename(self, cmd, filename):
        if "%s" in cmd:
            return cmd % filename
        else:
            return " ".join((cmd, filename))
    def _play_track_system(self, track):
        track.prepend_bpm(60)
        self.create_midifile(track)
        os.system(self.join_cmd_and_filename(self.m_cmd, self.m_tmpfilename))
        # spawnl(os.P_DETACH, 'c:\\python20\\python', 'python', 'script.py')
        # #P_DETACH'ed can be killed
    def _play_track_fork_execl(self, track):
        track.prepend_bpm(60)
        self.create_midifile(track)
        if not os.path.isfile(self.m_cmd.split()[0]):
            print >> sys.stderr, "The program '%s' does not exist." % self.m_cmd.split()[0]
            print >> sys.stderr, "Please check the sound setup in the preferences window."
            return
        if self.__child_pid:
            try:
                os.kill(self.__child_pid, signal.SIGKILL)
            except OSError:
                pass
            os.wait()
            self.__child_pid = None
        pid = os.fork()
        if pid == 0:
            v = self.join_cmd_and_filename(self.m_cmd, self.m_tmpfilename).split(" ")
            v = v[:1] + [""] + v[1:]
            try:
                os.execlp(*v)
            except OSError, x:
                print "OSError while trying to invoke external midiplayer:", x
                print "Please check your sound setup!"
                os._exit(-1)
        else:
            self.__child_pid = pid
    def create_midifile(self, track):
        v = []
        notelen = 0
        for e in track.m_v:
            if e[0] == track.TEMPO:
                v = v + mfutils.mf_tempo(e[1] * 4 / e[2])
            elif e[0] == track.NOTELEN_TIME:
                notelen = e[1]
            elif e[0] == track.NOTE_ON:
                v = v + mfutils.mf_note_on(int(1000 * notelen), e[1], e[2], e[3])
                notelen = 0
            elif e[0] == track.NOTE_OFF:
                v = v + mfutils.mf_note_off(int(1000 * notelen), e[1], e[2], e[3])
                notelen = 0
            elif e[0] == track.SET_PATCH:
                v = v + mfutils.mf_program_change(e[1], e[2])
            elif e[0] == track.BENDER:
                print "FIXME todo: seq_bender for play_with_drvmidi"
                #m.seq_bender(DEV, e[1], e[2])
            else:
                raise "Corrupt track error"
        f = open(self.m_tmpfilename, "w")
        mfutils.MThd(f)
        f.write("MTrk")
        mfutils.write_int32(f, len(v)+4)
        v = v + mfutils.mf_end_of_track()
        mfutils.write_vect(f, v)
        f.close()
    def stop(self):
        if sys.platform != 'win32':
            if self.__child_pid:
                try:
                    os.kill(self.__child_pid, signal.SIGKILL)
                except OSError:
                    pass
                os.wait()
                self.__child_pid = None
