From 9da9712cf17467ff3b67a16dd4d67ab416ecdb81 Mon Sep 17 00:00:00 2001 From: Mojito Sorbet Date: Thu, 29 Oct 2009 22:09:40 +0000 Subject: [PATCH] More Voice tab items Create three temporary Core modules that will eventually move to libomv git-svn-id: https://radegast.googlecode.com/svn/trunk@391 f7a694da-4d33-11de-9ad6-1127a62b9fcd --- Radegast/Core/VoiceGateway.cs | 1015 ++++++++++++++++++++++++ Radegast/Core/VoiceParticipant.cs | 75 ++ Radegast/Core/VoiceSession.cs | 119 +++ Radegast/GUI/Consoles/VoiceConsole.Designer.cs | 176 +++- Radegast/GUI/Consoles/VoiceConsole.cs | 125 ++- Radegast/GUI/Consoles/VoiceConsole.resx | 73 ++ plugins/Radegast.Plugin.Voice/Session.cs | 1 - plugins/Radegast.Plugin.Voice/VoiceClient.cs | 12 +- 8 files changed, 1545 insertions(+), 51 deletions(-) create mode 100644 Radegast/Core/VoiceGateway.cs create mode 100644 Radegast/Core/VoiceParticipant.cs create mode 100644 Radegast/Core/VoiceSession.cs diff --git a/Radegast/Core/VoiceGateway.cs b/Radegast/Core/VoiceGateway.cs new file mode 100644 index 0000000..e96ea87 --- /dev/null +++ b/Radegast/Core/VoiceGateway.cs @@ -0,0 +1,1015 @@ +// This class is temporary. It contains functions that will eventually +// move into OpenMetaverse.Voice. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using System.Threading; +using System.Windows.Forms; +using OpenMetaverse; +using OpenMetaverse.Voice; +using OpenMetaverse.StructuredData; + +namespace Radegast.Core +{ + public class VoiceGateway : IDisposable + { + public enum ConnectionState + { + None = 0, + Provisioned, + DaemonStarted, + DaemonConnected, + ConnectorConnected, + AccountLogin, + RegionCapAvailable, + SessionRunning + } + + private OpenMetaverse.Voice.VoiceGateway connector; + + internal string sipServer = ""; + private string acctServer = "https://www.bhr.vivox.com/api2/"; + private string connectionHandle; + private string accountHandle; + private string sessionHandle; + private string slvoicePath = ""; + private string slvoiceArgs = "-ll -1"; + private string daemonNode = "127.0.0.1"; + private int daemonPort = 44124; + private string voiceUser; + private string voicePassword; + private string spatialUri; + private string spatialCredentials; + private Dictionary sessions; + private VoiceSession spatialSession; + private Uri currentParcelCap; + private Uri nextParcelCap; + private string regionName; + private Thread posThread; + private ManualResetEvent posRestart; + public GridClient Client; + + private OpenMetaverse.Voice.VoiceGateway.VoicePosition position; + + // Last places Self was located + private Vector3d oldPosition; + private Vector3d oldAt; + + private List inputDevices; + public List CaptureDevices { get { return inputDevices; } } + private List outputDevices; + public List PlaybackDevices { get { return outputDevices; } } + + public event EventHandler OnSessionCreate; + public event EventHandler OnSessionRemove; + public delegate void VoiceConnectionChangeCallback( ConnectionState state ); + public event VoiceConnectionChangeCallback OnVoiceConnectionChange; + public delegate void VoiceMicTestCallback(float level); + public event VoiceMicTestCallback OnVoiceMicTest; + + internal VoiceGateway( GridClient c ) + { + Client = c; + + sessions = new Dictionary(); + position = new OpenMetaverse.Voice.VoiceGateway.VoicePosition(); + position.UpOrientation = new Vector3d(0.0, 1.0, 0.0); + position.Velocity = new Vector3d(0.0, 0.0, 0.0); + oldPosition = new Vector3d(0, 0, 0); + oldAt = new Vector3d(1, 0, 0); + + slvoiceArgs = "-c"; // Cleanup old instances + slvoiceArgs += " -ll -1"; // Min logging + slvoiceArgs += " -i 0.0.0.0:" + daemonPort.ToString(); +// slvoiceArgs += " -lf " + control.instance.ClientDir; + } + + public int Request(string action, string requestXML) + { + return connector.Request(action, requestXML); + } + + /// + /// Start up the Voice service. + /// + public void Start() + { + // Start the background thread + if (posThread != null && posThread.IsAlive) + posThread.Abort(); + posThread = new Thread(new ThreadStart(PositionThreadBody)); + posThread.Name = "VoicePositionUpdate"; + posThread.IsBackground = true; + posRestart = new ManualResetEvent(false); + posThread.Start(); + + connector = new OpenMetaverse.Voice.VoiceGateway(); + + Client.Network.OnEventQueueRunning += + new NetworkManager.EventQueueRunningCallback(Network_OnEventQueueRunning); + + // Connection events + connector.OnDaemonRunning += + new OpenMetaverse.Voice.VoiceGateway.DaemonRunningCallback(connector_OnDaemonRunning); + connector.OnDaemonCouldntRun += + new OpenMetaverse.Voice.VoiceGateway.DaemonCouldntRunCallback(connector_OnDaemonCouldntRun); + connector.OnConnectorCreateResponse += + new OpenMetaverse.Voice.VoiceGateway.ConnectorCreateResponseCallback(connector_OnConnectorCreateResponse); + connector.OnDaemonConnected += + new OpenMetaverse.Voice.VoiceGateway.DaemonConnectedCallback(connector_OnDaemonConnected); + connector.OnDaemonCouldntConnect += + new OpenMetaverse.Voice.VoiceGateway.DaemonCouldntConnectCallback(connector_OnDaemonCouldntConnect); + connector.OnAuxAudioPropertiesEvent += + new OpenMetaverse.Voice.VoiceGateway.AuxAudioPropertiesEventCallback(connector_OnAuxAudioPropertiesEvent); + + // Session events + connector.OnSessionCreateResponse += + new OpenMetaverse.Voice.VoiceGateway.SessionCreateResponseCallback(connector_OnSessionCreateResponse); + connector.OnSessionStateChangeEvent += + new OpenMetaverse.Voice.VoiceGateway.SessionStateChangeEventCallback(connector_OnSessionStateChangeEvent); + connector.OnSessionAddedEvent += + new OpenMetaverse.Voice.VoiceGateway.SessionAddedEventCallback(connector_OnSessionAddedEvent); + + // Session Participants events + connector.OnSessionParticipantStateChangeEvent += + new OpenMetaverse.Voice.VoiceGateway.SessionParticipantStateChangeEventCallback(connector_OnSessionParticipantStateChangeEvent); + connector.OnSessionParticipantUpdatedEvent += + new OpenMetaverse.Voice.VoiceGateway.SessionParticipantUpdatedEventCallback(connector_OnSessionParticipantUpdatedEvent); + connector.OnSessionParticipantAddedEvent += + new OpenMetaverse.Voice.VoiceGateway.SessionParticipantAddedEventCallback(connector_OnSessionParticipantAddedEvent); + + // Tuning events + connector.OnAuxCaptureAudioStartResponse += + new OpenMetaverse.Voice.VoiceGateway.AuxCaptureAudioStartResponseCallback(connector_OnAuxCaptureAudioStartResponse); + connector.OnAuxGetCaptureDevicesResponse += + new OpenMetaverse.Voice.VoiceGateway.AuxGetCaptureDevicesResponseCallback(connector_OnAuxGetCaptureDevicesResponse); + connector.OnAuxGetRenderDevicesResponse += + new OpenMetaverse.Voice.VoiceGateway.AuxGetRenderDevicesResponseCallback(connector_OnAuxGetRenderDevicesResponse); + + // Account events + connector.OnAccountLoginResponse += + new OpenMetaverse.Voice.VoiceGateway.AccountLoginResponseCallback(connector_OnAccountLoginResponse); + + Logger.Log("Voice initialized", Helpers.LogLevel.Info); + } + + internal void Stop() + { + Client.Network.OnEventQueueRunning -= + new NetworkManager.EventQueueRunningCallback(Network_OnEventQueueRunning); + + if (connector != null) + { + // Connection events + connector.OnDaemonRunning -= + new OpenMetaverse.Voice.VoiceGateway.DaemonRunningCallback(connector_OnDaemonRunning); + connector.OnDaemonCouldntRun -= + new OpenMetaverse.Voice.VoiceGateway.DaemonCouldntRunCallback(connector_OnDaemonCouldntRun); + connector.OnConnectorCreateResponse -= + new OpenMetaverse.Voice.VoiceGateway.ConnectorCreateResponseCallback(connector_OnConnectorCreateResponse); + connector.OnDaemonConnected -= + new OpenMetaverse.Voice.VoiceGateway.DaemonConnectedCallback(connector_OnDaemonConnected); + connector.OnDaemonCouldntConnect -= + new OpenMetaverse.Voice.VoiceGateway.DaemonCouldntConnectCallback(connector_OnDaemonCouldntConnect); + connector.OnAuxAudioPropertiesEvent -= + new OpenMetaverse.Voice.VoiceGateway.AuxAudioPropertiesEventCallback(connector_OnAuxAudioPropertiesEvent); + + // Session events + connector.OnSessionCreateResponse -= + new OpenMetaverse.Voice.VoiceGateway.SessionCreateResponseCallback(connector_OnSessionCreateResponse); + connector.OnSessionStateChangeEvent -= + new OpenMetaverse.Voice.VoiceGateway.SessionStateChangeEventCallback(connector_OnSessionStateChangeEvent); + connector.OnSessionAddedEvent -= + new OpenMetaverse.Voice.VoiceGateway.SessionAddedEventCallback(connector_OnSessionAddedEvent); + + // Session Participants events + connector.OnSessionParticipantStateChangeEvent -= + new OpenMetaverse.Voice.VoiceGateway.SessionParticipantStateChangeEventCallback(connector_OnSessionParticipantStateChangeEvent); + connector.OnSessionParticipantUpdatedEvent -= + new OpenMetaverse.Voice.VoiceGateway.SessionParticipantUpdatedEventCallback(connector_OnSessionParticipantUpdatedEvent); + connector.OnSessionParticipantAddedEvent -= + new OpenMetaverse.Voice.VoiceGateway.SessionParticipantAddedEventCallback(connector_OnSessionParticipantAddedEvent); + + // Tuning events + connector.OnAuxCaptureAudioStartResponse -= + new OpenMetaverse.Voice.VoiceGateway.AuxCaptureAudioStartResponseCallback(connector_OnAuxCaptureAudioStartResponse); + connector.OnAuxGetCaptureDevicesResponse -= + new OpenMetaverse.Voice.VoiceGateway.AuxGetCaptureDevicesResponseCallback(connector_OnAuxGetCaptureDevicesResponse); + connector.OnAuxGetRenderDevicesResponse -= + new OpenMetaverse.Voice.VoiceGateway.AuxGetRenderDevicesResponseCallback(connector_OnAuxGetRenderDevicesResponse); + + // Account events + connector.OnAccountLoginResponse -= + new OpenMetaverse.Voice.VoiceGateway.AccountLoginResponseCallback(connector_OnAccountLoginResponse); + } + + // Stop the background thread + if (posThread != null) + { + PosUpdating(false); + + if (posThread.IsAlive) + posThread.Abort(); + posThread = null; + } + + // Close all sessions + foreach (VoiceSession s in sessions.Values) + { + s.Close(); + } + + if (connector != null) + { + connector.ConnectorInitiateShutdown(connectionHandle); + connector.StopDaemon(); + } + } + + /// + /// Cleanup oject resources + /// + public void Dispose() + { + Stop(); + } + + internal string GetVoiceDaemonPath() + { + if (Environment.OSVersion.Platform != PlatformID.MacOSX && + Environment.OSVersion.Platform != PlatformID.Unix) + { + string progFiles; + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("ProgramFiles(x86)"))) + { + progFiles = Environment.GetEnvironmentVariable("ProgramFiles(x86)"); + } + else + { + progFiles = Environment.GetEnvironmentVariable("ProgramFiles"); + } + + return Path.Combine(progFiles, @"SecondLife\SLVoice.exe"); + } + else + { + //TODO return Path.Combine(progFiles, @"SecondLife\SLVoice"); + } + + return string.Empty; + } + /// + /// Request voice cap when changeing regions + /// + /// + void Network_OnEventQueueRunning(Simulator simulator) + { + // We only care about the sim we are in. + if (simulator != Client.Network.CurrentSim) + return; + + // Did we provision voice login info? + if (string.IsNullOrEmpty(voiceUser)) + { + // The startup steps are + // 0. Get voice account info + // 1. Start Daemon + // 2. Create TCP connection + // 3. Create Connector + // 4. Account login + // 5. Create session + + // Get the voice provisioning data + System.Uri vCap = + Client.Network.CurrentSim.Caps.CapabilityURI("ProvisionVoiceAccountRequest"); + + // Do we have voice capability? + if (vCap == null) + { + Logger.Log("Null voice capability", Helpers.LogLevel.Warning); + } + else + { + OpenMetaverse.Http.CapsClient capClient = + new OpenMetaverse.Http.CapsClient(vCap); + capClient.OnComplete += + new OpenMetaverse.Http.CapsClient.CompleteCallback(cClient_OnComplete); + OSD postData = new OSD(); + + // STEP 0 + Logger.Log("Requesting voice capability", Helpers.LogLevel.Info); + capClient.BeginGetResponse(postData, OSDFormat.Xml, 10000); + } + + return; + } + else + { + // Change voice session for this region. + ParcelChanged(); + } + } + + + #region Partcipants + void connector_OnSessionParticipantStateChangeEvent( + string SessionHandle, + int StatusCode, + string StatusString, + OpenMetaverse.Voice.VoiceGateway.ParticipantState State, + string ParticipantURI, + string AccountName, + string DisplayName, + OpenMetaverse.Voice.VoiceGateway.ParticipantType ParticipantType) + { + + } + + void connector_OnSessionParticipantUpdatedEvent(string sessionHandle, + string URI, + bool isMuted, + bool isSpeaking, + int volume, + float energy) + { + VoiceSession s = FindSession(sessionHandle, false); + if (s == null) return; + s.ParticipantUpdate(URI, isMuted, isSpeaking, volume, energy); + } + + public string SIPFromUUID(UUID id) + { + return "sip:" + + nameFromID(id) + + "@" + + sipServer; + } + + private static string nameFromID(UUID id) + { + string result = null; + + if (id==UUID.Zero) + return result; + + // Prepending this apparently prevents conflicts with reserved names inside the vivox and diamondware code. + result = "x"; + + // Base64 encode and replace the pieces of base64 that are less compatible + // with e-mail local-parts. + // See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet" + byte[] encbuff = id.GetBytes(); + result += Convert.ToBase64String(encbuff); + result = result.Replace('+', '-'); + result = result.Replace('/', '_'); + + return result; + } + + void connector_OnSessionParticipantAddedEvent( + string SessionGroupHandle, + string SessionHandle, + string ParticipantUri, + string AccountName, + string DisplayName, + OpenMetaverse.Voice.VoiceGateway.ParticipantType type, + string Application ) + { + VoiceSession s = FindSession(sessionHandle, false); + if (s == null) return; + s.AddParticipant( ParticipantUri ); + } + + void connector_OnSessionParticipantRemovedEvent( + string SessionGroupHandle, + string SessionHandle, + string ParticipantUri, + string AccountName, + string Reason ) + { + VoiceSession s = FindSession(sessionHandle, false); + if (s == null) return; + s.RemoveParticipant(ParticipantUri); + } + #endregion + + #region Sessions + void connector_OnSessionAddedEvent(string sessionGroupHandle, + string newSessionHandle, + string URI, + bool isChannel, + bool isIncoming) + { + sessionHandle = newSessionHandle; + + // Create our session context. + VoiceSession s = FindSession(sessionHandle, true); + s.RegionName = regionName; + + Logger.Log("Added voice session in " + regionName, Helpers.LogLevel.Info); + spatialSession = s; + + // Tell any user-facing code. + if (OnSessionCreate != null) + OnSessionCreate(s, null); + } + + /// + /// Handle session creation + /// + void connector_OnSessionCreateResponse(int ReturnCode, + int StatusCode, string StatusString, + string SessionHandle, + OpenMetaverse.Voice.VoiceGateway.VoiceRequest Request) + { + if (StatusCode == 0) return; + } + + /// + /// Handle a change in session state + /// + /// + /// + /// + /// + /// + /// + /// + void connector_OnSessionStateChangeEvent(string SessionHandle, + int StatusCode, + string StatusString, + OpenMetaverse.Voice.VoiceGateway.SessionState State, + string URI, + bool IsChannel, + string ChannelName) + { + VoiceSession s; + + switch (State) + { + case OpenMetaverse.Voice.VoiceGateway.SessionState.Connected: + s = FindSession(SessionHandle, true); + sessionHandle = SessionHandle; + s.RegionName = regionName; + spatialSession = s; + + Logger.Log("Voice connected in " + regionName, Helpers.LogLevel.Info); + // Tell any user-facing code. + if (OnSessionCreate != null) + OnSessionCreate(s, null); + break; + + case OpenMetaverse.Voice.VoiceGateway.SessionState.Disconnected: + s = FindSession(sessionHandle, false); + sessions.Remove(sessionHandle); + + if (s != null) + { + Logger.Log("Voice disconnected in " + s.RegionName, Helpers.LogLevel.Info); + + // Inform interested parties + if (OnSessionRemove != null) + OnSessionRemove(s, null); + + if (s == spatialSession) + spatialSession = null; + } + + // The previous session is now ended. Check for a new one and + // start it going. + if (nextParcelCap != null) + { + currentParcelCap = nextParcelCap; + nextParcelCap = null; + RequestParcelInfo(currentParcelCap); + } + break; + } + + + } + + /// + /// Close a voice session + /// + /// + internal void CloseSession(string sessionHandle) + { + if (!sessions.ContainsKey(sessionHandle)) + return; + + // Clean up spatial pointers. + VoiceSession s = sessions[sessionHandle]; + if (s.IsSpatial) + { + spatialSession = null; + currentParcelCap = null; + } + + // Remove this session from the master session list + sessions.Remove(sessionHandle); + + // Let any user-facing code clean up. + if (OnSessionRemove != null) + OnSessionRemove(s, null); + + // Tell SLVoice to clean it up as well. + connector.SessionTerminate(sessionHandle); + } + + /// + /// Locate a Session context from its handle + /// + /// + /// + /// Creates the session context if it does not exist. + VoiceSession FindSession(string sessionHandle, bool make) + { + if (sessions.ContainsKey(sessionHandle)) + return sessions[sessionHandle]; + + if (!make) return null; + + // Create a new session and add it to the sessions list. + VoiceSession s = new VoiceSession( this, sessionHandle); + sessions.Add(sessionHandle, s); + return s; + } + +#endregion + + #region MinorResponses + + void connector_OnAuxCaptureAudioStartResponse(int ReturnCode, int StatusCode, + string StatusString, OpenMetaverse.Voice.VoiceGateway.VoiceRequest Request) + { + + } + + void connector_OnAuxAudioPropertiesEvent(bool MicIsActive, + float MicEnergy, float MicVolume, float SpeakerVolume) + { + if (OnVoiceMicTest != null) + OnVoiceMicTest(MicEnergy); + } + + #endregion + + private void ReportConnectionState(ConnectionState s) + { + if (OnVoiceConnectionChange == null) return; + + OnVoiceConnectionChange(s); + } + + /// + /// Handle completion of main voice cap request. + /// + /// + /// + /// + void cClient_OnComplete(OpenMetaverse.Http.CapsClient client, + OpenMetaverse.StructuredData.OSD result, + Exception error) + { + if (error != null) + { + Logger.Log("Voice cap error "+error.Message, Helpers.LogLevel.Error); + return; + } + + Logger.Log("Voice provisioned", Helpers.LogLevel.Info); + ReportConnectionState(ConnectionState.Provisioned); + + OpenMetaverse.StructuredData.OSDMap pMap = result as OpenMetaverse.StructuredData.OSDMap; + + // We can get back 4 interesting values: + // voice_sip_uri_hostname + // voice_account_server_name (actually a full URI) + // username + // password + if (pMap.ContainsKey("voice_sip_uri_hostname")) + sipServer = pMap["voice_sip_uri_hostname"].AsString(); + if (pMap.ContainsKey("voice_account_server_name")) + acctServer = pMap["voice_account_server_name"].AsString(); + voiceUser = pMap["username"].AsString(); + voicePassword = pMap["password"].AsString(); + + // Start the SLVoice daemon + slvoicePath = GetVoiceDaemonPath(); + + // Test if the executable exists + if (!System.IO.File.Exists(slvoicePath)) + { + Logger.Log("SLVoice is missing", Helpers.LogLevel.Error); + return; + } + + // STEP 1 + connector.StartDaemon(slvoicePath, slvoiceArgs); + } + + #region Daemon + void connector_OnDaemonCouldntConnect() + { + Logger.Log("No voice daemon connect", Helpers.LogLevel.Error); + } + + void connector_OnDaemonCouldntRun() + { + Logger.Log("Daemon not started", Helpers.LogLevel.Error); + } + + /// + /// Daemon has started so connect to it. + /// + void connector_OnDaemonRunning() + { + connector.OnDaemonRunning -= + new OpenMetaverse.Voice.VoiceGateway.DaemonRunningCallback(connector_OnDaemonRunning); + + Logger.Log("Daemon started", Helpers.LogLevel.Info); + ReportConnectionState(ConnectionState.DaemonStarted); + + // STEP 2 + connector.ConnectToDaemon(daemonNode, daemonPort); + } + + /// + /// The daemon TCP connection is open. + /// + /// + /// + /// + /// + /// + /// + void connector_OnDaemonConnected() + { + Logger.Log("Daemon connected", Helpers.LogLevel.Info); + + // The connector is what does the logging. + OpenMetaverse.Voice.VoiceGateway.VoiceLoggingSettings vLog = + new OpenMetaverse.Voice.VoiceGateway.VoiceLoggingSettings(); +//TODO vLog.Folder = control.instance.ClientDir; + vLog.Enabled = true; + vLog.FileNamePrefix = "RadegastVoice"; + vLog.FileNameSuffix = ".log"; + vLog.LogLevel = 5; + + // STEP 3 + int reqId = connector.ConnectorCreate( + "V2 SDK", // Magic value keeps SLVoice happy + acctServer, // Account manager server + 30000, 30099, // port range + vLog); + if (reqId < 0) + { + Logger.Log("No voice connector request", Helpers.LogLevel.Error); + } + } + + /// + /// Handle creation of the Connector. + /// + /// + /// + /// + /// + /// + /// + void connector_OnConnectorCreateResponse(int ReturnCode, + string VersionID, int StatusCode, + string StatusString, string ConnectorHandle, + OpenMetaverse.Voice.VoiceGateway.VoiceRequest Request) + { + Logger.Log("Voice daemon protocol started "+StatusString, Helpers.LogLevel.Info); + + connectionHandle = ConnectorHandle; + + if (StatusCode != 0) + return; + + // Request the available devices, to populate the GUI for choosing. + connector.AuxGetCaptureDevices(); + connector.AuxGetRenderDevices(); + + // STEP 4 + connector.AccountLogin( + connectionHandle, + voiceUser, + voicePassword, + "VerifyAnswer", // This can also be "AutoAnswer" + "", // Default account management server URI + 10, // Throttle state changes + true); // Enable buddies and presence + } + #endregion + + void connector_OnAccountLoginResponse(int ReturnCode, int StatusCode, string StatusString, + string AccountHandle, + OpenMetaverse.Voice.VoiceGateway.VoiceRequest Request) + { + Logger.Log("Account Login "+StatusString, Helpers.LogLevel.Info ); + accountHandle = AccountHandle; + + ParcelChanged(); + } + + #region Tuning + /// + /// Handle response to audio output device query + /// + /// + /// + /// + /// + /// + /// + void connector_OnAuxGetRenderDevicesResponse(int ReturnCode, + int StatusCode, + string StatusString, + List RenderDevices, + string CurrentRenderDevice, + OpenMetaverse.Voice.VoiceGateway.VoiceRequest Request) + { + outputDevices = RenderDevices; + } + + /// + /// Handle response to audio input device query + /// + /// + /// + /// + /// + /// + /// + void connector_OnAuxGetCaptureDevicesResponse(int ReturnCode, + int StatusCode, string StatusString, + List CaptureDevices, + string CurrentCaptureDevice, + OpenMetaverse.Voice.VoiceGateway.VoiceRequest Request) + { + inputDevices = CaptureDevices; + } + + #endregion + + + + /// + /// Set voice channel for new parcel + /// + /// + internal void ParcelChanged() + { + // Get the capability for this parcel. + Caps c = Client.Network.CurrentSim.Caps; + System.Uri pCap = c.CapabilityURI("ParcelVoiceInfoRequest"); + + if (pCap == null) + { + Logger.Log("Null voice capability", Helpers.LogLevel.Error); + return; + } + + if (pCap == currentParcelCap) + { + // Parcel has not changed, so nothing to do. + return; + } + + // Parcel has changed. If we were already in a spatial session, we have to close it first. + if (spatialSession != null) + { + nextParcelCap = pCap; + CloseSession( spatialSession.SessionHandle ); + } + + // Not already in a session, so can start the new one. + RequestParcelInfo(pCap); + } + + private OpenMetaverse.Http.CapsClient parcelCap; + + /// + /// Request info from a parcel capability Uri. + /// + /// + + void RequestParcelInfo(Uri cap) + { + Logger.Log("Requesting region voice info", Helpers.LogLevel.Info); + + parcelCap = new OpenMetaverse.Http.CapsClient(cap); + parcelCap.OnComplete += + new OpenMetaverse.Http.CapsClient.CompleteCallback(pCap_OnComplete); + OSD postData = new OSD(); + + currentParcelCap = cap; + parcelCap.BeginGetResponse(postData, OSDFormat.Xml, 10000); + } + + /// + /// Receive parcel voice cap + /// + /// + /// + /// + void pCap_OnComplete(OpenMetaverse.Http.CapsClient client, + OpenMetaverse.StructuredData.OSD result, + Exception error) + { + parcelCap.OnComplete -= + new OpenMetaverse.Http.CapsClient.CompleteCallback(pCap_OnComplete); + parcelCap = null; + + if (error != null) + { + Logger.Log("Region voice cap "+error.Message, Helpers.LogLevel.Error); + return; + } + + OpenMetaverse.StructuredData.OSDMap pMap = result as OpenMetaverse.StructuredData.OSDMap; + + regionName = pMap["region_name"].AsString(); + ReportConnectionState(ConnectionState.RegionCapAvailable); + + if (pMap.ContainsKey("voice_credentials")) + { + OpenMetaverse.StructuredData.OSDMap cred = + pMap["voice_credentials"] as OpenMetaverse.StructuredData.OSDMap; + + if (cred.ContainsKey("channel_uri")) + spatialUri = cred["channel_uri"].AsString(); + if (cred.ContainsKey("channel_credentials")) + spatialCredentials = cred["channel_credentials"].AsString(); + } + + if (spatialUri == null || spatialUri == "") + { + // "No voice chat allowed here"); + return; + } + + Logger.Log("Voice connecting for region " + regionName, Helpers.LogLevel.Info); + + // STEP 5 + int reqId = connector.SessionCreate( + accountHandle, + spatialUri, // uri + "", // Channel name seems to be always null + spatialCredentials, // spatialCredentials, // session password + true, // Join Audio + false, // Join Text + ""); + if (reqId < 0) + { + Logger.Log("Voice Session ReqID " + reqId.ToString(), Helpers.LogLevel.Error); + } + } + + +#region GUI + internal int MicLevel + { + set + { + connector.ConnectorSetLocalMicVolume(connectionHandle, value); + } + } + internal int SpkrLevel + { + set + { + connector.ConnectorSetLocalSpeakerVolume(connectionHandle, value); + } + } + + /// + /// Change the audio capture device by user input + /// + /// + internal void SetMicDevice(string name) + { + connector.AuxSetCaptureDevice(name); + } + + /// + /// Change the audio render device by user input + /// + /// + internal void SetSpkrDevice(string name) + { + connector.AuxSetRenderDevice(name); + } + + internal bool MicMute + { + set + { + connector.ConnectorMuteLocalMic(connectionHandle, value); + } + } + + internal bool SpkrMute + { + set + { + connector.ConnectorMuteLocalSpeaker(connectionHandle, value); + } + } + + /// + /// Toggle test mode + /// + /// + internal void TestMode(bool on) + { + if (on) + { + if (spatialSession != null) + { + spatialSession.Close(); + spatialSession = null; + } + connector.AuxCaptureAudioStart(0); + } + else + { + connector.AuxCaptureAudioStop(); + ParcelChanged(); + } + } +#endregion + + internal class SessionListItem : System.Windows.Forms.ListViewItem + { +// OpenMetaverse.Voice.VoiceParticipant participant; + } + +#region Location Update + /// + /// Tell Vivox where we are standing + /// + /// This has to be called when we move or turn. + internal void UpdatePosition(AgentManager self) + { + // Get position in Global coordinates + Vector3d OMVpos = new Vector3d(self.GlobalPosition); + + // Do not send trivial updates. + if (OMVpos.ApproxEquals(oldPosition, 1.0)) + return; + + oldPosition = OMVpos; + + // Convert to the coordinate space that Vivox uses + // OMV X is East, Y is North, Z is up + // VVX X is East, Y is up, Z is South + position.Position = new Vector3d(OMVpos.X, OMVpos.Z, -OMVpos.Y); + + // TODO Rotate these two vectors + + // Get azimuth from the facing Quaternion. + // By definition, facing.W = Cos( angle/2 ) + double angle = 2.0 * Math.Acos(self.Movement.BodyRotation.W); + + position.LeftOrientation = new Vector3d(-1.0, 0.0, 0.0); + position.AtOrientation = new Vector3d((float)Math.Acos(angle), 0.0, -(float)Math.Asin(angle)); + + connector.SessionSet3DPosition( + sessionHandle, + position, + position); + } + + /// + /// Start and stop updating out position. + /// + /// + internal void PosUpdating( bool go ) + { + if (go) + posRestart.Set(); + else + posRestart.Reset(); + } + + private void PositionThreadBody() + { + while (true) + { + posRestart.WaitOne(); + Thread.Sleep(1500); + UpdatePosition(Client.Self); + } + } +#endregion + + } +} diff --git a/Radegast/Core/VoiceParticipant.cs b/Radegast/Core/VoiceParticipant.cs new file mode 100644 index 0000000..b0f1242 --- /dev/null +++ b/Radegast/Core/VoiceParticipant.cs @@ -0,0 +1,75 @@ +// This class is temporary. It contains functions that will eventually +// move into OpenMetaverse.Voice. +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using OpenMetaverse.Voice; +using OpenMetaverse; + +namespace Radegast.Core +{ + public class VoiceParticipant + { + public string Name + { + get { return part.AvatarName; } + set { part.AvatarName = value; } + } + + private bool muted; + private int volume; + private VoiceSession session; + private float energy; + private OpenMetaverse.Voice.VoiceParticipant part; + public float Energy { get { return energy; } } + private bool speaking; + public bool IsSpeaking { get { return speaking; } } + public string URI { get { return part.Sip; } } + public UUID ID { get { return part.id; } } + + public VoiceParticipant(string uri, VoiceSession s) + { + session = s; + + // Some of the functionality is already in OMV + part = new OpenMetaverse.Voice.VoiceParticipant(uri); + Volume = 64; + } + + public bool IsMuted + { + get { return muted; } + set + { + muted = value; + StringBuilder sb = new StringBuilder(); + sb.Append(OpenMetaverse.Voice.VoiceGateway.MakeXML("SessionHandle", session.SessionHandle)); + sb.Append(OpenMetaverse.Voice.VoiceGateway.MakeXML("ParticipantURI", part.Sip)); + sb.Append(OpenMetaverse.Voice.VoiceGateway.MakeXML("Mute", muted ? "1" : "0")); + session.Connector.Request("Session.SetParticipantMuteForMe.1", sb.ToString()); + } + } + + public int Volume + { + get { return volume; } + set + { + volume = value; + StringBuilder sb = new StringBuilder(); + sb.Append(OpenMetaverse.Voice.VoiceGateway.MakeXML("SessionHandle", session.SessionHandle)); + sb.Append(OpenMetaverse.Voice.VoiceGateway.MakeXML("ParticipantURI", part.Sip)); + sb.Append(OpenMetaverse.Voice.VoiceGateway.MakeXML("Volume", volume.ToString())); + session.Connector.Request("Session.SetParticipantVolumeForMe.1", sb.ToString()); + } + } + + internal void SetProperties(bool speak, bool mute, float en) + { + speaking = speak; + muted = mute; + energy = en; + } + } +} diff --git a/Radegast/Core/VoiceSession.cs b/Radegast/Core/VoiceSession.cs new file mode 100644 index 0000000..e57caf3 --- /dev/null +++ b/Radegast/Core/VoiceSession.cs @@ -0,0 +1,119 @@ +// This class is temporary. It contains functions that will eventually +// move into OpenMetaverse.Voice. +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using OpenMetaverse; +using System.Windows.Forms; +using OpenMetaverse.Voice; +using Radegast; + +namespace Radegast.Core +{ + public class VoiceSession + { + internal string SessionHandle; + private static Dictionary knownParticipants; + public string RegionName; + internal bool IsSpatial { get; set; } + private VoiceGateway connector; + + public VoiceGateway Connector { get { return connector; } } + + public event System.EventHandler OnParticipantAdded; + public event System.EventHandler OnParticipantUpdate; + public event System.EventHandler OnParticipantRemoved; + + public VoiceSession(VoiceGateway conn, string handle) + { + SessionHandle = handle; + connector = conn; + + IsSpatial = true; + knownParticipants = new Dictionary(); + } + + /// + /// Close this session. + /// + internal void Close() + { + } + + internal void ParticipantUpdate(string URI, + bool isMuted, + bool isSpeaking, + int volume, + float energy) + { + // Locate in this session + VoiceParticipant p = FindParticipant(URI); + + // Set properties + p.SetProperties(isSpeaking, isMuted, energy); + + // Inform interested parties. + if (OnParticipantUpdate != null) + OnParticipantUpdate(p, null); + } + + internal void AddParticipant(string URI) + { + VoiceParticipant p = FindParticipant(URI); + + // Inform interested parties. + if (OnParticipantAdded != null) + OnParticipantAdded(p, null); + + // If this is ME, start sending position updates. + if (p.ID == connector.Client.Self.AgentID) + connector.PosUpdating(true); + } + + internal void RemoveParticipant(string URI) + { + if (!knownParticipants.ContainsKey(URI)) + return; + + VoiceParticipant p = knownParticipants[URI]; + + // Remove from list for this session. + knownParticipants.Remove(URI); + + // Inform interested parties. + if (OnParticipantRemoved != null) + OnParticipantRemoved(p, null); + + // If this was ME, stop sending position. + if (p.ID == connector.Client.Self.AgentID) + connector.PosUpdating(false); + } + + private VoiceParticipant FindParticipant(string puri) + { + VoiceParticipant p; + + lock (knownParticipants) + { + if (knownParticipants.ContainsKey(puri)) + p = knownParticipants[puri]; + else + { + // It was not found, so add it. + p = new VoiceParticipant(puri, this); + knownParticipants.Add(puri, p); + } + } +/* TODO + // Fill in the name. + if (p.Name == null || p.Name.StartsWith("Loading...")) + p.Name = control.instance.getAvatarName(p.ID); + return p; +*/ + return p; + } + + + } +} diff --git a/Radegast/GUI/Consoles/VoiceConsole.Designer.cs b/Radegast/GUI/Consoles/VoiceConsole.Designer.cs index eb7ca57..a4138ca 100644 --- a/Radegast/GUI/Consoles/VoiceConsole.Designer.cs +++ b/Radegast/GUI/Consoles/VoiceConsole.Designer.cs @@ -63,6 +63,16 @@ namespace Radegast this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(VoiceConsole)); this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.pictureBox2 = new System.Windows.Forms.PictureBox(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.progressBar1 = new System.Windows.Forms.ProgressBar(); + this.label2 = new System.Windows.Forms.Label(); + this.micLevel = new System.Windows.Forms.TrackBar(); + this.spkrLevel = new System.Windows.Forms.TrackBar(); + this.label1 = new System.Windows.Forms.Label(); + this.spkrDevice = new System.Windows.Forms.ComboBox(); + this.micDevice = new System.Windows.Forms.ComboBox(); + this.chkVoiceEnable = new System.Windows.Forms.CheckBox(); this.participants = new Radegast.ListViewNoFlicker(); this.avatarContext = new Radegast.RadegastContextMenuStrip(this.components); this.ctxProfile = new System.Windows.Forms.ToolStripMenuItem(); @@ -75,11 +85,16 @@ namespace Radegast this.ctxAnim = new System.Windows.Forms.ToolStripMenuItem(); this.ctxPoint = new System.Windows.Forms.ToolStripMenuItem(); this.ctxSource = new System.Windows.Forms.ToolStripMenuItem(); - this.chkVoiceEnable = new System.Windows.Forms.CheckBox(); this.TalkStates = new System.Windows.Forms.ImageList(this.components); + this.micMute = new System.Windows.Forms.CheckBox(); + this.spkrMute = new System.Windows.Forms.CheckBox(); this.splitContainer1.Panel1.SuspendLayout(); this.splitContainer1.Panel2.SuspendLayout(); this.splitContainer1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.micLevel)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.spkrLevel)).BeginInit(); this.avatarContext.SuspendLayout(); this.SuspendLayout(); // @@ -91,6 +106,17 @@ namespace Radegast // // splitContainer1.Panel1 // + this.splitContainer1.Panel1.Controls.Add(this.spkrMute); + this.splitContainer1.Panel1.Controls.Add(this.micMute); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox2); + this.splitContainer1.Panel1.Controls.Add(this.pictureBox1); + this.splitContainer1.Panel1.Controls.Add(this.progressBar1); + this.splitContainer1.Panel1.Controls.Add(this.label2); + this.splitContainer1.Panel1.Controls.Add(this.micLevel); + this.splitContainer1.Panel1.Controls.Add(this.spkrLevel); + this.splitContainer1.Panel1.Controls.Add(this.label1); + this.splitContainer1.Panel1.Controls.Add(this.spkrDevice); + this.splitContainer1.Panel1.Controls.Add(this.micDevice); this.splitContainer1.Panel1.Controls.Add(this.chkVoiceEnable); // // splitContainer1.Panel2 @@ -101,6 +127,105 @@ namespace Radegast this.splitContainer1.TabIndex = 7; this.splitContainer1.TabStop = false; // + // pictureBox2 + // + this.pictureBox2.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox2.Image"))); + this.pictureBox2.InitialImage = ((System.Drawing.Image)(resources.GetObject("pictureBox2.InitialImage"))); + this.pictureBox2.Location = new System.Drawing.Point(8, 290); + this.pictureBox2.Name = "pictureBox2"; + this.pictureBox2.Size = new System.Drawing.Size(31, 29); + this.pictureBox2.TabIndex = 19; + this.pictureBox2.TabStop = false; + // + // pictureBox1 + // + this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image"))); + this.pictureBox1.InitialImage = ((System.Drawing.Image)(resources.GetObject("pictureBox1.InitialImage"))); + this.pictureBox1.Location = new System.Drawing.Point(218, 205); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(31, 29); + this.pictureBox1.TabIndex = 18; + this.pictureBox1.TabStop = false; + // + // progressBar1 + // + this.progressBar1.Location = new System.Drawing.Point(158, 14); + this.progressBar1.Maximum = 8; + this.progressBar1.Name = "progressBar1"; + this.progressBar1.Size = new System.Drawing.Size(147, 15); + this.progressBar1.TabIndex = 17; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(13, 205); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(37, 13); + this.label2.TabIndex = 16; + this.label2.Text = "Levels"; + // + // micLevel + // + this.micLevel.BackColor = System.Drawing.SystemColors.Control; + this.micLevel.LargeChange = 20; + this.micLevel.Location = new System.Drawing.Point(108, 205); + this.micLevel.Maximum = 100; + this.micLevel.Minimum = -100; + this.micLevel.Name = "micLevel"; + this.micLevel.Size = new System.Drawing.Size(104, 42); + this.micLevel.TabIndex = 15; + this.micLevel.TickFrequency = 20; + this.micLevel.ValueChanged += new System.EventHandler(this.micLevel_ValueChanged); + // + // spkrLevel + // + this.spkrLevel.Location = new System.Drawing.Point(249, 205); + this.spkrLevel.Maximum = 100; + this.spkrLevel.Minimum = -100; + this.spkrLevel.Name = "spkrLevel"; + this.spkrLevel.Size = new System.Drawing.Size(104, 42); + this.spkrLevel.TabIndex = 14; + this.spkrLevel.TickFrequency = 20; + this.spkrLevel.ValueChanged += new System.EventHandler(this.spkrLevel_ValueChanged); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(10, 234); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(70, 13); + this.label1.TabIndex = 13; + this.label1.Text = "Device select"; + // + // spkrDevice + // + this.spkrDevice.FormattingEnabled = true; + this.spkrDevice.Location = new System.Drawing.Point(45, 298); + this.spkrDevice.Name = "spkrDevice"; + this.spkrDevice.Size = new System.Drawing.Size(308, 21); + this.spkrDevice.TabIndex = 12; + this.spkrDevice.SelectedIndexChanged += new System.EventHandler(this.spkrDevice_SelectedIndexChanged); + // + // micDevice + // + this.micDevice.FormattingEnabled = true; + this.micDevice.Location = new System.Drawing.Point(45, 264); + this.micDevice.Name = "micDevice"; + this.micDevice.Size = new System.Drawing.Size(308, 21); + this.micDevice.TabIndex = 11; + this.micDevice.SelectedIndexChanged += new System.EventHandler(this.micDevice_SelectedIndexChanged); + // + // chkVoiceEnable + // + this.chkVoiceEnable.AutoSize = true; + this.chkVoiceEnable.Location = new System.Drawing.Point(13, 12); + this.chkVoiceEnable.Name = "chkVoiceEnable"; + this.chkVoiceEnable.Size = new System.Drawing.Size(86, 17); + this.chkVoiceEnable.TabIndex = 0; + this.chkVoiceEnable.Text = "Enable voice"; + this.chkVoiceEnable.UseVisualStyleBackColor = true; + this.chkVoiceEnable.Click += new System.EventHandler(this.chkVoiceEnable_Click); + // // participants // this.participants.AllowDrop = true; @@ -118,7 +243,6 @@ namespace Radegast this.participants.TabIndex = 8; this.participants.UseCompatibleStateImageBehavior = false; this.participants.View = System.Windows.Forms.View.List; - this.participants.SelectedIndexChanged += new System.EventHandler(this.lvwObjects_SelectedIndexChanged); // // avatarContext // @@ -135,7 +259,6 @@ namespace Radegast this.ctxSource}); this.avatarContext.Name = "avatarContext"; this.avatarContext.Size = new System.Drawing.Size(68, 224); - this.avatarContext.Opening += new System.ComponentModel.CancelEventHandler(this.avatarContext_Opening); // // ctxProfile // @@ -187,16 +310,6 @@ namespace Radegast this.ctxSource.Name = "ctxSource"; this.ctxSource.Size = new System.Drawing.Size(67, 22); // - // chkVoiceEnable - // - this.chkVoiceEnable.AutoSize = true; - this.chkVoiceEnable.Location = new System.Drawing.Point(13, 12); - this.chkVoiceEnable.Name = "chkVoiceEnable"; - this.chkVoiceEnable.Size = new System.Drawing.Size(86, 17); - this.chkVoiceEnable.TabIndex = 0; - this.chkVoiceEnable.Text = "Enable voice"; - this.chkVoiceEnable.UseVisualStyleBackColor = true; - // // TalkStates // this.TalkStates.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("TalkStates.ImageStream"))); @@ -205,6 +318,28 @@ namespace Radegast this.TalkStates.Images.SetKeyName(1, "Talking.png"); this.TalkStates.Images.SetKeyName(2, "TalkMute.png"); // + // micMute + // + this.micMute.AutoSize = true; + this.micMute.Checked = true; + this.micMute.CheckState = System.Windows.Forms.CheckState.Checked; + this.micMute.Location = new System.Drawing.Point(119, 182); + this.micMute.Name = "micMute"; + this.micMute.Size = new System.Drawing.Size(50, 17); + this.micMute.TabIndex = 20; + this.micMute.Text = "Mute"; + this.micMute.UseVisualStyleBackColor = true; + // + // spkrMute + // + this.spkrMute.AutoSize = true; + this.spkrMute.Location = new System.Drawing.Point(249, 182); + this.spkrMute.Name = "spkrMute"; + this.spkrMute.Size = new System.Drawing.Size(50, 17); + this.spkrMute.TabIndex = 21; + this.spkrMute.Text = "Mute"; + this.spkrMute.UseVisualStyleBackColor = true; + // // VoiceConsole // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -217,6 +352,10 @@ namespace Radegast this.splitContainer1.Panel1.PerformLayout(); this.splitContainer1.Panel2.ResumeLayout(false); this.splitContainer1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.micLevel)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.spkrLevel)).EndInit(); this.avatarContext.ResumeLayout(false); this.ResumeLayout(false); @@ -239,5 +378,16 @@ namespace Radegast public ToolStripMenuItem ctxPay; private CheckBox chkVoiceEnable; private ImageList TalkStates; + private ComboBox micDevice; + private Label label1; + private ComboBox spkrDevice; + private TrackBar spkrLevel; + private Label label2; + private TrackBar micLevel; + private ProgressBar progressBar1; + private PictureBox pictureBox2; + private PictureBox pictureBox1; + private CheckBox micMute; + private CheckBox spkrMute; } } diff --git a/Radegast/GUI/Consoles/VoiceConsole.cs b/Radegast/GUI/Consoles/VoiceConsole.cs index dcff17c..a0f03e6 100644 --- a/Radegast/GUI/Consoles/VoiceConsole.cs +++ b/Radegast/GUI/Consoles/VoiceConsole.cs @@ -57,6 +57,8 @@ namespace Radegast private Avatar currentAvatar; private Dictionary avatars = new Dictionary(); private Dictionary bots = new Dictionary(); + private Radegast.Core.VoiceGateway gateway; + private Radegast.Core.VoiceSession session; public VoiceConsole(RadegastInstance instance) { @@ -69,22 +71,64 @@ namespace Radegast // Callbacks netcom.ClientLoginStatus += new EventHandler(netcom_ClientLoginStatus); netcom.ClientLoggedOut += new EventHandler(netcom_ClientLoggedOut); + + gateway = new Radegast.Core.VoiceGateway(this.instance.Client); + SetProgress(0); RegisterClientEvents(client); this.instance.MainForm.Load += new EventHandler(MainForm_Load); SorterClass sc = new SorterClass(); participants.ListViewItemSorter = sc; + + + chkVoiceEnable.Checked = instance.GlobalSettings["Voice.enabled"].AsBoolean(); +// if (chkVoiceEnable.Checked) +// gateway.Start(); } private void RegisterClientEvents(GridClient client) { + gateway.OnSessionCreate += new EventHandler(gateway_OnSessionCreate); + } + + void gateway_OnSessionCreate(object sender, EventArgs e) + { + session = sender as Radegast.Core.VoiceSession; + participants.Items.Clear(); + + // Default Mic off and Spkr on + gateway.MicMute = true; + gateway.SpkrMute = false; + gateway.SpkrLevel = 64; + gateway.MicLevel = 64; } private void UnregisterClientEvents(GridClient client) { } +#region Connection Status + void gateway_OnDaemonRunning() + { + SetProgress( 1 ); + } + + void SetProgress(int value) + { + if (value == progressBar1.Maximum) + progressBar1.ForeColor = Color.Green; + else if (value > (progressBar1.Maximum / 2)) + progressBar1.ForeColor = Color.Yellow; + else + progressBar1.ForeColor = Color.Red; + + progressBar1.Value = value; + } +#endregion + #region Sessions + #endregion + void instance_ClientChanged(object sender, ClientChangedEventArgs e) { UnregisterClientEvents(e.OldClient); @@ -106,69 +150,78 @@ namespace Radegast private void netcom_ClientLoginStatus(object sender, ClientLoginEventArgs e) { if (e.Status != LoginStatus.Success) return; - - client.Avatars.RequestAvatarProperties(client.Self.AgentID); - - } + } private void netcom_ClientLoggedOut(object sender, EventArgs e) { participants.Items.Clear(); } - private void lvwObjects_SelectedIndexChanged(object sender, EventArgs e) + #region Talk control + void OnMouseUp(object sender, System.Windows.Forms.MouseEventArgs e) { - if (participants.SelectedItems.Count == 0) + if (e.Button == System.Windows.Forms.MouseButtons.Middle) { - currentAvatar = null; - } - else - { - currentAvatar = client.Network.CurrentSim.ObjectsAvatars.Find(delegate(Avatar a) - { - return a.ID == (UUID)participants.SelectedItems[0].Tag; - }); - + micMute_Set(true); + gateway.MicMute = true; } } - private void avatarContext_Opening(object sender, CancelEventArgs e) + void OnMouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { - if (participants.SelectedItems.Count == 0 && !instance.State.IsPointing) + if (e.Button == System.Windows.Forms.MouseButtons.Middle) { - e.Cancel = true; - return; + micMute_Set(false); + gateway.MicMute = false; } - else if (instance.State.IsPointing) + } + #endregion + + #region Audio Settings + private void micMute_Set(bool on) + { + this.BeginInvoke(new MethodInvoker(delegate() { - ctxPoint.Enabled = true; - ctxPoint.Text = "Unpoint"; - } - instance.ContextActionManager.AddContributions( - avatarContext, typeof(Avatar), participants.SelectedItems[0]); + micMute.Checked = on; + })); } - private void ctxSource_Click(object sender, EventArgs e) + private void spkrDevice_SelectedIndexChanged(object sender, EventArgs e) { - if (participants.SelectedItems.Count != 1) return; - instance.State.EffectSource = (UUID)participants.SelectedItems[0].Tag; } - private void ctxPay_Click(object sender, EventArgs e) + private void micDevice_SelectedIndexChanged(object sender, EventArgs e) { - if (participants.SelectedItems.Count != 1) return; - (new frmPay(instance, (UUID)participants.SelectedItems[0].Tag, instance.getAvatarName((UUID)participants.SelectedItems[0].Tag), false)).ShowDialog(); + } - private void rtbChat_MouseUp(object sender, MouseEventArgs e) + private void micLevel_ValueChanged(object sender, EventArgs e) { - if (e.Button!=MouseButtons.Right) return; - RadegastContextMenuStrip cms = new RadegastContextMenuStrip(); - instance.ContextActionManager.AddContributions(cms,instance.Client); - cms.Show((Control)sender,new Point(e.X,e.Y)); + } + private void spkrLevel_ValueChanged(object sender, EventArgs e) + { + + } +#endregion + + private void chkVoiceEnable_Click(object sender, EventArgs e) + { + chkVoiceEnable.Checked = !chkVoiceEnable.Checked; + instance.GlobalSettings["Voice.enabled"] = + OpenMetaverse.StructuredData.OSD.FromBoolean(chkVoiceEnable.Checked); + + if (chkVoiceEnable.Checked) + { + gateway.Start(); + } + else + { + gateway.Stop(); + } + } } } diff --git a/Radegast/GUI/Consoles/VoiceConsole.resx b/Radegast/GUI/Consoles/VoiceConsole.resx index eeb4ff7..06e23d1 100644 --- a/Radegast/GUI/Consoles/VoiceConsole.resx +++ b/Radegast/GUI/Consoles/VoiceConsole.resx @@ -117,6 +117,79 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + 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 + + 122, 17 diff --git a/plugins/Radegast.Plugin.Voice/Session.cs b/plugins/Radegast.Plugin.Voice/Session.cs index 5c3594a..94b83a6 100644 --- a/plugins/Radegast.Plugin.Voice/Session.cs +++ b/plugins/Radegast.Plugin.Voice/Session.cs @@ -14,7 +14,6 @@ namespace Radegast.Plugin.Voice private VoiceControl control; internal string SessionHandle; private static Dictionary knownParticipants; - private SessionForm sessionForm; public string RegionName; internal bool IsSpatial { get; set; } private VoiceGateway connector; diff --git a/plugins/Radegast.Plugin.Voice/VoiceClient.cs b/plugins/Radegast.Plugin.Voice/VoiceClient.cs index 13528d2..27f430f 100644 --- a/plugins/Radegast.Plugin.Voice/VoiceClient.cs +++ b/plugins/Radegast.Plugin.Voice/VoiceClient.cs @@ -24,7 +24,6 @@ namespace Radegast.Plugin.Voice private string slvoiceArgs = "-ll -1"; private string daemonNode = "127.0.0.1"; private int daemonPort = 44124; - private VoiceGateway.VoicePosition position; private string voiceUser; private string voicePassword; private string spatialUri; @@ -36,9 +35,18 @@ namespace Radegast.Plugin.Voice private string regionName; private Thread posThread; private ManualResetEvent posRestart; + + private VoiceGateway.VoicePosition position; + + // Last places Self was located private Vector3d oldPosition; private Vector3d oldAt; + private SessionForm sessionForm; + private List inputDevices; + public List CaptureDevices { get { return inputDevices; } } + private List outputDevices; + public List PlaybackDevices { get { return outputDevices; } } public event EventHandler OnSessionCreate; public event EventHandler OnSessionRemove; @@ -795,6 +803,7 @@ namespace Radegast.Plugin.Voice // Default comes form the settings file string selectedOutput = control.instance.GlobalSettings["plugin.voice.render"].AsString(); + outputDevices = RenderDevices; control.voiceConsole.SetRenderDevices(RenderDevices, selectedOutput); } @@ -817,6 +826,7 @@ namespace Radegast.Plugin.Voice // Default comes from the settings file. string selectedInput = control.instance.GlobalSettings["plugin.voice.capture"].AsString(); + inputDevices = CaptureDevices; control.voiceConsole.SetCaptureDevices( CaptureDevices, selectedInput); } -- 2.11.0