@neurodevs/node-lsl 2.0.2 → 3.2.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 (43) hide show
  1. package/README.md +1 -1
  2. package/build/Liblsl.d.ts +33 -25
  3. package/build/Liblsl.js +29 -30
  4. package/build/Liblsl.js.map +1 -1
  5. package/build/LslOutlet.d.ts +8 -7
  6. package/build/LslOutlet.js +28 -21
  7. package/build/LslOutlet.js.map +1 -1
  8. package/build/TimeMarkerOutlet.d.ts +4 -0
  9. package/build/TimeMarkerOutlet.js +26 -0
  10. package/build/TimeMarkerOutlet.js.map +1 -0
  11. package/build/__tests__/behavioral/LslOutlet.test.d.ts +10 -12
  12. package/build/__tests__/behavioral/LslOutlet.test.js +47 -98
  13. package/build/__tests__/behavioral/LslOutlet.test.js.map +1 -1
  14. package/build/__tests__/behavioral/TimeMarkerOutlet.test.d.ts +8 -0
  15. package/build/__tests__/behavioral/TimeMarkerOutlet.test.js +85 -0
  16. package/build/__tests__/behavioral/TimeMarkerOutlet.test.js.map +1 -0
  17. package/build/__tests__/implementation/Liblsl.test.d.ts +9 -9
  18. package/build/__tests__/implementation/Liblsl.test.js +76 -66
  19. package/build/__tests__/implementation/Liblsl.test.js.map +1 -1
  20. package/build/__tests__/support/SpyLiblsl.d.ts +14 -0
  21. package/build/__tests__/support/SpyLiblsl.js +27 -0
  22. package/build/__tests__/support/SpyLiblsl.js.map +1 -0
  23. package/build/__tests__/support/consts.d.ts +2 -0
  24. package/build/__tests__/support/consts.js +14 -0
  25. package/build/__tests__/support/consts.js.map +1 -0
  26. package/build/__tests__/support/generateRandomOutletOptions.d.ts +2 -0
  27. package/build/__tests__/support/generateRandomOutletOptions.js +21 -0
  28. package/build/__tests__/support/generateRandomOutletOptions.js.map +1 -0
  29. package/build/errors/SpruceError.js.map +1 -1
  30. package/build/index.d.ts +1 -0
  31. package/build/index.js +3 -1
  32. package/build/index.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/Liblsl.ts +76 -73
  35. package/src/LslOutlet.ts +39 -37
  36. package/src/TimeMarkerOutlet.ts +19 -0
  37. package/src/__tests__/behavioral/LslOutlet.test.ts +50 -119
  38. package/src/__tests__/behavioral/TimeMarkerOutlet.test.ts +54 -0
  39. package/src/__tests__/implementation/Liblsl.test.ts +96 -84
  40. package/src/__tests__/support/SpyLiblsl.ts +41 -0
  41. package/src/__tests__/support/consts.ts +12 -0
  42. package/src/__tests__/support/generateRandomOutletOptions.ts +19 -0
  43. package/src/index.ts +2 -0
package/src/Liblsl.ts CHANGED
@@ -11,51 +11,35 @@ export default class LiblslImpl implements Liblsl {
11
11
 
12
12
  public static getInstance() {
13
13
  if (!this.instance) {
14
- this.instance = new this()
14
+ this.setInstance(new this())
15
15
  }
16
16
  return this.instance!
17
17
  }
18
18
 
19
- public static reset() {
20
- delete this.instance
21
- }
22
-
23
19
  public static setInstance(instance: Liblsl) {
24
20
  this.instance = instance
25
21
  }
26
22
 
23
+ public static resetInstance() {
24
+ delete this.instance
25
+ }
26
+
27
27
  public constructor() {
28
28
  const path = process.env.LIBLSL_PATH!
29
29
 
30
30
  if (!path) {
31
31
  assertOptions(process, ['env.LIBLSL_PATH'])
32
32
  }
33
+
33
34
  try {
34
35
  this.bindings = this.loadBindings(path)
35
- } catch (err) {
36
- console.error(err)
36
+ } catch {
37
37
  throw new SpruceError({
38
38
  code: 'FAILED_TO_LOAD_LIBLSL',
39
39
  liblslPath: path,
40
40
  })
41
41
  }
42
42
  }
43
- public appendChannelsToStreamInfo(
44
- info: LslBindingsStreamInfo,
45
- channels: LslChannel[]
46
- ): void {
47
- assertOptions({ info, channels }, ['info', 'channels'])
48
- const desc = this.bindings.lsl_get_desc(info)
49
-
50
- const parent = this.bindings.lsl_append_child(desc, 'channels')
51
-
52
- for (const channel of channels) {
53
- const c = this.bindings.lsl_append_child(parent, 'channel')
54
- this.bindings.lsl_append_child_value(c, 'label', channel.label)
55
- this.bindings.lsl_append_child_value(c, 'unit', channel.unit)
56
- this.bindings.lsl_append_child_value(c, 'type', channel.type)
57
- }
58
- }
59
43
 
60
44
  private loadBindings(path: string) {
61
45
  return LiblslImpl.ffi.Library(path!, {
@@ -69,28 +53,10 @@ export default class LiblslImpl implements Liblsl {
69
53
  lsl_get_desc: [xmlPtr, [streamInfo]],
70
54
  lsl_append_child: [xmlPtr, [xmlPtr, 'string']],
71
55
  lsl_append_child_value: [xmlPtr, [xmlPtr, 'string', 'string']],
72
- }) as unknown as LiblslBindings
73
- }
74
-
75
- public pushSample(outlet: LslBindingsOutlet, sample: LslSample): void {
76
- assertOptions({ outlet, sample }, ['outlet', 'sample'])
77
- const clock = this.bindings.lsl_local_clock()
78
- this.bindings.lsl_push_sample_ft(outlet, sample, clock)
56
+ }) as LiblslBindings
79
57
  }
80
58
 
81
- public createOutlet(options: CreateOutletOptions) {
82
- const { info, chunkSize, maxBuffered } = assertOptions(options, [
83
- 'info',
84
- 'chunkSize',
85
- 'maxBuffered',
86
- ])
87
-
88
- return this.bindings.lsl_create_outlet(info, chunkSize, maxBuffered)
89
- }
90
-
91
- public createStreamInfo(
92
- options: CreateStreamInfoOptions
93
- ): LslBindingsStreamInfo {
59
+ public createStreamInfo(options: CreateStreamInfoOptions): BoundStreamInfo {
94
60
  const { name, type, channelCount, sampleRate, channelFormat, sourceId } =
95
61
  assertOptions(options, [
96
62
  'name',
@@ -110,20 +76,49 @@ export default class LiblslImpl implements Liblsl {
110
76
  sourceId
111
77
  )
112
78
  }
79
+
80
+ public appendChannelsToStreamInfo(
81
+ options: AppendChannelsToStreamInfoOptions
82
+ ): void {
83
+ const { info, channels } = assertOptions(options, ['info', 'channels'])
84
+
85
+ const desc = this.bindings.lsl_get_desc(info)
86
+
87
+ const parent = this.bindings.lsl_append_child(desc, 'channels')
88
+
89
+ for (const channel of channels) {
90
+ const child = this.bindings.lsl_append_child(parent, 'channel')
91
+ this.bindings.lsl_append_child_value(child, 'label', channel.label)
92
+ this.bindings.lsl_append_child_value(child, 'unit', channel.unit)
93
+ this.bindings.lsl_append_child_value(child, 'type', channel.type)
94
+ }
95
+ }
96
+
97
+ public createOutlet(options: CreateOutletOptions) {
98
+ const { info, chunkSize, maxBuffered } = assertOptions(options, [
99
+ 'info',
100
+ 'chunkSize',
101
+ 'maxBuffered',
102
+ ])
103
+
104
+ return this.bindings.lsl_create_outlet(info, chunkSize, maxBuffered)
105
+ }
106
+
107
+ public pushSample(options: PushSampleOptions): void {
108
+ const { outlet, sample } = assertOptions(options, ['outlet', 'sample'])
109
+
110
+ const clock = this.bindings.lsl_local_clock()
111
+ this.bindings.lsl_push_sample_ft(outlet, sample, clock)
112
+ }
113
113
  }
114
114
 
115
115
  export interface Liblsl {
116
- appendChannelsToStreamInfo(
117
- info: LslBindingsStreamInfo,
118
- channels: LslChannel[]
119
- ): void
120
- createStreamInfo(options: CreateStreamInfoOptions): LslBindingsStreamInfo
121
- createOutlet(options: CreateOutletOptions): LslBindingsOutlet
122
- pushSample(outlet: LslBindingsOutlet, sample: LslSample): void
116
+ createStreamInfo(options: CreateStreamInfoOptions): BoundStreamInfo
117
+ appendChannelsToStreamInfo(options: AppendChannelsToStreamInfoOptions): void
118
+ createOutlet(options: CreateOutletOptions): BoundOutlet
119
+ pushSample(options: PushSampleOptions): void
123
120
  }
124
121
 
125
- export type LslSample = (number | string | undefined)[]
126
-
127
122
  export interface CreateStreamInfoOptions {
128
123
  name: string
129
124
  type: string
@@ -135,11 +130,28 @@ export interface CreateStreamInfoOptions {
135
130
  unit?: string
136
131
  }
137
132
 
133
+ export interface AppendChannelsToStreamInfoOptions {
134
+ info: BoundStreamInfo
135
+ channels: LslChannel[]
136
+ }
137
+
138
+ export interface CreateOutletOptions {
139
+ info: BoundStreamInfo
140
+ chunkSize: number
141
+ maxBuffered: number
142
+ }
143
+
144
+ export interface PushSampleOptions {
145
+ outlet: BoundOutlet
146
+ sample: LslSample
147
+ }
148
+
138
149
  export interface LslChannel {
139
150
  label: string
140
151
  unit: string
141
152
  type: string
142
153
  }
154
+ export type LslSample = (number | string | undefined)[]
143
155
 
144
156
  export interface LiblslBindings {
145
157
  lsl_create_streaminfo(
@@ -149,41 +161,32 @@ export interface LiblslBindings {
149
161
  sampleRate: number,
150
162
  channelFormat: number,
151
163
  sourceId: string
152
- ): LslBindingsStreamInfo
164
+ ): BoundStreamInfo
153
165
 
154
166
  lsl_create_outlet(
155
- info: LslBindingsStreamInfo,
167
+ info: BoundStreamInfo,
156
168
  chunkSize: number,
157
169
  maxBuffered: number
158
- ): LslBindingsOutlet
170
+ ): BoundOutlet
159
171
 
160
172
  lsl_push_sample_ft(
161
- outlet: LslBindingsOutlet,
173
+ outlet: BoundOutlet,
162
174
  sample: LslSample,
163
175
  timestamp: number
164
176
  ): void
165
177
 
166
178
  lsl_local_clock(): number
167
- lsl_get_desc(info: LslBindingsStreamInfo): LslBindingsDesc
168
- lsl_append_child(desc: LslBindingsDesc, name: string): LslBindingsChild
169
- lsl_append_child_value(
170
- child: LslBindingsChild,
171
- name: string,
172
- value: string
173
- ): void
179
+ lsl_get_desc(info: BoundStreamInfo): BoundDesc
180
+ lsl_append_child(desc: BoundDesc, name: string): BoundChild
181
+ lsl_append_child_value(child: BoundChild, name: string, value: string): void
174
182
  }
175
183
 
184
+ export interface BoundStreamInfo {}
185
+ export interface BoundOutlet {}
186
+ export interface BoundDesc {}
187
+ export interface BoundChild {}
188
+
176
189
  const streamInfo = ref.refType(ref.types.void)
177
190
  const outletType = ref.refType(ref.types.void)
178
191
  const FloatArray = ArrayType(ref.types.float)
179
192
  const xmlPtr = ref.refType(ref.types.void)
180
- export interface CreateOutletOptions {
181
- info: LslBindingsStreamInfo
182
- chunkSize: number
183
- maxBuffered: number
184
- }
185
-
186
- export interface LslBindingsStreamInfo {}
187
- export interface LslBindingsOutlet {}
188
- export interface LslBindingsDesc {}
189
- export interface LslBindingsChild {}
package/src/LslOutlet.ts CHANGED
@@ -1,21 +1,17 @@
1
1
  import { SchemaError, assertOptions } from '@sprucelabs/schema'
2
- import LiblslImpl, {
3
- LslBindingsOutlet,
4
- LslBindingsStreamInfo,
5
- LslSample,
6
- } from './Liblsl'
2
+ import LiblslImpl, { BoundOutlet, BoundStreamInfo, LslSample } from './Liblsl'
7
3
 
8
4
  export default class LslOutletImpl implements LslOutlet {
9
5
  private outletOptions: LslOutletOptions
10
6
  public static Class?: new (options: LslOutletOptions) => LslOutlet
11
- private streamInfo: LslBindingsStreamInfo
12
- private outlet: LslBindingsOutlet
7
+ private streamInfo: BoundStreamInfo
8
+ private outlet: BoundOutlet
13
9
 
14
10
  protected constructor(options: LslOutletOptions) {
15
11
  const { sampleRate, channelFormat } = assertOptions(options, [
16
12
  'name',
17
13
  'type',
18
- 'channelLabels',
14
+ 'channelNames',
19
15
  'sampleRate',
20
16
  'channelFormat',
21
17
  'sourceId',
@@ -27,10 +23,12 @@ export default class LslOutletImpl implements LslOutlet {
27
23
 
28
24
  this.outletOptions = options
29
25
 
30
- const { chunkSize, maxBuffered, channelLabels, ...streamInfoOptions } = this
26
+ const { chunkSize, maxBuffered, channelNames, ...streamInfoOptions } = this
31
27
  .outletOptions as any
32
28
 
33
- this.assertValidChannelCount(channelLabels.length)
29
+ const channelCount = channelNames.length
30
+
31
+ this.assertValidChannelCount(channelCount)
34
32
  this.assertValidSampleRate(sampleRate)
35
33
  this.assertValidChannelFormat(channelFormat)
36
34
  this.assertValidChunkSize(chunkSize)
@@ -41,18 +39,18 @@ export default class LslOutletImpl implements LslOutlet {
41
39
 
42
40
  this.streamInfo = this.lsl.createStreamInfo({
43
41
  ...streamInfoOptions,
44
- channelCount: channelLabels.length,
45
- channelFormat: CHANNEL_FORMATS.indexOf(this.outletOptions.channelFormat),
42
+ channelCount,
43
+ channelFormat: this.lookupChannelFormat(channelFormat),
46
44
  })
47
45
 
48
- this.lsl.appendChannelsToStreamInfo(
49
- this.streamInfo,
50
- channelLabels.map((label: string) => ({
46
+ this.lsl.appendChannelsToStreamInfo({
47
+ info: this.streamInfo,
48
+ channels: channelNames.map((label: string) => ({
51
49
  label,
52
50
  unit: this.outletOptions.unit,
53
51
  type: this.outletOptions.type,
54
- }))
55
- )
52
+ })),
53
+ })
56
54
 
57
55
  this.outlet = this.lsl.createOutlet({
58
56
  info: this.streamInfo,
@@ -65,6 +63,14 @@ export default class LslOutletImpl implements LslOutlet {
65
63
  return new (this.Class ?? this)(options)
66
64
  }
67
65
 
66
+ public pushSample(sample: LslSample) {
67
+ this.lsl.pushSample({ outlet: this.outlet, sample })
68
+ }
69
+
70
+ private lookupChannelFormat(channelFormat: ChannelFormat): number {
71
+ return CHANNEL_FORMATS.indexOf(channelFormat)
72
+ }
73
+
68
74
  private assertValidMaxBufferred(maxBuffered: number) {
69
75
  if (!this.isPositiveIntegerOrZero(maxBuffered)) {
70
76
  throw new SchemaError({
@@ -87,7 +93,6 @@ export default class LslOutletImpl implements LslOutlet {
87
93
 
88
94
  private assertValidChannelFormat(channelFormat: ChannelFormat) {
89
95
  const validFormats = CHANNEL_FORMATS
90
-
91
96
  if (validFormats.indexOf(channelFormat) === -1) {
92
97
  throw new SchemaError({
93
98
  code: 'INVALID_PARAMETERS',
@@ -98,11 +103,11 @@ export default class LslOutletImpl implements LslOutlet {
98
103
  }
99
104
 
100
105
  private assertValidSampleRate(sampleRate: number) {
101
- if (!this.isPositiveNumber(sampleRate)) {
106
+ if (!this.isGreaterThanZero(sampleRate)) {
102
107
  throw new SchemaError({
103
108
  code: 'INVALID_PARAMETERS',
104
109
  parameters: ['sampleRate'],
105
- friendlyMessage: 'Sample rate must be a positive number.',
110
+ friendlyMessage: 'Sample rate must be a positive number or zero.',
106
111
  })
107
112
  }
108
113
  }
@@ -111,30 +116,26 @@ export default class LslOutletImpl implements LslOutlet {
111
116
  if (!this.isPositiveInteger(channelCount)) {
112
117
  throw new SchemaError({
113
118
  code: 'INVALID_PARAMETERS',
114
- parameters: ['channelLabels'],
115
- friendlyMessage: 'channelLabels must have 1 or more labels.',
119
+ parameters: ['channelNames'],
120
+ friendlyMessage: 'channelNames must have 1 or more labels.',
116
121
  })
117
122
  }
118
123
  }
119
124
 
120
- private isPositiveNumber(value: number) {
121
- return value > 0
122
- }
123
-
124
- private isPositiveIntegerOrZero(value: number) {
125
- return Number.isInteger(value) && value >= 0
125
+ private isGreaterThanZero(value: number) {
126
+ return value >= 0
126
127
  }
127
128
 
128
129
  private isPositiveInteger(value: number) {
129
130
  return Number.isInteger(value) && value > 0
130
131
  }
131
132
 
132
- private get lsl() {
133
- return LiblslImpl.getInstance()
133
+ private isPositiveIntegerOrZero(value: number) {
134
+ return Number.isInteger(value) && value >= 0
134
135
  }
135
136
 
136
- public pushSample(sample: LslSample) {
137
- this.lsl.pushSample(this.outlet, sample)
137
+ private get lsl() {
138
+ return LiblslImpl.getInstance()
138
139
  }
139
140
  }
140
141
 
@@ -150,10 +151,15 @@ const CHANNEL_FORMATS = [
150
151
  ] as const
151
152
 
152
153
  export type ChannelFormat = (typeof CHANNEL_FORMATS)[number]
154
+
155
+ export interface LslOutlet {
156
+ pushSample(sample: LslSample): void
157
+ }
158
+
153
159
  export interface LslOutletOptions {
154
160
  name: string
155
161
  type: string
156
- channelLabels: string[]
162
+ channelNames: string[]
157
163
  sampleRate: number
158
164
  channelFormat: ChannelFormat
159
165
  sourceId: string
@@ -162,7 +168,3 @@ export interface LslOutletOptions {
162
168
  chunkSize: number
163
169
  maxBuffered: number
164
170
  }
165
-
166
- export interface LslOutlet {
167
- pushSample(sample: LslSample): void
168
- }
@@ -0,0 +1,19 @@
1
+ import LslOutletImpl, { LslOutletOptions } from './LslOutlet'
2
+
3
+ export default class TimeMarkerOutlet extends LslOutletImpl {
4
+ public static Outlet(options?: Partial<LslOutletOptions>) {
5
+ const defaultOptions = {
6
+ name: 'Time markers',
7
+ type: 'Markers',
8
+ channelNames: ['Markers'],
9
+ sampleRate: 0,
10
+ channelFormat: 'string',
11
+ sourceId: 'time-markers',
12
+ manufacturer: 'N/A',
13
+ unit: 'N/A',
14
+ chunkSize: 0,
15
+ maxBuffered: 0,
16
+ } as LslOutletOptions
17
+ return new (this.Class ?? this)({ ...defaultOptions, ...options })
18
+ }
19
+ }
@@ -5,63 +5,37 @@ import AbstractSpruceTest, {
5
5
  errorAssert,
6
6
  generateId,
7
7
  } from '@sprucelabs/test-utils'
8
- import LiblslImpl, {
9
- CreateOutletOptions,
10
- CreateStreamInfoOptions,
11
- Liblsl,
12
- LslBindingsOutlet,
13
- LslBindingsStreamInfo,
14
- LslChannel,
15
- LslSample,
16
- } from '../../Liblsl'
8
+ import LiblslImpl, { LslSample } from '../../Liblsl'
17
9
  import LslOutlet, { ChannelFormat, LslOutletOptions } from '../../LslOutlet'
18
-
19
- export const CHANNEL_FORMATS = [
20
- 'undefined',
21
- 'float32',
22
- 'double64',
23
- 'string',
24
- 'int32',
25
- 'int16',
26
- 'int8',
27
- 'int64',
28
- ] as const
10
+ import { TEST_CHANNEL_FORMATS } from '../support/consts'
11
+ import generateRandomOutletOptions from '../support/generateRandomOutletOptions'
12
+ import { SpyLiblsl } from '../support/SpyLiblsl'
29
13
 
30
14
  export default class LslOutletTest extends AbstractSpruceTest {
31
- private static spyLsl: SpyLiblsl
15
+ private static spyLiblsl: SpyLiblsl
32
16
  private static randomOutletOptions: LslOutletOptions
33
- private static channelIdx: number
17
+ private static channelFormatIdx: number
34
18
 
35
19
  protected static async beforeEach() {
36
20
  await super.beforeEach()
37
21
  delete LslOutlet.Class
38
- this.channelIdx = randomInt(7)
39
- this.randomOutletOptions = {
40
- name: generateId(),
41
- type: generateId(),
42
- channelLabels: new Array(randomInt(1, 10)).fill(generateId()),
43
- sampleRate: Math.random() * 10,
44
- channelFormat: CHANNEL_FORMATS[this.channelIdx],
45
- sourceId: generateId(),
46
- manufacturer: generateId(),
47
- unit: generateId(),
48
- chunkSize: randomInt(0, 10),
49
- maxBuffered: randomInt(0, 10),
50
- }
51
-
52
- this.spyLsl = new SpyLiblsl()
53
- LiblslImpl.setInstance(this.spyLsl)
22
+ this.channelFormatIdx = randomInt(8)
23
+ this.randomOutletOptions = generateRandomOutletOptions(
24
+ this.channelFormatIdx
25
+ )
26
+ this.spyLiblsl = new SpyLiblsl()
27
+ LiblslImpl.setInstance(this.spyLiblsl)
54
28
  }
55
29
 
56
30
  @test()
57
- protected static async throwsWhenMissingRequiredParameters() {
31
+ protected static async throwsWhenMissingRequiredParams() {
58
32
  //@ts-ignore
59
33
  const err = assert.doesThrow(() => new LslOutlet())
60
34
  errorAssert.assertError(err, 'MISSING_PARAMETERS', {
61
35
  parameters: [
62
36
  'name',
63
37
  'type',
64
- 'channelLabels',
38
+ 'channelNames',
65
39
  'sampleRate',
66
40
  'channelFormat',
67
41
  'sourceId',
@@ -74,18 +48,13 @@ export default class LslOutletTest extends AbstractSpruceTest {
74
48
  }
75
49
 
76
50
  @test()
77
- protected static async canCreateWithRequiredParams() {
78
- this.Outlet()
79
- }
80
-
81
- @test()
82
- protected static async throwsWithInvalidChannelCount() {
83
- this.assertThrowsInvalidChannelLabels(0)
51
+ protected static async throwsWithInvalidChannelNames() {
52
+ this.assertThrowsInvalidChannelNames(0)
84
53
  }
85
54
 
86
55
  @test()
87
56
  protected static async throwsWithInvalidSampleRate() {
88
- this.assertThrowsInvalidSampleRate(0)
57
+ this.Outlet({ sampleRate: 0 })
89
58
  this.assertThrowsInvalidSampleRate(-1)
90
59
  this.assertThrowsInvalidSampleRate(-1.5)
91
60
  }
@@ -99,15 +68,7 @@ export default class LslOutletTest extends AbstractSpruceTest {
99
68
  }
100
69
 
101
70
  @test()
102
- protected static async supportsAllKnownChannelFormats() {
103
- const valid = CHANNEL_FORMATS
104
- for (const format of valid) {
105
- this.Outlet({ channelFormat: format as ChannelFormat })
106
- }
107
- }
108
-
109
- @test()
110
- protected static async throwWithInvalidChunkSize() {
71
+ protected static async throwsWithInvalidChunkSize() {
111
72
  this.Outlet({ chunkSize: 0 })
112
73
  this.assertThrowsInvalidChunkSize(-1)
113
74
  this.assertThrowsInvalidChunkSize(-1.5)
@@ -122,56 +83,66 @@ export default class LslOutletTest extends AbstractSpruceTest {
122
83
  this.assertThrowsInvalidMaxBuffered(1.5)
123
84
  }
124
85
 
125
- @test('pushing sample sends to lsl 1', [1])
126
- @test('pushing sample sends to lsl 2', [1, 2])
127
- protected static async pushingSampleSendsToLsl(sample: LslSample) {
86
+ @test()
87
+ protected static async supportsAllKnownChannelFormats() {
88
+ for (const format of TEST_CHANNEL_FORMATS) {
89
+ this.Outlet({ channelFormat: format as ChannelFormat })
90
+ }
91
+ }
92
+
93
+ @test('pushing [1, 2] sample sends to LSL', [1, 2])
94
+ @test('pushing [1] sample sends to LSL', [1])
95
+ protected static async canPushSampleToLsl(sample: LslSample) {
128
96
  this.pushSample(sample)
129
- assert.isEqual(this.spyLsl.lastPushedOutlet, this.spyLsl.outlet)
130
- assert.isEqualDeep(this.spyLsl.lastPushedSample, sample)
131
- assert.isEqualDeep(this.spyLsl.lastOutletOptions, {
132
- info: this.spyLsl.streamInfo,
97
+ assert.isEqual(
98
+ this.spyLiblsl.lastPushSampleOptions?.outlet,
99
+ this.spyLiblsl.outlet
100
+ )
101
+ assert.isEqualDeep(this.spyLiblsl.lastPushSampleOptions?.sample, sample)
102
+ assert.isEqualDeep(this.spyLiblsl.lastCreateOutletOptions, {
103
+ info: this.spyLiblsl.streamInfo,
133
104
  chunkSize: this.randomOutletOptions.chunkSize,
134
105
  maxBuffered: this.randomOutletOptions.maxBuffered,
135
106
  })
136
107
  }
137
108
 
138
109
  @test()
139
- protected static async constructionPassesCorrectOptionsToCreateStreamInfo() {
110
+ protected static async constructionCreatesStreamInfo() {
140
111
  this.Outlet()
141
112
 
142
113
  const { ...options } = this.randomOutletOptions as any
143
114
 
144
- options.channelCount = options.channelLabels.length
115
+ options.channelCount = options.channelNames.length
145
116
 
146
117
  delete options.manufacturer
147
118
  delete options.unit
148
119
  delete options.chunkSize
149
120
  delete options.maxBuffered
150
- delete options.channelLabels
121
+ delete options.channelNames
151
122
 
152
- assert.isEqualDeep(this.spyLsl.lastStreamInfoOptions, {
123
+ assert.isEqualDeep(this.spyLiblsl.lastCreateStreamInfoOptions, {
153
124
  ...options,
154
- channelFormat: this.channelIdx,
125
+ channelFormat: this.channelFormatIdx,
155
126
  })
156
127
  }
157
128
 
158
129
  @test()
159
- protected static async canOverrideClasInstantiatedInFactory() {
130
+ protected static async canOverrideClassInstantiatedInFactory() {
160
131
  LslOutlet.Class = CheckingOutlet
161
132
  const instance = this.Outlet()
162
133
  assert.isInstanceOf(instance, CheckingOutlet)
163
134
  }
164
135
 
165
- @test('can add one label', [generateId()])
166
- @test('can add two labels', [generateId(), generateId()])
136
+ @test('can add one label to channels', [generateId()])
137
+ @test('can add two labels to channels', [generateId(), generateId()])
167
138
  protected static async appendsChannelsBasedOnCount(labels: string[]) {
168
139
  const type = generateId()
169
140
  const unit = generateId()
170
141
 
171
- this.Outlet({ channelLabels: labels, type, unit })
142
+ this.Outlet({ channelNames: labels, type, unit })
172
143
 
173
- assert.isEqualDeep(this.spyLsl.lastAppendChannelsToStreamInfo, {
174
- info: this.spyLsl.streamInfo,
144
+ assert.isEqualDeep(this.spyLiblsl.lastAppendChannelsToStreamInfoOptions, {
145
+ info: this.spyLiblsl.streamInfo,
175
146
  channels: labels.map((label) => ({
176
147
  label,
177
148
  type,
@@ -187,7 +158,7 @@ export default class LslOutletTest extends AbstractSpruceTest {
187
158
  outlet.pushSample([2])
188
159
  outlet.pushSample([3])
189
160
 
190
- assert.isEqual(this.spyLsl.createStreamInfoHitCount, 1)
161
+ assert.isEqual(this.spyLiblsl.createStreamInfoHitCount, 1)
191
162
  }
192
163
 
193
164
  private static pushSample(sample: LslSample) {
@@ -207,10 +178,10 @@ export default class LslOutletTest extends AbstractSpruceTest {
207
178
  this.assertDoesThrowInvalidParameters({ sampleRate }, ['sampleRate'])
208
179
  }
209
180
 
210
- private static assertThrowsInvalidChannelLabels(count: number) {
181
+ private static assertThrowsInvalidChannelNames(count: number) {
211
182
  this.assertDoesThrowInvalidParameters(
212
- { channelLabels: new Array(count).fill(generateId()) },
213
- ['channelLabels']
183
+ { channelNames: new Array(count).fill(generateId()) },
184
+ ['channelNames']
214
185
  )
215
186
  }
216
187
 
@@ -232,46 +203,6 @@ export default class LslOutletTest extends AbstractSpruceTest {
232
203
  }
233
204
  }
234
205
 
235
- class SpyLiblsl implements Liblsl {
236
- public lastPushedOutlet?: LslBindingsOutlet
237
- public lastPushedSample?: LslSample
238
- public lastOutletOptions?: CreateOutletOptions
239
- public lastStreamInfoOptions?: CreateStreamInfoOptions
240
- public outlet: LslBindingsOutlet = {} as LslBindingsOutlet
241
- public streamInfo: LslBindingsStreamInfo = {} as LslBindingsStreamInfo
242
- public lastAppendChannelsToStreamInfo?: {
243
- info: LslBindingsStreamInfo
244
- channels: LslChannel[]
245
- }
246
- public createStreamInfoHitCount = 0
247
-
248
- public createStreamInfo(
249
- options: CreateStreamInfoOptions
250
- ): LslBindingsStreamInfo {
251
- this.createStreamInfoHitCount++
252
- this.lastStreamInfoOptions = options
253
- return this.streamInfo
254
- }
255
- public appendChannelsToStreamInfo(
256
- info: LslBindingsStreamInfo,
257
- channels: LslChannel[]
258
- ): void {
259
- this.lastAppendChannelsToStreamInfo = {
260
- info,
261
- channels,
262
- }
263
- }
264
-
265
- public createOutlet(options: CreateOutletOptions): LslBindingsOutlet {
266
- this.lastOutletOptions = options
267
- return this.outlet
268
- }
269
- public pushSample(outlet: LslBindingsOutlet, sample: LslSample): void {
270
- this.lastPushedOutlet = outlet
271
- this.lastPushedSample = sample
272
- }
273
- }
274
-
275
206
  class CheckingOutlet extends LslOutlet {
276
207
  public constructor(options: LslOutletOptions) {
277
208
  super(options)