public void ProcessRateHalf() { var r = new SoxResampler(1.0d, 0.5d, 1); int inSize = 4 * 10; int outSize = 12; var sampleDataIn = new byte[inSize]; var sampleDataOut = new byte[outSize]; int inputLengthUsed = 0; int outputLengthGenerated = 0; int remainingIn = inSize; int totalIn = 0, totalOut = 0; do { r.Process(sampleDataIn, 0, remainingIn, sampleDataOut, 0, outSize, remainingIn == 0, out inputLengthUsed, out outputLengthGenerated); totalIn += inputLengthUsed; totalOut += outputLengthGenerated; remainingIn -= inputLengthUsed; }while (inputLengthUsed > 0 || outputLengthGenerated > 0); Assert.AreEqual(inSize, totalIn, "not all data has been read"); Assert.AreEqual(inSize / 2, totalOut, "not all data has been put out"); }
/// <summary> /// Reads resampled data from the stream. /// </summary> /// <remarks> /// this function has been extensively analyzed and should not contain any bugs /// </remarks> /// <param name="buffer"></param> /// <param name="offset"></param> /// <param name="count"></param> /// <returns></returns> public override int Read(byte[] buffer, int offset, int count) { // TODO debug this block (it might give problems if the sampling rate of the stream is changed // dynamically - there might be source stream position issues (because of the SRC prereading, // and there might be local buffering issues in terms of the buffer containing unfitting samples) if (properties.SampleRate == sourceStream.Properties.SampleRate) { int bytesRead = sourceStream.Read(buffer, offset, count); position += bytesRead; return(bytesRead); } int inputLengthUsed, outputLengthGenerated; bool endOfStream = false; // loop while the sample rate converter consumes samples until it produces an output do { sourceBuffer.FillIfEmpty(sourceStream, count); // if the sourceBufferFillLevel is 0 at this position, the end of the source stream has been reached, // and endOfInput needs to be set to true in order to retrieve eventually buffered samples from // the sample rate converter // this is also the reason why the source stream's Read() method may be called multiple times although // it already signalled that it has reached the end of the stream endOfStream = sourceStream.Position >= sourceStream.Length && sourceBuffer.Empty; soxr.Process(sourceBuffer.Data, sourceBuffer.Offset, sourceBuffer.Count, buffer, offset, count, endOfStream, out inputLengthUsed, out outputLengthGenerated); sourceBuffer.Read(inputLengthUsed); }while (inputLengthUsed > 0 && outputLengthGenerated == 0); position += outputLengthGenerated; // in some cases it can happen that the SRC returns more data than the stream length suggests, // this data is cut off here to avoid a stream position that is greater than the stream's length. // NOTE max observed overflow: 8 bytes (1 2ch 32bit sample) long length = Length; if (length == 0) { // This is a special case in which the length suddenly drops to zero, which can happen when reading // from a dynamic source (e.g. a mixer). In this case no valid overflow can be calculated, so just // return the read bytes. return(outputLengthGenerated); } else if (position > length) { int overflow = (int)(position - length); // The resampler is expected to return a few samples too much, and they can just be thrown away // http://comments.gmane.org/gmane.comp.audio.src.general/168 Debug.WriteLine("ResamplingStream overflow: {0} bytes cut off", overflow); position -= overflow; return(outputLengthGenerated - overflow); } else if (position < length && inputLengthUsed == 0 && outputLengthGenerated == 0 && endOfStream) { int underflow = (int)(length - position); if (count < underflow) { underflow = count; } Debug.WriteLine("ResamplingStream UNDERFLOW WARNING: {0} bytes added", underflow); position += underflow; Array.Clear(buffer, offset, underflow); // set bytes to zero return(underflow); } return(outputLengthGenerated); }