Exemple #1
0
def save_midi(grid, path, click_track=False, click_track_start=0, accent_downbeat=False, period=0, pulse=1, bpm=240, channel=9, pitch=75, velocity=100):
    '''
    bpm means grid-units per second in this case
    '''

    phase, iois = rhythm_tools.grid_to_iois(grid)

    track = 0

    mf = MIDIFile(1)

    mf.addTrackName(track, 0, "Sample Track")
    mf.addTempo(track, 0, bpm)

    rhythm_start = 0

    if click_track: 

        if click_track_start < 0:
            rhythm_start = -click_track_start
            click_track_start = 0

        click_track_end = (
            rhythm_start + len(iois)
        )

        print(rhythm_start, click_track_start)

        write_click_track(mf, start_time=click_track_start, end_time=click_track_end, period=period, pulse=pulse, velocity=velocity, accent_downbeat=accent_downbeat)

    write_iois(mf, iois, start_time=rhythm_start, track=track, channel=channel, pitch=pitch, velocity=velocity)

    with open(path, 'wb') as midi_file:
        mf.writeFile(midi_file)
class GenerateMelody(object):
    def __init__(self, file, pitch, length, beat_duration):
        self.pitch = pitch
        self.length = abs(length)
        self.file = file
        self.midi_melody = MIDIFile(1)
        self.beat_duration = abs(beat_duration)

    def get_filename(self):
        if not self.file.endswith(".mid"):
            return self.file + ".mid"
        return self.file

    def get_random_pitch(self):
        step = random.randint(-5, 5)
        return abs(self.pitch + step)

    @staticmethod
    def get_random_volume():
        return random.randint(70, 127)

    def generate(self):
        time = 0

        self.midi_melody.addTempo(0, time, 60)
        while time < self.length:
            self.midi_melody.addNote(0, 0, self.get_random_pitch(), time,
                                     self.beat_duration, self.get_random_volume())
            time += self.beat_duration

        binfile = open(self.get_filename(), 'wb')
        self.midi_melody.writeFile(binfile)
        binfile.close()
Exemple #3
0
class Midi:
    """Musique midi"""
    def __init__(self,partition,tempo):
        # Définition des paramètres MIDI.
        piste = 0
        temps = 0
        self.sortieMidi = MIDIFile(1)
        # Nom de la piste.
        self.sortieMidi.addTrackName(piste,temps,"Gregorien")
        # Tempo.
        self.sortieMidi.addTempo(piste,temps, tempo)
        # Instrument (74 : flûte).
        self.sortieMidi.addProgramChange(piste,0,temps,74)
        # À partir des propriétés de la note, création des évènements
        # MIDI.
        for note in partition:
            channel = 0
            pitch = note.hauteur
            duree = note.duree
            volume = 127
            self.sortieMidi.addNote(piste,
                                    channel,
                                    pitch,
                                    temps,
                                    duree,
                                    volume)
            temps += duree
    def ecrire(self,chemin):
        """Écriture effective du fichier MIDI"""
        binfile = open(chemin, 'wb')
        self.sortieMidi.writeFile(binfile)
        binfile.close()
 def createFile(self):
     MIDI = MIDIFile(1)
     MIDI.addTrackName(0,0,self.name)
     MIDI.addTempo(0,0, self.bpm)
     beat = 0
     for chord in self.chords:
         for degree in chord.degrees:
             MIDI.addNote(0,0,self.scale.scaleNotes[degree],beat,1,chord.intensity)
         beat = beat + 1
     if not os.path.exists(songFolder):
       os.makedirs(songFolder)
     midiFile = open("%s/%d.%d-%s.mid"%(songFolder, self.generation, self.songnum, self.name), 'wb')
     MIDI.writeFile(midiFile)
     midiFile.close()
Exemple #5
0
 def make(self):
     with open("output.mid", 'wb') as f:
         MyMIDI = MIDIFile(1)
         track = 0
         time = 0
         channel = 0
         volume = 100
         MyMIDI.addTrackName(track, self.tempo, "Sample Track")
         MyMIDI.addTempo(track, time, 120)
         for part in self.structure:
             for note in self.riffs[part][0].score:
                 MyMIDI.addNote(track, channel, note.pitch, time, note.duration, volume)
                 time += note.duration
         MyMIDI.writeFile(f)
def arrayToMidiDouble(aArray, count):
    MyMIDI = MIDIFile(1)
    MyMIDI.addTrackName(0,0,"Red")
    MyMIDI.addTempo(0,0,120)
    time = 0
    for j in range(len(aArray)):
        # randDur = choice([0.5, 0.25, 1, 2, 4])
        randDur = 1
        pitch = aArray[j]
        MyMIDI.addNote(0,0,pitch,time,randDur,100)
        time += randDur
    name = str(count) + ".mid"
    print(name)
    binfile = open(name, 'wb')
    MyMIDI.writeFile(binfile)
    binfile.close()
    print("finishing producing midi")
    return (name)
Exemple #7
0
class Midi:
    """Musique midi"""
    def __init__(self, partition, titre, tempo):
        # Définition des paramètres MIDI.
        piste = 0
        temps = 0
        self.sortiemidi = MIDIFile(1)
        # Nom de la piste.
        self.sortiemidi.addTrackName(piste, temps, sansaccents(titre))
        # Tempo.
        self.sortiemidi.addTempo(piste, temps, tempo)
        # Instrument (74 : flûte).
        self.sortiemidi.addProgramChange(piste, 0, temps, 74)
        self.traiter_partition(partition, piste, temps)

    def traiter_partition(self, partition, piste, temps):
        """Création des évènements MIDI"""
        transposition = partition.transposition
        for neume in partition.musique:
            for note in (
                    notes for notes in neume if isinstance(notes, Note)
            ):
                channel = 0
                pitch = note.hauteur + transposition
                duree = note.duree
                volume = 127
                self.sortiemidi.addNote(
                    piste,
                    channel,
                    pitch,
                    temps,
                    duree,
                    volume
                )
                temps += duree

    def ecrire(self, chemin):
        """Écriture effective du fichier MIDI"""
        with (
            open(sys.stdout.fileno(), 'wb')
            if chemin == '-'
            else open(chemin, 'wb')
        )as sortie:
            self.sortiemidi.writeFile(sortie)
Exemple #8
0
def midiSing(sheet, instruments, key, ticktime, filename):
    offset = NOTES.index(key) + 60 # Middle C is MIDI note #60    
    midi=MIDIFile(len(sheet))
    replaceprint('Creating midi...')
    for t in range(0,len(sheet)): 
        midi.addTrackName(t, 0, "Track %s"%t)
        midi.addTempo(t, 0, 60000/(ticktime))
        sheet[t]=sheet[t][1:]+[(sheet[t][0],0)]
        tracklen=len(sheet[t])
        for n in range(0,tracklen-1):
            time, note = sheet[t][n]
            duration = sheet[t][(n+1)%tracklen][0]-time
            midi.addNote(t,0,offset+note,time,duration,100)#MyMIDI.addNote(track,channel,pitch,time,duration,volume)
    replaceprint('Writing to file...')
    binfile = open(filename+".mid", 'wb')
    midi.writeFile(binfile)
    binfile.close()
    replaceprint('Synth complete!')
    print("\nMID output to: \"" + filename+ ".mid\"")
    def midi(self, path, bpm=240, channel=9, pitch=75, velocity=100):

        from midiutil.MidiFile3 import MIDIFile

        track = 0
        time = 0

        mf = MIDIFile(1)

        mf.addTrackName(track, time, "Sample Track")
        mf.addTempo(track, time, bpm)

        for duration in self.durations:

            mf.addNote(track, channel, pitch, time, duration, velocity)
            time += duration

        # write it to disk
        with open(path, 'wb') as midi_file:
            mf.writeFile(midi_file)
Exemple #10
0
def construct_midi(filename, bpm, trackname, beat_intervals):
	# Create a MIDI with one track
	MyMIDI = MIDIFile(1)

	track = 0 
	time = 0
	MyMIDI.addTrackName(track, time, trackname) 
	MyMIDI.addTempo(track, time, bpm)

	TIME_COUNTER = 0

	for beat_interval in beat_intervals:
		acappella_measure = chord_from_beat(beat_interval)
		TIME_COUNTER = _add_measure(
			acappella_measure, 
			TIME_COUNTER, 
			MyMIDI
		)

	binfile = open("../output/" + filename, 'wb') 
	MyMIDI.writeFile(binfile) 
	binfile.close()
Exemple #11
0
precipitation = 1
moonphase = 8

highTempAdjustment = 30
lowTempAdjustment = 30

# Create the MIDIFile Object with 3 tracks plus names of tracks

MyMIDI = MIDIFile(3)
MyMIDI.addTrackName(track1,time,"Temperature MusicHI")
time = time +1
MyMIDI.addTrackName(track2,time,"Temperature MusicLOW")
time = time +1
MyMIDI.addTrackName(track3,time,"Temperature MusicPrecip")
time = time +1
MyMIDI.addTempo(track1,time, beats)
time = time +1
MyMIDI.addTempo(track2,time, beats)
time = time +1
MyMIDI.addTempo(track3,time, beats)

# set voice (sound) to be played on tracks
#  we used General Midi sounds ( see General Midi docs )
time = time +1
MyMIDI.addProgramChange(track1,0, time, 47)    # voice 1 = 86   fretless bass
#time = time +1
MyMIDI.addProgramChange(track2,1, time, 112)    # voice 2 = 53
time = time +1
MyMIDI.addProgramChange(track3,2, time, 77)   # cymbal = 119

time = time +1
Exemple #12
0
#Import the library
from midiutil.MidiFile3 import MIDIFile

import csv

track1 = 0
track2 = 1
time = 0

MyMIDI = MIDIFile(2)
MyMIDI.addTrackName(track1,time,"Temperature MusicHI")
time = time +1
MyMIDI.addTrackName(track2,time,"Temperature MusicLOW")
time = time +1
MyMIDI.addTempo(track1,time, 540)
time = time +1
MyMIDI.addTempo(track2,time, 540)

time = time +1
MyMIDI.addProgramChange(track1,0, time, 1)
time = time +1
MyMIDI.addProgramChange(track2,1, time, 2)

time = time +1

#f = open("climate2010.txt")
#for row in csv.reader(f):
    
channel = 0
channel2 = 1
Exemple #13
0
from midiutil.MidiFile3 import MIDIFile

MyMIDI = MIDIFile(1)
track = 0 
time = 0
MyMIDI.addTrackName(track, time, "Sample Track") 
MyMIDI.addTempo(track, time, 120)

track = 0 
channel = 0 
pitch = 60 
time = 4 
duration = 1 
volume = 100

MyMIDI.addNote(track,channel,pitch,time,duration,volume)

track = 0
channel = 1 
pitch = 64 
time = 8 
duration = 1
volume = 100

MyMIDI.addNote(track,channel,pitch,time,duration,volume)

track = 0
channel = 2
pitch = 67 
time = 12 
duration = 1 
def create_midi_from_progression(progression):
    """
	Given a chord progression in the form of a list of chord instances,
	creates a MIDI file as an output.
	"""
    MyMIDI = MIDIFile(4)
    track = 0
    time = 0
    MyMIDI.addTrackName(track, time, "Soprano")
    MyMIDI.addTempo(track, time, 60)
    track += 1
    MyMIDI.addTrackName(track, time, "Alto")
    MyMIDI.addTempo(track, time, 60)
    track += 1
    MyMIDI.addTrackName(track, time, "Tenor")
    MyMIDI.addTempo(track, time, 60)
    track += 1
    MyMIDI.addTrackName(track, time, "Bass")
    MyMIDI.addTempo(track, time, 60)

    channel = 0
    duration = 1
    volume = 100

    for index, chord in enumerate(progression):
        track = 3
        for note in chord.get_notes():
            pitch = note.get_midi_number()
            MyMIDI.addNote(track, channel, pitch, time, duration, volume)
            track -= 1
        time += 1
        if index == len(progression) - 2:
            duration = 2
    binfile = open("output_individual_voices.mid", 'wb')
    MyMIDI.writeFile(binfile)
    binfile.close()

    MyMIDI = MIDIFile(2)
    track = 0
    time = 0
    MyMIDI.addTrackName(track, time, "Upper Voices")
    MyMIDI.addTempo(track, time, 60)
    track += 1
    MyMIDI.addTrackName(track, time, "Lower Voices")
    MyMIDI.addTempo(track, time, 60)

    duration = 1

    for index, chord in enumerate(progression):
        track = 1
        count = 0
        for note in chord.get_notes():
            pitch = note.get_midi_number()
            MyMIDI.addNote(track, channel, pitch, time, duration, volume)
            if count % 2 == 1:
                track -= 1
            count += 1
        time += 1
        if index == len(progression) - 2:
            duration = 2
    binfile = open("output_two_hands.mid", 'wb')
    MyMIDI.writeFile(binfile)
    binfile.close()
    def save_midi_file(self):
        if len(self.messages_captured) == 0:
            return

        my_midi = MIDIFile(2)
        track = 0

        my_midi.addTrackName(track, 0, "Tempo track")
        my_midi.addTempo(track, 0, self.bpm)

        track += 1
        my_midi.addTrackName(track, 0, "Song track")

        total_time = 0
        midi_messages_on = []
        midi_messages_off = []
        midi_messages_controller = []

        for message in self.messages_captured:
            if len(message) != 3:
                self.write_message("wrong length: skipping " + str(message))
                continue

            total_time += message.time_stamp
            # seconds -> beat conversion
            total_time_adjusted = total_time * float(self.bpm) / float(60)

            if message.type == MidiEventTypes.NOTE_ON:
                midi_messages_on.append(
                    {'note': message[1], 'velocity': message[2], 'time': total_time_adjusted, 'channel': message.channel})
            elif message.type == MidiEventTypes.NOTE_OFF:
                midi_messages_off.append(
                    {'note': message[1], 'velocity': message[2], 'time': total_time_adjusted, 'channel': message.channel})
            elif message.type == MidiEventTypes.CONTROL_CHANGE:
                midi_messages_controller.append(
                    {'type': message[1], 'value': message[2], 'time': total_time_adjusted, 'channel': message.channel})
            else:
                self.write_message("unknown message: skipping " + str(message))
                continue

        for m_on in midi_messages_on:
            for m_off in midi_messages_off:
                if m_off['note'] == m_on['note'] and m_off['time'] > m_on['time']:
                    m_on['duration'] = m_off['time'] - m_on['time']
                    m_off['note'] = -1
                    break
            else:
                m_on['duration'] = float(
                    15) * float(self.bpm) / float(60)  # suspended

        for m in midi_messages_on:
            my_midi.addNote(
                track, m['channel'], m['note'], m['time'], m['duration'], m['velocity'])

        for m in midi_messages_controller:
            my_midi.addControllerEvent(
                track, m['channel'], m['time'], m['type'], m['value'])

        file_name = self.midi_file_name.format(
            datetime.datetime.now().strftime("%Y%m%d-%H%M%S"))
        file_path = os.path.join(os.path.dirname(sys.argv[0]), file_name)
        self.write_message("Saving {0} MIDI messages to {1}...".format(
            len(self.messages_captured), file_name))
        binfile = open(file_path, 'wb')
        my_midi.writeFile(binfile)
        binfile.close()
        self.messages_captured = []
        self.write_message("Saved.")
Exemple #16
0
    filename = 'mattys-tune.txt'
    beats_per_minute = 756
with open(filename) as f:
    for line in f:
        if len(line) > 3 and (line[1] == '|' or line[2] == '|'):
            line = line.replace('\n', '')
            lines.append(line)
assert len(lines) % 6 == 0

# Initialize the MIDIFile object (with 1 track)
time = 0
duration = 10
volume = 100
song = MIDIFile(1)
song.addTrackName(0, time, "pianized_guitar_tab.")
song.addTempo(0, time, beats_per_minute)

# The root-pitches of the guitar
guitar = list(reversed([52, 57, 62, 67, 71, 76])) # Assume EADGBe tuning
def add_note(string, fret):
    song.addNote(0, string, guitar[string] + fret, time, duration, volume)

# Process the entire tab
for current in range(0, len(lines), 6):  # The current base string
    for i in range(len(lines[current])): # The position in the current string
        time += 1
        for s in range(6):               # The number of the string
            c = lines[current + s][i]
            try: next_char = lines[current + s][i + 1]
            except: next_char = ''
            if c in '-x\\/bhp':
Exemple #17
0
windSpeed = 6
precipitation = 1

highTempAdjustment = 30
lowTempAdjustment = 30

# Create the MIDIFile Object with 3 tracks plus names of tracks

MyMIDI = MIDIFile(3)
MyMIDI.addTrackName(track1,time,"Temperature MusicHI")
time = time +1
MyMIDI.addTrackName(track2,time,"Temperature MusicLOW")
time = time +1
MyMIDI.addTrackName(track3,time,"Temperature MusicPrecip")
time = time +1
MyMIDI.addTempo(track1,time, beats)
time = time +1
MyMIDI.addTempo(track2,time, beats)
time = time +1
MyMIDI.addTempo(track3,time, beats)

# set voice (sound) to be played on tracks
#  we used General Midi sounds ( see General Midi docs )
time = time +1
MyMIDI.addProgramChange(track1,0, time, 112)    # voice 1 = 86   fretless bass
#time = time +1
MyMIDI.addProgramChange(track2,1, time, 112)    # voice 2 = 53
time = time +1
MyMIDI.addProgramChange(track3,2, time, 119)   # cymbal = 119

time = time +1
Exemple #18
0
class MIDITime(object):

    def __init__(self, tempo=120, outfile='miditime.mid', seconds_per_year=5, base_octave=5, octave_range=1, custom_epoch=None):
        self.tempo = tempo
        self.outfile = outfile
        self.tracks = []
        if custom_epoch:  # Only necessary if you have data that starts before 1970 and DO NOT want to start your midi at the first sound.
            self.epoch = custom_epoch
        else:
            self.epoch = datetime.datetime(1970, 1, 1)
        self.seconds_per_year = seconds_per_year
        self.base_octave = base_octave
        self.octave_range = octave_range
        self.note_chart = [["C"], ["C#", "Db"], ["D"], ["D#", "Eb"], ["E"], ["F"], ["F#", "Gb"], ["G"], ["G#", "Ab"], ["A"], ["A#", "Bb"], ["B"]]

    def beat(self, numdays):
        beats_per_second = self.tempo / 60.0
        beats_per_datayear = self.seconds_per_year * beats_per_second
        beats_per_dataday = beats_per_datayear / 365.25

        return round(beats_per_dataday * numdays, 2)

    def check_tz(self, input):
        if input.tzinfo:
            return input.tzinfo
        else:
            return None

    # Match the compare date to the timezone of whatever your input date is, if the input datetime is timezone-aware
    def normalize_datetime(self, input, compare_date):
        # if input is date, make epoch a date
        if type(input) is datetime.date:
            return compare_date.date()
        # # First, coerce to datetime in case it's a date
        # if type(input) is datetime.date:
        #     input = datetime.datetime.combine(input, datetime.datetime.min.time())

        # If tz data present, make epoch tz-aware
        tz = self.check_tz(input)
        if tz:
            return tz.localize(compare_date)
        else:
            return compare_date

    def days_since_epoch(self, input):
        normalized_epoch = self.normalize_datetime(input, self.epoch)
        return (input - normalized_epoch).total_seconds() / 60 / 60 / 24  # How many days, with fractions

    def map_week_to_day(self, year, week_num, desired_day_num=None):
        ''' Helper for weekly data, so when you jump to a new year you don't have notes playing too close together. Basically returns the first Sunday, Monday, etc. in 0-indexed integer format that is in that week.

        Usage: Once without a desired_day_num, then feed it a day_num in the loop

        Example:
        first_day = self.map_week_to_day(filtered_data[0]['Year'], filtered_data[0]['Week'])

        for r in filtered_data:
            # Convert the week to a date in that week
            week_start_date = self.map_week_to_day(r['Year'], r['Week'], first_day.weekday())
            # To get your date into an integer format, convert that date into the number of days since Jan. 1, 1970
            days_since_epoch = self.mymidi.days_since_epoch(week_start_date)

        '''
        year_start = datetime.datetime(int(year), 1, 1).date()
        year_start_day = year_start.weekday()
        week_start_date = year_start + datetime.timedelta(weeks=1 * (int(week_num) - 1))
        week_start_day = week_start_date.weekday()
        if desired_day_num and week_start_day < desired_day_num:
            return week_start_date + datetime.timedelta(days=(desired_day_num - week_start_day))
        return week_start_date

    def get_data_range(self, data_list, attribute_name, ignore_nulls=True):
        data_list = list(data_list)  # If the data is still a CSV object, once you loop through it you'll get rewind issues. So coercing to list.
        if ignore_nulls:
            minimum = min([float(d[attribute_name]) for d in data_list if d[attribute_name]])
            maximum = max([float(d[attribute_name]) for d in data_list if d[attribute_name]])
        else:
            minimum = min([float(d[attribute_name]) for d in data_list])
            maximum = max([float(d[attribute_name]) for d in data_list])
        return [minimum, maximum]

    def scale_to_note_classic(self, scale_pct, mode):  # Only works in multi-octave mode if in C Major (i.e. all the notes are used. Should not be used in other keys, unless octave range is 1.)
        full_mode = []
        n = 0
        while n < self.octave_range:
            for m in mode:
                current_octave = str(self.base_octave + (n * 1))
                full_mode.append(m + current_octave)
            n += 1
        index = int(scale_pct * float(len(full_mode)))
        if index >= len(full_mode):
            index = len(full_mode) - 1
        print(full_mode[index])
        return full_mode[index]

    def scale_to_note(self, scale_pct, mode):  # Manually go through notes so it doesn't inaccurately jump an octave sometimes.
        # First, write out a list of the possible notes for your octave range (i.e. all of the notes on the keyboard)
        full_c_haystack = []
        n = 0
        while n < self.octave_range:
            for note_group in self.note_chart:
                out_group = []
                for note in note_group:
                    current_octave = self.base_octave + (n * 1)
                    out_group.append(note + str(current_octave))
                full_c_haystack.append(out_group)
            n += 1

        full_mode = []
        n = 0
        while n < self.octave_range:
            for note in mode:
                note_found = False
                note_key = None
                for groupkey, group in enumerate(full_c_haystack):
                    for gnote in group:
                        if gnote[:-1] == note:
                            full_mode.append(gnote)
                            note_found = True
                            note_key = groupkey
                    if note_found:
                        break
                full_c_haystack = full_c_haystack[note_key:]
            n += 1

        # Now run through your specified mode and pick the exact notes in those octaves
        index = int(scale_pct * float(len(full_mode)))
        if index >= len(full_mode):
            index = len(full_mode) - 1

        return full_mode[index]

    def note_to_midi_pitch(self, notename):
        midinum = 0
        letter = notename[:-1]
        octave = notename[-1]

        i = 0
        for note in self.note_chart:
            for form in note:
                if letter == form:
                    midinum = i
                    break
            i += 1
        midinum += (int(octave)) * 12
        return midinum

    def linear_scale_pct(self, domain_min, domain_max, input, reverse=False):
        domain_range = float(domain_max) - float(domain_min)
        domain_pct = (input - domain_min) / domain_range

        if reverse:
            domain_pct = 1 - domain_pct
        return domain_pct

    def log_scale_pct(self, domain_min, domain_max, input, reverse=False, direction='exponential'):
        if direction == 'exponential':  # E.G. earthquakes
            min_log_domain = pow(10, domain_min)
            max_log_domain = pow(10, domain_max)
            domain_range = max_log_domain - min_log_domain

            log_input = pow(10, input)
        elif direction == 'log':  # natural log scale
            if domain_min > 0:
                min_log_domain = log(domain_min)
            else:
                min_log_domain = log(0.1)  # Technically this is not a true log scale. Someone smarter than me will have to figure this out.
            if domain_max > 0:
                max_log_domain = log(domain_max)
            else:
                max_log_domain = log(0.1)  # Technically this is not a true log scale. Someone smarter than me will have to figure this out.
            domain_range = max_log_domain - min_log_domain

            log_input = log(input)

        domain_pct = (log_input - min_log_domain) / domain_range

        if reverse:
            domain_pct = 1 - domain_pct
        return domain_pct

    def scale(self, range_min, range_max, input_pct):
        scale_range = range_max - range_min
        return range_min + (input_pct * scale_range)

    def add_track(self, note_list):
        self.tracks.append(note_list)

    def add_note(self, track, channel, note):
        time = note[0]
        pitch = note[1]
        volume = note[2]
        duration = note[3]

        print(pitch, time, duration, volume)

        # Now add the note.
        self.MIDIFile.addNote(track, channel, pitch, time, duration, volume)

    def save_midi(self):
        # Create the MIDIFile Object with 1 track
        self.MIDIFile = MIDIFile(len(self.tracks))

        for i, note_list in enumerate(self.tracks):

            # Tracks are numbered from zero. Times are measured in beats.
            track = i
            time = 0

            # Add track name and tempo.
            self.MIDIFile.addTrackName(track, time, "Track 1")
            self.MIDIFile.addTempo(track, time, self.tempo)

            for n in note_list:
                if len(n) == 2:
                    note = n[0]
                    channel = n[1]
                else:
                    note = n
                    channel = 0
                self.add_note(track, channel, note)

        # And write it to disk.
        binfile = open(self.outfile, 'wb')
        self.MIDIFile.writeFile(binfile)
        binfile.close()
Exemple #19
0
from midiutil.MidiFile3 import MIDIFile
import random
# Create the MIDIFile Object
MyMIDI = MIDIFile(6)

# Add track name and tempo. The first argument to addTrackName and
# addTempo is the time to write the event.
track = 0
time = 0
channel = 0
program = 0
volume = 100

#Adding instruments
MyMIDI.addTrackName(track,time,"Mellow Ambience I")
MyMIDI.addTempo(track,time,92)
MyMIDI.addProgramChange(track,channel,time,program+53)

MyMIDI.addTrackName(track+1,time,"Harpsichord")
MyMIDI.addTempo(track+1,time, 92)
MyMIDI.addProgramChange(track+1,channel+1,time,program+107)

MyMIDI.addTrackName(track+2,time,"Percussion")
MyMIDI.addTempo(track+2,time, 92)
MyMIDI.addProgramChange(track+2,channel+2,time,program+47)

#Saving Randomness state
random.seed(1337,2)

# Get pitch of note
def getPitch(key, pitchOct):
Exemple #20
0
# and write to disk.
############################################################################

#Import the library
from midiutil.MidiFile3 import MIDIFile


MyMIDI = MIDIFile(1)

# Add track name and tempo. The first argument to addTrackName and
# addTempo is the time to write the event.
track = 0
time = 0
channel = 9
MyMIDI.addTrackName(track,time,"Sample Track")
MyMIDI.addTempo(track,time, 120)

time = time = 2
MyMIDI.addProgramChange(0,channel, time, 44)    # voice 1 = 86   fretless bass
# Add a note. addNote expects the following information:

pitch = 60
duration = 10
volume = 100
duration2 = 5

# Now add the note.
MyMIDI.addNote(track,channel,pitch,time,duration,volume)
time = time + 1
MyMIDI.addNote(track,channel,pitch,time,duration,volume)
time = time + 1.25
Exemple #21
0
class MidiCreator(object):
    def __init__(self, config_file_name):
        """Convert data in a CSV file to a MIDI file."""
        # load instrument names and MIDI note ranges from config file
        self._config = configparser.ConfigParser()
        self._config.read(config_file_name)
        # Create the MIDIFile object with 1 track
        self._midi = MIDIFile(1)
        # Add track name and tempo.
        self._midi.addTrackName(
            0,  # track number
            0,  # time
            self._config.get('midi',
                             'track name',
                             fallback=DEFAULT_TRACK_NAME))
        self._midi.addTempo(
            0,  # track number
            0,  # time
            int(self._config.get('midi',
                                 'tempo',
                                 fallback=DEFAULT_TEMPO)))

    def _init_note_makers(self, csv_file_name):
        """Create a list of NoteMaker objects, one for each csv column that is
        mapped to an instrument by the config file."""
        self._note_makers = []
        csv_file = open(csv_file_name)
        # first line of csv file must have column names
        columns = csv_file.readline().strip().split(',')
        # Create one notemaker instance for every csv column that we'll be
        # using to produce music. Each notemaker is assigned to a separate
        # channel, since each instrument must have its own channel.
        channel = 0
        for column_index, column_name in enumerate(columns):
            if NoteMaker.is_musical_column(column_name, self._config):
                if self._config['columns'][column_name]:
                    self._note_makers.append(
                        NoteMaker(column_name, column_index, self._midi,
                                  self._config, channel))
                channel += 1
            if channel > 15:
                print("Warning: more than 16 channels, ignoring excess.")
                break
        # Now each notemaker object needs to know the maximum value for its
        # data column, so that it can be scaled to fit the range of the
        # instrument assigned to that column.
        for line in csv_file:
            split_line = line.strip().split(',')
            for note_maker in self._note_makers:
                note_maker.test_for_max(split_line)

    def write_midi_file(self, csv_file_name, midi_file_name):
        # Create a list of pitchmaker objects, one for each column that will be
        # used to produce music.
        self._init_note_makers(csv_file_name)
        # Write notes to the midi file
        csv_file = open(csv_file_name)
        csv_file.readline()  # skip header
        for line in csv_file:
            split_line = line.strip().split(',')
            for note_maker in self._note_makers:
                note_maker.add_note(split_line)
        # write the midi data to disk
        with open(midi_file_name, 'wb') as midi_file:
            self._midi.writeFile(midi_file)
def create_midi_from_progression(progression):
	"""
	Given a chord progression in the form of a list of chord instances,
	creates a MIDI file as an output.
	"""
	MyMIDI = MIDIFile(4)
	track = 0
	time = 0
	MyMIDI.addTrackName(track, time, "Soprano")
	MyMIDI.addTempo(track, time, 60)
	track += 1
	MyMIDI.addTrackName(track, time, "Alto")
	MyMIDI.addTempo(track, time, 60)
	track += 1
	MyMIDI.addTrackName(track, time, "Tenor")
	MyMIDI.addTempo(track, time, 60)
	track += 1
	MyMIDI.addTrackName(track, time, "Bass")
	MyMIDI.addTempo(track, time, 60)

	channel = 0
	duration = 1
	volume = 100

	for index, chord in enumerate(progression):
		track = 3
		for note in chord.get_notes():
			pitch = note.get_midi_number()
			MyMIDI.addNote(track, channel, pitch, time, duration, volume)
			track -= 1
		time += 1
		if index == len(progression) - 2:
			duration = 2
	binfile = open("output_individual_voices.mid", 'wb')
	MyMIDI.writeFile(binfile)
	binfile.close()


	MyMIDI = MIDIFile(2)
	track = 0
	time = 0
	MyMIDI.addTrackName(track, time, "Upper Voices")
	MyMIDI.addTempo(track, time, 60)
	track += 1
	MyMIDI.addTrackName(track, time, "Lower Voices")
	MyMIDI.addTempo(track, time, 60)

	duration = 1

	for index, chord in enumerate(progression):
		track = 1
		count = 0
		for note in chord.get_notes():
			pitch = note.get_midi_number()
			MyMIDI.addNote(track, channel, pitch, time, duration, volume)
			if count % 2 == 1:
				track -= 1
			count += 1
		time += 1
		if index == len(progression) - 2:
			duration = 2
	binfile = open("output_two_hands.mid", 'wb')
	MyMIDI.writeFile(binfile)
	binfile.close()
Exemple #23
0
def image_process_2():
    image = cv2.imread('temp/src_image.jpg', 0)
    list_ = image.tolist()
    if max(map(max, list_)) > 225:
        thresh_1 = 0.66 * max(map(max, list_))
        thresh_2 = 0.70 * max(map(max, list_))
        thresh_3 = 0.72 * max(map(max, list_))
    elif max(map(max, list_)) < 200:
        thresh_1 = 0.65 * max(map(max, list_))
        thresh_2 = 0.75 * max(map(max, list_))
        thresh_3 = 0.84 * max(map(max, list_))
    else:
        thresh_1 = 0.66 * max(map(max, list_))
        thresh_2 = 0.72 * max(map(max, list_))
        thresh_3 = 0.75 * max(map(max, list_))

    ret, img_gray1 = cv2.threshold(dstImg, thresh_1, 255, cv2.THRESH_BINARY)
    ret, img_gray2 = cv2.threshold(dstImg, thresh_2, 255, cv2.THRESH_BINARY)
    ret, img_gray3 = cv2.threshold(dstImg, thresh_3, 255, cv2.THRESH_BINARY)

    img_gray = 255 * np.ones(shape=[780, 551], dtype=np.uint8)

    # cv2.namedWindow("img_gray", cv2.WINDOW_AUTOSIZE)
    # cv2.imshow("img_gray", img_gray)

    for i in range(780):
        for j in range(551):
            if img_gray1[i][j] == 0 and img_gray2[i][j] == 0 and img_gray3[i][
                    j] == 0:
                img_gray[i][j] = 0
            elif img_gray1[i][j] == 0 or img_gray2[i][j] == 0:
                if img_gray3[i][j] == 0:
                    img_gray[i][j] = 0
            elif img_gray1[i][j] == 0 or img_gray3[i][j] == 0:
                if img_gray2[i][j] == 0:
                    img_gray[i][j] = 0
            elif img_gray2[i][j] == 0 or img_gray3[i][j] == 0:
                if img_gray1[i][j] == 0:
                    img_gray[i][j] = 0

    img = img_gray

    # height, width = img.shape[:2]
    # img_width, img_height = img_gray.shape[::-1]

    # <<找五線譜
    staff_recs = locate_images(img_gray, staff_imgs, staff_lower, staff_upper,
                               staff_thresh)
    staff_recs = [j for i in staff_recs for j in i]
    heights = [r.y for r in staff_recs] + [0]
    histo = [heights.count(i) for i in range(0, max(heights) + 1)]
    avg = np.mean(list(set(histo)))
    staff_recs = [r for r in staff_recs if histo[r.y] > avg]
    staff_recs = merge_recs(staff_recs, 0.01)
    # staff_recs_img = img.copy()
    # for r in staff_recs:
    #     r.draw(staff_recs_img, (0, 0, 255), 2)
    # >>

    #  <<找五線譜的模板
    resul = []
    resul.append(staff_recs[0])
    for index, item in enumerate(staff_recs):
        if abs(resul[-1].y - item.y) > 100:
            resul.append(item)
        else:
            continue
    # print("resul", resul)
    # >>

    # <<找五線譜的y座標
    staff = []
    line_axis = []
    for item in resul:
        # print("item.y", item.y)
        line_axis.append(item.y)
        y_project = []
        line_ = []
        for i in range(int(item.h)):
            count = 0
            for j in range(int(item.w)):
                if img[item.y + i, item.x + j] == 0:
                    count += 1
                else:
                    continue
            y_project.append(count)
        # print("y_project(count)", y_project)

        i = 1
        while i < len(y_project):
            if (y_project[i] == 0):
                i += 1
                continue
            elif (y_project[i] > 0 and y_project[i + 1] > 0
                  and y_project[i + 2] > 0):
                line = (i + i + 1 + i + 2) // 3
                line_.append(line + item.y)
                i += 3
            elif (y_project[i] > 0 and y_project[i + 1] > 0):
                line = (i + i + 1) // 2
                line_.append(line + item.y)
                i += 2
            else:
                line = i
                line_.append(line + item.y)
                i += 1
                continue
        staff.append(line_)
    # print("line_axis", line_axis)   #每行譜的五條線的最上面那條
    # print("staff", staff)   #每行譜的五條線
    # >>

    ##### 第一行對x投影
    x_range = [102] * (len(resul))
    x_range[0] = 120
    # print('ra_list',ra_list)
    quarter_recs = []
    half_recs = []
    for x_range_index, x_range_ in enumerate(x_range):
        x_project1 = []
        for x in range(x_range_, 485):
            count = 0
            for y in range(staff[x_range_index][0] - 15,
                           staff[x_range_index][4] + 15):
                if img[y, x] == 0:
                    count += 1
                else:
                    continue
            x_project1.append(count)

        # <<音符的x範圍
        note_xposition = []
        one_note = []
        next_to = False
        for index, item in enumerate(x_project1):
            if item > 8 and next_to == False:  #找到第一個大於9的x
                one_note.append(index)
                next_to = True  #觸發next_to等於True
            elif item > 8 and next_to == True:  #next_to等於True的情況下如果還是大於九則不做理會
                continue
            elif item < 8 and next_to == True:  #next_to等於True的情況下如果小於九則存入one_note
                one_note.append(index - 1)
                if one_note[1] - one_note[
                        0] > 5:  #one_note[0]是起始x,one_note[1]是結束的x,間距要超過5才會把它存入note_xposition
                    # print("index" ,index)
                    note_xposition.append(one_note)
                one_note = []
                next_to = False  #next_to等於False
        # print("note_xposition", note_xposition)
        # print('xpo', time.time() - start_time)
        # 音符的x範圍>>

        # <<音符的y範圍
        note_yposition = []
        note_xpos_yproject = []
        # for index__ in note_xposition:
        # note_xpos_yproject = []
        for r in range(len(note_xposition)):
            for j in range(staff[x_range_index][0] - 15,
                           staff[x_range_index][4] + 15):
                count = 0
                for i in range(note_xposition[r][0] + x_range_,
                               note_xposition[r][1] + x_range_):
                    if img[j, i] == 0:
                        count += 1
                    else:
                        continue
                note_xpos_yproject.append(count)

            one_note_ = []
            next_to_ = False
            for index_, item in enumerate(note_xpos_yproject):
                if item > 3 and next_to_ == False:  #找到第一個大於3的y
                    one_note_.append(index_)
                    next_to_ = True  #觸發next_to_等於True
                elif item > 3 and next_to_ == True:  #next_to_等於True的情況下如果還是大於3則不做理會
                    continue
                elif item < 3 and next_to_ == True:  #next_to_等於True的情況下如果小於3則存入one_note_
                    one_note_.append(index_ - 1)
                    if one_note_[1] - one_note_[
                            0] > 6:  #one_note_[0]是起始y,one_noteY[1]是結束的y,間距要超過6才會把它存入note_xposition
                        note_yposition.append(one_note_)
                    one_note_ = []
                    next_to_ = False  #next_to等於False
            # print("note_xpos_yproject", note_xpos_yproject)
            note_xpos_yproject = []
        # print("note_yposition", note_yposition)
        # 音符的y範圍>>

        # fingers = []
        # for i in range(len(note_xposition)):
        #     crop_img = img[staff[x_range_index][4]+15 : staff[x_range_index][4]+30, x_range[x_range_index] + note_xposition[i][0] : x_range[x_range_index] + note_xposition[i][1]]
        #     if i == 1:
        #         print("crop_img", crop_img)
        #     # 找finger1
        #     finger1_recs = locate_images(crop_img, finger1_imgs, finger1_lower, finger1_upper, finger1_thresh)

        #     # finger1_recs = finger1_recs[0]
        #     finger1_recs = merge_recs([j for i in finger1_recs for j in i], 0.5)
        #     finger1_recs_img = img.copy()
        #     if i == 1:
        #         print("finger1_recs", len(finger1_recs))
        #     for r in finger1_recs:
        #         r.draw(finger1_recs_img, (0, 0, 255), 2)
        # cv2.imwrite('finger1_recs_img.png', finger1_recs_img)
        # open_file('finger1_recs_img.png')

        global recs
        recs = []
        for r in range(len(note_xposition)):
            count = 0
            for j in range(staff[x_range_index][0] - 15 + note_yposition[r][0],
                           staff[x_range_index][0] - 15 +
                           note_yposition[r][1]):
                for i in range(note_xposition[r][0] + x_range_,
                               note_xposition[r][1] + x_range_):
                    if img[j, i] == 0:
                        count += 1
                    else:
                        continue
            # print(count/((note_xposition[r][1]-note_xposition[r][0])*(note_yposition[r][1]-note_yposition[r][0])))
            if (count /
                ((note_xposition[r][1] - note_xposition[r][0]) *
                 (note_yposition[r][1] - note_yposition[r][0])) > 0.64):
                rec = Rectangle(
                    note_xposition[r][0] + x_range_,
                    staff[x_range_index][0] - 15 + note_yposition[r][0],
                    note_xposition[r][1] - note_xposition[r][0],
                    note_yposition[r][1] - note_yposition[r][0])
                quarter_recs.append(rec)
                recs.append(rec)
            elif (count /
                  ((note_xposition[r][1] - note_xposition[r][0]) *
                   (note_yposition[r][1] - note_yposition[r][0])) <= 0.64):
                rec = Rectangle(
                    note_xposition[r][0] + x_range_,
                    staff[x_range_index][0] - 15 + note_yposition[r][0],
                    note_xposition[r][1] - note_xposition[r][0],
                    note_yposition[r][1] - note_yposition[r][0])
                half_recs.append(rec)
                recs.append(rec)

    # print("quarter_recs", quarter_recs)
    # print("half_recs", half_recs)
    # print("quarter_recs", len(quarter_recs))
    # print("half_recs", len(half_recs))
    l = recs
    # print("rec", rec)
    with open("temp/output.txt", "wb") as fp:  #Pickling
        pickle.dump(l, fp)

    # with open("test.txt", "rb") as fp:   # Unpickling
    #     b = pickle.load(fp)

    staff_boxes = [
        Rectangle(x_range[r], staff[r][2] - 33, 485 - x_range[r], 68)
        for r in range(len(staff))
    ]
    # staff_boxes_img = img.copy()
    # for r in staff_boxes:
    #     r.draw(staff_boxes_img, (0, 0, 255), 2)
    # cv2.imwrite('staff_boxes_img.png', staff_boxes_img)
    # open_file('staff_boxes_img.png')

    # objects_img = staff_boxes_img

    # 畫四分音符
    # quarter_recs_img = img.copy()
    # for r in quarter_recs:
    #     r.draw(quarter_recs_img, (0, 0, 255), 2)
    # cv2.imwrite('quarter_recs_img.png', quarter_recs_img)
    # open_file('quarter_recs_img.png')

    # 畫二分音符
    # half_recs_img = img.copy()
    # for r in half_recs:
    #     r.draw(half_recs_img, (0, 0, 255), 2)
    # cv2.imwrite('half_recs_img.png', half_recs_img)
    # open_file('half_recs_img.png')

    staff_notes = []
    note_groups = []
    for box in staff_boxes:
        staff_sharps = []
        staff_flats = []
        quarter_notes = [
            Note(r, "4,8", box, staff_sharps, staff_flats)
            for r in quarter_recs
            if abs(r.middle[1] - box.middle[1]) < box.h * 5.0 / 8.0
        ]
        half_notes = [
            Note(r, "2", box, staff_sharps, staff_flats) for r in half_recs
            if abs(r.middle[1] - box.middle[1]) < box.h * 5.0 / 8.0
        ]

        staff_notes = quarter_notes + half_notes

        staff_notes.sort(key=lambda n: n.rec.x)
        staffs = [r for r in staff_recs if r.overlap(box) > 0]
        staffs.sort(key=lambda r: r.x)
        note_color = (randint(0, 255), randint(0, 255), randint(0, 255))
        note_group = []
        i = 0
        j = 0
        while (i < len(staff_notes)):
            if j < len(staffs):
                if staff_notes[i].rec.x > staffs[j].x:
                    r = staffs[j]
                    j += 1
                    if len(note_group) > 0:
                        note_groups.append(note_group)
                        note_group = []
                    note_color = (randint(0,
                                          255), randint(0,
                                                        255), randint(0, 255))
                else:
                    note_group.append(staff_notes[i])
                    # staff_notes[i].rec.draww(img, note_color, 2)
                    i += 1
            else:
                note_group.append(staff_notes[i])
                # staff_notes[i].rec.draww(img, note_color, 2)
                i += 1
        note_groups.append(note_group)

    # for r in staff_boxes:
    #     r.draw(img, (0, 0, 255), 2)

    # cv2.imwrite('res.png', img)
    # open_file('res.png')

    # for note_group in note_groups:
    #     print([ note.note + " " + note.sym for note in note_group])

    midi = MIDIFile(1)

    track = 0
    time = 0
    channel = 0
    volume = 100

    midi.addTrackName(track, time, "Track")
    midi.addTempo(track, time, 60)

    for note_group in note_groups:
        duration = None
        for note in note_group:
            note_type = note.sym
            if note_type == "1":
                duration = 4
            elif note_type == "2":
                duration = 2
            elif note_type == "4,8":
                # duration = 1 if len(note_group) == 1 else 0.5
                duration = 1
            pitch = note.pitch
            midi.addNote(track, channel, pitch, time, duration, volume)
            time += duration

    # And write it to disk.
    binfile = open("temp/output.mid", 'wb')
    midi.writeFile(binfile)
    binfile.close()
Exemple #24
0
    cv2.imwrite('res.png', img)
    open_file('res.png')
   
    for note_group in note_groups:
        print([ note.note + " " + note.sym for note in note_group])

    # note를 미디 파일로 저장
    midi = MIDIFile(1)
     
    track = 0   
    time = 0
    channel = 0
    volume = 100
    
    midi.addTrackName(track, time, "Track")
    midi.addTempo(track, time, 140)
    
    for note_group in note_groups:
        duration = None
        for note in note_group:
            note_type = note.sym
            if note_type == "1":
                duration = 4
            elif note_type == "2":
                duration = 2
            elif note_type == "4,8":
                duration = 1 if len(note_group) == 1 else 0.5
            pitch = note.pitch
            midi.addNote(track,channel,pitch,time,duration,volume)
            time += duration
Exemple #25
0
# A sample program to create a single-track MIDI file, add a note,
# and write to disk.
############################################################################

#Import the library
from midiutil.MidiFile3 import MIDIFile
import random
# Create the MIDIFile Object
MyMIDI = MIDIFile(2)

# Add track name and tempo. The first argument to addTrackName and
# addTempo is the time to write the event.
track = 0
LHtime = 0
MyMIDI.addTrackName(track, LHtime, "Jazzzzzy Left Hand")
MyMIDI.addTempo(track,LHtime, 160)

# Creating second track
track2 = 1
RHtime = 0
MyMIDI.addTrackName(track2, RHtime, "Jazzzzzy Right Hand")
MyMIDI.addTempo(track,RHtime,160)
# Add a note 'C'. addNote expects the following information:
channel = 0
pitch = 60
duration = 2
volume = 100

# Create Randomness
random.seed(1338, 2)
Exemple #26
0
class MIDITime(object):
    def __init__(self,
                 tempo=120,
                 outfile='miditime.mid',
                 seconds_per_year=5,
                 base_octave=5,
                 octave_range=1,
                 custom_epoch=None):
        self.tempo = tempo
        self.outfile = outfile
        self.tracks = []
        if custom_epoch:  # Only necessary if you have data that starts before 1970 and DO NOT want to start your midi at the first sound.
            self.epoch = custom_epoch
        else:
            self.epoch = datetime.datetime(1970, 1, 1)
        self.seconds_per_year = seconds_per_year
        self.base_octave = base_octave
        self.octave_range = octave_range
        self.note_chart = [["C"], ["C#", "Db"], ["D"], ["D#", "Eb"], ["E"],
                           ["F"], ["F#", "Gb"], ["G"], ["G#", "Ab"], ["A"],
                           ["A#", "Bb"], ["B"]]

    def beat(self, numdays):
        beats_per_second = self.tempo / 60.0
        beats_per_datayear = self.seconds_per_year * beats_per_second
        beats_per_dataday = beats_per_datayear / 365.25

        return round(beats_per_dataday * numdays, 2)

    def check_tz(self, input):
        if input.tzinfo:
            return input.tzinfo
        else:
            return None

    # Match the compare date to the timezone of whatever your input date is, if the input datetime is timezone-aware
    def normalize_datetime(self, input, compare_date):
        # if input is date, make epoch a date
        if type(input) is datetime.date:
            return compare_date.date()
        # # First, coerce to datetime in case it's a date
        # if type(input) is datetime.date:
        #     input = datetime.datetime.combine(input, datetime.datetime.min.time())

        # If tz data present, make epoch tz-aware
        tz = self.check_tz(input)
        if tz:
            return tz.localize(compare_date)
        else:
            return compare_date

    def days_since_epoch(self, input):
        normalized_epoch = self.normalize_datetime(input, self.epoch)
        return (input - normalized_epoch).total_seconds(
        ) / 60 / 60 / 24  # How many days, with fractions

    def map_week_to_day(self, year, week_num, desired_day_num=None):
        ''' Helper for weekly data, so when you jump to a new year you don't have notes playing too close together. Basically returns the first Sunday, Monday, etc. in 0-indexed integer format that is in that week.

        Usage: Once without a desired_day_num, then feed it a day_num in the loop

        Example:
        first_day = self.map_week_to_day(filtered_data[0]['Year'], filtered_data[0]['Week'])

        for r in filtered_data:
            # Convert the week to a date in that week
            week_start_date = self.map_week_to_day(r['Year'], r['Week'], first_day.weekday())
            # To get your date into an integer format, convert that date into the number of days since Jan. 1, 1970
            days_since_epoch = self.mymidi.days_since_epoch(week_start_date)

        '''
        year_start = datetime.datetime(int(year), 1, 1).date()
        year_start_day = year_start.weekday()
        week_start_date = year_start + datetime.timedelta(weeks=1 *
                                                          (int(week_num) - 1))
        week_start_day = week_start_date.weekday()
        if desired_day_num and week_start_day < desired_day_num:
            return week_start_date + datetime.timedelta(days=(desired_day_num -
                                                              week_start_day))
        return week_start_date

    def get_data_range(self, data_list, attribute_name, ignore_nulls=True):
        data_list = list(
            data_list
        )  # If the data is still a CSV object, once you loop through it you'll get rewind issues. So coercing to list.
        if ignore_nulls:
            minimum = min([
                float(d[attribute_name]) for d in data_list
                if d[attribute_name]
            ])
            maximum = max([
                float(d[attribute_name]) for d in data_list
                if d[attribute_name]
            ])
        else:
            minimum = min([float(d[attribute_name]) for d in data_list])
            maximum = max([float(d[attribute_name]) for d in data_list])
        return [minimum, maximum]

    def scale_to_note_classic(
        self, scale_pct, mode
    ):  # Only works in multi-octave mode if in C Major (i.e. all the notes are used. Should not be used in other keys, unless octave range is 1.)
        full_mode = []
        n = 0
        while n < self.octave_range:
            for m in mode:
                current_octave = str(self.base_octave + (n * 1))
                full_mode.append(m + current_octave)
            n += 1
        index = int(scale_pct * float(len(full_mode)))
        if index >= len(full_mode):
            index = len(full_mode) - 1
        print(full_mode[index])
        return full_mode[index]

    def scale_to_note(
        self, scale_pct, mode
    ):  # Manually go through notes so it doesn't inaccurately jump an octave sometimes.
        # First, write out a list of the possible notes for your octave range (i.e. all of the notes on the keyboard)
        full_c_haystack = []
        n = 0
        while n < self.octave_range:
            for note_group in self.note_chart:
                out_group = []
                for note in note_group:
                    current_octave = self.base_octave + (n * 1)
                    out_group.append(note + str(current_octave))
                full_c_haystack.append(out_group)
            n += 1

        full_mode = []
        n = 0
        while n < self.octave_range:
            for note in mode:
                note_found = False
                note_key = None
                for groupkey, group in enumerate(full_c_haystack):
                    for gnote in group:
                        if gnote[:-1] == note:
                            full_mode.append(gnote)
                            note_found = True
                            note_key = groupkey
                    if note_found:
                        break
                full_c_haystack = full_c_haystack[note_key:]
            n += 1

        # Now run through your specified mode and pick the exact notes in those octaves
        index = int(scale_pct * float(len(full_mode)))
        if index >= len(full_mode):
            index = len(full_mode) - 1

        return full_mode[index]

    def note_to_midi_pitch(self, notename):
        midinum = 0
        letter = notename[:-1]
        octave = notename[-1]

        i = 0
        for note in self.note_chart:
            for form in note:
                if letter == form:
                    midinum = i
                    break
            i += 1
        midinum += (int(octave)) * 12
        return midinum

    def linear_scale_pct(self, domain_min, domain_max, input, reverse=False):
        domain_range = float(domain_max) - float(domain_min)
        domain_pct = (input - domain_min) / domain_range

        if reverse:
            domain_pct = 1 - domain_pct
        return domain_pct

    def log_scale_pct(self,
                      domain_min,
                      domain_max,
                      input,
                      reverse=False,
                      direction='exponential'):
        if direction == 'exponential':  # E.G. earthquakes
            min_log_domain = pow(10, domain_min)
            max_log_domain = pow(10, domain_max)
            domain_range = max_log_domain - min_log_domain

            log_input = pow(10, input)
        elif direction == 'log':  # natural log scale
            if domain_min > 0:
                min_log_domain = log(domain_min)
            else:
                min_log_domain = log(
                    0.1
                )  # Technically this is not a true log scale. Someone smarter than me will have to figure this out.
            if domain_max > 0:
                max_log_domain = log(domain_max)
            else:
                max_log_domain = log(
                    0.1
                )  # Technically this is not a true log scale. Someone smarter than me will have to figure this out.
            domain_range = max_log_domain - min_log_domain

            log_input = log(input)

        domain_pct = (log_input - min_log_domain) / domain_range

        if reverse:
            domain_pct = 1 - domain_pct
        return domain_pct

    def scale(self, range_min, range_max, input_pct):
        scale_range = range_max - range_min
        return range_min + (input_pct * scale_range)

    def add_track(self, note_list):
        self.tracks.append(note_list)

    def add_note(self, track, channel, note):
        time = note[0]
        pitch = note[1]
        velocity = note[2]
        duration = note[3]

        print(pitch, time, duration, velocity)

        # Now add the note.
        self.MIDIFile.addNote(track, channel, pitch, time, duration, velocity)

    def save_midi(self):
        # Create the MIDIFile Object with 1 track
        self.MIDIFile = MIDIFile(len(self.tracks))

        for i, note_list in enumerate(self.tracks):

            # Tracks are numbered from zero. Times are measured in beats.
            track = i
            time = 0

            # Add track name and tempo.
            self.MIDIFile.addTrackName(track, time, "Track %s" % i)
            self.MIDIFile.addTempo(track, time, self.tempo)

            for n in note_list:
                if len(n) == 2:
                    note = n[0]
                    channel = n[1]
                else:
                    note = n
                    channel = 0
                self.add_note(track, channel, note)

        # And write it to disk.
        binfile = open(self.outfile, 'wb')
        self.MIDIFile.writeFile(binfile)
        binfile.close()