/** * Calculates the distance between this voice sample and the voice prints previously extracted * and returns the closest matches sorted by distance * <p> * Usage of a closed set is assumed : the speaker's voice print was extracted before and is known to the system. * This means you'll always get MatchResults even if the speaker is absolutely unknown to the system. * The MatchResult class provides a likelihood ratio in order to help determining the usefulness of the result * </p> * @param voiceSample the voice sample, values between -1.0 and 1.0 * @return a list MatchResults sorted by distance */ public IEnumerable <MatchResult <T> > Identify(double[] voiceSample, double maxDistance = 0) { if (store.Count == 0) { throw new InvalidOperationException("There is no voice print enrolled in the system yet"); } var voicePrint = new VoicePrint(audioProcessor.ProcessAndExtract(voiceSample)); var calculator = new EuclideanDistanceCalculator(); var matches = new List <MatchResult <T> >(store.Count); var distanceFromUniversalModel = voicePrint.GetDistance(calculator, universalModel); foreach (var entry in store) { var distance = entry.Value.GetDistance(calculator, voicePrint); if (maxDistance > 0 && distance > maxDistance) { continue; } // likelihood : how close is the given voice sample to the current VoicePrint // compared to the total distance between the current VoicePrint and the universal model int likelihood = 100 - (int)(distance / (distance + distanceFromUniversalModel) * 100); matches.Add(new MatchResult <T>(entry.Key, likelihood, distance)); } return(matches.OrderBy(m => m.Distance)); }
/** * Creates a voice print and stores it along with the user key for later comparison with new samples * <p> * Threading : this method is synchronized to prevent inadvertently erasing an existing user key * </p> * @param userKey the user key associated with this voice print * @param voiceSample the voice sample, values between -1.0 and 1.0 * @return the voice print extracted from the given sample */ public VoicePrint CreateVoicePrint(T userKey, double[] voiceSample) { lock (_lock) { if (userKey == null) { throw new ArgumentNullException(nameof(userKey), "The userKey is null"); } if (store.ContainsKey(userKey)) { throw new ArgumentException("The userKey already exists: [{userKey}"); } double[] features = audioProcessor.ProcessAndExtract(voiceSample); VoicePrint voicePrint = new VoicePrint(features); if (!universalModelWasSetByUser) { if (universalModel == null) { universalModel = new VoicePrint(voicePrint); } else { universalModel.Merge(features); } } store.Add(userKey, voicePrint); return(voicePrint); } }
/** * Sets the universal model to be used to calculate likelihood ratios * Once set, further voice print create / merge operations won't modify this model * @param universalModel the universal model to set, may not be null */ public void SetUniversalModel(VoicePrint universalModel) { lock (_lock) { this.universalModel = universalModel ?? throw new ArgumentNullException(nameof(universalModel), "The universal model may not be null"); universalModelWasSetByUser = false; } }
/** * Returns the distance between this voice print and the given one using the calculator. * Threading : it is safe to call this method while other threads may merge this voice print instance * with another one in the sense that the distance calculation will not happen on half merged voice print. * Since this method is read only, it is safe to call it from multiple threads for a single instance * @param calculator the distance calculator * @param voicePrint the voice print * @return the distance */ public double GetDistance(DistanceCalculator calculator, VoicePrint voicePrint) { rwl.EnterReadLock(); try { return(calculator.GetDistance(features, voicePrint.features)); } finally { rwl.ExitReadLock(); } }
/** * Constructor taking previously extracted voice prints directly into the system * @param sampleRate the sample rate, at least 8000.0 Hz (preferably higher) * @param voicePrintsByUserKey a {@code Map} containing user keys and their respective {@code VoicePrint} */ public Recognito(float sampleRate, Dictionary <T, VoicePrint> voicePrintsByUserKey) : this(sampleRate) { VoicePrint universalModel = null; foreach (var item in voicePrintsByUserKey.Values) { if (universalModel == null) { universalModel = new VoicePrint(item); continue; } universalModel.Merge(item); } store.AddRange(voicePrintsByUserKey); }
/** * Copy constructor * @param print the VoicePrint to copy */ public VoicePrint(VoicePrint print) : this(ArrayHelper.Copy(print.features, print.features.Length)) { }
/** * Convenience method to merge voice prints * @param print the voice print to merge * @see VoicePrint#merge(double[]) */ public void Merge(VoicePrint print) { Merge(print.features); }