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.
- package/Tone/core/context/OnRunning.test.ts +63 -0
- package/Tone/core/context/OnRunning.ts +26 -0
- package/Tone/core/context/ToneWithContext.test.ts +82 -0
- package/Tone/core/context/ToneWithContext.ts +24 -0
- package/Tone/core/util/Interface.ts +8 -2
- package/Tone/core/util/TypeCheck.ts +9 -9
- package/Tone/effect/FrequencyShifter.ts +5 -3
- package/Tone/effect/LFOStereoEffect.test.ts +85 -0
- package/Tone/effect/LFOStereoEffect.ts +136 -0
- package/Tone/effect/Phaser.ts +9 -45
- package/Tone/effect/PitchShift.ts +8 -5
- package/Tone/effect/StereoWidener.ts +12 -2
- package/Tone/effect/Tremolo.test.ts +0 -24
- package/Tone/effect/Tremolo.ts +13 -80
- package/Tone/effect/Vibrato.ts +5 -3
- package/Tone/signal/ToneConstantSource.ts +7 -19
- package/Tone/signal/WaveShaper.ts +15 -8
- package/Tone/source/UserMedia.test.ts +131 -112
- package/Tone/source/UserMedia.ts +37 -26
- package/Tone/source/oscillator/Oscillator.test.ts +3 -4
- package/Tone/version.ts +1 -1
- package/build/Tone.js +1 -1
- package/build/Tone.js.map +1 -1
- package/build/esm/core/context/OnRunning.d.ts +8 -0
- package/build/esm/core/context/OnRunning.js +21 -0
- package/build/esm/core/context/OnRunning.js.map +1 -0
- package/build/esm/core/context/ToneWithContext.d.ts +14 -0
- package/build/esm/core/context/ToneWithContext.js +18 -0
- package/build/esm/core/context/ToneWithContext.js.map +1 -1
- package/build/esm/core/util/Interface.d.ts +2 -2
- package/build/esm/core/util/Interface.js.map +1 -1
- package/build/esm/core/util/TypeCheck.d.ts +8 -8
- package/build/esm/core/util/TypeCheck.js.map +1 -1
- package/build/esm/effect/FrequencyShifter.js +5 -3
- package/build/esm/effect/FrequencyShifter.js.map +1 -1
- package/build/esm/effect/LFOStereoEffect.d.ts +53 -0
- package/build/esm/effect/LFOStereoEffect.js +96 -0
- package/build/esm/effect/LFOStereoEffect.js.map +1 -0
- package/build/esm/effect/Phaser.d.ts +3 -16
- package/build/esm/effect/Phaser.js +6 -28
- package/build/esm/effect/Phaser.js.map +1 -1
- package/build/esm/effect/PitchShift.js +7 -5
- package/build/esm/effect/PitchShift.js.map +1 -1
- package/build/esm/effect/StereoWidener.d.ts +4 -0
- package/build/esm/effect/StereoWidener.js +7 -2
- package/build/esm/effect/StereoWidener.js.map +1 -1
- package/build/esm/effect/Tremolo.d.ts +4 -32
- package/build/esm/effect/Tremolo.js +9 -59
- package/build/esm/effect/Tremolo.js.map +1 -1
- package/build/esm/effect/Vibrato.js +4 -3
- package/build/esm/effect/Vibrato.js.map +1 -1
- package/build/esm/signal/ToneConstantSource.d.ts +1 -1
- package/build/esm/signal/ToneConstantSource.js +16 -27
- package/build/esm/signal/ToneConstantSource.js.map +1 -1
- package/build/esm/signal/WaveShaper.d.ts +5 -0
- package/build/esm/signal/WaveShaper.js +15 -7
- package/build/esm/signal/WaveShaper.js.map +1 -1
- package/build/esm/source/UserMedia.d.ts +3 -3
- package/build/esm/source/UserMedia.js +32 -25
- package/build/esm/source/UserMedia.js.map +1 -1
- package/build/esm/version.js +1 -1
- 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
|
-
|
|
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
|
});
|
package/Tone/effect/Tremolo.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { Gain } from "../core/context/Gain.js";
|
|
2
|
-
import { Degrees, Frequency, NormalRange
|
|
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 {
|
|
7
|
+
import { LFOStereoEffect, LFOStereoEffectOptions } from "./LFOStereoEffect.js";
|
|
9
8
|
|
|
10
|
-
export interface TremoloOptions extends
|
|
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
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
}
|
package/Tone/effect/Vibrato.ts
CHANGED
|
@@ -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
|
-
|
|
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:
|
|
57
|
+
param: !this._source
|
|
66
58
|
? // placeholder param until the context is started
|
|
67
59
|
this.context.createGain().gain
|
|
68
|
-
: this._source
|
|
69
|
-
swappable:
|
|
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
|
|
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
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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 {
|
|
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",
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
160
|
-
|
|
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", (
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
extIn.open()
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
});
|