Skip to content

Note Density

note_density

note_density(
    score: Score, timetype: Optional[str] = "quarters"
) -> float

Returns the number of notes per quarter or per second in a Score as a float.

Specifically, compute note density as (number of notes - 1) divided by the time span from the first note onset to the last note onset. The subtraction of 1 ensures that density is measured in terms of intervals between notes. If there are no notes, return 0.0.

Author: Tai Nakamura

Parameters:

  • score (Score) –

    The musical score to analyze.

  • timetype (str, default: 'quarters' ) –

    Time unit for calculation:

    • 'quarters': notes per quarter (default)
    • 'seconds' : notes per second

Returns:

  • float

    Computed note density. Returns 0.0 if the score is empty or if all notes have the same onset time (i.e., time span equals zero).

Raises:

  • ValueError

    If timetype is not 'quarters' or 'seconds'.

Examples:

>>> score = Score.from_melody([60, 62, 64, 65])  # all quarter notes
>>> note_density(score, timetype='quarters')
1.0
>>> from amads.core.timemap import TimeMap
>>> score = Score.from_melody([60, 62, 64, 65])  # all quarter notes
>>> score.time_map = TimeMap(qpm = 120)  # set QPM to 120
>>> note_density(score, timetype='seconds')
2.0
>>> score = Score.from_melody([60, 62, 64, 65], durations = [1.0, 2.0, 3.0, 4.0])  # mixed durations
>>> note_density(score, timetype='quarters')
0.5
Source code in amads/time/notedensity.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
def note_density(score: Score, timetype: Optional[str] = "quarters") -> float:
    """
    Returns the number of notes per quarter or per second in a Score as a float.

    Specifically, compute note density as (number of notes - 1) divided by
    the time span from the first note onset to the last note onset.
    The subtraction of 1 ensures that density is measured in terms
    of intervals between notes.
    If there are no notes, return 0.0.

    <small>**Author**: Tai Nakamura</small>

    Parameters
    ----------
    score : Score
        The musical score to analyze.
    timetype : str, optional, default='quarters'
        Time unit for calculation:

        - 'quarters': notes per quarter (default)
        - 'seconds' : notes per second

    Returns
    -------
    float
        Computed note density. Returns 0.0 if the score
        is empty or if all notes have the same onset time
        (i.e., time span equals zero).

    Raises
    ------
    ValueError
        If `timetype` is not 'quarters' or 'seconds'.

    Examples
    --------
    >>> score = Score.from_melody([60, 62, 64, 65])  # all quarter notes
    >>> note_density(score, timetype='quarters')
    1.0
    >>> from amads.core.timemap import TimeMap
    >>> score = Score.from_melody([60, 62, 64, 65])  # all quarter notes
    >>> score.time_map = TimeMap(qpm = 120)  # set QPM to 120
    >>> note_density(score, timetype='seconds')
    2.0
    >>> score = Score.from_melody([60, 62, 64, 65], durations = [1.0, 2.0, 3.0, 4.0])  # mixed durations
    >>> note_density(score, timetype='quarters')
    0.5
    """
    notes = score.get_sorted_notes()
    if not notes:
        return 0.0

    if timetype == "seconds":
        if score.units_are_seconds:
            start_onset = notes[0].onset
            end_onset = notes[-1].onset
        else:
            start_onset = score.time_map.quarter_to_time(notes[0].onset)
            end_onset = score.time_map.quarter_to_time(notes[-1].onset)
    elif timetype == "quarters":
        if score.units_are_seconds:
            start_onset = score.time_map.time_to_quarter(notes[0].onset)
            end_onset = score.time_map.time_to_quarter(notes[-1].onset)
        else:
            start_onset = notes[0].onset
            end_onset = notes[-1].onset
    else:
        raise ValueError(
            f"Invalid timetype: {timetype}. Use 'quarters' or 'seconds'."
        )
    duration = end_onset - start_onset
    if duration <= 0:
        return 0.0
    return (len(notes) - 1) / duration