Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding CongaCelebration #95

Open
wants to merge 2 commits into
base: conga-2021
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions audio/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class AudioInput extends EventEmitter {
constructor(options) {
super();
// this._sampleRate = options._sampleRate || 48000;
const deviceIndex = options.deviceIndex || null;
const deviceIndex = options.deviceIndex ?? null;
const profile = options.profile || null;
this._args = [ '-u' ];
if (!profile) {
Expand All @@ -22,7 +22,7 @@ class AudioInput extends EventEmitter {
const profilePath = options.profilePath || 'audioprofile.pstats';
this._args.push('-m', 'cProfile', '-o', profilePath, mainScript);
}
if (deviceIndex) {
if(deviceIndex !== undefined ){
this._args.push('-d', deviceIndex.toString());
}
this._subprocess = null;
Expand Down
68 changes: 68 additions & 0 deletions server/setups/program-presets/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,74 @@
}
],
"multiply": true
},
"ShootingArcCelebration": {
"programs": [
{
"programName": "congaShooting2",
"shape": "fullArc",
"config": {
"fireThreshold": 0.31,
"soundMetricP1": "mic2_rms",
"soundMetricP2": "mic2_rms"
}
},
{
"programName": "congaScore",
"shape": "highestSide",
"config": {
"playerNumber": 1,
"globalBrightness": 0.47,
"reverse": true,
"colorHue": 0.7,
"colorSat": 0.7
}
},
{
"programName": "congaScore",
"shape": "lowestSide",
"config": {
"globalBrightness": 1,
"reverse": true,
"playerNumber": "0",
"colorHue": 0.71,
"colorSat": 0.84
}
},
{
"programName": "congaCelebration",
"shape": "head",
"config": {
"programs": [
{
"programName": "radial3d",
"config": {
"colorMap": "a_pink_r"
}
}
],
"animateOnlyOnCelebration": true
}
}
],
"fps": 60
},
"Rope w/ Celebration": {
"programs": [
{
"programName": "congaCelebration",
"shape": "man"
},
{
"programName": "congaRope",
"shape": "fullArc",
"config": {
"soundMetricP1": "peakDecay",
"soundMetricP2": "mic2_peakDecay"
}
}
],
"multiply": false
}
},
"sound-waves": {
Expand Down
1 change: 1 addition & 0 deletions server/src/LightController.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const programNames = [
"mix",
"congaShooting2",
"congaScore",
"congaCelebration",
"congaShooting",
"congaRope",
"PROGRAM_Main_fuego2019",
Expand Down
4 changes: 2 additions & 2 deletions server/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ controller.start();

console.log('Available audio devices:\n', listDevices());

const audioInput = new AudioInput({deviceIndex: 1,});
const audioInput = new AudioInput({deviceIndex: 0,});
// audioInput.on('audioframe', audioEmitter.updateFrame.bind(audioEmitter));

const audioInput2 = new AudioInput({deviceIndex: 3,});
const audioInput2 = new AudioInput({deviceIndex: 2,});
audioInput2.on('audioframe', (frame) => {
audioEmitter.frame2 = frame;
});
Expand Down
18 changes: 18 additions & 0 deletions server/src/light-programs/conga-utils/GlobalGame.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
const ColorUtils = require("./../utils/ColorUtils");

class Game {
constructor() {
this.score = [0,0];
this.player1Color = ColorUtils.HSVtoRGB(400, 1, 1);
this.player2Color = ColorUtils.HSVtoRGB(400+0.33, 1, 1);
}

addPoint(playerIndex, points = 1) {
Expand All @@ -14,6 +18,20 @@ class Game {
restart() {
this.score = [0,0];
}

winner(){
let winner = false;
if (this.score[0] == this.max()){
winner = 1;
}
else if(this.score[1] == this.max()){
winner = 2;
}
if(winner){
this.restart();
}
return winner;
}
}

module.exports = {
Expand Down
135 changes: 135 additions & 0 deletions server/src/light-programs/programs/congaCelebration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
const LightProgram = require("./../base-programs/LightProgram");
const ColorUtils = require("./../utils/ColorUtils");
const GlobalGame = require("./../conga-utils/GlobalGame");
const _ = require("lodash");
const programsByShape = require("./../base-programs/ProgramsByShape");

module.exports = class CongaCelebratiton extends LightProgram {
init() {
this.past = null;
this.celebrate = 0;
this.winner = false;
this.colors = new Array(this.numberOfLeds).fill(ColorUtils.HSVtoRGB(0, 0, this.config.brillo));
// Shallow copy of schedule
this.programs = _.map(this.config.programs, config => this.getProgramInstanceFromParam(config));
}

getProgramInstanceFromParam({programName, config, shape}) {
let p = null;
// For performance, only use programsByShape if there is a shape
if(shape) {
const programClass = this.lightController.programs[programName].generator;
const byShapeClass = programsByShape({[shape]: [programClass, config || {}]});
p = new byShapeClass(this.config, this.geometry, this.shapeMapping, this.lightController);
} else {
p = this.lightController.instanciateProgram(programName);
p.updateConfig({...p.config, ...config})
}

p.init();
return p;
}

getCelebrationBaseColors() {



}

drawFrame(draw, audio) {
this.colors = new Array(this.numberOfLeds).fill(ColorUtils.HSVtoRGB(0, 0, this.config.brillo/100));
let winner = GlobalGame.game.winner();
let combinedColors = this.colors;
if (winner && (this.celebrate == 0 || winner != this.winner)){
this.winner = winner;
this.celebrate = this.config.celebrationDurationInFrames;
}
if (this.celebrate > 0){
let winnerColor = this.winner == 1 ? GlobalGame.game.player1Color : GlobalGame.game.player2Color;
this.colors = new Array(this.numberOfLeds).fill(winnerColor);
this.celebrate--;
}
if(!this.config.animateOnlyOnCelebration || this.celebrate > 0){
combinedColors = this.colors;

this.extraTime = (this.extraTime || 0) + Math.random() * 10;

for (const prog of this.programs) {
// Done by ProgramScheduler, has to be replicated here
prog.timeInMs = this.timeInMs;
let globalBrightness = prog.config.globalBrightness || 0;
prog.drawFrame((colors) => {
for (let i = 0; i < this.numberOfLeds; i++) {
let [r, g, b, a] = combinedColors[i]
const [r2, g2, b2, a2] = colors[i];
if (this.config.multiply) {
// globalBrightness of 0 means "the layer does not darken the other layer"
r = r * ((r2+(255-r2)*(1-globalBrightness)) || 0) / 255;
g = g * ((g2+(255-g2)*(1-globalBrightness)) || 0) / 255;
b = b * ((b2+(255-b2)*(1-globalBrightness)) || 0) / 255;
a = a + (a2 || 0)
} else {
r += (r2 || 0) * globalBrightness;
g += (g2 || 0) * globalBrightness;
b += (b2 || 0) * globalBrightness;
a += a2 || 0;
}
combinedColors[i] = [r, g, b, a];
}
}, audio)
}
}
draw(combinedColors);
}

updateConfig(newConfig) {
// TODO: backwards compatibility with previous version of mix
if(newConfig.a && newConfig.b) {
let {a, b, ... other} = newConfig;
newConfig = {... other, programs: [a, b]}
}

// Override LightProgram version to decide when a program init needs to be called
if (this.programs) {
let updated = newConfig.programs;
let oldConfigs = this.config.programs;

this.programs = _.map(updated, (newProgDef, i) => {
let oldProgDef = oldConfigs[i];

let subprogram = null;

// Detect if the selected program type is the same or it changed
if (oldProgDef && oldProgDef.programName === newProgDef.programName && oldProgDef.shape === newProgDef.shape) {
subprogram = this.programs[i]
subprogram.updateConfig({ ... subprogram.config, ... newProgDef.config })
} else {
subprogram = this.getProgramInstanceFromParam(newProgDef)
}

// Detect if a different preset was selected and apply the default+preset program config
if(oldProgDef && oldProgDef.presetName !== newProgDef.presetName && newProgDef.presetName) {
const presets = this.lightController.getProgramPresets(newProgDef.programName);
const defaults = this.lightController.getProgramDefaultParams(newProgDef.programName);
newProgDef.config = presets[newProgDef.presetName];
subprogram.updateConfig({ ... defaults, ... presets[newProgDef.presetName] })
}

return subprogram
});
}

super.updateConfig(newConfig)
}

// Override and extend config Schema
static configSchema() {
let res = super.configSchema();
res.brillo = { type: Number, min: 0, max: 1, step: 0.01, default: 0.5 };
res.celebrationDurationInFrames = { type: Number, min: 0, max: 200, step: 5, default: 500 };
res.animateOnlyOnCelebration = {type: Boolean, default: false};
res.programs = {type: 'programs', default: [{programName: 'circles'}]};
res.multiply = {type: Boolean, default: false};
return res;
}
};
16 changes: 8 additions & 8 deletions server/src/light-programs/programs/congaRope.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const LightProgram = require("./../base-programs/LightProgram");
const ColorUtils = require("./../utils/ColorUtils");
const _ = require("lodash");
const GlobalGame = require("./../conga-utils/GlobalGame");

module.exports = class CongaShooting extends LightProgram {

Expand All @@ -11,8 +12,8 @@ module.exports = class CongaShooting extends LightProgram {
this.rope = {pos: this.center - this.segmentSize, length: 2 * this.segmentSize};
this.explosionLevel = 0;
this.time = 0;
this.audioWindow1 = new Array(1000).fill(0);
this.audioWindow2 = new Array(1000).fill(0);
this.audioWindow1 = new Array(600).fill(0);
this.audioWindow2 = new Array(600).fill(0);
this.maxVolume = 0;
}

Expand All @@ -21,17 +22,16 @@ module.exports = class CongaShooting extends LightProgram {
let volP2 = (audio[this.config.soundMetricP2] || 0) * this.config.multiplier;

this.audioWindow1.unshift();
this.audioWindow1.push(volP1 > this.config.fireThreshold ? 1 : 0);
this.audioWindow1.push(volP1 > this.config.fireThreshold ? volP1 : 0);

this.audioWindow2.unshift();
this.audioWindow2.push(volP2 > this.config.fireThreshold ? 1 : 0);
this.audioWindow2.push(volP2 > this.config.fireThreshold ? volP2 : 0);
}

detectBursts(audio){
this.updateAudioWindow(audio);
let burstSizeP1 = _.sum(this.audioWindow1);
let burstSizeP2 = _.sum(this.audioWindow2);
console.log(burstSizeP1, burstSizeP2);

if(burstSizeP1 < this.config.burstThreshold){
burstSizeP1 = 0;
Expand Down Expand Up @@ -82,7 +82,7 @@ module.exports = class CongaShooting extends LightProgram {
}
gameOver(winner){
this.rope = {pos: this.center - this.segmentSize, length: 2 * this.segmentSize};
this.paintAll();
GlobalGame.game.score[winner] = GlobalGame.game.max();
}

drawFrame(draw, audio) {
Expand All @@ -98,10 +98,10 @@ module.exports = class CongaShooting extends LightProgram {
}

if (this.rope.pos == 0){
this.gameOver('P1');
this.gameOver(0);
}
if(this.rope.pos + this.rope.length == this.numberOfLeds){
this.gameOver('P2');
this.gameOver(1);
}
let baseColor = ColorUtils.HSVtoRGB(0, 0, this.explosionLevel/20);
for (let i = 0; i < this.numberOfLeds; i++) {
Expand Down
12 changes: 2 additions & 10 deletions server/src/light-programs/programs/congaShooting2.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,6 @@ module.exports = class CongaShooting extends LightProgram {
if(b.pos < 0 || b.pos > this.numberOfLeds) {
this.bulletsA = _.without(this.bulletsA, b);
GlobalGame.game.addPoint(0);

if(GlobalGame.game.score[0] === 10) {
GlobalGame.game.restart()
}
}
}

Expand All @@ -54,10 +50,6 @@ module.exports = class CongaShooting extends LightProgram {
if(b.pos < 0 || b.pos > this.numberOfLeds) {
this.bulletsB = _.without(this.bulletsB, b);
GlobalGame.game.addPoint(1);

if(GlobalGame.game.score[1] === 10) {
GlobalGame.game.restart()
}
}
}

Expand Down Expand Up @@ -97,12 +89,12 @@ module.exports = class CongaShooting extends LightProgram {
this.colors[i] = baseColor;
for(const b of this.bulletsA){
if (Math.abs(b.pos - i) < this.config.speed){
this.colors[i] = ColorUtils.HSVtoRGB(b.size/400, 1, 1);
this.colors[i] = GlobalGame.game.player1Color;
}
}
for(const b of this.bulletsB){
if (Math.abs(b.pos - i) < this.config.speed){
this.colors[i] = ColorUtils.HSVtoRGB(b.size/400+0.33, 1, 1);
this.colors[i] = GlobalGame.game.player2Color;
}
}
}
Expand Down