Compare commits

...

18 Commits

12 changed files with 4696 additions and 79 deletions

4
.gitignore vendored
View File

@ -1 +1,5 @@
node_modules node_modules
.~*~
.*~undo-tree~
.~lock*#
.*.swp

View File

@ -4,6 +4,22 @@ OSC controlled visual (used with motion controller)
Also see https://git.kompot.si/g1smo/pifcamp-2021 Also see https://git.kompot.si/g1smo/pifcamp-2021
* Running
0. Install dependencies
`npm i`
1. Serial port
`node server.js /dev/ttyUSB0 115200`
2. Bluetooth
`node server.js /dev/rfcomm0 115200`
3. WiFi
`node server.js`
(wait for it)
* older prototypes * older prototypes
- https://kreten.si/spin/ - https://kreten.si/spin/
- https://kreten.si/spin-color/ - https://kreten.si/spin-color/

88
anim.js
View File

@ -38,8 +38,13 @@ var rotacijaX = 0;
var rotacijaY = 0; var rotacijaY = 0;
var rotacijaZ = 0; var rotacijaZ = 0;
var crotacijaX = 0.000;
var crotacijaY = 0.000;
var crotacijaZ = 0.000;
// Premik obstojecih barv // Premik obstojecih barv
var zamikBarve = 0.01; var zamikBarve = 0.01;
var barvapuls = 10;
// Zamik pri novem objektu // Zamik pri novem objektu
var barva_mod = 0.003; var barva_mod = 0.003;
@ -47,16 +52,14 @@ var saturacija = 1;
var svetlost = 0.4; var svetlost = 0.4;
// Rotiranje kamere // Rotiranje kamere
var kameraX = 0; var qKamera = new THREE.Quaternion();
var kameraY = 0;
var kameraZ = 0;
// Quaternioni za rotacijo in kalibracijo // Quaternioni za rotacijo in kalibracijo
var qWW = new THREE.Quaternion(-1, 0, 0, 0); var qWW = new THREE.Quaternion();
var qWWo = new THREE.Quaternion(-1, 0, 0, 0); var qWWo = new THREE.Quaternion();
var qWWd = new THREE.Quaternion(-1, 0, 0, 0); var qWWd = new THREE.Quaternion();
var qObj = new THREE.Quaternion(-1, 0, 0, 0); var qObj = new THREE.Quaternion();
var qStart = new THREE.Quaternion(-1, 0, 0, 0); var qStart = new THREE.Quaternion();
var calibrate = true; var calibrate = true;
var reset = false; var reset = false;
@ -85,6 +88,8 @@ renderer.setSize(window.innerWidth, window.innerHeight);
// Črno ozadje // Črno ozadje
renderer.setClearColor(0x000000, 1); renderer.setClearColor(0x000000, 1);
//var pivot = new THREE.Group();
//scene.add( pivot );
@ -107,8 +112,13 @@ function render () {
function modulirajParametre() { function modulirajParametre() {
// Vsi gumbi? => RESET // Vsi gumbi? => RESET
if ((keysPressed[0] + keysPressed[1] + keysPressed[2] + keysPressed[3]) === 4 && !reset) { if ((keysPressed[0] + keysPressed[1] + keysPressed[2] + keysPressed[3]) === 4 && !reset) {
reset = true; setTimeout(() => {
if ((keysPressed[0] + keysPressed[1] + keysPressed[2] + keysPressed[3]) === 4) {
setTimeout(() => {
window.location.reload() window.location.reload()
}, 1000);
}
}, 1000)
} }
// Posodobi kvaternion polozaja kontrolerja // Posodobi kvaternion polozaja kontrolerja
@ -121,20 +131,32 @@ function modulirajParametre() {
} }
// Rotiranje manualno (z rocnimi gibi "iz sredine") // Rotiranje manualno (z rocnimi gibi "iz sredine")
if (keysPressed[3]) { if (keysPressed[0]) {
var k = objekti.slice(-1)[0]; var k = objekti.slice(-1)[0];
qObj.multiply(qWWd); qObj.multiply(qWWd);
k.quaternion.multiply(qWWd); k.quaternion.multiply(qWWd);
} }
if (keysPressed[2]) { if (keysPressed[1]) {
drotacijaX += qWWd.x / 10; drotacijaX += qWWd.x / 10;
drotacijaY += qWWd.y / 10; drotacijaY += qWWd.y / 10;
drotacijaZ += qWWd.z / 10; drotacijaZ += qWWd.z / 10;
} }
if (keysPressed[0]) { if (keysPressed[2]) {
width *= 1 + dqX; crotacijaX += qWWd.x / 30;
barva_mod += (dqZ / 100); //crotacijaY += qWWd.y / 30;
obj_limit *= 1 + (dqY); crotacijaZ += qWWd.z / 30;
barvapuls += qWWd.y;
}
if (keysPressed[3]) {
width *= 1 + (dqX / 3);
barva_mod += (dqZ / 1000);
obj_limit *= 1 - dqY;
}
if (kbdPressed['c']) {
crotacijaX *= 0.9;
crotacijaY *= 0.9;
crotacijaZ *= 0.9;
} }
} }
@ -156,7 +178,8 @@ function objAnim() {
// Funkcija za dodajanje novih objektov // Funkcija za dodajanje novih objektov
function addObj(w, h) { function addObj(w, h) {
var col = new THREE.Color(); var col = new THREE.Color();
col.setHSL(stevec * barva_mod, saturacija, svetlost); //col.setHSL(stevec * barva_mod, saturacija, svetlost);
col.setHSL(stevec * barva_mod, saturacija, (Math.sin(stevec/barvapuls) / 6) + 0.3);
var mat = new THREE.LineBasicMaterial({ var mat = new THREE.LineBasicMaterial({
color: col color: col
@ -180,6 +203,7 @@ function addObj(w, h) {
var obj = new THREE.Line(geo, mat, THREE.LineSegments); var obj = new THREE.Line(geo, mat, THREE.LineSegments);
scene.add(obj); scene.add(obj);
//pivot.add(obj);
obj.setRotationFromQuaternion(qObj); obj.setRotationFromQuaternion(qObj);
// Pocisti za seboj // Pocisti za seboj
@ -191,27 +215,14 @@ function addObj(w, h) {
} }
}; };
var xAksa = new THREE.Vector3(0, 1, 0); qK = new THREE.Quaternion()
function camRotate () { function camRotate () {
// rotiraj po z osi //scene.setRotationFromQuaternion(qKamera)
camera.translateX(kameraX); scene.rotation.x += crotacijaX
camera.translateZ(odmik_kamere - Math.sqrt(Math.pow(odmik_kamere, 2) + Math.pow(kameraX, 2))); scene.rotation.y += crotacijaY
scene.rotation.z += crotacijaZ
/* }
camera.translateY(kameraY);
camera.translateX(odmik_kamere - Math.sqrt(Math.pow(odmik_kamere, 2) + Math.pow(kameraY, 2)));
camera.translateZ(kameraZ);
camera.translateX(odmik_kamere - Math.sqrt(Math.pow(odmik_kamere, 2) + Math.pow(kameraZ, 2)));
*/
/*
camera.position.x = Math.sin((stevec % 10) / 10) * cam_rot_offset;
camera.position.y = Math.cos((stevec % 10) / 10) * cam_rot_offset;
*/
camera.lookAt(scene.position);
};
// Inicializiraj // Inicializiraj
document.onreadystatechange = function () { document.onreadystatechange = function () {
@ -235,11 +246,12 @@ function getVal(msg) {
return msg.value; return msg.value;
} }
kbdPressed = { const kbdPressed = {
a: false, a: false,
s: false, s: false,
d: false, d: false,
f: false f: false,
c: false
}; };
window.addEventListener('keydown', (e) => { window.addEventListener('keydown', (e) => {

4282
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@
"description": "epilepsija", "description": "epilepsija",
"dependencies": { "dependencies": {
"express": "^4.17.1", "express": "^4.17.1",
"midi": "^2.0.0",
"nodemon": "^2.0.12", "nodemon": "^2.0.12",
"nouislider": "14.6.2", "nouislider": "14.6.2",
"osc": "^2.4.2", "osc": "^2.4.2",

View File

@ -64,7 +64,7 @@ o = OSCFunc({
~oX = ~gX; ~oX = ~gX;
~oY = ~gY; ~oY = ~gY;
~oZ = ~gZ; ~oZ = ~gZ;
msg.postln;
//[msg, time, addr, recvPort].postln; //[msg, time, addr, recvPort].postln;
~gW = msg[1]; ~gW = msg[1];
~gZ = msg[2]; ~gZ = msg[2];
@ -92,11 +92,11 @@ o = OSCFunc({
~euler = ~quaternionToEuler.([~dgW, ~dgX, ~dgY, ~dgZ]); ~euler = ~quaternionToEuler.([~dgW, ~dgX, ~dgY, ~dgZ]);
~euler.postln; ~euler.postln;
}, '/quaternion/', n); }, '/quaternion', n);
) )
// ENDWW // ENDWW
OSCFunc.trace(true); OSCFunc.trace(false);

134
sc/ww1.scd 100644
View File

@ -0,0 +1,134 @@
// Zacetek / konec
Server.local.boot;
Server.local.quit;
s.boot;
s.stop;
// Server -> Boot /// Ctrl+B
{SinOsc.ar}.play; // Shift+Enter evalvira
// Ctrl + . ustavi zvok!
// Start/stop prek spremenljivke
x = {SinOsc.ar}.play;
x.free;
// HELP: shift+ctrl+D
// recimo ugen
{SinOsc.ar(SinOsc.kr(1, 0, 1, 1), 0, 1, 0)}.play
{SinOsc.ar(520, 0, SinOsc.ar(3, 0.75, add: 1, phase: 0))}.play;
{VarSaw.ar(42, 0, SinOsc.ar(2, 0.75, add: 1, phase: 0))}.play;
{SinOsc.ar(32, 0, VarSaw.kr(3, 0.75, add: 1, phase: 0))}.play;
{SinOsc.ar(100, )}.play
// Kurzor na keyword in Ctrl+d odpre help zanj
(
{SinOsc.ar(100,)}.play;
{SinOsc.ar(100, 3.14)}.play;
)
()
(
SynthDef.new(\kFaza, {
var a = SinOsc.ar(100);
var b = SinOsc.ar(100, 3.14);
Out.ar(0, a);
Out.ar(1, b);
}).add;
)
(
SynthDef.new(\faza, {
var a = SinOsc.ar(100);
var b = SinOsc.ar(100);
Out.ar(0, a);
Out.ar(1, b);
}).add;
)
s = Synth.new(\kFaza);
v = Synth.new(\faza);
z.set(\f, 80);
z.set(\f, 200, \amp, 0.5);
NetAddr.langPort;
a= 0;
b= 0;
c= 0;
d= 0;
// WW
// Receiver function
(
SynthDef.new(\slOSC, {
// Najprej argumenti, potem variable
arg f=0, ampOsc=0, ch=0, off=0;
var so;
so = SinOsc.ar(f, off, SinOsc.kr(ampOsc));
Out.ar(ch, so);
}).add;
a = Synth.new(\slOSC);
b = Synth.new(\slOSC);
c = Synth.new(\slOSC);
d = Synth.new(\slOSC);
s = [a, b, c, d];
OSCFunc({
arg msg, time, addr, recvPort;
//[msg, time, addr, recvPort].postln;
~gX = msg[1] * 100;
~gY = msg[2] / 3;
}, '/euler', n);
OSCFunc({
arg msg, time, addr, recvPort;
//[msg, time, addr, recvPort].postln;
4.do({
arg i;
if (msg[i + 1] == 1.0) {
s[i].set(\f, ~gX);
s[i].set(\ampOsc, ~gY);
s[i].set(\off, ~gZ);
}
});
}, '/keys', n);
)
// ENDWW
(
w = {
arg noiseHz = 2.5;
var f, sig, amp;
// Med 200 in 1000 Hz
//f = LFNoise0.kr(8, 400, 600);
// f = LFNoise0.kr(8).range(200, 1000)
f = LFNoise0.kr(noiseHz).exprange(300, 600);
amp = LFNoise1.kr(9).exprange(0.2, 1);
sig = SinOsc.ar(f) * amp;
}.play;
)
w.set(\noiseHz, 48);
w.set(\noiseHz, exprand(260, 400));
w.free;

57
sc/ww2.scd 100644
View File

@ -0,0 +1,57 @@
// Zacetek / konec
s.boot;
s.stop;
// Server -> Boot /// Ctrl+B
// HELP: shift+ctrl+D
// recimo ugen
NetAddr.langPort;
(
SynthDef.new(\slOSC, {
// Najprej argumenti, potem variable
arg f=440, ampOsc=0, ch=0, off=0;
var so;
so = SinOsc.ar(f, off, SinOsc.kr(ampOsc));
Out.ar(ch, so);
}).add;
s = [
Synth.new(\slOSC),
Synth.new(\slOSC),
Synth.new(\slOSC),
Synth.new(\slOSC)
];
OSCFunc({
arg msg, time, addr, recvPort;
//[msg, time, addr, recvPort].postln;
~gX = msg[1] * 100;
~gY = msg[2] / 3;
~gZ = msg[3] / 5;
}, '/eulerDiff', n);
OSCFunc({
arg msg, time, addr, recvPort;
msg.postln;
}, '/euler', n);
OSCFunc({
arg msg, time, addr, recvPort;
//[msg, time, addr, recvPort].postln;
4.do({
arg i;
if (msg[i + 1] == 1.0) {
s[i].get(\f, {arg f; s[i].set(\f, f + ~gX)});
s[i].get(\ampOsc, {arg ao; s[i].set(\ampOsc, ao + ~gY)});
s[i].get(\off, {arg o; s[i].set(\off, o + ~gZ)});
}
});
}, '/keys', n);
)

70
sc/ww3.scd 100644
View File

@ -0,0 +1,70 @@
// Zacetek / konec
s.boot;
s.stop;
// Server -> Boot /// Ctrl+B
// HELP: shift+ctrl+D
// recimo ugen
NetAddr.langPort;
(
(
SynthDef.new(\slOSC, {
// Najprej argumenti, potem variable
arg f=120, ampOsc=1, ch=0, off=1;
var so;
so = SinOsc.ar(f, off, SinOsc.kr(ampOsc));
Out.ar(ch, so);
}).add);
s = [
Synth.new(\slOSC),
Synth.new(\slOSC, [\ampOsc, 1.5]),
Synth.new(\slOSC, [\ampOsc, 3]),
Synth.new(\slOSC, [\ampOsc, 4])
];
OSCFunc({
arg msg, time, addr, recvPort;
//[msg, time, addr, recvPort].postln;
~gX = msg[1];
~gY = msg[2];
~gZ = msg[3];
}, '/eulerDiff', n);
OSCFunc({
arg msg, time, addr, recvPort;
msg.postln;
}, '/euler', n);
OSCFunc({
arg msg, time, addr, recvPort;
//[msg, time, addr, recvPort].postln;
/*
if (msg[0]+msg[1]+msg[2]+msg[3] == 4.0) {
4.do({
arg i;
if (msg[i + 1] == 1.0) {
s[i].set(\f, 100);
s[i].set(\ampOsc, 1);
s[i].set(\off, 1);
}
});
} else {*/
4.do({
arg i;
if (msg[i + 1] == 1.0) {
s[i].get(\f, {arg f; s[i].set(\f, f * (1 + ~gX))});
s[i].get(\ampOsc, {arg ao; s[i].set(\ampOsc, ao * (1 + ~gY))});
s[i].get(\off, {arg o; s[i].set(\off, o * (1 + ~gZ))});
}
});
//}
}, '/keys', n);
)

108
server.js
View File

@ -1,11 +1,36 @@
// Which port do I send OSC messages to? (SupperCollider, ...)
const OSCPORT = 57120;
// Which port do I listen to (for visuals, calibration)
const PORT = 6676
// Do we have a problem, shall we debug?
const DEBUG = {
osc: true,
udp: false,
midi: true
}
// MIDI out
const MIDI = true
const express = require('express') const express = require('express')
const http = require('http') const http = require('http')
const WebSocket = require('ws') const WebSocket = require('ws')
const osc = require('osc') const osc = require('osc')
const readline = require('readline') const readline = require('readline')
const fs = require('fs') const fs = require('fs')
const midi = require('midi')
const port = 6676
// Vzemi iz argumenta // Vzemi iz argumenta
const tty = process.argv[2] const tty = process.argv[2]
@ -30,11 +55,19 @@ const include_files = [
const app = express(); const app = express();
const server = http.Server(app); const server = http.Server(app);
const DEBUG = true
// Odprti serijski OSC link // Odprti serijski OSC link
let scon = null let scon = null
let mo = null
if (MIDI) {
// Midi port
mo = new midi.Output()
//mo.getPortCount()
//mo.getPortName(0)
//mo.openPort(0)
mo.openVirtualPort("kegel")
}
function openSerial() { function openSerial() {
console.log('opening ', tty, baudrate) console.log('opening ', tty, baudrate)
@ -44,7 +77,6 @@ function openSerial() {
autoOpen: true, autoOpen: true,
useSLIP: true useSLIP: true
}) })
scon.open()
scon.on('open', e => { scon.on('open', e => {
console.log('serial connection opened') console.log('serial connection opened')
@ -56,18 +88,21 @@ function openSerial() {
}) })
scon.on('close', e => { scon.on('close', e => {
console.warn('serial connection closed, restarting in 1 second') console.warn('serial connection closed, restarting in 1 second')
setTimeout(openSerial, 1000)
}) })
// Arduino OSC gre v web // Arduino OSC gre v web
scon.on('message', msg => { scon.on('message', msg => {
// Debug incoming osc // Debug incoming osc
if (DEBUG) { if (DEBUG.osc) {
console.log('osc msg', msg) console.log('osc msg', msg)
} }
sendAll(msg, null, null, osclients) sendAll(msg, null, null, osclients)
}) })
scon.open()
if (scon._closeCode) { if (scon._closeCode) {
scon = null scon = null
console.log('restarting serial connection') console.log('restarting serial connection')
@ -91,6 +126,14 @@ app.get('/test', (req, res) => {
res.sendFile(__dirname + '/test.html'); res.sendFile(__dirname + '/test.html');
}); });
// Hydra inclusion
app.get('/hydra', function(req, res) {
res.sendFile(__dirname + '/hydra-osc-main/index.html');
});
app.get('/lib/osc.min.js', function(req, res) {
res.sendFile(__dirname + '/hydra-osc-main/lib/osc.min.js');
});
let settings = {}; let settings = {};
app.get('/settings', function(req, res) { app.get('/settings', function(req, res) {
res.send(settings); res.send(settings);
@ -102,7 +145,7 @@ include_files.map(function(file) {
}); });
}); });
server.listen(port, () => console.log('listening on *:' + port)) server.listen(PORT, () => console.log('listening on *:' + PORT))
// Websocket init // Websocket init
const wss = new WebSocket.Server({ server }) const wss = new WebSocket.Server({ server })
@ -110,15 +153,18 @@ const wss = new WebSocket.Server({ server })
// Relay multicast to websockets // Relay multicast to websockets
// @TODO still sends to supercollider? Do we need two sockets? // @TODO still sends to supercollider? Do we need two sockets?
/*
var dgram = require('dgram'); var dgram = require('dgram');
var sss = dgram.createSocket('udp4'); var sss = dgram.createSocket('udp4');
sss.on('listening', () => { sss.on('listening', () => {
sss.addMembership('224.0.1.9'); sss.addMembership('224.0.1.9');
}) })
sss.bind(6696, '224.0.1.9'); sss.bind(6696, '224.0.1.9');
*/
const scudp = new osc.UDPPort({ const scudp = new osc.UDPPort({
socket: sss remotePort: OSCPORT
//socket: sss
}) })
scudp.on('open', () => { scudp.on('open', () => {
@ -126,7 +172,10 @@ scudp.on('open', () => {
}) })
scudp.on('message', (msg) => { scudp.on('message', (msg) => {
if (DEBUG.udp) {
console.log('got UDP msg', msg); console.log('got UDP msg', msg);
}
osclients.forEach( client => { osclients.forEach( client => {
if (client) { if (client) {
//console.log("sending", msg, info) //console.log("sending", msg, info)
@ -173,6 +222,12 @@ function eulerFromQuaternion(quaternion) {
} }
const sendAll = (msg, info, oscWS, osclients) => { const sendAll = (msg, info, oscWS, osclients) => {
// Reset euler rotation to 0
if (msg.address == '/keys') {
if (msg.args[0] && msg.args[1] && msg.args[2] && msg.args[3]) {
eulerRotation = [0, 0, 0]
}
}
// Convert quaternion diff to euler angle diff // Convert quaternion diff to euler angle diff
if (msg.address == '/quaternionDiff') { if (msg.address == '/quaternionDiff') {
@ -200,30 +255,53 @@ const sendAll = (msg, info, oscWS, osclients) => {
}) })
if (scudp) { if (scudp) {
if (DEBUG.udp) {
console.log("UDP SEND", msg)
}
scudp.send(msg) scudp.send(msg)
} }
} }
const osclients = [] let osclients = []
wss.on('connection', function (ws) { wss.on('connection', function (ws) {
console.log('new client connection') console.log('new client connection', ws._socket.remoteAddress)
const oscWS = new osc.WebSocketPort({ let oscWS = new osc.WebSocketPort({
socket: ws, socket: ws
metadata: false
}); });
// Vsi OSC sem grejo naprej na kliente OSC // Vsi OSC sem grejo naprej na kliente OSC
oscWS.on('packet', (packet, info) => { oscWS.on('message', ({ address, args}, info) => {
// Broadcast adjust msg console.log('fasal sem', address, args)
const [address, args] = packet if (MIDI) {
if (address == '/midi') {
if (DEBUG.midi) {
console.log('midi SEND', args)
}
mo.send(args)
}
}
sendAll({ address, args}, info, oscWS, osclients) sendAll({ address, args}, info, oscWS, osclients)
}) })
oscWS.on('error', error => { oscWS.on('error', error => {
console.warn('Ignoring invalid OSC') console.warn('Ignoring invalid OSC')
console.warn(error) console.warn(error)
oscWS.close()
osclients = osclients.filter(ws => ws !== oscWS)
}) })
osclients.push(oscWS) osclients.push(oscWS)
oscWS.on('close', () => {
console.log('closing socket', oscWS.socket.remoteAddress)
osclients = osclients.filter(ws => ws !== oscWS)
})
}) })
// Zapri midi
/*
if (MIDI && mo) {
mo.closePort()
}
*/

View File

@ -65,9 +65,8 @@ scene.add(skupina);
// Quaternioni za rotacijo in kalibracijo // Quaternioni za rotacijo in kalibracijo
var qWW = new THREE.Quaternion(); var qWW = new THREE.Quaternion();
var qObj = new THREE.Quaternion(-1, 0, 0, 0); var qObj = new THREE.Quaternion();
var qStart = new THREE.Quaternion(-1, 0, 0, 0); var qStart = new THREE.Quaternion();
var qCalibrate = new THREE.Quaternion();
var reset = false; var reset = false;