PageRenderTime 183ms CodeModel.GetById 81ms app.highlight 11ms RepoModel.GetById 89ms app.codeStats 0ms

/examples/midi/enToMIDI.py

http://echo-nest-remix.googlecode.com/
Python | 106 lines | 63 code | 18 blank | 25 comment | 18 complexity | d84f7c7faeb17603c67ba78b156d571b MD5 | raw file
  1#!/usr/bin/env python
  2# encoding: utf-8
  3"""
  4enToMIDI.py
  5
  6Created by Brian Whitman on 2008-11-25.
  7Copyright (c) 2008 __MyCompanyName__. All rights reserved.
  8"""
  9
 10import sys
 11import os
 12import echonest.audio as audio
 13from copy import copy
 14from echonest.support import midi
 15from echonest.support.midi.MidiOutFile import MidiOutFile
 16from math import pow
 17
 18def main():
 19    # Examples:
 20    # TRLYNOP11DE633DD31 church bells
 21    # TRWMWTX11DE6393849 a7 unt by lithops
 22    # TRMTWYL11DD5A1D829 valley hi by stereolab
 23    #a = audio.ExistingTrack("TRMTWYL11DD5A1D829").analysis 
 24    a = audio.LocalAudioFile(sys.argv[1]).analysis
 25    midi = MidiOutFile('output.mid')
 26    midi.header()
 27    midi.start_of_track()
 28    midi.tempo(int(60000000.00 / 60.0)) # 60 BPM, one Q per second, 96 ticks per Q, 96 ticks per second.)
 29    BOOST = 30 # Boost volumes if you want
 30
 31    # Do you want the channels to be split by timbre or no? 
 32    splitChannels = True
 33
 34    for seg_index in xrange(len(a.segments)):
 35        s = a.segments[seg_index]
 36
 37        if(splitChannels):
 38            # Figure out a channel to assign this segment to. Let PCA do the work here... we'll just take the sign of coeffs 1->5 as a 4-bit #
 39            bits = [0,0,0,0]
 40            for i in xrange(4):
 41                # Can't use the first coeff because it's (always?) positive.
 42                if(s.timbre[i+1]>=0): bits[i] =1
 43            channel = bits[0]*8 + bits[1]*4 + bits[2]*2 + bits[3]*1
 44        else:
 45            channel = 0
 46    
 47        # Get the loudnesses in MIDI cc #7 vals for the start of the segment, the loudest part, and the start of the next segment.
 48        # db -> voltage ratio http://www.mogami.com/e/cad/db.html
 49        linearMaxVolume = int(pow(10.0,s.loudness_max/20.0)*127.0)+BOOST
 50        linearStartVolume = int(pow(10.0,s.loudness_begin/20.0)*127.0)+BOOST
 51        if(seg_index == len(a.segments)-1): # if this is the last segment
 52            linearNextStartVolume = 0
 53        else:
 54            linearNextStartVolume = int(pow(10.0,a.segments[seg_index+1].loudness_begin/20.0)*127.0)+BOOST
 55        whenMaxVolume = s.time_loudness_max
 56
 57        # Count the # of ticks I wait in doing the volume ramp so I can fix up rounding errors later.
 58        tt = 0
 59        
 60        # take pitch vector and hit a note on for each pitch at its relative volume. That's 12 notes per segment.
 61        for note in xrange(12):
 62            volume = int(s.pitches[note]*127.0)
 63            midi.update_time(0)
 64            midi.note_on(channel=channel, note=0x3C+note, velocity=volume)
 65        midi.update_time(0)
 66        
 67        # Set volume of this segment. Start at the start volume, ramp up to the max volume , then ramp back down to the next start volume.
 68        curVol = float(linearStartVolume)
 69        
 70        # Do the ramp up to max from start
 71        ticksToMaxLoudnessFromHere = int(96.0 * whenMaxVolume)
 72        if(ticksToMaxLoudnessFromHere > 0):
 73            howMuchVolumeToIncreasePerTick = float(linearMaxVolume - linearStartVolume)/float(ticksToMaxLoudnessFromHere)
 74            for ticks in xrange(ticksToMaxLoudnessFromHere):
 75                midi.continuous_controller(channel,7,int(curVol))
 76                curVol = curVol + howMuchVolumeToIncreasePerTick
 77                tt = tt + 1
 78                midi.update_time(1)
 79        
 80        # Now ramp down from max to start of next seg
 81        ticksToNextSegmentFromHere = int(96.0 * (s.duration-whenMaxVolume))
 82        if(ticksToNextSegmentFromHere > 0):
 83            howMuchVolumeToDecreasePerTick = float(linearMaxVolume - linearNextStartVolume)/float(ticksToNextSegmentFromHere)
 84            for ticks in xrange(ticksToNextSegmentFromHere):
 85                curVol = curVol - howMuchVolumeToDecreasePerTick
 86                midi.continuous_controller(channel,7,int(curVol))
 87                tt = tt + 1
 88                midi.update_time(1)
 89
 90        # Account for rounding error if any
 91        midi.update_time(int(96.0*s.duration)-tt)
 92
 93        # Send the note off
 94        for note in xrange(12):
 95            midi.note_off(channel=channel, note=0x3C+note)
 96            midi.update_time(0)
 97
 98    midi.update_time(0)
 99    midi.end_of_track() 
100    midi.eof()
101
102        
103
104if __name__ == '__main__':
105    main()
106