1 /*
2 ** sndseq.cpp
3 **
4 **---------------------------------------------------------------------------
5 ** Copyright 2013 Braden Obrzut
6 ** All rights reserved.
7 **
8 ** Redistribution and use in source and binary forms, with or without
9 ** modification, are permitted provided that the following conditions
10 ** are met:
11 **
12 ** 1. Redistributions of source code must retain the above copyright
13 **    notice, this list of conditions and the following disclaimer.
14 ** 2. Redistributions in binary form must reproduce the above copyright
15 **    notice, this list of conditions and the following disclaimer in the
16 **    documentation and/or other materials provided with the distribution.
17 ** 3. The name of the author may not be used to endorse or promote products
18 **    derived from this software without specific prior written permission.
19 **
20 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 **---------------------------------------------------------------------------
31 **
32 **
33 */
34 
35 #include "id_sd.h"
36 #include "m_random.h"
37 #include "scanner.h"
38 #include "sndseq.h"
39 #include "sndinfo.h"
40 #include "w_wad.h"
41 #include "wl_game.h"
42 
43 #include <climits>
44 
45 enum ESndSeqFlag
46 {
47 	SSF_NoStopCutOff = 0x1
48 };
49 
50 // We used a fixed size instruction. What the instruction does is determined
51 // by what flags are set, so a single instruction can play a sound and delay
52 // for example.
53 enum ESndSeqInstruction
54 {
55 	SSI_PlaySound = 0x1,
56 	SSI_Delay = 0x2,
57 	SSI_End = 0x4,
58 	SSI_WaitForFinish = 0x8,
59 	SSI_Repeat = 0x10
60 };
61 
SoundSequence()62 SoundSequence::SoundSequence() : Flags(0)
63 {
64 }
65 
AddInstruction(const SndSeqInstruction & instr)66 void SoundSequence::AddInstruction(const SndSeqInstruction &instr)
67 {
68 	Instructions.Push(instr);
69 }
70 
Clear()71 void SoundSequence::Clear()
72 {
73 	for(unsigned int i = 0;i < NUM_SEQ_TYPES;++i)
74 		AltSequences[i] = NAME_None;
75 	Instructions.Clear();
76 }
77 
GetSequence(SequenceType type) const78 const SoundSequence &SoundSequence::GetSequence(SequenceType type) const
79 {
80 	if(AltSequences[type] == NAME_None)
81 		return *this;
82 	return SoundSeq(AltSequences[type], type);
83 }
84 
SetFlag(unsigned int flag,bool set)85 void SoundSequence::SetFlag(unsigned int flag, bool set)
86 {
87 	if(set)
88 		Flags |= flag;
89 	else
90 		Flags &= ~flag;
91 }
92 
SetSequence(SequenceType type,FName sequence)93 void SoundSequence::SetSequence(SequenceType type, FName sequence)
94 {
95 	AltSequences[type] = sequence;
96 }
97 
Start() const98 const SndSeqInstruction *SoundSequence::Start() const
99 {
100 	if(Instructions.Size() == 0)
101 		return NULL;
102 	return &Instructions[0];
103 }
104 
105 //------------------------------------------------------------------------------
106 
107 SndSeqTable SoundSeq;
108 
Init()109 void SndSeqTable::Init()
110 {
111 	Printf("S_Init: Reading SNDSEQ defintions.\n");
112 
113 	int lastLump = 0;
114 	int lump = 0;
115 	while((lump = Wads.FindLump("SNDSEQ", &lastLump)) != -1)
116 	{
117 		ParseSoundSequence(lump);
118 	}
119 }
120 
ParseSoundSequence(int lumpnum)121 void SndSeqTable::ParseSoundSequence(int lumpnum)
122 {
123 	FMemLump lump = Wads.ReadLump(lumpnum);
124 	Scanner sc((const char*)(lump.GetMem()), lump.GetSize());
125 	sc.SetScriptIdentifier(Wads.GetLumpFullName(lumpnum));
126 
127 	while(sc.TokensLeft())
128 	{
129 		if(sc.CheckToken('['))
130 		{
131 			sc.MustGetToken(TK_Identifier);
132 			SoundSequence &seq = Sequences[sc->str];
133 			seq.Name = sc->str;
134 			seq.Clear();
135 
136 			while(!sc.CheckToken(']'))
137 			{
138 				sc.MustGetToken(TK_IntConst);
139 				SequenceType type = static_cast<SequenceType>(sc->number);
140 
141 				if(!sc.GetNextString())
142 					sc.ScriptMessage(Scanner::ERROR, "Expected logical sound name.");
143 				seq.SetSequence(type, sc->str);
144 			}
145 		}
146 		else
147 		{
148 			sc.MustGetToken(':');
149 			sc.MustGetToken(TK_Identifier);
150 			SoundSequence &seq = Sequences[sc->str];
151 			seq.Name = sc->str;
152 			seq.Clear();
153 
154 			do
155 			{
156 				sc.MustGetToken(TK_Identifier);
157 
158 				if(sc->str.CompareNoCase("end") == 0)
159 				{
160 					SndSeqInstruction instr;
161 					instr.Instruction = SSI_End;
162 					seq.AddInstruction(instr);
163 					break;
164 				}
165 				else if(sc->str.CompareNoCase("delay") == 0)
166 				{
167 					SndSeqInstruction instr;
168 					instr.Instruction = SSI_Delay;
169 					instr.ArgumentRand = 0;
170 
171 					sc.MustGetToken(TK_IntConst);
172 					instr.Argument = sc->number;
173 
174 					seq.AddInstruction(instr);
175 				}
176 				else if(sc->str.CompareNoCase("delayrand") == 0)
177 				{
178 					SndSeqInstruction instr;
179 					instr.Instruction = SSI_Delay;
180 
181 					sc.MustGetToken(TK_IntConst);
182 					instr.Argument = sc->number;
183 
184 					sc.MustGetToken(TK_IntConst);
185 					instr.ArgumentRand = sc->number - instr.Argument;
186 
187 					seq.AddInstruction(instr);
188 				}
189 				else if(sc->str.CompareNoCase("play") == 0)
190 				{
191 					SndSeqInstruction instr;
192 					instr.Instruction = SSI_PlaySound;
193 
194 					if(!sc.GetNextString())
195 						sc.ScriptMessage(Scanner::ERROR, "Expected logical sound name.");
196 					instr.Sound = sc->str;
197 
198 					seq.AddInstruction(instr);
199 				}
200 				else if(sc->str.CompareNoCase("playrepeat") == 0)
201 				{
202 					SndSeqInstruction instr;
203 					instr.Instruction = SSI_PlaySound|SSI_WaitForFinish|SSI_Repeat;
204 
205 					if(!sc.GetNextString())
206 						sc.ScriptMessage(Scanner::ERROR, "Expected logical sound name.");
207 					instr.Sound = sc->str;
208 
209 					seq.AddInstruction(instr);
210 				}
211 				else if(sc->str.CompareNoCase("nostopcutoff") == 0)
212 				{
213 					seq.SetFlag(SSF_NoStopCutOff, true);
214 				}
215 				else if(sc->str.CompareNoCase("stopsound") == 0)
216 				{
217 					if(!sc.GetNextString())
218 						sc.ScriptMessage(Scanner::ERROR, "Expected logical sound name.");
219 					seq.StopSound = sc->str;
220 				}
221 				else
222 				{
223 					sc.ScriptMessage(Scanner::ERROR, "Unknown sound sequence command '%s'.", sc->str.GetChars());
224 				}
225 			}
226 			while(sc.TokensLeft());
227 		}
228 	}
229 }
230 
operator ()(FName sequence,SequenceType type) const231 const SoundSequence &SndSeqTable::operator() (FName sequence, SequenceType type) const
232 {
233 	static const SoundSequence NullSequence;
234 
235 	const SoundSequence *seq = Sequences.CheckKey(sequence);
236 	if(seq)
237 		return seq->GetSequence(type);
238 	return NullSequence;
239 }
240 
241 //------------------------------------------------------------------------------
242 
SndSeqPlayer(const SoundSequence & sequence,MapSpot Source)243 SndSeqPlayer::SndSeqPlayer(const SoundSequence &sequence, MapSpot Source) :
244 	Sequence(sequence), Source(Source), Delay(0), Playing(true), WaitForDone(false)
245 {
246 	Current = Sequence.Start();
247 	if(Current == NULL)
248 		Playing = false;
249 }
250 
~SndSeqPlayer()251 SndSeqPlayer::~SndSeqPlayer()
252 {
253 	if(Playing)
254 		Stop();
255 }
256 
257 // SD_SoundPlaying() seems to intentionally be for adlib/pc speaker only. At
258 // least it has been like that since the beginning of ECWolf.
259 extern FString SoundPlaying;
Tick()260 void SndSeqPlayer::Tick()
261 {
262 	if(!Playing || (Delay != 0 && --Delay > 0))
263 		return;
264 
265 	if(WaitForDone)
266 	{
267 		if(SoundPlaying.IsNotEmpty())
268 			return;
269 		else
270 			WaitForDone = false;
271 	}
272 
273 	do
274 	{
275 		if(Current->Instruction & SSI_PlaySound)
276 		{
277 			PlaySoundLocMapSpot(Current->Sound, Source);
278 		}
279 
280 		if(Current->Instruction & SSI_Delay)
281 		{
282 			Delay = Current->Argument + (Current->ArgumentRand ? (M_Random.GenRand32() % Current->ArgumentRand) : 0);
283 		}
284 
285 		if(Current->Instruction & SSI_End)
286 		{
287 			Playing = false;
288 		}
289 
290 		if(Current->Instruction & SSI_WaitForFinish)
291 		{
292 			WaitForDone = true;
293 			if(Delay == 0)
294 				Delay = 1;
295 		}
296 
297 		if(!(Current->Instruction & SSI_Repeat))
298 		{
299 			++Current;
300 		}
301 	}
302 	while(Delay == 0 && Playing);
303 }
304 
Stop()305 void SndSeqPlayer::Stop()
306 {
307 	Playing = false;
308 
309 	// Unfortunately due to limitations of the sound code we can't determine
310 	// what sound is playing much less stop the sound.
311 
312 	if(Sequence.GetStopSound() != NAME_None)
313 		PlaySoundLocMapSpot(Sequence.GetStopSound(), Source);
314 }
315 
operator <<(FArchive & arc,SndSeqPlayer * & seqplayer)316 FArchive &operator<< (FArchive &arc, SndSeqPlayer *&seqplayer)
317 {
318 	FName seqname;
319 	MapSpot source;
320 	unsigned int offs;
321 
322 	if(arc.IsStoring())
323 	{
324 		if(seqplayer == NULL)
325 		{
326 			// Can't do much here, so flag and move on.
327 			offs = UINT_MAX;
328 			arc << offs;
329 			return arc;
330 		}
331 
332 		seqname = seqplayer->Sequence.GetSeqName();
333 		source = seqplayer->Source;
334 		offs = static_cast<unsigned int>(seqplayer->Current - seqplayer->Sequence.Start());
335 	}
336 
337 	// First check that we actually stored a sequence player.
338 	arc << offs;
339 	if(arc.IsLoading() && offs == UINT_MAX)
340 	{
341 		delete seqplayer;
342 		seqplayer = NULL;
343 		return arc;
344 	}
345 
346 	arc << seqname << source;
347 
348 	if(arc.IsLoading())
349 	{
350 		// We don't need to worry about the sequence type here since seqname
351 		// should have already resolved to the proper sequence.
352 		delete seqplayer;
353 		seqplayer = new SndSeqPlayer(SoundSeq(seqname, SEQ_OpenNormal), source);
354 		seqplayer->Current += offs;
355 	}
356 
357 	arc	<< seqplayer->Delay
358 		<< seqplayer->Playing
359 		<< seqplayer->WaitForDone;
360 
361 	return arc;
362 }
363