christopher@45
|
1 '''
|
christopher@45
|
2 Author: Chunyang Song
|
christopher@45
|
3 Institution: Centre for Digital Music, Queen Mary University of London
|
christopher@45
|
4
|
christopher@45
|
5 '''
|
christopher@45
|
6 from rhythm_parser import *
|
christopher@45
|
7 from music_objects import *
|
christopher@45
|
8
|
christopher@45
|
9
|
christopher@45
|
10 def sync_perbar_permodel (model, bar, parameters=None):
|
christopher@45
|
11 return model.get_syncopation(bar, parameters)
|
christopher@45
|
12
|
christopher@75
|
13 def calculate_syncopation(model, source, parameters=None, outfile=None, barRange=None):
|
christopher@45
|
14 total = 0.0
|
christopher@45
|
15 barResults = []
|
christopher@45
|
16 numberOfNotes = 0
|
christopher@45
|
17
|
christopher@45
|
18 barlist = None
|
christopher@45
|
19
|
christopher@45
|
20 if isinstance(source, BarList):
|
christopher@45
|
21 barlist = source
|
christopher@45
|
22 sourceType = "bar list"
|
christopher@45
|
23 elif isinstance(source, Bar):
|
christopher@50
|
24 barlist = BarList()
|
christopher@50
|
25 barlist.append(source)
|
christopher@50
|
26 print barlist
|
christopher@45
|
27 sourceType = "single bar"
|
christopher@45
|
28 elif isinstance(source, basestring):
|
christopher@45
|
29 #treat source as a filename
|
christopher@45
|
30 sourceType = source
|
christopher@45
|
31 if source[-4:]==".mid":
|
christopher@45
|
32 import readmidi
|
christopher@45
|
33 midiFile = readmidi.read_midi_file(source)
|
christopher@45
|
34 barlist = readmidi.get_bars_from_midi(midiFile)
|
christopher@45
|
35
|
christopher@45
|
36 elif source[-4:]==".rhy":
|
christopher@45
|
37 #import rhythm_parser
|
christopher@45
|
38 barlist = read_rhythm(source)
|
christopher@45
|
39 else:
|
christopher@45
|
40 print "Error in syncopation_barlist_permodel(): Unrecognised file type."
|
christopher@45
|
41 else:
|
christopher@45
|
42 print "Error in syncopation_barlist_permodel(): unrecognised source type."
|
christopher@45
|
43
|
christopher@45
|
44 barsDiscarded=0
|
christopher@45
|
45 discardedlist = []
|
christopher@45
|
46 includedlist = []
|
christopher@45
|
47
|
christopher@50
|
48
|
christopher@45
|
49 if barlist!=None:
|
christopher@75
|
50
|
christopher@75
|
51 if barRange==None:
|
christopher@75
|
52 barstart=0
|
christopher@75
|
53 barend=len(barlist)
|
christopher@75
|
54 else:
|
christopher@75
|
55 barstart = barRange[0]
|
christopher@75
|
56 barend = barRange[1]
|
christopher@75
|
57
|
christopher@75
|
58
|
christopher@75
|
59 for bar in barlist[barstart:barend]:
|
christopher@50
|
60 print 'processing bar %d' % (barlist.index(bar)+1)
|
christopher@50
|
61
|
christopher@50
|
62 barSyncopation = sync_perbar_permodel(model, bar, parameters)
|
christopher@50
|
63
|
christopher@50
|
64
|
christopher@50
|
65 # if not bar.is_empty():
|
christopher@50
|
66 # barSyncopation = sync_perbar_permodel(model, bar, parameters)
|
christopher@50
|
67 # else:
|
christopher@50
|
68 # barSyncopation = None
|
christopher@50
|
69 # print 'Bar %d cannot be measured because it is empty, returning None.' % barlist.index(bar)
|
christopher@45
|
70
|
christopher@45
|
71 barResults.append(barSyncopation)
|
christopher@45
|
72 if barSyncopation != None:
|
christopher@45
|
73 total += barSyncopation
|
christopher@45
|
74 numberOfNotes += sum(bar.get_binary_sequence())
|
christopher@45
|
75 includedlist.append(barlist.index(bar))
|
christopher@45
|
76 else:
|
christopher@45
|
77 barsDiscarded += 1
|
christopher@45
|
78 discardedlist.append(barlist.index(bar))
|
christopher@50
|
79 print 'Model could not measure bar %d, returning None.' % (barlist.index(bar)+1)
|
christopher@45
|
80
|
christopher@45
|
81 import WNBD
|
christopher@45
|
82 if model is WNBD:
|
christopher@45
|
83 total = total / numberOfNotes
|
christopher@75
|
84
|
christopher@50
|
85 if len(barResults)>barsDiscarded:
|
christopher@50
|
86 average = total / (len(barResults)-barsDiscarded)
|
christopher@50
|
87 else:
|
christopher@50
|
88 average = total
|
christopher@45
|
89
|
christopher@45
|
90 output = {
|
christopher@45
|
91 "model_name":model.__name__ ,
|
christopher@45
|
92 "summed_syncopation":total,
|
christopher@45
|
93 "mean_syncopation_per_bar":average,
|
christopher@45
|
94 "source":sourceType,
|
christopher@45
|
95 "number_of_bars":len(barResults),
|
christopher@45
|
96 "number_of_bars_not_measured":barsDiscarded,
|
christopher@45
|
97 "bars_with_valid_output":includedlist,
|
christopher@50
|
98 "bars_without_valid_output":discardedlist,
|
christopher@45
|
99 "syncopation_by_bar":barResults
|
christopher@45
|
100 }
|
christopher@45
|
101
|
christopher@45
|
102 if outfile!=None:
|
christopher@45
|
103
|
christopher@45
|
104 if ".xml" in outfile:
|
christopher@45
|
105 results_to_xml(output,outfile)
|
christopher@45
|
106 elif ".json" in outfile:
|
christopher@45
|
107 results_to_json(output,outfile)
|
christopher@45
|
108 else:
|
christopher@45
|
109 print "Error in syncopation.py: Unrecognised output file type: ", outfile
|
christopher@45
|
110
|
christopher@45
|
111 return output
|
christopher@45
|
112
|
christopher@45
|
113
|
christopher@45
|
114
|
christopher@45
|
115 def results_to_xml(results, outputFilename):
|
christopher@45
|
116 from xml.etree.ElementTree import Element, ElementTree
|
christopher@45
|
117
|
christopher@45
|
118 elem = Element("syncopation_results")
|
christopher@45
|
119
|
christopher@45
|
120 for key, val in results.items():
|
christopher@45
|
121 child = Element(key)
|
christopher@45
|
122 child.text = str(val)
|
christopher@45
|
123 elem.append(child)
|
christopher@45
|
124
|
christopher@45
|
125 ElementTree(elem).write(outputFilename)
|
christopher@45
|
126
|
christopher@45
|
127 def results_to_json(results, outputFilename):
|
christopher@45
|
128 import json
|
christopher@45
|
129
|
christopher@45
|
130 fileHandle = open(outputFilename, 'w')
|
christopher@45
|
131 json.dump(results, fileHandle, sort_keys=True, indent=4, separators=(',', ': '))
|
christopher@45
|
132 fileHandle.flush()
|
christopher@45
|
133
|