diff --git a/FileAudioSource/App.config b/FileAudioSource/App.config
new file mode 100644
index 0000000..b50c74f
--- /dev/null
+++ b/FileAudioSource/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FileAudioSource/App.xaml b/FileAudioSource/App.xaml
new file mode 100644
index 0000000..07ef911
--- /dev/null
+++ b/FileAudioSource/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/FileAudioSource/App.xaml.cs b/FileAudioSource/App.xaml.cs
new file mode 100644
index 0000000..f23539d
--- /dev/null
+++ b/FileAudioSource/App.xaml.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace FileAudioSource
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/FileAudioSource/FileAudioSource.csproj b/FileAudioSource/FileAudioSource.csproj
new file mode 100644
index 0000000..006fa03
--- /dev/null
+++ b/FileAudioSource/FileAudioSource.csproj
@@ -0,0 +1,122 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {DF44A453-1359-4A30-828F-9D1B169032B8}
+ WinExe
+ FileAudioSource
+ FileAudioSource
+ v4.6.2
+ 512
+ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 4
+ true
+ true
+
+
+
+
+ x64
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ true
+ false
+
+
+ x64
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ true
+
+
+
+ ..\packages\OpenTok.Client.2.26.2.1\lib\net462\OpenTokNetStandard.dll
+
+
+
+
+
+
+
+
+
+
+ 4.0
+
+
+
+
+
+ ..\packages\OpenTok.Client.2.26.2.1\lib\net462\WinFormsVideoRenderer.dll
+
+
+ ..\packages\OpenTok.Client.2.26.2.1\lib\net462\WPFVideoRenderer.dll
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+ MSBuild:Compile
+ Designer
+
+
+ App.xaml
+ Code
+
+
+
+ MainWindow.xaml
+ Code
+
+
+
+
+ Code
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ Settings.settings
+ True
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
\ No newline at end of file
diff --git a/FileAudioSource/FileSourceAudioDevice.cs b/FileAudioSource/FileSourceAudioDevice.cs
new file mode 100644
index 0000000..b7eff17
--- /dev/null
+++ b/FileAudioSource/FileSourceAudioDevice.cs
@@ -0,0 +1,287 @@
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using OpenTok;
+
+namespace FileAudioSource
+{
+ public class FileSourceAudioDevice : IAudioDevice, IDisposable
+ {
+ #region Public
+
+ public void SelectSourceFile(string file)
+ {
+ if (!File.Exists(file))
+ throw new ArgumentException("File does not exist");
+ if (!file.EndsWith(".wav"))
+ throw new ArgumentException("Invalid file format");
+ lock (audioDeviceLock)
+ {
+ sourceFile = file;
+ AudioDevice.RestartInputAudioDevice();
+ }
+ }
+
+ public delegate void AudioPropertiesChangedHandler(int numberOfChannels, int samplingRate);
+ public event AudioPropertiesChangedHandler AudioPropertiesChanged;
+
+ #endregion
+
+ #region IAudioDevice
+
+ public void InitAudio(AudioDevice.AudioBus audioBus)
+ {
+ lock (audioDeviceLock)
+ {
+ this.audioBus = audioBus;
+ }
+ }
+
+ public void DestroyAudio()
+ {
+ lock (audioDeviceLock)
+ {
+ audioBus = null;
+ }
+ }
+
+ public void InitAudioCapturer()
+ {
+ lock (audioDeviceLock)
+ {
+ if (IsAudioCapturerInitialized())
+ return;
+ if (!string.IsNullOrWhiteSpace(sourceFile))
+ {
+ sourceFileStream = File.OpenRead(sourceFile);
+ /* WAV file format header struct can be found at https://docs.fileformat.com/audio/wav/ */
+ byte[] wavHeader = new byte[40];
+ sourceFileStream.Read(wavHeader, 0, wavHeader.Length);
+ capturerSettings = new AudioDeviceSettings();
+ capturerSettings.NumChannels = wavHeader[22] | (wavHeader[23] << 8);
+ capturerSettings.SamplingRate = wavHeader[24] | (wavHeader[25] << 8) | (wavHeader[26] << 16) | (wavHeader[27] << 24);
+ AudioPropertiesChanged?.Invoke(capturerSettings.NumChannels, capturerSettings.SamplingRate);
+ }
+ else
+ {
+ capturerSettings = new AudioDeviceSettings() { NumChannels = 2, SamplingRate = 48000 };
+ AudioPropertiesChanged?.Invoke(0, 0);
+ }
+ isAudioCapturerInitialized = true;
+ }
+ }
+
+ public void DestroyAudioCapturer()
+ {
+ lock (audioDeviceLock)
+ {
+ StopAudioCapturer();
+ if (!IsAudioCapturerInitialized())
+ return;
+ if (sourceFileStream != null)
+ {
+ sourceFileStream.Close();
+ sourceFileStream.Dispose();
+ sourceFileStream = null;
+ }
+ isAudioCapturerInitialized = false;
+ }
+ }
+
+ public void StartAudioCapturer()
+ {
+ lock (audioDeviceLock)
+ {
+ if (IsAudioCapturerStarted())
+ return;
+ if (!IsAudioCapturerInitialized())
+ throw new Exception("Audio capturer not initialized");
+
+ isAudioCapturerStarted = true;
+
+ _ = Task.Factory.StartNew(() =>
+ {
+ int numberOfSamplesPer10ms = capturerSettings.NumChannels * capturerSettings.SamplingRate / 100;
+ byte[] buffer = new byte[numberOfSamplesPer10ms * 2];
+ unsafe
+ {
+ fixed (byte* bufferPtr = buffer)
+ {
+ DateTime nextBatchTime = DateTime.Now;
+ while (isAudioCapturerStarted)
+ {
+ try
+ {
+ if (DateTime.Now >= nextBatchTime)
+ {
+ nextBatchTime += new TimeSpan(0, 0, 0, 0, 10);
+
+ int bytesRead = buffer.Length;
+ if (sourceFileStream != null)
+ bytesRead = sourceFileStream.Read(buffer, 0, buffer.Length);
+ audioBus.WriteCaptureData((IntPtr)bufferPtr, bytesRead / (2 * capturerSettings.NumChannels));
+ }
+ Thread.Sleep(1);
+ }
+ catch (Exception ex)
+ {
+ }
+ }
+ }
+ }
+ });
+ }
+ }
+
+ public void StopAudioCapturer()
+ {
+ lock (audioDeviceLock)
+ {
+ if (!IsAudioCapturerStarted())
+ return;
+ isAudioCapturerStarted = false;
+ Thread.Sleep(20); //Wait two whole cycles to make sure it's stopped
+ }
+ }
+
+ public bool IsAudioCapturerInitialized()
+ {
+ lock (audioDeviceLock)
+ {
+ return isAudioCapturerInitialized;
+ }
+ }
+
+ public bool IsAudioCapturerStarted()
+ {
+ lock (audioDeviceLock)
+ {
+ return isAudioCapturerStarted;
+ }
+ }
+
+ public int GetEstimatedAudioCaptureDelay()
+ {
+ lock (audioDeviceLock)
+ {
+ return 0;
+ }
+ }
+
+ public AudioDeviceSettings GetAudioCapturerSettings()
+ {
+ lock (audioDeviceLock)
+ {
+ return capturerSettings;
+ }
+ }
+
+ public void InitAudioRenderer()
+ {
+ lock (audioDeviceLock)
+ {
+ isAudioRendererInitialized = true;
+ }
+ }
+
+ public void DestroyAudioRenderer()
+ {
+ lock (audioDeviceLock)
+ {
+ StopAudioRenderer();
+ isAudioRendererInitialized = false;
+ }
+ }
+
+ public void StartAudioRenderer()
+ {
+ lock (audioDeviceLock)
+ {
+ isAudioRendererStarted = true;
+ }
+ }
+
+ public void StopAudioRenderer()
+ {
+ lock (audioDeviceLock)
+ {
+ isAudioRendererStarted = false;
+ }
+ }
+
+ public bool IsAudioRendererInitialized()
+ {
+ lock (audioDeviceLock)
+ {
+ return isAudioRendererInitialized;
+ }
+ }
+
+ public bool IsAudioRendererStarted()
+ {
+ lock (audioDeviceLock)
+ {
+ return isAudioRendererStarted;
+ }
+ }
+
+ public int GetEstimatedAudioRenderDelay()
+ {
+ return 0;
+ }
+
+ public AudioDeviceSettings GetAudioRendererSettings()
+ {
+ return new AudioDeviceSettings();
+ }
+
+ #endregion
+
+ #region IDisposable
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (isDisposed)
+ return;
+ isDisposed = true;
+
+ DestroyAudioCapturer();
+ DestroyAudioRenderer();
+
+ if (disposing)
+ {
+ }
+ }
+
+ ~FileSourceAudioDevice()
+ {
+ Dispose(disposing: false);
+ }
+
+ public void Dispose()
+ {
+ Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
+
+ #endregion
+
+ #region Private
+
+ private readonly object audioDeviceLock = new object();
+ private AudioDevice.AudioBus audioBus;
+ private bool isAudioCapturerInitialized;
+ private bool isAudioRendererInitialized;
+ private bool isAudioCapturerStarted;
+ private bool isAudioRendererStarted;
+ private string sourceFile;
+ private FileStream sourceFileStream;
+ private AudioDeviceSettings capturerSettings;
+ private bool isDisposed;
+
+ #endregion
+ }
+}
diff --git a/FileAudioSource/MainWindow.xaml b/FileAudioSource/MainWindow.xaml
new file mode 100644
index 0000000..3af6696
--- /dev/null
+++ b/FileAudioSource/MainWindow.xaml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
diff --git a/FileAudioSource/MainWindow.xaml.cs b/FileAudioSource/MainWindow.xaml.cs
new file mode 100644
index 0000000..8b09a00
--- /dev/null
+++ b/FileAudioSource/MainWindow.xaml.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Windows;
+using Microsoft.Win32;
+using OpenTok;
+
+namespace FileAudioSource
+{
+ public partial class MainWindow : Window
+ {
+ private const string API_KEY = "";
+ private const string SESSION_ID = "";
+ private const string TOKEN = "";
+
+ private Context context;
+ private Session session;
+ private Publisher publisher;
+ private FileSourceAudioDevice fileSourceAudioDevice = new FileSourceAudioDevice();
+
+ public MainWindow()
+ {
+ InitializeComponent();
+
+ context = new Context(new WPFDispatcher());
+
+ AudioDevice.SetCustomAudioDevice(context, fileSourceAudioDevice);
+ fileSourceAudioDevice.AudioPropertiesChanged += FileSourceAudioDevice_AudioPropertiesChanged;
+
+ // Uncomment following line to get debug logging
+ // LogUtil.Instance.EnableLogging();
+
+ publisher = new Publisher.Builder(context)
+ {
+ HasVideoTrack = false,
+ HasAudioTrack = true
+ }.Build();
+
+ session = new Session.Builder(context, API_KEY, SESSION_ID).Build();
+ session.Connected += Session_Connected;
+ session.Disconnected += Session_Disconnected;
+ session.Error += Session_Error;
+ session.Connect(TOKEN);
+ }
+
+ private void Session_Connected(object sender, System.EventArgs e)
+ {
+ session.Publish(publisher);
+ }
+
+ private void Session_Disconnected(object sender, System.EventArgs e)
+ {
+ Trace.WriteLine("Session disconnected.");
+ }
+
+ private void Session_Error(object sender, Session.ErrorEventArgs e)
+ {
+ Trace.WriteLine("Session error:" + e.ErrorCode);
+ }
+
+ private void FileSourceAudioDevice_AudioPropertiesChanged(int numberOfChannels, int samplingRate)
+ {
+ Application.Current.Dispatcher.BeginInvoke(new Action(() =>
+ {
+ if (numberOfChannels == 0 || samplingRate == 0)
+ SelectedFileInfo.Text = "No selected file";
+ else
+ SelectedFileInfo.Text = string.Format("Channels {0}, Sampling rate {1} samples/s", numberOfChannels, samplingRate);
+ }));
+ }
+
+ private void SelectSourceFileButton_Click(object sender, RoutedEventArgs e)
+ {
+ OpenFileDialog openFileDialog = new OpenFileDialog();
+ openFileDialog.Filter = "Wave files|*.wav";
+ openFileDialog.FileOk += (s_, e_) =>
+ {
+ string sourceFileName = openFileDialog.FileName;
+ fileSourceAudioDevice.SelectSourceFile(sourceFileName);
+ };
+ openFileDialog.ShowDialog();
+ }
+ }
+}
\ No newline at end of file
diff --git a/FileAudioSource/Properties/AssemblyInfo.cs b/FileAudioSource/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..23b6b80
--- /dev/null
+++ b/FileAudioSource/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("FileAudioSource")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("FileAudioSource")]
+[assembly: AssemblyCopyright("Copyright © 2023")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+//In order to begin building localizable applications, set
+//CultureYouAreCodingWith in your .csproj file
+//inside a . For example, if you are using US english
+//in your source files, set the to en-US. Then uncomment
+//the NeutralResourceLanguage attribute below. Update the "en-US" in
+//the line below to match the UICulture setting in the project file.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
+
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/FileAudioSource/Properties/Resources.Designer.cs b/FileAudioSource/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..2f5ca50
--- /dev/null
+++ b/FileAudioSource/Properties/Resources.Designer.cs
@@ -0,0 +1,70 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+namespace FileAudioSource.Properties
+{
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources
+ {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources()
+ {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager
+ {
+ get
+ {
+ if ((resourceMan == null))
+ {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("FileAudioSource.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture
+ {
+ get
+ {
+ return resourceCulture;
+ }
+ set
+ {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/FileAudioSource/Properties/Resources.resx b/FileAudioSource/Properties/Resources.resx
new file mode 100644
index 0000000..af7dbeb
--- /dev/null
+++ b/FileAudioSource/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/FileAudioSource/Properties/Settings.Designer.cs b/FileAudioSource/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..8beaf2c
--- /dev/null
+++ b/FileAudioSource/Properties/Settings.Designer.cs
@@ -0,0 +1,29 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+namespace FileAudioSource.Properties
+{
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+ {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default
+ {
+ get
+ {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/FileAudioSource/Properties/Settings.settings b/FileAudioSource/Properties/Settings.settings
new file mode 100644
index 0000000..033d7a5
--- /dev/null
+++ b/FileAudioSource/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/FileAudioSource/packages.config b/FileAudioSource/packages.config
new file mode 100644
index 0000000..8839b82
--- /dev/null
+++ b/FileAudioSource/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index a552539..3bec1b2 100644
--- a/README.md
+++ b/README.md
@@ -81,6 +81,11 @@ This app shows more advanced management of audio devices with notifications capa
that allow detecting when new devices are added, removed or the default selection is changed at
windows OS level.
+### FileAudioSource
+
+This app shows how to leverage IAudioDevice to provide a cusotm audio device that reads audio from
+a file and uses it as input for the audio stream.
+
## Development and Contributing
Interested in contributing? We :heart: pull requests! See the
diff --git a/Samples.sln b/Samples.sln
index dfeb34c..73b60d5 100644
--- a/Samples.sln
+++ b/Samples.sln
@@ -26,6 +26,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VideoTransformers", "VideoTransformers\VideoTransformers.csproj", "{A024FCB3-2AAE-4FC5-A693-AC1EBF2B4EA3}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileAudioSource", "FileAudioSource\FileAudioSource.csproj", "{DF44A453-1359-4A30-828F-9D1B169032B8}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -144,6 +146,18 @@ Global
{A024FCB3-2AAE-4FC5-A693-AC1EBF2B4EA3}.Release|x64.Build.0 = Release|Any CPU
{A024FCB3-2AAE-4FC5-A693-AC1EBF2B4EA3}.Release|x86.ActiveCfg = Release|Any CPU
{A024FCB3-2AAE-4FC5-A693-AC1EBF2B4EA3}.Release|x86.Build.0 = Release|Any CPU
+ {DF44A453-1359-4A30-828F-9D1B169032B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DF44A453-1359-4A30-828F-9D1B169032B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DF44A453-1359-4A30-828F-9D1B169032B8}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {DF44A453-1359-4A30-828F-9D1B169032B8}.Debug|x64.Build.0 = Debug|Any CPU
+ {DF44A453-1359-4A30-828F-9D1B169032B8}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DF44A453-1359-4A30-828F-9D1B169032B8}.Debug|x86.Build.0 = Debug|Any CPU
+ {DF44A453-1359-4A30-828F-9D1B169032B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DF44A453-1359-4A30-828F-9D1B169032B8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DF44A453-1359-4A30-828F-9D1B169032B8}.Release|x64.ActiveCfg = Release|Any CPU
+ {DF44A453-1359-4A30-828F-9D1B169032B8}.Release|x64.Build.0 = Release|Any CPU
+ {DF44A453-1359-4A30-828F-9D1B169032B8}.Release|x86.ActiveCfg = Release|Any CPU
+ {DF44A453-1359-4A30-828F-9D1B169032B8}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE