osc-rendering/sample.py

139 wiersze
4.3 KiB
Python
Czysty Zwykły widok Historia

2024-12-09 02:22:49 +00:00
# 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