1
/*
2
 *  The Mana World
3
 *  Copyright (C) 2004  The Mana World Development Team
4
 *
5
 *  This file is part of The Mana World.
6
 *
7
 *  This program is free software; you can redistribute it and/or modify
8
 *  it under the terms of the GNU General Public License as published by
9
 *  the Free Software Foundation; either version 2 of the License, or
10
 *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
 */
21
22
#include <SDL.h>
23
24
#include "log.h"
25
#include "sound.h"
26
27
#include "resources/resourcemanager.h"
28
#include "resources/soundeffect.h"
29
30
Sound::Sound():
31
    mInstalled(false),
32
    mSfxVolume(100),
33
    mMusicVolume(60),
34
    mMusic(NULL)
35
{
36
}
37
38
Sound::~Sound()
39
{
40
}
41
42
void Sound::init()
43
{
44
    // Don't initialize sound engine twice
45
    if (mInstalled)
46
        return;
47
48
    logger->log("Sound::init() Initializing sound...");
49
50
    if (SDL_InitSubSystem(SDL_INIT_AUDIO) == -1)
51
    {
52
        logger->log("Sound::init() Failed to initialize audio subsystem");
53
        return;
54
    }
55
56
    const size_t audioBuffer = 4096;
57
58
    const int res = Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT,
59
                                  MIX_DEFAULT_CHANNELS, audioBuffer);
60
    if (res < 0)
61
    {
62
        logger->log("Sound::init Could not initialize audio: %s",
63
                    Mix_GetError());
64
        return;
65
    }
66
67
    Mix_AllocateChannels(16);
68
    Mix_VolumeMusic(mMusicVolume);
69
    Mix_Volume(-1, mSfxVolume);
70
71
    info();
72
73
    mInstalled = true;
74
75
    if (!mCurrentMusicFile.empty())
76
        playMusic(mCurrentMusicFile);
77
}
78
79
void Sound::info()
80
{
81
    SDL_version compiledVersion;
82
    const SDL_version *linkedVersion;
83
    char driver[40] = "Unknown";
84
    const char *format = "Unknown";
85
    int rate = 0;
86
    Uint16 audioFormat = 0;
87
    int channels = 0;
88
89
    MIX_VERSION(&compiledVersion);
90
    linkedVersion = Mix_Linked_Version();
91
92
    SDL_AudioDriverName(driver, 40);
93
94
    Mix_QuerySpec(&rate, &audioFormat, &channels);
95
    switch (audioFormat) {
96
        case AUDIO_U8: format = "U8"; break;
97
        case AUDIO_S8: format = "S8"; break;
98
        case AUDIO_U16LSB: format = "U16LSB"; break;
99
        case AUDIO_S16LSB: format = "S16LSB"; break;
100
        case AUDIO_U16MSB: format = "U16MSB"; break;
101
        case AUDIO_S16MSB: format = "S16MSB"; break;
102
    }
103
104
    logger->log("Sound::info() SDL_mixer: %i.%i.%i (compiled)",
105
            compiledVersion.major,
106
            compiledVersion.minor,
107
            compiledVersion.patch);
108
    logger->log("Sound::info() SDL_mixer: %i.%i.%i (linked)",
109
            linkedVersion->major,
110
            linkedVersion->minor,
111
            linkedVersion->patch);
112
    logger->log("Sound::info() Driver: %s", driver);
113
    logger->log("Sound::info() Format: %s", format);
114
    logger->log("Sound::info() Rate: %i", rate);
115
    logger->log("Sound::info() Channels: %i", channels);
116
}
117
118
int Sound::getMaxVolume() const
119
{
120
    return MIX_MAX_VOLUME;
121
}
122
123
void Sound::setMusicVolume(int volume)
124
{
125
    mMusicVolume = volume;
126
127
    if (mInstalled)
128
        Mix_VolumeMusic(mMusicVolume);
129
}
130
131
void Sound::setSfxVolume(int volume)
132
{
133
    mSfxVolume = volume;
134
135
    if (mInstalled)
136
        Mix_Volume(-1, mSfxVolume);
137
}
138
139
static Mix_Music *loadMusic(const std::string &filename)
140
{
141
    ResourceManager *resman = ResourceManager::getInstance();
142
    std::string path = resman->getPath("music/" + filename);
143
144
    if (path.find(".zip/") != std::string::npos ||
145
        path.find(".zip\\") != std::string::npos)
146
    {
147
        // Music file is a virtual file inside a zip archive - we have to copy
148
        // it to a temporary physical file so that SDL_mixer can stream it.
149
        logger->log("Loading music \"%s\" from temporary file tempMusic.ogg",
150
                    path.c_str());
151
        bool success = resman->copyFile("music/" + filename, "tempMusic.ogg");
152
        if (success)
153
        {
154
            path = resman->getPath("tempMusic.ogg");
155
        } else {
156
            return NULL;
157
        }
158
    } else {
159
        logger->log("Loading music \"%s\"", path.c_str());
160
    }
161
162
    Mix_Music *music = Mix_LoadMUS(path.c_str());
163
164
    if (!music)
165
    {
166
        logger->log("Mix_LoadMUS() Error loading '%s': %s", path.c_str(),
167
                    Mix_GetError());
168
    }
169
170
    return music;
171
}
172
173
void Sound::playMusic(const std::string &filename)
174
{
175
    mCurrentMusicFile = filename;
176
177
    if (!mInstalled)
178
        return;
179
180
    haltMusic();
181
182
    if ((mMusic = loadMusic(filename)))
183
        Mix_PlayMusic(mMusic, -1); // Loop forever
184
}
185
186
void Sound::stopMusic()
187
{
188
    if (!mInstalled)
189
        return;
190
191
    logger->log("Sound::stopMusic()");
192
193
    if (mMusic) {
194
        Mix_HaltMusic();
195
        Mix_FreeMusic(mMusic);
196
        mMusic = NULL;
197
    }
198
}
199
200
void Sound::fadeInMusic(const std::string &path, int ms)
201
{
202
    mCurrentMusicFile = path;
203
204
    if (!mInstalled)
205
        return;
206
207
    haltMusic();
208
209
    if ((mMusic = loadMusic(path.c_str())))
210
        Mix_FadeInMusic(mMusic, -1, ms); // Loop forever
211
}
212
213
void Sound::fadeOutMusic(int ms)
214
{
215
    mCurrentMusicFile.clear();
216
217
    if (!mInstalled)
218
        return;
219
220
    logger->log("Sound::fadeOutMusic() Fading-out (%i ms)", ms);
221
222
    if (mMusic) {
223
        Mix_FadeOutMusic(ms);
224
        Mix_FreeMusic(mMusic);
225
        mMusic = NULL;
226
    }
227
}
228
229
void Sound::playSfx(const std::string &path)
230
{
231
    if (!mInstalled || path.empty())
232
        return;
233
234
    ResourceManager *resman = ResourceManager::getInstance();
235
    SoundEffect *sample = resman->getSoundEffect(path);
236
    if (sample) {
237
        logger->log("Sound::playSfx() Playing: %s", path.c_str());
238
        sample->play(0, 120);
239
    }
240
}
241
242
void Sound::close()
243
{
244
    if (!mInstalled)
245
        return;
246
247
    haltMusic();
248
    logger->log("Sound::close() Shutting down sound...");
249
    Mix_CloseAudio();
250
251
    mInstalled = false;
252
}
253
254
void Sound::haltMusic()
255
{
256
    if (!mMusic)
257
        return;
258
259
    Mix_HaltMusic();
260
    Mix_FreeMusic(mMusic);
261
    mMusic = NULL;
262
}