Skip to content

Instantly share code, notes, and snippets.

@jhw
Last active February 7, 2025 12:32
Show Gist options
  • Save jhw/97565885272092ae62a28304b02a65b2 to your computer and use it in GitHub Desktop.
Save jhw/97565885272092ae62a28304b02a65b2 to your computer and use it in GitHub Desktop.
Critter and Guitari Eyesy local emulator
*.pyc
__pycache__
env
"""
https://github.com/critterandguitari/EYESY_OS/blob/v2.3/engines/python/etc_system.py
"""
import math
import random
class MinimalV3System:
xres = 1280
yres = 720
# audio
audio_in = [0] * 100
# knobs a used by mode
knob1 = 0.2
knob2 = 0.2
knob3 = 0.2
knob4 = 0.2
knob5 = 0.2
# system stuff
bg_color = (0, 0, 0)
def __init__(self):
pass
def color_picker( self, val ):
# convert knob to 0-1
c = float(val)
# all the way down random bw
rando = random.randrange(0, 2)
color = (rando * 255, rando * 255, rando * 255)
# random greys
if c > .02 :
rando = random.randrange(0,255)
color = (rando, rando, rando)
# grey 1
if c > .04 :
color = (50, 50, 50)
# grey 2
if c > .06 :
color = (100, 100 ,100)
# grey 3
if c > .08 :
color = (150, 150 ,150)
# grey 4
if c > .10 :
color = (150, 150 ,150)
# grey 5
if c > .12 :
color = (200, 200 ,200)
# white
if c > .14 :
color = (250, 250 ,250)
#colors
if c > .16 :
#r = float(control) / 1024 * 255
#g = float((control * 2) % 1024) / 1024 * 255
#b = float((control * 4) % 1024) / 1024 * 255
r = math.sin(c * 2 * math.pi) * .5 + .5
g = math.sin(c * 4 * math.pi) * .5 + .5
b = math.sin(c * 8 * math.pi) * .5 + .5
color = (r * 255,g * 255,b * 255)
# full ranoms
if c > .96 :
color = (random.randrange(0,255), random.randrange(0,255), random.randrange(0,255))
# primary randoms
if c > .98 :
r = random.randrange(0, 2) * 255
g = random.randrange(0, 2) * 255
b = random.randrange(0, 2) * 255
color = (r,g,b)
color2 = (color[0], color[1], color[2])
return color2
def color_picker_bg( self, val):
c = float(val)
r = (1 - (math.cos(c * 3 * math.pi) * .5 + .5)) * c
g = (1 - (math.cos(c * 7 * math.pi) * .5 + .5)) * c
b = (1 - (math.cos(c * 11 * math.pi) * .5 + .5)) * c
color = (r * 255,g * 255,b * 255)
self.bg_color = color
return color
if __name__ == "__main__":
pass
from etc_system_v3 import MinimalV3System as Etc
import argparse
import importlib
import logging
import os
import pygame
import sys
import traceback
import yaml
logging.basicConfig(stream = sys.stdout,
level = logging.INFO,
format = "%(levelname)s: %(message)s")
def init_handler(inc):
def handler(event, etc):
if event.type == pygame.QUIT:
logging.warning("terminating")
raise RuntimeError("quit")
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
etc.knob1 = min(1, max(0, etc.knob1 + inc))
elif event.key == pygame.K_z:
etc.knob1 = min(1, max(0, etc.knob1 - inc))
elif event.key == pygame.K_s:
etc.knob2 = min(1, max(0, etc.knob2 + inc))
elif event.key == pygame.K_x:
etc.knob2 = min(1, max(0, etc.knob2 - inc))
elif event.key == pygame.K_d:
etc.knob3 = min(1, max(0, etc.knob3 + inc))
elif event.key == pygame.K_c:
etc.knob3 = min(1, max(0, etc.knob3 - inc))
elif event.key == pygame.K_f:
etc.knob4 = min(1, max(0, etc.knob4 + inc))
elif event.key == pygame.K_v:
etc.knob4 = min(1, max(0, etc.knob4 - inc))
elif event.key == pygame.K_g:
etc.knob5 = min(1, max(0, etc.knob5 + inc))
elif event.key == pygame.K_b:
etc.knob5 = min(1, max(0, etc.knob5 - inc))
return handler
def run_emulator(screen, mode, etc, handler):
mode.setup(screen = screen,
etc = etc)
while True:
for event in pygame.event.get():
handler(event = event,
etc = etc)
mode.draw(screen = screen,
etc = etc)
pygame.display.flip()
def parse_args(config):
parser = argparse.ArgumentParser(description="SV client")
for item in config:
kwargs = {"type": eval(item["type"])}
if "default" in item:
kwargs["default"] = item["default"]
parser.add_argument(f"--{item['name']}", **kwargs)
args, errors = parser.parse_args(), []
for item in config:
if getattr(args, item['name']) == None:
errors.append(f"please supply {item['name']}")
else:
value = getattr(args, item["name"])
if ("options" in item and
value not in item["options"]):
errors.append(f"{value} is not a valid options for {item['name']}")
elif ("min" in item and
value < item["min"]):
errors.append(f"{item['name']} is below min value")
elif ("max" in item and
value > item["max"]):
errors.append(f"{item['name']} exceeds max value")
if errors != []:
raise RuntimeError(", ".join(errors))
return args
ArgsConfig = yaml.safe_load("""
- name: mode_file
type: str
default: "iz_hello.py"
- name: knob_inc
type: float
min: 0.01
max: 0.2
default: 0.05
""")
if __name__ == "__main__":
try:
args = parse_args(ArgsConfig)
if not os.path.exists(args.mode_file):
raise RuntimeError(f"mode file {args.mode_file} does not exist")
mode_name = ".".join(args.mode_file.split(".")[0].split("/"))
mode = importlib.import_module(mode_name)
pygame.init()
pygame.display.set_caption("Eyesy Emulator")
screen = pygame.display.set_mode((800, 600))
logging.info(f"Rendering mode {mode_name}")
logging.info(f"Click the red UI icon to quit :)")
handler = init_handler(inc = args.knob_inc)
run_emulator(screen = screen,
mode = mode,
etc = Etc(),
handler = handler)
except RuntimeError as error:
if "quit" not in str(error):
logging.error(error)
pygame.quit()
except Exception as error:
logging.warning(traceback.format_exc())
pygame.quit()
def setup(screen, etc):
screen.fill(etc.bg_color)
def draw(screen, etc):
bg_color = etc.color_picker_bg(etc.knob1)
screen.fill(bg_color)
import os
import pygame
import math
class LFO : #uses three arguments: start point, max, and how far each step is.
def __init__(self, start, max, step):
self.start = start
self.max = max
self.step = step
self.current = 0
self.direction = 1
def update(self):
# when it gets to the top, flip direction
if (self.current >= self.max) :
self.direction = -1
self.current = self.max # in case it steps above max
# when it gets to the bottom, flip direction
if (self.current <= self.start) :
self.direction = 1
self.current = self.start # in case it steps below min
self.current += self.step * self.direction
return self.current
def setup(screen, etc):
global b1,b2,b3,b4,y,xr,yr,x100
xr = etc.xres
yr = etc.yres
x100 = (100*xr)/1280
b1 = LFO(x100,(xr-x100),10)
b2 = LFO(x100,(xr-x100),19)
b3 = LFO(0,(yr/2),2)
b4 = LFO((yr/2),yr,2)
y = 0
pass
def draw(screen, etc):
global b1,b2,b3,b4,y,xr,yr,x100
etc.color_picker_bg(etc.knob5)
y = etc.audio_in[50] / 150
#create many so the random colors are different....
color = etc.color_picker(etc.knob4)
color2 = etc.color_picker(etc.knob4)
color3 = etc.color_picker(etc.knob4)
color4 = etc.color_picker(etc.knob4)
size1 = int(etc.knob1 * x100) +1
size2 = int(etc.knob2 * (x100/2)) +1
b1.step = int(etc.knob3 * ((15*xr)/1280))+5
b2.step = int(etc.knob3 * ((30*xr)/1280))+5
b3.step = int(etc.knob3 * (x100/20))+2
b4.step = int(etc.knob3 * (x100/20))+2
posx1 = b1.update()
posx2 = b2.update()
posy1 = b3.update()
posy2 = b4.update()
pygame.draw.line(screen, color3, [0, posy1], [xr, posy1], size2)
pygame.draw.line(screen, color4, [0, posy2], [xr, posy2], size2)
pygame.draw.line(screen, color, [posx1, (yr/4)-y], [posx1, (yr/2)], size1)
pygame.draw.line(screen, color2, [posx2, (yr/2)], [posx2, (yr*0.75)+y], size1)
numpy
pygame
pyyaml
#!/usr/bin/env bash
export PYTHONPATH=.

short

  • is the loop structure in run_emulator correct?
  • how to control time?
  • how to diable freeze?

medium

  • abstract key mapping
  • persistent key presses

long

done

  • test line bounce four
  • add increment arg
  • argparse
  • rename as eyesy_emulator
  • strip py from mode name if exists
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment