Skip to content

Commit

Permalink
Generate standard drumset from online instruments spreadsheet
Browse files Browse the repository at this point in the history
The spreadsheet is now the source of truth for all instrument data.
  • Loading branch information
shoogle committed Jan 14, 2025
1 parent 1000457 commit 8323a8e
Show file tree
Hide file tree
Showing 3 changed files with 289 additions and 48 deletions.
2 changes: 1 addition & 1 deletion share/instruments/instruments.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7308,7 +7308,7 @@
</Instrument>
<Instrument id="drumset">
<family>drums</family>
<!--Drums for the basic drumset are hard-coded in instrument.cpp for some reason.-->
<!--Drums are hard-coded in drumset.cpp to initialize MIDI and playback systems at startup before instruments.xml is loaded.-->
<trackName>Large Drum Kit</trackName>
<longName>Drum Kit</longName>
<shortName>D. Kit</shortName>
Expand Down
70 changes: 69 additions & 1 deletion share/instruments/update_instruments_xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def eprint(*args, **kwargs):
import csv
import io
import os
import re
import requests
import sys
import xml.etree.ElementTree as ET
Expand All @@ -54,6 +55,8 @@ def eprint(*args, **kwargs):
'GM+GS_Percussion': '1216482735',
}

standard_drumset_id = 'drumset'

parser = argparse.ArgumentParser(description='Fetch the latest spreadsheet and generate instruments.xml.')
parser.add_argument('-c', '--cached', action='store_true', help='Use cached version instead of downloading')
parser.add_argument('-d', '--download', action='append', choices=sheet_ids.keys(), help='Override cached option for a specific sheet')
Expand Down Expand Up @@ -293,7 +296,7 @@ def to_list(str):
to_subelement(el, instrument, 'transpDia', 'transposeDiatonic')
to_subelement(el, instrument, 'transpChr', 'transposeChromatic')

if instrument['id'] in drumsets:
if instrument['id'] != standard_drumset_id and instrument['id'] in drumsets:
for drum in drumsets[instrument['id']].values():
pitch = drum['pitch']
d_el = ET.SubElement(el, 'Drum')
Expand Down Expand Up @@ -498,3 +501,68 @@ def get_comment(instrument, nameType: str, hasTrait: bool):
ordersTree = ET.parse('orders.xml')
for order in ordersTree.getroot().findall('Order'):
add_translatable_string(f, 'engraving/scoreorder', order.find('name').text)

def noteheadgroup(tag):
if tag == 'altbrevis':
return 'HEAD_BREVIS_ALT'

if re.match(r'^[a-h](-sharp|-flat)?-name$', tag):
tag = tag[:-5] # remove '-name' from end of tag

return 'HEAD_' + tag.upper().replace('-', '_')

# Generate the standard drumset. This must be hard-coded in C++ to ensure it's
# available at startup when systems are initialized (engraving, playback, MIDI
# & MusicXML import), which happens prior to instruments.xml being loaded.
standard_drumset_cpp_path = '../../src/engraving/dom/drumset.cpp'
gen_code = ''

for drum in drumsets[standard_drumset_id].values():
pitch = drum['pitch']
head = 'HEAD_' + drum['head'].upper().replace('-', '_')
stem = 'DOWN' if drum['stem'] == '2' else 'UP'

shortcut = drum['shortcut']
shortcut = 'Key_' + shortcut if shortcut and shortcut != null else '0'

gen_code += f"""
// {drum['drum']}
smDrumset->drum({pitch}) = DrumInstrument(
TConv::userName(DrumNum({pitch})),
NoteHeadGroup::{noteheadgroup(drum['head'])},
/*line*/ {drum['line']},
DirectionV::{stem},
/*panelRow*/ {drum['row']},
/*panelColumn*/ {drum['col']},
/*voice*/ {drum['voice']},
/*shortcut*/ {shortcut});
"""

with open(standard_drumset_cpp_path, newline='\n', encoding='utf-8') as file:
old_code = file.read()

old_lines = old_code.split('\n')
begin_line = -1
end_line = -1
new_code = ''

for num, line in enumerate(old_lines, 1):
if 'BEGIN GENERATED CODE' in line:
begin_line = num
break

assert begin_line > 0

for num, line in enumerate(old_lines[begin_line:], 1):
if 'END GENERATED CODE' in line:
end_line = begin_line + num
break

assert end_line > begin_line

new_code = '\n'.join(old_lines[:begin_line]) + f'\n{gen_code}\n' + '\n'.join(old_lines[end_line-1:])

if new_code != old_code:
eprint('Writing ' + standard_drumset_cpp_path)
with open(standard_drumset_cpp_path, 'w', newline='\n', encoding='utf-8') as file:
file.write(new_code)
Loading

0 comments on commit 8323a8e

Please sign in to comment.