you can customize your playback settings now. also removed uneeded samples from the repo
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -20,7 +20,7 @@ class ConfigHandler:
|
|||||||
|
|
||||||
return config_dir
|
return config_dir
|
||||||
|
|
||||||
def get(self, section: str, option: str):
|
def get(self, section: str, option: str) -> str:
|
||||||
if not self.config.has_section(section):
|
if not self.config.has_section(section):
|
||||||
self.config.add_section(section)
|
self.config.add_section(section)
|
||||||
if not self.config.has_option(section, option):
|
if not self.config.has_option(section, option):
|
||||||
@@ -53,6 +53,10 @@ class ConfigHandler:
|
|||||||
}
|
}
|
||||||
self.config["appearance"] = {
|
self.config["appearance"] = {
|
||||||
"colour_theme": "textual-dark"
|
"colour_theme": "textual-dark"
|
||||||
|
},
|
||||||
|
self.config["audio"] = {
|
||||||
|
"output_device": "None",
|
||||||
|
"input_device": "None"
|
||||||
}
|
}
|
||||||
|
|
||||||
with open(self.config_dir / "config.ini", "w") as configfile:
|
with open(self.config_dir / "config.ini", "w") as configfile:
|
||||||
|
|||||||
@@ -88,11 +88,26 @@ class SongPlayer:
|
|||||||
if not self.stream.closed:
|
if not self.stream.closed:
|
||||||
self.stream.close()
|
self.stream.close()
|
||||||
|
|
||||||
|
# figure out which device we're using
|
||||||
|
devices = sd.query_devices()
|
||||||
|
device_index = None
|
||||||
|
for device in devices:
|
||||||
|
if device["name"] == self.timeline.app.config_handler.get("audio", "output_device"):
|
||||||
|
device_index = device["index"]
|
||||||
|
|
||||||
|
# something horrible has happened to our user config lmao
|
||||||
|
# not even the config manager could find a device, idek
|
||||||
|
# how that would happen.
|
||||||
|
if not device_index:
|
||||||
|
self.pause()
|
||||||
|
self.timeline.app.notify(f"Failed to find the output device you have specified in settings, please check your config.", title="Error while playing song", severity="error")
|
||||||
|
return False
|
||||||
|
|
||||||
self.stream = sd.OutputStream(
|
self.stream = sd.OutputStream(
|
||||||
channels=self.audio.shape[1] if self.audio.ndim > 1 else 1,
|
channels=self.audio.shape[1] if self.audio.ndim > 1 else 1,
|
||||||
callback=self.play_callback,
|
callback=self.play_callback,
|
||||||
blocksize=256,
|
blocksize=int(self.timeline.app.config_handler.get("audio", "block_size")),
|
||||||
device=7
|
device=device_index
|
||||||
)
|
)
|
||||||
|
|
||||||
self.stream.start()
|
self.stream.start()
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ from textual.widgets import Label, Select, TabbedContent, TabPane, Switch, Input
|
|||||||
from textual.containers import Vertical, HorizontalGroup, VerticalGroup, VerticalScroll
|
from textual.containers import Vertical, HorizontalGroup, VerticalGroup, VerticalScroll
|
||||||
from textual.binding import Binding
|
from textual.binding import Binding
|
||||||
|
|
||||||
|
from textual_slider import Slider
|
||||||
|
|
||||||
|
import sounddevice as sd
|
||||||
|
|
||||||
|
|
||||||
class SettingsScreen(ModalScreen):
|
class SettingsScreen(ModalScreen):
|
||||||
border_title = "Settings"
|
border_title = "Settings"
|
||||||
@@ -31,6 +35,12 @@ class SettingsScreen(ModalScreen):
|
|||||||
text-style: bold;
|
text-style: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#block-size-text {
|
||||||
|
width: 5;
|
||||||
|
height: 3;
|
||||||
|
content-align: left middle;
|
||||||
|
}
|
||||||
|
|
||||||
.setting {
|
.setting {
|
||||||
padding: 0 2;
|
padding: 0 2;
|
||||||
content-align: center middle;
|
content-align: center middle;
|
||||||
@@ -52,8 +62,8 @@ class SettingsScreen(ModalScreen):
|
|||||||
margin-right: 1;
|
margin-right: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Select, Input {
|
Select, Input, Slider {
|
||||||
max-width: 30;
|
max-width: 35;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,7 +82,16 @@ class SettingsScreen(ModalScreen):
|
|||||||
def on_select_changed(self, event: Select.Changed):
|
def on_select_changed(self, event: Select.Changed):
|
||||||
if event.select.id == "colour-theme":
|
if event.select.id == "colour-theme":
|
||||||
self.app.theme = event.value
|
self.app.theme = event.value
|
||||||
self.app.config_handler.set("appearance", "colour_theme", str(event.value))
|
self.app.config_handler.set("appearance", "colour_theme", event.value)
|
||||||
|
elif event.select.id == "output-device":
|
||||||
|
self.app.config_handler.set("audio", "output_device", event.value)
|
||||||
|
elif event.select.id == "input-device":
|
||||||
|
self.app.config_handler.set("audio", "input_device", event.value)
|
||||||
|
|
||||||
|
def on_slider_changed(self, event: Slider.Changed):
|
||||||
|
if event.slider.id == "block-size":
|
||||||
|
self.query_one("#block-size-text").update(str(event.value))
|
||||||
|
self.app.config_handler.set("audio", "block_size", str(event.value))
|
||||||
|
|
||||||
def compose(self):
|
def compose(self):
|
||||||
with Vertical(id="window") as window:
|
with Vertical(id="window") as window:
|
||||||
@@ -91,3 +110,46 @@ class SettingsScreen(ModalScreen):
|
|||||||
id="colour-theme",
|
id="colour-theme",
|
||||||
value=self.app.theme
|
value=self.app.theme
|
||||||
)
|
)
|
||||||
|
with TabPane("Audio"):
|
||||||
|
with VerticalScroll():
|
||||||
|
with HorizontalGroup(classes="setting"):
|
||||||
|
with VerticalGroup():
|
||||||
|
yield Label("Output Device", classes="setting-name")
|
||||||
|
yield Label("The device that audio will be outputted to. If a device is not shown, then [i]sounddevice[/i] thinks it has 0 output channels.", classes="setting-desc")
|
||||||
|
|
||||||
|
devices: list[str] = [device["name"] for device in sd.query_devices() if device["max_output_channels"] > 0]
|
||||||
|
output_device_name = self.app.config_handler.get("audio", "output_device")
|
||||||
|
|
||||||
|
yield Select.from_values(
|
||||||
|
devices,
|
||||||
|
allow_blank=False,
|
||||||
|
id="output-device",
|
||||||
|
value=output_device_name if output_device_name in devices else devices[0]
|
||||||
|
)
|
||||||
|
with HorizontalGroup(classes="setting"):
|
||||||
|
with VerticalGroup():
|
||||||
|
yield Label("Input Device", classes="setting-name")
|
||||||
|
yield Label("The device that will be used for recording audio. If a device is not shown, then [i]sounddevice[/i] thinks it has 0 input channels. (this setting is unused for now)", classes="setting-desc")
|
||||||
|
|
||||||
|
devices: list[str] = [device["name"] for device in sd.query_devices() if device["max_input_channels"] > 0]
|
||||||
|
input_device_name = self.app.config_handler.get("audio", "input_device")
|
||||||
|
|
||||||
|
yield Select.from_values(
|
||||||
|
devices,
|
||||||
|
allow_blank=False,
|
||||||
|
id="input-device",
|
||||||
|
value=input_device_name if input_device_name in devices else devices[0]
|
||||||
|
)
|
||||||
|
with HorizontalGroup(classes="setting"):
|
||||||
|
with VerticalGroup():
|
||||||
|
yield Label("Block Size", classes="setting-name")
|
||||||
|
yield Label("Block size of audio playback. Higher block size will reduce stutters or cut outs, but will also lead to higher CPU strain.", classes="setting-desc")
|
||||||
|
|
||||||
|
yield Static("N/A", id="block-size-text")
|
||||||
|
yield Slider(
|
||||||
|
16,
|
||||||
|
2048,
|
||||||
|
step=16,
|
||||||
|
value=int(self.app.config_handler.get("audio", "block_size")),
|
||||||
|
id="block-size"
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user