klotho-cac 2.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.
- klotho_cac-2.1.0/LICENSE +15 -0
- klotho_cac-2.1.0/MANIFEST.in +7 -0
- klotho_cac-2.1.0/PKG-INFO +123 -0
- klotho_cac-2.1.0/README.md +82 -0
- klotho_cac-2.1.0/klotho/__init__.py +35 -0
- klotho_cac-2.1.0/klotho/aikous/__init__.py +42 -0
- klotho_cac-2.1.0/klotho/aikous/expression/__init__.py +29 -0
- klotho_cac-2.1.0/klotho/aikous/expression/dynamics.py +174 -0
- klotho_cac-2.1.0/klotho/aikous/expression/enevelopes.py +89 -0
- klotho_cac-2.1.0/klotho/aikous/instruments/__init__.py +1 -0
- klotho_cac-2.1.0/klotho/aikous/instruments/instrument.py +76 -0
- klotho_cac-2.1.0/klotho/aikous/parameters/__init__.py +1 -0
- klotho_cac-2.1.0/klotho/aikous/parameters/instruments.py +142 -0
- klotho_cac-2.1.0/klotho/aikous/parameters/parameter_tree.py +69 -0
- klotho_cac-2.1.0/klotho/chronos/__init__.py +53 -0
- klotho_cac-2.1.0/klotho/chronos/rhythm_pairs/__init__.py +3 -0
- klotho_cac-2.1.0/klotho/chronos/rhythm_pairs/rhythm_pair.py +102 -0
- klotho_cac-2.1.0/klotho/chronos/rhythm_trees/__init__.py +7 -0
- klotho_cac-2.1.0/klotho/chronos/rhythm_trees/algorithms.py +219 -0
- klotho_cac-2.1.0/klotho/chronos/rhythm_trees/meas.py +232 -0
- klotho_cac-2.1.0/klotho/chronos/rhythm_trees/rhythm_tree.py +212 -0
- klotho_cac-2.1.0/klotho/chronos/temporal_units/__init__.py +4 -0
- klotho_cac-2.1.0/klotho/chronos/temporal_units/algorithms.py +142 -0
- klotho_cac-2.1.0/klotho/chronos/temporal_units/temporal.py +865 -0
- klotho_cac-2.1.0/klotho/chronos/utils/__init__.py +3 -0
- klotho_cac-2.1.0/klotho/chronos/utils/beat.py +50 -0
- klotho_cac-2.1.0/klotho/chronos/utils/tempo.py +85 -0
- klotho_cac-2.1.0/klotho/chronos/utils/time_conversion.py +129 -0
- klotho_cac-2.1.0/klotho/skora/__init__.py +16 -0
- klotho_cac-2.1.0/klotho/skora/animation/__init__.py +1 -0
- klotho_cac-2.1.0/klotho/skora/animation/animate.py +193 -0
- klotho_cac-2.1.0/klotho/skora/notation/__init__.py +1 -0
- klotho_cac-2.1.0/klotho/skora/notation/notation.py +546 -0
- klotho_cac-2.1.0/klotho/skora/notation/notation_OLD.py +261 -0
- klotho_cac-2.1.0/klotho/skora/skora.py +329 -0
- klotho_cac-2.1.0/klotho/skora/visualization/__init__.py +3 -0
- klotho_cac-2.1.0/klotho/skora/visualization/field_plots.py +78 -0
- klotho_cac-2.1.0/klotho/skora/visualization/plots.py +999 -0
- klotho_cac-2.1.0/klotho/skora/visualization/ut_plots.py +54 -0
- klotho_cac-2.1.0/klotho/tonos/__init__.py +125 -0
- klotho_cac-2.1.0/klotho/tonos/chords/__init__.py +1 -0
- klotho_cac-2.1.0/klotho/tonos/chords/chord.py +144 -0
- klotho_cac-2.1.0/klotho/tonos/pitch/__init__.py +21 -0
- klotho_cac-2.1.0/klotho/tonos/pitch/pitch.py +173 -0
- klotho_cac-2.1.0/klotho/tonos/pitch/pitch_collections.py +610 -0
- klotho_cac-2.1.0/klotho/tonos/scales/__init__.py +1 -0
- klotho_cac-2.1.0/klotho/tonos/scales/scale.py +127 -0
- klotho_cac-2.1.0/klotho/tonos/systems/__init__.py +17 -0
- klotho_cac-2.1.0/klotho/tonos/systems/combination_product_sets/__init__.py +13 -0
- klotho_cac-2.1.0/klotho/tonos/systems/combination_product_sets/combination_product_networks.py +36 -0
- klotho_cac-2.1.0/klotho/tonos/systems/combination_product_sets/cps.py +435 -0
- klotho_cac-2.1.0/klotho/tonos/systems/harmonic_trees/__init__.py +7 -0
- klotho_cac-2.1.0/klotho/tonos/systems/harmonic_trees/algorithms.py +16 -0
- klotho_cac-2.1.0/klotho/tonos/systems/harmonic_trees/harmonic_tree.py +83 -0
- klotho_cac-2.1.0/klotho/tonos/systems/harmonic_trees/spectrum.py +207 -0
- klotho_cac-2.1.0/klotho/tonos/utils/__init__.py +17 -0
- klotho_cac-2.1.0/klotho/tonos/utils/frequency_conversion.py +175 -0
- klotho_cac-2.1.0/klotho/tonos/utils/harmonics.py +48 -0
- klotho_cac-2.1.0/klotho/tonos/utils/interval_normalization.py +179 -0
- klotho_cac-2.1.0/klotho/tonos/utils/intervals.py +251 -0
- klotho_cac-2.1.0/klotho/topos/__init__.py +55 -0
- klotho_cac-2.1.0/klotho/topos/collections/__init__.py +7 -0
- klotho_cac-2.1.0/klotho/topos/collections/patterns.py +226 -0
- klotho_cac-2.1.0/klotho/topos/collections/sequences.py +114 -0
- klotho_cac-2.1.0/klotho/topos/collections/sets.py +397 -0
- klotho_cac-2.1.0/klotho/topos/formal_grammars/__init__.py +5 -0
- klotho_cac-2.1.0/klotho/topos/formal_grammars/alphabets.py +582 -0
- klotho_cac-2.1.0/klotho/topos/formal_grammars/grammars.py +92 -0
- klotho_cac-2.1.0/klotho/topos/graphs/__init__.py +8 -0
- klotho_cac-2.1.0/klotho/topos/graphs/fields/__init__.py +1 -0
- klotho_cac-2.1.0/klotho/topos/graphs/fields/algorithms/__init__.py +1 -0
- klotho_cac-2.1.0/klotho/topos/graphs/fields/algorithms/field_algs.py +63 -0
- klotho_cac-2.1.0/klotho/topos/graphs/fields/fields.py +140 -0
- klotho_cac-2.1.0/klotho/topos/graphs/fields/functions/__init__.py +1 -0
- klotho_cac-2.1.0/klotho/topos/graphs/fields/functions/field_funcs.py +89 -0
- klotho_cac-2.1.0/klotho/topos/graphs/graphs.py +244 -0
- klotho_cac-2.1.0/klotho/topos/graphs/networks/__init__.py +1 -0
- klotho_cac-2.1.0/klotho/topos/graphs/networks/algorithms/__init__.py +1 -0
- klotho_cac-2.1.0/klotho/topos/graphs/networks/algorithms/network_algs.py +49 -0
- klotho_cac-2.1.0/klotho/topos/graphs/networks/networks.py +75 -0
- klotho_cac-2.1.0/klotho/topos/graphs/trees/__init__.py +2 -0
- klotho_cac-2.1.0/klotho/topos/graphs/trees/algorithms.py +171 -0
- klotho_cac-2.1.0/klotho/topos/graphs/trees/trees.py +379 -0
- klotho_cac-2.1.0/klotho/topos/random/__init__.py +4 -0
- klotho_cac-2.1.0/klotho/topos/random/rando.py +48 -0
- klotho_cac-2.1.0/klotho/types.py +83 -0
- klotho_cac-2.1.0/klotho/utils/__init__.py +0 -0
- klotho_cac-2.1.0/klotho/utils/algorithms/__init__.py +6 -0
- klotho_cac-2.1.0/klotho/utils/algorithms/costs.py +78 -0
- klotho_cac-2.1.0/klotho/utils/algorithms/cps_algorithms.py +37 -0
- klotho_cac-2.1.0/klotho/utils/algorithms/factors.py +42 -0
- klotho_cac-2.1.0/klotho/utils/data_structures/__init__.py +7 -0
- klotho_cac-2.1.0/klotho/utils/data_structures/dictionaries.py +5 -0
- klotho_cac-2.1.0/klotho/utils/data_structures/enums.py +32 -0
- klotho_cac-2.1.0/klotho/utils/data_structures/graphs.py +136 -0
- klotho_cac-2.1.0/klotho/utils/data_structures/group.py +29 -0
- klotho_cac-2.1.0/klotho/utils/playback/__init__.py +1 -0
- klotho_cac-2.1.0/klotho/utils/playback/player.py +378 -0
- klotho_cac-2.1.0/klotho/utils/playback/scheduler copy.py +175 -0
- klotho_cac-2.1.0/klotho/utils/playback/scheduler.py +281 -0
- klotho_cac-2.1.0/klotho_cac.egg-info/PKG-INFO +123 -0
- klotho_cac-2.1.0/klotho_cac.egg-info/SOURCES.txt +106 -0
- klotho_cac-2.1.0/klotho_cac.egg-info/dependency_links.txt +1 -0
- klotho_cac-2.1.0/klotho_cac.egg-info/requires.txt +10 -0
- klotho_cac-2.1.0/klotho_cac.egg-info/top_level.txt +1 -0
- klotho_cac-2.1.0/requirements.txt +10 -0
- klotho_cac-2.1.0/setup.cfg +4 -0
- klotho_cac-2.1.0/setup.py +47 -0
klotho_cac-2.1.0/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Creative Commons Attribution-ShareAlike 4.0 International License
|
|
2
|
+
|
|
3
|
+
Klotho © 2023 by Ryan Millett is licensed under CC BY-SA 4.0.
|
|
4
|
+
|
|
5
|
+
You are free to:
|
|
6
|
+
- Share — copy and redistribute the material in any medium or format
|
|
7
|
+
- Adapt — remix, transform, and build upon the material for any purpose, even commercially.
|
|
8
|
+
|
|
9
|
+
Under the following terms:
|
|
10
|
+
- Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
|
|
11
|
+
- ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.
|
|
12
|
+
|
|
13
|
+
No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
|
|
14
|
+
|
|
15
|
+
For the full license text, visit: http://creativecommons.org/licenses/by-sa/4.0/
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: klotho-cac
|
|
3
|
+
Version: 2.1.0
|
|
4
|
+
Summary: Graph-Oriented Computer-Assisted Composition in Python
|
|
5
|
+
Home-page: https://github.com/kr4g/Klotho
|
|
6
|
+
Author: Ryan Millett
|
|
7
|
+
Author-email: rmillett@mat.ucsb.edu
|
|
8
|
+
License: CC-BY-SA-4.0
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: License :: Other/Proprietary License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
16
|
+
Classifier: Topic :: Artistic Software
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: pandas
|
|
21
|
+
Requires-Dist: numpy
|
|
22
|
+
Requires-Dist: sympy
|
|
23
|
+
Requires-Dist: regex
|
|
24
|
+
Requires-Dist: matplotlib
|
|
25
|
+
Requires-Dist: tabulate
|
|
26
|
+
Requires-Dist: networkx
|
|
27
|
+
Requires-Dist: hypernetx
|
|
28
|
+
Requires-Dist: scipy
|
|
29
|
+
Requires-Dist: python-osc
|
|
30
|
+
Dynamic: author
|
|
31
|
+
Dynamic: author-email
|
|
32
|
+
Dynamic: classifier
|
|
33
|
+
Dynamic: description
|
|
34
|
+
Dynamic: description-content-type
|
|
35
|
+
Dynamic: home-page
|
|
36
|
+
Dynamic: license
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
Dynamic: requires-dist
|
|
39
|
+
Dynamic: requires-python
|
|
40
|
+
Dynamic: summary
|
|
41
|
+
|
|
42
|
+
# Klotho
|
|
43
|
+
`Klotho` is an open source computer-assisted composition toolkit implemented in Python. It is designed to work in tandem with external synthesis applications and as a general resource for the methods, models, works, and frameworks associated with the art and craft of music composition and metacomposition.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
Klotho works as both a Python scripting toolkit and 'on-the-fly' via a Python interpreter.
|
|
50
|
+
|
|
51
|
+
### Option 1: Install from PyPI (Recommended)
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install klotho-cac
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Option 2: Install from Source
|
|
58
|
+
|
|
59
|
+
1. **Clone the Repository**:
|
|
60
|
+
|
|
61
|
+
First, clone the Klotho repository by running the following command in your terminal or command prompt:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
git clone https://github.com/kr4g/Klotho.git
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
2. **Navigate to the `Klotho/` Directory**:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
cd Klotho/
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
3. **Install in Development Mode**:
|
|
74
|
+
|
|
75
|
+
Install Klotho in development mode (recommended for development):
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
pip install -e .
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Or install the required dependencies separately:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
pip install -r requirements.txt
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
<!-- 4. **Install Klotho (Development Mode)**:
|
|
88
|
+
|
|
89
|
+
To install Klotho in development mode, which allows you to modify the source code and have the changes reflected immediately:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
pip install -e .
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
5. **Play**:
|
|
96
|
+
|
|
97
|
+
To work with Klotho as an 'on-the-fly' compositional-aid, initiate a Python interpreter from within the `Klotho/` directory by running the command:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
Python
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Once the interpreter loads, import from `klotho` as needed. -->
|
|
104
|
+
|
|
105
|
+
## About
|
|
106
|
+
|
|
107
|
+
Klotho extends from a lineage of CAC-oriented theories and softwares. This means that, while Klotho provides many classes and functions for 'standard' music materials, its strengths are best utilized when working with more complex, abstract, or otherwise unconventional materials not easily accessible with standard notation softwares.
|
|
108
|
+
|
|
109
|
+
The ethos of Klotho draws heavily from the concepts and computations possible with patching-based softwares like [OpenMusic](https://openmusic-project.github.io/) (which also influenced [Bach](https://www.bachproject.net/) and [Cage](https://www.bachproject.net/cage/) for Max).
|
|
110
|
+
|
|
111
|
+
Klotho seeks to avoid this patching paradigm in favor of a high-level scripting syntax that more closely resembles the underlying mathematical expressions at play when working with computational composition tools. Many of Klotho's core features, particularly in the implementation of Rhythm Trees, adhere to a "LISP-like" presentation and programming paradigm inspired by the underlying Common LISP source code for OpenMusic. It is then also closer to the abstract, algebraic language of music in its symbolic representations.
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
[Klotho](https://github.com/kr4g/Klotho) by Ryan Millett is licensed under [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/?ref=chooser-v1).
|
|
116
|
+
|
|
117
|
+

|
|
118
|
+

|
|
119
|
+

|
|
120
|
+
|
|
121
|
+
Klotho © 2023 by Ryan Millett is licensed under CC BY-SA 4.0. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/
|
|
122
|
+
|
|
123
|
+
---
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Klotho
|
|
2
|
+
`Klotho` is an open source computer-assisted composition toolkit implemented in Python. It is designed to work in tandem with external synthesis applications and as a general resource for the methods, models, works, and frameworks associated with the art and craft of music composition and metacomposition.
|
|
3
|
+
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
Klotho works as both a Python scripting toolkit and 'on-the-fly' via a Python interpreter.
|
|
9
|
+
|
|
10
|
+
### Option 1: Install from PyPI (Recommended)
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install klotho-cac
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### Option 2: Install from Source
|
|
17
|
+
|
|
18
|
+
1. **Clone the Repository**:
|
|
19
|
+
|
|
20
|
+
First, clone the Klotho repository by running the following command in your terminal or command prompt:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
git clone https://github.com/kr4g/Klotho.git
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
2. **Navigate to the `Klotho/` Directory**:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
cd Klotho/
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
3. **Install in Development Mode**:
|
|
33
|
+
|
|
34
|
+
Install Klotho in development mode (recommended for development):
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
pip install -e .
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Or install the required dependencies separately:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
pip install -r requirements.txt
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
<!-- 4. **Install Klotho (Development Mode)**:
|
|
47
|
+
|
|
48
|
+
To install Klotho in development mode, which allows you to modify the source code and have the changes reflected immediately:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
pip install -e .
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
5. **Play**:
|
|
55
|
+
|
|
56
|
+
To work with Klotho as an 'on-the-fly' compositional-aid, initiate a Python interpreter from within the `Klotho/` directory by running the command:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
Python
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Once the interpreter loads, import from `klotho` as needed. -->
|
|
63
|
+
|
|
64
|
+
## About
|
|
65
|
+
|
|
66
|
+
Klotho extends from a lineage of CAC-oriented theories and softwares. This means that, while Klotho provides many classes and functions for 'standard' music materials, its strengths are best utilized when working with more complex, abstract, or otherwise unconventional materials not easily accessible with standard notation softwares.
|
|
67
|
+
|
|
68
|
+
The ethos of Klotho draws heavily from the concepts and computations possible with patching-based softwares like [OpenMusic](https://openmusic-project.github.io/) (which also influenced [Bach](https://www.bachproject.net/) and [Cage](https://www.bachproject.net/cage/) for Max).
|
|
69
|
+
|
|
70
|
+
Klotho seeks to avoid this patching paradigm in favor of a high-level scripting syntax that more closely resembles the underlying mathematical expressions at play when working with computational composition tools. Many of Klotho's core features, particularly in the implementation of Rhythm Trees, adhere to a "LISP-like" presentation and programming paradigm inspired by the underlying Common LISP source code for OpenMusic. It is then also closer to the abstract, algebraic language of music in its symbolic representations.
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
[Klotho](https://github.com/kr4g/Klotho) by Ryan Millett is licensed under [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/?ref=chooser-v1).
|
|
75
|
+
|
|
76
|
+

|
|
77
|
+

|
|
78
|
+

|
|
79
|
+
|
|
80
|
+
Klotho © 2023 by Ryan Millett is licensed under CC BY-SA 4.0. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/
|
|
81
|
+
|
|
82
|
+
---
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Klotho: A graph-oriented Python package for computational composition.
|
|
3
|
+
|
|
4
|
+
This package provides tools for working with musical structures across multiple domains:
|
|
5
|
+
- Topos: Abstract structures and relationships
|
|
6
|
+
- Chronos: Time and rhythm
|
|
7
|
+
- Tonos: Pitch and harmony
|
|
8
|
+
- Aikous: Expression and parameters
|
|
9
|
+
- Skora: Visualization and notation
|
|
10
|
+
"""
|
|
11
|
+
from . import topos
|
|
12
|
+
from . import chronos
|
|
13
|
+
from . import tonos
|
|
14
|
+
from . import aikous
|
|
15
|
+
from . import skora
|
|
16
|
+
from . import utils
|
|
17
|
+
|
|
18
|
+
from .topos.collections import patterns, sequences, sets, Pattern, CombinationSet, PartitionSet
|
|
19
|
+
from .topos.graphs import trees, networks, fields, Tree, Network, Field, Graph
|
|
20
|
+
|
|
21
|
+
from .chronos import RhythmPair, RhythmTree, TemporalUnit, TemporalUnitSequence, TemporalBlock
|
|
22
|
+
|
|
23
|
+
from .tonos import Pitch, Scale, Chord, AddressedScale, AddressedChord
|
|
24
|
+
|
|
25
|
+
from .types import frequency, cent, midicent, midi, amplitude, decibel, onset, duration
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
'topos', 'chronos', 'tonos', 'aikous', 'skora',
|
|
29
|
+
'Pitch', 'Scale', 'Chord',
|
|
30
|
+
'AddressedScale', 'AddressedChord',
|
|
31
|
+
'frequency', 'cent', 'midicent', 'midi',
|
|
32
|
+
'amplitude', 'decibel', 'onset', 'duration'
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
__version__ = '2.1.0'
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Aikous: A specialized module for working with expression and parameters in music.
|
|
3
|
+
|
|
4
|
+
From the Greek "ακούω" (akoúō) meaning "to hear" or "to listen," this module
|
|
5
|
+
deals with the expressive aspects of music that affect how we perceive sound.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from . import expression
|
|
9
|
+
from . import parameters
|
|
10
|
+
from . import instruments
|
|
11
|
+
|
|
12
|
+
# Import classes
|
|
13
|
+
from .expression import Dynamic, DynamicRange
|
|
14
|
+
from .parameters import ParameterTree
|
|
15
|
+
from .instruments import Instrument
|
|
16
|
+
|
|
17
|
+
# Import expression utility functions
|
|
18
|
+
from .expression import dbamp, ampdb, freq_amp_scale
|
|
19
|
+
from .expression import line, arch, map_curve
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
# Modules
|
|
23
|
+
'expression',
|
|
24
|
+
'parameters',
|
|
25
|
+
'instruments',
|
|
26
|
+
|
|
27
|
+
# Classes
|
|
28
|
+
'DynamicRange',
|
|
29
|
+
'Dynamic',
|
|
30
|
+
'ParameterTree',
|
|
31
|
+
'Instrument',
|
|
32
|
+
|
|
33
|
+
# Expression functions
|
|
34
|
+
'dbamp',
|
|
35
|
+
'ampdb',
|
|
36
|
+
'freq_amp_scale',
|
|
37
|
+
'line',
|
|
38
|
+
'arch',
|
|
39
|
+
'map_curve',
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
__version__ = '2.0.0'
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'''
|
|
2
|
+
--------------------------------------------------------------------------------------
|
|
3
|
+
General psychoacoustic tools for working with music synthesis.
|
|
4
|
+
|
|
5
|
+
`Aikous` is a specialized module for working with psychoacoustics in the context of music.
|
|
6
|
+
|
|
7
|
+
see: https://en.wikipedia.org/wiki/Psychoacoustics
|
|
8
|
+
|
|
9
|
+
The word "aikous" is a portmanteau derived from Ancient Greek, blending the elements of
|
|
10
|
+
"αἰσθάνομαι" (aisthanomai), meaning "to perceive" or "to feel," and "ἀκούω" (akouo),
|
|
11
|
+
meaning "to hear."
|
|
12
|
+
|
|
13
|
+
The `aikous` module contains tools for translating physics phenomena, as perceived by
|
|
14
|
+
humans, into algebraic musical representations.
|
|
15
|
+
--------------------------------------------------------------------------------------
|
|
16
|
+
'''
|
|
17
|
+
from .dynamics import Dynamic, DynamicRange, dbamp, ampdb, freq_amp_scale
|
|
18
|
+
from .enevelopes import line, arch, map_curve
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
'Dynamic',
|
|
22
|
+
'DynamicRange',
|
|
23
|
+
'dbamp',
|
|
24
|
+
'ampdb',
|
|
25
|
+
'freq_amp_scale',
|
|
26
|
+
'line',
|
|
27
|
+
'arch',
|
|
28
|
+
'map_curve'
|
|
29
|
+
]
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# ------------------------------------------------------------------------------------
|
|
2
|
+
# Klotho/klotho/aikous/dynamics.py
|
|
3
|
+
# ------------------------------------------------------------------------------------
|
|
4
|
+
'''
|
|
5
|
+
--------------------------------------------------------------------------------------
|
|
6
|
+
Classes and functions for working with musical dynamics.
|
|
7
|
+
--------------------------------------------------------------------------------------
|
|
8
|
+
'''
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
from numpy.polynomial import Polynomial
|
|
12
|
+
from scipy import interpolate
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
'DynamicRange',
|
|
16
|
+
'ampdb',
|
|
17
|
+
'dbamp',
|
|
18
|
+
# 'amp_freq_scale',
|
|
19
|
+
'freq_amp_scale',
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
DYNAMIC_MARKINGS = ('ppp', 'pp', 'p', 'mp', 'mf', 'f', 'ff', 'fff')
|
|
23
|
+
|
|
24
|
+
class Dynamic:
|
|
25
|
+
def __init__(self, db_value):
|
|
26
|
+
self._db_value = db_value
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def db(self):
|
|
30
|
+
return self._db_value
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def amp(self):
|
|
34
|
+
return dbamp(self._db_value)
|
|
35
|
+
|
|
36
|
+
def __float__(self):
|
|
37
|
+
return float(self._db_value)
|
|
38
|
+
|
|
39
|
+
def __repr__(self):
|
|
40
|
+
return f"Dynamic(db={self._db_value:.2f}, amp={self.amp:.4f})"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class DynamicRange:
|
|
44
|
+
'''
|
|
45
|
+
Musical dynamics mapped to decibels.
|
|
46
|
+
|
|
47
|
+
Note: the decibel level for the loudest
|
|
48
|
+
dynamic (ffff) is 0 dB as this translates
|
|
49
|
+
to an amplitude of 1.0.
|
|
50
|
+
|
|
51
|
+
----------------|---------|----------------
|
|
52
|
+
Name | Letters | Level
|
|
53
|
+
----------------|---------|----------------
|
|
54
|
+
fortississimo | fff | very very loud
|
|
55
|
+
fortissimo | ff | very loud
|
|
56
|
+
forte | f | loud
|
|
57
|
+
mezzo-forte | mf | moderately loud
|
|
58
|
+
mezzo-piano | mp | moderately quiet
|
|
59
|
+
piano | p | quiet
|
|
60
|
+
pianissimo | pp | very quiet
|
|
61
|
+
pianississimo | ppp | very very quiet
|
|
62
|
+
----------------|---------|----------------
|
|
63
|
+
|
|
64
|
+
see https://en.wikipedia.org/wiki/Dynamics_(music)#
|
|
65
|
+
'''
|
|
66
|
+
def __init__(self, min_dynamic=-60, max_dynamic=0, curve=0, dynamics=DYNAMIC_MARKINGS):
|
|
67
|
+
self._min_dynamic = min_dynamic if isinstance(min_dynamic, Dynamic) else Dynamic(min_dynamic)
|
|
68
|
+
self._max_dynamic = max_dynamic if isinstance(max_dynamic, Dynamic) else Dynamic(max_dynamic)
|
|
69
|
+
self._curve = curve
|
|
70
|
+
self._dynamics = dynamics
|
|
71
|
+
self._range = self._calculate_range()
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def min_dynamic(self):
|
|
75
|
+
return self._min_dynamic
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def max_dynamic(self):
|
|
79
|
+
return self._max_dynamic
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def curve(self):
|
|
83
|
+
return self._curve
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def ranges(self):
|
|
87
|
+
return self._range
|
|
88
|
+
|
|
89
|
+
def _calculate_range(self):
|
|
90
|
+
min_db = float(self._min_dynamic.db)
|
|
91
|
+
max_db = float(self._max_dynamic.db)
|
|
92
|
+
num_dynamics = len(self._dynamics)
|
|
93
|
+
|
|
94
|
+
result = {}
|
|
95
|
+
for i, dyn in enumerate(self._dynamics):
|
|
96
|
+
normalized_pos = i / (num_dynamics - 1)
|
|
97
|
+
|
|
98
|
+
if self._curve == 0:
|
|
99
|
+
curved_pos = normalized_pos
|
|
100
|
+
elif self._curve > 0:
|
|
101
|
+
curved_pos = normalized_pos ** (1 + self._curve)
|
|
102
|
+
else:
|
|
103
|
+
curved_pos = 1 - ((1 - normalized_pos) ** (1 - self._curve))
|
|
104
|
+
|
|
105
|
+
db_value = min_db + curved_pos * (max_db - min_db)
|
|
106
|
+
result[dyn] = Dynamic(db_value)
|
|
107
|
+
|
|
108
|
+
return result
|
|
109
|
+
|
|
110
|
+
def __getitem__(self, dynamic):
|
|
111
|
+
return self._range[dynamic]
|
|
112
|
+
|
|
113
|
+
def ampdb(amp: float) -> float:
|
|
114
|
+
'''
|
|
115
|
+
Convert amplitude to decibels (dB).
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
amp (float): The amplitude to convert.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
float: The amplitude in decibels.
|
|
122
|
+
'''
|
|
123
|
+
return 20 * np.log10(amp)
|
|
124
|
+
|
|
125
|
+
def dbamp(db: float) -> float:
|
|
126
|
+
'''
|
|
127
|
+
Convert decibels (dB) to amplitude.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
db (float): The decibels to convert.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
float: The amplitude.
|
|
134
|
+
'''
|
|
135
|
+
return 10 ** (db / 20)
|
|
136
|
+
|
|
137
|
+
# def amp_freq_scale(freq: float,
|
|
138
|
+
# freqs: list = [20, 100, 250, 500, 1000, 2000, 3000, 4000, 6000, 10000, 20000],
|
|
139
|
+
# amps: list = [0.3, 0.5, 0.7, 0.8, 0.5, 0.6, 0.5, 0.6, 0.7, 0.5, 0.3],
|
|
140
|
+
# deg: int = 4) -> float:
|
|
141
|
+
# frequencies_sample = np.array(freqs, dtype=float)
|
|
142
|
+
# loudness_sample = np.array(amps, dtype=float)
|
|
143
|
+
# p = Polynomial.fit(frequencies_sample, loudness_sample, deg=deg)
|
|
144
|
+
# return p(freq)
|
|
145
|
+
|
|
146
|
+
def freq_amp_scale(freq: float, db_level: float, min_db: float = -60) -> float:
|
|
147
|
+
"""
|
|
148
|
+
Scale amplitude based on frequency and loudness according to psychoacoustic principles.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
freq (float): The frequency in Hz
|
|
152
|
+
db_level (float): The input level in dB
|
|
153
|
+
min_db (float): The minimum dB level in the dynamic range (default -60)
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
float: The perceptually scaled amplitude (linear scale)
|
|
157
|
+
"""
|
|
158
|
+
range_db = abs(min_db)
|
|
159
|
+
phon_level = 40 + ((db_level - min_db) / range_db) * 60
|
|
160
|
+
|
|
161
|
+
frequencies = np.array([20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000], dtype=float)
|
|
162
|
+
|
|
163
|
+
if phon_level <= 40:
|
|
164
|
+
scaling_curve = np.array([0.2, 0.3, 0.5, 0.7, 0.9, 1.0, 1.0, 0.9, 0.7, 0.4], dtype=float)
|
|
165
|
+
elif phon_level <= 70:
|
|
166
|
+
scaling_curve = np.array([0.3, 0.45, 0.6, 0.8, 0.95, 1.0, 1.0, 0.95, 0.8, 0.5], dtype=float)
|
|
167
|
+
else:
|
|
168
|
+
scaling_curve = np.array([0.5, 0.6, 0.7, 0.85, 0.95, 1.0, 1.0, 0.95, 0.85, 0.6], dtype=float)
|
|
169
|
+
|
|
170
|
+
spline = interpolate.CubicSpline(frequencies, scaling_curve, extrapolate=True)
|
|
171
|
+
scaling_factor = max(0.01, float(spline(freq)))
|
|
172
|
+
|
|
173
|
+
raw_amp = dbamp(db_level)
|
|
174
|
+
return raw_amp * scaling_factor
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# --------------------------------------------------
|
|
2
|
+
# Klotho/klotho/aikous/envelopes.py
|
|
3
|
+
# --------------------------------------------------
|
|
4
|
+
'''
|
|
5
|
+
--------------------------------------------------------------------------------------
|
|
6
|
+
Envelopes for shaping the dynamics of a sequence of discrete values.
|
|
7
|
+
--------------------------------------------------------------------------------------
|
|
8
|
+
'''
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
'line',
|
|
14
|
+
'arch',
|
|
15
|
+
'map_curve',
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
def line(start=0.0, end=1.0, steps=100, curve=0.0):
|
|
19
|
+
'''
|
|
20
|
+
Generate a curved line from start to end value over n steps.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
start: Starting value
|
|
24
|
+
end: Ending value
|
|
25
|
+
steps: Number of steps
|
|
26
|
+
curve: Shape of the curve. Negative for exponential, positive for logarithmic, 0 for linear
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
numpy.ndarray: Array of values following the specified curve
|
|
30
|
+
'''
|
|
31
|
+
if curve == 0:
|
|
32
|
+
return np.linspace(start, end, steps)
|
|
33
|
+
|
|
34
|
+
t = np.linspace(0, 1, steps)
|
|
35
|
+
curved_t = np.exp(curve * t) - 1
|
|
36
|
+
curved_t = curved_t / (np.exp(curve) - 1)
|
|
37
|
+
|
|
38
|
+
return start + (end - start) * curved_t
|
|
39
|
+
|
|
40
|
+
def arch(base=0.0, peak=1.0, steps=100, curve=0.0, axis=0):
|
|
41
|
+
'''
|
|
42
|
+
Generate a swelling curve that rises and falls, starting and ending at base value, peaking at peak value.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
base: Starting and ending value
|
|
46
|
+
peak: Peak value
|
|
47
|
+
steps: Number of steps
|
|
48
|
+
curve: Shape of the curve. Can be:
|
|
49
|
+
- A single number: Same curve applied to both sides (negative for exponential, positive for logarithmic)
|
|
50
|
+
- A tuple/list of two values: First value for ascending curve, second for descending
|
|
51
|
+
axis: Position of the peak (-1 to 1). 0 centers the peak, negative shifts earlier, positive shifts later
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
numpy.ndarray: Array of values following a swell curve
|
|
55
|
+
'''
|
|
56
|
+
axis = np.clip(axis, -1, 1)
|
|
57
|
+
split_point = int((0.5 + axis * 0.4) * steps)
|
|
58
|
+
|
|
59
|
+
if isinstance(curve, (list, tuple)) and len(curve) == 2:
|
|
60
|
+
up_curve, down_curve = curve
|
|
61
|
+
else:
|
|
62
|
+
up_curve = down_curve = curve
|
|
63
|
+
|
|
64
|
+
up = line(base, peak, split_point + 1, up_curve)
|
|
65
|
+
down = line(peak, base, steps - split_point, down_curve)
|
|
66
|
+
|
|
67
|
+
return np.concatenate([up[:-1], down])
|
|
68
|
+
|
|
69
|
+
def map_curve(value, in_range, out_range, curve=0.0):
|
|
70
|
+
'''
|
|
71
|
+
Map a value from an input range to an output range with optional curve shaping.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
value: Input value to map
|
|
75
|
+
in_range: Tuple of (min, max) for input range
|
|
76
|
+
out_range: Tuple of (min, max) for output range
|
|
77
|
+
curve: Shape of the curve. Negative for exponential, positive for logarithmic, 0 for linear
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
float: Mapped value with curve applied
|
|
81
|
+
'''
|
|
82
|
+
normalized = np.interp(value, in_range, (0, 1))
|
|
83
|
+
|
|
84
|
+
if curve != 0:
|
|
85
|
+
normalized = np.exp(curve * normalized) - 1
|
|
86
|
+
normalized = normalized / (np.exp(curve) - 1)
|
|
87
|
+
|
|
88
|
+
return np.interp(normalized, (0, 1), out_range)
|
|
89
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .instrument import Instrument
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from klotho.aikous.expression.dynamics import Dynamic, DynamicRange
|
|
3
|
+
from klotho.tonos.pitch import Pitch
|
|
4
|
+
from klotho.utils.data_structures.dictionaries import SafeDict
|
|
5
|
+
from typing import List, Dict, TypeVar, Union
|
|
6
|
+
|
|
7
|
+
class Instrument(ABC):
|
|
8
|
+
|
|
9
|
+
def __init__(self, name, freq_range=None, dynamic_range=None, pfields=None):
|
|
10
|
+
"""
|
|
11
|
+
Initialize an Instrument.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
name (str): The name of the instrument
|
|
15
|
+
freq_range (tuple): A tuple of (min, max) frequency values or Pitch instances
|
|
16
|
+
dynamic_range: A DynamicRange instance or a tuple of (min, max) dB values
|
|
17
|
+
pfields (dict or SafeDict): Parameter fields with default values
|
|
18
|
+
"""
|
|
19
|
+
self._name = name
|
|
20
|
+
|
|
21
|
+
if freq_range is None:
|
|
22
|
+
freq_range = (20.0, 20000.0)
|
|
23
|
+
self._freq_range = self._process_freq_range(freq_range)
|
|
24
|
+
|
|
25
|
+
if dynamic_range is None:
|
|
26
|
+
dynamic_range = (-60, 0)
|
|
27
|
+
self._dynamic_range = self._process_dynamic_range(dynamic_range)
|
|
28
|
+
|
|
29
|
+
if pfields is None:
|
|
30
|
+
pfields = {}
|
|
31
|
+
self._pfields = pfields if isinstance(pfields, SafeDict) else SafeDict(pfields)
|
|
32
|
+
|
|
33
|
+
def _process_freq_range(self, freq_range):
|
|
34
|
+
min_freq, max_freq = freq_range
|
|
35
|
+
|
|
36
|
+
if not isinstance(min_freq, Pitch):
|
|
37
|
+
min_freq = Pitch.from_freq(float(min_freq))
|
|
38
|
+
|
|
39
|
+
if not isinstance(max_freq, Pitch):
|
|
40
|
+
max_freq = Pitch.from_freq(float(max_freq))
|
|
41
|
+
|
|
42
|
+
return (min_freq, max_freq)
|
|
43
|
+
|
|
44
|
+
def _process_dynamic_range(self, dynamic_range):
|
|
45
|
+
if isinstance(dynamic_range, DynamicRange):
|
|
46
|
+
return dynamic_range
|
|
47
|
+
|
|
48
|
+
min_dyn, max_dyn = dynamic_range
|
|
49
|
+
|
|
50
|
+
if isinstance(min_dyn, Dynamic):
|
|
51
|
+
min_dyn = min_dyn.db
|
|
52
|
+
|
|
53
|
+
if isinstance(max_dyn, Dynamic):
|
|
54
|
+
max_dyn = max_dyn.db
|
|
55
|
+
|
|
56
|
+
return DynamicRange(min_dynamic=min_dyn, max_dynamic=max_dyn)
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def name(self):
|
|
60
|
+
return self._name
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def freq_range(self):
|
|
64
|
+
return self._freq_range
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def dynamic_range(self):
|
|
68
|
+
return self._dynamic_range
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def pfields(self):
|
|
72
|
+
return self._pfields
|
|
73
|
+
|
|
74
|
+
def __repr__(self):
|
|
75
|
+
# return f"Instrument(name='{self._name}', freq_range={self._freq_range}, dynamic_range={self._dynamic_range}, pfields={dict(self._pfields)})"
|
|
76
|
+
return f"Instrument(name='{self._name}', pfields={dict(self._pfields)})"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .parameter_tree import *
|