/// ------------------------------------------------------------------------------------ public static Tuple <float, float>[,] GetSamples(IWaveStreamReader stream, uint numberOfSamplesToReturn) { var sampleCount = stream.SampleCount; if (numberOfSamplesToReturn == 0 || sampleCount == 0 || (stream.BitsPerSample == 32 && stream.Encoding != WaveFormatEncoding.IeeeFloat)) { return(new Tuple <float, float> [0, 0]); } if (sampleCount < numberOfSamplesToReturn) { numberOfSamplesToReturn = (uint)sampleCount; } stream.Seek(0, SeekOrigin.Begin); var channels = stream.SamplingChannelCount; var samplesPerAggregate = sampleCount / (double)numberOfSamplesToReturn; var samplesToReturn = new Tuple <float, float> [numberOfSamplesToReturn, stream.NativeChannelCount]; // To avoid compounding rounding errors as we get further along in the file, we need to separately track // the number of samples we've actually read and the ideal (unrounded) number of samples we should have // processed. Every time the rounding error builds to a whole integer, we read an extra sample to get them // back in synch. The buffer needs to be big enough to hold the extra sample for the times through the loop // when we're reading the higher number of samples. double idealSamplesProcessed = 0; var buffer = new float[(int)(Math.Ceiling(samplesPerAggregate)) * channels]; int valuesToRead = (int)(Math.Floor(samplesPerAggregate)) * channels; int sampleIndex = 0; int read; long totalRead = 0; while (sampleIndex < numberOfSamplesToReturn && (read = stream.Read(buffer, valuesToRead)) > 0) { for (var c = 0; c < stream.NativeChannelCount; c++) { var biggestSample = float.MinValue; var smallestSample = float.MaxValue; for (int i = 0; i < read; i += channels) { biggestSample = Math.Max(biggestSample, buffer[i + c]); smallestSample = Math.Min(smallestSample, buffer[i + c]); } samplesToReturn[sampleIndex, c] = new Tuple <float, float>(biggestSample, smallestSample); } // See big comment above. valuesToRead = (int)(Math.Floor(samplesPerAggregate)) * channels; totalRead += (read / channels); idealSamplesProcessed += samplesPerAggregate; if (totalRead < Math.Floor(idealSamplesProcessed)) { valuesToRead += channels; } sampleIndex++; } return(samplesToReturn); }
/// ------------------------------------------------------------------------------------ /// <summary> /// Constructor to facilitate testing /// </summary> /// ---------------------------------------------------------------------------------------- public AutoSegmenter(IWaveStreamReader sampleProvider, IAutoSegmenterSettings settings) { _streamReader = sampleProvider; _settings = settings; }