claudible 0.1.3__tar.gz → 0.1.5__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.
- {claudible-0.1.3 → claudible-0.1.5}/PKG-INFO +37 -11
- {claudible-0.1.3 → claudible-0.1.5}/README.md +35 -9
- {claudible-0.1.3 → claudible-0.1.5}/pyproject.toml +2 -2
- {claudible-0.1.3 → claudible-0.1.5}/src/claudible/audio.py +39 -12
- {claudible-0.1.3 → claudible-0.1.5}/src/claudible/cli.py +34 -13
- claudible-0.1.5/src/claudible/materials.py +741 -0
- {claudible-0.1.3 → claudible-0.1.5}/src/claudible.egg-info/PKG-INFO +37 -11
- claudible-0.1.3/src/claudible/materials.py +0 -336
- {claudible-0.1.3 → claudible-0.1.5}/setup.cfg +0 -0
- {claudible-0.1.3 → claudible-0.1.5}/src/claudible/__init__.py +0 -0
- {claudible-0.1.3 → claudible-0.1.5}/src/claudible/__main__.py +0 -0
- {claudible-0.1.3 → claudible-0.1.5}/src/claudible/monitor.py +0 -0
- {claudible-0.1.3 → claudible-0.1.5}/src/claudible.egg-info/SOURCES.txt +0 -0
- {claudible-0.1.3 → claudible-0.1.5}/src/claudible.egg-info/dependency_links.txt +0 -0
- {claudible-0.1.3 → claudible-0.1.5}/src/claudible.egg-info/entry_points.txt +0 -0
- {claudible-0.1.3 → claudible-0.1.5}/src/claudible.egg-info/requires.txt +0 -0
- {claudible-0.1.3 → claudible-0.1.5}/src/claudible.egg-info/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claudible
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary: Ambient audio soundscape feedback for terminal output - an opus for your terminals
|
|
3
|
+
Version: 0.1.5
|
|
4
|
+
Summary: Ambient audio soundscape feedback for terminal output - an opus for your terminals, claude code by default
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/anthropics/claudible
|
|
7
7
|
Classifier: Development Status :: 4 - Beta
|
|
@@ -40,7 +40,9 @@ Requires-Dist: sounddevice>=0.4.0
|
|
|
40
40
|
|
|
41
41
|
*An opus for your terminals.*
|
|
42
42
|
|
|
43
|
-
Possibly the most annoying Claude utility ever made but here it is anyway.
|
|
43
|
+
Possibly the most annoying Claude utility ever made but here it is anyway.
|
|
44
|
+
|
|
45
|
+
Ambient audio soundscape feedback for terminal output. It really shines when used with a lot of busy busy Claude Code terminals running at the same time.
|
|
44
46
|
|
|
45
47
|
## 🎼 The Idea
|
|
46
48
|
|
|
@@ -64,7 +66,7 @@ pip install claudible
|
|
|
64
66
|
## 🎹 Usage
|
|
65
67
|
|
|
66
68
|
```bash
|
|
67
|
-
# Run claude with audio feedback (default)
|
|
69
|
+
# Run claude with audio feedback (default: ambient sound set)
|
|
68
70
|
claudible
|
|
69
71
|
|
|
70
72
|
# Run a different command
|
|
@@ -74,16 +76,20 @@ claudible "python my_script.py"
|
|
|
74
76
|
some-command 2>&1 | claudible --pipe
|
|
75
77
|
|
|
76
78
|
# Choose a sound character
|
|
77
|
-
claudible --character
|
|
79
|
+
claudible --character drift
|
|
80
|
+
|
|
81
|
+
# Use the percussive material sound set
|
|
82
|
+
claudible --set material -c bell
|
|
78
83
|
|
|
79
84
|
# Adjust volume
|
|
80
85
|
claudible --volume 0.3
|
|
81
86
|
|
|
82
|
-
# List available characters
|
|
87
|
+
# List available characters across all sets
|
|
83
88
|
claudible --list-characters
|
|
84
89
|
|
|
85
|
-
# Demo all sound characters
|
|
90
|
+
# Demo all sound characters in a set
|
|
86
91
|
claudible --demo
|
|
92
|
+
claudible --demo --set material
|
|
87
93
|
```
|
|
88
94
|
|
|
89
95
|
### 🔇 Reverse Mode
|
|
@@ -113,7 +119,26 @@ npm run dev 2>&1 | claudible --pipe
|
|
|
113
119
|
tail -f /var/log/app.log | claudible --pipe -c droplet
|
|
114
120
|
```
|
|
115
121
|
|
|
116
|
-
## 🎻 Sound
|
|
122
|
+
## 🎻 Sound Sets
|
|
123
|
+
|
|
124
|
+
Claudible ships with two sound sets. The **ambient** set (default) produces soft, throbbing textures with rich overtones. The **material** set has crisp, percussive sounds modelled on physical materials.
|
|
125
|
+
|
|
126
|
+
### Ambient (default) `--set ambient`
|
|
127
|
+
|
|
128
|
+
| Character | | Description |
|
|
129
|
+
|-----------|---|-------------|
|
|
130
|
+
| `drift` | 🌊 | Slow undulating low throb, gentle beating pairs |
|
|
131
|
+
| `tide` | 🌀 | Oceanic wash, phase interference, wide and enveloping |
|
|
132
|
+
| `breath` | 💨 | Soft exhale texture, breathy warmth with filtered noise |
|
|
133
|
+
| `haze` | 🌫️ | Dense foggy overtones, warm and thick with close partial clusters |
|
|
134
|
+
| `pulse` | 💗 | Gentle rhythmic throbbing from detuned pairs, hypnotic |
|
|
135
|
+
| `glow` | 🕯️ | Warm radiant harmonics, rich natural overtone series |
|
|
136
|
+
| `cloud` | ☁️ | Diffuse and soft, massive reverb space, floating |
|
|
137
|
+
| `murmur` | 🫧 | Low gentle rumble, warm harmonic murmur with subtle throb |
|
|
138
|
+
| `shimmer` | ✨ | High ethereal overtones, floating and luminous |
|
|
139
|
+
| `deep` | 🎚️ | Sub-bass throb, felt more than heard, very deep and slow |
|
|
140
|
+
|
|
141
|
+
### Material `--set material`
|
|
117
142
|
|
|
118
143
|
| Character | | Description |
|
|
119
144
|
|-----------|---|-------------|
|
|
@@ -137,12 +162,13 @@ tail -f /var/log/app.log | claudible --pipe -c droplet
|
|
|
137
162
|
| Flag | Description |
|
|
138
163
|
|------|-------------|
|
|
139
164
|
| `--pipe` | 📥 Read from stdin instead of wrapping |
|
|
140
|
-
| `--
|
|
165
|
+
| `--set`, `-s` | 🎼 Sound set: `ambient` (default), `material` |
|
|
166
|
+
| `--character`, `-c` | 🎵 Sound character within the set |
|
|
141
167
|
| `--volume`, `-v` | 🔊 Volume 0.0–1.0 (default: 0.5) |
|
|
142
168
|
| `--attention`, `-a` | ⏰ Silence alert seconds (default: 30) |
|
|
143
169
|
| `--reverse`, `-r` | 🔄 Reverse mode: sound during silence, quiet during output |
|
|
144
|
-
| `--list-characters` | 📋 Show
|
|
145
|
-
| `--demo` | 🔊
|
|
170
|
+
| `--list-characters` | 📋 Show all characters across all sets |
|
|
171
|
+
| `--demo` | 🔊 Demo characters in the selected set |
|
|
146
172
|
|
|
147
173
|
## 🛠️ Development
|
|
148
174
|
|
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
|
|
16
16
|
*An opus for your terminals.*
|
|
17
17
|
|
|
18
|
-
Possibly the most annoying Claude utility ever made but here it is anyway.
|
|
18
|
+
Possibly the most annoying Claude utility ever made but here it is anyway.
|
|
19
|
+
|
|
20
|
+
Ambient audio soundscape feedback for terminal output. It really shines when used with a lot of busy busy Claude Code terminals running at the same time.
|
|
19
21
|
|
|
20
22
|
## 🎼 The Idea
|
|
21
23
|
|
|
@@ -39,7 +41,7 @@ pip install claudible
|
|
|
39
41
|
## 🎹 Usage
|
|
40
42
|
|
|
41
43
|
```bash
|
|
42
|
-
# Run claude with audio feedback (default)
|
|
44
|
+
# Run claude with audio feedback (default: ambient sound set)
|
|
43
45
|
claudible
|
|
44
46
|
|
|
45
47
|
# Run a different command
|
|
@@ -49,16 +51,20 @@ claudible "python my_script.py"
|
|
|
49
51
|
some-command 2>&1 | claudible --pipe
|
|
50
52
|
|
|
51
53
|
# Choose a sound character
|
|
52
|
-
claudible --character
|
|
54
|
+
claudible --character drift
|
|
55
|
+
|
|
56
|
+
# Use the percussive material sound set
|
|
57
|
+
claudible --set material -c bell
|
|
53
58
|
|
|
54
59
|
# Adjust volume
|
|
55
60
|
claudible --volume 0.3
|
|
56
61
|
|
|
57
|
-
# List available characters
|
|
62
|
+
# List available characters across all sets
|
|
58
63
|
claudible --list-characters
|
|
59
64
|
|
|
60
|
-
# Demo all sound characters
|
|
65
|
+
# Demo all sound characters in a set
|
|
61
66
|
claudible --demo
|
|
67
|
+
claudible --demo --set material
|
|
62
68
|
```
|
|
63
69
|
|
|
64
70
|
### 🔇 Reverse Mode
|
|
@@ -88,7 +94,26 @@ npm run dev 2>&1 | claudible --pipe
|
|
|
88
94
|
tail -f /var/log/app.log | claudible --pipe -c droplet
|
|
89
95
|
```
|
|
90
96
|
|
|
91
|
-
## 🎻 Sound
|
|
97
|
+
## 🎻 Sound Sets
|
|
98
|
+
|
|
99
|
+
Claudible ships with two sound sets. The **ambient** set (default) produces soft, throbbing textures with rich overtones. The **material** set has crisp, percussive sounds modelled on physical materials.
|
|
100
|
+
|
|
101
|
+
### Ambient (default) `--set ambient`
|
|
102
|
+
|
|
103
|
+
| Character | | Description |
|
|
104
|
+
|-----------|---|-------------|
|
|
105
|
+
| `drift` | 🌊 | Slow undulating low throb, gentle beating pairs |
|
|
106
|
+
| `tide` | 🌀 | Oceanic wash, phase interference, wide and enveloping |
|
|
107
|
+
| `breath` | 💨 | Soft exhale texture, breathy warmth with filtered noise |
|
|
108
|
+
| `haze` | 🌫️ | Dense foggy overtones, warm and thick with close partial clusters |
|
|
109
|
+
| `pulse` | 💗 | Gentle rhythmic throbbing from detuned pairs, hypnotic |
|
|
110
|
+
| `glow` | 🕯️ | Warm radiant harmonics, rich natural overtone series |
|
|
111
|
+
| `cloud` | ☁️ | Diffuse and soft, massive reverb space, floating |
|
|
112
|
+
| `murmur` | 🫧 | Low gentle rumble, warm harmonic murmur with subtle throb |
|
|
113
|
+
| `shimmer` | ✨ | High ethereal overtones, floating and luminous |
|
|
114
|
+
| `deep` | 🎚️ | Sub-bass throb, felt more than heard, very deep and slow |
|
|
115
|
+
|
|
116
|
+
### Material `--set material`
|
|
92
117
|
|
|
93
118
|
| Character | | Description |
|
|
94
119
|
|-----------|---|-------------|
|
|
@@ -112,12 +137,13 @@ tail -f /var/log/app.log | claudible --pipe -c droplet
|
|
|
112
137
|
| Flag | Description |
|
|
113
138
|
|------|-------------|
|
|
114
139
|
| `--pipe` | 📥 Read from stdin instead of wrapping |
|
|
115
|
-
| `--
|
|
140
|
+
| `--set`, `-s` | 🎼 Sound set: `ambient` (default), `material` |
|
|
141
|
+
| `--character`, `-c` | 🎵 Sound character within the set |
|
|
116
142
|
| `--volume`, `-v` | 🔊 Volume 0.0–1.0 (default: 0.5) |
|
|
117
143
|
| `--attention`, `-a` | ⏰ Silence alert seconds (default: 30) |
|
|
118
144
|
| `--reverse`, `-r` | 🔄 Reverse mode: sound during silence, quiet during output |
|
|
119
|
-
| `--list-characters` | 📋 Show
|
|
120
|
-
| `--demo` | 🔊
|
|
145
|
+
| `--list-characters` | 📋 Show all characters across all sets |
|
|
146
|
+
| `--demo` | 🔊 Demo characters in the selected set |
|
|
121
147
|
|
|
122
148
|
## 🛠️ Development
|
|
123
149
|
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "claudible"
|
|
7
|
-
version = "0.1.
|
|
8
|
-
description = "Ambient audio soundscape feedback for terminal output - an opus for your terminals"
|
|
7
|
+
version = "0.1.5"
|
|
8
|
+
description = "Ambient audio soundscape feedback for terminal output - an opus for your terminals, claude code by default"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
11
11
|
requires-python = ">=3.8"
|
|
@@ -26,6 +26,7 @@ class SoundEngine:
|
|
|
26
26
|
self._stream: Optional[sd.OutputStream] = None
|
|
27
27
|
self._running = False
|
|
28
28
|
self._hum_phase = 0.0
|
|
29
|
+
self._last_pitch = 1.0 # portamento tracking
|
|
29
30
|
self._lock = threading.Lock()
|
|
30
31
|
|
|
31
32
|
# Ring buffer for mixing audio
|
|
@@ -142,11 +143,11 @@ class SoundEngine:
|
|
|
142
143
|
# --- Sound generation ---
|
|
143
144
|
|
|
144
145
|
def _build_grain_cache(self) -> list:
|
|
145
|
-
"""Build grain cache with voices at full-octave intervals,
|
|
146
|
+
"""Build grain cache with voices at full-octave intervals, filtered by material range."""
|
|
146
147
|
# Each register is a full octave apart.
|
|
147
148
|
# (octave_shift, noise_mult, decay_mult, duration_mult)
|
|
148
149
|
# Multiple character variations per register.
|
|
149
|
-
|
|
150
|
+
all_voices = [
|
|
150
151
|
# --- oct -3: sub rumble (3 voices) ---
|
|
151
152
|
(-3, 3.5, 2.5, 1.8), # sub thump
|
|
152
153
|
(-3, 2.0, 3.0, 2.0), # sub knock
|
|
@@ -181,6 +182,8 @@ class SoundEngine:
|
|
|
181
182
|
( 3, 0.3, 0.2, 0.45), # air tick
|
|
182
183
|
( 3, 0.05, 0.3, 0.35), # air wisp
|
|
183
184
|
]
|
|
185
|
+
oct_min, oct_max = self.material.octave_range
|
|
186
|
+
voices = [v for v in all_voices if oct_min <= v[0] <= oct_max]
|
|
184
187
|
cache = []
|
|
185
188
|
for octave, noise_m, decay_m, dur_m in voices:
|
|
186
189
|
cache.append(self._generate_grain(
|
|
@@ -269,6 +272,16 @@ class SoundEngine:
|
|
|
269
272
|
h = ((h * 33) ^ ord(c)) & 0xFFFFFFFF
|
|
270
273
|
return h
|
|
271
274
|
|
|
275
|
+
# Minor pentatonic scale degrees in semitones (wraps at octave)
|
|
276
|
+
_SCALE_DEGREES = [0, 3, 5, 7, 10]
|
|
277
|
+
|
|
278
|
+
def _snap_to_scale(self, semitones: float) -> float:
|
|
279
|
+
"""Quantise a semitone offset to the nearest scale degree."""
|
|
280
|
+
octave = int(semitones // 12)
|
|
281
|
+
remainder = semitones % 12
|
|
282
|
+
nearest = min(self._SCALE_DEGREES, key=lambda d: abs(d - remainder))
|
|
283
|
+
return octave * 12 + nearest
|
|
284
|
+
|
|
272
285
|
def play_grain(self, token: str = ""):
|
|
273
286
|
"""Play a grain/sparkle sound, deterministic for a given token."""
|
|
274
287
|
if token:
|
|
@@ -280,16 +293,30 @@ class SoundEngine:
|
|
|
280
293
|
idx = rng.randint(len(self._grain_cache))
|
|
281
294
|
grain = self._grain_cache[idx].copy()
|
|
282
295
|
|
|
283
|
-
#
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
296
|
+
# Pitch variation snapped to minor pentatonic scale
|
|
297
|
+
raw_semi = rng.uniform(-1.5, 1.5)
|
|
298
|
+
snapped_semi = self._snap_to_scale(raw_semi)
|
|
299
|
+
pitch_shift = 2 ** (snapped_semi / 12)
|
|
300
|
+
|
|
301
|
+
# Fast portamento: slide from last pitch to new target
|
|
302
|
+
prev_pitch = self._last_pitch
|
|
303
|
+
self._last_pitch = pitch_shift
|
|
304
|
+
|
|
305
|
+
n = len(grain)
|
|
306
|
+
if n > 0 and (abs(pitch_shift - 1.0) > 0.001 or abs(prev_pitch - 1.0) > 0.001):
|
|
307
|
+
# Glide over first 30% of grain, then hold target
|
|
308
|
+
glide_len = int(n * 0.3)
|
|
309
|
+
pitch_env = np.ones(n, dtype=np.float32)
|
|
310
|
+
if glide_len > 0:
|
|
311
|
+
pitch_env[:glide_len] = np.linspace(
|
|
312
|
+
prev_pitch, pitch_shift, glide_len, dtype=np.float32,
|
|
313
|
+
)
|
|
314
|
+
pitch_env[glide_len:] = pitch_shift
|
|
315
|
+
|
|
316
|
+
# Time-varying resample using cumulative phase
|
|
317
|
+
phase = np.cumsum(pitch_env)
|
|
318
|
+
phase = phase / phase[-1] * (n - 1)
|
|
319
|
+
grain = np.interp(phase, np.arange(n), grain).astype(np.float32)
|
|
293
320
|
|
|
294
321
|
grain *= rng.uniform(0.7, 1.0)
|
|
295
322
|
|
|
@@ -10,14 +10,18 @@ import time
|
|
|
10
10
|
from typing import Optional
|
|
11
11
|
|
|
12
12
|
from .audio import SoundEngine
|
|
13
|
-
from .materials import
|
|
13
|
+
from .materials import (
|
|
14
|
+
get_material, get_random_material, list_materials, list_sound_sets,
|
|
15
|
+
get_sound_set, SOUND_SETS, DEFAULT_SOUND_SET,
|
|
16
|
+
)
|
|
14
17
|
from .monitor import ActivityMonitor
|
|
15
18
|
|
|
16
19
|
|
|
17
|
-
def run_demo(volume: float):
|
|
18
|
-
"""Play a short demo of every sound character."""
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
def run_demo(volume: float, sound_set: str = DEFAULT_SOUND_SET):
|
|
21
|
+
"""Play a short demo of every sound character in a set."""
|
|
22
|
+
materials = get_sound_set(sound_set)
|
|
23
|
+
print(f"Claudible character demo [{sound_set}]\n", file=sys.stderr)
|
|
24
|
+
for name, mat in materials.items():
|
|
21
25
|
print(f" {name:10} - {mat.description}", file=sys.stderr)
|
|
22
26
|
engine = SoundEngine(material=mat, volume=volume)
|
|
23
27
|
engine.start()
|
|
@@ -129,10 +133,15 @@ def main():
|
|
|
129
133
|
action='store_true',
|
|
130
134
|
help='Pipe mode: read from stdin instead of wrapping a command',
|
|
131
135
|
)
|
|
136
|
+
parser.add_argument(
|
|
137
|
+
'--set', '-s',
|
|
138
|
+
choices=list_sound_sets(),
|
|
139
|
+
default=DEFAULT_SOUND_SET,
|
|
140
|
+
help=f'Sound set (default: {DEFAULT_SOUND_SET})',
|
|
141
|
+
)
|
|
132
142
|
parser.add_argument(
|
|
133
143
|
'--character', '-c',
|
|
134
|
-
|
|
135
|
-
help='Sound character (default: random)',
|
|
144
|
+
help='Sound character (default: random). Use --list-characters to see options.',
|
|
136
145
|
)
|
|
137
146
|
parser.add_argument(
|
|
138
147
|
'--volume', '-v',
|
|
@@ -164,21 +173,33 @@ def main():
|
|
|
164
173
|
|
|
165
174
|
args = parser.parse_args()
|
|
166
175
|
|
|
176
|
+
sound_set = args.set
|
|
177
|
+
|
|
167
178
|
if args.list_characters:
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
print(f" {
|
|
179
|
+
for set_name, materials in SOUND_SETS.items():
|
|
180
|
+
default_tag = " (default)" if set_name == DEFAULT_SOUND_SET else ""
|
|
181
|
+
print(f"\n [{set_name}]{default_tag}\n")
|
|
182
|
+
for name, mat in materials.items():
|
|
183
|
+
print(f" {name:10} - {mat.description}")
|
|
184
|
+
print()
|
|
171
185
|
return
|
|
172
186
|
|
|
173
187
|
if args.demo:
|
|
174
188
|
volume = max(0.0, min(1.0, args.volume))
|
|
175
|
-
run_demo(volume)
|
|
189
|
+
run_demo(volume, sound_set)
|
|
176
190
|
return
|
|
177
191
|
|
|
192
|
+
# Validate character against the chosen set
|
|
178
193
|
if args.character:
|
|
179
|
-
|
|
194
|
+
available = list_materials(sound_set)
|
|
195
|
+
if args.character not in available:
|
|
196
|
+
parser.error(
|
|
197
|
+
f"character '{args.character}' not in set '{sound_set}'. "
|
|
198
|
+
f"Available: {', '.join(available)}"
|
|
199
|
+
)
|
|
200
|
+
material = get_material(args.character, sound_set)
|
|
180
201
|
else:
|
|
181
|
-
material = get_random_material()
|
|
202
|
+
material = get_random_material(sound_set)
|
|
182
203
|
|
|
183
204
|
volume = max(0.0, min(1.0, args.volume))
|
|
184
205
|
|