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