Compare commits

...

7 Commits

10 changed files with 215 additions and 203 deletions

8
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,8 @@
{
"recommendations": [
"biomejs.biome",
"jnoortheen.nix-ide",
"ms-vscode.vscode-typescript-tslint-plugin",
"ms-python.vscode-pylance"
]
}

2
.vscode/launch.json vendored
View File

@ -33,7 +33,7 @@
"type": "node-terminal",
"name": "Serve",
"request": "launch",
"command": "npm run serve -- --no-client-overlay",
"command": "npm run serve -- --no-client",
"cwd": "${workspaceFolder}"
}
]

View File

@ -3,14 +3,15 @@
import argparse
import asyncio
from pathlib import Path
from collections import namedtuple
from functools import partial
import websockets
from .mmelodies_pb2 import UpdateRequest, Update, Parameter
from .mmelodies_pb2 import UpdateRequest, Update
CONNECTIONS = {}
def connections(*paths):
if not paths:
paths = CONNECTIONS.keys()
@ -19,7 +20,7 @@ def connections(*paths):
for path in paths:
clients.extend(CONNECTIONS.get(path, []))
return clients
return clients
async def request_handler(ws, writer):
@ -41,11 +42,12 @@ async def request_handler(ws, writer):
writer.write(b"scope;")
if request.param_refresh:
writer.write(b"params;")
if request.pgm_event:
writer.write(f"pgmevt {request.pgm_event};".encode())
for param in request.param_changes:
writer.write(f"set {param.id} {param.value};".encode())
writer.write(b"\n")
await writer.drain()
@ -94,7 +96,6 @@ async def update_handler(reader):
websockets.broadcast(clients, update.SerializeToString())
except Exception as e:
print(repr(e))
async def _main(listen_port, pd_port):
@ -143,7 +144,7 @@ async def _main(listen_port, pd_port):
async with ws:
while True:
await update_handler(pd_reader)
await update_handler(pd_reader)
def port(arg: str):

View File

@ -14,7 +14,7 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0fmmelodies.proto\"&\n\tParameter\x12\n\n\x02id\x18\x01 \x01(\r\x12\r\n\x05value\x18\x02 \x01(\r\"\x8e\x01\n\rUpdateRequest\x12\x1a\n\rscope_samples\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12\x1a\n\rparam_refresh\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12!\n\rparam_changes\x18\x02 \x03(\x0b\x32\n.ParameterB\x10\n\x0e_scope_samplesB\x10\n\x0e_param_refresh\";\n\x06Update\x12\x15\n\rscope_samples\x18\x01 \x03(\x02\x12\x1a\n\x06params\x18\x02 \x03(\x0b\x32\n.Parameterb\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0fmmelodies.proto\"&\n\tParameter\x12\n\n\x02id\x18\x01 \x01(\r\x12\r\n\x05value\x18\x02 \x01(\r\"\xb4\x01\n\rUpdateRequest\x12\x1a\n\rscope_samples\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12\x1a\n\rparam_refresh\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x16\n\tpgm_event\x18\x04 \x01(\rH\x02\x88\x01\x01\x12!\n\rparam_changes\x18\x02 \x03(\x0b\x32\n.ParameterB\x10\n\x0e_scope_samplesB\x10\n\x0e_param_refreshB\x0c\n\n_pgm_event\";\n\x06Update\x12\x15\n\rscope_samples\x18\x01 \x03(\x02\x12\x1a\n\x06params\x18\x02 \x03(\x0b\x32\n.Parameterb\x06proto3')
@ -48,7 +48,7 @@ if _descriptor._USE_C_DESCRIPTORS == False:
_PARAMETER._serialized_start=19
_PARAMETER._serialized_end=57
_UPDATEREQUEST._serialized_start=60
_UPDATEREQUEST._serialized_end=202
_UPDATE._serialized_start=204
_UPDATE._serialized_end=263
_UPDATEREQUEST._serialized_end=240
_UPDATE._serialized_start=242
_UPDATE._serialized_end=301
# @@protoc_insertion_point(module_scope)

View File

@ -96,11 +96,11 @@
]
},
"locked": {
"lastModified": 1696434240,
"narHash": "sha256-DDOn4JmDfl7GkWIsFMF9puGSmJvAO1x1t2rQIYg+DTQ=",
"lastModified": 1696489753,
"narHash": "sha256-7o3sAmftK9KGoAD4d3UPg9hveviT5fgQA42u+3DUclQ=",
"ref": "refs/heads/main",
"rev": "e36c08b5291c38796494b7d0c17d190f097f18a7",
"revCount": 25,
"rev": "728d931a877c7324bd652ebcdd9dfe63f7f1d596",
"revCount": 26,
"type": "git",
"url": "https://fem.mint.lgbt/mmelodies/phyzzy.git"
},

View File

@ -8,6 +8,7 @@ message Parameter {
message UpdateRequest {
optional bool scope_samples = 1;
optional bool param_refresh = 3;
optional uint32 pgm_event = 4;
repeated Parameter param_changes = 2;
}

View File

@ -18,3 +18,19 @@ h3 {
.combo-switch [type=range] {
flex-grow: 1;
}
#gyroBox {
display: none;
}
#accelBox {
display: none;
}
#orientBox {
display: none;
}
#unsupported-warning {
display: none;
}

View File

@ -38,11 +38,26 @@ h1 {
text-align: center;
}
.warning {
background-color: var(--secondary);
border-radius: 10px;
padding: 20px;
font-weight: bold;
margin: auto;
width: 75%;
display: block;
}
.button-box a:focus {
border: 15px solid var(--foreground);
outline: none;
}
p {
text-align: center;
font-size: large;
}
body {
background-color: var(--background);
font-family: sans-serif;

View File

@ -16,7 +16,7 @@ function connect_ws() {
ws.binaryType = "arraybuffer";
ws.addEventListener("open", () => {
const message = UpdateRequest.create({ paramRefresh: true});
const message = UpdateRequest.create({ paramRefresh: true });
const request = UpdateRequest.encode(message).finish();
if (socket) {
socket.send(request);
@ -63,197 +63,111 @@ sliders.forEach((slider, i) => {
});
});
// Select all checkbox input elements
const switches = document.querySelectorAll(
"input[type=checkbox]",
) as NodeListOf<HTMLInputElement>;
// Add an event listener to each switch
switches.forEach((switchElement) => {
switchElement.addEventListener("change", function () {
console.log(`Switch ${this.id} state: ${this.checked}`);
});
});
// Declare a global interface for the window object
declare global {
interface Window {
Accelerometer: any;
Gyroscope: any;
AbsoluteOrientationSensor: any;
}
}
if (window.AbsoluteOrientationSensor) {
const sensor = new window.AbsoluteOrientationSensor();
// Show elements hidden by default
function show(element: HTMLElement) {
if (element) {
element.style.display = "flex";
}
}
Promise.all([
// @ts-ignore
navigator.permissions.query({ name: "accelerometer" }),
// @ts-ignore
navigator.permissions.query({ name: "magnetometer" }),
// @ts-ignore
navigator.permissions.query({ name: "gyroscope" }),
]).then((results: PermissionStatus[]) => {
if (results.every((result) => result.state === "granted")) {
sensor.start();
console.log("started")
// …
function checkPerms() {
const permissionsToCheck = ['accelerometer', 'gyroscope', 'magnetometer'];
const permissionPromises = permissionsToCheck.map(permissionName => {
if (!navigator.permissions.query) {
console.log('Permissions API not supported');
return Promise.resolve(null);
}
return navigator.permissions.query({ name: permissionName as any })
.catch(error => {
console.log(`Permission not supported: ${permissionName}`);
return null;
});
}).filter(Boolean);
Promise.all(permissionPromises).then((results: (PermissionStatus | null)[]) => {
const warning = document.getElementById("unsupported-warning") as HTMLElement;
const gyro = document.getElementById("gyroBox") as HTMLElement;
const accel = document.getElementById("accelBox") as HTMLElement;
const orient = document.getElementById("orientBox") as HTMLElement;
let isAccelerometerSupported = "Accelerometer" in window;
let isGyroscopeSupported = "Gyroscope" in window;
let isAbsoluteOrientationSensorSupported = "AbsoluteOrientationSensor" in window;
if (!results.every((result) => result !== null && result.state === "granted")) {
show(warning)
console.log("shown")
} else {
console.log("No permissions to use AbsoluteOrientationSensor.");
if (isAccelerometerSupported && false) {
console.log("Accelerometer is supported.");
show(accel)
sensorStart("Accelerometer", accel, "accel");
}
if (isGyroscopeSupported && false) {
console.log("Gyroscope is supported.");
show(gyro)
sensorStart("Gyroscope", gyro, "gyro");
}
if (isAccelerometerSupported) {
console.log("AbsoluteOrientationSensor is supported.");
show(orient)
sensorStart("AbsoluteOrientationSensor", orient, "orient");
}
if (!isAccelerometerSupported && !isGyroscopeSupported && !isAbsoluteOrientationSensorSupported) {
console.log("None of the interfaces are supported.");
show(warning)
}
}
});
} else {
console.log('AbsoluteOrientationSensor is not supported in this browser.');
})
};
function setupThreeJsScene() {
const sceneSize = new THREE.Vector3(100, 100, 100);
const renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setSize(sceneSize.x, sceneSize.y);
renderer.setPixelRatio(window.devicePixelRatio / 2.5);
const scene = new THREE.Scene();
const axes = new THREE.AxesHelper(5);
scene.add(axes);
const camera = new THREE.PerspectiveCamera(75, sceneSize.x / sceneSize.y, 0.1, 1000);
camera.translateOnAxis(scene.up.clone(), 2);
camera.lookAt(axes.position);
return { renderer, scene, axes, camera };
}
// Create a renderer
const sceneWidth = 100;
const sceneHeight = 100;
const downscaling = 1; // 1 is no downscaling and higher is more downscaling
const renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setSize(sceneWidth, sceneHeight);
renderer.setPixelRatio(window.devicePixelRatio / downscaling);
(document.getElementById("gyroBox") || document.body).appendChild(renderer.domElement);
// Create a scene
const scene = new THREE.Scene();
// Create a camera
const camera = new THREE.PerspectiveCamera(50, sceneWidth / sceneHeight, 0.1, 1000);
// Move the camera backwards
camera.position.y = 2;
// Point the camera towards the origin
camera.lookAt(new THREE.Vector3(0, 0, 0));
// Define a vector representing the initial state (e.g., pointing up)
let xVec = new THREE.Vector3(1, 0, 0); // x-arrow pointing in positive x-direction
let yVec = new THREE.Vector3(0, 1, 0); // y-arrow pointing in positive y-direction
let zVec = new THREE.Vector3(0, 0, 1); // z-arrow pointing in positive z-direction
// Create an arrow
const origin = new THREE.Vector3(0, 0, 0);
const length = 1;
const xColor = 0xff0000;
const yColor = 0x00ff00;
const zColor = 0x0000ff;
const xArrow = new THREE.ArrowHelper(xVec, origin, length, xColor);
const yArrow = new THREE.ArrowHelper(yVec, origin, length, yColor);
const zArrow = new THREE.ArrowHelper(zVec, origin, length, zColor);
let orientation = new THREE.Quaternion();
// Add the arrow to the scene
scene.add(xArrow, yArrow, zArrow);
// Profiling
let frameTime = 0; // Time last frame was taken
let frameCount = 0; // Number of frames since the last second
let frameSec = 0; // Time last second was recorded for a frame
let sampleTime = 0; // Time last frame was taken
let sampleCount = 0; // Number of frames since the last second
let sampleSec = 0; // Time last second was recorded for a frame
// Animation loop
let setFPS = 60; // Desired framerate
let fps = 0; // Startup fps
let clock = new THREE.Clock();
let delta = 0;
function animate() {
requestAnimationFrame(animate);
delta += clock.getDelta();
if (delta > 1 / fps) {
// Orient and render the cursor
orientCursor();
render();
// Profiling
let currTime = Date.now();
if (currTime - frameTime >= 1000) {
console.log("Frames: " + frameCount)
frameCount = 0;
frameTime = currTime;
}
else { frameCount++; }
delta %= 1 / fps;
}
}
function render() {
// Render the scene with the camera
renderer.render(scene, camera);
}
// Initialize default orientation
let initialOrientation: THREE.Quaternion | null = null;
let relOrientation: THREE.Quaternion | null = null;
function orientCursor() {
if (initialOrientation !== null) {
relOrientation = new THREE.Quaternion().multiplyQuaternions(orientation, initialOrientation?.clone().invert());
}
else {
relOrientation = orientation;
console.log("fallback orientation")
}
// Create a vector for each arrow's initial orientation
let xInit = new THREE.Vector3(1, 0, 0); // x-arrow pointing in positive x-direction
let yInit = new THREE.Vector3(0, 1, 0); // y-arrow pointing in positive y-direction
let zInit = new THREE.Vector3(0, 0, 1); // z-arrow pointing in positive z-direction
// Apply the orientation quaternion to the initial vectors
let xOrient = xInit.clone().applyQuaternion(relOrientation);
let yOrient = yInit.clone().applyQuaternion(relOrientation);
let zOrient = zInit.clone().applyQuaternion(relOrientation);
// Set the direction of the arrows
xArrow.setDirection(xOrient);
yArrow.setDirection(yOrient);
zArrow.setDirection(zOrient);
}
function calibrate() {
if (initialOrientation == null) {
console.log("Oriented");
initialOrientation = orientation;
}
}
// Check if AbsoluteOrientationSensor is defined
if (window.AbsoluteOrientationSensor) {
// Create an AbsoluteOrientationSensor object
const sensor = new window.AbsoluteOrientationSensor({ frequency: 60, referenceFrame: 'device' });
// Add an event listener for sensor readings
function handleSensorReading(
sensor: any,
scene: THREE.Scene,
calibratedRotation: THREE.Quaternion | null,
render: (direction: THREE.Quaternion | null) => void,
checkbox: HTMLInputElement | null
) {
let fps = 0;
const setFPS = 60;
sensor.addEventListener('reading', () => {
// Apply the sensor's quaternion to the vector
orientation = new THREE.Quaternion(...sensor.quaternion);
// Normalize the quaternion
orientation.normalize;
const orientation = new THREE.Quaternion(...sensor.quaternion).normalize();
// Set the initial orientation if it's not set already
calibrate();
// Profiling
let currTime = new Date().getTime()
if (sampleSec < currTime - 1000) {
console.log("Samples: " + sampleCount)
sampleCount = 0;
sampleSec = currTime;
if (!calibratedRotation) {
calibratedRotation = orientation.invert();
}
else { sampleCount++; }
sampleTime = currTime;
const relativeOrientation = orientation.clone().multiply(calibratedRotation.clone());
render(relativeOrientation);
});
// Get the button element
const checkbox = document.getElementById('gyro') as HTMLInputElement;
console.log("checkboxgotted: " + checkbox)
// Check if the button was found
if (checkbox) {
// Add a click event listener to the button
@ -262,15 +176,13 @@ if (window.AbsoluteOrientationSensor) {
if (!this.checked) {
sensor.stop();
console.log("stopped");
orientation = new THREE.Quaternion();
fps = 0;
initialOrientation = null;
orientCursor();
render();
render(null);
} else {
// Otherwise, start the sensor
sensor.start();
console.log("started");
calibratedRotation = null;
fps = setFPS;
}
});
@ -278,11 +190,48 @@ if (window.AbsoluteOrientationSensor) {
else {
console.log(checkbox)
}
// Start the animation loop
animate();
render();
} else {
// Log a message to the console if AbsoluteOrientationSensor is not supported
console.log('AbsoluteOrientationSensor is not supported in this browser.');
}
type sensorType = "Accelerometer" | "Gyroscope" | "AbsoluteOrientationSensor";
function sensorStart(sensorName: sensorType, box: HTMLElement, checkboxId: string) {
const sensorConstructors: { [K in sensorType]: any } = {
"Accelerometer": window.Accelerometer,
"Gyroscope": window.Gyroscope,
"AbsoluteOrientationSensor": window.AbsoluteOrientationSensor
};
const SensorConstructor = sensorConstructors[sensorName];
if (SensorConstructor) {
const sensor = new SensorConstructor({ frequency: 60, referenceFrame: 'device' });
// Set up Three.js scene
const { renderer, scene, axes, camera } = setupThreeJsScene();
box.appendChild(renderer.domElement)
// Define render function
const render = (direction: THREE.Quaternion | null) => {
if (!direction) {
direction = new THREE.Quaternion().identity();
}
axes.setRotationFromQuaternion(direction);
renderer.render(scene, camera);
}
// Initialize default orientation
let calibratedRotation: THREE.Quaternion | null = null;
let currentDirection: THREE.Vector3 | null = null;
let relativeDirection: THREE.Vector3 | null = null;
var checkbox = document.getElementById(checkboxId) as HTMLInputElement;
// Handle sensor readings
handleSensorReading(sensor, scene, calibratedRotation, render, checkbox);
render(null);
// Render the first frame
} else {
console.log(`No constructor found for sensor: ${sensorName}`);
}
}
checkPerms();

View File

@ -60,11 +60,33 @@
<input type="range" id="fr_size" name="freq_range" min="0" max="127">
</div>
</div>
</div>
<h1>Controls</h1>
<div class="flex-container">
<div class="button-box">
<div hidden class="combo-switch" id="gyroBox">
<input type="checkbox" id="gyro" name="tilt">
<h3>Tilt</h3>
<div id="debugGyro"></div>
</div>
<div hidden class="combo-switch" id="accelBox">
<input type="checkbox" id="accel" name="swing">
<h3>Swing</h3>
<div id="debugAccel"></div>
</div>
<div hidden class="combo-switch" id="orientBox">
<input type="checkbox" id="orient" name="move">
<h3>Move</h3>
<div id="debugOrient"></div>
</div>
<div class="combo-switch" id="gyroBox">
<input type="checkbox" id="gyro" name="gyro">
<h3>Gyro</h3>
</div>
<p hidden class="warning" id="unsupported-warning">
Uh Oh! Seems like your device doesn't support any control options :(
You can still use the sliders above though !
</p>
</div>
</body>