Search code examples
c#cscore

How to play back a array of samples after reading them from an 'ISampleSource'


So, I have a piece of code that reads out an ISampleSource in to a float[][], the first array layer being for the number of channels and the second being for the sample data within the channel. I am going to take this data and attempt to apply signal processing to it, however for debugging purposes I might want to manipulate the sample array and then play it back so that I can "hear" what the code is doing. is there an easy way to take the data returned by ISampleSource.Read and stick it back in to a new ISampleSource so it can then be converted to an IWaveSource and played using WasapiOut?

Here is the class I tried to make so far, you pass it the float[][] and basically all the data in a WaveFormat for it to make one from.. but it doesn't actually do anything. doesn't error, doesn't play.. just does nothing. What am I doing wrong?

private class SampleSource : ISampleSource
{
    public long Position { get; set; }
    public WaveFormat WaveFormat { get; private set; }
    public bool CanSeek => true;
    public long Length  => _data.Length;

    private float[] _data;
    private long readPoint = 0;

    public SampleSource(float[][] samples, int sampleRate, int bits, int channels)
    {
        WaveFormat = new WaveFormat(sampleRate, bits, channels);
        if (samples.Length <= 0) return;

        _data = new float[samples[0].Length * samples.Length];
        int cchannels = samples.Length;
        int sampleLength = samples[0].Length;

        for (var i = 0; i < sampleLength; i += cchannels)
            for (var n = 0; n < cchannels; n++)
                _data[i + n] = samples[n][i / cchannels];
    }

    public int Read(float[] buffer, int offset, int count)
    {
        if (_data.Length < Position + count)
            count = (int) (_data.Length - Position);

        float[] outFloats = new float[count];
        for (var i = 0; i < count; i++)
            outFloats[i] = _data[i + Position + offset];

        buffer = outFloats;
        Position += count;
        return count;
    }

    public void Dispose() =>_data = null;
}

Solution

  • Rather than trying to set buffer to a new array (which makes no sense) I needed to directly write to the buffer array elements, so that they can be used outside of the function call. I don't really like doing it this way, maybe it's to fix an issue I don't see, but clearly that's how the library I'm using does it.

    private class SampleSource : ISampleSource
    {
        public long Position { get; set; }
        public WaveFormat WaveFormat { get; private set; }
        public bool CanSeek => true;
        public long Length  => _data.Length;
    
        private float[] _data;
        private long readPoint = 0;
    
        public SampleSource(float[][] samples, int sampleRate, int bits, int channels)
        {
            WaveFormat = new WaveFormat(sampleRate, bits, channels);
            if (samples.Length <= 0) return;
    
            _data = new float[samples[0].Length * samples.Length];
            int cchannels = samples.Length;
            int sampleLength = samples[0].Length;
    
            for (var i = 0; i < sampleLength; i += cchannels)
                for (var n = 0; n < cchannels; n++)
                    _data[i + n] = samples[n][i / cchannels];
        }
    
        public int Read(float[] buffer, int offset, int count)
        {
            /*THIS IS THE CHANGED FUNCTION*/
            if (_data.Length < Position + count)
                count = (int) (_data.Length - Position);
    
            for (var i = 0; i < count; i++)
                buffer[i] = _data[i + Position + offset];
    
            Position += count;
            return count;
        }
    
        public void Dispose() =>_data = null;
    }