1

I'm writing a program in C# which communicates with pure data using the libpd library (more information: http://libpd.cc/)

My pure data patch is simply creating a sine wave.

I wrote some test code to see if I get any data from my pure data patch. The LibPD.Process method writes samples into the outbuffer array and the contents are continuous values between -1 and 1:

// Initialise Pd with 0 input and 2 output channels and a samplerate of 44100. Project dependent.
int pdOpen = -1;
pdOpen = LibPD.OpenAudio(0, 2, 44100);

if (pdOpen != 0)
{
    Console.WriteLine("Error opening audio... exiting");
    return;
}

string patchFile = @"..\..\..\test.pd";
int patch = LibPD.OpenPatch(patchFile);
LibPD.ComputeAudio(true);

//Read from Process Function...
// The size of each buffer must be the product of the number of channels, the number of ticks, 
// and the number of samples per tick. (The number of samples per tick is the value 
// returned by libpd_blocksize(), i.e., 64.)

int ret;
float[] outbuffer = new float[2 * 1 * LibPD.BlockSize]; // Size = 128

for (int i = 0; i < 10; i++)
{
    ret = LibPD.Process(1, new float[0], outbuffer);
    Thread.Sleep(1000);
}

LibPD.ClosePatch(patch);
LibPD.Release();

So I am sure that I am getting processed data from my patch.

Now I would like to play back this float array using CSCore. I found the ISampleSource interface in the documentation and I think it's the right one to go for this task. I tried something like this in the Read method of my interface implementation:

...

public PureDataSource()
{
    _WaveFormat = new WaveFormat(44100, 16, 2);

    int pdOpen = -1;
    pdOpen = LibPD.OpenAudio(0, 2, 44100);

    if (pdOpen != 0)
    {
        Console.WriteLine("Error opening audio... exiting");
        return;
    }

    string patch = @"..\..\..\test.pd";
    _Patch = LibPD.OpenPatch(patch);
    LibPD.ComputeAudio(true);
}

...

public int Read(float[] buffer, int offset, int count)
{
    LibPD.Process(1, new float[0], buffer);

    return count;
}

But it doesn't work out - I only get crackling sounds (guess what). I know it has something to do with the buffer size of the Read method, but where can I configure this? The Process function of LibPd works like this:

The size of each buffer must be the product of the number of channels, the      number of ticks, 
and the number of samples per tick. (The number of samples per tick is the value 
returned by libpd_blocksize(), i.e., 64.)

in my case that's: 2 channels (output channels), 1 tick and number of samples per tick is 64 --> 128.

EDIT: I wrote a PureDataSource class which implements the ISampleSource interface using the information above:

class Program
{
    static void Main(string[] args)
    {
        PureDataSource pdSource = new PureDataSource();
        WasapiOut soundOut = new WasapiOut();

        soundOut.Initialize(pdSource.ToWaveSource());

        soundOut.Play();
        Thread.Sleep(5000);
        soundOut.Stop();
    }
}

class PureDataSource : ISampleSource
{
    public long Length
    {
        get
        {
            return 0;
        }
    }

    public long Position
    {
        get
        {
            return 0;
        }

        set
        {
            throw new NotImplementedException();
        }
    }
    private WaveFormat _WaveFormat;
    public WaveFormat WaveFormat
    {
        get
        {
            return _WaveFormat;
        }
    }

    private int _Patch;
    public int Patch
    {
        get { return _Patch; }
        //set { _Patch = value; }
    }

    public PureDataSource()
    {
        _WaveFormat = new WaveFormat(44100, 16, 2);

        // Initialise Pd with 2 ins and outs and a samplerate of 44100. Project dependent.
        int pdOpen = -1;
        pdOpen = LibPD.OpenAudio(0, 2, 44100);

        if (pdOpen != 0)
        {
            Console.WriteLine("Error opening audio... exiting");
            return;
        }

        string patch = @"..\..\..\test.pd";
        _Patch = LibPD.OpenPatch(patch);
        LibPD.ComputeAudio(true);
    }

    public void Dispose()
    {
        LibPD.ClosePatch(_Patch);
        LibPD.Release();
    }

    public int Read(float[] buffer, int offset, int count)
    {
        int ticks = 1;

        int pdBufferPos = 0;
        float[] pdBuffer = new float[2 * ticks * LibPD.BlockSize];
        LibPD.Process(ticks, new float[0], pdBuffer);

        for (int i = offset; i < count; i++)
        { 
            if (pdBufferPos >= pdBuffer.Length)
            {
                pdBufferPos = 0;
                LibPD.Process(ticks, new float[0], pdBuffer);
            }

            buffer[i] = pdBuffer[pdBufferPos];
            pdBufferPos++;
        }            

        return count;
    }
}

The Read method fills the whole buffer with the output provided by LibPD.Process (this is a float array with the size of 128 each time).

I can hear a sine wave now, but with a lot of crackling sound - it seems the samples are not processed continuously. Any ideas how to solve this?

Powerslave
  • 11
  • 4
  • where's the `CSCore` part?? Anything missing or wrong tagged? – cramopy Dec 25 '15 at 17:44
  • nope. the CSCore part is the second code snippet. It's a implementation draft for the ISampleSource interface, which works with samples in form of float arrays instead of byte arrays. – Powerslave Dec 25 '15 at 18:44
  • You are just using the ISampleSource interface. Are there any components of cscore you are using? – Florian Dec 31 '15 at 14:20
  • Yes, I use WasapiOut in my Main method and initialize it using .ToWaveSource() of my PureDataSource instance. I added the code of my Main method above the PureDataSource class. Sorry, I took that for granted. – Powerslave Jan 02 '16 at 13:50

1 Answers1

2

But it doesn't work out - I only get crackling sounds (guess what). I know it has something to do with the buffer size of the Read method, but where can I configure this?

You can't "configure" it. If the WasapiOut requests a certain amount of data, it is in your hands, how much data you want to return. Fill the buffer, starting at the specified offset, with the data you want. Return the number of provided samples. That's how you "configure" it.

Florian
  • 5,918
  • 3
  • 47
  • 86
  • Thank you for your answer, but it is not the answer for my problem. After the EDIT of my post I clearly posted an implementation of the ISampleSource interface where I iterate through the number of samples in the Read Method (I copied it from the SineGenerator) - long story short: I got it how the WasapiOut requests the data ;) I thought I will leave my original problem before the EDIT of my post. My current problem can be found after my code block of my program ("I can hear a sine wave now but with crackling sounds..."). – Powerslave Jan 06 '16 at 19:08
  • I think it is a buffering problem. I get my data from pure data (LibPD.Process) into a float array and at the same time I write that data into the buffer. I get 64 stereo samples (= 128 floats) and write these 128 floats into the buffer. Then I get the next 64 samples and write them again etc. until "count". I hope this is clearer now. Thanks for your support! – Powerslave Jan 06 '16 at 19:12
  • ok, since I haven't found a solution I will ask my question in a more general way: how can I playback samples provided by another software in my C# application using CSCore? The other software provides the samples at a sample rate of 44100 Hz. The other software provides a float array. – Powerslave Feb 16 '16 at 19:01
  • Take a look at the WriteableBufferSource class. You would have to convert the samples to bytes, but it would work. As an alternative, you can also create a similar class, implementing the ISampleSource interface and storing floats instead of bytes. – Florian Feb 18 '16 at 18:48
  • I already implemented ISampleSource (code above) and the Read Method. Or would you generally recommend WriteableBufferingSource? Please take a look at my code.I hear the samples, but crackling. Now there is something funny:I changed the sample rate of my audio card (default format in shared mode - Windows advanced Audio Options) to 44100 instead of 48000 and I hear the samples perfectly. Any ideas on that?I would like it to work without changing anything in the audio card settings. pd is providing the samples at 44100 and I also set the WaveFormat of my ISampleSource implementation to 44100... – Powerslave Feb 19 '16 at 10:29