/* * For information on usage and redistribution, and for a DISCLAIMER OF ALL * WARRANTIES, see the file, "LICENSE.txt," in this distribution. * * Copyright(c) 2016 Thomas Mayer */ using System; using LibPDBinding; using NAudio.Utils; using NAudio.Wave; namespace LibPdBindingNaudio { class PdProvider : IWaveProvider, IDisposable { /// /// number of ticks for libPd to compute in a computation cycle. /// /// lower values may lead to distortion because of switching between threads. /// static readonly int Ticks = 8; static readonly int BlockSize = LibPD.BlockSize; static readonly int SampleRate = 44100; static readonly int Channels = 2; static readonly int BufferSize = Channels*Ticks*BlockSize; /// /// buffer for libPd to fill on computation. /// readonly float[] _buffer = new float[BufferSize]; /// /// use a CircularBuffer similar to BufferedWaveProvider. /// CircularBuffer _circularBuffer; int _minBuffer; int _patchHandle; public PdProvider() { SetUpBuffer(); SetUpPd(); RefillBuffer(); } ~PdProvider() { Dispose(false); } /// /// Sets up CircularBuffer for storage and float[] for getting data from libPd. /// void SetUpBuffer() { _circularBuffer = new CircularBuffer(BlockSize * Ticks * Channels * 4); // make the circular buffer large enough _minBuffer = BlockSize * Ticks * Channels * 2; } /// /// Sets up communication with libPd. /// void SetUpPd() { // Open Pd file _patchHandle = LibPD.OpenPatch("../../pd/test.pd"); // Subscribe to receiver LibPD.Float += LibPd_Float; LibPD.Subscribe(CursorReceiver); // Set up audio format for Pd LibPD.OpenAudio(0, 2, SampleRate); // Start audio LibPD.ComputeAudio(true); } /// /// An example for reading messages from LibPD /// void LibPd_Float(string receiver, float value) { Console.WriteLine("{0}, {1}", receiver, value); } /// /// Let libPd compute data, while the CircularBuffer has less than _minBuffer bytes available. /// void RefillBuffer() { while (_circularBuffer.Count < _minBuffer) { // Compute audio. Take care of the array sizes for audio in and out. LibPD.Process(Ticks, new float[0], _buffer); _circularBuffer.Write(PcmFromFloat(_buffer), 0, _buffer.Length * 4); } } /// /// Convert float[] from libPd to byte[] for CircularBuffer. /// /// This is surely optimizable /// byte[] PcmFromFloat(float[] pdOutput) { WaveBuffer wavebuffer = new WaveBuffer(pdOutput.Length * 4); for (var i = 0; i < pdOutput.Length; i++) { wavebuffer.FloatBuffer[i] = pdOutput[i]; } return wavebuffer.ByteBuffer; } public int Read(byte[] buffer, int offset, int count) { int read = _circularBuffer.Read(buffer, offset, count); RefillBuffer(); return read; } public WaveFormat WaveFormat { get { // We have float wave format return WaveFormat.CreateIeeeFloatWaveFormat(SampleRate, Channels); } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } void Dispose(bool isDisposing) { // Unsubscribe from all message receivers LibPD.Unsubscribe(CursorReceiver); LibPD.Float -= LibPd_Float; // Disable audio LibPD.ComputeAudio(false); if (_patchHandle > 0) { // Close patch LibPD.ClosePatch(_patchHandle); } LibPD.Release(); } public static string CursorReceiver = "cursor"; } }