From 30f955a29c08146e039de3dd31e518442ca2d28e Mon Sep 17 00:00:00 2001 From: dvonthenen Date: Thu, 28 Mar 2024 07:20:43 -0700 Subject: [PATCH] Streaming Refactor --- Deepgram.Dev.sln | 67 ++++++- Deepgram.DevBuild.sln | 10 +- Deepgram.Microphone/Constants.cs | 20 ++ .../Deepgram.Microphone.csproj | 125 +++++++++++++ Deepgram.Microphone/GlobalUsings.cs | 5 + Deepgram.Microphone/Library.cs | 19 ++ Deepgram.Microphone/Microphone.cs | 177 ++++++++++++++++++ .../ClientTests/AbstractRestClientTests.cs | 3 +- .../ClientTests/AnalyzeClientTests.cs | 2 +- .../UnitTests/ClientTests/LiveClientTests.cs | 2 +- .../UnitTests/ClientTests/ManageClientTest.cs | 4 +- .../ClientTests/OnPremClientTests.cs | 4 +- .../ClientTests/PrerecordedClientTests.cs | 2 +- .../UnitTests/ClientTests/SpeakClientTests.cs | 2 +- .../HttpClientExtensionTests.cs | 10 +- Deepgram.sln | 6 + Deepgram/Abstractions/AbstractRestClient.cs | 4 +- Deepgram/AnalyzeClient.cs | 2 +- Deepgram/Clients/Analyze/v1/Client.cs | 2 +- Deepgram/Clients/Live/v1/Client.cs | 38 ++-- Deepgram/Clients/Manage/v1/Client.cs | 2 +- Deepgram/Clients/OnPrem/v1/Client.cs | 2 +- Deepgram/Clients/Speak/v1/Client.cs | 2 +- Deepgram/Constants/Defaults.cs | 7 +- Deepgram/Deepgram.csproj | 4 +- Deepgram/Factory/HttpClientFactory.cs | 21 +-- Deepgram/LiveClient.cs | 2 +- Deepgram/ManageClient.cs | 2 +- .../Authenticate/v1/DeepgramClientOptions.cs | 48 ++++- Deepgram/Models/Live/v1/Alternative.cs | 6 +- Deepgram/Models/Live/v1/Channel.cs | 2 +- Deepgram/Models/Live/v1/LiveSchema.cs | 4 +- Deepgram/Models/Live/v1/Metadata.cs | 6 +- Deepgram/Models/Live/v1/ModelInfo.cs | 6 +- .../Models/Live/v1/SpeechStartedResponse.cs | 2 +- .../Models/Live/v1/TranscriptionResponse.cs | 16 +- Deepgram/Models/Live/v1/Word.cs | 8 +- Deepgram/OnPremClient.cs | 2 +- Deepgram/PreRecordedClient.cs | 2 +- Deepgram/SpeakClient.cs | 2 +- clean-up.ps1 | 16 ++ clean-up.sh | 19 ++ .../prerecorded/{ => file}/Prerecorded.csproj | 5 +- examples/prerecorded/{ => file}/Program.cs | 7 +- .../speak/{ => file/hello-world}/Program.cs | 6 +- .../speak/{ => file/hello-world}/Speak.csproj | 2 +- examples/streaming/{ => file}/Program.cs | 16 +- .../streaming/{ => file}/Streaming.csproj | 2 +- examples/streaming/{ => file}/preamble.wav | Bin examples/streaming/microphone/Program.cs | 76 ++++++++ .../streaming/microphone/Streaming.csproj | 20 ++ 51 files changed, 687 insertions(+), 132 deletions(-) create mode 100644 Deepgram.Microphone/Constants.cs create mode 100644 Deepgram.Microphone/Deepgram.Microphone.csproj create mode 100644 Deepgram.Microphone/GlobalUsings.cs create mode 100644 Deepgram.Microphone/Library.cs create mode 100644 Deepgram.Microphone/Microphone.cs create mode 100644 clean-up.ps1 create mode 100644 clean-up.sh rename examples/prerecorded/{ => file}/Prerecorded.csproj (70%) rename examples/prerecorded/{ => file}/Program.cs (70%) rename examples/speak/{ => file/hello-world}/Program.cs (71%) rename examples/speak/{ => file/hello-world}/Speak.csproj (83%) rename examples/streaming/{ => file}/Program.cs (74%) rename examples/streaming/{ => file}/Streaming.csproj (87%) rename examples/streaming/{ => file}/preamble.wav (100%) create mode 100644 examples/streaming/microphone/Program.cs create mode 100644 examples/streaming/microphone/Streaming.csproj diff --git a/Deepgram.Dev.sln b/Deepgram.Dev.sln index 81f9488d..4294d135 100644 --- a/Deepgram.Dev.sln +++ b/Deepgram.Dev.sln @@ -7,11 +7,39 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Deepgram", "Deepgram\Deepgr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Deepgram.Tests", "Deepgram.Tests\Deepgram.Tests.csproj", "{12C80273-08DD-494C-B06D-DFC6D40B1D95}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Streaming", "examples\streaming\Streaming.csproj", "{5DD81E37-D575-476B-9D1C-803A093A4E3C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PreRecorded", "examples\prerecorded\file\PreRecorded.csproj", "{70B63CBA-1130-46D1-A022-6CD31C37C60E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prerecorded", "examples\prerecorded\Prerecorded.csproj", "{70B63CBA-1130-46D1-A022-6CD31C37C60E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speak", "examples\speak\file\hello-world\Speak.csproj", "{C3AA63DB-4555-4BEF-B2DD-89A3B19A265B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speak", "examples\speak\Speak.csproj", "{C3AA63DB-4555-4BEF-B2DD-89A3B19A265B}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{34821A99-3DF4-42CA-B12D-F6D30AB11BFC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "speak", "speak", "{2D46D7C4-2CEE-4EC4-BFD4-A8ACB41A6FB0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "streaming", "streaming", "{B675976B-59A1-4827-9F0E-ADBE70109175}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "analyze", "analyze", "{759F3200-CE05-4164-84A1-61B3FED028A6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "prerecorded", "prerecorded", "{5791AC6B-A202-4DED-8788-CD4451C35755}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "manage", "manage", "{6ADFF8A2-1114-4419-83E3-14B05DF3056E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "advanced", "advanced", "{048DB116-04EF-468E-83F2-DE9E13F873FD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "file", "file", "{CD2B8E2D-9D95-46DE-899A-CD6E49449CA1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "file", "file", "{EAED4363-A298-4511-9941-31512FBA1FAF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "hello-world", "hello-world", "{80C7E9DD-18A8-4A24-AAF9-8BE7484A0D05}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "microphone", "microphone", "{4F2E8A7F-A634-470F-8897-F5F14DBDA2CB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "file", "file", "{9A92C390-7E41-40B6-BF22-6D63B4FB6C64}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Streaming", "examples\streaming\file\Streaming.csproj", "{FD8507CC-EECF-4750-81AF-3CF8E536C007}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Streaming", "examples\streaming\microphone\Streaming.csproj", "{647D3479-6725-45D0-A5F4-51D1B6E9C256}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Deepgram.Microphone", "Deepgram.Microphone\Deepgram.Microphone.csproj", "{1D9621D6-8925-44DF-AE89-1381BF4514E4}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -27,10 +55,6 @@ Global {12C80273-08DD-494C-B06D-DFC6D40B1D95}.Debug|Any CPU.Build.0 = Debug|Any CPU {12C80273-08DD-494C-B06D-DFC6D40B1D95}.Release|Any CPU.ActiveCfg = Release|Any CPU {12C80273-08DD-494C-B06D-DFC6D40B1D95}.Release|Any CPU.Build.0 = Release|Any CPU - {5DD81E37-D575-476B-9D1C-803A093A4E3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5DD81E37-D575-476B-9D1C-803A093A4E3C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5DD81E37-D575-476B-9D1C-803A093A4E3C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5DD81E37-D575-476B-9D1C-803A093A4E3C}.Release|Any CPU.Build.0 = Release|Any CPU {70B63CBA-1130-46D1-A022-6CD31C37C60E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {70B63CBA-1130-46D1-A022-6CD31C37C60E}.Debug|Any CPU.Build.0 = Debug|Any CPU {70B63CBA-1130-46D1-A022-6CD31C37C60E}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -39,10 +63,39 @@ Global {C3AA63DB-4555-4BEF-B2DD-89A3B19A265B}.Debug|Any CPU.Build.0 = Debug|Any CPU {C3AA63DB-4555-4BEF-B2DD-89A3B19A265B}.Release|Any CPU.ActiveCfg = Release|Any CPU {C3AA63DB-4555-4BEF-B2DD-89A3B19A265B}.Release|Any CPU.Build.0 = Release|Any CPU + {FD8507CC-EECF-4750-81AF-3CF8E536C007}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD8507CC-EECF-4750-81AF-3CF8E536C007}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD8507CC-EECF-4750-81AF-3CF8E536C007}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD8507CC-EECF-4750-81AF-3CF8E536C007}.Release|Any CPU.Build.0 = Release|Any CPU + {647D3479-6725-45D0-A5F4-51D1B6E9C256}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {647D3479-6725-45D0-A5F4-51D1B6E9C256}.Debug|Any CPU.Build.0 = Debug|Any CPU + {647D3479-6725-45D0-A5F4-51D1B6E9C256}.Release|Any CPU.ActiveCfg = Release|Any CPU + {647D3479-6725-45D0-A5F4-51D1B6E9C256}.Release|Any CPU.Build.0 = Release|Any CPU + {1D9621D6-8925-44DF-AE89-1381BF4514E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1D9621D6-8925-44DF-AE89-1381BF4514E4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1D9621D6-8925-44DF-AE89-1381BF4514E4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1D9621D6-8925-44DF-AE89-1381BF4514E4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {70B63CBA-1130-46D1-A022-6CD31C37C60E} = {CD2B8E2D-9D95-46DE-899A-CD6E49449CA1} + {C3AA63DB-4555-4BEF-B2DD-89A3B19A265B} = {80C7E9DD-18A8-4A24-AAF9-8BE7484A0D05} + {2D46D7C4-2CEE-4EC4-BFD4-A8ACB41A6FB0} = {34821A99-3DF4-42CA-B12D-F6D30AB11BFC} + {B675976B-59A1-4827-9F0E-ADBE70109175} = {34821A99-3DF4-42CA-B12D-F6D30AB11BFC} + {759F3200-CE05-4164-84A1-61B3FED028A6} = {34821A99-3DF4-42CA-B12D-F6D30AB11BFC} + {5791AC6B-A202-4DED-8788-CD4451C35755} = {34821A99-3DF4-42CA-B12D-F6D30AB11BFC} + {6ADFF8A2-1114-4419-83E3-14B05DF3056E} = {34821A99-3DF4-42CA-B12D-F6D30AB11BFC} + {048DB116-04EF-468E-83F2-DE9E13F873FD} = {34821A99-3DF4-42CA-B12D-F6D30AB11BFC} + {CD2B8E2D-9D95-46DE-899A-CD6E49449CA1} = {5791AC6B-A202-4DED-8788-CD4451C35755} + {EAED4363-A298-4511-9941-31512FBA1FAF} = {2D46D7C4-2CEE-4EC4-BFD4-A8ACB41A6FB0} + {80C7E9DD-18A8-4A24-AAF9-8BE7484A0D05} = {EAED4363-A298-4511-9941-31512FBA1FAF} + {4F2E8A7F-A634-470F-8897-F5F14DBDA2CB} = {B675976B-59A1-4827-9F0E-ADBE70109175} + {9A92C390-7E41-40B6-BF22-6D63B4FB6C64} = {B675976B-59A1-4827-9F0E-ADBE70109175} + {FD8507CC-EECF-4750-81AF-3CF8E536C007} = {9A92C390-7E41-40B6-BF22-6D63B4FB6C64} + {647D3479-6725-45D0-A5F4-51D1B6E9C256} = {4F2E8A7F-A634-470F-8897-F5F14DBDA2CB} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8D4ABC6D-7126-4EE2-9303-43A954616B2A} EndGlobalSection diff --git a/Deepgram.DevBuild.sln b/Deepgram.DevBuild.sln index 353ce42c..b129dff2 100644 --- a/Deepgram.DevBuild.sln +++ b/Deepgram.DevBuild.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 17.0.31912.275 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Deepgram", "Deepgram\Deepgram.csproj", "{1F72D53C-2EF5-4556-A0E6-18D57BF9648B}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Deepgram.Microphone", "Deepgram.Microphone\Deepgram.Microphone.csproj", "{E9374C11-B475-4692-9D46-C2734756CFA3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,10 +17,10 @@ Global {1F72D53C-2EF5-4556-A0E6-18D57BF9648B}.Debug|Any CPU.Build.0 = Debug|Any CPU {1F72D53C-2EF5-4556-A0E6-18D57BF9648B}.Release|Any CPU.ActiveCfg = Release|Any CPU {1F72D53C-2EF5-4556-A0E6-18D57BF9648B}.Release|Any CPU.Build.0 = Release|Any CPU - {ED1EF53E-BA86-44FA-B1C0-484B3864E459}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ED1EF53E-BA86-44FA-B1C0-484B3864E459}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ED1EF53E-BA86-44FA-B1C0-484B3864E459}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ED1EF53E-BA86-44FA-B1C0-484B3864E459}.Release|Any CPU.Build.0 = Release|Any CPU + {E9374C11-B475-4692-9D46-C2734756CFA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E9374C11-B475-4692-9D46-C2734756CFA3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E9374C11-B475-4692-9D46-C2734756CFA3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E9374C11-B475-4692-9D46-C2734756CFA3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Deepgram.Microphone/Constants.cs b/Deepgram.Microphone/Constants.cs new file mode 100644 index 00000000..967c841e --- /dev/null +++ b/Deepgram.Microphone/Constants.cs @@ -0,0 +1,20 @@ +namespace Deepgram.Microphone; + +public static class Defaults +{ + // Default sample rate + public const int RATE = 16000; + + // Number of channels + public const int CHANNELS = 1; + + // Default chunk size + public const int CHUNK_SIZE = 8194; + + // Default input device index + public const SampleFormat SAMPLE_FORMAT = SampleFormat.Int16; + + // Default input device index + public const int DEVICE_INDEX = PortAudio.NoDevice; +} + diff --git a/Deepgram.Microphone/Deepgram.Microphone.csproj b/Deepgram.Microphone/Deepgram.Microphone.csproj new file mode 100644 index 00000000..c8e0a2da --- /dev/null +++ b/Deepgram.Microphone/Deepgram.Microphone.csproj @@ -0,0 +1,125 @@ + + + + net6.0;net7.0;net8.0;netstandard2.0 + enable + enable + latest + + + + + true + Deepgram + Deepgram.Microphone + Deepgram .NET Microphone + Helper Microphone Package + Helper Microphone Package + Deepgram .NET SDK Contributors + 2024 Deepgram + dg_logo.png + False + MIT + https://developers.deepgram.com/sdks-tools/sdks/dotnet-sdk/ + README.md + https://github.com/deepgram/deepgram-dotnet-sdk + speech-to-text,captions,speech-recognition,deepgram,dotnet + True + True + latest + + + + true + Deepgram + Deepgram.Unstable.Microphone.Builds + Deepgram.Microphone - UNSTABLE DEVELOPER BUILDS + UNSTABLE DEVELOPER BUILDS - Deepgram .NET Microphone + UNSTABLE DEVELOPER BUILDS - Deepgram .NET Microphone + Deepgram .NET SDK Contributors + 2024 Deepgram + dg_logo.png + False + MIT + https://developers.deepgram.com/sdks-tools/sdks/dotnet-sdk/ + README.md + https://github.com/deepgram/deepgram-dotnet-sdk + speech-to-text,captions,speech-recognition,deepgram,dotnet + True + True + latest + + + 7 + + + 7 + + + 7 + + + 7 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + \ + + + True + \ + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + \ No newline at end of file diff --git a/Deepgram.Microphone/GlobalUsings.cs b/Deepgram.Microphone/GlobalUsings.cs new file mode 100644 index 00000000..2d24e4d3 --- /dev/null +++ b/Deepgram.Microphone/GlobalUsings.cs @@ -0,0 +1,5 @@ +// Copyright 2024 Deepgram .NET SDK contributors. All Rights Reserved. +// Use of this source code is governed by a MIT license that can be found in the LICENSE file. +// SPDX-License-Identifier: MIT + +global using PortAudioSharp; diff --git a/Deepgram.Microphone/Library.cs b/Deepgram.Microphone/Library.cs new file mode 100644 index 00000000..47ee10a0 --- /dev/null +++ b/Deepgram.Microphone/Library.cs @@ -0,0 +1,19 @@ + +namespace Deepgram.Microphone; + +public class Library +{ + public static void Initialize() + { + // TODO: logging + Console.WriteLine("Portaudio Version: {0}\n\n", PortAudio.VersionInfo.versionText); + PortAudio.Initialize(); + } + + public static void Terminate() + { + // TODO: logging + // Terminate PortAudio + PortAudio.Terminate(); + } +} diff --git a/Deepgram.Microphone/Microphone.cs b/Deepgram.Microphone/Microphone.cs new file mode 100644 index 00000000..3fcb2774 --- /dev/null +++ b/Deepgram.Microphone/Microphone.cs @@ -0,0 +1,177 @@ +// Copyright 2024 Deepgram .NET SDK contributors. All Rights Reserved. +// Use of this source code is governed by a MIT license that can be found in the LICENSE file. +// SPDX-License-Identifier: MIT + +using System.Runtime.InteropServices; + +namespace Deepgram.Microphone; + +/// +/// Implements a Microphone using PortAudio +/// +public class Microphone +{ + private Action _push_callback; + + private int _rate; + private uint _chunk; + private int _channels; + private int _device_index; + private SampleFormat _format; + private bool _isMuted = false; + + private PortAudioSharp.Stream? _stream = null; + private CancellationTokenSource? _exitToken = null; + + /// + /// Constructor for Microphone + /// + public Microphone( + Action push_callback, + int rate = Defaults.RATE, + uint chunkSize = Defaults.CHUNK_SIZE, + int channels = Defaults.CHANNELS, + int device_index = Defaults.DEVICE_INDEX, + SampleFormat format = Defaults.SAMPLE_FORMAT + ) + { + _push_callback = push_callback; + _rate = rate; + _chunk = chunkSize; + _channels = channels; + _device_index = device_index; + _format = format; + } + + // Start begins the listening on the microphone + public bool Start() + { + if (_stream != null) + { + return false; + } + + // reset exit token + _exitToken = new CancellationTokenSource(); + + // Get the device info + if (_device_index == Defaults.DEVICE_INDEX) + { + _device_index = PortAudio.DefaultInputDevice; + if (_device_index == PortAudio.NoDevice) + { + // TODO: logging + Console.WriteLine("No default input device found"); + return false; + } + } + + DeviceInfo info = PortAudio.GetDeviceInfo(_device_index); + + // Set the stream parameters + StreamParameters param = new StreamParameters(); + param.device = _device_index; + param.channelCount = _channels; + param.sampleFormat = _format; + param.suggestedLatency = info.defaultLowInputLatency; + param.hostApiSpecificStreamInfo = IntPtr.Zero; + + // Set the callback + PortAudioSharp.Stream.Callback callback = _callback; + + // Create the stream + _stream = new PortAudioSharp.Stream( + inParams: param, + outParams: null, + sampleRate: _rate, + framesPerBuffer: _chunk, + streamFlags: StreamFlags.ClipOff, + callback: _callback, + userData: IntPtr.Zero + ); + + // Start the stream + _stream.Start(); + return true; + } + + private StreamCallbackResult _callback(nint input, nint output, uint frameCount, ref StreamCallbackTimeInfo timeInfo, StreamCallbackFlags statusFlags, nint userDataPtr) + { + // Check if the input is null + if (input == IntPtr.Zero) + { + // TODO: logging + Console.WriteLine("Input is null"); + return StreamCallbackResult.Continue; + } + + // Check if the exit token is set + if (_exitToken != null && _exitToken.IsCancellationRequested) + { + Console.WriteLine("Exiting!"); + return StreamCallbackResult.Abort; + } + + // copy and send the data + byte[] buf = new byte[frameCount * sizeof(Int16)]; + + if (_isMuted) + { + Console.WriteLine("Muted!"); + buf = new byte[buf.Length]; + } + else + { + Marshal.Copy(input, buf, 0, buf.Length); + } + + // Push the data to the callback + Console.WriteLine("Sending buffer!"); + _push_callback(buf); + + return StreamCallbackResult.Continue; + } + + public void Mute() + { + if (_stream != null) + { + // TODO: logging + return; + } + + // TODO: logging + _isMuted = true; + } + + public void Unmute() + { + if (_stream != null) + { + // TODO: logging + return; + } + + // TODO: logging + _isMuted = false; + } + + public void Stop() + { + // Check if we have a stream + if (_stream == null) + { + // TODO: logging + return; + } + + // signal stop + if (_exitToken != null) + { + _exitToken.Cancel(); + } + + // Stop the stream + _stream.Stop(); + } +} diff --git a/Deepgram.Tests/UnitTests/ClientTests/AbstractRestClientTests.cs b/Deepgram.Tests/UnitTests/ClientTests/AbstractRestClientTests.cs index 177a5d38..5176f1de 100644 --- a/Deepgram.Tests/UnitTests/ClientTests/AbstractRestClientTests.cs +++ b/Deepgram.Tests/UnitTests/ClientTests/AbstractRestClientTests.cs @@ -19,8 +19,8 @@ public class AbstractRestfulClientTests [SetUp] public void Setup() { - _clientOptions = new DeepgramClientOptions(); _apiKey = new Faker().Random.Guid().ToString(); + _clientOptions = new DeepgramClientOptions(_apiKey); } @@ -152,7 +152,6 @@ await client.Invoking(async y => await y.DeleteAsync($"{Default .Should().ThrowAsync(); } - [Test] public void DeleteAsync_TResponse_Should_Throws_HttpRequestException_On_UnsuccessfulResponse() { diff --git a/Deepgram.Tests/UnitTests/ClientTests/AnalyzeClientTests.cs b/Deepgram.Tests/UnitTests/ClientTests/AnalyzeClientTests.cs index e848f3f9..a9430b72 100644 --- a/Deepgram.Tests/UnitTests/ClientTests/AnalyzeClientTests.cs +++ b/Deepgram.Tests/UnitTests/ClientTests/AnalyzeClientTests.cs @@ -16,8 +16,8 @@ public class AnalyzeClientTests [SetUp] public void Setup() { - _options = new DeepgramClientOptions(); _apiKey = new Faker().Random.Guid().ToString(); + _options = new DeepgramClientOptions(_apiKey); } [Test] diff --git a/Deepgram.Tests/UnitTests/ClientTests/LiveClientTests.cs b/Deepgram.Tests/UnitTests/ClientTests/LiveClientTests.cs index 0be41896..626066d1 100644 --- a/Deepgram.Tests/UnitTests/ClientTests/LiveClientTests.cs +++ b/Deepgram.Tests/UnitTests/ClientTests/LiveClientTests.cs @@ -21,7 +21,7 @@ public void Setup() { var apiKey = new Faker().Random.Guid().ToString(); // will set up with base address set to - api.deepgram.com - _options = new DeepgramClientOptions(); + _options = new DeepgramClientOptions(apiKey); _webSocketReceiveResult = new WebSocketReceiveResult(1, WebSocketMessageType.Text, true); _liveClient = new LiveClient(apiKey, _options); } diff --git a/Deepgram.Tests/UnitTests/ClientTests/ManageClientTest.cs b/Deepgram.Tests/UnitTests/ClientTests/ManageClientTest.cs index e3d80621..ae9168f0 100644 --- a/Deepgram.Tests/UnitTests/ClientTests/ManageClientTest.cs +++ b/Deepgram.Tests/UnitTests/ClientTests/ManageClientTest.cs @@ -18,9 +18,9 @@ public class ManageClientTest [SetUp] public void Setup() { - _options = new DeepgramClientOptions(); - _projectId = new Faker().Random.Guid().ToString(); _apiKey = new Faker().Random.Guid().ToString(); + _options = new DeepgramClientOptions(_apiKey); + _projectId = new Faker().Random.Guid().ToString(); } #region Projects diff --git a/Deepgram.Tests/UnitTests/ClientTests/OnPremClientTests.cs b/Deepgram.Tests/UnitTests/ClientTests/OnPremClientTests.cs index 45eb1885..31cdd63d 100644 --- a/Deepgram.Tests/UnitTests/ClientTests/OnPremClientTests.cs +++ b/Deepgram.Tests/UnitTests/ClientTests/OnPremClientTests.cs @@ -16,9 +16,9 @@ public class OnPremClientTests [SetUp] public void Setup() { - _options = new DeepgramClientOptions(); - _projectId = new Faker().Random.Guid().ToString(); _apiKey = new Faker().Random.Guid().ToString(); + _options = new DeepgramClientOptions(_apiKey); + _projectId = new Faker().Random.Guid().ToString(); } [Test] diff --git a/Deepgram.Tests/UnitTests/ClientTests/PrerecordedClientTests.cs b/Deepgram.Tests/UnitTests/ClientTests/PrerecordedClientTests.cs index dfde6521..f72170b7 100644 --- a/Deepgram.Tests/UnitTests/ClientTests/PrerecordedClientTests.cs +++ b/Deepgram.Tests/UnitTests/ClientTests/PrerecordedClientTests.cs @@ -16,8 +16,8 @@ public class PreRecordedClientTests [SetUp] public void Setup() { - _options = new DeepgramClientOptions(); _apiKey = new Faker().Random.Guid().ToString(); + _options = new DeepgramClientOptions(_apiKey); } [Test] diff --git a/Deepgram.Tests/UnitTests/ClientTests/SpeakClientTests.cs b/Deepgram.Tests/UnitTests/ClientTests/SpeakClientTests.cs index 25cac224..8f735bbf 100644 --- a/Deepgram.Tests/UnitTests/ClientTests/SpeakClientTests.cs +++ b/Deepgram.Tests/UnitTests/ClientTests/SpeakClientTests.cs @@ -16,8 +16,8 @@ public class SpeakClientTests [SetUp] public void Setup() { - _options = new DeepgramClientOptions(); _apiKey = new Faker().Random.Guid().ToString(); + _options = new DeepgramClientOptions(_apiKey); } //[Test] diff --git a/Deepgram.Tests/UnitTests/HttpExtensionsTests/HttpClientExtensionTests.cs b/Deepgram.Tests/UnitTests/HttpExtensionsTests/HttpClientExtensionTests.cs index 63d8e70a..6debb69a 100644 --- a/Deepgram.Tests/UnitTests/HttpExtensionsTests/HttpClientExtensionTests.cs +++ b/Deepgram.Tests/UnitTests/HttpExtensionsTests/HttpClientExtensionTests.cs @@ -32,7 +32,7 @@ public void Should_Return_HttpClient_With_Default_BaseAddress() _httpClientFactory.CreateClient().Returns(httpClient); //Act - var SUT = HttpClientFactory.ConfigureDeepgram(httpClient, _apiKey, _clientOptions); + var SUT = HttpClientFactory.ConfigureDeepgram(httpClient, _clientOptions); //Assert using (new AssertionScope()) @@ -53,7 +53,7 @@ public void Should_Return_HttpClient_With_Custom_BaseAddress() _httpClientFactory.CreateClient().Returns(httpClient); //Act - var SUT = HttpClientFactory.ConfigureDeepgram(httpClient, _apiKey, _clientOptions); + var SUT = HttpClientFactory.ConfigureDeepgram(httpClient, _clientOptions); //Assert using (new AssertionScope()) @@ -72,7 +72,7 @@ public void Should_Return_HttpClient_With_Default_BaseAddress_And_Custom_Headers _httpClientFactory.CreateClient().Returns(httpClient); //Act - var SUT = HttpClientFactory.ConfigureDeepgram(httpClient, _apiKey, _clientOptions); + var SUT = HttpClientFactory.ConfigureDeepgram(httpClient, _clientOptions); //Assert using (new AssertionScope()) @@ -99,7 +99,7 @@ public void Should_Return_HttpClient_With_Custom_BaseAddress_And_Custom_Headers( //Act - var SUT = HttpClientFactory.ConfigureDeepgram(httpClient, _apiKey, _clientOptions); + var SUT = HttpClientFactory.ConfigureDeepgram(httpClient, _clientOptions); //Assert using (new AssertionScope()) @@ -123,7 +123,7 @@ public void Should_Return_HttpClient_With_Predefined_values() _httpClientFactory.CreateClient().Returns(httpClient); //Act - var SUT = HttpClientFactory.ConfigureDeepgram(httpClient, _apiKey, _clientOptions); + var SUT = HttpClientFactory.ConfigureDeepgram(httpClient, _clientOptions); //Assert using (new AssertionScope()) diff --git a/Deepgram.sln b/Deepgram.sln index 491895af..50521863 100644 --- a/Deepgram.sln +++ b/Deepgram.sln @@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Deepgram", "Deepgram\Deepgr EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Deepgram.Tests", "Deepgram.Tests\Deepgram.Tests.csproj", "{12C80273-08DD-494C-B06D-DFC6D40B1D95}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Deepgram.Microphone", "Deepgram.Microphone\Deepgram.Microphone.csproj", "{C8E58048-EC70-4549-AC67-F68C1D4FA4D8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {12C80273-08DD-494C-B06D-DFC6D40B1D95}.Debug|Any CPU.Build.0 = Debug|Any CPU {12C80273-08DD-494C-B06D-DFC6D40B1D95}.Release|Any CPU.ActiveCfg = Release|Any CPU {12C80273-08DD-494C-B06D-DFC6D40B1D95}.Release|Any CPU.Build.0 = Release|Any CPU + {C8E58048-EC70-4549-AC67-F68C1D4FA4D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C8E58048-EC70-4549-AC67-F68C1D4FA4D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C8E58048-EC70-4549-AC67-F68C1D4FA4D8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C8E58048-EC70-4549-AC67-F68C1D4FA4D8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Deepgram/Abstractions/AbstractRestClient.cs b/Deepgram/Abstractions/AbstractRestClient.cs index e8926a86..6ab8619e 100644 --- a/Deepgram/Abstractions/AbstractRestClient.cs +++ b/Deepgram/Abstractions/AbstractRestClient.cs @@ -31,9 +31,9 @@ public abstract class AbstractRestClient internal AbstractRestClient(string apiKey = "", DeepgramClientOptions? options = null) { - options ??= new DeepgramClientOptions(); + options ??= new DeepgramClientOptions(apiKey); _httpClient = HttpClientFactory.Create(); - _httpClient = HttpClientFactory.ConfigureDeepgram(_httpClient, apiKey, options); + _httpClient = HttpClientFactory.ConfigureDeepgram(_httpClient, options); _options = options; } diff --git a/Deepgram/AnalyzeClient.cs b/Deepgram/AnalyzeClient.cs index 3b6ee561..94eaa456 100644 --- a/Deepgram/AnalyzeClient.cs +++ b/Deepgram/AnalyzeClient.cs @@ -12,7 +12,7 @@ namespace Deepgram; /// public class AnalyzeClient : Client { - public AnalyzeClient(string apiKey, DeepgramClientOptions? deepgramClientOptions = null) : base(apiKey, deepgramClientOptions) + public AnalyzeClient(string apiKey = "", DeepgramClientOptions? deepgramClientOptions = null) : base(apiKey, deepgramClientOptions) { } } diff --git a/Deepgram/Clients/Analyze/v1/Client.cs b/Deepgram/Clients/Analyze/v1/Client.cs index 931a54ca..c2840a16 100644 --- a/Deepgram/Clients/Analyze/v1/Client.cs +++ b/Deepgram/Clients/Analyze/v1/Client.cs @@ -12,7 +12,7 @@ namespace Deepgram.Clients.Analyze.v1; /// /// Required DeepgramApiKey /// for HttpClient Configuration -public class Client(string apiKey, DeepgramClientOptions? deepgramClientOptions = null) +public class Client(string apiKey = "", DeepgramClientOptions? deepgramClientOptions = null) : AbstractRestClient(apiKey, deepgramClientOptions) { #region NoneCallBacks diff --git a/Deepgram/Clients/Live/v1/Client.cs b/Deepgram/Clients/Live/v1/Client.cs index a48a80cc..8abb53bd 100644 --- a/Deepgram/Clients/Live/v1/Client.cs +++ b/Deepgram/Clients/Live/v1/Client.cs @@ -15,7 +15,6 @@ public class Client : IDisposable #region Fields internal ILogger logger => LogProvider.GetLogger(this.GetType().Name); internal readonly DeepgramClientOptions _deepgramClientOptions; - internal readonly string _apiKey; internal ClientWebSocket? _clientWebSocket; internal CancellationTokenSource _cancellationTokenSource; internal bool _isDisposed; @@ -25,28 +24,8 @@ public class Client : IDisposable /// for HttpClient Configuration public Client(string apiKey = "", DeepgramClientOptions? options = null) { - options ??= new DeepgramClientOptions(); - - // user provided takes precedence - if (string.IsNullOrWhiteSpace(apiKey)) - { - // then try the environment variable - // TODO: log - apiKey = Environment.GetEnvironmentVariable(variable: Defaults.DEEPGRAM_API_KEY) ?? ""; - if (string.IsNullOrEmpty(apiKey)) - { - // TODO: log - } - } - if (!options.OnPrem && string.IsNullOrEmpty(apiKey)) - { - // TODO: log - throw new ArgumentException("Deepgram API Key is invalid"); - } - - // housekeeping + options ??= new DeepgramClientOptions(apiKey); _deepgramClientOptions = options; - _apiKey = apiKey; } #region Subscribe Events @@ -63,19 +42,26 @@ public Client(string apiKey = "", DeepgramClientOptions? options = null) /// /// Options to use when transcribing audio /// The task object representing the asynchronous operation. - public async Task Connect(LiveSchema options, CancellationTokenSource? cancellationToken = null, Dictionary? addons = null) + public async Task Connect(LiveSchema options, CancellationTokenSource? cancellationToken = null, Dictionary? addons = null, Dictionary? headers = null) { // create client _clientWebSocket = new ClientWebSocket(); // set headers - _clientWebSocket.Options.SetRequestHeader("Authorization", $"token {_apiKey}"); + _clientWebSocket.Options.SetRequestHeader("Authorization", $"token {_deepgramClientOptions.ApiKey}"); if (_deepgramClientOptions.Headers is not null) { foreach (var header in _deepgramClientOptions.Headers) { _clientWebSocket.Options.SetRequestHeader(header.Key, header.Value); } } + if (headers is not null) + { + foreach (var header in headers) + { + _clientWebSocket.Options.SetRequestHeader(header.Key, header.Value); + } + } // cancelation token if (cancellationToken != null) @@ -88,7 +74,9 @@ public async Task Connect(LiveSchema options, CancellationTokenSource? cancellat try { - await _clientWebSocket.ConnectAsync(GetUri(_deepgramClientOptions, options, addons), _cancellationTokenSource.Token).ConfigureAwait(false); + var _uri = GetUri(_deepgramClientOptions, options, addons); + Console.WriteLine(_uri); // TODO: logging + await _clientWebSocket.ConnectAsync(_uri, _cancellationTokenSource.Token).ConfigureAwait(false); StartSenderBackgroundThread(); StartReceiverBackgroundThread(); } diff --git a/Deepgram/Clients/Manage/v1/Client.cs b/Deepgram/Clients/Manage/v1/Client.cs index 82eb75b6..d8f80756 100644 --- a/Deepgram/Clients/Manage/v1/Client.cs +++ b/Deepgram/Clients/Manage/v1/Client.cs @@ -12,7 +12,7 @@ namespace Deepgram.Clients.Manage.v1; /// /// Required DeepgramApiKey /// for HttpClient Configuration -public class Client(string apiKey, DeepgramClientOptions? deepgramClientOptions = null) +public class Client(string apiKey = "", DeepgramClientOptions? deepgramClientOptions = null) : AbstractRestClient(apiKey, deepgramClientOptions) { #region Projects diff --git a/Deepgram/Clients/OnPrem/v1/Client.cs b/Deepgram/Clients/OnPrem/v1/Client.cs index e012df0b..5fb45fa4 100644 --- a/Deepgram/Clients/OnPrem/v1/Client.cs +++ b/Deepgram/Clients/OnPrem/v1/Client.cs @@ -12,7 +12,7 @@ namespace Deepgram.Clients.OnPrem.v1; /// /// Required DeepgramApiKey /// for HttpClient Configuration -public class Client(string apiKey, DeepgramClientOptions? deepgramClientOptions = null) +public class Client(string apiKey = "", DeepgramClientOptions? deepgramClientOptions = null) : AbstractRestClient(apiKey, deepgramClientOptions) { /// diff --git a/Deepgram/Clients/Speak/v1/Client.cs b/Deepgram/Clients/Speak/v1/Client.cs index c00dd1c7..0d53eb6d 100644 --- a/Deepgram/Clients/Speak/v1/Client.cs +++ b/Deepgram/Clients/Speak/v1/Client.cs @@ -12,7 +12,7 @@ namespace Deepgram.Clients.Speak.v1; /// /// Required DeepgramApiKey /// for HttpClient Configuration -public class Client(string apiKey, DeepgramClientOptions? deepgramClientOptions = null) +public class Client(string apiKey = "", DeepgramClientOptions? deepgramClientOptions = null) : AbstractRestClient(apiKey, deepgramClientOptions) { #region NoneCallBacks diff --git a/Deepgram/Constants/Defaults.cs b/Deepgram/Constants/Defaults.cs index cbdf630b..1121ef97 100644 --- a/Deepgram/Constants/Defaults.cs +++ b/Deepgram/Constants/Defaults.cs @@ -6,7 +6,12 @@ namespace Deepgram.Constants; public static class Defaults { - // Deepgram specific consts + // Default URI for the Deepgram API public const string DEFAULT_URI = "api.deepgram.com"; + + // Current supported API version + public const string DEFAULT_API_VERSION = "v1"; + + // Default API key environment variable public const string DEEPGRAM_API_KEY = "DEEPGRAM_API_KEY"; } diff --git a/Deepgram/Deepgram.csproj b/Deepgram/Deepgram.csproj index 9210a53c..39a2c19c 100644 --- a/Deepgram/Deepgram.csproj +++ b/Deepgram/Deepgram.csproj @@ -22,7 +22,7 @@ MIT https://developers.deepgram.com/sdks-tools/sdks/dotnet-sdk/ README.md - https://github.com/deepgram-devs/deepgram-dotnet-sdk + https://github.com/deepgram/deepgram-dotnet-sdk speech-to-text,captions,speech-recognition,deepgram,dotnet True True @@ -43,7 +43,7 @@ MIT https://developers.deepgram.com/sdks-tools/sdks/dotnet-sdk/ README.md - https://github.com/deepgram-devs/deepgram-dotnet-sdk + https://github.com/deepgram/deepgram-dotnet-sdk speech-to-text,captions,speech-recognition,deepgram,dotnet True True diff --git a/Deepgram/Factory/HttpClientFactory.cs b/Deepgram/Factory/HttpClientFactory.cs index d4fa4bfb..ab6386f6 100644 --- a/Deepgram/Factory/HttpClientFactory.cs +++ b/Deepgram/Factory/HttpClientFactory.cs @@ -27,32 +27,15 @@ public static HttpClient Create(string httpId = HTTPCLIENT_NAME) return client; } - internal static HttpClient ConfigureDeepgram(HttpClient client, string apiKey = "", DeepgramClientOptions? options = null) + internal static HttpClient ConfigureDeepgram(HttpClient client, DeepgramClientOptions? options = null) { options ??= new DeepgramClientOptions(); - // user provided takes precedence - if (string.IsNullOrWhiteSpace(apiKey)) - { - // then try the environment variable - // TODO: log - apiKey = Environment.GetEnvironmentVariable(variable: Defaults.DEEPGRAM_API_KEY) ?? ""; - if (string.IsNullOrEmpty(apiKey)) - { - // TODO: log - } - } - if (!options.OnPrem && string.IsNullOrEmpty(apiKey)) - { - // TODO: log - throw new ArgumentException("Deepgram API Key is invalid"); - } - // headers client.DefaultRequestHeaders.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); client.DefaultRequestHeaders.UserAgent.ParseAdd(UserAgentUtil.GetInfo()); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("token", apiKey); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("token", options.ApiKey); if (options.Headers is not null) foreach (var header in options.Headers) diff --git a/Deepgram/LiveClient.cs b/Deepgram/LiveClient.cs index cd45285e..00511337 100644 --- a/Deepgram/LiveClient.cs +++ b/Deepgram/LiveClient.cs @@ -12,7 +12,7 @@ namespace Deepgram; /// public class LiveClient : Client { - public LiveClient(string apiKey, DeepgramClientOptions? deepgramClientOptions = null) : base(apiKey, deepgramClientOptions) + public LiveClient(string apiKey = "", DeepgramClientOptions? deepgramClientOptions = null) : base(apiKey, deepgramClientOptions) { } } diff --git a/Deepgram/ManageClient.cs b/Deepgram/ManageClient.cs index 3d1a4e04..a86b0d93 100644 --- a/Deepgram/ManageClient.cs +++ b/Deepgram/ManageClient.cs @@ -12,7 +12,7 @@ namespace Deepgram; /// public class ManageClient : Client { - public ManageClient(string apiKey, DeepgramClientOptions? deepgramClientOptions = null) : base(apiKey, deepgramClientOptions) + public ManageClient(string apiKey = "", DeepgramClientOptions? deepgramClientOptions = null) : base(apiKey, deepgramClientOptions) { } } diff --git a/Deepgram/Models/Authenticate/v1/DeepgramClientOptions.cs b/Deepgram/Models/Authenticate/v1/DeepgramClientOptions.cs index a8464482..8791baad 100644 --- a/Deepgram/Models/Authenticate/v1/DeepgramClientOptions.cs +++ b/Deepgram/Models/Authenticate/v1/DeepgramClientOptions.cs @@ -2,6 +2,8 @@ // Use of this source code is governed by a MIT license that can be found in the LICENSE file. // SPDX-License-Identifier: MIT +using Microsoft.Extensions.Options; + namespace Deepgram.Models.Authenticate.v1; /// @@ -12,6 +14,11 @@ public class DeepgramClientOptions /*****************************/ // General Options /*****************************/ + /// + /// Deepgram API KEY + /// + public string ApiKey { get; set; } + /// /// BaseAddress of the server :defaults to api.deepgram.com /// no need to attach the protocol it will be added internally @@ -24,15 +31,10 @@ public class DeepgramClientOptions public string APIVersion { get; set; } = "v1"; /// - /// Additional headers + /// Global headers to always be added to the request /// public Dictionary Headers { get; set; } - /// - /// Enable when using OnPrem mode - /// - public bool OnPrem { get; set; } = false; - /*****************************/ // Prerecorded /*****************************/ @@ -48,6 +50,10 @@ public class DeepgramClientOptions /*****************************/ // OnPrem /*****************************/ + /// + /// Enable when using OnPrem mode + /// + public bool OnPrem { get; set; } = false; /*****************************/ // Manage @@ -56,4 +62,34 @@ public class DeepgramClientOptions /*****************************/ // Analyze /*****************************/ + + /*****************************/ + // Initialize + /*****************************/ + public DeepgramClientOptions(string? apiKey = null, bool? keepAlive = false, bool? onPrem = false, string? baseAddress = null, Dictionary? headers = null, string? apiVersion = null) + { + ApiKey = apiKey ?? ""; + BaseAddress = baseAddress ?? Defaults.DEFAULT_URI; + APIVersion = apiVersion ?? Defaults.DEFAULT_API_VERSION; + Headers = headers ?? new Dictionary(); + OnPrem = onPrem ?? false; + EnableKeepAlive = keepAlive ?? false; + + // user provided takes precedence + if (string.IsNullOrWhiteSpace(ApiKey)) + { + // then try the environment variable + // TODO: log + ApiKey = Environment.GetEnvironmentVariable(variable: Defaults.DEEPGRAM_API_KEY) ?? ""; + if (string.IsNullOrEmpty(ApiKey)) + { + // TODO: log + } + } + if (!OnPrem && string.IsNullOrEmpty(ApiKey)) + { + // TODO: log + throw new ArgumentException("Deepgram API Key is invalid"); + } + } } diff --git a/Deepgram/Models/Live/v1/Alternative.cs b/Deepgram/Models/Live/v1/Alternative.cs index 10902924..82d0d140 100644 --- a/Deepgram/Models/Live/v1/Alternative.cs +++ b/Deepgram/Models/Live/v1/Alternative.cs @@ -10,16 +10,16 @@ public record Alternative /// Single-string transcript containing what the model hears in this channel of audio. /// [JsonPropertyName("transcript")] - public string? Transcript { get; set; } + public string Transcript { get; set; } /// /// Value between 0 and 1 indicating the model's relative confidence in this transcript. /// [JsonPropertyName("confidence")] - public double? Confidence { get; set; } + public double Confidence { get; set; } /// /// ReadOnly List of objects. /// [JsonPropertyName("words")] - public IReadOnlyList? Words { get; set; } + public IReadOnlyList Words { get; set; } } diff --git a/Deepgram/Models/Live/v1/Channel.cs b/Deepgram/Models/Live/v1/Channel.cs index b1c52dee..c9498ba2 100644 --- a/Deepgram/Models/Live/v1/Channel.cs +++ b/Deepgram/Models/Live/v1/Channel.cs @@ -10,5 +10,5 @@ public record Channel /// ReadOnlyList of objects. /// [JsonPropertyName("alternatives")] - public IReadOnlyList? Alternatives { get; set; } + public IReadOnlyList Alternatives { get; set; } } diff --git a/Deepgram/Models/Live/v1/LiveSchema.cs b/Deepgram/Models/Live/v1/LiveSchema.cs index 44065bc8..91404f9b 100644 --- a/Deepgram/Models/Live/v1/LiveSchema.cs +++ b/Deepgram/Models/Live/v1/LiveSchema.cs @@ -187,13 +187,13 @@ public class LiveSchema /// /// [JsonPropertyName("utterance_end_ms")] - public int? UtteranceEnd { get; set; } + public string? UtteranceEnd { get; set; } /// /// TODO /// [JsonPropertyName("vad_events")] - public int? VadEvents { get; set; } + public bool? VadEvents { get; set; } /// /// Version of the model to use. diff --git a/Deepgram/Models/Live/v1/Metadata.cs b/Deepgram/Models/Live/v1/Metadata.cs index 7c4e293f..d7c93753 100644 --- a/Deepgram/Models/Live/v1/Metadata.cs +++ b/Deepgram/Models/Live/v1/Metadata.cs @@ -10,19 +10,19 @@ public record MetaData /// TODO /// [JsonPropertyName("request_id")] - public string? RequestId { get; set; } + public string RequestId { get; set; } /// /// TODO /// [JsonPropertyName("model_uuid")] - public string? ModelUUID { get; set; } + public string ModelUUID { get; set; } /// /// IReadonlyDictionary of /// [JsonPropertyName("model_info")] - public ModelInfo? ModelInfo { get; set; } + public ModelInfo ModelInfo { get; set; } /// /// Deepgram’s Extra Metadata feature allows you to attach arbitrary key-value pairs to your API requests that are attached to the API response for usage in downstream processing. diff --git a/Deepgram/Models/Live/v1/ModelInfo.cs b/Deepgram/Models/Live/v1/ModelInfo.cs index d1f44da6..c0b4d013 100644 --- a/Deepgram/Models/Live/v1/ModelInfo.cs +++ b/Deepgram/Models/Live/v1/ModelInfo.cs @@ -10,17 +10,17 @@ public record ModelInfo /// Architecture of the model /// [JsonPropertyName("arch")] - public string? Arch { get; set; } + public string Arch { get; set; } /// /// Name of the model /// [JsonPropertyName("name")] - public string? Name { get; set; } + public string Name { get; set; } /// /// Version of the model /// [JsonPropertyName("version")] - public string? Version { get; set; } + public string Version { get; set; } } diff --git a/Deepgram/Models/Live/v1/SpeechStartedResponse.cs b/Deepgram/Models/Live/v1/SpeechStartedResponse.cs index e9a5fa2b..a86f75e2 100644 --- a/Deepgram/Models/Live/v1/SpeechStartedResponse.cs +++ b/Deepgram/Models/Live/v1/SpeechStartedResponse.cs @@ -22,6 +22,6 @@ public record SpeechStartedResponse /// /// TODO /// - [JsonPropertyName("last_word_end")] + [JsonPropertyName("timestamp")] public decimal? Timestamp { get; set; } } diff --git a/Deepgram/Models/Live/v1/TranscriptionResponse.cs b/Deepgram/Models/Live/v1/TranscriptionResponse.cs index cd78b9bc..6e46adf9 100644 --- a/Deepgram/Models/Live/v1/TranscriptionResponse.cs +++ b/Deepgram/Models/Live/v1/TranscriptionResponse.cs @@ -9,50 +9,50 @@ public record TranscriptionResponse /// TODO /// [JsonPropertyName("channel")] - public Channel? Channel { get; set; } + public Channel Channel { get; set; } /// /// TODO /// [JsonPropertyName("channel_index")] - public IReadOnlyList? ChannelIndex { get; set; } + public IReadOnlyList ChannelIndex { get; set; } /// /// TODO /// [JsonPropertyName("duration")] - public double? Duration { get; set; } + public double Duration { get; set; } /// /// TODO /// [JsonPropertyName("is_final")] - public bool? IsFinal { get; set; } + public bool IsFinal { get; set; } /// /// TODO /// [JsonPropertyName("metadata")] - public MetaData? MetaData { get; set; } + public MetaData MetaData { get; set; } /// /// TODO /// [JsonPropertyName("speech_final")] - public bool? SpeechFinal { get; set; } + public bool SpeechFinal { get; set; } /// /// TODO /// [JsonPropertyName("start")] - public decimal? Start { get; set; } + public decimal Start { get; set; } /// /// TODO /// [JsonPropertyName("type")] [JsonConverter(typeof(JsonStringEnumConverter))] - public LiveType? Type { get; set; } = LiveType.Results; + public LiveType Type { get; set; } = LiveType.Results; // TODO: DYV is this needed??? /// diff --git a/Deepgram/Models/Live/v1/Word.cs b/Deepgram/Models/Live/v1/Word.cs index 4bcc5360..3de2c56a 100644 --- a/Deepgram/Models/Live/v1/Word.cs +++ b/Deepgram/Models/Live/v1/Word.cs @@ -10,25 +10,25 @@ public record Word /// Distinct word heard by the model. /// [JsonPropertyName("word")] - public string? HeardWord { get; set; } + public string HeardWord { get; set; } /// /// Offset in seconds from the start of the audio to where the spoken word starts. /// [JsonPropertyName("start")] - public decimal? Start { get; set; } + public decimal Start { get; set; } /// /// Offset in seconds from the start of the audio to where the spoken word ends. /// [JsonPropertyName("end")] - public decimal? End { get; set; } + public decimal End { get; set; } /// /// Value between 0 and 1 indicating the model's relative confidence in this word. /// [JsonPropertyName("confidence")] - public double? Confidence { get; set; } + public double Confidence { get; set; } /// /// Punctuated version of the word diff --git a/Deepgram/OnPremClient.cs b/Deepgram/OnPremClient.cs index 3121eb38..befc0b28 100644 --- a/Deepgram/OnPremClient.cs +++ b/Deepgram/OnPremClient.cs @@ -12,7 +12,7 @@ namespace Deepgram; /// public class OnPremClient : Client { - public OnPremClient(string apiKey, DeepgramClientOptions? deepgramClientOptions = null) : base(apiKey, deepgramClientOptions) + public OnPremClient(string apiKey = "", DeepgramClientOptions? deepgramClientOptions = null) : base(apiKey, deepgramClientOptions) { } } diff --git a/Deepgram/PreRecordedClient.cs b/Deepgram/PreRecordedClient.cs index f1702171..31bcc65e 100644 --- a/Deepgram/PreRecordedClient.cs +++ b/Deepgram/PreRecordedClient.cs @@ -12,7 +12,7 @@ namespace Deepgram; /// public class PreRecordedClient : Client { - public PreRecordedClient(string apiKey, DeepgramClientOptions? deepgramClientOptions = null) : base(apiKey, deepgramClientOptions) + public PreRecordedClient(string apiKey = "", DeepgramClientOptions? deepgramClientOptions = null) : base(apiKey, deepgramClientOptions) { } } diff --git a/Deepgram/SpeakClient.cs b/Deepgram/SpeakClient.cs index 5588fa3e..b7efa7f8 100644 --- a/Deepgram/SpeakClient.cs +++ b/Deepgram/SpeakClient.cs @@ -12,7 +12,7 @@ namespace Deepgram; /// public class SpeakClient : Client { - public SpeakClient(string apiKey, DeepgramClientOptions? deepgramClientOptions = null) : base(apiKey, deepgramClientOptions) + public SpeakClient(string apiKey = "", DeepgramClientOptions? deepgramClientOptions = null) : base(apiKey, deepgramClientOptions) { } } diff --git a/clean-up.ps1 b/clean-up.ps1 new file mode 100644 index 00000000..59ac4527 --- /dev/null +++ b/clean-up.ps1 @@ -0,0 +1,16 @@ +Write-Output "Cleaning up the environment" + +# Deepgram +# Remove-Item -Recurse -Force +Remove-Item -Recurse -Force -LiteralPath "./.vs" +Remove-Item -Recurse -Force -LiteralPath "./dist" +Remove-Item -Recurse -Force -LiteralPath "./Deepgram/obj" +Remove-Item -Recurse -Force -LiteralPath "./Deepgram/bin" + +#Deepgram.Tests +Remove-Item -Recurse -Force -LiteralPath "./Deepgram.Tests/bin" +Remove-Item -Recurse -Force -LiteralPath "./Deepgram.Tests/obj" + +#Deepgram.Microphone +Remove-Item -Recurse -Force -LiteralPath "./Deepgram.Microphone/bin" +Remove-Item -Recurse -Force -LiteralPath "./Deepgram.Microphone/obj" diff --git a/clean-up.sh b/clean-up.sh new file mode 100644 index 00000000..280d2f5e --- /dev/null +++ b/clean-up.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# set -o errexit +set -o nounset +set -o pipefail +set -o xtrace + +rm -rf ./.vs +rm -rf ./dist +rm -rf ./Deepgram/obj +rm -rf ./Deepgram/bin + +# Deepgram.Tests +rm -rf ./Deepgram.Tests/bin +rm -rf ./Deepgram.Tests/obj + +# Deepgram.Microphone +rm -rf ./Deepgram.Microphone/bin +rm -rf ./Deepgram.Microphone/obj \ No newline at end of file diff --git a/examples/prerecorded/Prerecorded.csproj b/examples/prerecorded/file/Prerecorded.csproj similarity index 70% rename from examples/prerecorded/Prerecorded.csproj rename to examples/prerecorded/file/Prerecorded.csproj index 0dbe2714..4721fb83 100644 --- a/examples/prerecorded/Prerecorded.csproj +++ b/examples/prerecorded/file/Prerecorded.csproj @@ -12,8 +12,11 @@ - + + + + diff --git a/examples/prerecorded/Program.cs b/examples/prerecorded/file/Program.cs similarity index 70% rename from examples/prerecorded/Program.cs rename to examples/prerecorded/file/Program.cs index 3ca9d98f..f2876fac 100644 --- a/examples/prerecorded/Program.cs +++ b/examples/prerecorded/file/Program.cs @@ -2,15 +2,14 @@ using Deepgram.Models.PreRecorded.v1; using System.Text.Json; -namespace SampleApp +namespace PreRecorded { class Program { static async Task Main(string[] args) { - // Replace "REPLACE-WITH-YOUR-API-KEY" with your actual Deepgram API key - var apiKey = "REPLACE-WITH-YOUR-API-KEY"; - var deepgramClient = new PreRecordedClient(apiKey); + // Set "DEEPGRAM_API_KEY" environment variable to your Deepgram API Key + var deepgramClient = new PreRecordedClient(); var response = await deepgramClient.TranscribeUrl( new UrlSource("https://static.deepgram.com/examples/Bueller-Life-moves-pretty-fast.wav"), diff --git a/examples/speak/Program.cs b/examples/speak/file/hello-world/Program.cs similarity index 71% rename from examples/speak/Program.cs rename to examples/speak/file/hello-world/Program.cs index 1e6ef572..c97a21af 100644 --- a/examples/speak/Program.cs +++ b/examples/speak/file/hello-world/Program.cs @@ -1,7 +1,6 @@ using Deepgram; using Deepgram.Models.Speak.v1; using System.Text.Json; -using System.Text.RegularExpressions; namespace SampleApp { @@ -9,9 +8,8 @@ class Program { static async Task Main(string[] args) { - // Replace "REPLACE-WITH-YOUR-API-KEY" with your actual Deepgram API key - var apiKey = "REPLACE-WITH-YOUR-API-KEY"; - var deepgramClient = new SpeakClient(apiKey); + // Set "DEEPGRAM_API_KEY" environment variable to your Deepgram API Key + var deepgramClient = new SpeakClient(); var response = await deepgramClient.ToFile( new TextSource("Hello World!"), diff --git a/examples/speak/Speak.csproj b/examples/speak/file/hello-world/Speak.csproj similarity index 83% rename from examples/speak/Speak.csproj rename to examples/speak/file/hello-world/Speak.csproj index 105d5060..87509ec4 100644 --- a/examples/speak/Speak.csproj +++ b/examples/speak/file/hello-world/Speak.csproj @@ -12,7 +12,7 @@ - + diff --git a/examples/streaming/Program.cs b/examples/streaming/file/Program.cs similarity index 74% rename from examples/streaming/Program.cs rename to examples/streaming/file/Program.cs index cc105d48..8bdccd25 100644 --- a/examples/streaming/Program.cs +++ b/examples/streaming/file/Program.cs @@ -13,16 +13,22 @@ class Program { static async Task Main(string[] args) { - // Replace "REPLACE-WITH-YOUR-API-KEY" with your actual Deepgram API key - var apiKey = "REPLACE-WITH-YOUR-API-KEY"; - var liveClient = new LiveClient(apiKey); + // Set "DEEPGRAM_API_KEY" environment variable to your Deepgram API Key + var liveClient = new LiveClient(); // Subscribe to the EventResponseReceived event liveClient.EventResponseReceived += (sender, e) => { if (e.Response.Transcription != null) { - Console.WriteLine("Transcription received: " + JsonSerializer.Serialize(e.Response.Transcription)); + if (e.Response.Transcription.Channel.Alternatives[0].Transcript == "") + { + Console.WriteLine("Empty transcription received."); + return; + } + + // Console.WriteLine("Transcription received: " + JsonSerializer.Serialize(e.Response.Transcription)); + Console.WriteLine($"Speaker: {e.Response.Transcription.Channel.Alternatives[0].Transcript}"); } else if (e.Response.SpeechStarted != null) { @@ -54,7 +60,7 @@ static async Task Main(string[] args) liveClient.Send(audioData); // Wait for a while to receive responses - await Task.Delay(10000); + await Task.Delay(45000); // Stop the connection await liveClient.Stop(); diff --git a/examples/streaming/Streaming.csproj b/examples/streaming/file/Streaming.csproj similarity index 87% rename from examples/streaming/Streaming.csproj rename to examples/streaming/file/Streaming.csproj index aed0a953..f02f97b4 100644 --- a/examples/streaming/Streaming.csproj +++ b/examples/streaming/file/Streaming.csproj @@ -8,7 +8,7 @@ - + diff --git a/examples/streaming/preamble.wav b/examples/streaming/file/preamble.wav similarity index 100% rename from examples/streaming/preamble.wav rename to examples/streaming/file/preamble.wav diff --git a/examples/streaming/microphone/Program.cs b/examples/streaming/microphone/Program.cs new file mode 100644 index 00000000..d23e9a51 --- /dev/null +++ b/examples/streaming/microphone/Program.cs @@ -0,0 +1,76 @@ +using Deepgram.Models.Live.v1; +using Deepgram.Microphone; +using System.Text.Json; + +namespace SampleApp +{ + class Program + { + static async Task Main(string[] args) + { + Library.Initialize(); + + // Set "DEEPGRAM_API_KEY" environment variable to your Deepgram API Key + var liveClient = new LiveClient(); + + // Subscribe to the EventResponseReceived event + liveClient.EventResponseReceived += (sender, e) => + { + if (e.Response.Transcription != null) + { + if (e.Response.Transcription.Channel.Alternatives[0].Transcript == "") + { + Console.WriteLine("Empty transcription received."); + return; + } + + // Console.WriteLine("Transcription received: " + JsonSerializer.Serialize(e.Response.Transcription)); + Console.WriteLine($"Speaker: {e.Response.Transcription.Channel.Alternatives[0].Transcript}"); + } + else if (e.Response.SpeechStarted != null) + { + Console.WriteLine("SpeechStarted received: " + JsonSerializer.Serialize(e.Response.SpeechStarted)); + } + else if (e.Response.UtteranceEnd != null) + { + Console.WriteLine("UtteranceEnd received: " + JsonSerializer.Serialize(e.Response.UtteranceEnd)); + } + else if (e.Response.MetaData != null) + { + Console.WriteLine("Metadata received: " + JsonSerializer.Serialize(e.Response.MetaData)); + } + else if (e.Response.Error != null) + { + Console.WriteLine("Error: " + JsonSerializer.Serialize(e.Response.Error.Message)); + } + }; + + // Start the connection + var liveSchema = new LiveSchema() + { + Model = "nova-2", + Encoding = "linear16", + SampleRate = 16000, + InterimResults = true, + UtteranceEnd = "1000", + VadEvents = true, + }; + await liveClient.Connect(liveSchema); + + // Microphone streaming + var microphone = new Microphone(liveClient.Send); + microphone.Start(); + + Thread.Sleep(3600000); + + // Stop the connection + await liveClient.Stop(); + + // Dispose the client + liveClient.Dispose(); + + // Terminate PortAudio + Library.Terminate(); + } + } +} diff --git a/examples/streaming/microphone/Streaming.csproj b/examples/streaming/microphone/Streaming.csproj new file mode 100644 index 00000000..a7cf3923 --- /dev/null +++ b/examples/streaming/microphone/Streaming.csproj @@ -0,0 +1,20 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + + + + + +