The Battle for Wesnoth  1.17.0-dev
soundsource.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2021
3  by Karol Nowak <grzywacz@sul.uni.lodz.pl>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "display.hpp"
17 #include "log.hpp"
18 #include "random.hpp"
19 #include "sound.hpp"
20 #include "soundsource.hpp"
21 
22 #include <SDL2/SDL_timer.h>
23 
24 namespace soundsource {
25 
26 const unsigned DEFAULT_CHANCE = 100;
27 const unsigned DEFAULT_DELAY = 1000;
28 
29 unsigned int positional_source::last_id = 0;
30 
31 manager::manager(const display &disp) :
32  observer(),
33  sources_(),
34  disp_(disp)
35 {
38 }
39 
41 {
42  sources_.clear();
43 }
44 
45 void manager::handle_generic_event(const std::string &event_name)
46 {
47  if(event_name == "scrolled")
49 }
50 
51 void manager::add(const sourcespec &spec)
52 {
53  sources_[spec.id()].reset(new positional_source(spec));
54 }
55 
56 sourcespec manager::get(const std::string &id)
57 {
58  config cfg;
60  if(it != sources_.end()) {
61  it->second->write_config(cfg);
62  }
63  return cfg;
64 }
65 
66 void manager::remove(const std::string &id)
67 {
69 
70  if((it = sources_.find(id)) == sources_.end())
71  return;
72  else {
73  sources_.erase(it);
74  }
75 }
76 
77 bool manager::contains(const std::string& id)
78 {
79  return sources_.find(id) != sources_.end();
80 }
81 
83 {
84  unsigned int time = SDL_GetTicks();
85 
86  for(positional_source_iterator it = sources_.begin(); it != sources_.end(); ++it) {
87  (*it).second->update(time, disp_);
88  }
89 }
90 
92 {
93  unsigned int time = SDL_GetTicks();
94 
95  for(positional_source_iterator it = sources_.begin(); it != sources_.end(); ++it) {
96  (*it).second->update_positions(time, disp_);
97  }
98 }
99 
101 {
102  for(positional_source_const_iterator i = sources_.begin(); i != sources_.end(); ++i) {
103  assert(i->second);
104 
105  config& child = cfg.add_child("sound_source");
106  child["id"] = i->first;
107  i->second->write_config(child);
108  }
109 }
110 
112  last_played_(0),
113  min_delay_(spec.minimum_delay()),
114  chance_(spec.chance()),
115  loops_(spec.loops()),
116  id_(last_id++),
117  range_(spec.full_range()),
118  faderange_(spec.fade_range()),
119  check_fogged_(spec.check_fogged()),
120  check_shrouded_(spec.check_shrouded()),
121  files_(spec.files()),
122  locations_(spec.get_locations())
123 {
124  assert(range_ > 0);
125  assert(faderange_ > 0);
126 }
127 
129 {
131 }
132 
134 {
135  return locations_.empty();
136 }
137 
138 void positional_source::update(unsigned int time, const display &disp)
139 {
140  if (time - last_played_ < static_cast<unsigned>(min_delay_) || sound::is_sound_playing(id_))
141  return;
142 
144 
145  if(i <= chance_) {
146  last_played_ = time;
147 
148  // If no locations have been specified, treat the source as if
149  // it was present everywhere on the map
150  if(locations_.empty()) {
151  sound::play_sound_positioned(files_, id_, loops_, 0); // max volume
152  return;
153  }
154 
155  int distance_volume = DISTANCE_SILENT;
156  for(const map_location& l : locations_) {
157  int v = calculate_volume(l, disp);
158  if(v < distance_volume) {
159  distance_volume = v;
160  }
161  }
162 
163  if(distance_volume >= DISTANCE_SILENT)
164  return;
165 
166  sound::play_sound_positioned(files_, id_, loops_, distance_volume);
167  }
168 }
169 
170 void positional_source::update_positions(unsigned int time, const display &disp)
171 {
172  if(is_global()) {
173  return;
174  }
175 
176  int distance_volume = DISTANCE_SILENT;
177  for(std::vector<map_location>::iterator i = locations_.begin(); i != locations_.end(); ++i) {
178  int v = calculate_volume(*i, disp);
179  if(v < distance_volume) {
180  distance_volume = v;
181  }
182  }
183 
185  sound::reposition_sound(id_, distance_volume);
186  } else {
187  update(time, disp);
188  }
189 }
190 
192 {
193  assert(range_ > 0);
194  assert(faderange_ > 0);
195 
196  if((check_shrouded_ && disp.shrouded(loc)) || (check_fogged_ && disp.fogged(loc)))
197  return DISTANCE_SILENT;
198 
199  SDL_Rect area = disp.map_area();
200  map_location center = disp.hex_clicked_on(area.x + area.w / 2, area.y + area.h / 2);
201  int distance = distance_between(loc, center);
202 
203  if(distance <= range_) {
204  return 0;
205  }
206 
207  return static_cast<int>((((distance - range_)
208  / static_cast<double>(faderange_)) * DISTANCE_SILENT));
209 }
210 
212 {
213  cfg["sounds"] = files_;
214  cfg["delay"] = min_delay_;
215  cfg["chance"] = chance_;
216  cfg["check_fogged"] = check_fogged_;
217  cfg["check_shrouded"] = check_shrouded_;
218  cfg["loop"] = loops_;
219  cfg["full_range"] = range_;
220  cfg["fade_range"] = faderange_;
222 }
223 
224 void sourcespec::write(config& cfg) const
225 {
226  cfg["id"] = id_;
227  cfg["sounds"] = files_;
228  cfg["delay"] = min_delay_;
229  cfg["chance"] = chance_;
230  cfg["check_fogged"] = check_fogged_;
231  cfg["check_shrouded"] = check_shrouded_;
232  cfg["loop"] = loops_;
233  cfg["full_range"] = range_;
234  cfg["fade_range"] = faderange_;
236 }
237 
239  id_(cfg["id"]),
240  files_(cfg["sounds"]),
241  min_delay_(cfg["delay"].to_int(DEFAULT_DELAY)),
242  chance_(cfg["chance"].to_int(DEFAULT_CHANCE)),
243  loops_(cfg["loop"]),
244  range_(cfg["full_range"].to_int(3)),
245  faderange_(cfg["fade_range"].to_int(14)),
246  check_fogged_(cfg["check_fogged"].to_bool(true)),
247  check_shrouded_(cfg["check_shrouded"].to_bool(true)),
248  locations_()
249 {
251 }
252 
253 } // namespace soundsource
int calculate_volume(const map_location &loc, const display &disp)
void write(config &cfg) const
Serializes information into cfg as a new (appended) child of key "sound_source".
void read_locations(const config &cfg, std::vector< map_location > &locs)
Parse x,y keys of a config into a vector of locations.
Definition: location.cpp:443
bool is_sound_playing(int id)
Definition: sound.cpp:878
void play_sound_positioned(const std::string &files, int id, int repeats, unsigned int distance)
Definition: sound.cpp:1028
const map_location hex_clicked_on(int x, int y) const
given x,y co-ordinates of an onscreen pixel, will return the location of the hex that this pixel corr...
Definition: display.cpp:599
events::generic_event & scroll_event() const
Expose the event, so observers can be notified about map scrolling.
Definition: display.hpp:566
const SDL_Rect & map_area() const
Returns the area used for the map.
Definition: display.cpp:562
void stop_sound()
Definition: sound.cpp:565
void remove(const std::string &id)
Definition: soundsource.cpp:66
void reposition_sound(int id, unsigned int distance)
Definition: sound.cpp:862
const std::string & id() const
positional_source_map::iterator positional_source_iterator
Definition: soundsource.hpp:81
void update(unsigned int time, const display &disp)
sourcespec get(const std::string &id)
Definition: soundsource.cpp:56
void handle_generic_event(const std::string &event_name)
Definition: soundsource.cpp:45
std::vector< map_location > locations_
positional_source_map sources_
Definition: soundsource.hpp:84
void write_config(config &cfg) const
Serializes attributes as WML config.
map_display and display: classes which take care of displaying the map and game-data on the screen...
void update_positions(unsigned int time, const display &disp)
bool fogged(const map_location &loc) const
Returns true if location (x,y) is covered in fog.
Definition: display.cpp:745
Encapsulates the map of the game.
Definition: location.hpp:38
bool shrouded(const map_location &loc) const
Returns true if location (x,y) is covered in shroud.
Definition: display.cpp:740
std::vector< map_location > locations_
Definition: soundsource.hpp:47
Sound source info class.
virtual bool attach_handler(observer *obs)
std::size_t i
Definition: function.cpp:967
void add(const sourcespec &source)
Definition: soundsource.cpp:51
const unsigned DEFAULT_CHANCE
Definition: soundsource.cpp:26
positional_source(const sourcespec &spec)
sourcespec(const std::string &id, const std::string &files, int min_delay, int chance)
Parameter-list constructor.
int get_random_int(int min, int max)
This helper method provides a random int from the underlying generator, using results of next_random...
Definition: random.hpp:52
void write_sourcespecs(config &cfg) const
Serializes information into cfg as new children of key "sound_source", appended to existing content...
std::string observer
manager(const display &disp)
Definition: soundsource.cpp:31
config & add_child(config_key_type key)
Definition: config.cpp:514
std::size_t distance_between(const map_location &a, const map_location &b)
Function which gives the number of hexes between two tiles (i.e.
Definition: location.cpp:546
#define DISTANCE_SILENT
Definition: sound.hpp:76
Standard logging facilities (interface).
const display & disp_
Definition: soundsource.hpp:85
const unsigned DEFAULT_DELAY
Definition: soundsource.cpp:27
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
static rng & default_instance()
Definition: random.cpp:74
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
positional_source_map::const_iterator positional_source_const_iterator
Definition: soundsource.hpp:82
static unsigned int last_id
Definition: soundsource.hpp:51
bool contains(const std::string &id)
Definition: soundsource.cpp:77
void write_locations(const std::vector< map_location > &locs, config &cfg)
Write a vector of locations into a config adding keys x=x1,x2,..,xn and y=y1,y2,..,yn.
Definition: location.cpp:455