80 lines
2.2 KiB
Python
80 lines
2.2 KiB
Python
import numpy as np
|
|
import matplotlib.pyplot as plt
|
|
from matplotlib.animation import FuncAnimation
|
|
|
|
class DecayingSineWave:
|
|
def __init__(self, freq=5, decay=0.05, sample_rate=60):
|
|
self.freq = freq
|
|
self.decay = decay
|
|
self.sample_rate = sample_rate
|
|
self.triggers = []
|
|
|
|
def trigger(self, t):
|
|
self.triggers.append(t)
|
|
|
|
def sample(self, t):
|
|
value = 0.0
|
|
still_active = []
|
|
for start_time in self.triggers:
|
|
age = t - start_time
|
|
if age >= 0:
|
|
v = np.sin(2 * np.pi * self.freq * age / self.sample_rate) * np.exp(-self.decay * age)
|
|
value += v
|
|
if np.exp(-self.decay * age) > 1e-3:
|
|
still_active.append(start_time)
|
|
self.triggers = still_active
|
|
return value
|
|
|
|
# --- Initialize ---
|
|
wave = DecayingSineWave(freq=5, decay=0.05, sample_rate=60)
|
|
wave_array = np.zeros(512)
|
|
time = [0]
|
|
max_len = 512
|
|
|
|
fig, ax = plt.subplots()
|
|
line, = ax.plot(np.arange(512), wave_array, lw=2)
|
|
trig_dots, = ax.plot([], [], 'ro', markersize=4)
|
|
|
|
ax.set_xlim(0, 511)
|
|
ax.set_ylim(-1.2, 1.2)
|
|
ax.set_title("Click to Trigger Decaying Sine Wave")
|
|
ax.set_xlabel("Sample Index (0 = current)")
|
|
ax.set_ylabel("Amplitude")
|
|
ax.grid(True)
|
|
|
|
trigger_times = []
|
|
|
|
# --- Click handler ---
|
|
def on_click(event):
|
|
current_time = time[0]
|
|
wave.trigger(current_time)
|
|
trigger_times.append(current_time)
|
|
|
|
fig.canvas.mpl_connect('button_press_event', on_click)
|
|
|
|
# --- Animation update ---
|
|
def update(frame):
|
|
global wave_array
|
|
current_time = time[0]
|
|
|
|
# Shift buffer to the right (older samples move toward the end)
|
|
wave_array = wave_array * 0.995
|
|
wave_array = np.roll(wave_array, 1)
|
|
# Insert new sample at index 0
|
|
wave_array[0] = wave.sample(current_time)
|
|
print (wave_array)
|
|
|
|
line.set_data(np.arange(512), wave_array)
|
|
|
|
# Trigger markers
|
|
visible_triggers = [tt for tt in trigger_times if current_time - 512 < tt <= current_time]
|
|
x = [current_time - tt for tt in visible_triggers] # 0 = current time
|
|
y = [1.0 for _ in x]
|
|
trig_dots.set_data(x, y)
|
|
|
|
time[0] += 1
|
|
return line, trig_dots
|
|
|
|
ani = FuncAnimation(fig, update, interval=1000 / 60, blit=True)
|
|
plt.show()
|