@neurodevs/node-lsl 16.1.0 → 17.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/__tests__/AbstractPackageTest.d.ts +3 -1
- package/build/__tests__/AbstractPackageTest.js +3 -7
- package/build/__tests__/AbstractPackageTest.js.map +1 -1
- package/build/__tests__/impl/LslStreamInfo.test.d.ts +3 -2
- package/build/__tests__/impl/LslStreamInfo.test.js +19 -12
- package/build/__tests__/impl/LslStreamInfo.test.js.map +1 -1
- package/build/__tests__/impl/LslStreamInlet.test.d.ts +1 -3
- package/build/__tests__/impl/LslStreamInlet.test.js +6 -27
- package/build/__tests__/impl/LslStreamInlet.test.js.map +1 -1
- package/build/__tests__/impl/LslWebSocketBridge.test.js +10 -16
- package/build/__tests__/impl/LslWebSocketBridge.test.js.map +1 -1
- package/build/impl/LslStreamInfo.d.ts +5 -8
- package/build/impl/LslStreamInfo.js +13 -13
- package/build/impl/LslStreamInfo.js.map +1 -1
- package/build/impl/LslStreamInlet.d.ts +6 -17
- package/build/impl/LslStreamInlet.js +5 -21
- package/build/impl/LslStreamInlet.js.map +1 -1
- package/build/impl/LslStreamOutlet.d.ts +3 -3
- package/build/impl/LslStreamOutlet.js +5 -8
- package/build/impl/LslStreamOutlet.js.map +1 -1
- package/build/impl/LslWebSocketBridge.d.ts +3 -8
- package/build/impl/LslWebSocketBridge.js +3 -3
- package/build/impl/LslWebSocketBridge.js.map +1 -1
- package/build/scripts/runLslStreamOutlet.js +15 -10
- package/build/scripts/runLslStreamOutlet.js.map +1 -1
- package/build/scripts/runOutletToInlet.js +2 -7
- package/build/scripts/runOutletToInlet.js.map +1 -1
- package/build/testDoubles/Liblsl/FakeLiblsl.d.ts +1 -1
- package/build/testDoubles/Liblsl/FakeLiblsl.js +2 -2
- package/build/testDoubles/Liblsl/FakeLiblsl.js.map +1 -1
- package/build/testDoubles/StreamInfo/FakeStreamInfo.d.ts +4 -4
- package/build/testDoubles/StreamInfo/FakeStreamInfo.js +4 -4
- package/build/testDoubles/StreamInfo/FakeStreamInfo.js.map +1 -1
- package/build/testDoubles/StreamInfo/SpyStreamInfo.d.ts +0 -1
- package/build/testDoubles/StreamInfo/SpyStreamInfo.js +0 -3
- package/build/testDoubles/StreamInfo/SpyStreamInfo.js.map +1 -1
- package/build/testDoubles/StreamInlet/FakeStreamInlet.d.ts +3 -3
- package/build/testDoubles/StreamInlet/FakeStreamInlet.js.map +1 -1
- package/build/testDoubles/StreamInlet/SpyStreamInlet.d.ts +2 -3
- package/build/testDoubles/StreamInlet/SpyStreamInlet.js +0 -3
- package/build/testDoubles/StreamInlet/SpyStreamInlet.js.map +1 -1
- package/build/testDoubles/StreamOutlet/FakeStreamOutlet.d.ts +1 -0
- package/build/testDoubles/StreamOutlet/FakeStreamOutlet.js +3 -0
- package/build/testDoubles/StreamOutlet/FakeStreamOutlet.js.map +1 -1
- package/package.json +11 -11
- package/src/__tests__/AbstractPackageTest.ts +3 -7
- package/src/__tests__/impl/LslStreamInfo.test.ts +22 -11
- package/src/__tests__/impl/LslStreamInlet.test.ts +9 -34
- package/src/__tests__/impl/LslWebSocketBridge.test.ts +11 -25
- package/src/impl/LslStreamInfo.ts +24 -23
- package/src/impl/LslStreamInlet.ts +15 -46
- package/src/impl/LslStreamOutlet.ts +6 -9
- package/src/impl/LslWebSocketBridge.ts +6 -12
- package/src/scripts/runLslStreamOutlet.ts +16 -10
- package/src/scripts/runOutletToInlet.ts +7 -9
- package/src/testDoubles/Liblsl/FakeLiblsl.ts +2 -2
- package/src/testDoubles/StreamInfo/FakeStreamInfo.ts +6 -6
- package/src/testDoubles/StreamInfo/SpyStreamInfo.ts +0 -4
- package/src/testDoubles/StreamInlet/FakeStreamInlet.ts +6 -3
- package/src/testDoubles/StreamInlet/SpyStreamInlet.ts +2 -6
- package/src/testDoubles/StreamOutlet/FakeStreamOutlet.ts +4 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neurodevs/node-lsl",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "17.1.0",
|
|
4
4
|
"description": "Lab Streaming Layer (LSL) bindings for Node",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@neurodevs/generate-id": "^1.0.9",
|
|
48
|
-
"ffi-rs": "^1.3.
|
|
49
|
-
"node-gyp": "^
|
|
48
|
+
"ffi-rs": "^1.3.1",
|
|
49
|
+
"node-gyp": "^12.1.0",
|
|
50
50
|
"ref-array-napi": "^1.2.2",
|
|
51
51
|
"ref-napi": "^3.0.3",
|
|
52
52
|
"ws": "^8.18.3"
|
|
@@ -54,18 +54,18 @@
|
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@neurodevs/node-tdd": "^0.2.2",
|
|
56
56
|
"@types/ffi-napi": "^4.0.10",
|
|
57
|
-
"@types/node": "^
|
|
57
|
+
"@types/node": "^24.10.1",
|
|
58
58
|
"@types/ws": "^8.18.1",
|
|
59
59
|
"chokidar-cli": "^3.0.0",
|
|
60
|
-
"concurrently": "^9.1
|
|
61
|
-
"eslint": "^9.
|
|
60
|
+
"concurrently": "^9.2.1",
|
|
61
|
+
"eslint": "^9.39.1",
|
|
62
62
|
"eslint-config-spruce": "^11.2.26",
|
|
63
|
-
"jest": "^
|
|
64
|
-
"jest-circus": "^
|
|
65
|
-
"prettier": "^3.
|
|
63
|
+
"jest": "^30.2.0",
|
|
64
|
+
"jest-circus": "^30.2.0",
|
|
65
|
+
"prettier": "^3.7.4",
|
|
66
66
|
"ts-node": "^10.9.2",
|
|
67
|
-
"tsc-watch": "^
|
|
68
|
-
"typescript": "^5.
|
|
67
|
+
"tsc-watch": "^7.2.0",
|
|
68
|
+
"typescript": "^5.9.3"
|
|
69
69
|
},
|
|
70
70
|
"jest": {
|
|
71
71
|
"testRunner": "jest-circus/runner",
|
|
@@ -78,6 +78,7 @@ export default class AbstractPackageTest extends AbstractModuleTest {
|
|
|
78
78
|
FakeWebSocketServer.resetTestDouble()
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
protected static readonly info = new FakeStreamInfo()
|
|
81
82
|
protected static readonly name_ = this.generateId()
|
|
82
83
|
protected static readonly type = this.generateId()
|
|
83
84
|
protected static readonly sourceId = this.generateId()
|
|
@@ -97,19 +98,14 @@ export default class AbstractPackageTest extends AbstractModuleTest {
|
|
|
97
98
|
protected static readonly sampleRateHz = Math.floor(Math.random() * 100)
|
|
98
99
|
protected static readonly maxBufferedMs = Math.floor(Math.random() * 1000)
|
|
99
100
|
|
|
100
|
-
protected static LslStreamInlet(
|
|
101
|
+
protected static async LslStreamInlet(
|
|
101
102
|
options: Partial<StreamInletOptions>,
|
|
102
103
|
onData: OnDataCallback
|
|
103
104
|
) {
|
|
104
105
|
const defaultOptions = {
|
|
105
|
-
|
|
106
|
-
channelNames: this.channelNames,
|
|
107
|
-
channelFormat: 'float32',
|
|
106
|
+
info: this.info,
|
|
108
107
|
chunkSize: this.chunkSize,
|
|
109
108
|
maxBufferedMs: this.maxBufferedMs,
|
|
110
|
-
name: this.name_,
|
|
111
|
-
type: this.type,
|
|
112
|
-
sourceId: this.sourceId,
|
|
113
109
|
...options,
|
|
114
110
|
} as StreamInletOptions
|
|
115
111
|
|
|
@@ -196,7 +196,7 @@ export default class LslStreamInfoTest extends AbstractPackageTest {
|
|
|
196
196
|
assert.isEqualDeep(
|
|
197
197
|
this.fakeLiblsl.lastAppendChannelsToStreamInfoOptions,
|
|
198
198
|
{
|
|
199
|
-
info: this.instance.
|
|
199
|
+
info: this.instance.boundInfo,
|
|
200
200
|
channels: this.channelNames.map((label: string) => ({
|
|
201
201
|
label,
|
|
202
202
|
units: this.units,
|
|
@@ -213,15 +213,15 @@ export default class LslStreamInfoTest extends AbstractPackageTest {
|
|
|
213
213
|
|
|
214
214
|
assert.isEqualDeep(
|
|
215
215
|
this.fakeLiblsl.lastDestroyStreamInfoOptions?.info,
|
|
216
|
-
this.instance.
|
|
216
|
+
this.instance.boundInfo,
|
|
217
217
|
'Should have called destroyStreamInfo!'
|
|
218
218
|
)
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
@test()
|
|
222
|
-
protected static async
|
|
222
|
+
protected static async doesNotCreateNewStreamInfoIfPassedBoundInfo() {
|
|
223
223
|
this.LslStreamInfo({
|
|
224
|
-
|
|
224
|
+
boundInfo: this.instance.boundInfo,
|
|
225
225
|
})
|
|
226
226
|
|
|
227
227
|
assert.isEqual(
|
|
@@ -232,22 +232,33 @@ export default class LslStreamInfoTest extends AbstractPackageTest {
|
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
@test()
|
|
235
|
-
protected static async
|
|
235
|
+
protected static async setsBoundInfoWhenPassed() {
|
|
236
236
|
const instance = this.LslStreamInfo({
|
|
237
|
-
|
|
237
|
+
boundInfo: this.instance.boundInfo,
|
|
238
238
|
})
|
|
239
239
|
|
|
240
240
|
assert.isEqualDeep(
|
|
241
|
-
instance.
|
|
242
|
-
this.instance.
|
|
241
|
+
instance.boundInfo,
|
|
242
|
+
this.instance.boundInfo,
|
|
243
|
+
'Should have set bound stream info!'
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
@test()
|
|
248
|
+
protected static async cachesInfoAttributesByBoundInfo() {
|
|
249
|
+
const cached = LslStreamInfo.From(this.instance.boundInfo)
|
|
250
|
+
|
|
251
|
+
assert.isEqualDeep(
|
|
252
|
+
cached,
|
|
253
|
+
this.instance,
|
|
243
254
|
'Should have set bound stream info!'
|
|
244
255
|
)
|
|
245
256
|
}
|
|
246
257
|
|
|
247
258
|
private static readonly defaultOptions = {
|
|
248
|
-
name: this.
|
|
249
|
-
type: this.
|
|
250
|
-
sourceId: this.
|
|
259
|
+
name: this.name,
|
|
260
|
+
type: this.type,
|
|
261
|
+
sourceId: this.sourceId,
|
|
251
262
|
channelNames: this.channelNames,
|
|
252
263
|
channelFormat: 'float32',
|
|
253
264
|
sampleRateHz: 100 * Math.random(),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { test, assert } from '@neurodevs/node-tdd'
|
|
2
|
+
|
|
2
3
|
import { StreamInletOptions } from '../../impl/LslStreamInlet.js'
|
|
3
|
-
import FakeStreamInfo from '../../testDoubles/StreamInfo/FakeStreamInfo.js'
|
|
4
4
|
import { SpyStreamInlet } from '../../testDoubles/StreamInlet/SpyStreamInlet.js'
|
|
5
5
|
import AbstractPackageTest from '../AbstractPackageTest.js'
|
|
6
6
|
|
|
@@ -21,7 +21,7 @@ export default class LslStreamInletTest extends AbstractPackageTest {
|
|
|
21
21
|
|
|
22
22
|
this.callsToOnData = []
|
|
23
23
|
|
|
24
|
-
this.instance = this.LslStreamInlet()
|
|
24
|
+
this.instance = await this.LslStreamInlet()
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
@test()
|
|
@@ -29,33 +29,6 @@ export default class LslStreamInletTest extends AbstractPackageTest {
|
|
|
29
29
|
assert.isTruthy(this.instance, 'Failed to create instance!')
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
@test()
|
|
33
|
-
protected static async createsStreamInfoWithExpectedOptions() {
|
|
34
|
-
assert.isEqualDeep(
|
|
35
|
-
FakeStreamInfo.callsToConstructor[0],
|
|
36
|
-
{
|
|
37
|
-
channelNames: this.channelNames,
|
|
38
|
-
channelFormat: 'float32',
|
|
39
|
-
sampleRateHz: 0,
|
|
40
|
-
name: this.name_,
|
|
41
|
-
type: this.type,
|
|
42
|
-
sourceId: this.sourceId,
|
|
43
|
-
},
|
|
44
|
-
'Stream info should have expected options!'
|
|
45
|
-
)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
@test()
|
|
49
|
-
protected static async uniqueNameHasSetPrefix() {
|
|
50
|
-
const instance = this.LslStreamInlet({ name: undefined })
|
|
51
|
-
|
|
52
|
-
assert.doesInclude(
|
|
53
|
-
instance.getName(),
|
|
54
|
-
'lsl-inlet-',
|
|
55
|
-
'Name should have set prefix!'
|
|
56
|
-
)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
32
|
@test()
|
|
60
33
|
protected static async callsBindingsToCreateStreamInlet() {
|
|
61
34
|
const fakeInfo = this.instance.getStreamInfo()
|
|
@@ -65,7 +38,7 @@ export default class LslStreamInletTest extends AbstractPackageTest {
|
|
|
65
38
|
assert.isEqualDeep(
|
|
66
39
|
this.fakeLiblsl.lastCreateInletOptions,
|
|
67
40
|
{
|
|
68
|
-
info: fakeInfo.
|
|
41
|
+
info: fakeInfo.boundInfo,
|
|
69
42
|
maxBufferedMs: this.maxBufferedMs,
|
|
70
43
|
},
|
|
71
44
|
'Should have called createInlet!'
|
|
@@ -282,7 +255,7 @@ export default class LslStreamInletTest extends AbstractPackageTest {
|
|
|
282
255
|
private static async runInletWithOptions(
|
|
283
256
|
options?: Partial<StreamInletOptions>
|
|
284
257
|
) {
|
|
285
|
-
const inlet = this.LslStreamInlet(options)
|
|
258
|
+
const inlet = await this.LslStreamInlet(options)
|
|
286
259
|
await this.startThenStop(inlet)
|
|
287
260
|
return inlet
|
|
288
261
|
}
|
|
@@ -317,12 +290,14 @@ export default class LslStreamInletTest extends AbstractPackageTest {
|
|
|
317
290
|
this.callsToOnData.push({ samples, timestamps })
|
|
318
291
|
}
|
|
319
292
|
|
|
320
|
-
protected static LslStreamInlet(
|
|
321
|
-
|
|
293
|
+
protected static async LslStreamInlet(
|
|
294
|
+
options?: Partial<StreamInletOptions>
|
|
295
|
+
) {
|
|
296
|
+
return (await AbstractPackageTest.LslStreamInlet(
|
|
322
297
|
{
|
|
323
298
|
...options,
|
|
324
299
|
},
|
|
325
300
|
this.onData
|
|
326
|
-
) as SpyStreamInlet
|
|
301
|
+
)) as SpyStreamInlet
|
|
327
302
|
}
|
|
328
303
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { randomInt } from 'crypto'
|
|
2
|
-
|
|
3
2
|
import { test, assert } from '@neurodevs/node-tdd'
|
|
4
3
|
|
|
5
4
|
import LslWebSocketBridge, {
|
|
6
5
|
WebSocketBridgeOptions,
|
|
7
6
|
} from '../../impl/LslWebSocketBridge.js'
|
|
7
|
+
import FakeStreamInfo from '../../testDoubles/StreamInfo/FakeStreamInfo.js'
|
|
8
8
|
import FakeStreamInlet from '../../testDoubles/StreamInlet/FakeStreamInlet.js'
|
|
9
9
|
import SpyLslWebSocketBridge from '../../testDoubles/WebSocketBridge/SpyLslWebSocketBridge.js'
|
|
10
10
|
import FakeWebSocket from '../../testDoubles/WebSockets/FakeWebSocket.js'
|
|
@@ -23,7 +23,8 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
23
23
|
this.setFakeWebSocketServer()
|
|
24
24
|
this.setSpyLslWebSocketBridge()
|
|
25
25
|
|
|
26
|
-
this.instance =
|
|
26
|
+
this.instance =
|
|
27
|
+
(await this.LslWebSocketBridge()) as SpyLslWebSocketBridge
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
@test()
|
|
@@ -33,25 +34,15 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
33
34
|
|
|
34
35
|
@test()
|
|
35
36
|
protected static async createsLslStreamInlet() {
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
chunkSize,
|
|
41
|
-
name,
|
|
42
|
-
type,
|
|
43
|
-
sourceId,
|
|
44
|
-
} = FakeStreamInlet.callsToConstructor[0]?.options ?? {}
|
|
37
|
+
const call = FakeStreamInlet.callsToConstructor[0]
|
|
38
|
+
|
|
39
|
+
const info = call?.info
|
|
40
|
+
const chunkSize = call?.options?.chunkSize
|
|
45
41
|
|
|
46
42
|
assert.isEqualDeep(
|
|
47
43
|
{
|
|
48
|
-
|
|
49
|
-
channelFormat,
|
|
50
|
-
sampleRateHz,
|
|
44
|
+
info,
|
|
51
45
|
chunkSize,
|
|
52
|
-
name,
|
|
53
|
-
type,
|
|
54
|
-
sourceId,
|
|
55
46
|
},
|
|
56
47
|
this.inletOptions
|
|
57
48
|
)
|
|
@@ -254,8 +245,8 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
254
245
|
|
|
255
246
|
@test()
|
|
256
247
|
protected static async throwsIfNeitherPortNorRemoteUrlsPassed() {
|
|
257
|
-
assert.
|
|
258
|
-
this.LslWebSocketBridge({
|
|
248
|
+
await assert.doesThrowAsync(async () => {
|
|
249
|
+
await this.LslWebSocketBridge({
|
|
259
250
|
listenPort: undefined,
|
|
260
251
|
connectUrls: undefined,
|
|
261
252
|
})
|
|
@@ -287,13 +278,8 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
|
|
|
287
278
|
private static readonly connectUrls = [this.generateId(), this.generateId()]
|
|
288
279
|
|
|
289
280
|
private static readonly inletOptions = {
|
|
290
|
-
|
|
291
|
-
channelFormat: 'float32' as const,
|
|
292
|
-
sampleRateHz: 100 * Math.random(),
|
|
281
|
+
info: new FakeStreamInfo(),
|
|
293
282
|
chunkSize: randomInt(1, 100),
|
|
294
|
-
name: this.generateId(),
|
|
295
|
-
type: this.generateId(),
|
|
296
|
-
sourceId: this.generateId(),
|
|
297
283
|
}
|
|
298
284
|
|
|
299
285
|
private static readonly baseOptions = {
|
|
@@ -7,6 +7,8 @@ import LiblslAdapter from './LiblslAdapter.js'
|
|
|
7
7
|
export default class LslStreamInfo implements StreamInfo {
|
|
8
8
|
public static Class?: StreamInfoConstructor
|
|
9
9
|
|
|
10
|
+
private static instanceCache = new Map<BoundStreamInfo, StreamInfo>()
|
|
11
|
+
|
|
10
12
|
public readonly name: string
|
|
11
13
|
public readonly type: string
|
|
12
14
|
public readonly sourceId: string
|
|
@@ -15,26 +17,20 @@ export default class LslStreamInfo implements StreamInfo {
|
|
|
15
17
|
public readonly channelFormat: ChannelFormat
|
|
16
18
|
public readonly sampleRateHz: number
|
|
17
19
|
public readonly units: string
|
|
18
|
-
|
|
19
|
-
protected boundInfo!: BoundStreamInfo
|
|
20
|
-
|
|
21
|
-
private readonly defaultName = `lsl-stream-info-${generateId()}`
|
|
22
|
-
private readonly defaultType = generateId()
|
|
23
|
-
private readonly defaultSourceId = generateId()
|
|
24
|
-
private readonly defaultUnits = 'N/A'
|
|
20
|
+
public readonly boundInfo: BoundStreamInfo
|
|
25
21
|
|
|
26
22
|
private lsl = LiblslAdapter.getInstance()
|
|
27
23
|
|
|
28
24
|
protected constructor(options: StreamInfoOptions) {
|
|
29
25
|
const {
|
|
30
|
-
name =
|
|
31
|
-
type =
|
|
32
|
-
sourceId =
|
|
26
|
+
name = `lsl-stream-info-${generateId()}`,
|
|
27
|
+
type = generateId(),
|
|
28
|
+
sourceId = generateId(),
|
|
33
29
|
channelNames,
|
|
34
30
|
channelFormat,
|
|
35
31
|
sampleRateHz,
|
|
36
|
-
units =
|
|
37
|
-
|
|
32
|
+
units = 'N/A',
|
|
33
|
+
boundInfo,
|
|
38
34
|
} = options
|
|
39
35
|
|
|
40
36
|
this.name = name
|
|
@@ -46,20 +42,29 @@ export default class LslStreamInfo implements StreamInfo {
|
|
|
46
42
|
this.sampleRateHz = sampleRateHz
|
|
47
43
|
this.units = units
|
|
48
44
|
|
|
49
|
-
if (!
|
|
50
|
-
this.createStreamInfo()
|
|
45
|
+
if (!boundInfo) {
|
|
46
|
+
this.boundInfo = this.createStreamInfo()
|
|
51
47
|
this.appendChannelsToStreamInfo()
|
|
52
48
|
} else {
|
|
53
|
-
this.boundInfo =
|
|
49
|
+
this.boundInfo = boundInfo
|
|
54
50
|
}
|
|
55
51
|
}
|
|
56
52
|
|
|
57
53
|
public static Create(options: StreamInfoOptions) {
|
|
58
|
-
|
|
54
|
+
const instance = new (this.Class ?? this)(options)
|
|
55
|
+
|
|
56
|
+
const { boundInfo: handle } = instance
|
|
57
|
+
this.instanceCache.set(handle, instance)
|
|
58
|
+
|
|
59
|
+
return instance
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public static From(handle: BoundStreamInfo) {
|
|
63
|
+
return this.instanceCache.get(handle)!
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
private createStreamInfo() {
|
|
62
|
-
|
|
67
|
+
return this.lsl.createStreamInfo({
|
|
63
68
|
name: this.name,
|
|
64
69
|
type: this.type,
|
|
65
70
|
sourceId: this.sourceId,
|
|
@@ -87,10 +92,6 @@ export default class LslStreamInfo implements StreamInfo {
|
|
|
87
92
|
public destroy() {
|
|
88
93
|
this.lsl.destroyStreamInfo({ info: this.boundInfo })
|
|
89
94
|
}
|
|
90
|
-
|
|
91
|
-
public get boundStreamInfo() {
|
|
92
|
-
return this.boundInfo
|
|
93
|
-
}
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
export interface StreamInfo {
|
|
@@ -103,7 +104,7 @@ export interface StreamInfo {
|
|
|
103
104
|
readonly channelCount: number
|
|
104
105
|
readonly channelFormat: ChannelFormat
|
|
105
106
|
readonly sampleRateHz: number
|
|
106
|
-
readonly
|
|
107
|
+
readonly boundInfo: BoundStreamInfo
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
type StreamInfoConstructor = new (options: StreamInfoOptions) => StreamInfo
|
|
@@ -116,5 +117,5 @@ export interface StreamInfoOptions {
|
|
|
116
117
|
type?: string
|
|
117
118
|
sourceId?: string
|
|
118
119
|
units?: string
|
|
119
|
-
|
|
120
|
+
boundInfo?: BoundStreamInfo
|
|
120
121
|
}
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import generateId from '@neurodevs/generate-id'
|
|
2
1
|
import { createPointer, DataType, JsExternal, unwrapPointer } from 'ffi-rs'
|
|
3
2
|
|
|
4
3
|
import handleError from '../handleError.js'
|
|
5
|
-
import { BoundInlet
|
|
4
|
+
import { BoundInlet } from './LiblslAdapter.js'
|
|
6
5
|
import LiblslAdapter from './LiblslAdapter.js'
|
|
7
|
-
import LslStreamInfo
|
|
8
|
-
StreamInfo,
|
|
9
|
-
StreamInfoOptions,
|
|
10
|
-
} from './LslStreamInfo.js'
|
|
6
|
+
import { StreamInfo } from './LslStreamInfo.js'
|
|
11
7
|
|
|
12
8
|
export default class LslStreamInlet implements StreamInlet {
|
|
13
9
|
public static Class?: StreamInletConstructor
|
|
@@ -15,13 +11,10 @@ export default class LslStreamInlet implements StreamInlet {
|
|
|
15
11
|
public isRunning = false
|
|
16
12
|
|
|
17
13
|
protected info: StreamInfo
|
|
18
|
-
protected name: string
|
|
19
|
-
private channelNames: string[]
|
|
20
14
|
private channelCount: number
|
|
21
15
|
private chunkSize: number
|
|
22
16
|
private maxBufferedMs: number
|
|
23
17
|
private timeoutMs: number
|
|
24
|
-
|
|
25
18
|
private onData: OnDataCallback
|
|
26
19
|
|
|
27
20
|
protected inlet!: BoundInlet
|
|
@@ -40,28 +33,19 @@ export default class LslStreamInlet implements StreamInlet {
|
|
|
40
33
|
private errorCodeBuffer!: Buffer<ArrayBuffer>
|
|
41
34
|
private errorCodeBufferPtr!: JsExternal
|
|
42
35
|
|
|
43
|
-
private readonly defaultName = `lsl-inlet-${generateId()}`
|
|
44
36
|
private readonly sixMinutesInMs = 360 * 1000
|
|
45
37
|
|
|
46
38
|
private lsl = LiblslAdapter.getInstance()
|
|
47
39
|
|
|
48
40
|
protected constructor(
|
|
49
41
|
info: StreamInfo,
|
|
50
|
-
options:
|
|
42
|
+
options: StreamInletConstructorOptions,
|
|
51
43
|
onData: OnDataCallback
|
|
52
44
|
) {
|
|
53
|
-
const {
|
|
54
|
-
name = this.defaultName,
|
|
55
|
-
channelNames,
|
|
56
|
-
chunkSize,
|
|
57
|
-
maxBufferedMs,
|
|
58
|
-
timeoutMs,
|
|
59
|
-
} = options ?? {}
|
|
45
|
+
const { chunkSize, maxBufferedMs, timeoutMs } = options ?? {}
|
|
60
46
|
|
|
61
47
|
this.info = info
|
|
62
|
-
this.
|
|
63
|
-
this.channelNames = channelNames
|
|
64
|
-
this.channelCount = this.channelNames.length
|
|
48
|
+
this.channelCount = this.info.channelCount
|
|
65
49
|
this.chunkSize = chunkSize
|
|
66
50
|
this.maxBufferedMs = maxBufferedMs ?? this.sixMinutesInMs
|
|
67
51
|
this.timeoutMs = timeoutMs ?? 0
|
|
@@ -71,13 +55,11 @@ export default class LslStreamInlet implements StreamInlet {
|
|
|
71
55
|
this.createBoundInlet()
|
|
72
56
|
}
|
|
73
57
|
|
|
74
|
-
public static Create(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const info = this.LslStreamInfo(infoOptions)
|
|
80
|
-
|
|
58
|
+
public static async Create(
|
|
59
|
+
options: StreamInletOptions,
|
|
60
|
+
onData: OnDataCallback
|
|
61
|
+
) {
|
|
62
|
+
const { info } = options
|
|
81
63
|
return new (this.Class ?? this)(info, options, onData)
|
|
82
64
|
}
|
|
83
65
|
|
|
@@ -91,15 +73,11 @@ export default class LslStreamInlet implements StreamInlet {
|
|
|
91
73
|
|
|
92
74
|
private createBoundInlet() {
|
|
93
75
|
this.inlet = this.lsl.createInlet({
|
|
94
|
-
info: this.
|
|
76
|
+
info: this.info.boundInfo,
|
|
95
77
|
maxBufferedMs: this.maxBufferedMs,
|
|
96
78
|
})
|
|
97
79
|
}
|
|
98
80
|
|
|
99
|
-
private get boundStreamInfo() {
|
|
100
|
-
return this.info.boundStreamInfo
|
|
101
|
-
}
|
|
102
|
-
|
|
103
81
|
public startPulling() {
|
|
104
82
|
this.isRunning = true
|
|
105
83
|
|
|
@@ -266,10 +244,6 @@ export default class LslStreamInlet implements StreamInlet {
|
|
|
266
244
|
private destroyBoundInlet() {
|
|
267
245
|
this.lsl.destroyInlet({ inlet: this.inlet })
|
|
268
246
|
}
|
|
269
|
-
|
|
270
|
-
private static LslStreamInfo(options: StreamInfoOptions) {
|
|
271
|
-
return LslStreamInfo.Create(options)
|
|
272
|
-
}
|
|
273
247
|
}
|
|
274
248
|
|
|
275
249
|
export interface StreamInlet {
|
|
@@ -282,24 +256,19 @@ export interface StreamInlet {
|
|
|
282
256
|
|
|
283
257
|
export type StreamInletConstructor = new (
|
|
284
258
|
info: StreamInfo,
|
|
285
|
-
options:
|
|
259
|
+
options: StreamInletConstructorOptions,
|
|
286
260
|
onData: OnDataCallback
|
|
287
261
|
) => StreamInlet
|
|
288
262
|
|
|
289
263
|
export interface StreamInletOptions {
|
|
290
|
-
|
|
291
|
-
channelFormat: ChannelFormat
|
|
292
|
-
sampleRateHz: number
|
|
264
|
+
info: StreamInfo
|
|
293
265
|
chunkSize: number
|
|
294
266
|
maxBufferedMs?: number
|
|
295
267
|
timeoutMs?: number
|
|
296
|
-
name?: string
|
|
297
|
-
type?: string
|
|
298
|
-
sourceId?: string
|
|
299
|
-
manufacturer?: string
|
|
300
|
-
units?: string
|
|
301
268
|
}
|
|
302
269
|
|
|
270
|
+
export type StreamInletConstructorOptions = Omit<StreamInletOptions, 'info'>
|
|
271
|
+
|
|
303
272
|
export type OnDataCallback = (
|
|
304
273
|
samples: Float32Array,
|
|
305
274
|
timestamps: Float64Array
|
|
@@ -21,6 +21,7 @@ import LslStreamInfo, {
|
|
|
21
21
|
export default class LslStreamOutlet implements StreamOutlet {
|
|
22
22
|
public static Class?: StreamOutletConstructor
|
|
23
23
|
|
|
24
|
+
public readonly info: StreamInfo
|
|
24
25
|
public readonly name: string
|
|
25
26
|
public readonly type: string
|
|
26
27
|
public readonly sourceId: string
|
|
@@ -33,7 +34,6 @@ export default class LslStreamOutlet implements StreamOutlet {
|
|
|
33
34
|
public readonly manufacturer: string = 'N/A'
|
|
34
35
|
public readonly units: string = 'N/A'
|
|
35
36
|
|
|
36
|
-
private info: StreamInfo
|
|
37
37
|
private boundOutlet!: BoundOutlet
|
|
38
38
|
private pushSampleMethod!: (options: unknown) => LslErrorCode
|
|
39
39
|
|
|
@@ -109,16 +109,12 @@ export default class LslStreamOutlet implements StreamOutlet {
|
|
|
109
109
|
|
|
110
110
|
private createStreamOutlet() {
|
|
111
111
|
this.boundOutlet = this.lsl.createOutlet({
|
|
112
|
-
info: this.
|
|
112
|
+
info: this.info.boundInfo,
|
|
113
113
|
chunkSize: this.chunkSize,
|
|
114
114
|
maxBufferedMs: this.maxBufferedMs,
|
|
115
115
|
})
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
private get boundStreamInfo() {
|
|
119
|
-
return this.info.boundStreamInfo
|
|
120
|
-
}
|
|
121
|
-
|
|
122
118
|
private setPushSampleMethod() {
|
|
123
119
|
this.pushSampleMethod = (
|
|
124
120
|
this.lsl[this.pushMethod] as (options: unknown) => LslErrorCode
|
|
@@ -146,12 +142,12 @@ export default class LslStreamOutlet implements StreamOutlet {
|
|
|
146
142
|
}
|
|
147
143
|
|
|
148
144
|
public destroy() {
|
|
149
|
-
this.
|
|
145
|
+
this.destroyBoundInfo()
|
|
150
146
|
this.destroyBoundOutlet()
|
|
151
147
|
}
|
|
152
148
|
|
|
153
|
-
private
|
|
154
|
-
this.lsl.destroyStreamInfo({ info: this.
|
|
149
|
+
private destroyBoundInfo() {
|
|
150
|
+
this.lsl.destroyStreamInfo({ info: this.info.boundInfo })
|
|
155
151
|
}
|
|
156
152
|
|
|
157
153
|
private destroyBoundOutlet() {
|
|
@@ -190,6 +186,7 @@ export default class LslStreamOutlet implements StreamOutlet {
|
|
|
190
186
|
export interface StreamOutlet {
|
|
191
187
|
pushSample(sample: LslSample): void
|
|
192
188
|
destroy(): void
|
|
189
|
+
readonly info: StreamInfo
|
|
193
190
|
readonly name: string
|
|
194
191
|
readonly type: string
|
|
195
192
|
readonly sourceId: string
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import WebSocket, { WebSocketServer } from 'ws'
|
|
2
|
-
import {
|
|
2
|
+
import { StreamInfo } from './LslStreamInfo.js'
|
|
3
3
|
import LslStreamInlet, {
|
|
4
4
|
StreamInlet,
|
|
5
5
|
StreamInletOptions,
|
|
@@ -26,14 +26,14 @@ export default class LslWebSocketBridge implements WebSocketBridge {
|
|
|
26
26
|
this.throwIfNoLocalServerOrRemoteSockets()
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
public static Create(options: WebSocketBridgeOptions) {
|
|
29
|
+
public static async Create(options: WebSocketBridgeOptions) {
|
|
30
30
|
const { listenPort, connectUrls, ...rest } = options
|
|
31
31
|
const inletOptions: StreamInletOptions = rest
|
|
32
32
|
|
|
33
33
|
const localServer = this.WebSocketServer(listenPort)
|
|
34
34
|
const remoteSockets = this.createSocketsFrom(connectUrls)
|
|
35
35
|
|
|
36
|
-
const inlet = this.LslStreamInlet(
|
|
36
|
+
const inlet = await this.LslStreamInlet(
|
|
37
37
|
inletOptions,
|
|
38
38
|
localServer,
|
|
39
39
|
remoteSockets
|
|
@@ -154,7 +154,7 @@ export default class LslWebSocketBridge implements WebSocketBridge {
|
|
|
154
154
|
return listenPort ? new this.WSS({ port: listenPort }) : undefined
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
private static LslStreamInlet(
|
|
157
|
+
private static async LslStreamInlet(
|
|
158
158
|
options: StreamInletOptions,
|
|
159
159
|
localServer?: WebSocketServer,
|
|
160
160
|
remoteSockets?: WebSocket[]
|
|
@@ -175,19 +175,13 @@ export type WebSocketBridgeConstructor = new (
|
|
|
175
175
|
) => WebSocketBridge
|
|
176
176
|
|
|
177
177
|
export interface WebSocketBridgeOptions {
|
|
178
|
-
|
|
179
|
-
channelFormat: ChannelFormat
|
|
180
|
-
sampleRateHz: number
|
|
178
|
+
info: StreamInfo
|
|
181
179
|
chunkSize: number
|
|
182
|
-
name?: string
|
|
183
|
-
type?: string
|
|
184
|
-
sourceId?: string
|
|
185
180
|
listenPort?: number
|
|
186
181
|
connectUrls?: string | string[]
|
|
187
182
|
}
|
|
188
183
|
|
|
189
|
-
export interface WebSocketBridgeConstructorOptions
|
|
190
|
-
extends WebSocketBridgeOptions {
|
|
184
|
+
export interface WebSocketBridgeConstructorOptions extends WebSocketBridgeOptions {
|
|
191
185
|
inlet: StreamInlet
|
|
192
186
|
localServer?: WebSocketServer
|
|
193
187
|
remoteSockets?: WebSocket[]
|