Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions .github/workflows/dotnet-demos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ jobs:

strategy:
matrix:
device: [cpu, cpu:1]
os: [ubuntu-latest, windows-latest, macos-latest]
include:
- os: ubuntu-latest
Expand All @@ -45,20 +46,30 @@ jobs:
- name: Package restore
run: dotnet restore

# *********** REMOVE AFTER RELEASE **********************
- name: Pack binding for local ref
run: dotnet pack -c Release
working-directory: binding/dotnet

- name: Add binding to demo
run: dotnet add package -s ../../../binding/dotnet/Rhino/bin/Release Rhino
# ******************************************************

- name: Dotnet build micdemo
run: dotnet build -c MicDemo.Release

- name: Dotnet build filedemo
run: dotnet build -c FileDemo.Release

- name: Run Dotnet filedemo
run: dotnet run -c FileDemo.Release -- --input_audio_path ../../../resources/audio_samples/test_within_context.wav --access_key ${{secrets.PV_VALID_ACCESS_KEY}} --context_path ../../../resources/contexts/${{ matrix.platform }}/coffee_maker_${{ matrix.platform }}.rhn
run: dotnet run -c FileDemo.Release -- --input_audio_path ../../../resources/audio_samples/test_within_context.wav --access_key ${{secrets.PV_VALID_ACCESS_KEY}} --device ${{ matrix.device }} --context_path ../../../resources/contexts/${{ matrix.platform }}/coffee_maker_${{ matrix.platform }}.rhn

build-self-hosted:
runs-on: ${{ matrix.machine }}

strategy:
matrix:
device: [best]
machine: [rpi3-32, rpi3-64, rpi4-32, rpi4-64, rpi5-64, pv-windows-arm64]
include:
- machine: rpi3-32
Expand All @@ -80,11 +91,20 @@ jobs:
- name: Package restore
run: dotnet restore

# *********** REMOVE AFTER RELEASE **********************
- name: Pack binding for local ref
run: dotnet pack -c Release
working-directory: binding/dotnet

- name: Add binding to demo
run: dotnet add package -s ../../../binding/dotnet/Rhino/bin/Release Rhino
# ******************************************************

- name: Dotnet build micdemo
run: dotnet build -c MicDemo.Release

- name: Dotnet build filedemo
run: dotnet build -c FileDemo.Release

- name: Run Dotnet filedemo
run: dotnet run -c FileDemo.Release -- --input_audio_path ../../../resources/audio_samples/test_within_context.wav --access_key ${{secrets.PV_VALID_ACCESS_KEY}} --context_path ../../../resources/contexts/${{ matrix.platform }}/coffee_maker_${{ matrix.platform }}.rhn
run: dotnet run -c FileDemo.Release -- --input_audio_path ../../../resources/audio_samples/test_within_context.wav --access_key ${{secrets.PV_VALID_ACCESS_KEY}} --device ${{ matrix.device }} --context_path ../../../resources/contexts/${{ matrix.platform }}/coffee_maker_${{ matrix.platform }}.rhn
61 changes: 27 additions & 34 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,48 +49,29 @@ jobs:

strategy:
matrix:
os: [ubuntu-latest, macos-latest, macos-13, windows-latest]
dotnet-version: [2.1.x, 3.0.x, 3.1.x, 5.0.x, 6.0.x, 8.0.x]
device: [cpu, cpu:1]
os: [ubuntu-latest, macos-latest, macos-15-intel, windows-latest]
dotnet-version: [2.1.x, 3.1.x, 6.0.x, 8.0.x, 10.0.x]
include:
- dotnet-version: 2.1.x
binding-framework: netstandard2.0
test-framework: netcoreapp2.1
- dotnet-version: 3.0.x
binding-framework: netcoreapp3.0
test-framework: netcoreapp3.0
- dotnet-version: 3.1.x
binding-framework: netcoreapp3.0
test-framework: netcoreapp3.1
- dotnet-version: 5.0.x
binding-framework: netcoreapp3.0
test-framework: net5.0
- dotnet-version: 6.0.x
binding-framework: net6.0
test-framework: net6.0
- dotnet-version: 8.0.x
binding-framework: net8.0
test-framework: net8.0
- dotnet-version: 10.0.x
test-framework: net10.0
exclude:
- os: ubuntu-latest
dotnet-version: 2.1.x
- os: ubuntu-latest
dotnet-version: 3.0.x
- os: ubuntu-latest
dotnet-version: 3.1.x
- os: ubuntu-latest
dotnet-version: 5.0.x
- os: macos-latest
dotnet-version: 2.1.x
- os: macos-latest
dotnet-version: 3.0.x
- os: macos-latest
dotnet-version: 3.1.x
- os: macos-latest
dotnet-version: 5.0.x
- os: macos-latest
dotnet-version: 6.0.x
- os: macos-13
dotnet-version: 8.0.x

steps:
- uses: actions/checkout@v3
Expand All @@ -100,17 +81,25 @@ jobs:
with:
dotnet-version: ${{ matrix.dotnet-version }}

- name: Set up .NET (8)
if: ${{ matrix.os == 'ubuntu-latest' && matrix.dotnet-version == '6.0.x' }}
uses: actions/setup-dotnet@v3
with:
dotnet-version: 8.0.x

- name: Build binding
run: dotnet build Rhino/Rhino.csproj --framework ${{ matrix.binding-framework }}
run: dotnet build Rhino/Rhino.csproj

- name: Set test framework version (linux)
if: ${{ matrix.os == 'ubuntu-latest' }}
run: sed -i 's/net6.0/${{matrix.test-framework}}/g' RhinoTest/RhinoTest.csproj

- name: Set test framework version (macos)
if: ${{ matrix.os == 'macos-latest' || matrix.os == 'macos-15-intel' }}
run: sed -i '.bak' 's/net6.0/${{matrix.test-framework}}/g' RhinoTest/RhinoTest.csproj

- name: Set test framework version (windows)
if: ${{ matrix.os == 'windows-latest' }}
run: (Get-Content -Path "RhinoTest/RhinoTest.csproj") -replace "net6.0", "${{matrix.test-framework}}" | Set-Content -Path "RhinoTest/RhinoTest.csproj"

- name: Test
run: dotnet test --framework ${{ matrix.test-framework }} -v n
run: dotnet test -v n
env:
DEVICE: ${{ matrix.device }}

build-self-hosted:
runs-on: ${{ matrix.machine }}
Expand All @@ -120,12 +109,16 @@ jobs:
strategy:
matrix:
machine: [rpi3-32, rpi3-64, rpi4-32, rpi4-64, rpi5-64, pv-windows-arm64]
device: [best]

steps:
- uses: actions/checkout@v3

- name: Build binding
run: dotnet build Rhino/Rhino.csproj --framework net8.0
run: dotnet build Rhino/Rhino.csproj

- name: Test
run: dotnet test --framework net8.0 -v n
run: dotnet test -v n
env:
DEVICE: ${{ matrix.device }}
DOTNET_ROLL_FORWARD: LatestMajor
16 changes: 5 additions & 11 deletions binding/dotnet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,21 @@ Rhino is:

## Compatibility

Platform compatible with .NET Framework 4.6.1+:
Platforms compatible with .NET Standard 2.0+:

- Windows (x86_64, arm64)
- macOS (x86_64)
- Windows (x86_64)

Platforms compatible with .NET Core 2.0+:
Platforms compatible with .NET 6.0+:

- Linux (x86_64)
- macOS (x86_64)
- macOS (x86_64, arm64)
- Windows (x86_64, arm64)

Platforms compatible with .NET Core 3.0+:

- Raspberry Pi:
- 3 (32 and 64 bit)
- 4 (32 and 64 bit)
- 5 (32 and 64 bit)

Platform compatible with .NET 6.0+:

- macOS (arm64)

## Installation

You can install the latest version of Rhino by getting the latest [Rhino Nuget package](https://www.nuget.org/packages/Rhino/) in Visual Studio or using the .NET CLI:
Expand Down
73 changes: 67 additions & 6 deletions binding/dotnet/Rhino/Rhino.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2020-2023 Picovoice Inc.
Copyright 2020-2025 Picovoice Inc.

You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE"
file accompanying this source.
Expand Down Expand Up @@ -68,7 +68,7 @@ public class Rhino : IDisposable
static Rhino()
{

#if NETCOREAPP3_0_OR_GREATER
#if NET6_0_OR_GREATER

NativeLibrary.SetDllImportResolver(typeof(Rhino).Assembly, ImportResolver);

Expand All @@ -77,7 +77,7 @@ static Rhino()
DEFAULT_MODEL_PATH = Utils.PvModelPath();
}

#if NETCOREAPP3_0_OR_GREATER
#if NET6_0_OR_GREATER

private static IntPtr ImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
{
Expand All @@ -92,6 +92,7 @@ private static IntPtr ImportResolver(string libraryName, Assembly assembly, DllI
private static extern RhinoStatus pv_rhino_init(
IntPtr accessKey,
IntPtr modelPath,
IntPtr device,
IntPtr contextPath,
float sensitivity,
float endpointDurationSec,
Expand Down Expand Up @@ -143,6 +144,16 @@ private static extern RhinoStatus pv_rhino_context_info(
[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
private static extern int pv_rhino_frame_length();

[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
private static extern RhinoStatus pv_rhino_list_hardware_devices(
out IntPtr hardwareDevices,
out int numHardwareDevices);

[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
private static extern void pv_rhino_free_hardware_devices(
IntPtr hardwareDevices,
int numHardwareDevices);

[DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)]
private static extern void pv_set_sdk(string sdk);

Expand All @@ -167,6 +178,13 @@ private static extern RhinoStatus pv_rhino_context_info(
/// Absolute path to the file containing model parameters. If not set it will be set to the
/// default location.
/// </param>
/// <param name="device">
/// String representation of the device (e.g., CPU or GPU) to use. If set to `best`, the most
/// suitable device is selected automatically. If set to `gpu`, the engine uses the first available GPU device. To select a specific
/// GPU device, set this argument to `gpu:${GPU_INDEX}`, where `${GPU_INDEX}` is the index of the target GPU. If set to
/// `cpu`, the engine will run on the CPU with the default number of threads. To specify the number of threads, set this
/// argument to `cpu:${NUM_THREADS}`, where `${NUM_THREADS}` is the desired number of threads.
/// </param>
/// <param name="sensitivity">
/// Inference sensitivity expressed as floating point value within [0,1]. A higher sensitivity value results in fewer misses
/// at the cost of (potentially) increasing the erroneous inference rate.
Expand All @@ -187,11 +205,12 @@ public static Rhino Create(
string accessKey,
string contextPath,
string modelPath = null,
string device = null,
float sensitivity = 0.5f,
float endpointDurationSec = 1.0f,
bool requireEndpoint = true)
{
return new Rhino(accessKey, modelPath ?? DEFAULT_MODEL_PATH, contextPath, sensitivity, endpointDurationSec, requireEndpoint);
return new Rhino(accessKey, modelPath ?? DEFAULT_MODEL_PATH, contextPath, device, sensitivity, endpointDurationSec, requireEndpoint);
}

/// <summary>
Expand All @@ -206,6 +225,13 @@ public static Rhino Create(
/// Absolute path to the file containing model parameters. If not set it will be set to the
/// default location.
/// </param>
/// <param name="device">
/// String representation of the device (e.g., CPU or GPU) to use. If set to `best`, the most
/// suitable device is selected automatically. If set to `gpu`, the engine uses the first available GPU device. To select a specific
/// GPU device, set this argument to `gpu:${GPU_INDEX}`, where `${GPU_INDEX}` is the index of the target GPU. If set to
/// `cpu`, the engine will run on the CPU with the default number of threads. To specify the number of threads, set this
/// argument to `cpu:${NUM_THREADS}`, where `${NUM_THREADS}` is the desired number of threads.
/// </param>
/// <param name="sensitivity">
/// Inference sensitivity expressed as floating point value within [0,1]. A higher sensitivity value results in fewer misses
/// at the cost of (potentially) increasing the erroneous inference rate.
Expand All @@ -225,6 +251,7 @@ private Rhino(
string accessKey,
string modelPath,
string contextPath,
string device = null,
float sensitivity = 0.5f,
float endpointDurationSec = 1.0f,
bool requireEndpoint = true)
Expand All @@ -244,6 +271,8 @@ private Rhino(
throw new RhinoIOException($"Couldn't find context file at '{contextPath}'");
}

device = device ?? "best";

if (sensitivity < 0 || sensitivity > 1)
{
throw new RhinoInvalidArgumentException("Sensitivity value should be within [0, 1].");
Expand All @@ -257,12 +286,14 @@ private Rhino(
IntPtr accessKeyPtr = Utils.GetPtrFromUtf8String(accessKey);
IntPtr modelPathPtr = Utils.GetPtrFromUtf8String(modelPath);
IntPtr contextPathPtr = Utils.GetPtrFromUtf8String(contextPath);
IntPtr devicePtr = Utils.GetPtrFromUtf8String(device);

pv_set_sdk("dotnet");

RhinoStatus status = pv_rhino_init(
accessKeyPtr,
modelPathPtr,
devicePtr,
contextPathPtr,
sensitivity,
endpointDurationSec,
Expand All @@ -271,6 +302,7 @@ private Rhino(

Marshal.FreeHGlobal(accessKeyPtr);
Marshal.FreeHGlobal(modelPathPtr);
Marshal.FreeHGlobal(devicePtr);
Marshal.FreeHGlobal(contextPathPtr);

if (status != RhinoStatus.SUCCESS)
Expand Down Expand Up @@ -404,7 +436,7 @@ public Inference GetInference()
if (status != RhinoStatus.SUCCESS)
{
string[] messageStack = GetMessageStack();
throw RhinoStatusToException(status, "Failed to reset");
throw RhinoStatusToException(status, "Failed to reset", messageStack);
}

return new Inference(isUnderstood, intent, slots);
Expand All @@ -416,7 +448,6 @@ public Inference GetInference()
/// <returns>Context information</returns>
public string ContextInfo { get; private set; }


/// <summary>
/// Gets the version number of the Rhino library.
/// </summary>
Expand All @@ -435,6 +466,36 @@ public Inference GetInference()
/// <returns>Required sample rate.</returns>
public int SampleRate { get; private set; }

/// <summary>
/// Retrieves a list of hardware devices that can be specified when constructing the model.
/// </summary>
/// <returns>An array of available hardware devices.</returns>
/// <exception cref="RhinoException">Thrown when an error occurs while retrieving the hardware devices.</exception>
public static string[] GetAvailableDevices()
{
IntPtr hardwareDevicesPtr;
int numDevices;
RhinoStatus status = pv_rhino_list_hardware_devices(
out hardwareDevicesPtr,
out numDevices);
if (status != RhinoStatus.SUCCESS)
{
throw RhinoStatusToException(
status,
"Get available devices failed");
}

string[] devices = new string[numDevices];
int elementSize = Marshal.SizeOf(typeof(IntPtr));
for (int i = 0; i < numDevices; i++)
{
devices[i] = Utils.GetUtf8StringFromPtr(Marshal.ReadIntPtr(hardwareDevicesPtr, i * elementSize));
}

pv_rhino_free_hardware_devices(hardwareDevicesPtr, numDevices);
return devices;
}

/// <summary>
/// Coverts status codes to relevant .NET exceptions
/// </summary>
Expand Down
Loading