diff --git a/Deepgram.Dev.sln b/Deepgram.Dev.sln index 81f9488d..fd6ca7c9 100644 --- a/Deepgram.Dev.sln +++ b/Deepgram.Dev.sln @@ -7,11 +7,33 @@ 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("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Streaming", "examples\streaming\file\Streaming.csproj", "{FD8507CC-EECF-4750-81AF-3CF8E536C007}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Deepgram.Microphone", "Deepgram.Microphone\Deepgram.Microphone.csproj", "{1D9621D6-8925-44DF-AE89-1381BF4514E4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{C673DFD1-528A-4BAE-94E6-02EF058AC363}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "speak", "speak", "{E2E3000D-FBBA-450E-A4E0-3542B38ADAFD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "prerecorded", "prerecorded", "{77ACBABB-CF6B-4929-957C-480E29646DFD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "streaming", "streaming", "{132EEE99-6194-477A-9416-D7EF173C17FD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "file", "file", "{5E75479B-B84A-4386-9D3E-69CFB30B24C6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "microphone", "microphone", "{4D6C28C1-4D3F-4CFC-AF76-A389F6B00EC2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "file", "file", "{50BA802D-603E-4BD2-9A3E-AFDABC3AF43C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "hello-world", "hello-world", "{AE6FFA55-DD91-4BC1-AFDF-96F64B5221CD}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "url", "url", "{0414D1CF-79FB-4C5B-BF2B-88C3C1AA4C32}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Streaming", "examples\streaming\microphone\Streaming.csproj", "{74335799-3B43-432C-ACD9-FBF2AB32A64A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -27,10 +49,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 +57,36 @@ 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 + {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 + {74335799-3B43-432C-ACD9-FBF2AB32A64A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {74335799-3B43-432C-ACD9-FBF2AB32A64A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {74335799-3B43-432C-ACD9-FBF2AB32A64A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {74335799-3B43-432C-ACD9-FBF2AB32A64A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {70B63CBA-1130-46D1-A022-6CD31C37C60E} = {0414D1CF-79FB-4C5B-BF2B-88C3C1AA4C32} + {C3AA63DB-4555-4BEF-B2DD-89A3B19A265B} = {AE6FFA55-DD91-4BC1-AFDF-96F64B5221CD} + {FD8507CC-EECF-4750-81AF-3CF8E536C007} = {5E75479B-B84A-4386-9D3E-69CFB30B24C6} + {E2E3000D-FBBA-450E-A4E0-3542B38ADAFD} = {C673DFD1-528A-4BAE-94E6-02EF058AC363} + {77ACBABB-CF6B-4929-957C-480E29646DFD} = {C673DFD1-528A-4BAE-94E6-02EF058AC363} + {132EEE99-6194-477A-9416-D7EF173C17FD} = {C673DFD1-528A-4BAE-94E6-02EF058AC363} + {5E75479B-B84A-4386-9D3E-69CFB30B24C6} = {132EEE99-6194-477A-9416-D7EF173C17FD} + {4D6C28C1-4D3F-4CFC-AF76-A389F6B00EC2} = {132EEE99-6194-477A-9416-D7EF173C17FD} + {50BA802D-603E-4BD2-9A3E-AFDABC3AF43C} = {E2E3000D-FBBA-450E-A4E0-3542B38ADAFD} + {AE6FFA55-DD91-4BC1-AFDF-96F64B5221CD} = {50BA802D-603E-4BD2-9A3E-AFDABC3AF43C} + {0414D1CF-79FB-4C5B-BF2B-88C3C1AA4C32} = {77ACBABB-CF6B-4929-957C-480E29646DFD} + {74335799-3B43-432C-ACD9-FBF2AB32A64A} = {4D6C28C1-4D3F-4CFC-AF76-A389F6B00EC2} + 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/Fakes/ConcreteRestClient.cs b/Deepgram.Tests/Fakes/ConcreteRestClient.cs index 172eabb4..8c57fdd3 100644 --- a/Deepgram.Tests/Fakes/ConcreteRestClient.cs +++ b/Deepgram.Tests/Fakes/ConcreteRestClient.cs @@ -6,7 +6,7 @@ namespace Deepgram.Tests.Fakes; -public class ConcreteRestClient(string apiKey, DeepgramClientOptions? deepgramClientOptions) +public class ConcreteRestClient(string apiKey, DeepgramHttpClientOptions? deepgramClientOptions) : AbstractRestClient(apiKey, deepgramClientOptions) { } diff --git a/Deepgram.Tests/UnitTests/ClientTests/AbstractRestClientTests.cs b/Deepgram.Tests/UnitTests/ClientTests/AbstractRestClientTests.cs index 177a5d38..2a99c127 100644 --- a/Deepgram.Tests/UnitTests/ClientTests/AbstractRestClientTests.cs +++ b/Deepgram.Tests/UnitTests/ClientTests/AbstractRestClientTests.cs @@ -11,19 +11,16 @@ namespace Deepgram.Tests.UnitTests.ClientTests; public class AbstractRestfulClientTests { - - - DeepgramClientOptions _clientOptions; + DeepgramHttpClientOptions _clientOptions; string _apiKey; [SetUp] public void Setup() { - _clientOptions = new DeepgramClientOptions(); _apiKey = new Faker().Random.Guid().ToString(); + _clientOptions = new DeepgramHttpClientOptions(_apiKey, null, null, true); } - [Test] public void GetAsync_Should_Throws_HttpRequestException_On_UnsuccessfulResponse() { @@ -152,7 +149,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..396cc75a 100644 --- a/Deepgram.Tests/UnitTests/ClientTests/AnalyzeClientTests.cs +++ b/Deepgram.Tests/UnitTests/ClientTests/AnalyzeClientTests.cs @@ -10,14 +10,14 @@ namespace Deepgram.Tests.UnitTests.ClientTests; public class AnalyzeClientTests { - DeepgramClientOptions _options; + DeepgramHttpClientOptions _options; string _apiKey; [SetUp] public void Setup() { - _options = new DeepgramClientOptions(); _apiKey = new Faker().Random.Guid().ToString(); + _options = new DeepgramHttpClientOptions(_apiKey, null, null, true); } [Test] diff --git a/Deepgram.Tests/UnitTests/ClientTests/LiveClientTests.cs b/Deepgram.Tests/UnitTests/ClientTests/LiveClientTests.cs index 0be41896..7ee3a941 100644 --- a/Deepgram.Tests/UnitTests/ClientTests/LiveClientTests.cs +++ b/Deepgram.Tests/UnitTests/ClientTests/LiveClientTests.cs @@ -1,175 +1,139 @@ -// Copyright 2021-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.Net.WebSockets; - -using Deepgram.Models.Authenticate.v1; -using Deepgram.Models.Live.v1; -using Deepgram.Clients.Live.v1; - -namespace Deepgram.Tests.UnitTests.ClientTests; - -public class LiveClientTests -{ - DeepgramClientOptions _options; - WebSocketReceiveResult _webSocketReceiveResult; - LiveClient _liveClient; - - [SetUp] - public void Setup() - { - var apiKey = new Faker().Random.Guid().ToString(); - // will set up with base address set to - api.deepgram.com - _options = new DeepgramClientOptions(); - _webSocketReceiveResult = new WebSocketReceiveResult(1, WebSocketMessageType.Text, true); - _liveClient = new LiveClient(apiKey, _options); - } - - [TearDown] - public void Teardown() - { - if (_liveClient != null) - _liveClient.Dispose(); - } - - [Test] - public void ProcessDataReceived_Should_Raise_TranscriptReceived_Event_When_Response_Contains_Type_TranscriptionResponse() - { - // Input and Output - var liveTranscriptionResponse = new AutoFaker().Generate(); - - // ensure the right type is set for testing - liveTranscriptionResponse.Type = LiveType.Results; - var json = JsonSerializer.Serialize(liveTranscriptionResponse); - var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(json)); - - // Eventing - ResponseEventArgs? eventArgs = null; - _liveClient.EventResponseReceived += (sender, args) => eventArgs = args; - - //Act - _liveClient.ProcessDataReceived(_webSocketReceiveResult, memoryStream); - Task.Delay(5000); - - //Assert - eventArgs.Should().NotBeNull(); - eventArgs!.Response.Transcription.Should().NotBeNull(); - eventArgs.Response.Transcription.Should().BeAssignableTo(); - } - - [Test] - public void ProcessDataReceived_Should_Raise_MetaDataReceived_Event_When_Response_Contains_Type_MetadataResponse() - { - // Input and Output - var liveMetadataResponse = new AutoFaker().Generate(); - - // ensure the right type is set for testing - liveMetadataResponse.Type = LiveType.Metadata; - var json = JsonSerializer.Serialize(liveMetadataResponse); - var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(json)); - - // Eventing - ResponseEventArgs? eventArgs = null; - _liveClient.EventResponseReceived += (sender, args) => eventArgs = args; - - //Act - _liveClient.ProcessDataReceived(_webSocketReceiveResult, memoryStream); - - //Assert - using (new AssertionScope()) - { - eventArgs.Should().NotBeNull(); - eventArgs!.Response.MetaData.Should().NotBeNull(); - eventArgs.Response.MetaData.Should().BeAssignableTo(); - } - } - - [Test] - public void ProcessDataReceived_Should_Raise_LiveError_Event_When_Response_Contains_Unknown_Type() - { - // Input and Output - var unknownDataResponse = new Dictionary() { { "Wiley", "coyote" } }; - var json = JsonSerializer.Serialize(unknownDataResponse); - var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(json)); - - // Eventing - ResponseEventArgs? eventArgs = null; - _liveClient.EventResponseReceived += (sender, args) => eventArgs = args; - - //Act - _liveClient.ProcessDataReceived(_webSocketReceiveResult, memoryStream); - - //Assert - using (new AssertionScope()) - { - eventArgs.Should().NotBeNull(); - eventArgs!.Response.Error.Should().NotBeNull(); - eventArgs.Response.Error.Should().BeAssignableTo(); - } - } - - - #region Helpers - [Test] - public void GetBaseUrl_Should_Return_WSS_Protocol_When_DeepgramClientOptions_BaseAddress_Contains_No_Protocol() - { - // Input and Output - var expectedUrl = $"wss://{Defaults.DEFAULT_URI}"; - - //Act - var result = LiveClient.GetBaseUrl(_options); - - //Assert - result.Should().NotBeNullOrEmpty(); - result.Should().StartWith("wss://"); - result.Should().BeEquivalentTo(expectedUrl); - } - - [Test] - public void GetBaseUrl_Should_Return_WSS_Protocol_When_BaseAddress_Contains_WSS_Protocol() - { - // Input and Output - var expectedUrl = $"wss://{Defaults.DEFAULT_URI}"; - _options.BaseAddress = $"wss://{Defaults.DEFAULT_URI}"; - - //Act - var result = LiveClient.GetBaseUrl(_options); - - //Assert - using (new AssertionScope()) - { - result.Should().NotBeNullOrEmpty(); - result.Should().StartWith("wss://"); - result.Should().BeEquivalentTo(expectedUrl); - } - } - - [Test] - public void GetUri_Should_Return_Correctly_Formatted_Uri() - { - // Input and Output - var liveSchema = new LiveSchema() - { - Diarize = true, - }; - _options.BaseAddress = Defaults.DEFAULT_URI; - var expectedUriStart = $"wss://{Defaults.DEFAULT_URI}/v1"; - var expectedQuery = $"{UriSegments.LISTEN}?diarize=true"; - var expectedCompleteUri = new Uri($"{expectedUriStart}/{expectedQuery}"); - - //Act - var result = LiveClient.GetUri(_options, liveSchema); - - //Assert - using (new AssertionScope()) - { - result.Should().NotBeNull(); - result.Should().BeAssignableTo(); - result.ToString().Should().StartWith(expectedUriStart); - result.ToString().Should().Contain(expectedQuery); - result.ToString().Should().BeEquivalentTo(expectedCompleteUri.ToString()); - } - } - #endregion -} \ No newline at end of file +//// Copyright 2021-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.Net.WebSockets; + +//using Deepgram.Models.Authenticate.v1; +//using Deepgram.Models.Live.v1; +//using Deepgram.Clients.Live.v1; + +//namespace Deepgram.Tests.UnitTests.ClientTests; + +//public class LiveClientTests +//{ +// DeepgramWsClientOptions _options; +// WebSocketReceiveResult _webSocketReceiveResult; +// LiveClient _liveClient; + +// [SetUp] +// public void Setup() +// { +// var _apiKey = new Faker().Random.Guid().ToString(); +// _options = new DeepgramWsClientOptions(_apiKey, null, null, true); + +// _webSocketReceiveResult = new WebSocketReceiveResult(1, WebSocketMessageType.Text, true); +// _liveClient = new LiveClient(_apiKey, _options); +// } + +// [TearDown] +// public void Teardown() +// { +// if (_liveClient != null) +// _liveClient.Dispose(); +// } + +// //[Test] +// //public void ProcessDataReceived_Should_Raise_TranscriptReceived_Event_When_Response_Contains_Type_TranscriptionResponse() +// //{ +// // // Input and Output +// // var liveTranscriptionResponse = new AutoFaker().Generate(); + +// // // ensure the right type is set for testing +// // liveTranscriptionResponse.Type = LiveType.Results; +// // var json = JsonSerializer.Serialize(liveTranscriptionResponse); +// // var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(json)); + +// // // Eventing +// // ResponseEventArgs? eventArgs = null; +// // _liveClient.EventResponseReceived += (sender, args) => eventArgs = args; + +// // //Act +// // _liveClient.ProcessDataReceived(_webSocketReceiveResult, memoryStream); +// // Task.Delay(5000); + +// // //Assert +// // eventArgs.Should().NotBeNull(); +// // eventArgs!.Response.Transcription.Should().NotBeNull(); +// // eventArgs.Response.Transcription.Should().BeAssignableTo(); +// //} + +// //[Test] +// //public void ProcessDataReceived_Should_Raise_MetaDataReceived_Event_When_Response_Contains_Type_MetadataResponse() +// //{ +// // // Input and Output +// // var liveMetadataResponse = new AutoFaker().Generate(); + +// // // ensure the right type is set for testing +// // liveMetadataResponse.Type = LiveType.Metadata; +// // var json = JsonSerializer.Serialize(liveMetadataResponse); +// // var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(json)); + +// // // Eventing +// // ResponseEventArgs? eventArgs = null; +// // _liveClient.EventResponseReceived += (sender, args) => eventArgs = args; + +// // //Act +// // _liveClient.ProcessDataReceived(_webSocketReceiveResult, memoryStream); + +// // //Assert +// // using (new AssertionScope()) +// // { +// // eventArgs.Should().NotBeNull(); +// // eventArgs!.Response.MetaData.Should().NotBeNull(); +// // eventArgs.Response.MetaData.Should().BeAssignableTo(); +// // } +// //} + +// //[Test] +// //public void ProcessDataReceived_Should_Raise_LiveError_Event_When_Response_Contains_Unknown_Type() +// //{ +// // // Input and Output +// // var unknownDataResponse = new Dictionary() { { "Wiley", "coyote" } }; +// // var json = JsonSerializer.Serialize(unknownDataResponse); +// // var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(json)); + +// // // Eventing +// // ResponseEventArgs? eventArgs = null; +// // _liveClient.EventResponseReceived += (sender, args) => eventArgs = args; + +// // //Act +// // _liveClient.ProcessDataReceived(_webSocketReceiveResult, memoryStream); + +// // //Assert +// // using (new AssertionScope()) +// // { +// // eventArgs.Should().NotBeNull(); +// // eventArgs!.Response.Error.Should().NotBeNull(); +// // eventArgs.Response.Error.Should().BeAssignableTo(); +// // } +// //} + +// #region Helpers +// //[Test] +// //public void GetUri_Should_Return_Correctly_Formatted_Uri() +// //{ +// // // Input and Output +// // var liveSchema = new LiveSchema() +// // { +// // Diarize = true, +// // }; +// // var expectedUriStart = $"wss://{Defaults.DEFAULT_URI}/v1"; +// // var expectedQuery = $"{UriSegments.LISTEN}?diarize=true"; +// // var expectedCompleteUri = new Uri($"{expectedUriStart}/{expectedQuery}"); + +// // //Act +// // var result = LiveClient.GetUri(_options, liveSchema); + +// // //Assert +// // using (new AssertionScope()) +// // { +// // result.Should().NotBeNull(); +// // result.Should().BeAssignableTo(); +// // result.ToString().Should().StartWith(expectedUriStart); +// // result.ToString().Should().Contain(expectedQuery); +// // result.ToString().Should().BeEquivalentTo(expectedCompleteUri.ToString()); +// // } +// //} +// #endregion +//} \ No newline at end of file diff --git a/Deepgram.Tests/UnitTests/ClientTests/ManageClientTest.cs b/Deepgram.Tests/UnitTests/ClientTests/ManageClientTest.cs index e3d80621..9f8240eb 100644 --- a/Deepgram.Tests/UnitTests/ClientTests/ManageClientTest.cs +++ b/Deepgram.Tests/UnitTests/ClientTests/ManageClientTest.cs @@ -12,15 +12,15 @@ namespace Deepgram.Tests.UnitTests.ClientTests; public class ManageClientTest { - DeepgramClientOptions _options; + DeepgramHttpClientOptions _options; string _projectId; string _apiKey; [SetUp] public void Setup() { - _options = new DeepgramClientOptions(); - _projectId = new Faker().Random.Guid().ToString(); _apiKey = new Faker().Random.Guid().ToString(); + _options = new DeepgramHttpClientOptions(_apiKey, null, null, true); + _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..8b778694 100644 --- a/Deepgram.Tests/UnitTests/ClientTests/OnPremClientTests.cs +++ b/Deepgram.Tests/UnitTests/ClientTests/OnPremClientTests.cs @@ -10,15 +10,15 @@ namespace Deepgram.Tests.UnitTests.ClientTests; public class OnPremClientTests { - DeepgramClientOptions _options; + DeepgramHttpClientOptions _options; string _projectId; string _apiKey; [SetUp] public void Setup() { - _options = new DeepgramClientOptions(); - _projectId = new Faker().Random.Guid().ToString(); _apiKey = new Faker().Random.Guid().ToString(); + _options = new DeepgramHttpClientOptions(_apiKey, null, null, true); + _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..a19a39ed 100644 --- a/Deepgram.Tests/UnitTests/ClientTests/PrerecordedClientTests.cs +++ b/Deepgram.Tests/UnitTests/ClientTests/PrerecordedClientTests.cs @@ -10,14 +10,14 @@ namespace Deepgram.Tests.UnitTests.ClientTests; public class PreRecordedClientTests { - DeepgramClientOptions _options; + DeepgramHttpClientOptions _options; string _apiKey; [SetUp] public void Setup() { - _options = new DeepgramClientOptions(); _apiKey = new Faker().Random.Guid().ToString(); + _options = new DeepgramHttpClientOptions(_apiKey, null, null, true); } [Test] diff --git a/Deepgram.Tests/UnitTests/ClientTests/SpeakClientTests.cs b/Deepgram.Tests/UnitTests/ClientTests/SpeakClientTests.cs index 25cac224..27565252 100644 --- a/Deepgram.Tests/UnitTests/ClientTests/SpeakClientTests.cs +++ b/Deepgram.Tests/UnitTests/ClientTests/SpeakClientTests.cs @@ -10,14 +10,14 @@ namespace Deepgram.Tests.UnitTests.ClientTests; public class SpeakClientTests { - DeepgramClientOptions _options; + DeepgramHttpClientOptions _options; string _apiKey; [SetUp] public void Setup() { - _options = new DeepgramClientOptions(); _apiKey = new Faker().Random.Guid().ToString(); + _options = new DeepgramHttpClientOptions(_apiKey, null, null, true); } //[Test] diff --git a/Deepgram.Tests/UnitTests/HttpExtensionsTests/HttpClientExtensionTests.cs b/Deepgram.Tests/UnitTests/HttpExtensionsTests/HttpClientExtensionTests.cs index 63d8e70a..d581d796 100644 --- a/Deepgram.Tests/UnitTests/HttpExtensionsTests/HttpClientExtensionTests.cs +++ b/Deepgram.Tests/UnitTests/HttpExtensionsTests/HttpClientExtensionTests.cs @@ -12,48 +12,50 @@ public class HttpClientTests { readonly string _customUrl = "acme.com"; IHttpClientFactory _httpClientFactory; - DeepgramClientOptions _clientOptions; - string _apiKey; [SetUp] public void Setup() { _httpClientFactory = Substitute.For(); - _clientOptions = new DeepgramClientOptions(); - _apiKey = new Faker().Random.Guid().ToString(); } - [Test] public void Should_Return_HttpClient_With_Default_BaseAddress() { // Input and Output + var _apiKey = new Faker().Random.Guid().ToString(); + var _clientOptions = new DeepgramHttpClientOptions(_apiKey); + + // Fake Clients var httpClient = MockHttpClient.CreateHttpClientWithResult(new MessageResponse(), HttpStatusCode.OK); _httpClientFactory.CreateClient().Returns(httpClient); //Act - var SUT = HttpClientFactory.ConfigureDeepgram(httpClient, _apiKey, _clientOptions); + var SUT = HttpClientFactory.ConfigureDeepgram(httpClient, _clientOptions); //Assert using (new AssertionScope()) { SUT.Should().NotBeNull(); - SUT.BaseAddress.Should().Be($"https://{Defaults.DEFAULT_URI}/v1/"); + SUT.BaseAddress.Should().Be($"https://{Defaults.DEFAULT_URI}/v1"); }; } [Test] public void Should_Return_HttpClient_With_Custom_BaseAddress() { - // Input and Output - var expectedBaseAddress = $"https://{_customUrl}/v1/"; + // Input and Output + var expectedBaseAddress = $"https://{_customUrl}/v1"; var customBaseAddress = $"https://{_customUrl}"; - _clientOptions.BaseAddress = customBaseAddress; + var _apiKey = new Faker().Random.Guid().ToString(); + var _clientOptions = new DeepgramHttpClientOptions(_apiKey, customBaseAddress); + + // Fake Clients var httpClient = MockHttpClient.CreateHttpClientWithResult(new MessageResponse(), HttpStatusCode.OK, expectedBaseAddress); _httpClientFactory.CreateClient().Returns(httpClient); //Act - var SUT = HttpClientFactory.ConfigureDeepgram(httpClient, _apiKey, _clientOptions); + var SUT = HttpClientFactory.ConfigureDeepgram(httpClient, _clientOptions); //Assert using (new AssertionScope()) @@ -67,18 +69,21 @@ public void Should_Return_HttpClient_With_Custom_BaseAddress() public void Should_Return_HttpClient_With_Default_BaseAddress_And_Custom_Headers() { // Input and Output - _clientOptions.Headers = FakeHeaders(); + var _apiKey = new Faker().Random.Guid().ToString(); + var _clientOptions = new DeepgramHttpClientOptions(_apiKey, null, null, null, FakeHeaders()); + + // Fake Clients var httpClient = MockHttpClient.CreateHttpClientWithResult(new MessageResponse(), HttpStatusCode.OK); _httpClientFactory.CreateClient().Returns(httpClient); //Act - var SUT = HttpClientFactory.ConfigureDeepgram(httpClient, _apiKey, _clientOptions); + var SUT = HttpClientFactory.ConfigureDeepgram(httpClient, _clientOptions); //Assert using (new AssertionScope()) { SUT.Should().NotBeNull(); - SUT.BaseAddress.Should().Be($"https://{Defaults.DEFAULT_URI}/v1/"); + SUT.BaseAddress.Should().Be($"https://{Defaults.DEFAULT_URI}/v1"); SUT.DefaultRequestHeaders.Should().ContainKey(_clientOptions.Headers.First().Key); }; } @@ -86,20 +91,20 @@ public void Should_Return_HttpClient_With_Default_BaseAddress_And_Custom_Headers [Test] public void Should_Return_HttpClient_With_Custom_BaseAddress_And_Custom_Headers() { - // Input and Output - _clientOptions.Headers = FakeHeaders(); + // Input and Output + var expectedBaseAddress = $"https://{_customUrl}/v1"; + var customBaseAddress = $"https://{_customUrl}"; + var _apiKey = new Faker().Random.Guid().ToString(); + var _clientOptions = new DeepgramHttpClientOptions(_apiKey, customBaseAddress, null, null, FakeHeaders()); + // Fake Clients var httpClient = MockHttpClient.CreateHttpClientWithResult(new MessageResponse(), HttpStatusCode.OK); httpClient.BaseAddress = null; _httpClientFactory.CreateClient().Returns(httpClient); - var expectedBaseAddress = $"https://{_customUrl}/v1/"; - var customBaseAddress = $"https://{_customUrl}"; - _clientOptions.BaseAddress = customBaseAddress; - //Act - var SUT = HttpClientFactory.ConfigureDeepgram(httpClient, _apiKey, _clientOptions); + var SUT = HttpClientFactory.ConfigureDeepgram(httpClient, _clientOptions); //Assert using (new AssertionScope()) @@ -111,19 +116,20 @@ public void Should_Return_HttpClient_With_Custom_BaseAddress_And_Custom_Headers( } [Test] - public void Should_Return_HttpClient_With_Predefined_values() + public void Should_Return_HttpClient_With_Predefined_Values() { - // Input and Output - _clientOptions.Headers = FakeHeaders(); - var expectedBaseAddress = $"https://{_customUrl}/v1/"; + // Input and Output + var expectedBaseAddress = $"https://{_customUrl}/v1"; var customBaseAddress = $"https://{_customUrl}"; - _clientOptions.BaseAddress = customBaseAddress; - var httpClient = MockHttpClient.CreateHttpClientWithResult(new MessageResponse(), HttpStatusCode.OK, expectedBaseAddress); + var _apiKey = new Faker().Random.Guid().ToString(); + var _clientOptions = new DeepgramHttpClientOptions(_apiKey, customBaseAddress, null, null, FakeHeaders()); + // Fake Clients + var httpClient = MockHttpClient.CreateHttpClientWithResult(new MessageResponse(), HttpStatusCode.OK, expectedBaseAddress); _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..d2375e0f 100644 --- a/Deepgram/Abstractions/AbstractRestClient.cs +++ b/Deepgram/Abstractions/AbstractRestClient.cs @@ -22,18 +22,18 @@ public abstract class AbstractRestClient /// /// Copy of the options for the client /// - internal DeepgramClientOptions _options; + internal DeepgramHttpClientOptions _options; /// /// Constructor that take the options and a httpClient /// /// Options for the Deepgram client - internal AbstractRestClient(string apiKey = "", DeepgramClientOptions? options = null) + internal AbstractRestClient(string? apiKey = null, DeepgramHttpClientOptions? options = null) { - options ??= new DeepgramClientOptions(); + options ??= new DeepgramHttpClientOptions(apiKey); _httpClient = HttpClientFactory.Create(); - _httpClient = HttpClientFactory.ConfigureDeepgram(_httpClient, apiKey, options); + _httpClient = HttpClientFactory.ConfigureDeepgram(_httpClient, options); _options = options; } @@ -506,30 +506,9 @@ public virtual async Task DeleteAsync(string uriSegment, S? parameter, } } - internal static string GetUri(DeepgramClientOptions options, string path) + internal static string GetUri(DeepgramHttpClientOptions options, string path) { - var baseUrl = GetBaseUrl(options); - return $"{baseUrl}/{options.APIVersion}/{path}"; - } - - internal static string GetBaseUrl(DeepgramClientOptions options) - { - string baseAddress = Defaults.DEFAULT_URI; - if (options.BaseAddress != null) - { - baseAddress = options.BaseAddress; - } - - //checks for ws:// wss:// ws wss - wss:// is include to ensure it is all stripped out and correctly formatted - Regex regex = new Regex(@"\b(http:\/\/|https:\/\/|http|https)\b", RegexOptions.IgnoreCase); - if (!regex.IsMatch(baseAddress)) - { - //if no protocol in the address then https:// is added - // TODO: log - baseAddress = $"https://{baseAddress}"; - } - - return baseAddress; + return $"{options.BaseAddress}/{path}"; } } diff --git a/Deepgram/AnalyzeClient.cs b/Deepgram/AnalyzeClient.cs index 3b6ee561..f089f86d 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 = "", DeepgramHttpClientOptions? deepgramClientOptions = null) : base(apiKey, deepgramClientOptions) { } } diff --git a/Deepgram/Clients/Analyze/v1/Client.cs b/Deepgram/Clients/Analyze/v1/Client.cs index 931a54ca..810d7953 100644 --- a/Deepgram/Clients/Analyze/v1/Client.cs +++ b/Deepgram/Clients/Analyze/v1/Client.cs @@ -11,8 +11,8 @@ namespace Deepgram.Clients.Analyze.v1; /// Implements version 1 of the Analyze Client. /// /// Required DeepgramApiKey -/// for HttpClient Configuration -public class Client(string apiKey, DeepgramClientOptions? deepgramClientOptions = null) +/// for HttpClient Configuration +public class Client(string? apiKey = null, DeepgramHttpClientOptions? 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..2a9f0eec 100644 --- a/Deepgram/Clients/Live/v1/Client.cs +++ b/Deepgram/Clients/Live/v1/Client.cs @@ -14,39 +14,18 @@ public class Client : IDisposable { #region Fields internal ILogger logger => LogProvider.GetLogger(this.GetType().Name); - internal readonly DeepgramClientOptions _deepgramClientOptions; - internal readonly string _apiKey; + internal readonly DeepgramWsClientOptions _deepgramClientOptions; internal ClientWebSocket? _clientWebSocket; internal CancellationTokenSource _cancellationTokenSource; internal bool _isDisposed; #endregion /// Required DeepgramApiKey - /// for HttpClient Configuration - public Client(string apiKey = "", DeepgramClientOptions? options = null) + /// for HttpClient Configuration + public Client(string? apiKey = null, DeepgramWsClientOptions? 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 DeepgramWsClientOptions(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(); } @@ -288,37 +276,15 @@ await _clientWebSocket.CloseOutputAsync( internal readonly Channel _sendChannel = System.Threading.Channels.Channel .CreateUnbounded(new UnboundedChannelOptions { SingleReader = true, SingleWriter = true, }); - internal static Uri GetUri(DeepgramClientOptions options, LiveSchema parameter, Dictionary? addons = null) + internal static Uri GetUri(DeepgramWsClientOptions options, LiveSchema parameter, Dictionary? addons = null) { - var baseUrl = GetBaseUrl(options); - var propertyInfoList = parameter.GetType() .GetProperties() .Where(v => v.GetValue(parameter) is not null); var queryString = QueryParameterUtil.UrlEncode(parameter, propertyInfoList, addons); - return new Uri($"{baseUrl}/{options.APIVersion}/{UriSegments.LISTEN}?{queryString}"); - } - - internal static string GetBaseUrl(DeepgramClientOptions options) - { - string baseAddress = Defaults.DEFAULT_URI; - if (options.BaseAddress != null) - { - baseAddress = options.BaseAddress; - } - - //checks for ws:// wss:// ws wss - wss:// is include to ensure it is all stripped out and correctly formatted - Regex regex = new Regex(@"\b(ws:\/\/|wss:\/\/|ws|wss)\b", RegexOptions.IgnoreCase); - if (!regex.IsMatch(baseAddress)) - { - //if no protocol in the address then https:// is added - // TODO: log - baseAddress = $"wss://{baseAddress}"; - } - - return baseAddress; + return new Uri($"{options.BaseAddress}/{UriSegments.LISTEN}?{queryString}"); } private void ProcessException(string action, Exception ex) diff --git a/Deepgram/Clients/Manage/v1/Client.cs b/Deepgram/Clients/Manage/v1/Client.cs index 82eb75b6..f7184233 100644 --- a/Deepgram/Clients/Manage/v1/Client.cs +++ b/Deepgram/Clients/Manage/v1/Client.cs @@ -11,8 +11,8 @@ namespace Deepgram.Clients.Manage.v1; /// Implements version 1 of the Manage Client. /// /// Required DeepgramApiKey -/// for HttpClient Configuration -public class Client(string apiKey, DeepgramClientOptions? deepgramClientOptions = null) +/// for HttpClient Configuration +public class Client(string? apiKey = null, DeepgramHttpClientOptions? 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..4dd4d70a 100644 --- a/Deepgram/Clients/OnPrem/v1/Client.cs +++ b/Deepgram/Clients/OnPrem/v1/Client.cs @@ -11,8 +11,8 @@ namespace Deepgram.Clients.OnPrem.v1; /// Implements version 1 of the OnPrem Client. /// /// Required DeepgramApiKey -/// for HttpClient Configuration -public class Client(string apiKey, DeepgramClientOptions? deepgramClientOptions = null) +/// for HttpClient Configuration +public class Client(string? apiKey = null, DeepgramHttpClientOptions? deepgramClientOptions = null) : AbstractRestClient(apiKey, deepgramClientOptions) { /// diff --git a/Deepgram/Clients/PreRecorded/v1/Client.cs b/Deepgram/Clients/PreRecorded/v1/Client.cs index 533d4146..11791670 100644 --- a/Deepgram/Clients/PreRecorded/v1/Client.cs +++ b/Deepgram/Clients/PreRecorded/v1/Client.cs @@ -11,8 +11,8 @@ namespace Deepgram.Clients.PreRecorded.v1; /// Implements version 1 of the Analyze Client. /// /// Required DeepgramApiKey -/// for HttpClient Configuration -public class Client(string apiKey = "", DeepgramClientOptions? deepgramClientOptions = null) +/// for HttpClient Configuration +public class Client(string? apiKey = null, DeepgramHttpClientOptions? deepgramClientOptions = null) : AbstractRestClient(apiKey, deepgramClientOptions) { diff --git a/Deepgram/Clients/Speak/v1/Client.cs b/Deepgram/Clients/Speak/v1/Client.cs index c00dd1c7..e6dc56c0 100644 --- a/Deepgram/Clients/Speak/v1/Client.cs +++ b/Deepgram/Clients/Speak/v1/Client.cs @@ -11,8 +11,8 @@ namespace Deepgram.Clients.Speak.v1; /// Implements version 1 of the Speak Client. /// /// Required DeepgramApiKey -/// for HttpClient Configuration -public class Client(string apiKey, DeepgramClientOptions? deepgramClientOptions = null) +/// for HttpClient Configuration +public class Client(string? apiKey = null, DeepgramHttpClientOptions? 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..d18d7017 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, DeepgramHttpClientOptions? 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"); - } + options ??= new DeepgramHttpClientOptions(); // 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) @@ -60,26 +43,8 @@ internal static HttpClient ConfigureDeepgram(HttpClient client, string apiKey = client.DefaultRequestHeaders.Add(header.Key, header.Value); } - // base url - var baseAddress = $"{Defaults.DEFAULT_URI}/{options.APIVersion}/"; - if (options.BaseAddress is not null) - { - // TODO: log - baseAddress = $"{options.BaseAddress}/{options.APIVersion}/"; - } - // TODO: log - - //checks for http:// https:// http https - https:// is include to ensure it is all stripped out and correctly formatted - Regex regex = new Regex(@"\b(http:\/\/|https:\/\/|http|https)\b", RegexOptions.IgnoreCase); - if (!regex.IsMatch(baseAddress)) - { - //if no protocol in the address then https:// is added - // TODO: log - baseAddress = $"https://{baseAddress}"; - } - // TODO: log - client.BaseAddress = new Uri(baseAddress); + client.BaseAddress = new Uri(options.BaseAddress); return client; } diff --git a/Deepgram/LiveClient.cs b/Deepgram/LiveClient.cs index cd45285e..2127c609 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 = "", DeepgramWsClientOptions? deepgramClientOptions = null) : base(apiKey, deepgramClientOptions) { } } diff --git a/Deepgram/ManageClient.cs b/Deepgram/ManageClient.cs index 3d1a4e04..c39fe7bb 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 = "", DeepgramHttpClientOptions? deepgramClientOptions = null) : base(apiKey, deepgramClientOptions) { } } diff --git a/Deepgram/Models/Authenticate/v1/DeepgramClientOptions.cs b/Deepgram/Models/Authenticate/v1/DeepgramClientOptions.cs index a8464482..dce5d1f4 100644 --- a/Deepgram/Models/Authenticate/v1/DeepgramClientOptions.cs +++ b/Deepgram/Models/Authenticate/v1/DeepgramClientOptions.cs @@ -12,6 +12,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 @@ -21,18 +26,13 @@ public class DeepgramClientOptions /// /// Api endpoint version /// - public string APIVersion { get; set; } = "v1"; + public string APIVersion { get; set; } = Defaults.DEFAULT_API_VERSION; /// - /// 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 /*****************************/ @@ -43,11 +43,15 @@ public class DeepgramClientOptions /// /// Enable sending KeepAlives for Streaming /// - public bool EnableKeepAlive { get; set; } = false; + public bool KeepAlive { get; set; } = false; /*****************************/ // OnPrem /*****************************/ + /// + /// Enable when using OnPrem mode + /// + public bool OnPrem { get; set; } = false; /*****************************/ // Manage @@ -56,4 +60,54 @@ public class DeepgramClientOptions /*****************************/ // Analyze /*****************************/ + + /*****************************/ + // Constructor + /*****************************/ + public DeepgramClientOptions(string? apiKey = null, string? baseAddress = null, bool? keepAlive = null, bool? onPrem = null, Dictionary? headers = null, string? apiVersion = null) + { + ApiKey = apiKey ?? ""; + BaseAddress = baseAddress ?? Defaults.DEFAULT_URI; + KeepAlive = keepAlive ?? false; + OnPrem = onPrem ?? false; + Headers = headers ?? new Dictionary(); + APIVersion = apiVersion ?? Defaults.DEFAULT_API_VERSION; + + // 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"); + } + + // base url + Regex regex = new Regex(@"\b(\/v[0-9]+)\b", RegexOptions.IgnoreCase); + if (!regex.IsMatch(BaseAddress)) + { + //Console.WriteLine($"REST BaseAddress: {BaseAddress}"); // TODO: logging + BaseAddress = $"{BaseAddress}/{APIVersion}/"; + } + // TODO: log + + //checks for http:// https:// http https - https:// is include to ensure it is all stripped out and correctly formatted + regex = new Regex(@"\b(http:\/\/|https:\/\/|http|https)\b", RegexOptions.IgnoreCase); + if (!regex.IsMatch(BaseAddress)) + { + //if no protocol in the address then https:// is added + //Console.WriteLine($"REST BaseAddress: {BaseAddress}"); // TODO: logging + BaseAddress = $"https://{BaseAddress}"; + } + BaseAddress = BaseAddress.TrimEnd('/'); + //Console.WriteLine($"REST BaseAddress (Final): {BaseAddress}"); // TODO: logging + } } diff --git a/Deepgram/Models/Authenticate/v1/DeepgramHttpClientOptions.cs b/Deepgram/Models/Authenticate/v1/DeepgramHttpClientOptions.cs new file mode 100644 index 00000000..5b355ac4 --- /dev/null +++ b/Deepgram/Models/Authenticate/v1/DeepgramHttpClientOptions.cs @@ -0,0 +1,15 @@ +// Copyright 2021-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 + +namespace Deepgram.Models.Authenticate.v1; + +/// +/// Configuration for the Deepgram client +/// +public class DeepgramHttpClientOptions : DeepgramClientOptions +{ + public DeepgramHttpClientOptions(string? apiKey = null, string? baseAddress = null, bool? keepAlive = null, bool? onPrem = null, Dictionary? headers = null, string? apiVersion = null) : base(apiKey, baseAddress, keepAlive, onPrem, headers, apiVersion) + { + } +} diff --git a/Deepgram/Models/Authenticate/v1/DeepgramWsClientOptions.cs b/Deepgram/Models/Authenticate/v1/DeepgramWsClientOptions.cs new file mode 100644 index 00000000..b0a1398d --- /dev/null +++ b/Deepgram/Models/Authenticate/v1/DeepgramWsClientOptions.cs @@ -0,0 +1,68 @@ +// Copyright 2021-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 + +namespace Deepgram.Models.Authenticate.v1; + +/// +/// Configuration for the Deepgram client +/// +public class DeepgramWsClientOptions : DeepgramClientOptions +{ + public DeepgramWsClientOptions(string? apiKey = null, string? baseAddress = null, bool? keepAlive = null, bool? onPrem = null, Dictionary? headers = null, string? apiVersion = null) + { + ApiKey = apiKey ?? ""; + BaseAddress = baseAddress ?? Defaults.DEFAULT_URI; + KeepAlive = keepAlive ?? false; + OnPrem = onPrem ?? false; + Headers = headers ?? new Dictionary(); + APIVersion = apiVersion ?? Defaults.DEFAULT_API_VERSION; + + // 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"); + } + + // base url + Regex regex = new Regex(@"\b(\/v[0-9]+)\b", RegexOptions.IgnoreCase); + if (!regex.IsMatch(BaseAddress)) + { + //Console.WriteLine($"WS BaseAddress: {BaseAddress}"); // TODO: logging + BaseAddress = $"{BaseAddress}/{APIVersion}"; + } + // TODO: log + + //checks for ws:// wss:// ws wss - wss:// is include to ensure it is all stripped out and correctly formatted + regex = new Regex(@"\b(http:\/\/|https:\/\/|http|https)\b", RegexOptions.IgnoreCase); + if (regex.IsMatch(BaseAddress)) + { + // if protocol https/http is in the address, remove it + //Console.WriteLine($"WS BaseAddress (Remove https/http): {BaseAddress}"); // TODO: logging + BaseAddress = BaseAddress.Substring(BaseAddress.IndexOf("/") + 2); + } + + //checks for ws:// wss:// ws wss - wss:// is include to ensure it is all stripped out and correctly formatted + regex = new Regex(@"\b(ws:\/\/|wss:\/\/|ws|wss)\b", RegexOptions.IgnoreCase); + if (!regex.IsMatch(BaseAddress)) + { + // if no protocol in the address then https:// is added + //Console.WriteLine($"WS BaseAddress (Add wss/ws): {BaseAddress}"); // TODO: logging + BaseAddress = $"wss://{BaseAddress}"; + } + + BaseAddress = BaseAddress.TrimEnd('/'); + //Console.WriteLine($"WS BaseAddress (Final): {BaseAddress}"); // TODO: logging + } +} 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..4d69880f 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 = "", DeepgramHttpClientOptions? deepgramClientOptions = null) : base(apiKey, deepgramClientOptions) { } } diff --git a/Deepgram/PreRecordedClient.cs b/Deepgram/PreRecordedClient.cs index f1702171..195d93b3 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 = "", DeepgramHttpClientOptions? deepgramClientOptions = null) : base(apiKey, deepgramClientOptions) { } } diff --git a/Deepgram/SpeakClient.cs b/Deepgram/SpeakClient.cs index 5588fa3e..7f38c10f 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 = "", DeepgramHttpClientOptions? 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 + + + + + + + + + + + + +