VB.net - 同步音频

时间:2013-12-07 04:38:31

标签: vb.net audio

所以我正在为学校开发一个简单的VB.net游戏,你可以在其中弹出气泡。弹出气泡时我们需要有一个声音播放,这对于音频播放功能非常简单;

Private Sub bubblePop(sender As Object, e As EventArgs) Handles bubble.Click
    My.Computer.Audio.Play(My.Resources.pop, _
        AudioPlayMode.Background)
End Sub

然而,我们还有一个小游戏背景,我们想要无限循环在后台。我们尝试使用该函数的类似实例;

Private Sub GameScreen_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    My.Computer.Audio.Play(My.Resources.musicLoop, _
        AudioPlayMode.BackgroundLoop)
End Sub

上面显示的功能只允许一次播放一个音频文件,这意味着当弹出气泡时,音乐会消失。

我尝试过使用两个单独的Windows媒体播放器的东西,但这也不起作用;

Public pop As String = "pop.wav"
Public minesound As String = "mine.wav"

Public Sub soundEffects(sound)
    If sound = pop Then
        GameScreen.AxWindowsMediaPlayer2.URL = pop
    ElseIf sound = minesound Then
        GameScreen.AxWindowsMediaPlayer2.URL = minesound
    End If
End Sub

非常感谢任何帮助或建议!谢谢!

1 个答案:

答案 0 :(得分:1)

基本上,您需要运行异步操作才能一次播放多个文件。

我已经开始使用My.Computer方法编写解决方案但是即使使用任务/线程,似乎(奇怪的是)不足以播放辅助文件而不停止播放第一个文件,也许其他可能涉及因素(我不知道),然后我使用MCI解决了它。

用法可以是:

Dim TaskCancellationTokenSource As New CancellationTokenSource
Dim TaskToken As CancellationToken = TaskCancellationTokenSource.Token

Private Sub BubbleLoop(ByVal CancellationToken As Threading.CancellationToken)

    Dim AudioFileLoop = New MCIPlayer(Me, "C:\BubbleLoop.wav")

    Do Until CancellationToken.IsCancellationRequested
        AudioFileLoop.Play(AudioPlayMode.WaitToComplete)
    Loop

    AudioFileLoop.Close()

End Sub

Private Sub Test()

    ' This plays a file asynchronously into an infinite loop.
    Task.Factory.StartNew(Sub() BubbleLoop(TaskToken), TaskToken)

    ' Wait 2 seconds (just to demonstrate this example)
    Threading.Thread.Sleep(2 * 1000)

    ' Play any other file while the loop is still playing.
    Dim AudioFile = New MCIPlayer(Me, "C:\SingleBubble.mp3")
    AudioFile.Play(AudioPlayMode.Background)

    ' Cancel the Bubble Loop.
    TaskCancellationTokenSource.Cancel()

End Sub

你需要添加我已经完成的基本 MCI课程(它没有完整):

' [ MCI Player ]
'
' // By Elektro H@cker

#Region " Usage Examples "

'Dim AudioFile As New MCIPlayer(Me, "C:\Audio.wav")
'AudioFile.Play(AudioPlayMode.BackgroundLoop)

'Dim sb As New System.Text.StringBuilder
'sb.AppendLine("Filename: " & AudioFile.Filename)
'sb.AppendLine("State...: " & AudioFile.State.ToString)
'sb.AppendLine("Mode....: " & AudioFile.PlaybackMode.ToString)
'sb.AppendLine("Channels: " & CStr(AudioFile.Channels))
'sb.AppendLine("Duration: " & TimeSpan.FromMilliseconds(AudioFile.Duration).ToString("hh\:mm\:ss"))

'MessageBox.Show(sb.ToString, "MCI Player", MessageBoxButtons.OK, MessageBoxIcon.Information)

'AudioFile.Stop()

#End Region

#Region " MCI Player "

''' <summary>
''' Play Wave, MP3 or MIDI files
''' </summary>
Public Class MCIPlayer
    Inherits NativeWindow
    Implements IDisposable

#Region " API "

    ''' <summary>
    ''' Sends a command string to an MCI device.
    ''' The device that the command is sent to is specified in the command string.
    ''' </summary>
    ''' <param name="command">
    ''' Pointer to a null-terminated string that specifies an MCI command string. 
    ''' For a list, see Multimedia Command Strings.
    ''' </param>
    ''' <param name="buffer">
    ''' Buffer that receives return information. 
    ''' If no return information is needed, this parameter can be NULL.
    ''' </param>
    ''' <param name="bufferSize">
    ''' Size, in characters, of the return buffer specified.
    ''' </param>
    ''' <param name="hwndCallback">
    ''' Handle to a callback window if the "notify" flag was specified in the command string.
    ''' </param>
    <System.Runtime.InteropServices.
    DllImport("winmm.dll", SetLastError:=True)>
    Private Shared Function mciSendString(
            ByVal command As String,
            ByVal buffer As System.Text.StringBuilder,
            ByVal bufferSize As Integer,
            ByVal hwndCallback As IntPtr
    ) As Integer
    End Function

#End Region

#Region " Variables "

    ''' <summary>
    ''' The form to manage Windows Messages.
    ''' </summary>
    Private WithEvents form As Form = Nothing

    ''' <summary>
    ''' Indicates the audio play command of mciSendString.
    ''' </summary>
    Private PlayCommand As String = String.Empty

    ''' <summary>
    ''' Buffer that receives return information.
    ''' </summary>
    Private ReturnInfo As New System.Text.StringBuilder() With {.Capacity = 255}

    ''' <summary>
    ''' The current filename of the file that is to be played.
    ''' </summary>
    Private _filename As String = String.Empty

    ''' <summary>
    ''' Indicates the current playback mode.
    ''' </summary>
    Private _PlaybackMode As AudioPlayMode

    ''' <summary>
    ''' Flag to cancel the BackgroundLoop PlaybackMode.
    ''' </summary>
    Private CancelLoop As Boolean = False

#End Region

#Region " Properties "

    ''' <summary>
    ''' The current filename of the file that is to be played.
    ''' </summary>
    Public Property Filename() As String

        Get
            Return _filename
        End Get

        Set(ByVal value As String)

            If Not IO.File.Exists(value) Then
                Throw New IO.FileNotFoundException
                Exit Property
            End If

            _filename = value

        End Set

    End Property

    ''' <summary>
    ''' Gets che current Playback State.
    ''' </summary>
    Public ReadOnly Property State As PlaybackState
        Get
            mciSendString("status file mode", ReturnInfo, ReturnInfo.Capacity, IntPtr.Zero)
            Return [Enum].Parse(GetType(PlaybackState), ReturnInfo.ToString, True)
        End Get
    End Property

    ''' <summary>
    ''' Gets or sets the playback mode of the current file.
    ''' </summary>
    Public Property PlaybackMode As AudioPlayMode
        Get
            Return _PlaybackMode
        End Get
        Set(value As AudioPlayMode)
            _PlaybackMode = value
        End Set
    End Property

    ''' <summary>
    ''' Gets the channels of the file.
    ''' </summary>
    ReadOnly Property Channels() As Integer
        Get
            mciSendString("status file channels", ReturnInfo, ReturnInfo.Capacity, IntPtr.Zero)
            Return If(IsNumeric(ReturnInfo.ToString),
                      CInt(ReturnInfo.ToString),
                      -1)
        End Get
    End Property

    ''' <summary>
    ''' Gets the file duration in Milleseconds.
    ''' </summary>
    ReadOnly Property Duration() As Integer
        Get
            mciSendString("set file time format milliseconds", Nothing, 0, IntPtr.Zero)
            mciSendString("status file length", ReturnInfo, ReturnInfo.Capacity, IntPtr.Zero)
            Return If(String.IsNullOrEmpty(ReturnInfo.ToString), 0, CInt(ReturnInfo.ToString))
        End Get
    End Property

#End Region

#Region " Enumerations "

    ''' <summary>
    ''' Audio File playback state.
    ''' </summary>
    Public Enum PlaybackState As Short

        ''' <summary>
        ''' File is playing.
        ''' </summary>
        Playing = 0

        ''' <summary>
        ''' File is paused.
        ''' </summary>
        Paused = 1

        ''' <summary>
        ''' File is stopped.
        ''' </summary>
        Stopped = 2

    End Enum

    ''' <summary>
    ''' Windows Message Identifiers.
    ''' </summary>
    Public Enum KnownMessages As Integer

        ''' <summary>
        ''' Notifies an application that an MCI device has completed an operation. 
        ''' MCI devices send this message only when the MCI_NOTIFY flag is used.
        ''' </summary>
        MM_MCINOTIFY = 953

    End Enum

#End Region

#Region " Constructor "

    ''' <summary>
    ''' Play Wave, MP3 or MIDI files.
    ''' </summary>
    ''' <param name="AudioFile">Indicates the filename of the media to play.</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal form As Form, ByVal AudioFile As String)

        Me.Filename = AudioFile

        ' Set the Formulary.
        Me.form = form

        ' Assign the form handle.
        SetFormHandle()

    End Sub

#End Region

#Region " Public Methods "

    ''' <summary>
    ''' Plays the file that is specified as the filename.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub Play(ByVal PlayMode As AudioPlayMode)

        DisposedCheck()

        Select Case PlayMode

            Case AudioPlayMode.Background
                PlayCommand = "play file from 0"
                Me.PlaybackMode = AudioPlayMode.Background

            Case AudioPlayMode.BackgroundLoop
                PlayCommand = "play file from 0 notify"
                Me.PlaybackMode = AudioPlayMode.BackgroundLoop

            Case AudioPlayMode.WaitToComplete
                PlayCommand = "play file from 0 wait"
                Me.PlaybackMode = AudioPlayMode.WaitToComplete

        End Select

        ' Open command
        Select Case Me.Filename.Split(".").LastOrDefault

            Case "mp3"
                mciSendString(String.Format("open ""{0}"" type mpegvideo alias file", Me.Filename),
                              Nothing,
                              0,
                              IntPtr.Zero)

            Case "wav"
                mciSendString(String.Format("open ""{0}"" type waveaudio alias file", Me.Filename),
                              Nothing,
                              0,
                              IntPtr.Zero)

            Case "mid", "midi"
                mciSendString("stop midi", Nothing, 0, 0)
                mciSendString("close midi", Nothing, 0, 0)
                mciSendString(String.Format("open sequencer! ""{0}"" alias file", Me.Filename),
                              Nothing,
                              0, IntPtr.Zero)

            Case Else
                Throw New Exception("File type not supported.")
                [Close]()

        End Select

        ' Play command
        mciSendString(PlayCommand, Nothing, 0, If(PlaybackMode = AudioPlayMode.BackgroundLoop,
                                                  Me.Handle,
                                                  IntPtr.Zero))

    End Sub

    ''' <summary>
    ''' Pause the current playback.
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub Pause()
        DisposedCheck()
        CancelLoop = True
        mciSendString("pause file", Nothing, 0, IntPtr.Zero)
    End Sub

    ''' <summary>
    ''' Resume the current playback if it is currently paused.
    ''' </summary>
    Public Sub [Resume]()
        DisposedCheck()
        If Me.State = PlaybackState.Paused Then
            CancelLoop = False
            mciSendString("resume file", Nothing, 0, IntPtr.Zero)
        End If
    End Sub

    ''' <summary>
    ''' Stop the current playback.
    ''' </summary>
    Public Sub [Stop]()
        DisposedCheck()
        CancelLoop = True
        mciSendString("stop file", Nothing, 0, IntPtr.Zero)
    End Sub

    ''' <summary>
    ''' Close the current file.
    ''' </summary>
    Public Overloads Sub [Close]()
        DisposedCheck()
        CancelLoop = True
        mciSendString("close file", Nothing, 0, IntPtr.Zero)
    End Sub

#End Region

#Region " Event Handlers "

    ''' <summary>
    ''' Assign the handle of the target form to this NativeWindow,
    ''' necessary to override WndProc.
    ''' </summary>
    Private Sub SetFormHandle() _
    Handles form.HandleCreated, form.Load, form.Shown

        Try
            If Not Me.Handle.Equals(Me.form.Handle) Then
                Me.AssignHandle(Me.form.Handle)
            End If
        Catch ' ex As InvalidOperationException
        End Try

    End Sub

    ''' <summary>
    ''' Releases the Handle.
    ''' </summary>
    Private Sub OnHandleDestroyed() _
    Handles form.HandleDestroyed

        Me.ReleaseHandle()

    End Sub

#End Region

#Region " Windows Messages "

    ''' <summary>
    ''' Processes Windows messages for this Window.
    ''' </summary>
    ''' <param name="m">
    ''' Contains the Windows Message parameters.
    ''' </param>
    Protected Overrides Sub WndProc(ByRef m As Message)

        MyBase.WndProc(m)

        If m.Msg = KnownMessages.MM_MCINOTIFY Then

            If Not CancelLoop Then
                Play(AudioPlayMode.BackgroundLoop)
            Else
                CancelLoop = False
            End If

        End If

    End Sub

#End Region

#Region " IDisposable "

    ''' <summary>
    ''' To detect redundant calls when disposing.
    ''' </summary>
    Private IsDisposed As Boolean = False

    ''' <summary>
    ''' Prevents calls to methods after disposing.
    ''' </summary>
    Private Sub DisposedCheck()
        If Me.IsDisposed Then
            Throw New ObjectDisposedException(Me.GetType().FullName)
        End If
    End Sub

    ''' <summary>
    ''' Disposes the objects generated by this instance.
    ''' </summary>
    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    ' IDisposable
    Protected Overridable Sub Dispose(IsDisposing As Boolean)

        If Not Me.IsDisposed Then

            If IsDisposing Then
                [Close]()
                Me.form = Nothing
                Me.ReleaseHandle()
                Me.DestroyHandle()
            End If

        End If

        Me.IsDisposed = True

    End Sub

#End Region

End Class

#End Region