@@ -6,7 +6,6 @@ use std::ptr::null_mut;
66use std:: rc:: Rc ;
77use std:: sync:: atomic:: { AtomicBool , AtomicU32 , Ordering } ;
88
9- use atomic_refcell:: AtomicRefCell ;
109use plinth_core:: signals:: ptr_signal:: { PtrSignal , PtrSignalMut } ;
1110use plinth_core:: signals:: signal:: SignalMut ;
1211use vst3:: Steinberg :: Vst :: ControllerNumbers_ :: kPitchBend;
@@ -32,7 +31,7 @@ const ROOT_UNIT_ID: i32 = 0;
3231const FIRST_UNIT_ID : i32 = 1 ;
3332
3433pub struct AudioThreadState < P : Vst3Plugin > {
35- processor : AtomicRefCell < Option < P :: Processor > > ,
34+ processor : parking_lot :: Mutex < Option < P :: Processor > > ,
3635 aux_active : AtomicBool ,
3736}
3837
@@ -257,7 +256,7 @@ impl<P: Vst3Plugin> IAudioProcessorTrait for PluginComponent<P> {
257256 return kResultFalse;
258257 } ;
259258
260- let mut processor = self . audio_thread_state . processor . borrow_mut ( ) ;
259+ let mut processor = self . audio_thread_state . processor . lock ( ) ;
261260 * processor = Some ( plugin. create_processor ( processor_config) ) ;
262261
263262 // Cache latency since it's not allowed to change during processing
@@ -272,7 +271,7 @@ impl<P: Vst3Plugin> IAudioProcessorTrait for PluginComponent<P> {
272271 let processing = state != 0 ;
273272 self . processing . store ( processing, Ordering :: Release ) ;
274273
275- let mut processor = self . audio_thread_state . processor . borrow_mut ( ) ;
274+ let mut processor = self . audio_thread_state . processor . lock ( ) ;
276275 if let Some ( processor) = processor. as_mut ( ) && !processing {
277276 processor. reset ( ) ;
278277 }
@@ -287,42 +286,60 @@ impl<P: Vst3Plugin> IAudioProcessorTrait for PluginComponent<P> {
287286 let parameter_change_iterator = ParameterChangeIterator :: new ( data. inputParameterChanges , * self . pitch_bend_parameter_ids . borrow ( ) ) ;
288287 let event_iterator = EventIterator :: new ( data. inputEvents ) ;
289288 let all_events = event_iterator. chain ( parameter_change_iterator) ;
289+ let is_data_dump = data. inputs . is_null ( ) || data. outputs . is_null ( ) || data. numInputs == 0 || data. numSamples == 0 ;
290290
291- let mut processor = self . audio_thread_state . processor . borrow_mut ( ) ;
292- let Some ( processor) = processor. as_mut ( ) else {
291+ // On some platforms, this cast is needed
292+ #[ allow( clippy:: unnecessary_cast) ]
293+ if !is_data_dump && data. symbolicSampleSize != SymbolicSampleSizes_ :: kSample32 as i32 {
293294 return kResultFalse;
295+ }
296+
297+ // Prepare inputs & outputs
298+ let ( main_input, main_output, aux_input) = if is_data_dump {
299+ ( None , None , None )
300+ } else {
301+ let inputs = unsafe { std:: slice:: from_raw_parts ( data. inputs , data. numInputs as _ ) } ;
302+ let outputs = unsafe { std:: slice:: from_raw_parts ( data. outputs , data. numOutputs as _ ) } ;
303+ let main_input = inputs[ 0 ] ;
304+ let main_output = outputs[ 0 ] ;
305+ assert_eq ! ( main_input. numChannels, main_output. numChannels) ;
306+
307+ let aux_input = if P :: HAS_AUX_INPUT && self . audio_thread_state . aux_active . load ( Ordering :: Acquire ) {
308+ assert_eq ! ( data. numInputs, 2 ) ;
309+ let aux_input = inputs[ 1 ] ;
310+ Some ( unsafe { PtrSignal :: from_pointers ( aux_input. numChannels as usize , data. numSamples as usize , aux_input. __field0 . channelBuffers32 as _ ) } )
311+ } else {
312+ None
313+ } ;
314+
315+ let main_input = unsafe { PtrSignal :: from_pointers ( main_input. numChannels as usize , data. numSamples as usize , main_input. __field0 . channelBuffers32 as _ ) } ;
316+ let main_output = unsafe { PtrSignalMut :: from_pointers ( main_output. numChannels as usize , data. numSamples as usize , main_output. __field0 . channelBuffers32 ) } ;
317+
318+ ( Some ( main_input) , Some ( main_output) , aux_input)
294319 } ;
295320
296- let aux_active = self . audio_thread_state . aux_active . load ( Ordering :: Acquire ) ;
321+ // Real-time safety: parking_lot Mutex is guaranteed to not do syscalls when uncontested
322+ // Contestion can only occur if we're setting up or tearing down the processor while process is called
323+ // In that case, we will simply output silence
324+ let Some ( mut processor) = self . audio_thread_state . processor . try_lock ( ) else {
325+ if let Some ( mut main_output) = main_output {
326+ main_output. fill ( 0.0 ) ;
327+ }
297328
298- // Empty input: this is a parameter dump
299- if data. inputs . is_null ( ) || data. outputs . is_null ( ) || data. numInputs == 0 || data. numSamples == 0 {
300- processor. process_events ( all_events) ;
301329 return kResultOk;
302- }
330+ } ;
303331
304- // On some platforms, this cast is needed
305- #[ allow( clippy:: unnecessary_cast) ]
306- if data. symbolicSampleSize != SymbolicSampleSizes_ :: kSample32 as i32 {
332+ let Some ( processor) = processor. as_mut ( ) else {
307333 return kResultFalse;
308- }
309-
310- let inputs = unsafe { std:: slice:: from_raw_parts ( data. inputs , data. numInputs as _ ) } ;
311- let outputs = unsafe { std:: slice:: from_raw_parts ( data. outputs , data. numOutputs as _ ) } ;
312- let main_input = inputs[ 0 ] ;
313- let main_output = outputs[ 0 ] ;
314- assert_eq ! ( main_input. numChannels, main_output. numChannels) ;
315-
316- let aux_input = if P :: HAS_AUX_INPUT && aux_active {
317- assert_eq ! ( data. numInputs, 2 ) ;
318- let aux_input = inputs[ 1 ] ;
319- Some ( unsafe { PtrSignal :: from_pointers ( aux_input. numChannels as usize , data. numSamples as usize , aux_input. __field0 . channelBuffers32 as _ ) } )
320- } else {
321- None
322334 } ;
323335
324- let main_input = unsafe { PtrSignal :: from_pointers ( main_input. numChannels as usize , data. numSamples as usize , main_input. __field0 . channelBuffers32 as _ ) } ;
325- let mut main_output = unsafe { PtrSignalMut :: from_pointers ( main_output. numChannels as usize , data. numSamples as usize , main_output. __field0 . channelBuffers32 ) } ;
336+ if is_data_dump {
337+ processor. process_events ( all_events) ;
338+ return kResultOk;
339+ }
340+
341+ let main_input = main_input. unwrap ( ) ;
342+ let mut main_output = main_output. unwrap ( ) ;
326343
327344 // If processing out-of-place, copy input to output
328345 if zip ( main_input. pointers ( ) . iter ( ) , main_output. pointers ( ) . iter ( ) )
0 commit comments