0

I am having trouble looping through my playlist of mp3 tracks.

Can anybody shed some light on where I am going wrong. This is the code. It play the first track, but then stops.

Code has been edited 011020

     ' The following Menu Item plays the full list of mp3s in the list box
    Private Sub PlayListToolStripMenuItem_Click(sender As Object, e As EventArgs) 
    Handles PlayListToolStripMenuItem.Click
    FromDictionary = False
    PlaySongsFromListBox()
    End Sub

    Private Sub PlaySongsFromListBox()
    'MsgBox("Index Counter = " & NextIndex)
    'MsgBox("Total No of Tracks = " & ListBox1.Items.Count)
    If NextIndex = ListBox1.Items.Count Then
        NextIndex = 0
        MessageBox.Show("All music has been played")
        Exit Sub
    End If
    Dim item As KeyValuePair(Of String, String) = 
 DirectCast(ListBox1.Items(NextIndex), KeyValuePair(Of String, String))
    Dim SongPath = Path.Combine(item.Value, item.Key)
    PlayASong(SongPath)
End Sub
Private Sub PlayASong(SongPath As String)
    MsgBox("Playing Track: " & SongPath)
    CurrentMediaStreamName = SongPath
    MediaPlayer1.URL = SongPath
    MediaPlayer1.Ctlcontrols.play()
    MsgBox("Playing Track: " & SongPath)
End Sub

Private Sub MediaPlayer1_PlayStateChange(sender As Object, e As 
AxWMPLib._WMPOCXEvents_PlayStateChangeEvent) Handles 
MediaPlayer1.PlayStateChange
    If MediaPlayer1.playState = WMPLib.WMPPlayState.wmppsMediaEnded Then
        NextIndex += 1
        PlaySongsFromListBox()
    End If
 End Sub
  • As soon as I add the Loop Counter, PlaySong(i), it causes the app to error and the track does not play at all. – John Henderson Sep 27 '20 at 08:28
  • Does `MediaPlayer1.Ctlcontrols.play()` block execution of your code until the song has finished playing? I think it unlikely.. Hence.. Think about it. If you have 100 songs in a list, and you use a for loop to execute "play" one after the other then it's going to zip through the list in about 0.01 seconds telling the media player to play pay play play pay play play pay play play pay play play pay play play pay play play pay play play paly play... and the player is probably only going to play the last one – Caius Jard Sep 27 '20 at 08:52
  • Yep that's exactly what its doing, Im trying to use the state change to pick up that its playing... If MediaPlayer1.playState = WMPLib.WMPPlayState.wmppsMediaEnded Then – John Henderson Sep 27 '20 at 09:18
  • Sounds like you need to NOT be issuing your play commands in a loop but instead have a "current Index in a class level property, play the item at index X, increment X so it's +1, and in the "playback has ended" event, issue a command to play the current value of X (and increment it again) – Caius Jard Sep 27 '20 at 09:31
  • You are looping through numbers i in your loop but you never use i inside the loop code. You just keep trying to play the same song over and over again. I don't think the selected item in a list box has a .Value property or a .Key property. You seem to have some confusion between your MusicDictionary and your ListBox items. – Mary Sep 27 '20 at 09:33
  • Yes I do Mary, I cant see the wood for the trees now. My problem is adding the counter in the right place to get the loop to work correctly. – John Henderson Sep 27 '20 at 09:48
  • The value of the dictionary holds the filepath and the key holds the file name that is stored in the listbox. The PlaySong variable Contains the value and the key. When I add a counter to it, PlaySong(i) it then fails to play the file. I am lost. – John Henderson Sep 27 '20 at 09:52
  • Write your algorithm in comments using English, then translate it to vb. you're getting lost trying to speak a new language and think in it at the same time (humans don't do that- they learn french by carrying on thinking in English for some time , translating to french. Gradually they starts to think in french. VB is the same – Caius Jard Sep 27 '20 at 14:07
  • Thank you Caius, for putting it in an eloquent manner. I am very much a newby to vb.net and may have bitten off more than I can chew, but I am also determined to continue and finish off what I have started. Once again thank you for your valuable comments. They mean a lot to a newby. – John Henderson Sep 28 '20 at 06:44

2 Answers2

0

At the class level (Form1) I declared 4 variables that are used in more than one method.

In the Form.Load I filled the MusicDictionary. You may be filling this from a database or text file. I also bound the ListBox to the dictionary. The ListBox.DataSource cannot take a Dictionary directly but a BindingSource can handle it.

PlayASong simply does what it says.

PlaySongsFromDictionary first checks if we have come to the end of the Dictionary. Note that I reset NextIndex back to 0. I used Path.Combine to get the path for the song. Notice that in one dictionary entry, I left out a back slash at the end of the directories and in the other entry I included the backslash. Path.Combine accepted both and added a back slash where necessary. Also notice that the index is applied to the Keys collection and the Values collection of the Dictionary not the Dictionary itself. If we tried to apply the index to the dictionary it would be trying to look for the Key inside the brackets. Default Public Property Item(key As TKey) As TValue

PlaySongsFromListBox casts the list box item (which is an object) back to its underlying type, KeyValuePair.

Finally, the important part! I used the MediaEnded event of the MediaPlayer1 to determine when to play the next song. Here I incremented the index and called the one of the PlaySongs... methods.

Private NextIndex As Integer
Private WithEvents MediaPlayer1 As New MediaPlayer
Private MusicDictionary As New Dictionary(Of String, String)
Private FromDictionary As Boolean

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    MusicDictionary.Add("2018-11-27_-_Track_A_-_FesliyanStudios.com_By_Stephen_Bennett.mp3", "C:\Users\maryo\Documents\Marys Music")
    MusicDictionary.Add("file_example_MP3_700KB.mp3", "C:\Users\maryo\Documents\Marys Music\")
    ListBox1.DataSource = New BindingSource(MusicDictionary, Nothing)
    ListBox1.DisplayMember = "Key"
    ListBox1.ValueMember = "Value"
End Sub

Private Sub PlayASong(SongPath As String)
    MediaPlayer1.Open(New Uri(SongPath))
    MediaPlayer1.Play()
End Sub

Private Sub PlaySongsFromDictionary()
    If NextIndex = MusicDictionary.Count Then
        NextIndex = 0
        MessageBox.Show("All music has been played")
        Exit Sub
    End If
    Dim SongPath = Path.Combine(MusicDictionary.Values(NextIndex), MusicDictionary.Keys(NextIndex))
    PlayASong(SongPath)
End Sub

Private Sub PlaySongsFromListBox()
    If NextIndex = ListBox1.SelectedItems.Count Then
        NextIndex = 0
        MessageBox.Show("All music has been played")
        Exit Sub
    End If
    Dim item As KeyValuePair(Of String, String) = DirectCast(ListBox1.SelectedItems(NextIndex), KeyValuePair(Of String, String))
    Dim SongPath = Path.Combine(item.Value, item.Key)
    PlayASong(SongPath)
End Sub

Public Sub MediaPlayers1_MediaEnded() Handles MediaPlayer1.MediaEnded
    NextIndex += 1
    If FromDictionary Then
        PlaySongsFromDictionary()
    Else
        PlaySongsFromListBox()
    End If
End Sub

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    FromDictionary = False
    PlaySongsFromListBox()
End Sub

Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
    FromDictionary = True
    PlaySongsFromDictionary()
End Sub
Mary
  • 14,926
  • 3
  • 18
  • 27
  • Thanks Mary I will give it go, and try and understand what you have explained. Will comment again later. – John Henderson Sep 28 '20 at 06:50
  • I managed to get your code to work @Mary, but for some reason it wont loop and play all the tracks as it is meant to. I had to change a few things to get it to work. I will update the original code so that you can see what is going on. – John Henderson Oct 01 '20 at 07:53
  • I have put the msgboxes there as it is only when they are there that it loops through the whole song list. – John Henderson Oct 01 '20 at 08:00
0

The problem with the MP3's not looping was solved using the following invoke method

If e.newState = WMPLib.WMPPlayState.wmppsStopped Then
    Me.BeginInvoke(New Action(AddressOf NextSong))
End If

Found at the following location Function call only works when MessageBox.Show() is included?