fork download
  1. names = list('CDEFGAB')
  2. solfege = ['Do', 'Re', 'Mi', 'Fa', 'So', 'La', 'Ti']
  3. offsets = {'C': 0, 'D': 2, 'E': 4, 'F': 5, 'G': 7, 'A': 9, 'B': 11}
  4. offsets.update({note + '#': off + 1 for note, off in offsets.items()})
  5. offsets.update({note + 'b': off - 1 for note, off in offsets.items() if len(note) == 1})
  6.  
  7.  
  8. def clean_name(note_name):
  9. """Cancel out sharp/flat pairs, and change a double sharp to the standard x symbol."""
  10. base = note_name[0]
  11. offset = note_name.count('#') - note_name.count('b')
  12. return base + ('' if offset == 0 else '#' if offset == 1 else 'x' if offset == 2 else 'b' * -offset)
  13.  
  14.  
  15. def note(scale, position):
  16. base_name = scale[0]
  17. suffix = scale[1:]
  18. start = names.index(base_name)
  19. # Wrap to start the scale in the right place
  20. scale_names = [name + suffix for name in names[start:] + names[:start]]
  21. offset = offsets[scale]
  22. # expected offsets is just the standard 0,2,4,5,7,9,11
  23. expected_offsets = sorted({v for k, v in offsets.items() if len(k) == 1})
  24. # Find our current offsets, based on the note names
  25. true_offsets = [(offsets[note] - offset) % 12 for note in scale_names]
  26. # See how we need to add accidentals to get a proper major scale
  27. adjustments = [expected - true for expected, true in zip(expected_offsets, true_offsets)]
  28. scale_names = [clean_name(name + ('#' if adj == 1 else 'b' if adj == -1 else '')) for name, adj in
  29. zip(scale_names, adjustments)]
  30. return scale_names[solfege.index(position)]
  31.  
  32.  
  33. if __name__ == '__main__':
  34. print(note('C', 'Do')) # C
  35. print(note('C', 'Re')) # D
  36. print(note('C', 'Mi')) # E
  37. print(note('D', 'Mi')) # F#
  38. print(note('Bb', 'Fa')) # Eb
  39. print(note('A#', 'Fa')) # D#
  40. print(note('A#', 'Mi')) # Cx (C##)
  41. print(note('Fb', 'Fa')) # Bbb
Success #stdin #stdout 0.04s 9280KB
stdin
Standard input is empty
stdout
C
D
E
F#
Eb
D#
Cx
Bbb