Skip to content

Instantly share code, notes, and snippets.

@arseniiv
Created October 3, 2023 18:59
Show Gist options
  • Select an option

  • Save arseniiv/3c845e06d375aee05d68beab0c370b87 to your computer and use it in GitHub Desktop.

Select an option

Save arseniiv/3c845e06d375aee05d68beab0c370b87 to your computer and use it in GitHub Desktop.
replacenotes.py
'''
Replaces notes with flats with enharmonic sharps in SFZ and DSPRESET files.
'''
from pathlib import Path
from typing import Final
import re
DS_EXTS: Final = frozenset(['.dspreset'])
SFZ_EXTS: Final = frozenset(['.sfz'])
_NOTE_PATTERN: Final = r'''
(?P<note> [A-Ga-g] b | [EeBb] \# )
(?P<oct> -1 | [0-9] )'''
DS_PATTERN: Final = re.compile(fr'''(?x)
(?P<pre> (root | hi | lo) Note = " )
{_NOTE_PATTERN}
(?P<post> " )''')
SFZ_PATTERN: Final = re.compile(fr'''(?x)
(?P<pre> ((hi | lo |) key | pitch_keycenter) = )
{_NOTE_PATTERN}
(?P<post> )''')
OCT_SHIFT: Final = {'cb': -1, 'b#': +1}
REPL_UPPER: Final = {'Cb': 'B', 'Db': 'C#', 'Eb': 'D#', 'Fb': 'E', 'Gb': 'F#',
'Ab': 'G#', 'Bb': 'A#', 'E#': 'F', 'B#': 'C'}
REPL_LOWER: Final = {k.lower(): v.lower() for k, v in REPL_UPPER.items()}
REPL: Final = REPL_UPPER | REPL_LOWER
IDLE_RUN: Final[bool] = False
class NoteReplacer:
def __init__(self) -> None:
self.replaced: dict[str, str] = dict()
def __call__(self, match: re.Match[str]) -> str:
src_note = match['note']
src_oct = match['oct']
res = self.replaced.get(src_note + src_oct)
if res is None:
res_note = REPL.get(src_note, src_note)
oct_shift = OCT_SHIFT.get(src_note.lower(), 0)
res = f'{res_note}{int(src_oct) + oct_shift}'
self.replaced[src_note + src_oct] = res
return match['pre'] + res + match['post']
def main() -> None:
print('HEYA. 🐙 I replace note names like Gb4 with names like F#4.')
print('Type/paste .sfz/.dspreset files here; leave blank to exit:')
while True:
try:
path = input('> ')
except KeyboardInterrupt:
print('\nRuude.')
break
except EOFError:
print()
break
if not path.strip():
break
path = Path(path)
if not path.is_file():
print('That’s not a file!')
continue
suffixes = set(map(str.casefold, path.suffixes))
for exts, pat in ((DS_EXTS, DS_PATTERN), (SFZ_EXTS, SFZ_PATTERN)):
if not exts.isdisjoint(suffixes):
pattern = pat
break
else:
print('This file’s extension is not a known one.')
continue
with open(path, 'r', encoding='utf8') as f:
source = f.read()
note_replacer = NoteReplacer()
new_source = pattern.sub(note_replacer, source)
if new_source != source:
print('Changes are made...')
for from_, to in note_replacer.replaced.items():
print(f' {from_} -> {to}')
if not IDLE_RUN:
with open(path, 'w', encoding='utf8') as f:
f.write(new_source)
print('OK')
else:
print('The file is left intact!')
print('Later')
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment