Skip to content

Commit

Permalink
AsioDriver class import
Browse files Browse the repository at this point in the history
Minor updates
  • Loading branch information
Maxx53 committed Apr 11, 2020
1 parent cd8f353 commit df244d0
Show file tree
Hide file tree
Showing 9 changed files with 460 additions and 40 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ Simple GUI for https://github.com/mdias/rs_asio
## Features
* Provides simple GUI that allows you edit RS_ASIO settings easy without opening RS_ASIO.ini in text editor.
* Reads ASIO drivers names from registry, no need to look into RS_ASIO-log.txt anymore.
* Uses [AsioDriver class from NAudio project](https://github.com/naudio/NAudio/blob/master/NAudio/Wave/Asio/ASIODriver.cs) to reading driver info and opening control panel.


## Tips
* Place EXE-file into Rocksmith directory, near RS_ASIO.ini
* ASIO driver control panel can be opened by "Config" button
* Button "OK" is for saving RS_ASIO.ini and closing RS_ASIO_GUI
* Button "Cancel" is for closing RS_ASIO_GUI without saving RS_ASIO.ini
* Button "Run Rocksmith" saving RS_ASIO.ini, running Steam version of Rocksmith 2014 and closing RS_ASIO_GUI


## Screenshot
![Steam Icon](https://maxx.illuzor.com/img/rsgui.png)
274 changes: 274 additions & 0 deletions src/RS_ASIO_GUI/ASIODriver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;

namespace NAudio.Wave.Asio
{
/// <summary>
/// Main AsioDriver Class. To use this class, you need to query first the GetAsioDriverNames() and
/// then use the GetAsioDriverByName to instantiate the correct AsioDriver.
/// This is the first AsioDriver binding fully implemented in C#!
///
/// Contributor: Alexandre Mutel - email: alexandre_mutel at yahoo.fr
/// </summary>
///

public class AsioDriver
{
private IntPtr pAsioComObject;
private AsioDriverVTable asioDriverVTable;

private AsioDriver()
{
}

/// <summary>
/// Instantiate the ASIO driver by GUID.
/// </summary>
/// <param name="guid">The GUID.</param>
/// <returns>an AsioDriver instance</returns>
public static AsioDriver GetAsioDriverByGuid(Guid guid)
{
var driver = new AsioDriver();
driver.InitFromGuid(guid);
return driver;
}

/// <summary>
/// Inits the AsioDriver..
/// </summary>
/// <param name="sysHandle">The sys handle.</param>
/// <returns></returns>
public bool Init(IntPtr sysHandle)
{
int ret = asioDriverVTable.init(pAsioComObject, sysHandle);
return ret == 1;
}

/// <summary>
/// Gets the name of the driver.
/// </summary>
/// <returns></returns>
public string GetDriverName()
{
var name = new StringBuilder(256);
asioDriverVTable.getDriverName(pAsioComObject, name);
return name.ToString();
}

/// <summary>
/// Gets the driver version.
/// </summary>
/// <returns></returns>
public int GetDriverVersion()
{
return asioDriverVTable.getDriverVersion(pAsioComObject);
}

/// <summary>
/// Gets the number of channels.
/// </summary>
/// <param name="numInputChannels">The num input channels.</param>
/// <param name="numOutputChannels">The num output channels.</param>
public void GetChannels(out int numInputChannels, out int numOutputChannels)
{
asioDriverVTable.getChannels(pAsioComObject, out numInputChannels, out numOutputChannels);
}

/// <summary>
/// Gets the latencies (n.b. does not throw an exception)
/// </summary>
/// <param name="inputLatency">The input latency.</param>
/// <param name="outputLatency">The output latency.</param>
public int GetLatencies(out int inputLatency, out int outputLatency)
{
return asioDriverVTable.getLatencies(pAsioComObject, out inputLatency, out outputLatency);
}

/// <summary>
/// Gets the size of the buffer.
/// </summary>
/// <param name="minSize">Size of the min.</param>
/// <param name="maxSize">Size of the max.</param>
/// <param name="preferredSize">Size of the preferred.</param>
/// <param name="granularity">The granularity.</param>
public void GetBufferSize(out int minSize, out int maxSize, out int preferredSize, out int granularity)
{
asioDriverVTable.getBufferSize(pAsioComObject, out minSize, out maxSize, out preferredSize, out granularity);
}

/// <summary>
/// Gets the sample rate.
/// </summary>
/// <returns></returns>
public double GetSampleRate()
{
double sampleRate;
asioDriverVTable.getSampleRate(pAsioComObject, out sampleRate);
return sampleRate;
}

/// <summary>
/// Controls the panel.
/// </summary>
public void ControlPanel()
{
asioDriverVTable.controlPanel(pAsioComObject);
}


/// <summary>
/// Releases this instance.
/// </summary>
public void ReleaseComAsioDriver()
{
Marshal.Release(pAsioComObject);
}


/// <summary>
/// Inits the vTable method from GUID. This is a tricky part of this class.
/// </summary>
/// <param name="asioGuid">The ASIO GUID.</param>
private void InitFromGuid(Guid asioGuid)
{
const uint CLSCTX_INPROC_SERVER = 1;
// Start to query the virtual table a index 3 (init method of AsioDriver)
const int INDEX_VTABLE_FIRST_METHOD = 3;

// Pointer to the ASIO object
// USE CoCreateInstance instead of builtin COM-Class instantiation,
// because the AsioDriver expect to have the ASIOGuid used for both COM Object and COM interface
// The CoCreateInstance is working only in STAThread mode.
int hresult = CoCreateInstance(ref asioGuid, IntPtr.Zero, CLSCTX_INPROC_SERVER, ref asioGuid, out pAsioComObject);
if (hresult != 0)
{
throw new COMException("Unable to instantiate ASIO. Check if STAThread is set", hresult);
}

// The first pointer at the adress of the ASIO Com Object is a pointer to the
// C++ Virtual table of the object.
// Gets a pointer to VTable.
IntPtr pVtable = Marshal.ReadIntPtr(pAsioComObject);

// Instantiate our Virtual table mapping
asioDriverVTable = new AsioDriverVTable();

// This loop is going to retrieve the pointer from the C++ VirtualTable
// and attach an internal delegate in order to call the method on the COM Object.
FieldInfo[] fieldInfos = typeof(AsioDriverVTable).GetFields();
for (int i = 0; i < fieldInfos.Length; i++)
{
FieldInfo fieldInfo = fieldInfos[i];
// Read the method pointer from the VTable
IntPtr pPointerToMethodInVTable = Marshal.ReadIntPtr(pVtable, (i + INDEX_VTABLE_FIRST_METHOD) * IntPtr.Size);
// Instantiate a delegate
object methodDelegate = Marshal.GetDelegateForFunctionPointer(pPointerToMethodInVTable, fieldInfo.FieldType);
// Store the delegate in our C# VTable
fieldInfo.SetValue(asioDriverVTable, methodDelegate);
}
}

/// <summary>
/// Internal VTable structure to store all the delegates to the C++ COM method.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 2)]
private class AsioDriverVTable
{
//3 virtual ASIOBool init(void *sysHandle) = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int ASIOInit(IntPtr _pUnknown, IntPtr sysHandle);
public ASIOInit init = null;
//4 virtual void getDriverName(char *name) = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate void ASIOgetDriverName(IntPtr _pUnknown, StringBuilder name);
public ASIOgetDriverName getDriverName = null;
//5 virtual long getDriverVersion() = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int ASIOgetDriverVersion(IntPtr _pUnknown);
public ASIOgetDriverVersion getDriverVersion = null;
//6 virtual void getErrorMessage(char *string) = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate void ASIOgetErrorMessage(IntPtr _pUnknown, StringBuilder errorMessage);
public ASIOgetErrorMessage getErrorMessage = null;
//7 virtual ASIOError start() = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int ASIOstart(IntPtr _pUnknown);
public ASIOstart start = null;
//8 virtual ASIOError stop() = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int ASIOstop(IntPtr _pUnknown);
public ASIOstop stop = null;
//9 virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels) = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int ASIOgetChannels(IntPtr _pUnknown, out int numInputChannels, out int numOutputChannels);
public ASIOgetChannels getChannels = null;
//10 virtual ASIOError getLatencies(long *inputLatency, long *outputLatency) = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int ASIOgetLatencies(IntPtr _pUnknown, out int inputLatency, out int outputLatency);
public ASIOgetLatencies getLatencies = null;
//11 virtual ASIOError getBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity) = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int ASIOgetBufferSize(IntPtr _pUnknown, out int minSize, out int maxSize, out int preferredSize, out int granularity);
public ASIOgetBufferSize getBufferSize = null;
//12 virtual ASIOError canSampleRate(ASIOSampleRate sampleRate) = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int ASIOcanSampleRate(IntPtr _pUnknown, double sampleRate);
public ASIOcanSampleRate canSampleRate = null;
//13 virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate) = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int ASIOgetSampleRate(IntPtr _pUnknown, out double sampleRate);
public ASIOgetSampleRate getSampleRate = null;
//14 virtual ASIOError setSampleRate(ASIOSampleRate sampleRate) = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int ASIOsetSampleRate(IntPtr _pUnknown, double sampleRate);
public ASIOsetSampleRate setSampleRate = null;
//15 virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources) = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int ASIOgetClockSources(IntPtr _pUnknown, out long clocks, int numSources);
public ASIOgetClockSources getClockSources = null;
//16 virtual ASIOError setClockSource(long reference) = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int ASIOsetClockSource(IntPtr _pUnknown, int reference);
public ASIOsetClockSource setClockSource = null;
//17 virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int ASIOgetSamplePosition(IntPtr _pUnknown, out long samplePos, ref object timeStamp);
public ASIOgetSamplePosition getSamplePosition = null;
//18 virtual ASIOError getChannelInfo(ASIOChannelInfo *info) = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int ASIOgetChannelInfo(IntPtr _pUnknown, ref object info);
public ASIOgetChannelInfo getChannelInfo = null;
//19 virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks) = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
//public delegate ASIOError ASIOcreateBuffers(IntPtr _pUnknown, ref ASIOBufferInfo[] bufferInfos, int numChannels, int bufferSize, ref ASIOCallbacks callbacks);
public delegate int ASIOcreateBuffers(IntPtr _pUnknown, IntPtr bufferInfos, int numChannels, int bufferSize, IntPtr callbacks);
public ASIOcreateBuffers createBuffers = null;
//20 virtual ASIOError disposeBuffers() = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int ASIOdisposeBuffers(IntPtr _pUnknown);
public ASIOdisposeBuffers disposeBuffers = null;
//21 virtual ASIOError controlPanel() = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int ASIOcontrolPanel(IntPtr _pUnknown);
public ASIOcontrolPanel controlPanel = null;
//22 virtual ASIOError future(long selector,void *opt) = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int ASIOfuture(IntPtr _pUnknown, int selector, IntPtr opt);
public ASIOfuture future = null;
//23 virtual ASIOError outputReady() = 0;
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int ASIOoutputReady(IntPtr _pUnknown);
public ASIOoutputReady outputReady = null;

}

[DllImport("ole32.Dll")]
private static extern int CoCreateInstance(ref Guid clsid,
IntPtr inner,
uint context,
ref Guid uuid,
out IntPtr rReturnedComObject);
}
}
26 changes: 13 additions & 13 deletions src/RS_ASIO_GUI/Form1.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit df244d0

Please sign in to comment.