3

I'm trying to stop sound as soon as I release the button, but what happens is that file is played fully. I don't want to stop the sound abruptly as it happened with sp.Stop(). Is there a way to do this?

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())*/ }
    }
}
test
  • 2,432
  • 2
  • 22
  • 48
Gleb Eliseev
  • 55
  • 11

1 Answers1

3

I'm interpreting your question as:

How do I fade out the audio of a .wav file using SoundPlayer instead of stopping it immediately?

The short answer is that you can't do it with SoundPlayer alone. You'll need the use of a couple apis from the winmm.dll library.

If you are comfortable with WPF, then using a MediaElement with a DoubleAnimation on it's volume property is probably a better way to do this. See this question for information on that.

However if you really want to use WinForms, the following is an implementation that should work.

Notes about Form1:

  • Two buttons, named startButton and stopButton
  • A timer named timer1 that I use to fade out the sound

Here is the code to get this working:

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

    }

}

Hopefully this should be pretty self-explanatory. I think most people will recognize this as a crude "game-loop-esqe" approach, but it seems to work well. Of note here is that to tweak the speed at which the fade happens is the Velocity constant.

The code is setup to fade out over 1 second. It's easy to figure that out by looking at the timer1.Interval and the Velocity. After 1000 milliseconds (10 timer ticks) then 1000 * 0.001 = 1 which causes the stop code to complete.

The only reason to change the timer1.Interval is to make more "fades" occur. Current setup does 10 volume fades before stopping.

test
  • 2,432
  • 2
  • 22
  • 48
  • Wow. Just wow. You rock! I managed to assign them to actual events, so now its working how i need it to work. Thanks a lot! – Gleb Eliseev Dec 14 '15 at 23:20