from music21 import converter, dynamics, note
import matplotlib.pyplot as plt
import json
class MusicXMLDynamicsParser:
def __init__(self, score_path, start_dynamic=0.3, end_dynamic=3.5):
self.score = converter.parse(score_path)
self.start_dynamic = start_dynamic
self.end_dynamic = end_dynamic
self.notes_list = []
self.energy_envelope = []
def process_notes(self):
for n in self.score.recurse().notes:
self.notes_list.append({
'start_time': n.offset,
'end_time': n.offset + n.duration.quarterLength,
'type': 'general',
'language': 'en',
'pitch': n.pitch.midi,
'phone': ['aa']
})
def process_wedges(self):
for element in self.score.recurse().getElementsByClass(dynamics.DynamicWedge):
end_time = end_element.offset + end_element.quarterLength
duration = end_time - start_time
if isinstance(element, dynamics.Crescendo):
start_dynamic = self.start_dynamic
end_dynamic = self.end_dynamic
elif isinstance(element, dynamics.Diminuendo):
start_dynamic = self.end_dynamic
end_dynamic = self.start_dynamic
num_values = 20 # Number of values to span the dynamic change
hop_time = duration / num_values
dynamic_values = [
start_dynamic + i * (end_dynamic - start_dynamic) / (num_values - 1)
for i in range(num_values)
]
self.energy_envelope.append({
'hop_time': hop_time,
'start_time': start_time,
'values': dynamic_values
})
print(f"Processing {'crescendo' if isinstance(element, dynamics.Crescendo) else 'diminuendo'} wedge")
print(f"Start time: {start_time}, End time: {end_time}, Duration: {duration}")
print(f"Dynamic values: {dynamic_values}")
def create_aces_file(self, output_path):
self.process_notes()
self.process_wedges()
aces_data = {
'version': 1.1,
'notes': self.notes_list,
'piece_params': {
'energy': {
'envelope': self.energy_envelope,
'user': []
},
'air': {
'envelope': [],
'user': []
},
'falsetto': {
'envelope': [],
'user': []
},
'pitch': {
'user': []
},
'tension': {
'envelope': [],
'user': []
}
}
}
with open(output_path, 'w') as f:
json.dump(aces_data, f, indent=4)
class ACESVisualizer:
def __init__(self, aces_file):
with open(aces_file, 'r') as f:
self.aces_data = json.load(f)
def plot_energy(self):
energy_data = self.aces_data['piece_params']['energy']['envelope']
for segment in energy_data:
times = [segment['start_time'] + i * segment['hop_time'] for i in range(len(segment['values']))]
plt.plot(times, segment['values'], 'r-')
plt.title('Energy Envelope')
plt.xlabel('Time (s)')
plt.ylabel('Energy')
plt.grid(True)
plt.show()
# Paths
score_path = 'minimalDynamicsTest2.musicxml'
output_path = '/minimalDynamicsTest2.aces'
# Create ACES file
parser = MusicXMLDynamicsParser(score_path)
parser.create_aces_file(output_path)
# Visualize the energy envelope
visualizer = ACESVisualizer(output_path)
visualizer.plot_energy()
I think I ALMOST have it, but the timing of the dynamics still isn't quite right. Any suggestions?