forked from Manuel/meeting-assistant
98 lines
3.2 KiB
C#
98 lines
3.2 KiB
C#
namespace MeetingAssistant.Speakers;
|
|
|
|
internal static class SpeakerIdentityMerger
|
|
{
|
|
public static void MergeIntoAndPrune(
|
|
SpeakerIdentity target,
|
|
SpeakerIdentity source,
|
|
int maxSnippets,
|
|
int maxVoiceVectors,
|
|
ResemblyzerVoiceVectorOutlierPruner outlierPruner)
|
|
{
|
|
outlierPruner.Prune(target);
|
|
outlierPruner.Prune(source);
|
|
MergeInto(target, source, maxSnippets, maxVoiceVectors);
|
|
outlierPruner.Prune(target);
|
|
}
|
|
|
|
public static void MergeInto(
|
|
SpeakerIdentity target,
|
|
SpeakerIdentity source,
|
|
int maxSnippets,
|
|
int maxVoiceVectors = int.MaxValue)
|
|
{
|
|
AddAlias(target, source.CanonicalName);
|
|
foreach (var alias in source.Aliases)
|
|
{
|
|
AddAlias(target, alias.Name);
|
|
}
|
|
|
|
foreach (var candidate in source.CandidateNames)
|
|
{
|
|
AddAlias(target, candidate.Name);
|
|
}
|
|
|
|
foreach (var reference in source.References)
|
|
{
|
|
SpeakerIdentityReferences.AddIfMissing(target, reference);
|
|
}
|
|
|
|
var retainedSnippets = target.Snippets
|
|
.Concat(source.Snippets)
|
|
.OrderBy(snippet => StableSnippetKey(snippet.WavBytes))
|
|
.Take(Math.Max(1, maxSnippets))
|
|
.Select(snippet => new SpeakerSnippet
|
|
{
|
|
WavBytes = snippet.WavBytes,
|
|
CreatedAt = snippet.CreatedAt
|
|
})
|
|
.ToList();
|
|
target.Snippets.Clear();
|
|
target.Snippets.AddRange(retainedSnippets);
|
|
|
|
var retainedVectors = target.VoiceVectors
|
|
.Concat(source.VoiceVectors)
|
|
.GroupBy(vector => vector.Fingerprint, StringComparer.Ordinal)
|
|
.Select(group => group.OrderByDescending(vector => vector.CreatedAt).First())
|
|
.OrderByDescending(vector => vector.CreatedAt)
|
|
.ThenBy(vector => vector.Fingerprint, StringComparer.Ordinal)
|
|
.Take(Math.Max(1, maxVoiceVectors))
|
|
.Select(vector => new SpeakerVoiceVector
|
|
{
|
|
SpeakerIdentity = target,
|
|
ModelId = vector.ModelId,
|
|
Dimensions = vector.Dimensions,
|
|
VectorBytes = vector.VectorBytes.ToArray(),
|
|
Fingerprint = vector.Fingerprint,
|
|
CreatedAt = vector.CreatedAt
|
|
})
|
|
.ToList();
|
|
target.VoiceVectors.Clear();
|
|
target.VoiceVectors.AddRange(retainedVectors);
|
|
target.UpdatedAt = DateTimeOffset.UtcNow;
|
|
}
|
|
|
|
private static void AddAlias(SpeakerIdentity identity, string? alias)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(alias) ||
|
|
string.Equals(identity.CanonicalName, alias, StringComparison.OrdinalIgnoreCase) ||
|
|
identity.Aliases.Any(existing => string.Equals(existing.Name, alias, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
identity.Aliases.Add(new SpeakerAlias { Name = alias.Trim() });
|
|
}
|
|
|
|
private static int StableSnippetKey(byte[] snippet)
|
|
{
|
|
var hash = new HashCode();
|
|
foreach (var value in snippet)
|
|
{
|
|
hash.Add(value);
|
|
}
|
|
|
|
return hash.ToHashCode();
|
|
}
|
|
}
|