klotho-cac 2.1.0__py3-none-any.whl

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 (101) hide show
  1. klotho/__init__.py +35 -0
  2. klotho/aikous/__init__.py +42 -0
  3. klotho/aikous/expression/__init__.py +29 -0
  4. klotho/aikous/expression/dynamics.py +174 -0
  5. klotho/aikous/expression/enevelopes.py +89 -0
  6. klotho/aikous/instruments/__init__.py +1 -0
  7. klotho/aikous/instruments/instrument.py +76 -0
  8. klotho/aikous/parameters/__init__.py +1 -0
  9. klotho/aikous/parameters/instruments.py +142 -0
  10. klotho/aikous/parameters/parameter_tree.py +69 -0
  11. klotho/chronos/__init__.py +53 -0
  12. klotho/chronos/rhythm_pairs/__init__.py +3 -0
  13. klotho/chronos/rhythm_pairs/rhythm_pair.py +102 -0
  14. klotho/chronos/rhythm_trees/__init__.py +7 -0
  15. klotho/chronos/rhythm_trees/algorithms.py +219 -0
  16. klotho/chronos/rhythm_trees/meas.py +232 -0
  17. klotho/chronos/rhythm_trees/rhythm_tree.py +212 -0
  18. klotho/chronos/temporal_units/__init__.py +4 -0
  19. klotho/chronos/temporal_units/algorithms.py +142 -0
  20. klotho/chronos/temporal_units/temporal.py +865 -0
  21. klotho/chronos/utils/__init__.py +3 -0
  22. klotho/chronos/utils/beat.py +50 -0
  23. klotho/chronos/utils/tempo.py +85 -0
  24. klotho/chronos/utils/time_conversion.py +129 -0
  25. klotho/skora/__init__.py +16 -0
  26. klotho/skora/animation/__init__.py +1 -0
  27. klotho/skora/animation/animate.py +193 -0
  28. klotho/skora/notation/__init__.py +1 -0
  29. klotho/skora/notation/notation.py +546 -0
  30. klotho/skora/notation/notation_OLD.py +261 -0
  31. klotho/skora/skora.py +329 -0
  32. klotho/skora/visualization/__init__.py +3 -0
  33. klotho/skora/visualization/field_plots.py +78 -0
  34. klotho/skora/visualization/plots.py +999 -0
  35. klotho/skora/visualization/ut_plots.py +54 -0
  36. klotho/tonos/__init__.py +125 -0
  37. klotho/tonos/chords/__init__.py +1 -0
  38. klotho/tonos/chords/chord.py +144 -0
  39. klotho/tonos/pitch/__init__.py +21 -0
  40. klotho/tonos/pitch/pitch.py +173 -0
  41. klotho/tonos/pitch/pitch_collections.py +610 -0
  42. klotho/tonos/scales/__init__.py +1 -0
  43. klotho/tonos/scales/scale.py +127 -0
  44. klotho/tonos/systems/__init__.py +17 -0
  45. klotho/tonos/systems/combination_product_sets/__init__.py +13 -0
  46. klotho/tonos/systems/combination_product_sets/combination_product_networks.py +36 -0
  47. klotho/tonos/systems/combination_product_sets/cps.py +435 -0
  48. klotho/tonos/systems/harmonic_trees/__init__.py +7 -0
  49. klotho/tonos/systems/harmonic_trees/algorithms.py +16 -0
  50. klotho/tonos/systems/harmonic_trees/harmonic_tree.py +83 -0
  51. klotho/tonos/systems/harmonic_trees/spectrum.py +207 -0
  52. klotho/tonos/utils/__init__.py +17 -0
  53. klotho/tonos/utils/frequency_conversion.py +175 -0
  54. klotho/tonos/utils/harmonics.py +48 -0
  55. klotho/tonos/utils/interval_normalization.py +179 -0
  56. klotho/tonos/utils/intervals.py +251 -0
  57. klotho/topos/__init__.py +55 -0
  58. klotho/topos/collections/__init__.py +7 -0
  59. klotho/topos/collections/patterns.py +226 -0
  60. klotho/topos/collections/sequences.py +114 -0
  61. klotho/topos/collections/sets.py +397 -0
  62. klotho/topos/formal_grammars/__init__.py +5 -0
  63. klotho/topos/formal_grammars/alphabets.py +582 -0
  64. klotho/topos/formal_grammars/grammars.py +92 -0
  65. klotho/topos/graphs/__init__.py +8 -0
  66. klotho/topos/graphs/fields/__init__.py +1 -0
  67. klotho/topos/graphs/fields/algorithms/__init__.py +1 -0
  68. klotho/topos/graphs/fields/algorithms/field_algs.py +63 -0
  69. klotho/topos/graphs/fields/fields.py +140 -0
  70. klotho/topos/graphs/fields/functions/__init__.py +1 -0
  71. klotho/topos/graphs/fields/functions/field_funcs.py +89 -0
  72. klotho/topos/graphs/graphs.py +244 -0
  73. klotho/topos/graphs/networks/__init__.py +1 -0
  74. klotho/topos/graphs/networks/algorithms/__init__.py +1 -0
  75. klotho/topos/graphs/networks/algorithms/network_algs.py +49 -0
  76. klotho/topos/graphs/networks/networks.py +75 -0
  77. klotho/topos/graphs/trees/__init__.py +2 -0
  78. klotho/topos/graphs/trees/algorithms.py +171 -0
  79. klotho/topos/graphs/trees/trees.py +379 -0
  80. klotho/topos/random/__init__.py +4 -0
  81. klotho/topos/random/rando.py +48 -0
  82. klotho/types.py +83 -0
  83. klotho/utils/__init__.py +0 -0
  84. klotho/utils/algorithms/__init__.py +6 -0
  85. klotho/utils/algorithms/costs.py +78 -0
  86. klotho/utils/algorithms/cps_algorithms.py +37 -0
  87. klotho/utils/algorithms/factors.py +42 -0
  88. klotho/utils/data_structures/__init__.py +7 -0
  89. klotho/utils/data_structures/dictionaries.py +5 -0
  90. klotho/utils/data_structures/enums.py +32 -0
  91. klotho/utils/data_structures/graphs.py +136 -0
  92. klotho/utils/data_structures/group.py +29 -0
  93. klotho/utils/playback/__init__.py +1 -0
  94. klotho/utils/playback/player.py +378 -0
  95. klotho/utils/playback/scheduler copy.py +175 -0
  96. klotho/utils/playback/scheduler.py +281 -0
  97. klotho_cac-2.1.0.dist-info/METADATA +123 -0
  98. klotho_cac-2.1.0.dist-info/RECORD +101 -0
  99. klotho_cac-2.1.0.dist-info/WHEEL +5 -0
  100. klotho_cac-2.1.0.dist-info/licenses/LICENSE +15 -0
  101. klotho_cac-2.1.0.dist-info/top_level.txt +1 -0
klotho/__init__.py ADDED
@@ -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 *
@@ -0,0 +1,142 @@
1
+ from enum import Enum
2
+ from klotho.utils.data_structures.dictionaries import SafeDict
3
+
4
+ class PFIELDS(Enum):
5
+ def __call__(self):
6
+ # return SafeDict(self.value.copy())
7
+ return self.value.copy()
8
+
9
+ SineEnv = SafeDict({
10
+ 'start' : 0,
11
+ 'dur' : 1,
12
+ 'synthName' : 'SineEnv',
13
+ 'amplitude' : 0.45,
14
+ 'frequency' : 440,
15
+ 'attackTime' : 0.01,
16
+ 'releaseTime': 0.1,
17
+ 'pan' : 0.0,
18
+ })
19
+
20
+ Vib = SafeDict({
21
+ 'start' : 0,
22
+ 'dur' : 1,
23
+ 'synthName' : 'Vib',
24
+ 'amplitude' : 0.45,
25
+ 'frequency' : 440,
26
+ 'attackTime' : 0.01,
27
+ 'releaseTime': 0.1,
28
+ 'sustain' : 0.5,
29
+ 'curve' : 4.0,
30
+ 'pan' : 0.0,
31
+ 'table' : 0,
32
+ 'vibRate1' : 3.5,
33
+ 'vibRate2' : 5.8,
34
+ 'vibRise' : 0.5,
35
+ 'vibDepth' : 0.005,
36
+ })
37
+
38
+ FMWT = SafeDict({
39
+ 'start' : 0,
40
+ 'dur' : 1,
41
+ 'synthName' : 'FMWT',
42
+ 'frequency' : 440,
43
+ 'amplitude' : 0.45,
44
+ 'attackTime' : 0.01,
45
+ 'releaseTime' : 0.1,
46
+ 'sustain' : 0.5,
47
+ 'idx1' : 0.01,
48
+ 'idx2' : 7,
49
+ 'idx3' : 5,
50
+ 'carMul' : 1,
51
+ 'modMul' : 1.0007,
52
+ 'vibRate1' : 0.01,
53
+ 'vibRate2' : 0.5,
54
+ 'vibRise' : 0,
55
+ 'vibDepth' : 0,
56
+ 'pan' : 0.0,
57
+ 'table' : 0,
58
+ 'reverberation': 0.0,
59
+ })
60
+
61
+ OscTrm = SafeDict({
62
+ 'start' : 0,
63
+ 'dur' : 1,
64
+ 'synthName' : 'OscTrm',
65
+ 'amplitude' : 0.45,
66
+ 'frequency' : 440,
67
+ 'attackTime' : 0.01,
68
+ 'releaseTime': 0.1,
69
+ 'sustain' : 0.5,
70
+ 'curve' : 4.0,
71
+ 'pan' : 0.0,
72
+ 'table' : 0,
73
+ 'trm1' : 3.5,
74
+ 'trm2' : 5.8,
75
+ 'trmRise' : 0.5,
76
+ 'trmDepth' : 0.1,
77
+ })
78
+
79
+ OscAM = SafeDict({
80
+ 'start' : 0,
81
+ 'dur' : 1,
82
+ 'synthName' : 'OscAM',
83
+ 'amplitude' : 0.45,
84
+ 'frequency' : 440,
85
+ 'attackTime' : 0.01,
86
+ 'releaseTime' : 0.1,
87
+ 'sustain' : 0.5,
88
+ 'pan' : 0.0,
89
+ 'amFunc' : 0.0,
90
+ 'am1' : 0.75,
91
+ 'am2' : 0.75,
92
+ 'amRise' : 0.75,
93
+ 'amRatio' : 0.75,
94
+ 'reverberation': 0.0,
95
+ 'visualMode' : 0.0,
96
+ })
97
+
98
+ AddSyn = SafeDict({
99
+ 'start' : 0,
100
+ 'dur' : 1,
101
+ 'synthName' : 'AddSyn',
102
+ 'amp' : 0.45,
103
+ 'frequency' : 440,
104
+ 'ampStri' : 0.5,
105
+ 'attackStri' : 0.1,
106
+ 'releaseStri' : 0.1,
107
+ 'sustainStri' : 0.8,
108
+ 'ampLow' : 0.5,
109
+ 'attackLow' : 0.001,
110
+ 'releaseLow' : 0.1,
111
+ 'sustainLow' : 0.8,
112
+ 'ampUp' : 0.6,
113
+ 'attackUp' : 0.01,
114
+ 'releaseUp' : 0.075,
115
+ 'sustainUp' : 0.9,
116
+ 'freqStri1' : 1.0,
117
+ 'freqStri2' : 2.001,
118
+ 'freqStri3' : 3.0,
119
+ 'freqLow1' : 4.009,
120
+ 'freqLow2' : 5.002,
121
+ 'freqUp1' : 6.0,
122
+ 'freqUp2' : 7.0,
123
+ 'freqUp3' : 8.0,
124
+ 'freqUp4' : 9.0,
125
+ 'pan' : 0.0,
126
+ 'reverberation': 0.0,
127
+ })
128
+
129
+ PluckedString = SafeDict({
130
+ 'start' : 0,
131
+ 'dur' : 1,
132
+ 'synthName' : 'PluckedString',
133
+ 'amplitude' : 0.45,
134
+ 'frequency' : 440,
135
+ 'attackTime' : 0.01,
136
+ 'releaseTime': 0.1,
137
+ 'sustain' : 0.5,
138
+ 'Pan1' : 0.0,
139
+ 'Pan2' : 0.0,
140
+ 'PanRise' : 0.0,
141
+ })
142
+
@@ -0,0 +1,69 @@
1
+ from ...topos.graphs.trees import Tree
2
+ import pandas as pd
3
+
4
+ class ParameterTree(Tree):
5
+ def __init__(self, root, children:tuple):
6
+ super().__init__(root, children)
7
+ self._meta['pfields'] = pd.Series([set()], index=[''])
8
+
9
+ def __getitem__(self, node):
10
+ return ParameterNode(self, node)
11
+
12
+ @property
13
+ def pfields(self):
14
+ return sorted(self._meta.loc['', 'pfields'])
15
+
16
+ def set(self, node, **kwargs):
17
+ self._meta.loc['', 'pfields'].update(kwargs.keys())
18
+
19
+ for key, value in kwargs.items():
20
+ self.graph.nodes[node][key] = value
21
+
22
+ for descendant in self.descendants(node):
23
+ for key, value in kwargs.items():
24
+ self.graph.nodes[descendant][key] = value
25
+
26
+ def get(self, node, key):
27
+ if key in self.graph.nodes[node]:
28
+ return self.graph.nodes[node][key]
29
+ return None
30
+
31
+ def clear(self, node=None):
32
+ if node is None:
33
+ for n in self.graph.nodes:
34
+ self.graph.nodes[n].clear()
35
+ else:
36
+ self.graph.nodes[node].clear()
37
+ for descendant in self.descendants(node):
38
+ self.graph.nodes[descendant].clear()
39
+
40
+ def items(self, node):
41
+ return dict(self.graph.nodes[node])
42
+
43
+ class ParameterNode:
44
+ def __init__(self, tree, node):
45
+ self._tree = tree
46
+ self._node = node
47
+
48
+ def __getitem__(self, key):
49
+ if isinstance(key, str):
50
+ return self._tree.get(self._node, key)
51
+ raise TypeError("Key must be a string")
52
+
53
+ def __setitem__(self, key, value):
54
+ self._tree.set(self._node, **{key: value})
55
+
56
+ def clear(self):
57
+ self._tree.clear(self._node)
58
+
59
+ def items(self):
60
+ return self._tree.items(self._node)
61
+
62
+ def __dict__(self):
63
+ return self._tree.items(self._node)
64
+
65
+ def __str__(self):
66
+ return str(self._tree.items(self._node))
67
+
68
+ def __repr__(self):
69
+ return repr(self._tree.items(self._node))