/* * Copyright (c) 2006-2014, openmetaverse.org * 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.org 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.Text; using OpenMetaverse; namespace OpenMetaverse.Voice { /// /// Represents a single Voice Session to the Vivox service. /// public class VoiceSession { private string m_Handle; private static Dictionary knownParticipants; public string RegionName; private bool m_spatial; public bool IsSpatial { get { return m_spatial; } } private VoiceGateway connector; public VoiceGateway Connector { get { return connector; } } public string Handle { get { return m_Handle; } } public event System.EventHandler OnParticipantAdded; public event System.EventHandler OnParticipantUpdate; public event System.EventHandler OnParticipantRemoved; public VoiceSession(VoiceGateway conn, string handle) { m_Handle = handle; connector = conn; m_spatial = true; knownParticipants = new Dictionary(); } /// /// Close this session. /// internal void Close() { knownParticipants.Clear(); } internal void ParticipantUpdate(string URI, bool isMuted, bool isSpeaking, int volume, float energy) { lock (knownParticipants) { // Locate in this session VoiceParticipant p = FindParticipant(URI); if (p == null) return; // Set properties p.SetProperties(isSpeaking, isMuted, energy); // Inform interested parties. if (OnParticipantUpdate != null) OnParticipantUpdate(p, null); } } internal void AddParticipant(string URI) { lock (knownParticipants) { VoiceParticipant p = FindParticipant(URI); // We expect that to come back null. If it is not // null, this is a duplicate if (p != null) { return; } // It was not found, so add it. p = new VoiceParticipant(URI, this); knownParticipants.Add(URI, p); /* TODO // Fill in the name. if (p.Name == null || p.Name.StartsWith("Loading...")) p.Name = control.instance.getAvatarName(p.ID); return p; */ // Inform interested parties. if (OnParticipantAdded != null) OnParticipantAdded(p, null); } } internal void RemoveParticipant(string URI) { lock (knownParticipants) { VoiceParticipant p = FindParticipant(URI); if (p == null) return; // Remove from list for this session. knownParticipants.Remove(URI); // Inform interested parties. if (OnParticipantRemoved != null) OnParticipantRemoved(p, null); } } /// /// Look up an existing Participants in this session /// /// /// private VoiceParticipant FindParticipant(string puri) { if (knownParticipants.ContainsKey(puri)) return knownParticipants[puri]; return null; } public void Set3DPosition(VoicePosition SpeakerPosition, VoicePosition ListenerPosition) { connector.SessionSet3DPosition(m_Handle, SpeakerPosition, ListenerPosition); } } public partial class VoiceGateway { /// /// Create a Session /// Sessions typically represent a connection to a media session with one or more /// participants. This is used to generate an ‘outbound’ call to another user or /// channel. The specifics depend on the media types involved. A session handle is /// required to control the local user functions within the session (or remote /// users if the current account has rights to do so). Currently creating a /// session automatically connects to the audio media, there is no need to call /// Session.Connect at this time, this is reserved for future use. /// /// Handle returned from successful Connector ‘create’ request /// This is the URI of the terminating point of the session (ie who/what is being called) /// This is the display name of the entity being called (user or channel) /// Only needs to be supplied when the target URI is password protected /// This indicates the format of the password as passed in. This can either be /// “ClearText” or “SHA1UserName”. If this element does not exist, it is assumed to be “ClearText”. If it is /// “SHA1UserName”, the password as passed in is the SHA1 hash of the password and username concatenated together, /// then base64 encoded, with the final “=” character stripped off. /// /// /// public int SessionCreate(string AccountHandle, string URI, string Name, string Password, bool JoinAudio, bool JoinText, string PasswordHashAlgorithm) { StringBuilder sb = new StringBuilder(); sb.Append(VoiceGateway.MakeXML("AccountHandle", AccountHandle)); sb.Append(VoiceGateway.MakeXML("URI", URI)); sb.Append(VoiceGateway.MakeXML("Name", Name)); if (Password != null && Password != "") { sb.Append(VoiceGateway.MakeXML("Password", Password)); sb.Append(VoiceGateway.MakeXML("PasswordHashAlgorithm", PasswordHashAlgorithm)); } sb.Append(VoiceGateway.MakeXML("ConnectAudio", JoinAudio ? "true" : "false")); sb.Append(VoiceGateway.MakeXML("ConnectText", JoinText ? "true" : "false")); sb.Append(VoiceGateway.MakeXML("JoinAudio", JoinAudio ? "true" : "false")); sb.Append(VoiceGateway.MakeXML("JoinText", JoinText ? "true" : "false")); sb.Append(VoiceGateway.MakeXML("VoiceFontID", "0")); return Request("Session.Create.1", sb.ToString()); } /// /// Used to accept a call /// /// SessionHandle such as received from SessionNewEvent /// "default" /// public int SessionConnect(string SessionHandle, string AudioMedia) { StringBuilder sb = new StringBuilder(); sb.Append(VoiceGateway.MakeXML("SessionHandle", SessionHandle)); sb.Append(VoiceGateway.MakeXML("AudioMedia", AudioMedia)); return Request("Session.Connect.1", sb.ToString()); } /// /// This command is used to start the audio render process, which will then play /// the passed in file through the selected audio render device. This command /// should not be issued if the user is on a call. /// /// The fully qualified path to the sound file. /// True if the file is to be played continuously and false if it is should be played once. /// public int SessionRenderAudioStart(string SoundFilePath, bool Loop) { StringBuilder sb = new StringBuilder(); sb.Append(VoiceGateway.MakeXML("SoundFilePath", SoundFilePath)); sb.Append(VoiceGateway.MakeXML("Loop", Loop ? "1" : "0")); return Request("Session.RenderAudioStart.1", sb.ToString()); } /// /// This command is used to stop the audio render process. /// /// The fully qualified path to the sound file issued in the start render command. /// public int SessionRenderAudioStop(string SoundFilePath) { string RequestXML = VoiceGateway.MakeXML("SoundFilePath", SoundFilePath); return Request("Session.RenderAudioStop.1", RequestXML); } /// /// This is used to ‘end’ an established session (i.e. hang-up or disconnect). /// /// Handle returned from successful Session ‘create’ request or a SessionNewEvent /// public int SessionTerminate(string SessionHandle) { string RequestXML = VoiceGateway.MakeXML("SessionHandle", SessionHandle); return Request("Session.Terminate.1", RequestXML); } /// /// Set the combined speaking and listening position in 3D space. /// /// Handle returned from successful Session ‘create’ request or a SessionNewEvent /// Speaking position /// Listening position /// public int SessionSet3DPosition(string SessionHandle, VoicePosition SpeakerPosition, VoicePosition ListenerPosition) { StringBuilder sb = new StringBuilder(); sb.Append(VoiceGateway.MakeXML("SessionHandle", SessionHandle)); sb.Append(""); sb.Append(""); sb.Append(VoiceGateway.MakeXML("X", SpeakerPosition.Position.X.ToString())); sb.Append(VoiceGateway.MakeXML("Y", SpeakerPosition.Position.Y.ToString())); sb.Append(VoiceGateway.MakeXML("Z", SpeakerPosition.Position.Z.ToString())); sb.Append(""); sb.Append(""); sb.Append(VoiceGateway.MakeXML("X", SpeakerPosition.Velocity.X.ToString())); sb.Append(VoiceGateway.MakeXML("Y", SpeakerPosition.Velocity.Y.ToString())); sb.Append(VoiceGateway.MakeXML("Z", SpeakerPosition.Velocity.Z.ToString())); sb.Append(""); sb.Append(""); sb.Append(VoiceGateway.MakeXML("X", SpeakerPosition.AtOrientation.X.ToString())); sb.Append(VoiceGateway.MakeXML("Y", SpeakerPosition.AtOrientation.Y.ToString())); sb.Append(VoiceGateway.MakeXML("Z", SpeakerPosition.AtOrientation.Z.ToString())); sb.Append(""); sb.Append(""); sb.Append(VoiceGateway.MakeXML("X", SpeakerPosition.UpOrientation.X.ToString())); sb.Append(VoiceGateway.MakeXML("Y", SpeakerPosition.UpOrientation.Y.ToString())); sb.Append(VoiceGateway.MakeXML("Z", SpeakerPosition.UpOrientation.Z.ToString())); sb.Append(""); sb.Append(""); sb.Append(VoiceGateway.MakeXML("X", SpeakerPosition.LeftOrientation.X.ToString())); sb.Append(VoiceGateway.MakeXML("Y", SpeakerPosition.LeftOrientation.Y.ToString())); sb.Append(VoiceGateway.MakeXML("Z", SpeakerPosition.LeftOrientation.Z.ToString())); sb.Append(""); sb.Append(""); sb.Append(""); sb.Append(""); sb.Append(VoiceGateway.MakeXML("X", ListenerPosition.Position.X.ToString())); sb.Append(VoiceGateway.MakeXML("Y", ListenerPosition.Position.Y.ToString())); sb.Append(VoiceGateway.MakeXML("Z", ListenerPosition.Position.Z.ToString())); sb.Append(""); sb.Append(""); sb.Append(VoiceGateway.MakeXML("X", ListenerPosition.Velocity.X.ToString())); sb.Append(VoiceGateway.MakeXML("Y", ListenerPosition.Velocity.Y.ToString())); sb.Append(VoiceGateway.MakeXML("Z", ListenerPosition.Velocity.Z.ToString())); sb.Append(""); sb.Append(""); sb.Append(VoiceGateway.MakeXML("X", ListenerPosition.AtOrientation.X.ToString())); sb.Append(VoiceGateway.MakeXML("Y", ListenerPosition.AtOrientation.Y.ToString())); sb.Append(VoiceGateway.MakeXML("Z", ListenerPosition.AtOrientation.Z.ToString())); sb.Append(""); sb.Append(""); sb.Append(VoiceGateway.MakeXML("X", ListenerPosition.UpOrientation.X.ToString())); sb.Append(VoiceGateway.MakeXML("Y", ListenerPosition.UpOrientation.Y.ToString())); sb.Append(VoiceGateway.MakeXML("Z", ListenerPosition.UpOrientation.Z.ToString())); sb.Append(""); sb.Append(""); sb.Append(VoiceGateway.MakeXML("X", ListenerPosition.LeftOrientation.X.ToString())); sb.Append(VoiceGateway.MakeXML("Y", ListenerPosition.LeftOrientation.Y.ToString())); sb.Append(VoiceGateway.MakeXML("Z", ListenerPosition.LeftOrientation.Z.ToString())); sb.Append(""); sb.Append(""); return Request("Session.Set3DPosition.1", sb.ToString()); } /// /// Set User Volume for a particular user. Does not affect how other users hear that user. /// /// Handle returned from successful Session ‘create’ request or a SessionNewEvent /// /// The level of the audio, a number between -100 and 100 where 0 represents ‘normal’ speaking volume /// public int SessionSetParticipantVolumeForMe(string SessionHandle, string ParticipantURI, int Volume) { StringBuilder sb = new StringBuilder(); sb.Append(VoiceGateway.MakeXML("SessionHandle", SessionHandle)); sb.Append(VoiceGateway.MakeXML("ParticipantURI", ParticipantURI)); sb.Append(VoiceGateway.MakeXML("Volume", Volume.ToString())); return Request("Session.SetParticipantVolumeForMe.1", sb.ToString()); } } }