Search code examples
vb.netmetadatamp3naudio

naudio not releasing file after dispose vb.net, It's really an declaration issue I think


I only post here when I'm really stuck, so here goes.

I'm creating a program where I can edit the metadata of MP3 files (with all data pulled from a mysql db), using taglib-sharp. If I preview an audio file using naudio, I can hear the audio no problems and I can stop it, so that is all good. For the sake of what is happening in my code, It's a matter of choosing a file from a listbox, and then I push a 'play' button to start, and another button to stop. My error is showing up with a right click context menu when I'm trying to save metadata.

The problem is when I go to edit the metadata of that same file I just played, the error appears - as per this shown in the following example of code. When I try and call the mp3.save() line, I receive the following error:

************** Exception Text **************

System.IO.IOException: The process cannot access the file 'C:\temp\test.mp3' because it is being used by another process. at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share) at TagLib.File.LocalFileAbstraction.get_WriteStream() at TagLib.File.set_Mode(AccessMode value) at TagLib.NonContainer.File.Save() at LinersDB_META.Form1.menuChoice(Object sender, EventArgs e) in C:\Users\smonro\source\repos\WindowsApp1\WindowsApp1\Form1.vb:line 2281 at System.Windows.Forms.ToolStripItem.RaiseEvent(Object key, EventArgs e) at System.Windows.Forms.ToolStripMenuItem.OnClick(EventArgs e) at System.Windows.Forms.ToolStripItem.HandleClick(EventArgs e) at System.Windows.Forms.ToolStripItem.HandleMouseUp(MouseEventArgs e)
at System.Windows.Forms.ToolStrip.OnMouseUp(MouseEventArgs mea) at System.Windows.Forms.ToolStripDropDown.OnMouseUp(MouseEventArgs mea)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks) at System.Windows.Forms.Control.WndProc(Message& m) at System.Windows.Forms.ToolStrip.WndProc(Message& m) at System.Windows.Forms.ToolStripDropDown.WndProc(Message& m) at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

etc

I know why it's happening, but I'm lost as to how to solve it.

According to this page, (which is in C#) I need to release the "Mp3FileReader".

In my case, it would be "data" - this: Dim data As New NAudio.Wave.Mp3FileReader(file)

The problem is, it's in a private sub, as a private variable.

However, I'm struggling to see how I can make this declaration public, when I need to have file also declared, but it would also need to be known too, and reinitialized every time I choose a different audio file.

Any thoughts?

Should I just try and rearrange my program to work differently? Thanks in advance.

'...

Imports System.IO 'File Operations
Imports TagLib ' Tagging
Imports NAudio
'...

    'Wave out device for playing the sound
    Public Shared Wave1 As New NAudio.Wave.WaveOut 'Wave out device for playing the sound    

    Public Sub start_audio()

        Dim file As String = "C:\test\test.mp3"

        If IO.File.Exists(file) Then
            Dim data As New NAudio.Wave.Mp3FileReader(file)
            Wave1.Init(data)
            Wave1.Play()
        End If
    End Sub

     Public Sub Button5_Click_1(sender As Object, e As EventArgs) Handles Button5.Click        
        Wave1.Stop()        
    End Sub

    Public Sub menuChoice(ByVal sender As Object, ByVal e As EventArgs)
        Dim item = CType(sender, ToolStripMenuItem)
        Dim selection = CInt(item.Tag)        
        Select Case selection
            Case 1
                ' Remove the Artwork
                'Insert Album art and save to file and dispose.
                If IO.File.Exists(GLOBAL_Full_Path) Then

                    Wave1.Stop()
                    Wave1.Dispose()

                    System.GC.Collect()
                    System.GC.WaitForPendingFinalizers()

                    Dim mp3 As TagLib.File = TagLib.File.Create(GLOBAL_Full_Path)

                    ' Clear the artwork from the file.. (It's a big process)
                    Dim pics As Picture = New Picture()
                    Dim picsFrame As New TagLib.Id3v2.AttachedPictureFrame(pics)
                    picsFrame.MimeType = System.Net.Mime.MediaTypeNames.Image.Jpeg
                    'set the type of picture (front cover)
                    picsFrame.Type = TagLib.PictureType.FrontCover
                    picsFrame.Description = "" ' So the Actual location of the image is not stored in the file. This would otherwise reveal internal systems to the public.
                    'Id3v2 allows more than one type of image, just one is needed here.
                    Dim pictFrames() As TagLib.IPicture = {picsFrame}
                    'set the pictures in the tag
                    mp3.Tag.Pictures = pictFrames

                    ' ******************************************
                    ' Error occurs here after a file has been played.
                    mp3.Save()
                    ' Error occurs here                 
                    ' ******************************************

                    mp3.Dispose()
                End If
            Case Else
        End Select
    End Sub             

Solution

  • After @TnTinMn pointed me in the right direction, I solved the issue in the end. While this is not the full example, I've now solved this by adding and changing a couple of minor things.

    Imports NAudio
    ' NEW/required to get the Mp3FileReader, so it can be dynamically populated.
    Imports NAudio.Wave 
    
    Dim WavePlayOut As New NAudio.Wave.WaveOut 'Wave out device for playing the sound
     Dim WaveFile As String = "" ' *** NEW/Declared ***
     Dim WaveData As Mp3FileReader ' *** Changed ***
    
     Public Sub start_audio()
    
        If File_ListView.SelectedItems.Count > 0 Then
            WaveFile = TxtBx_Path.Text & "\" & File_ListView.FocusedItem.Text
            If WavePlayOut IsNot Nothing Then
                WavePlayOut = New NAudio.Wave.WaveOut
            End If
            If WaveData IsNot Nothing Then
                WaveData.Dispose()
                WaveData = New Mp3FileReader(WaveFile)
                If WaveData.Length > 1 Then
                    If IO.File.Exists(WaveFile) Then
                        WavePlayOut.Init(WaveData)
                        WavePlayOut.Play()
                    End If
                End If
            Else
                WaveData = New Mp3FileReader(WaveFile)
                If WaveData.Length > 1 Then
                    If IO.File.Exists(WaveFile) Then
                        WavePlayOut.Init(WaveData)
                        WavePlayOut.Play()
                    End If
                End If
            End If
        End If
    End Sub
    

    To unload the audio file, use the following:

    If WaveData IsNot Nothing Then
         WaveData.Dispose()
    End If
    If WavePlayOut IsNot Nothing Then
         WavePlayOut.Stop()
         WavePlayOut.Dispose()
    End If