Permalink
Fetching contributors…
Cannot retrieve contributors at this time
293 lines (269 sloc) 8.76 KB
using System;
using NAudio.Dsp;
namespace NAudio.Wave
{
/// <summary>
/// A simple compressor
/// </summary>
public class SimpleCompressorStream : WaveStream
{
private WaveStream 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)
{
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;
simpleCompressor.MakeUpGain = 16;
}
/// <summary>
/// Make-up Gain
/// </summary>
public double MakeUpGain
{
get
{
return simpleCompressor.MakeUpGain;
}
set
{
lock (lockObject)
{
simpleCompressor.MakeUpGain = value;
}
}
}
/// <summary>
/// Threshold
/// </summary>
public double Threshold
{
get
{
return simpleCompressor.Threshold;
}
set
{
lock (lockObject)
{
simpleCompressor.Threshold = value;
}
}
}
/// <summary>
/// Ratio
/// </summary>
public double Ratio
{
get
{
return simpleCompressor.Ratio;
}
set
{
lock (lockObject)
{
simpleCompressor.Ratio = value;
}
}
}
/// <summary>
/// Attack time
/// </summary>
public double Attack
{
get
{
return simpleCompressor.Attack;
}
set
{
lock (lockObject)
{
simpleCompressor.Attack = value;
}
}
}
/// <summary>
/// Release time
/// </summary>
public double Release
{
get
{
return simpleCompressor.Release;
}
set
{
lock (lockObject)
{
simpleCompressor.Release = value;
}
}
}
/// <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;
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
/// </summary>
/// <param name="array">Buffer to read into</param>
/// <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)
{
lock (lockObject)
{
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++)
{
int start = sample * bytesPerSample * channels;
double in1;
double in2;
ReadSamples(sourceBuffer, start, out in1, out in2);
simpleCompressor.Process(ref in1, ref in2);
WriteSamples(array, offset + start, in1, 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;
}
}
}
}