using LibPDBinding.Managed.Data; using LibPDBinding.Managed.Utils; using LibPDBinding.Native; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.CompilerServices; namespace LibPDBinding.Managed { /// /// Class for a Pd instance. /// /// NB: If the C library is built without multi instance support, only one instance of Pd should be instanciated. /// public sealed class Pd : IDisposable { public static int NumberOfInstances { get { if (!_initialized) { return 0; } return MultiInstance.num_instances () - 1; } } /// /// Gets the number of input channels. /// public int Inputs { get; private set; } /// /// Gets the number of output channels. /// public int Outputs { get; private set; } /// /// Gets the sample rate. /// public int SampleRate { get; private set; } /// /// Gets the block size. /// public int BlockSize { get { return Audio.blocksize (); } } Messaging _messaging; /// /// Gets the messaging object. /// /// The messaging object. public Messaging Messaging { get { return _messaging; } } Midi _midi; /// /// Get the object for MIDI communication /// public Midi Midi { get { return _midi; } } /// /// Returns [true] when audio computation is enabled, and [false] when audio computation is disabled. /// public bool IsComputing { get; private set; } /// /// Initializes a new instance of Pd /// /// Number of input channels. /// Number of output channels. /// Sample rate for project. public Pd (int inputChannels, int outputChannels, int sampleRate) : this (inputChannels, outputChannels, sampleRate, null) { } /// /// Initializes a new instance of Pd /// /// Number of input channels. /// Number of output channels. /// Sample rate for project. /// Paths for Pd to search for externals. public Pd (int inputChannels, int outputChannels, int sampleRate, IEnumerable searchPaths) { Inputs = inputChannels; Outputs = outputChannels; SampleRate = sampleRate; if (!_initialized) { General.libpd_init (); _initialized = true; } _thisInstance = MultiInstance.new_instance (); Activate (); _messaging = new Messaging (this); _midi = new Midi (this); foreach (string path in searchPaths ?? Enumerable.Empty()) { General.add_to_search_path (path); } } ~Pd () { Dispose (false); } public void Dispose () { Dispose (true); GC.SuppressFinalize (this); } void Dispose (bool disposing) { if (disposing) { Stop (); } Messaging.Dispose (); Midi.Dispose (); MultiInstance.free_instance (_thisInstance); _currentInstance = IntPtr.Zero; } static bool _initialized; static IntPtr _currentInstance = IntPtr.Zero; readonly IntPtr _thisInstance; internal void Activate () { if (_currentInstance == _thisInstance) { return; } MultiInstance.set_instance (_thisInstance); _currentInstance = _thisInstance; } /// /// Starts audio computation. /// [MethodImpl (MethodImplOptions.Synchronized)] public void Start () { Activate (); Audio.init_audio (Inputs, Outputs, SampleRate); MessageInvocation.SendMessage ("pd", "dsp", new Float (1)); IsComputing = true; } [MethodImpl (MethodImplOptions.Synchronized)] public bool Process (int ticks, short[] inBuffer, short[] outBuffer) { Activate (); return Audio.process_short (ticks, inBuffer, outBuffer) == 0; } /// /// Reads samples from inBuffer and processes them with Pd. /// /// Number of ticks to process. To reduce overhead of function calls, raise this number. To lower latency, reduce this number. /// Interleaved input buffer. Must be at least of size [InputChannels] * [Blocksize] * [ticks] /// Output buffer for storing interleaved processed audio. Must be at least of size [OutputChannels] * [Blocksize] * [ticks] /// [true] if audio processing was successful, [false] otherwise. [MethodImpl (MethodImplOptions.Synchronized)] public bool Process (int ticks, float[] inBuffer, float[] outBuffer) { Activate (); return Audio.process_float (ticks, inBuffer, outBuffer) == 0; } [MethodImpl (MethodImplOptions.Synchronized)] public bool Process (int ticks, double[] inBuffer, double[] outBuffer) { Activate (); return Audio.process_double (ticks, inBuffer, outBuffer) == 0; } /// /// Stops audio computation. /// [MethodImpl (MethodImplOptions.Synchronized)] public void Stop () { Activate (); MessageInvocation.SendMessage ("pd", "dsp", new Float (0)); IsComputing = false; } /// /// Loads a Pd patch from the specified file path. /// /// Path to the Pd file. /// New Patch, if loading is successful, else null. [MethodImpl (MethodImplOptions.Synchronized)] public Patch LoadPatch (string path) { Activate (); if (path.StartsWith (".")) { string currentDirectory = AppDomain.CurrentDomain.BaseDirectory; path = Path.Combine (currentDirectory, path); } if (!File.Exists (path)) { return null; } var ptr = General.openfile (Path.GetFileName (path), Path.GetDirectoryName (path)); return new Patch (ptr, this); } /// /// Gets a Pd array with the specified name. /// /// Name of the array. /// New PdArray. [MethodImpl (MethodImplOptions.Synchronized)] public PdArray GetArray (string array) { Activate (); return new PdArray (this, array); } } }