Vegastrike 0.5.1 rc1  1.0
Original sources for Vegastrike Evolved
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
softvolume.cpp
Go to the documentation of this file.
1 #if !defined (SDL_MIX_MAXVOLUME)
2 #define SDL_MIX_MAXVOLUME 128
3 #endif
4 #ifdef HAVE_SDL
5 #include <SDL/SDL.h>
6 #include <SDL/SDL_thread.h>
7 #include <SDL/SDL_mixer.h>
8 #else
9 typedef int Mix_Music;
10 #endif
11 
12 //Catch SDL 1.2.10's new 64-bit macros
13 #if ( ( SDL_MAJOR_VERSION > 1) || (SDL_MINOR_VERSION > 2) || (SDL_PATCHLEVEL >= 10 ) )
14  #if (defined (SDL_HAS_64BIT_TYPE) && (SDL_HAS_64BIT_TYPE != 0 ) )
15  #define SDL_INT64 int64_t
16  #define SDL_UINT64 uint64_t
17  #else
18  #define SDL_INT64 __undefined_64bit_type__
19  #define SDL_UINT64 __undefined_64bit_type__
20  #endif
21 #else
22  #if (defined (SDL_HAS_64BIT_TYPE ) )
23  #define SDL_INT64 signed SDL_HAS_64BIT_TYPE
24  #define SDL_UINT64 unsigned SDL_HAS_64BIT_TYPE
25  #else
26  #define SDL_INT64 __undefined_64bit_type__
27  #define SDL_UINT64 __undefined_64bit_type__
28  #endif
29 #endif
30 
31 #include <stdlib.h>
32 #include <map>
33 #include <memory.h>
34 #include <stdio.h>
35 #include <math.h>
36 #if defined (_WIN32) || __GNUC__ != 2
37 #include <limits>
38 #endif
39 
40 #include "softvolume.h"
41 #if defined (_WIN32) || __GNUC__ != 2
42 #define UC8_MIN ( (unsigned long) std::numeric_limits< unsigned char >::min() )
43 #define UC8_MAX ( (unsigned long) std::numeric_limits< unsigned char >::max() )
44 #define SC8_MIN ( (signed long) std::numeric_limits< signed char >::min() )
45 #define SC8_MAX ( (signed long) std::numeric_limits< signed char >::max() )
46 #define US16_MIN ( (unsigned long) std::numeric_limits< unsigned short >::min() )
47 #define US16_MAX ( (unsigned long) std::numeric_limits< unsigned short >::max() )
48 #define SS16_MIN ( (signed long) std::numeric_limits< signed short >::min() )
49 #define SS16_MAX ( (signed long) std::numeric_limits< signed short >::max() )
50 #else
51 #define UC8_MIN ( (unsigned long) 0 )
52 #define UC8_MAX ( (unsigned long) 255 )
53 #define SC8_MIN ( (signed long) -128 )
54 #define SC8_MAX ( (signed long) 127 )
55 #define US16_MIN ( (unsigned long) 0 )
56 #define US16_MAX ( (unsigned long) 16384 )
57 #define SS16_MIN ( (signed long) -32768 )
58 #define SS16_MAX ( (signed long) 32767 )
59 #endif
60 //-144db is, for all practical purposes, -inf
61 #define DB_INF -144.0
62 
63 #if defined (_WIN32) && defined (_WINDOWS)
64 FILE *anotherstdout = stdout;
65 #define STD_ERR anotherstdout
66 #define STD_OUT anotherstdout
67 #else
68 #define STD_ERR stderr
69 #define STD_OUT stdout
70 #endif
71 
72 #define SHAPE_SAMPLE_ORDER 10
73 #define SHAPE_SAMPLES (1<<SHAPE_SAMPLE_ORDER)
74 #define SHAPE_SAMPLE_SAFEPOS( p ) ( (p)&( (1<<SHAPE_SAMPLE_ORDER)-1 ) )
75 
76 #define RNG_COUNT (1<<16)
77 #define RNG_MASK ( (1<<16)-1 )
78 
79 template < typename T >
80 T min( const T a, const T b )
81 {
82  if (a < b) return a;
83 
84  else return b;
85 }
86 
87 template < typename T >
88 T max( const T a, const T b )
89 {
90  if (a > b) return a;
91 
92  else return b;
93 }
94 
95 class t_rng16
96 {
97 public: t_rng16() : pos( 0 )
98  {
99  for (int i = 0; i < RNG_COUNT; i++)
100  _data[i] = (rand()&0xFFFF)-0x8000;
101  } //NOTE: It would be a good idea to perform a highpass - perhaps later
102 
103  signed long get()
104  {
105  return _data[pos];
106 
107  pos = (pos+1)&RNG_MASK;
108  }
109 
110 private:
111  signed long _data[RNG_COUNT]; //wastes memory, but improves performance
112  int pos;
113 }
114 rng16;
115 
116 #if (defined (SDL_HAS_64BIT_TYPE) && !defined (USE_FAST_F16MATH ) )
117 
118 inline unsigned long interpolateF16F16( unsigned long a, unsigned long b, unsigned long t )
119 {
120  return (unsigned long) ( (a*(SDL_INT64) (0x10000-t)+b*(SDL_INT64) t)>>16 );
121 }
122 
123 #else
124 
125 #if !defined (USE_FAST_F16MATH)
126 #pragma message ( "64-bit type unavailable - softvolume will have lower quality" )
127 #endif
128 
129 inline unsigned long interpolateF16F16( unsigned long a, unsigned long b, unsigned long t )
130 {
131  return (a>>8)*( (0x10000-t)>>8 )+(b>>8)*(t>>8);
132 }
133 
134 #endif
135 
137 {
138 public: shape_sampler() : pos( 0 )
139  , step( 0 ) {}
140 
141  void init( unsigned long total_samples )
142  {
143  if (total_samples == 0) total_samples = 1;
144  this->step = (SHAPE_SAMPLES<<16)/total_samples;
145  this->pos = 0;
146  }
147  void close()
148  {
149  this->pos = this->step = 0;
150  }
151 
152  void operator++()
153  {
154  this->pos += this->step;
155  }
156  int done()
157  {
158  return ( (this->pos>>16)&~( (1<<SHAPE_SAMPLE_ORDER)-1 ) ) != 0;
159  }
160  unsigned long operator*()
161  {
163  this->pos>>16 )+1], this->pos&0xFFFF );
164  }
165  unsigned long finalsample() const
166  {
167  return shape[SHAPE_SAMPLES];
168  }
169 
170 private:
171  unsigned long pos; //16.16 fixed point
172  unsigned long step; //16.16 fixed point
173 
174 public:
175  unsigned long shape[SHAPE_SAMPLES+1];
176 };
177 
178 inline unsigned long dtof16( double f )
179 {
180  return (unsigned long) max( 0.0f, min( (float) 0xFFFFFFFF, (float) (f*0x10000) ) );
181 }
182 inline double f16tod( unsigned long f16 )
183 {
184  return f16/(double) 0x10000;
185 }
186 
187 #define LOG_10 2.3025850929940456840179914546844
188 #define INV_LOG_10 (1.0/LOG_10)
189 #define C_PI 3.1415926535897932384626433832795
190 
191 inline double log2linear( double lg )
192 {
193  return exp( lg*0.05*LOG_10 );
194 }
195 inline double linear2log( double lin )
196 {
197  return (lin <= 0) ? DB_INF : (20*log( lin )*INV_LOG_10);
198 }
199 
200 #if (defined (SDL_HAS_64BIT_TYPE) && !defined (USE_FAST_F16MATH ) )
201 
202 inline unsigned short mpyUS16F16( unsigned short a, unsigned long f16 )
203 {
204  return (unsigned short) min( (SDL_UINT64) US16_MAX,
206  (SDL_UINT64) ( ( ( ( ( ( (SDL_INT64) a )-0x8000 )*f16 )+rng16.get() )>>16 )+0x8000 ) ) );
207 }
208 
209 inline signed short mpySS16F16( signed short a, unsigned long f16 )
210 {
211  return (signed short) min( (SDL_INT64) SS16_MAX,
212  max( (SDL_INT64) SS16_MIN, ( (SDL_INT64) ( ( ( (SDL_INT64) a ) )*f16 )+rng16.get() )/(1<<16) ) );
213 }
214 
215 #else
216 
217 #if !defined (USE_FAST_F16MATH)
218 #pragma message ( "64-bit type unavailable - softvolume will have lower quality" )
219 #endif
220 
221 inline unsigned short mpyUS16F16( unsigned short a, unsigned long f16 )
222 {
223  return (unsigned short) min( US16_MAX,
224  max( US16_MIN,
225  (unsigned long) ( ( ( ( ( ( (signed long) a )
226  -0x8000 )*(f16>>8) )+rng16.get() )>>8 )+0x8000 ) ) );
227 }
228 
229 inline signed short mpySS16F16( signed short a, unsigned long f16 )
230 {
231  return (signed short) min( SS16_MAX,
232  max( SS16_MIN, ( (signed long) ( ( ( (signed long) a )*(f16>>8) )+rng16.get() )/(1<<8) ) ) );
233 }
234 
235 #endif
236 
237 inline unsigned char mpyUC8F16( unsigned char a, unsigned long f16 )
238 {
239  return (unsigned char) min( UC8_MAX,
240  max( UC8_MIN,
241  (unsigned long) ( ( ( ( ( ( (signed long) a )-0x80 )*f16 )+rng16.get() )>>16 )+0x80 ) ) );
242 }
243 
244 inline signed char mpySC8F16( signed char a, unsigned long f16 )
245 {
246  return (signed char) min( SC8_MAX, max( SC8_MIN, ( (signed long) ( ( ( (signed long) a )*f16 )+rng16.get() )/(1<<16) ) ) );
247 }
248 
249 #if (SDL_BYTEORDER == SDL_LIL_ENDIAN)
250 inline signed short big_endian_to_native( signed short x )
251 {
252  return (x>>8)|(x<<8);
253 }
254 inline unsigned short big_endian_to_native( unsigned short x )
255 {
256  return (x>>8)|(x<<8);
257 }
258 #define lil_endian_to_native( x ) x
259 #define native_to_big_endian( x ) big_endian_to_native( x )
260 #define native_to_lil_endian( x ) lil_endian_to_native( x )
261 #else
262 inline signed short lil_endian_to_native( signed short x )
263 {
264  return (x>>8)|(x<<8);
265 }
266 inline unsigned short lil_endian_to_native( unsigned short x )
267 {
268  return (x>>8)|(x<<8);
269 }
270 #define big_endian_to_native( x ) x
271 #define native_to_big_endian( x ) big_endian_to_native( x )
272 #define native_to_lil_endian( x ) lil_endian_to_native( x )
273 #endif
274 
275 typedef struct t_SoftVolume_State
276 {
278  , in_transition( 0 )
279  , autoStopMusic( 0 ) {}
280 
281  unsigned long current_volume; //16.16 fixed point
282 
283  int autoStopMusic; //nonzero: stop when volume reaches 0
284 
285  int in_transition; //nonzero: transition in progress, otherwise, stable at current_volume
288 
289 std::map< int, SoftVolume_State >channel_state;
293 int g_sdl_init = 0;
294 Mix_EffectFunc_t g_sdl_effect_func = 0;
295 
296 void sdl_softvolume_effect_U8( int chan, void *stream, int len, void* )
297 {
298  SoftVolume_State &cstat = channel_state[chan];
299  unsigned char *buf = (unsigned char*) stream;
300  if (cstat.in_transition) {
301  while ( !cstat.t_shape.done() && (len > 0) ) {
302  for ( int i = g_sdl_channels; i > 0; i--, buf++, len -= sizeof (*buf) )
303  *buf = mpyUC8F16( *buf, *cstat.t_shape );
304  ++cstat.t_shape;
305  }
306  cstat.current_volume = (cstat.t_shape.done() ? cstat.t_shape.finalsample() : *cstat.t_shape);
307  cstat.in_transition = !cstat.t_shape.done();
308  }
309  if (cstat.current_volume != 0x10000) {
310  while (len > 0) {
311  *buf = mpyUC8F16( *buf, cstat.current_volume );
312  buf++, len -= sizeof (*buf);
313  }
314  }
315  if ( (cstat.current_volume == 0) && cstat.autoStopMusic )
316  Mix_HaltMusic();
317 }
318 void sdl_softvolume_effect_S8( int chan, void *stream, int len, void* )
319 {
320  SoftVolume_State &cstat = channel_state[chan];
321  signed char *buf = (signed char*) stream;
322  if (cstat.in_transition) {
323  while ( !cstat.t_shape.done() && (len > 0) ) {
324  for ( int i = g_sdl_channels; i > 0; i--, buf++, len -= sizeof (*buf) )
325  *buf = mpySC8F16( *buf, *cstat.t_shape );
326  ++cstat.t_shape;
327  }
328  cstat.current_volume = (cstat.t_shape.done() ? cstat.t_shape.finalsample() : *cstat.t_shape);
329  cstat.in_transition = !cstat.t_shape.done();
330  }
331  if (cstat.current_volume == 0) {
332  memset( buf, 0, len );
333  } else if (cstat.current_volume != 0x10000) {
334  while (len > 0) {
335  *buf = mpySC8F16( *buf, cstat.current_volume );
336  buf++, len -= sizeof (*buf);
337  }
338  }
339  if ( (cstat.current_volume == 0) && cstat.autoStopMusic )
340  Mix_HaltMusic();
341 }
342 void sdl_softvolume_effect_U16LSB( int chan, void *stream, int len, void* )
343 {
344  SoftVolume_State &cstat = channel_state[chan];
345  unsigned short *buf = (unsigned short*) stream;
346  if (cstat.in_transition) {
347  while ( !cstat.t_shape.done() && (len > 0) ) {
348  for ( int i = g_sdl_channels; i > 0; i--, buf++, len -= sizeof (*buf) )
349  *buf = native_to_lil_endian( mpyUS16F16( lil_endian_to_native( *buf ), *cstat.t_shape ) );
350  ++cstat.t_shape;
351  }
352  cstat.current_volume = (cstat.t_shape.done() ? cstat.t_shape.finalsample() : *cstat.t_shape);
353  cstat.in_transition = !cstat.t_shape.done();
354  }
355  if (cstat.current_volume == 0) {
356  unsigned short ct = native_to_lil_endian( (unsigned short) 0x8000 );
357  while (len > 0)
358  *(buf++) = ct, len -= sizeof (*buf);
359  } else if (cstat.current_volume != 0x10000) {
360  while (len > 0) {
362  buf++, len -= sizeof (*buf);
363  }
364  }
365  if ( (cstat.current_volume == 0) && cstat.autoStopMusic )
366  Mix_HaltMusic();
367 }
368 
369 void sdl_softvolume_effect_S16LSB( int chan, void *stream, int len, void* )
370 {
371  SoftVolume_State &cstat = channel_state[chan];
372  signed short *buf = (signed short*) stream;
373  if (cstat.in_transition) {
374  while ( !cstat.t_shape.done() && (len > 0) ) {
375  for ( int i = g_sdl_channels; i > 0; i--, buf++, len -= sizeof (*buf) )
376  *buf = native_to_lil_endian( mpySS16F16( lil_endian_to_native( *buf ), *cstat.t_shape ) );
377  ++cstat.t_shape;
378  }
379  cstat.current_volume = (cstat.t_shape.done() ? cstat.t_shape.finalsample() : *cstat.t_shape);
380  cstat.in_transition = !cstat.t_shape.done();
381  }
382  if (cstat.current_volume == 0) {
383  memset( buf, 0, len );
384  } else if (cstat.current_volume != 0x10000) {
385  while (len > 0) {
387  buf++, len -= sizeof (*buf);
388  }
389  }
390  if ( (cstat.current_volume == 0) && cstat.autoStopMusic )
391  Mix_HaltMusic();
392 }
393 void sdl_softvolume_effect_U16MSB( int chan, void *stream, int len, void* )
394 {
395  SoftVolume_State &cstat = channel_state[chan];
396  unsigned short *buf = (unsigned short*) stream;
397  if (cstat.in_transition) {
398  while ( !cstat.t_shape.done() && (len > 0) ) {
399  for ( int i = g_sdl_channels; i > 0; i--, buf++, len -= sizeof (*buf) )
400  *buf = native_to_big_endian( mpyUS16F16( big_endian_to_native( *buf ), *cstat.t_shape ) );
401  ++cstat.t_shape;
402  }
403  cstat.current_volume = (cstat.t_shape.done() ? cstat.t_shape.finalsample() : *cstat.t_shape);
404  cstat.in_transition = !cstat.t_shape.done();
405  }
406  if (cstat.current_volume == 0) {
407  unsigned short ct = native_to_big_endian( (unsigned short) 0x8000 );
408  while (len > 0)
409  *(buf++) = ct, len -= sizeof (*buf);
410  } else if (cstat.current_volume != 0x10000) {
411  while (len > 0) {
413  buf++, len -= sizeof (*buf);
414  }
415  }
416  if ( (cstat.current_volume == 0) && cstat.autoStopMusic )
417  Mix_HaltMusic();
418 }
419 void sdl_softvolume_effect_S16MSB( int chan, void *stream, int len, void* )
420 {
421  SoftVolume_State &cstat = channel_state[chan];
422  signed short *buf = (signed short*) stream;
423  if (cstat.in_transition) {
424  while ( !cstat.t_shape.done() && (len > 0) ) {
425  for ( int i = g_sdl_channels; i > 0; i--, buf++, len -= sizeof (*buf) )
426  *buf = native_to_big_endian( mpySS16F16( big_endian_to_native( *buf ), *cstat.t_shape ) );
427  ++cstat.t_shape;
428  }
429  cstat.current_volume = (cstat.t_shape.done() ? cstat.t_shape.finalsample() : *cstat.t_shape);
430  cstat.in_transition = !cstat.t_shape.done();
431  }
432  if (cstat.current_volume == 0) {
433  memset( buf, 0, len );
434  } else if (cstat.current_volume != 0x10000) {
435  while (len > 0) {
437  buf++, len -= sizeof (*buf);
438  }
439  }
440  if ( (cstat.current_volume == 0) && cstat.autoStopMusic )
441  Mix_HaltMusic();
442 }
443 
445 {
446  //Query device format
447  if (!g_sdl_init) {
448  Mix_QuerySpec( &g_sdl_frequency, &g_sdl_format, &g_sdl_channels );
449  switch (g_sdl_format)
450  {
451  case AUDIO_U8:
452  g_sdl_effect_func = &sdl_softvolume_effect_U8;
453  break;
454  case AUDIO_S8:
455  g_sdl_effect_func = &sdl_softvolume_effect_S8;
456  break;
457  case AUDIO_U16LSB:
458  g_sdl_effect_func = &sdl_softvolume_effect_U16LSB;
459  break;
460  case AUDIO_S16LSB:
461  g_sdl_effect_func = &sdl_softvolume_effect_S16LSB;
462  break;
463  case AUDIO_U16MSB:
464  g_sdl_effect_func = &sdl_softvolume_effect_U16MSB;
465  break;
466  case AUDIO_S16MSB:
467  g_sdl_effect_func = &sdl_softvolume_effect_S16MSB;
468  break;
469  }
470  g_sdl_init = 1;
471  }
472 }
473 
474 void Mix_SoftVolume_Change( int chan, double newvolume, double time, Mix_SoftVolume_Shape shape )
475 {
477 
478  //Setup transition record
479  {
480  SDL_LockAudio();
481 
482  SoftVolume_State &cstat = channel_state[chan];
483 
484  double cur_volume = f16tod( cstat.current_volume );
485  double new_volume = newvolume;
486 
487  int i;
488  cstat.in_transition = true;
489  switch (shape)
490  {
491  case MIX_SV_SHAPE_LINEAR:
492 mix_sv_shape_linear:
493  for (i = 0; i <= SHAPE_SAMPLES; i++)
494  cstat.t_shape.shape[i] = dtof16( cur_volume+(new_volume-cur_volume)*( i*(1.0/SHAPE_SAMPLES) ) );
495  break;
496  case MIX_SV_SHAPE_EXP:
497  cur_volume = linear2log( cur_volume );
498  new_volume = linear2log( new_volume );
499  for (i = 0; i <= SHAPE_SAMPLES; i++)
500  cstat.t_shape.shape[i] = dtof16( log2linear( cur_volume+(new_volume-cur_volume)*( i*(1.0/SHAPE_SAMPLES) ) ) );
501  break;
502  case MIX_SV_SHAPE_EASED:
503  for (i = 0; i <= SHAPE_SAMPLES; i++)
504  cstat.t_shape.shape[i] =
505  dtof16( cur_volume+(new_volume-cur_volume)*( 0.5-0.5*cos( i*(1.0/SHAPE_SAMPLES)*C_PI ) ) );
506  break;
508  cur_volume = linear2log( cur_volume );
509  new_volume = linear2log( new_volume );
510  for (i = 0; i <= SHAPE_SAMPLES; i++)
511  cstat.t_shape.shape[i] =
512  dtof16( log2linear( cur_volume+(new_volume-cur_volume)*( 0.5-0.5*cos( i*(1.0/SHAPE_SAMPLES)*C_PI ) ) ) );
513  break;
514  default:
515  fprintf( STD_ERR, "WARNING(softvolume.cpp): unrecognized transition shape, using default (linear)\n" );
516  goto mix_sv_shape_linear;
517  break;
518  }
519  cstat.t_shape.init( (unsigned long) (time*g_sdl_frequency) );
520  cstat.autoStopMusic = 0;
521 
522  SDL_UnlockAudio();
523  }
524 
525  //Register effect
526  Mix_UnregisterEffect( chan, g_sdl_effect_func ); //Is it necessary?
527  Mix_RegisterEffect( chan, g_sdl_effect_func, 0, 0 );
528 }
529 
530 void Mix_SoftVolume_Force( int chan, double newvolume )
531 {
533 
534  SDL_LockAudio();
535 
536  SoftVolume_State &cstat = channel_state[chan];
537  cstat.current_volume = dtof16( newvolume );
538  cstat.in_transition = false;
539 
540  SDL_UnlockAudio();
541 }
542 
544 {
545  double res;
546 
548 
549  SDL_LockAudio();
550  if (channel_state.count( chan ) > 0)
551  res = f16tod( channel_state[chan].current_volume );
552 
553  else
554  res = 1;
555  SDL_UnlockAudio();
556 
557  return res;
558 }
559 
560 void Mix_SoftVolume_AutoStopMusic( int chan, int enable )
561 {
563 
564  SDL_LockAudio();
565  if (channel_state.count( chan ) > 0)
566  channel_state[chan].autoStopMusic = enable;
567  SDL_UnlockAudio();
568 }
569