Skip to content

Commit bfc2de7

Browse files
committed
add glide parameter in SineGenerator
1 parent 22233b2 commit bfc2de7

File tree

1 file changed

+26
-5
lines changed

1 file changed

+26
-5
lines changed

src/arduino/app_utils/audio.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def __init__(self, sample_rate: int):
3131
# envelope parameters (attack/release in seconds)
3232
self.attack = 0.01
3333
self.release = 0.03
34+
self.glide = 0.02
3435

3536
# reusable buffers
3637
self._buf_N = 0
@@ -88,7 +89,7 @@ def set_state(self, state: dict) -> None:
8889
if "freq_last" in state:
8990
self._freq_last = float(state["freq_last"])
9091

91-
def set_envelope_params(self, attack: float, release: float) -> None:
92+
def set_envelope_params(self, attack: float, release: float, glide: float) -> None:
9293
"""Update attack and release envelope parameters.
9394
9495
Args:
@@ -99,6 +100,7 @@ def set_envelope_params(self, attack: float, release: float) -> None:
99100
"""
100101
self.attack = float(max(0.0, attack))
101102
self.release = float(max(0.0, release))
103+
self.glide = float(max(0.0, glide))
102104

103105
def generate_block(self, freq: float, amp_target: float, block_dur: float, master_volume: float):
104106
"""Generate a block of float32 audio samples.
@@ -149,10 +151,29 @@ def generate_block(self, freq: float, amp_target: float, block_dur: float, maste
149151
envelope[:] = np.linspace(amp_current, next_amp, N, dtype=np.float32)
150152
amp_current = float(envelope[-1])
151153

152-
# oscillator
153-
phase_incr = 2.0 * math.pi * float(freq) / float(self.sample_rate)
154+
# frequency glide (portamento)
155+
freq_current = float(self._freq_last)
156+
freq_target = float(freq)
157+
glide = float(self.glide)
154158
phase_incs = self._buf_phase_incs[:N]
155-
phase_incs.fill(phase_incr)
159+
160+
if glide > 0.0 and freq_current != freq_target:
161+
# Apply glide smoothing over time
162+
frac = min(1.0, block_dur / glide)
163+
next_freq = freq_current + (freq_target - freq_current) * frac
164+
165+
# Linear interpolation within block
166+
freq_ramp = np.linspace(freq_current, next_freq, N, dtype=np.float32)
167+
phase_incs[:] = 2.0 * math.pi * freq_ramp / float(self.sample_rate)
168+
169+
freq_current = float(next_freq)
170+
else:
171+
# No glide or already at target
172+
phase_incr = 2.0 * math.pi * freq_target / float(self.sample_rate)
173+
phase_incs.fill(phase_incr)
174+
freq_current = freq_target
175+
176+
# oscillator (phase accumulation)
156177
np.cumsum(phase_incs, dtype=np.float32, out=phases)
157178
phases += self._phase
158179
self._phase = float(phases[-1] % (2.0 * math.pi))
@@ -168,6 +189,6 @@ def generate_block(self, freq: float, amp_target: float, block_dur: float, maste
168189

169190
# update state
170191
self._amp_current = amp_current
171-
self._freq_last = float(freq)
192+
self._freq_last = freq_current
172193

173194
return samples

0 commit comments

Comments
 (0)