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@45
|
13 def calculate_syncopation(model, source, parameters=None, outfile=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@45
|
50 for bar in barlist:
|
christopher@50
|
51 print 'processing bar %d' % (barlist.index(bar)+1)
|
christopher@50
|
52
|
christopher@50
|
53 barSyncopation = sync_perbar_permodel(model, bar, parameters)
|
christopher@50
|
54
|
christopher@50
|
55
|
christopher@50
|
56 # if not bar.is_empty():
|
christopher@50
|
57 # barSyncopation = sync_perbar_permodel(model, bar, parameters)
|
christopher@50
|
58 # else:
|
christopher@50
|
59 # barSyncopation = None
|
christopher@50
|
60 # print 'Bar %d cannot be measured because it is empty, returning None.' % barlist.index(bar)
|
christopher@45
|
61
|
christopher@45
|
62 barResults.append(barSyncopation)
|
christopher@45
|
63 if barSyncopation != None:
|
christopher@45
|
64 total += barSyncopation
|
christopher@45
|
65 numberOfNotes += sum(bar.get_binary_sequence())
|
christopher@45
|
66 includedlist.append(barlist.index(bar))
|
christopher@45
|
67 else:
|
christopher@45
|
68 barsDiscarded += 1
|
christopher@45
|
69 discardedlist.append(barlist.index(bar))
|
christopher@50
|
70 print 'Model could not measure bar %d, returning None.' % (barlist.index(bar)+1)
|
christopher@45
|
71
|
christopher@45
|
72 import WNBD
|
christopher@45
|
73 if model is WNBD:
|
christopher@45
|
74 total = total / numberOfNotes
|
christopher@45
|
75
|
christopher@50
|
76 if len(barResults)>barsDiscarded:
|
christopher@50
|
77 average = total / (len(barResults)-barsDiscarded)
|
christopher@50
|
78 else:
|
christopher@50
|
79 average = total
|
christopher@45
|
80
|
christopher@45
|
81 output = {
|
christopher@45
|
82 "model_name":model.__name__ ,
|
christopher@45
|
83 "summed_syncopation":total,
|
christopher@45
|
84 "mean_syncopation_per_bar":average,
|
christopher@45
|
85 "source":sourceType,
|
christopher@45
|
86 "number_of_bars":len(barResults),
|
christopher@45
|
87 "number_of_bars_not_measured":barsDiscarded,
|
christopher@45
|
88 "bars_with_valid_output":includedlist,
|
christopher@50
|
89 "bars_without_valid_output":discardedlist,
|
christopher@45
|
90 "syncopation_by_bar":barResults
|
christopher@45
|
91 }
|
christopher@45
|
92
|
christopher@45
|
93 if outfile!=None:
|
christopher@45
|
94
|
christopher@45
|
95 if ".xml" in outfile:
|
christopher@45
|
96 results_to_xml(output,outfile)
|
christopher@45
|
97 elif ".json" in outfile:
|
christopher@45
|
98 results_to_json(output,outfile)
|
christopher@45
|
99 else:
|
christopher@45
|
100 print "Error in syncopation.py: Unrecognised output file type: ", outfile
|
christopher@45
|
101
|
christopher@45
|
102 return output
|
christopher@45
|
103
|
christopher@45
|
104
|
christopher@45
|
105
|
christopher@45
|
106 def results_to_xml(results, outputFilename):
|
christopher@45
|
107 from xml.etree.ElementTree import Element, ElementTree
|
christopher@45
|
108
|
christopher@45
|
109 elem = Element("syncopation_results")
|
christopher@45
|
110
|
christopher@45
|
111 for key, val in results.items():
|
christopher@45
|
112 child = Element(key)
|
christopher@45
|
113 child.text = str(val)
|
christopher@45
|
114 elem.append(child)
|
christopher@45
|
115
|
christopher@45
|
116 ElementTree(elem).write(outputFilename)
|
christopher@45
|
117
|
christopher@45
|
118 def results_to_json(results, outputFilename):
|
christopher@45
|
119 import json
|
christopher@45
|
120
|
christopher@45
|
121 fileHandle = open(outputFilename, 'w')
|
christopher@45
|
122 json.dump(results, fileHandle, sort_keys=True, indent=4, separators=(',', ': '))
|
christopher@45
|
123 fileHandle.flush()
|
christopher@45
|
124
|