# test generowania dzwieku stereo jako oscyloskop-rendering X/Y, # dynamiczna zmiany danych sampli sterowana z klawiatury: # - zmiany pozycji obiektu X/Y kur sorami # - zmiana kształtu obektu spacją import numpy as np import sounddevice as sd import threading import time from pynput import keyboard # Parametry sample_rate = 44100 # częstotliwość próbkowania (Hz) sample_duration = 1 # czas trwania próbki (sekundy) duration = 30 # czas odtwarzania dźwięku (sekundy) frequency = 440 # początkowa częstotliwość dźwięku (Hz) scale = 0.5 # skala poczatkowa obrazu # zródłowe sample do aktualnego kształtu l_org_samples = np.empty(0) r_org_samples = np.empty(0) # wynikowe sample , po przekształeceniach, z których będzie wygenerowany dźwięk l_out_samples = np.empty(0) r_out_samples = np.empty(0) # Mechanizm synchronizacji i konczenie wątku lock = threading.Lock() stop_thread = False # zmienne z aktualym przesunięciem X/Y obiektu generowanego z sampli dx = 0.0 dy = 0.0 shape = False # False = koło , True = kwadrat # kształt = koło def gen_circle(): global l_org_samples, r_org_samples t = np.arange(0, sample_duration, 1/sample_rate) # czas l_org_samples = np.sin(2 * np.pi * frequency * t) * scale # lewy kanał r_org_samples = np.cos(2 * np.pi * frequency * t) * scale # prawy kanał # kształt = kwadrat def gen_square(): global l_org_samples, r_org_samples t = np.arange(0, sample_duration, 1/sample_rate) # czas l_org_samples = np.sign(np.sin(2 * np.pi * frequency * t)) * scale # lewy kanał r_org_samples = np.sign(np.cos(2 * np.pi * frequency * t)) * scale # prawy kanał def set_shape(shape): if shape: gen_square() else: gen_circle() # generowanie zmodyfikowanych sampli dla obrazu przesunietego o dx,dy def gen_samples(dx, dy): global l_out_samples, r_out_samples with lock: l_out_samples = l_org_samples + dx r_out_samples = r_org_samples + dy # Funkcja do generowania dźwięku na żywo def live_audio_callback(outdata, frames, time, status): # Funkcja zwrotna, która będzie wywoływana przez sounddevice do przetwarzania próbek if status: print(status) global l_out_samples, r_out_samples with lock: outdata[:, 0] = l_out_samples[:frames] outdata[:, 1] = r_out_samples[:frames] l_out_samples = np.roll(l_out_samples, -frames) r_out_samples = np.roll(r_out_samples, -frames) # Ustawiamy parametry i uruchamiamy odtwarzanie dźwięku def play_audio(): global stop_thread, current_position print("Odtwarzanie dźwięku...\n") while not stop_thread: with sd.OutputStream(channels=2, samplerate=sample_rate, callback=live_audio_callback): sd.sleep(10000) # Odtwarzanie przez określony czas (w ms) # Funkcja wywoływana, gdy naciśniemy klawisz def on_press(key): global dx,dy,shape try: if key == keyboard.Key.up: dy = 1.0 if dy >= 1.0 else dy + 0.1 print(f"X: {dx}, Y: {dy}") gen_samples(dx, dy) elif key == keyboard.Key.down: dy = -1.0 if dy <= -1.0 else dy - 0.1 print(f"X: {dx}, Y: {dy}") gen_samples(dx, dy) elif key == keyboard.Key.left: dx = -1.0 if dx <= -1.0 else dx - 0.1 print(f"X: {dx}, Y: {dy}") gen_samples(dx, dy) elif key == keyboard.Key.right: dx = 1.0 if dx >= 1.0 else dx + 0.1 print(f"X: {dx}, Y: {dy}") gen_samples(dx, dy) elif key == keyboard.Key.space: shape = not shape print("Kształt zmieniony") set_shape(shape) gen_samples(dx, dy) elif key == keyboard.Key.esc: print("Zakończenie programu!") return False # Zatrzymanie nasłuchiwania except AttributeError: pass # Odtwarzanie dźwięku w osobnym wątku set_shape(shape) gen_samples(dx, dy) thread = threading.Thread(target=play_audio) thread.start() print("klawisze strzałek: zmiana pozycji X/Y\nklawisz spacji: zmiana kształtu\nklawisz ESC - wyjście") print(f"X: {dx}, Y: {dy}") # Listener do nasłuchiwania klawiszy with keyboard.Listener(on_press=on_press) as listener: listener.join() stop_thread = True