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);
}
}
}