diff --git a/LibreMetaverse.Voice/LibreMetaverse.Voice.csproj b/LibreMetaverse.Voice/LibreMetaverse.Voice.csproj
new file mode 100644
index 00000000..f7be7be8
--- /dev/null
+++ b/LibreMetaverse.Voice/LibreMetaverse.Voice.csproj
@@ -0,0 +1,52 @@
+
+
+ LibreMetaverse.Voice
+ LibreMetaverse.Voice
+ Library interface for Vivox voice client
+ Library
+ LibreMetaverse
+ true
+ true
+ 0419,1574,1591
+ netstandard2.0;netstandard2.1;net5.0
+ x64;x86;AnyCPU
+ ..\bin\
+
+
+ TRACE;DEBUG
+
+
+ TRACE;DEBUG
+
+
+ TRACE;DEBUG
+
+
+ true
+ pdbonly
+ TRACE
+ True
+
+
+ true
+ pdbonly
+ TRACE
+ True
+
+
+ true
+ pdbonly
+ TRACE
+ True
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LibreMetaverse/Voice/TCPPipe.cs b/LibreMetaverse.Voice/TCPPipe.cs
similarity index 99%
rename from LibreMetaverse/Voice/TCPPipe.cs
rename to LibreMetaverse.Voice/TCPPipe.cs
index 840fc9cd..4273548a 100644
--- a/LibreMetaverse/Voice/TCPPipe.cs
+++ b/LibreMetaverse.Voice/TCPPipe.cs
@@ -29,7 +29,7 @@ using System;
using System.Net;
using System.Net.Sockets;
-namespace OpenMetaverse.Voice
+namespace LibreMetaverse.Voice
{
public class TCPPipe
{
diff --git a/LibreMetaverse/Voice/VoiceAccount.cs b/LibreMetaverse.Voice/VoiceAccount.cs
similarity index 97%
rename from LibreMetaverse/Voice/VoiceAccount.cs
rename to LibreMetaverse.Voice/VoiceAccount.cs
index a9aa96f3..4eaff06d 100644
--- a/LibreMetaverse/Voice/VoiceAccount.cs
+++ b/LibreMetaverse.Voice/VoiceAccount.cs
@@ -1,80 +1,80 @@
-/*
- * Copyright (c) 2006-2016, openmetaverse.co
- * Copyright (c) 2021-2022, Sjofn LLC.
- * All rights reserved.
- *
- * - Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- * - Neither the name of the openmetaverse.co nor the names
- * of its contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-using System.Text;
-
-namespace OpenMetaverse.Voice
-{
- public partial class VoiceGateway
- {
- ///
- /// This is used to login a specific user account(s). It may only be called after
- /// Connector initialization has completed successfully
- ///
- /// Handle returned from successful Connector ‘create’ request
- /// User's account name
- /// User's account password
- /// Values may be “AutoAnswer” or “VerifyAnswer”
- /// ""
- /// This is an integer that specifies how often
- /// the daemon will send participant property events while in a channel. If this is not set
- /// the default will be “on state change”, which means that the events will be sent when
- /// the participant starts talking, stops talking, is muted, is unmuted.
- /// The valid values are:
- /// 0 – Never
- /// 5 – 10 times per second
- /// 10 – 5 times per second
- /// 50 – 1 time per second
- /// 100 – on participant state change (this is the default)
- /// false
- ///
- public int AccountLogin(string ConnectorHandle, string AccountName, string AccountPassword, string AudioSessionAnswerMode, string AccountURI, int ParticipantPropertyFrequency, bool EnableBuddiesAndPresence)
- {
- StringBuilder sb = new StringBuilder();
- sb.Append(VoiceGateway.MakeXML("ConnectorHandle", ConnectorHandle));
- sb.Append(VoiceGateway.MakeXML("AccountName", AccountName));
- sb.Append(VoiceGateway.MakeXML("AccountPassword", AccountPassword));
- sb.Append(VoiceGateway.MakeXML("AudioSessionAnswerMode", AudioSessionAnswerMode));
- sb.Append(VoiceGateway.MakeXML("AccountURI", AccountURI));
- sb.Append(VoiceGateway.MakeXML("ParticipantPropertyFrequency", ParticipantPropertyFrequency.ToString()));
- sb.Append(VoiceGateway.MakeXML("EnableBuddiesAndPresence", EnableBuddiesAndPresence ? "true" : "false"));
- sb.Append(VoiceGateway.MakeXML("BuddyManagementMode", "Application"));
- return Request("Account.Login.1", sb.ToString());
- }
-
- ///
- /// This is used to logout a user session. It should only be called with a valid AccountHandle.
- ///
- /// Handle returned from successful Connector ‘login’ request
- ///
- public int AccountLogout(string AccountHandle)
- {
- string RequestXML = VoiceGateway.MakeXML("AccountHandle", AccountHandle);
- return Request("Account.Logout.1", RequestXML);
- }
- }
-}
+/*
+ * Copyright (c) 2006-2016, openmetaverse.co
+ * Copyright (c) 2021-2022, Sjofn LLC.
+ * All rights reserved.
+ *
+ * - Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * - Neither the name of the openmetaverse.co nor the names
+ * of its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System.Text;
+
+namespace LibreMetaverse.Voice
+{
+ public partial class VoiceGateway
+ {
+ ///
+ /// This is used to login a specific user account(s). It may only be called after
+ /// Connector initialization has completed successfully
+ ///
+ /// Handle returned from successful Connector ‘create’ request
+ /// User's account name
+ /// User's account password
+ /// Values may be “AutoAnswer” or “VerifyAnswer”
+ /// ""
+ /// This is an integer that specifies how often
+ /// the daemon will send participant property events while in a channel. If this is not set
+ /// the default will be “on state change”, which means that the events will be sent when
+ /// the participant starts talking, stops talking, is muted, is unmuted.
+ /// The valid values are:
+ /// 0 – Never
+ /// 5 – 10 times per second
+ /// 10 – 5 times per second
+ /// 50 – 1 time per second
+ /// 100 – on participant state change (this is the default)
+ /// false
+ ///
+ public int AccountLogin(string ConnectorHandle, string AccountName, string AccountPassword, string AudioSessionAnswerMode, string AccountURI, int ParticipantPropertyFrequency, bool EnableBuddiesAndPresence)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append(VoiceGateway.MakeXML("ConnectorHandle", ConnectorHandle));
+ sb.Append(VoiceGateway.MakeXML("AccountName", AccountName));
+ sb.Append(VoiceGateway.MakeXML("AccountPassword", AccountPassword));
+ sb.Append(VoiceGateway.MakeXML("AudioSessionAnswerMode", AudioSessionAnswerMode));
+ sb.Append(VoiceGateway.MakeXML("AccountURI", AccountURI));
+ sb.Append(VoiceGateway.MakeXML("ParticipantPropertyFrequency", ParticipantPropertyFrequency.ToString()));
+ sb.Append(VoiceGateway.MakeXML("EnableBuddiesAndPresence", EnableBuddiesAndPresence ? "true" : "false"));
+ sb.Append(VoiceGateway.MakeXML("BuddyManagementMode", "Application"));
+ return Request("Account.Login.1", sb.ToString());
+ }
+
+ ///
+ /// This is used to logout a user session. It should only be called with a valid AccountHandle.
+ ///
+ /// Handle returned from successful Connector ‘login’ request
+ ///
+ public int AccountLogout(string AccountHandle)
+ {
+ string RequestXML = VoiceGateway.MakeXML("AccountHandle", AccountHandle);
+ return Request("Account.Logout.1", RequestXML);
+ }
+ }
+}
diff --git a/LibreMetaverse/Voice/VoiceAux.cs b/LibreMetaverse.Voice/VoiceAux.cs
similarity index 97%
rename from LibreMetaverse/Voice/VoiceAux.cs
rename to LibreMetaverse.Voice/VoiceAux.cs
index 7412d9c7..02c781e1 100644
--- a/LibreMetaverse/Voice/VoiceAux.cs
+++ b/LibreMetaverse.Voice/VoiceAux.cs
@@ -1,119 +1,120 @@
-/*
- * Copyright (c) 2006-2016, openmetaverse.co
- * All rights reserved.
- *
- * - Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- * - Neither the name of the openmetaverse.co nor the names
- * of its contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-namespace OpenMetaverse.Voice
-{
- public partial class VoiceGateway
- {
- ///
- /// This is used to get a list of audio devices that can be used for capture (input) of voice.
- ///
- ///
- public int AuxGetCaptureDevices()
- {
- return Request("Aux.GetCaptureDevices.1");
- }
-
- ///
- /// This is used to get a list of audio devices that can be used for render (playback) of voice.
- ///
- public int AuxGetRenderDevices()
- {
- return Request("Aux.GetRenderDevices.1");
- }
-
- ///
- /// This command is used to select the render device.
- ///
- /// The name of the device as returned by the Aux.GetRenderDevices command.
- public int AuxSetRenderDevice(string RenderDeviceSpecifier)
- {
- string RequestXML = VoiceGateway.MakeXML("RenderDeviceSpecifier", RenderDeviceSpecifier);
- return Request("Aux.SetRenderDevice.1", RequestXML);
- }
-
- ///
- /// This command is used to select the capture device.
- ///
- /// The name of the device as returned by the Aux.GetCaptureDevices command.
- public int AuxSetCaptureDevice(string CaptureDeviceSpecifier)
- {
- string RequestXML = VoiceGateway.MakeXML("CaptureDeviceSpecifier", CaptureDeviceSpecifier);
- return Request("Aux.SetCaptureDevice.1", RequestXML);
- }
-
- ///
- /// This command is used to start the audio capture process which will cause
- /// AuxAudioProperty Events to be raised. These events can be used to display a
- /// microphone VU meter for the currently selected capture device. This command
- /// should not be issued if the user is on a call.
- ///
- /// (unused but required)
- ///
- public int AuxCaptureAudioStart(int Duration)
- {
- string RequestXML = VoiceGateway.MakeXML("Duration", Duration.ToString());
- return Request("Aux.CaptureAudioStart.1", RequestXML);
- }
-
- ///
- /// This command is used to stop the audio capture process.
- ///
- ///
- public int AuxCaptureAudioStop()
- {
- return Request("Aux.CaptureAudioStop.1");
- }
-
- ///
- /// This command is used to set the mic volume while in the audio tuning process.
- /// Once an acceptable mic level is attained, the application must issue a
- /// connector set mic volume command to have that level be used while on voice
- /// calls.
- ///
- /// the microphone volume (-100 to 100 inclusive)
- ///
- public int AuxSetMicLevel(int Level)
- {
- string RequestXML = VoiceGateway.MakeXML("Level", Level.ToString());
- return Request("Aux.SetMicLevel.1", RequestXML);
- }
-
- ///
- /// This command is used to set the speaker volume while in the audio tuning
- /// process. Once an acceptable speaker level is attained, the application must
- /// issue a connector set speaker volume command to have that level be used while
- /// on voice calls.
- ///
- /// the speaker volume (-100 to 100 inclusive)
- ///
- public int AuxSetSpeakerLevel(int Level)
- {
- string RequestXML = VoiceGateway.MakeXML("Level", Level.ToString());
- return Request("Aux.SetSpeakerLevel.1", RequestXML);
- }
- }
-}
+/*
+ * Copyright (c) 2006-2016, openmetaverse.co
+ * Copyright (c) 2021-2022, Sjofn LLC.
+ * All rights reserved.
+ *
+ * - Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * - Neither the name of the openmetaverse.co nor the names
+ * of its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+namespace LibreMetaverse.Voice
+{
+ public partial class VoiceGateway
+ {
+ ///
+ /// This is used to get a list of audio devices that can be used for capture (input) of voice.
+ ///
+ ///
+ public int AuxGetCaptureDevices()
+ {
+ return Request("Aux.GetCaptureDevices.1");
+ }
+
+ ///
+ /// This is used to get a list of audio devices that can be used for render (playback) of voice.
+ ///
+ public int AuxGetRenderDevices()
+ {
+ return Request("Aux.GetRenderDevices.1");
+ }
+
+ ///
+ /// This command is used to select the render device.
+ ///
+ /// The name of the device as returned by the Aux.GetRenderDevices command.
+ public int AuxSetRenderDevice(string RenderDeviceSpecifier)
+ {
+ string RequestXML = VoiceGateway.MakeXML("RenderDeviceSpecifier", RenderDeviceSpecifier);
+ return Request("Aux.SetRenderDevice.1", RequestXML);
+ }
+
+ ///
+ /// This command is used to select the capture device.
+ ///
+ /// The name of the device as returned by the Aux.GetCaptureDevices command.
+ public int AuxSetCaptureDevice(string CaptureDeviceSpecifier)
+ {
+ string RequestXML = VoiceGateway.MakeXML("CaptureDeviceSpecifier", CaptureDeviceSpecifier);
+ return Request("Aux.SetCaptureDevice.1", RequestXML);
+ }
+
+ ///
+ /// This command is used to start the audio capture process which will cause
+ /// AuxAudioProperty Events to be raised. These events can be used to display a
+ /// microphone VU meter for the currently selected capture device. This command
+ /// should not be issued if the user is on a call.
+ ///
+ /// (unused but required)
+ ///
+ public int AuxCaptureAudioStart(int Duration)
+ {
+ string RequestXML = VoiceGateway.MakeXML("Duration", Duration.ToString());
+ return Request("Aux.CaptureAudioStart.1", RequestXML);
+ }
+
+ ///
+ /// This command is used to stop the audio capture process.
+ ///
+ ///
+ public int AuxCaptureAudioStop()
+ {
+ return Request("Aux.CaptureAudioStop.1");
+ }
+
+ ///
+ /// This command is used to set the mic volume while in the audio tuning process.
+ /// Once an acceptable mic level is attained, the application must issue a
+ /// connector set mic volume command to have that level be used while on voice
+ /// calls.
+ ///
+ /// the microphone volume (-100 to 100 inclusive)
+ ///
+ public int AuxSetMicLevel(int Level)
+ {
+ string RequestXML = VoiceGateway.MakeXML("Level", Level.ToString());
+ return Request("Aux.SetMicLevel.1", RequestXML);
+ }
+
+ ///
+ /// This command is used to set the speaker volume while in the audio tuning
+ /// process. Once an acceptable speaker level is attained, the application must
+ /// issue a connector set speaker volume command to have that level be used while
+ /// on voice calls.
+ ///
+ /// the speaker volume (-100 to 100 inclusive)
+ ///
+ public int AuxSetSpeakerLevel(int Level)
+ {
+ string RequestXML = VoiceGateway.MakeXML("Level", Level.ToString());
+ return Request("Aux.SetSpeakerLevel.1", RequestXML);
+ }
+ }
+}
diff --git a/LibreMetaverse/Voice/VoiceConnector.cs b/LibreMetaverse.Voice/VoiceConnector.cs
similarity index 97%
rename from LibreMetaverse/Voice/VoiceConnector.cs
rename to LibreMetaverse.Voice/VoiceConnector.cs
index b4fd8566..77241667 100644
--- a/LibreMetaverse/Voice/VoiceConnector.cs
+++ b/LibreMetaverse.Voice/VoiceConnector.cs
@@ -1,128 +1,129 @@
-/*
- * Copyright (c) 2006-2016, openmetaverse.co
- * All rights reserved.
- *
- * - Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- * - Neither the name of the openmetaverse.co nor the names
- * of its contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-using System.Text;
-
-namespace OpenMetaverse.Voice
-{
- public partial class VoiceGateway
- {
- ///
- /// This is used to initialize and stop the Connector as a whole. The Connector
- /// Create call must be completed successfully before any other requests are made
- /// (typically during application initialization). The shutdown should be called
- /// when the application is shutting down to gracefully release resources
- ///
- /// A string value indicting the Application name
- /// URL for the management server
- /// LoggingSettings
- ///
- ///
- public int ConnectorCreate(string ClientName, string AccountManagementServer, ushort MinimumPort,
- ushort MaximumPort, VoiceLoggingSettings Logging)
- {
- StringBuilder sb = new StringBuilder();
- sb.Append(VoiceGateway.MakeXML("ClientName", ClientName));
- sb.Append(VoiceGateway.MakeXML("AccountManagementServer", AccountManagementServer));
- sb.Append(VoiceGateway.MakeXML("MinimumPort", MinimumPort.ToString()));
- sb.Append(VoiceGateway.MakeXML("MaximumPort", MaximumPort.ToString()));
- sb.Append(VoiceGateway.MakeXML("Mode", "Normal"));
- sb.Append("");
- sb.Append(VoiceGateway.MakeXML("Enabled", Logging.Enabled ? "true" : "false"));
- sb.Append(VoiceGateway.MakeXML("Folder", Logging.Folder));
- sb.Append(VoiceGateway.MakeXML("FileNamePrefix", Logging.FileNamePrefix));
- sb.Append(VoiceGateway.MakeXML("FileNameSuffix", Logging.FileNameSuffix));
- sb.Append(VoiceGateway.MakeXML("LogLevel", Logging.LogLevel.ToString()));
- sb.Append("");
- return Request("Connector.Create.1", sb.ToString());
- }
-
- ///
- /// Shutdown Connector -- Should be called when the application is shutting down
- /// to gracefully release resources
- ///
- /// Handle returned from successful Connector ‘create’ request
- public int ConnectorInitiateShutdown(string ConnectorHandle)
- {
- string RequestXML = VoiceGateway.MakeXML("ConnectorHandle", ConnectorHandle);
- return Request("Connector.InitiateShutdown.1", RequestXML);
- }
-
- ///
- /// Mute or unmute the microphone
- ///
- /// Handle returned from successful Connector ‘create’ request
- /// true (mute) or false (unmute)
- public int ConnectorMuteLocalMic(string ConnectorHandle, bool Mute)
- {
- StringBuilder sb = new StringBuilder();
- sb.Append(VoiceGateway.MakeXML("ConnectorHandle", ConnectorHandle));
- sb.Append(VoiceGateway.MakeXML("Value", Mute ? "true" : "false"));
- return Request("Connector.MuteLocalMic.1", sb.ToString());
- }
-
- ///
- /// Mute or unmute the speaker
- ///
- /// Handle returned from successful Connector ‘create’ request
- /// true (mute) or false (unmute)
- public int ConnectorMuteLocalSpeaker(string ConnectorHandle, bool Mute)
- {
- StringBuilder sb = new StringBuilder();
- sb.Append(VoiceGateway.MakeXML("ConnectorHandle", ConnectorHandle));
- sb.Append(VoiceGateway.MakeXML("Value", Mute ? "true" : "false"));
- return Request("Connector.MuteLocalSpeaker.1", sb.ToString());
- }
-
- ///
- /// Set microphone volume
- ///
- /// Handle returned from successful Connector ‘create’ request
- /// The level of the audio, a number between -100 and 100 where
- /// 0 represents ‘normal’ speaking volume
- public int ConnectorSetLocalMicVolume(string ConnectorHandle, int Value)
- {
- StringBuilder sb = new StringBuilder();
- sb.Append(VoiceGateway.MakeXML("ConnectorHandle", ConnectorHandle));
- sb.Append(VoiceGateway.MakeXML("Value", Value.ToString()));
- return Request("Connector.SetLocalMicVolume.1", sb.ToString());
- }
-
- ///
- /// Set local speaker volume
- ///
- /// Handle returned from successful Connector ‘create’ request
- /// The level of the audio, a number between -100 and 100 where
- /// 0 represents ‘normal’ speaking volume
- public int ConnectorSetLocalSpeakerVolume(string ConnectorHandle, int Value)
- {
- StringBuilder sb = new StringBuilder();
- sb.Append(VoiceGateway.MakeXML("ConnectorHandle", ConnectorHandle));
- sb.Append(VoiceGateway.MakeXML("Value", Value.ToString()));
- return Request("Connector.SetLocalSpeakerVolume.1", sb.ToString());
- }
- }
-}
+/*
+ * Copyright (c) 2006-2016, openmetaverse.co
+ * Copyright (c) 2021-2022, Sjofn LLC.
+ * All rights reserved.
+ *
+ * - Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * - Neither the name of the openmetaverse.co nor the names
+ * of its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System.Text;
+
+namespace LibreMetaverse.Voice
+{
+ public partial class VoiceGateway
+ {
+ ///
+ /// This is used to initialize and stop the Connector as a whole. The Connector
+ /// Create call must be completed successfully before any other requests are made
+ /// (typically during application initialization). The shutdown should be called
+ /// when the application is shutting down to gracefully release resources
+ ///
+ /// A string value indicting the Application name
+ /// URL for the management server
+ /// LoggingSettings
+ ///
+ ///
+ public int ConnectorCreate(string ClientName, string AccountManagementServer, ushort MinimumPort,
+ ushort MaximumPort, VoiceLoggingSettings Logging)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append(VoiceGateway.MakeXML("ClientName", ClientName));
+ sb.Append(VoiceGateway.MakeXML("AccountManagementServer", AccountManagementServer));
+ sb.Append(VoiceGateway.MakeXML("MinimumPort", MinimumPort.ToString()));
+ sb.Append(VoiceGateway.MakeXML("MaximumPort", MaximumPort.ToString()));
+ sb.Append(VoiceGateway.MakeXML("Mode", "Normal"));
+ sb.Append("");
+ sb.Append(VoiceGateway.MakeXML("Enabled", Logging.Enabled ? "true" : "false"));
+ sb.Append(VoiceGateway.MakeXML("Folder", Logging.Folder));
+ sb.Append(VoiceGateway.MakeXML("FileNamePrefix", Logging.FileNamePrefix));
+ sb.Append(VoiceGateway.MakeXML("FileNameSuffix", Logging.FileNameSuffix));
+ sb.Append(VoiceGateway.MakeXML("LogLevel", Logging.LogLevel.ToString()));
+ sb.Append("");
+ return Request("Connector.Create.1", sb.ToString());
+ }
+
+ ///
+ /// Shutdown Connector -- Should be called when the application is shutting down
+ /// to gracefully release resources
+ ///
+ /// Handle returned from successful Connector ‘create’ request
+ public int ConnectorInitiateShutdown(string ConnectorHandle)
+ {
+ string RequestXML = VoiceGateway.MakeXML("ConnectorHandle", ConnectorHandle);
+ return Request("Connector.InitiateShutdown.1", RequestXML);
+ }
+
+ ///
+ /// Mute or unmute the microphone
+ ///
+ /// Handle returned from successful Connector ‘create’ request
+ /// true (mute) or false (unmute)
+ public int ConnectorMuteLocalMic(string ConnectorHandle, bool Mute)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append(VoiceGateway.MakeXML("ConnectorHandle", ConnectorHandle));
+ sb.Append(VoiceGateway.MakeXML("Value", Mute ? "true" : "false"));
+ return Request("Connector.MuteLocalMic.1", sb.ToString());
+ }
+
+ ///
+ /// Mute or unmute the speaker
+ ///
+ /// Handle returned from successful Connector ‘create’ request
+ /// true (mute) or false (unmute)
+ public int ConnectorMuteLocalSpeaker(string ConnectorHandle, bool Mute)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append(VoiceGateway.MakeXML("ConnectorHandle", ConnectorHandle));
+ sb.Append(VoiceGateway.MakeXML("Value", Mute ? "true" : "false"));
+ return Request("Connector.MuteLocalSpeaker.1", sb.ToString());
+ }
+
+ ///
+ /// Set microphone volume
+ ///
+ /// Handle returned from successful Connector ‘create’ request
+ /// The level of the audio, a number between -100 and 100 where
+ /// 0 represents ‘normal’ speaking volume
+ public int ConnectorSetLocalMicVolume(string ConnectorHandle, int Value)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append(VoiceGateway.MakeXML("ConnectorHandle", ConnectorHandle));
+ sb.Append(VoiceGateway.MakeXML("Value", Value.ToString()));
+ return Request("Connector.SetLocalMicVolume.1", sb.ToString());
+ }
+
+ ///
+ /// Set local speaker volume
+ ///
+ /// Handle returned from successful Connector ‘create’ request
+ /// The level of the audio, a number between -100 and 100 where
+ /// 0 represents ‘normal’ speaking volume
+ public int ConnectorSetLocalSpeakerVolume(string ConnectorHandle, int Value)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append(VoiceGateway.MakeXML("ConnectorHandle", ConnectorHandle));
+ sb.Append(VoiceGateway.MakeXML("Value", Value.ToString()));
+ return Request("Connector.SetLocalSpeakerVolume.1", sb.ToString());
+ }
+ }
+}
diff --git a/LibreMetaverse/Voice/VoiceControl.cs b/LibreMetaverse.Voice/VoiceControl.cs
similarity index 99%
rename from LibreMetaverse/Voice/VoiceControl.cs
rename to LibreMetaverse.Voice/VoiceControl.cs
index c9cc1515..a8af6e35 100644
--- a/LibreMetaverse/Voice/VoiceControl.cs
+++ b/LibreMetaverse.Voice/VoiceControl.cs
@@ -31,10 +31,11 @@ using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
+using OpenMetaverse;
using OpenMetaverse.StructuredData;
using OpenMetaverse.Http;
-namespace OpenMetaverse.Voice
+namespace LibreMetaverse.Voice
{
public partial class VoiceGateway : IDisposable
{
diff --git a/LibreMetaverse/Voice/VoiceDefinitions.cs b/LibreMetaverse.Voice/VoiceDefinitions.cs
similarity index 99%
rename from LibreMetaverse/Voice/VoiceDefinitions.cs
rename to LibreMetaverse.Voice/VoiceDefinitions.cs
index 552eb585..861f39fd 100644
--- a/LibreMetaverse/Voice/VoiceDefinitions.cs
+++ b/LibreMetaverse.Voice/VoiceDefinitions.cs
@@ -28,8 +28,9 @@
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
+using OpenMetaverse;
-namespace OpenMetaverse.Voice
+namespace LibreMetaverse.Voice
{
public partial class VoiceGateway
{
diff --git a/LibreMetaverse/Voice/VoiceGateway.cs b/LibreMetaverse.Voice/VoiceGateway.cs
similarity index 99%
rename from LibreMetaverse/Voice/VoiceGateway.cs
rename to LibreMetaverse.Voice/VoiceGateway.cs
index 8e53218c..f7d81ddb 100644
--- a/LibreMetaverse/Voice/VoiceGateway.cs
+++ b/LibreMetaverse.Voice/VoiceGateway.cs
@@ -32,8 +32,9 @@ using System.Net.Sockets;
using System.Diagnostics;
using System.Threading;
using System.Text;
+using OpenMetaverse;
-namespace OpenMetaverse.Voice
+namespace LibreMetaverse.Voice
{
public partial class VoiceGateway
{
diff --git a/LibreMetaverse.Utilities/VoiceManager.cs b/LibreMetaverse.Voice/VoiceManager.cs
similarity index 97%
rename from LibreMetaverse.Utilities/VoiceManager.cs
rename to LibreMetaverse.Voice/VoiceManager.cs
index f058c026..0e11b2d1 100644
--- a/LibreMetaverse.Utilities/VoiceManager.cs
+++ b/LibreMetaverse.Voice/VoiceManager.cs
@@ -1,813 +1,815 @@
-/*
- * Copyright (c) 2006-2016, openmetaverse.co
- * All rights reserved.
- *
- * - Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- * - Neither the name of the openmetaverse.co nor the names
- * of its contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-using System;
-using System.Collections.Generic;
-using System.Net.Sockets;
-using System.Text;
-using System.IO;
-using System.Xml;
-using OpenMetaverse.StructuredData;
-using OpenMetaverse.Http;
-using OpenMetaverse.Interfaces;
-using OpenMetaverse.Messages.Linden;
-
-namespace OpenMetaverse.Utilities
-{
- public enum VoiceStatus
- {
- StatusLoginRetry,
- StatusLoggedIn,
- StatusJoining,
- StatusJoined,
- StatusLeftChannel,
- BeginErrorStatus,
- ErrorChannelFull,
- ErrorChannelLocked,
- ErrorNotAvailable,
- ErrorUnknown
- }
-
- public enum VoiceServiceType
- {
- /// Unknown voice service level
- Unknown,
- /// Spatialized local chat
- TypeA,
- /// Remote multi-party chat
- TypeB,
- /// One-to-one and small group chat
- TypeC
- }
-
- public partial class VoiceManager
- {
- public const int VOICE_MAJOR_VERSION = 1;
- public const string DAEMON_ARGS = " -p tcp -h -c -ll ";
- public const int DAEMON_LOG_LEVEL = 1;
- public const int DAEMON_PORT = 44124;
- public const string VOICE_RELEASE_SERVER = "bhr.vivox.com";
- public const string VOICE_DEBUG_SERVER = "bhd.vivox.com";
- public const string REQUEST_TERMINATOR = "\n\n\n";
-
- public delegate void LoginStateChangeCallback(int cookie, string accountHandle, int statusCode, string statusString, int state);
- public delegate void NewSessionCallback(int cookie, string accountHandle, string eventSessionHandle, int state, string nameString, string uriString);
- public delegate void SessionStateChangeCallback(int cookie, string uriString, int statusCode, string statusString, string eventSessionHandle, int state, bool isChannel, string nameString);
- public delegate void ParticipantStateChangeCallback(int cookie, string uriString, int statusCode, string statusString, int state, string nameString, string displayNameString, int participantType);
- public delegate void ParticipantPropertiesCallback(int cookie, string uriString, int statusCode, string statusString, bool isLocallyMuted, bool isModeratorMuted, bool isSpeaking, int volume, float energy);
- public delegate void AuxAudioPropertiesCallback(int cookie, float energy);
- public delegate void BasicActionCallback(int cookie, int statusCode, string statusString);
- public delegate void ConnectorCreatedCallback(int cookie, int statusCode, string statusString, string connectorHandle);
- public delegate void LoginCallback(int cookie, int statusCode, string statusString, string accountHandle);
- public delegate void SessionCreatedCallback(int cookie, int statusCode, string statusString, string sessionHandle);
- public delegate void DevicesCallback(int cookie, int statusCode, string statusString, string currentDevice);
- public delegate void ProvisionAccountCallback(string username, string password);
- public delegate void ParcelVoiceInfoCallback(string regionName, int localID, string channelURI);
-
- public event LoginStateChangeCallback OnLoginStateChange;
- public event NewSessionCallback OnNewSession;
- public event SessionStateChangeCallback OnSessionStateChange;
- public event ParticipantStateChangeCallback OnParticipantStateChange;
- public event ParticipantPropertiesCallback OnParticipantProperties;
- public event AuxAudioPropertiesCallback OnAuxAudioProperties;
- public event ConnectorCreatedCallback OnConnectorCreated;
- public event LoginCallback OnLogin;
- public event SessionCreatedCallback OnSessionCreated;
- public event BasicActionCallback OnSessionConnected;
- public event BasicActionCallback OnAccountLogout;
- public event BasicActionCallback OnConnectorInitiateShutdown;
- public event BasicActionCallback OnAccountChannelGetList;
- public event BasicActionCallback OnSessionTerminated;
- public event DevicesCallback OnCaptureDevices;
- public event DevicesCallback OnRenderDevices;
- public event ProvisionAccountCallback OnProvisionAccount;
- public event ParcelVoiceInfoCallback OnParcelVoiceInfo;
-
- public GridClient Client;
- public string VoiceServer = VOICE_RELEASE_SERVER;
- public bool Enabled;
-
- protected Voice.TCPPipe _DaemonPipe;
- protected VoiceStatus _Status;
- protected int _CommandCookie = 0;
- protected string _TuningSoundFile = string.Empty;
- protected Dictionary _ChannelMap = new Dictionary();
- protected List _CaptureDevices = new List();
- protected List _RenderDevices = new List();
-
- #region Response Processing Variables
-
- private bool isEvent;
- private bool isChannel;
- private bool isLocallyMuted;
- private bool isModeratorMuted;
- private bool isSpeaking;
- private int cookie;
- //private int returnCode;
- private int statusCode;
- private int volume;
- private int state;
- private int participantType;
- private float energy;
- private string statusString = string.Empty;
- //private string uuidString = string.Empty;
- private string actionString = string.Empty;
- private string connectorHandle = string.Empty;
- private string accountHandle = string.Empty;
- private string sessionHandle = string.Empty;
- private string eventSessionHandle = string.Empty;
- private string eventTypeString = string.Empty;
- private string uriString = string.Empty;
- private string nameString = string.Empty;
- //private string audioMediaString = string.Empty;
- private string displayNameString = string.Empty;
-
- #endregion Response Processing Variables
-
- public VoiceManager(GridClient client)
- {
- Client = client;
- Client.Network.RegisterEventCallback("RequiredVoiceVersion", RequiredVoiceVersionEventHandler);
-
- // Register callback handlers for the blocking functions
- RegisterCallbacks();
-
- Enabled = true;
- }
-
- public bool IsDaemonRunning()
- {
- throw new NotImplementedException();
- }
-
- public bool StartDaemon()
- {
- throw new NotImplementedException();
- }
-
- public void StopDaemon()
- {
- throw new NotImplementedException();
- }
-
- public bool ConnectToDaemon()
- {
- if (!Enabled) return false;
-
- return ConnectToDaemon("127.0.0.1", DAEMON_PORT);
- }
-
- public bool ConnectToDaemon(string address, int port)
- {
- if (!Enabled) return false;
-
- _DaemonPipe = new Voice.TCPPipe();
- _DaemonPipe.OnDisconnected += _DaemonPipe_OnDisconnected;
- _DaemonPipe.OnReceiveLine += _DaemonPipe_OnReceiveLine;
-
- var se = _DaemonPipe.Connect(address, port);
-
- if (se == null)
- {
- return true;
- }
- Console.WriteLine("Connection failed: " + se.Message);
- return false;
- }
-
- public Dictionary GetChannelMap()
- {
- return new Dictionary(_ChannelMap);
- }
-
- public List CurrentCaptureDevices()
- {
- return new List(_CaptureDevices);
- }
-
- public List CurrentRenderDevices()
- {
- return new List(_RenderDevices);
- }
-
- public string VoiceAccountFromUUID(UUID id)
- {
- var result = "x" + Convert.ToBase64String(id.GetBytes());
- return result.Replace('+', '-').Replace('/', '_');
- }
-
- public UUID UUIDFromVoiceAccount(string accountName)
- {
- if (accountName.Length == 25 && accountName[0] == 'x' && accountName[23] == '=' && accountName[24] == '=')
- {
- accountName = accountName.Replace('/', '_').Replace('+', '-');
- var idBytes = Convert.FromBase64String(accountName);
-
- return idBytes.Length == 16 ? new UUID(idBytes, 0) : UUID.Zero;
- }
- return UUID.Zero;
- }
-
- public string SIPURIFromVoiceAccount(string account)
- {
- return $"sip:{account}@{VoiceServer}";
- }
-
- public int RequestCaptureDevices()
- {
- if (_DaemonPipe.Connected)
- {
- _DaemonPipe.SendData(Encoding.ASCII.GetBytes(
- $"{REQUEST_TERMINATOR}"));
-
- return _CommandCookie - 1;
- }
- Logger.Log("VoiceManager.RequestCaptureDevices() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
- return -1;
- }
-
- public int RequestRenderDevices()
- {
- if (_DaemonPipe.Connected)
- {
- _DaemonPipe.SendData(Encoding.ASCII.GetBytes(
- $"{REQUEST_TERMINATOR}"));
-
- return _CommandCookie - 1;
- }
- Logger.Log("VoiceManager.RequestRenderDevices() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
- return -1;
- }
-
- public int RequestCreateConnector()
- {
- return RequestCreateConnector(VoiceServer);
- }
-
- public int RequestCreateConnector(string voiceServer)
- {
- if (_DaemonPipe.Connected)
- {
- VoiceServer = voiceServer;
-
- var accountServer = $"https://www.{VoiceServer}/api2/";
- var logPath = ".";
-
- var request = new StringBuilder();
- request.Append($"");
- request.Append("V2 SDK");
- request.Append($"{accountServer}");
- request.Append("");
- request.Append("false");
- request.Append($"{logPath}");
- request.Append("vivox-gateway");
- request.Append(".log");
- request.Append("0");
- request.Append("");
- request.Append("");
- request.Append(REQUEST_TERMINATOR);
-
- _DaemonPipe.SendData(Encoding.ASCII.GetBytes(request.ToString()));
-
- return _CommandCookie - 1;
- }
- else
- {
- Logger.Log("VoiceManager.CreateConnector() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
- return -1;
- }
- }
-
- private bool RequestVoiceInternal(string me, CapsClient.CompleteCallback callback, string capsName)
- {
- if (Enabled && Client.Network.Connected)
- {
- if (Client.Network.CurrentSim != null && Client.Network.CurrentSim.Caps != null)
- {
- var url = Client.Network.CurrentSim.Caps.CapabilityURI(capsName);
-
- if (url != null)
- {
- var request = new CapsClient(url);
- var body = new OSDMap();
- request.OnComplete += callback;
- request.PostRequestAsync(body, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT);
-
- return true;
- }
- Logger.Log("VoiceManager." + me + "(): " + capsName + " capability is missing",
- Helpers.LogLevel.Info, Client);
- return false;
- }
- }
-
- Logger.Log("VoiceManager.RequestVoiceInternal(): Voice system is currently disabled",
- Helpers.LogLevel.Info, Client);
- return false;
-
- }
-
- public bool RequestProvisionAccount()
- {
- return RequestVoiceInternal("RequestProvisionAccount", ProvisionCapsResponse, "ProvisionVoiceAccountRequest");
- }
-
- public bool RequestParcelVoiceInfo()
- {
- return RequestVoiceInternal("RequestParcelVoiceInfo", ParcelVoiceInfoResponse, "ParcelVoiceInfoRequest");
- }
-
- public int RequestLogin(string accountName, string password, string connectorHandle)
- {
- if (_DaemonPipe.Connected)
- {
- var request = new StringBuilder();
- request.Append($"");
- request.Append($"{connectorHandle}");
- request.Append($"{accountName}");
- request.Append($"{password}");
- request.Append("VerifyAnswer");
- request.Append("");
- request.Append("10");
- request.Append("false");
- request.Append("");
- request.Append(REQUEST_TERMINATOR);
-
- _DaemonPipe.SendData(Encoding.ASCII.GetBytes(request.ToString()));
-
- return _CommandCookie - 1;
- }
- else
- {
- Logger.Log("VoiceManager.Login() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
- return -1;
- }
- }
-
- public int RequestSetRenderDevice(string deviceName)
- {
- if (_DaemonPipe.Connected)
- {
- _DaemonPipe.SendData(Encoding.ASCII.GetBytes(
- $"{deviceName}{REQUEST_TERMINATOR}"));
-
- return _CommandCookie - 1;
- }
- else
- {
- Logger.Log("VoiceManager.RequestSetRenderDevice() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
- return -1;
- }
- }
-
- public int RequestStartTuningMode(int duration)
- {
- if (_DaemonPipe.Connected)
- {
- _DaemonPipe.SendData(Encoding.ASCII.GetBytes(
- $"{duration}{REQUEST_TERMINATOR}"));
-
- return _CommandCookie - 1;
- }
- else
- {
- Logger.Log("VoiceManager.RequestStartTuningMode() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
- return -1;
- }
- }
-
- public int RequestStopTuningMode()
- {
- if (_DaemonPipe.Connected)
- {
- _DaemonPipe.SendData(Encoding.ASCII.GetBytes(
- $"{REQUEST_TERMINATOR}"));
-
- return _CommandCookie - 1;
- }
- else
- {
- Logger.Log("VoiceManager.RequestStopTuningMode() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
- return _CommandCookie - 1;
- }
- }
-
- public int RequestSetSpeakerVolume(int volume_)
- {
- if (volume_ < 0 || volume_ > 100)
- throw new ArgumentException("volume must be between 0 and 100", nameof(volume_));
-
- if (_DaemonPipe.Connected)
- {
- _DaemonPipe.SendData(Encoding.ASCII.GetBytes(
- $"{volume_}{REQUEST_TERMINATOR}"));
-
- return _CommandCookie - 1;
- }
- else
- {
- Logger.Log("VoiceManager.RequestSetSpeakerVolume() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
- return -1;
- }
- }
-
- public int RequestSetCaptureVolume(int volume_)
- {
- if (volume_ < 0 || volume_ > 100)
- throw new ArgumentException("volume must be between 0 and 100", nameof(volume_));
-
- if (_DaemonPipe.Connected)
- {
- _DaemonPipe.SendData(Encoding.ASCII.GetBytes(
- $"{volume_}{REQUEST_TERMINATOR}"));
-
- return _CommandCookie - 1;
- }
- else
- {
- Logger.Log("VoiceManager.RequestSetCaptureVolume() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
- return -1;
- }
- }
-
- ///
- /// Does not appear to be working
- ///
- ///
- ///
- public int RequestRenderAudioStart(string fileName, bool loop)
- {
- if (_DaemonPipe.Connected)
- {
- _TuningSoundFile = fileName;
-
- _DaemonPipe.SendData(Encoding.ASCII.GetBytes(
- $"{_TuningSoundFile}{(loop ? "1" : "0")}{REQUEST_TERMINATOR}"));
-
- return _CommandCookie - 1;
- }
- else
- {
- Logger.Log("VoiceManager.RequestRenderAudioStart() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
- return -1;
- }
- }
-
- public int RequestRenderAudioStop()
- {
- if (_DaemonPipe.Connected)
- {
- _DaemonPipe.SendData(Encoding.ASCII.GetBytes(
- $"{_TuningSoundFile}{REQUEST_TERMINATOR}"));
-
- return _CommandCookie - 1;
- }
- else
- {
- Logger.Log("VoiceManager.RequestRenderAudioStop() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
- return -1;
- }
- }
-
- #region Callbacks
-
- private void RequiredVoiceVersionEventHandler(string capsKey, IMessage message, Simulator simulator)
- {
- var msg = (RequiredVoiceVersionMessage)message;
-
- if (VOICE_MAJOR_VERSION != msg.MajorVersion)
- {
- Logger.Log(
- $"Voice version mismatch! Got {msg.MajorVersion}, expecting {VOICE_MAJOR_VERSION}. Disabling the voice manager", Helpers.LogLevel.Error, Client);
- Enabled = false;
- }
- else
- {
- Logger.DebugLog("Voice version " + msg.MajorVersion + " verified", Client);
- }
- }
-
- private void ProvisionCapsResponse(CapsClient client, OSD response, Exception error)
- {
- if (!(response is OSDMap respMap)) return;
-
- if (OnProvisionAccount == null) return;
- try { OnProvisionAccount(respMap["username"].AsString(), respMap["password"].AsString()); }
- catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
- }
-
- private void ParcelVoiceInfoResponse(CapsClient client, OSD response, Exception error)
- {
- if (!(response is OSDMap respMap)) return;
-
- var regionName = respMap["region_name"].AsString();
- var localID = respMap["parcel_local_id"].AsInteger();
-
- string channelURI = null;
- if (respMap["voice_credentials"] is OSDMap)
- {
- var creds = (OSDMap)respMap["voice_credentials"];
- channelURI = creds["channel_uri"].AsString();
- }
-
- OnParcelVoiceInfo?.Invoke(regionName, localID, channelURI);
- }
-
- private static void _DaemonPipe_OnDisconnected(SocketException se)
- {
- if (se != null) Console.WriteLine("Disconnected! " + se.Message);
- else Console.WriteLine("Disconnected!");
- }
-
- private void _DaemonPipe_OnReceiveLine(string line)
- {
- var reader = new XmlTextReader(new StringReader(line));
-
- while (reader.Read())
- {
- switch (reader.NodeType)
- {
- case XmlNodeType.Element:
- {
- if (reader.Depth == 0)
- {
- isEvent = (reader.Name == "Event");
-
- if (isEvent || reader.Name == "Response")
- {
- for (var i = 0; i < reader.AttributeCount; i++)
- {
- reader.MoveToAttribute(i);
-
- if (reader.Name == "action")
- actionString = reader.Value;
- else if (reader.Name == "type")
- eventTypeString = reader.Value;
- }
- }
- }
- else
- {
- switch (reader.Name)
- {
- case "InputXml":
- cookie = -1;
-
- // Parse through here to get the cookie value
- reader.Read();
- if (reader.Name == "Request")
- {
- for (var i = 0; i < reader.AttributeCount; i++)
- {
- reader.MoveToAttribute(i);
-
- if (reader.Name != "requestId") continue;
- int.TryParse(reader.Value, out cookie);
- break;
- }
- }
-
- if (cookie == -1)
- {
- Logger.Log(
- "VoiceManager._DaemonPipe_OnReceiveLine(): Failed to parse InputXml for the cookie",
- Helpers.LogLevel.Warning, Client);
- }
- break;
- case "CaptureDevices":
- _CaptureDevices.Clear();
- break;
- case "RenderDevices":
- _RenderDevices.Clear();
- break;
-// case "ReturnCode":
-// returnCode = reader.ReadElementContentAsInt();
-// break;
- case "StatusCode":
- statusCode = reader.ReadElementContentAsInt();
- break;
- case "StatusString":
- statusString = reader.ReadElementContentAsString();
- break;
- case "State":
- state = reader.ReadElementContentAsInt();
- break;
- case "ConnectorHandle":
- connectorHandle = reader.ReadElementContentAsString();
- break;
- case "AccountHandle":
- accountHandle = reader.ReadElementContentAsString();
- break;
- case "SessionHandle":
- sessionHandle = reader.ReadElementContentAsString();
- break;
- case "URI":
- uriString = reader.ReadElementContentAsString();
- break;
- case "IsChannel":
- isChannel = reader.ReadElementContentAsBoolean();
- break;
- case "Name":
- nameString = reader.ReadElementContentAsString();
- break;
-// case "AudioMedia":
-// audioMediaString = reader.ReadElementContentAsString();
-// break;
- case "ChannelName":
- nameString = reader.ReadElementContentAsString();
- break;
- case "ParticipantURI":
- uriString = reader.ReadElementContentAsString();
- break;
- case "DisplayName":
- displayNameString = reader.ReadElementContentAsString();
- break;
- case "AccountName":
- nameString = reader.ReadElementContentAsString();
- break;
- case "ParticipantType":
- participantType = reader.ReadElementContentAsInt();
- break;
- case "IsLocallyMuted":
- isLocallyMuted = reader.ReadElementContentAsBoolean();
- break;
- case "IsModeratorMuted":
- isModeratorMuted = reader.ReadElementContentAsBoolean();
- break;
- case "IsSpeaking":
- isSpeaking = reader.ReadElementContentAsBoolean();
- break;
- case "Volume":
- volume = reader.ReadElementContentAsInt();
- break;
- case "Energy":
- energy = reader.ReadElementContentAsFloat();
- break;
- case "MicEnergy":
- energy = reader.ReadElementContentAsFloat();
- break;
- case "ChannelURI":
- uriString = reader.ReadElementContentAsString();
- break;
- case "ChannelListResult":
- _ChannelMap[nameString] = uriString;
- break;
- case "CaptureDevice":
- reader.Read();
- _CaptureDevices.Add(reader.ReadElementContentAsString());
- break;
- case "CurrentCaptureDevice":
- reader.Read();
- nameString = reader.ReadElementContentAsString();
- break;
- case "RenderDevice":
- reader.Read();
- _RenderDevices.Add(reader.ReadElementContentAsString());
- break;
- case "CurrentRenderDevice":
- reader.Read();
- nameString = reader.ReadElementContentAsString();
- break;
- }
- }
-
- break;
- }
- case XmlNodeType.EndElement:
- if (reader.Depth == 0)
- ProcessEvent();
- break;
- case XmlNodeType.None:
- break;
- case XmlNodeType.Attribute:
- break;
- case XmlNodeType.Text:
- break;
- case XmlNodeType.CDATA:
- break;
- case XmlNodeType.EntityReference:
- break;
- case XmlNodeType.Entity:
- break;
- case XmlNodeType.ProcessingInstruction:
- break;
- case XmlNodeType.Comment:
- break;
- case XmlNodeType.Document:
- break;
- case XmlNodeType.DocumentType:
- break;
- case XmlNodeType.DocumentFragment:
- break;
- case XmlNodeType.Notation:
- break;
- case XmlNodeType.Whitespace:
- break;
- case XmlNodeType.SignificantWhitespace:
- break;
- case XmlNodeType.EndEntity:
- break;
- case XmlNodeType.XmlDeclaration:
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
-
- if (isEvent)
- {
- }
-
- //Client.DebugLog("VOICE: " + line);
- }
-
- private void ProcessEvent()
- {
- if (isEvent)
- {
- switch (eventTypeString)
- {
- case "LoginStateChangeEvent":
- OnLoginStateChange?.Invoke(cookie, accountHandle, statusCode, statusString, state);
- break;
- case "SessionNewEvent":
- OnNewSession?.Invoke(cookie, accountHandle, eventSessionHandle, state, nameString, uriString);
- break;
- case "SessionStateChangeEvent":
- OnSessionStateChange?.Invoke(cookie, uriString, statusCode, statusString, eventSessionHandle, state, isChannel, nameString);
- break;
- case "ParticipantStateChangeEvent":
- OnParticipantStateChange?.Invoke(cookie, uriString, statusCode, statusString, state, nameString, displayNameString, participantType);
- break;
- case "ParticipantPropertiesEvent":
- OnParticipantProperties?.Invoke(cookie, uriString, statusCode, statusString, isLocallyMuted, isModeratorMuted, isSpeaking, volume, energy);
- break;
- case "AuxAudioPropertiesEvent":
- OnAuxAudioProperties?.Invoke(cookie, energy);
- break;
- }
- }
- else
- {
- switch (actionString)
- {
- case "Connector.Create.1":
- OnConnectorCreated?.Invoke(cookie, statusCode, statusString, connectorHandle);
- break;
- case "Account.Login.1":
- OnLogin?.Invoke(cookie, statusCode, statusString, accountHandle);
- break;
- case "Session.Create.1":
- OnSessionCreated?.Invoke(cookie, statusCode, statusString, sessionHandle);
- break;
- case "Session.Connect.1":
- OnSessionConnected?.Invoke(cookie, statusCode, statusString);
- break;
- case "Session.Terminate.1":
- OnSessionTerminated?.Invoke(cookie, statusCode, statusString);
- break;
- case "Account.Logout.1":
- OnAccountLogout?.Invoke(cookie, statusCode, statusString);
- break;
- case "Connector.InitiateShutdown.1":
- OnConnectorInitiateShutdown?.Invoke(cookie, statusCode, statusString);
- break;
- case "Account.ChannelGetList.1":
- OnAccountChannelGetList?.Invoke(cookie, statusCode, statusString);
- break;
- case "Aux.GetCaptureDevices.1":
- OnCaptureDevices?.Invoke(cookie, statusCode, statusString, nameString);
- break;
- case "Aux.GetRenderDevices.1":
- OnRenderDevices?.Invoke(cookie, statusCode, statusString, nameString);
- break;
- }
- }
- }
-
- #endregion Callbacks
- }
-}
+/*
+ * Copyright (c) 2006-2016, openmetaverse.co
+ * Copyright (c) 2021-2022, Sjofn LLC.
+ * All rights reserved.
+ *
+ * - Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * - Neither the name of the openmetaverse.co nor the names
+ * of its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Net.Sockets;
+using System.Text;
+using System.IO;
+using System.Xml;
+using OpenMetaverse.StructuredData;
+using OpenMetaverse;
+using OpenMetaverse.Http;
+using OpenMetaverse.Interfaces;
+using OpenMetaverse.Messages.Linden;
+
+namespace LibreMetaverse.Voice
+{
+ public enum VoiceStatus
+ {
+ StatusLoginRetry,
+ StatusLoggedIn,
+ StatusJoining,
+ StatusJoined,
+ StatusLeftChannel,
+ BeginErrorStatus,
+ ErrorChannelFull,
+ ErrorChannelLocked,
+ ErrorNotAvailable,
+ ErrorUnknown
+ }
+
+ public enum VoiceServiceType
+ {
+ /// Unknown voice service level
+ Unknown,
+ /// Spatialized local chat
+ TypeA,
+ /// Remote multi-party chat
+ TypeB,
+ /// One-to-one and small group chat
+ TypeC
+ }
+
+ public partial class VoiceManager
+ {
+ public const int VOICE_MAJOR_VERSION = 1;
+ public const string DAEMON_ARGS = " -p tcp -h -c -ll ";
+ public const int DAEMON_LOG_LEVEL = 1;
+ public const int DAEMON_PORT = 44124;
+ public const string VOICE_RELEASE_SERVER = "bhr.vivox.com";
+ public const string VOICE_DEBUG_SERVER = "bhd.vivox.com";
+ public const string REQUEST_TERMINATOR = "\n\n\n";
+
+ public delegate void LoginStateChangeCallback(int cookie, string accountHandle, int statusCode, string statusString, int state);
+ public delegate void NewSessionCallback(int cookie, string accountHandle, string eventSessionHandle, int state, string nameString, string uriString);
+ public delegate void SessionStateChangeCallback(int cookie, string uriString, int statusCode, string statusString, string eventSessionHandle, int state, bool isChannel, string nameString);
+ public delegate void ParticipantStateChangeCallback(int cookie, string uriString, int statusCode, string statusString, int state, string nameString, string displayNameString, int participantType);
+ public delegate void ParticipantPropertiesCallback(int cookie, string uriString, int statusCode, string statusString, bool isLocallyMuted, bool isModeratorMuted, bool isSpeaking, int volume, float energy);
+ public delegate void AuxAudioPropertiesCallback(int cookie, float energy);
+ public delegate void BasicActionCallback(int cookie, int statusCode, string statusString);
+ public delegate void ConnectorCreatedCallback(int cookie, int statusCode, string statusString, string connectorHandle);
+ public delegate void LoginCallback(int cookie, int statusCode, string statusString, string accountHandle);
+ public delegate void SessionCreatedCallback(int cookie, int statusCode, string statusString, string sessionHandle);
+ public delegate void DevicesCallback(int cookie, int statusCode, string statusString, string currentDevice);
+ public delegate void ProvisionAccountCallback(string username, string password);
+ public delegate void ParcelVoiceInfoCallback(string regionName, int localID, string channelURI);
+
+ public event LoginStateChangeCallback OnLoginStateChange;
+ public event NewSessionCallback OnNewSession;
+ public event SessionStateChangeCallback OnSessionStateChange;
+ public event ParticipantStateChangeCallback OnParticipantStateChange;
+ public event ParticipantPropertiesCallback OnParticipantProperties;
+ public event AuxAudioPropertiesCallback OnAuxAudioProperties;
+ public event ConnectorCreatedCallback OnConnectorCreated;
+ public event LoginCallback OnLogin;
+ public event SessionCreatedCallback OnSessionCreated;
+ public event BasicActionCallback OnSessionConnected;
+ public event BasicActionCallback OnAccountLogout;
+ public event BasicActionCallback OnConnectorInitiateShutdown;
+ public event BasicActionCallback OnAccountChannelGetList;
+ public event BasicActionCallback OnSessionTerminated;
+ public event DevicesCallback OnCaptureDevices;
+ public event DevicesCallback OnRenderDevices;
+ public event ProvisionAccountCallback OnProvisionAccount;
+ public event ParcelVoiceInfoCallback OnParcelVoiceInfo;
+
+ public GridClient Client;
+ public string VoiceServer = VOICE_RELEASE_SERVER;
+ public bool Enabled;
+
+ protected Voice.TCPPipe _DaemonPipe;
+ protected VoiceStatus _Status;
+ protected int _CommandCookie = 0;
+ protected string _TuningSoundFile = string.Empty;
+ protected Dictionary _ChannelMap = new Dictionary();
+ protected List _CaptureDevices = new List();
+ protected List _RenderDevices = new List();
+
+ #region Response Processing Variables
+
+ private bool isEvent;
+ private bool isChannel;
+ private bool isLocallyMuted;
+ private bool isModeratorMuted;
+ private bool isSpeaking;
+ private int cookie;
+ //private int returnCode;
+ private int statusCode;
+ private int volume;
+ private int state;
+ private int participantType;
+ private float energy;
+ private string statusString = string.Empty;
+ //private string uuidString = string.Empty;
+ private string actionString = string.Empty;
+ private string connectorHandle = string.Empty;
+ private string accountHandle = string.Empty;
+ private string sessionHandle = string.Empty;
+ private string eventSessionHandle = string.Empty;
+ private string eventTypeString = string.Empty;
+ private string uriString = string.Empty;
+ private string nameString = string.Empty;
+ //private string audioMediaString = string.Empty;
+ private string displayNameString = string.Empty;
+
+ #endregion Response Processing Variables
+
+ public VoiceManager(GridClient client)
+ {
+ Client = client;
+ Client.Network.RegisterEventCallback("RequiredVoiceVersion", RequiredVoiceVersionEventHandler);
+
+ // Register callback handlers for the blocking functions
+ RegisterCallbacks();
+
+ Enabled = true;
+ }
+
+ public bool IsDaemonRunning()
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool StartDaemon()
+ {
+ throw new NotImplementedException();
+ }
+
+ public void StopDaemon()
+ {
+ throw new NotImplementedException();
+ }
+
+ public bool ConnectToDaemon()
+ {
+ if (!Enabled) return false;
+
+ return ConnectToDaemon("127.0.0.1", DAEMON_PORT);
+ }
+
+ public bool ConnectToDaemon(string address, int port)
+ {
+ if (!Enabled) return false;
+
+ _DaemonPipe = new Voice.TCPPipe();
+ _DaemonPipe.OnDisconnected += _DaemonPipe_OnDisconnected;
+ _DaemonPipe.OnReceiveLine += _DaemonPipe_OnReceiveLine;
+
+ var se = _DaemonPipe.Connect(address, port);
+
+ if (se == null)
+ {
+ return true;
+ }
+ Console.WriteLine("Connection failed: " + se.Message);
+ return false;
+ }
+
+ public Dictionary GetChannelMap()
+ {
+ return new Dictionary(_ChannelMap);
+ }
+
+ public List CurrentCaptureDevices()
+ {
+ return new List(_CaptureDevices);
+ }
+
+ public List CurrentRenderDevices()
+ {
+ return new List(_RenderDevices);
+ }
+
+ public string VoiceAccountFromUUID(UUID id)
+ {
+ var result = "x" + Convert.ToBase64String(id.GetBytes());
+ return result.Replace('+', '-').Replace('/', '_');
+ }
+
+ public UUID UUIDFromVoiceAccount(string accountName)
+ {
+ if (accountName.Length == 25 && accountName[0] == 'x' && accountName[23] == '=' && accountName[24] == '=')
+ {
+ accountName = accountName.Replace('/', '_').Replace('+', '-');
+ var idBytes = Convert.FromBase64String(accountName);
+
+ return idBytes.Length == 16 ? new UUID(idBytes, 0) : UUID.Zero;
+ }
+ return UUID.Zero;
+ }
+
+ public string SIPURIFromVoiceAccount(string account)
+ {
+ return $"sip:{account}@{VoiceServer}";
+ }
+
+ public int RequestCaptureDevices()
+ {
+ if (_DaemonPipe.Connected)
+ {
+ _DaemonPipe.SendData(Encoding.ASCII.GetBytes(
+ $"{REQUEST_TERMINATOR}"));
+
+ return _CommandCookie - 1;
+ }
+ Logger.Log("VoiceManager.RequestCaptureDevices() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
+ return -1;
+ }
+
+ public int RequestRenderDevices()
+ {
+ if (_DaemonPipe.Connected)
+ {
+ _DaemonPipe.SendData(Encoding.ASCII.GetBytes(
+ $"{REQUEST_TERMINATOR}"));
+
+ return _CommandCookie - 1;
+ }
+ Logger.Log("VoiceManager.RequestRenderDevices() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
+ return -1;
+ }
+
+ public int RequestCreateConnector()
+ {
+ return RequestCreateConnector(VoiceServer);
+ }
+
+ public int RequestCreateConnector(string voiceServer)
+ {
+ if (_DaemonPipe.Connected)
+ {
+ VoiceServer = voiceServer;
+
+ var accountServer = $"https://www.{VoiceServer}/api2/";
+ var logPath = ".";
+
+ var request = new StringBuilder();
+ request.Append($"");
+ request.Append("V2 SDK");
+ request.Append($"{accountServer}");
+ request.Append("");
+ request.Append("false");
+ request.Append($"{logPath}");
+ request.Append("vivox-gateway");
+ request.Append(".log");
+ request.Append("0");
+ request.Append("");
+ request.Append("");
+ request.Append(REQUEST_TERMINATOR);
+
+ _DaemonPipe.SendData(Encoding.ASCII.GetBytes(request.ToString()));
+
+ return _CommandCookie - 1;
+ }
+ else
+ {
+ Logger.Log("VoiceManager.CreateConnector() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
+ return -1;
+ }
+ }
+
+ private bool RequestVoiceInternal(string me, CapsClient.CompleteCallback callback, string capsName)
+ {
+ if (Enabled && Client.Network.Connected)
+ {
+ if (Client.Network.CurrentSim != null && Client.Network.CurrentSim.Caps != null)
+ {
+ var url = Client.Network.CurrentSim.Caps.CapabilityURI(capsName);
+
+ if (url != null)
+ {
+ var request = new CapsClient(url);
+ var body = new OSDMap();
+ request.OnComplete += callback;
+ request.PostRequestAsync(body, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT);
+
+ return true;
+ }
+ Logger.Log("VoiceManager." + me + "(): " + capsName + " capability is missing",
+ Helpers.LogLevel.Info, Client);
+ return false;
+ }
+ }
+
+ Logger.Log("VoiceManager.RequestVoiceInternal(): Voice system is currently disabled",
+ Helpers.LogLevel.Info, Client);
+ return false;
+
+ }
+
+ public bool RequestProvisionAccount()
+ {
+ return RequestVoiceInternal("RequestProvisionAccount", ProvisionCapsResponse, "ProvisionVoiceAccountRequest");
+ }
+
+ public bool RequestParcelVoiceInfo()
+ {
+ return RequestVoiceInternal("RequestParcelVoiceInfo", ParcelVoiceInfoResponse, "ParcelVoiceInfoRequest");
+ }
+
+ public int RequestLogin(string accountName, string password, string connectorHandle)
+ {
+ if (_DaemonPipe.Connected)
+ {
+ var request = new StringBuilder();
+ request.Append($"");
+ request.Append($"{connectorHandle}");
+ request.Append($"{accountName}");
+ request.Append($"{password}");
+ request.Append("VerifyAnswer");
+ request.Append("");
+ request.Append("10");
+ request.Append("false");
+ request.Append("");
+ request.Append(REQUEST_TERMINATOR);
+
+ _DaemonPipe.SendData(Encoding.ASCII.GetBytes(request.ToString()));
+
+ return _CommandCookie - 1;
+ }
+ else
+ {
+ Logger.Log("VoiceManager.Login() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
+ return -1;
+ }
+ }
+
+ public int RequestSetRenderDevice(string deviceName)
+ {
+ if (_DaemonPipe.Connected)
+ {
+ _DaemonPipe.SendData(Encoding.ASCII.GetBytes(
+ $"{deviceName}{REQUEST_TERMINATOR}"));
+
+ return _CommandCookie - 1;
+ }
+ else
+ {
+ Logger.Log("VoiceManager.RequestSetRenderDevice() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
+ return -1;
+ }
+ }
+
+ public int RequestStartTuningMode(int duration)
+ {
+ if (_DaemonPipe.Connected)
+ {
+ _DaemonPipe.SendData(Encoding.ASCII.GetBytes(
+ $"{duration}{REQUEST_TERMINATOR}"));
+
+ return _CommandCookie - 1;
+ }
+ else
+ {
+ Logger.Log("VoiceManager.RequestStartTuningMode() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
+ return -1;
+ }
+ }
+
+ public int RequestStopTuningMode()
+ {
+ if (_DaemonPipe.Connected)
+ {
+ _DaemonPipe.SendData(Encoding.ASCII.GetBytes(
+ $"{REQUEST_TERMINATOR}"));
+
+ return _CommandCookie - 1;
+ }
+ else
+ {
+ Logger.Log("VoiceManager.RequestStopTuningMode() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
+ return _CommandCookie - 1;
+ }
+ }
+
+ public int RequestSetSpeakerVolume(int volume_)
+ {
+ if (volume_ < 0 || volume_ > 100)
+ throw new ArgumentException("volume must be between 0 and 100", nameof(volume_));
+
+ if (_DaemonPipe.Connected)
+ {
+ _DaemonPipe.SendData(Encoding.ASCII.GetBytes(
+ $"{volume_}{REQUEST_TERMINATOR}"));
+
+ return _CommandCookie - 1;
+ }
+ else
+ {
+ Logger.Log("VoiceManager.RequestSetSpeakerVolume() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
+ return -1;
+ }
+ }
+
+ public int RequestSetCaptureVolume(int volume_)
+ {
+ if (volume_ < 0 || volume_ > 100)
+ throw new ArgumentException("volume must be between 0 and 100", nameof(volume_));
+
+ if (_DaemonPipe.Connected)
+ {
+ _DaemonPipe.SendData(Encoding.ASCII.GetBytes(
+ $"{volume_}{REQUEST_TERMINATOR}"));
+
+ return _CommandCookie - 1;
+ }
+ else
+ {
+ Logger.Log("VoiceManager.RequestSetCaptureVolume() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
+ return -1;
+ }
+ }
+
+ ///
+ /// Does not appear to be working
+ ///
+ ///
+ ///
+ public int RequestRenderAudioStart(string fileName, bool loop)
+ {
+ if (_DaemonPipe.Connected)
+ {
+ _TuningSoundFile = fileName;
+
+ _DaemonPipe.SendData(Encoding.ASCII.GetBytes(
+ $"{_TuningSoundFile}{(loop ? "1" : "0")}{REQUEST_TERMINATOR}"));
+
+ return _CommandCookie - 1;
+ }
+ else
+ {
+ Logger.Log("VoiceManager.RequestRenderAudioStart() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
+ return -1;
+ }
+ }
+
+ public int RequestRenderAudioStop()
+ {
+ if (_DaemonPipe.Connected)
+ {
+ _DaemonPipe.SendData(Encoding.ASCII.GetBytes(
+ $"{_TuningSoundFile}{REQUEST_TERMINATOR}"));
+
+ return _CommandCookie - 1;
+ }
+ else
+ {
+ Logger.Log("VoiceManager.RequestRenderAudioStop() called when the daemon pipe is disconnected", Helpers.LogLevel.Error, Client);
+ return -1;
+ }
+ }
+
+ #region Callbacks
+
+ private void RequiredVoiceVersionEventHandler(string capsKey, IMessage message, Simulator simulator)
+ {
+ var msg = (RequiredVoiceVersionMessage)message;
+
+ if (VOICE_MAJOR_VERSION != msg.MajorVersion)
+ {
+ Logger.Log(
+ $"Voice version mismatch! Got {msg.MajorVersion}, expecting {VOICE_MAJOR_VERSION}. Disabling the voice manager", Helpers.LogLevel.Error, Client);
+ Enabled = false;
+ }
+ else
+ {
+ Logger.DebugLog("Voice version " + msg.MajorVersion + " verified", Client);
+ }
+ }
+
+ private void ProvisionCapsResponse(CapsClient client, OSD response, Exception error)
+ {
+ if (!(response is OSDMap respMap)) return;
+
+ if (OnProvisionAccount == null) return;
+ try { OnProvisionAccount(respMap["username"].AsString(), respMap["password"].AsString()); }
+ catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); }
+ }
+
+ private void ParcelVoiceInfoResponse(CapsClient client, OSD response, Exception error)
+ {
+ if (!(response is OSDMap respMap)) return;
+
+ var regionName = respMap["region_name"].AsString();
+ var localID = respMap["parcel_local_id"].AsInteger();
+
+ string channelURI = null;
+ if (respMap["voice_credentials"] is OSDMap)
+ {
+ var creds = (OSDMap)respMap["voice_credentials"];
+ channelURI = creds["channel_uri"].AsString();
+ }
+
+ OnParcelVoiceInfo?.Invoke(regionName, localID, channelURI);
+ }
+
+ private static void _DaemonPipe_OnDisconnected(SocketException se)
+ {
+ if (se != null) Console.WriteLine("Disconnected! " + se.Message);
+ else Console.WriteLine("Disconnected!");
+ }
+
+ private void _DaemonPipe_OnReceiveLine(string line)
+ {
+ var reader = new XmlTextReader(new StringReader(line));
+
+ while (reader.Read())
+ {
+ switch (reader.NodeType)
+ {
+ case XmlNodeType.Element:
+ {
+ if (reader.Depth == 0)
+ {
+ isEvent = (reader.Name == "Event");
+
+ if (isEvent || reader.Name == "Response")
+ {
+ for (var i = 0; i < reader.AttributeCount; i++)
+ {
+ reader.MoveToAttribute(i);
+
+ if (reader.Name == "action")
+ actionString = reader.Value;
+ else if (reader.Name == "type")
+ eventTypeString = reader.Value;
+ }
+ }
+ }
+ else
+ {
+ switch (reader.Name)
+ {
+ case "InputXml":
+ cookie = -1;
+
+ // Parse through here to get the cookie value
+ reader.Read();
+ if (reader.Name == "Request")
+ {
+ for (var i = 0; i < reader.AttributeCount; i++)
+ {
+ reader.MoveToAttribute(i);
+
+ if (reader.Name != "requestId") continue;
+ int.TryParse(reader.Value, out cookie);
+ break;
+ }
+ }
+
+ if (cookie == -1)
+ {
+ Logger.Log(
+ "VoiceManager._DaemonPipe_OnReceiveLine(): Failed to parse InputXml for the cookie",
+ Helpers.LogLevel.Warning, Client);
+ }
+ break;
+ case "CaptureDevices":
+ _CaptureDevices.Clear();
+ break;
+ case "RenderDevices":
+ _RenderDevices.Clear();
+ break;
+// case "ReturnCode":
+// returnCode = reader.ReadElementContentAsInt();
+// break;
+ case "StatusCode":
+ statusCode = reader.ReadElementContentAsInt();
+ break;
+ case "StatusString":
+ statusString = reader.ReadElementContentAsString();
+ break;
+ case "State":
+ state = reader.ReadElementContentAsInt();
+ break;
+ case "ConnectorHandle":
+ connectorHandle = reader.ReadElementContentAsString();
+ break;
+ case "AccountHandle":
+ accountHandle = reader.ReadElementContentAsString();
+ break;
+ case "SessionHandle":
+ sessionHandle = reader.ReadElementContentAsString();
+ break;
+ case "URI":
+ uriString = reader.ReadElementContentAsString();
+ break;
+ case "IsChannel":
+ isChannel = reader.ReadElementContentAsBoolean();
+ break;
+ case "Name":
+ nameString = reader.ReadElementContentAsString();
+ break;
+// case "AudioMedia":
+// audioMediaString = reader.ReadElementContentAsString();
+// break;
+ case "ChannelName":
+ nameString = reader.ReadElementContentAsString();
+ break;
+ case "ParticipantURI":
+ uriString = reader.ReadElementContentAsString();
+ break;
+ case "DisplayName":
+ displayNameString = reader.ReadElementContentAsString();
+ break;
+ case "AccountName":
+ nameString = reader.ReadElementContentAsString();
+ break;
+ case "ParticipantType":
+ participantType = reader.ReadElementContentAsInt();
+ break;
+ case "IsLocallyMuted":
+ isLocallyMuted = reader.ReadElementContentAsBoolean();
+ break;
+ case "IsModeratorMuted":
+ isModeratorMuted = reader.ReadElementContentAsBoolean();
+ break;
+ case "IsSpeaking":
+ isSpeaking = reader.ReadElementContentAsBoolean();
+ break;
+ case "Volume":
+ volume = reader.ReadElementContentAsInt();
+ break;
+ case "Energy":
+ energy = reader.ReadElementContentAsFloat();
+ break;
+ case "MicEnergy":
+ energy = reader.ReadElementContentAsFloat();
+ break;
+ case "ChannelURI":
+ uriString = reader.ReadElementContentAsString();
+ break;
+ case "ChannelListResult":
+ _ChannelMap[nameString] = uriString;
+ break;
+ case "CaptureDevice":
+ reader.Read();
+ _CaptureDevices.Add(reader.ReadElementContentAsString());
+ break;
+ case "CurrentCaptureDevice":
+ reader.Read();
+ nameString = reader.ReadElementContentAsString();
+ break;
+ case "RenderDevice":
+ reader.Read();
+ _RenderDevices.Add(reader.ReadElementContentAsString());
+ break;
+ case "CurrentRenderDevice":
+ reader.Read();
+ nameString = reader.ReadElementContentAsString();
+ break;
+ }
+ }
+
+ break;
+ }
+ case XmlNodeType.EndElement:
+ if (reader.Depth == 0)
+ ProcessEvent();
+ break;
+ case XmlNodeType.None:
+ break;
+ case XmlNodeType.Attribute:
+ break;
+ case XmlNodeType.Text:
+ break;
+ case XmlNodeType.CDATA:
+ break;
+ case XmlNodeType.EntityReference:
+ break;
+ case XmlNodeType.Entity:
+ break;
+ case XmlNodeType.ProcessingInstruction:
+ break;
+ case XmlNodeType.Comment:
+ break;
+ case XmlNodeType.Document:
+ break;
+ case XmlNodeType.DocumentType:
+ break;
+ case XmlNodeType.DocumentFragment:
+ break;
+ case XmlNodeType.Notation:
+ break;
+ case XmlNodeType.Whitespace:
+ break;
+ case XmlNodeType.SignificantWhitespace:
+ break;
+ case XmlNodeType.EndEntity:
+ break;
+ case XmlNodeType.XmlDeclaration:
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+
+ if (isEvent)
+ {
+ }
+
+ //Client.DebugLog("VOICE: " + line);
+ }
+
+ private void ProcessEvent()
+ {
+ if (isEvent)
+ {
+ switch (eventTypeString)
+ {
+ case "LoginStateChangeEvent":
+ OnLoginStateChange?.Invoke(cookie, accountHandle, statusCode, statusString, state);
+ break;
+ case "SessionNewEvent":
+ OnNewSession?.Invoke(cookie, accountHandle, eventSessionHandle, state, nameString, uriString);
+ break;
+ case "SessionStateChangeEvent":
+ OnSessionStateChange?.Invoke(cookie, uriString, statusCode, statusString, eventSessionHandle, state, isChannel, nameString);
+ break;
+ case "ParticipantStateChangeEvent":
+ OnParticipantStateChange?.Invoke(cookie, uriString, statusCode, statusString, state, nameString, displayNameString, participantType);
+ break;
+ case "ParticipantPropertiesEvent":
+ OnParticipantProperties?.Invoke(cookie, uriString, statusCode, statusString, isLocallyMuted, isModeratorMuted, isSpeaking, volume, energy);
+ break;
+ case "AuxAudioPropertiesEvent":
+ OnAuxAudioProperties?.Invoke(cookie, energy);
+ break;
+ }
+ }
+ else
+ {
+ switch (actionString)
+ {
+ case "Connector.Create.1":
+ OnConnectorCreated?.Invoke(cookie, statusCode, statusString, connectorHandle);
+ break;
+ case "Account.Login.1":
+ OnLogin?.Invoke(cookie, statusCode, statusString, accountHandle);
+ break;
+ case "Session.Create.1":
+ OnSessionCreated?.Invoke(cookie, statusCode, statusString, sessionHandle);
+ break;
+ case "Session.Connect.1":
+ OnSessionConnected?.Invoke(cookie, statusCode, statusString);
+ break;
+ case "Session.Terminate.1":
+ OnSessionTerminated?.Invoke(cookie, statusCode, statusString);
+ break;
+ case "Account.Logout.1":
+ OnAccountLogout?.Invoke(cookie, statusCode, statusString);
+ break;
+ case "Connector.InitiateShutdown.1":
+ OnConnectorInitiateShutdown?.Invoke(cookie, statusCode, statusString);
+ break;
+ case "Account.ChannelGetList.1":
+ OnAccountChannelGetList?.Invoke(cookie, statusCode, statusString);
+ break;
+ case "Aux.GetCaptureDevices.1":
+ OnCaptureDevices?.Invoke(cookie, statusCode, statusString, nameString);
+ break;
+ case "Aux.GetRenderDevices.1":
+ OnRenderDevices?.Invoke(cookie, statusCode, statusString, nameString);
+ break;
+ }
+ }
+ }
+
+ #endregion Callbacks
+ }
+}
diff --git a/LibreMetaverse.Utilities/VoiceManagerBlocking.cs b/LibreMetaverse.Voice/VoiceManagerBlocking.cs
similarity index 96%
rename from LibreMetaverse.Utilities/VoiceManagerBlocking.cs
rename to LibreMetaverse.Voice/VoiceManagerBlocking.cs
index b6eca486..e066f607 100644
--- a/LibreMetaverse.Utilities/VoiceManagerBlocking.cs
+++ b/LibreMetaverse.Voice/VoiceManagerBlocking.cs
@@ -1,143 +1,144 @@
-/*
- * Copyright (c) 2006-2016, openmetaverse.co
- * All rights reserved.
- *
- * - Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- * - Neither the name of the openmetaverse.co nor the names
- * of its contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-using System.Collections.Generic;
-using System.Threading;
-
-namespace OpenMetaverse.Utilities
-{
- public partial class VoiceManager
- {
- /// Amount of time to wait for the voice daemon to respond.
- /// The value needs to stay relatively high because some of the calls
- /// require the voice daemon to make remote queries before replying
- public int BlockingTimeout = 30 * 1000;
-
- protected Dictionary Events = new Dictionary();
-
- public List CaptureDevices()
- {
- var evt = new AutoResetEvent(false);
- Events[_CommandCookie] = evt;
-
- if (RequestCaptureDevices() == -1)
- {
- Events.Remove(_CommandCookie);
- return new List();
- }
-
- return evt.WaitOne(BlockingTimeout, false) ? CurrentCaptureDevices() : new List();
- }
-
- public List RenderDevices()
- {
- var evt = new AutoResetEvent(false);
- Events[_CommandCookie] = evt;
-
- if (RequestRenderDevices() == -1)
- {
- Events.Remove(_CommandCookie);
- return new List();
- }
-
- return evt.WaitOne(BlockingTimeout, false) ? CurrentRenderDevices() : new List();
- }
-
- public string CreateConnector(out int status)
- {
- status = 0;
-
- var evt = new AutoResetEvent(false);
- Events[_CommandCookie] = evt;
-
- if (RequestCreateConnector() == -1)
- {
- Events.Remove(_CommandCookie);
- return string.Empty;
- }
-
- var success = evt.WaitOne(BlockingTimeout, false);
- status = statusCode;
-
- return success && statusCode == 0 ? connectorHandle : string.Empty;
- }
-
- public string Login(string accountName, string password, string connectorHandle, out int status)
- {
- status = 0;
-
- var evt = new AutoResetEvent(false);
- Events[_CommandCookie] = evt;
-
- if (RequestLogin(accountName, password, connectorHandle) == -1)
- {
- Events.Remove(_CommandCookie);
- return string.Empty;
- }
-
- var success = evt.WaitOne(BlockingTimeout, false);
- status = statusCode;
-
- return success && statusCode == 0 ? accountHandle : string.Empty;
- }
-
- protected void RegisterCallbacks()
- {
- OnCaptureDevices += VoiceManager_OnCaptureDevices;
- OnRenderDevices += VoiceManager_OnRenderDevices;
- OnConnectorCreated += VoiceManager_OnConnectorCreated;
- OnLogin += VoiceManager_OnLogin;
- }
-
- #region Callbacks
-
- private void VoiceManager_OnCaptureDevices(int cookie, int statusCode, string statusString, string currentDevice)
- {
- if (Events.ContainsKey(cookie))
- Events[cookie].Set();
- }
-
- private void VoiceManager_OnRenderDevices(int cookie, int statusCode, string statusString, string currentDevice)
- {
- if (Events.ContainsKey(cookie))
- Events[cookie].Set();
- }
-
- private void VoiceManager_OnConnectorCreated(int cookie, int statusCode, string statusString, string connectorHandle)
- {
- if (Events.ContainsKey(cookie))
- Events[cookie].Set();
- }
-
- private void VoiceManager_OnLogin(int cookie, int statusCode, string statusString, string accountHandle)
- {
- if (Events.ContainsKey(cookie))
- Events[cookie].Set();
- }
-
- #endregion Callbacks
- }
+/*
+ * Copyright (c) 2006-2016, openmetaverse.co
+ * Copyright (c) 2021-2022, Sjofn LLC.
+ * All rights reserved.
+ *
+ * - Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * - Neither the name of the openmetaverse.co nor the names
+ * of its contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System.Collections.Generic;
+using System.Threading;
+
+namespace LibreMetaverse.Voice
+{
+ public partial class VoiceManager
+ {
+ /// Amount of time to wait for the voice daemon to respond.
+ /// The value needs to stay relatively high because some of the calls
+ /// require the voice daemon to make remote queries before replying
+ public int BlockingTimeout = 30 * 1000;
+
+ protected Dictionary Events = new Dictionary();
+
+ public List CaptureDevices()
+ {
+ var evt = new AutoResetEvent(false);
+ Events[_CommandCookie] = evt;
+
+ if (RequestCaptureDevices() == -1)
+ {
+ Events.Remove(_CommandCookie);
+ return new List();
+ }
+
+ return evt.WaitOne(BlockingTimeout, false) ? CurrentCaptureDevices() : new List();
+ }
+
+ public List RenderDevices()
+ {
+ var evt = new AutoResetEvent(false);
+ Events[_CommandCookie] = evt;
+
+ if (RequestRenderDevices() == -1)
+ {
+ Events.Remove(_CommandCookie);
+ return new List();
+ }
+
+ return evt.WaitOne(BlockingTimeout, false) ? CurrentRenderDevices() : new List();
+ }
+
+ public string CreateConnector(out int status)
+ {
+ status = 0;
+
+ var evt = new AutoResetEvent(false);
+ Events[_CommandCookie] = evt;
+
+ if (RequestCreateConnector() == -1)
+ {
+ Events.Remove(_CommandCookie);
+ return string.Empty;
+ }
+
+ var success = evt.WaitOne(BlockingTimeout, false);
+ status = statusCode;
+
+ return success && statusCode == 0 ? connectorHandle : string.Empty;
+ }
+
+ public string Login(string accountName, string password, string connectorHandle, out int status)
+ {
+ status = 0;
+
+ var evt = new AutoResetEvent(false);
+ Events[_CommandCookie] = evt;
+
+ if (RequestLogin(accountName, password, connectorHandle) == -1)
+ {
+ Events.Remove(_CommandCookie);
+ return string.Empty;
+ }
+
+ var success = evt.WaitOne(BlockingTimeout, false);
+ status = statusCode;
+
+ return success && statusCode == 0 ? accountHandle : string.Empty;
+ }
+
+ protected void RegisterCallbacks()
+ {
+ OnCaptureDevices += VoiceManager_OnCaptureDevices;
+ OnRenderDevices += VoiceManager_OnRenderDevices;
+ OnConnectorCreated += VoiceManager_OnConnectorCreated;
+ OnLogin += VoiceManager_OnLogin;
+ }
+
+ #region Callbacks
+
+ private void VoiceManager_OnCaptureDevices(int cookie, int statusCode, string statusString, string currentDevice)
+ {
+ if (Events.ContainsKey(cookie))
+ Events[cookie].Set();
+ }
+
+ private void VoiceManager_OnRenderDevices(int cookie, int statusCode, string statusString, string currentDevice)
+ {
+ if (Events.ContainsKey(cookie))
+ Events[cookie].Set();
+ }
+
+ private void VoiceManager_OnConnectorCreated(int cookie, int statusCode, string statusString, string connectorHandle)
+ {
+ if (Events.ContainsKey(cookie))
+ Events[cookie].Set();
+ }
+
+ private void VoiceManager_OnLogin(int cookie, int statusCode, string statusString, string accountHandle)
+ {
+ if (Events.ContainsKey(cookie))
+ Events[cookie].Set();
+ }
+
+ #endregion Callbacks
+ }
}
\ No newline at end of file
diff --git a/LibreMetaverse/Voice/VoiceParticipant.cs b/LibreMetaverse.Voice/VoiceParticipant.cs
similarity index 99%
rename from LibreMetaverse/Voice/VoiceParticipant.cs
rename to LibreMetaverse.Voice/VoiceParticipant.cs
index aefc989a..6a8a56e0 100644
--- a/LibreMetaverse/Voice/VoiceParticipant.cs
+++ b/LibreMetaverse.Voice/VoiceParticipant.cs
@@ -28,8 +28,9 @@
using System;
using System.Text;
using System.Text.RegularExpressions;
+using OpenMetaverse;
-namespace OpenMetaverse.Voice
+namespace LibreMetaverse.Voice
{
public class VoiceParticipant
{
diff --git a/LibreMetaverse/Voice/VoiceSession.cs b/LibreMetaverse.Voice/VoiceSession.cs
similarity index 99%
rename from LibreMetaverse/Voice/VoiceSession.cs
rename to LibreMetaverse.Voice/VoiceSession.cs
index fcede9e4..72392751 100644
--- a/LibreMetaverse/Voice/VoiceSession.cs
+++ b/LibreMetaverse.Voice/VoiceSession.cs
@@ -29,7 +29,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Text;
-namespace OpenMetaverse.Voice
+namespace LibreMetaverse.Voice
{
///
/// Represents a single Voice Session to the Vivox service.
diff --git a/LibreMetaverse.sln b/LibreMetaverse.sln
index 7bac22b7..a35df357 100644
--- a/LibreMetaverse.sln
+++ b/LibreMetaverse.sln
@@ -56,6 +56,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GridAccountant", "Programs\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibreMetaverse.LslTools", "LibreMetaverse.LslTools\LibreMetaverse.LslTools.csproj", "{989E5E15-D99B-4CF1-AF64-90C568FC979A}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibreMetaverse.Voice", "LibreMetaverse.Voice\LibreMetaverse.Voice.csproj", "{FB07C6DE-F791-4336-B6E2-B32EEAC34792}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -381,6 +383,24 @@ Global
{989E5E15-D99B-4CF1-AF64-90C568FC979A}.ReleaseNoGui|x64.Build.0 = Release|Any CPU
{989E5E15-D99B-4CF1-AF64-90C568FC979A}.ReleaseNoGui|x86.ActiveCfg = Release|Any CPU
{989E5E15-D99B-4CF1-AF64-90C568FC979A}.ReleaseNoGui|x86.Build.0 = Release|Any CPU
+ {FB07C6DE-F791-4336-B6E2-B32EEAC34792}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FB07C6DE-F791-4336-B6E2-B32EEAC34792}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FB07C6DE-F791-4336-B6E2-B32EEAC34792}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {FB07C6DE-F791-4336-B6E2-B32EEAC34792}.Debug|x64.Build.0 = Debug|Any CPU
+ {FB07C6DE-F791-4336-B6E2-B32EEAC34792}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {FB07C6DE-F791-4336-B6E2-B32EEAC34792}.Debug|x86.Build.0 = Debug|Any CPU
+ {FB07C6DE-F791-4336-B6E2-B32EEAC34792}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FB07C6DE-F791-4336-B6E2-B32EEAC34792}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FB07C6DE-F791-4336-B6E2-B32EEAC34792}.Release|x64.ActiveCfg = Release|Any CPU
+ {FB07C6DE-F791-4336-B6E2-B32EEAC34792}.Release|x64.Build.0 = Release|Any CPU
+ {FB07C6DE-F791-4336-B6E2-B32EEAC34792}.Release|x86.ActiveCfg = Release|Any CPU
+ {FB07C6DE-F791-4336-B6E2-B32EEAC34792}.Release|x86.Build.0 = Release|Any CPU
+ {FB07C6DE-F791-4336-B6E2-B32EEAC34792}.ReleaseNoGui|Any CPU.ActiveCfg = Release|Any CPU
+ {FB07C6DE-F791-4336-B6E2-B32EEAC34792}.ReleaseNoGui|Any CPU.Build.0 = Release|Any CPU
+ {FB07C6DE-F791-4336-B6E2-B32EEAC34792}.ReleaseNoGui|x64.ActiveCfg = Release|Any CPU
+ {FB07C6DE-F791-4336-B6E2-B32EEAC34792}.ReleaseNoGui|x64.Build.0 = Release|Any CPU
+ {FB07C6DE-F791-4336-B6E2-B32EEAC34792}.ReleaseNoGui|x86.ActiveCfg = Release|Any CPU
+ {FB07C6DE-F791-4336-B6E2-B32EEAC34792}.ReleaseNoGui|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Programs/VoiceTest/VoiceTest.cs b/Programs/VoiceTest/VoiceTest.cs
index bc16a3ea..1e54c33a 100644
--- a/Programs/VoiceTest/VoiceTest.cs
+++ b/Programs/VoiceTest/VoiceTest.cs
@@ -28,7 +28,7 @@ using System;
using System.Collections.Generic;
using System.Threading;
using OpenMetaverse;
-using OpenMetaverse.Utilities;
+using LibreMetaverse.Voice;
namespace VoiceTest
{
diff --git a/Programs/VoiceTest/VoiceTest.csproj b/Programs/VoiceTest/VoiceTest.csproj
index ec9bb129..644935e1 100644
--- a/Programs/VoiceTest/VoiceTest.csproj
+++ b/Programs/VoiceTest/VoiceTest.csproj
@@ -14,7 +14,7 @@
-
+
diff --git a/Programs/examples/TestClient/TestClient.cs b/Programs/examples/TestClient/TestClient.cs
index 7a54911f..4819b46e 100644
--- a/Programs/examples/TestClient/TestClient.cs
+++ b/Programs/examples/TestClient/TestClient.cs
@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Threading;
using System.Reflection;
using OpenMetaverse.Packets;
-using OpenMetaverse.Utilities;
+using LibreMetaverse.Voice;
namespace OpenMetaverse.TestClient
{
diff --git a/Programs/examples/TestClient/TestClient.csproj b/Programs/examples/TestClient/TestClient.csproj
index 2d3aaed5..b3c5e3bf 100644
--- a/Programs/examples/TestClient/TestClient.csproj
+++ b/Programs/examples/TestClient/TestClient.csproj
@@ -12,7 +12,7 @@
-
+