random-tone-generator 0.0.1__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.
- random_tone_generator-0.0.1/PKG-INFO +60 -0
- random_tone_generator-0.0.1/README.md +34 -0
- random_tone_generator-0.0.1/pyproject.toml +43 -0
- random_tone_generator-0.0.1/random_tone_generator.egg-info/PKG-INFO +60 -0
- random_tone_generator-0.0.1/random_tone_generator.egg-info/SOURCES.txt +8 -0
- random_tone_generator-0.0.1/random_tone_generator.egg-info/dependency_links.txt +1 -0
- random_tone_generator-0.0.1/random_tone_generator.egg-info/requires.txt +2 -0
- random_tone_generator-0.0.1/random_tone_generator.egg-info/top_level.txt +2 -0
- random_tone_generator-0.0.1/random_tone_generator.py +148 -0
- random_tone_generator-0.0.1/setup.cfg +4 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: random_tone_generator
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Random sweep/hold tone generator with explicit-duration playback.
|
|
5
|
+
Author: Sithu Ye Htun
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/leo007-htun/random_tone_generator
|
|
8
|
+
Project-URL: Repository, https://github.com/leo007-htun/random_tone_generator
|
|
9
|
+
Project-URL: Issues, https://github.com/leo007-htun/random_tone_generator/issues
|
|
10
|
+
Keywords: audio,tone,signal,generator,sweep,sound
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
Requires-Dist: numpy>=1.23
|
|
25
|
+
Requires-Dist: sounddevice>=0.4.6
|
|
26
|
+
|
|
27
|
+
# random_tone_generator
|
|
28
|
+
|
|
29
|
+
A small Python utility to generate randomized tone sweeps and holds in real time.
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
- Real-time audio output via `sounddevice`
|
|
34
|
+
- Random sweep patterns:
|
|
35
|
+
- `base -> random -> base`
|
|
36
|
+
- `base -> random -> random -> base`
|
|
37
|
+
- Adjustable:
|
|
38
|
+
- playback speed
|
|
39
|
+
- min/max frequency
|
|
40
|
+
- amplitude
|
|
41
|
+
- Explicit playback duration with `play_for(seconds)`
|
|
42
|
+
|
|
43
|
+
## function calls
|
|
44
|
+
gen.set_speed(speed) # float, must be > 0
|
|
45
|
+
gen.set_max_freq(max_freq) # float, must be >= min_freq
|
|
46
|
+
gen.set_min_freq(min_freq) # float, must be <= max_freq (if implemented)
|
|
47
|
+
gen.set_base_freq(base_freq) # float (if implemented)
|
|
48
|
+
gen.set_amplitude(amplitude) # float in [0.0, 1.0] (if implemented)
|
|
49
|
+
state = gen.get_state() # returns dict of current settings
|
|
50
|
+
gen.play_for(seconds) # play for N seconds (explicit mode)
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install random_tone_generator
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# random_tone_generator
|
|
2
|
+
|
|
3
|
+
A small Python utility to generate randomized tone sweeps and holds in real time.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Real-time audio output via `sounddevice`
|
|
8
|
+
- Random sweep patterns:
|
|
9
|
+
- `base -> random -> base`
|
|
10
|
+
- `base -> random -> random -> base`
|
|
11
|
+
- Adjustable:
|
|
12
|
+
- playback speed
|
|
13
|
+
- min/max frequency
|
|
14
|
+
- amplitude
|
|
15
|
+
- Explicit playback duration with `play_for(seconds)`
|
|
16
|
+
|
|
17
|
+
## function calls
|
|
18
|
+
gen.set_speed(speed) # float, must be > 0
|
|
19
|
+
gen.set_max_freq(max_freq) # float, must be >= min_freq
|
|
20
|
+
gen.set_min_freq(min_freq) # float, must be <= max_freq (if implemented)
|
|
21
|
+
gen.set_base_freq(base_freq) # float (if implemented)
|
|
22
|
+
gen.set_amplitude(amplitude) # float in [0.0, 1.0] (if implemented)
|
|
23
|
+
state = gen.get_state() # returns dict of current settings
|
|
24
|
+
gen.play_for(seconds) # play for N seconds (explicit mode)
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install random_tone_generator
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "random_tone_generator"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
description = "Random sweep/hold tone generator with explicit-duration playback."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Sithu Ye Htun" }
|
|
14
|
+
]
|
|
15
|
+
keywords = ["audio", "tone", "signal", "generator", "sweep", "sound"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
23
|
+
"Programming Language :: Python :: 3.9",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Topic :: Multimedia :: Sound/Audio"
|
|
28
|
+
]
|
|
29
|
+
dependencies = [
|
|
30
|
+
"numpy>=1.23",
|
|
31
|
+
"sounddevice>=0.4.6"
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://github.com/leo007-htun/random_tone_generator"
|
|
36
|
+
Repository = "https://github.com/leo007-htun/random_tone_generator"
|
|
37
|
+
Issues = "https://github.com/leo007-htun/random_tone_generator/issues"
|
|
38
|
+
|
|
39
|
+
[tool.setuptools]
|
|
40
|
+
py-modules = ["random_tone_generator"]
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.packages.find]
|
|
43
|
+
where = ["."]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: random_tone_generator
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Random sweep/hold tone generator with explicit-duration playback.
|
|
5
|
+
Author: Sithu Ye Htun
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/leo007-htun/random_tone_generator
|
|
8
|
+
Project-URL: Repository, https://github.com/leo007-htun/random_tone_generator
|
|
9
|
+
Project-URL: Issues, https://github.com/leo007-htun/random_tone_generator/issues
|
|
10
|
+
Keywords: audio,tone,signal,generator,sweep,sound
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
Requires-Dist: numpy>=1.23
|
|
25
|
+
Requires-Dist: sounddevice>=0.4.6
|
|
26
|
+
|
|
27
|
+
# random_tone_generator
|
|
28
|
+
|
|
29
|
+
A small Python utility to generate randomized tone sweeps and holds in real time.
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
- Real-time audio output via `sounddevice`
|
|
34
|
+
- Random sweep patterns:
|
|
35
|
+
- `base -> random -> base`
|
|
36
|
+
- `base -> random -> random -> base`
|
|
37
|
+
- Adjustable:
|
|
38
|
+
- playback speed
|
|
39
|
+
- min/max frequency
|
|
40
|
+
- amplitude
|
|
41
|
+
- Explicit playback duration with `play_for(seconds)`
|
|
42
|
+
|
|
43
|
+
## function calls
|
|
44
|
+
gen.set_speed(speed) # float, must be > 0
|
|
45
|
+
gen.set_max_freq(max_freq) # float, must be >= min_freq
|
|
46
|
+
gen.set_min_freq(min_freq) # float, must be <= max_freq (if implemented)
|
|
47
|
+
gen.set_base_freq(base_freq) # float (if implemented)
|
|
48
|
+
gen.set_amplitude(amplitude) # float in [0.0, 1.0] (if implemented)
|
|
49
|
+
state = gen.get_state() # returns dict of current settings
|
|
50
|
+
gen.play_for(seconds) # play for N seconds (explicit mode)
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install random_tone_generator
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
random_tone_generator.py
|
|
4
|
+
random_tone_generator.egg-info/PKG-INFO
|
|
5
|
+
random_tone_generator.egg-info/SOURCES.txt
|
|
6
|
+
random_tone_generator.egg-info/dependency_links.txt
|
|
7
|
+
random_tone_generator.egg-info/requires.txt
|
|
8
|
+
random_tone_generator.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import random
|
|
2
|
+
import numpy as np
|
|
3
|
+
import sounddevice as sd
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class RandomToneGenerator:
|
|
7
|
+
def __init__(
|
|
8
|
+
self,
|
|
9
|
+
sample_rate=44100,
|
|
10
|
+
base_freq=100.0,
|
|
11
|
+
min_freq=100.0,
|
|
12
|
+
max_freq=8000.0,
|
|
13
|
+
amplitude=0.2,
|
|
14
|
+
speed=1.0,
|
|
15
|
+
base_sweep_min=0.04,
|
|
16
|
+
base_sweep_max=0.25,
|
|
17
|
+
base_hold_min=0.00,
|
|
18
|
+
base_hold_max=0.05,
|
|
19
|
+
min_duration_floor=0.003,
|
|
20
|
+
):
|
|
21
|
+
self.sample_rate = sample_rate
|
|
22
|
+
self.base_freq = float(base_freq)
|
|
23
|
+
self.min_freq = float(min_freq)
|
|
24
|
+
self.max_freq = float(max_freq)
|
|
25
|
+
self.amplitude = float(amplitude)
|
|
26
|
+
self.speed = float(speed)
|
|
27
|
+
|
|
28
|
+
self.base_sweep_min = float(base_sweep_min)
|
|
29
|
+
self.base_sweep_max = float(base_sweep_max)
|
|
30
|
+
self.base_hold_min = float(base_hold_min)
|
|
31
|
+
self.base_hold_max = float(base_hold_max)
|
|
32
|
+
self.min_duration_floor = float(min_duration_floor)
|
|
33
|
+
|
|
34
|
+
self._phase = 0.0
|
|
35
|
+
|
|
36
|
+
def set_speed(self, speed: float):
|
|
37
|
+
if speed <= 0:
|
|
38
|
+
raise ValueError("speed must be > 0")
|
|
39
|
+
self.speed = float(speed)
|
|
40
|
+
|
|
41
|
+
def set_max_freq(self, max_freq: float):
|
|
42
|
+
if max_freq < self.min_freq:
|
|
43
|
+
raise ValueError("max_freq must be >= min_freq")
|
|
44
|
+
self.max_freq = float(max_freq)
|
|
45
|
+
|
|
46
|
+
def get_state(self):
|
|
47
|
+
return {
|
|
48
|
+
"base_freq": self.base_freq,
|
|
49
|
+
"min_freq": self.min_freq,
|
|
50
|
+
"max_freq": self.max_freq,
|
|
51
|
+
"amplitude": self.amplitude,
|
|
52
|
+
"speed": self.speed,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
def _scaled_times(self):
|
|
56
|
+
sweep_min = max(self.min_duration_floor, self.base_sweep_min / self.speed)
|
|
57
|
+
sweep_max = max(self.min_duration_floor, self.base_sweep_max / self.speed)
|
|
58
|
+
hold_min = max(0.0, self.base_hold_min / self.speed)
|
|
59
|
+
hold_max = max(0.0, self.base_hold_max / self.speed)
|
|
60
|
+
return sweep_min, sweep_max, hold_min, hold_max
|
|
61
|
+
|
|
62
|
+
def _sweep(self, f0, f1, dur):
|
|
63
|
+
n = max(1, int(self.sample_rate * dur))
|
|
64
|
+
freq_curve = np.linspace(f0, f1, n, endpoint=False)
|
|
65
|
+
phase_inc = 2 * np.pi * freq_curve / self.sample_rate
|
|
66
|
+
phase_curve = self._phase + np.cumsum(phase_inc)
|
|
67
|
+
y = self.amplitude * np.sin(phase_curve)
|
|
68
|
+
|
|
69
|
+
fade_len = min(256, n // 8)
|
|
70
|
+
if fade_len > 1:
|
|
71
|
+
fade = np.linspace(0, 1, fade_len)
|
|
72
|
+
y[:fade_len] *= fade
|
|
73
|
+
y[-fade_len:] *= fade[::-1]
|
|
74
|
+
|
|
75
|
+
self._phase = float(phase_curve[-1] % (2 * np.pi))
|
|
76
|
+
return y.astype(np.float32)
|
|
77
|
+
|
|
78
|
+
def _hold(self, f, dur):
|
|
79
|
+
n = max(1, int(self.sample_rate * dur))
|
|
80
|
+
phase_inc = 2 * np.pi * f / self.sample_rate
|
|
81
|
+
phase_curve = self._phase + phase_inc * np.arange(1, n + 1)
|
|
82
|
+
y = self.amplitude * np.sin(phase_curve)
|
|
83
|
+
self._phase = float(phase_curve[-1] % (2 * np.pi))
|
|
84
|
+
return y.astype(np.float32)
|
|
85
|
+
|
|
86
|
+
def _write(self, stream, block):
|
|
87
|
+
stream.write(block.reshape(-1, 1))
|
|
88
|
+
|
|
89
|
+
def play_for(self, seconds: float):
|
|
90
|
+
"""
|
|
91
|
+
Plays only for `seconds`, then returns.
|
|
92
|
+
Pattern:
|
|
93
|
+
1) 100 -> rand -> 100
|
|
94
|
+
2) 100 -> rand -> rand -> 100
|
|
95
|
+
"""
|
|
96
|
+
if seconds <= 0:
|
|
97
|
+
return
|
|
98
|
+
|
|
99
|
+
sweep_min, sweep_max, hold_min, hold_max = self._scaled_times()
|
|
100
|
+
|
|
101
|
+
elapsed = 0.0
|
|
102
|
+
with sd.OutputStream(samplerate=self.sample_rate, channels=1, dtype="float32") as stream:
|
|
103
|
+
while elapsed < seconds:
|
|
104
|
+
# Pattern 1
|
|
105
|
+
a = random.uniform(self.min_freq, self.max_freq)
|
|
106
|
+
|
|
107
|
+
d = random.uniform(sweep_min, sweep_max)
|
|
108
|
+
b = self._sweep(self.base_freq, a, d); self._write(stream, b); elapsed += d
|
|
109
|
+
if elapsed >= seconds: break
|
|
110
|
+
|
|
111
|
+
h = random.uniform(hold_min, hold_max)
|
|
112
|
+
if h > 0:
|
|
113
|
+
b = self._hold(a, h); self._write(stream, b); elapsed += h
|
|
114
|
+
if elapsed >= seconds: break
|
|
115
|
+
|
|
116
|
+
d = random.uniform(sweep_min, sweep_max)
|
|
117
|
+
b = self._sweep(a, self.base_freq, d); self._write(stream, b); elapsed += d
|
|
118
|
+
if elapsed >= seconds: break
|
|
119
|
+
|
|
120
|
+
h = random.uniform(hold_min, hold_max)
|
|
121
|
+
if h > 0:
|
|
122
|
+
b = self._hold(self.base_freq, h); self._write(stream, b); elapsed += h
|
|
123
|
+
if elapsed >= seconds: break
|
|
124
|
+
|
|
125
|
+
# Pattern 2
|
|
126
|
+
bfreq = random.uniform(self.min_freq, self.max_freq)
|
|
127
|
+
cfreq = random.uniform(self.min_freq, self.max_freq)
|
|
128
|
+
|
|
129
|
+
d = random.uniform(sweep_min, sweep_max)
|
|
130
|
+
b = self._sweep(self.base_freq, bfreq, d); self._write(stream, b); elapsed += d
|
|
131
|
+
if elapsed >= seconds: break
|
|
132
|
+
|
|
133
|
+
h = random.uniform(hold_min, hold_max)
|
|
134
|
+
if h > 0:
|
|
135
|
+
b = self._hold(bfreq, h); self._write(stream, b); elapsed += h
|
|
136
|
+
if elapsed >= seconds: break
|
|
137
|
+
|
|
138
|
+
d = random.uniform(sweep_min, sweep_max)
|
|
139
|
+
b = self._sweep(bfreq, cfreq, d); self._write(stream, b); elapsed += d
|
|
140
|
+
if elapsed >= seconds: break
|
|
141
|
+
|
|
142
|
+
h = random.uniform(hold_min, hold_max)
|
|
143
|
+
if h > 0:
|
|
144
|
+
b = self._hold(cfreq, h); self._write(stream, b); elapsed += h
|
|
145
|
+
if elapsed >= seconds: break
|
|
146
|
+
|
|
147
|
+
d = random.uniform(sweep_min, sweep_max)
|
|
148
|
+
b = self._sweep(cfreq, self.base_freq, d); self._write(stream, b); elapsed += d
|