OpenTTD Source  1.11.0-beta2
mixer.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
10 #include "stdafx.h"
11 #include <math.h>
12 #include "core/math_func.hpp"
13 #include "framerate_type.h"
14 
15 #include "safeguards.h"
16 #include "mixer.h"
17 
18 struct MixerChannel {
19  bool active;
20 
21  /* pointer to allocated buffer memory */
22  int8 *memory;
23 
24  /* current position in memory */
25  uint32 pos;
26  uint32 frac_pos;
27  uint32 frac_speed;
28  uint32 samples_left;
29 
30  /* Mixing volume */
31  int volume_left;
32  int volume_right;
33 
34  bool is16bit;
35 };
36 
37 static MixerChannel _channels[8];
38 static uint32 _play_rate = 11025;
39 static uint32 _max_size = UINT_MAX;
40 static MxStreamCallback _music_stream = nullptr;
41 
48 static const int MAX_VOLUME = 128 * 128;
49 
57 template <typename T>
58 static int RateConversion(T *b, int frac_pos)
59 {
60  return ((b[0] * ((1 << 16) - frac_pos)) + (b[1] * frac_pos)) >> 16;
61 }
62 
63 static void mix_int16(MixerChannel *sc, int16 *buffer, uint samples)
64 {
65  if (samples > sc->samples_left) samples = sc->samples_left;
66  sc->samples_left -= samples;
67  assert(samples > 0);
68 
69  const int16 *b = (const int16 *)sc->memory + sc->pos;
70  uint32 frac_pos = sc->frac_pos;
71  uint32 frac_speed = sc->frac_speed;
72  int volume_left = sc->volume_left;
73  int volume_right = sc->volume_right;
74 
75  if (frac_speed == 0x10000) {
76  /* Special case when frac_speed is 0x10000 */
77  do {
78  buffer[0] = Clamp(buffer[0] + (*b * volume_left >> 16), -MAX_VOLUME, MAX_VOLUME);
79  buffer[1] = Clamp(buffer[1] + (*b * volume_right >> 16), -MAX_VOLUME, MAX_VOLUME);
80  b++;
81  buffer += 2;
82  } while (--samples > 0);
83  } else {
84  do {
85  int data = RateConversion(b, frac_pos);
86  buffer[0] = Clamp(buffer[0] + (data * volume_left >> 16), -MAX_VOLUME, MAX_VOLUME);
87  buffer[1] = Clamp(buffer[1] + (data * volume_right >> 16), -MAX_VOLUME, MAX_VOLUME);
88  buffer += 2;
89  frac_pos += frac_speed;
90  b += frac_pos >> 16;
91  frac_pos &= 0xffff;
92  } while (--samples > 0);
93  }
94 
95  sc->frac_pos = frac_pos;
96  sc->pos = b - (const int16 *)sc->memory;
97 }
98 
99 static void mix_int8_to_int16(MixerChannel *sc, int16 *buffer, uint samples)
100 {
101  if (samples > sc->samples_left) samples = sc->samples_left;
102  sc->samples_left -= samples;
103  assert(samples > 0);
104 
105  const int8 *b = sc->memory + sc->pos;
106  uint32 frac_pos = sc->frac_pos;
107  uint32 frac_speed = sc->frac_speed;
108  int volume_left = sc->volume_left;
109  int volume_right = sc->volume_right;
110 
111  if (frac_speed == 0x10000) {
112  /* Special case when frac_speed is 0x10000 */
113  do {
114  buffer[0] = Clamp(buffer[0] + (*b * volume_left >> 8), -MAX_VOLUME, MAX_VOLUME);
115  buffer[1] = Clamp(buffer[1] + (*b * volume_right >> 8), -MAX_VOLUME, MAX_VOLUME);
116  b++;
117  buffer += 2;
118  } while (--samples > 0);
119  } else {
120  do {
121  int data = RateConversion(b, frac_pos);
122  buffer[0] = Clamp(buffer[0] + (data * volume_left >> 8), -MAX_VOLUME, MAX_VOLUME);
123  buffer[1] = Clamp(buffer[1] + (data * volume_right >> 8), -MAX_VOLUME, MAX_VOLUME);
124  buffer += 2;
125  frac_pos += frac_speed;
126  b += frac_pos >> 16;
127  frac_pos &= 0xffff;
128  } while (--samples > 0);
129  }
130 
131  sc->frac_pos = frac_pos;
132  sc->pos = b - sc->memory;
133 }
134 
135 static void MxCloseChannel(MixerChannel *mc)
136 {
137  mc->active = false;
138 }
139 
140 void MxMixSamples(void *buffer, uint samples)
141 {
142  PerformanceMeasurer framerate(PFE_SOUND);
143  static uint last_samples = 0;
144  if (samples != last_samples) {
145  framerate.SetExpectedRate((double)_play_rate / samples);
146  last_samples = samples;
147  }
148 
149  MixerChannel *mc;
150 
151  /* Clear the buffer */
152  memset(buffer, 0, sizeof(int16) * 2 * samples);
153 
154  /* Fetch music if a sampled stream is available */
155  if (_music_stream) _music_stream((int16*)buffer, samples);
156 
157  /* Mix each channel */
158  for (mc = _channels; mc != endof(_channels); mc++) {
159  if (mc->active) {
160  if (mc->is16bit) {
161  mix_int16(mc, (int16*)buffer, samples);
162  } else {
163  mix_int8_to_int16(mc, (int16*)buffer, samples);
164  }
165  if (mc->samples_left == 0) MxCloseChannel(mc);
166  }
167  }
168 }
169 
170 MixerChannel *MxAllocateChannel()
171 {
172  MixerChannel *mc;
173  for (mc = _channels; mc != endof(_channels); mc++) {
174  if (!mc->active) {
175  free(mc->memory);
176  mc->memory = nullptr;
177  return mc;
178  }
179  }
180  return nullptr;
181 }
182 
183 void MxSetChannelRawSrc(MixerChannel *mc, int8 *mem, size_t size, uint rate, bool is16bit)
184 {
185  mc->memory = mem;
186  mc->frac_pos = 0;
187  mc->pos = 0;
188 
189  mc->frac_speed = (rate << 16) / _play_rate;
190 
191  if (is16bit) size /= 2;
192 
193  /* adjust the magnitude to prevent overflow */
194  while (size >= _max_size) {
195  size >>= 1;
196  rate = (rate >> 1) + 1;
197  }
198 
199  mc->samples_left = (uint)size * _play_rate / rate;
200  mc->is16bit = is16bit;
201 }
202 
209 void MxSetChannelVolume(MixerChannel *mc, uint volume, float pan)
210 {
211  /* Use sinusoidal pan to maintain overall sound power level regardless
212  * of position. */
213  mc->volume_left = (uint)(sin((1.0 - pan) * M_PI / 2.0) * volume);
214  mc->volume_right = (uint)(sin(pan * M_PI / 2.0) * volume);
215 }
216 
217 
218 void MxActivateChannel(MixerChannel *mc)
219 {
220  mc->active = true;
221 }
222 
228 uint32 MxSetMusicSource(MxStreamCallback music_callback)
229 {
230  _music_stream = music_callback;
231  return _play_rate;
232 }
233 
234 
235 bool MxInitialize(uint rate)
236 {
237  _play_rate = rate;
238  _max_size = UINT_MAX / _play_rate;
239  _music_stream = nullptr; /* rate may have changed, any music source is now invalid */
240  return true;
241 }
math_func.hpp
PerformanceMeasurer
RAII class for measuring simple elements of performance.
Definition: framerate_type.h:92
safeguards.h
MxStreamCallback
void(* MxStreamCallback)(int16 *buffer, size_t samples)
Type of callback functions for supplying PCM music.
Definition: mixer.h:21
stdafx.h
PFE_SOUND
@ PFE_SOUND
Speed of mixing audio samples.
Definition: framerate_type.h:60
Clamp
static T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition: math_func.hpp:77
MixerChannel
Definition: mixer.cpp:18
endof
#define endof(x)
Get the end element of an fixed size array.
Definition: stdafx.h:375
framerate_type.h
MAX_VOLUME
static const int MAX_VOLUME
The theoretical maximum volume for a single sound sample.
Definition: mixer.cpp:48
RateConversion
static int RateConversion(T *b, int frac_pos)
Perform the rate conversion between the input and output.
Definition: mixer.cpp:58
MxSetChannelVolume
void MxSetChannelVolume(MixerChannel *mc, uint volume, float pan)
Set volume and pan parameters for a sound.
Definition: mixer.cpp:209
MxSetMusicSource
uint32 MxSetMusicSource(MxStreamCallback music_callback)
Set source of PCM music.
Definition: mixer.cpp:228
free
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: stdafx.h:454
mixer.h