# GNU Solfege - ear training for GNOME
# Copyright (C) 2000, 2001, 2002, 2003, 2004  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 rat

class Track:
    TEMPO = 'tempo'
    NOTE_ON = 'note-on'
    NOTE_OFF = 'note-off'
    NOTELEN_TIME = 'notelen-time'
    BENDER = 'bender'
    SET_PATCH = 'program-change'
    def txtdump(self):
        p = rat.Rat(0, 1)
        for event in self.m_v:
            if event[0] == self.NOTELEN_TIME:
                p = p + event[1]
            print event
    def __init__(self):
        self.m_v = []
    def start_note(self, chn, pitch, vel):
        assert 0 <= chn < 16
        self.m_v.append([self.NOTE_ON, chn, int(pitch), vel])
    def stop_note(self, chn, pitch, vel):
        assert 0 <= chn < 16
        self.m_v.append([self.NOTE_OFF, chn, int(pitch), vel])
    def notelen_time(self, notelen):
        if isinstance(notelen, int):
            self.m_v.append([self.NOTELEN_TIME, rat.Rat(1, notelen)])
        else:
            assert isinstance(notelen, rat.Rat)
            self.m_v.append([self.NOTELEN_TIME, 1/notelen])
    def note(self, notelen, chn, pitch, vel):
        assert 0 <= chn < 16
        self.start_note(chn, pitch, vel)
        self.notelen_time(notelen)
        self.stop_note(chn, pitch, vel)
    def set_patch(self, chn, prg):
        assert 0 <= chn < 16
        self.m_v.append([self.SET_PATCH, chn, prg])
    def prepend_patch(self, chn, prg):
        assert 0 <= chn < 16
        self.m_v.insert(0, [self.SET_PATCH, chn, prg])
    def set_bpm(self, bpm, notelen=4):
        self.m_v.append([self.TEMPO, bpm, notelen])
    def prepend_bpm(self, bpm, notelen=4):
        self.m_v.insert(0, [self.TEMPO, bpm, notelen])
    def bender(self, chn, value):
        "value >= 0"
        self.m_v.append([self.BENDER, chn, value])
    def merge_with(self, B):
        D = {}
        for track in [self, B]:
            pos = rat.Rat(0, 1)
            for event in track.m_v:
                if event[0] == Track.NOTELEN_TIME:
                    pos = pos + event[1]
                else:
                    if pos not in D:
                        D[pos] = []
                    D[pos].append(event)
        kv = D.keys()
        kv.sort()
        self.m_v = []
        for x in range(len(kv)-1):
            for event in D[kv[x]]:
                self.m_v.append(event)
            self.m_v.append((Track.NOTELEN_TIME, kv[x+1]-kv[x]))
        for event in D[kv[-1]]:
            self.m_v.append(event)
    def replace_note(self, old, new, chan):
        assert isinstance(old, int)
        assert isinstance(new, int)
        assert isinstance(chan, int)
        for event in self.m_v:
            if event[0] in (Track.NOTE_ON, Track.NOTE_OFF) \
                    and event[1] == chan and event[2] == old:
                event[2] = new

