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.
Files changed (108) hide show
  1. klotho_cac-2.1.0/LICENSE +15 -0
  2. klotho_cac-2.1.0/MANIFEST.in +7 -0
  3. klotho_cac-2.1.0/PKG-INFO +123 -0
  4. klotho_cac-2.1.0/README.md +82 -0
  5. klotho_cac-2.1.0/klotho/__init__.py +35 -0
  6. klotho_cac-2.1.0/klotho/aikous/__init__.py +42 -0
  7. klotho_cac-2.1.0/klotho/aikous/expression/__init__.py +29 -0
  8. klotho_cac-2.1.0/klotho/aikous/expression/dynamics.py +174 -0
  9. klotho_cac-2.1.0/klotho/aikous/expression/enevelopes.py +89 -0
  10. klotho_cac-2.1.0/klotho/aikous/instruments/__init__.py +1 -0
  11. klotho_cac-2.1.0/klotho/aikous/instruments/instrument.py +76 -0
  12. klotho_cac-2.1.0/klotho/aikous/parameters/__init__.py +1 -0
  13. klotho_cac-2.1.0/klotho/aikous/parameters/instruments.py +142 -0
  14. klotho_cac-2.1.0/klotho/aikous/parameters/parameter_tree.py +69 -0
  15. klotho_cac-2.1.0/klotho/chronos/__init__.py +53 -0
  16. klotho_cac-2.1.0/klotho/chronos/rhythm_pairs/__init__.py +3 -0
  17. klotho_cac-2.1.0/klotho/chronos/rhythm_pairs/rhythm_pair.py +102 -0
  18. klotho_cac-2.1.0/klotho/chronos/rhythm_trees/__init__.py +7 -0
  19. klotho_cac-2.1.0/klotho/chronos/rhythm_trees/algorithms.py +219 -0
  20. klotho_cac-2.1.0/klotho/chronos/rhythm_trees/meas.py +232 -0
  21. klotho_cac-2.1.0/klotho/chronos/rhythm_trees/rhythm_tree.py +212 -0
  22. klotho_cac-2.1.0/klotho/chronos/temporal_units/__init__.py +4 -0
  23. klotho_cac-2.1.0/klotho/chronos/temporal_units/algorithms.py +142 -0
  24. klotho_cac-2.1.0/klotho/chronos/temporal_units/temporal.py +865 -0
  25. klotho_cac-2.1.0/klotho/chronos/utils/__init__.py +3 -0
  26. klotho_cac-2.1.0/klotho/chronos/utils/beat.py +50 -0
  27. klotho_cac-2.1.0/klotho/chronos/utils/tempo.py +85 -0
  28. klotho_cac-2.1.0/klotho/chronos/utils/time_conversion.py +129 -0
  29. klotho_cac-2.1.0/klotho/skora/__init__.py +16 -0
  30. klotho_cac-2.1.0/klotho/skora/animation/__init__.py +1 -0
  31. klotho_cac-2.1.0/klotho/skora/animation/animate.py +193 -0
  32. klotho_cac-2.1.0/klotho/skora/notation/__init__.py +1 -0
  33. klotho_cac-2.1.0/klotho/skora/notation/notation.py +546 -0
  34. klotho_cac-2.1.0/klotho/skora/notation/notation_OLD.py +261 -0
  35. klotho_cac-2.1.0/klotho/skora/skora.py +329 -0
  36. klotho_cac-2.1.0/klotho/skora/visualization/__init__.py +3 -0
  37. klotho_cac-2.1.0/klotho/skora/visualization/field_plots.py +78 -0
  38. klotho_cac-2.1.0/klotho/skora/visualization/plots.py +999 -0
  39. klotho_cac-2.1.0/klotho/skora/visualization/ut_plots.py +54 -0
  40. klotho_cac-2.1.0/klotho/tonos/__init__.py +125 -0
  41. klotho_cac-2.1.0/klotho/tonos/chords/__init__.py +1 -0
  42. klotho_cac-2.1.0/klotho/tonos/chords/chord.py +144 -0
  43. klotho_cac-2.1.0/klotho/tonos/pitch/__init__.py +21 -0
  44. klotho_cac-2.1.0/klotho/tonos/pitch/pitch.py +173 -0
  45. klotho_cac-2.1.0/klotho/tonos/pitch/pitch_collections.py +610 -0
  46. klotho_cac-2.1.0/klotho/tonos/scales/__init__.py +1 -0
  47. klotho_cac-2.1.0/klotho/tonos/scales/scale.py +127 -0
  48. klotho_cac-2.1.0/klotho/tonos/systems/__init__.py +17 -0
  49. klotho_cac-2.1.0/klotho/tonos/systems/combination_product_sets/__init__.py +13 -0
  50. klotho_cac-2.1.0/klotho/tonos/systems/combination_product_sets/combination_product_networks.py +36 -0
  51. klotho_cac-2.1.0/klotho/tonos/systems/combination_product_sets/cps.py +435 -0
  52. klotho_cac-2.1.0/klotho/tonos/systems/harmonic_trees/__init__.py +7 -0
  53. klotho_cac-2.1.0/klotho/tonos/systems/harmonic_trees/algorithms.py +16 -0
  54. klotho_cac-2.1.0/klotho/tonos/systems/harmonic_trees/harmonic_tree.py +83 -0
  55. klotho_cac-2.1.0/klotho/tonos/systems/harmonic_trees/spectrum.py +207 -0
  56. klotho_cac-2.1.0/klotho/tonos/utils/__init__.py +17 -0
  57. klotho_cac-2.1.0/klotho/tonos/utils/frequency_conversion.py +175 -0
  58. klotho_cac-2.1.0/klotho/tonos/utils/harmonics.py +48 -0
  59. klotho_cac-2.1.0/klotho/tonos/utils/interval_normalization.py +179 -0
  60. klotho_cac-2.1.0/klotho/tonos/utils/intervals.py +251 -0
  61. klotho_cac-2.1.0/klotho/topos/__init__.py +55 -0
  62. klotho_cac-2.1.0/klotho/topos/collections/__init__.py +7 -0
  63. klotho_cac-2.1.0/klotho/topos/collections/patterns.py +226 -0
  64. klotho_cac-2.1.0/klotho/topos/collections/sequences.py +114 -0
  65. klotho_cac-2.1.0/klotho/topos/collections/sets.py +397 -0
  66. klotho_cac-2.1.0/klotho/topos/formal_grammars/__init__.py +5 -0
  67. klotho_cac-2.1.0/klotho/topos/formal_grammars/alphabets.py +582 -0
  68. klotho_cac-2.1.0/klotho/topos/formal_grammars/grammars.py +92 -0
  69. klotho_cac-2.1.0/klotho/topos/graphs/__init__.py +8 -0
  70. klotho_cac-2.1.0/klotho/topos/graphs/fields/__init__.py +1 -0
  71. klotho_cac-2.1.0/klotho/topos/graphs/fields/algorithms/__init__.py +1 -0
  72. klotho_cac-2.1.0/klotho/topos/graphs/fields/algorithms/field_algs.py +63 -0
  73. klotho_cac-2.1.0/klotho/topos/graphs/fields/fields.py +140 -0
  74. klotho_cac-2.1.0/klotho/topos/graphs/fields/functions/__init__.py +1 -0
  75. klotho_cac-2.1.0/klotho/topos/graphs/fields/functions/field_funcs.py +89 -0
  76. klotho_cac-2.1.0/klotho/topos/graphs/graphs.py +244 -0
  77. klotho_cac-2.1.0/klotho/topos/graphs/networks/__init__.py +1 -0
  78. klotho_cac-2.1.0/klotho/topos/graphs/networks/algorithms/__init__.py +1 -0
  79. klotho_cac-2.1.0/klotho/topos/graphs/networks/algorithms/network_algs.py +49 -0
  80. klotho_cac-2.1.0/klotho/topos/graphs/networks/networks.py +75 -0
  81. klotho_cac-2.1.0/klotho/topos/graphs/trees/__init__.py +2 -0
  82. klotho_cac-2.1.0/klotho/topos/graphs/trees/algorithms.py +171 -0
  83. klotho_cac-2.1.0/klotho/topos/graphs/trees/trees.py +379 -0
  84. klotho_cac-2.1.0/klotho/topos/random/__init__.py +4 -0
  85. klotho_cac-2.1.0/klotho/topos/random/rando.py +48 -0
  86. klotho_cac-2.1.0/klotho/types.py +83 -0
  87. klotho_cac-2.1.0/klotho/utils/__init__.py +0 -0
  88. klotho_cac-2.1.0/klotho/utils/algorithms/__init__.py +6 -0
  89. klotho_cac-2.1.0/klotho/utils/algorithms/costs.py +78 -0
  90. klotho_cac-2.1.0/klotho/utils/algorithms/cps_algorithms.py +37 -0
  91. klotho_cac-2.1.0/klotho/utils/algorithms/factors.py +42 -0
  92. klotho_cac-2.1.0/klotho/utils/data_structures/__init__.py +7 -0
  93. klotho_cac-2.1.0/klotho/utils/data_structures/dictionaries.py +5 -0
  94. klotho_cac-2.1.0/klotho/utils/data_structures/enums.py +32 -0
  95. klotho_cac-2.1.0/klotho/utils/data_structures/graphs.py +136 -0
  96. klotho_cac-2.1.0/klotho/utils/data_structures/group.py +29 -0
  97. klotho_cac-2.1.0/klotho/utils/playback/__init__.py +1 -0
  98. klotho_cac-2.1.0/klotho/utils/playback/player.py +378 -0
  99. klotho_cac-2.1.0/klotho/utils/playback/scheduler copy.py +175 -0
  100. klotho_cac-2.1.0/klotho/utils/playback/scheduler.py +281 -0
  101. klotho_cac-2.1.0/klotho_cac.egg-info/PKG-INFO +123 -0
  102. klotho_cac-2.1.0/klotho_cac.egg-info/SOURCES.txt +106 -0
  103. klotho_cac-2.1.0/klotho_cac.egg-info/dependency_links.txt +1 -0
  104. klotho_cac-2.1.0/klotho_cac.egg-info/requires.txt +10 -0
  105. klotho_cac-2.1.0/klotho_cac.egg-info/top_level.txt +1 -0
  106. klotho_cac-2.1.0/requirements.txt +10 -0
  107. klotho_cac-2.1.0/setup.cfg +4 -0
  108. klotho_cac-2.1.0/setup.py +47 -0
@@ -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,7 @@
1
+ include README.md
2
+ include requirements.txt
3
+ include LICENSE
4
+ recursive-include klotho *.py
5
+ recursive-exclude * __pycache__
6
+ recursive-exclude * *.py[co]
7
+ recursive-exclude * .DS_Store
@@ -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
+ ![CC Icon](https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1)
118
+ ![BY Icon](https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1)
119
+ ![SA Icon](https://mirrors.creativecommons.org/presskit/icons/sa.svg?ref=chooser-v1)
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
+ ![CC Icon](https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1)
77
+ ![BY Icon](https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1)
78
+ ![SA Icon](https://mirrors.creativecommons.org/presskit/icons/sa.svg?ref=chooser-v1)
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 *