如何使用SoundPlayer淡出.wav文件的音频,而不是立即停止

本文关键字:音频 SoundPlayer 何使用 淡出 wav 文件 | 更新日期: 2023-09-27 18:26:43

我试图在释放按钮后立即停止声音,但结果是文件被完全播放。我不想像sp.Stop()那样突然停止声音。有办法做到这一点吗?

private void button1_MouseDown(object sender, MouseEventArgs e)
{
    if(sender == mk)
    {
        if(e.Button == MouseButtons.Left)
        {
            SoundPlayer sp = new SoundPlayer();
            sp.SoundLocation = (@"C:'my path'sound.wav");
            sp.Play();
        }
    }
}
private void button1_MouseUp(object sender, MouseEventArgs e)
{
    if(sender == mk)
    {
        if(e.Button == MouseButtons.Left)
        { /*some code here (without sp.Stop())*/ }
    }
}

如何使用SoundPlayer淡出.wav文件的音频,而不是立即停止

我将您的问题解释为:

如何使用SoundPlayer淡出.wav文件的音频,而不是立即停止它?

简单的答案是,单独使用SoundPlayer是做不到的。您需要使用winmm.dll库中的几个api。

如果您对WPF感到满意,那么在其volume属性上使用MediaElementDoubleAnimation可能是更好的方法。有关这方面的信息,请参阅此问题。

但是,如果您真的想使用WinForms,下面的实现应该是可行的。

关于Form1的说明:

  • 两个按钮,分别命名为startButtonstopButton
  • 一个名为timer1的计时器,我用它来淡出声音

以下是使其工作的代码:

using System;
using System.Media;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        [DllImport("winmm.dll", EntryPoint = "waveOutGetVolume")]
        private static extern int WaveOutGetVolume(IntPtr hwo, out uint dwVolume);
        [DllImport("winmm.dll", EntryPoint="waveOutSetVolume")]
        private static extern int WaveOutSetVolume(IntPtr hwo, uint dwVolume);
        private SoundPlayer player = new SoundPlayer();
        // a crude delta time field
        private float totalElapsedTime;
        // tweak this value to determine how quickly you want the fade to happen
        private const float Velocity = 0.001f;
        public Form1()
        {
            this.InitializeComponent();
            // i was using 100 milliseconds as my "frame rate"
            this.timer1.Interval = 100;
            this.stopButton.Enabled = false;
        }
        private void startButton_Click(object sender, EventArgs e)
        {
            // sets the audio device volume to the max.
            // this is not the computer's volume so it won't
            // blast out your ear drums by doing this unless you
            // have the computer volume super high - which is not this
            // code's fault
            WaveOutSetVolume(IntPtr.Zero, uint.MaxValue);
            this.startButton.Enabled = false;
            this.stopButton.Enabled = true;
            this.totalElapsedTime = 0f;
            this.player.SoundLocation = @"Music File.wav";
            this.player.Load();
            this.player.Play();
        }
        private void stopButton_Click(object sender, EventArgs e)
        {
            // being the stop
            this.timer1.Start();
            this.stopButton.Enabled = false;
        }
        private void timer1_Tick(object sender, EventArgs e)
        {
            // amount to interpolate (value between 0 and 1 inclusive)
            float amount = Math.Min(1f, this.totalElapsedTime * Velocity);
            // the new channel volume after a lerp
            float lerped = Lerp(ushort.MaxValue, 0, amount);
            // each channel's volume is actually represented as a ushort
            ushort channelVolume = (ushort)lerped;
            // the new volume for all the channels
            uint volume = (uint)channelVolume | ((uint)channelVolume << 16);
            // sets the volume 
            WaveOutSetVolume(IntPtr.Zero, volume);
            // checks if the interpolation is finished
            if (amount >= 1f)
            {
                // stop the timer 
                this.timer1.Stop();
                // stop the player
                this.player.Stop();
                // stop is complete so let user start again
                this.startButton.Enabled = true;
            }
            // add the elapsed milliseconds (very crude delta time)
            this.totalElapsedTime += this.timer1.Interval;
        }
        public static float Lerp(float value1, float value2, float amount)
        {
            // does a linear interpolation
            return (value1 + ((value2 - value1) * amount));
        }
    }
}

希望这应该是不言自明的。我想大多数人都会认为这是一种粗糙的"游戏循环esqe"方法,但它似乎很有效。这里需要注意的是,调整衰落发生的速度是Velocity常数。

该代码设置为在1秒内淡出。通过查看timer1.IntervalVelocity可以很容易地理解这一点。在1000毫秒(10个计时器滴答)之后,1000*0.001=1,这将导致停止代码完成。

更改timer1.Interval的唯一原因是为了产生更多的"淡入淡出"。当前设置在停止前进行10次音量渐变。