diff --git a/js/components/AvsWindow/index.js b/js/components/AvsWindow/index.js
index c48c79ae..b6e773eb 100644
--- a/js/components/AvsWindow/index.js
+++ b/js/components/AvsWindow/index.js
@@ -1,21 +1,74 @@
import React from "react";
-import GenWindow from "../GenWindow";
-import "../../../css/gen-window.css";
+import Webvs from "webvs";
+import DropTarget from "../DropTarget";
+import { genTextFromFileReference } from "../../fileUtils";
-const AvsWindow = () => (
- {}} windowId="AVS_WINDOW">
-
-
-);
+import jelloCubePreset from "./jelloCubePreset";
+
+class AvsWindow extends React.PureComponent {
+ constructor() {
+ super();
+ this._handleDrop = this._handleDrop.bind(this);
+ }
+ componentDidMount() {
+ const analyserNode = this.props.analyser;
+ const analyser = new Webvs.WebAudioAnalyser({
+ context: analyserNode.context
+ });
+ analyser.connectToNode(analyserNode);
+ this._webvsMain = new Webvs.Main({
+ canvas: this._canvasNode,
+ analyser: analyser
+ });
+ this._loadPreset(jelloCubePreset);
+ this._webvsMain.start();
+ }
+ componentWillUnmount() {
+ // TODO: Dispose?
+ }
+ componentDidUpdate() {
+ // TODO: Consider debouncing
+ this._webvsMain.notifyResize();
+ }
+ _loadPreset(preset) {
+ this._webvsMain.loadPreset(preset);
+ }
+ async _handleDrop(e) {
+ e.preventDefault();
+ const file = e.dataTransfer.files[0];
+ if (!file) {
+ return;
+ }
+ const json = await genTextFromFileReference(file);
+ let preset;
+ try {
+ preset = JSON.parse(json);
+ } catch (err) {
+ console.warn("Error parsing AVS preset JSON:", json);
+ }
+ this._loadPreset(preset);
+ }
+ render() {
+ return (
+
+
+ );
+ }
+}
export default AvsWindow;
diff --git a/js/components/AvsWindow/jelloCubePreset.js b/js/components/AvsWindow/jelloCubePreset.js
new file mode 100644
index 00000000..ccc0ef4c
--- /dev/null
+++ b/js/components/AvsWindow/jelloCubePreset.js
@@ -0,0 +1,188 @@
+const preset = {
+ name: "Jello Cube",
+ author: "Steven Wittens / UnConeD (http://acko.net)",
+ clearFrame: false,
+ components: [
+ {
+ type: "EffectList",
+ enabled: true,
+
+ components: [
+ {
+ type: "DynamicMovement",
+ noGrid: true,
+ compat: true,
+ code: {
+ perPixel: "d=sin(d*(1+d*sin(r*150)*.15))*.5+d*.5;r=r+.01;"
+ }
+ },
+ {
+ type: "EffectList",
+ enableOnBeat: true,
+ enableOnBeatFor: 1,
+ output: "MAXIMUM",
+ components: [
+ {
+ type: "BufferSave",
+ action: "RESTORE"
+ },
+ {
+ type: "ColorClip",
+ enabled: false,
+ mode: "ABOVE",
+ color: "#9F9F9F",
+ outColor: "#9F9F9F"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ type: "ColorMap",
+ output: "REPLACE",
+ key: "RED",
+ maps: [
+ {
+ enabled: true,
+ colors: [
+ { color: "#FFFFFF", position: 52 },
+ { color: "#0FA7F0", position: 130 },
+ { color: "#000000", position: 255 }
+ ]
+ }
+ ]
+ },
+ {
+ type: "EffectList",
+ clearFrame: true,
+ input: "IGNORE",
+ output: "SUBTRACTIVE1",
+ components: [
+ {
+ type: "SuperScope",
+ clone: 12,
+ blendMode: "ADDITIVE",
+ code: {
+ init: "dx=8;n=sqr(dx);id=2/dx;id1=1/(dx-1);",
+ onBeat: [
+ "rxt=(abs((getosc(.55,0,0)+getosc(.91,0,0))*2000)%628)*.01;",
+ "ryt=(abs((getosc(.12,0,0)+getosc(.41,0,0))*2000)%628)*.01;",
+ "rzt=(abs((getosc(.55,0,0)+getosc(.91,0,0))*2000)%628)*.01;"
+ ].join("\n"),
+ perFrame: [
+ "t=t-.05;",
+ "dt=sin(t)*sin(t*.411+1)*cos(sin(t*.117))*.5+3;",
+ "gx=-1;",
+ "gy=0;",
+ "rx=rx*.95+rxt*.05;",
+ "ry=ry*.95+ryt*.05;",
+ "rz=rz*.95+rzt*.05;",
+ "cx=cos(rx);",
+ "sx=sin(rx);",
+ "cy=cos(ry);",
+ "sy=sin(ry);",
+ "cz=cos(rz);",
+ "sz=sin(rz);",
+ "af=w/h;",
+ "p00=getosc(select(cid,0.41,0.41,0.49,0.49,0.36,0.36,0.41,0.41,0.41,0.41,0.67,0.67),0,0)*.35+p00*.65;",
+ "p10=getosc(select(cid,0.00,0.00,0.15,0.15,0.24,0.24,0.60,0.60,0.00,0.00,0.76,0.76),0,0)*.35+p10*.65;",
+ "p20=getosc(select(cid,0.92,0.92,0.84,0.84,0.20,0.20,0.74,0.74,0.92,0.92,0.37,0.37),0,0)*.35+p20*.65;",
+ "p30=getosc(select(cid,0.36,0.36,0.38,0.38,0.17,0.17,0.67,0.67,0.36,0.36,0.17,0.17),0,0)*.35+p30*.65;",
+ "p01=getosc(select(cid,0.60,0.60,0.26,0.26,0.19,0.19,0.06,0.06,0.06,0.06,0.97,0.97),0,0)*.35+p01*.65;",
+ "p11=getosc(select(cid,0.59,0.59,0.48,0.48,0.54,0.54,0.42,0.42,0.85,0.85,0.23,0.23),0,0)*.55+p11*.65;",
+ "p21=getosc(select(cid,0.77,0.77,0.11,0.11,0.41,0.41,0.56,0.56,0.39,0.39,0.30,0.30),0,0)*.55+p21*.65;",
+ "p31=getosc(select(cid,0.84,0.84,0.25,0.25,0.01,0.01,0.97,0.97,0.19,0.19,0.01,0.01),0,0)*.35+p31*.65;",
+ "p02=getosc(select(cid,0.74,0.74,0.19,0.19,0.33,0.33,0.49,0.49,0.49,0.49,0.70,0.70),0,0)*.35+p02*.65;",
+ "p12=getosc(select(cid,0.52,0.52,1.00,1.00,0.07,0.07,0.75,0.75,0.61,0.61,0.61,0.61),0,0)*.55+p12*.65;",
+ "p22=getosc(select(cid,0.13,0.13,0.75,0.75,0.27,0.27,0.83,0.83,0.73,0.73,0.43,0.43),0,0)*.55+p22*.65;",
+ "p32=getosc(select(cid,0.20,0.20,0.74,0.74,0.93,0.93,0.70,0.70,0.33,0.33,0.93,0.93),0,0)*.35+p32*.65;",
+ "p03=getosc(select(cid,0.67,0.67,0.20,0.20,0.38,0.38,0.49,0.49,0.49,0.49,0.20,0.20),0,0)*.35+p03*.65;",
+ "p13=getosc(select(cid,0.76,0.76,0.94,0.94,0.25,0.25,0.26,0.26,0.15,0.15,0.94,0.94),0,0)*.35+p13*.65;",
+ "p23=getosc(select(cid,0.37,0.37,0.35,0.35,0.74,0.74,0.19,0.19,0.84,0.84,0.35,0.35),0,0)*.35+p23*.65;",
+ "p33=getosc(select(cid,0.17,0.17,0.93,0.93,0.93,0.93,0.20,0.20,0.38,0.38,0.93,0.93),0,0)*.35+p33*.65;",
+ "cr=sin(hu)*.5+.9;",
+ "cg=sin(hu+2.09)*.5+.9;",
+ "cb=sin(hu+4.18)*.5+.9;",
+ "xo=sin(t*.741)*sin(t*.114)*cos(sin(t*.41));",
+ "yo=sin(t*.574)*sin(t*.319)*cos(sin(t*.33));"
+ ].join("\n"),
+ perPoint: [
+ "gy=if(equal(gx,dx-1),gy+1,gy);",
+ "gx=if(below(gx,dx-1),gx+1,0);",
+
+ "x1=select(cid%2, gx, gy)*id1;",
+ "y2=select(cid%2, gy, gx)*id1;",
+
+ "red=1-x1;",
+ "green=1-y2;",
+ "c1=sqr(red)*red*p00 + 3*sqr(red)*x1*p10 + 3*red*sqr(x1)*p20 + sqr(x1)*x1*p30;",
+ "c2=sqr(red)*red*p01 + 3*sqr(red)*x1*p11 + 3*red*sqr(x1)*p21 + sqr(x1)*x1*p31;",
+ "c3=sqr(red)*red*p02 + 3*sqr(red)*x1*p12 + 3*red*sqr(x1)*p22 + sqr(x1)*x1*p32;",
+ "c4=sqr(red)*red*p03 + 3*sqr(red)*x1*p13 + 3*red*sqr(x1)*p23 + sqr(x1)*x1*p33;",
+ "c1=3*(sqr(green)*green*c1 + 3*sqr(green)*y2*c2 + 3*green*sqr(y2)*c3 + sqr(y2)*y2*c4) + 1;",
+
+ "px=select(cid, x1*2-1, x1*2-1, x1*2-1, x1*2-1, 1, 1, -1, -1, x1*2-1, x1*2-1, x1*2-1, x1*2-1);",
+ "py=select(cid, y2*2-1, y2*2-1, y2*2-1, y2*2-1, x1*2-1, x1*2-1, x1*2-1, x1*2-1, -1, -1, 1, 1);",
+ "pz=select(cid, -1, -1, 1, 1, y2*2-1, y2*2-1, y2*2-1, y2*2-1, y2*2-1, y2*2-1, y2*2-1, y2*2-1);",
+
+ "px=px*c1;",
+ "py=py*c1;",
+ "pz=pz*c1;",
+
+ "x1=px*cz+py*sz;",
+ "py=px*sz-py*cz;",
+ "y2=py*cx+pz*sx+yo;",
+ "z2=py*sx-pz*cx;",
+ "x3=x1*cy+z2*sy+xo;",
+ "z2=x1*sy-z2*cy+dt;",
+ "x1=if(above(z2,.1),1/z2,0);",
+ "x=x3*x1;",
+ "y=y2*x1*af;",
+ "x1=bnot(equal(gx,0))*x1*2;",
+ "red=x1*.5;",
+ "green=x1*2;",
+ "blue=x1*2;"
+ ].join("\n")
+ }
+ },
+ {
+ type: "Convolution",
+ scale: 8,
+ kernel: [
+ 0,
+ 0,
+ 1,
+ 0,
+ 0,
+ 0,
+ 2,
+ 4,
+ 2,
+ 0,
+ 1,
+ 4,
+ 2,
+ 4,
+ 1,
+ 0,
+ 2,
+ 4,
+ 2,
+ 0,
+ 0,
+ 0,
+ 1,
+ 0,
+ 0
+ ]
+ },
+ {
+ type: "BufferSave",
+ action: "SAVE"
+ }
+ ]
+ }
+ ]
+};
+
+export default preset;
diff --git a/js/fileUtils.js b/js/fileUtils.js
index 899cc2f7..320ddded 100644
--- a/js/fileUtils.js
+++ b/js/fileUtils.js
@@ -73,6 +73,24 @@ export async function genArrayBufferFromFileReference(fileReference) {
});
}
+export async function genTextFromFileReference(fileReference) {
+ invariant(
+ fileReference != null,
+ "Attempted to get an ArrayBuffer without assing a fileReference"
+ );
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.onload = function(e) {
+ resolve(e.target.result);
+ };
+ reader.onerror = function(e) {
+ reject(e);
+ };
+
+ reader.readAsText(fileReference);
+ });
+}
+
export async function genArrayBufferFromUrl(url) {
return new Promise((resolve, reject) => {
const oReq = new XMLHttpRequest();
diff --git a/js/index.js b/js/index.js
index 8913fa15..415ced31 100644
--- a/js/index.js
+++ b/js/index.js
@@ -8,6 +8,7 @@ import visor from "../skins/Vizor1-01.wsz";
import xmms from "../skins/XMMS-Turquoise.wsz";
import zaxon from "../skins/ZaxonRemake1-0.wsz";
import green from "../skins/Green-Dimension-V2.wsz";
+import AvsWindow from "./components/AvsWindow";
import Webamp from "./webamp";
import {
STEP_MARQUEE,
@@ -138,6 +139,7 @@ Raven.context(() => {
}
],
enableHotkeys: true,
+ __extraWindows: [{ title: "Avs", Component: AvsWindow }],
__initialState: initialState,
__customMiddlewares: [analyticsMiddleware, ravenMiddleware]
});
diff --git a/js/media/index.js b/js/media/index.js
index 676bcca0..8770e57b 100644
--- a/js/media/index.js
+++ b/js/media/index.js
@@ -176,6 +176,7 @@ export default class Media {
_resetChannels() {
this._setChannels(null);
}
+
/* Properties */
duration() {
return this._source.getDuration();
diff --git a/package.json b/package.json
index ec7e814e..8b7275f2 100644
--- a/package.json
+++ b/package.json
@@ -98,6 +98,7 @@
"webpack": "^3.6.0",
"webpack-merge": "^4.1.2",
"webpack-pwa-manifest": "^3.6.2",
+ "webvs": "^3.0.0",
"winamp-eqf": "^1.0.0"
},
"jest": {
diff --git a/yarn.lock b/yarn.lock
index a678eb0d..14d224e3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2538,6 +2538,10 @@ eventemitter3@1.x.x:
version "1.2.0"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508"
+eventemitter3@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.0.1.tgz#4ce66c3fc5b5a6b9f2245e359e1938f1ab10f960"
+
events@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
@@ -4460,7 +4464,7 @@ locate-path@^2.0.0:
p-locate "^2.0.0"
path-exists "^3.0.0"
-lodash-es@^4.17.5:
+lodash-es@^4.17.5, lodash-es@^4.17.8:
version "4.17.8"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.8.tgz#6fa8c8c5d337481df0bdf1c0d899d42473121e45"
@@ -6725,6 +6729,10 @@ static-extend@^0.1.1:
define-property "^0.2.5"
object-copy "^0.1.0"
+stats.js@*:
+ version "0.17.0"
+ resolved "https://registry.yarnpkg.com/stats.js/-/stats.js-0.17.0.tgz#b1c3dc46d94498b578b7fd3985b81ace7131cc7d"
+
"statuses@>= 1.3.1 < 2":
version "1.4.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
@@ -7477,6 +7485,14 @@ websocket-extensions@>=0.1.1:
version "0.1.3"
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29"
+webvs@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/webvs/-/webvs-3.0.0.tgz#867d9015d2c203c6e79a177e7a48ec1076aeb01c"
+ dependencies:
+ eventemitter3 "^3.0.1"
+ lodash-es "^4.17.8"
+ stats.js "*"
+
whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz#57c235bc8657e914d24e1a397d3c82daee0a6ba3"