1 ///////////////////////////////////////////////////////////////////////////////
2 //            Copyright (C) 2004-2011 by The Allacrost Project
3 //            Copyright (C) 2012-2016 by Bertram (Valyria Tear)
4 //                         All Rights Reserved
5 //
6 // This code is licensed under the GNU GPL version 2. It is free software
7 // and you may modify it and/or redistribute it under the terms of this license.
8 // See https://www.gnu.org/copyleft/gpl.html for details.
9 ///////////////////////////////////////////////////////////////////////////////
10 
11 #include "modes/map/map_objects/map_sound.h"
12 
13 #include "modes/map/map_mode.h"
14 #include "modes/map/map_object_supervisor.h"
15 
16 #include "engine/audio/audio.h"
17 
18 using namespace vt_common;
19 
20 namespace vt_map
21 {
22 
23 namespace private_map
24 {
25 
SoundObject(const std::string & sound_filename,float x,float y,float strength)26 SoundObject::SoundObject(const std::string& sound_filename, float x, float y, float strength):
27     MapObject(NO_LAYER_OBJECT), // This is a special object
28     _strength(strength),
29     _sound_volume(0.0f),
30     _max_sound_volume(1.0f),
31     _time_remaining(0.0f),
32     _activated(true),
33     _playing(false)
34 {
35     _object_type = SOUND_TYPE;
36 
37     // We use the AudioManager to mutualize the sound descriptors instances.
38     bool loaded = true;
39     loaded = vt_audio::AudioManager->LoadSound(sound_filename, MapMode::CurrentInstance());
40     _sound = vt_audio::AudioManager->RetrieveSound(sound_filename);
41     if (!loaded || _sound == nullptr) {
42         PRINT_WARNING << "Couldn't load environmental sound file: "
43             << sound_filename << std::endl;
44     }
45 
46     // Invalidates negative or near 0 values.
47     if (_strength <= 0.2f)
48         _strength = 0.0f;
49 
50     _tile_position.x = x;
51     _tile_position.y = y;
52 
53     _collision_mask = NO_COLLISION;
54 
55     if (_sound) {
56         _sound->SetLooping(true);
57         _sound->SetVolume(0.0f);
58         _sound->Stop();
59     }
60 
61     // Register the object to the sound vector
62     MapMode::CurrentInstance()->GetObjectSupervisor()->AddAmbientSound(this);
63 }
64 
Create(const std::string & sound_filename,float x,float y,float strength)65 SoundObject* SoundObject::Create(const std::string& sound_filename,
66                                  float x, float y, float strength)
67 {
68     // The object auto registers to the object supervisor
69     // and will later handle deletion.
70     return new SoundObject(sound_filename, x, y, strength);
71 }
72 
SetMaxVolume(float max_volume)73 void SoundObject::SetMaxVolume(float max_volume)
74 {
75     _max_sound_volume = max_volume;
76 
77     if (_max_sound_volume < 0.0f)
78         _max_sound_volume = 0.0f;
79     else if (_max_sound_volume > 1.0f)
80         _max_sound_volume = 1.0f;
81 }
82 
UpdateVolume()83 void SoundObject::UpdateVolume()
84 {
85     // Don't activate a sound which is too weak to be heard anyway.
86     if (_strength < 1.0f || _max_sound_volume <= 0.0f) {
87         _sound_volume = 0.0f;
88         return;
89     }
90 
91     if (!_activated) {
92         _sound_volume = 0.0f;
93         return;
94     }
95 
96     // Update the volume only every 100ms
97     _time_remaining -= static_cast<int32_t>(vt_system::SystemManager->GetUpdateTime());
98     if (_time_remaining > 0)
99         return;
100     _time_remaining = 100;
101 
102     // N.B.: The distance between two point formula is:
103     // squareroot((x2 - x1)^2+(y2 - y1)^2)
104     MapMode *mm = MapMode::CurrentInstance();
105     if(!mm)
106         return;
107 
108     const MapFrame& frame = mm->GetMapFrame();
109 
110     Position2D center;
111     center.x = frame.screen_edges.left + (frame.screen_edges.right - frame.screen_edges.left) / 2.0f;
112     center.y = frame.screen_edges.top + (frame.screen_edges.bottom - frame.screen_edges.top) / 2.0f;
113 
114     float distance = _tile_position.GetDistance2(center);
115     //distance = sqrtf(_distance); <-- We don't actually need it as it is slow.
116 
117     float strength2 = _strength * _strength;
118 
119     if (distance >= strength2) {
120         if (_playing) {
121             _sound_volume = 0.0f;
122             _playing = false;
123         }
124         return;
125     }
126 
127     // We add a one-half-tile neutral margin where nothing happens
128     // to avoid the edge case where the sound repeatedly starts/stops
129     // because of the camera position rounding.
130     if (distance >= (strength2 - 0.5f))
131         return;
132 
133     _sound_volume = _max_sound_volume - (_max_sound_volume * (distance / strength2));
134     _playing = true;
135 }
136 
ApplyVolume()137 void SoundObject::ApplyVolume()
138 {
139     if (!_sound)
140         return;
141 
142     // Stop sound if needed.
143     if (_sound_volume <= 0.1f) {
144         if (_sound->GetState() == vt_audio::AUDIO_STATE_PLAYING
145                || _sound->GetState() == vt_audio::AUDIO_STATE_FADE_IN) {
146            _sound->FadeOut(1000.0f);
147         }
148         return;
149     }
150 
151     if (_sound->GetState() != vt_audio::AUDIO_STATE_PLAYING
152             && _sound->GetState() != vt_audio::AUDIO_STATE_FADE_IN) {
153         _sound->FadeIn(1000.0f);
154     }
155     _sound->SetVolume(_sound_volume);
156 }
157 
Stop()158 void SoundObject::Stop()
159 {
160     if (!_activated)
161         return;
162 
163     if (_sound)
164         _sound->FadeOut(1000.0f);
165     _activated = false;
166 }
167 
Start()168 void SoundObject::Start()
169 {
170     if (_activated)
171         return;
172 
173     _activated = true;
174 
175     // Restores the sound state
176     UpdateVolume();
177 }
178 
179 } // namespace private_map
180 
181 } // namespace vt_map
182