@neurodevs/node-lsl 16.0.3 → 17.0.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 +1 -1
- package/build/__tests__/AbstractPackageTest.js +1 -3
- package/build/__tests__/AbstractPackageTest.js.map +1 -1
- package/build/__tests__/impl/LiblslAdapter.test.d.ts +13 -0
- package/build/__tests__/impl/LiblslAdapter.test.js +87 -1
- package/build/__tests__/impl/LiblslAdapter.test.js.map +1 -1
- package/build/__tests__/impl/LslStreamInfo.test.d.ts +3 -1
- package/build/__tests__/impl/LslStreamInfo.test.js +26 -9
- package/build/__tests__/impl/LslStreamInfo.test.js.map +1 -1
- package/build/__tests__/impl/LslStreamInlet.test.d.ts +3 -2
- package/build/__tests__/impl/LslStreamInlet.test.js +27 -21
- package/build/__tests__/impl/LslStreamInlet.test.js.map +1 -1
- package/build/__tests__/impl/LslWebSocketBridge.test.js +8 -16
- package/build/__tests__/impl/LslWebSocketBridge.test.js.map +1 -1
- package/build/impl/LiblslAdapter.d.ts +14 -4
- package/build/impl/LiblslAdapter.js +49 -5
- package/build/impl/LiblslAdapter.js.map +1 -1
- package/build/impl/LslStreamInfo.d.ts +4 -1
- package/build/impl/LslStreamInfo.js +18 -19
- package/build/impl/LslStreamInfo.js.map +1 -1
- package/build/impl/LslStreamInlet.d.ts +3 -13
- package/build/impl/LslStreamInlet.js +12 -15
- package/build/impl/LslStreamInlet.js.map +1 -1
- package/build/impl/LslWebSocketBridge.d.ts +2 -8
- package/build/impl/LslWebSocketBridge.js +2 -2
- package/build/impl/LslWebSocketBridge.js.map +1 -1
- package/build/scripts/runOutletToInlet.js +1 -1
- package/build/scripts/runOutletToInlet.js.map +1 -1
- package/build/testDoubles/Liblsl/FakeLiblsl.d.ts +8 -4
- package/build/testDoubles/Liblsl/FakeLiblsl.js +12 -4
- package/build/testDoubles/Liblsl/FakeLiblsl.js.map +1 -1
- package/build/testDoubles/StreamInfo/FakeStreamInfo.js +1 -1
- package/build/testDoubles/StreamInfo/FakeStreamInfo.js.map +1 -1
- package/build/testDoubles/StreamInlet/SpyStreamInlet.d.ts +0 -1
- package/build/testDoubles/StreamInlet/SpyStreamInlet.js +0 -3
- package/build/testDoubles/StreamInlet/SpyStreamInlet.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/AbstractPackageTest.ts +1 -5
- package/src/__tests__/impl/LiblslAdapter.test.ts +118 -2
- package/src/__tests__/impl/LslStreamInfo.test.ts +34 -9
- package/src/__tests__/impl/LslStreamInlet.test.ts +36 -26
- package/src/__tests__/impl/LslWebSocketBridge.test.ts +9 -24
- package/src/impl/LiblslAdapter.ts +84 -11
- package/src/impl/LslStreamInfo.ts +21 -28
- package/src/impl/LslStreamInlet.ts +20 -37
- package/src/impl/LslWebSocketBridge.ts +3 -9
- package/src/scripts/runOutletToInlet.ts +6 -3
- package/src/testDoubles/Liblsl/FakeLiblsl.ts +18 -6
- package/src/testDoubles/StreamInfo/FakeStreamInfo.ts +1 -1
- package/src/testDoubles/StreamInlet/SpyStreamInlet.ts +0 -4
|
@@ -7,7 +7,7 @@ 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
|
|
10
|
+
private static instanceCache = new Map<BoundStreamInfo, StreamInfo>()
|
|
11
11
|
|
|
12
12
|
public readonly name: string
|
|
13
13
|
public readonly type: string
|
|
@@ -36,6 +36,7 @@ export default class LslStreamInfo implements StreamInfo {
|
|
|
36
36
|
channelFormat,
|
|
37
37
|
sampleRateHz,
|
|
38
38
|
units = this.defaultUnits,
|
|
39
|
+
boundStreamInfo,
|
|
39
40
|
} = options
|
|
40
41
|
|
|
41
42
|
this.name = name
|
|
@@ -47,40 +48,27 @@ export default class LslStreamInfo implements StreamInfo {
|
|
|
47
48
|
this.sampleRateHz = sampleRateHz
|
|
48
49
|
this.units = units
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
if (!boundStreamInfo) {
|
|
52
|
+
this.createStreamInfo()
|
|
53
|
+
this.appendChannelsToStreamInfo()
|
|
54
|
+
} else {
|
|
55
|
+
this.boundInfo = boundStreamInfo
|
|
56
|
+
}
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
public static Create(options: StreamInfoOptions) {
|
|
55
|
-
const
|
|
56
|
-
name,
|
|
57
|
-
type,
|
|
58
|
-
sourceId,
|
|
59
|
-
channelNames,
|
|
60
|
-
channelFormat,
|
|
61
|
-
sampleRateHz,
|
|
62
|
-
} = options
|
|
63
|
-
|
|
64
|
-
const channelCount = channelNames.length
|
|
65
|
-
|
|
66
|
-
const key = JSON.stringify({
|
|
67
|
-
name,
|
|
68
|
-
type,
|
|
69
|
-
sourceId,
|
|
70
|
-
channelCount,
|
|
71
|
-
channelFormat,
|
|
72
|
-
sampleRateHz,
|
|
73
|
-
})
|
|
60
|
+
const instance = new (this.Class ?? this)(options)
|
|
74
61
|
|
|
75
|
-
|
|
62
|
+
const { boundStreamInfo: handle } = instance
|
|
63
|
+
this.instanceCache.set(handle, instance)
|
|
76
64
|
|
|
77
|
-
if (instance === undefined) {
|
|
78
|
-
instance = new (this.Class ?? this)(options)
|
|
79
|
-
this.instanceCache[key] = instance
|
|
80
|
-
}
|
|
81
65
|
return instance
|
|
82
66
|
}
|
|
83
67
|
|
|
68
|
+
public static From(handle: BoundStreamInfo) {
|
|
69
|
+
return this.instanceCache.get(handle)!
|
|
70
|
+
}
|
|
71
|
+
|
|
84
72
|
private createStreamInfo() {
|
|
85
73
|
this.boundInfo = this.lsl.createStreamInfo({
|
|
86
74
|
name: this.name,
|
|
@@ -114,6 +102,10 @@ export default class LslStreamInfo implements StreamInfo {
|
|
|
114
102
|
public get boundStreamInfo() {
|
|
115
103
|
return this.boundInfo
|
|
116
104
|
}
|
|
105
|
+
|
|
106
|
+
public set boundStreamInfo(boundInfo: BoundStreamInfo) {
|
|
107
|
+
this.boundInfo = boundInfo
|
|
108
|
+
}
|
|
117
109
|
}
|
|
118
110
|
|
|
119
111
|
export interface StreamInfo {
|
|
@@ -126,7 +118,7 @@ export interface StreamInfo {
|
|
|
126
118
|
readonly channelCount: number
|
|
127
119
|
readonly channelFormat: ChannelFormat
|
|
128
120
|
readonly sampleRateHz: number
|
|
129
|
-
|
|
121
|
+
boundStreamInfo: BoundStreamInfo
|
|
130
122
|
}
|
|
131
123
|
|
|
132
124
|
type StreamInfoConstructor = new (options: StreamInfoOptions) => StreamInfo
|
|
@@ -139,4 +131,5 @@ export interface StreamInfoOptions {
|
|
|
139
131
|
type?: string
|
|
140
132
|
sourceId?: string
|
|
141
133
|
units?: string
|
|
134
|
+
boundStreamInfo?: BoundStreamInfo
|
|
142
135
|
}
|
|
@@ -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 LslStreamInfo, { 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,7 +33,6 @@ 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()
|
|
@@ -50,18 +42,10 @@ export default class LslStreamInlet implements StreamInlet {
|
|
|
50
42
|
options: StreamInletOptions,
|
|
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,12 @@ 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(
|
|
80
|
-
|
|
58
|
+
public static async Create(
|
|
59
|
+
options: StreamInletOptions,
|
|
60
|
+
onData: OnDataCallback
|
|
61
|
+
) {
|
|
62
|
+
const { sourceId } = options
|
|
63
|
+
const info = await this.LslStreamInfo(sourceId)
|
|
81
64
|
return new (this.Class ?? this)(info, options, onData)
|
|
82
65
|
}
|
|
83
66
|
|
|
@@ -267,8 +250,15 @@ export default class LslStreamInlet implements StreamInlet {
|
|
|
267
250
|
this.lsl.destroyInlet({ inlet: this.inlet })
|
|
268
251
|
}
|
|
269
252
|
|
|
270
|
-
private static LslStreamInfo(
|
|
271
|
-
|
|
253
|
+
private static async LslStreamInfo(sourceId: string) {
|
|
254
|
+
const lsl = LiblslAdapter.getInstance()
|
|
255
|
+
|
|
256
|
+
const handles = await lsl.resolveByProp({
|
|
257
|
+
prop: 'source_id',
|
|
258
|
+
value: sourceId,
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
return LslStreamInfo.From(handles[0])
|
|
272
262
|
}
|
|
273
263
|
}
|
|
274
264
|
|
|
@@ -287,17 +277,10 @@ export type StreamInletConstructor = new (
|
|
|
287
277
|
) => StreamInlet
|
|
288
278
|
|
|
289
279
|
export interface StreamInletOptions {
|
|
290
|
-
|
|
291
|
-
channelFormat: ChannelFormat
|
|
292
|
-
sampleRateHz: number
|
|
280
|
+
sourceId: string
|
|
293
281
|
chunkSize: number
|
|
294
282
|
maxBufferedMs?: number
|
|
295
283
|
timeoutMs?: number
|
|
296
|
-
name?: string
|
|
297
|
-
type?: string
|
|
298
|
-
sourceId?: string
|
|
299
|
-
manufacturer?: string
|
|
300
|
-
units?: string
|
|
301
284
|
}
|
|
302
285
|
|
|
303
286
|
export type OnDataCallback = (
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import WebSocket, { WebSocketServer } from 'ws'
|
|
2
|
-
import { ChannelFormat } from './LiblslAdapter.js'
|
|
3
2
|
import LslStreamInlet, {
|
|
4
3
|
StreamInlet,
|
|
5
4
|
StreamInletOptions,
|
|
@@ -26,14 +25,14 @@ export default class LslWebSocketBridge implements WebSocketBridge {
|
|
|
26
25
|
this.throwIfNoLocalServerOrRemoteSockets()
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
public static Create(options: WebSocketBridgeOptions) {
|
|
28
|
+
public static async Create(options: WebSocketBridgeOptions) {
|
|
30
29
|
const { listenPort, connectUrls, ...rest } = options
|
|
31
30
|
const inletOptions: StreamInletOptions = rest
|
|
32
31
|
|
|
33
32
|
const localServer = this.WebSocketServer(listenPort)
|
|
34
33
|
const remoteSockets = this.createSocketsFrom(connectUrls)
|
|
35
34
|
|
|
36
|
-
const inlet = this.LslStreamInlet(
|
|
35
|
+
const inlet = await this.LslStreamInlet(
|
|
37
36
|
inletOptions,
|
|
38
37
|
localServer,
|
|
39
38
|
remoteSockets
|
|
@@ -175,13 +174,8 @@ export type WebSocketBridgeConstructor = new (
|
|
|
175
174
|
) => WebSocketBridge
|
|
176
175
|
|
|
177
176
|
export interface WebSocketBridgeOptions {
|
|
178
|
-
|
|
179
|
-
channelFormat: ChannelFormat
|
|
180
|
-
sampleRateHz: number
|
|
177
|
+
sourceId: string
|
|
181
178
|
chunkSize: number
|
|
182
|
-
name?: string
|
|
183
|
-
type?: string
|
|
184
|
-
sourceId?: string
|
|
185
179
|
listenPort?: number
|
|
186
180
|
connectUrls?: string | string[]
|
|
187
181
|
}
|
|
@@ -26,9 +26,12 @@ const inletOptions = {
|
|
|
26
26
|
chunkSize: 1,
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
const inlet = LslStreamInlet.Create(
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
const inlet = await LslStreamInlet.Create(
|
|
30
|
+
inletOptions,
|
|
31
|
+
(samples, timestamps) => {
|
|
32
|
+
console.log('Received samples:', samples, 'at timestamps:', timestamps)
|
|
33
|
+
}
|
|
34
|
+
)
|
|
32
35
|
|
|
33
36
|
console.log('Start pulling...')
|
|
34
37
|
inlet.startPulling()
|
|
@@ -16,9 +16,13 @@ import {
|
|
|
16
16
|
PullChunkOptions,
|
|
17
17
|
PullSampleOptions,
|
|
18
18
|
DestroyStreamInfoOptions,
|
|
19
|
+
ResolveByPropOptions,
|
|
20
|
+
DestroyOutletOptions,
|
|
19
21
|
} from 'impl/LiblslAdapter.js'
|
|
20
22
|
|
|
21
23
|
export default class FakeLiblsl implements Liblsl {
|
|
24
|
+
public static fakeStreamInfoHandles: BoundStreamInfo[] = []
|
|
25
|
+
|
|
22
26
|
public static fakeSamples: Float32Array[] = [
|
|
23
27
|
new Float32Array([1, 2, 3]),
|
|
24
28
|
new Float32Array([4, 5, 6]),
|
|
@@ -46,12 +50,14 @@ export default class FakeLiblsl implements Liblsl {
|
|
|
46
50
|
public streamInfo: BoundStreamInfo = {} as BoundStreamInfo
|
|
47
51
|
|
|
48
52
|
public lastCreateStreamInfoOptions?: CreateStreamInfoOptions
|
|
49
|
-
public lastDestroyStreamInfoOptions?: DestroyStreamInfoOptions
|
|
50
53
|
public lastAppendChannelsToStreamInfoOptions?: AppendChannelsToStreamInfoOptions
|
|
54
|
+
public lastDestroyStreamInfoOptions?: DestroyStreamInfoOptions
|
|
55
|
+
public lastResolveByPropOptions?: ResolveByPropOptions
|
|
51
56
|
public lastCreateOutletOptions?: CreateOutletOptions
|
|
52
57
|
public lastPushSampleFloatTimestampOptions?: PushSampleFloatTimestampOptions
|
|
53
58
|
public lastPushSampleStringTimestampOptions?: PushSampleStringTimestampOptions
|
|
54
59
|
public lastCreateInletOptions?: CreateInletOptions
|
|
60
|
+
public lastDestroyOutletOptions?: DestroyOutletOptions
|
|
55
61
|
public lastPullSampleOptions?: PullSampleOptions
|
|
56
62
|
public lastPullChunkOptions?: PullChunkOptions
|
|
57
63
|
public lastFlushInletOptions?: FlushInletOptions
|
|
@@ -72,16 +78,21 @@ export default class FakeLiblsl implements Liblsl {
|
|
|
72
78
|
return this.streamInfo
|
|
73
79
|
}
|
|
74
80
|
|
|
75
|
-
public destroyStreamInfo(options: DestroyStreamInfoOptions) {
|
|
76
|
-
this.lastDestroyStreamInfoOptions = options
|
|
77
|
-
}
|
|
78
|
-
|
|
79
81
|
public appendChannelsToStreamInfo(
|
|
80
82
|
options: AppendChannelsToStreamInfoOptions
|
|
81
83
|
) {
|
|
82
84
|
this.lastAppendChannelsToStreamInfoOptions = options
|
|
83
85
|
}
|
|
84
86
|
|
|
87
|
+
public destroyStreamInfo(options: DestroyStreamInfoOptions) {
|
|
88
|
+
this.lastDestroyStreamInfoOptions = options
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
public async resolveByProp(options: ResolveByPropOptions) {
|
|
92
|
+
this.lastResolveByPropOptions = options
|
|
93
|
+
return FakeLiblsl.fakeStreamInfoHandles as bigint[]
|
|
94
|
+
}
|
|
95
|
+
|
|
85
96
|
public pushSampleFloatTimestamp(options: PushSampleFloatTimestampOptions) {
|
|
86
97
|
this.lastPushSampleFloatTimestampOptions = options
|
|
87
98
|
return FakeLiblsl.fakeErrorCode
|
|
@@ -101,8 +112,9 @@ export default class FakeLiblsl implements Liblsl {
|
|
|
101
112
|
return this.outlet
|
|
102
113
|
}
|
|
103
114
|
|
|
104
|
-
public destroyOutlet() {
|
|
115
|
+
public destroyOutlet(options: DestroyOutletOptions) {
|
|
105
116
|
this.destroyOutletHitCount++
|
|
117
|
+
this.lastDestroyOutletOptions = options
|
|
106
118
|
}
|
|
107
119
|
|
|
108
120
|
public createInlet(options: CreateInletOptions) {
|
|
@@ -12,7 +12,7 @@ export default class FakeStreamInfo implements StreamInfo {
|
|
|
12
12
|
public name = generateId()
|
|
13
13
|
public type = generateId()
|
|
14
14
|
public sourceId = generateId()
|
|
15
|
-
public channelNames: string[] = [generateId(), generateId()]
|
|
15
|
+
public channelNames: string[] = [generateId(), generateId(), generateId()]
|
|
16
16
|
public channelFormat = 'float32' as const
|
|
17
17
|
public sampleRateHz = 0
|
|
18
18
|
public units = 'N/A'
|