none
Play the same audio file again without stopping the previously playing audio WAV file RRS feed

  • Question

  • Hello community,
    I'm stuck at a position where I'm creating a VB .NET standalone application using VS 2017.

    I've made a little progress in playing 2 audio files on clicking a button among the several others without stopping the previously playing WAV audio file.
    My code here plays a new audio file by the click of a second button on the application, without stopping the old but stops the already playing audio when the same button on the application is clicked.

    But now I want to achieve a  something where a single button should play an audio file (which I now have already come up with) but on clicking the same button on the application, the sound that it was playing by the click of the same old button should not be interrupted.
    I'm completely a beginner to .NET and still have come up with this code so far. Help me with this new problem of mine. Ignore the comments.

    Imports System.Runtime.InteropServices
    Public Class Form1
        Private Wave1 As New NAudio.Wave.WaveOut
    
        Private Snds As New MultiSounds
        Private WithEvents ButtonA As New Button With {.Parent = Me, .Text = "Play A", .Location = New Point(20, 20)}
        Private WithEvents ButtonB As New Button With {.Parent = Me, .Text = "Play B", .Location = New Point(120, 20)}
        Private WithEvents RepeatCb As New CheckBox With {.Parent = Me, .Text = "Repeat", .Location = New Point(220, 20)}
    
        Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
            If e.KeyCode = Keys.B Then TextBox1.BackColor = Color.Red
        End Sub
        Private Sub ButtonA_Click_1(sender As Object, e As EventArgs) Handles ButtonA.Click
            Dim xa() As Byte = IO.File.ReadAllBytes("c:\<MY PATH>\1.wav")
            Dim data As New IO.MemoryStream(xa) 'data stream for the buffer
            Wave1.Init(New NAudio.Wave.BlockAlignReductionStream(NAudio.Wave.WaveFormatConversionStream.CreatePcmStream(New NAudio.Wave.WaveFileReader(data))))
            Wave1.Volume = 0.5
            Wave1.Play()
            GC.SuppressFinalize(Me)
            'Snds.Play("Note A")
        End Sub
    
        Private Sub ButtonB_Click(sender As Object, e As EventArgs) Handles ButtonB.Click
            Dim xa() As Byte = IO.File.ReadAllBytes("c:\<MY PATH\3.wav")
            Dim data As New IO.MemoryStream(xa) 'data stream for the buffer
            Wave1.Init(New NAudio.Wave.BlockAlignReductionStream(NAudio.Wave.WaveFormatConversionStream.CreatePcmStream(New NAudio.Wave.WaveFileReader(data))))
            'If (key(1)==128) Then 
            Wave1.Volume = 0.5
            'End If
            Wave1.Play()
            GC.SuppressFinalize(Me)
            'Snds.Play("Note B")
        End Sub
    
        Private Sub RepeatCb_CheckedChanged(sender As Object, e As EventArgs) Handles RepeatCb.CheckedChanged
            Snds.repeat = RepeatCb.Checked
        End Sub
    End Class
    
    Public Class MultiSounds
        Private Snds As New Dictionary(Of String, String)
        Private sndcnt As Integer = 0
        Public repeat As Boolean
    
        <DllImport("winmm.dll", EntryPoint:="mciSendStringW")>
        Private Shared Function mciSendStringW(<MarshalAs(UnmanagedType.LPTStr)> ByVal lpszCommand As String, <MarshalAs(UnmanagedType.LPWStr)> ByVal lpszReturnString As System.Text.StringBuilder, ByVal cchReturn As UInteger, ByVal hwndCallback As IntPtr) As Integer
        End Function
    
        Public Function AddSound(ByVal SoundName As String, ByVal SndFilePath As String) As Boolean
            If SoundName.Trim = "" Or Not IO.File.Exists(SndFilePath) Then Return False
            'to repeat must use type mpegvideo ????
            If mciSendStringW("open " & Chr(34) & SndFilePath & Chr(34) & " type mpegvideo alias " & "Snd_" & sndcnt.ToString, Nothing, 0, IntPtr.Zero) <> 0 Then Return False
            'If mciSendStringW("open " & Chr(34) & SndFilePath & Chr(34) & " alias " & "Snd_" & sndcnt.ToString, Nothing, 0, IntPtr.Zero) <> 0 Then Return False
            Snds.Add(SoundName, "Snd_" & sndcnt.ToString)
            sndcnt += 1
            Return True
        End Function
    
        Public Function Play(ByVal SoundName As String) As Boolean
            If Not Snds.ContainsKey(SoundName) Then Return False
            mciSendStringW("seek " & Snds.Item(SoundName) & " to start", Nothing, 0, IntPtr.Zero)
    
            If repeat Then
                If mciSendStringW("play " & Snds.Item(SoundName) & " repeat", Nothing, 0, IntPtr.Zero) <> 0 Then Return False
            Else
                If mciSendStringW("play " & Snds.Item(SoundName), Nothing, 0, IntPtr.Zero) <> 0 Then Return False
            End If
    
            Return True
        End Function
    
        Public Function [Stop](ByVal SoundName As String) As Boolean
            If Not Snds.ContainsKey(SoundName) Then Return False
            If mciSendStringW("stop " & Snds.Item(SoundName), Nothing, 0, IntPtr.Zero) <> 0 Then Return False
            Return True
        End Function
    End Class
    Thanks in advance.

    Wednesday, January 8, 2020 7:16 PM

All replies

  • Hi

    I have no idea what NAudio is - nor do I want to know. However, here is bare bones of code that does play simultaneous .wav files. This needs Media Player to be installed and a Media Player control to be added in the Designer (which in turn adds the needed DLL's to the references.)

    As mentioned at the top of the code, I know very little of using the Media Player and/or playing music in general. This code is some I posted in a previous similar question. It does no harm to try it out to see if it helps.

    ' an overly simple example (due to
    ' the fact that I know little
    ' of using Media Player)
    ' Multiple simultaneous sounds
    ' Form1 with Windows Media Player1,
    ' 5 Buttons
    Option Strict On
    Option Explicit On
    Imports AxWMPLib
    Public Class Form1
    	Dim ax1, ax2, ax3, ax4, ax5 As New AxWindowsMediaPlayer
    	Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    		ax1.CreateControl()
    		ax2.CreateControl()
    		ax3.CreateControl()
    		ax4.CreateControl()
    		ax5.CreateControl()
    	End Sub
    	Private Sub Button1_Click_1(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
    		ax1.URL = Application.StartupPath & "\Data\GameOver.wav"
    	End Sub
    	Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    		ax2.URL = Application.StartupPath & "\Data\bargain.wav"
    	End Sub
    	Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
    		ax3.URL = Application.StartupPath & "\Data\click.wav"
    	End Sub
    	Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
    		ax4.URL = Application.StartupPath & "\Data\yes.wav"
    	End Sub
    	Private Sub Button5_Click(sender As Object, e As EventArgs) Handles Button5.Click
    		ax5.URL = Application.StartupPath & "\Data\cases.wav"
    	End Sub
    End Class


    Regards Les, Livingston, Scotland

    Wednesday, January 8, 2020 7:32 PM
  •  

    Thats the trouble with 3rd party tools.  :)

    Here is MCI control that creates a new instance of the song each time the button is clicked. So now the previous song keeps playing instead of restarting. 

    WndProc Notify is used and then the songs that are done playing are closed.

    The mci messages are displayed in the textbox as they occur.

    The example makes the controls. Just cut and paste the code into an empty form. Change the form name as required.

    Imports System.Runtime.InteropServices
    Imports System.Text
    
    Public Class Form4
        Private WithEvents SongAButton As New Button With {.Parent = Me, .Text = "Play A",
                .Location = New Point(20, 20)}
        Private WithEvents SongBButton As New Button With {.Parent = Me, .Text = "Play B",
                .Location = New Point(120, 20)}
        Private WithEvents ClearButton As New Button With {.Parent = Me, .Text = "Clear",
                .Location = New Point(220, 20)}
        Private WithEvents RepeatCb As New CheckBox With {.Parent = Me, .Text = "Repeat",
                .Location = New Point(320, 20)}
        Private TextBox1 As New TextBox With {.Parent = Me, .Text = "Ready",
                .Location = New Point(50, 60), .Size = New Size(300, 220),
                .Multiline = True, .WordWrap = True, .ScrollBars = ScrollBars.Vertical}
    
        <DllImport("winmm.dll")>
        Private Shared Function mciSendString(ByVal command As String, ByVal buffer As StringBuilder, ByVal bufferSize As Integer, ByVal hwndCallback As IntPtr) As Integer
        End Function
    
        Private Songs As New List(Of String)
        Private SoundCount As Integer
    
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            Text = "Play Multiple Sounds"
            ClientSize = New Size(400, 300)
        End Sub
    
        Private Sub SongAButton_Click_1(sender As Object, e As EventArgs) Handles SongAButton.Click
            PlaySong("C:\test sounds\w_hwy.wav")
        End Sub
    
        Private Sub SongBButton_Click(sender As Object, e As EventArgs) Handles SongBButton.Click
            PlaySong("C:\test sounds\W_LL484.wav")
        End Sub
    
        Private Sub ClearButton_Click(sender As Object, e As EventArgs) Handles ClearButton.Click
            Dim r As Integer
    
            For i = Songs.Count - 1 To 0 Step -1
                r = mciSendString("close " & Songs(i), Nothing, 0, IntPtr.Zero)
                TextBox1.AppendText(vbCrLf & "Close " & r.ToString & "  " & Songs(i))
                Songs.Remove(Songs(i))
            Next
            TextBox1.Refresh()
    
        End Sub
    
        Private Sub PlaySong(FilePath As String)
            Dim MediaFile As String = "Snd_" & SoundCount
            Songs.Add(MediaFile)
            mciSendString("open " & Chr(34) & FilePath & Chr(34) & " type mpegvideo alias " & MediaFile, Nothing, 0, IntPtr.Zero)
            mciSendString("seek " & MediaFile & " to start", Nothing, 0, IntPtr.Zero)
    
            Dim PCommand As String = "play " & MediaFile
            If RepeatCb.Checked Then
                PCommand += " repeat notify "
            Else
                PCommand += " notify "
            End If
            mciSendString(PCommand, Nothing, 0, Me.Handle) 'must use handle return callback
    
            TextBox1.AppendText(vbCrLf & PCommand & "  " & FilePath)
    
            SoundCount += 1
        End Sub
    
        Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
            Const MM_MCINOTIFY As Integer = &H3B9
            Const MCI_NOTIFY_SUCCESSFUL As Integer = &H1
            Const MCI_NOTIFY_ABORTED As Integer = &H4
            Const MCI_NOTIFY_FAILURE As Integer = &H8
            Const MCI_NOTIFY_SUPERSEDED As Integer = &H2
            'MCIERR_BASE = 256
            'MCIERR_INVALID_DEVICE_NAME = (MCIERR_BASE + 7) 'So 263
            'MCIERR_INTERNAL = (MCIERR_BASE + 21) 'So 277
    
            Select Case (m.Msg)
                Case MM_MCINOTIFY
    
                    Select Case m.WParam.ToInt32()
                        Case MCI_NOTIFY_SUCCESSFUL
                            'a song has ended so search all songs and close any that are not playing
                            For i = Songs.Count - 1 To 0 Step -1
                                If Not IsPlaying(Songs(i)) Then
                                    Dim r As Integer = mciSendString("close " & Songs(i), Nothing, 0, IntPtr.Zero)
                                    TextBox1.AppendText(vbCrLf & "Close " & r.ToString & "  " & Songs(i))
                                    Songs.Remove(Songs(i))
                                Else
                                    TextBox1.AppendText(vbCrLf & "Playing " & Songs(i))
                                End If
                            Next
                            TextBox1.Refresh()
    
                        Case MCI_NOTIFY_ABORTED
                        Case MCI_NOTIFY_SUPERSEDED
                        Case MCI_NOTIFY_FAILURE
                        Case Else
                    End Select
            End Select
    
            MyBase.WndProc(m)
        End Sub
    
        Public Function IsPlaying(MediaFile As String) As Boolean
            Dim Pcommand As String = "status " & MediaFile & " mode"
            Dim returnData As New StringBuilder
            Dim r As Integer = mciSendString(Pcommand, returnData, 128, IntPtr.Zero)
    
            If returnData.Length = 7 AndAlso returnData.ToString().Substring(0, 7) = "playing" Then
                Return True
            Else
                Return False
            End If
        End Function
    End Class
    
    

     
    Thursday, January 9, 2020 3:08 AM
  • Hi Shashank V Ray, 

    Thank you for posting here.

    For your question, I make a test on my side and successfully play the same audio file again without stopping the previously playing audio WAV file.

    First of all, you need to add the Windows Media Player Control.

    Check ‘Adding the Windows Media Player Control’ part in the following reference:

    Using the Windows Media Player Control with Microsoft Visual Studio

    After adding the Windows Media Player Control in your form.

    You can refer to the following code to play the audio.

        Private Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs)
            Dim player1 = New WMPLib.WindowsMediaPlayer()
            player1.URL = "D:\TestFile\Test1.wav"
        End Sub
    
        Private Sub button2_Click(ByVal sender As Object, ByVal e As EventArgs)
            Dim player2 = New WMPLib.WindowsMediaPlayer()
            player2.URL = "D:\TestFile\Test1.wav"
        End Sub
    

    Hope it can help you.

    Best Regards,

    Xingyu Zhao


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Thursday, January 9, 2020 9:52 AM
    Moderator
  • Hey tommytwotrain

    I need a minor off-the-topic help.

    I want to set in milli seconds, when the Repeat checkbox is selected to apply the delay to play the same audio file after a certain milli seconds gap.

    When repeat is selected, the audio file going on repeat has to be played again and again with each time having a minor delay in playing the same audio file next time.
    For eg say, if delay is set to 300 ms, the audio file XYZ.wav has to play (when the Repeat is checked) and wait for 300 ms and play that again completely and wait 300 ms and so on..
    Thank you

    Sunday, January 12, 2020 3:57 AM
  • Hey tommytwotrain,
    In your code, when the repeat is put on, the audio file that is put on repeat doesn't stop even after the repeat checkbox is unchecked.

    Can you please help me with this??
    Thanks in advance...


    • Edited by Shashank V Ray Sunday, January 12, 2020 4:27 AM spelling mistake
    Sunday, January 12, 2020 4:23 AM
  • Hey tommytwotrain,
    In your code, when the repeat is put on, the audio file that is put on repeat doesn't stop even after the repeat checkbox is unchecked.

    Can you please help me with this??
    Thanks in advance...


    Ray,

    RE last two qustions.

    That is because this method of playing the multiple songs, lets call it the separate instance method, makes a new instance of the song each time the button is clicked. Then each instance of a song plays to the end unless that specific exact instance of the song is canceled.

    See how that works in the code? We keep a list of the instances we start and then we dispose them when the song is marked as not playing by mci ie song is over. Then if not playing we delete that instance. We could just start a new instance here for repeat???

    So you need to understand what is happening there and then you can answer both your questions.

    Its not perfectly clear what you want from the repeat.

    Again, to control repeat delay, you could maybe just start a new instance instead of using the repeat flag in the mci command. ie make your own repeat. When the song has stopped then start it again instead of disposing.

    I have never done it of course and can think of some issues but...

    To make the repeat stop is the same issue. Thats what the cancel button is for now. If you make your own repeat then perhaps you can come up with a way of identifying the song and disposing it when on repeat. It depends how exact you mean to be.

    Comprende?

    Why dont you give it a try and then come back with issues you run into. Maybe start a new question if complicated it gets others interested again.

    Sunday, January 12, 2020 12:31 PM
  • PS Have you considered using the WindowsMediaPlayer control like Les and Xing have shown? Have your tried the examples and what did not work etc?

    I just show MCI but sometimes it wont work and you need to use Media Player. Or the other way around. Or in my wav studio I use both. Or others even.

    The reason I bring it up is media player  has the best current play position property it seems. And length of song (time). So you can maybe use a timer to get the end of the song and repeat or dispose.

    https://docs.microsoft.com/en-us/windows/win32/wmp/controls-currentposition

    Sometimes we need to try it both ways just to figure what will work and then work best.

    :)

    Sunday, January 12, 2020 1:05 PM