tone 15.5.1 → 15.5.3

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 (62) hide show
  1. package/Tone/core/context/OnRunning.test.ts +63 -0
  2. package/Tone/core/context/OnRunning.ts +26 -0
  3. package/Tone/core/context/ToneWithContext.test.ts +82 -0
  4. package/Tone/core/context/ToneWithContext.ts +24 -0
  5. package/Tone/core/util/Interface.ts +8 -2
  6. package/Tone/core/util/TypeCheck.ts +9 -9
  7. package/Tone/effect/FrequencyShifter.ts +5 -3
  8. package/Tone/effect/LFOStereoEffect.test.ts +85 -0
  9. package/Tone/effect/LFOStereoEffect.ts +136 -0
  10. package/Tone/effect/Phaser.ts +9 -45
  11. package/Tone/effect/PitchShift.ts +8 -5
  12. package/Tone/effect/StereoWidener.ts +12 -2
  13. package/Tone/effect/Tremolo.test.ts +0 -24
  14. package/Tone/effect/Tremolo.ts +13 -80
  15. package/Tone/effect/Vibrato.ts +5 -3
  16. package/Tone/signal/ToneConstantSource.ts +7 -19
  17. package/Tone/signal/WaveShaper.ts +15 -8
  18. package/Tone/source/UserMedia.test.ts +131 -112
  19. package/Tone/source/UserMedia.ts +37 -26
  20. package/Tone/source/oscillator/Oscillator.test.ts +3 -4
  21. package/Tone/version.ts +1 -1
  22. package/build/Tone.js +1 -1
  23. package/build/Tone.js.map +1 -1
  24. package/build/esm/core/context/OnRunning.d.ts +8 -0
  25. package/build/esm/core/context/OnRunning.js +21 -0
  26. package/build/esm/core/context/OnRunning.js.map +1 -0
  27. package/build/esm/core/context/ToneWithContext.d.ts +14 -0
  28. package/build/esm/core/context/ToneWithContext.js +18 -0
  29. package/build/esm/core/context/ToneWithContext.js.map +1 -1
  30. package/build/esm/core/util/Interface.d.ts +2 -2
  31. package/build/esm/core/util/Interface.js.map +1 -1
  32. package/build/esm/core/util/TypeCheck.d.ts +8 -8
  33. package/build/esm/core/util/TypeCheck.js.map +1 -1
  34. package/build/esm/effect/FrequencyShifter.js +5 -3
  35. package/build/esm/effect/FrequencyShifter.js.map +1 -1
  36. package/build/esm/effect/LFOStereoEffect.d.ts +53 -0
  37. package/build/esm/effect/LFOStereoEffect.js +96 -0
  38. package/build/esm/effect/LFOStereoEffect.js.map +1 -0
  39. package/build/esm/effect/Phaser.d.ts +3 -16
  40. package/build/esm/effect/Phaser.js +6 -28
  41. package/build/esm/effect/Phaser.js.map +1 -1
  42. package/build/esm/effect/PitchShift.js +7 -5
  43. package/build/esm/effect/PitchShift.js.map +1 -1
  44. package/build/esm/effect/StereoWidener.d.ts +4 -0
  45. package/build/esm/effect/StereoWidener.js +7 -2
  46. package/build/esm/effect/StereoWidener.js.map +1 -1
  47. package/build/esm/effect/Tremolo.d.ts +4 -32
  48. package/build/esm/effect/Tremolo.js +9 -59
  49. package/build/esm/effect/Tremolo.js.map +1 -1
  50. package/build/esm/effect/Vibrato.js +4 -3
  51. package/build/esm/effect/Vibrato.js.map +1 -1
  52. package/build/esm/signal/ToneConstantSource.d.ts +1 -1
  53. package/build/esm/signal/ToneConstantSource.js +16 -27
  54. package/build/esm/signal/ToneConstantSource.js.map +1 -1
  55. package/build/esm/signal/WaveShaper.d.ts +5 -0
  56. package/build/esm/signal/WaveShaper.js +15 -7
  57. package/build/esm/signal/WaveShaper.js.map +1 -1
  58. package/build/esm/source/UserMedia.d.ts +3 -3
  59. package/build/esm/source/UserMedia.js +32 -25
  60. package/build/esm/source/UserMedia.js.map +1 -1
  61. package/build/esm/version.js +1 -1
  62. package/package.json +1 -1
@@ -1,4 +1,3 @@
1
- import { connect } from "../core/context/ToneAudioNode.js";
2
1
  import { NormalRange } from "../core/type/Units.js";
3
2
  import { optionsFromArguments } from "../core/util/Defaults.js";
4
3
  import { readOnly } from "../core/util/Interface.js";
@@ -9,6 +8,7 @@ import {
9
8
  import { Multiply } from "../signal/Multiply.js";
10
9
  import { Signal } from "../signal/Signal.js";
11
10
  import { Subtract } from "../signal/Subtract.js";
11
+ import { ToneConstantSource } from "../signal/ToneConstantSource.js";
12
12
 
13
13
  export interface StereoWidenerOptions extends MidSideEffectOptions {
14
14
  width: NormalRange;
@@ -57,6 +57,11 @@ export class StereoWidener extends MidSideEffect<StereoWidenerOptions> {
57
57
  */
58
58
  private _sideMult: Multiply;
59
59
 
60
+ /**
61
+ * A constant source to get the value of 1 for the subtract node
62
+ */
63
+ private _constant: ToneConstantSource;
64
+
60
65
  /**
61
66
  * @param width The stereo width. A width of 0 is mono and 1 is stereo. 0.5 is no change.
62
67
  */
@@ -90,7 +95,11 @@ export class StereoWidener extends MidSideEffect<StereoWidenerOptions> {
90
95
 
91
96
  this._oneMinusWidth = new Subtract({ context: this.context });
92
97
  this._oneMinusWidth.connect(this._twoTimesWidthMid);
93
- connect(this.context.getConstant(1), this._oneMinusWidth);
98
+ this._constant = new ToneConstantSource({
99
+ context: this.context,
100
+ offset: 1,
101
+ }).start();
102
+ this._constant.connect(this._oneMinusWidth);
94
103
  this.width.connect(this._oneMinusWidth.subtrahend);
95
104
 
96
105
  this._sideMult = new Multiply({ context: this.context });
@@ -113,6 +122,7 @@ export class StereoWidener extends MidSideEffect<StereoWidenerOptions> {
113
122
  this._twoTimesWidthMid.dispose();
114
123
  this._twoTimesWidthSide.dispose();
115
124
  this._oneMinusWidth.dispose();
125
+ this._constant.dispose();
116
126
  return this;
117
127
  }
118
128
  }
@@ -3,7 +3,6 @@ import { expect } from "chai";
3
3
  import { BasicTests } from "../../test/helper/Basic.js";
4
4
  import { CompareToFile } from "../../test/helper/CompareToFile.js";
5
5
  import { EffectTests } from "../../test/helper/EffectTests.js";
6
- import { Offline } from "../../test/helper/Offline.js";
7
6
  import { Oscillator } from "../source/index.js";
8
7
  import { Tremolo } from "./Tremolo.js";
9
8
 
@@ -60,28 +59,5 @@ describe("Tremolo", () => {
60
59
  expect(tremolo.frequency.value).to.be.closeTo(0.4, 0.01);
61
60
  tremolo.dispose();
62
61
  });
63
-
64
- it("can sync the frequency to the transport", async () => {
65
- const buffer = await Offline(({ transport }) => {
66
- const tremolo = new Tremolo(2);
67
- tremolo.sync();
68
- tremolo.frequency.toDestination();
69
- transport.bpm.setValueAtTime(transport.bpm.value * 2, 0.05);
70
- }, 0.1);
71
- expect(buffer.getValueAtTime(0)).to.be.closeTo(2, 0.1);
72
- expect(buffer.getValueAtTime(0.05)).to.be.closeTo(4, 0.1);
73
- });
74
-
75
- it("can unsync the frequency to the transport", async () => {
76
- const buffer = await Offline(({ transport }) => {
77
- const tremolo = new Tremolo(2);
78
- tremolo.sync();
79
- tremolo.frequency.toDestination();
80
- transport.bpm.setValueAtTime(transport.bpm.value * 2, 0.05);
81
- tremolo.unsync();
82
- }, 0.1);
83
- expect(buffer.getValueAtTime(0)).to.be.closeTo(2, 0.1);
84
- expect(buffer.getValueAtTime(0.05)).to.be.closeTo(2, 0.1);
85
- });
86
62
  });
87
63
  });
@@ -1,13 +1,12 @@
1
1
  import { Gain } from "../core/context/Gain.js";
2
- import { Degrees, Frequency, NormalRange, Time } from "../core/type/Units.js";
2
+ import { Degrees, Frequency, NormalRange } from "../core/type/Units.js";
3
3
  import { optionsFromArguments } from "../core/util/Defaults.js";
4
4
  import { readOnly } from "../core/util/Interface.js";
5
5
  import { Signal } from "../signal/Signal.js";
6
- import { LFO } from "../source/oscillator/LFO.js";
7
6
  import { ToneOscillatorType } from "../source/oscillator/OscillatorInterface.js";
8
- import { StereoEffect, StereoEffectOptions } from "./StereoEffect.js";
7
+ import { LFOStereoEffect, LFOStereoEffectOptions } from "./LFOStereoEffect.js";
9
8
 
10
- export interface TremoloOptions extends StereoEffectOptions {
9
+ export interface TremoloOptions extends LFOStereoEffectOptions {
11
10
  frequency: Frequency;
12
11
  type: ToneOscillatorType;
13
12
  depth: NormalRange;
@@ -26,19 +25,9 @@ export interface TremoloOptions extends StereoEffectOptions {
26
25
  *
27
26
  * @category Effect
28
27
  */
29
- export class Tremolo extends StereoEffect<TremoloOptions> {
28
+ export class Tremolo extends LFOStereoEffect<TremoloOptions> {
30
29
  readonly name: string = "Tremolo";
31
30
 
32
- /**
33
- * The tremolo LFO in the left channel
34
- */
35
- private _lfoL: LFO;
36
-
37
- /**
38
- * The tremolo LFO in the left channel
39
- */
40
- private _lfoR: LFO;
41
-
42
31
  /**
43
32
  * Where the gain is multiplied
44
33
  */
@@ -49,11 +38,6 @@ export class Tremolo extends StereoEffect<TremoloOptions> {
49
38
  */
50
39
  private _amplitudeR: Gain;
51
40
 
52
- /**
53
- * The frequency of the tremolo.
54
- */
55
- readonly frequency: Signal<"frequency">;
56
-
57
41
  /**
58
42
  * The depth of the effect. A depth of 0, has no effect
59
43
  * on the amplitude, and a depth of 1 makes the amplitude
@@ -74,25 +58,16 @@ export class Tremolo extends StereoEffect<TremoloOptions> {
74
58
  ]);
75
59
  super(options);
76
60
 
77
- this._lfoL = new LFO({
78
- context: this.context,
79
- type: options.type,
80
- min: 1,
81
- max: 0,
82
- });
83
- this._lfoR = new LFO({
84
- context: this.context,
85
- type: options.type,
86
- min: 1,
87
- max: 0,
88
- });
61
+ // invert the lfo min/max so it moves from full gain to 0 gain
62
+ this._lfoL.min = 1;
63
+ this._lfoL.max = 0;
64
+ this._lfoR.min = 1;
65
+ this._lfoR.max = 0;
66
+ this.type = options.type;
67
+
89
68
  this._amplitudeL = new Gain({ context: this.context });
90
69
  this._amplitudeR = new Gain({ context: this.context });
91
- this.frequency = new Signal({
92
- context: this.context,
93
- value: options.frequency,
94
- units: "frequency",
95
- });
70
+
96
71
  this.depth = new Signal({
97
72
  context: this.context,
98
73
  value: options.depth,
@@ -104,13 +79,12 @@ export class Tremolo extends StereoEffect<TremoloOptions> {
104
79
  this.connectEffectRight(this._amplitudeR);
105
80
  this._lfoL.connect(this._amplitudeL.gain);
106
81
  this._lfoR.connect(this._amplitudeR.gain);
107
- this.frequency.fan(this._lfoL.frequency, this._lfoR.frequency);
108
82
  this.depth.fan(this._lfoR.amplitude, this._lfoL.amplitude);
109
83
  this.spread = options.spread;
110
84
  }
111
85
 
112
86
  static getDefaults(): TremoloOptions {
113
- return Object.assign(StereoEffect.getDefaults(), {
87
+ return Object.assign(LFOStereoEffect.getDefaults(), {
114
88
  frequency: 10,
115
89
  type: "sine" as const,
116
90
  depth: 0.5,
@@ -118,44 +92,6 @@ export class Tremolo extends StereoEffect<TremoloOptions> {
118
92
  });
119
93
  }
120
94
 
121
- /**
122
- * Start the tremolo.
123
- */
124
- start(time?: Time): this {
125
- this._lfoL.start(time);
126
- this._lfoR.start(time);
127
- return this;
128
- }
129
-
130
- /**
131
- * Stop the tremolo.
132
- */
133
- stop(time?: Time): this {
134
- this._lfoL.stop(time);
135
- this._lfoR.stop(time);
136
- return this;
137
- }
138
-
139
- /**
140
- * Sync the effect to the transport.
141
- */
142
- sync(): this {
143
- this._lfoL.sync();
144
- this._lfoR.sync();
145
- this.context.transport.syncSignal(this.frequency);
146
- return this;
147
- }
148
-
149
- /**
150
- * Unsync the filter from the transport
151
- */
152
- unsync(): this {
153
- this._lfoL.unsync();
154
- this._lfoR.unsync();
155
- this.context.transport.unsyncSignal(this.frequency);
156
- return this;
157
- }
158
-
159
95
  /**
160
96
  * The oscillator type.
161
97
  */
@@ -181,11 +117,8 @@ export class Tremolo extends StereoEffect<TremoloOptions> {
181
117
 
182
118
  dispose(): this {
183
119
  super.dispose();
184
- this._lfoL.dispose();
185
- this._lfoR.dispose();
186
120
  this._amplitudeL.dispose();
187
121
  this._amplitudeR.dispose();
188
- this.frequency.dispose();
189
122
  this.depth.dispose();
190
123
  return this;
191
124
  }
@@ -66,15 +66,17 @@ export class Vibrato extends Effect<VibratoOptions> {
66
66
  max: options.maxDelay,
67
67
  frequency: options.frequency,
68
68
  phase: -90, // offset the phase so the resting position is in the center
69
- })
70
- .start()
71
- .connect(this._delayNode.delayTime);
69
+ }).connect(this._delayNode.delayTime);
72
70
  this.frequency = this._lfo.frequency;
73
71
  this.depth = this._lfo.amplitude;
74
72
 
75
73
  this.depth.value = options.depth;
76
74
  readOnly(this, ["frequency", "depth"]);
77
75
  this.effectSend.chain(this._delayNode, this.effectReturn);
76
+
77
+ this._onContextRunning(() => {
78
+ this._lfo.start();
79
+ });
78
80
  }
79
81
 
80
82
  static getDefaults(): VibratoOptions {
@@ -49,24 +49,16 @@ export class ToneConstantSource<
49
49
  );
50
50
  super(options);
51
51
 
52
- const isSuspended =
53
- !this.context.isOffline && this.context.state !== "running";
54
-
55
- if (!isSuspended) {
56
- this._source = this.context.createConstantSource();
57
- connect(this._source, this._gainNode);
58
- } else {
59
- this.context.on("statechange", this._contextStarted);
60
- }
52
+ this._onContextRunning(() => this._contextStarted());
61
53
 
62
54
  this.offset = new Param({
63
55
  context: this.context,
64
56
  convert: options.convert,
65
- param: isSuspended
57
+ param: !this._source
66
58
  ? // placeholder param until the context is started
67
59
  this.context.createGain().gain
68
- : this._source?.offset,
69
- swappable: isSuspended,
60
+ : this._source.offset,
61
+ swappable: !this._source,
70
62
  units: options.units,
71
63
  value: options.offset,
72
64
  minValue: options.minValue,
@@ -85,17 +77,14 @@ export class ToneConstantSource<
85
77
  /**
86
78
  * Once the context is started, kick off source.
87
79
  */
88
- private readonly _contextStarted = (state: AudioContextState) => {
89
- if (state !== "running") {
90
- return;
91
- }
80
+ private _contextStarted() {
92
81
  this._source = this.context.createConstantSource();
93
82
  connect(this._source, this._gainNode);
94
- this.offset.setParam(this._source.offset);
83
+ this.offset?.setParam(this._source.offset);
95
84
  if (this.state === "started") {
96
85
  this._source.start(0);
97
86
  }
98
- };
87
+ }
99
88
 
100
89
  /**
101
90
  * Start the source node at the given time
@@ -123,7 +112,6 @@ export class ToneConstantSource<
123
112
  }
124
113
  this._source?.disconnect();
125
114
  this.offset.dispose();
126
- this.context.off("statechange", this._contextStarted);
127
115
  return this;
128
116
  }
129
117
  }
@@ -66,14 +66,9 @@ export class WaveShaper extends SignalOperator<WaveShaperOptions> {
66
66
  );
67
67
  super(options);
68
68
 
69
- if (
70
- isArray(options.mapping) ||
71
- options.mapping instanceof Float32Array
72
- ) {
73
- this.curve = Float32Array.from(options.mapping);
74
- } else if (isFunction(options.mapping)) {
75
- this.setMap(options.mapping, options.length);
76
- }
69
+ this._onContextRunning(() => {
70
+ this.initCurve(options.mapping, options.length);
71
+ });
77
72
  }
78
73
 
79
74
  static getDefaults(): WaveShaperOptions {
@@ -82,6 +77,18 @@ export class WaveShaper extends SignalOperator<WaveShaperOptions> {
82
77
  });
83
78
  }
84
79
 
80
+ /**
81
+ * Set the curve for the first time. This is run only after the audio context is
82
+ * running to avoid any context warnings.
83
+ */
84
+ private initCurve(mapping?: WaveShaperMapping, length?: number): void {
85
+ if (isArray(mapping) || mapping instanceof Float32Array) {
86
+ this.curve = Float32Array.from(mapping);
87
+ } else if (isFunction(mapping)) {
88
+ this.setMap(mapping, length);
89
+ }
90
+ }
91
+
85
92
  /**
86
93
  * Uses a mapping function to set the value of the curve.
87
94
  * @param mapping The function used to define the values.
@@ -1,7 +1,11 @@
1
- import { expect } from "chai";
1
+ import { expect, use } from "chai";
2
+ import sinon from "sinon";
3
+ import sinonChai from "sinon-chai";
4
+ use(sinonChai);
2
5
 
3
6
  import { BasicTests } from "../../test/helper/Basic.js";
4
- import { OfflineContext } from "../core/index.js";
7
+ import { Context } from "../core/context/Context.js";
8
+ import { OfflineContext } from "../core/context/OfflineContext.js";
5
9
  import { UserMedia } from "./UserMedia.js";
6
10
 
7
11
  describe("UserMedia", () => {
@@ -37,139 +41,154 @@ describe("UserMedia", () => {
37
41
  });
38
42
  });
39
43
 
40
- context("Opening and closing", function () {
41
- // long timeout to give testers time to allow the microphone
42
- this.timeout(100000);
43
-
44
- let HAS_USER_MEDIA_INPUTS = false;
45
-
46
- before(() => {
47
- return UserMedia.enumerateDevices().then((devices) => {
48
- HAS_USER_MEDIA_INPUTS = devices.length > 0;
49
- });
44
+ context("Opening and closing", () => {
45
+ beforeEach(() => {
46
+ const mockTrack = { stop: sinon.stub() };
47
+ const mockStream = {
48
+ active: true,
49
+ getAudioTracks: () => [mockTrack],
50
+ } as unknown as MediaStream;
51
+ const mockMediaStreamSource = {
52
+ connect: sinon.stub(),
53
+ disconnect: sinon.stub(),
54
+ numberOfOutputs: 1,
55
+ } as unknown as MediaStreamAudioSourceNode;
56
+ sinon.stub(UserMedia, "enumerateDevices").resolves([
57
+ {
58
+ deviceId: "default",
59
+ groupId: "default",
60
+ label: "Default Device",
61
+ },
62
+ {
63
+ deviceId: "other",
64
+ groupId: "default",
65
+ label: "Other Device",
66
+ },
67
+ ] as MediaDeviceInfo[]);
68
+ sinon
69
+ .stub(navigator.mediaDevices, "getUserMedia")
70
+ .resolves(mockStream);
71
+ sinon
72
+ .stub(Context.prototype, "createMediaStreamSource")
73
+ .returns(mockMediaStreamSource);
74
+ });
75
+
76
+ afterEach(() => {
77
+ sinon.restore();
78
+ });
79
+
80
+ it("open returns a promise", async () => {
81
+ const extIn = new UserMedia();
82
+ const promise = extIn.open();
83
+ expect(promise).to.be.instanceOf(Promise);
84
+ await promise;
85
+ extIn.dispose();
50
86
  });
51
87
 
52
- it("open returns a promise", () => {
53
- if (HAS_USER_MEDIA_INPUTS) {
54
- const extIn = new UserMedia();
55
- const promise = extIn.open();
56
- expect(promise).to.have.property("then");
57
- return promise.then(() => {
58
- extIn.dispose();
59
- });
60
- }
88
+ it("can open an input", async () => {
89
+ const extIn = new UserMedia();
90
+ await extIn.open();
91
+ extIn.dispose();
61
92
  });
62
93
 
63
- it("can open an input", () => {
64
- if (HAS_USER_MEDIA_INPUTS) {
65
- const extIn = new UserMedia();
66
- return extIn.open().then(() => {
67
- extIn.dispose();
68
- });
69
- }
94
+ it("can open an input by name", async () => {
95
+ const extIn = new UserMedia();
96
+ const devices = await UserMedia.enumerateDevices();
97
+ const name = devices[0].deviceId;
98
+
99
+ await extIn.open(name);
100
+
101
+ expect(extIn.deviceId).to.equal(name);
102
+ expect(navigator.mediaDevices.getUserMedia).to.have.been.calledWith(
103
+ sinon.match.hasNested("audio.deviceId", name)
104
+ );
105
+ extIn.dispose();
70
106
  });
71
107
 
72
- it("can open an input by name", () => {
73
- if (HAS_USER_MEDIA_INPUTS) {
74
- const extIn = new UserMedia();
75
- let name: string;
76
- return UserMedia.enumerateDevices()
77
- .then((devices) => {
78
- name = devices[0].deviceId;
79
- return extIn.open(name);
80
- })
81
- .then(() => {
82
- expect(extIn.deviceId).to.equal(name);
83
- extIn.dispose();
84
- });
85
- }
108
+ it("can open an input by index", async () => {
109
+ const extIn = new UserMedia();
110
+
111
+ await extIn.open(0);
112
+
113
+ expect(navigator.mediaDevices.getUserMedia).to.have.been.calledWith(
114
+ sinon.match.hasNested("audio.deviceId", "default")
115
+ );
116
+ extIn.dispose();
86
117
  });
87
118
 
88
- it("can open an input by index", () => {
89
- if (HAS_USER_MEDIA_INPUTS) {
90
- const extIn = new UserMedia();
91
- return extIn.open(0).then(() => {
92
- extIn.dispose();
93
- });
94
- }
119
+ it("can pass in additional constraints", async () => {
120
+ const extIn = new UserMedia();
121
+ await extIn.open({
122
+ preferCurrentTab: true,
123
+ });
124
+ expect(navigator.mediaDevices.getUserMedia).to.have.been.calledWith(
125
+ sinon.match.hasNested("preferCurrentTab", true)
126
+ );
127
+ extIn.dispose();
95
128
  });
96
129
 
97
- it("throws an error if it cant find the device name", () => {
98
- if (HAS_USER_MEDIA_INPUTS) {
99
- const extIn = new UserMedia();
100
- return extIn
101
- .open("doesn't exist")
102
- .then(() => {
103
- throw new Error("shouldn't call 'then'");
104
- })
105
- .catch(() => {
106
- extIn.dispose();
107
- });
130
+ it("throws an error if it cant find the device name", async () => {
131
+ const extIn = new UserMedia();
132
+ try {
133
+ await extIn.open("doesn't exist");
134
+ throw new Error("shouldn't reach here");
135
+ } catch {
136
+ extIn.dispose();
108
137
  }
109
138
  });
110
139
 
111
- it("is 'started' after media is open and 'stopped' otherwise", () => {
112
- if (HAS_USER_MEDIA_INPUTS) {
113
- const extIn = new UserMedia();
114
- expect(extIn.state).to.equal("stopped");
115
- return extIn.open().then(() => {
116
- expect(extIn.state).to.equal("started");
117
- extIn.dispose();
118
- });
119
- }
140
+ it("is 'started' after media is open and 'stopped' otherwise", async () => {
141
+ const extIn = new UserMedia();
142
+ expect(extIn.state).to.equal("stopped");
143
+
144
+ await extIn.open();
145
+
146
+ expect(extIn.state).to.equal("started");
147
+ extIn.dispose();
120
148
  });
121
149
 
122
- it("has a label, group and device id when open", () => {
123
- if (HAS_USER_MEDIA_INPUTS) {
124
- const extIn = new UserMedia();
125
- return extIn.open().then(() => {
126
- expect(extIn.deviceId).to.be.a("string");
127
- expect(extIn.groupId).to.be.a("string");
128
- expect(extIn.label).to.be.a("string");
129
- extIn.dispose();
130
- });
131
- }
150
+ it("has a label, group and device id when open", async () => {
151
+ const extIn = new UserMedia();
152
+ expect(extIn.deviceId).to.be.undefined;
153
+ expect(extIn.groupId).to.be.undefined;
154
+ expect(extIn.label).to.be.undefined;
155
+
156
+ await extIn.open();
157
+
158
+ expect(extIn.deviceId).to.be.a("string");
159
+ expect(extIn.groupId).to.be.a("string");
160
+ expect(extIn.label).to.be.a("string");
161
+ extIn.dispose();
132
162
  });
133
163
 
134
- it("can reopen an input", () => {
135
- if (HAS_USER_MEDIA_INPUTS) {
136
- const extIn = new UserMedia();
137
- return extIn
138
- .open()
139
- .then(() => {
140
- return extIn.open();
141
- })
142
- .then(() => {
143
- extIn.dispose();
144
- });
145
- }
164
+ it("can reopen an input", async () => {
165
+ const extIn = new UserMedia();
166
+ await extIn.open();
167
+ extIn.close();
168
+ await extIn.open();
169
+ extIn.dispose();
146
170
  });
147
171
 
148
- it("can close an input", () => {
149
- if (HAS_USER_MEDIA_INPUTS) {
150
- const extIn = new UserMedia();
151
- return extIn.open().then(() => {
152
- extIn.close();
153
- extIn.dispose();
154
- });
155
- }
172
+ it("can close an input", async () => {
173
+ const extIn = new UserMedia();
174
+ await extIn.open();
175
+ extIn.close();
176
+ extIn.dispose();
156
177
  });
157
178
 
158
- it("can enumerate devices", () => {
159
- return UserMedia.enumerateDevices().then((devices) => {
160
- expect(devices).to.be.instanceOf(Array);
161
- });
179
+ it("can enumerate devices", async () => {
180
+ const devices = await UserMedia.enumerateDevices();
181
+ expect(devices).to.be.instanceOf(Array);
162
182
  });
163
183
 
164
- it("doesn't work in OfflineContext", (done) => {
165
- if (HAS_USER_MEDIA_INPUTS) {
166
- const context = new OfflineContext(2, 2, 44100);
167
- const extIn = new UserMedia({ context });
168
- extIn.open().catch(() => {
169
- done();
170
- });
171
- } else {
172
- done();
184
+ it("doesn't work in OfflineContext", async () => {
185
+ const context = new OfflineContext(2, 2, 44100);
186
+ const extIn = new UserMedia({ context });
187
+ try {
188
+ await extIn.open();
189
+ throw new Error("shouldn't reach here");
190
+ } catch {
191
+ // expected to throw in OfflineContext
173
192
  }
174
193
  });
175
194
  });