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