Example #1
0
    def separate_track(original_track, num_track=1, default_tempo=100):
        set_tempo_event = SetTempoEvent()
        set_tempo_event.set_bpm(default_tempo)
        original_track.make_ticks_abs()
        channel_set = set()
        for event in original_track:
            if type(event) is NoteOnEvent or type(event) is NoteOffEvent:
                channel_set.add(event.channel)
            if type(event) is SetTempoEvent:
                set_tempo_event = event

        channel_track_map = {}
        index = 0
        for channel in channel_set:
            channel_track_map[channel] = index
            index += 1

        tracks = []
        for i in range(max(num_track, len(channel_set))):
            tracks.append(
                Track(events=[MIDI.copy_event(set_tempo_event)],
                      tick_relative=False))

        for event in original_track:
            if type(event) is NoteOnEvent or type(event) is NoteOffEvent:
                copied_event = MIDI.copy_event(event)
                tracks[channel_track_map[copied_event.channel]].append(
                    copied_event)

        for track in tracks:
            track.sort()
            track.make_ticks_rel()

        original_track.make_ticks_rel()
        return tracks
Example #2
0
 def to_midi(self):
     """
     Constructs a L{MIDI EventStream<midi.EventStream>} from the 
     data in this stream.
     This can then be output to a file to be played.
     
     Note that TPCNotes will be output as normal MIDI notes. We 
     can't do anything of the clever tuning stuff that we can do 
     with tonal space coordinates, since we'd need to do a further 
     step of analysis to work out the fully specified TS point from 
     the pitch class.
     
     """
     tempo = 120
     
     from midi import EventStream, NoteOffEvent, NoteOnEvent, SetTempoEvent
     mid = EventStream()
     mid.add_track()
     # Set the tempo first at the beginning
     temp = SetTempoEvent()
     temp.tempo = tempo
     temp.tick = 0
     mid.add_event(temp)
     # Work out how many ticks there are in a millisecond
     ticks_per_ms = float(mid.resolution) * tempo / 60000
     # Create midi events for every event in our stream
     for ev in self.events:
         if isinstance(ev, TPCNoteEvent):
             # Create note-on and note-off events
             note = ev.note
             
             noteon = NoteOnEvent()
             noteon.pitch = note
             noteon.tick = int(ev.start * ticks_per_ms)
             noteon.velocity = 100
             mid.add_event(noteon)
             
             noteoff = NoteOffEvent()
             noteoff.pitch = note
             noteoff.tick = int(ev.end * ticks_per_ms)
             noteoff.velocity = 100
             mid.add_event(noteoff)
         elif isinstance(ev, (ChordEvent,BeatEvent)):
             # These events don't affect the midi data
             continue
         else:
             raise TypeError, "event type %s not recognised by "\
                 "MIDI converter." % type(ev).__name__
     return mid
Example #3
0
    def to_midi(self):
        """
        Constructs a L{MIDI EventStream<midi.EventStream>} from the 
        data in this stream.
        This can then be output to a file to be played.
        
        Note that TPCNotes will be output as normal MIDI notes. We 
        can't do anything of the clever tuning stuff that we can do 
        with tonal space coordinates, since we'd need to do a further 
        step of analysis to work out the fully specified TS point from 
        the pitch class.
        
        """
        tempo = 120

        from midi import EventStream, NoteOffEvent, NoteOnEvent, SetTempoEvent
        mid = EventStream()
        mid.add_track()
        # Set the tempo first at the beginning
        temp = SetTempoEvent()
        temp.tempo = tempo
        temp.tick = 0
        mid.add_event(temp)
        # Work out how many ticks there are in a millisecond
        ticks_per_ms = float(mid.resolution) * tempo / 60000
        # Create midi events for every event in our stream
        for ev in self.events:
            if isinstance(ev, TPCNoteEvent):
                # Create note-on and note-off events
                note = ev.note

                noteon = NoteOnEvent()
                noteon.pitch = note
                noteon.tick = int(ev.start * ticks_per_ms)
                noteon.velocity = 100
                mid.add_event(noteon)

                noteoff = NoteOffEvent()
                noteoff.pitch = note
                noteoff.tick = int(ev.end * ticks_per_ms)
                noteoff.velocity = 100
                mid.add_event(noteoff)
            elif isinstance(ev, (ChordEvent, BeatEvent)):
                # These events don't affect the midi data
                continue
            else:
                raise TypeError, "event type %s not recognised by "\
                    "MIDI converter." % type(ev).__name__
        return mid
Example #4
0
    def copy_track(original_track, channel_offset=1, default_tempo=100):
        set_tempo_event = SetTempoEvent()
        set_tempo_event.set_bpm(default_tempo)
        for event in original_track:
            if type(event) is SetTempoEvent:
                set_tempo_event = event

        new_track = Track(events=[MIDI.copy_event(set_tempo_event)],
                          tick_relative=True)
        for event in original_track:
            if type(event) is NoteOnEvent or type(event) is NoteOffEvent:
                copied_event = MIDI.copy_event(event)
                copied_event.channel += channel_offset
                new_track.append(copied_event)
        return new_track
Example #5
0
 def from_data(data, directives):
     # Build the tone matrix straight up
     state = {
         'equal_temperament' : False,
         'double_root' : False,
         'envelope' : None,
         'origin' : 440,
         'time' : 0.0,
     }
     tone_matrix = ToneMatrix()
     
     #### Prepare a midi stream
     mid = EventStream()
     mid.add_track()
     # Add a tempo event
     tempo = SetTempoEvent()
     tempo.tempo = directives['tempo']
     tempo.tick = 0
     mid.add_event(tempo)
     
     # Each line represents a single chord or some kind of directive
     for line in data:
         first_word = line.split()[0].lower()
         if "=" in line:
             # This is an assignment
             key, __, value = line.partition("=")
             key = key.strip()
             value = value.strip()
             # Check it's valid
             if key == "equal_temperament":
                 if value not in ['off','on']:
                     raise HarmonicalInputFileReadError, \
                         "equal_temperament must be 'off' or 'on', "\
                         "not '%s'" % value
                 value = (value == 'on')
             elif key == "origin":
                 try:
                     value = int(value)
                 except ValueError:
                     # Try interpreting as a coordinate
                     try:
                         coord = _read_coord(value)
                     except HarmonicalInputFileReadError:
                         raise HarmonicalInputFileReadError, "origin "\
                             "value must be an integer or a coordinate."
                     value = _get_pitch(coord)
             elif key == "double_root":
                 if value not in ['off','on']:
                     raise HarmonicalInputFileReadError, \
                         "double_root must be 'off' or 'on', "\
                         "not '%s'" % value
                 value = (value == 'on')
             elif key == "envelope":
                 if value not in ENVELOPES:
                     raise HarmonicalInputFileReadError, "unknown "\
                         "envelope '%s'. Must be one of: %s" % \
                         (value, ", ".join(ENVELOPES.keys()))
                 value = ENVELOPES[value]()
             elif key == "program":
                 # Midi program change
                 try:
                     value = int(value)
                     if value > 127 or value < 0:
                         raise ValueError
                 except ValueError:
                     raise HarmonicalInputFileReadError, "invalid program "\
                         "change: %s. Should be an integer 0-127" % value
                 pchange = ProgramChangeEvent()
                 pchange.value = value
                 pchange.tick = int(state['time'] * mid.resolution)
                 mid.add_event(pchange)
             else:
                 raise HarmonicalInputFileReadError, "invalid "\
                     "assignment key: '%s'" % key
             # Make this assignment when we get to it in the score
             state[key] = value
         elif first_word == "rest":
             tokens = line.split()
             duration = _get_duration(tokens)
             
             # Just move the time counter on without doing anything
             state['time'] += duration
         elif first_word == "chord":
             tokens = line.lstrip("chord").split()
             duration = _get_duration(tokens)
             root = _get_root(tokens)
             volume = _get_volume(tokens)
             sec_duration = _qn_to_seconds(duration)
             
             # Must be just a chord type left
             if len(tokens) > 1:
                 raise HarmonicalInputFileReadError, "chord must "\
                     "include just a chord type"
             if len(tokens) == 0:
                 ctype = ''
             else:
                 ctype = tokens[0]
             
             # Add the chord to the tone matrix
             tone_matrix.add_tone(
                     _qn_to_seconds(state['time']), 
                     SineChordEvent(
                         _get_pitch(root), 
                         ctype,
                         duration=sec_duration, 
                         amplitude=volume,
                         equal_temperament=state['equal_temperament'],
                         double_root=state['double_root'],
                         envelope=state['envelope']
                     )
                 )
                 
             # Add the same chord to the midi file
             tick_time = int(mid.resolution * state['time'])
             tick_duration = int(duration * mid.resolution)
             # TODO: currently this will always treat C as the origin 
             #  even if you change it with directives
             events = events_for_chord(root, 
                                       ctype,
                                       tick_time,
                                       tick_duration, 
                                       velocity = int(volume*127),
                                       equal_temperament=state['equal_temperament'],
                                       double_root=state['double_root'])
             for ev in events:
                 mid.add_event(ev)
             
             # Move the timer on ready for the next chord
             state['time'] += duration
         elif first_word in ["tones", "t"]:
             # Must be a chord made up of coordinates
             tokens = line.lstrip("tones").split()
             duration = _get_duration(tokens)
             root = _get_root(tokens)
             volume = _get_volume(tokens)
             sec_duration = _qn_to_seconds(duration)
                 
             # The rest should be the list of coordinates
             coordinates = [_read_coord(token) for token in tokens]
             
             # Add the chord to the tone matrix
             tone_matrix.add_tone(
                     _qn_to_seconds(state['time']), 
                     SineClusterEvent(
                         _get_pitch(root), 
                         coordinates,
                         duration=sec_duration, 
                         amplitude=volume,
                         equal_temperament=state['equal_temperament'],
                         double_root=state['double_root'],
                         envelope=state['envelope']
                     )
                 )
                 
             
             # Add the same chord to the midi file
             tick_time = int(mid.resolution * state['time'])
             tick_duration = int(duration * mid.resolution)
             for note in coordinates:
                 # TODO: currently this will always treat C as the origin 
                 #  even if you change it with directives
                 events = tonal_space_note_events(note, 
                                                  tick_time,
                                                  tick_duration, 
                                                  velocity = int(volume*127))
                 if state['equal_temperament']:
                     # Omit tuning event (the first one)
                     events = events[1:]
                 for ev in events:
                     mid.add_event(ev)
             
             # Move the timer on ready for the next chord
             state['time'] += duration
         else:
             raise HarmonicalInputFileReadError, "could not make sense "\
                 "of the line: %s" % line
     return ChordInputFile(tone_matrix, midi_file=mid)
Example #6
0
    def render(self):
        """
        Creates MIDI data from the path and chord types.
        
        @rtype: midi.EventStream
        @return: an event stream containing all the midi events
        
        """
        mid = EventStream()
        mid.add_track()

        # Set the tempo at the beginning
        tempo = SetTempoEvent()
        tempo.tempo = self.tempo
        mid.add_event(tempo)

        # Set the instrument at the beginning
        instr = ProgramChangeEvent()
        instr.value = self.instrument
        mid.add_event(instr)

        beat_length = mid.resolution
        # Work out when each root change occurs
        time = Fraction(0)
        root_times = []
        for root, length in self.path:
            root_times.append((root, time))
            time += length

        def _root_at_time(time):
            current_root = root_times[0][0]
            for root, rtime in root_times[1:]:
                # Move through root until we get the first one that
                #  occurs after the previous time
                if rtime > time:
                    return current_root
                current_root = root
            # If we're beyond the time of the last root, use that one
            return current_root

        # Add each chord
        time = Fraction(0)
        bass_events = []
        bass = self.bass_root is not None
        for chord_type, length in self.chord_types:
            tick_length = length * beat_length - 10
            tick_time = time * beat_length
            # Find out what root we're on at this time
            root = _root_at_time(time)
            # Add all the necessary events for this chord
            chord_events = events_for_chord(
                root,
                chord_type,
                int(tick_time),
                int(tick_length),
                equal_temperament=self.equal_temperament,
                root_octave=self.root_octave,
                double_root=(self.double_root or bass))
            if bass:
                # Add the bass note to the bass track
                bass_events.extend([copy.copy(ev) for ev in chord_events[-1]])
            if bass and not self.double_root:
                # Remove the doubled root that we got for the bass line
                chord_events = sum(chord_events[:-1], [])
            # Add the main chord notes to the midi track
            for ev in chord_events:
                mid.add_event(ev)
            time += length

        if bass:
            bass_channel = 1
            # Add another track to the midi file for the bass notes
            mid.add_track()
            # Select a bass instrument - picked bass
            instr = ProgramChangeEvent()
            instr.value = 33
            instr.channel = bass_channel
            mid.add_event(instr)
            # Add all the bass notes
            for ev in bass_events:
                ev.channel = bass_channel
                mid.add_event(ev)
        return mid
Example #7
0
 def render(self):
     """
     Creates MIDI data from the path and chord types.
     
     @rtype: midi.EventStream
     @return: an event stream containing all the midi events
     
     """
     mid = EventStream()
     mid.add_track()
     
     # Set the tempo at the beginning
     tempo = SetTempoEvent()
     tempo.tempo = self.tempo
     mid.add_event(tempo)
     
     # Set the instrument at the beginning
     instr = ProgramChangeEvent()
     instr.value = self.instrument
     mid.add_event(instr)
     
     beat_length = mid.resolution
     # Work out when each root change occurs
     time = Fraction(0)
     root_times = []
     for root,length in self.path:
         root_times.append((root,time))
         time += length
     def _root_at_time(time):
         current_root = root_times[0][0]
         for root,rtime in root_times[1:]:
             # Move through root until we get the first one that 
             #  occurs after the previous time
             if rtime > time:
                 return current_root
             current_root = root
         # If we're beyond the time of the last root, use that one
         return current_root
     
     # Add each chord
     time = Fraction(0)
     bass_events = []
     bass = self.bass_root is not None
     for chord_type,length in self.chord_types:
         tick_length = length * beat_length - 10
         tick_time = time * beat_length
         # Find out what root we're on at this time
         root = _root_at_time(time)
         # Add all the necessary events for this chord
         chord_events = events_for_chord(root, chord_type, int(tick_time), 
                             int(tick_length), 
                             equal_temperament=self.equal_temperament,
                             root_octave=self.root_octave, 
                             double_root=(self.double_root or bass))
         if bass:
             # Add the bass note to the bass track
             bass_events.extend([copy.copy(ev) for ev in chord_events[-1]])
         if bass and not self.double_root:
             # Remove the doubled root that we got for the bass line
             chord_events = sum(chord_events[:-1], [])
         # Add the main chord notes to the midi track
         for ev in chord_events:
             mid.add_event(ev)
         time += length
     
     if bass:
         bass_channel = 1
         # Add another track to the midi file for the bass notes
         mid.add_track()
         # Select a bass instrument - picked bass
         instr = ProgramChangeEvent()
         instr.value = 33
         instr.channel = bass_channel
         mid.add_event(instr)
         # Add all the bass notes
         for ev in bass_events:
             ev.channel = bass_channel
             mid.add_event(ev)
     return mid
Example #8
0
 def copy_event(event):
     if type(event) is SetTempoEvent:
         new_event = SetTempoEvent()
         new_event.tick = event.tick
         new_event.data[0] = event.data[0]
         new_event.data[1] = event.data[1]
         new_event.data[2] = event.data[2]
         return new_event
     elif type(event) is NoteOnEvent:
         new_event = NoteOnEvent()
         new_event.tick = event.tick
         new_event.channel = event.channel
         new_event.data[0] = event.data[0]
         new_event.data[1] = event.data[1]
         return new_event
     elif type(event) is NoteOffEvent:
         new_event = NoteOffEvent()
         new_event.tick = event.tick
         new_event.channel = event.channel
         new_event.data[0] = event.data[0]
         new_event.data[1] = event.data[1]
         return new_event
     elif type(event) is ProgramChangeEvent:
         new_event = ProgramChangeEvent
         new_event.tick = event.tick
         new_event.channel = event.channel
         new_event.data[0] = event.data[0]
         return new_event
     else:
         raise ('Not Supported')
Example #9
0
 def new_track(tempo=100):
     track = Track()
     set_tempo_event = SetTempoEvent()
     set_tempo_event.set_bpm(tempo)
     track.append(set_tempo_event)
     return track