Files
libremetaverse/OpenMetaverse/Voice/VoiceSession.cs
2014-02-25 15:31:10 +01:00

338 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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
{
/// <summary>
/// Represents a single Voice Session to the Vivox service.
/// </summary>
public class VoiceSession
{
private string m_Handle;
private static Dictionary<string, VoiceParticipant> 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<string, VoiceParticipant>();
}
/// <summary>
/// Close this session.
/// </summary>
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);
}
}
/// <summary>
/// Look up an existing Participants in this session
/// </summary>
/// <param name="puri"></param>
/// <returns></returns>
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
{
/// <summary>
/// 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.
/// </summary>
/// <param name="AccountHandle">Handle returned from successful Connector create request</param>
/// <param name="URI">This is the URI of the terminating point of the session (ie who/what is being called)</param>
/// <param name="Name">This is the display name of the entity being called (user or channel)</param>
/// <param name="Password">Only needs to be supplied when the target URI is password protected</param>
/// <param name="PasswordHashAlgorithm">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.</param>
/// <param name="JoinAudio"></param>
/// <param name="JoinText"></param>
/// <returns></returns>
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());
}
/// <summary>
/// Used to accept a call
/// </summary>
/// <param name="SessionHandle">SessionHandle such as received from SessionNewEvent</param>
/// <param name="AudioMedia">"default"</param>
/// <returns></returns>
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());
}
/// <summary>
/// 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.
/// </summary>
/// <param name="SoundFilePath">The fully qualified path to the sound file.</param>
/// <param name="Loop">True if the file is to be played continuously and false if it is should be played once.</param>
/// <returns></returns>
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());
}
/// <summary>
/// This command is used to stop the audio render process.
/// </summary>
/// <param name="SoundFilePath">The fully qualified path to the sound file issued in the start render command.</param>
/// <returns></returns>
public int SessionRenderAudioStop(string SoundFilePath)
{
string RequestXML = VoiceGateway.MakeXML("SoundFilePath", SoundFilePath);
return Request("Session.RenderAudioStop.1", RequestXML);
}
/// <summary>
/// This is used to end an established session (i.e. hang-up or disconnect).
/// </summary>
/// <param name="SessionHandle">Handle returned from successful Session create request or a SessionNewEvent</param>
/// <returns></returns>
public int SessionTerminate(string SessionHandle)
{
string RequestXML = VoiceGateway.MakeXML("SessionHandle", SessionHandle);
return Request("Session.Terminate.1", RequestXML);
}
/// <summary>
/// Set the combined speaking and listening position in 3D space.
/// </summary>
/// <param name="SessionHandle">Handle returned from successful Session create request or a SessionNewEvent</param>
/// <param name="SpeakerPosition">Speaking position</param>
/// <param name="ListenerPosition">Listening position</param>
/// <returns></returns>
public int SessionSet3DPosition(string SessionHandle, VoicePosition SpeakerPosition, VoicePosition ListenerPosition)
{
StringBuilder sb = new StringBuilder();
sb.Append(VoiceGateway.MakeXML("SessionHandle", SessionHandle));
sb.Append("<SpeakerPosition>");
sb.Append("<Position>");
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("</Position>");
sb.Append("<Velocity>");
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("</Velocity>");
sb.Append("<AtOrientation>");
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("</AtOrientation>");
sb.Append("<UpOrientation>");
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("</UpOrientation>");
sb.Append("<LeftOrientation>");
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("</LeftOrientation>");
sb.Append("</SpeakerPosition>");
sb.Append("<ListenerPosition>");
sb.Append("<Position>");
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("</Position>");
sb.Append("<Velocity>");
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("</Velocity>");
sb.Append("<AtOrientation>");
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("</AtOrientation>");
sb.Append("<UpOrientation>");
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("</UpOrientation>");
sb.Append("<LeftOrientation>");
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("</LeftOrientation>");
sb.Append("</ListenerPosition>");
return Request("Session.Set3DPosition.1", sb.ToString());
}
/// <summary>
/// Set User Volume for a particular user. Does not affect how other users hear that user.
/// </summary>
/// <param name="SessionHandle">Handle returned from successful Session create request or a SessionNewEvent</param>
/// <param name="ParticipantURI"></param>
/// <param name="Volume">The level of the audio, a number between -100 and 100 where 0 represents normal speaking volume</param>
/// <returns></returns>
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());
}
}
}