Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
albho committed Sep 11, 2024
1 parent e972e18 commit 28be7a4
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 19 deletions.
41 changes: 24 additions & 17 deletions binding/dotnet/PvSpeaker/PvSpeaker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.");
}
}

Expand All @@ -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.");
}
}

Expand All @@ -213,14 +213,15 @@ public void Stop()
/// <returns>Number of samples that were successfully written.</returns>
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();

PvSpeakerStatus status = pv_speaker_write(_libraryPointer, pcmPtr, pcm.Length / (BitsPerSample / 8), out int writtenLength);
pinnedArray.Free();
if (status != PvSpeakerStatus.SUCCESS)
{
throw PvSpeakerStatusToException(status);
throw PvSpeakerStatusToException(status, "Failed to write to PvSpeaker.");
}
return writtenLength;
}
Expand All @@ -235,14 +236,16 @@ public int Write(byte[] pcm)
public int Flush(byte[] pcm = null)
{
pcm = pcm ?? Array.Empty<byte>();

// Create a pointer to the byte array to pass to `pv_speaker_flush`.
GCHandle pinnedArray = GCHandle.Alloc(pcm, GCHandleType.Pinned);
IntPtr pcmPtr = pinnedArray.AddrOfPinnedObject();

PvSpeakerStatus status = pv_speaker_flush(_libraryPointer, pcmPtr, pcm.Length / (BitsPerSample / 8), out int writtenLength);
pinnedArray.Free();
if (status != PvSpeakerStatus.SUCCESS)
{
throw PvSpeakerStatusToException(status);
throw PvSpeakerStatusToException(status, "Failed to flush PCM data from PvSpeaker.");
}
return writtenLength;
}
Expand All @@ -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.");
}
}

/// <summary>
Expand Down Expand Up @@ -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));
Expand All @@ -345,26 +352,26 @@ public static string[] GetAvailableDevices()
/// </summary>
/// <param name="status">Status code.</param>
/// <returns>PvSpeakerExceptions</returns>
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.");
}
Expand Down
20 changes: 18 additions & 2 deletions demo/dotnet/PvSpeakerDemo/PvSpeakerDemo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -224,6 +238,8 @@ static void Main(string[] args)

int bytesPerSample = wavInfo.BitsPerSample / 8;
List<byte[]> pcmList = SplitArray(wavInfo.AudioData, wavInfo.SampleRate * bytesPerSample);

Console.WriteLine($"Playing {inputWavPath}...");
foreach (byte[] pcmSublist in pcmList)
{
int totalWrittenLength = 0;
Expand Down

0 comments on commit 28be7a4

Please sign in to comment.