// // Radegast Metaverse Client // Copyright (c) 2009-2014, Radegast Development Team // 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. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the application "Radegast", 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 HOLDER 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. // // $Id: Sound.cs 502 2010-03-14 23:13:46Z latifer $ // using System; using System.Runtime.InteropServices; using FMOD; using OpenMetaverse; namespace Radegast.Media { public class Speech : MediaObject { /// /// Fired when a stream meta data is received /// /// Sender /// Key, value are sent in e public delegate void SpeechDoneCallback(object sender, EventArgs e); /// /// Fired when a stream meta data is received /// public event SpeechDoneCallback OnSpeechDone; private String filename; private Vector3 speakerPos; /// /// If true, decrease distance falloff effect in volume. /// private static Boolean compress = true; public static Boolean Compress { set { compress = value; } get { return compress; } } /// /// If truee use FMODEx 3D sound /// private static bool surround = false; public static bool Surround { set { surround = value; } get { return surround; } } /// /// Creates a new sound object /// /// Sound system public Speech() : base() { extraInfo.format = SOUND_FORMAT.PCM16; } /// /// Releases resources of this sound object /// public override void Dispose() { if (sound != null) { sound.release(); sound = null; } base.Dispose(); } /// /// Plays audio from a file, as created by an external speech synthesizer. /// /// Name of a WAV file /// /// Length of the sound in ms public uint Play(string speakfile, bool global, Vector3 pos) { uint len = 0; speakerPos = pos; filename = speakfile; // Set flags to determine how it will be played. FMOD.MODE mode = FMOD.MODE.SOFTWARE; if (Surround) { mode |= FMOD.MODE._3D; // Set coordinate space interpretation. if (global) mode |= FMOD.MODE._3D_WORLDRELATIVE; else mode |= FMOD.MODE._3D_HEADRELATIVE; } System.Threading.AutoResetEvent done = new System.Threading.AutoResetEvent(false); invoke(new SoundDelegate( delegate { try { FMODExec( system.createSound(filename, mode, ref extraInfo, ref sound)); // Register for callbacks. RegisterSound(sound); // Find out how long the sound should play FMODExec(sound.getLength(ref len, TIMEUNIT.MS)); // Allocate a channel, initially paused. FMODExec(system.playSound(CHANNELINDEX.FREE, sound, true, ref channel)); // Set general Speech volume. //TODO Set this in the GUI volume = 1.0f; FMODExec(channel.setVolume(volume)); if (Surround) { // Set attenuation limits so distant people get a little softer, // but not TOO soft if (compress) FMODExec(sound.set3DMinMaxDistance( 2f, // Any closer than this gets no louder 3.5f)); // Further than this gets no softer. else FMODExec(sound.set3DMinMaxDistance( 2f, // Any closer than this gets no louder 10f)); // Further than this gets no softer. // Set speaker position. position = FromOMVSpace(speakerPos); FMODExec(channel.set3DAttributes( ref position, ref ZeroVector)); Logger.Log( String.Format( "Speech at <{0:0.0},{1:0.0},{2:0.0}>", position.x, position.y, position.z), Helpers.LogLevel.Debug); } // SET a handler for when it finishes. FMODExec(channel.setCallback(endCallback)); RegisterChannel(channel); // Un-pause the sound. FMODExec(channel.setPaused(false)); } catch (Exception ex) { Logger.Log("Error playing speech: ", Helpers.LogLevel.Error, ex); } finally { done.Set(); } })); done.WaitOne(30 * 1000, false); return len; } /// /// Handler for reaching the end of playback of a speech. /// /// protected override RESULT EndCallbackHandler() { invoke(new SoundDelegate( delegate { UnRegisterChannel(); channel = null; UnRegisterSound(); FMODExec(sound.release()); sound = null; // Tell speech control the file has been played. Note // the event is dispatched on FMOD's thread, to make sure // the event handler does not start a new sound before the // old one is cleaned up. if (OnSpeechDone != null) try { OnSpeechDone(this, new EventArgs()); } catch (Exception) { } })); return RESULT.OK; } } }