~ubuntu-branches/debian/jessie/scummvm/jessie

« back to all changes in this revision

Viewing changes to engines/hugo/sound.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Moritz Muehlenhoff
  • Date: 2011-05-25 19:02:23 UTC
  • mto: This revision was merged to the branch mainline in revision 23.
  • Revision ID: james.westby@ubuntu.com-20110525190223-fiqm0oaec714xk31
Tags: upstream-1.3.0
ImportĀ upstreamĀ versionĀ 1.3.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
 * along with this program; if not, write to the Free Software
19
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
20
 *
21
 
 * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-1-2-1/engines/hugo/sound.cpp $
22
 
 * $Id: sound.cpp 52406 2010-08-27 09:48:53Z strangerke $
 
21
 * $URL$
 
22
 * $Id$
23
23
 *
24
24
 */
25
25
 
30
30
 *
31
31
 */
32
32
 
33
 
/* sound.c - sound effects and music support */
 
33
// sound.c - sound effects and music support
34
34
 
 
35
#include "common/debug.h"
35
36
#include "common/system.h"
 
37
#include "common/textconsole.h"
 
38
#include "common/config-manager.h"
36
39
 
37
 
#include "sound/decoders/raw.h"
38
 
#include "sound/audiostream.h"
39
 
#include "sound/midiparser.h"
40
 
#include "sound/mididrv.h"
 
40
#include "audio/decoders/raw.h"
 
41
#include "audio/audiostream.h"
 
42
#include "audio/midiparser.h"
41
43
 
42
44
#include "hugo/hugo.h"
43
45
#include "hugo/game.h"
44
46
#include "hugo/file.h"
45
47
#include "hugo/sound.h"
 
48
#include "hugo/text.h"
46
49
 
47
50
namespace Hugo {
48
51
 
49
 
class MidiPlayer : public MidiDriver {
50
 
public:
51
 
 
52
 
        enum {
53
 
                NUM_CHANNELS = 16
54
 
        };
55
 
 
56
 
        MidiPlayer(MidiDriver *driver);
57
 
        ~MidiPlayer();
58
 
 
59
 
        void play(uint8 *stream, uint16 size);
60
 
        void stop();
61
 
        void pause(bool p);
62
 
        void updateTimer();
63
 
        void adjustVolume(int diff);
64
 
        void setVolume(int volume);
65
 
        int getVolume() const { return _masterVolume; }
66
 
        void setLooping(bool loop) { _isLooping = loop; }
67
 
 
68
 
        // MidiDriver interface
69
 
        int open();
70
 
        void close();
71
 
        void send(uint32 b);
72
 
        void metaEvent(byte type, byte *data, uint16 length);
73
 
        void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { }
74
 
        uint32 getBaseTempo() { return _driver ? _driver->getBaseTempo() : 0; }
75
 
        MidiChannel *allocateChannel() { return 0; }
76
 
        MidiChannel *getPercussionChannel() { return 0; }
77
 
 
78
 
private:
79
 
 
80
 
        static void timerCallback(void *p);
81
 
 
82
 
        MidiDriver *_driver;
83
 
        MidiParser *_parser;
84
 
        uint8 *_midiData;
85
 
        bool _isLooping;
86
 
        bool _isPlaying;
87
 
        bool _paused;
88
 
        int _masterVolume;
89
 
        MidiChannel *_channelsTable[NUM_CHANNELS];
90
 
        uint8 _channelsVolume[NUM_CHANNELS];
91
 
        Common::Mutex _mutex;
92
 
};
93
 
 
94
 
MidiPlayer::MidiPlayer(MidiDriver *driver)
95
 
        : _driver(driver), _parser(0), _midiData(0), _isLooping(false), _isPlaying(false), _paused(false), _masterVolume(0) {
 
52
MidiPlayer::MidiPlayer() {
 
53
        MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
 
54
        _driver = MidiDriver::createMidi(dev);
96
55
        assert(_driver);
97
 
        memset(_channelsTable, 0, sizeof(_channelsTable));
98
 
        for (int i = 0; i < NUM_CHANNELS; i++) {
99
 
                _channelsVolume[i] = 127;
 
56
        _paused = false;
 
57
 
 
58
 
 
59
        int ret = _driver->open();
 
60
        if (ret == 0) {
 
61
                _driver->sendGMReset();
 
62
 
 
63
                _driver->setTimerCallback(this, &timerCallback);
100
64
        }
101
65
}
102
66
 
103
 
MidiPlayer::~MidiPlayer() {
104
 
        close();
105
 
}
106
 
 
107
67
void MidiPlayer::play(uint8 *stream, uint16 size) {
108
 
        if (!stream) {
109
 
                stop();
 
68
        debugC(3, kDebugMusic, "MidiPlayer::play");
 
69
 
 
70
        Common::StackLock lock(_mutex);
 
71
 
 
72
        stop();
 
73
        if (!stream)
110
74
                return;
111
 
        }
112
75
 
113
76
        _midiData = (uint8 *)malloc(size);
114
77
        if (_midiData) {
115
78
                memcpy(_midiData, stream, size);
116
 
                _mutex.lock();
 
79
 
 
80
                syncVolume();   // FIXME: syncVolume calls setVolume which in turn also locks the mutex! ugh
 
81
 
 
82
                _parser = MidiParser::createParser_SMF();
117
83
                _parser->loadMusic(_midiData, size);
118
84
                _parser->setTrack(0);
119
 
                _isLooping = true;
 
85
                _parser->setMidiDriver(this);
 
86
                _parser->setTimerRate(_driver->getBaseTempo());
 
87
                _isLooping = false;
120
88
                _isPlaying = true;
121
 
                _mutex.unlock();
122
 
        }
123
 
}
124
 
 
125
 
void MidiPlayer::stop() {
126
 
        _mutex.lock();
127
 
        if (_isPlaying) {
128
 
                _isPlaying = false;
129
 
                _parser->unloadMusic();
130
 
                free(_midiData);
131
 
                _midiData = 0;
132
 
        }
133
 
        _mutex.unlock();
 
89
        }
134
90
}
135
91
 
136
92
void MidiPlayer::pause(bool p) {
137
93
        _paused = p;
138
94
 
139
 
        for (int i = 0; i < NUM_CHANNELS; ++i) {
 
95
        for (int i = 0; i < kNumChannels; ++i) {
140
96
                if (_channelsTable[i]) {
141
97
                        _channelsTable[i]->volume(_paused ? 0 : _channelsVolume[i] * _masterVolume / 255);
142
98
                }
143
99
        }
144
100
}
145
101
 
146
 
void MidiPlayer::updateTimer() {
147
 
        if (_paused) {
148
 
                return;
149
 
        }
 
102
void MidiPlayer::onTimer() {
 
103
        Common::StackLock lock(_mutex);
150
104
 
151
 
        _mutex.lock();
152
 
        if (_isPlaying) {
 
105
        if (!_paused && _isPlaying && _parser) {
153
106
                _parser->onTimer();
154
107
        }
155
 
        _mutex.unlock();
156
 
}
157
 
 
158
 
void MidiPlayer::adjustVolume(int diff) {
159
 
        setVolume(_masterVolume + diff);
160
 
}
161
 
 
162
 
void MidiPlayer::setVolume(int volume) {
163
 
        _masterVolume = CLIP(volume, 0, 255);
164
 
        _mutex.lock();
165
 
        for (int i = 0; i < NUM_CHANNELS; ++i) {
166
 
                if (_channelsTable[i]) {
167
 
                        _channelsTable[i]->volume(_channelsVolume[i] * _masterVolume / 255);
168
 
                }
169
 
        }
170
 
        _mutex.unlock();
171
 
}
172
 
 
173
 
int MidiPlayer::open() {
174
 
        _driver->open();
175
 
 
176
 
        _parser = MidiParser::createParser_SMF();
177
 
        _parser->setMidiDriver(this);
178
 
        _parser->setTimerRate(_driver->getBaseTempo());
179
 
        _driver->setTimerCallback(this, &timerCallback);
180
 
 
181
 
        return 0;
182
 
}
183
 
 
184
 
void MidiPlayer::close() {
185
 
        stop();
186
 
        _mutex.lock();
187
 
        _driver->setTimerCallback(NULL, NULL);
188
 
        _driver->close();
189
 
        delete _driver;
190
 
        _driver = 0;
191
 
        _parser->setMidiDriver(NULL);
192
 
        delete _parser;
193
 
        _mutex.unlock();
194
 
}
195
 
 
196
 
void MidiPlayer::send(uint32 b) {
197
 
        byte volume, ch = (byte)(b & 0xF);
198
 
        switch (b & 0xFFF0) {
199
 
        case 0x07B0: // volume change
200
 
                volume = (byte)((b >> 16) & 0x7F);
201
 
                _channelsVolume[ch] = volume;
202
 
                volume = volume * _masterVolume / 255;
203
 
                b = (b & 0xFF00FFFF) | (volume << 16);
204
 
                break;
205
 
        case 0x7BB0: // all notes off
206
 
                if (!_channelsTable[ch]) {
207
 
                        // channel not yet allocated, no need to send the event
208
 
                        return;
209
 
                }
210
 
                break;
211
 
        }
212
 
 
213
 
        if (!_channelsTable[ch]) {
214
 
                _channelsTable[ch] = (ch == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
215
 
        }
216
 
        if (_channelsTable[ch]) {
217
 
                _channelsTable[ch]->send(b);
218
 
        }
219
 
}
220
 
 
221
 
void MidiPlayer::metaEvent(byte type, byte *data, uint16 length) {
222
 
        switch (type) {
223
 
        case 0x2F: // end of Track
224
 
                if (_isLooping) {
225
 
                        _parser->jumpToTick(0);
226
 
                } else {
227
 
                        stop();
228
 
                }
229
 
                break;
230
 
        default:
231
 
//              warning("Unhandled meta event: %02x", type);
232
 
                break;
233
 
        }
234
 
}
235
 
 
236
 
void MidiPlayer::timerCallback(void *p) {
237
 
        MidiPlayer *player = (MidiPlayer *)p;
238
 
 
239
 
        player->updateTimer();
240
 
}
241
 
 
242
 
SoundHandler::SoundHandler(HugoEngine &vm) : _vm(vm) {
243
 
        MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
244
 
        MidiDriver *driver = MidiDriver::createMidi(dev);
245
 
 
246
 
        _midiPlayer = new MidiPlayer(driver);
247
 
}
248
 
 
 
108
}
 
109
 
 
110
void MidiPlayer::sendToChannel(byte channel, uint32 b) {
 
111
        if (!_channelsTable[channel]) {
 
112
                _channelsTable[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
 
113
                // If a new channel is allocated during the playback, make sure
 
114
                // its volume is correctly initialized.
 
115
                if (_channelsTable[channel])
 
116
                        _channelsTable[channel]->volume(_channelsVolume[channel] * _masterVolume / 255);
 
117
        }
 
118
 
 
119
        if (_channelsTable[channel])
 
120
                _channelsTable[channel]->send(b);
 
121
}
 
122
 
 
123
 
 
124
SoundHandler::SoundHandler(HugoEngine *vm) : _vm(vm) {
 
125
        _midiPlayer = new MidiPlayer();
 
126
        _speakerStream = new Audio::PCSpeaker(_vm->_mixer->getOutputRate());
 
127
        _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_speakerHandle,
 
128
                                                _speakerStream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
 
129
        DOSSongPtr = 0;
 
130
        curPriority = 0;
 
131
        pcspkrTimer = 0;
 
132
        pcspkrOctave = 3;
 
133
        pcspkrNoteDuration = 2; 
 
134
}
 
135
 
 
136
SoundHandler::~SoundHandler() {
 
137
        _vm->getTimerManager()->removeTimerProc(&loopPlayer);
 
138
        _vm->_mixer->stopHandle(_speakerHandle);
 
139
        delete _speakerStream;
 
140
        delete _midiPlayer;
 
141
}
 
142
 
 
143
/**
 
144
 * Set the FM music volume from config.mvolume (0..100%)
 
145
 */
249
146
void SoundHandler::setMusicVolume() {
250
 
        /* Set the FM music volume from config.mvolume (0..100%) */
251
 
        
252
 
        _midiPlayer->setVolume(_config.musicVolume * 255 / 100);
 
147
        _midiPlayer->syncVolume();
253
148
}
254
149
 
 
150
/**
 
151
 * Stop any sound that might be playing
 
152
 */
255
153
void SoundHandler::stopSound() {
256
 
        /* Stop any sound that might be playing */
257
 
        _vm._mixer->stopAll();
 
154
        _vm->_mixer->stopAll();
258
155
}
259
156
 
 
157
/**
 
158
 * Stop any tune that might be playing
 
159
 */
260
160
void SoundHandler::stopMusic() {
261
 
        /* Stop any tune that might be playing */
262
161
        _midiPlayer->stop();
263
162
}
264
163
 
 
164
/**
 
165
 * Turn music on and off
 
166
 */
265
167
void SoundHandler::toggleMusic() {
266
 
// Turn music on and off
267
 
        _config.musicFl = !_config.musicFl;
268
 
        
269
 
        _midiPlayer->pause(_config.musicFl);
 
168
        _vm->_config.musicFl = !_vm->_config.musicFl;
 
169
 
 
170
        _midiPlayer->pause(!_vm->_config.musicFl);
270
171
}
271
172
 
 
173
/**
 
174
 * Turn digitized sound on and off
 
175
 */
272
176
void SoundHandler::toggleSound() {
273
 
// Turn digitized sound on and off
274
 
        _config.soundFl = !_config.soundFl;
 
177
        _vm->_config.soundFl = !_vm->_config.soundFl;
275
178
}
276
179
 
277
180
void SoundHandler::playMIDI(sound_pt seq_p, uint16 size) {
278
181
        _midiPlayer->play(seq_p, size);
279
182
}
280
183
 
281
 
 
 
184
/**
 
185
 * Read a tune sequence from the sound database and start playing it
 
186
 */
282
187
void SoundHandler::playMusic(int16 tune) {
283
 
        /* Read a tune sequence from the sound database and start playing it */
284
188
        sound_pt seqPtr;                                // Sequence data from file
285
189
        uint16 size;                                    // Size of sequence data
286
190
 
287
 
        if (_config.musicFl) {
288
 
                _vm.getGameStatus().song = tune;
289
 
                seqPtr = _vm.file().getSound(tune, &size);
 
191
        if (_vm->_config.musicFl) {
 
192
                _vm->getGameStatus().song = tune;
 
193
                seqPtr = _vm->_file->getSound(tune, &size);
290
194
                playMIDI(seqPtr, size);
 
195
                free(seqPtr);
291
196
        }
292
197
}
293
198
 
294
 
 
295
 
void SoundHandler::playSound(int16 sound, stereo_t channel, byte priority) {
296
 
        /* Produce various sound effects on supplied stereo channel(s) */
297
 
        /* Override currently playing sound only if lower or same priority */
298
 
 
 
199
/**
 
200
 * Produce various sound effects on supplied stereo channel(s)
 
201
 * Override currently playing sound only if lower or same priority
 
202
 */
 
203
void SoundHandler::playSound(int16 sound, const byte priority) {
299
204
        // uint32 dwVolume;                             // Left, right volume of sound
300
205
        sound_pt sound_p;                               // Sound data
301
206
        uint16 size;                                    // Size of data
302
 
        static byte curPriority = 0;                    // Priority of currently playing sound
303
 
        //
304
 
        /* Sound disabled */
305
 
        if (!_config.soundFl || !_vm._mixer->isReady())
 
207
 
 
208
        // Sound disabled
 
209
        if (!_vm->_config.soundFl || !_vm->_mixer->isReady())
306
210
                return;
307
 
        //
308
 
        // // See if last wave still playing - if so, check priority
309
 
        // if (waveOutUnprepareHeader(hwav, lphdr, sizeof(WAVEHDR)) == WAVERR_STILLPLAYING)
310
 
        //  if (priority < curPriority)                 // Don't override unless priority >= current
311
 
        //      return;
312
 
        //  else
313
 
        //      Stop_sound();
 
211
 
 
212
        syncVolume();
314
213
        curPriority = priority;
315
 
        //
316
 
        /* Get sound data */
317
 
        if ((sound_p = _vm.file().getSound(sound, &size)) == NULL)
 
214
 
 
215
        // Get sound data
 
216
        if ((sound_p = _vm->_file->getSound(sound, &size)) == 0)
318
217
                return;
319
218
 
320
219
        Audio::AudioStream *stream = Audio::makeRawStream(sound_p, size, 11025, Audio::FLAG_UNSIGNED);
321
 
        _vm._mixer->playStream(Audio::Mixer::kSpeechSoundType, &_soundHandle, stream);
322
 
 
 
220
        _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, stream);
323
221
}
324
222
 
 
223
/**
 
224
 * Initialize for MCI sound and midi
 
225
 */
325
226
void SoundHandler::initSound() {
326
 
        /* Initialize for MCI sound and midi */
327
 
 
328
 
        _midiPlayer->open();
 
227
        //_midiPlayer->open();
 
228
}
 
229
 
 
230
void SoundHandler::syncVolume() {
 
231
        int soundVolume;
 
232
 
 
233
        if (ConfMan.getBool("sfx_mute") || ConfMan.getBool("mute"))
 
234
                soundVolume = -1;
 
235
        else
 
236
                soundVolume = MIN(255, ConfMan.getInt("sfx_volume"));
 
237
 
 
238
        _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, soundVolume);
 
239
        _midiPlayer->syncVolume();
 
240
}
 
241
 
 
242
/**
 
243
 * Check if music is still playing.
 
244
 * If not, select the next track in the playlist and play it
 
245
 */
 
246
void SoundHandler::checkMusic() {
 
247
        if (_midiPlayer->isPlaying())
 
248
                return;
 
249
 
 
250
        for (int i = 0; _vm->_defltTunes[i] != -1; i++) {
 
251
                if (_vm->_defltTunes[i] == _vm->getGameStatus().song) {
 
252
                        if (_vm->_defltTunes[i + 1] != -1)
 
253
                                playMusic(_vm->_defltTunes[i + 1]);
 
254
                        else
 
255
                                playMusic(_vm->_defltTunes[0]);
 
256
                        break;
 
257
                }
 
258
        }
 
259
}
 
260
 
 
261
void SoundHandler::loopPlayer(void *refCon) {
 
262
        ((SoundHandler*)refCon)->pcspkr_player();
 
263
}
 
264
 
 
265
/**
 
266
 * Decrement last note's timer and see if time to play next note yet.
 
267
 * If so, interpret next note in string and play it.  Update ptr to string
 
268
 * Timer: >0 - song still going, 0 - Stop note, -1 - Set next note
 
269
 */
 
270
void SoundHandler::pcspkr_player() {
 
271
        static const uint16 pcspkrNotes[8] =  {1352, 1205, 2274, 2026, 1805, 1704, 1518}; // The 3rd octave note counts A..G
 
272
        static const uint16 pcspkrSharps[8] = {1279, 1171, 2150, 1916, 1755, 1611, 1435}; // The sharps, A# to B#
 
273
        static const uint16 pcspkrFlats[8] =  {1435, 1279, 2342, 2150, 1916, 1755, 1611}; // The flats, Ab to Bb
 
274
 
 
275
        _vm->getTimerManager()->removeTimerProc(&loopPlayer);
 
276
        _vm->getTimerManager()->installTimerProc(&loopPlayer, 1000000 / 9, this);
 
277
 
 
278
        uint16 count;                                   // Value to set timer chip to for note
 
279
        bool   cmd_note;
 
280
 
 
281
        if (!_vm->_config.soundFl || !_vm->_mixer->isReady())
 
282
                return;                                     // Poo!  User doesn't want sound!
 
283
 
 
284
        if (!DOSSongPtr)
 
285
                return;
 
286
 
 
287
        if (!*DOSSongPtr)                               // Song has finished
 
288
                return;
 
289
 
 
290
        if (!--pcspkrTimer) {                           // timer zero, stop note
 
291
                _speakerStream->stop();
 
292
                return;
 
293
        } else if (pcspkrTimer >= 0) {                  // Note still going
 
294
                return;
 
295
        }
 
296
        
 
297
        // Time to play next note
 
298
        do {
 
299
                cmd_note = true;
 
300
                switch (*DOSSongPtr) {
 
301
                case 'O':                                   // Switch to new octave 1..7
 
302
                        DOSSongPtr++;
 
303
                        pcspkrOctave = *DOSSongPtr - '0';
 
304
                        if ((pcspkrOctave < 0) || (pcspkrOctave > 7))
 
305
                                error("pcspkr_player() - Bad octave");
 
306
                        DOSSongPtr++;
 
307
                        break;
 
308
                case 'L':                                   // Switch to new duration (in ticks)
 
309
                        DOSSongPtr++;
 
310
                        pcspkrNoteDuration = *DOSSongPtr - '0';
 
311
                        if (pcspkrNoteDuration < 0)
 
312
                                error("pcspkr_player() - Bad duration");
 
313
                        pcspkrNoteDuration--;
 
314
                        DOSSongPtr++;
 
315
                        break;
 
316
                case '<':
 
317
                case '^':                                   // Move up an octave
 
318
                        pcspkrOctave++;
 
319
                        DOSSongPtr++;
 
320
                        break;
 
321
                case '>':
 
322
                case 'v':                                   // Move down an octave
 
323
                        pcspkrOctave--;
 
324
                        DOSSongPtr++;
 
325
                        break;
 
326
                default:
 
327
                        cmd_note = false;
 
328
                        break;
 
329
                }
 
330
        } while (cmd_note);
 
331
 
 
332
        switch (*DOSSongPtr) {
 
333
        case 'A':                                       // The notes.
 
334
        case 'B':
 
335
        case 'C':
 
336
        case 'D':
 
337
        case 'E':
 
338
        case 'F':
 
339
        case 'G':
 
340
                count = pcspkrNotes[*DOSSongPtr - 'A'];
 
341
                switch (DOSSongPtr[1]) {                    // Check for sharp or flat (#, -)
 
342
                case '#':
 
343
                        count = pcspkrSharps[*DOSSongPtr++ - 'A'];
 
344
                        break;
 
345
                case 'b':
 
346
                        count = pcspkrFlats[*DOSSongPtr++ - 'A'];
 
347
                        break;
 
348
                default:
 
349
                        break;
 
350
                }
 
351
                if (pcspkrOctave > 3)                       // Adjust for octave
 
352
                        count /= (1 << (pcspkrOctave - 3));
 
353
                else if (pcspkrOctave < 3)
 
354
                        count *= (1 << (3 - pcspkrOctave));
 
355
                _speakerStream->play(Audio::PCSpeaker::kWaveFormSaw, kHugoCNT / count, (int32) ((1 + pcspkrNoteDuration) * _vm->_normalTPS) * 8);
 
356
                pcspkrTimer = pcspkrNoteDuration;
 
357
                DOSSongPtr++;
 
358
                break;
 
359
        case '.':                                       // A rest note
 
360
                _speakerStream->stop();
 
361
                pcspkrTimer = pcspkrNoteDuration;
 
362
                DOSSongPtr++;
 
363
                break;
 
364
        default:
 
365
                warning("pcspkr_player() - Unhandled note");
 
366
        }
 
367
}
 
368
 
 
369
void SoundHandler::loadIntroSong(Common::ReadStream &in) {
 
370
        for (int varnt = 0; varnt < _vm->_numVariant; varnt++) {
 
371
                uint16 numBuf = in.readUint16BE();
 
372
                if (varnt == _vm->_gameVariant)
 
373
                        DOSIntroSong = _vm->_text->getTextData(numBuf);
 
374
        }
 
375
}
 
376
 
 
377
void SoundHandler::initPcspkrPlayer() {
 
378
        _vm->getTimerManager()->installTimerProc(&loopPlayer, 1000000 / 9, this);
329
379
}
330
380
 
331
381
} // End of namespace Hugo