text: # -*- coding: ISO-8859-1 -*-
  
  # std library
  from struct import unpack
  
  # custom
  from midi.DataTypeConverters import readBew, readVar, varLen, toBytes
  
  # uhh I don't really like this, but there are so many constants to 
  # import otherwise
  from midi.constants import *
  
  
  class EventDispatcher:
  
  
      def __init__(self, outstream):
          
          """
          
          The event dispatcher generates events on the outstream.
          
          """
          
          # internal values, don't mess with 'em directly
          self.outstream = outstream
          
          # public flags
  
          # A note_on with a velocity of 0x00 is actually the same as a 
          # note_off with a velocity of 0x40. When 
          # "convert_zero_velocity" is set, the zero velocity note_on's 
          # automatically gets converted into note_off's. This is a less 
          # suprising behaviour for those that are not into the intimate 
          # details of the midi spec.
          self.convert_zero_velocity = 1
          
          # If dispatch_continuos_controllers is true, continuos 
          # controllers gets dispatched to their defined handlers. Else 
          # they just trigger the "continuous_controller" event handler.
          self.dispatch_continuos_controllers = 1 # NOT IMPLEMENTED YET
          
          # If dispatch_meta_events is true, meta events get's dispatched 
          # to their defined events. Else they all they trigger the 
          # "meta_event" handler.
          self.dispatch_meta_events = 1
  
  
  
      def header(self, format, nTracks, division):
          "Triggers the header event"
          self.outstream.header(format, nTracks, division)
  
  
      def start_of_track(self, current_track):
          "Triggers the start of track event"
          
          # I do this twice so that users can overwrite the 
          # start_of_track event handler without worrying whether the 
          # track number is updated correctly.
          self.outstream.set_current_track(current_track)
          self.outstream.start_of_track(current_track)
          
      
      def sysex_event(self, data):
          "Dispatcher for sysex events"
          self.outstream.sysex_event(data)
      
      
      def eof(self):
          "End of file!"
          self.outstream.eof()
  
  
      def update_time(self, new_time=0, relative=1):
          "Updates relative/absolute time."
          self.outstream.update_time(new_time, relative)
          
          
      def reset_time(self):
          "Updates relative/absolute time."
          self.outstream.reset_time()
          
          
      # Event dispatchers for similar types of events
      
      
      def channel_messages(self, hi_nible, channel, data):
      
          "Dispatches channel messages"
          
          stream = self.outstream
          data = toBytes(data)
          
          if (NOTE_ON & 0xF0) == hi_nible:
              note, velocity = data
              # note_on with velocity 0x00 are same as note 
              # off with velocity 0x40 according to spec!
              if velocity==0 and self.convert_zero_velocity:
                  stream.note_off(channel, note, 0x40)
              else:
                  stream.note_on(channel, note, velocity)
          
          elif (NOTE_OFF & 0xF0) == hi_nible:
              note, velocity = data
              stream.note_off(channel, note, velocity)
          
          elif (AFTERTOUCH & 0xF0) == hi_nible:
              note, velocity = data
              stream.aftertouch(channel, note, velocity)
          
          elif (CONTINUOUS_CONTROLLER & 0xF0) == hi_nible:
              controller, value = data
              # A lot of the cc's are defined, so we trigger those directly
              if self.dispatch_continuos_controllers:
                  self.continuous_controllers(channel, controller, value)
              else:
                  stream.continuous_controller(channel, controller, value)
              
          elif (PATCH_CHANGE & 0xF0) == hi_nible:
              program = data[0]
              stream.patch_change(channel, program)
              
          elif (CHANNEL_PRESSURE & 0xF0) == hi_nible:
              pressure = data[0]
              stream.channel_pressure(channel, pressure)
              
          elif (PITCH_BEND & 0xF0) == hi_nible:
              hibyte, lobyte = data
              value = (hibyte<<7) + lobyte
              stream.pitch_bend(channel, value)
  
          else:
  
              raise ValueError('Illegal channel message!')
  
  
  
      def continuous_controllers(self, channel, controller, value):
      
          "Dispatches channel messages"
  
          stream = self.outstream
          
          # I am not really shure if I ought to dispatch continuous controllers
          # There's so many of them that it can clutter up the OutStream 
          # classes.
          
          # So I just trigger the default event handler
          stream.continuous_controller(channel, controller, value)
  
  
  
      def system_commons(self, common_type, common_data):
      
          "Dispatches system common messages"
          
          stream = self.outstream
          
          # MTC Midi time code Quarter value
          if common_type == MTC:
              data = readBew(common_data)
              msg_type = (data & 0x07) >> 4
              values = (data & 0x0F)
              stream.midi_time_code(msg_type, values)
          
          elif common_type == SONG_POSITION_POINTER:
              hibyte, lobyte = toBytes(common_data)
              value = (hibyte<<7) + lobyte
              stream.song_position_pointer(value)
  
          elif common_type == SONG_SELECT:
              data = readBew(common_data)
              stream.song_select(data)
  
          elif common_type == TUNING_REQUEST:
              # no data then
              stream.tuning_request(time=None)
  
  
  
      def meta_event(self, meta_type, data):
          
          "Dispatches meta events"
          
          stream = self.outstream
          
          # SEQUENCE_NUMBER = 0x00 (00 02 ss ss (seq-number))
          if meta_type == SEQUENCE_NUMBER:
              number = readBew(data)
              stream.sequence_number(number)
          
          # TEXT = 0x01 (01 len text...)
          elif meta_type == TEXT:
              stream.text(data)
          
          # COPYRIGHT = 0x02 (02 len text...)
          elif meta_type == COPYRIGHT:
              stream.copyright(data)
          
          # SEQUENCE_NAME = 0x03 (03 len text...)
          elif meta_type == SEQUENCE_NAME:
              stream.sequence_name(data)
          
          # INSTRUMENT_NAME = 0x04 (04 len text...)
          elif meta_type == INSTRUMENT_NAME:
              stream.instrument_name(data)
          
          # LYRIC = 0x05 (05 len text...)
          elif meta_type == LYRIC:
              stream.lyric(data)
          
          # MARKER = 0x06 (06 len text...)
          elif meta_type == MARKER:
              stream.marker(data)
          
          # CUEPOINT = 0x07 (07 len text...)
          elif meta_type == CUEPOINT:
              stream.cuepoint(data)
          
          # PROGRAM_NAME = 0x08 (05 len text...)
          elif meta_type == PROGRAM_NAME:
              stream.program_name(data)
          
          # DEVICE_NAME = 0x09 (09 len text...)
          elif meta_type == DEVICE_NAME:
              stream.device_name(data)
          
          # MIDI_CH_PREFIX = 0x20 (20 01 channel)
          elif meta_type == MIDI_CH_PREFIX:
              channel = readBew(data)
              stream.midi_ch_prefix(channel)
          
          # MIDI_PORT  = 0x21 (21 01 port (legacy stuff))
          elif meta_type == MIDI_PORT:
              port = readBew(data)
              stream.midi_port(port)
          
          # END_OFF_TRACK = 0x2F (2F 00)
          elif meta_type == END_OF_TRACK:
              stream.end_of_track()
          
          # TEMPO = 0x51 (51 03 tt tt tt (tempo in us/quarternote))
          elif meta_type == TEMPO:
              b1, b2, b3 = toBytes(data)
              # uses 3 bytes to represent time between quarter 
              # notes in microseconds
              stream.tempo((b1<<16) + (b2<<8) + b3)
          
          # SMTP_OFFSET = 0x54 (54 05 hh mm ss ff xx)
          elif meta_type == SMTP_OFFSET:
              hour, minute, second, frame, framePart = toBytes(data)
              stream.smtp_offset(
                      hour, minute, second, frame, framePart)
          
          # TIME_SIGNATURE = 0x58 (58 04 nn dd cc bb)
          elif meta_type == TIME_SIGNATURE:
              nn, dd, cc, bb = toBytes(data)
              stream.time_signature(nn, dd, cc, bb)
          
          # KEY_SIGNATURE = 0x59 (59 02 sf mi)
          elif meta_type == KEY_SIGNATURE:
              sf, mi = toBytes(data)
              stream.key_signature(sf, mi)
          
          # SPECIFIC = 0x7F (Sequencer specific event)
          elif meta_type == SPECIFIC:
              meta_data = toBytes(data)
              stream.sequencer_specific(meta_data)
          
          # Handles any undefined meta events
          else: # undefined meta type
              meta_data = toBytes(data)
              stream.meta_event(meta_type, meta_data)
  
  
  
  
  
  if __name__ == '__main__':
  
  
      from MidiToText import MidiToText
      
      outstream = MidiToText()
      dispatcher = EventDispatcher(outstream)
      dispatcher.channel_messages(NOTE_ON, 0x00, '\x40\x40')
  