The Battle for Wesnoth  1.17.0-dev
soundsource.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2021
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include "display.hpp"
16 #include "log.hpp"
17 #include "random.hpp"
18 #include "sound.hpp"
19 #include "soundsource.hpp"
20 
21 #include <SDL2/SDL_timer.h>
22 
23 namespace soundsource {
24 
25 const unsigned DEFAULT_CHANCE = 100;
26 const unsigned DEFAULT_DELAY = 1000;
27 
28 unsigned int positional_source::last_id = 0;
29 
30 manager::manager(const display &disp) :
31  observer(),
32  sources_(),
33  disp_(disp)
34 {
37 }
38 
40 {
41  sources_.clear();
42 }
43 
44 void manager::handle_generic_event(const std::string &event_name)
45 {
46  if(event_name == "scrolled")
48 }
49 
50 void manager::add(const sourcespec &spec)
51 {
52  sources_[spec.id()].reset(new positional_source(spec));
53 }
54 
55 sourcespec manager::get(const std::string &id)
56 {
57  config cfg;
59  if(it != sources_.end()) {
60  it->second->write_config(cfg);
61  }
62  return cfg;
63 }
64 
65 void manager::remove(const std::string &id)
66 {
68 
69  if((it = sources_.find(id)) == sources_.end())
70  return;
71  else {
72  sources_.erase(it);
73  }
74 }
75 
76 bool manager::contains(const std::string& id)
77 {
78  return sources_.find(id) != sources_.end();
79 }
80 
82 {
83  unsigned int time = SDL_GetTicks();
84 
85  for(positional_source_iterator it = sources_.begin(); it != sources_.end(); ++it) {
86  (*it).second->update(time, disp_);
87  }
88 }
89 
91 {
92  unsigned int time = SDL_GetTicks();
93 
94  for(positional_source_iterator it = sources_.begin(); it != sources_.end(); ++it) {
95  (*it).second->update_positions(time, disp_);
96  }
97 }
98 
100 {
101  for(positional_source_const_iterator i = sources_.begin(); i != sources_.end(); ++i) {
102  assert(i->second);
103 
104  config& child = cfg.add_child("sound_source");
105  child["id"] = i->first;
106  i->second->write_config(child);
107  }
108 }
109 
111  last_played_(0),
112  min_delay_(spec.minimum_delay()),
113  chance_(spec.chance()),
114  loops_(spec.loops()),
115  id_(last_id++),
116  range_(spec.full_range()),
117  faderange_(spec.fade_range()),
118  check_fogged_(spec.check_fogged()),
119  check_shrouded_(spec.check_shrouded()),
120  files_(spec.files()),
121  locations_(spec.get_locations())
122 {
123  assert(range_ > 0);
124  assert(faderange_ > 0);
125 }
126 
128 {
130 }
131 
133 {
134  return locations_.empty();
135 }
136 
137 void positional_source::update(unsigned int time, const display &disp)
138 {
139  if (time - last_played_ < static_cast<unsigned>(min_delay_) || sound::is_sound_playing(id_))
140  return;
141 
143 
144  if(i <= chance_) {
145  last_played_ = time;
146 
147  // If no locations have been specified, treat the source as if
148  // it was present everywhere on the map
149  if(locations_.empty()) {
150  sound::play_sound_positioned(files_, id_, loops_, 0); // max volume
151  return;
152  }
153 
154  int distance_volume = DISTANCE_SILENT;
155  for(const map_location& l : locations_) {
156  int v = calculate_volume(l, disp);
157  if(v < distance_volume) {
158  distance_volume = v;
159  }
160  }
161 
162  if(distance_volume >= DISTANCE_SILENT)
163  return;
164 
165  sound::play_sound_positioned(files_, id_, loops_, distance_volume);
166  }
167 }
168 
169 void positional_source::update_positions(unsigned int time, const display &disp)
170 {
171  if(is_global()) {
172  return;
173  }
174 
175  int distance_volume = DISTANCE_SILENT;
176  for(std::vector<map_location>::iterator i = locations_.begin(); i != locations_.end(); ++i) {
177  int v = calculate_volume(*i, disp);
178  if(v < distance_volume) {
179  distance_volume = v;
180  }
181  }
182 
184  sound::reposition_sound(id_, distance_volume);
185  } else {
186  update(time, disp);
187  }
188 }
189 
191 {
192  assert(range_ > 0);
193  assert(faderange_ > 0);
194 
195  if((check_shrouded_ && disp.shrouded(loc)) || (check_fogged_ && disp.fogged(loc)))
196  return DISTANCE_SILENT;
197 
198  SDL_Rect area = disp.map_area();
199  map_location center = disp.hex_clicked_on(area.x + area.w / 2, area.y + area.h / 2);
200  int distance = distance_between(loc, center);
201 
202  if(distance <= range_) {
203  return 0;
204  }
205 
206  return static_cast<int>((((distance - range_)
207  / static_cast<double>(faderange_)) * DISTANCE_SILENT));
208 }
209 
211 {
212  cfg["sounds"] = files_;
213  cfg["delay"] = min_delay_;
214  cfg["chance"] = chance_;
215  cfg["check_fogged"] = check_fogged_;
216  cfg["check_shrouded"] = check_shrouded_;
217  cfg["loop"] = loops_;
218  cfg["full_range"] = range_;
219  cfg["fade_range"] = faderange_;
221 }
222 
223 void sourcespec::write(config& cfg) const
224 {
225  cfg["id"] = id_;
226  cfg["sounds"] = files_;
227  cfg["delay"] = min_delay_;
228  cfg["chance"] = chance_;
229  cfg["check_fogged"] = check_fogged_;
230  cfg["check_shrouded"] = check_shrouded_;
231  cfg["loop"] = loops_;
232  cfg["full_range"] = range_;
233  cfg["fade_range"] = faderange_;
235 }
236 
238  id_(cfg["id"]),
239  files_(cfg["sounds"]),
240  min_delay_(cfg["delay"].to_int(DEFAULT_DELAY)),
241  chance_(cfg["chance"].to_int(DEFAULT_CHANCE)),
242  loops_(cfg["loop"]),
243  range_(cfg["full_range"].to_int(3)),
244  faderange_(cfg["fade_range"].to_int(14)),
245  check_fogged_(cfg["check_fogged"].to_bool(true)),
246  check_shrouded_(cfg["check_shrouded"].to_bool(true)),
247  locations_()
248 {
250 }
251 
252 } // 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:442
bool is_sound_playing(int id)
Definition: sound.cpp:877
void play_sound_positioned(const std::string &files, int id, int repeats, unsigned int distance)
Definition: sound.cpp:1027
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:598
events::generic_event & scroll_event() const
Expose the event, so observers can be notified about map scrolling.
Definition: display.hpp:565
const SDL_Rect & map_area() const
Returns the area used for the map.
Definition: display.cpp:561
void stop_sound()
Definition: sound.cpp:564
void remove(const std::string &id)
Definition: soundsource.cpp:65
void reposition_sound(int id, unsigned int distance)
Definition: sound.cpp:861
const std::string & id() const
positional_source_map::iterator positional_source_iterator
Definition: soundsource.hpp:80
void update(unsigned int time, const display &disp)
sourcespec get(const std::string &id)
Definition: soundsource.cpp:55
void handle_generic_event(const std::string &event_name)
Definition: soundsource.cpp:44
std::vector< map_location > locations_
positional_source_map sources_
Definition: soundsource.hpp:83
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:744
Encapsulates the map of the game.
Definition: location.hpp:37
bool shrouded(const map_location &loc) const
Returns true if location (x,y) is covered in shroud.
Definition: display.cpp:739
std::vector< map_location > locations_
Definition: soundsource.hpp:46
Sound source info class.
virtual bool attach_handler(observer *obs)
std::size_t i
Definition: function.cpp:940
void add(const sourcespec &source)
Definition: soundsource.cpp:50
const unsigned DEFAULT_CHANCE
Definition: soundsource.cpp:25
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:51
void write_sourcespecs(config &cfg) const
Serializes information into cfg as new children of key "sound_source", appended to existing content...
Definition: soundsource.cpp:99
std::string observer
manager(const display &disp)
Definition: soundsource.cpp:30
config & add_child(config_key_type key)
Definition: config.cpp:503
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:545
#define DISTANCE_SILENT
Definition: sound.hpp:75
Standard logging facilities (interface).
const display & disp_
Definition: soundsource.hpp:84
const unsigned DEFAULT_DELAY
Definition: soundsource.cpp:26
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:59
static rng & default_instance()
Definition: random.cpp:73
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
positional_source_map::const_iterator positional_source_const_iterator
Definition: soundsource.hpp:81
static unsigned int last_id
Definition: soundsource.hpp:50
bool contains(const std::string &id)
Definition: soundsource.cpp:76
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:454