OSDN Git Service

de84f8fc0f9c31b1d90a194f515fc7b95907fdf4
[radegast/radegast.git] / Radegast / Core / Media / Speech.cs
1 // 
2 // Radegast Metaverse Client
3 // Copyright (c) 2009-2013, Radegast Development Team
4 // All rights reserved.
5 // 
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // 
9 //     * Redistributions of source code must retain the above copyright notice,
10 //       this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above copyright
12 //       notice, this list of conditions and the following disclaimer in the
13 //       documentation and/or other materials provided with the distribution.
14 //     * Neither the name of the application "Radegast", nor the names of its
15 //       contributors may be used to endorse or promote products derived from
16 //       this software without specific prior written permission.
17 // 
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 //
29 // $Id: Sound.cs 502 2010-03-14 23:13:46Z latifer $
30 //
31 using System;
32 using System.Runtime.InteropServices;
33 using FMOD;
34 using OpenMetaverse;
35
36 namespace Radegast.Media
37 {
38     public class Speech : MediaObject
39     {
40         /// <summary>
41         /// Fired when a stream meta data is received
42         /// </summary>
43         /// <param name="sender">Sender</param>
44         /// <param name="e">Key, value are sent in e</param>
45         public delegate void SpeechDoneCallback(object sender, EventArgs e);
46         /// <summary>
47         /// Fired when a stream meta data is received
48         /// </summary>
49         public event SpeechDoneCallback OnSpeechDone;
50         private String filename;
51         private Vector3 speakerPos;
52
53         /// <summary>
54         /// If true, decrease distance falloff effect in volume.
55         /// </summary>
56         private static Boolean compress = true;
57         public static Boolean Compress
58         {
59             set { compress = value; }
60             get { return compress; }
61         }
62
63         /// <summary>
64         /// Creates a new sound object
65         /// </summary>
66         /// <param name="system">Sound system</param>
67         public Speech()
68             : base()
69         {
70             extraInfo.format = SOUND_FORMAT.PCM16;
71         }
72
73
74         /// <summary>
75         /// Releases resources of this sound object
76         /// </summary>
77         public override void Dispose()
78         {
79             if (sound != null)
80             {
81                 sound.release();
82                 sound = null;
83             }
84             base.Dispose();
85         }
86
87         /// <summary>
88         /// Plays audio from a file, as created by an external speech synthesizer.
89         /// </summary>
90         /// <param name="filename">Name of a WAV file</param>
91         /// <param name="global"></param>
92         /// <returns>Length of the sound in ms</returns>
93         public uint Play(string speakfile, bool global, Vector3 pos)
94         {
95             uint len = 0;
96
97             speakerPos = pos;
98             filename = speakfile;
99
100             // Set flags to determine how it will be played.
101             FMOD.MODE mode = FMOD.MODE.SOFTWARE | FMOD.MODE._3D;
102
103             // Set coordinate space interpretation.
104             if (global)
105                 mode |= FMOD.MODE._3D_WORLDRELATIVE;
106             else
107                 mode |= FMOD.MODE._3D_HEADRELATIVE;
108
109             System.Threading.AutoResetEvent done = new System.Threading.AutoResetEvent(false);
110
111             invoke(new SoundDelegate(
112                 delegate
113                 {
114                     try
115                     {
116                         FMODExec(
117                         system.createSound(filename,
118                         mode,
119                         ref extraInfo,
120                         ref sound));
121
122                         // Register for callbacks.
123                         RegisterSound(sound);
124
125                         // Find out how long the sound should play
126                         FMODExec(sound.getLength(ref len, TIMEUNIT.MS));
127
128                         // Allocate a channel, initially paused.
129                         FMODExec(system.playSound(CHANNELINDEX.FREE, sound, true, ref channel));
130
131                         // Set general Speech volume.
132                         //TODO Set this in the GUI
133                         volume = 1.0f;
134                         FMODExec(channel.setVolume(volume));
135
136                         // Set attenuation limits so distant people get a little softer,
137                         // but not TOO soft
138                         if (compress)
139                             FMODExec(sound.set3DMinMaxDistance(
140                                     2f,       // Any closer than this gets no louder
141                                     3.5f));     // Further than this gets no softer.
142                         else
143                             FMODExec(sound.set3DMinMaxDistance(
144                                     2f,       // Any closer than this gets no louder
145                                     10f));     // Further than this gets no softer.
146
147                         // Set speaker position.
148                         position = FromOMVSpace(speakerPos);
149                         FMODExec(channel.set3DAttributes(
150                            ref position,
151                            ref ZeroVector));
152
153                         Logger.Log(
154                             String.Format(
155                                 "Speech at <{0:0.0},{1:0.0},{2:0.0}>",
156                                 position.x,
157                                 position.y,
158                                 position.z),
159                             Helpers.LogLevel.Debug);
160
161                         // SET a handler for when it finishes.
162                         FMODExec(channel.setCallback(endCallback));
163                         RegisterChannel(channel);
164
165                         // Un-pause the sound.
166                         FMODExec(channel.setPaused(false));
167                     }
168                     catch (Exception ex)
169                     {
170                         Logger.Log("Error playing speech: ", Helpers.LogLevel.Error, ex);
171                     }
172                     finally
173                     {
174                         done.Set();
175                     }
176                 }));
177
178             done.WaitOne(30 * 1000, false);
179             return len;
180         }
181
182         /// <summary>
183         /// Handler for reaching the end of playback of a speech.
184         /// </summary>
185         /// <returns></returns>
186         protected override RESULT EndCallbackHandler()
187         {
188             invoke(new SoundDelegate(
189                  delegate
190                  {
191                      UnRegisterChannel();
192                      channel = null;
193                      UnRegisterSound();
194                      FMODExec(sound.release());
195                      sound = null;
196
197                      // Tell speech control the file has been played.  Note
198                      // the event is dispatched on FMOD's thread, to make sure
199                      // the event handler does not start a new sound before the
200                      // old one is cleaned up.
201                      if (OnSpeechDone != null)
202                          try
203                          {
204                              OnSpeechDone(this, new EventArgs());
205                          }
206                          catch (Exception) { }
207                  }));
208
209
210             return RESULT.OK;
211         }
212     }
213 }