Skip to content

Commit dac6228

Browse files
Fix weird audio noise and make audio separate from video
1 parent 2e4101a commit dac6228

8 files changed

Lines changed: 59 additions & 37 deletions

File tree

cnds/cnds.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ NB_MODULE(cnds, m) {
1818
.def("get_top_nds_frame", &Nds::getTopNdsFrame)
1919
.def("get_bot_nds_frame", &Nds::getBotNdsFrame)
2020
.def("get_audio_samples", &Nds::getAudioSamples)
21+
.def("get_audio_buffer_number", &Nds::getAudioBufferNumber)
2122

2223
// Save methods
2324
.def("save_state", &Nds::saveState)

cnds/include/nds.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class Nds {
2323

2424
// Audio methods
2525
nb::ndarray<nb::numpy, int16_t> getAudioSamples(int count);
26+
uint32_t getAudioBufferNumber();
2627

2728
// Savestate methods
2829
void saveState(std::string path);

cnds/nds.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ nb::ndarray<nb::numpy, int16_t> Nds::getAudioSamples(int count) {
8282
return nb::ndarray<nb::numpy, int16_t>(samples, {(size_t)count, 2}, owner);
8383
}
8484

85+
uint32_t Nds::getAudioBufferNumber() {
86+
return m_core->spu.getBufferNumber();
87+
}
88+
8589
void Nds::saveState(std::string path) {
8690
m_core->saveStates.setPath(path, m_isGba);
8791
m_core->saveStates.saveState();

externals/NooDS

pynds/audio.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import time
2+
import threading
3+
4+
import sounddevice as sd
5+
import numpy as np
6+
7+
8+
class Audio:
9+
def __init__(self, pynds) -> None:
10+
self._pynds = pynds
11+
12+
self.running = False
13+
14+
def start(self) -> None:
15+
self.stream = sd.OutputStream(samplerate=32768, channels=2, dtype='int16', blocksize=0)
16+
self.running = True
17+
18+
self.audio_thread = threading.Thread(target=self.play_audio)
19+
self.audio_thread.start()
20+
21+
def close(self) -> None:
22+
if (self.running):
23+
self.running = False
24+
self.audio_thread.join()
25+
26+
def play_audio(self):
27+
self.stream.start()
28+
29+
while (self.running):
30+
samples = self._pynds.get_audio(699)
31+
data_to_write = np.ascontiguousarray(samples)
32+
self.stream.write(data_to_write)
33+
34+
time.sleep(0.001)
35+
36+
self.stream.stop()
37+
self.stream.close()

pynds/pynds.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from .memory import Memory
77
from .button import Button
88
from .window import Window
9+
from .audio import Audio
910
from .config import config
1011

1112

@@ -23,6 +24,7 @@ def __init__(self, path: str, save_path: str = "", auto_detect: bool = True, is_
2324
self.button = Button(self._nds)
2425
self.memory = Memory(self._nds)
2526
self.window = Window(self)
27+
self.audio = Audio(self)
2628

2729
def get_is_gba(self) -> bool:
2830
return self.is_gba
@@ -57,6 +59,9 @@ def get_frame_shape(self) -> Tuple[int, int]:
5759
def get_audio(self, count: int = 699) -> np.ndarray:
5860
return self._nds.get_audio_samples(count)
5961

62+
def get_audio_buffer_number(self) -> int:
63+
return self._nds.get_audio_buffer_number()
64+
6065
def open_window(self, width: int = 800, height: int = 800) -> None:
6166
if (self.window.running):
6267
self.window.close()
@@ -66,6 +71,15 @@ def open_window(self, width: int = 800, height: int = 800) -> None:
6671
def close_window(self) -> None:
6772
self.window.close()
6873

74+
def open_audio(self) -> None:
75+
if (self.audio.running):
76+
self.audio.close()
77+
78+
self.audio.start()
79+
80+
def close_audio(self) -> None:
81+
self.audio.close()
82+
6983
def render(self) -> None:
7084
if (self.window.running):
7185
self.window.render()

pynds/window.py

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,10 @@ def __init__(self, pynds) -> None:
1010
self.running = False
1111

1212
def init(self, width: int, height: int) -> None:
13-
pygame.mixer.pre_init(frequency=44100, size=-16, channels=2, buffer=512)
1413
pygame.init()
1514
self.screen = pygame.display.set_mode((width, height), pygame.RESIZABLE)
1615
pygame.display.set_caption("PyNDS")
1716
self.running = True
18-
self._audio_buffer = np.zeros((0, 2), dtype=np.int16) # continuous buffer
1917

2018
def close(self) -> None:
2119
pygame.quit()
@@ -30,9 +28,6 @@ def render(self) -> None:
3028
elif (self.running):
3129
self.process_frame_nds()
3230

33-
if (self.running):
34-
self.process_audio()
35-
3631
def handle_events(self) -> None:
3732
for event in pygame.event.get():
3833
if event.type == pygame.QUIT:
@@ -142,33 +137,3 @@ def process_frame_nds(self):
142137
surface = pygame.transform.scale(surface, (width, height // 2))
143138
self.screen.blit(surface, surface.get_rect(topleft=(0, height // 2)))
144139
pygame.display.flip()
145-
146-
def process_audio(self):
147-
samples = self._pynds.get_audio(699)
148-
if samples is not None and len(samples) > 0:
149-
# Resample 32768 -> 44100
150-
ratio = 44100 / 32768
151-
new_len = int(len(samples) * ratio)
152-
left = np.interp(
153-
np.linspace(0, len(samples), new_len),
154-
np.arange(len(samples)),
155-
samples[:, 0].astype(np.float32)
156-
).astype(np.int16)
157-
right = np.interp(
158-
np.linspace(0, len(samples), new_len),
159-
np.arange(len(samples)),
160-
samples[:, 1].astype(np.float32)
161-
).astype(np.int16)
162-
resampled = np.column_stack((left, right))
163-
self._audio_buffer = np.concatenate((self._audio_buffer, resampled))
164-
165-
# Only play when we have enough buffered samples
166-
CHUNK = 2048
167-
while len(self._audio_buffer) >= CHUNK:
168-
chunk = np.ascontiguousarray(self._audio_buffer[:CHUNK])
169-
self._audio_buffer = self._audio_buffer[CHUNK:]
170-
# Find a free channel or wait
171-
ch = pygame.mixer.find_channel(False)
172-
if ch is not None:
173-
sound = pygame.sndarray.make_sound(chunk)
174-
ch.play(sound)

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def build_extensions(self):
9090

9191
setup(name='pynds',
9292
packages=find_packages(),
93-
install_requires=['numpy', 'pygame'],
93+
install_requires=['numpy', 'pygame', 'sounddevice'],
9494
version=version,
9595
description='Python bindings for NooDS',
9696
author='unexploredtest',

0 commit comments

Comments
 (0)