Compare commits

...

45 Commits

Author SHA1 Message Date
bed8cae617 display selected athmo and disappear after playback ends 2025-09-01 21:41:34 +02:00
0448355401 changed the display style of the selected beat 2025-09-01 20:31:01 +02:00
ce9b7c39ec change the actual controller fx_mode when tapping the lozenge_buttons 2025-09-01 11:47:16 +02:00
5fe9d7cada made gui compatible with the changes of the controller 2025-08-30 21:56:09 +02:00
ca861f1530 added right-side-knobs and mode-select buttons 2025-08-30 20:25:40 +02:00
468e7974be Merge branch 'main' of https://code.w4d.dev/sallar/SantoscopeUI 2025-08-28 11:07:44 +02:00
a507916f1c optimized some path calculation code 2025-08-18 18:04:21 +02:00
306c8755eb added an option for changing the arrangement according to whether it's a left or right side knobs module 2025-08-18 17:53:56 +02:00
b176c11cc1 made gui compatible with the system 2025-08-07 12:04:51 +02:00
1d247fc0ed selected track and clip gets displayed at the bottom of the UI 2025-08-03 20:09:55 +02:00
46c08009de adjusted item_selection and created a module 'beatplayer' 2025-08-03 19:25:18 +02:00
9f9f667a86 extracted the ItemSelection from athmos and created a parent class 2025-08-03 18:29:25 +02:00
f55522f03c changed the apparence of athmos.py and added max_width property 2025-08-03 18:01:18 +02:00
8265ba0f0a changed GuiMain in order to make it testable with laptop 2025-08-03 17:58:42 +02:00
6dc72353d7 unknown commit 2025-08-03 13:04:12 +02:00
4fb5a899d4 bugfixed last commit 2025-04-08 18:50:06 +02:00
b5b792b449 added a label for the loop input mode 2025-04-08 18:41:46 +02:00
8f9e5257f6 athmos display been bugfixed and can now be switched by the knobs display 2025-04-08 16:03:58 +02:00
bfd5b41e93 bugfix: athmo should not blit at all, when showlist is false 2025-04-05 16:05:47 +02:00
2bb807aca9 the athmo list display has been bug-fixed and a opacity gradient has been added to the list 2025-04-02 23:42:32 +02:00
4ed2a6d3aa the display of athmos has been implemented 2025-04-02 22:59:01 +02:00
bda08dab84 added a module for displaying athmos 2025-04-02 21:27:14 +02:00
ba5f1a6ace the endpoint for setting fx_mode has been improved 2025-03-30 12:10:59 +02:00
6269e6f9f6 the end-point for setting the knob values has been created 2025-03-13 21:48:01 +01:00
24a60d7745 adapted the modules for integrating into the santoscope repository 2025-03-13 19:42:58 +01:00
692da4b3f2 the circle buttons have been refactored in a more object oriented way 2025-03-02 22:40:13 +01:00
6f0e1e3e5e added a fade_in effect to the knobs 2025-03-02 20:16:40 +01:00
27ea7f2504 added a vanish animation for the knob labels 2025-03-02 18:24:08 +01:00
f0237bbb7e added an animation for vanishing the label path 2025-03-01 19:30:48 +01:00
899c065eb0 added a rec button 2025-02-28 20:04:50 +01:00
134d9aa8e4 added a circle button for the loop stations and the beat player 2025-02-28 19:53:20 +01:00
813f8d3a35 added a binary_button module for the mute function 2025-02-28 16:51:32 +01:00
d82c3921d7 the color of the lozege buttons has been turned lighter 2025-02-28 15:26:39 +01:00
df52042972 the knob labels are centered between the right border of the middle knobs and the right border of the display 2025-02-28 15:21:31 +01:00
31ae63b594 the focus of the knobs is removed, when the fx_mode is changed 2025-02-28 14:21:06 +01:00
b960bcbd7f added a knobs instance for every fx_mode 2025-02-28 14:04:53 +01:00
3eae3a7cf6 knobs module takes an attribute 'values' 2025-02-28 13:33:11 +01:00
06006194a8 created a knobs module to outsource the grouping of the knobs 2025-02-28 13:16:39 +01:00
043c847801 replaced the sidebar outline by new components 2025-02-25 22:10:16 +01:00
0719a092dc improved the sidebar look 2025-02-25 21:45:27 +01:00
082126d3c5 editted the sidebar look 2025-02-25 21:37:26 +01:00
f7a9f790cf changed screen sizing properties 2025-02-25 21:26:16 +01:00
3963f404f6 corrected the position of the fx_mode letters 2025-02-25 21:00:58 +01:00
7890c91bd7 adjusted the screen to the santoscope screen 2025-02-25 20:36:47 +01:00
958eec2aa0 outsourced the fx_mode buttons to a new class and some refactoring 2025-02-25 19:59:56 +01:00
12 changed files with 740 additions and 114 deletions

0
__init__.py Executable file
View File

26
athmos.py Executable file
View File

@ -0,0 +1,26 @@
from .fonts import *
from .colors import *
from .item_selection import ItemSelection
import pygame
class Athmos(ItemSelection):
def __init__(self, screen):
self.screen = screen
self.show_list = True
self.max_name_width = 350
super().__init__(self.max_name_width)
self.current_duration = 0
self.time_started = 0
def set_filenames(self,filenames):
super().set_items(filenames)
def get_name_by_index(self, index):
return self.items[index]
def update(self):
if self.show_list:
super().update()
self.screen.blit(self.surface, ((self.screen.get_width()-self.max_name_width)/2,
(self.screen.get_height() - self.surface.get_height())/2))

40
beatplayer.py Normal file
View File

@ -0,0 +1,40 @@
from .item_selection import ItemSelection
import pygame
from .colors import *
from .fonts import *
class BeatPlayer:
def __init__(self, screen):
self.screen = screen
self.max_name_width = 200
self.clip_selection = ItemSelection(self.max_name_width)
self.track_selection = ItemSelection(self.max_name_width)
self.show_selection = False
def set_clip_names(self, clips):
self.clip_selection.set_items(clips)
def set_track_names(self, tracks):
self.track_selection.set_items(tracks)
def update(self):
if self.show_selection:
self.track_selection.update()
self.clip_selection.update()
self.screen.blit(self.track_selection.surface, ((self.screen.get_width()/4),
(self.screen.get_height() - self.track_selection.surface.get_height())/2))
self.screen.blit(self.clip_selection.surface, ((self.screen.get_width()/4 + self.max_name_width) + 100,
(self.screen.get_height() - self.clip_selection.surface.get_height())/2))
else:
selected_track = self.track_selection.get_selected_item()
selected_clip = self.clip_selection.get_selected_item()
if len(selected_track) > 15: selected_track = selected_track[0:13] + '...'
if len(selected_clip) > 30: selected_clip = selected_clip[0:28] + '...'
track_surface = font_helvetica16.render(f"{selected_track}",
False, color_primary_dark)
clip_surface = font_helvetica16.render(f"/ {selected_clip}",
False, color_primary_dark)
self.screen.blit(clip_surface, (self.screen.get_width()/2, 15))
self.screen.blit(track_surface, (self.screen.get_width()/2-track_surface.get_width()-10, 15))

30
binary_button.py Normal file
View File

@ -0,0 +1,30 @@
import pygame
from .fonts import *
from .colors import *
class BinaryButton:
def __init__(self, screen, position, size, label, state):
self.screen = screen
self.position = position
self.size = size
self.label = label
self.state = state
def check_click(self, pos):
if pos[0] >= self.position[0] and pos[1] >= self.position[1] and pos[0] <= self.position[0] + self.size[0] and pos[1] <= self.position[1] + self.size[1]:
self.state = not self.state
def display(self):
if self.state:
w = 0
text_color = "black"
else:
w = 2
text_color = color_primary
pygame.draw.rect(self.screen, color_primary, (self.position, self.size), w)
label = font_helvetica16.render(self.label, False, text_color)
self.screen.blit(label, (self.position[0] + (self.size[0] - label.get_width())/2,
self.position[1] + (self.size[1] - label.get_height())/2))

65
circle_button.py Normal file
View File

@ -0,0 +1,65 @@
import pygame
from .colors import *
import math
class CircleButton:
def __init__(self, gui, position, radius):
self.surface = gui.screen
self.position = position
self.radius = radius
self.color = color_primary
def check_click(self, pos):
if math.sqrt(math.pow(pos[0] - self.position[0], 2) +
math.pow(pos[1] - self.position[1], 2)) < self.radius:
self.on_click()
def on_click(self):
pass
def draw(self):
pass
def display(self):
pygame.draw.circle(self.surface, self.color, self.position, self.radius)
self.draw()
class BeatButton(CircleButton):
def __init__(self, gui, position, radius):
self.playing = False
super().__init__(gui, position, radius)
def on_click(self):
self.playing = not self.playing
def draw(self):
x, y = self.position
pygame.draw.polygon(self.surface, "black", [
(x-self.radius/2.5, y-self.radius/1.5),
(x+self.radius/1.5, y),
(x-self.radius/2.5, y+self.radius/1.5)
], 2 if self.playing else 0)
class LoopButton(CircleButton):
def __init__(self, gui, position, radius):
self.state = False
self.counter = 0
self.frame_threshold = 30
self.recording = False
super().__init__(gui, position, radius)
def on_click(self):
self.recording = not self.recording
self.state = not self.state
def draw(self):
pygame.draw.circle(self.surface, "black", self.position, self.radius/1.5)
pygame.draw.circle(self.surface, self.color, self.position, self.radius/2,
2 if not self.state else 0)
if self.recording:
if self.counter == self.frame_threshold:
self.state = not self.state
self.counter = 0
self.counter += 1

34
exit Normal file
View File

@ -0,0 +1,34 @@
usage: git diff [<options>] [<commit>] [--] [<path>...]
or: git diff [<options>] --cached [--merge-base] [<commit>] [--] [<path>...]
or: git diff [<options>] [--merge-base] <commit> [<commit>...] <commit> [--] [<path>...]
or: git diff [<options>] <commit>...<commit> [--] [<path>...]
or: git diff [<options>] <blob> <blob>
or: git diff [<options>] --no-index [--] <path> <path>
common diff options:
-z output diff-raw with lines terminated with NUL.
-p output patch format.
-u synonym for -p.
--patch-with-raw
output both a patch and the diff-raw format.
--stat show diffstat instead of patch.
--numstat show numeric diffstat instead of patch.
--patch-with-stat
output a patch and prepend its diffstat.
--name-only show only names of changed files.
--name-status show names and status of changed files.
--full-index show full object name on index lines.
--abbrev=<n> abbreviate object names in diff-tree header and diff-raw.
-R swap input file pairs.
-B detect complete rewrites.
-M detect renames.
-C detect copies.
--find-copies-harder
try unchanged files as candidate for copy detection.
-l<n> limit rename attempts up to <n> paths.
-O<file> reorder diffs according to the <file>.
-S<string> find filepair whose only one side contains the string.
--pickaxe-all
show all files diff when -S is used and hit is found.
-a --text treat all files as text.

View File

@ -2,5 +2,5 @@ import pygame
pygame.font.init() pygame.font.init()
font_helvetica = pygame.font.SysFont('Helvetica', 30) font_helvetica = pygame.font.SysFont('Helvetica', 45)
font_helvetica16 = pygame.font.SysFont('Helvetica', 15) font_helvetica16 = pygame.font.SysFont('Helvetica', 22)

52
item_selection.py Normal file
View File

@ -0,0 +1,52 @@
from .fonts import *
from .colors import *
import pygame
import math
class ItemSelection:
def __init__(self, max_item_width, max_display_count = 9):
self.index = 0
self.items = []
self.surface = pygame.Surface((0,0), pygame.SRCALPHA)
self.max_item_width = max_item_width
self.max_display_count = max_display_count
self.selected_item_srf = None
def set_items(self, items):
self.items = items
self.surface = pygame.Surface(
(self.max_item_width+50, self.get_display_count()*40), pygame.SRCALPHA)
def get_display_count(self):
return min(self.max_display_count, len(self.items))
def get_count(self):
return len(self.items)
def get_selected_item(self):
return self.items[self.index]
def update(self):
self.surface.fill("black")
for i in range(self.get_display_count()):
text = self.items[(i+self.index-math.floor(self.get_display_count()/2))%len(self.items)]
if len(text)> math.floor(self.max_item_width/10):
text = text[0:math.floor(self.max_item_width/10)-2]
text += '...'
#text = self.items[i]
if i == math.floor(self.get_display_count()/2):
#if i == self.index:
text_surface = font_helvetica16.render(text, False, color_primary_light)
self.selected_item_srf = text_surface
else:
text_surface = font_helvetica16.render(text, False, color_primary)
opacity = -15 + math.floor((i+1) * 2 * 255 / self.max_display_count
if i < self.max_display_count/2
else (self.max_display_count-i) * 2 * 255 / self.max_display_count)
text_surface.set_alpha(opacity)
#if text_surface.get_width() > self.max_item_width: self.max_item_width = text_surface.get_width()
self.surface.blit(text_surface, ((10, self.surface.get_height()/self.get_display_count()*i)))
pygame.draw.rect(self.surface, color_primary_light,
(0, self.surface.get_height()/2-25, self.max_item_width+20, 40), 2)

36
knob.py
View File

@ -1,21 +1,26 @@
import pygame import pygame
import math import math
from colors import * from .colors import *
FADE_COUNT_DOWN = 150
class Knob: class Knob:
def __init__(self, gui, name, radius, position): def __init__(self, group, name, radius, position, value = 0):
self.gui = gui self.group = group
self.name = name self.name = name
self.screen = gui.screen
self.color = color_primary self.color = color_primary
self.radius = radius self.radius = radius
self.position = position self.position = position
self.value = 0 self.value = value
self.focused = False self.focused = False
self.count_down = FADE_COUNT_DOWN
self.vanish_label_path = False
self.label_path = []
def set_value(self, value): def set_value(self, value):
self.value = value self.value = value
self.count_down = FADE_COUNT_DOWN
def set_color(self, color): def set_color(self, color):
self.color = color self.color = color
@ -24,16 +29,21 @@ class Knob:
if focused: if focused:
self.focused = True self.focused = True
self.color = color_primary_light self.color = color_primary_light
self.gui.deactivate_knobs_except(self)
else: else:
self.focused = False self.focused = False
self.color = color_primary self.color = color_primary
self.count_down = FADE_COUNT_DOWN
if not self.vanish_label_path: self.label_path = []
def get_position(self): def get_position(self):
return self.position return self.position
def get_index(self, knobs):
return knobs.index(self)
def __eq__(self, other): def __eq__(self, other):
return self.position == other.get_position() if other is None: return False
return self.name == other.name
def get_pointer_position(self): def get_pointer_position(self):
angle = (self.value * 0.8 * 2 + 0.7) * math.pi angle = (self.value * 0.8 * 2 + 0.7) * math.pi
@ -41,13 +51,17 @@ class Knob:
y = (self.radius - 5) * math.sin(angle) + self.position[1] y = (self.radius - 5) * math.sin(angle) + self.position[1]
return(x, y) return(x, y)
def display(self): def display(self, surface):
m1, m2, m3 = pygame.mouse.get_pressed(3) m1, m2, m3 = pygame.mouse.get_pressed(3)
if m1: if m1:
pos = pygame.mouse.get_pos() pos = pygame.mouse.get_pos()
if math.sqrt(math.pow(pos[0]-self.position[0],2) + math.pow(pos[1]-self.position[1],2)) < self.radius: if math.sqrt(math.pow(pos[0]-self.position[0],2) + math.pow(pos[1]-self.position[1],2)) < self.radius:
self.set_focused(True) self.group.set_focused(self)
if self.focused: self.count_down -= 1
pygame.draw.circle(self.screen, self.color, self.position, self.radius) if self.count_down == 0:
pygame.draw.line(self.screen, "black", self.position, self.get_pointer_position(), 4) self.vanish_label_path = True
self.set_focused(False)
pygame.draw.circle(surface, self.color + (self.group.opacity,), self.position, self.radius)
pygame.draw.line(surface, "black", self.position, self.get_pointer_position(), 4)

160
knobs.py Normal file
View File

@ -0,0 +1,160 @@
import pygame
from .colors import *
from .fonts import *
from .knob import *
import random
class Knobs:
def __init__(self, gui, knob_names, is_left_side=True):
self.gui = gui
self.knobs_surface = pygame.Surface((gui.screenW, gui.screenH), pygame.SRCALPHA)
self.opacity = 0
self.fade_speed = 5
self.fade_in = False
self.fade_out = False
self.knob_names = knob_names
self.is_left_side = is_left_side
# Create and position the knobs
self.knobs_radius = self.gui.screenW / 25
knobs_spacing = self.knobs_radius * 2
knobs_x = (self.gui.screenW - (self.knobs_radius + knobs_spacing) * 2) / 2
x = knobs_x
knobs_y = (self.gui.screenH - (self.knobs_radius + knobs_spacing) * 3) / 2
y = knobs_y
self.knobs = []
for i in range(len(knob_names)):
self.knobs.append(Knob(self, knob_names[i], self.knobs_radius, (x, y), 0))
y += self.knobs_radius + knobs_spacing
if i == 3 or (self.is_left_side and i == 6) or (not self.is_left_side and i == 0):
x += self.knobs_radius + knobs_spacing
y = knobs_y
self.label_vanish_parameter = 0
self.label_border_height = 0
def display_label(self, knob):
if not knob.vanish_label_path: self.label_vanish_parameter = 0
if len(knob.label_path) == 2:
if self.label_vanish_parameter > 0:
self.label_vanish_parameter -= 1
elif knob.vanish_label_path: self.label_vanish_parameter = 5
label_fx_name = font_helvetica.render(knob.name, False, color_primary_light)
label_fx_value = font_helvetica.render('[ ' + str(int(round(knob.value * 100, 0))) + '% ]',
False, color_primary_light)
min_x = self.knobs[5].get_position()[0] + self.knobs_radius * 1.5
label_x = min_x + (self.gui.screenW - min_x - label_fx_name.get_width()) / 2
label_y = (self.gui.screenH - label_fx_name.get_height() - label_fx_value.get_height()) / 2
if self.label_vanish_parameter == 0:
self.gui.label_surface.blit(label_fx_name, (label_x, label_y))
self.gui.label_surface.blit(label_fx_value, (label_x, self.gui.screenH / 2))
if knob.vanish_label_path:
if self.label_border_height < self.gui.screenH / 2:
self.label_border_height += 2
else: self.label_border_height = self.gui.screenH / 2
else:
self.label_border_height = label_y
pygame.draw.line(
self.gui.label_surface,
color_primary_dark,
(label_x - 10, self.label_border_height),
(label_x - 10, self.gui.screenH - self.label_border_height),
2
)
self.calculate_label_path(knob, label_x)
for i in range(len(knob.label_path)-1):
pygame.draw.line(
self.gui.label_surface,
color_primary_dark,
knob.label_path[i],
knob.label_path[i+1],
2
)
def calculate_label_path(self, knob, label_x):
if len(knob.label_path) == 0:
knob.label_path.append(knob.get_position())
knob_index = knob.get_index(self.knobs)
if self.is_left_side:
if knob_index in [0, 1, 4, 5] :
x = knob.get_position()[0] + self.knobs_radius * 1.5
y = knob.get_position()[1] + self.knobs_radius * 1.5
elif knob_index in [2, 3, 6]:
x = knob.get_position()[0] + self.knobs_radius * 1.5
y = knob.get_position()[1] - self.knobs_radius * 1.5
else: # knob 7
x = knob.get_position()[0] - self.knobs_radius * 1.5
y = knob.get_position()[1] + self.knobs_radius * 1.5
knob.label_path.append((x, y))
if (knob == self.knobs[0] or knob == self.knobs[3]):
knob.label_path.append((knob.get_position()[0] + self.knobs_radius * 1.5, self.gui.screenH/2))
elif (knob == self.knobs[4] or knob == self.knobs[7]):
knob.label_path.append((self.knobs[4].get_position()[0] + self.knobs_radius * 1.5, self.gui.screenH/2))
else:
if knob_index in [1, 2, 4, 5]:
x = knob.get_position()[0] + self.knobs_radius * 1.5
y = knob.get_position()[1] + self.knobs_radius * 1.5
elif knob_index in [3, 6, 7]:
x = knob.get_position()[0] + self.knobs_radius * 1.5
y = knob.get_position()[1] - self.knobs_radius * 1.5
else: # knob 0
x = knob.get_position()[0] + self.knobs_radius * 1.5
y = knob.get_position()[1] + self.knobs_radius * 1.5
knob.label_path.append((x, y))
if (knob == self.knobs[0] or knob == self.knobs[1]):
knob.label_path.append((knob.get_position()[0] + self.knobs_radius * 1.5, self.gui.screenH/2))
elif (knob == self.knobs[4] or knob == self.knobs[7]):
knob.label_path.append((knob.get_position()[0] + self.knobs_radius * 1.5, self.gui.screenH/2))
knob.label_path.append((label_x - 10, self.gui.screenH/2))
elif knob.vanish_label_path:
if len(knob.label_path) > 1:
p1 = list(knob.label_path[0])
p2 = list(knob.label_path[1])
if abs(p1[0] - p2[0]) <= 10 and abs(p1[1] - p2[1]) <= 10:
knob.label_path.remove(tuple(p1))
else:
if p1[0] > p2[0]:
p1[0] -= 10
elif p1[0] < p2[0]:
p1[0] += 10
if p1[1] > p2[1]:
p1[1] -= 10
elif p1[1] < p2[1]:
p1[1] += 10
knob.label_path[0] = tuple(p1)
else:
knob.vanish_label_path = False
knob.label_path = []
def set_focused(self, knob):
for k in self.knobs:
k.set_focused(k == knob)
if knob is None:
k.vanish_label_path = False
def set_value(self, index, value):
self.set_focused(self.knobs[index])
self.knobs[index].set_value(value)
def display(self):
if self.gui.show_knobs:
if self.fade_in and self.opacity < 255: self.opacity += self.fade_speed
if self.fade_out and self.opacity > 0: self.opacity -= self.fade_speed
self.knobs_surface = pygame.Surface((self.gui.screenW, self.gui.screenH), pygame.SRCALPHA)
for knob in self.knobs:
if knob.focused or knob.vanish_label_path: self.display_label(knob)
knob.display(self.knobs_surface)
self.gui.screen.blit(self.knobs_surface, (0,0))

41
lozenge_button.py Normal file
View File

@ -0,0 +1,41 @@
import pygame
import math
from .colors import *
from .fonts import *
class LozengeButton:
def __init__(self, gui, name, position, radius_x, radius_y, side):
self.gui = gui
self.screen = gui.screen
self.name = name
self.x = position[0]
self.y = position[1]
self.radius_x = radius_x
self.radius_y = radius_y
self.side = side
self.focused = False
def display(self):
m1, m2, m3 = pygame.mouse.get_pressed(3)
if m1:
pos = pygame.mouse.get_pos()
if math.sqrt(math.pow(pos[0] - self.x, 2) + math.pow(pos[1] - self.y, 2)) < self.radius_y:
self.focused = True
self.gui.set_fx_mode_by_name(self.side, self.name)
self.gui.set_controller_fx_mode(self.side, self.name)
if self.focused:
w = 0
fx_mode_label = font_helvetica16.render(self.name, False, color_primary_light)
self.screen.blit(fx_mode_label, (self.x + (20 if self.side == 0 else -20-fx_mode_label.get_width()), self.y - 8))
else:
w = 2
fx_mode_label = font_helvetica16.render(str(self.name)[0:1], False, color_primary_light)
self.screen.blit(fx_mode_label, (self.x - fx_mode_label.get_width()/2 + 1,
self.y - fx_mode_label.get_height()/2 + 1))
pygame.draw.polygon(self.screen, color_primary_light,
[(self.x - self.radius_x, self.y),
(self.x, self.y - self.radius_y),
(self.x + self.radius_x, self.y),
(self.x, self.y + self.radius_y)], w)

366
main.py Normal file → Executable file
View File

@ -1,26 +1,42 @@
import pygame import pygame
from knob import * from .colors import *
from colors import * from .fonts import *
from fonts import * from .knobs import Knobs
from .lozenge_button import LozengeButton
from .binary_button import BinaryButton
from .circle_button import *
from .athmos import Athmos
from .beatplayer import BeatPlayer
class GuiMain: class GuiMain:
def __init__(self): def __init__(self, controller):
self.controller = controller
# pygame setup # pygame setup
pygame.init() pygame.init()
self.screenW = 561 pygame.display.set_caption("SantoscopeUI")
self.screenH = 325 self.screen = pygame.display.set_mode((800, 480))
self.screen = pygame.display.set_mode((self.screenW, self.screenH)) #self.screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN)
self.screenH = self.screen.get_height()
self.screenW = self.screen.get_width()
self.fx_modes = [ self.label_surface = pygame.Surface((self.screenW, self.screenH), pygame.SRCALPHA)
'Strings',
'Beat', self.fx_mode_labels = [
'L-Loop', [
'R-Loop', 'Strings',
'Jack', 'L-Loop',
'Athmo' 'R-Loop',
'Jack',
'Athmo'
],
[
'Mixer',
'Beat'
]
] ]
knob_names = [
left_knob_names = [
'Delay Time', 'Delay Time',
'Delay Vol', 'Delay Vol',
'Reverb Wet', 'Reverb Wet',
@ -31,111 +47,259 @@ class GuiMain:
'Gain' 'Gain'
] ]
# Create and position the knobs mixer_knob_names = [
self.knobs_radius = self.screenW / 25 'Jack Vol',
knobs_spacing = self.knobs_radius * 2 'Athmo Vol',
knobs_x = (self.screenW - (self.knobs_radius + knobs_spacing) * 2) / 2 'Beat speed',
x = knobs_x 'L-loop Vol',
knobs_y = (self.screenH - (self.knobs_radius + knobs_spacing) * 3) / 2 '-unassigned-',
y = knobs_y 'Beat Vol',
self.knobs = [] 'R-loop Vol',
for i in range(8): 'Master Vol'
self.knobs.append(Knob(self, knob_names[i], self.knobs_radius, (x, y))) ]
y += self.knobs_radius + knobs_spacing
if i == 3 or i == 6: # Create and position the fx_mode buttons
x += self.knobs_radius + knobs_spacing self.fx_mode_buttons = [[],[]]
y = knobs_y
self.fx_mode = [0, 0]
for i in range(len(self.fx_mode_labels[0])):
x = self.screenW * 2.5 / 19
y = i * self.screenH / 7.5 + 45
r_x = self.screenW * 0.35 / 19
r_y = (self.screenH-20) / 16
self.fx_mode_buttons[0].append(
LozengeButton(self, self.fx_mode_labels[0][i], (x,y), r_x, r_y, 0)
)
for i in range(len(self.fx_mode_labels[1])):
x = self.screenW - 30
y = i * self.screenH / 7.5 + 90
r_x = self.screenW * 0.35 / 19
r_y = (self.screenH-20) / 16
self.fx_mode_buttons[1].append(
LozengeButton(self, self.fx_mode_labels[1][i], (x,y), r_x, r_y, 1)
)
self.knobs = [[], []]
for i in range(len(self.fx_mode_labels[0])):
self.knobs[0].append(Knobs(self, left_knob_names))
for i in range(len(self.fx_mode_labels[1])):
self.knobs[1].append(Knobs(self, left_knob_names if i==1 else mixer_knob_names, is_left_side=False))
self.set_fx_mode(0, self.fx_mode[0])
self.set_fx_mode(1, self.fx_mode[1])
self.show_knobs = True
self.knobs_display_side = 0
self.mute_button = BinaryButton(
self.screen,
(20, self.fx_mode_buttons[0][0].y-20),
(40, 40), 'M',
False)
self.beat_button = BeatButton(self, (40, self.fx_mode_buttons[0][1].y), 20)
self.rloop_button = LoopButton(self, (40, self.fx_mode_buttons[0][2].y), 20)
self.lloop_button = LoopButton(self, (40, self.fx_mode_buttons[0][3].y), 20)
self.rec_button = BinaryButton(
self.screen,
(self.screenW-70, 15),
(60, 30),
"REC",
False
)
self.loop_input_mode = None
self.set_loop_input_mode(0)
self.beatplayer = BeatPlayer(self.screen)
self.athmos = Athmos(self.screen)
self.athmos.show_list = False
fnames = [
"01_7Qp3xL8gK2tA5mR9eZ1vC4bN6sH0jF",
"02_yP5cT8kL3wR0vX6mN9bQ2fZ4hJ7dS1",
"03_3aB7eF2gH9jK5lM1nP6qR0sT8uV4B7eF2gH9jK5lM1nP6qR0sT8uV4wX",
"04_6mV0cX8zL5jH7gF2dS9aP1oI3uY4tR",
"05_2pQ8oR3iK6jL1fH7gK9wL0q3xZ4jL1fH7gK9wL0q3xZ4rE5",
"06_9eZ1vC4bN6sH0jF7Qp3xL8gK2tA5mR",
"07_0vX6mN9bQ2fZ4hJ7dS1yP5cT8kL3wR",
"08_1nP6qR0sT8uV4wX3aB7eF2gH9jK5lM",
"09_5jH7gF2dS9aP1oI3uY4tR6mV0cX8zL",
"10_6jL1fH7gK9wL0q3xZ4rE5pQ8oR3iK2",
"11_0jF7Qp3xL8gK2tA5mR9eZ1vC4bN6sH",
"12_4hJ7dS1yP5cT8kL3wR0vX6mN9bQ2fZ",
"13_9jK5lM1nP6qR0sT8uV4wX3aB7eF2gH"
]
#self.beatplayer.set_clip_names(fnames[0:5])
#self.beatplayer.set_track_names(fnames[0:9])
#self.set_athmo_filenames(fnames)
self.selected_athmo_srf = None
self.clock = pygame.time.Clock() self.clock = pygame.time.Clock()
self.running = True self.running = True
self.run()
def display_label(self, knob): def set_knob_value(self, side, index, value):
fx_label = font_helvetica.render(knob.name, False, color_primary_light) self.knobs_display_side = side
fx_value = font_helvetica.render('[ ' + str(knob.value) + '% ]', self.knobs[side][self.fx_mode[side]].set_value(index, value)
False, color_primary_light)
label_x = self.screenW - fx_label.get_width() - 20 def set_fx_mode(self, side, i):
label_y = (self.screenH - fx_label.get_height() - fx_value.get_height()) / 2 self.set_fx_mode_by_name(side, self.fx_mode_labels[side][i])
self.screen.blit(fx_label, (label_x, label_y))
self.screen.blit(fx_value, (label_x, self.screenH / 2)) def set_fx_mode_by_name(self, side, mode):
pygame.draw.line(self.screen, color_primary_dark, (label_x - 10, label_y), (label_x - 10, self.screenH - label_y), 2) self.knobs_display_side = side
self.knobs[side][self.fx_mode[side]].opacity = 0 #checke
self.fx_mode[side] = self.fx_mode_labels[side].index(mode)
self.knobs[side][self.fx_mode[side]].set_focused(None)
self.knobs[side][self.fx_mode[side]].fade_in = True
for b in self.fx_mode_buttons[side]:
b.focused = (b.name == mode)
def set_controller_fx_mode(self, side, mode):
i = self.fx_mode_labels[side].index(mode)
self.controller.set_fx_mode(side, i)
def show_athmos(self, show=True):
self.athmos.show_list = show
self.show_knobs = not show
self.beatplayer.show_selection = False
"""
if show == False:
self.set_selected_athmo()
"""
def show_beatplayer(self, show=True):
self.beatplayer.show_selection = show
self.show_knobs = not show
self.athmos.show_list = False
def set_athmo_index(self, index):
self.athmos.index = index
line_points = [] def set_beat_clip_index(self, index):
line_points.append(knob.get_position()) self.beatplayer.clip_selection.index = index
def set_beat_track_index(self, index):
self.beatplayer.track_selection.index = index
if (knob == self.knobs[0] or knob == self.knobs[1] or def set_selected_athmo(self, duration):
knob == self.knobs[4] or knob == self.knobs[5]): """
x = knob.get_position()[0] + self.knobs_radius * 1.5 name = self.athmos.get_name_by_index(self.athmos.index)
y = knob.get_position()[1] + self.knobs_radius * 1.5 if len(name) > 20: name = name[0:22] + '...'
line_points.append((x, y)) self.selected_athmo_srf = font_helvetica16.render(name, False, color_primary)
elif (knob == self.knobs[2] or knob == self.knobs[3] or knob == self.knobs[6]): """
x = knob.get_position()[0] + self.knobs_radius * 1.5 self.selected_athmo_srf = self.athmos.selected_item_srf
y = knob.get_position()[1] - self.knobs_radius * 1.5 self.athmos.current_duration = duration * 1000 if duration is not None else 0
line_points.append((x, y)) self.athmos.time_started = pygame.time.get_ticks()
elif (knob == self.knobs[7]):
x = knob.get_position()[0] - self.knobs_radius * 1.5
y = knob.get_position()[1] + self.knobs_radius * 1.5
line_points.append((x, y))
if (knob == self.knobs[0] or knob == self.knobs[3]): def set_athmo_filenames(self, filenames):
line_points.append((self.knobs[0].get_position()[0] + self.knobs_radius * 1.5, self.screenH/2)) self.athmos.set_filenames(filenames)
elif (knob == self.knobs[4] or knob == self.knobs[6] or knob == self.knobs[7]):
line_points.append((self.knobs[4].get_position()[0] + self.knobs_radius * 1.5, self.screenH/2))
line_points.append((label_x - 10, self.screenH/2)) def set_beat_track_names(self, names):
self.beatplayer.set_track_names(names)
for i in range(len(line_points)-1): def set_beat_clip_names(self, names):
pygame.draw.line(self.screen, color_primary_dark, line_points[i], line_points[i+1], 2) self.beatplayer.set_clip_names(names)
def draw_lozi(self, x, y, color, outlined = False): def set_loop_input_mode(self, mode):
rX = self.screenW * 0.35 / 19 str_mode = 'SJ'
rY = (self.screenH-20) / 16 if mode == 0:
w = 2 if outlined else 0 str_mode = 'S'
pygame.draw.polygon(self.screen, color, [(x-rX, y), (x, y-rY), (x+rX, y), (x, y+rY)], w) elif mode == 1:
str_mode = 'J'
def deactivate_knobs_except(self, knob): self.loop_input_mode = font_helvetica16.render(str_mode, False, color_primary)
for k in self.knobs:
if k != knob: k.set_focused(False)
def run(self):
while self.running:
# poll for events
# pygame.QUIT event means the user clicked X to close your window
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
def update(self):
if self.running:
# fill the screen with a color to wipe away anything from last frame # fill the screen with a color to wipe away anything from last frame
self.screen.fill("black") self.screen.fill("black")
# RENDER YOUR GAME HERE self.screen.blit(self.label_surface, (0,0))
self.label_surface = pygame.Surface((self.screenW, self.screenH), pygame.SRCALPHA)
for knob in self.knobs: self.beatplayer.update()
if knob.focused: self.display_label(knob) self.athmos.update()
knob.display()
pygame.draw.rect(self.screen, color_primary_dark, ((10, 10), (self.screenW * 1.5 / 19, self.screenH - 18))) self.knobs[self.knobs_display_side][self.fx_mode[self.knobs_display_side]].display()
fx_mode = 1
for i in range(6):
x = self.screenW * 2.5 / 19
y = i * self.screenH / 7.3 + 35
if i == fx_mode:
self.draw_lozi(x, y, color_primary)
fx_mode_label = font_helvetica16.render(self.fx_modes[i], False, color_primary)
self.screen.blit(fx_mode_label, (x+20, y-8))
else:
self.draw_lozi(x, y, color_primary, True)
fx_mode_label = font_helvetica16.render(str(self.fx_modes[i])[0:1], False, color_primary)
self.screen.blit(fx_mode_label, (x-4, y-7))
pygame.draw.polygon(self.screen, "black", [(0, self.screenH/2), (self.screenW*3 / 19, self.screenH), (0, self.screenH)], 20) for i in range(2):
for button in self.fx_mode_buttons[i]:
button.display()
self.mute_button.display()
for i in range(1,4):
pygame.draw.line(self.screen, color_primary_dark,
(self.fx_mode_buttons[0][i].radius_x*0.5 + 60, self.fx_mode_buttons[0][i].y),
(self.fx_mode_buttons[0][i].x - self.fx_mode_buttons[0][i].radius_x * 1.5,
self.fx_mode_buttons[0][i].y))
self.beat_button.display()
self.lloop_button.display()
self.rloop_button.display()
self.rec_button.display()
if self.selected_athmo_srf is not None:
self.screen.blit(self.selected_athmo_srf,
(self.screenW*3 / 18,
self.screenH - self.selected_athmo_srf.get_height()-10))
if pygame.time.get_ticks() - self.athmos.time_started >= self.athmos.current_duration:
self.selected_athmo_srf = None
if self.loop_input_mode is not None:
self.screen.blit(self.loop_input_mode,
((self.fx_mode_buttons[0][3].x+self.lloop_button.position[0])/2-self.loop_input_mode.get_width()/2,
(self.lloop_button.position[1]+self.rloop_button.position[1])/2-self.loop_input_mode.get_height()/2))
pygame.draw.polygon(self.screen, "white", [(0, self.screenH/2), (self.screenW*3 / 19, self.screenH), (0, self.screenH)]) pygame.draw.polygon(self.screen, "white", [(0, self.screenH/2), (self.screenW*3 / 19, self.screenH), (0, self.screenH)])
# flip() the display to put your work on screen # flip() the display to put your work on screen
pygame.display.flip() pygame.display.flip()
self.clock.tick(60) # limits FPS to 60 self.clock.tick(30) # limits FPS to 60
pygame.quit() for event in pygame.event.get():
# exit when esc is pressed
GuiMain() if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
self.running = False
if event.key == pygame.K_RETURN:
if not self.beatplayer.show_selection:
self.show_beatplayer()
else:
self.show_beatplayer(False)
if event.key == pygame.K_INSERT:
if not self.athmos.show_list:
self.show_athmos()
else:
self.show_athmos(False)
if event.key == pygame.K_UP:
if self.athmos.show_list:
self.athmos.index -= 1
self.athmos.index %= self.athmos.get_count()
else:
self.beatplayer.clip_selection.index -= 1
self.beatplayer.clip_selection.index %= self.beatplayer.clip_selection.get_count()
if event.key == pygame.K_DOWN:
if self.athmos.show_list:
self.athmos.index += 1
self.athmos.index %= self.athmos.get_count()
else:
self.beatplayer.clip_selection.index += 1
self.beatplayer.clip_selection.index %= self.beatplayer.clip_selection.get_count()
if event.key == pygame.K_RIGHT:
self.beatplayer.track_selection.index += 1
self.beatplayer.track_selection.index %= self.beatplayer.track_selection.get_count()
if event.key == pygame.K_LEFT:
self.beatplayer.track_selection.index -= 1
self.beatplayer.track_selection.index %= self.beatplayer.track_selection.get_count()
elif event.type == pygame.QUIT:
self.running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
self.mute_button.check_click(pygame.mouse.get_pos())
self.rec_button.check_click(pygame.mouse.get_pos())
self.beat_button.check_click(pygame.mouse.get_pos())
self.lloop_button.check_click(pygame.mouse.get_pos())
self.rloop_button.check_click(pygame.mouse.get_pos())
else:
pygame.quit()