@@ -44,19 +44,14 @@ let stopButton;
44
44
// Transformation chain elements
45
45
let processor ;
46
46
let generator ;
47
- let transformer ;
48
47
49
48
// Stream from getUserMedia
50
49
let stream ;
51
50
// Output from the transform
52
51
let processedStream ;
53
52
54
- // Adjust this value to increase/decrease the amount of filtering.
55
- // eslint-disable-next-line prefer-const
56
- let cutoff = 100 ;
57
-
58
- // An AbortController used to stop the transform.
59
- let abortController ;
53
+ // Worker for processing
54
+ let worker ;
60
55
61
56
// Initialize on page load.
62
57
async function init ( ) {
@@ -73,85 +68,35 @@ const constraints = window.constraints = {
73
68
video : false
74
69
} ;
75
70
76
- // Returns a low-pass transform function for use with TransformStream.
77
- function lowPassFilter ( ) {
78
- const format = 'f32-planar' ;
79
- let lastValuePerChannel = undefined ;
80
- return ( data , controller ) => {
81
- const rc = 1.0 / ( cutoff * 2 * Math . PI ) ;
82
- const dt = 1.0 / data . sampleRate ;
83
- const alpha = dt / ( rc + dt ) ;
84
- const nChannels = data . numberOfChannels ;
85
- if ( ! lastValuePerChannel ) {
86
- console . log ( `Audio stream has ${ nChannels } channels.` ) ;
87
- lastValuePerChannel = Array ( nChannels ) . fill ( 0 ) ;
88
- }
89
- const buffer = new Float32Array ( data . numberOfFrames * nChannels ) ;
90
- for ( let c = 0 ; c < nChannels ; c ++ ) {
91
- const offset = data . numberOfFrames * c ;
92
- const samples = buffer . subarray ( offset , offset + data . numberOfFrames ) ;
93
- data . copyTo ( samples , { planeIndex : c , format} ) ;
94
- let lastValue = lastValuePerChannel [ c ] ;
95
-
96
- // Apply low-pass filter to samples.
97
- for ( let i = 0 ; i < samples . length ; ++ i ) {
98
- lastValue = lastValue + alpha * ( samples [ i ] - lastValue ) ;
99
- samples [ i ] = lastValue ;
100
- }
101
-
102
- lastValuePerChannel [ c ] = lastValue ;
103
- }
104
- controller . enqueue ( new AudioData ( {
105
- format,
106
- sampleRate : data . sampleRate ,
107
- numberOfFrames : data . numberOfFrames ,
108
- numberOfChannels : nChannels ,
109
- timestamp : data . timestamp ,
110
- data : buffer
111
- } ) ) ;
112
- } ;
113
- }
114
-
115
71
async function start ( ) {
116
72
startButton . disabled = true ;
117
73
try {
118
74
stream = await navigator . mediaDevices . getUserMedia ( constraints ) ;
75
+ const audioTracks = stream . getAudioTracks ( ) ;
76
+ console . log ( 'Using audio device: ' + audioTracks [ 0 ] . label ) ;
77
+ stream . oninactive = ( ) => {
78
+ console . log ( 'Stream ended' ) ;
79
+ } ;
80
+
81
+ processor = new MediaStreamTrackProcessor ( audioTracks [ 0 ] ) ;
82
+ generator = new MediaStreamTrackGenerator ( 'audio' ) ;
83
+ const source = processor . readable ;
84
+ const sink = generator . writable ;
85
+ worker = new Worker ( 'js/worker.js' ) ;
86
+ worker . postMessage ( { source : source , sink : sink } , [ source , sink ] ) ;
87
+
88
+ processedStream = new MediaStream ( ) ;
89
+ processedStream . addTrack ( generator ) ;
90
+ audio . srcObject = processedStream ;
91
+ stopButton . disabled = false ;
92
+ await audio . play ( ) ;
119
93
} catch ( error ) {
120
94
const errorMessage =
121
95
'navigator.MediaDevices.getUserMedia error: ' + error . message + ' ' +
122
96
error . name ;
123
97
document . getElementById ( 'errorMsg' ) . innerText = errorMessage ;
124
98
console . log ( errorMessage ) ;
125
99
}
126
- const audioTracks = stream . getAudioTracks ( ) ;
127
- console . log ( 'Using audio device: ' + audioTracks [ 0 ] . label ) ;
128
- stream . oninactive = ( ) => {
129
- console . log ( 'Stream ended' ) ;
130
- } ;
131
-
132
- processor = new MediaStreamTrackProcessor ( audioTracks [ 0 ] ) ;
133
- generator = new MediaStreamTrackGenerator ( 'audio' ) ;
134
- const source = processor . readable ;
135
- const sink = generator . writable ;
136
- transformer = new TransformStream ( { transform : lowPassFilter ( ) } ) ;
137
- abortController = new AbortController ( ) ;
138
- const signal = abortController . signal ;
139
- const promise = source . pipeThrough ( transformer , { signal} ) . pipeTo ( sink ) ;
140
- promise . catch ( ( e ) => {
141
- if ( signal . aborted ) {
142
- console . log ( 'Shutting down streams after abort.' ) ;
143
- } else {
144
- console . error ( 'Error from stream transform:' , e ) ;
145
- }
146
- source . cancel ( e ) ;
147
- sink . abort ( e ) ;
148
- } ) ;
149
-
150
- processedStream = new MediaStream ( ) ;
151
- processedStream . addTrack ( generator ) ;
152
- audio . srcObject = processedStream ;
153
- stopButton . disabled = false ;
154
- await audio . play ( ) ;
155
100
}
156
101
157
102
async function stop ( ) {
@@ -161,8 +106,7 @@ async function stop() {
161
106
stream . getTracks ( ) . forEach ( track => {
162
107
track . stop ( ) ;
163
108
} ) ;
164
- abortController . abort ( ) ;
165
- abortController = null ;
109
+ worker . postMessage ( { command : 'abort' } ) ;
166
110
startButton . disabled = false ;
167
111
}
168
112
0 commit comments