@@ -16,7 +16,6 @@ limitations under the License.
1616
1717import * as Recorder from 'opus-recorder' ;
1818import encoderPath from 'opus-recorder/dist/encoderWorker.min.js' ;
19- import mxVoiceWorkletPath from './mxVoiceWorklet' ;
2019import { MatrixClient } from "matrix-js-sdk/src/client" ;
2120import CallMediaHandler from "../CallMediaHandler" ;
2221import { SimpleObservable } from "matrix-widget-api" ;
@@ -37,7 +36,7 @@ export class VoiceRecorder {
3736 private recorderSource : MediaStreamAudioSourceNode ;
3837 private recorderStream : MediaStream ;
3938 private recorderFFT : AnalyserNode ;
40- private recorderWorklet : AudioWorkletNode ;
39+ private recorderProcessor : ScriptProcessorNode ;
4140 private buffer = new Uint8Array ( 0 ) ;
4241 private mxc : string ;
4342 private recording = false ;
@@ -71,20 +70,18 @@ export class VoiceRecorder {
7170 // it makes the time domain less than helpful.
7271 this . recorderFFT . fftSize = 64 ;
7372
74- await this . recorderContext . audioWorklet . addModule ( mxVoiceWorkletPath ) ;
75- this . recorderWorklet = new AudioWorkletNode ( this . recorderContext , "mx-voice-worklet" ) ;
73+ // We use an audio processor to get accurate timing information.
74+ // The size of the audio buffer largely decides how quickly we push timing/waveform data
75+ // out of this class. Smaller buffers mean we update more frequently as we can't hold as
76+ // many bytes. Larger buffers mean slower updates. For scale, 1024 gives us about 30Hz of
77+ // updates and 2048 gives us about 20Hz. We use 1024 to get as close to perceived realtime
78+ // as possible. Must be a power of 2.
79+ this . recorderProcessor = this . recorderContext . createScriptProcessor ( 1024 , CHANNELS , CHANNELS ) ;
7680
7781 // Connect our inputs and outputs
7882 this . recorderSource . connect ( this . recorderFFT ) ;
79- this . recorderSource . connect ( this . recorderWorklet ) ;
80- this . recorderWorklet . connect ( this . recorderContext . destination ) ;
81-
82- // Dev note: we can't use `addEventListener` for some reason. It just doesn't work.
83- this . recorderWorklet . port . onmessage = ( ev ) => {
84- if ( ev . data [ 'ev' ] === 'proc' ) {
85- this . tryUpdateLiveData ( ev . data [ 'timeMs' ] ) ;
86- }
87- } ;
83+ this . recorderSource . connect ( this . recorderProcessor ) ;
84+ this . recorderProcessor . connect ( this . recorderContext . destination ) ;
8885
8986 this . recorder = new Recorder ( {
9087 encoderPath, // magic from webpack
@@ -131,7 +128,7 @@ export class VoiceRecorder {
131128 return this . mxc ;
132129 }
133130
134- private tryUpdateLiveData = ( timeMillis : number ) => {
131+ private tryUpdateLiveData = ( ev : AudioProcessingEvent ) => {
135132 if ( ! this . recording ) return ;
136133
137134 // The time domain is the input to the FFT, which means we use an array of the same
@@ -153,7 +150,7 @@ export class VoiceRecorder {
153150
154151 this . observable . update ( {
155152 waveform : translatedData ,
156- timeSeconds : timeMillis / 1000 ,
153+ timeSeconds : ev . playbackTime ,
157154 } ) ;
158155 } ;
159156
@@ -169,6 +166,7 @@ export class VoiceRecorder {
169166 }
170167 this . observable = new SimpleObservable < IRecordingUpdate > ( ) ;
171168 await this . makeRecorder ( ) ;
169+ this . recorderProcessor . addEventListener ( "audioprocess" , this . tryUpdateLiveData ) ;
172170 await this . recorder . start ( ) ;
173171 this . recording = true ;
174172 }
@@ -180,7 +178,6 @@ export class VoiceRecorder {
180178
181179 // Disconnect the source early to start shutting down resources
182180 this . recorderSource . disconnect ( ) ;
183- this . recorderWorklet . disconnect ( ) ;
184181 await this . recorder . stop ( ) ;
185182
186183 // close the context after the recorder so the recorder doesn't try to
@@ -192,6 +189,7 @@ export class VoiceRecorder {
192189
193190 // Finally do our post-processing and clean up
194191 this . recording = false ;
192+ this . recorderProcessor . removeEventListener ( "audioprocess" , this . tryUpdateLiveData ) ;
195193 await this . recorder . close ( ) ;
196194
197195 return this . buffer ;
0 commit comments