1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "sci/sci.h"
24 #include "sci/engine/features.h"
25 #include "sci/engine/state.h"
26 #include "sci/engine/kernel.h"
27 #include "sci/engine/vm.h"		// for Object
28 #include "sci/sound/audio.h"
29 #ifdef ENABLE_SCI32
30 #include "sci/sound/audio32.h"
31 #endif
32 #include "sci/sound/soundcmd.h"
33 #include "sci/sound/sync.h"
34 
35 #include "audio/mixer.h"
36 #include "common/system.h"
37 
38 namespace Sci {
39 
40 /**
41  * Used for synthesized music playback
42  */
kDoSound(EngineState * s,int argc,reg_t * argv)43 reg_t kDoSound(EngineState *s, int argc, reg_t *argv) {
44 	if (!s)
45 		return make_reg(0, g_sci->_features->detectDoSoundType());
46 	error("not supposed to call this");
47 }
48 
49 #define CREATE_DOSOUND_FORWARD(_name_) reg_t k##_name_(EngineState *s, int argc, reg_t *argv) { return g_sci->_soundCmd->k##_name_(s, argc, argv); }
50 
51 CREATE_DOSOUND_FORWARD(DoSoundInit)
CREATE_DOSOUND_FORWARD(DoSoundPlay)52 CREATE_DOSOUND_FORWARD(DoSoundPlay)
53 CREATE_DOSOUND_FORWARD(DoSoundDispose)
54 CREATE_DOSOUND_FORWARD(DoSoundMute)
55 CREATE_DOSOUND_FORWARD(DoSoundStop)
56 CREATE_DOSOUND_FORWARD(DoSoundStopAll)
57 CREATE_DOSOUND_FORWARD(DoSoundPause)
58 CREATE_DOSOUND_FORWARD(DoSoundResumeAfterRestore)
59 CREATE_DOSOUND_FORWARD(DoSoundMasterVolume)
60 CREATE_DOSOUND_FORWARD(DoSoundUpdate)
61 CREATE_DOSOUND_FORWARD(DoSoundFade)
62 CREATE_DOSOUND_FORWARD(DoSoundGetPolyphony)
63 CREATE_DOSOUND_FORWARD(DoSoundUpdateCues)
64 CREATE_DOSOUND_FORWARD(DoSoundSendMidi)
65 CREATE_DOSOUND_FORWARD(DoSoundGlobalReverb)
66 CREATE_DOSOUND_FORWARD(DoSoundSetHold)
67 CREATE_DOSOUND_FORWARD(DoSoundGetAudioCapability)
68 CREATE_DOSOUND_FORWARD(DoSoundSuspend)
69 CREATE_DOSOUND_FORWARD(DoSoundSetVolume)
70 CREATE_DOSOUND_FORWARD(DoSoundSetPriority)
71 CREATE_DOSOUND_FORWARD(DoSoundSetLoop)
72 
73 #ifdef ENABLE_SCI32
74 reg_t kDoSoundMac32(EngineState *s, int argc, reg_t *argv) {
75 	// Several SCI 2.1 Middle Mac games, but not all, contain a modified kDoSound
76 	//  in which all but eleven subops were removed, changing their subop values to
77 	//  zero through ten. PQSWAT then restored all of the subops, but kept the new
78 	//  subop values that removing caused in the first place, and assigned new
79 	//  values to the restored subops. It is the only game that does this and it
80 	//  only uses two of them.
81 	switch (argv[0].toUint16()) {
82 	case 0:
83 		return g_sci->_soundCmd->kDoSoundMasterVolume(s, argc - 1, argv + 1);
84 	case 1:
85 		return g_sci->_soundCmd->kDoSoundGetAudioCapability(s, argc - 1, argv + 1);
86 	case 2:
87 		return g_sci->_soundCmd->kDoSoundInit(s, argc - 1, argv + 1);
88 	case 3:
89 		return g_sci->_soundCmd->kDoSoundDispose(s, argc - 1, argv + 1);
90 	case 4:
91 		return g_sci->_soundCmd->kDoSoundPlay(s, argc - 1, argv + 1);
92 	case 5:
93 		return g_sci->_soundCmd->kDoSoundStop(s, argc - 1, argv + 1);
94 	case 6:
95 		return g_sci->_soundCmd->kDoSoundPause(s, argc - 1, argv + 1);
96 	case 7:
97 		return g_sci->_soundCmd->kDoSoundFade(s, argc - 1, argv + 1);
98 	case 8:
99 		return g_sci->_soundCmd->kDoSoundSetVolume(s, argc - 1, argv + 1);
100 	case 9:
101 		return g_sci->_soundCmd->kDoSoundSetLoop(s, argc - 1, argv + 1);
102 	case 10:
103 		return g_sci->_soundCmd->kDoSoundUpdateCues(s, argc - 1, argv + 1);
104 	// PQSWAT only
105 	case 12: // kDoSoundRestore
106 		return kEmpty(s, argc - 1, argv + 1);
107 	case 13:
108 		return g_sci->_soundCmd->kDoSoundGetPolyphony(s, argc - 1, argv + 1);
109 	default:
110 		break;
111 	}
112 
113 	error("Unknown kDoSoundMac32 subop %d", argv[0].toUint16());
114 	return s->r_acc;
115 }
116 #endif
117 
kDoCdAudio(EngineState * s,int argc,reg_t * argv)118 reg_t kDoCdAudio(EngineState *s, int argc, reg_t *argv) {
119 	switch (argv[0].toUint16()) {
120 	case kSciAudioPlay: {
121 		if (argc < 2)
122 			return NULL_REG;
123 
124 		uint16 track = argv[1].toUint16();
125 		uint32 startFrame = (argc > 2) ? argv[2].toUint16() * 75 : 0;
126 		uint32 totalFrames = (argc > 3) ? argv[3].toUint16() * 75 : 0;
127 
128 		return make_reg(0, g_sci->_audio->audioCdPlay(track, startFrame, totalFrames));
129 	}
130 	case kSciAudioStop:
131 		g_sci->_audio->audioCdStop();
132 
133 		if (getSciVersion() == SCI_VERSION_1_1)
134 			return make_reg(0, 1);
135 
136 		break;
137 	case kSciAudioPause:
138 		warning("Can't pause CD Audio");
139 		break;
140 	case kSciAudioResume:
141 		// This seems to be hacked up to update the CD instead of resuming
142 		// audio like kDoAudio does.
143 		g_sci->_audio->audioCdUpdate();
144 		break;
145 	case kSciAudioPosition:
146 		return make_reg(0, g_sci->_audio->audioCdPosition());
147 	case kSciAudioWPlay: // CD Audio can't be preloaded
148 	case kSciAudioRate: // No need to set the audio rate
149 	case kSciAudioVolume: // The speech setting isn't used by CD Audio
150 	case kSciAudioLanguage: // No need to set the language
151 		break;
152 	case kSciAudioCD:
153 		// Init
154 		return make_reg(0, 1);
155 	default:
156 		error("kCdDoAudio: Unhandled case %d", argv[0].toUint16());
157 	}
158 
159 	return s->r_acc;
160 }
161 
162 /**
163  * Used for speech playback and digital soundtracks in CD games.
164  * This is the SCI16 version; SCI32 is handled separately.
165  */
kDoAudio(EngineState * s,int argc,reg_t * argv)166 reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) {
167 	// JonesCD and Mothergoose256 CD use different functions
168 	// based on the cdaudio.map file to use red book tracks.
169 	if (g_sci->_features->usesCdTrack()) {
170 		if (g_sci->getGameId() == GID_MOTHERGOOSE256) {
171 			// The CD audio version of Mothergoose256 CD is unique with a
172 			// custom interpreter for its audio. English is only in the CD
173 			// audio track while the other four languages are only in audio
174 			// resource files. This is transparent to the scripts which are
175 			// the same in all versions. The interpreter detected when
176 			// English was selected and used CD audio in that case.
177 			if (g_sci->getSciLanguage() == K_LANG_ENGLISH &&
178 				argv[0].toUint16() != kSciAudioLanguage) {
179 				return kDoCdAudio(s, argc, argv);
180 			}
181 		} else {
182 			return kDoCdAudio(s, argc, argv);
183 		}
184 	}
185 
186 	Audio::Mixer *mixer = g_system->getMixer();
187 
188 	switch (argv[0].toUint16()) {
189 	case kSciAudioWPlay:
190 	case kSciAudioPlay: {
191 		uint16 module;
192 		uint32 number;
193 
194 		g_sci->_audio->stopAudio();
195 		// In SSCI, kDoAudio handles all samples, even if started by kDoSound.
196 		// We manage samples started by kDoSound in SoundCommandParser.
197 		g_sci->_soundCmd->stopAllSamples();
198 
199 		if (argc == 2) {
200 			module = 65535;
201 			number = argv[1].toUint16();
202 		} else if (argc == 6 || argc == 8) {
203 			module = argv[1].toUint16();
204 			number = ((argv[2].toUint16() & 0xff) << 24) |
205 			         ((argv[3].toUint16() & 0xff) << 16) |
206 			         ((argv[4].toUint16() & 0xff) <<  8) |
207 			          (argv[5].toUint16() & 0xff);
208 		} else {
209 			warning("kDoAudio: Play called with an unknown number of parameters (%d)", argc);
210 			return NULL_REG;
211 		}
212 
213 		if (argv[0].toUint16() == kSciAudioPlay) {
214 			g_sci->_audio->incrementPlayCounter();
215 		}
216 
217 		debugC(kDebugLevelSound, "kDoAudio: play sample %d, module %d", number, module);
218 
219 		// return sample length in ticks
220 		if (argv[0].toUint16() == kSciAudioWPlay)
221 			return make_reg(0, g_sci->_audio->wPlayAudio(module, number));
222 		else
223 			return make_reg(0, g_sci->_audio->startAudio(module, number));
224 	}
225 	case kSciAudioStop:
226 		debugC(kDebugLevelSound, "kDoAudio: stop");
227 		g_sci->_audio->stopAudio();
228 		// In SSCI, kDoAudio handles all samples, even if started by kDoSound.
229 		// We manage samples started by kDoSound in SoundCommandParser.
230 		g_sci->_soundCmd->stopAllSamples();
231 		break;
232 	case kSciAudioPause:
233 		debugC(kDebugLevelSound, "kDoAudio: pause");
234 		g_sci->_audio->pauseAudio();
235 		break;
236 	case kSciAudioResume:
237 		debugC(kDebugLevelSound, "kDoAudio: resume");
238 		g_sci->_audio->resumeAudio();
239 		break;
240 	case kSciAudioPosition:
241 		//debugC(kDebugLevelSound, "kDoAudio: get position");	// too verbose
242 		return make_reg(0, g_sci->_audio->getAudioPosition());
243 	case kSciAudioRate:
244 		debugC(kDebugLevelSound, "kDoAudio: set audio rate to %d", argv[1].toUint16());
245 		g_sci->_audio->setAudioRate(argv[1].toUint16());
246 		break;
247 	case kSciAudioVolume: {
248 		int16 volume = argv[1].toUint16();
249 		volume = CLIP<int16>(volume, 0, AUDIO_VOLUME_MAX);
250 		debugC(kDebugLevelSound, "kDoAudio: set volume to %d", volume);
251 		mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, volume * 2);
252 		break;
253 	}
254 	case kSciAudioLanguage:
255 		// In SCI1.1: tests for digital audio support
256 		if (getSciVersion() == SCI_VERSION_1_1) {
257 			debugC(kDebugLevelSound, "kDoAudio: audio capability test");
258 			return make_reg(0, 1);
259 		} else {
260 			int16 language = argv[1].toSint16();
261 
262 			// athrxx: It seems from disasm that the original KQ5 FM-Towns loads a default language (Japanese) audio map at the beginning
263 			// right after loading the video and audio drivers. The -1 language argument in here simply means that the original will stick
264 			// with Japanese. Instead of doing that we switch to the language selected in the launcher.
265 			if (g_sci->getPlatform() == Common::kPlatformFMTowns && language == -1) {
266 				// FM-Towns calls us to get the current language / also set the default language
267 				// This doesn't just happen right at the start, but also when the user clicks on the Sierra logo in the game menu
268 				// It uses the result of this call to either show "English Voices" or "Japanese Voices".
269 
270 				// Language should have been set by setLauncherLanguage() already (or could have been modified by the scripts).
271 				// Get this language setting, so that the chosen language will get set for resource manager.
272 				language = g_sci->getSciLanguage();
273 			}
274 
275 			debugC(kDebugLevelSound, "kDoAudio: set language to %d", language);
276 
277 			if (language != -1)
278 				g_sci->getResMan()->setAudioLanguage(language);
279 
280 			kLanguage kLang = g_sci->getSciLanguage();
281 			if (g_sci->_features->usesCdTrack() && language == K_LANG_ENGLISH) {
282 				// Mothergoose 256 CD has a multi-lingual version with English only on CD audio,
283 				// so setAudioLanguage() will fail because there are no English resource files.
284 				// The scripts cycle through languages to test which are available for the main
285 				// menu, so setting English must succeed. This was handled by a custom interpreter.
286 				kLang = K_LANG_ENGLISH;
287 			}
288 			g_sci->setSciLanguage(kLang);
289 
290 			return make_reg(0, kLang);
291 		}
292 		break;
293 	case kSciAudioCD:
294 		debugC(kDebugLevelSound, "kDoAudio: CD audio subop");
295 		return kDoCdAudio(s, argc - 1, argv + 1);
296 
297 	// 3 new subops in Pharkas CD (including CD demo). kDoAudio in Pharkas sits at seg026:038C
298 	case 11:
299 		// Not sure where this is used yet
300 		warning("kDoAudio: Unhandled case 11, %d extra arguments passed", argc - 1);
301 		break;
302 	case 12:
303 		// SSCI calls this function with no parameters from
304 		// the TalkRandCycle class and branches on the return
305 		// value like a boolean. The conjectured purpose of
306 		// this function is to ensure that the talker's mouth
307 		// does not move if there is read jitter (slow CD
308 		// drive, scratched CD). The old behavior here of not
309 		// doing anything caused a nonzero value to be left in
310 		// the accumulator by chance. This is equivalent, but
311 		// more explicit.
312 
313 		return make_reg(0, 1);
314 	case 13:
315 		// SSCI returns a counter that increments each time a sample
316 		//  is played. This is used by the PointsSound and Narrator
317 		//  classes to detect if their sound was interrupted by
318 		//  another sample playing, since only one can play at a time.
319 		//  The counter is incremented on each kDoAudio(Play) and on
320 		//  each kDoSound(Play) when the sound is a sample, since SSCI
321 		//  just passes those on to kDoAudio.
322 		//
323 		// When awarding points, the icon bar is often disabled, and
324 		//  PointsSound:check polls its signal to detect when the
325 		//  sound is completed so that it can re-enable the icon bar.
326 		//  If the sound is interrupted by another sample then the icon
327 		//  bar could remain permanently disabled, so the play counter
328 		//  is stored before playing and PointsSound:check polls for
329 		//  a change. The Narrator class also does this to detect
330 		//  if speech was interrupted so that the game can continue.
331 		return make_reg(0, g_sci->_audio->getPlayCounter());
332 	case 17:
333 		// Seems to be some sort of audio sync, used in SQ6. Silenced the
334 		// warning due to the high level of spam it produces. (takes no params)
335 		//warning("kDoAudio: Unhandled case 17, %d extra arguments passed", argc - 1);
336 		break;
337 	default:
338 		warning("kDoAudio: Unhandled case %d, %d extra arguments passed", argv[0].toUint16(), argc - 1);
339 	}
340 
341 	return s->r_acc;
342 }
343 
kDoSync(EngineState * s,int argc,reg_t * argv)344 reg_t kDoSync(EngineState *s, int argc, reg_t *argv) {
345 	switch (argv[0].toUint16()) {
346 	case kSciAudioSyncStart: {
347 		ResourceId id;
348 
349 		g_sci->_sync->stop();
350 
351 		if (argc == 3) {
352 			id = ResourceId(kResourceTypeSync, argv[2].toUint16());
353 		} else if (argc == 7) {
354 			id = ResourceId(kResourceTypeSync36, argv[2].toUint16(), argv[3].toUint16(), argv[4].toUint16(),
355 							argv[5].toUint16(), argv[6].toUint16());
356 		} else {
357 			warning("kDoSync: Start called with an unknown number of parameters (%d)", argc);
358 			return s->r_acc;
359 		}
360 
361 		g_sci->_sync->start(id, argv[1]);
362 		break;
363 	}
364 	case kSciAudioSyncNext:
365 		g_sci->_sync->next(argv[1]);
366 		break;
367 	case kSciAudioSyncStop:
368 		g_sci->_sync->stop();
369 		break;
370 	default:
371 		error("DoSync: Unhandled subfunction %d", argv[0].toUint16());
372 	}
373 
374 	return s->r_acc;
375 }
376 
377 #ifdef ENABLE_SCI32
kDoAudio32(EngineState * s,int argc,reg_t * argv)378 reg_t kDoAudio32(EngineState *s, int argc, reg_t *argv) {
379 	if (!s)
380 		return make_reg(0, getSciVersion());
381 	error("not supposed to call this");
382 }
383 
kDoAudioInit(EngineState * s,int argc,reg_t * argv)384 reg_t kDoAudioInit(EngineState *s, int argc, reg_t *argv) {
385 	return make_reg(0, 0);
386 }
387 
kDoAudioWaitForPlay(EngineState * s,int argc,reg_t * argv)388 reg_t kDoAudioWaitForPlay(EngineState *s, int argc, reg_t *argv) {
389 	if (argc == 0) {
390 		if (g_sci->_features->hasSci3Audio()) {
391 			return make_reg(0, g_sci->_audio32->getNumUnlockedChannels());
392 		} else {
393 			return make_reg(0, g_sci->_audio32->getNumActiveChannels());
394 		}
395 	}
396 
397 	return g_sci->_audio32->kernelPlay(false, s, argc, argv);
398 }
399 
kDoAudioPlay(EngineState * s,int argc,reg_t * argv)400 reg_t kDoAudioPlay(EngineState *s, int argc, reg_t *argv) {
401 	if (argc == 0) {
402 		return make_reg(0, g_sci->_audio32->getNumActiveChannels());
403 	}
404 
405 	return g_sci->_audio32->kernelPlay(true, s, argc, argv);
406 }
407 
kDoAudioStop(EngineState * s,int argc,reg_t * argv)408 reg_t kDoAudioStop(EngineState *s, int argc, reg_t *argv) {
409 	return g_sci->_audio32->kernelStop(s, argc, argv);
410 }
411 
kDoAudioPause(EngineState * s,int argc,reg_t * argv)412 reg_t kDoAudioPause(EngineState *s, int argc, reg_t *argv) {
413 	return g_sci->_audio32->kernelPause(s, argc, argv);
414 }
415 
kDoAudioResume(EngineState * s,int argc,reg_t * argv)416 reg_t kDoAudioResume(EngineState *s, int argc, reg_t *argv) {
417 	return g_sci->_audio32->kernelResume(s, argc, argv);
418 }
419 
kDoAudioPosition(EngineState * s,int argc,reg_t * argv)420 reg_t kDoAudioPosition(EngineState *s, int argc, reg_t *argv) {
421 	return g_sci->_audio32->kernelPosition(s, argc, argv);
422 }
423 
kDoAudioRate(EngineState * s,int argc,reg_t * argv)424 reg_t kDoAudioRate(EngineState *s, int argc, reg_t *argv) {
425 	if (argc > 0) {
426 		const uint16 sampleRate = argv[0].toUint16();
427 		if (sampleRate != 0) {
428 			g_sci->_audio32->setSampleRate(sampleRate);
429 		}
430 	}
431 
432 	return make_reg(0, g_sci->_audio32->getSampleRate());
433 }
434 
kDoAudioVolume(EngineState * s,int argc,reg_t * argv)435 reg_t kDoAudioVolume(EngineState *s, int argc, reg_t *argv) {
436 	return g_sci->_audio32->kernelVolume(s, argc, argv);
437 }
438 
kDoAudioGetCapability(EngineState * s,int argc,reg_t * argv)439 reg_t kDoAudioGetCapability(EngineState *s, int argc, reg_t *argv) {
440 	return make_reg(0, 1);
441 }
442 
kDoAudioBitDepth(EngineState * s,int argc,reg_t * argv)443 reg_t kDoAudioBitDepth(EngineState *s, int argc, reg_t *argv) {
444 	if (argc > 0) {
445 		const uint16 bitDepth = argv[0].toUint16();
446 		if (bitDepth != 0) {
447 			g_sci->_audio32->setBitDepth(bitDepth);
448 		}
449 	}
450 
451 	return make_reg(0, g_sci->_audio32->getBitDepth());
452 }
453 
kDoAudioMixing(EngineState * s,int argc,reg_t * argv)454 reg_t kDoAudioMixing(EngineState *s, int argc, reg_t *argv) {
455 	return g_sci->_audio32->kernelMixing(argc, argv);
456 }
457 
kDoAudioChannels(EngineState * s,int argc,reg_t * argv)458 reg_t kDoAudioChannels(EngineState *s, int argc, reg_t *argv) {
459 	if (argc > 0) {
460 		const int16 numChannels = argv[0].toSint16();
461 		if (numChannels != 0) {
462 			g_sci->_audio32->setNumOutputChannels(numChannels);
463 		}
464 	}
465 
466 	return make_reg(0, g_sci->_audio32->getNumOutputChannels());
467 }
468 
kDoAudioPreload(EngineState * s,int argc,reg_t * argv)469 reg_t kDoAudioPreload(EngineState *s, int argc, reg_t *argv) {
470 	if (argc > 0) {
471 		g_sci->_audio32->setPreload(argv[0].toUint16());
472 	}
473 
474 	return make_reg(0, g_sci->_audio32->getPreload());
475 }
476 
kDoAudioFade(EngineState * s,int argc,reg_t * argv)477 reg_t kDoAudioFade(EngineState *s, int argc, reg_t *argv) {
478 	return g_sci->_audio32->kernelFade(s, argc, argv);
479 }
480 
kDoAudioHasSignal(EngineState * s,int argc,reg_t * argv)481 reg_t kDoAudioHasSignal(EngineState *s, int argc, reg_t *argv) {
482 	return make_reg(0, g_sci->_audio32->hasSignal());
483 }
484 
kDoAudioSetLoop(EngineState * s,int argc,reg_t * argv)485 reg_t kDoAudioSetLoop(EngineState *s, int argc, reg_t *argv) {
486 	g_sci->_audio32->kernelLoop(s, argc, argv);
487 	return s->r_acc;
488 }
489 
kDoAudioPan(EngineState * s,int argc,reg_t * argv)490 reg_t kDoAudioPan(EngineState *s, int argc, reg_t *argv) {
491 	g_sci->_audio32->kernelPan(s, argc, argv);
492 	return s->r_acc;
493 }
494 
kDoAudioPanOff(EngineState * s,int argc,reg_t * argv)495 reg_t kDoAudioPanOff(EngineState *s, int argc, reg_t *argv) {
496 	g_sci->_audio32->kernelPanOff(s, argc, argv);
497 	return s->r_acc;
498 }
499 
kSetLanguage(EngineState * s,int argc,reg_t * argv)500 reg_t kSetLanguage(EngineState *s, int argc, reg_t *argv) {
501 	// Used by script 90 of MUMG Deluxe from the main menu to toggle between
502 	// English and Spanish in some versions and English and Spanish and
503 	// French and German in others.
504 	const Common::String audioDirectory = s->_segMan->getString(argv[0]);
505 	if (g_sci->getPlatform() == Common::kPlatformMacintosh) {
506 		g_sci->getResMan()->changeMacAudioDirectory(audioDirectory);
507 	} else {
508 		g_sci->getResMan()->changeAudioDirectory(audioDirectory);
509 	}
510 	return s->r_acc;
511 }
512 
513 #endif
514 
515 } // End of namespace Sci
516