-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
14167b0
commit b987680
Showing
10 changed files
with
558 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { Mapping } from './mapping' | ||
import { DeviceType, OSCDeviceType } from '.' | ||
|
||
export interface MultiOSCOptions { | ||
connections: { | ||
connectionId: string | ||
host: string | ||
port: number | ||
type: OSCDeviceType | ||
}[] | ||
timeBetweenCommands: number // todo - move this to the individual timeline objects? | ||
} | ||
|
||
export interface MappingMultiOSC extends Mapping { | ||
device: DeviceType.MULTI_OSC | ||
connectionId: string | ||
} | ||
|
||
export enum TimelineContentTypeMultiOSC { | ||
OSC = 'osc', | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
133 changes: 133 additions & 0 deletions
133
packages/timeline-state-resolver/src/integrations/multiOsc/__tests__/multiOsc.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import { Conductor } from '../../../conductor' | ||
import { OSCMessageDevice } from '../../osc' | ||
import { | ||
Mappings, | ||
DeviceType, | ||
TimelineContentTypeOSC, | ||
OSCValueType, | ||
TimelineContentOSCMessage, | ||
OSCDeviceType, | ||
TSRTimelineObj, | ||
MappingMultiOSC, | ||
} from 'timeline-state-resolver-types' | ||
import { MockTime } from '../../../__tests__/mockTime' | ||
import { literal } from '../../../devices/device' | ||
import { ThreadedClass } from 'threadedclass' | ||
import { getMockCall } from '../../../__tests__/lib' | ||
|
||
// let nowActual = Date.now() | ||
describe('MultiOSC-Message', () => { | ||
const mockTime = new MockTime() | ||
beforeEach(() => { | ||
mockTime.init() | ||
}) | ||
test('MultiOSC message', async () => { | ||
const commandReceiver0: any = jest.fn(async () => { | ||
return Promise.resolve() | ||
}) | ||
const myLayerMapping0: MappingMultiOSC = { | ||
device: DeviceType.MULTI_OSC, | ||
deviceId: 'osc0', | ||
connectionId: 'osc0', | ||
} | ||
const myLayerMapping: Mappings = { | ||
myLayer0: myLayerMapping0, | ||
} | ||
|
||
const myConductor = new Conductor({ | ||
multiThreadedResolver: false, | ||
getCurrentTime: mockTime.getCurrentTime, | ||
}) | ||
myConductor.on('error', (e) => console.error(e)) | ||
await myConductor.init() | ||
await myConductor.addDevice('osc0', { | ||
type: DeviceType.MULTI_OSC, | ||
options: { | ||
connections: [ | ||
{ | ||
connectionId: 'osc0', | ||
host: '127.0.0.1', | ||
port: 80, | ||
type: OSCDeviceType.UDP, | ||
}, | ||
], | ||
timeBetweenCommands: 160, | ||
}, | ||
// @ts-ignore: @todo - why doesn't it let me do this? | ||
oscSenders: { osc0: commandReceiver0 }, | ||
}) | ||
myConductor.setTimelineAndMappings([], myLayerMapping) | ||
await mockTime.advanceTimeToTicks(10100) | ||
|
||
const deviceContainer = myConductor.getDevice('osc0') | ||
const device = deviceContainer!.device as ThreadedClass<OSCMessageDevice> | ||
|
||
// Check that no commands has been scheduled: | ||
expect(await device.queue).toHaveLength(0) | ||
|
||
myConductor.setTimelineAndMappings([ | ||
literal<TSRTimelineObj<TimelineContentOSCMessage>>({ | ||
id: 'obj0', | ||
enable: { | ||
start: mockTime.now + 1000, // in 1 second | ||
duration: 2000, | ||
}, | ||
layer: 'myLayer0', | ||
content: { | ||
deviceType: DeviceType.OSC, | ||
type: TimelineContentTypeOSC.OSC, | ||
|
||
path: '/test-path', | ||
values: [ | ||
{ | ||
type: OSCValueType.INT, | ||
value: 123, | ||
}, | ||
{ | ||
type: OSCValueType.FLOAT, | ||
value: 123.45, | ||
}, | ||
{ | ||
type: OSCValueType.STRING, | ||
value: 'abc', | ||
}, | ||
{ | ||
type: OSCValueType.BLOB, | ||
value: new Uint8Array([1, 3, 5]), | ||
}, | ||
], | ||
}, | ||
}), | ||
]) | ||
|
||
await mockTime.advanceTimeToTicks(10990) | ||
expect(commandReceiver0).toHaveBeenCalledTimes(0) | ||
await mockTime.advanceTimeToTicks(11100) | ||
|
||
expect(commandReceiver0).toHaveBeenCalledTimes(1) | ||
// console.log(commandReceiver0.mock.calls) | ||
expect(getMockCall(commandReceiver0, 0, 0)).toMatchObject({ | ||
address: '/test-path', | ||
args: [ | ||
{ | ||
type: OSCValueType.INT, | ||
value: 123, | ||
}, | ||
{ | ||
type: OSCValueType.FLOAT, | ||
value: 123.45, | ||
}, | ||
{ | ||
type: OSCValueType.STRING, | ||
value: 'abc', | ||
}, | ||
{ | ||
type: OSCValueType.BLOB, | ||
value: new Uint8Array([1, 3, 5]), | ||
}, | ||
], | ||
}) | ||
await mockTime.advanceTimeToTicks(16000) | ||
expect(commandReceiver0).toHaveBeenCalledTimes(1) | ||
}) | ||
}) |
92 changes: 92 additions & 0 deletions
92
packages/timeline-state-resolver/src/integrations/multiOsc/deviceConnection.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import * as osc from 'osc' | ||
import { EventEmitter } from 'events' | ||
import { MultiOSCOptions, OSCDeviceType } from 'timeline-state-resolver-types' | ||
|
||
export type OSCConnectionOptions = MultiOSCOptions['connections'][any] & { | ||
oscSender?: OSCSender | ||
} | ||
|
||
type OSCSender = (msg: osc.OscMessage, address?: string | undefined, port?: number | undefined) => void | ||
|
||
export class OSCConnection extends EventEmitter { | ||
connectionId: string | ||
host: string | ||
port: number | ||
|
||
private _oscClient: osc.UDPPort | osc.TCPSocketPort | ||
private _oscSender: OSCSender | ||
|
||
private _connected = false | ||
|
||
/** | ||
* Connnects to the OSC server. | ||
* @param host ip to connect to | ||
* @param port port the osc server is hosted on | ||
*/ | ||
async connect(options: OSCConnectionOptions): Promise<void> { | ||
this.connectionId = options.connectionId | ||
this.host = options.host | ||
this.port = options.port | ||
this._oscSender = options.oscSender || this._defaultOscSender.bind(this) | ||
|
||
if (options.type === OSCDeviceType.UDP) { | ||
this._oscClient = new osc.UDPPort({ | ||
localAddress: '0.0.0.0', | ||
localPort: 0, | ||
remoteAddress: this.host, | ||
remotePort: this.port, | ||
metadata: true, | ||
}) | ||
} else { | ||
this._oscClient = new osc.TCPSocketPort({ | ||
address: this.host, | ||
port: this.port, | ||
metadata: true, | ||
}) | ||
;(this._oscClient as osc.TCPSocketPort).socket.on('close', () => this.updateIsConnected(false)) | ||
;(this._oscClient as osc.TCPSocketPort).socket.on('connect', () => this.updateIsConnected(true)) | ||
} | ||
this._oscClient.on('error', (error: any) => this.emit('error', error)) | ||
// this._oscClient.on('message', (received: osc.OscMessage) => this.receiver(received)) | ||
|
||
return new Promise((resolve) => { | ||
this._oscClient.on('ready', () => { | ||
resolve() | ||
}) | ||
this._oscClient.open() | ||
}) | ||
} | ||
dispose() { | ||
this.updateIsConnected(false) | ||
this._oscClient.close() | ||
} | ||
|
||
private _defaultOscSender(msg: osc.OscMessage, address?: string | undefined, port?: number | undefined): void { | ||
this.emit('debug', 'sending ' + msg.address) | ||
this._oscClient.send(msg, address, port) | ||
} | ||
|
||
sendOsc(msg: osc.OscMessage, address?: string | undefined, port?: number | undefined): void { | ||
this._oscSender(msg, address, port) | ||
} | ||
|
||
disconnect() { | ||
this._oscClient.close() | ||
} | ||
|
||
get connected(): boolean { | ||
return this._connected | ||
} | ||
|
||
private updateIsConnected(connected: boolean) { | ||
if (this._connected !== connected) { | ||
this._connected = connected | ||
|
||
if (connected) { | ||
this.emit('connected') | ||
} else { | ||
this.emit('disconnected') | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.