Package jazzparser :: Package harmonical :: Package midi
[hide private]
[frames] | no frames]

Source Code for Package jazzparser.harmonical.midi

  1  from __future__ import absolute_import 
  2  """Tuned MIDI file generation to reflect just intonation. 
  3   
  4  Uses the MIDI processing library by Giles Hall that I've worked on to  
  5  improve and fix bugs. 
  6   
  7  """ 
  8  """ 
  9  ============================== License ======================================== 
 10   Copyright (C) 2008, 2010-12 University of Edinburgh, Mark Granroth-Wilding 
 11    
 12   This file is part of The Jazz Parser. 
 13    
 14   The Jazz Parser is free software: you can redistribute it and/or modify 
 15   it under the terms of the GNU General Public License as published by 
 16   the Free Software Foundation, either version 3 of the License, or 
 17   (at your option) any later version. 
 18    
 19   The Jazz Parser is distributed in the hope that it will be useful, 
 20   but WITHOUT ANY WARRANTY; without even the implied warranty of 
 21   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 22   GNU General Public License for more details. 
 23    
 24   You should have received a copy of the GNU General Public License 
 25   along with The Jazz Parser.  If not, see <http://www.gnu.org/licenses/>. 
 26   
 27  ============================ End license ====================================== 
 28   
 29  """ 
 30  __author__ = "Mark Granroth-Wilding <mark.granroth-wilding@ed.ac.uk>"  
 31   
 32  import math 
 33  # Can't by default import from midi, because this module's name clashes 
 34  # But absolute_import forces absolute paths to be resolved unless .s are used 
 35  from midi import single_note_tuning_event, NoteOnEvent, NoteOffEvent 
 36  from jazzparser.utils.tonalspace import coordinate_to_et, \ 
 37                          cents_to_pitch_ratio, pitch_ratio_to_cents 
 38  from ..tones import tonal_space_pitch 
 39   
40 -def tonal_space_note_events(coord, start, duration, origin_note=60, velocity=100):
41 """ 42 Creates the MIDI events needed to play the given tonal space 43 coordinate. These will be a single-note tuning event (to retune 44 the appropriate note), a note-on event and a note-off event. 45 46 @type coord: tuple 47 @param coord: 3D tonal space coordinate 48 @type start: int 49 @param start: start time of note in ticks 50 @type duration: int 51 @param duration: length of the note in ticks 52 @type origin_note: int 53 @param origin_note: midi note number to assume the origin of the 54 tonal space is equal to in pitch (default 60, middle C) 55 @rtype: tuple 56 @return: (tuning event, note on event, note off event) 57 58 """ 59 # Work out what note to retune for the root and how 60 change = tonal_space_tuning(coord) 61 note = change[0] 62 63 # Create the tuning event 64 tuning = single_note_tuning_event([change]) 65 tuning.tick = start 66 # Play the note 67 note_on = NoteOnEvent() 68 note_on.pitch = note 69 note_on.velocity = velocity 70 note_on.tick = start 71 # Stop the note 72 note_off = NoteOffEvent() 73 note_off.pitch = note 74 note_off.tick = start+duration 75 note_off.velocity = velocity 76 77 return (tuning, note_on, note_off)
78
79 -def tonal_space_tuning(coord, origin_note=60):
80 """ 81 Produces MIDI single note tuning event data that will retune the 82 equal temperament equivalent note of the given coordinate to the 83 true pitch of that coordinate. This can, for example, be placed in 84 a stream directly before a note-on event for that note to play 85 the tonal space point. 86 87 If the resulting note number is outside the range of midi notes 88 (0-127), it is shifted to the nearest octave that is within the 89 range. 90 91 @type origin_note: int 92 @param origin_note: midi note number to assume the origin of the 93 tonal space is equal to in pitch (default 60, middle C) 94 @rtype: tuple 95 @return: (note,semitone,cents), where note is the note to be tuned, 96 and semitone and cents between them define the pitch to tune to. 97 This can be used as note change data for a midi single note 98 tuning event. 99 100 """ 101 note = tonal_space_et_note(coord, valid_note=True, origin_note=origin_note) 102 # Now work out how to tune this note to the desired pitch 103 origin_pitch = 440.0 * cents_to_pitch_ratio((origin_note-69)*100) 104 freq = origin_pitch * tonal_space_pitch(coord) 105 # First get a base semitone 106 semitone = frequency_note_number_floor(freq) 107 # Get the number of cents the frequency is above this note 108 cents_above = pitch_ratio_to_cents(freq/et_note_frequency(semitone)) 109 return (note,semitone,cents_above)
110
111 -def frequency_note_number_floor(freq):
112 """ 113 Given a float frequency, returns the midi note number that 114 represents to nearest ET note below this frequency. 115 116 """ 117 # Calculate the interval between this freq and the A above middle C 118 # in cents 119 cents_from_a = pitch_ratio_to_cents(float(freq) / 440.0) 120 # Number of semitones above midi note 0 121 above_zero = (cents_from_a/100.0) + 69.0 122 return int(math.floor(above_zero))
123
124 -def tonal_space_et_note(coord, valid_note=False, origin_note=60):
125 """ 126 Returns the MIDI note number of the note to which equal temperament 127 maps the given 3D tonal space coordinate. 128 129 @type valid_note: bool 130 @param valid_note: if True, permits only valid MIDI notes (i.e. 131 in range 0-127). If the true note is outside this range, returns 132 the note in the nearest octave that is in the range 133 @type origin_note: int 134 @param origin_note: midi note number to assume the origin of the 135 tonal space is equal to in pitch (default 60, middle C) 136 137 """ 138 # Work out the ET midi note number for this point 139 note_number = origin_note + coordinate_to_et(coord) 140 if valid_note: 141 # Make sure it's a valid MIDI note number 142 if note_number < 0: 143 # Bring up to the lowest octave 144 note_number = note_number % 12 145 elif note_number > 127: 146 # Bring down to the top octave 147 chrom_number = note_number % 12 148 if chrom_number <= 7: 149 note_number = 120 + chrom_number 150 else: 151 note_number = 108 + chrom_number 152 return note_number
153
154 -def et_note_frequency(note):
155 """ 156 Calculates the frequency in Hz of the given MIDI note number, assuming 157 equal temperament and 440Hz as the A above middle C. 158 159 """ 160 # Work out the pitch ratio between this note and middle A 161 ratio = cents_to_pitch_ratio(100.0 * (note - 57)) 162 return 220.0 * ratio
163