-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpoo.py
More file actions
101 lines (80 loc) · 3.05 KB
/
poo.py
File metadata and controls
101 lines (80 loc) · 3.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import librosa
import numpy as np
import sounddevice as sd
import threading
import tkinter as tk
from tkinter import ttk
from queue import Queue
import time
# Load audio file (mp3, wav, etc.)
#audio_path = "./assets/audio/Kevin MacLeod - Hep Cats.mp3"
audio_path = "./assets/audio/Move Forward 111bpm FULL MIX.wav"
y, sr = librosa.load(audio_path, sr=None)
# Playback controls
playback_rate = [1.0]
playing = [True]
chunk_duration = 1/120 # seconds, if put at 1/4410, bitcrush
chunk_samples = int(sr * chunk_duration)
max_queue_size = 15 #if put at 100, bitcrush also
audio_queue = Queue(maxsize=max_queue_size)
def producer_thread():
start = 0
while start < len(y) and playing[0]:
if audio_queue.full():
time.sleep(0.01)
continue
end = start + int(chunk_samples/playback_rate[0])
chunk = y[start:end]
# Zero-pad input chunk to fixed length
chunk = librosa.util.fix_length(chunk, size=int(chunk_samples/playback_rate[0]))
# Resample based on playback rate
rate = playback_rate[0]
new_sr = int(sr * rate)
resampled = librosa.resample(chunk, orig_sr=sr, target_sr=new_sr)
# Fix output length to constant size (based on original sample rate)
target_output_len = chunk_samples
resampled = librosa.util.fix_length(resampled, size=target_output_len)
# Normalize safely
max_val = np.max(np.abs(resampled))
if max_val > 0:
resampled = resampled / max_val
resampled = np.clip(resampled, -1.0, 1.0)
audio_queue.put(resampled.astype(np.float32))
#start += int(chunk_samples/playback_rate[0])
start += int(chunk_samples/1)
def audio_callback(outdata, frames, time_info, status):
if not audio_queue.empty():
chunk = audio_queue.get()
outdata[:] = chunk.reshape(-1, 1)
else:
outdata.fill(0) # silence if buffer underrun
def update_rate(val):
playback_rate[0] = .5**float(val)
def update_gui():
if y is not None:
fill = int((audio_queue.qsize() / max_queue_size) * 100)
buffer_bar['value'] = fill
if playing[0]:
root.after(100, update_gui)
# GUI Setup
root = tk.Tk()
root.title("Smooth Audio Player with Pitch & Speed Control")
ttk.Label(root, text="Playback Rate (Pitch & Speed):").pack(pady=5)
slider = ttk.Scale(root, from_=-3, to=3, value=0, orient="horizontal", command=update_rate)
slider.pack(fill='x', padx=10)
ttk.Label(root, text="Buffer Fill:").pack(pady=5)
buffer_bar = ttk.Progressbar(root, length=200, mode='determinate')
buffer_bar.pack(fill='x', padx=10, pady=(0, 10))
ttk.Button(root, text="Stop", command=lambda: (playing.__setitem__(0, False), root.destroy())).pack(pady=10)
# Start producer thread
threading.Thread(target=producer_thread, daemon=True).start()
# Start audio stream
stream = sd.OutputStream(channels=1, samplerate=sr, dtype='float32',
callback=audio_callback, blocksize=chunk_samples)
stream.start()
update_gui()
root.mainloop()
# Cleanup on exit
playing[0] = False
stream.stop()
stream.close()