hyperscore 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,128 @@
1
+ Metadata-Version: 2.3
2
+ Name: hyperscore
3
+ Version: 0.1.0
4
+ Summary: Structural music composition framework
5
+ Author: inaba1115
6
+ Author-email: inaba1115 <inaba1115@gmail.com>
7
+ Classifier: Programming Language :: Python :: 3 :: Only
8
+ Classifier: Programming Language :: Python :: 3.10
9
+ Classifier: Topic :: Multimedia :: Sound/Audio
10
+ Classifier: Topic :: Scientific/Engineering
11
+ Requires-Dist: lark>=1.3.1
12
+ Requires-Dist: mido>=1.3.3
13
+ Requires-Dist: python-rtmidi>=1.5.8
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+
17
+ # hyperscore
18
+
19
+ **hyperscore** is a structural music composition framework that models music as explicit, composable structure rather than notation or performance.
20
+
21
+ Time is represented uniformly using immutable time spans, pitch is handled as pitch-class structure independent of register, and rhythm is expressed as relative proportions that are resolved into concrete time only when needed. External formats such as MIDI are treated as lossy boundaries rather than as the source of truth.
22
+
23
+ hyperscore is designed for experimentation, analysis, and integration with algorithmic systems, rather than for score engraving or DAW-style workflows.
24
+
25
+ ---
26
+
27
+ ## Minimal example
28
+
29
+ This minimal example demonstrates the core hyperscore workflow: selecting pitch material using theory objects, describing rhythm structurally, generating time-based events, and exporting the result to MIDI.
30
+
31
+ ```python
32
+ from hyperscore import CHORDS, Score, parse_rhythm
33
+ from hyperscore.core import NoteEvent, bpm_to_ms
34
+ from hyperscore.io import MidiExporter
35
+ from hyperscore.rhythm import rhythm_ast_to_timespans
36
+
37
+ # ----------------
38
+ # theory
39
+ # ----------------
40
+ chord = CHORDS["major7"]
41
+
42
+ pitches = [n for n in range(60, 72) if n % 12 in chord.intervals]
43
+ pitch_iter = iter(pitches)
44
+
45
+ # ----------------
46
+ # rhythm
47
+ # ----------------
48
+ ast = parse_rhythm("1*4")
49
+ total = int(bpm_to_ms(120, 1))
50
+ spans = rhythm_ast_to_timespans(ast, total=total)
51
+
52
+ # ----------------
53
+ # score
54
+ # ----------------
55
+ score = Score()
56
+
57
+ score.add_timespans(
58
+ spans,
59
+ factory=lambda span: NoteEvent(
60
+ pitch=next(pitch_iter),
61
+ velocity=100,
62
+ span=span,
63
+ channel=0,
64
+ ),
65
+ )
66
+
67
+ # ----------------
68
+ # output
69
+ # ----------------
70
+ MidiExporter().export(score, "example.mid")
71
+ ```
72
+
73
+ This example intentionally avoids musical interpretation and focuses on structural composition. Pitch, rhythm, and time are treated as independent layers that are combined explicitly.
74
+
75
+ ---
76
+
77
+ ## What this example demonstrates
78
+
79
+ - Rhythm is expressed as **structure**, not notation
80
+ - Time is handled explicitly via immutable `TimeSpan` objects
81
+ - Pitch and rhythm are **independent layers**
82
+ - MIDI is treated as a **lossy output format**
83
+
84
+ For more advanced usage, see:
85
+
86
+ - `examples/`
87
+ - `tests/test_smoke.py`
88
+
89
+ ---
90
+
91
+ ## Optional: ZippedNotes shortcut
92
+
93
+ For simple sequential note generation without explicit TimeSpan construction, hyperscore provides the `ZippedNotes` convenience API.
94
+
95
+ This approach is suitable for basic sketches or quick tests, but offers less control than TimeSpan-based workflows.
96
+
97
+ ```python
98
+ from hyperscore import Score
99
+ from hyperscore.core import NoteEvent
100
+
101
+ score = Score()
102
+
103
+ score.add(
104
+ pitch=[60, 64, 67, 71], # C major 7 chord tones
105
+ velocity=[100],
106
+ duration=[125],
107
+ channel=[0],
108
+ event_factory=lambda **kw: NoteEvent(
109
+ pitch=kw["pitch"],
110
+ velocity=kw["velocity"],
111
+ span=kw["span"],
112
+ channel=kw["channel"],
113
+ ),
114
+ )
115
+ ```
116
+
117
+ For complex timing, transformations, or algorithmic rhythm generation, prefer TimeSpan-based workflows using `parse_rhythm`, `rhythm_ast_to_timespans`, and `TimeSpanPipeline`.
118
+
119
+ ---
120
+
121
+ ## Project status
122
+
123
+ - Python >= 3.10
124
+ - Typed (`py.typed`)
125
+ - Experimental / research-oriented
126
+ - API may evolve between minor versions
127
+
128
+ hyperscore favors clarity of structure and explicitness of time over completeness or stylistic prescription.
@@ -0,0 +1,112 @@
1
+ # hyperscore
2
+
3
+ **hyperscore** is a structural music composition framework that models music as explicit, composable structure rather than notation or performance.
4
+
5
+ Time is represented uniformly using immutable time spans, pitch is handled as pitch-class structure independent of register, and rhythm is expressed as relative proportions that are resolved into concrete time only when needed. External formats such as MIDI are treated as lossy boundaries rather than as the source of truth.
6
+
7
+ hyperscore is designed for experimentation, analysis, and integration with algorithmic systems, rather than for score engraving or DAW-style workflows.
8
+
9
+ ---
10
+
11
+ ## Minimal example
12
+
13
+ This minimal example demonstrates the core hyperscore workflow: selecting pitch material using theory objects, describing rhythm structurally, generating time-based events, and exporting the result to MIDI.
14
+
15
+ ```python
16
+ from hyperscore import CHORDS, Score, parse_rhythm
17
+ from hyperscore.core import NoteEvent, bpm_to_ms
18
+ from hyperscore.io import MidiExporter
19
+ from hyperscore.rhythm import rhythm_ast_to_timespans
20
+
21
+ # ----------------
22
+ # theory
23
+ # ----------------
24
+ chord = CHORDS["major7"]
25
+
26
+ pitches = [n for n in range(60, 72) if n % 12 in chord.intervals]
27
+ pitch_iter = iter(pitches)
28
+
29
+ # ----------------
30
+ # rhythm
31
+ # ----------------
32
+ ast = parse_rhythm("1*4")
33
+ total = int(bpm_to_ms(120, 1))
34
+ spans = rhythm_ast_to_timespans(ast, total=total)
35
+
36
+ # ----------------
37
+ # score
38
+ # ----------------
39
+ score = Score()
40
+
41
+ score.add_timespans(
42
+ spans,
43
+ factory=lambda span: NoteEvent(
44
+ pitch=next(pitch_iter),
45
+ velocity=100,
46
+ span=span,
47
+ channel=0,
48
+ ),
49
+ )
50
+
51
+ # ----------------
52
+ # output
53
+ # ----------------
54
+ MidiExporter().export(score, "example.mid")
55
+ ```
56
+
57
+ This example intentionally avoids musical interpretation and focuses on structural composition. Pitch, rhythm, and time are treated as independent layers that are combined explicitly.
58
+
59
+ ---
60
+
61
+ ## What this example demonstrates
62
+
63
+ - Rhythm is expressed as **structure**, not notation
64
+ - Time is handled explicitly via immutable `TimeSpan` objects
65
+ - Pitch and rhythm are **independent layers**
66
+ - MIDI is treated as a **lossy output format**
67
+
68
+ For more advanced usage, see:
69
+
70
+ - `examples/`
71
+ - `tests/test_smoke.py`
72
+
73
+ ---
74
+
75
+ ## Optional: ZippedNotes shortcut
76
+
77
+ For simple sequential note generation without explicit TimeSpan construction, hyperscore provides the `ZippedNotes` convenience API.
78
+
79
+ This approach is suitable for basic sketches or quick tests, but offers less control than TimeSpan-based workflows.
80
+
81
+ ```python
82
+ from hyperscore import Score
83
+ from hyperscore.core import NoteEvent
84
+
85
+ score = Score()
86
+
87
+ score.add(
88
+ pitch=[60, 64, 67, 71], # C major 7 chord tones
89
+ velocity=[100],
90
+ duration=[125],
91
+ channel=[0],
92
+ event_factory=lambda **kw: NoteEvent(
93
+ pitch=kw["pitch"],
94
+ velocity=kw["velocity"],
95
+ span=kw["span"],
96
+ channel=kw["channel"],
97
+ ),
98
+ )
99
+ ```
100
+
101
+ For complex timing, transformations, or algorithmic rhythm generation, prefer TimeSpan-based workflows using `parse_rhythm`, `rhythm_ast_to_timespans`, and `TimeSpanPipeline`.
102
+
103
+ ---
104
+
105
+ ## Project status
106
+
107
+ - Python >= 3.10
108
+ - Typed (`py.typed`)
109
+ - Experimental / research-oriented
110
+ - API may evolve between minor versions
111
+
112
+ hyperscore favors clarity of structure and explicitness of time over completeness or stylistic prescription.
@@ -0,0 +1,59 @@
1
+ [project]
2
+ name = "hyperscore"
3
+ version = "0.1.0"
4
+ description = "Structural music composition framework"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "inaba1115", email = "inaba1115@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.10"
10
+ dependencies = [
11
+ "lark>=1.3.1",
12
+ "mido>=1.3.3",
13
+ "python-rtmidi>=1.5.8",
14
+ ]
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3 :: Only",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Topic :: Multimedia :: Sound/Audio",
19
+ "Topic :: Scientific/Engineering",
20
+ ]
21
+
22
+ [build-system]
23
+ requires = ["uv_build>=0.9.21,<0.10.0"]
24
+ build-backend = "uv_build"
25
+
26
+ [dependency-groups]
27
+ dev = [
28
+ "pytest>=9.0.2",
29
+ "pytest-cov>=7.0.0",
30
+ "ruff>=0.14.10",
31
+ "ty>=0.0.8",
32
+ ]
33
+
34
+ [tool.ruff]
35
+ line-length = 120
36
+
37
+ [tool.ruff.lint]
38
+ extend-select = ["I", "RUF", "UP", "TID", "C", "SIM", "PTH"]
39
+ extend-ignore = ["C901", "F401", "F403", "E722", "RUF007"]
40
+
41
+ [tool.pytest.ini_options]
42
+ minversion = "9.0"
43
+ testpaths = ["tests"]
44
+ addopts = [
45
+ "-ra",
46
+ "--strict-markers",
47
+ ]
48
+
49
+ [tool.coverage.run]
50
+ branch = true
51
+ source = ["hyperscore"]
52
+ omit = [
53
+ "*/__init__.py",
54
+ ]
55
+
56
+ [tool.coverage.report]
57
+ show_missing = true
58
+ skip_covered = true
59
+ precision = 2
@@ -0,0 +1,35 @@
1
+ """
2
+ hyperscore: a structural music composition framework.
3
+
4
+ hyperscore provides a minimal, composable foundation for
5
+ algorithmic and structural music composition.
6
+
7
+ Key ideas
8
+ ---------
9
+ - Music is modeled as explicit structure, not notation.
10
+ - Time is represented uniformly using immutable TimeSpan objects.
11
+ - Pitch is handled as pitch-class structure, independent of register.
12
+ - External formats (e.g. MIDI) are treated as lossy boundaries.
13
+
14
+ The library is organized into clear layers:
15
+ - core: time and event primitives
16
+ - rhythm: structural rhythm descriptions
17
+ - theory: pitch-class and scale structures
18
+ - io: adapters to external systems
19
+
20
+ hyperscore is designed for experimentation, analysis,
21
+ and integration with other algorithmic systems,
22
+ rather than for direct score engraving or DAW replacement.
23
+ """
24
+
25
+ from hyperscore.core import Score, ZippedNotes
26
+ from hyperscore.rhythm import parse_rhythm
27
+ from hyperscore.theory import CHORDS, SCALES
28
+
29
+ __all__ = [
30
+ "CHORDS",
31
+ "SCALES",
32
+ "Score",
33
+ "ZippedNotes",
34
+ "parse_rhythm",
35
+ ]
@@ -0,0 +1,42 @@
1
+ """
2
+ Core time and event primitives for hyperscore.
3
+
4
+ This module defines the foundational abstractions used throughout
5
+ hyperscore:
6
+
7
+ - TimeSpan: immutable representation of linear time intervals
8
+ - NoteEvent: minimal time-bounded musical event
9
+ - Score: container and coordinator for events on a time axis
10
+ - TimeSpanPipeline: composable transformations over TimeSpans
11
+ - ZippedNotes: convenience API for simple sequential note generation
12
+
13
+ Design principles
14
+ -----------------
15
+ - Time is represented explicitly and uniformly via TimeSpan.
16
+ - Core abstractions are unit-agnostic (milliseconds, ticks, etc.).
17
+ - Musical interpretation (harmony, rhythm, MIDI) is handled in
18
+ higher-level modules.
19
+ - All core objects favor immutability and composability.
20
+
21
+ This module is intentionally minimal and free of musical semantics.
22
+ It serves as the stable foundation upon which rhythm, theory,
23
+ and I/O layers are built.
24
+ """
25
+
26
+ from .score import NoteEvent, Score, ZippedNotes
27
+ from .time import TimeSpan, bpm_to_ms
28
+ from .time_pipeline import TimeSpanPipeline
29
+ from .time_transforms import gate, probability, shift, stretch
30
+
31
+ __all__ = [
32
+ "NoteEvent",
33
+ "Score",
34
+ "TimeSpan",
35
+ "TimeSpanPipeline",
36
+ "ZippedNotes",
37
+ "bpm_to_ms",
38
+ "gate",
39
+ "probability",
40
+ "shift",
41
+ "stretch",
42
+ ]