@@ -108,6 +108,7 @@ public class IntIntEvent : UnityEvent<int, int> {}
108108/// change in future.
109109///
110110/// </remarks>
111+ [ RequireComponent ( typeof ( AudioSource ) ) ]
111112public class LibPdInstance : MonoBehaviour {
112113
113114 #region libpd imports
@@ -430,6 +431,13 @@ public delegate void LibPdMidiControlChangeHook(int channel,
430431
431432 /// Global variable used to ensure we don't initialise LibPd more than once.
432433 private static bool pdInitialised = false ;
434+
435+ /// Holds any actions (events) sent from the audio thread.
436+ private List < System . Action > audioThreadActions = new List < System . Action > ( ) ;
437+ /// Holds any actions (events) that have been copied over into the main thread to execute.
438+ private List < System . Action > mainThreadActions = new List < System . Action > ( ) ;
439+ /// True if there are audioThreadActions pending.
440+ private volatile bool actionsPending = false ;
433441 #endregion
434442
435443 #region events
@@ -638,6 +646,29 @@ void OnApplicationQuit ()
638646 }
639647 }
640648
649+ //--------------------------------------------------------------------------
650+ /// We use this to dispatch any events that we've been sent from libpd.
651+ /// Any send/MIDI events sent from libpd will be sent from the audio thread,
652+ /// so we have to queue them and send them from the main thread, or Unity
653+ /// will get very upset.
654+ public void Update ( ) {
655+ if ( actionsPending ) {
656+ mainThreadActions . Clear ( ) ;
657+
658+ //Copy events queued from audio thread.
659+ lock ( audioThreadActions ) {
660+ mainThreadActions . AddRange ( audioThreadActions ) ;
661+
662+ audioThreadActions . Clear ( ) ;
663+ actionsPending = false ;
664+ }
665+
666+ //Dispatch events on main thread.
667+ for ( int i = 0 ; i < mainThreadActions . Count ; ++ i )
668+ mainThreadActions [ i ] . Invoke ( ) ;
669+ }
670+ }
671+
641672 //--------------------------------------------------------------------------
642673 /// This function updates our static pipePrintToConsole variable when the
643674 /// public one changes, and ensures all other active LibPdInstances are
@@ -659,7 +690,8 @@ private void OnValidate()
659690
660691 //We use this to store the name of our PD patch as a string, as we need
661692 //to feed the filename and directory into libpd.
662- patchName = patch . name ;
693+ if ( patch != null )
694+ patchName = patch . name ;
663695
664696 if ( lastName != patchName ) {
665697 patchDir = AssetDatabase . GetAssetPath ( patch . GetInstanceID ( ) ) ;
@@ -804,6 +836,7 @@ public void SendMidiCc(int channel, int controller, int value)
804836 //--------------------------------------------------------------------------
805837 /// Send a MIDI program change to the open patch.
806838 /// <param name="value">The program to change to (0-127).</param>
839+ [ MethodImpl ( MethodImplOptions . Synchronized ) ]
807840 public void SendMidiProgramChange ( int channel , int value )
808841 {
809842 libpd_set_instance ( instance ) ;
@@ -815,6 +848,7 @@ public void SendMidiProgramChange(int channel, int value)
815848 //--------------------------------------------------------------------------
816849 /// Send a MIDI pitch bend to the open patch.
817850 /// <param name ="value">The bend value has a range -8192 -> +8192.</param>
851+ [ MethodImpl ( MethodImplOptions . Synchronized ) ]
818852 public void SendMidiPitchBend ( int channel , int value )
819853 {
820854 libpd_set_instance ( instance ) ;
@@ -825,6 +859,7 @@ public void SendMidiPitchBend(int channel, int value)
825859
826860 //--------------------------------------------------------------------------
827861 /// Send a MIDI aftertouch message to the open patch.
862+ [ MethodImpl ( MethodImplOptions . Synchronized ) ]
828863 public void SendMidiAftertouch ( int channel , int value )
829864 {
830865 libpd_set_instance ( instance ) ;
@@ -835,6 +870,7 @@ public void SendMidiAftertouch(int channel, int value)
835870
836871 //--------------------------------------------------------------------------
837872 /// Send a MIDI polyphonic aftertouch to the open patch.
873+ [ MethodImpl ( MethodImplOptions . Synchronized ) ]
838874 public void SendMidiPolyAftertouch ( int channel , int pitch , int value )
839875 {
840876 libpd_set_instance ( instance ) ;
@@ -845,6 +881,7 @@ public void SendMidiPolyAftertouch(int channel, int pitch, int value)
845881
846882 //--------------------------------------------------------------------------
847883 /// Send a MIDI byte(?) to the open patch.
884+ [ MethodImpl ( MethodImplOptions . Synchronized ) ]
848885 public void SendMidiByte ( int port , int value )
849886 {
850887 libpd_set_instance ( instance ) ;
@@ -855,6 +892,7 @@ public void SendMidiByte(int port, int value)
855892
856893 //--------------------------------------------------------------------------
857894 /// Send a MIDI sysex byte(?) to the open patch.
895+ [ MethodImpl ( MethodImplOptions . Synchronized ) ]
858896 public void SendMidiSysex ( int port , int value )
859897 {
860898 libpd_set_instance ( instance ) ;
@@ -865,6 +903,7 @@ public void SendMidiSysex(int port, int value)
865903
866904 //--------------------------------------------------------------------------
867905 /// Send a MIDI sysrealtime byte(?) to the open patch.
906+ [ MethodImpl ( MethodImplOptions . Synchronized ) ]
868907 public void SendMidiSysRealtime ( int port , int value )
869908 {
870909 libpd_set_instance ( instance ) ;
@@ -938,21 +977,21 @@ static void PrintOutput(string message)
938977 /// Receive bang messages.
939978 void BangOutput ( string symbol )
940979 {
941- pureDataEvents . Bang . Invoke ( symbol ) ;
980+ addAudioAction ( ( ) => { pureDataEvents . Bang . Invoke ( symbol ) ; } ) ;
942981 }
943982
944983 //--------------------------------------------------------------------------
945984 /// Receive float messages.
946985 void FloatOutput ( string symbol , float val )
947986 {
948- pureDataEvents . Float . Invoke ( symbol , val ) ;
987+ addAudioAction ( ( ) => { pureDataEvents . Float . Invoke ( symbol , val ) ; } ) ;
949988 }
950989
951990 //--------------------------------------------------------------------------
952991 /// Receive symbol messages.
953992 void SymbolOutput ( string symbol , string val )
954993 {
955- pureDataEvents . Symbol . Invoke ( symbol , val ) ;
994+ addAudioAction ( ( ) => { pureDataEvents . Symbol . Invoke ( symbol , val ) ; } ) ;
956995 }
957996
958997 //--------------------------------------------------------------------------
@@ -961,7 +1000,7 @@ void ListOutput(string source, int argc, IntPtr argv)
9611000 {
9621001 var args = ConvertList ( argc , argv ) ;
9631002
964- pureDataEvents . List . Invoke ( source , args ) ;
1003+ addAudioAction ( ( ) => { pureDataEvents . List . Invoke ( source , args ) ; } ) ;
9651004 }
9661005
9671006 //--------------------------------------------------------------------------
@@ -970,56 +1009,56 @@ void MessageOutput(string source, string symbol, int argc, IntPtr argv)
9701009 {
9711010 var args = ConvertList ( argc , argv ) ;
9721011
973- pureDataEvents . Message . Invoke ( source , symbol , args ) ;
1012+ addAudioAction ( ( ) => { pureDataEvents . Message . Invoke ( source , symbol , args ) ; } ) ;
9741013 }
9751014
9761015 //--------------------------------------------------------------------------
9771016 /// Receive MIDI note on messages.
9781017 void MidiNoteOnOutput ( int channel , int pitch , int velocity )
9791018 {
980- midiEvents . MidiNoteOn . Invoke ( channel , pitch , velocity ) ;
1019+ addAudioAction ( ( ) => { midiEvents . MidiNoteOn . Invoke ( channel , pitch , velocity ) ; } ) ;
9811020 }
9821021
9831022 //--------------------------------------------------------------------------
9841023 /// Receive MIDI control change messages.
9851024 void MidiControlChangeOutput ( int channel , int controller , int value )
9861025 {
987- midiEvents . MidiControlChange . Invoke ( channel , controller , value ) ;
1026+ addAudioAction ( ( ) => { midiEvents . MidiControlChange . Invoke ( channel , controller , value ) ; } ) ;
9881027 }
9891028
9901029 //--------------------------------------------------------------------------
9911030 /// Receive MIDI program change messages.
9921031 void MidiProgramChangeOutput ( int channel , int program )
9931032 {
994- midiEvents . MidiProgramChange . Invoke ( channel , program ) ;
1033+ addAudioAction ( ( ) => { midiEvents . MidiProgramChange . Invoke ( channel , program ) ; } ) ;
9951034 }
9961035
9971036 //--------------------------------------------------------------------------
9981037 /// Receive MIDI pitch bend messages.
9991038 void MidiPitchBendOutput ( int channel , int value )
10001039 {
1001- midiEvents . MidiPitchBend . Invoke ( channel , value ) ;
1040+ addAudioAction ( ( ) => { midiEvents . MidiPitchBend . Invoke ( channel , value ) ; } ) ;
10021041 }
10031042
10041043 //--------------------------------------------------------------------------
10051044 /// Receive MIDI aftertouch messages.
10061045 void MidiAftertouchOutput ( int channel , int value )
10071046 {
1008- midiEvents . MidiAftertouch . Invoke ( channel , value ) ;
1047+ addAudioAction ( ( ) => { midiEvents . MidiAftertouch . Invoke ( channel , value ) ; } ) ;
10091048 }
10101049
10111050 //--------------------------------------------------------------------------
10121051 /// Receive MIDI polyphonic aftertouch messages.
10131052 void MidiPolyAftertouchOutput ( int channel , int pitch , int value )
10141053 {
1015- midiEvents . MidiPolyAftertouch . Invoke ( channel , pitch , value ) ;
1054+ addAudioAction ( ( ) => { midiEvents . MidiPolyAftertouch . Invoke ( channel , pitch , value ) ; } ) ;
10161055 }
10171056
10181057 //--------------------------------------------------------------------------
10191058 /// Receive MIDI byte messages.
10201059 void MidiByteOutput ( int channel , int value )
10211060 {
1022- midiEvents . MidiByte . Invoke ( channel , value ) ;
1061+ addAudioAction ( ( ) => { midiEvents . MidiByte . Invoke ( channel , value ) ; } ) ;
10231062 }
10241063 #endregion
10251064
@@ -1072,5 +1111,20 @@ private object[] ConvertList(int argc, IntPtr argv)
10721111
10731112 return retval ;
10741113 }
1114+
1115+ //--------------------------------------------------------------------------
1116+ /// Used to queue an event/action from the audio thread, to be dispatched in
1117+ /// the main thread.
1118+ private void addAudioAction ( System . Action action ) {
1119+ if ( action == null )
1120+ throw new ArgumentNullException ( "action" ) ;
1121+
1122+ lock ( audioThreadActions ) {
1123+ audioThreadActions . Add ( action ) ;
1124+
1125+ actionsPending = true ;
1126+ }
1127+ }
1128+
10751129 #endregion
10761130}
0 commit comments