2013-12-07 3 views
2

Итак, я работаю над простой игрой VB.net для школы, в которой вы поп пузыри. Мы должны иметь звуковую игру, когда вы всплываете пузырь, что очень просто с функцией воспроизведения звука;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 

Любая помощь или совет очень ценится! Спасибо!

+0

@MitchWheat - более добавлено спасибо, спасибо – NauZeated

+0

Хорошо, я беру это только из своих воспоминаний (не проверял это): Если вы работаете над версией windows nt> = 6 (Vista или более поздней), каждая программа получает только один поток в звуковой драйвер (посмотрите: WDM Audio Driver и UAA) Таким образом, программа должна сама тренироваться, как для смешивания звука, поскольку он может отправлять только один поток водителю. Я точно знаю, что DirectSound позволяет объединять несколько буферов, но я не знаю, есть ли жизнеспособный способ сделать это любым другим способом (хотя у меня есть изображение ...) Но я знаю, что есть .Net Wrapper для DirectSound в DirectX для управляемых приложений – MrPaulch

+0

@MrPaulch, спасибо за очень глубокий ответ, это, безусловно, делает причину этой досадной проблемы очень ясной. спасибо – NauZeated

ответ

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 класс, который я сделал (Это не полный tsted):

' [ MCI Player ] 
' 
' // By Elektro [email protected] 

#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 
+0

спасибо за очень сложный ответ. Кажется очень сложным, я пробовал простую копию и вставку вашего кода, но визуальная студия сообщает мне, что «Тип« CancellationTokenSource »не определен». Просматривая другие предложения, предлагаемые SDK, и не очень помогает. Объяснение MSDN o CancellationTokens также кажется немного выше моего понимания. Цените свою помощь хотя- - - * это на этих линиях, кстати; Dim TaskCancellationTokenSource As New CancellationTokenSource Dim TaskToken As CancellationToken = TaskCancellationTokenSource.Token – NauZeated

+0

Вам нужно импортировать необходимые пространства имен ... поместите это вверху класса: 'Импортирует System.Threading'. Предложение: в следующий раз вы просто можете щелкнуть красную рамку «Параметры исправления ошибок», которую VisualStudio дает вам на конкретной строке для решения проблемы без какой-либо помощи. PS: Извините за мой английский – ElektroStudios

Смежные вопросы