Skip to content

Commit

Permalink
Remove DirectX for sound, and use NAudio instead.
Browse files Browse the repository at this point in the history
  • Loading branch information
RoosterDragon committed Mar 19, 2023
1 parent 1a00729 commit f64d487
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 70 deletions.
41 changes: 34 additions & 7 deletions Desktop Ponies/Desktop Ponies.vbproj
Original file line number Diff line number Diff line change
Expand Up @@ -98,27 +98,53 @@
<PropertyGroup />
<ItemGroup>
<Reference Include="atk-sharp">
<HintPath>..\..\..\..\..\..\..\..\Program Files (x86)\Mono\lib\atk\atk-sharp.dll</HintPath>
<HintPath>C:\Program Files (x86)\Mono\lib\atk\atk-sharp.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="gdk-sharp">
<HintPath>..\..\..\..\..\..\..\..\Program Files (x86)\Mono\lib\gdk\gdk-sharp.dll</HintPath>
<HintPath>C:\Program Files (x86)\Mono\lib\gdk\gdk-sharp.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="glib-sharp">
<HintPath>..\..\..\..\..\..\..\..\Program Files (x86)\Mono\lib\glib\glib-sharp.dll</HintPath>
<HintPath>C:\Program Files (x86)\Mono\lib\glib\glib-sharp.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="gtk-sharp">
<HintPath>..\..\..\..\..\..\..\..\Program Files (x86)\Mono\lib\gtk\gtk-sharp.dll</HintPath>
<HintPath>C:\Program Files (x86)\Mono\lib\gtk\gtk-sharp.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.DirectX.AudioVideoPlayback, Version=1.0.2902.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\..\Windows\assembly\GAC\Microsoft.DirectX.AudioVideoPlayback\1.0.2902.0__31bf3856ad364e35\Microsoft.DirectX.AudioVideoPlayback.dll</HintPath>
<Reference Include="Microsoft.Win32.Registry, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Win32.Registry.4.7.0\lib\net461\Microsoft.Win32.Registry.dll</HintPath>
</Reference>
<Reference Include="NAudio, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.2.1.0\lib\net472\NAudio.dll</HintPath>
</Reference>
<Reference Include="NAudio.Asio, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.Asio.2.1.0\lib\netstandard2.0\NAudio.Asio.dll</HintPath>
</Reference>
<Reference Include="NAudio.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.Core.2.1.0\lib\netstandard2.0\NAudio.Core.dll</HintPath>
</Reference>
<Reference Include="NAudio.Midi, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.Midi.2.1.0\lib\netstandard2.0\NAudio.Midi.dll</HintPath>
</Reference>
<Reference Include="NAudio.Wasapi, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.Wasapi.2.1.0\lib\netstandard2.0\NAudio.Wasapi.dll</HintPath>
</Reference>
<Reference Include="NAudio.WinForms, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.WinForms.2.1.0\lib\net472\NAudio.WinForms.dll</HintPath>
</Reference>
<Reference Include="NAudio.WinMM, Version=2.1.0.0, Culture=neutral, PublicKeyToken=e279aa5131008a41, processorArchitecture=MSIL">
<HintPath>..\packages\NAudio.WinMM.2.1.0\lib\netstandard2.0\NAudio.WinMM.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Drawing" />
<Reference Include="System.Security.AccessControl, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.AccessControl.4.7.0\lib\net461\System.Security.AccessControl.dll</HintPath>
</Reference>
<Reference Include="System.Security.Principal.Windows, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Principal.Windows.4.7.0\lib\net461\System.Security.Principal.Windows.dll</HintPath>
</Reference>
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Core" />
<Reference Include="System.XML" />
Expand Down Expand Up @@ -577,6 +603,7 @@
</None>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="Resources\Twilight.ico" />
</ItemGroup>
<ItemGroup>
Expand Down
27 changes: 12 additions & 15 deletions Desktop Ponies/DesktopPonies/Globals.vb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
''' <summary>
''' <summary>
''' Application global properties.
''' </summary>
Public NotInheritable Class Globals
Expand All @@ -10,27 +10,24 @@ Public NotInheritable Class Globals
Return Environment.GetCommandLineArgs()(0).EndsWith(".scr", PathEquality.Comparison)
End Function

Private Shared ReadOnly directXSoundAvailableSync As New Object()
Private Shared _directXSoundAvailable As Boolean?
Public Shared ReadOnly Property DirectXSoundAvailable As Boolean
Private Shared ReadOnly soundAvailableSync As New Object()
Private Shared _SoundAvailable As Boolean?
Public Shared ReadOnly Property SoundAvailable As Boolean
Get
SyncLock directXSoundAvailableSync
If Not _directXSoundAvailable.HasValue Then
_directXSoundAvailable = IsDirectXSoundAvailable()
SyncLock soundAvailableSync
If Not _SoundAvailable.HasValue Then
_SoundAvailable = IsSoundAvailable()
End If
Return _directXSoundAvailable.Value
Return _SoundAvailable.Value
End SyncLock
End Get
End Property

Private Shared Function IsDirectXSoundAvailable() As Boolean
' Check to see if the right version of DirectX is installed for sounds.
Private Shared Function IsSoundAvailable() As Boolean
Try
' You may get a LoaderLock exception here when debugging. It does not occur normally - only under a debugger. Ignoring it
' appears to be harmless and the load will not be affected.
Reflection.Assembly.Load(
"Microsoft.DirectX.AudioVideoPlayback, Version=1.0.2902.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")
Return True
Reflection.Assembly.Load("NAudio")
Reflection.Assembly.Load("NAudio.WinMM")
Return OperatingSystemInfo.IsWindows
Catch ex As Exception
' If we can't load the assembly, just don't enable sound.
End Try
Expand Down
2 changes: 1 addition & 1 deletion Desktop Ponies/DesktopPonies/OptionsForm.vb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Public Class OptionsForm
Private Sub OptionsForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
MonitorsSelection.Items.AddRange(Screen.AllScreens)

SoundGroup.Visible = Globals.DirectXSoundAvailable
SoundGroup.Visible = Globals.SoundAvailable

If Options.GetInterfaceType = GetType(DesktopSprites.SpriteManagement.GtkSpriteInterface) Then
ShowViewerInTaskbar.Visible = False
Expand Down
51 changes: 29 additions & 22 deletions Desktop Ponies/DesktopPonies/PonyAnimator.vb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Imports DesktopSprites.SpriteManagement
Imports DesktopSprites.SpriteManagement

Public Enum ExitRequest
None
Expand Down Expand Up @@ -211,7 +211,7 @@ Public MustInherit Class PonyAnimator
Return
End If
Sort(ZOrderer)
If Globals.DirectXSoundAvailable Then
If Globals.SoundAvailable Then
PlaySounds()
CleanupSounds(False)
End If
Expand Down Expand Up @@ -271,24 +271,29 @@ Public MustInherit Class PonyAnimator
End If
End If

Dim audio As Microsoft.DirectX.AudioVideoPlayback.Audio = Nothing

Dim output As NAudio.Wave.WaveOutEvent = Nothing
Dim sound As NAudio.Wave.AudioFileReader = Nothing
Try
' Volume is between -10000 and 0, with 0 being the loudest.
audio = New Microsoft.DirectX.AudioVideoPlayback.Audio(soundfulSprite.SoundPath) With {
.Volume = CInt(Options.SoundVolume * 10000 - 10000)
output = New NAudio.Wave.WaveOutEvent()
sound = New NAudio.Wave.AudioFileReader(soundfulSprite.SoundPath) With {
.Volume = Options.SoundVolume
}
audio.Play()
output.Init(sound)
output.Play()
Catch ex As Exception
' Swallow any exception here. The sound file may be missing, inaccessible, not a playable format, etc.
If audio IsNot Nothing Then audio.Dispose()
If sound IsNot Nothing Then sound.Dispose()
If output IsNot Nothing Then output.Dispose()
Finally
If audio IsNot Nothing AndAlso Not audio.Disposed Then
If audio.Duration > TimeSpan.FromDays(1).TotalSeconds Then
If sound IsNot Nothing Then
If sound.TotalTime > TimeSpan.FromDays(1) Then
' Ignore sounds with a crazy duration value - they're probably corrupt and mess with our date arithmetic.
audio.Dispose()
sound.Dispose()
output.Dispose()
Else
activeSounds.Add(audio)
Dim endTime = Date.UtcNow + TimeSpan.FromSeconds(audio.Duration)
activeSounds.Add((sound, output))
Dim endTime = Date.UtcNow + sound.TotalTime
If Options.SoundSingleChannelOnly Then
globalSoundEnd = endTime
Else
Expand All @@ -301,14 +306,16 @@ Public MustInherit Class PonyAnimator
End Sub

Private Sub CleanupSounds(force As Boolean)
Dim soundsToRemove As LinkedList(Of Microsoft.DirectX.AudioVideoPlayback.Audio) = Nothing

For Each sound As Microsoft.DirectX.AudioVideoPlayback.Audio In activeSounds
If force OrElse sound.CurrentPosition >= sound.Duration Then
sound.StopWhenReady()
sound.Dispose()
If soundsToRemove Is Nothing Then soundsToRemove = New LinkedList(Of Microsoft.DirectX.AudioVideoPlayback.Audio)()
soundsToRemove.AddLast(sound)
Dim soundsToRemove As LinkedList(Of (NAudio.Wave.AudioFileReader, NAudio.Wave.WaveOutEvent)) = Nothing

For Each activeSoundObject In activeSounds
Dim activeSound = DirectCast(activeSoundObject, (Sound As NAudio.Wave.AudioFileReader, Output As NAudio.Wave.WaveOutEvent))
If force OrElse activeSound.Sound.CurrentTime >= activeSound.Sound.TotalTime Then
activeSound.Output.Stop()
activeSound.Output.Dispose()
activeSound.Sound.Dispose()
If soundsToRemove Is Nothing Then soundsToRemove = New LinkedList(Of (NAudio.Wave.AudioFileReader, NAudio.Wave.WaveOutEvent))()
soundsToRemove.AddLast(activeSound)
End If
Next

Expand Down Expand Up @@ -348,7 +355,7 @@ Public MustInherit Class PonyAnimator
Next
End If
MyBase.Finish()
If Not alreadyDisposed AndAlso Globals.DirectXSoundAvailable Then CleanupSounds(True)
If Not alreadyDisposed AndAlso Globals.SoundAvailable Then CleanupSounds(True)
End Sub

Protected Function GetClosestUnderPoint(Of T)(location As Point) As T
Expand Down
49 changes: 28 additions & 21 deletions Desktop Ponies/PonyEditor2/SpeechEditor.vb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Friend Class SpeechEditor
Friend Class SpeechEditor
Private Shadows Property Edited As Speech
Get
Return DirectCast(MyBase.Edited, Speech)
Expand All @@ -18,13 +18,16 @@
End Get
End Property

Private sound As Object
Private sound As Tuple(Of Object, Object)

Public Sub New()
InitializeComponent()
AddHandler Disposed, Sub()
Dim disposable = TryCast(sound, IDisposable)
If disposable IsNot Nothing Then disposable.Dispose()
If sound Is Nothing Then Return
Dim disposable1 = TryCast(sound.Item1, IDisposable)
If disposable1 IsNot Nothing Then disposable1.Dispose()
Dim disposable2 = TryCast(sound.Item2, IDisposable)
If disposable2 IsNot Nothing Then disposable2.Dispose()
End Sub
End Sub

Expand All @@ -48,8 +51,8 @@

Private Sub SoundFileSelector_FilePathSelected(sender As Object, e As EventArgs) Handles SoundFileSelector.FilePathSelected
StopSound()
SoundPreviewPanel.Enabled = SoundFileSelector.FilePath IsNot Nothing AndAlso Globals.DirectXSoundAvailable
UpdateSoundLabel(0, 0)
SoundPreviewPanel.Enabled = SoundFileSelector.FilePath IsNot Nothing AndAlso Globals.SoundAvailable
UpdateSoundLabel(TimeSpan.Zero, TimeSpan.Zero)
End Sub

Private Sub SoundPreviewButton_Click(sender As Object, e As EventArgs) Handles SoundPreviewButton.Click
Expand All @@ -63,13 +66,15 @@
Private Sub PlaySound()
Try
Dim fullPath = IO.Path.Combine(SoundFileSelector.BaseDirectory, SoundFileSelector.FilePath)
Dim audio As New Microsoft.DirectX.AudioVideoPlayback.Audio(fullPath) With {
.Volume = CInt(Options.SoundVolume * 10000 - 10000)
Dim audio As New NAudio.Wave.AudioFileReader(fullPath) With {
.Volume = Options.SoundVolume
}
audio.Play()
sound = audio
Dim output = New NAudio.Wave.WaveOutEvent()
output.Init(audio)
output.Play()
sound = New Tuple(Of Object, Object)(audio, output)
SoundPreviewButton.Text = "Stop"
UpdateSoundLabel(audio.CurrentPosition, audio.Duration)
UpdateSoundLabel(audio.CurrentTime, audio.TotalTime)
SoundTimer.Start()
Catch ex As Exception
SoundPreviewLabel.Text = "Unable to play sound file."
Expand All @@ -78,29 +83,31 @@
End Sub

Private Sub SoundTimer_Tick(sender As Object, e As EventArgs) Handles SoundTimer.Tick
Dim audio = DirectCast(sound, Microsoft.DirectX.AudioVideoPlayback.Audio)
Dim currentPosition As Double = 0
Dim duration As Double = audio.Duration
If audio.CurrentPosition >= duration Then
Dim audio = DirectCast(sound.Item1, NAudio.Wave.AudioFileReader)
Dim currentPosition As TimeSpan = TimeSpan.Zero
Dim duration As TimeSpan = audio.TotalTime
If audio.CurrentTime >= duration Then
StopSound()
Else
currentPosition = audio.CurrentPosition
currentPosition = audio.CurrentTime
End If
UpdateSoundLabel(currentPosition, duration)
End Sub

Private Sub UpdateSoundLabel(currentPosition As Double, duration As Double)
Private Sub UpdateSoundLabel(currentPosition As TimeSpan, duration As TimeSpan)
SoundPreviewLabel.Text = "{0:0.000} / {1:0.000}".FormatWith(
TimeSpan.FromSeconds(currentPosition).TotalSeconds, TimeSpan.FromSeconds(duration).TotalSeconds)
currentPosition.TotalSeconds, duration.TotalSeconds)
End Sub

Private Sub StopSound()
SoundTimer.Stop()
If sound Is Nothing Then Return
Dim audio = DirectCast(sound, Microsoft.DirectX.AudioVideoPlayback.Audio)
Dim audio = DirectCast(sound.Item1, NAudio.Wave.AudioFileReader)
Dim output = DirectCast(sound.Item2, NAudio.Wave.WaveOutEvent)
sound = Nothing
audio.Stop()
UpdateSoundLabel(0, audio.Duration)
output.Stop()
UpdateSoundLabel(TimeSpan.Zero, audio.TotalTime)
output.Dispose()
audio.Dispose()
SoundPreviewButton.Text = "Play"
End Sub
Expand Down
13 changes: 13 additions & 0 deletions Desktop Ponies/packages.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Win32.Registry" version="4.7.0" targetFramework="net48" />
<package id="NAudio" version="2.1.0" targetFramework="net48" />
<package id="NAudio.Asio" version="2.1.0" targetFramework="net48" />
<package id="NAudio.Core" version="2.1.0" targetFramework="net48" />
<package id="NAudio.Midi" version="2.1.0" targetFramework="net48" />
<package id="NAudio.Wasapi" version="2.1.0" targetFramework="net48" />
<package id="NAudio.WinForms" version="2.1.0" targetFramework="net48" />
<package id="NAudio.WinMM" version="2.1.0" targetFramework="net48" />
<package id="System.Security.AccessControl" version="4.7.0" targetFramework="net48" />
<package id="System.Security.Principal.Windows" version="4.7.0" targetFramework="net48" />
</packages>
2 changes: 0 additions & 2 deletions Release Tool/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -504,8 +504,6 @@ private static void PackageReleaseFiles(string sourceDirectory, string destinati
"Desktop Ponies.vshost.exe.manifest",
"Desktop Ponies.xml",
"Desktop Sprites.xml",
"Microsoft.DirectX.xml",
"Microsoft.DirectX.Direct3D.xml"
})
{
var entry = zip.GetEntry(entryToRemove);
Expand Down
2 changes: 0 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ This project evolved from a Windows only solution and whilst it just about runs

You will need to install [Mono](https://www.mono-project.com/download/) in order to acquire the libraries for the Gtk/Cairo portion of the program that runs on non-Windows platforms. You will probably need to update the references for those dlls in each of the projects that requires them.

The Microsoft.DirectX.AudioVideoPlayback library is long since obsolete but is used to play audio. You need to install the [DirectX 9 redistributable](https://www.microsoft.com/en-us/download/details.aspx?id=35) in order to resolve this reference.

Once resolved, you have a standard Visual Studio solution split into three projects. Desktop Sprites is the library that handles rendering, Desktop Ponies is the pony specific part of the application and Release Tool is used to run image optimizers and package new releases.

If you want to use the Release Tool to optimize images, you will need to acquire the [gifsicle](http://www.lcdf.org/gifsicle/) and [pngout](http://advsys.net/ken/utils.htm) and drop them into the application directory for them to work.

0 comments on commit f64d487

Please sign in to comment.