Fix managed iOS native media linking
This commit is contained in:
@@ -98,6 +98,11 @@ internal sealed class AppModel
|
|||||||
Status = restoring ? "Reconnecting…" : "Connecting…"; Notify();
|
Status = restoring ? "Reconnecting…" : "Connecting…"; Notify();
|
||||||
lifetime?.Cancel(); lifetime?.Dispose(); lifetime = new();
|
lifetime?.Cancel(); lifetime?.Dispose(); lifetime = new();
|
||||||
VoiceCatClient next = new("VoiceCat-iOS", "0.0.1", storage.TofuPath);
|
VoiceCatClient next = new("VoiceCat-iOS", "0.0.1", storage.TofuPath);
|
||||||
|
next.ConnectionStateChanged += state =>
|
||||||
|
{
|
||||||
|
if (state == ClientConnectionState.Disconnected && next.ConnectionFailure is { } failure)
|
||||||
|
Console.Error.WriteLine($"VoiceCat control connection failed: {failure}");
|
||||||
|
};
|
||||||
client = next;
|
client = next;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -118,8 +123,10 @@ internal sealed class AppModel
|
|||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
IsConnecting = false; Status = exception.Message; Notify();
|
System.Diagnostics.Debug.WriteLine($"Connection failed: {exception}");
|
||||||
|
IsConnecting = false; Status = exception.Message;
|
||||||
await next.DisposeAsync(); if (ReferenceEquals(client, next)) client = null;
|
await next.DisposeAsync(); if (ReferenceEquals(client, next)) client = null;
|
||||||
|
Notify();
|
||||||
if (restoring && !explicitDisconnect) ScheduleReconnect();
|
if (restoring && !explicitDisconnect) ScheduleReconnect();
|
||||||
else throw;
|
else throw;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,10 @@
|
|||||||
<Kind>Static</Kind>
|
<Kind>Static</Kind>
|
||||||
<ForceLoad>true</ForceLoad>
|
<ForceLoad>true</ForceLoad>
|
||||||
</NativeReference>
|
</NativeReference>
|
||||||
|
<LinkerArgument Include="-Wl,-force_load,$(VoiceCatNativeMediaPath)" Condition="Exists('$(VoiceCatNativeMediaPath)')" />
|
||||||
|
<ReferenceNativeSymbol Include="vcm_opus_version;vcm_opus_error;vcm_encoder_create;vcm_encoder_destroy;vcm_encoder_set;vcm_encoder_get_dred;vcm_encode;vcm_decoder_create;vcm_decoder_destroy;vcm_decode;vcm_dred_decoder_create;vcm_dred_decoder_destroy;vcm_dred_create;vcm_dred_destroy;vcm_dred_parse;vcm_dred_decode;vcm_rnnoise_create;vcm_rnnoise_destroy;vcm_rnnoise_process">
|
||||||
|
<SymbolType>Function</SymbolType>
|
||||||
|
</ReferenceNativeSymbol>
|
||||||
<NativeReference Include="$(VoiceCatIosCaptureOutput)/libvoicecat_ios_capture.a">
|
<NativeReference Include="$(VoiceCatIosCaptureOutput)/libvoicecat_ios_capture.a">
|
||||||
<Kind>Static</Kind>
|
<Kind>Static</Kind>
|
||||||
<ForceLoad>true</ForceLoad>
|
<ForceLoad>true</ForceLoad>
|
||||||
|
|||||||
@@ -30,6 +30,18 @@ if [ -n "${VOICECAT_CODESIGN_PROVISION:-}" ]; then set -- "$@" -p:CodesignProvis
|
|||||||
|
|
||||||
app="$root/clients/apple/dotnet/VoiceCat.iOS/bin/$configuration/net10.0-ios27.0/ios-arm64/VoiceCat.iOS.app"
|
app="$root/clients/apple/dotnet/VoiceCat.iOS/bin/$configuration/net10.0-ios27.0/ios-arm64/VoiceCat.iOS.app"
|
||||||
[ -d "$app" ] || { echo "device app was not produced at $app" >&2; exit 1; }
|
[ -d "$app" ] || { echo "device app was not produced at $app" >&2; exit 1; }
|
||||||
|
/usr/bin/strings "$app/VoiceCat.Codec.dll" | /usr/bin/grep -q GetMainProgramHandle || {
|
||||||
|
echo "device codec does not contain the iOS static-library resolver" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
/usr/bin/nm -gU "$app/VoiceCat.iOS" | /usr/bin/grep -q _vcm_decoder_create || {
|
||||||
|
echo "device executable does not export the statically linked media codec" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
/usr/bin/nm -gU "$app/VoiceCat.iOS" | /usr/bin/grep -q _vcm_rnnoise_create || {
|
||||||
|
echo "device executable does not export the statically linked RNNoise processor" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
mkdir -p "$output"
|
mkdir -p "$output"
|
||||||
/usr/bin/ditto "$app" "$output/VoiceCat.iOS.app"
|
/usr/bin/ditto "$app" "$output/VoiceCat.iOS.app"
|
||||||
/usr/bin/codesign --verify --deep --strict "$output/VoiceCat.iOS.app"
|
/usr/bin/codesign --verify --deep --strict "$output/VoiceCat.iOS.app"
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(VoiceCatIosStatic)' == 'true'">
|
<PropertyGroup Condition="'$(VoiceCatIosStatic)' == 'true'">
|
||||||
<DefineConstants>$(DefineConstants);VOICECAT_IOS_STATIC</DefineConstants>
|
|
||||||
<RuntimeIdentifiers>ios-arm64;iossimulator-arm64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>ios-arm64;iossimulator-arm64</RuntimeIdentifiers>
|
||||||
<NuGetLockFilePath>packages.ios.lock.json</NuGetLockFilePath>
|
<NuGetLockFilePath>packages.ios.lock.json</NuGetLockFilePath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace VoiceCat.Codec;
|
namespace VoiceCat.Codec;
|
||||||
|
|
||||||
internal static unsafe partial class NativeMethods
|
internal static unsafe partial class NativeMethods
|
||||||
{
|
{
|
||||||
#if VOICECAT_IOS_STATIC
|
static NativeMethods()
|
||||||
private const string Library = "__Internal";
|
{
|
||||||
#else
|
if (OperatingSystem.IsIOS()) NativeLibrary.SetDllImportResolver(typeof(NativeMethods).Assembly, ResolveIosStaticLibrary);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static nint ResolveIosStaticLibrary(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) =>
|
||||||
|
libraryName == "voicecat_media" ? NativeLibrary.GetMainProgramHandle() : 0;
|
||||||
|
|
||||||
private const string Library = "voicecat_media";
|
private const string Library = "voicecat_media";
|
||||||
#endif
|
|
||||||
[LibraryImport(Library, EntryPoint = "vcm_opus_version")]
|
[LibraryImport(Library, EntryPoint = "vcm_opus_version")]
|
||||||
internal static partial nint Version();
|
internal static partial nint Version();
|
||||||
[LibraryImport(Library, EntryPoint = "vcm_opus_error")]
|
[LibraryImport(Library, EntryPoint = "vcm_opus_error")]
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ public sealed partial class VoiceCatClient : IAsyncDisposable
|
|||||||
|
|
||||||
public event Action<ClientConnectionState>? ConnectionStateChanged;
|
public event Action<ClientConnectionState>? ConnectionStateChanged;
|
||||||
public ClientConnectionState State { get { lock (stateGate) return state; } }
|
public ClientConnectionState State { get { lock (stateGate) return state; } }
|
||||||
|
public Exception? ConnectionFailure { get; private set; }
|
||||||
public Task Completion => reader;
|
public Task Completion => reader;
|
||||||
public AuthResult? Authentication { get { lock (stateGate) return authentication?.Clone(); } }
|
public AuthResult? Authentication { get { lock (stateGate) return authentication?.Clone(); } }
|
||||||
public ServerHello? ServerHello { get { lock (stateGate) return hello?.Clone(); } }
|
public ServerHello? ServerHello { get { lock (stateGate) return hello?.Clone(); } }
|
||||||
@@ -72,6 +73,7 @@ public sealed partial class VoiceCatClient : IAsyncDisposable
|
|||||||
ObjectDisposedException.ThrowIf(disposed.IsCancellationRequested, this);
|
ObjectDisposedException.ThrowIf(disposed.IsCancellationRequested, this);
|
||||||
if (control is not null) throw new InvalidOperationException("Disconnect before reconnecting.");
|
if (control is not null) throw new InvalidOperationException("Disconnect before reconnecting.");
|
||||||
started = true;
|
started = true;
|
||||||
|
ConnectionFailure = null;
|
||||||
connectionLifetime = CancellationTokenSource.CreateLinkedTokenSource(disposed.Token);
|
connectionLifetime = CancellationTokenSource.CreateLinkedTokenSource(disposed.Token);
|
||||||
using var connecting = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, connectionLifetime.Token);
|
using var connecting = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, connectionLifetime.Token);
|
||||||
CancellationToken token = connecting.Token;
|
CancellationToken token = connecting.Token;
|
||||||
@@ -201,7 +203,7 @@ public sealed partial class VoiceCatClient : IAsyncDisposable
|
|||||||
if (message.Disconnect is not null) { connection.CompleteWrites(); break; }
|
if (message.Disconnect is not null) { connection.CompleteWrites(); break; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception exception) when (exception is IOException or OperationCanceledException or SocketException or ObjectDisposedException) { failure = exception; }
|
catch (Exception exception) { failure = exception; ConnectionFailure = exception; }
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
connectionLifetime?.Cancel();
|
connectionLifetime?.Cancel();
|
||||||
|
|||||||
@@ -1,21 +1,26 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Reflection;
|
||||||
using Microsoft.Win32.SafeHandles;
|
using Microsoft.Win32.SafeHandles;
|
||||||
|
|
||||||
namespace VoiceCat.Dsp;
|
namespace VoiceCat.Dsp;
|
||||||
|
|
||||||
public sealed unsafe partial class RnnoiseProcessor : IDisposable
|
public sealed unsafe partial class RnnoiseProcessor : IDisposable
|
||||||
{
|
{
|
||||||
#if VOICECAT_IOS_STATIC
|
|
||||||
private const string NativeLibrary = "__Internal";
|
|
||||||
#else
|
|
||||||
private const string NativeLibrary = "voicecat_media";
|
private const string NativeLibrary = "voicecat_media";
|
||||||
#endif
|
|
||||||
public const int SampleRate = 48000;
|
public const int SampleRate = 48000;
|
||||||
public const int FrameSamples = 480;
|
public const int FrameSamples = 480;
|
||||||
private readonly RnnoiseHandle handle;
|
private readonly RnnoiseHandle handle;
|
||||||
private readonly float[] input = new float[FrameSamples];
|
private readonly float[] input = new float[FrameSamples];
|
||||||
private readonly float[] output = new float[FrameSamples];
|
private readonly float[] output = new float[FrameSamples];
|
||||||
|
|
||||||
|
static RnnoiseProcessor()
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsIOS()) System.Runtime.InteropServices.NativeLibrary.SetDllImportResolver(typeof(RnnoiseProcessor).Assembly, ResolveIosStaticLibrary);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static nint ResolveIosStaticLibrary(string libraryName, Assembly assembly, DllImportSearchPath? searchPath) =>
|
||||||
|
libraryName == NativeLibrary ? System.Runtime.InteropServices.NativeLibrary.GetMainProgramHandle() : 0;
|
||||||
|
|
||||||
public RnnoiseProcessor()
|
public RnnoiseProcessor()
|
||||||
{
|
{
|
||||||
handle = new(Create());
|
handle = new(Create());
|
||||||
|
|||||||
Reference in New Issue
Block a user