1 /*
2  * CRRCsim - the Charles River Radio Control Club Flight Simulator Project
3  *
4  * Copyright (C) 2005, 2006, 2008, 2009 Jan Reucker (original author)
5  * Copyright (C) 2005, 2008 Jens Wilhelm Wulf
6  * Copyright (C) 2006 Todd Templeton
7  * Copyright (C) 2008 Jens Wilhelm Wulf
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2
11  * as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330,
21  * Boston, MA 02111-1307, USA.
22  *
23  */
24 
25 
26 /**
27  *  \file crrc_soundserver.cpp
28  *
29  *  The CRRCsim sound server and related functions.
30  *
31  *  Author: Jan Reucker
32  *  eMail:  slowhand_47@gmx.de
33  */
34 
35 #include "crrc_soundserver.h"
36 #include "crrc_main.h"
37 #include "mod_misc/lib_conversions.h"
38 
39 
40 // --- generic functions ----------------------------------
41 
42 /** \brief Print SDL_AudioSpec data
43  *
44  *  Prints the data contained in an SDL_AudioSpec in
45  *  a user-friendly format.
46  */
PrintFormat(const char * title,SDL_AudioSpec * fmt)47 void PrintFormat(const char *title, SDL_AudioSpec *fmt)
48 {
49   printf("%s: %d bit %s audio (%s) at %u Hz\n",
50               title,
51               (fmt->format&0xFF),
52               (fmt->format&0x8000) ? "signed" : "unsigned",
53               (fmt->channels > 2) ? "surround" :
54               (fmt->channels > 1) ? "stereo" : "mono",
55               fmt->freq);
56 }
57 
58 
59 /** \brief The sound callback
60  *
61  *  This callback routine is the low-level workhorse which
62  *  interfaces the sound server to SDL. It is called by the
63  *  SDL routines whenever the sound card accepts new input
64  *  data.
65  */
snd_callback(void * _unused,Uint8 * stream,int len)66 void snd_callback(void *_unused, Uint8 *stream, int len)
67 {
68   Uint32 samples;
69   CRRCAudioServer *server = CRRCAudioServer::getRunningInstance();
70 
71   if (!server->is_paused)
72   {
73     for (int i = 0; i < CRRC_AUDIO_CHANNELS; i++)
74     {
75       if (server->channel[i] != NULL && server->channel[i]->sample != NULL)
76       {
77         samples = len;
78         Uint8  *pos = server->channel[i]->sample->getMixableData(server->channel[i]->playpos, &samples);
79 
80         // end of sample reached?
81         if (samples == 0)
82         {
83           server->stopChannel(i);
84         }
85         else
86         {
87           SDL_MixAudio(stream, pos, samples, server->channel[i]->volume);
88           server->channel[i]->playpos += samples;
89         }
90       }
91     }
92   }
93 }
94 
95 
96 // --- Implementation of class CRRCAudioServer ------------
97 
98 CRRCAudioServer* CRRCAudioServer::instance = NULL;
99 
100 /** \brief Initialize the audio server.
101  *
102  *  \param config Pointer to the XML config file.
103  */
CRRCAudioServer(SimpleXMLTransfer * config)104 CRRCAudioServer::CRRCAudioServer(SimpleXMLTransfer *config)
105   : audio_spec(NULL), is_paused(true)
106 {
107   // Prepare config files
108   config->makeSureAttributeExists("sound.samplerate", "48000");
109 
110   SDL_AudioSpec *desired;
111   desired  = (SDL_AudioSpec*)malloc(sizeof(SDL_AudioSpec));
112   audio_spec = (SDL_AudioSpec*)malloc(sizeof(SDL_AudioSpec));
113 
114   desired->freq = config->getInt("sound.samplerate");
115   desired->format = AUDIO_S16SYS;
116   desired->channels = 1;
117   desired->callback = snd_callback;
118   desired->userdata = NULL;
119 
120   desired->samples = 2048;
121   if (desired->freq > 30000)
122   {
123     desired->samples *= 2;
124   }
125 
126   for (int i = 0; i < CRRC_AUDIO_CHANNELS; i++)
127   {
128     channel[i] = NULL;
129   }
130 
131   // try to open
132   if ( SDL_OpenAudio(desired, audio_spec) < 0 )
133   {
134     std::string s = "Couldn't open audiodevice: ";
135     s += SDL_GetError();
136     fprintf(stderr, "%s\n", s.c_str());
137     instance = NULL;
138     throw std::runtime_error(s);
139   }
140   else
141   {
142     PrintFormat("Opened audio device", audio_spec);
143     instance = this;
144   }
145   free(desired);
146 
147   double dModelVolume = config->getDouble("sound.model.vol", 1.0);
148   if (dModelVolume > 1.0)
149   {
150     dModelVolume = 1.0;
151     config->setAttributeOverwrite("sound.model.vol", "1.0");
152   }
153   ucModelVolume = dModelVolume * SDL_MIX_MAXVOLUME;
154 
155 }
156 
157 
158 
159 /** \brief Delete the sound server.
160  *
161  *  Stops the sound server and deletes all related data.
162  */
~CRRCAudioServer()163 CRRCAudioServer::~CRRCAudioServer()
164 {
165   SDL_PauseAudio(1);
166 
167   // free any allocated samples
168   for (int i = 0; i < CRRC_AUDIO_CHANNELS; i++)
169   {
170     if (channel[i] != NULL)
171     {
172       if (channel[i]->discard)
173       {
174         #if DEBUG_SOUND_SERVER > 0
175         printf("Discarding sample %s.\n", channel[i]->sample->getName().c_str());
176         #endif
177         delete channel[i]->sample;
178       }
179       delete channel[i];
180       channel[i] = NULL;
181     }
182   }
183   free(audio_spec);
184   SDL_CloseAudio();
185 }
186 
187 
188 /** \brief Store configurable values in config file
189  *
190  *  This method stores all configuration values in the
191  *  given config file.
192  *
193  *  \param config Pointer to the XML config file.
194  */
putBackIntoConfig(SimpleXMLTransfer * config) const195 void CRRCAudioServer::putBackIntoConfig(SimpleXMLTransfer *config) const
196 {
197   if (config != NULL)
198   {
199     float flModelVol = (float)ucModelVolume / (float)SDL_MIX_MAXVOLUME;
200     std::string s = ftoStr(flModelVol, 1, 3);
201     std::cout << "CRRCAudioServer::putBackIntoConfig: sound.model.vol == " << s << std::endl;
202     config->setAttributeOverwrite("sound.model.vol", s);
203   }
204 }
205 
206 
207 /** \brief Play a preloaded sample.
208  *
209  *  Adds the sample to the CRRCAudioServer. The sample will
210  *  be played at the given volume, or at the maximum volume
211  *  if this parameter is omitted.
212  *  If all channels of the server are busy, the method will
213  *  return -1, else it will return the number of the channel
214  *  which now plays the sample.
215  *
216  *  \param sample Pointer to the sample to be played.
217  *  \param volume playback volume
218  *  \return channel number or -1 on error
219  */
playSample(T_SoundSample * sample,unsigned int volume)220 int CRRCAudioServer::playSample(T_SoundSample *sample, unsigned int volume)
221 {
222   return addSample(sample, volume, false);
223 }
224 
225 
226 /** \brief Play a sample directly from a file.
227  *
228  *  Creates a temporary T_SoundSample object from a file,
229  *  adds the sample to the CRRCAudioServer and discards
230  *  the temporary T_SoundSample afterwards. The sample will
231  *  be played at the given volume, or at the maximum volume
232  *  if this parameter is omitted.
233  *  If all channels of the server are busy, the method will
234  *  return -1, else it will return the number of the channel
235  *  which now plays the sample.
236  *
237  *  Compared to playing a preloaded sample, this method may
238  *  introduce extra system load and a little sound delay,
239  *  because the sample might have to be converted to the
240  *  sound server's format.
241  *
242  *  \param filename Name of the .wav file to be played.
243  *  \param volume playback volume
244  *  \return channel number or -1 on error
245  */
playSample(const char * filename,unsigned int volume)246 int CRRCAudioServer::playSample(const char *filename, unsigned int volume)
247 {
248   int chan;
249   T_SoundSample *sample = NULL;
250 
251   try
252   {
253     sample = new T_SoundSample(filename, audio_spec);
254   }
255   catch (std::runtime_error& e)
256   {
257     fprintf(stderr, "%s\n", e.what());
258     return -1;
259   }
260 
261   chan = addSample(sample, volume, true);
262   if (chan < 0)
263   {
264     delete sample;
265   }
266   return chan;
267 }
268 
269 
270 /** \brief Add a sample to the server.
271  *
272  *  This handles the internal adding process and is called
273  *  from all flavours of playSample(). It returns the
274  *  number of the channel to which the sample was assigned
275  *  or -1 if there was no more free channel.
276  *  \param sample Pointer to the sample to be played.
277  *  \param volume playback volume
278  *  \param disc   Discard sample after playback?
279  *  \return channel number or -1 on error
280  */
addSample(T_SoundSample * sample,unsigned int volume,bool disc)281 int CRRCAudioServer::addSample(T_SoundSample *sample,
282                                 unsigned int volume,
283                                 bool disc)
284 {
285   int ret = -1;
286 
287   if ((sample->getFrequency() != audio_spec->freq)
288         ||
289       (sample->getFormat() != audio_spec->format)
290         ||
291       (sample->getNumChannels() != audio_spec->channels))
292   {
293     // sample does not match playback format, convert it
294     sample->convert(audio_spec);
295   }
296 
297   SDL_LockAudio();
298   for (int i = 0; i < CRRC_AUDIO_CHANNELS; i++)
299   {
300     if (channel[i] == NULL)
301     {
302       T_PlaybackContainer *pb = new T_PlaybackContainer;
303       pb->sample = sample;
304       pb->volume = volume;
305       pb->discard = disc;
306       pb->playpos = 0;
307       channel[i] = pb;
308       ret = i;
309       #if DEBUG_SOUND_SERVER > 0
310       printf("Added sample %s to channel %d.\n", sample->getName().c_str(), i);
311       #endif
312       break;
313     }
314   }
315   SDL_UnlockAudio();
316 
317   #if DEBUG_SOUND_SERVER > 0
318   if (ret < 0)
319   {
320     fprintf(stderr, "*** Unable to play sample %s: No free channels!\n", sample->getName().c_str());
321   }
322   #endif
323   return ret;
324 }
325 
326 
327 /** \brief Get the server's audio format.
328  *
329  *  This method returns a pointer to the server's SDL_AudioSpec.
330  */
getAudioSpec() const331 SDL_AudioSpec* CRRCAudioServer::getAudioSpec() const
332 {
333   return audio_spec;
334 }
335 
336 
337 /** \brief Stop playback on a channel.
338  *
339  *  This stops the sample playing on channel c. If the
340  *  sample was created by the server, it will automatically
341  *  be deleted.
342  *
343  *  \param c channel number
344  */
stopChannel(int c)345 void CRRCAudioServer::stopChannel(int c)
346 {
347   if ((c >= 0) && (c < CRRC_AUDIO_CHANNELS))
348   {
349     if (channel[c] != NULL)
350     {
351       #if DEBUG_SOUND_SERVER > 0
352       printf("Stopping sample %s on channel %d.\n", channel[c]->sample->getName().c_str(), c);
353       #endif
354       SDL_LockAudio();
355       if (channel[c]->discard)
356       {
357         #if DEBUG_SOUND_SERVER > 0
358         printf("Discarding sample %s.\n", channel[c]->sample->getName().c_str());
359         #endif
360         delete channel[c]->sample;
361 
362       }
363       delete channel[c];
364       channel[c] = NULL;
365       SDL_UnlockAudio();
366     }
367   }
368 }
369 
370 
371 /** \brief Stop playback an all channels.
372  *
373  *  This stops all samples currently playing. All
374  *  channels will be freed. All samples created by
375  *  the server will be deleted.
376  */
stopAllChannels()377 void CRRCAudioServer::stopAllChannels()
378 {
379   for (int c = 0; c < CRRC_AUDIO_CHANNELS; c++)
380   {
381     stopChannel(c);
382   }
383 }
384 
385 
386 /** \brief Set the volume of a channel.
387  *
388  *  Set a channel's playback volume to the given level. The
389  *  vol parameter will be clamped to SDL_MIX_MAXVOLUME, which
390  *  is usually defined as 128.
391  *
392  *  \param c channel number
393  *  \param vol desired volume (0 ... SDL_MIX_MAXVOLUME)
394  */
setChannelVolume(int c,unsigned char vol)395 void CRRCAudioServer::setChannelVolume(int c, unsigned char vol)
396 {
397   if ((c >= 0) && (c < CRRC_AUDIO_CHANNELS))
398   {
399     if (channel[c] != NULL)
400     {
401       if (vol > SDL_MIX_MAXVOLUME)
402       {
403         vol = SDL_MIX_MAXVOLUME;
404       }
405       channel[c]->volume = vol;
406     }
407   }
408 }
409 
410 
411 /**
412  *  Set the volume for all models.
413  *
414  *  \param vol desired volume (0...SDL_MIX_MAXVOLUME)
415  */
setModelVolume(unsigned char vol)416 void CRRCAudioServer::setModelVolume(unsigned char vol)
417 {
418   if (vol > SDL_MIX_MAXVOLUME)
419   {
420     vol = SDL_MIX_MAXVOLUME;
421   }
422   SDL_LockAudio();
423   ucModelVolume = vol;
424   SDL_UnlockAudio();
425 }
426 
427 
428 // --- Implementation of class T_SoundSample --------------
429 
430 /** \brief Create an empty sound sample.
431  *
432  *  This ctor is mainly useful for derived classes which
433  *  provide their own mechanism to create or read sound
434  *  data.
435  */
T_SoundSample(SDL_AudioSpec * fmt)436 T_SoundSample::T_SoundSample(SDL_AudioSpec *fmt)
437   : samplename(""), length(0), buffer(NULL)
438 {
439   spec.format = fmt->format;
440   spec.freq   = fmt->freq;
441   spec.channels = fmt->channels;
442 }
443 
444 
445 /** \brief Create a sound sample from a file.
446  *
447  * Creates a sound sample with the desired format from
448  * a file. A std::runtime_error will be thrown on error.
449  * This includes:
450  * - file does not exist
451  * - file format not recognized or invalid
452  * - conversion into desired format failed
453  *
454  * The conversion mechanism was shamelessly stolen from
455  * the SDL_mixer library. Many thanks to the original
456  * author(s).
457  *
458  * \param filename the file to be loaded
459  * \param fmt desired audio format
460  */
T_SoundSample(const char * filename,SDL_AudioSpec * fmt)461 T_SoundSample::T_SoundSample(const char *filename, SDL_AudioSpec *fmt)
462   : samplename(""), length(0), buffer(NULL)
463 {
464   SDL_AudioSpec *ret = SDL_LoadWAV(filename, &spec, &buffer, &length);
465   if (NULL == ret)
466   {
467     std::string s = "T_SoundSample: ";
468     s += SDL_GetError();
469     throw std::runtime_error(s);
470   }
471   #if DEBUG_SOUND_SERVER > 0
472   else
473   {
474     PrintFormat(filename, &spec);
475     printf("    (%u samples)\n", length);
476   }
477   #endif
478 
479   samplename    = filename;
480 
481   convert(fmt);
482 }
483 
484 
485 /** \brief Convert the sample to the given format.
486  *
487  *  Converts the sample to the audio format specified
488  *  by fmt. If the conversion fails, a std::runtime_error
489  *  exception might be thrown.
490  *
491  *  \param fmt The desired sample format.
492  */
convert(SDL_AudioSpec * fmt)493 void T_SoundSample::convert(SDL_AudioSpec *fmt)
494 {
495   SDL_AudioCVT  wavecvt;
496 
497   /* Build the audio converter and create conversion buffers */
498   if (SDL_BuildAudioCVT(&wavecvt,
499                         spec.format,
500                         spec.channels,
501                         spec.freq,
502                         fmt->format,
503                         fmt->channels,
504                         fmt->freq) < 0 )
505   {
506     SDL_FreeWAV(buffer);
507     std::string s = "Could not initialize converter: ";
508     s += SDL_GetError();
509     throw std::runtime_error(s);
510   }
511   wavecvt.len = length & ~(getSampleSize()-1);
512   wavecvt.buf = (Uint8 *)malloc(wavecvt.len * wavecvt.len_mult);
513   if ( wavecvt.buf == NULL )
514   {
515     SDL_FreeWAV(buffer);
516     std::string s = "Could not initialize converter: ";
517     s += SDL_GetError();
518     throw std::runtime_error(s);
519   }
520   memcpy(wavecvt.buf, buffer, length);
521   SDL_FreeWAV(buffer);
522 
523   /* Run the audio converter */
524   if (SDL_ConvertAudio(&wavecvt) < 0)
525   {
526     free(wavecvt.buf);
527     std::string s = "Conversion failed: ";
528     s += SDL_GetError();
529     throw std::runtime_error(s);
530   }
531 
532   /* conversion succeeded, fetch the results */
533   buffer = wavecvt.buf;
534   length = wavecvt.len_cvt;
535   spec.format   = fmt->format;
536   spec.channels = fmt->channels;
537   spec.freq     = fmt->freq;
538 
539   #if DEBUG_SOUND_SERVER > 0
540   printf("Converted ");
541   PrintFormat(samplename.c_str(), &spec);
542   printf("    (%u samples)\n", length);
543   #endif
544 }
545 
546 
547 /** \brief Destroy the sound sample.
548  *
549  * Deletes the sound sample object.
550  */
~T_SoundSample()551 T_SoundSample::~T_SoundSample()
552 {
553   SDL_FreeWAV(buffer);
554 }
555 
556 
557 /** \brief Get the length of the T_SoundSample
558  *
559  *  \return length of the sample
560  */
getLength() const561 Uint32 T_SoundSample::getLength() const
562 {
563   return length;
564 }
565 
566 
567 /** \brief Get a pointer to a chunk of data.
568  *
569  *  This method returns a pointer to the sample buffer,
570  *  pointing playCount samples behind the start of the
571  *  buffer. It tests if there are <code>length</code>
572  *  samples left. If there's less data, <code>length</code>
573  *  will be adjusted to the remaining number of samples.
574  *  \param  playCount  The current playback position.
575  *  \param  len        Requested data size, will be set to number of actually remaining samples.
576  *  \return A pointer to the <code>playCount</code>th sample.
577  */
getMixableData(Uint32 playCount,Uint32 * len)578 Uint8*  T_SoundSample::getMixableData(Uint32 playCount, Uint32 *len)
579 {
580   Uint32 left = length - playCount;
581   if (*len >= left)
582   {
583     *len = left;
584   }
585   return buffer + playCount;
586 }
587 
588 
589 /** \brief Get the number of bytes per sample.
590  *
591  *  This method returns the number of bytes per sample.
592  *  For example, a 16-bit stereo sample is
593  *  4 byte wide (16 bit = 2 byte per channel and two channels).
594  *  \return Size of a sample in bytes
595  */
getSampleSize()596 int T_SoundSample::getSampleSize()
597 {
598   return ((spec.format & 0xFF) >> 3) * spec.channels;
599 }
600 
601 
602 /** \brief Get the bit-depth of a sample.
603  *
604  *  The bit-depth represents the quality of the amplitude
605  *  values of a sample.
606  *  \return bit-depth of the sample
607  */
bits()608 int T_SoundSample::bits()
609 {
610   return (spec.format & 0xFF);
611 }
612 
613 
614 /** \brief Determine if a sample is in signed or unsigned format.
615  *
616  *  \return true if the sample is in signed format
617  */
isSigned()618 bool T_SoundSample::isSigned()
619 {
620   return ((spec.format & 0x8000) != 0);
621 }
622 
623 
624 // --- Implementation of class T_PitchVariableLoop --------
625 /** \brief Create a pitch variable sound loop.
626  *
627  *  Reads the sample from a file and sets up the dynamic
628  *  buffer.
629  */
T_PitchVariableLoop(const char * filename,SDL_AudioSpec * fmt)630 T_PitchVariableLoop::T_PitchVariableLoop(const char *filename, SDL_AudioSpec *fmt)
631   : T_SoundSample(filename, fmt), pitch(1.0), soundpos(0)
632 {
633   #if DEBUG_SOUND_SERVER > 0
634   printf("Reserving %d bytes dynamic sound sample buffer for %s.\n",
635           fmt->samples * getSampleSize(), filename);
636   #endif
637   dyn_buffer.reserve(fmt->samples * getSampleSize());
638 }
639 
640 
641 /** \brief Deletes the sample.
642  *
643  *
644  */
~T_PitchVariableLoop()645 T_PitchVariableLoop::~T_PitchVariableLoop()
646 {
647 }
648 
649 
650 /** \brief Get a pointer to a chunk of data.
651  *
652  *  This method returns a pointer to the dynamic sample buffer,
653  *  filling the dynamic buffer with interpolated sample
654  *  values based on the current pitch setting. If more data
655  *  is requested than the buffer can hold, the buffer will
656  *  be reallocated. The value of len will therefore never
657  *  change, and the sound will loop forever.
658  *
659  *  The method uses two kinds of arithmetic: integer-arithmetic,
660  *  where all values are shifted left by the EIS constant to get
661  *  (2 to the power of EIS) intermediate steps between two int
662  *  values for better interpolation precision, and sample-arithmetic,
663  *  where one integer step corresponds to one sample. Be careful
664  *  not to perform any calculations with operands of different
665  *  arithmetic without proper conversion!
666  *
667  *  \todo Test stereo processing with a real stereo test sound.
668  *
669  *  \param  playpos   The current playback position.
670  *  \param  len       Requested data size.
671  *  \return A pointer to the dynamic sample buffer.
672  */
getMixableData(Uint32 playpos,Uint32 * len)673 Uint8* T_PitchVariableLoop::getMixableData(Uint32 playpos, Uint32 *len)
674 {
675   if (dyn_buffer.capacity() < *len)
676   {
677     #if DEBUG_SOUND_SERVER > 0
678     printf("Reallocating dynamic sound sample buffer for %s (%d --> %d).\n",
679             getName().c_str(), dyn_buffer.capacity(), *len);
680     #endif
681     dyn_buffer.reserve(*len);
682   }
683 
684 #if CRRC_SOUND_STEREO == 0
685   // 16-bit mono samples, so we have to work through len/2 samples
686   int     nSamplesToCopy = *len/2;
687   Uint32  uiSoundlen = getLength()/2 << EIS;  // length in integer-arithmetic, local copy for fast access;
688   Uint32  uiSoundlenSamples = getLength()/2;  // length in sample-arithmetic
689 #else
690   // 16-bit stereo samples, so we have to work through len/4 samples
691   int     nSamplesToCopy = *len/4;
692   Uint32  uiSoundlen = getLength()/4 << EIS;  // length in integer-arithmetic, local copy for fast access;
693   Uint32  uiSoundlenSamples = getLength()/4;  // length in sample-arithmetic
694 #endif
695 
696   Sint16  *writeptr   = (Sint16*)&dyn_buffer[0];
697   Uint32  uiSoundpos  = soundpos;         // position in integer-arithmetic (<< EIS), local copy for fast access
698   Sint16* sndptr      = (Sint16*)buffer;  // local copy for fast access
699   Uint32  uiPitch     = (Uint32)( (1<<EIS) * pitch); // pitch in integer-arithmetic
700   while (nSamplesToCopy--)
701   {
702     uiSoundpos += uiPitch;
703     while (uiSoundpos >= uiSoundlen)
704     {
705       uiSoundpos -= uiSoundlen;
706     }
707 
708     // linear interpolation in integer arithmetic
709     int    diff;
710     Sint32 out_l;
711     Uint32 pos1    = (uiSoundpos >> EIS);   // position, in sample-arithmetic (1 = one sample)
712 
713 #if CRRC_SOUND_STEREO == 0
714     Sint32 sample_l1 = *(sndptr + pos1);
715 #else
716     Sint16 *psample = sndptr + 2 * pos1;
717     Sint32 sample_l1  = *(psample);
718     Sint32 sample_r1  = *(psample + 1);
719     Sint32 sample_r2;
720     Sint32 out_r;
721 #endif
722     Sint32 sample_l2;
723 
724     if (++pos1 >= uiSoundlenSamples)
725       pos1 = 0;
726 
727 #if CRRC_SOUND_STEREO == 0
728     sample_l2 = *(sndptr + pos1);
729 #else
730     psample = sndptr + 2 * pos1;
731     sample_l2 = *(psample);
732     sample_r2 = *(psample + 1);
733 #endif
734 
735     diff    = uiSoundpos & ((1 << EIS) - 1);
736     diff = (((sample_l2 - sample_l1)*diff) >> EIS);
737     out_l = sample_l1 + diff;
738 
739     // Limit to 16 bit samples
740     if (out_l > 32767)
741       out_l = 32767;
742     else if (out_l < -32767)
743       out_l = -32767;
744 
745     *writeptr++ = out_l;
746 
747 #if CRRC_SOUND_STEREO == 1
748     diff    = uiSoundpos & ((1 << EIS) - 1);
749     diff = (((sample_r2 - sample_r1)*diff) >> EIS);
750     out_r = sample_r1 + diff;
751 
752     // Limit to 16 bit samples
753     if (out_r > 32767)
754       out_r = 32767;
755     else if (out_r < -32767)
756       out_r = -32767;
757 
758     *writeptr++ = out_r;
759 #endif
760   }
761   soundpos = uiSoundpos;    // write back the locally changed value
762   return &dyn_buffer[0];
763 }
764 
765 
766 /** \brief Set the pitch value for the sound loop.
767  *
768  *  This method controls the sample's pitch. A value
769  *  of 1.0 will play the sample at the original pitch.
770  *  Pitch values are automatically clamped to positive
771  *  non-zero values.
772  */
setPitch(float p)773 void T_PitchVariableLoop::setPitch(float p)
774 {
775   SDL_LockAudio();
776   if (p < 0.0001)
777   {
778     pitch = 0.0001;
779   }
780   else
781   {
782     pitch = p;
783   }
784   SDL_UnlockAudio();
785 }
786 
787 
788 // --- A test routine for the sound server ----------------
789 
790 #ifdef STANDALONE_SOUND_TEST
791 CRRCAudioServer *soundserver;
792 #endif
793 
794 #ifdef STANDALONE_SOUND_TEST
main(int argc,char * argv[])795 int main(int argc, char *argv[])
796 {
797   SimpleXMLTransfer *cfgfile = new SimpleXMLTransfer("crrcsim.xml");
798 
799   printf("\n------------------------------------------\n");
800   printf("Standalone test for the CRRCsim sound code\n");
801   printf("------------------------------------------\n\n");
802 
803   /* Load the SDL library */
804   printf("Initializing SDL...\n");
805   if ( SDL_Init(SDL_INIT_AUDIO) < 0 )
806   {
807     fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
808     exit(1);
809   }
810 
811   printf("Initializing sound server...\n");
812   try
813   {
814     soundserver = new CRRCAudioServer(cfgfile);
815   }
816   catch (std::runtime_error& e)
817   {
818     fprintf(stderr, "%s\n", e.what());
819     exit(0);
820   }
821   soundserver->pause(false);
822 
823   // play directly from file
824   printf("Playing fan.wav directly from file...\n");
825   soundserver->playSample("sounds/fan.wav");
826 
827   SDL_Delay(1000);
828   T_PitchVariableLoop engine("sounds/electric.wav", soundserver->getAudioSpec());
829   int engine_channel = soundserver->playSample((T_SoundSample*)&engine);
830   printf("Ramping up pitch...\n");
831   for (int i = 5; i < 130; i++)
832   {
833     float f = (float)i/40.0;
834     engine.setPitch(f);
835     SDL_Delay(25);
836   }
837   printf("Ramping down pitch...\n");
838   for (int i = 130; i > 5; i--)
839   {
840     float f = (float)i/100.0;
841     engine.setPitch(f);
842     SDL_Delay(25);
843   }
844   engine.setPitch(1.0);
845   SDL_Delay(1000);
846 
847   printf("Stopping sound loop...\n");
848   soundserver->stopChannel(engine_channel);
849 
850   printf("Waiting...\n");
851   SDL_Delay(2000);
852 
853   printf("Deleting sound server...\n");
854   soundserver->pause(true);
855   delete soundserver;
856 
857   printf("Shutting down SDL...\n");
858   SDL_Quit();
859   printf("Finished!\n");
860   return 0;
861 }
862 #endif
863 
864 
865 
866 
867