@@ -48,14 +48,28 @@ struct OfflineAudioContextRenderer {
4848 suspend_callbacks : Vec < ( usize , Box < OfflineAudioContextCallback > ) > ,
4949 /// channel to listen for `resume` calls on a suspended context
5050 resume_receiver : mpsc:: Receiver < ( ) > ,
51- /// event handler for oncomplete event
51+ /// event handler for statechange event
52+ onstatechange_handler : Option < Box < dyn FnMut ( Event ) + Send + ' static > > ,
53+ /// event handler for complete event
5254 oncomplete_handler : Option < Box < dyn FnOnce ( OfflineAudioCompletionEvent ) + Send + ' static > > ,
5355}
5456
5557impl BaseAudioContext for OfflineAudioContext {
5658 fn base ( & self ) -> & ConcreteBaseAudioContext {
5759 & self . base
5860 }
61+
62+ fn set_onstatechange < F : FnMut ( Event ) + Send + ' static > ( & self , callback : F ) {
63+ if let Some ( renderer) = self . renderer . lock ( ) . unwrap ( ) . as_mut ( ) {
64+ renderer. onstatechange_handler = Some ( Box :: new ( callback) ) ;
65+ }
66+ }
67+
68+ fn clear_onstatechange ( & self ) {
69+ if let Some ( renderer) = self . renderer . lock ( ) . unwrap ( ) . as_mut ( ) {
70+ renderer. onstatechange_handler = None ;
71+ }
72+ }
5973}
6074
6175impl OfflineAudioContext {
@@ -114,6 +128,7 @@ impl OfflineAudioContext {
114128 suspend_promises : Vec :: new ( ) ,
115129 suspend_callbacks : Vec :: new ( ) ,
116130 resume_receiver,
131+ onstatechange_handler : None ,
117132 oncomplete_handler : None ,
118133 } ;
119134
@@ -147,15 +162,20 @@ impl OfflineAudioContext {
147162 let OfflineAudioContextRenderer {
148163 renderer,
149164 suspend_callbacks,
150- mut oncomplete_handler,
165+ oncomplete_handler,
166+ mut onstatechange_handler,
151167 ..
152168 } = renderer;
153169
154170 self . base . set_state ( AudioContextState :: Running ) ;
171+ Self :: emit_statechange ( & mut onstatechange_handler) ;
172+
155173 let result = renderer. render_audiobuffer_sync ( self . length , suspend_callbacks, self ) ;
174+
156175 self . base . set_state ( AudioContextState :: Closed ) ;
176+ Self :: emit_statechange ( & mut onstatechange_handler) ;
157177
158- Self :: emit_oncomplete ( & mut oncomplete_handler, & result) ;
178+ Self :: emit_complete ( oncomplete_handler, & result) ;
159179
160180 result
161181 }
@@ -184,28 +204,31 @@ impl OfflineAudioContext {
184204 renderer,
185205 suspend_promises,
186206 resume_receiver,
187- mut oncomplete_handler,
207+ oncomplete_handler,
208+ mut onstatechange_handler,
188209 ..
189210 } = renderer;
190211
191212 self . base . set_state ( AudioContextState :: Running ) ;
213+ Self :: emit_statechange ( & mut onstatechange_handler) ;
192214
193215 let result = renderer
194216 . render_audiobuffer ( self . length , suspend_promises, resume_receiver)
195217 . await ;
196218
197219 self . base . set_state ( AudioContextState :: Closed ) ;
220+ Self :: emit_statechange ( & mut onstatechange_handler) ;
198221
199- Self :: emit_oncomplete ( & mut oncomplete_handler, & result) ;
222+ Self :: emit_complete ( oncomplete_handler, & result) ;
200223
201224 result
202225 }
203226
204- fn emit_oncomplete (
205- oncomplete_handler : & mut Option < Box < dyn FnOnce ( OfflineAudioCompletionEvent ) + Send > > ,
227+ fn emit_complete (
228+ oncomplete_handler : Option < Box < dyn FnOnce ( OfflineAudioCompletionEvent ) + Send > > ,
206229 result : & AudioBuffer ,
207230 ) {
208- if let Some ( callback) = oncomplete_handler. take ( ) {
231+ if let Some ( callback) = oncomplete_handler {
209232 let event = OfflineAudioCompletionEvent {
210233 rendered_buffer : result. clone ( ) ,
211234 event : Event { type_ : "complete" } ,
@@ -214,6 +237,15 @@ impl OfflineAudioContext {
214237 }
215238 }
216239
240+ fn emit_statechange ( onstatechange_handler : & mut Option < Box < dyn FnMut ( Event ) + Send > > ) {
241+ if let Some ( callback) = onstatechange_handler. as_mut ( ) {
242+ let event = Event {
243+ type_ : "statechange" ,
244+ } ;
245+ ( callback) ( event) ;
246+ }
247+ }
248+
217249 /// get the length of rendering audio buffer
218250 // false positive: OfflineAudioContext is not const
219251 #[ allow( clippy:: missing_const_for_fn, clippy:: unused_self) ]
@@ -540,6 +572,21 @@ mod tests {
540572 context. suspend_sync ( 0.0 , |_| ( ) ) ;
541573 }
542574
575+ #[ test]
576+ fn test_onstatechange ( ) {
577+ let mut context = OfflineAudioContext :: new ( 2 , 555 , 44_100. ) ;
578+
579+ let changed = Arc :: new ( AtomicBool :: new ( false ) ) ;
580+ let changed_clone = Arc :: clone ( & changed) ;
581+ context. set_onstatechange ( move |_event| {
582+ changed_clone. store ( true , Ordering :: Relaxed ) ;
583+ } ) ;
584+
585+ let _ = context. start_rendering_sync ( ) ;
586+
587+ assert ! ( changed. load( Ordering :: Relaxed ) ) ;
588+ }
589+
543590 #[ test]
544591 fn test_oncomplete ( ) {
545592 let mut context = OfflineAudioContext :: new ( 2 , 555 , 44_100. ) ;
0 commit comments