diff --git a/music21/musicxml/m21ToXml.py b/music21/musicxml/m21ToXml.py index 0af7d5dd71..ecc994b177 100644 --- a/music21/musicxml/m21ToXml.py +++ b/music21/musicxml/m21ToXml.py @@ -6398,6 +6398,16 @@ def staffLayoutToXmlStaffDetails(self, staffLayout): mxStaffLines.text = str(staffLayout.staffLines) # TODO: staff-tuning + if hasattr(staffLayout, 'fretboard'): + tuning_pitches = staffLayout.fretboard.tuning + for i in range(len(tuning_pitches)): + mxStaffTuning = SubElement(mxStaffDetails, 'staff-tuning') + mxStaffTuning.set('line', str(i + 1)) + mxTuningStep = SubElement(mxStaffTuning, 'tuning-step') + mxTuningStep.text = tuning_pitches[i].name + mxTuningOctave = SubElement(mxStaffTuning, 'tuning-octave') + mxTuningOctave.text = str(tuning_pitches[i].octave) + # TODO: capo # TODO: staff-size return mxStaffDetails diff --git a/music21/musicxml/xmlToM21.py b/music21/musicxml/xmlToM21.py index 496a29e030..61acad64b8 100644 --- a/music21/musicxml/xmlToM21.py +++ b/music21/musicxml/xmlToM21.py @@ -5948,7 +5948,18 @@ def xmlStaffLayoutFromStaffDetails( except ValueError: warnings.warn( f'Got an incorrect staff-type in details: {mxStaffType}', MusicXMLWarning) - # TODO: staff-tuning* + + mxStaffTuning = mxDetails.findall('staff-tuning') + if mxStaffTuning is not None: + tuning_pitches = [] + for i in range(len(mxStaffTuning)): + staff_tuning = mxStaffTuning[i] + tuning_step = staff_tuning.find('tuning-step').text + tuning_octave = int(staff_tuning.find('tuning-octave').text) + tuning_pitches.append(pitch.Pitch(tuning_step + str(tuning_octave))) + fretboard = tablature.FretBoard.getFretBoardFromTuning(tuning_pitches) + setattr(stl, 'fretboard', fretboard) + # TODO: capo seta(stl, mxDetails, 'staff-size', transform=_floatOrIntStr) # TODO: musicxml 4: staff-size has a scaling attribute for the notation diff --git a/music21/tablature.py b/music21/tablature.py index 0de66c33bb..7c71d34006 100644 --- a/music21/tablature.py +++ b/music21/tablature.py @@ -244,6 +244,14 @@ def getPitches(self): return pitchList + @staticmethod + def getFretBoardFromTuning(pitches): + standard_fret_boards = [GuitarFretBoard, UkeleleFretBoard, + BassGuitarFretBoard, MandolinFretBoard] + for standard_fret_board in standard_fret_boards: + if pitches == standard_fret_board().tuning: + return standard_fret_board() + return CustomFretBoard(pitches) class FirstFret: ''' @@ -344,6 +352,17 @@ def __init__(self, fretNotes=None, displayFrets=4): super().__init__(numStrings, fretNotes, displayFrets) self.tuning = [pitch.Pitch('G3'), pitch.Pitch('D4'), pitch.Pitch('A4'), pitch.Pitch('E5')] + +class CustomFretBoard(FretBoard): + ''' + A custom fretboard tuned with any list of pitches + ''' + + def __init__(self, pitches, fretNotes=None, displayFrets=4): + numStrings = len(pitches) + super().__init__(numStrings, fretNotes, displayFrets) + + self.tuning = pitches.copy() # ------------------------------------------------------------------------------