162 lines
5 KiB
C#
162 lines
5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using NAudio.Wave;
|
|
|
|
namespace AutoDance.Model
|
|
{
|
|
public class FadeInOutSampleProvider : ISampleProvider
|
|
{
|
|
enum FadeState
|
|
{
|
|
Silence,
|
|
FadingIn,
|
|
FullVolume,
|
|
FadingOut,
|
|
}
|
|
|
|
private const float Log10dividedBy20 = 0.1151292546497f;
|
|
|
|
private readonly object lockObject = new object();
|
|
private readonly ISampleProvider source;
|
|
private int fadeSamplePosition;
|
|
private int fadeSampleCount;
|
|
private FadeState fadeState;
|
|
private float _volumeNormalisationLinear = 0;
|
|
private float _volumeNormalisationDecibels = 0;
|
|
|
|
public float VolumeNormalisationDecibels
|
|
{
|
|
get { return _volumeNormalisationDecibels; }
|
|
set
|
|
{
|
|
_volumeNormalisationDecibels = value;
|
|
_volumeNormalisationLinear = GetLinearVolumeFromDecibelVolume(value);
|
|
}
|
|
}
|
|
|
|
public FadeInOutSampleProvider(ISampleProvider source)
|
|
{
|
|
this.source = source;
|
|
this.fadeState = FadeState.FullVolume;
|
|
VolumeNormalisationDecibels = 0;
|
|
}
|
|
|
|
public void BeginFadeIn(double fadeDurationInMilliseconds)
|
|
{
|
|
lock (lockObject)
|
|
{
|
|
fadeSamplePosition = 0;
|
|
fadeSampleCount = (int)((fadeDurationInMilliseconds * source.WaveFormat.SampleRate) / 1000);
|
|
fadeState = FadeState.FadingIn;
|
|
}
|
|
}
|
|
|
|
public void BeginFadeOut(double fadeDurationInMilliseconds)
|
|
{
|
|
lock (lockObject)
|
|
{
|
|
fadeSamplePosition = 0;
|
|
fadeSampleCount = (int)((fadeDurationInMilliseconds * source.WaveFormat.SampleRate) / 1000);
|
|
fadeState = FadeState.FadingOut;
|
|
}
|
|
}
|
|
|
|
public int Read(float[] buffer, int offset, int count)
|
|
{
|
|
int sourceSamplesRead = source.Read(buffer, offset, count);
|
|
|
|
// apply volume normalisation
|
|
int sample = 0;
|
|
while (sample < sourceSamplesRead)
|
|
{
|
|
for (int ch = 0; ch < source.WaveFormat.Channels; ch++)
|
|
{
|
|
buffer[offset + sample++] *= _volumeNormalisationLinear;
|
|
}
|
|
}
|
|
|
|
lock (lockObject)
|
|
{
|
|
if (fadeState == FadeState.FadingIn)
|
|
{
|
|
FadeIn(buffer, offset, sourceSamplesRead);
|
|
}
|
|
else if (fadeState == FadeState.FadingOut)
|
|
{
|
|
FadeOut(buffer, offset, sourceSamplesRead);
|
|
}
|
|
else if (fadeState == FadeState.Silence)
|
|
{
|
|
ClearBuffer(buffer, offset, count);
|
|
}
|
|
}
|
|
return sourceSamplesRead;
|
|
}
|
|
|
|
private static void ClearBuffer(float[] buffer, int offset, int count)
|
|
{
|
|
for (int n = 0; n < count; n++)
|
|
{
|
|
buffer[n + offset] = 0;
|
|
}
|
|
}
|
|
|
|
private void FadeOut(float[] buffer, int offset, int sourceSamplesRead)
|
|
{
|
|
int sample = 0;
|
|
while (sample < sourceSamplesRead)
|
|
{
|
|
float multiplier = 1.0f - (fadeSamplePosition / (float)fadeSampleCount);
|
|
for (int ch = 0; ch < source.WaveFormat.Channels; ch++)
|
|
{
|
|
buffer[offset + sample++] *= multiplier;
|
|
}
|
|
fadeSamplePosition++;
|
|
if (fadeSamplePosition > fadeSampleCount)
|
|
{
|
|
fadeState = FadeState.Silence;
|
|
// clear out the end
|
|
ClearBuffer(buffer, sample + offset, sourceSamplesRead - sample);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void FadeIn(float[] buffer, int offset, int sourceSamplesRead)
|
|
{
|
|
int sample = 0;
|
|
while (sample < sourceSamplesRead)
|
|
{
|
|
float multiplier = (fadeSamplePosition / (float)fadeSampleCount);
|
|
for (int ch = 0; ch < source.WaveFormat.Channels; ch++)
|
|
{
|
|
buffer[offset + sample++] *= multiplier;
|
|
}
|
|
fadeSamplePosition++;
|
|
if (fadeSamplePosition > fadeSampleCount)
|
|
{
|
|
fadeState = FadeState.FullVolume;
|
|
// no need to multiply any more
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public WaveFormat WaveFormat
|
|
{
|
|
get { return source.WaveFormat; }
|
|
}
|
|
|
|
private static float GetLinearVolumeFromDecibelVolume(float decibelVolume)
|
|
{
|
|
if (decibelVolume < -99.0f)
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
return (float)Math.Exp(decibelVolume * Log10dividedBy20);
|
|
}
|
|
}
|
|
}
|