making channel controls interactable

This commit is contained in:
2026-01-14 14:43:57 +11:00
parent d757b839d7
commit 7ca336c321
11 changed files with 69 additions and 18 deletions

View File

@@ -1,3 +1,8 @@
App {
layers: top bottom;
}
#top-menu {
dock: top;
layout: horizontal;
}

View File

View File

@@ -5,16 +5,14 @@ import librosa
if __name__ == "__main__":
print("Loading project...")
test_project = Project(channels=[
"""test_project = Project(channels=[
ProjectChannel(chunks=[
AudioChannelChunk(*librosa.load("120 bpm amen break.mp3", mono=False), position=0, name="120 bpm amen break.mp3"),
AudioChannelChunk(*librosa.load("120 bpm amen break.mp3", mono=False), position=1, name="120 bpm amen break.mp3"),
AudioChannelChunk(*librosa.load("120 bpm amen break.mp3", mono=False), position=2, name="120 bpm amen break.mp3")
], name="drums"),
ProjectChannel(chunks=[
AudioChannelChunk(*librosa.load("piano chords - Bmin 120BPM.wav", mono=False), name="piano chords - Bmin 120BPM.wav")
], name="piano")
])#.from_file("test_project.tdp")
])
#test_project.write_to_file("test_project.tdp")"""
test_project = Project.from_file("test_project.tdp")
# start the ui
print("Starting UI...")

13
src/song_player.py Normal file
View File

@@ -0,0 +1,13 @@
from project import Project
import pedalboard
import sounddevice as sd
import numpy as np
from textual.app import App
class SongPlayer:
def __init__(self, app: App):
self.app = app
def play_song(self, project: Project):
pass

Binary file not shown.

View File

@@ -1,22 +1,36 @@
from textual.app import App, ComposeResult
from textual.widgets import Footer
from textual.widgets import Footer, Tab, Tabs, Header
from ui.widgets.sidebar import Sidebar
from ui.widgets.timeline import Timeline
from ui.widgets.project_settings import ProjectSettings
from song_player import SongPlayer
class AppUI(App):
CSS_PATH = "../assets/style.tcss"
theme = "tokyo-night"
#ENABLE_COMMAND_PALETTE = False
def __init__(self, project):
super().__init__()
self.zoom_level = 0.05
self.last_zoom_level = self.zoom_level
self.project = project
self.song_player = SongPlayer(self)
def on_mount(self):
self.song_player.play_song(self.app.project)
def compose(self) -> ComposeResult:
with Tabs(id="top-menu"):
yield Tab("File")
yield Tab("Edit")
yield Tab("View")
yield Tab("Help")
yield Sidebar()
yield Timeline()
yield ProjectSettings()

View File

@@ -4,6 +4,8 @@ from textual.widgets import Checkbox, Input, Static
from textual_slider import Slider
from textual import on, events
class Channel(VerticalGroup):
DEFAULT_CSS = """
@@ -44,23 +46,39 @@ class Channel(VerticalGroup):
}
"""
def __init__(self, channel_name: str = "", muted: bool = False, solo: bool = False, pan: float = 0, volume: float = 0):
def __init__(self, channel_index: int, channel_name: str = "", muted: bool = False, solo: bool = False, pan: float = 0, volume: float = 0):
super().__init__()
self.channel_index = channel_index
self.channel_name = channel_name
self.muted = muted
self.solo = solo
self.pan = pan
self.volume = volume
def on_slider_changed(self, event: Slider.Changed):
if event.slider.id == "volume":
self.volume = round(event.value, 2)
self.query_one("#volume-text").update(f"Volume ({self.volume}Db):")
elif event.slider.id == "pan":
self.pan = round(event.value)
pan_text = self.query_one("#pan-text")
if self.pan == 0:
pan_text.update("Pan (center):")
elif self.pan < 0:
pan_text.update(f"Pan ({-self.pan}% left):")
elif self.pan > 0:
pan_text.update(f"Pan ({self.pan}% right):")
def compose(self) -> ComposeResult:
yield Input(placeholder="Channel Name", compact=True, id="name", value=self.channel_name)
with Horizontal(classes="channel-slider"):
yield Static("Pan (center):")
yield Slider(-100, 100, step=1, value=self.pan)
yield Static("Pan (center):", id="pan-text")
yield Slider(-100, 100, step=1, value=self.pan, id="pan")
with Horizontal(classes="channel-slider"):
yield Static("Volume (+ 0Db):")
yield Slider(-15, 15, step=0.1, value=self.volume)
yield Static("Volume (0Db):", id="volume-text")
yield Slider(-15, 15, step=0.1, value=self.volume, id="volume")
with Horizontal(id="channel-buttons"):
yield Checkbox("Mute", compact=True, id="mute", tooltip="Mute this track", value=self.muted)

View File

@@ -10,9 +10,10 @@ class Sidebar(Vertical):
Sidebar {
dock: left;
background: $surface;
width: 40;
width: 43;
border-right: tall $surface-lighten-1;
padding: 1;
margin-top: 1;
#add-channel {
min-width: 100%;
@@ -23,8 +24,9 @@ class Sidebar(Vertical):
def compose(self) -> ComposeResult:
with VerticalScroll(id="channels"):
for channel in self.app.project.channels:
for i, channel in enumerate(self.app.project.channels):
yield Channel(
i,
channel.name,
channel.mute,
channel.solo,

View File

@@ -95,8 +95,6 @@ class Timeline(Vertical):
for channel in self.app.project.channels:
self.notify(str(channel))
with TimelineRow():
for chunk in channel.chunks:
if chunk.chunk_type == ChunkType.CHUNK:

View File

@@ -0,0 +1 @@
from textual.widgets import Tabs, Tab