forked from Manuel/meeting-assistant
feat: add local Resemblyzer speaker recognition
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
using MeetingAssistant.Speakers;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
namespace MeetingAssistant.Tests;
|
||||
|
||||
public sealed class SpeakerRecognitionFeatureSelectionTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(false, typeof(SpeakerIdentityService), typeof(SpeakerIdentityMergeService), 1, false)]
|
||||
[InlineData(true, typeof(ResemblyzerSpeakerIdentificationService), typeof(ResemblyzerSpeakerIdentityMergeService), 1000, true)]
|
||||
public async Task FeatureFlagSelectsExactlyOneIdentificationBackend(
|
||||
bool enabled,
|
||||
Type expectedIdentificationType,
|
||||
Type expectedMergeType,
|
||||
int expectedMinimumSamples,
|
||||
bool expectedNonOverlappingSamples)
|
||||
{
|
||||
var directory = Path.Combine(Path.GetTempPath(), "meeting-assistant-tests", Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(directory);
|
||||
try
|
||||
{
|
||||
await using var factory = new WebApplicationFactory<Program>()
|
||||
.WithWebHostBuilder(builder =>
|
||||
{
|
||||
builder.ConfigureAppConfiguration((_, configuration) =>
|
||||
{
|
||||
configuration.AddInMemoryCollection(new Dictionary<string, string?>
|
||||
{
|
||||
["MeetingAssistant:FunAsr:Backend:Enabled"] = "false",
|
||||
["MeetingAssistant:SpeakerIdentification:DatabasePath"] = Path.Combine(directory, "identities.db"),
|
||||
["MeetingAssistant:SpeakerIdentification:PyannoteValidation:Enabled"] = "false",
|
||||
["MeetingAssistant:SpeakerIdentification:Resemblyzer:Enabled"] = enabled.ToString()
|
||||
});
|
||||
});
|
||||
builder.ConfigureTestServices(services =>
|
||||
{
|
||||
services.RemoveAll<IResemblyzerVoiceEncoder>();
|
||||
services.AddSingleton<IResemblyzerVoiceEncoder, NoopEncoder>();
|
||||
});
|
||||
});
|
||||
|
||||
var service = factory.Services.GetRequiredService<ISpeakerIdentificationService>();
|
||||
|
||||
Assert.IsType(expectedIdentificationType, service);
|
||||
Assert.Single(factory.Services.GetServices<ISpeakerIdentificationService>());
|
||||
var mergeService = factory.Services.GetRequiredService<ISpeakerIdentityMergeService>();
|
||||
Assert.IsType(expectedMergeType, mergeService);
|
||||
Assert.Single(factory.Services.GetServices<ISpeakerIdentityMergeService>());
|
||||
var policy = factory.Services.GetRequiredService<SpeakerSampleCollectionPolicy>();
|
||||
Assert.Equal(expectedMinimumSamples, policy.MinimumRetainedSamples);
|
||||
Assert.Equal(expectedNonOverlappingSamples, policy.RequireNonOverlappingSamples);
|
||||
}
|
||||
finally
|
||||
{
|
||||
SqliteConnection.ClearAllPools();
|
||||
Directory.Delete(directory, recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class NoopEncoder : IResemblyzerVoiceEncoder
|
||||
{
|
||||
public Task<IReadOnlyList<float[]>> EncodeAsync(
|
||||
IReadOnlyList<byte[]> wavSamples,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult<IReadOnlyList<float[]>>([]);
|
||||
}
|
||||
|
||||
public Task WarmUpAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user