From 28be7a4960e650dd940578a19feda5b4b34fc147 Mon Sep 17 00:00:00 2001 From: Albert Ho Date: Wed, 11 Sep 2024 13:12:13 -0700 Subject: [PATCH] update --- binding/dotnet/PvSpeaker/PvSpeaker.cs | 41 +++++++++++++--------- demo/dotnet/PvSpeakerDemo/PvSpeakerDemo.cs | 20 +++++++++-- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/binding/dotnet/PvSpeaker/PvSpeaker.cs b/binding/dotnet/PvSpeaker/PvSpeaker.cs index b30a852..444b967 100644 --- a/binding/dotnet/PvSpeaker/PvSpeaker.cs +++ b/binding/dotnet/PvSpeaker/PvSpeaker.cs @@ -170,7 +170,7 @@ private PvSpeaker(int sampleRate, int bitsPerSample, int bufferSizeSecs, int dev PvSpeakerStatus status = pv_speaker_init(sampleRate, bitsPerSample, bufferSizeSecs, deviceIndex, out _libraryPointer); if (status != PvSpeakerStatus.SUCCESS) { - throw PvSpeakerStatusToException(status); + throw PvSpeakerStatusToException(status, "Failed to initialize PvSpeaker."); } SampleRate = sampleRate; @@ -188,7 +188,7 @@ public void Start() PvSpeakerStatus status = pv_speaker_start(_libraryPointer); if (status != PvSpeakerStatus.SUCCESS) { - throw PvSpeakerStatusToException(status); + throw PvSpeakerStatusToException(status, "Failed to start PvSpeaker."); } } @@ -200,7 +200,7 @@ public void Stop() PvSpeakerStatus status = pv_speaker_stop(_libraryPointer); if (status != PvSpeakerStatus.SUCCESS) { - throw PvSpeakerStatusToException(status); + throw PvSpeakerStatusToException(status, "Failed to stop PvSpeaker."); } } @@ -213,6 +213,7 @@ public void Stop() /// Number of samples that were successfully written. public int Write(byte[] pcm) { + // Create a pointer to the byte array to pass to `pv_speaker_write` . GCHandle pinnedArray = GCHandle.Alloc(pcm, GCHandleType.Pinned); IntPtr pcmPtr = pinnedArray.AddrOfPinnedObject(); @@ -220,7 +221,7 @@ public int Write(byte[] pcm) pinnedArray.Free(); if (status != PvSpeakerStatus.SUCCESS) { - throw PvSpeakerStatusToException(status); + throw PvSpeakerStatusToException(status, "Failed to write to PvSpeaker."); } return writtenLength; } @@ -235,6 +236,8 @@ public int Write(byte[] pcm) public int Flush(byte[] pcm = null) { pcm = pcm ?? Array.Empty(); + + // Create a pointer to the byte array to pass to `pv_speaker_flush`. GCHandle pinnedArray = GCHandle.Alloc(pcm, GCHandleType.Pinned); IntPtr pcmPtr = pinnedArray.AddrOfPinnedObject(); @@ -242,7 +245,7 @@ public int Flush(byte[] pcm = null) pinnedArray.Free(); if (status != PvSpeakerStatus.SUCCESS) { - throw PvSpeakerStatusToException(status); + throw PvSpeakerStatusToException(status, "Failed to flush PCM data from PvSpeaker."); } return writtenLength; } @@ -255,14 +258,18 @@ public void WriteToFile(string outputPath) { if (String.IsNullOrEmpty(outputPath)) { - throw new PvSpeakerInvalidArgumentException("Output file path was empty"); + throw new PvSpeakerInvalidArgumentException("Output file path was empty."); } byte[] utf8Bytes = Encoding.UTF8.GetBytes(outputPath + '\0'); IntPtr outputPathPtr = Marshal.AllocHGlobal(utf8Bytes.Length); Marshal.Copy(utf8Bytes, 0, outputPathPtr, utf8Bytes.Length); - pv_speaker_write_to_file(_libraryPointer, outputPathPtr); + PvSpeakerStatus status = pv_speaker_write_to_file(_libraryPointer, outputPathPtr); + if (status != PvSpeakerStatus.SUCCESS) + { + throw PvSpeakerStatusToException(status, "Failed to write to output file."); + } } /// @@ -325,7 +332,7 @@ public static string[] GetAvailableDevices() PvSpeakerStatus status = pv_speaker_get_available_devices(out int deviceListLength, out IntPtr deviceList); if (status != PvSpeakerStatus.SUCCESS) { - throw PvSpeakerStatusToException(status); + throw PvSpeakerStatusToException(status, "Failed to get available output devices."); } int elementSize = Marshal.SizeOf(typeof(IntPtr)); @@ -345,26 +352,26 @@ public static string[] GetAvailableDevices() /// /// Status code. /// PvSpeakerExceptions - private static PvSpeakerException PvSpeakerStatusToException(PvSpeakerStatus status) + private static PvSpeakerException PvSpeakerStatusToException(PvSpeakerStatus status, string message = "") { switch (status) { case PvSpeakerStatus.OUT_OF_MEMORY: - return new PvSpeakerMemoryException(); + return new PvSpeakerMemoryException(message); case PvSpeakerStatus.INVALID_ARGUMENT: - return new PvSpeakerInvalidArgumentException(); + return new PvSpeakerInvalidArgumentException(message); case PvSpeakerStatus.INVALID_STATE: - return new PvSpeakerInvalidStateException("PvSpeaker failed with invalid state."); + return new PvSpeakerInvalidStateException(message ?? "PvSpeaker failed with invalid state."); case PvSpeakerStatus.BACKEND_ERROR: - return new PvSpeakerBackendException("PvSpeaker audio backend error."); + return new PvSpeakerBackendException(message ?? "PvSpeaker audio backend error."); case PvSpeakerStatus.DEVICE_ALREADY_INITIALIZED: - return new PvSpeakerDeviceAlreadyInitializedException("PvSpeaker audio device already initialized."); + return new PvSpeakerDeviceAlreadyInitializedException(message ?? "PvSpeaker audio device already initialized."); case PvSpeakerStatus.DEVICE_NOT_INITIALIZED: - return new PvSpeakerDeviceNotInitializedException("PvSpeaker audio device not initialized."); + return new PvSpeakerDeviceNotInitializedException(message ?? "PvSpeaker audio device not initialized."); case PvSpeakerStatus.IO_ERROR: - return new PvSpeakerIOException(); + return new PvSpeakerIOException(message); case PvSpeakerStatus.RUNTIME_ERROR: - return new PvSpeakerRuntimeException("PvSpeaker runtime error."); + return new PvSpeakerRuntimeException(message ?? "PvSpeaker runtime error."); default: return new PvSpeakerException("Unknown status returned from PvSpeaker."); } diff --git a/demo/dotnet/PvSpeakerDemo/PvSpeakerDemo.cs b/demo/dotnet/PvSpeakerDemo/PvSpeakerDemo.cs index 92c9931..5e30bd0 100644 --- a/demo/dotnet/PvSpeakerDemo/PvSpeakerDemo.cs +++ b/demo/dotnet/PvSpeakerDemo/PvSpeakerDemo.cs @@ -58,12 +58,12 @@ public static WavFileInfo GetWavFileInfo(string filePath) { if (string.IsNullOrEmpty(filePath)) { - throw new ArgumentException("File path cannot be null or empty.", nameof(filePath)); + throw new ArgumentException("File path cannot be null or empty."); } if (!File.Exists(filePath)) { - throw new FileNotFoundException("The specified file was not found.", filePath); + throw new FileNotFoundException("The specified file was not found."); } using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) @@ -145,6 +145,7 @@ static void Main(string[] args) string inputWavPath = null; int bufferSizeSecs = 20; string outputWavPath = null; + bool showHelp = false; int argIndex = 0; while (argIndex < args.Length) @@ -184,12 +185,25 @@ static void Main(string[] args) outputWavPath = args[argIndex++]; } } + else if (args[argIndex] == "-h" || args[argIndex] == "--help") + { + showHelp = true; + argIndex++; + } else { argIndex++; } } + // print help text and exit + if (showHelp) + { + Console.WriteLine(HELP_STR); + Console.Read(); + return; + } + if (showAudioDevices) { ShowAudioDevices(); @@ -224,6 +238,8 @@ static void Main(string[] args) int bytesPerSample = wavInfo.BitsPerSample / 8; List pcmList = SplitArray(wavInfo.AudioData, wavInfo.SampleRate * bytesPerSample); + + Console.WriteLine($"Playing {inputWavPath}..."); foreach (byte[] pcmSublist in pcmList) { int totalWrittenLength = 0;