Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clef map testing #396

Merged
merged 13 commits into from
Oct 29, 2024
17 changes: 6 additions & 11 deletions partitura/musicanalysis/note_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@

import types
from typing import List, Union, Tuple
from partitura.utils import ensure_notearray, ensure_rest_array
from partitura.utils import (
ensure_notearray,
ensure_rest_array,
clef_sign_to_int
)
from partitura.score import ScoreLike
from collections import defaultdict

Expand Down Expand Up @@ -554,15 +558,6 @@ def clef_feature(na, part, **kwargs):
see staff_feature for this information.
"""
notes = {n.id: n for n in part.notes_tied}
numerical_clef_dict = {
"G": 0,
"F": 1,
"C": 2,
"percussion": 3,
"TAB": 4,
"jianpu": 5,
"none": 6,
}
names = ["clef_sign", "clef_line", "clef_octave_change"]
clef_dict = defaultdict(list)
staff_numbers = set()
Expand Down Expand Up @@ -600,7 +595,7 @@ def clef_feature(na, part, **kwargs):
clef_idx = clef_dict[interpolator_key][0](time)
clef = clef_dict[clef_key][int(clef_idx)]
sign = clef.sign or "none"
W[i, 0] = numerical_clef_dict[sign]
W[i, 0] = clef_sign_to_int(sign)
W[i, 1] = clef.line or 0
W[i, 2] = clef.octave_change or 0

Expand Down
72 changes: 71 additions & 1 deletion partitura/score.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
object). This object serves as a timeline at which musical elements
are registered in terms of their start and end times.
"""

from copy import copy, deepcopy
from collections import defaultdict
from collections.abc import Iterable
Expand Down Expand Up @@ -46,6 +45,7 @@
key_mode_to_int,
_OrderedSet,
update_note_ids_after_unfolding,
clef_sign_to_int,
)
from partitura.utils.generic import interp1d
from partitura.utils.music import transpose_note, step2pc
Expand Down Expand Up @@ -229,6 +229,76 @@ def key_signature_map(self):
fill_value="extrapolate",
)

@property
def clef_map(self):
"""A function mapping timeline times to the clef in each
staff at that time. The function can take scalar
values or lists/arrays of values

Returns
-------
function
The mapping function
"""
clefs = np.array(
[
(
c.start.t,
c.staff,
clef_sign_to_int(c.sign),
c.line,
c.octave_change if c.octave_change is not None else 0
)
for c in self.iter_all(Clef)
]
)

interpolators = []
for s in range(1, self.number_of_staves + 1):
staff_clefs = clefs[clefs[:, 1] == s]
if len(staff_clefs) == 0:
# default treble clef
staff, clef, line, octave_change = s, clef_sign_to_int("none"), 0, 0
Comment on lines +260 to +261

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe change this comment as the default is now "none"?


warnings.warn(
"No clefs found on staff {}, assuming {} clef.".format(s, clef)
)
if self.first_point is None:
t0, tN = 0, 0
else:
t0 = self.first_point.t
tN = self.last_point.t
staff_clefs = np.array(
[
(t0, staff, clef, line, octave_change),
(tN, staff, clef, line, octave_change),
]
)
elif len(staff_clefs) == 1:
# If there is only a single clef
staff_clefs = np.array([staff_clefs[0, :], staff_clefs[0, :]])

if staff_clefs[0, 0] > self.first_point.t:
staff_clefs = np.vstack(
((self.first_point.t, *staff_clefs[0, 1:]), staff_clefs)
)

interpolators.append(
interp1d(
staff_clefs[:, 0],
staff_clefs[:, 1:],
axis=0,
kind="previous",
bounds_error=False,
fill_value="extrapolate",
)
)

def collator(time: Union[int, np.ndarray]) -> np.ndarray:
return np.array([interpolator(time) for interpolator in interpolators], dtype=int)

return collator

@property
def measure_map(self):
"""A function mapping timeline times to the start and end of
Expand Down
4 changes: 4 additions & 0 deletions partitura/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
pianoroll_to_notearray,
match_note_arrays,
key_mode_to_int,
clef_sign_to_int,
clef_int_to_sign,
remove_silence_from_performed_part,
note_array_from_part_list,
slice_notearray_by_time,
Expand Down Expand Up @@ -74,6 +76,8 @@
"key_name_to_fifths_mode",
"fifths_mode_to_key_name",
"key_mode_to_int",
"clef_sign_to_int",
"clef_int_to_sign",
"pitch_spelling_to_midi_pitch",
"pitch_spelling_to_note_name",
"show_diff",
Expand Down
12 changes: 12 additions & 0 deletions partitura/utils/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -729,3 +729,15 @@
"vii": (7, "M"),
},
}

#["G", "F", "C", "percussion", "TAB", "jianpu", "none"]
CLEF_TO_INT = {
"G": 0,
"F": 1,
"C": 2,
"percussion": 3,
"TAB": 4,
"jianpu": 5,
"none": 6,
}
INT_TO_CLEF = {v: k for k, v in CLEF_TO_INT.items()}
6 changes: 6 additions & 0 deletions partitura/utils/music.py
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,12 @@ def key_int_to_mode(mode):
raise ValueError("Unknown mode {}".format(mode))


def clef_sign_to_int(clef_sign: str) -> int:
return CLEF_TO_INT[clef_sign]

def clef_int_to_sign(clef_int: int) -> str:
return INT_TO_CLEF[clef_int]

def estimate_symbolic_duration(
dur, div, eps=10**-3, return_com_durations=False
) -> Union[Dict[str, Any], Tuple[Dict[str, Any]]]:
Expand Down
5 changes: 5 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@
for fn in ["test_ts_map_ts_starts_not_at_zero.xml"]
]

CLEF_MAP_TESTFILES = [
os.path.join(MUSICXML_PATH, fn)
for fn in ["test_clef_map.musicxml"]
]

REST_ARRAY_TESTFILES = [
os.path.join(MUSICXML_PATH, fn)
for fn in ["test_unfold_complex.xml", "test_rest.musicxml"]
Expand Down
Loading
Loading