From a0583a32f07a8dfada80b15009f82830d6477da3 Mon Sep 17 00:00:00 2001 From: Kwangsoo Yeo Date: Mon, 6 Nov 2023 14:57:00 -0800 Subject: [PATCH 1/7] initial dotnet changes --- .github/workflows/dotnet-demos.yml | 18 +++ binding/dotnet/Cheetah/Cheetah.cs | 122 ++++++++++++++------- binding/dotnet/Cheetah/Cheetah.csproj | 2 +- binding/dotnet/Cheetah/CheetahException.cs | 48 ++++++++ binding/dotnet/CheetahTest/MainTest.cs | 73 ++++++++++++ 5 files changed, 225 insertions(+), 38 deletions(-) diff --git a/.github/workflows/dotnet-demos.yml b/.github/workflows/dotnet-demos.yml index 45089fd4..228771c7 100644 --- a/.github/workflows/dotnet-demos.yml +++ b/.github/workflows/dotnet-demos.yml @@ -38,6 +38,15 @@ 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/Cheetah/bin/Release Cheetah + # ****************************************************** + - name: Dotnet build micdemo run: dotnet build -c MicDemo.Release @@ -60,6 +69,15 @@ 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/Cheetah/bin/Release Cheetah + # ****************************************************** + - name: Dotnet build micdemo run: dotnet build -c MicDemo.Release diff --git a/binding/dotnet/Cheetah/Cheetah.cs b/binding/dotnet/Cheetah/Cheetah.cs index 5e2efe43..3d6fb010 100644 --- a/binding/dotnet/Cheetah/Cheetah.cs +++ b/binding/dotnet/Cheetah/Cheetah.cs @@ -19,7 +19,7 @@ namespace Pv /// /// Status codes returned by Cheetah library /// - public enum PvStatus + public enum CheetahStatus { SUCCESS = 0, OUT_OF_MEMORY = 1, @@ -69,7 +69,7 @@ private static IntPtr ImportResolver(string libraryName, Assembly assembly, DllI #endif [DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)] - private static extern PvStatus pv_cheetah_init( + private static extern CheetahStatus pv_cheetah_init( IntPtr accessKey, IntPtr modelPath, float endpointDurationSec, @@ -83,14 +83,14 @@ private static extern PvStatus pv_cheetah_init( private static extern void pv_cheetah_delete(IntPtr handle); [DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)] - private static extern PvStatus pv_cheetah_process( + private static extern CheetahStatus pv_cheetah_process( IntPtr handle, Int16[] pcm, out IntPtr transcriptPtr, out bool isEndpoint); [DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)] - private static extern PvStatus pv_cheetah_flush( + private static extern CheetahStatus pv_cheetah_flush( IntPtr handle, out IntPtr transcriptPtr); @@ -103,6 +103,15 @@ private static extern PvStatus pv_cheetah_flush( [DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)] private static extern void pv_free(IntPtr memoryPtr); + [DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern void pv_set_sdk(string sdk); + + [DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern CheetahStatus pv_get_error_stack(out IntPtr messageStack, out int messageStackDepth); + + [DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern void pv_free_error_stack(IntPtr messageStack); + /// /// Factory method for Cheetah Speech-to-Text engine. /// @@ -162,8 +171,10 @@ private Cheetah( IntPtr accessKeyPtr = Utils.GetPtrFromUtf8String(accessKey); IntPtr modelPathPtr = Utils.GetPtrFromUtf8String(modelPath); + + pv_set_sdk("dotnet"); - PvStatus status = pv_cheetah_init( + CheetahStatus status = pv_cheetah_init( accessKeyPtr, modelPathPtr, endpointDurationSec, @@ -173,9 +184,10 @@ private Cheetah( Marshal.FreeHGlobal(accessKeyPtr); Marshal.FreeHGlobal(modelPathPtr); - if (status != PvStatus.SUCCESS) + if (status != CheetahStatus.SUCCESS) { - throw PvStatusToException(status); + string[] messageStack = GetMessageStack(); + throw CheetahStatusToException(status, messageStack); } Version = Utils.GetUtf8StringFromPtr(pv_cheetah_version()); @@ -208,10 +220,11 @@ public CheetahTranscript Process(Int16[] pcm) IntPtr transcriptPtr = IntPtr.Zero; bool isEndpoint = false; - PvStatus status = pv_cheetah_process(_libraryPointer, pcm, out transcriptPtr, out isEndpoint); - if (status != PvStatus.SUCCESS) + CheetahStatus status = pv_cheetah_process(_libraryPointer, pcm, out transcriptPtr, out isEndpoint); + if (status != CheetahStatus.SUCCESS) { - throw PvStatusToException(status, "Cheetah failed to process the audio frame."); + string[] messageStack = GetMessageStack(); + throw CheetahStatusToException(status, "Cheetah failed to process the audio frame.", messageStack); } string transcript = Utils.GetUtf8StringFromPtr(transcriptPtr); @@ -228,10 +241,11 @@ public CheetahTranscript Process(Int16[] pcm) public CheetahTranscript Flush() { IntPtr transcriptPtr = IntPtr.Zero; - PvStatus status = pv_cheetah_flush(_libraryPointer, out transcriptPtr); - if (status != PvStatus.SUCCESS) + CheetahStatus status = pv_cheetah_flush(_libraryPointer, out transcriptPtr); + if (status != CheetahStatus.SUCCESS) { - throw PvStatusToException(status, "Cheetah failed to process the audio frame."); + string[] messageStack = GetMessageStack(); + throw CheetahStatusToException(status, "Cheetah failed to flush.", messageStack); } string transcript = Utils.GetUtf8StringFromPtr(transcriptPtr); @@ -261,35 +275,45 @@ public CheetahTranscript Flush() /// Coverts status codes to relevant .NET exceptions /// /// Picovoice library status code. + /// Default error message. + /// Error stack returned from Picovoice library. /// .NET exception - private static Exception PvStatusToException(PvStatus status, string message = "") + private static Exception CheetahStatusToException( + CheetahStatus status + string message = "", + string[] messageStack = null) { + if (messageStack == null) + { + messageStack = new string[] { }; + } + switch (status) { - case PvStatus.OUT_OF_MEMORY: - return new CheetahMemoryException(message); - case PvStatus.IO_ERROR: - return new CheetahIOException(message); - case PvStatus.INVALID_ARGUMENT: - return new CheetahInvalidArgumentException(message); - case PvStatus.STOP_ITERATION: - return new CheetahStopIterationException(message); - case PvStatus.KEY_ERROR: - return new CheetahKeyException(message); - case PvStatus.INVALID_STATE: - return new CheetahInvalidStateException(message); - case PvStatus.RUNTIME_ERROR: - return new CheetahRuntimeException(message); - case PvStatus.ACTIVATION_ERROR: - return new CheetahActivationException(message); - case PvStatus.ACTIVATION_LIMIT_REACHED: - return new CheetahActivationLimitException(message); - case PvStatus.ACTIVATION_THROTTLED: - return new CheetahActivationThrottledException(message); - case PvStatus.ACTIVATION_REFUSED: - return new CheetahActivationRefusedException(message); + case CheetahStatus.OUT_OF_MEMORY: + return new CheetahMemoryException(message, messageStack); + case CheetahStatus.IO_ERROR: + return new CheetahIOException(message, messageStack); + case CheetahStatus.INVALID_ARGUMENT: + return new CheetahInvalidArgumentException(message, messageStack); + case CheetahStatus.STOP_ITERATION: + return new CheetahStopIterationException(message, messageStack); + case CheetahStatus.KEY_ERROR: + return new CheetahKeyException(message, messageStack); + case CheetahStatus.INVALID_STATE: + return new CheetahInvalidStateException(message, messageStack); + case CheetahStatus.RUNTIME_ERROR: + return new CheetahRuntimeException(message, messageStack); + case CheetahStatus.ACTIVATION_ERROR: + return new CheetahActivationException(message, messageStack); + case CheetahStatus.ACTIVATION_LIMIT_REACHED: + return new CheetahActivationLimitException(message, messageStack); + case CheetahStatus.ACTIVATION_THROTTLED: + return new CheetahActivationThrottledException(message, messageStack); + case CheetahStatus.ACTIVATION_REFUSED: + return new CheetahActivationRefusedException(message, messageStack); default: - return new CheetahException("Unmapped error code returned from Cheetah."); + return new CheetahException("Unmapped error code returned from Cheetah.", messageStack); } } @@ -312,5 +336,29 @@ public void Dispose() { Dispose(); } + + private string[] GetMessageStack() + { + int messageStackDepth; + IntPtr messageStackRef; + + CheetahStatus status = pv_get_error_stack(out messageStackRef, out messageStackDepth); + if (status != CheetahStatus.SUCCESS) + { + throw CheetahStatusToException(status, "Unable to get Cheetah error state"); + } + + int elementSize = Marshal.SizeOf(typeof(IntPtr)); + string[] messageStack = new string[messageStackDepth]; + + for (int i = 0; i < messageStackDepth; i++) + { + messageStack[i] = Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(messageStackRef, i * elementSize)); + } + + pv_free_error_stack(messageStackRef); + + return messageStack; + } } } \ No newline at end of file diff --git a/binding/dotnet/Cheetah/Cheetah.csproj b/binding/dotnet/Cheetah/Cheetah.csproj index b61ca5de..b6b65ffc 100644 --- a/binding/dotnet/Cheetah/Cheetah.csproj +++ b/binding/dotnet/Cheetah/Cheetah.csproj @@ -1,7 +1,7 @@  net6.0;netcoreapp3.0;netstandard2.0 - 1.1.2 + 2.0.0 Picovoice Cheetah Speech-to-Text Engine diff --git a/binding/dotnet/Cheetah/CheetahException.cs b/binding/dotnet/Cheetah/CheetahException.cs index 534fc8b5..3ec89ec4 100644 --- a/binding/dotnet/Cheetah/CheetahException.cs +++ b/binding/dotnet/Cheetah/CheetahException.cs @@ -15,10 +15,36 @@ namespace Pv { public class CheetahException : Exception { + private readonly string[] _messageStack; + public CheetahException() { } public CheetahException(string message) : base(message) { } + public CheetahException(string message, string[] messageStack) : base(ModifyMessages(message, messageStack)) + { + this._messageStack = messageStack; + } + + public string[] MessageStack + { + get => _messageStack; + } + + private static string ModifyMessages(string message, string[] messageStack) + { + string messageString = message; + if (messageStack.Length > 0) + { + messageString += ":"; + for (int i = 0; i < messageStack.Length; i++) + { + messageString += $"\n [{i}] {messageStack[i]}"; + } + } + return messageString; + } + } public class CheetahMemoryException : CheetahException @@ -26,6 +52,8 @@ public class CheetahMemoryException : CheetahException public CheetahMemoryException() { } public CheetahMemoryException(string message) : base(message) { } + + public CheetahMemoryException(string message, string[] messageStack) : base(message, messageStack) { } } public class CheetahIOException : CheetahException @@ -33,6 +61,8 @@ public class CheetahIOException : CheetahException public CheetahIOException() { } public CheetahIOException(string message) : base(message) { } + + public CheetahIOException(string message, string[] messageStack) : base(message, messageStack) { } } public class CheetahInvalidArgumentException : CheetahException @@ -40,6 +70,8 @@ public class CheetahInvalidArgumentException : CheetahException public CheetahInvalidArgumentException() { } public CheetahInvalidArgumentException(string message) : base(message) { } + + public CheetahInvalidArgumentException(string message, string[] messageStack) : base(message, messageStack) { } } public class CheetahStopIterationException : CheetahException @@ -47,6 +79,8 @@ public class CheetahStopIterationException : CheetahException public CheetahStopIterationException() { } public CheetahStopIterationException(string message) : base(message) { } + + public CheetahStopIterationException(string message, string[] messageStack) : base(message, messageStack) { } } public class CheetahKeyException : CheetahException @@ -54,6 +88,8 @@ public class CheetahKeyException : CheetahException public CheetahKeyException() { } public CheetahKeyException(string message) : base(message) { } + + public CheetahKeyException(string message, string[] messageStack) : base(message, messageStack) { } } public class CheetahInvalidStateException : CheetahException @@ -61,6 +97,8 @@ public class CheetahInvalidStateException : CheetahException public CheetahInvalidStateException() { } public CheetahInvalidStateException(string message) : base(message) { } + + public CheetahInvalidStateException(string message, string[] messageStack) : base(message, messageStack) { } } public class CheetahRuntimeException : CheetahException @@ -68,6 +106,8 @@ public class CheetahRuntimeException : CheetahException public CheetahRuntimeException() { } public CheetahRuntimeException(string message) : base(message) { } + + public CheetahRuntimeException(string message, string[] messageStack) : base(message, messageStack) { } } public class CheetahActivationException : CheetahException @@ -75,6 +115,8 @@ public class CheetahActivationException : CheetahException public CheetahActivationException() { } public CheetahActivationException(string message) : base(message) { } + + public CheetahActivationException(string message, string[] messageStack) : base(message, messageStack) { } } public class CheetahActivationLimitException : CheetahException @@ -82,6 +124,8 @@ public class CheetahActivationLimitException : CheetahException public CheetahActivationLimitException() { } public CheetahActivationLimitException(string message) : base(message) { } + + public CheetahActivationLimitException(string message, string[] messageStack) : base(message, messageStack) { } } public class CheetahActivationThrottledException : CheetahException @@ -89,6 +133,8 @@ public class CheetahActivationThrottledException : CheetahException public CheetahActivationThrottledException() { } public CheetahActivationThrottledException(string message) : base(message) { } + + public CheetahActivationThrottledException(string message, string[] messageStack) : base(message, messageStack) { } } public class CheetahActivationRefusedException : CheetahException @@ -96,6 +142,8 @@ public class CheetahActivationRefusedException : CheetahException public CheetahActivationRefusedException() { } public CheetahActivationRefusedException(string message) : base(message) { } + + public CheetahActivationRefusedException(string message, string[] messageStack) : base(message, messageStack) { } } } \ No newline at end of file diff --git a/binding/dotnet/CheetahTest/MainTest.cs b/binding/dotnet/CheetahTest/MainTest.cs index bf8e8c19..0058a9cb 100644 --- a/binding/dotnet/CheetahTest/MainTest.cs +++ b/binding/dotnet/CheetahTest/MainTest.cs @@ -142,5 +142,78 @@ public void TestCustomModel(bool enableAutomaticPunctuation, string expectedTran Assert.AreEqual(transcript, expectedTranscript); } } + + [TestMethod] + public void TestMessageStack() + { + string modelPath = Path.Combine(_relativeDir, "lib/common/cheetah_params.pv"); + + Cheetah c; + string[] messageList = new string[] { }; + + try + { + c = Cheetah.Create( + accessKey: 'invalid', + modelPath: modelPath, + enableAutomaticPunctuation: true); + Assert.IsNull(c); + c.Dispose(); + } + catch (CheetahException e) + { + messageList = e.MessageStack; + } + + Assert.IsTrue(0 < messageList.Length); + Assert.IsTrue(messageList.Length < 8); + + try + { + c = Cheetah.Create( + accessKey: 'invalid', + modelPath: modelPath, + enableAutomaticPunctuation: true); + Assert.IsNull(c); + c.Dispose(); + } + catch (CheetahException e) + { + for (int i = 0; i < messageList.Length; i++) + { + Assert.AreEqual(messageList[i], e.MessageStack[i]); + } + } + } + + [TestMethod] + public void TestProcessMessageStack() + { + string modelPath = Path.Combine(_relativeDir, "lib/common/cheetah_params.pv"); + + Cheetah c = Cheetah.Create( + accessKey: ACCESS_KEY, + modelPath: modelPath, + enableAutomaticPunctuation: enableAutomaticPunctuation); + short[] testPcm = new short[c.FrameLength]; + + var obj = typeof(Cheetah).GetField("_libraryPointer", BindingFlags.NonPublic | BindingFlags.Instance); + IntPtr address = (IntPtr)obj.GetValue(c); + obj.SetValue(c, IntPtr.Zero); + + try + { + CheetahTranscript res = c.Process(testPcm); + Assert.IsTrue(res.Transcript.Length == -1); + } + catch (PorcupineException e) + { + Assert.IsTrue(0 < e.MessageStack.Length); + Assert.IsTrue(e.MessageStack.Length < 8); + } + + obj.SetValue(c, address); + c.Dispose(); + } } } \ No newline at end of file From b7fccde1ec6bedb37f0fa99f61cc9c085582402d Mon Sep 17 00:00:00 2001 From: Kwangsoo Yeo Date: Mon, 6 Nov 2023 15:36:43 -0800 Subject: [PATCH 2/7] update test --- binding/dotnet/CheetahTest/MainTest.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/binding/dotnet/CheetahTest/MainTest.cs b/binding/dotnet/CheetahTest/MainTest.cs index 0058a9cb..861ea7dd 100644 --- a/binding/dotnet/CheetahTest/MainTest.cs +++ b/binding/dotnet/CheetahTest/MainTest.cs @@ -187,7 +187,7 @@ public void TestMessageStack() } [TestMethod] - public void TestProcessMessageStack() + public void TestProcessFlushMessageStack() { string modelPath = Path.Combine(_relativeDir, "lib/common/cheetah_params.pv"); @@ -206,7 +206,18 @@ public void TestProcessMessageStack() CheetahTranscript res = c.Process(testPcm); Assert.IsTrue(res.Transcript.Length == -1); } - catch (PorcupineException e) + catch (CheetahException e) + { + Assert.IsTrue(0 < e.MessageStack.Length); + Assert.IsTrue(e.MessageStack.Length < 8); + } + + try + { + CheetahTranscript res = c.Flush(); + Assert.IsTrue(res.Transcript.Length == -1); + } + catch (CheetahException e) { Assert.IsTrue(0 < e.MessageStack.Length); Assert.IsTrue(e.MessageStack.Length < 8); From 5898b4b95f86a28ab4803f6a7f38b81b80746f0f Mon Sep 17 00:00:00 2001 From: Kwangsoo Yeo Date: Tue, 7 Nov 2023 13:49:43 -0800 Subject: [PATCH 3/7] update test --- binding/dotnet/CheetahTest/MainTest.cs | 77 +++++++++++++++++++++----- 1 file changed, 64 insertions(+), 13 deletions(-) diff --git a/binding/dotnet/CheetahTest/MainTest.cs b/binding/dotnet/CheetahTest/MainTest.cs index 861ea7dd..51cb89a1 100644 --- a/binding/dotnet/CheetahTest/MainTest.cs +++ b/binding/dotnet/CheetahTest/MainTest.cs @@ -13,6 +13,8 @@ specific language governing permissions and limitations under the License. using System.Collections.Generic; using System.IO; +using Fastenshtein; + using Microsoft.VisualStudio.TestTools.UnitTesting; using Pv; @@ -44,6 +46,46 @@ private List GetPcmFromFile(string audioFilePath, int expectedSampleRate) return data; } + public static IEnumerable TestParameters + { + get + { + List testParameters = new List(); + + string transcript = "Mr. Quilter is the apostle of the middle classes and we are glad to welcome his gospel." + string[] punctuations = new string[]{"."}; + + testParameters.Add(new object[] + { + "en", + "test.wav", + transcript, + true, + 0.025 + }); + + string transcriptWithoutPunctuation = transcript; + foreach (string p in punctuations) + { + transcriptWithoutPunctuation = transcriptWithoutPunctuation.Replace(p, ""); + } + + testParameters.Add(new object[] + { + "en", + "test.wav", + transcriptWithoutPunctuation, + false, + 0.025 + }); + + return testParameters; + } + } + + static float GetErrorRate(string transcript, string referenceTranscript) + => Levenshtein.Distance(transcript, referenceTranscript) / (float)referenceTranscript.Length; + [ClassInitialize] public static void ClassInitialize(TestContext _) { @@ -80,16 +122,20 @@ public void TestFrameLength() } [TestMethod] - [DataRow(true, "Mr. Quilter is the apostle of the middle classes and we are glad to welcome his gospel.")] - [DataRow(false, "Mr quilter is the apostle of the middle classes and we are glad to welcome his gospel")] - public void TestProcess(bool enableAutomaticPunctuation, string expectedTranscript) + [DynamicData(nameof(TestParameters))] + public void TestProcess( + string language, + string testAudioFile, + string referenceTranscript, + bool enablePunctuation, + float targetErrorRate) { using (Cheetah cheetah = Cheetah.Create( accessKey: ACCESS_KEY, endpointDurationSec: 0.2f, - enableAutomaticPunctuation: enableAutomaticPunctuation)) + enableAutomaticPunctuation: enablePunctuation)) { - string testAudioPath = Path.Combine(_relativeDir, "resources/audio_samples/test.wav"); + string testAudioPath = Path.Combine(_relativeDir, "resources/audio_samples", testAudioFile); List pcm = GetPcmFromFile(testAudioPath, cheetah.SampleRate); int frameLen = cheetah.FrameLength; @@ -107,23 +153,27 @@ public void TestProcess(bool enableAutomaticPunctuation, string expectedTranscri } CheetahTranscript finalTranscriptObj = cheetah.Flush(); transcript += finalTranscriptObj.Transcript; - Assert.AreEqual(transcript, expectedTranscript); - Assert.IsTrue(isEndpoint); + + Assert.IsTrue(GetErrorRate(transcript, referenceTranscript) < targetErrorRate); } } [TestMethod] - [DataRow(true, "Mr. Quilter is the apostle of the middle classes and we are glad to welcome his gospel.")] - [DataRow(false, "Mr quilter is the apostle of the middle classes and we are glad to welcome his gospel")] - public void TestCustomModel(bool enableAutomaticPunctuation, string expectedTranscript) + [DynamicData(nameof(TestParameters))] + public void TestCustomModel( + string language, + string testAudioFile, + string referenceTranscript, + bool enablePunctuation, + float targetErrorRate) { string testModelPath = Path.Combine(_relativeDir, "lib/common/cheetah_params.pv"); using (Cheetah cheetah = Cheetah.Create( accessKey: ACCESS_KEY, modelPath: testModelPath, - enableAutomaticPunctuation: enableAutomaticPunctuation)) + enableAutomaticPunctuation: enablePunctuation)) { - string testAudioPath = Path.Combine(_relativeDir, "resources/audio_samples/test.wav"); + string testAudioPath = Path.Combine(_relativeDir, "resources/audio_samples", testAudioFile); List pcm = GetPcmFromFile(testAudioPath, cheetah.SampleRate); int frameLen = cheetah.FrameLength; @@ -139,7 +189,8 @@ public void TestCustomModel(bool enableAutomaticPunctuation, string expectedTran } CheetahTranscript finalTranscriptObj = cheetah.Flush(); transcript += finalTranscriptObj.Transcript; - Assert.AreEqual(transcript, expectedTranscript); + + Assert.IsTrue(GetErrorRate(transcript, referenceTranscript) < targetErrorRate); } } From b8e2f6999fc58177293a85f5974d4b7876e7b10d Mon Sep 17 00:00:00 2001 From: Kwangsoo Yeo Date: Mon, 20 Nov 2023 13:29:16 -0800 Subject: [PATCH 4/7] update test --- binding/dotnet/Cheetah/Cheetah.cs | 4 +- binding/dotnet/CheetahTest/CheetahTest.csproj | 1 + binding/dotnet/CheetahTest/MainTest.cs | 75 ++++++++++++------- 3 files changed, 52 insertions(+), 28 deletions(-) diff --git a/binding/dotnet/Cheetah/Cheetah.cs b/binding/dotnet/Cheetah/Cheetah.cs index 3d6fb010..b96efa0b 100644 --- a/binding/dotnet/Cheetah/Cheetah.cs +++ b/binding/dotnet/Cheetah/Cheetah.cs @@ -187,7 +187,7 @@ private Cheetah( if (status != CheetahStatus.SUCCESS) { string[] messageStack = GetMessageStack(); - throw CheetahStatusToException(status, messageStack); + throw CheetahStatusToException(status, "Cheetah init failed", messageStack); } Version = Utils.GetUtf8StringFromPtr(pv_cheetah_version()); @@ -279,7 +279,7 @@ public CheetahTranscript Flush() /// Error stack returned from Picovoice library. /// .NET exception private static Exception CheetahStatusToException( - CheetahStatus status + CheetahStatus status, string message = "", string[] messageStack = null) { diff --git a/binding/dotnet/CheetahTest/CheetahTest.csproj b/binding/dotnet/CheetahTest/CheetahTest.csproj index 40aa4d34..10b485e1 100644 --- a/binding/dotnet/CheetahTest/CheetahTest.csproj +++ b/binding/dotnet/CheetahTest/CheetahTest.csproj @@ -6,6 +6,7 @@ + diff --git a/binding/dotnet/CheetahTest/MainTest.cs b/binding/dotnet/CheetahTest/MainTest.cs index 51cb89a1..b390f6f0 100644 --- a/binding/dotnet/CheetahTest/MainTest.cs +++ b/binding/dotnet/CheetahTest/MainTest.cs @@ -12,6 +12,7 @@ specific language governing permissions and limitations under the License. using System; using System.Collections.Generic; using System.IO; +using System.Reflection; using Fastenshtein; @@ -52,31 +53,16 @@ public static IEnumerable TestParameters { List testParameters = new List(); - string transcript = "Mr. Quilter is the apostle of the middle classes and we are glad to welcome his gospel." - string[] punctuations = new string[]{"."}; + string transcript = "Mr quilter is the apostle of the middle classes and we are glad to welcome his gospel"; + string transcriptWithPunctuation = "Mr. Quilter is the apostle of the middle classes and we are glad to welcome his gospel."; testParameters.Add(new object[] { "en", "test.wav", transcript, - true, - 0.025 - }); - - string transcriptWithoutPunctuation = transcript; - foreach (string p in punctuations) - { - transcriptWithoutPunctuation = transcriptWithoutPunctuation.Replace(p, ""); - } - - testParameters.Add(new object[] - { - "en", - "test.wav", - transcriptWithoutPunctuation, - false, - 0.025 + transcriptWithPunctuation, + 0.025f }); return testParameters; @@ -127,13 +113,50 @@ public void TestProcess( string language, string testAudioFile, string referenceTranscript, - bool enablePunctuation, + string _, + float targetErrorRate) + { + using (Cheetah cheetah = Cheetah.Create( + accessKey: ACCESS_KEY, + endpointDurationSec: 0.2f, + enableAutomaticPunctuation: false)) + { + string testAudioPath = Path.Combine(_relativeDir, "resources/audio_samples", testAudioFile); + List pcm = GetPcmFromFile(testAudioPath, cheetah.SampleRate); + + int frameLen = cheetah.FrameLength; + int framecount = (int)Math.Floor((float)(pcm.Count / frameLen)); + + string transcript = ""; + bool isEndpoint = false; + for (int i = 0; i < framecount; i++) + { + int start = i * cheetah.FrameLength; + List frame = pcm.GetRange(start, frameLen); + CheetahTranscript transcriptObj = cheetah.Process(frame.ToArray()); + transcript += transcriptObj.Transcript; + isEndpoint = transcriptObj.IsEndpoint; + } + CheetahTranscript finalTranscriptObj = cheetah.Flush(); + transcript += finalTranscriptObj.Transcript; + + Assert.IsTrue(GetErrorRate(transcript, referenceTranscript) < targetErrorRate); + } + } + + [TestMethod] + [DynamicData(nameof(TestParameters))] + public void TestProcessWithPunctuation( + string language, + string testAudioFile, + string _, + string referenceTranscript, float targetErrorRate) { using (Cheetah cheetah = Cheetah.Create( accessKey: ACCESS_KEY, endpointDurationSec: 0.2f, - enableAutomaticPunctuation: enablePunctuation)) + enableAutomaticPunctuation: true)) { string testAudioPath = Path.Combine(_relativeDir, "resources/audio_samples", testAudioFile); List pcm = GetPcmFromFile(testAudioPath, cheetah.SampleRate); @@ -164,14 +187,14 @@ public void TestCustomModel( string language, string testAudioFile, string referenceTranscript, - bool enablePunctuation, + string _, float targetErrorRate) { string testModelPath = Path.Combine(_relativeDir, "lib/common/cheetah_params.pv"); using (Cheetah cheetah = Cheetah.Create( accessKey: ACCESS_KEY, modelPath: testModelPath, - enableAutomaticPunctuation: enablePunctuation)) + enableAutomaticPunctuation: false)) { string testAudioPath = Path.Combine(_relativeDir, "resources/audio_samples", testAudioFile); List pcm = GetPcmFromFile(testAudioPath, cheetah.SampleRate); @@ -205,7 +228,7 @@ public void TestMessageStack() try { c = Cheetah.Create( - accessKey: 'invalid', + accessKey: "invalid", modelPath: modelPath, enableAutomaticPunctuation: true); Assert.IsNull(c); @@ -222,7 +245,7 @@ public void TestMessageStack() try { c = Cheetah.Create( - accessKey: 'invalid', + accessKey: "invalid", modelPath: modelPath, enableAutomaticPunctuation: true); Assert.IsNull(c); @@ -245,7 +268,7 @@ public void TestProcessFlushMessageStack() Cheetah c = Cheetah.Create( accessKey: ACCESS_KEY, modelPath: modelPath, - enableAutomaticPunctuation: enableAutomaticPunctuation); + enableAutomaticPunctuation: false); short[] testPcm = new short[c.FrameLength]; var obj = typeof(Cheetah).GetField("_libraryPointer", BindingFlags.NonPublic | BindingFlags.Instance); From 1c1e94076a2ff3102c71980edb22027ce44f42ae Mon Sep 17 00:00:00 2001 From: Kwangsoo Yeo Date: Mon, 20 Nov 2023 13:39:34 -0800 Subject: [PATCH 5/7] fix spell, demo and workflow --- .github/workflows/dotnet-demos.yml | 4 ++-- binding/dotnet/Cheetah/Cheetah.cs | 2 +- binding/dotnet/Cheetah/CheetahException.cs | 2 +- binding/dotnet/CheetahTest/MainTest.cs | 2 +- demo/dotnet/CheetahDemo/CheetahDemo.csproj | 2 +- resources/.lint/spell-check/dict.txt | 1 + 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/dotnet-demos.yml b/.github/workflows/dotnet-demos.yml index 228771c7..66962e39 100644 --- a/.github/workflows/dotnet-demos.yml +++ b/.github/workflows/dotnet-demos.yml @@ -44,7 +44,7 @@ jobs: working-directory: binding/dotnet - name: Add binding to demo - run: dotnet add package -s ../../../binding/dotnet/Cheetah/bin/Release Cheetah + run: dotnet add package -s ../../../binding/dotnet/Cheetah/bin/Release Picovoice.Cheetah # ****************************************************** - name: Dotnet build micdemo @@ -75,7 +75,7 @@ jobs: working-directory: binding/dotnet - name: Add binding to demo - run: dotnet add package -s ../../../binding/dotnet/Cheetah/bin/Release Cheetah + run: dotnet add package -s ../../../binding/dotnet/Cheetah/bin/Release Picovoice.Cheetah # ****************************************************** - name: Dotnet build micdemo diff --git a/binding/dotnet/Cheetah/Cheetah.cs b/binding/dotnet/Cheetah/Cheetah.cs index b96efa0b..8df0c168 100644 --- a/binding/dotnet/Cheetah/Cheetah.cs +++ b/binding/dotnet/Cheetah/Cheetah.cs @@ -171,7 +171,7 @@ private Cheetah( IntPtr accessKeyPtr = Utils.GetPtrFromUtf8String(accessKey); IntPtr modelPathPtr = Utils.GetPtrFromUtf8String(modelPath); - + pv_set_sdk("dotnet"); CheetahStatus status = pv_cheetah_init( diff --git a/binding/dotnet/Cheetah/CheetahException.cs b/binding/dotnet/Cheetah/CheetahException.cs index 3ec89ec4..22508c3e 100644 --- a/binding/dotnet/Cheetah/CheetahException.cs +++ b/binding/dotnet/Cheetah/CheetahException.cs @@ -16,7 +16,7 @@ namespace Pv public class CheetahException : Exception { private readonly string[] _messageStack; - + public CheetahException() { } public CheetahException(string message) : base(message) { } diff --git a/binding/dotnet/CheetahTest/MainTest.cs b/binding/dotnet/CheetahTest/MainTest.cs index b390f6f0..081218f6 100644 --- a/binding/dotnet/CheetahTest/MainTest.cs +++ b/binding/dotnet/CheetahTest/MainTest.cs @@ -143,7 +143,7 @@ public void TestProcess( Assert.IsTrue(GetErrorRate(transcript, referenceTranscript) < targetErrorRate); } } - + [TestMethod] [DynamicData(nameof(TestParameters))] public void TestProcessWithPunctuation( diff --git a/demo/dotnet/CheetahDemo/CheetahDemo.csproj b/demo/dotnet/CheetahDemo/CheetahDemo.csproj index 31a01d5e..57ec20dd 100644 --- a/demo/dotnet/CheetahDemo/CheetahDemo.csproj +++ b/demo/dotnet/CheetahDemo/CheetahDemo.csproj @@ -19,7 +19,7 @@ - + diff --git a/resources/.lint/spell-check/dict.txt b/resources/.lint/spell-check/dict.txt index b2b2ed30..008bf560 100644 --- a/resources/.lint/spell-check/dict.txt +++ b/resources/.lint/spell-check/dict.txt @@ -24,6 +24,7 @@ drwav dylib endpointing exopackage +Fastenshtein filedemo framecount gradlew From cd15e8042c12ce1b4d0a76551bcd5666812e9b06 Mon Sep 17 00:00:00 2001 From: Kwangsoo Yeo Date: Mon, 20 Nov 2023 13:43:33 -0800 Subject: [PATCH 6/7] fix build --- demo/dotnet/CheetahDemo/CheetahDemo.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/dotnet/CheetahDemo/CheetahDemo.csproj b/demo/dotnet/CheetahDemo/CheetahDemo.csproj index 57ec20dd..31a01d5e 100644 --- a/demo/dotnet/CheetahDemo/CheetahDemo.csproj +++ b/demo/dotnet/CheetahDemo/CheetahDemo.csproj @@ -19,7 +19,7 @@ - + From 9a97ad26dcf1fba5c54475afcb1f2145bcf691c9 Mon Sep 17 00:00:00 2001 From: Kwangsoo Yeo Date: Wed, 22 Nov 2023 13:01:27 -0800 Subject: [PATCH 7/7] add pv cheetah transcript delete --- binding/dotnet/Cheetah/Cheetah.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/binding/dotnet/Cheetah/Cheetah.cs b/binding/dotnet/Cheetah/Cheetah.cs index 8df0c168..a994e9e1 100644 --- a/binding/dotnet/Cheetah/Cheetah.cs +++ b/binding/dotnet/Cheetah/Cheetah.cs @@ -94,6 +94,9 @@ private static extern CheetahStatus pv_cheetah_flush( IntPtr handle, out IntPtr transcriptPtr); + [DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)] + private static extern void pv_cheetah_transcript_delete(IntPtr transcriptPtr); + [DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr pv_cheetah_version(); @@ -101,9 +104,7 @@ private static extern CheetahStatus pv_cheetah_flush( private static extern Int32 pv_cheetah_frame_length(); [DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)] - private static extern void pv_free(IntPtr memoryPtr); - [DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)] private static extern void pv_set_sdk(string sdk); [DllImport(LIBRARY, CallingConvention = CallingConvention.Cdecl)] @@ -228,7 +229,8 @@ public CheetahTranscript Process(Int16[] pcm) } string transcript = Utils.GetUtf8StringFromPtr(transcriptPtr); - pv_free(transcriptPtr); + pv_cheetah_transcript_delete(transcriptPtr); + return new CheetahTranscript(transcript, isEndpoint); } @@ -249,7 +251,8 @@ public CheetahTranscript Flush() } string transcript = Utils.GetUtf8StringFromPtr(transcriptPtr); - pv_free(transcriptPtr); + pv_cheetah_transcript_delete(transcriptPtr); + return new CheetahTranscript(transcript, false); }