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()