/*
* 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());
}
}
}