annotate Syncopation models/SG.py @ 1:b2da092dc2e0

The consolidated syncopation software. Have finished individual model and basic functions. Need to revise the coding in main.py, and add rhythm-input interface.
author Chunyang Song <csong@eecs.qmul.ac.uk>
date Sun, 05 Oct 2014 21:52:41 +0100
parents 76ce27beba95
children 031e2ccb1fb6
rev   line source
csong@0 1 '''
csong@0 2 Author: Chunyang Song
csong@0 3 Institution: Centre for Digital Music, Queen Mary University of London
csong@0 4
csong@0 5 '''
csong@0 6
csong@1 7 from basic_functions import get_H, get_min_timeSpan
csong@0 8
csong@1 9 def get_syncopation(seq, subdivision_seq, weight_seq, L_max, rhythm_category):
csong@1 10 syncopation = None
csong@1 11 if rhythm_category == 'poly':
csong@1 12 print 'Error: SG model cannot deal with polyrhythms.'
csong@0 13 else:
csong@1 14
csong@1 15 seq = get_min_timeSpan(seq) # converting to the minimum time-span format
csong@1 16
csong@1 17 # checking whether the given L_max is enough to analyse the given sequence, if not, request a bigger L_max
csong@1 18 new_L_max = True
csong@1 19 matching_level = L_max
csong@1 20 while matching_level >= 0:
csong@1 21 if len(get_H(weight_seq,subdivision_seq, matching_level)) == len(seq):
csong@1 22 new_L_max = False
csong@1 23 break
csong@1 24 else:
csong@1 25 matching_level = matching_level - 1
csong@0 26
csong@1 27 if new_L_max == True:
csong@1 28 print 'Error: needs a bigger L_max (i.e. the lowest metrical level) to match the given rhythm sequence.'
csong@0 29
csong@1 30 else:
csong@1 31 syncopation = 0
csong@1 32 # generate the metrical weights of the lowest level
csong@1 33 H = get_H(weight_seq,subdivision_seq, matching_level)
csong@0 34
csong@1 35 # The aveDif_neighbours function calculates the (weighted) average of the difference between the note at a certain index and its neighbours in a certain metrical level
csong@1 36 def aveDif_neighbours(index, level):
csong@0 37 averages = []
csong@1 38 parameter_garma = 0.8
csong@0 39
csong@1 40 # The findPre function is to calculate the index of the previous neighbour at a certain metrical level.
csong@1 41 def findPre(index, level):
csong@1 42 pre_index = (index - 1)%len(H)
csong@1 43 while(H[pre_index] > level):
csong@1 44 pre_index = (pre_index - 1)%len(H)
csong@0 45 return pre_index
csong@0 46
csong@1 47 # The findPost function is to calculate the index of the next neighbour at a certain metrical level.
csong@1 48 def findPost(index, level):
csong@1 49 post_index = (index + 1)%len(H)
csong@1 50 while(H[post_index] > level):
csong@1 51 post_index = (post_index + 1)%len(H)
csong@0 52 return post_index
csong@0 53
csong@1 54 # The dif function is to calculate a difference level factor between two notes (at note position index1 and index 2) in velocity sequence
csong@0 55 def dif(index1,index2):
csong@0 56 parameter_beta = 0.5
csong@1 57 dif_v = seq[index1]-seq[index2]
csong@1 58 dif_h = abs(H[index1]-H[index2])
csong@0 59 dif = dif_v*(parameter_beta*dif_h/4+1-parameter_beta)
csong@0 60 return dif
csong@0 61
csong@1 62 # From the highest to the lowest metrical levels where the current note resides, calculate the difference between the note and its neighbours at that level
csong@1 63 for l in range(level, max(H)+1):
csong@1 64 ave = ( parameter_garma*dif(index,findPre(index,l))+dif(index,findPost(index,l)) )/(1+parameter_garma)
csong@0 65 averages.append(ave)
csong@0 66 return averages
csong@0 67
csong@1 68 # Calculate the syncopation value for each note
csong@1 69 for index in range(len(seq)):
csong@1 70 if seq[index] != 0: # Onset detected
csong@1 71 h = H[index]
csong@1 72 potential = 1 - pow(0.5,h) # Syncopation potential according to its metrical level, which is equal to the metrical weight
csong@1 73 level = h # Metrical weight happens to be equal to its metrical level
csong@1 74 syncopation += min(aveDif_neighbours(index, h))*potential
csong@1 75
csong@1 76 return syncopation