1 /*
2 * Kuklomenos
3 * Copyright (C) 2008-2009 Martin Bays <mbays@sdf.lonestar.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 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see http://www.gnu.org/licenses/.
17 */
18
19 #include <cstdlib>
20 #include <vector>
21 #include <algorithm>
22
23 #include "sound.h"
24
25 Mix_Chunk* invDieChunk = NULL;
26 Mix_Chunk* invHitChunk = NULL;
27 Mix_Chunk* sparkChunk = NULL;
28 Mix_Chunk* shotChunk = NULL;
29 Mix_Chunk* shieldChunk = NULL;
30 Mix_Chunk* primedChunk = NULL;
31 Mix_Chunk* nodeHumChunk = NULL;
32 Mix_Chunk* mutChunk = NULL;
33
34 SoundEvents soundEvents;
35
36
37 #ifndef SOUND
newEvent(RelPolarCoord pos,Mix_Chunk * chunk,int volume,int stretch,bool noSight)38 void SoundEvents::newEvent(RelPolarCoord pos, Mix_Chunk* chunk,
39 int volume, int stretch, bool noSight)
40 {}
update(RelPolarCoord aimPos)41 void SoundEvents::update(RelPolarCoord aimPos)
42 {}
43 #else
44
45 #include "SDL_mixer/SDL_mixer.h"
46
47 #include "data.h"
48 #include "coords.h"
49 #include "geom.h"
50 #include "random.h"
51 #include "settings.h"
52
channelDone(int channel)53 void channelDone(int channel)
54 {
55 soundEvents.channelDone(channel);
56 }
57
SoundEvent(RelPolarCoord pos,Mix_Chunk * chunk,int volume,int stretch,bool noSight)58 SoundEvent::SoundEvent(RelPolarCoord pos, Mix_Chunk* chunk,
59 int volume, int stretch, bool noSight) :
60 pos(pos), chunk(chunk), volume(volume), noSight(noSight),
61 hadFirstUpdate(false), finished(false)
62 {
63 channel = Mix_PlayChannel(noSight ? -2 : -1, chunk, 0);
64 if (channel == -1)
65 finished = true;
66 else
67 {
68 Mix_ChannelFinished(channelDone);
69 Mix_Stretch(channel, stretch);
70 Mix_Volume(channel, 0);
71 }
72 }
73
update(RelPolarCoord aimPos)74 void SoundEvent::update(RelPolarCoord aimPos)
75 {
76 if (!hadFirstUpdate)
77 {
78 int evAngle;
79 if (pos.dist == 0)
80 evAngle=0;
81 else
82 {
83 Angle da = pos.angle - aimPos.angle;
84 evAngle = 360 - int(float(da)*90);
85 }
86 int evDist = 255*int(pos.dist)/ARENA_RAD;
87 SDL_LockAudio();
88 Mix_SetPosition(channel, evAngle, evDist);
89 SDL_UnlockAudio();
90 }
91
92 float sightBoundVolMult = 1;
93 if (!noSight)
94 {
95 // we can only hear the event when it's in sight
96
97 // sightBoundsDist: distance of event from centre of sight bounding
98 // circle, where 1.0 is the radius of that circle
99 const float sightBoundsDist =
100 dist((ARENA_CENTRE+pos) - (ARENA_CENTRE+aimPos)) /
101 (ARENA_RAD - aimPos.dist);
102
103 // linear dropoff at the edges
104 sightBoundVolMult =
105 sightBoundsDist > 1 ? 0.0 :
106 sightBoundsDist < 0.8 ? 1.0 :
107 1.0 - 5*(sightBoundsDist-0.8);
108
109 if (!hadFirstUpdate)
110 startVolMult = sightBoundVolMult;
111
112 // don't allow to vary too far from initial mult:
113 sightBoundVolMult = std::min(std::max(
114 startVolMult - 0.3f,
115 sightBoundVolMult),
116 startVolMult + 0.3f);
117 }
118
119 Mix_Volume(channel, int(volume*settings.volume*sightBoundVolMult));
120
121 if (!hadFirstUpdate)
122 hadFirstUpdate = true;
123 }
124
update(RelPolarCoord aimPos)125 void SoundEvents::update(RelPolarCoord aimPos)
126 {
127 for (std::vector<SoundEvent>::iterator it = begin(); it != end(); it++)
128 it->update(aimPos);
129
130 erase(remove_if(begin(),
131 end(), SoundEvent::isFinished),
132 end());
133 }
134
newEvent(RelPolarCoord pos,Mix_Chunk * chunk,int volume,int stretch,bool noSight)135 void SoundEvents::newEvent(RelPolarCoord pos, Mix_Chunk* chunk,
136 int volume, int stretch, bool noSight)
137 {
138 if (settings.sound && ( audioInitialised || initialiseAudio() ))
139 push_back(SoundEvent(pos, chunk, volume, stretch, noSight));
140 }
141
channelDone(int channel)142 void SoundEvents::channelDone(int channel)
143 {
144 for (std::vector<SoundEvent>::iterator it = begin(); it != end(); it++)
145 if (it->channel == channel)
146 it->finished = true;
147 }
148
initialiseAudio()149 bool SoundEvents::initialiseAudio()
150 {
151 const int audio_buffers = 512;
152 int audio_rate, audio_channels, bits;
153 Uint16 audio_format;
154 if (Mix_OpenAudio(settings.soundFreq, MIX_DEFAULT_FORMAT, 2,
155 audio_buffers) < 0)
156 return false;
157 // print out some info on the audio device and stream
158 Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels);
159 bits=audio_format&0xFF;
160 printf("Opened audio at %d Hz %d bit %s, %d bytes audio buffer\n", audio_rate,
161 bits, audio_channels>1?"stereo":"mono", audio_buffers );
162
163 invDieChunk = Mix_LoadWAV(findDataPath("sounds/invdie.ogg").c_str());
164 invHitChunk = Mix_LoadWAV(findDataPath("sounds/invhit.ogg").c_str());
165 sparkChunk = Mix_LoadWAV(findDataPath("sounds/spark.ogg").c_str());
166 primedChunk = Mix_LoadWAV(findDataPath("sounds/primed.ogg").c_str());
167 shieldChunk = Mix_LoadWAV(findDataPath("sounds/shield.ogg").c_str());
168 shotChunk = Mix_LoadWAV(findDataPath("sounds/shot.ogg").c_str());
169 nodeHumChunk = Mix_LoadWAV(findDataPath("sounds/hum.ogg").c_str());
170 mutChunk = Mix_LoadWAV(findDataPath("sounds/mutilation.ogg").c_str());
171
172 if (!shotChunk)
173 printf("Failed to open sound file 'sounds/shot.ogg'\n");
174
175 Mix_AllocateChannels(32);
176
177 audioInitialised = true;
178 return true;
179 }
180
181 #endif
182