concertina_helper.penalties
1from collections.abc import Callable 2 3from .type_defs import Direction 4 5from .layouts.bisonoric import ( 6 AnnotatedBisonoricFingering, BisonoricFingering) 7 8 9PenaltyFunction = Callable[[ 10 AnnotatedBisonoricFingering, AnnotatedBisonoricFingering], float] 11 12# TODO: Penalize outer columns? 13# TODO: Penalize top row? 14 15 16def penalize_bellows_change(cost: float) -> PenaltyFunction: 17 ''' 18 Penalize fingerings where the bellows changes direction between notes 19 ''' 20 def calculate( 21 f1: AnnotatedBisonoricFingering, 22 f2: AnnotatedBisonoricFingering) -> float: 23 return cost if f1.fingering.direction != f2.fingering.direction else 0 24 return calculate 25 26 27def penalize_finger_in_same_column(cost: float) -> PenaltyFunction: 28 ''' 29 Penalize fingerings where one finger changes rows between notes 30 ''' 31 def calculate( 32 f1: AnnotatedBisonoricFingering, 33 f2: AnnotatedBisonoricFingering) -> float: 34 ''' 35 This assumes fingers should be moving between notes: It will need to change 36 if this is extended to cover sustained bass notes under a melody. 37 ''' 38 return ( 39 cost if _find_columns_used(f1.fingering) == 40 _find_columns_used(f2.fingering) 41 else 0) 42 return calculate 43 44 45def penalize_outer_fingers(cost: float) -> PenaltyFunction: 46 ''' 47 Penalize fingerings that use outer fingers of either hand instead of inner. 48 This is useful as a tiebreaker. 49 ''' 50 def calculate( 51 f1: AnnotatedBisonoricFingering, 52 f2: AnnotatedBisonoricFingering) -> float: 53 return cost * sum(1 / abs(i) for i in _find_columns_used(f2.fingering)) 54 return calculate 55 56 57def penalize_pull_at_start_of_measure(cost: float) -> PenaltyFunction: 58 ''' 59 Penalize fingerings where a pull begins a measure; 60 Hitting the downbeat with a push can be more musical.''' 61 def calculate( 62 f1: AnnotatedBisonoricFingering, 63 f2: AnnotatedBisonoricFingering) -> float: 64 return cost if f2.fingering.direction == Direction.PULL else 0 65 return calculate 66 67 68def _find_columns_used(fingering: BisonoricFingering) -> set[int]: 69 ''' 70 Returns a set of integers representing the buttons used. 71 - On the left: 1 2 3 4 5 72 - On the right: -5 -4 -3 -2 -1 73 74 Taking the inverse of the absolute value gives us a number 75 which is small for the inner fingers, but larger for the pinkies. 76 77 This is used in `penalize_outer_fingers`. 78 ''' 79 used = set() 80 for row in fingering.left_mask: 81 for i, button in enumerate(reversed(row)): 82 if button: 83 used.add(i+1) 84 for row in fingering.right_mask: 85 for i, button in enumerate(row): 86 if button: 87 used.add(-(i+1)) 88 return used
def
penalize_bellows_change( cost: float) -> collections.abc.Callable[[concertina_helper.layouts.bisonoric.AnnotatedBisonoricFingering, concertina_helper.layouts.bisonoric.AnnotatedBisonoricFingering], float]:
17def penalize_bellows_change(cost: float) -> PenaltyFunction: 18 ''' 19 Penalize fingerings where the bellows changes direction between notes 20 ''' 21 def calculate( 22 f1: AnnotatedBisonoricFingering, 23 f2: AnnotatedBisonoricFingering) -> float: 24 return cost if f1.fingering.direction != f2.fingering.direction else 0 25 return calculate
Penalize fingerings where the bellows changes direction between notes
def
penalize_finger_in_same_column( cost: float) -> collections.abc.Callable[[concertina_helper.layouts.bisonoric.AnnotatedBisonoricFingering, concertina_helper.layouts.bisonoric.AnnotatedBisonoricFingering], float]:
28def penalize_finger_in_same_column(cost: float) -> PenaltyFunction: 29 ''' 30 Penalize fingerings where one finger changes rows between notes 31 ''' 32 def calculate( 33 f1: AnnotatedBisonoricFingering, 34 f2: AnnotatedBisonoricFingering) -> float: 35 ''' 36 This assumes fingers should be moving between notes: It will need to change 37 if this is extended to cover sustained bass notes under a melody. 38 ''' 39 return ( 40 cost if _find_columns_used(f1.fingering) == 41 _find_columns_used(f2.fingering) 42 else 0) 43 return calculate
Penalize fingerings where one finger changes rows between notes
def
penalize_outer_fingers( cost: float) -> collections.abc.Callable[[concertina_helper.layouts.bisonoric.AnnotatedBisonoricFingering, concertina_helper.layouts.bisonoric.AnnotatedBisonoricFingering], float]:
46def penalize_outer_fingers(cost: float) -> PenaltyFunction: 47 ''' 48 Penalize fingerings that use outer fingers of either hand instead of inner. 49 This is useful as a tiebreaker. 50 ''' 51 def calculate( 52 f1: AnnotatedBisonoricFingering, 53 f2: AnnotatedBisonoricFingering) -> float: 54 return cost * sum(1 / abs(i) for i in _find_columns_used(f2.fingering)) 55 return calculate
Penalize fingerings that use outer fingers of either hand instead of inner. This is useful as a tiebreaker.
def
penalize_pull_at_start_of_measure( cost: float) -> collections.abc.Callable[[concertina_helper.layouts.bisonoric.AnnotatedBisonoricFingering, concertina_helper.layouts.bisonoric.AnnotatedBisonoricFingering], float]:
58def penalize_pull_at_start_of_measure(cost: float) -> PenaltyFunction: 59 ''' 60 Penalize fingerings where a pull begins a measure; 61 Hitting the downbeat with a push can be more musical.''' 62 def calculate( 63 f1: AnnotatedBisonoricFingering, 64 f2: AnnotatedBisonoricFingering) -> float: 65 return cost if f2.fingering.direction == Direction.PULL else 0 66 return calculate
Penalize fingerings where a pull begins a measure; Hitting the downbeat with a push can be more musical.