Ejemplo n.º 1
0
 def load_events(self, from_file=None, from_events=None):
   if from_events:
     self.events = from_events
     self.stream = RendererMidiStream(events)
   elif from_file:
     if not os.path.exists(from_file):
       self.log_err("Could not find filename {}".format(from_file));
       sys.exit(1);
     self.events = self._csv_decompile(from_file);
     self.stream = RendererMidiStream(self.events);
   else:
     self.log_warn("must specify filename or events array");
     self.log_warn("events unchanged");
     return
Ejemplo n.º 2
0
class RendererMidiFile(RendererMidi):
  def __init__(self, filename=None, midicsv_path=MIDICSV_PATH, csvmidi_path=CSVMIDI_PATH, **kwargs):
    super(RendererMidiFile, self).__init__(**kwargs) 
    for filepath in [ midicsv_path, csvmidi_path ]:
      if not os.path.exists(filepath) and not find_executable(filepath):
        self.log_err("Could not find path {}".format(filepath));
        sys.exit(1)
    self._midicsv_path = midicsv_path 
    self._csvmidi_path = csvmidi_path
    self._filename = filename or self.filename_new("midi");
    self.events = []
    self.stream = None

  def filename_set(self, filename):
    self._filename = filename
  
  def filename_get(self):
    return self._filename

  def _csvmidi_cmd(self, inputfile=None, outputfile=None):
    if not inputfile or not outputfile:
      self.log_err("Bad csvmidi command. Must supply both inputfile and outputfile");
      sys.exit(1)
    try:
      subprocess.call([ self._csvmidi_path, inputfile, outputfile ]);
    except Exception as e:
      self.log_err(str(e))
      self.log_err("Could not call csvmidi");
      sys.exit(1);

  def _midicsv_cmd(self, inputfile=None):
    if not inputfile:
      self.log_err("Bad midicsv command. Must supply inputfile");
      sys.exit(1)
    try:
      return subprocess.Popen((self._midicsv_path, inputfile), stdout=subprocess.PIPE).communicate()[0]
    except Exception as e:
      self.log_err(str(e))
      self.log_err("Could not call midicsv");
      sys.exit(1);
  
  def load_events(self, from_file=None, from_events=None):
    if from_events:
      self.events = from_events
      self.stream = RendererMidiStream(events)
    elif from_file:
      if not os.path.exists(from_file):
        self.log_err("Could not find filename {}".format(from_file));
        sys.exit(1);
      self.events = self._csv_decompile(from_file);
      self.stream = RendererMidiStream(self.events);
    else:
      self.log_warn("must specify filename or events array");
      self.log_warn("events unchanged");
      return

  def midifile_out(self, filename=None):      
    return self._csv_recompile(filename=filename);

  def _csv_decompile(self, midi_filename):
    s = self._midicsv_cmd(inputfile=midi_filename);
    lines = s.split("\n");
    lines = map(lambda x: self._csv_line_to_obj(x), lines) 
    lines = filter(lambda x: x, lines)
    return lines
    
  def _csv_line_to_obj(self, line):
    #{{{ line parsing
    parts = line.split(",")
    parts = map(lambda x: x.replace(" ",""), parts)

    def _str_to_int(s):
      if re.findall(r'^[0-9]+$', s):
        return int(s)
      return s
    parts = map(lambda x: _str_to_int(x), parts);
    
    if len(parts) < 2:
      return {}
    data = { 
      "Track": parts[0],
      "Time" : parts[1],
      "Type": parts[2]
    }
    if data.get("Type") == "Header":
      data["format"] = parts[3]
      data["nTracks"] = parts[4]
      data["division"] = parts[5]
    elif data.get("Type") in [ "End_of_file", "Start_track" , "End_track" ]:
      pass
    elif data.get("Type") in [ 
        "Title_t", "Copyright_t", "Instrument_name_t", 
        "Marker_t", "Cue_point_t", "Lyric_t", "Text_t" 
      ]:
      data["Text"] = parts[3]
    elif data.get("Type") in [ "Sequence_number", "MIDI_port", "Channel_prefix" ]:
      data["Number"] = parts[3]
    elif data.get("Type") == "Time_signature":
      data["Num"] = parts[3]
      data["Denom"] = parts[4]
      data["Click"] = parts[5]
      data["NotesQ"] = parts[6]
    elif data.get("Type") == "Key_signature" :
      data["Key"] = parts[3]
      data["Major/Minor"] = parts[4]
    elif data.get("Type") == "Tempo":
      data["Number"] = parts[3]
    elif data.get("Type") == "SMPTE_offset":
      data["Hour"] = parts[3]
      data["Minute"] = parts[4]
      data["Second"] = parts[5]
      data["Frame"] = parts[6]
      data["FracFrame"] = parts[7]
    elif data.get("Type") == "Sequencer_specific":
      data["Length"] = parts[3]
      data["Data"] = parts[4:]
    elif data.get("Type") == "Unknown_meta_event":
      data["Type"] = parts[3]
      data["Length"] = parts[4]
      data["Data"] = parts[5:]

    elif data.get("Type") == "Note_on_c":
      data["Channel"] = parts[3]
      data["Note"] = parts[4]
      data["Velocity"] = parts[5]
    elif data.get("Type") == "Note_off_c":
      data["Channel"] = parts[3]
      data["Note"] = parts[4]
      data["Velocity"] = parts[4]
    elif data.get("Type") == "Control_c":
      data["Channel"] = parts[3]
      data["Control_Num"] = parts[4]
      data["Value"] = parts[5]
    elif data.get("Type") in [ "Channel_aftertouch_c", "Pitch_bend_c" ] :
      data["Channel"] = parts[3]
      data["Value"] = parts[4]
    elif data.get("Type") == "Program_c" : 
      data["Channel"] = parts[3]
      data["Program_num"] = parts[4]
    elif data.get("Type") == "Poly_aftertouch_c" : 
      data["Channel"] = parts[3]
      data["Note"] = parts[4]
      data["Value"] = parts[5]

    elif data.get("Type") in [ "System_exclusive" ] :
      data["Length"] = parts[3]
      data["Data"] = parts[4:]
    #}}}
    return data

  def _csv_recompile(self, filename=None):
    _output_array = [];
    #{{{ line parsing 
    for event in self.events:
      event_list = [
        event.get("Track"),
        event.get("Time"),
        event.get("Type")
      ]
      if event.get("Type") == "Header":
        event_list += [ 
          event.get("format"),  
          event.get("nTracks"),
          event.get("division"),
        ]
      elif event.get("Type") in [
        "Title_t", "Copyright_t", "Instrument_name_t", 
        "Marker_t", "Cue_point_t", "Lyric_t", "Text_t" 
      ]:
        event_list.append(event.get("Text"))
      elif event.get("Type") in [ "Sequence_number", "MIDI_port", "Channel_prefix" ]:
        event_list.append(event.get("Number"))
      elif event.get("Type") == "Time_signature":
        event_list += [
          event.get("Num"),
          event.get("Denom"),
          event.get("Click"),
          event.get("NotesQ"),
        ]
      elif event.get("Type") == "Key_signature" :
        event_list += [
          event.get("Key"),
          event.get("Major/Minor"),
        ]
      elif event.get("Type") == "Tempo":
        event_list.append(event.get("Number"))
      elif event.get("Type") == "SMPTE_offset":
        event_list += [
          event.get("Hour"),
          event.get("Minute"),
          event.get("Second"),
          event.get("Frame"),
          event.get("FracFrame"),
        ]
      elif event.get("Type") == "Sequencer_specific":
        event_list += [
          event.get("Length"),
          event.get("Data"),
        ]
      elif event.get("Type") == "Unknown_meta_event":
        event_list += [
          event.get("Type"),
          event.get("Length"),
          event.get("Data"),
        ]
      elif event.get("Type") == "Note_on_c":
        event_list += [
          event.get("Channel"),
          event.get("Note"),
          event.get("Velocity"),
        ]
      elif event.get("Type") == "Note_off_c":
        event_list += [
          event.get("Channel"),
          event.get("Note"),
          event.get("Velocity"),
        ]
      elif event.get("Type") == "Control_c":
        event_list += [
          event.get("Channel"),
          event.get("Control_Num"),
          event.get("Value"),
        ]
      elif event.get("Type") in [ "Channel_aftertouch_c", "Pitch_bend_c" ] :
        event_list += [
          event.get("Channel"),
          event.get("Value"),
        ]
      elif event.get("Type") == "Program_c" :
        event_list += [
          event.get("Channel"),
          event.get("Program_num"),
        ]
      elif event.get("Type") == "Poly_aftertouch_c" :
        event_list += [
          event.get("Channel"),
          event.get("Note"),
          event.get("Value"),
        ]

      elif event.get("Type") in [ "System_exclusive" ] :
        event_list += [
          event.get("Length"),
          event.get("Data"),
        ]
      _output_array.append(", ".join(map(lambda n: str(n), event_list)));
    #}}}
    _output_str = "\n".join(_output_array); 
    _output_csv = self.filename_temporary_new("csv");
    try:
      f = open(_output_csv, "w");
      f.write(_output_str);
      f.close()
    except Exception as e:
      self.log_err(str(e));
      self.log_err("Could not write to file {}".format(_output_csv));
      sys.exit(1)
    if self._verbose:
      self.log_info("Made temporary csv file {}".format(_output_csv))
    self._csvmidi_cmd(inputfile=_output_csv, outputfile=self._filename)
    if self._verbose:
      self.log_info("Removing {}".format(_output_csv))
    try:
      os.unlink(_output_csv)
    except Exception as e:
      self.log_warn("Unable to remove {}".format(_output_csv))
      if self._verbose: self.log_warn(str(e))
    return self._filename;

  def remove_track(self, track_number):
    new_events = []
    for idx in range(len(self.events)):
      _track_no = self.events[idx].get("Track")
      if self.events[idx].get("Type") == "Header":
        event = self.events[idx]
        event["nTracks"] -= 1
        new_events.append(event)
      elif _track_no == track_number:
        continue;
      elif _track_no > track_number:
        event = self.events[idx]
        event["Track"] = _track_no - 1 
        new_events.append(event)
      else:
        new_events.append(self.events[idx])
    self.load_events(from_events=new_events)

  def _find_channels(self):
    if self.stream:
      return self.stream.find_channels();
    else:
      return []