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@1
|
5 '''
|
csong@20
|
6 from basic_functions import repeat, get_note_indices
|
csong@0
|
7
|
csong@2
|
8 # To find the product of multiple numbers
|
csong@1
|
9 def cumu_multiply(numbers):
|
csong@1
|
10 product = 1
|
csong@1
|
11 for n in numbers:
|
csong@1
|
12 product = product*n
|
csong@1
|
13 return product
|
csong@0
|
14
|
csong@20
|
15 def get_syncopation(bar, parameters = None):
|
csong@1
|
16 syncopation = None
|
csong@1
|
17
|
csong@23
|
18 noteSequence = bar.get_note_sequence()
|
csong@23
|
19 barTicks = bar.get_bar_ticks()
|
csong@20
|
20 subdivisionSequence = bar.get_subdivision_sequence()
|
csong@20
|
21 strongBeatLevel = bar.get_beat_level()
|
csong@23
|
22
|
csong@23
|
23 nextbarNoteSequence = None
|
csong@23
|
24 if bar.get_next_bar() != None:
|
csong@23
|
25 nextbarNoteSequence = bar.get_next_bar().get_note_sequence()
|
csong@20
|
26
|
csong@23
|
27 # calculate each strong beat ticks
|
csong@23
|
28 numberOfBeats = cumu_multiply(subdivisionSequence[:strongBeatLevel+1])
|
csong@23
|
29 beatIntervalTicks = barTicks/numberOfBeats
|
csong@23
|
30 # beatsTicks represents the ticks for all the beats in the current bar and the first two beats in the next bar
|
csong@23
|
31 beatsTicks = [i*beatIntervalTicks for i in range(numberOfBeats+2)]
|
csong@23
|
32 #print beatsTicks
|
csong@23
|
33 totalSyncopation = 0
|
csong@23
|
34 for note in noteSequence:
|
csong@23
|
35 # print note.to_string()
|
csong@23
|
36 # find such beatIndex such that note.startTime is located between (including) beatsTicks[beatIndex] and (not including) beatsTicks[beatIndex+1]
|
csong@23
|
37 beatIndex = 0
|
csong@23
|
38 while note.startTime < beatsTicks[beatIndex] or note.startTime >= beatsTicks[beatIndex+1]:
|
csong@23
|
39 beatIndex += 1
|
csong@20
|
40
|
csong@23
|
41 # print beatIndex
|
csong@23
|
42 # calculate the distance of this note to its nearest beat
|
csong@23
|
43 distanceToBeatOnLeft = abs(note.startTime - beatsTicks[beatIndex])/float(beatIntervalTicks)
|
csong@23
|
44 distanceToBeatOnRight = abs(note.startTime - beatsTicks[beatIndex+1])/float(beatIntervalTicks)
|
csong@23
|
45 distanceToNearestBeat = min(distanceToBeatOnLeft,distanceToBeatOnRight)
|
csong@23
|
46 # print distanceToNearestBeat
|
csong@23
|
47
|
csong@23
|
48 # calculate the WNBD measure for this note, and add to total syncopation value for this bar
|
csong@23
|
49 if distanceToNearestBeat == 0:
|
csong@23
|
50 totalSyncopation += 0
|
csong@23
|
51 # or if this note is held on past the following beat, but ends on or before the later beat
|
csong@23
|
52 elif beatsTicks[beatIndex+1] < note.startTime+note.duration <= beatsTicks[beatIndex+2]:
|
csong@23
|
53 totalSyncopation += float(2)/distanceToNearestBeat
|
csong@23
|
54 else:
|
csong@23
|
55 totalSyncopation += float(1)/distanceToNearestBeat
|
csong@23
|
56 # print totalSyncopation
|
csong@23
|
57
|
csong@23
|
58 return totalSyncopation
|
csong@23
|
59
|
csong@23
|
60 #def get_syncopation(seq, subdivision_seq, strong_beat_level, postbar_seq):
|
csong@23
|
61 # def get_syncopation(bar, parameters = None):
|
csong@23
|
62 # syncopation = None
|
csong@2
|
63
|
csong@23
|
64 # binarySequence = bar.get_binary_sequence()
|
csong@23
|
65 # sequenceLength = len(binarySequence)
|
csong@23
|
66 # subdivisionSequence = bar.get_subdivision_sequence()
|
csong@23
|
67 # strongBeatLevel = bar.get_beat_level()
|
csong@23
|
68 # nextbarBinarySequence = None
|
csong@0
|
69
|
csong@23
|
70 # if bar.get_next_bar() != None:
|
csong@23
|
71 # nextbarBinarySequence = bar.get_next_bar().get_binary_sequence()
|
csong@0
|
72
|
csong@23
|
73 # numberOfBeats = cumu_multiply(subdivisionSequence[0:strongBeatLevel+1]) # numberOfBeats is the number of strong beats
|
csong@23
|
74
|
csong@23
|
75 # if sequenceLength % numberOfBeats != 0:
|
csong@23
|
76 # print 'Error: the length of sequence is not subdivable by the subdivision factor in subdivision sequence.'
|
csong@23
|
77 # else:
|
csong@23
|
78 # # Find the indices of all the strong-beats
|
csong@23
|
79 # beatIndices = []
|
csong@23
|
80 # beatInterval = sequenceLength / numberOfBeats
|
csong@23
|
81 # for i in range(numberOfBeats+1):
|
csong@23
|
82 # beatIndices.append(i*beatInterval)
|
csong@23
|
83 # if nextbarBinarySequence != None: # if there is a postbar_seq, add another two beats index for later calculation
|
csong@23
|
84 # beatIndices += [sequenceLength+beatInterval, sequenceLength+ 2* beatInterval]
|
csong@23
|
85
|
csong@23
|
86 # noteIndices = get_note_indices(binarySequence) # all the notes
|
csong@23
|
87
|
csong@23
|
88 # # Calculate the WNBD measure for each note
|
csong@23
|
89 # def measure_pernote(noteIndices, nextNoteIndex):
|
csong@23
|
90 # # Find the nearest beats where this note locates - in [beat_indices[j], beat_indices[j+1])
|
csong@23
|
91 # j = 0
|
csong@23
|
92 # while noteIndices < beatIndices[j] or noteIndices >= beatIndices[j+1]:
|
csong@23
|
93 # j = j + 1
|
csong@1
|
94
|
csong@23
|
95 # # The distance of note to nearest beat normalised by the beat interval
|
csong@23
|
96 # distanceToNearestBeat = min(abs(noteIndices - beatIndices[j]), abs(noteIndices - beatIndices[j+1]))/float(beatInterval)
|
csong@0
|
97
|
csong@23
|
98 # # if this note is on-beat
|
csong@23
|
99 # if distanceToNearestBeat == 0:
|
csong@23
|
100 # measure = 0
|
csong@23
|
101 # # or if this note is held on past the following beat, but ends on or before the later beat
|
csong@23
|
102 # elif beatIndices[j+1] < nextNoteIndex <= beatIndices[j+2]:
|
csong@23
|
103 # measure = float(2)/distanceToNearestBeat
|
csong@23
|
104 # else:
|
csong@23
|
105 # measure = float(1)/distanceToNearestBeat
|
csong@23
|
106 # return measure
|
csong@0
|
107
|
csong@23
|
108 # total = 0
|
csong@23
|
109 # for i in range(len(noteIndices)):
|
csong@23
|
110 # # if this is the last note, end_time is the index of the following note in the next bar
|
csong@23
|
111 # if i == len(noteIndices)-1:
|
csong@23
|
112 # # if the next bar is not none or a bar of full rest,
|
csong@23
|
113 # # the nextNoteIndex is the sum of sequence length in the current bar and the noteIndex in the next bar
|
csong@23
|
114 # if nextbarBinarySequence != None and nextbarBinarySequence != repeat([0],len(nextbarBinarySequence)):
|
csong@23
|
115 # nextNoteIndex = get_note_indices(nextbarBinarySequence)[0]+sequenceLength
|
csong@23
|
116 # # else when the next bar is none or full rest, end_time is the end of this sequence.
|
csong@23
|
117 # else:
|
csong@23
|
118 # nextNoteIndex = sequenceLength
|
csong@23
|
119 # # else this is not the last note, the nextNoteIndex is the following element in the noteIndices list
|
csong@23
|
120 # else:
|
csong@23
|
121 # nextNoteIndex = noteIndices[i+1]
|
csong@23
|
122 # # sum up the syncopation value for individual note at noteIndices[i]
|
csong@23
|
123 # total += measure_pernote(noteIndices[i],nextNoteIndex)
|
csong@0
|
124
|
csong@23
|
125 # #syncopation = float(total) / len(note_indices)
|
csong@0
|
126
|
csong@23
|
127 # # return the total value, leave the normalisation done in the end
|
csong@23
|
128 # return total
|