@@ -44,19 +44,14 @@ let stopButton;
4444// Transformation chain elements
4545let processor ;
4646let generator ;
47- let transformer ;
4847
4948// Stream from getUserMedia
5049let stream ;
5150// Output from the transform
5251let processedStream ;
5352
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 ;
6055
6156// Initialize on page load.
6257async function init ( ) {
@@ -73,85 +68,35 @@ const constraints = window.constraints = {
7368 video : false
7469} ;
7570
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-
11571async function start ( ) {
11672 startButton . disabled = true ;
11773 try {
11874 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 ( ) ;
11993 } catch ( error ) {
12094 const errorMessage =
12195 'navigator.MediaDevices.getUserMedia error: ' + error . message + ' ' +
12296 error . name ;
12397 document . getElementById ( 'errorMsg' ) . innerText = errorMessage ;
12498 console . log ( errorMessage ) ;
12599 }
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 ( ) ;
155100}
156101
157102async function stop ( ) {
@@ -161,8 +106,7 @@ async function stop() {
161106 stream . getTracks ( ) . forEach ( track => {
162107 track . stop ( ) ;
163108 } ) ;
164- abortController . abort ( ) ;
165- abortController = null ;
109+ worker . postMessage ( { command : 'abort' } ) ;
166110 startButton . disabled = false ;
167111}
168112
0 commit comments