Затухающий звук вход / выход

У меня фоновый звук играет в бесконечном цикле. Я хочу, чтобы он исчезал, когда пользователь нажимает кнопку.

Я попробовал следующее:

  • DirectSoundOut инициируется с помощью WaveStream
  • Таймер изменяет громкость WaveChannel32.

Эта проблема:

  • Изменение громкости во время воспроизведения звука создает шумы.

Кто-нибудь знает лучшее решение?

2 ответа

Чтобы выполнить плавное усиление или постепенное исчезновение, вы должны сделать это на уровне семпла. Затем вы умножаете каждый образец на постепенно увеличивающееся или уменьшающееся число. Вы используете WaveChannel32, поэтому ваш звук уже был преобразован в 32-разрядный формат. Затем я создал бы еще один инструмент реализации IWaveProvider, который отвечал бы за постепенное исчезновение. Обычно он будет проходить через сэмплы без изменений, но в методе Read, если вы находитесь в постепенном или постепенном исчезновении, он будет умножать каждый семпл (или пару, если он стереофонический).

Интерфейс ISampleProvider в NAudio 1.5 был разработан для того, чтобы сделать этот тип вещей намного проще, поскольку он позволяет обрабатывать сэмплы уже как 32-битные числа с плавающей запятой, а не реализовывать IWaveProvider, который требует преобразования из байта [] в float[]. Вот SampleProvider для постепенного появления и исчезновения, который я только что сделал, и который я включу в следующую NAudio, и, надеюсь, скоро опубликую блог. Просто позвони BeginFadeIn или же BeginFadeOut с соответствующей продолжительностью замирания.

public class FadeInOutSampleProvider : ISampleProvider
{
    enum FadeState
    {
        Silence,
        FadingIn,
        FullVolume,
        FadingOut,
    }

    private readonly object lockObject = new object();
    private readonly ISampleProvider source;
    private int fadeSamplePosition;
    private int fadeSampleCount;
    private FadeState fadeState;

    public FadeInOutSampleProvider(ISampleProvider source)
    {
        this.source = source;
        this.fadeState = FadeState.FullVolume;
    }

    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);
        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; }
    }
}

Или вы можете просто сделать это:

while (waveOut.volume > 0.1)
{
  waveOut.volume -= 0.1;
  System.Threading.Thread.Sleep(10);
}

^ Пример затухания. Я использую его в своих программах, отлично работает.

Другие вопросы по тегам