【问题标题】:VB.net - Simultaneous AudioVB.net - 同步音频
【发布时间】:2013-12-24 14:47:54
【问题描述】:

所以我正在为学校开发一个简单的 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

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

【问题讨论】:

  • @MitchWheat - 添加更多细节,谢谢
  • 好的,我只是从我的记忆中获取这个(还没有验证这个):如果你在 windows nt 版本 >= 6(vista 或更高版本)上工作,每个程序只有一个流到声音驱动程序(查找:WDM 音频驱动程序和 UAA)因此程序必须自行训练如何混合声音,因为它只能向驱动程序发送一个流。我肯定知道,DirectSound 允许您合并多个缓冲区,但我不知道是否有可行的方法来做到这一点(尽管我想象有..)但我知道有一个 .Net Wrapper用于 DirectX 中的 DirectSound 用于托管应用程序
  • @MrPaulch,感谢您非常深入的回复,这无疑使这个烦人问题的原因非常清楚。谢谢

标签: vb.net audio


【解决方案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

你需要添加我做过的这个basic MCI 类(它不是完整的 tsted):

' [ 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

【讨论】:

  • 感谢您提供非常详尽的答案。看起来很复杂,我尝试过简单地复制和粘贴您的代码,但 Visual Studio 通知我“未定义类型'CancellationTokenSource'”。查看 SDK 提供的其他建议并没有太大帮助。 MSDN 的 os CancellationTokens 解释似乎也有点超出我的理解。不过感谢您的帮助- - *顺便说一句,这是在这些线上;将 TaskCancellationTokenSource 调暗为新的 CancellationTokenSource 将 TaskToken 调暗为 CancellationToken = TaskCancellationTokenSource.Token
  • 您需要导入所需的命名空间...将其放在类的顶部:Imports System.Threading。建议:下次你可以点击VisualStudio在特定行给你的“错误更正选项”红色框来解决问题而无需任何帮助。 PS:对不起我的英语
猜你喜欢
  • 1970-01-01
  • 2012-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-16
  • 2012-07-11
  • 1970-01-01
  • 2013-02-13
相关资源
最近更新 更多