Python & Elektrik
I've been tinkering with generating rhythmic patterns from circuit signals and thought a Python script could make it real‑time. Want to hear the prototype?
Sounds cool, just share the script and we can walk through the logic and maybe tweak the timing loop for smoother beats.
Here’s a quick prototype that pulls a simple sine wave from a simulated voltage readout and plays it back in real time. Feel free to swap out the `read_voltage()` placeholder for your actual ADC routine and tweak the phase increments to get the groove you want.
```python
import numpy as np
import sounddevice as sd
import time
# ---- Settings ----
fs = 44100 # sampling rate (Hz)
duration = 5.0 # seconds to stream
freq = 110.0 # base tone (A2)
amp = 0.5 # amplitude (0.0‑1.0)
block_size = 1024 # samples per callback
# ---- Dummy voltage source (replace with real ADC) ----
def read_voltage():
# Simulate a slowly varying voltage (0‑5V) that will drive the phase
return 2.5 + 2.5 * np.sin(2 * np.pi * 0.1 * time.time())
# ---- Audio callback ----
phase = 0.0
def audio_callback(outdata, frames, time_info, status):
global phase
# Get the current voltage and map it to a phase speed (rad/s)
v = read_voltage()
speed = (v / 5.0) * 2 * np.pi * freq # scale 0‑5V to 0‑2π*freq
t = np.arange(frames) / fs
# Generate samples with dynamic phase
out = amp * np.sin(phase + speed * t)
outdata[:, 0] = out
phase += speed * frames / fs
# ---- Stream ----
with sd.OutputStream(channels=1, samplerate=fs, callback=audio_callback, blocksize=block_size):
print(f"Streaming for {duration}s – press Ctrl‑C to stop")
time.sleep(duration)
```
Nice skeleton—just a couple of tweaks. Use a local phase variable inside the callback instead of a global, or reset it when the stream starts, to avoid drift. Also, calculating `speed` every frame from a slowly changing voltage can introduce a jump; maybe interpolate between frames. And remember to handle the case where `status` is set, so you can catch buffer under‑/overflows. Overall, it’s a good start.
Got it—let’s tighten it up and keep the phase local to the callback. I’ll also smooth the speed with a simple moving average and add a status check so we can spot any buffer issues. Here’s the updated snippet:
```python
import numpy as np
import sounddevice as sd
import time
from collections import deque
fs = 44100
duration = 5.0
freq = 110.0
amp = 0.5
block_size = 1024
vol_window = deque([2.5]*10, maxlen=10) # holds recent voltage samples
def read_voltage():
return 2.5 + 2.5 * np.sin(2 * np.pi * 0.1 * time.time())
def audio_callback(outdata, frames, time_info, status):
if status:
print(f"Status: {status}") # catch under/overflows
v = read_voltage()
vol_window.append(v)
avg_v = sum(vol_window) / len(vol_window) # simple interpolation
speed = (avg_v / 5.0) * 2 * np.pi * freq
t = np.arange(frames) / fs
phase = audio_callback.phase
out = amp * np.sin(phase + speed * t)
outdata[:, 0] = out
audio_callback.phase = phase + speed * frames / fs
audio_callback.phase = 0.0 # initialize local phase
with sd.OutputStream(channels=1, samplerate=fs,
callback=audio_callback, blocksize=block_size):
print(f"Streaming for {duration}s – press Ctrl‑C to stop")
time.sleep(duration)
```
Now the phase stays with the callback, the voltage influence is smoothed, and any stream hiccups get logged. Happy tweaking!
Looks solid—just a tiny tweak: `deque` already gives you a moving window, so you could use `np.mean(vol_window)` instead of summing each time. And if you hit a status warning, maybe pause the stream for a split second before resuming to give the audio buffer a breather. Overall, good job!
Nice catch on the mean—switching to `np.mean(vol_window)` keeps it clean. And that little pause on a status warning is a smart buffer‑breather. All set, time to crank up the beat and see how it vibes live.