Change to ensure libpd events are only ever dispatched from the main thread; added AudioSource Component dependency to LibPdInstance.

This commit is contained in:
Niall Moody 2018-07-25 17:40:39 +01:00
parent ff116c45e1
commit fd909fda75

View file

@ -108,6 +108,7 @@ public class IntIntEvent : UnityEvent<int, int> {}
/// change in future. /// change in future.
/// ///
/// </remarks> /// </remarks>
[RequireComponent(typeof(AudioSource))]
public class LibPdInstance : MonoBehaviour { public class LibPdInstance : MonoBehaviour {
#region libpd imports #region libpd imports
@ -430,6 +431,13 @@ public class LibPdInstance : MonoBehaviour {
/// Global variable used to ensure we don't initialise LibPd more than once. /// Global variable used to ensure we don't initialise LibPd more than once.
private static bool pdInitialised = false; private static bool pdInitialised = false;
/// Holds any actions (events) sent from the audio thread.
private List<System.Action> audioThreadActions = new List<System.Action>();
/// Holds any actions (events) that have been copied over into the main thread to execute.
private List<System.Action> mainThreadActions = new List<System.Action>();
/// True if there are audioThreadActions pending.
private volatile bool actionsPending = false;
#endregion #endregion
#region events #region events
@ -638,6 +646,29 @@ public class LibPdInstance : MonoBehaviour {
} }
} }
//--------------------------------------------------------------------------
/// We use this to dispatch any events that we've been sent from libpd.
/// Any send/MIDI events sent from libpd will be sent from the audio thread,
/// so we have to queue them and send them from the main thread, or Unity
/// will get very upset.
public void Update() {
if(actionsPending) {
mainThreadActions.Clear();
//Copy events queued from audio thread.
lock(audioThreadActions) {
mainThreadActions.AddRange(audioThreadActions);
audioThreadActions.Clear();
actionsPending = false;
}
//Dispatch events on main thread.
for(int i=0;i<mainThreadActions.Count;++i)
mainThreadActions[i].Invoke();
}
}
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/// This function updates our static pipePrintToConsole variable when the /// This function updates our static pipePrintToConsole variable when the
/// public one changes, and ensures all other active LibPdInstances are /// public one changes, and ensures all other active LibPdInstances are
@ -659,7 +690,8 @@ public class LibPdInstance : MonoBehaviour {
//We use this to store the name of our PD patch as a string, as we need //We use this to store the name of our PD patch as a string, as we need
//to feed the filename and directory into libpd. //to feed the filename and directory into libpd.
patchName = patch.name; if(patch != null)
patchName = patch.name;
if(lastName != patchName) { if(lastName != patchName) {
patchDir = AssetDatabase.GetAssetPath(patch.GetInstanceID()); patchDir = AssetDatabase.GetAssetPath(patch.GetInstanceID());
@ -804,6 +836,7 @@ public class LibPdInstance : MonoBehaviour {
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/// Send a MIDI program change to the open patch. /// Send a MIDI program change to the open patch.
/// <param name="value">The program to change to (0-127).</param> /// <param name="value">The program to change to (0-127).</param>
[MethodImpl(MethodImplOptions.Synchronized)]
public void SendMidiProgramChange(int channel, int value) public void SendMidiProgramChange(int channel, int value)
{ {
libpd_set_instance(instance); libpd_set_instance(instance);
@ -815,6 +848,7 @@ public class LibPdInstance : MonoBehaviour {
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/// Send a MIDI pitch bend to the open patch. /// Send a MIDI pitch bend to the open patch.
/// <param name ="value">The bend value has a range -8192 -> +8192.</param> /// <param name ="value">The bend value has a range -8192 -> +8192.</param>
[MethodImpl(MethodImplOptions.Synchronized)]
public void SendMidiPitchBend(int channel, int value) public void SendMidiPitchBend(int channel, int value)
{ {
libpd_set_instance(instance); libpd_set_instance(instance);
@ -825,6 +859,7 @@ public class LibPdInstance : MonoBehaviour {
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/// Send a MIDI aftertouch message to the open patch. /// Send a MIDI aftertouch message to the open patch.
[MethodImpl(MethodImplOptions.Synchronized)]
public void SendMidiAftertouch(int channel, int value) public void SendMidiAftertouch(int channel, int value)
{ {
libpd_set_instance(instance); libpd_set_instance(instance);
@ -835,6 +870,7 @@ public class LibPdInstance : MonoBehaviour {
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/// Send a MIDI polyphonic aftertouch to the open patch. /// Send a MIDI polyphonic aftertouch to the open patch.
[MethodImpl(MethodImplOptions.Synchronized)]
public void SendMidiPolyAftertouch(int channel, int pitch, int value) public void SendMidiPolyAftertouch(int channel, int pitch, int value)
{ {
libpd_set_instance(instance); libpd_set_instance(instance);
@ -845,6 +881,7 @@ public class LibPdInstance : MonoBehaviour {
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/// Send a MIDI byte(?) to the open patch. /// Send a MIDI byte(?) to the open patch.
[MethodImpl(MethodImplOptions.Synchronized)]
public void SendMidiByte(int port, int value) public void SendMidiByte(int port, int value)
{ {
libpd_set_instance(instance); libpd_set_instance(instance);
@ -855,6 +892,7 @@ public class LibPdInstance : MonoBehaviour {
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/// Send a MIDI sysex byte(?) to the open patch. /// Send a MIDI sysex byte(?) to the open patch.
[MethodImpl(MethodImplOptions.Synchronized)]
public void SendMidiSysex(int port, int value) public void SendMidiSysex(int port, int value)
{ {
libpd_set_instance(instance); libpd_set_instance(instance);
@ -865,6 +903,7 @@ public class LibPdInstance : MonoBehaviour {
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/// Send a MIDI sysrealtime byte(?) to the open patch. /// Send a MIDI sysrealtime byte(?) to the open patch.
[MethodImpl(MethodImplOptions.Synchronized)]
public void SendMidiSysRealtime(int port, int value) public void SendMidiSysRealtime(int port, int value)
{ {
libpd_set_instance(instance); libpd_set_instance(instance);
@ -938,21 +977,21 @@ public class LibPdInstance : MonoBehaviour {
/// Receive bang messages. /// Receive bang messages.
void BangOutput(string symbol) void BangOutput(string symbol)
{ {
pureDataEvents.Bang.Invoke(symbol); addAudioAction(() => {pureDataEvents.Bang.Invoke(symbol);});
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/// Receive float messages. /// Receive float messages.
void FloatOutput(string symbol, float val) void FloatOutput(string symbol, float val)
{ {
pureDataEvents.Float.Invoke(symbol, val); addAudioAction(() => {pureDataEvents.Float.Invoke(symbol, val);});
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/// Receive symbol messages. /// Receive symbol messages.
void SymbolOutput(string symbol, string val) void SymbolOutput(string symbol, string val)
{ {
pureDataEvents.Symbol.Invoke(symbol, val); addAudioAction(() => {pureDataEvents.Symbol.Invoke(symbol, val);});
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
@ -961,7 +1000,7 @@ public class LibPdInstance : MonoBehaviour {
{ {
var args = ConvertList(argc, argv); var args = ConvertList(argc, argv);
pureDataEvents.List.Invoke(source, args); addAudioAction(() => {pureDataEvents.List.Invoke(source, args);});
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
@ -970,56 +1009,56 @@ public class LibPdInstance : MonoBehaviour {
{ {
var args = ConvertList(argc, argv); var args = ConvertList(argc, argv);
pureDataEvents.Message.Invoke(source, symbol, args); addAudioAction(() => {pureDataEvents.Message.Invoke(source, symbol, args);});
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/// Receive MIDI note on messages. /// Receive MIDI note on messages.
void MidiNoteOnOutput(int channel, int pitch, int velocity) void MidiNoteOnOutput(int channel, int pitch, int velocity)
{ {
midiEvents.MidiNoteOn.Invoke(channel, pitch, velocity); addAudioAction(() => {midiEvents.MidiNoteOn.Invoke(channel, pitch, velocity);});
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/// Receive MIDI control change messages. /// Receive MIDI control change messages.
void MidiControlChangeOutput(int channel, int controller, int value) void MidiControlChangeOutput(int channel, int controller, int value)
{ {
midiEvents.MidiControlChange.Invoke(channel, controller, value); addAudioAction(() => {midiEvents.MidiControlChange.Invoke(channel, controller, value);});
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/// Receive MIDI program change messages. /// Receive MIDI program change messages.
void MidiProgramChangeOutput(int channel, int program) void MidiProgramChangeOutput(int channel, int program)
{ {
midiEvents.MidiProgramChange.Invoke(channel, program); addAudioAction(() => {midiEvents.MidiProgramChange.Invoke(channel, program);});
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/// Receive MIDI pitch bend messages. /// Receive MIDI pitch bend messages.
void MidiPitchBendOutput(int channel, int value) void MidiPitchBendOutput(int channel, int value)
{ {
midiEvents.MidiPitchBend.Invoke(channel, value); addAudioAction(() => {midiEvents.MidiPitchBend.Invoke(channel, value);});
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/// Receive MIDI aftertouch messages. /// Receive MIDI aftertouch messages.
void MidiAftertouchOutput(int channel, int value) void MidiAftertouchOutput(int channel, int value)
{ {
midiEvents.MidiAftertouch.Invoke(channel, value); addAudioAction(() => {midiEvents.MidiAftertouch.Invoke(channel, value);});
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/// Receive MIDI polyphonic aftertouch messages. /// Receive MIDI polyphonic aftertouch messages.
void MidiPolyAftertouchOutput(int channel, int pitch, int value) void MidiPolyAftertouchOutput(int channel, int pitch, int value)
{ {
midiEvents.MidiPolyAftertouch.Invoke(channel, pitch, value); addAudioAction(() => {midiEvents.MidiPolyAftertouch.Invoke(channel, pitch, value);});
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/// Receive MIDI byte messages. /// Receive MIDI byte messages.
void MidiByteOutput(int channel, int value) void MidiByteOutput(int channel, int value)
{ {
midiEvents.MidiByte.Invoke(channel, value); addAudioAction(() => {midiEvents.MidiByte.Invoke(channel, value);});
} }
#endregion #endregion
@ -1072,5 +1111,20 @@ public class LibPdInstance : MonoBehaviour {
return retval; return retval;
} }
//--------------------------------------------------------------------------
/// Used to queue an event/action from the audio thread, to be dispatched in
/// the main thread.
private void addAudioAction(System.Action action) {
if (action == null)
throw new ArgumentNullException("action");
lock(audioThreadActions) {
audioThreadActions.Add(action);
actionsPending = true;
}
}
#endregion #endregion
} }