|
|
@ -1,4 +1,3 @@ |
|
|
|
using System; |
|
|
|
using NAudio.Dsp; |
|
|
|
|
|
|
|
namespace NAudio.Wave |
|
|
@ -6,24 +5,21 @@ namespace NAudio.Wave |
|
|
|
/// <summary>
|
|
|
|
/// A simple compressor
|
|
|
|
/// </summary>
|
|
|
|
public class SimpleCompressorStream : WaveStream |
|
|
|
public class SimpleCompressorEffect : ISampleProvider |
|
|
|
{ |
|
|
|
private WaveStream sourceStream; |
|
|
|
private readonly ISampleProvider sourceStream; |
|
|
|
private readonly SimpleCompressor simpleCompressor; |
|
|
|
private byte[] sourceBuffer; // buffer used by Read function
|
|
|
|
private readonly int channels; |
|
|
|
private readonly int bytesPerSample; |
|
|
|
private readonly object lockObject = new object(); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Create a new simple compressor stream
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="sourceStream">Source stream</param>
|
|
|
|
public SimpleCompressorStream(WaveStream sourceStream) |
|
|
|
public SimpleCompressorEffect(ISampleProvider sourceStream) |
|
|
|
{ |
|
|
|
this.sourceStream = sourceStream; |
|
|
|
channels = sourceStream.WaveFormat.Channels; |
|
|
|
bytesPerSample = sourceStream.WaveFormat.BitsPerSample / 8; |
|
|
|
simpleCompressor = new SimpleCompressor(5.0, 10.0, sourceStream.WaveFormat.SampleRate); |
|
|
|
simpleCompressor.Threshold = 16; |
|
|
|
simpleCompressor.Ratio = 6; |
|
|
@ -36,10 +32,7 @@ namespace NAudio.Wave |
|
|
|
/// </summary>
|
|
|
|
public double MakeUpGain |
|
|
|
{ |
|
|
|
get |
|
|
|
{ |
|
|
|
return simpleCompressor.MakeUpGain; |
|
|
|
} |
|
|
|
get => simpleCompressor.MakeUpGain; |
|
|
|
set |
|
|
|
{ |
|
|
|
lock (lockObject) |
|
|
@ -54,10 +47,7 @@ namespace NAudio.Wave |
|
|
|
/// </summary>
|
|
|
|
public double Threshold |
|
|
|
{ |
|
|
|
get |
|
|
|
{ |
|
|
|
return simpleCompressor.Threshold; |
|
|
|
} |
|
|
|
get => simpleCompressor.Threshold; |
|
|
|
set |
|
|
|
{ |
|
|
|
lock (lockObject) |
|
|
@ -72,10 +62,7 @@ namespace NAudio.Wave |
|
|
|
/// </summary>
|
|
|
|
public double Ratio |
|
|
|
{ |
|
|
|
get |
|
|
|
{ |
|
|
|
return simpleCompressor.Ratio; |
|
|
|
} |
|
|
|
get => simpleCompressor.Ratio; |
|
|
|
set |
|
|
|
{ |
|
|
|
lock (lockObject) |
|
|
@ -90,10 +77,7 @@ namespace NAudio.Wave |
|
|
|
/// </summary>
|
|
|
|
public double Attack |
|
|
|
{ |
|
|
|
get |
|
|
|
{ |
|
|
|
return simpleCompressor.Attack; |
|
|
|
} |
|
|
|
get => simpleCompressor.Attack; |
|
|
|
set |
|
|
|
{ |
|
|
|
lock (lockObject) |
|
|
@ -108,10 +92,7 @@ namespace NAudio.Wave |
|
|
|
/// </summary>
|
|
|
|
public double Release |
|
|
|
{ |
|
|
|
get |
|
|
|
{ |
|
|
|
return simpleCompressor.Release; |
|
|
|
} |
|
|
|
get => simpleCompressor.Release; |
|
|
|
set |
|
|
|
{ |
|
|
|
lock (lockObject) |
|
|
@ -121,103 +102,16 @@ namespace NAudio.Wave |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Determine whether the stream has the required amount of data.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="count">Number of bytes of data required from the stream.</param>
|
|
|
|
/// <returns>Flag indicating whether the required amount of data is avialable.</returns>
|
|
|
|
public override bool HasData(int count) |
|
|
|
{ |
|
|
|
return sourceStream.HasData(count); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Turns gain on or off
|
|
|
|
/// </summary>
|
|
|
|
public bool Enabled { get; set; } |
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Returns the stream length
|
|
|
|
/// </summary>
|
|
|
|
public override long Length => sourceStream.Length; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets or sets the current position in the stream
|
|
|
|
/// </summary>
|
|
|
|
public override long Position |
|
|
|
{ |
|
|
|
get |
|
|
|
{ |
|
|
|
return sourceStream.Position; |
|
|
|
} |
|
|
|
set |
|
|
|
{ |
|
|
|
lock (lockObject) |
|
|
|
{ |
|
|
|
sourceStream.Position = value; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the WaveFormat of this stream
|
|
|
|
/// </summary>
|
|
|
|
public override WaveFormat WaveFormat => sourceStream.WaveFormat; |
|
|
|
public WaveFormat WaveFormat => sourceStream.WaveFormat; |
|
|
|
|
|
|
|
private void ReadSamples(byte[] buffer, int start, out double left, out double right) |
|
|
|
{ |
|
|
|
if (bytesPerSample == 4) |
|
|
|
{ |
|
|
|
left = BitConverter.ToSingle(buffer, start); |
|
|
|
if (channels > 1) |
|
|
|
{ |
|
|
|
right = BitConverter.ToSingle(buffer, start + bytesPerSample); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
right = left; |
|
|
|
} |
|
|
|
} |
|
|
|
else if (bytesPerSample == 2) |
|
|
|
{ |
|
|
|
left = BitConverter.ToInt16(buffer, start) / 32768.0; |
|
|
|
if (channels > 1) |
|
|
|
{ |
|
|
|
right = BitConverter.ToInt16(buffer, start + bytesPerSample) / 32768.0; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
right = left; |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
throw new InvalidOperationException(String.Format("Unsupported bytes per sample: {0}", bytesPerSample)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void WriteSamples(byte[] buffer, int start, double left, double right) |
|
|
|
{ |
|
|
|
if (bytesPerSample == 4) |
|
|
|
{ |
|
|
|
Array.Copy(BitConverter.GetBytes((float)left), 0, buffer, start, bytesPerSample); |
|
|
|
if (channels > 1) |
|
|
|
{ |
|
|
|
Array.Copy(BitConverter.GetBytes((float)right), 0, buffer, start + bytesPerSample, bytesPerSample); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (bytesPerSample == 2) |
|
|
|
{ |
|
|
|
Array.Copy(BitConverter.GetBytes((short)(left * 32768.0)), 0, buffer, start, bytesPerSample); |
|
|
|
if (channels > 1) |
|
|
|
{ |
|
|
|
Array.Copy(BitConverter.GetBytes((short)(right * 32768.0)), 0, buffer, start + bytesPerSample, bytesPerSample); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Reads bytes from this stream
|
|
|
@ -226,65 +120,24 @@ namespace NAudio.Wave |
|
|
|
/// <param name="offset">Offset in array to read into</param>
|
|
|
|
/// <param name="count">Number of bytes to read</param>
|
|
|
|
/// <returns>Number of bytes read</returns>
|
|
|
|
public override int Read(byte[] array, int offset, int count) |
|
|
|
public int Read(float[] array, int offset, int count) |
|
|
|
{ |
|
|
|
lock (lockObject) |
|
|
|
{ |
|
|
|
int samplesRead = sourceStream.Read(array, offset, count); |
|
|
|
if (Enabled) |
|
|
|
{ |
|
|
|
if (sourceBuffer == null || sourceBuffer.Length < count) |
|
|
|
sourceBuffer = new byte[count]; |
|
|
|
int sourceBytesRead = sourceStream.Read(sourceBuffer, 0, count); |
|
|
|
int sampleCount = sourceBytesRead / (bytesPerSample * channels); |
|
|
|
for (int sample = 0; sample < sampleCount; sample++) |
|
|
|
for (int sample = 0; sample < samplesRead; sample+=channels) |
|
|
|
{ |
|
|
|
int start = sample * bytesPerSample * channels; |
|
|
|
double in1; |
|
|
|
double in2; |
|
|
|
ReadSamples(sourceBuffer, start, out in1, out in2); |
|
|
|
double in1 = array[offset+sample]; |
|
|
|
double in2 = (channels == 1) ? 0 : array[offset+sample+1]; |
|
|
|
simpleCompressor.Process(ref in1, ref in2); |
|
|
|
WriteSamples(array, offset + start, in1, in2); |
|
|
|
array[offset + sample] = (float)in1; |
|
|
|
if (channels > 1) |
|
|
|
array[offset + sample + 1] = (float)in2; |
|
|
|
} |
|
|
|
return count; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
return sourceStream.Read(array, offset, count); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Disposes this stream
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="disposing">true if the user called this</param>
|
|
|
|
protected override void Dispose(bool disposing) |
|
|
|
{ |
|
|
|
if (disposing) |
|
|
|
{ |
|
|
|
// Release managed resources.
|
|
|
|
if (sourceStream != null) |
|
|
|
{ |
|
|
|
sourceStream.Dispose(); |
|
|
|
sourceStream = null; |
|
|
|
} |
|
|
|
} |
|
|
|
// Release unmanaged resources.
|
|
|
|
// Set large fields to null.
|
|
|
|
// Call Dispose on your base class.
|
|
|
|
base.Dispose(disposing); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the block alignment for this stream
|
|
|
|
/// </summary>
|
|
|
|
public override int BlockAlign |
|
|
|
{ |
|
|
|
get |
|
|
|
{ |
|
|
|
// TODO: investigate forcing 20ms
|
|
|
|
return sourceStream.BlockAlign; |
|
|
|
return samplesRead; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|