Resourcer => Reader, singletonize reader, music, getSong => Music
[Tsunagari.git] / src / music.cpp
blobf4b3e406c91360a0c1029daefc21197a39eeaa30
1 /*********************************
2 ** Tsunagari Tile Engine **
3 ** music.cpp **
4 ** Copyright 2011-2012 OmegaSDG **
5 *********************************/
7 // **********
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to
10 // deal in the Software without restriction, including without limitation the
11 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 // sell copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 // IN THE SOFTWARE.
25 // **********
27 #include <Gosu/Audio.hpp> // for Gosu::SampleInstance
28 #include <Gosu/Math.hpp>
30 #include "client-conf.h"
31 #include "music.h"
32 #include "reader.h"
33 #include "readercache.h"
34 #include "python.h"
36 typedef boost::shared_ptr<Gosu::Song> SongRef;
38 enum MUSIC_STATE
40 NOT_PLAYING,
41 PLAYING_INTRO,
42 PLAYING_LOOP,
43 CHANGED_INTRO,
44 CHANGED_LOOP
47 static SongRef musicInst, introMusic, loopMusic;
49 static bool paused = false;
51 static MUSIC_STATE state = NOT_PLAYING;
53 static std::string newIntro;
54 static std::string newLoop;
55 static std::string curIntro;
56 static std::string curLoop;
59 static SongRef genSong(const std::string& name)
61 BufferPtr buffer(Reader::readBuffer(name));
62 if (!buffer)
63 return SongRef();
64 return SongRef(new Gosu::Song(buffer->frontReader()));
67 static ReaderCache<SongRef> songs(genSong);
69 static SongRef getSong(const std::string& name)
71 if (!conf.audioEnabled)
72 return SongRef();
73 return songs.lifetimeRequest(name);
78 static const char* stateStr(MUSIC_STATE state)
80 switch (state) {
81 case NOT_PLAYING:
82 return "NOT_PLAYING";
83 case PLAYING_INTRO:
84 return "PLAYING_INTRO";
85 case PLAYING_LOOP:
86 return "PLAYING_LOOP";
87 case CHANGED_INTRO:
88 return "CHANGED_INTRO";
89 case CHANGED_LOOP:
90 return "CHANGED_LOOP";
91 default:
92 return "";
97 static void setState(MUSIC_STATE state_)
99 // printf("State changed from %s to %s.\n", stateStr(this->state), stateStr(state));
100 state = state_;
103 static void playIntro()
105 if (musicInst && musicInst->playing())
106 musicInst->stop();
107 curIntro = newIntro;
108 introMusic->play(false);
109 introMusic->changeVolume(conf.musicVolume / 100.0);
110 musicInst = introMusic;
111 setState(PLAYING_INTRO);
114 static void playLoop()
116 if (musicInst && musicInst->playing())
117 musicInst->stop();
118 curLoop = newLoop;
119 loopMusic->play(true);
120 loopMusic->changeVolume(conf.musicVolume / 100.0);
121 musicInst = loopMusic;
122 setState(PLAYING_LOOP);
127 Music::Music()
131 Music::~Music()
133 if (musicInst && musicInst->playing())
134 musicInst->stop();
138 std::string Music::getIntro()
140 return newIntro;
143 std::string Music::getLoop()
145 return newLoop;
148 void Music::setIntro(const std::string& filename)
150 if (newIntro == filename)
151 return;
153 switch (state) {
154 case NOT_PLAYING:
155 case PLAYING_INTRO:
156 case PLAYING_LOOP:
157 setState(CHANGED_INTRO);
158 default: break;
161 newIntro = filename;
162 // Optimize XXX: Don't load until played.
163 introMusic = filename.size() ? getSong(filename) : SongRef();
166 void Music::setLoop(const std::string& filename)
168 if (newLoop == filename)
169 return;
171 switch (state) {
172 case NOT_PLAYING:
173 case PLAYING_INTRO:
174 case PLAYING_LOOP:
175 setState(CHANGED_LOOP);
176 default: break;
179 newLoop = filename;
180 // Optimize XXX: Don't load until played.
181 loopMusic = filename.size() ? getSong(filename) : SongRef();
184 int Music::getVolume()
186 return conf.musicVolume;
189 void Music::setVolume(int level)
191 if (0 < level || level > 100) {
192 Log::info("Music", "volume can only be set between 0 and 100");
193 level = Gosu::clamp(level, 0, 100);
195 conf.musicVolume = level;
196 if (musicInst)
197 musicInst->changeVolume(level);
200 bool Music::isPaused()
202 return paused;
205 void Music::setPaused(bool p)
207 if (paused == p)
208 return;
209 paused = p;
210 if (musicInst) {
211 if (p)
212 musicInst->pause();
213 else
214 musicInst->play();
218 void Music::stop()
220 paused = false;
221 if (musicInst)
222 musicInst->stop();
223 state = NOT_PLAYING;
226 void Music::tick()
228 if (paused)
229 return;
231 switch (state) {
232 case NOT_PLAYING:
233 if (musicInst && musicInst->playing())
234 musicInst->stop();
235 break;
236 case PLAYING_INTRO:
237 if (!musicInst->playing()) {
238 if (newLoop.size() && loopMusic)
239 playLoop();
240 else
241 setState(NOT_PLAYING);
243 break;
244 case PLAYING_LOOP:
245 break;
246 case CHANGED_INTRO:
247 if (newIntro.size() && introMusic)
248 playIntro();
249 else if (newLoop.size() && newLoop != curLoop)
250 setState(CHANGED_LOOP);
251 else if (newLoop.size())
252 setState(PLAYING_LOOP);
253 else
254 setState(NOT_PLAYING);
255 break;
256 case CHANGED_LOOP:
257 if (newIntro.size() && loopMusic)
258 playIntro();
259 else if (newLoop.size() && loopMusic)
260 playLoop();
261 else
262 setState(NOT_PLAYING);
263 break;
268 void exportMusic()
271 // FIXME: Broken with shift to singleton. No instantiated object to bind.
272 // Fix will require a stub object.
274 #if 0
275 boost::python::class_<Music>("MusicManager", boost::python::no_init)
276 .add_property("intro", &Music::getIntro, &Music::setIntro)
277 .add_property("loop", &Music::getLoop, &Music::setLoop)
278 .add_property("volume", &Music::getVolume, &Music::setVolume)
279 .add_property("paused", &Music::isPaused, &Music::setPaused)
280 .def("stop", &Music::stop)
283 pythonSetGlobal("Music", this);
284 #endif