@neurodevs/node-lsl 10.1.4 → 10.2.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.
Files changed (104) hide show
  1. package/build/__tests__/AbstractNodeLslTest.d.ts +18 -0
  2. package/build/__tests__/AbstractNodeLslTest.js +80 -0
  3. package/build/__tests__/AbstractNodeLslTest.js.map +1 -0
  4. package/build/__tests__/{implementation/Liblsl.test.d.ts → Liblsl.test.d.ts} +6 -0
  5. package/build/__tests__/{implementation/Liblsl.test.js → Liblsl.test.js} +60 -14
  6. package/build/__tests__/Liblsl.test.js.map +1 -0
  7. package/build/__tests__/LslInlet.test.d.ts +11 -0
  8. package/build/__tests__/LslInlet.test.js +94 -0
  9. package/build/__tests__/LslInlet.test.js.map +1 -0
  10. package/build/__tests__/{behavioral/LslOutlet.test.d.ts → LslOutlet.test.d.ts} +3 -6
  11. package/build/__tests__/{behavioral/LslOutlet.test.js → LslOutlet.test.js} +24 -68
  12. package/build/__tests__/LslOutlet.test.js.map +1 -0
  13. package/build/__tests__/LslStreamInfo.test.d.ts +21 -0
  14. package/build/__tests__/LslStreamInfo.test.js +166 -0
  15. package/build/__tests__/LslStreamInfo.test.js.map +1 -0
  16. package/build/__tests__/SpyLslStreamInfo.d.ts +5 -0
  17. package/build/__tests__/SpyLslStreamInfo.js +17 -0
  18. package/build/__tests__/SpyLslStreamInfo.js.map +1 -0
  19. package/build/__tests__/{behavioral/TimeMarkerOutlet.test.d.ts → TimeMarkerOutlet.test.d.ts} +2 -3
  20. package/build/__tests__/{behavioral/TimeMarkerOutlet.test.js → TimeMarkerOutlet.test.js} +7 -33
  21. package/build/__tests__/TimeMarkerOutlet.test.js.map +1 -0
  22. package/build/{implementations → components}/Liblsl.d.ts +4 -2
  23. package/build/{implementations → components}/Liblsl.js +23 -4
  24. package/build/components/Liblsl.js.map +1 -0
  25. package/build/components/LslInlet.d.ts +31 -0
  26. package/build/components/LslInlet.js +51 -0
  27. package/build/components/LslInlet.js.map +1 -0
  28. package/build/{implementations → components}/LslOutlet.d.ts +20 -9
  29. package/build/components/LslOutlet.js +118 -0
  30. package/build/components/LslOutlet.js.map +1 -0
  31. package/build/components/LslStreamInfo.d.ts +38 -0
  32. package/build/components/LslStreamInfo.js +65 -0
  33. package/build/components/LslStreamInfo.js.map +1 -0
  34. package/build/{implementations → components}/TimeMarkerOutlet.d.ts +2 -1
  35. package/build/{implementations → components}/TimeMarkerOutlet.js +1 -1
  36. package/build/components/TimeMarkerOutlet.js.map +1 -0
  37. package/build/demo.d.ts +1 -0
  38. package/build/demo.js +42 -0
  39. package/build/demo.js.map +1 -0
  40. package/build/index.d.ts +12 -7
  41. package/build/index.js +16 -8
  42. package/build/index.js.map +1 -1
  43. package/build/nodeLsl.types.d.ts +34 -18
  44. package/build/testDoubles/FakeLiblsl.d.ts +11 -3
  45. package/build/testDoubles/FakeLiblsl.js +29 -5
  46. package/build/testDoubles/FakeLiblsl.js.map +1 -1
  47. package/build/testDoubles/FakeLslOutlet.d.ts +4 -2
  48. package/build/testDoubles/FakeLslOutlet.js +2 -1
  49. package/build/testDoubles/FakeLslOutlet.js.map +1 -1
  50. package/build/testDoubles/FakeLslStreamInfo.d.ts +5 -0
  51. package/build/testDoubles/FakeLslStreamInfo.js +10 -0
  52. package/build/testDoubles/FakeLslStreamInfo.js.map +1 -0
  53. package/build/testDoubles/FakeStreamInfo.d.ts +9 -0
  54. package/build/testDoubles/FakeStreamInfo.js +17 -0
  55. package/build/testDoubles/FakeStreamInfo.js.map +1 -0
  56. package/build/testDoubles/FakeTimeMarkerOutlet.d.ts +1 -1
  57. package/build/testDoubles/SpyLslInlet.d.ts +7 -0
  58. package/build/testDoubles/SpyLslInlet.js +20 -0
  59. package/build/testDoubles/SpyLslInlet.js.map +1 -0
  60. package/build/testDoubles/SpyLslStreamInfo.d.ts +9 -0
  61. package/build/testDoubles/SpyLslStreamInfo.js +28 -0
  62. package/build/testDoubles/SpyLslStreamInfo.js.map +1 -0
  63. package/build/testDoubles/SpyTimeMarkerOutlet.d.ts +4 -3
  64. package/build/testDoubles/SpyTimeMarkerOutlet.js +3 -3
  65. package/build/testDoubles/SpyTimeMarkerOutlet.js.map +1 -1
  66. package/build/testDoubles/consts.js.map +1 -0
  67. package/build/{__tests__/support → testDoubles}/generateRandomOutletOptions.d.ts +1 -1
  68. package/build/testDoubles/generateRandomOutletOptions.js.map +1 -0
  69. package/package.json +1 -1
  70. package/src/__tests__/AbstractNodeLslTest.ts +50 -0
  71. package/src/__tests__/{implementation/Liblsl.test.ts → Liblsl.test.ts} +49 -8
  72. package/src/__tests__/LslInlet.test.ts +99 -0
  73. package/src/__tests__/{behavioral/LslOutlet.test.ts → LslOutlet.test.ts} +29 -64
  74. package/src/__tests__/LslStreamInfo.test.ts +209 -0
  75. package/src/__tests__/{behavioral/TimeMarkerOutlet.test.ts → TimeMarkerOutlet.test.ts} +12 -17
  76. package/src/{implementations → components}/Liblsl.ts +29 -5
  77. package/src/components/LslInlet.ts +85 -0
  78. package/src/{implementations → components}/LslOutlet.ts +91 -64
  79. package/src/components/LslStreamInfo.ts +105 -0
  80. package/src/{implementations → components}/TimeMarkerOutlet.ts +3 -1
  81. package/src/demo.ts +43 -0
  82. package/src/index.ts +15 -7
  83. package/src/nodeLsl.types.ts +36 -23
  84. package/src/testDoubles/FakeLiblsl.ts +40 -7
  85. package/src/testDoubles/FakeLslOutlet.ts +5 -2
  86. package/src/testDoubles/FakeStreamInfo.ts +19 -0
  87. package/src/testDoubles/FakeTimeMarkerOutlet.ts +1 -1
  88. package/src/testDoubles/SpyLslInlet.ts +16 -0
  89. package/src/testDoubles/SpyLslStreamInfo.ts +27 -0
  90. package/src/testDoubles/SpyTimeMarkerOutlet.ts +5 -4
  91. package/src/{__tests__/support → testDoubles}/generateRandomOutletOptions.ts +1 -1
  92. package/build/__tests__/behavioral/LslOutlet.test.js.map +0 -1
  93. package/build/__tests__/behavioral/TimeMarkerOutlet.test.js.map +0 -1
  94. package/build/__tests__/implementation/Liblsl.test.js.map +0 -1
  95. package/build/__tests__/support/consts.js.map +0 -1
  96. package/build/__tests__/support/generateRandomOutletOptions.js.map +0 -1
  97. package/build/implementations/Liblsl.js.map +0 -1
  98. package/build/implementations/LslOutlet.js +0 -94
  99. package/build/implementations/LslOutlet.js.map +0 -1
  100. package/build/implementations/TimeMarkerOutlet.js.map +0 -1
  101. /package/build/{__tests__/support → testDoubles}/consts.d.ts +0 -0
  102. /package/build/{__tests__/support → testDoubles}/consts.js +0 -0
  103. /package/build/{__tests__/support → testDoubles}/generateRandomOutletOptions.js +0 -0
  104. /package/src/{__tests__/support → testDoubles}/consts.ts +0 -0
@@ -0,0 +1,209 @@
1
+ import { test, assert, generateId, errorAssert } from '@sprucelabs/test-utils'
2
+ import LslStreamInfo, { StreamInfoOptions } from '../components/LslStreamInfo'
3
+ import { CHANNEL_FORMATS } from '../consts'
4
+ import SpyLslStreamInfo from '../testDoubles/SpyLslStreamInfo'
5
+ import AbstractNodeLslTest from './AbstractNodeLslTest'
6
+
7
+ export default class LslStreamInfoTest extends AbstractNodeLslTest {
8
+ private static instance: SpyLslStreamInfo
9
+
10
+ protected static async beforeEach() {
11
+ await super.beforeEach()
12
+
13
+ this.setSpyLslStreamInfo()
14
+
15
+ this.instance = this.LslStreamInfo()
16
+ }
17
+
18
+ @test()
19
+ protected static async canCreateLslStreamInfo() {
20
+ assert.isTruthy(this.instance, 'Instance should be created!')
21
+ }
22
+
23
+ @test()
24
+ protected static async throwsWithMissingRequiredOptions() {
25
+ const err = assert.doesThrow(
26
+ // @ts-ignore
27
+ () => LslStreamInfo.Create()
28
+ )
29
+ errorAssert.assertError(err, 'MISSING_PARAMETERS', {
30
+ parameters: ['channelNames', 'channelFormat', 'sampleRate'],
31
+ })
32
+ }
33
+
34
+ @test()
35
+ protected static async generatesUniqueNameIfNotProvided() {
36
+ const instance1 = this.LslStreamInfo({ name: undefined })
37
+ const instance2 = this.LslStreamInfo({ name: undefined })
38
+
39
+ assert.isNotEqual(
40
+ instance1.getName(),
41
+ instance2.getName(),
42
+ 'Inlet names should be unique!'
43
+ )
44
+ }
45
+
46
+ @test()
47
+ protected static async uniqueNameHasSetPrefix() {
48
+ const instance = this.LslStreamInfo({ name: undefined })
49
+
50
+ assert.doesInclude(
51
+ instance.getName(),
52
+ 'lsl-stream-info-',
53
+ 'Name should have set prefix!'
54
+ )
55
+ }
56
+
57
+ @test()
58
+ protected static async canManuallySetName() {
59
+ const name = generateId()
60
+ const instance = this.LslStreamInfo({ name })
61
+
62
+ assert.isEqual(
63
+ instance.getName(),
64
+ name,
65
+ 'Name should be set to provided value!'
66
+ )
67
+ }
68
+
69
+ @test()
70
+ protected static async generatesUniqueTypeIfNotProvided() {
71
+ const instance1 = this.LslStreamInfo({ type: undefined })
72
+ const instance2 = this.LslStreamInfo({ type: undefined })
73
+
74
+ assert.isNotEqual(
75
+ instance1.getType(),
76
+ instance2.getType(),
77
+ 'Inlet types should be unique!'
78
+ )
79
+ }
80
+
81
+ @test()
82
+ protected static async canManuallySetType() {
83
+ const type = generateId()
84
+ const instance = this.LslStreamInfo({ type })
85
+
86
+ assert.isEqual(
87
+ instance.getType(),
88
+ type,
89
+ 'Type should be set to provided value!'
90
+ )
91
+ }
92
+
93
+ @test()
94
+ protected static async generatesUniqueSourceIdIfNotProvided() {
95
+ const instance1 = this.LslStreamInfo({ sourceId: undefined })
96
+ const instance2 = this.LslStreamInfo({ sourceId: undefined })
97
+
98
+ assert.isNotEqual(
99
+ instance1.getSourceId(),
100
+ instance2.getSourceId(),
101
+ 'Inlet sourceIds should be unique!'
102
+ )
103
+ }
104
+
105
+ @test()
106
+ protected static async canManuallySetSourceId() {
107
+ const sourceId = generateId()
108
+ const instance = this.LslStreamInfo({ sourceId })
109
+
110
+ assert.isEqual(
111
+ instance.getSourceId(),
112
+ sourceId,
113
+ 'SourceId should be set to provided value!'
114
+ )
115
+ }
116
+
117
+ @test()
118
+ protected static async setsUnitsToNAIfNotProvided() {
119
+ assert.isEqual(
120
+ this.instance.getUnits(),
121
+ 'N/A',
122
+ 'Units should be set to "N/A"!'
123
+ )
124
+ }
125
+
126
+ @test()
127
+ protected static async canManuallySetUnits() {
128
+ const units = generateId()
129
+ const instance = this.LslStreamInfo({ units })
130
+
131
+ assert.isEqual(
132
+ instance.getUnits(),
133
+ units,
134
+ 'Units should be set to provided value!'
135
+ )
136
+ }
137
+
138
+ @test()
139
+ protected static async callsBindingsToCreateStreamInfo() {
140
+ assert.isEqual(
141
+ this.fakeLiblsl.createStreamInfoHitCount,
142
+ 1,
143
+ 'Should have called createStreamInfo!'
144
+ )
145
+ }
146
+
147
+ @test()
148
+ protected static async passesOptionsToStreamInfo() {
149
+ const randomChannelIdx = Math.floor(
150
+ Math.random() * CHANNEL_FORMATS.length
151
+ )
152
+
153
+ const options = {
154
+ name: generateId(),
155
+ type: generateId(),
156
+ sourceId: generateId(),
157
+ channelCount: this.channelNames.length,
158
+ channelFormat: CHANNEL_FORMATS[randomChannelIdx],
159
+ sampleRate: 100 * Math.random(),
160
+ }
161
+
162
+ this.LslStreamInfo(options)
163
+
164
+ const expected = {
165
+ ...options,
166
+ channelFormat: randomChannelIdx,
167
+ }
168
+
169
+ assert.isEqualDeep(
170
+ this.fakeLiblsl.lastCreateStreamInfoOptions,
171
+ expected,
172
+ 'Should have passed options to createStreamInfo!'
173
+ )
174
+ }
175
+
176
+ @test()
177
+ protected static async appendsChannelsToStreamInfo() {
178
+ this.LslStreamInfo({ units: this.units, type: this.type })
179
+
180
+ assert.isEqualDeep(
181
+ this.fakeLiblsl.lastAppendChannelsToStreamInfoOptions,
182
+ {
183
+ info: this.instance.getStreamInfo(),
184
+ channels: this.channelNames.map((label: string) => ({
185
+ label,
186
+ unit: this.units,
187
+ type: this.type,
188
+ })),
189
+ },
190
+ 'Should have called appendChannelsToStreamInfo!'
191
+ )
192
+ }
193
+
194
+ private static LslStreamInfo(options?: Partial<StreamInfoOptions>) {
195
+ return LslStreamInfo.Create({
196
+ ...this.defaultOptions,
197
+ ...options,
198
+ }) as SpyLslStreamInfo
199
+ }
200
+
201
+ private static readonly defaultOptions = {
202
+ name: generateId(),
203
+ type: generateId(),
204
+ sourceId: generateId(),
205
+ channelNames: this.channelNames,
206
+ channelFormat: 'float32',
207
+ sampleRate: 100 * Math.random(),
208
+ } as StreamInfoOptions
209
+ }
@@ -1,25 +1,20 @@
1
1
  import { randomInt } from 'crypto'
2
- import AbstractSpruceTest, {
3
- assert,
4
- generateId,
5
- test,
6
- } from '@sprucelabs/test-utils'
7
- import LiblslImpl from '../../implementations/Liblsl'
8
- import { LslOutletOptions } from '../../implementations/LslOutlet'
9
- import TimeMarkerOutletImpl from '../../implementations/TimeMarkerOutlet'
10
- import FakeLiblsl from '../../testDoubles/FakeLiblsl'
11
- import SpyTimeMarkerOutlet from '../../testDoubles/SpyTimeMarkerOutlet'
12
- import generateRandomOutletOptions from '../support/generateRandomOutletOptions'
13
-
14
- export default class TimeMarkerOutletTest extends AbstractSpruceTest {
15
- private static fakeLiblsl: FakeLiblsl
2
+ import { assert, generateId, test } from '@sprucelabs/test-utils'
3
+ import { LslOutletOptions } from '../components/LslOutlet'
4
+ import TimeMarkerOutletImpl from '../components/TimeMarkerOutlet'
5
+ import generateRandomOutletOptions from '../testDoubles/generateRandomOutletOptions'
6
+ import SpyTimeMarkerOutlet from '../testDoubles/SpyTimeMarkerOutlet'
7
+ import AbstractNodeLslTest from './AbstractNodeLslTest'
8
+
9
+ export default class TimeMarkerOutletTest extends AbstractNodeLslTest {
16
10
  private static outlet: SpyTimeMarkerOutlet
17
11
 
18
12
  protected static async beforeEach() {
19
13
  await super.beforeEach()
20
- TimeMarkerOutletImpl.Class = SpyTimeMarkerOutlet
21
- this.fakeLiblsl = new FakeLiblsl()
22
- LiblslImpl.setInstance(this.fakeLiblsl)
14
+
15
+ this.setSpyTimeMarkerOutlet()
16
+ this.setFakeLiblsl()
17
+
23
18
  this.outlet = await this.Outlet()
24
19
  }
25
20
 
@@ -10,6 +10,8 @@ import {
10
10
  DestroyOutletOptions,
11
11
  PushSampleFloatTimestampOptions,
12
12
  PushSampleStringTimestampOptions,
13
+ CreateInletOptions,
14
+ DestroyInletOptions,
13
15
  } from '../nodeLsl.types'
14
16
 
15
17
  export default class LiblslImpl implements Liblsl {
@@ -80,6 +82,16 @@ export default class LiblslImpl implements Liblsl {
80
82
  retType: DataType.Void,
81
83
  paramsType: [DataType.External],
82
84
  },
85
+ lsl_create_inlet: {
86
+ library: 'lsl',
87
+ retType: DataType.External,
88
+ paramsType: [DataType.External, DataType.I32, DataType.I32],
89
+ },
90
+ lsl_destroy_inlet: {
91
+ library: 'lsl',
92
+ retType: DataType.Void,
93
+ paramsType: [DataType.External],
94
+ },
83
95
  lsl_local_clock: {
84
96
  library: 'lsl',
85
97
  retType: DataType.Double,
@@ -182,11 +194,6 @@ export default class LiblslImpl implements Liblsl {
182
194
  return this.bindings.lsl_create_outlet([info, chunkSize, maxBuffered])
183
195
  }
184
196
 
185
- public destroyOutlet(options: DestroyOutletOptions) {
186
- const { outlet } = assertOptions(options, ['outlet'])
187
- this.bindings.lsl_destroy_outlet([outlet])
188
- }
189
-
190
197
  public pushSampleFloatTimestamp(options: PushSampleFloatTimestampOptions) {
191
198
  const { outlet, sample, timestamp } = assertOptions(options, [
192
199
  'outlet',
@@ -207,6 +214,23 @@ export default class LiblslImpl implements Liblsl {
207
214
  this.bindings.lsl_push_sample_strt([outlet, sample, timestamp])
208
215
  }
209
216
 
217
+ public destroyOutlet(options: DestroyOutletOptions) {
218
+ const { outlet } = assertOptions(options, ['outlet'])
219
+ this.bindings.lsl_destroy_outlet([outlet])
220
+ }
221
+
222
+ public createInlet(options: CreateInletOptions) {
223
+ const { info, chunkSize, maxBuffered } = assertOptions(options, [
224
+ 'info',
225
+ 'chunkSize',
226
+ 'maxBuffered',
227
+ ])
228
+
229
+ return this.bindings.lsl_create_inlet([info, chunkSize, maxBuffered])
230
+ }
231
+
232
+ public destroyInlet(_options: DestroyInletOptions) {}
233
+
210
234
  public localClock() {
211
235
  return this.bindings.lsl_local_clock([])
212
236
  }
@@ -0,0 +1,85 @@
1
+ import { assertOptions } from '@sprucelabs/schema'
2
+ import { generateId } from '@sprucelabs/test-utils'
3
+ import { ChannelFormat } from '../nodeLsl.types'
4
+ import LiblslImpl from './Liblsl'
5
+ import LslStreamInfo, { StreamInfo, StreamInfoOptions } from './LslStreamInfo'
6
+
7
+ export default class LslInlet implements StreamInlet {
8
+ public static Class?: LslInletConstructor
9
+
10
+ protected name: string
11
+ protected info: StreamInfo
12
+ private chunkSize: number
13
+ private maxBuffered: number
14
+
15
+ protected constructor(info: StreamInfo, options: LslInletOptions) {
16
+ const {
17
+ name = this.defaultName,
18
+ chunkSize,
19
+ maxBuffered,
20
+ } = options ?? {}
21
+
22
+ this.info = info
23
+ this.chunkSize = chunkSize
24
+ this.maxBuffered = maxBuffered
25
+ this.name = name
26
+
27
+ this.createLslInlet()
28
+ }
29
+
30
+ public static Create(options: LslInletOptions) {
31
+ assertOptions(options, [
32
+ 'channelNames',
33
+ 'channelFormat',
34
+ 'sampleRate',
35
+ 'chunkSize',
36
+ 'maxBuffered',
37
+ ])
38
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
39
+ const { maxBuffered, chunkSize, ...streamInfoOptions } = options
40
+ const info = this.LslStreamInfo(streamInfoOptions)
41
+ return new (this.Class ?? this)(info, options)
42
+ }
43
+
44
+ private createLslInlet() {
45
+ this.lsl.createInlet({
46
+ info: this.boundStreamInfo,
47
+ chunkSize: this.chunkSize,
48
+ maxBuffered: this.maxBuffered,
49
+ })
50
+ }
51
+
52
+ private get boundStreamInfo() {
53
+ return this.info.boundStreamInfo
54
+ }
55
+
56
+ private get lsl() {
57
+ return LiblslImpl.getInstance()
58
+ }
59
+
60
+ private readonly defaultName = `lsl-inlet-${generateId()}`
61
+
62
+ private static LslStreamInfo(options: StreamInfoOptions) {
63
+ return LslStreamInfo.Create(options)
64
+ }
65
+ }
66
+
67
+ export interface StreamInlet {}
68
+
69
+ export type LslInletConstructor = new (
70
+ info: StreamInfo,
71
+ options: LslInletOptions
72
+ ) => StreamInlet
73
+
74
+ export interface LslInletOptions {
75
+ sampleRate: number
76
+ channelNames: string[]
77
+ channelFormat: ChannelFormat
78
+ chunkSize: number
79
+ maxBuffered: number
80
+ name?: string
81
+ type?: string
82
+ sourceId?: string
83
+ manufacturer?: string
84
+ units?: string
85
+ }
@@ -6,90 +6,82 @@ import {
6
6
  assertValidMaxBuffered,
7
7
  assertValidSampleRate,
8
8
  } from '../assertions'
9
- import { CHANNEL_FORMATS_MAP } from '../consts'
10
- import {
11
- ChannelFormat,
12
- BoundOutlet,
13
- BoundStreamInfo,
14
- Liblsl,
15
- LslSample,
16
- } from '../nodeLsl.types'
9
+ import { ChannelFormat, BoundOutlet, Liblsl, LslSample } from '../nodeLsl.types'
17
10
  import LiblslImpl from './Liblsl'
11
+ import LslStreamInfo, { StreamInfo, StreamInfoOptions } from './LslStreamInfo'
18
12
 
19
13
  export default class LslOutletImpl implements LslOutlet {
20
14
  public static Class?: LslOutletConstructor
21
15
 
16
+ private info: StreamInfo
22
17
  private options: LslOutletOptions
23
- private streamInfo: BoundStreamInfo
24
- private outlet: BoundOutlet
25
- private pushSampleByType: (options: any) => void
18
+ private outlet!: BoundOutlet
19
+ private pushSampleByType!: (options: any) => void
20
+
21
+ protected constructor(info: StreamInfo, options: LslOutletOptions) {
22
+ this.info = info
23
+ this.options = options
24
+
25
+ this.validateOptions()
26
+ this.createLslOutlet()
27
+ this.setPushSampleType()
28
+ }
26
29
 
27
- protected constructor(options: LslOutletOptions) {
28
- const { sampleRate, channelFormat } = assertOptions(options, [
30
+ public static async Create(options: LslOutletOptions) {
31
+ assertOptions(options, [
29
32
  'name',
30
33
  'type',
34
+ 'sourceId',
31
35
  'channelNames',
32
- 'sampleRate',
33
36
  'channelFormat',
34
- 'sourceId',
35
- 'manufacturer',
36
- 'unit',
37
+ 'sampleRate',
37
38
  'chunkSize',
38
39
  'maxBuffered',
40
+ 'manufacturer',
41
+ 'unit',
39
42
  ])
40
43
 
41
- this.options = options
42
-
43
- const { chunkSize, maxBuffered, channelNames, ...streamInfoOptions } =
44
- this.options as any
44
+ const { waitAfterConstructionMs = 10 } = options ?? {}
45
45
 
46
- const channelCount = channelNames.length
46
+ const streamInfoOptions = {
47
+ channelNames: options.channelNames,
48
+ channelFormat: options.channelFormat,
49
+ sampleRate: options.sampleRate,
50
+ name: options.name,
51
+ type: options.type,
52
+ sourceId: options.sourceId,
53
+ units: options.unit,
54
+ }
47
55
 
48
- assertValidChannelCount(channelCount)
49
- assertValidSampleRate(sampleRate)
50
- assertValidChannelFormat(channelFormat)
51
- assertValidChunkSize(chunkSize)
52
- assertValidMaxBuffered(maxBuffered)
56
+ const info = this.LslStreamInfo(streamInfoOptions)
57
+ const instance = new (this.Class ?? this)(info, options)
53
58
 
54
- delete streamInfoOptions.manufacturer
55
- delete streamInfoOptions.unit
59
+ await this.wait(waitAfterConstructionMs)
56
60
 
57
- this.streamInfo = this.lsl.createStreamInfo({
58
- ...streamInfoOptions,
59
- channelCount,
60
- channelFormat: this.lookupChannelFormat(channelFormat),
61
- })
61
+ return instance
62
+ }
62
63
 
63
- this.lsl.appendChannelsToStreamInfo({
64
- info: this.streamInfo,
65
- channels: channelNames.map((label: string) => ({
66
- label,
67
- unit: this.options.unit,
68
- type: this.options.type,
69
- })),
70
- })
64
+ private validateOptions() {
65
+ assertValidChannelCount(this.channelCount)
66
+ assertValidSampleRate(this.sampleRate)
67
+ assertValidChannelFormat(this.channelFormat)
68
+ assertValidChunkSize(this.chunkSize)
69
+ assertValidMaxBuffered(this.maxBuffered)
70
+ }
71
71
 
72
+ private createLslOutlet() {
72
73
  this.outlet = this.lsl.createOutlet({
73
- info: this.streamInfo,
74
- chunkSize: this.options.chunkSize,
75
- maxBuffered: this.options.maxBuffered,
74
+ info: this.boundStreamInfo,
75
+ chunkSize: this.chunkSize,
76
+ maxBuffered: this.maxBuffered,
76
77
  })
78
+ }
77
79
 
80
+ private setPushSampleType() {
78
81
  const pushMethod = this.getPushMethod()
79
82
  this.pushSampleByType = this.lsl[pushMethod].bind(this.lsl)
80
83
  }
81
84
 
82
- public static async Create(options: LslOutletOptions) {
83
- const { waitAfterConstructionMs = 10 } = options ?? {}
84
- const instance = new (this.Class ?? this)(options)
85
- await this.wait(waitAfterConstructionMs)
86
- return instance
87
- }
88
-
89
- private static async wait(waitMs: number) {
90
- return new Promise((resolve) => setTimeout(resolve, waitMs))
91
- }
92
-
93
85
  public destroy() {
94
86
  this.lsl.destroyOutlet({ outlet: this.outlet })
95
87
  }
@@ -105,7 +97,7 @@ export default class LslOutletImpl implements LslOutlet {
105
97
  }
106
98
 
107
99
  private getPushMethod() {
108
- const channelFormat = this.options.channelFormat
100
+ const channelFormat = this.channelFormat
109
101
 
110
102
  const methodMap: Record<string, keyof Liblsl> = {
111
103
  float32: 'pushSampleFloatTimestamp',
@@ -120,13 +112,45 @@ export default class LslOutletImpl implements LslOutlet {
120
112
  return methodMap[channelFormat]
121
113
  }
122
114
 
123
- private lookupChannelFormat(channelFormat: ChannelFormat) {
124
- return CHANNEL_FORMATS_MAP[channelFormat]
115
+ private get channelNames() {
116
+ return this.options.channelNames
117
+ }
118
+
119
+ private get channelCount() {
120
+ return this.channelNames.length
121
+ }
122
+
123
+ private get channelFormat() {
124
+ return this.options.channelFormat
125
+ }
126
+
127
+ private get sampleRate() {
128
+ return this.options.sampleRate
129
+ }
130
+
131
+ private get chunkSize() {
132
+ return this.options.chunkSize
133
+ }
134
+
135
+ private get maxBuffered() {
136
+ return this.options.maxBuffered
137
+ }
138
+
139
+ private get boundStreamInfo() {
140
+ return this.info.boundStreamInfo
125
141
  }
126
142
 
127
143
  private get lsl() {
128
144
  return LiblslImpl.getInstance()
129
145
  }
146
+
147
+ private static async wait(waitMs: number) {
148
+ return new Promise((resolve) => setTimeout(resolve, waitMs))
149
+ }
150
+
151
+ private static LslStreamInfo(options: StreamInfoOptions) {
152
+ return LslStreamInfo.Create(options)
153
+ }
130
154
  }
131
155
 
132
156
  export interface LslOutlet {
@@ -134,18 +158,21 @@ export interface LslOutlet {
134
158
  pushSample(sample: LslSample): void
135
159
  }
136
160
 
137
- export type LslOutletConstructor = new (options: LslOutletOptions) => LslOutlet
161
+ export type LslOutletConstructor = new (
162
+ info: StreamInfo,
163
+ options: LslOutletOptions
164
+ ) => LslOutlet
138
165
 
139
166
  export interface LslOutletOptions {
140
167
  name: string
141
168
  type: string
169
+ sourceId: string
142
170
  channelNames: string[]
143
- sampleRate: number
144
171
  channelFormat: ChannelFormat
145
- sourceId: string
146
- manufacturer: string
147
- unit: string
172
+ sampleRate: number
148
173
  chunkSize: number
149
174
  maxBuffered: number
175
+ manufacturer: string
176
+ unit: string
150
177
  waitAfterConstructionMs?: number
151
178
  }