@neurodevs/node-lsl 17.0.0 → 17.1.1

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.
Files changed (64) hide show
  1. package/build/__tests__/AbstractPackageTest.d.ts +2 -0
  2. package/build/__tests__/AbstractPackageTest.js +2 -6
  3. package/build/__tests__/AbstractPackageTest.js.map +1 -1
  4. package/build/__tests__/impl/LslStreamInfo.test.d.ts +3 -3
  5. package/build/__tests__/impl/LslStreamInfo.test.js +12 -12
  6. package/build/__tests__/impl/LslStreamInfo.test.js.map +1 -1
  7. package/build/__tests__/impl/LslStreamInlet.test.d.ts +2 -3
  8. package/build/__tests__/impl/LslStreamInlet.test.js +20 -27
  9. package/build/__tests__/impl/LslStreamInlet.test.js.map +1 -1
  10. package/build/__tests__/impl/LslWebSocketBridge.test.js +6 -3
  11. package/build/__tests__/impl/LslWebSocketBridge.test.js.map +1 -1
  12. package/build/impl/LiblslAdapter.d.ts +1 -6
  13. package/build/impl/LiblslAdapter.js +3 -3
  14. package/build/impl/LiblslAdapter.js.map +1 -1
  15. package/build/impl/LslStreamInfo.d.ts +3 -9
  16. package/build/impl/LslStreamInfo.js +6 -16
  17. package/build/impl/LslStreamInfo.js.map +1 -1
  18. package/build/impl/LslStreamInlet.d.ts +7 -5
  19. package/build/impl/LslStreamInlet.js +20 -24
  20. package/build/impl/LslStreamInlet.js.map +1 -1
  21. package/build/impl/LslStreamOutlet.d.ts +3 -3
  22. package/build/impl/LslStreamOutlet.js +5 -8
  23. package/build/impl/LslStreamOutlet.js.map +1 -1
  24. package/build/impl/LslWebSocketBridge.d.ts +2 -1
  25. package/build/impl/LslWebSocketBridge.js +1 -1
  26. package/build/impl/LslWebSocketBridge.js.map +1 -1
  27. package/build/scripts/runLslStreamOutlet.js +15 -10
  28. package/build/scripts/runLslStreamOutlet.js.map +1 -1
  29. package/build/scripts/runOutletToInlet.js +1 -6
  30. package/build/scripts/runOutletToInlet.js.map +1 -1
  31. package/build/testDoubles/Liblsl/FakeLiblsl.d.ts +1 -1
  32. package/build/testDoubles/Liblsl/FakeLiblsl.js +1 -1
  33. package/build/testDoubles/Liblsl/FakeLiblsl.js.map +1 -1
  34. package/build/testDoubles/StreamInfo/FakeStreamInfo.d.ts +4 -4
  35. package/build/testDoubles/StreamInfo/FakeStreamInfo.js +3 -3
  36. package/build/testDoubles/StreamInfo/FakeStreamInfo.js.map +1 -1
  37. package/build/testDoubles/StreamInfo/SpyStreamInfo.d.ts +0 -1
  38. package/build/testDoubles/StreamInfo/SpyStreamInfo.js +0 -3
  39. package/build/testDoubles/StreamInfo/SpyStreamInfo.js.map +1 -1
  40. package/build/testDoubles/StreamInlet/FakeStreamInlet.d.ts +3 -3
  41. package/build/testDoubles/StreamInlet/FakeStreamInlet.js.map +1 -1
  42. package/build/testDoubles/StreamInlet/SpyStreamInlet.d.ts +2 -2
  43. package/build/testDoubles/StreamInlet/SpyStreamInlet.js.map +1 -1
  44. package/build/testDoubles/StreamOutlet/FakeStreamOutlet.d.ts +1 -0
  45. package/build/testDoubles/StreamOutlet/FakeStreamOutlet.js +3 -0
  46. package/build/testDoubles/StreamOutlet/FakeStreamOutlet.js.map +1 -1
  47. package/package.json +12 -12
  48. package/src/__tests__/AbstractPackageTest.ts +4 -8
  49. package/src/__tests__/impl/LslStreamInfo.test.ts +10 -10
  50. package/src/__tests__/impl/LslStreamInlet.test.ts +26 -36
  51. package/src/__tests__/impl/LslWebSocketBridge.test.ts +7 -5
  52. package/src/impl/LiblslAdapter.ts +4 -9
  53. package/src/impl/LslStreamInfo.ts +13 -27
  54. package/src/impl/LslStreamInlet.ts +35 -39
  55. package/src/impl/LslStreamOutlet.ts +6 -9
  56. package/src/impl/LslWebSocketBridge.ts +4 -4
  57. package/src/scripts/runLslStreamOutlet.ts +16 -10
  58. package/src/scripts/runOutletToInlet.ts +1 -6
  59. package/src/testDoubles/Liblsl/FakeLiblsl.ts +1 -1
  60. package/src/testDoubles/StreamInfo/FakeStreamInfo.ts +5 -5
  61. package/src/testDoubles/StreamInfo/SpyStreamInfo.ts +0 -4
  62. package/src/testDoubles/StreamInlet/FakeStreamInlet.ts +6 -3
  63. package/src/testDoubles/StreamInlet/SpyStreamInlet.ts +2 -2
  64. 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": "17.0.0",
3
+ "version": "17.1.1",
4
4
  "description": "Lab Streaming Layer (LSL) bindings for Node",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -45,27 +45,27 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@neurodevs/generate-id": "^1.0.9",
48
- "ffi-rs": "^1.3.0",
49
- "node-gyp": "^10.2.0",
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"
53
53
  },
54
54
  "devDependencies": {
55
- "@neurodevs/node-tdd": "^0.2.2",
55
+ "@neurodevs/node-tdd": "^0.2.3",
56
56
  "@types/ffi-napi": "^4.0.10",
57
- "@types/node": "^22.9.0",
57
+ "@types/node": "^25.0.2",
58
58
  "@types/ws": "^8.18.1",
59
59
  "chokidar-cli": "^3.0.0",
60
- "concurrently": "^9.1.0",
61
- "eslint": "^9.15.0",
60
+ "concurrently": "^9.2.1",
61
+ "eslint": "^9.39.2",
62
62
  "eslint-config-spruce": "^11.2.26",
63
- "jest": "^29.7.0",
64
- "jest-circus": "^29.7.0",
65
- "prettier": "^3.3.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": "^6.2.1",
68
- "typescript": "^5.6.3"
67
+ "tsc-watch": "^7.2.0",
68
+ "typescript": "^5.9.3"
69
69
  },
70
70
  "jest": {
71
71
  "testRunner": "jest-circus/runner",
@@ -1,7 +1,6 @@
1
1
  import AbstractModuleTest from '@neurodevs/node-tdd'
2
- import WebSocket from 'ws'
2
+ import WebSocket, { Server } from 'ws'
3
3
 
4
- import { Server } from 'ws'
5
4
  import LiblslAdapter from '../impl/LiblslAdapter.js'
6
5
  import LslEventMarkerOutlet from '../impl/LslEventMarkerOutlet.js'
7
6
  import LslStreamInfo from '../impl/LslStreamInfo.js'
@@ -11,6 +10,7 @@ import LslStreamInlet, {
11
10
  } from '../impl/LslStreamInlet.js'
12
11
  import LslStreamOutlet from '../impl/LslStreamOutlet.js'
13
12
  import LslWebSocketBridge from '../impl/LslWebSocketBridge.js'
13
+
14
14
  import SpyEventMarkerOutlet from '../testDoubles/EventMarkerOutlet/SpyEventMarkerOutlet.js'
15
15
  import FakeLiblsl from '../testDoubles/Liblsl/FakeLiblsl.js'
16
16
  import FakeStreamInfo from '../testDoubles/StreamInfo/FakeStreamInfo.js'
@@ -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()
@@ -102,14 +103,9 @@ export default class AbstractPackageTest extends AbstractModuleTest {
102
103
  onData: OnDataCallback
103
104
  ) {
104
105
  const defaultOptions = {
105
- sampleRateHz: 0,
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.getBoundInfo(),
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.getBoundInfo(),
216
+ this.instance.boundInfo,
217
217
  'Should have called destroyStreamInfo!'
218
218
  )
219
219
  }
220
220
 
221
221
  @test()
222
- protected static async doesNotCreateNewStreamInfoIfPassedBoundStreamInfo() {
222
+ protected static async doesNotCreateNewStreamInfoIfPassedBoundInfo() {
223
223
  this.LslStreamInfo({
224
- boundStreamInfo: this.instance.getBoundInfo(),
224
+ boundInfo: this.instance.boundInfo,
225
225
  })
226
226
 
227
227
  assert.isEqual(
@@ -232,21 +232,21 @@ export default class LslStreamInfoTest extends AbstractPackageTest {
232
232
  }
233
233
 
234
234
  @test()
235
- protected static async setsBoundStreamInfoWhenPassed() {
235
+ protected static async setsBoundInfoWhenPassed() {
236
236
  const instance = this.LslStreamInfo({
237
- boundStreamInfo: this.instance.getBoundInfo(),
237
+ boundInfo: this.instance.boundInfo,
238
238
  })
239
239
 
240
240
  assert.isEqualDeep(
241
- instance.getBoundInfo(),
242
- this.instance.getBoundInfo(),
241
+ instance.boundInfo,
242
+ this.instance.boundInfo,
243
243
  'Should have set bound stream info!'
244
244
  )
245
245
  }
246
246
 
247
247
  @test()
248
- protected static async cachesInfoAttributesByBoundStreamInfo() {
249
- const cached = LslStreamInfo.From(this.instance.getBoundInfo())
248
+ protected static async cachesInfoAttributesByBoundInfo() {
249
+ const cached = LslStreamInfo.From(this.instance.boundInfo)
250
250
 
251
251
  assert.isEqualDeep(
252
252
  cached,
@@ -1,16 +1,11 @@
1
1
  import { test, assert } from '@neurodevs/node-tdd'
2
- import { BoundStreamInfo } from '../../impl/LiblslAdapter.js'
3
- import LslStreamInfo, { StreamInfo } from '../../impl/LslStreamInfo.js'
2
+
4
3
  import { StreamInletOptions } from '../../impl/LslStreamInlet.js'
5
- import FakeLiblsl from '../../testDoubles/Liblsl/FakeLiblsl.js'
6
- import FakeStreamInfo from '../../testDoubles/StreamInfo/FakeStreamInfo.js'
7
4
  import { SpyStreamInlet } from '../../testDoubles/StreamInlet/SpyStreamInlet.js'
8
5
  import AbstractPackageTest from '../AbstractPackageTest.js'
9
6
 
10
7
  export default class LslStreamInletTest extends AbstractPackageTest {
11
8
  private static instance: SpyStreamInlet
12
- private static outletInfo: StreamInfo
13
- private static passedHandle?: BoundStreamInfo
14
9
 
15
10
  private static callsToOnData: {
16
11
  samples: Float32Array
@@ -26,26 +21,6 @@ export default class LslStreamInletTest extends AbstractPackageTest {
26
21
 
27
22
  this.callsToOnData = []
28
23
 
29
- this.outletInfo = LslStreamInfo.Create({
30
- sourceId: this.sourceId,
31
- channelNames: [],
32
- channelFormat: 'float32',
33
- sampleRateHz: 0,
34
- })
35
-
36
- FakeStreamInfo.resetTestDouble()
37
-
38
- FakeLiblsl.fakeStreamInfoHandles = [
39
- this.outletInfo.boundStreamInfo as bigint,
40
- ]
41
-
42
- const From = LslStreamInfo.From.bind(LslStreamInfo)
43
-
44
- LslStreamInfo.From = (handle: BoundStreamInfo) => {
45
- this.passedHandle = handle
46
- return From(handle) as FakeStreamInfo
47
- }
48
-
49
24
  this.instance = await this.LslStreamInlet()
50
25
  }
51
26
 
@@ -54,15 +29,6 @@ export default class LslStreamInletTest extends AbstractPackageTest {
54
29
  assert.isTruthy(this.instance, 'Failed to create instance!')
55
30
  }
56
31
 
57
- @test()
58
- protected static async createsStreamInfoWithExpectedOptions() {
59
- assert.isEqualDeep(
60
- this.passedHandle,
61
- this.outletInfo.boundStreamInfo,
62
- 'Stream info should have expected options!'
63
- )
64
- }
65
-
66
32
  @test()
67
33
  protected static async callsBindingsToCreateStreamInlet() {
68
34
  const fakeInfo = this.instance.getStreamInfo()
@@ -72,7 +38,7 @@ export default class LslStreamInletTest extends AbstractPackageTest {
72
38
  assert.isEqualDeep(
73
39
  this.fakeLiblsl.lastCreateInletOptions,
74
40
  {
75
- info: fakeInfo.boundStreamInfo,
41
+ info: fakeInfo.boundInfo,
76
42
  maxBufferedMs: this.maxBufferedMs,
77
43
  },
78
44
  'Should have called createInlet!'
@@ -101,6 +67,30 @@ export default class LslStreamInletTest extends AbstractPackageTest {
101
67
  )
102
68
  }
103
69
 
70
+ @test()
71
+ protected static async destroyCallsStopIfIsRunning() {
72
+ this.instance.startPulling()
73
+ this.instance.destroy()
74
+
75
+ assert.isFalse(
76
+ this.instance.isRunning,
77
+ 'Did not stop inlet before destroying!'
78
+ )
79
+ }
80
+
81
+ @test()
82
+ protected static async destroyDoesNotCallStopIfNotRunning() {
83
+ let wasHit = false
84
+
85
+ this.instance.stopPulling = () => {
86
+ wasHit = true
87
+ }
88
+
89
+ this.instance.destroy()
90
+
91
+ assert.isFalse(wasHit, 'Stop should not have been called!')
92
+ }
93
+
104
94
  @test()
105
95
  protected static async exposesIsRunningFieldThatIsFalseAtFirst() {
106
96
  assert.isFalse(this.isRunning, 'isRunning should be false at first!')
@@ -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'
@@ -34,12 +34,14 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
34
34
 
35
35
  @test()
36
36
  protected static async createsLslStreamInlet() {
37
- const { sourceId, chunkSize } =
38
- FakeStreamInlet.callsToConstructor[0]?.options ?? {}
37
+ const call = FakeStreamInlet.callsToConstructor[0]
38
+
39
+ const info = call?.info
40
+ const chunkSize = call?.options?.chunkSize
39
41
 
40
42
  assert.isEqualDeep(
41
43
  {
42
- sourceId,
44
+ info,
43
45
  chunkSize,
44
46
  },
45
47
  this.inletOptions
@@ -276,7 +278,7 @@ export default class LslWebSocketBridgeTest extends AbstractPackageTest {
276
278
  private static readonly connectUrls = [this.generateId(), this.generateId()]
277
279
 
278
280
  private static readonly inletOptions = {
279
- sourceId: this.generateId(),
281
+ info: new FakeStreamInfo(),
280
282
  chunkSize: randomInt(1, 100),
281
283
  }
282
284
 
@@ -267,12 +267,12 @@ export default class LiblslAdapter implements Liblsl {
267
267
  ],
268
268
  paramsValue: [
269
269
  inlet,
270
- unwrapPointer([dataBufferPtr])[0],
271
- unwrapPointer([timestampBufferPtr])[0],
270
+ dataBufferPtr,
271
+ timestampBufferPtr,
272
272
  dataBufferElements,
273
273
  timestampBufferElements,
274
274
  timeout,
275
- unwrapPointer([errcodePtr])[0],
275
+ errcodePtr,
276
276
  ],
277
277
  })
278
278
  }
@@ -494,14 +494,9 @@ export interface PullSampleOptions {
494
494
  errcodePtr: JsExternal
495
495
  }
496
496
 
497
- export interface PullChunkOptions {
498
- inlet: BoundInlet
499
- dataBufferPtr: JsExternal
497
+ export interface PullChunkOptions extends PullSampleOptions {
500
498
  timestampBufferPtr: JsExternal
501
- dataBufferElements: number
502
499
  timestampBufferElements: number
503
- timeout: number
504
- errcodePtr: JsExternal
505
500
  }
506
501
 
507
502
  export interface FlushInletOptions {
@@ -17,26 +17,20 @@ export default class LslStreamInfo implements StreamInfo {
17
17
  public readonly channelFormat: ChannelFormat
18
18
  public readonly sampleRateHz: number
19
19
  public readonly units: string
20
-
21
- protected boundInfo!: BoundStreamInfo
22
-
23
- private readonly defaultName = `lsl-stream-info-${generateId()}`
24
- private readonly defaultType = generateId()
25
- private readonly defaultSourceId = generateId()
26
- private readonly defaultUnits = 'N/A'
20
+ public readonly boundInfo: BoundStreamInfo
27
21
 
28
22
  private lsl = LiblslAdapter.getInstance()
29
23
 
30
24
  protected constructor(options: StreamInfoOptions) {
31
25
  const {
32
- name = this.defaultName,
33
- type = this.defaultType,
34
- sourceId = this.defaultSourceId,
26
+ name = `lsl-stream-info-${generateId()}`,
27
+ type = generateId(),
28
+ sourceId = generateId(),
35
29
  channelNames,
36
30
  channelFormat,
37
31
  sampleRateHz,
38
- units = this.defaultUnits,
39
- boundStreamInfo,
32
+ units = 'N/A',
33
+ boundInfo,
40
34
  } = options
41
35
 
42
36
  this.name = name
@@ -48,18 +42,18 @@ export default class LslStreamInfo implements StreamInfo {
48
42
  this.sampleRateHz = sampleRateHz
49
43
  this.units = units
50
44
 
51
- if (!boundStreamInfo) {
52
- this.createStreamInfo()
45
+ if (!boundInfo) {
46
+ this.boundInfo = this.createStreamInfo()
53
47
  this.appendChannelsToStreamInfo()
54
48
  } else {
55
- this.boundInfo = boundStreamInfo
49
+ this.boundInfo = boundInfo
56
50
  }
57
51
  }
58
52
 
59
53
  public static Create(options: StreamInfoOptions) {
60
54
  const instance = new (this.Class ?? this)(options)
61
55
 
62
- const { boundStreamInfo: handle } = instance
56
+ const { boundInfo: handle } = instance
63
57
  this.instanceCache.set(handle, instance)
64
58
 
65
59
  return instance
@@ -70,7 +64,7 @@ export default class LslStreamInfo implements StreamInfo {
70
64
  }
71
65
 
72
66
  private createStreamInfo() {
73
- this.boundInfo = this.lsl.createStreamInfo({
67
+ return this.lsl.createStreamInfo({
74
68
  name: this.name,
75
69
  type: this.type,
76
70
  sourceId: this.sourceId,
@@ -98,14 +92,6 @@ export default class LslStreamInfo implements StreamInfo {
98
92
  public destroy() {
99
93
  this.lsl.destroyStreamInfo({ info: this.boundInfo })
100
94
  }
101
-
102
- public get boundStreamInfo() {
103
- return this.boundInfo
104
- }
105
-
106
- public set boundStreamInfo(boundInfo: BoundStreamInfo) {
107
- this.boundInfo = boundInfo
108
- }
109
95
  }
110
96
 
111
97
  export interface StreamInfo {
@@ -118,7 +104,7 @@ export interface StreamInfo {
118
104
  readonly channelCount: number
119
105
  readonly channelFormat: ChannelFormat
120
106
  readonly sampleRateHz: number
121
- boundStreamInfo: BoundStreamInfo
107
+ readonly boundInfo: BoundStreamInfo
122
108
  }
123
109
 
124
110
  type StreamInfoConstructor = new (options: StreamInfoOptions) => StreamInfo
@@ -131,5 +117,5 @@ export interface StreamInfoOptions {
131
117
  type?: string
132
118
  sourceId?: string
133
119
  units?: string
134
- boundStreamInfo?: BoundStreamInfo
120
+ boundInfo?: BoundStreamInfo
135
121
  }
@@ -3,7 +3,7 @@ import { createPointer, DataType, JsExternal, unwrapPointer } from 'ffi-rs'
3
3
  import handleError from '../handleError.js'
4
4
  import { BoundInlet } from './LiblslAdapter.js'
5
5
  import LiblslAdapter from './LiblslAdapter.js'
6
- import LslStreamInfo, { StreamInfo } from './LslStreamInfo.js'
6
+ import { StreamInfo } from './LslStreamInfo.js'
7
7
 
8
8
  export default class LslStreamInlet implements StreamInlet {
9
9
  public static Class?: StreamInletConstructor
@@ -25,12 +25,15 @@ export default class LslStreamInlet implements StreamInlet {
25
25
  }
26
26
 
27
27
  private dataBuffer!: Buffer<ArrayBuffer>
28
+ private wrappedDataBufferPtr!: JsExternal[]
28
29
  private dataBufferPtr!: JsExternal
29
30
 
30
31
  private timestampBuffer!: Buffer<ArrayBuffer>
32
+ private wrappedTimestampBufferPtr!: JsExternal[]
31
33
  private timestampBufferPtr!: JsExternal
32
34
 
33
35
  private errorCodeBuffer!: Buffer<ArrayBuffer>
36
+ private wrappedErrorCodeBufferPtr!: JsExternal[]
34
37
  private errorCodeBufferPtr!: JsExternal
35
38
 
36
39
  private readonly sixMinutesInMs = 360 * 1000
@@ -39,7 +42,7 @@ export default class LslStreamInlet implements StreamInlet {
39
42
 
40
43
  protected constructor(
41
44
  info: StreamInfo,
42
- options: StreamInletOptions,
45
+ options: StreamInletConstructorOptions,
43
46
  onData: OnDataCallback
44
47
  ) {
45
48
  const { chunkSize, maxBufferedMs, timeoutMs } = options ?? {}
@@ -59,8 +62,7 @@ export default class LslStreamInlet implements StreamInlet {
59
62
  options: StreamInletOptions,
60
63
  onData: OnDataCallback
61
64
  ) {
62
- const { sourceId } = options
63
- const info = await this.LslStreamInfo(sourceId)
65
+ const { info } = options
64
66
  return new (this.Class ?? this)(info, options, onData)
65
67
  }
66
68
 
@@ -74,15 +76,11 @@ export default class LslStreamInlet implements StreamInlet {
74
76
 
75
77
  private createBoundInlet() {
76
78
  this.inlet = this.lsl.createInlet({
77
- info: this.boundStreamInfo,
79
+ info: this.info.boundInfo,
78
80
  maxBufferedMs: this.maxBufferedMs,
79
81
  })
80
82
  }
81
83
 
82
- private get boundStreamInfo() {
83
- return this.info.boundStreamInfo
84
- }
85
-
86
84
  public startPulling() {
87
85
  this.isRunning = true
88
86
 
@@ -127,29 +125,33 @@ export default class LslStreamInlet implements StreamInlet {
127
125
  }
128
126
 
129
127
  private createDataBufferPtr() {
130
- this.dataBufferPtr = unwrapPointer(
131
- createPointer({
132
- paramsType: [DataType.U8Array],
133
- paramsValue: [this.dataBuffer],
134
- })
135
- )[0]
128
+ this.wrappedDataBufferPtr = createPointer({
129
+ paramsType: [DataType.U8Array],
130
+ paramsValue: [this.dataBuffer],
131
+ })
132
+
133
+ this.dataBufferPtr = unwrapPointer(this.wrappedDataBufferPtr)[0]
136
134
  }
137
135
 
138
136
  private createTimestampBufferPtr() {
137
+ this.wrappedTimestampBufferPtr = createPointer({
138
+ paramsType: [DataType.U8Array],
139
+ paramsValue: [this.timestampBuffer],
140
+ })
141
+
139
142
  this.timestampBufferPtr = unwrapPointer(
140
- createPointer({
141
- paramsType: [DataType.U8Array],
142
- paramsValue: [this.timestampBuffer],
143
- })
143
+ this.wrappedTimestampBufferPtr
144
144
  )[0]
145
145
  }
146
146
 
147
147
  private createErrorCodeBufferPtr() {
148
+ this.wrappedErrorCodeBufferPtr = createPointer({
149
+ paramsType: [DataType.U8Array],
150
+ paramsValue: [this.errorCodeBuffer],
151
+ })
152
+
148
153
  this.errorCodeBufferPtr = unwrapPointer(
149
- createPointer({
150
- paramsType: [DataType.U8Array],
151
- paramsValue: [this.errorCodeBuffer],
152
- })
154
+ this.wrappedErrorCodeBufferPtr
153
155
  )[0]
154
156
  }
155
157
 
@@ -171,7 +173,7 @@ export default class LslStreamInlet implements StreamInlet {
171
173
  private pullSample() {
172
174
  const timestamp = this.callPullSampleBinding()
173
175
 
174
- if (timestamp) {
176
+ if (timestamp > 0) {
175
177
  return {
176
178
  samples: this.createFloatArrayFromDataBuffer(),
177
179
  timestamps: new Float64Array([timestamp]),
@@ -201,7 +203,7 @@ export default class LslStreamInlet implements StreamInlet {
201
203
  private pullChunk() {
202
204
  const firstTimestamp = this.callPullChunkBinding()
203
205
 
204
- if (firstTimestamp) {
206
+ if (firstTimestamp > 0) {
205
207
  return {
206
208
  samples: this.createFloatArrayFromDataBuffer(),
207
209
  timestamps: this.createDoubleArrayFromTimestampBuffer(),
@@ -214,8 +216,8 @@ export default class LslStreamInlet implements StreamInlet {
214
216
  return this.lsl.pullChunk({
215
217
  inlet: this.inlet,
216
218
  dataBufferPtr: this.dataBufferPtr,
217
- timestampBufferPtr: this.timestampBufferPtr,
218
219
  dataBufferElements: this.chunkSize * this.channelCount,
220
+ timestampBufferPtr: this.timestampBufferPtr,
219
221
  timestampBufferElements: this.chunkSize,
220
222
  timeout: this.timeoutMs / 1000,
221
223
  errcodePtr: this.errorCodeBufferPtr,
@@ -243,23 +245,15 @@ export default class LslStreamInlet implements StreamInlet {
243
245
  }
244
246
 
245
247
  public destroy() {
248
+ if (this.isRunning) {
249
+ this.stopPulling()
250
+ }
246
251
  this.destroyBoundInlet()
247
252
  }
248
253
 
249
254
  private destroyBoundInlet() {
250
255
  this.lsl.destroyInlet({ inlet: this.inlet })
251
256
  }
252
-
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])
262
- }
263
257
  }
264
258
 
265
259
  export interface StreamInlet {
@@ -272,17 +266,19 @@ export interface StreamInlet {
272
266
 
273
267
  export type StreamInletConstructor = new (
274
268
  info: StreamInfo,
275
- options: StreamInletOptions,
269
+ options: StreamInletConstructorOptions,
276
270
  onData: OnDataCallback
277
271
  ) => StreamInlet
278
272
 
279
273
  export interface StreamInletOptions {
280
- sourceId: string
274
+ info: StreamInfo
281
275
  chunkSize: number
282
276
  maxBufferedMs?: number
283
277
  timeoutMs?: number
284
278
  }
285
279
 
280
+ export type StreamInletConstructorOptions = Omit<StreamInletOptions, 'info'>
281
+
286
282
  export type OnDataCallback = (
287
283
  samples: Float32Array,
288
284
  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.boundStreamInfo,
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.destroyBoundStreamInfo()
145
+ this.destroyBoundInfo()
150
146
  this.destroyBoundOutlet()
151
147
  }
152
148
 
153
- private destroyBoundStreamInfo() {
154
- this.lsl.destroyStreamInfo({ info: this.boundStreamInfo })
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,4 +1,5 @@
1
1
  import WebSocket, { WebSocketServer } from 'ws'
2
+ import { StreamInfo } from './LslStreamInfo.js'
2
3
  import LslStreamInlet, {
3
4
  StreamInlet,
4
5
  StreamInletOptions,
@@ -153,7 +154,7 @@ export default class LslWebSocketBridge implements WebSocketBridge {
153
154
  return listenPort ? new this.WSS({ port: listenPort }) : undefined
154
155
  }
155
156
 
156
- private static LslStreamInlet(
157
+ private static async LslStreamInlet(
157
158
  options: StreamInletOptions,
158
159
  localServer?: WebSocketServer,
159
160
  remoteSockets?: WebSocket[]
@@ -174,14 +175,13 @@ export type WebSocketBridgeConstructor = new (
174
175
  ) => WebSocketBridge
175
176
 
176
177
  export interface WebSocketBridgeOptions {
177
- sourceId: string
178
+ info: StreamInfo
178
179
  chunkSize: number
179
180
  listenPort?: number
180
181
  connectUrls?: string | string[]
181
182
  }
182
183
 
183
- export interface WebSocketBridgeConstructorOptions
184
- extends WebSocketBridgeOptions {
184
+ export interface WebSocketBridgeConstructorOptions extends WebSocketBridgeOptions {
185
185
  inlet: StreamInlet
186
186
  localServer?: WebSocketServer
187
187
  remoteSockets?: WebSocket[]