File size: 3,077 Bytes
37aeb1f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
91
92
93
94
"""drum_dynamics — utilities for the drum-velocity "humanization" thesis.

This package extracts the helper code that was previously duplicated inline
across the exploratory notebooks (``poc.ipynb`` and ``ml/notebooks/eda.ipynb``):

* :mod:`drum_dynamics.core.midi`     — MIDI note access helpers (``MidiNote``, ``Idx``),
  the General MIDI percussion map, and loading helpers built on ``partitura``.
* :mod:`drum_dynamics.viz.viz`       — piano-roll / drum-roll visualization.
* :mod:`drum_dynamics.viz.playback`  — in-notebook audio rendering via FluidSynth.

The goal of the wider project is to predict a "best-fitting" velocity for every
note in a MIDI *drum* track — i.e. humanizing dynamics, analogous to existing
tempo humanization. We focus on the Expanded Groove MIDI Dataset (E-GMD).
"""

import importlib

from .core.midi import (
    Idx,
    MidiNote,
    DRUM_MIDI_NAME,
    EGMD_EXTRA_MIDI_NAME,
    TONES_FORMAT,
    drum_name,
    midi_number_to_tone,
    load_note_array,
)
from .core.voicemap import CANONICAL_VOICES, PITCH_TO_VOICE, voice_of, voice_index
from .data.features import build_note_features
from .models.baselines import GlobalMeanBaseline, LookupTableBaseline
from .eval.metrics import mae, rmse, evaluate, wasserstein1d, hist_intersection
from .viz.viz import piano_roll, drums_roll
from .viz.playback import play_midi_file, play_midi_notes, set_soundfont, get_soundfont

__all__ = [
    "Idx",
    "MidiNote",
    "DRUM_MIDI_NAME",
    "EGMD_EXTRA_MIDI_NAME",
    "TONES_FORMAT",
    "drum_name",
    "midi_number_to_tone",
    "load_note_array",
    "CANONICAL_VOICES",
    "PITCH_TO_VOICE",
    "voice_of",
    "voice_index",
    "build_note_features",
    "GlobalMeanBaseline",
    "LookupTableBaseline",
    "mae",
    "rmse",
    "evaluate",
    "wasserstein1d",
    "hist_intersection",
    "NUMERIC_FEATURES",
    "MAX_LEN",
    "build_genre_vocab",
    "bpm_stats",
    "build_split_tensors",
    "scatter_predictions",
    "VelocityTransformer",
    "warm_start_backbone",
    "piano_roll",
    "drums_roll",
    "play_midi_file",
    "play_midi_notes",
    "set_soundfont",
    "get_soundfont",
]

# Torch-dependent symbols are imported lazily: importing this package (or its
# light submodules like `features`/`midi`) must NOT pull in torch. On macOS,
# loading torch's OpenMP runtime before LightGBM's segfaults, so the tabular
# path must stay torch-free. Accessing these names (or importing the
# `.model`/`.seqdata` submodules directly) loads torch on demand.
_LAZY = {
    "NUMERIC_FEATURES": "data.seqdata",
    "MAX_LEN": "data.seqdata",
    "build_genre_vocab": "data.seqdata",
    "bpm_stats": "data.seqdata",
    "build_split_tensors": "data.seqdata",
    "scatter_predictions": "data.seqdata",
    "VelocityTransformer": "models.model",
    "warm_start_backbone": "models.model",
}


def __getattr__(name):
    if name in _LAZY:
        module = importlib.import_module(f".{_LAZY[name]}", __name__)
        return getattr(module, name)
    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")