diff --git a/.gitignore b/.gitignore index 3c25e1e..dc9966e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ node_modules/ dist/ *.log +*.swp +*.swo diff --git a/examples/balls/index.ts b/examples/balls/index.ts index d75dcb8..bd6f00a 100644 --- a/examples/balls/index.ts +++ b/examples/balls/index.ts @@ -1,6 +1,5 @@ import { DAC } from '@laser-dac/core'; -import { Simulator } from '@laser-dac/simulator'; -import { EtherDream } from '@laser-dac/ether-dream'; +import { getDevices } from '@laser-dac/device-selector'; import { Scene, Rect } from '@laser-dac/draw'; import { Ball } from './Ball'; @@ -8,10 +7,7 @@ const NUMBER_OF_BALLS = 4; (async () => { const dac = new DAC(); - dac.use(new Simulator()); - if (process.env.DEVICE) { - dac.use(new EtherDream()); - } + dac.useAll(await getDevices()); await dac.start(); const balls: Ball[] = []; diff --git a/examples/font/index.ts b/examples/font/index.ts index 3c0cd46..a172261 100644 --- a/examples/font/index.ts +++ b/examples/font/index.ts @@ -1,6 +1,5 @@ import { DAC } from '@laser-dac/core'; -import { Simulator } from '@laser-dac/simulator'; -import { EtherDream } from '@laser-dac/ether-dream'; +import { getDevices } from '@laser-dac/device-selector'; import { Scene, HersheyFont, loadHersheyFont, Timeline } from '@laser-dac/draw'; import * as path from 'path'; @@ -38,10 +37,7 @@ const textAnimation = new Timeline({ (async () => { const dac = new DAC(); - dac.use(new Simulator()); - if (process.env.DEVICE) { - dac.use(new EtherDream()); - } + dac.useAll(await getDevices()); await dac.start(); const scene = new Scene(); diff --git a/examples/ilda-animation/index.ts b/examples/ilda-animation/index.ts index f97fcf8..648a36a 100644 --- a/examples/ilda-animation/index.ts +++ b/examples/ilda-animation/index.ts @@ -1,6 +1,5 @@ import { DAC } from '@laser-dac/core'; -import { Simulator } from '@laser-dac/simulator'; -import { EtherDream } from '@laser-dac/ether-dream'; +import { getDevices } from '@laser-dac/device-selector'; import { Scene, Ilda, loadIldaFile } from '@laser-dac/draw'; import * as path from 'path'; @@ -8,10 +7,7 @@ const boeing = loadIldaFile(path.resolve(__dirname, './boeing.ild')); (async () => { const dac = new DAC(); - dac.use(new Simulator()); - if (process.env.DEVICE) { - dac.use(new EtherDream()); - } + dac.useAll(await getDevices()); await dac.start(); const scene = new Scene(); diff --git a/examples/pong/renderer.ts b/examples/pong/renderer.ts index 81d3e97..a77e5bd 100644 --- a/examples/pong/renderer.ts +++ b/examples/pong/renderer.ts @@ -1,6 +1,5 @@ import { DAC } from '@laser-dac/core'; -import { Simulator } from '@laser-dac/simulator'; -import { EtherDream } from '@laser-dac/ether-dream'; +import { getDevices } from '@laser-dac/device-selector'; import { Scene, Rect } from '@laser-dac/draw'; import { Player } from './Player'; import { Ball } from './Ball'; @@ -27,10 +26,7 @@ export class Renderer { async start() { const dac = new DAC(); - dac.use(new Simulator()); - if (process.env.DEVICE) { - dac.use(new EtherDream()); - } + dac.useAll(await getDevices()); await dac.start(); const ball = new Ball({ diff --git a/examples/square-interactive/renderer.ts b/examples/square-interactive/renderer.ts index c71ddf1..495761c 100755 --- a/examples/square-interactive/renderer.ts +++ b/examples/square-interactive/renderer.ts @@ -1,6 +1,5 @@ import { DAC } from '@laser-dac/core'; -import { Simulator } from '@laser-dac/simulator'; -import { EtherDream } from '@laser-dac/ether-dream'; +import { getDevices } from '@laser-dac/device-selector'; import { Scene, Rect, loadIldaFile, Ilda } from '@laser-dac/draw'; import * as path from 'path'; @@ -48,10 +47,7 @@ export class Renderer { async start() { const dac = new DAC(); - dac.use(new Simulator()); - if (process.env.DEVICE) { - dac.use(new EtherDream()); - } + dac.useAll(await getDevices()); await dac.start(); const scene = new Scene(); diff --git a/examples/static-shapes/index.ts b/examples/static-shapes/index.ts index 5301ea3..b0ab64e 100644 --- a/examples/static-shapes/index.ts +++ b/examples/static-shapes/index.ts @@ -1,14 +1,10 @@ import { DAC } from '@laser-dac/core'; -import { Simulator } from '@laser-dac/simulator'; -import { Helios } from '@laser-dac/helios'; +import { getDevices } from '@laser-dac/device-selector'; import { Scene, Rect, Path, Line } from '@laser-dac/draw'; (async () => { const dac = new DAC(); - dac.use(new Simulator()); - if (process.env.DEVICE) { - dac.use(new Helios()); - } + dac.useAll(await getDevices()); await dac.start(); const scene = new Scene({ diff --git a/examples/svg-path/index.ts b/examples/svg-path/index.ts index bd71d1a..54df4fe 100644 --- a/examples/svg-path/index.ts +++ b/examples/svg-path/index.ts @@ -1,6 +1,5 @@ import { DAC } from '@laser-dac/core'; -import { Simulator } from '@laser-dac/simulator'; -import { EtherDream } from '@laser-dac/ether-dream'; +import { getDevices } from '@laser-dac/device-selector'; import { Scene, Svg, loadSvgFile } from '@laser-dac/draw'; import * as path from 'path'; @@ -8,10 +7,8 @@ const logoFile = loadSvgFile(path.resolve(__dirname, './logo.svg')); (async () => { const dac = new DAC(); - dac.use(new Simulator()); - if (process.env.DEVICE) { - dac.use(new EtherDream()); - } + dac.useAll(await getDevices()); + await dac.start(); const scene = new Scene({ diff --git a/packages/beyond/src/BeyondLib.ts b/packages/beyond/src/BeyondLib.ts index 7393a3e..405c82a 100644 --- a/packages/beyond/src/BeyondLib.ts +++ b/packages/beyond/src/BeyondLib.ts @@ -26,7 +26,10 @@ const libPath = path // dont'get placed inside the "app.asar" bundle, but instead get placed in a separate directory called "app.asar.unpacked" .replace('app.asar', 'app.asar.unpacked'); -const BeyondLib = ffi.Library(libPath, { +// Even Windows 64-bit is called win32 here. +const isSupported = process.platform === "win32"; + +const BeyondLib = isSupported && ffi.Library(libPath, { ldbCreate: ['int', []], ldbDestroy: ['int', []], ldbBeyondExeReady: ['int', []], diff --git a/packages/beyond/src/index.ts b/packages/beyond/src/index.ts index b107e9d..8f73305 100644 --- a/packages/beyond/src/index.ts +++ b/packages/beyond/src/index.ts @@ -13,6 +13,9 @@ export class Beyond extends Device { private started = false; async start() { + if (!this.isSupported()) { + return false; + } this.stop(); beyondLib.ldbCreate(); this.started = true; @@ -37,6 +40,10 @@ export class Beyond extends Device { } } + isSupported(): boolean { + return process.platform === "win32"; + } + private convertPoint(p: Point) { return { x: relativeToPosition(p.x), diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 142d35f..ac74c11 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -17,6 +17,10 @@ export abstract class Device { abstract start(): Promise; abstract stop(): void; abstract stream(scene: Scene, pointsRate: number, fps: number): void; + + isSupported(): boolean { + return true; + }; } export class DAC { @@ -26,6 +30,10 @@ export class DAC { this.devices.push(device); } + useAll(devices: Device[]) { + devices.forEach((device) => this.use(device)); + } + remove(device: Device) { const index = this.devices.indexOf(device); if (index) { diff --git a/packages/device-selector/README.md b/packages/device-selector/README.md new file mode 100644 index 0000000..5d7e972 --- /dev/null +++ b/packages/device-selector/README.md @@ -0,0 +1,35 @@ +# @laser-dac/device-selector + +This package is an easy adapter for multiple devices. + +``` +yarn add @laser-dac/device-selector +npm i @laser-dac/device-selector +``` + +## Usage + +```js +import { DAC } from '@laser-dac/core'; +import { getDevices } from '@laser-dac/device-selector'; + +const dac = new DAC(); +// Automatically select a supported device and a Simulator. +dac.useAll(await getDevices()); +const started = await dac.start(); +if (started) { + const pps = 30000; // points per second + const fps = 120; // frames per second + // draw a horizontal red line from left to right in the center + // @laser-dac/draw can help you with drawing points! + const scene = { + points: [ + { x: 0.1, y: 0.5, r: 1, g: 0, b: 0 }, + { x: 0.9, y: 0.5, r: 1, g: 0, b: 0 } + ] + }; + dac.stream(scene, pps, fps); +} +``` + +See for more usage info and examples the [Laser DAC project on GitHub](https://github.com/Volst/laser-dac). diff --git a/packages/device-selector/package.json b/packages/device-selector/package.json new file mode 100644 index 0000000..4d78c84 --- /dev/null +++ b/packages/device-selector/package.json @@ -0,0 +1,35 @@ +{ + "name": "@laser-dac/device-selector", + "version": "0.4.1", + "description": "Device-selecting adapter for laser-dac", + "license": "MIT", + "author": "Sterling Hirsh ", + "repository": "Volst/laser-dac", + "keywords": [ + "laser" + ], + "engines": { + "node": ">=8.0" + }, + "main": "dist/index.js", + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@laser-dac/core": "^0.4.1", + "@laser-dac/beyond": "^0.3.0", + "@laser-dac/easylase": "^0.2.0", + "@laser-dac/ether-dream": "^0.4.1", + "@laser-dac/helios": "^0.3.0", + "@laser-dac/laserdock": "^0.4.0", + "@laser-dac/simulator": "^0.3.3" + }, + "scripts": { + "build": "rm -rf dist && tsc -p tsconfig.build.json", + "watch": "tsc -p tsconfig.build.json --watch", + "prepublishOnly": "npm run -s build" + } +} diff --git a/packages/device-selector/src/index.ts b/packages/device-selector/src/index.ts new file mode 100644 index 0000000..f916c4d --- /dev/null +++ b/packages/device-selector/src/index.ts @@ -0,0 +1,52 @@ +import { Device } from '@laser-dac/core'; + +import { Simulator } from '@laser-dac/simulator'; + +import { Beyond } from '@laser-dac/beyond'; +import { Easylase } from '@laser-dac/easylase'; +import { EtherDream } from '@laser-dac/ether-dream'; +import { Helios } from '@laser-dac/helios'; +import { Laserdock } from '@laser-dac/laserdock'; + +const apis: Record = { + helios: Helios, + laserdock: Laserdock, + beyond: Beyond, + easylase: Easylase, + 'ether-dream': EtherDream // This one is a bit slower to check. +}; + +// Get the first DAC that works. +export async function autoSelect(): Promise { + console.log("Searching for DAC in " + Object.keys(apis).join(', ')); + for (const api in apis) { + const dac = new apis[api](); + if (await dac.start()) { + console.log("Auto Selected DAC " + api); + await dac.stop(); + return dac; + } + } + console.log("No DAC found"); + return null; +} + +// Get a Simulator and the first autodetected device. +// TODO: Get multiple devices instead of just the first? +export async function getDevices(): Promise { + const devices: Device[] = [new Simulator()]; + const requestedDevice = process.env.DEVICE; + + if (requestedDevice) { + if (requestedDevice in apis) { + devices.push(new apis[requestedDevice]()); + } else { + const autoSelected = await autoSelect(); + if (autoSelected) { + devices.push(autoSelected); + } + } + } + + return devices; +} diff --git a/packages/device-selector/tsconfig.build.json b/packages/device-selector/tsconfig.build.json new file mode 100644 index 0000000..5a2694a --- /dev/null +++ b/packages/device-selector/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src/index.ts"] +} diff --git a/packages/easylase/src/EasylaseLib.ts b/packages/easylase/src/EasylaseLib.ts index aabbc05..8de5bfc 100644 --- a/packages/easylase/src/EasylaseLib.ts +++ b/packages/easylase/src/EasylaseLib.ts @@ -37,7 +37,10 @@ const libPath = path // dont'get placed inside the "app.asar" bundle, but instead get placed in a separate directory called "app.asar.unpacked" .replace('app.asar', 'app.asar.unpacked'); -const EasylaseLib = ffi.Library(libPath, { +// Even Windows 64-bit is called win32 here. +const isSupported = process.platform === "win32"; + +const EasylaseLib = isSupported && ffi.Library(libPath, { jmLaserEnumerateDevices: ['int', []], jmLaserStopOutput: ['int', ['int']], jmLaserCloseDevice: ['int', ['int']], diff --git a/packages/easylase/src/index.ts b/packages/easylase/src/index.ts index bdb82ee..b0ecb30 100644 --- a/packages/easylase/src/index.ts +++ b/packages/easylase/src/index.ts @@ -7,6 +7,10 @@ export class Easylase extends Device { deviceHandle?: number; async start() { + if (!this.isSupported()) { + return false; + } + this.stop(); const cards = easylaseLib.enumerateDevices(); if (cards) { @@ -32,6 +36,10 @@ export class Easylase extends Device { } } + isSupported(): boolean { + return process.platform === "win32"; + } + private convertPoint(p: Point) { return { x: relativeToPosition(p.x),