FlightGear next
soundmanager.cxx
Go to the documentation of this file.
1/*
2 * SPDX-FileName: soundmanager.cxx
3 * SPDX-FileComment: Wraps the SimGear OpenAl sound manager class
4 * SPDX-FileCopyrightText: Copyright (C) 2001 Curtis L. Olson - http://www.flightgear.org/~curt
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include <config.h>
9
10#include <simgear/sound/soundmgr.hxx>
11#include <simgear/structure/commands.hxx>
12
13#include "VoiceSynthesizer.hxx"
14#include "sample_queue.hxx"
15#include "soundmanager.hxx"
16#include <Main/globals.hxx>
17#include <Main/fg_props.hxx>
18#include <Viewer/view.hxx>
19
20#include <stdio.h>
21
22#include <vector>
23#include <string>
24
25#ifdef ENABLE_AUDIO_SUPPORT
29class Listener : public SGPropertyChangeListener
30{
31public:
32 Listener(FGSoundManager *wrapper) : _wrapper(wrapper) {}
33 virtual void valueChanged (SGPropertyNode * node);
34
35private:
36 FGSoundManager* _wrapper;
37};
38
39void Listener::valueChanged(SGPropertyNode * node)
40{
41 _wrapper->activate(node->getBoolValue());
42}
43
45 : _active_dt(0.0),
46 _is_initialized(false),
47 _enabled(false),
48 _listener(new Listener(this))
49{
50}
51
53{
54}
55
56void FGSoundManager::init()
57{
58 _sound_working = fgGetNode("/sim/sound/working");
59 _sound_enabled = fgGetNode("/sim/sound/enabled");
60 _volume = fgGetNode("/sim/sound/volume");
61 _device_name = fgGetNode("/sim/sound/device-name");
62
63 _velocityNorthFPS = fgGetNode("velocities/speed-north-fps", true);
64 _velocityEastFPS = fgGetNode("velocities/speed-east-fps", true);
65 _velocityDownFPS = fgGetNode("velocities/speed-down-fps", true);
66
67 _frozen = fgGetNode("sim/freeze/master");
68
69 SGPropertyNode_ptr scenery_loaded = fgGetNode("sim/sceneryloaded", true);
70 scenery_loaded->addChangeListener(_listener.get());
71
72 globals->get_commands()->addCommand("play-audio-sample", this, &FGSoundManager::playAudioSampleCommand);
73
74
75 reinit();
76}
77
78void FGSoundManager::shutdown()
79{
80 SGPropertyNode_ptr scenery_loaded = fgGetNode("sim/sceneryloaded", true);
81 scenery_loaded->removeChangeListener(_listener.get());
82
83 stop();
84
85 _queue.clear();
86 globals->get_commands()->removeCommand("play-audio-sample");
87
88
89 SGSoundMgr::shutdown();
90}
91
92void FGSoundManager::reinit()
93{
94 _is_initialized = false;
95
96 if (_is_initialized && !_sound_working->getBoolValue())
97 {
98 // shutdown sound support
99 stop();
100 return;
101 }
102
103 if (!_sound_working->getBoolValue())
104 {
105 return;
106 }
107
108 update_device_list();
109
110 select_device(_device_name->getStringValue().c_str());
111 SGSoundMgr::reinit();
112 _is_initialized = true;
113
114 activate(fgGetBool("sim/sceneryloaded", true));
115}
116
117void FGSoundManager::activate(bool State)
118{
119 if (_is_initialized)
120 {
121 if (State)
122 {
123 SGSoundMgr::activate();
124 }
125 }
126}
127
128void FGSoundManager::update_device_list()
129{
130 std::vector <std::string>devices = get_available_devices();
131 for (unsigned int i=0; i<devices.size(); i++) {
132 SGPropertyNode *p = fgGetNode("/sim/sound/devices/device", i, true);
133 p->setStringValue(devices[i].c_str());
134 }
135 devices.clear();
136}
137
138bool FGSoundManager::stationaryView() const
139{
140 // this is an ugly hack to decide if the *viewer* is stationary,
141 // since we don't model the viewer velocity directly.
142 flightgear::View* _view = globals->get_current_view();
143 return (_view->getXOffset_m () == 0.0 && _view->getYOffset_m () == 0.0 &&
144 _view->getZOffset_m () == 0.0);
145}
146
147// Update sound manager and propagate property values,
148// since the sound manager doesn't read any properties itself.
149// Actual sound update is triggered by the subsystem manager.
150void FGSoundManager::update(double dt)
151{
152 if (is_working() && _is_initialized && _sound_working->getBoolValue())
153 {
154 bool enabled = _sound_enabled->getBoolValue() && !_frozen->getBoolValue();
155 if (enabled != _enabled)
156 {
157 if (enabled)
158 resume();
159 else
160 suspend();
161 _enabled = enabled;
162 }
163 if (enabled)
164 {
165 flightgear::View* _view = globals->get_current_view();
166 set_position( _view->getViewPosition(), _view->getPosition() );
167 set_orientation( _view->getViewOrientation() );
168
169 SGVec3d velocity(SGVec3d::zeros());
170 if (!stationaryView()) {
171 velocity = SGVec3d(_velocityNorthFPS->getDoubleValue(),
172 _velocityEastFPS->getDoubleValue(),
173 _velocityDownFPS->getDoubleValue() );
174 }
175
176 set_velocity( velocity );
177
178 float vf = 1.0f;
179 if (_active_dt < 5.0) {
180 _active_dt += dt;
181 vf = std::min(std::pow(_active_dt*0.2, 5.0), 1.0);
182 }
183
184 set_volume(vf*_volume->getFloatValue());
185 SGSoundMgr::update(dt);
186 }
187 }
188}
189
196bool FGSoundManager::playAudioSampleCommand(const SGPropertyNode * arg, SGPropertyNode * root)
197{
198 std::string qname = arg->getStringValue("queue", "");
199 std::string name = !qname.empty() ? qname : "chatter";
200 std::string path = arg->getStringValue("path");
201 std::string file = arg->getStringValue("file");
202 float volume = arg->getFloatValue("volume");
203
204 const auto fullPath = SGPath(path) / file;
205 const auto foundPath = globals->resolve_maybe_aircraft_path(
206 fullPath.utf8Str());
207 if (!foundPath.exists()) {
208 SG_LOG(SG_GENERAL, SG_ALERT, "play-audio-sample: no such file: '" <<
209 fullPath.utf8Str() << "'");
210 return false;
211 }
212
213 // SG_LOG(SG_GENERAL, SG_ALERT, "Playing '" << foundPath.utf8Str() << "'");
214 try {
215 SGSoundSample *msg = new SGSoundSample(foundPath);
216 msg->set_volume( volume );
217
218 if (name == "instant")
219 {
220 static const char *r = "0123456789abcdefghijklmnopqrstuvwxyz"
221 "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
222 std::string rstr = "NASAL: ";
223 for (int i=0; i<10; i++) {
224 rstr.push_back( r[rand() % strlen(r)] );
225 }
226
227 // Add a special queue-name 'instant' which does not put samples
228 // into a sample queue but plays them instantly.
229 SGSampleGroup* sgr = find("NASAL instant queue", true);
230 sgr->tie_to_listener();
231 sgr->add(msg, rstr);
232 sgr->play_once(rstr);
233 }
234 else
235 {
236 if ( !_queue[name] ) {
237 _queue[name] = new FGSampleQueue(this, name);
238 _queue[name]->tie_to_listener();
239 }
240 _queue[name]->add( msg );
241 }
242
243 return true;
244
245 } catch (const sg_io_exception&) {
246 SG_LOG(SG_GENERAL, SG_ALERT, "play-audio-sample: "
247 "failed to load '" << foundPath.utf8Str() << "'");
248 return false;
249 }
250}
251
252VoiceSynthesizer * FGSoundManager::getSynthesizer( const std::string & voice )
253{
254 std::map<std::string,VoiceSynthesizer*>::iterator it = _synthesizers.find(voice);
255 if( it == _synthesizers.end() ) {
256 VoiceSynthesizer * synthesizer = new FLITEVoiceSynthesizer( voice );
257 _synthesizers[voice] = synthesizer;
258 return synthesizer;
259 }
260 return it->second;
261}
262#endif // ENABLE_AUDIO_SUPPORT
263
264
265// Register the subsystem.
266SGSubsystemMgr::Registrant<FGSoundManager> registrantFGSoundManager(
267 SGSubsystemMgr::SOUND,
268 {{"SGSoundMgr", SGSubsystemMgr::Dependency::HARD}});
#define p(x)
bool suspend
Definition JSBSim.cpp:93
#define i(x)
flightgear::View * get_current_view() const
Definition globals.cxx:960
SGPath resolve_maybe_aircraft_path(const std::string &branch) const
Same as above, but test for non 'Aircraft/' branch paths, and always resolve them against fg_root.
Definition globals.cxx:560
SGCommandMgr * get_commands()
Definition globals.hxx:330
void update(double dt)
A Voice Synthesizer Interface.
const SGGeod & getPosition() const
Definition view.hxx:101
double getXOffset_m() const
Definition view.hxx:121
const SGQuatd & getViewOrientation()
Definition view.hxx:178
double getYOffset_m() const
Definition view.hxx:122
const SGVec3d & getViewPosition()
Definition view.hxx:177
double getZOffset_m() const
Definition view.hxx:123
const char * name
FGGlobals * globals
Definition globals.cxx:142
bool fgGetBool(char const *name, bool def)
Get a bool value for a property.
Definition proptest.cpp:25
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27
SGSubsystemMgr::Registrant< FGSoundManager > registrantFGSoundManager(SGSubsystemMgr::SOUND, {{"SGSoundMgr", SGSubsystemMgr::Dependency::HARD}})