1#!/usr/bin/env python3
2#
3# Run:
4#  misc/make_resamplers.py | indent -kr -i3 -l0
5
6import sys, re
7
8# http://code.activestate.com/recipes/502257/
9def interp(string):
10   locals  = sys._getframe(1).f_locals
11   globals = sys._getframe(1).f_globals
12   for item in re.findall(r'#\{([^}]*)\}', string):
13      string = string.replace('#{%s}' % item, str(eval(item, globals, locals)))
14   return string
15
16
17class Depth:
18   def index(self, fmt):
19      if fmt == "f32":
20         return self.index_f32
21      if fmt == "s16":
22         return self.index_s16
23
24class Depth_f32(Depth):
25   def constant(self):
26      return "ALLEGRO_AUDIO_DEPTH_FLOAT32"
27   def index_f32(self, buf, index):
28      return interp("#{buf}.f32[ #{index} ]")
29   def index_s16(self, buf, index):
30      return interp("(int16_t) (#{buf}.f32[ #{index} ] * 0x7FFF)")
31
32class Depth_int24(Depth):
33   def constant(self):
34      return "ALLEGRO_AUDIO_DEPTH_INT24"
35   def index_f32(self, buf, index):
36      return interp("(float) #{buf}.s24[ #{index} ] / ((float)0x7FFFFF + 0.5f)")
37   def index_s16(self, buf, index):
38      return interp("(int16_t) (#{buf}.s24[ #{index} ] >> 9)")
39
40class Depth_uint24(Depth):
41   def constant(self):
42      return "ALLEGRO_AUDIO_DEPTH_UINT24"
43   def index_f32(self, buf, index):
44      return interp("(float) #{buf}.u24[ #{index} ] / ((float)0x7FFFFF + 0.5f) - 1.0f")
45   def index_s16(self, buf, index):
46      return interp("(int16_t) ((#{buf}.u24[ #{index} ] - 0x800000) >> 9)")
47
48class Depth_int16(Depth):
49   def constant(self):
50      return "ALLEGRO_AUDIO_DEPTH_INT16"
51   def index_f32(self, buf, index):
52      return interp("(float) #{buf}.s16[ #{index} ] / ((float)0x7FFF + 0.5f)")
53   def index_s16(self, buf, index):
54      return interp("#{buf}.s16[ #{index} ]")
55
56class Depth_uint16(Depth):
57   def constant(self):
58      return "ALLEGRO_AUDIO_DEPTH_UINT16"
59   def index_f32(self, buf, index):
60      return interp("(float) #{buf}.u16[ #{index} ] / ((float)0x7FFF + 0.5f) - 1.0f")
61   def index_s16(self, buf, index):
62      return interp("(int16_t) (#{buf}.u16[ #{index} ] - 0x8000)")
63
64class Depth_int8(Depth):
65   def constant(self):
66      return "ALLEGRO_AUDIO_DEPTH_INT8"
67   def index_f32(self, buf, index):
68      return interp("(float) #{buf}.s8[ #{index} ] / ((float)0x7F + 0.5f)")
69   def index_s16(self, buf, index):
70      return interp("(int16_t) #{buf}.s8[ #{index} ] << 7")
71
72class Depth_uint8(Depth):
73   def constant(self):
74      return "ALLEGRO_AUDIO_DEPTH_UINT8"
75   def index_f32(self, buf, index):
76      return interp("(float) #{buf}.u8[ #{index} ] / ((float)0x7F + 0.5f) - 1.0f")
77   def index_s16(self, buf, index):
78      return interp("(int16_t) (#{buf}.u8[ #{index} ] - 0x80) << 7")
79
80depths = [
81   Depth_f32(),
82   Depth_int24(),
83   Depth_uint24(),
84   Depth_int16(),
85   Depth_uint16(),
86   Depth_int8(),
87   Depth_uint8()
88]
89
90def make_point_interpolator(name, fmt):
91   print(interp("""\
92   static INLINE const void *
93      #{name}
94      (SAMP_BUF *samp_buf,
95       const ALLEGRO_SAMPLE_INSTANCE *spl,
96       unsigned int maxc)
97   {
98      unsigned int i0 = spl->pos*maxc;
99      unsigned int i;
100
101      switch (spl->spl_data.depth) {
102      """))
103
104   for depth in depths:
105      buf_index = depth.index(fmt)("spl->spl_data.buffer", "i0 + i")
106      print(interp("""\
107         case #{depth.constant()}:
108            for (i = 0; i < maxc; i++) {
109               samp_buf-> #{fmt} [i] = #{buf_index};
110            }
111            break;
112         """))
113
114   print(interp("""\
115      }
116      return samp_buf-> #{fmt} ;
117   }"""))
118
119def make_linear_interpolator(name, fmt):
120   assert fmt == "f32" or fmt == "s16"
121
122   print(interp("""\
123   static INLINE const void *
124      #{name}
125      (SAMP_BUF *samp_buf,
126       const ALLEGRO_SAMPLE_INSTANCE *spl,
127       unsigned int maxc)
128   {
129      int p0 = spl->pos;
130      int p1 = spl->pos+1;
131
132      switch (spl->loop) {
133         case ALLEGRO_PLAYMODE_ONCE:
134            if (p1 >= spl->spl_data.len)
135               p1 = p0;
136            break;
137         case ALLEGRO_PLAYMODE_LOOP:
138            if (p1 >= spl->loop_end)
139               p1 = spl->loop_start;
140            break;
141         case ALLEGRO_PLAYMODE_BIDIR:
142            if (p1 >= spl->loop_end) {
143               p1 = spl->loop_end - 1;
144               if (p1 < spl->loop_start)
145                  p1 = spl->loop_start;
146            }
147            break;
148         case _ALLEGRO_PLAYMODE_STREAM_ONCE:
149         case _ALLEGRO_PLAYMODE_STREAM_ONEDIR:""" +
150            # For audio streams, sample i+1 may be in the next buffer fragment,
151            # which may not even be generated yet.  So we lag by one sample and
152            # interpolate between sample i-1 and sample i.
153            #
154            # We arrange the buffers in memory such that indexing i-1 is always
155            # valid, even after wrapping around from the last buffer fragment to
156            # the first buffer fragment.  See _al_kcm_refill_stream.
157            """
158            p0--;
159            p1--;
160            break;
161      }
162
163      p0 *= maxc;
164      p1 *= maxc;
165
166      switch (spl->spl_data.depth) {
167      """))
168
169   for depth in depths:
170      x0 = depth.index(fmt)("spl->spl_data.buffer", "p0 + i")
171      x1 = depth.index(fmt)("spl->spl_data.buffer", "p1 + i")
172      print(interp("""\
173         case #{depth.constant()}:
174         {"""))
175
176      if fmt == "f32":
177         print(interp("""\
178            const float t = (float)spl->pos_bresenham_error / spl->step_denom;
179            int i;
180            for (i = 0; i < (int)maxc; i++) {
181               const float x0 = #{x0};
182               const float x1 = #{x1};
183               const float s = (x0 * (1.0f - t)) + (x1 * t);
184               samp_buf->f32[i] = s;
185            }"""))
186      elif fmt == "s16":
187         print(interp("""\
188            const int32_t t = 256 * spl->pos_bresenham_error / spl->step_denom;
189            int i;
190            for (i = 0; i < (int)maxc; i++) {
191               const int32_t x0 = #{x0};
192               const int32_t x1 = #{x1};
193               const int32_t s = ((x0 * (256 - t))>>8) + ((x1 * t)>>8);
194               samp_buf->s16[i] = (int16_t)s;
195            }"""))
196
197      print(interp("""\
198         }
199         break;
200         """))
201
202   print(interp("""\
203      }
204      return samp_buf-> #{fmt};
205   }"""))
206
207def make_cubic_interpolator(name, fmt):
208   assert fmt == "f32"
209
210   print(interp("""\
211   static INLINE const void *
212      #{name}
213      (SAMP_BUF *samp_buf,
214       const ALLEGRO_SAMPLE_INSTANCE *spl,
215       unsigned int maxc)
216   {
217      int p0 = spl->pos-1;
218      int p1 = spl->pos;
219      int p2 = spl->pos+1;
220      int p3 = spl->pos+2;
221
222      switch (spl->loop) {
223         case ALLEGRO_PLAYMODE_ONCE:
224            if (p0 < 0)
225               p0 = 0;
226            if (p2 >= spl->spl_data.len)
227               p2 = spl->spl_data.len - 1;
228            if (p3 >= spl->spl_data.len)
229               p3 = spl->spl_data.len - 1;
230            break;
231         case ALLEGRO_PLAYMODE_LOOP:
232         case ALLEGRO_PLAYMODE_BIDIR:
233            /* These positions should really wrap/bounce instead of clamping
234             * but it's probably unnoticeable.
235             */
236            if (p0 < spl->loop_start)
237               p0 = spl->loop_end - 1;
238            if (p2 >= spl->loop_end)
239               p2 = spl->loop_start;
240            if (p3 >= spl->loop_end)
241               p3 = spl->loop_start;
242            break;
243         case _ALLEGRO_PLAYMODE_STREAM_ONCE:
244         case _ALLEGRO_PLAYMODE_STREAM_ONEDIR:
245            /* Lag by three samples in total. */
246            p0 -= 2;
247            p1 -= 2;
248            p2 -= 2;
249            p3 -= 2;
250            break;
251      }
252
253      p0 *= maxc;
254      p1 *= maxc;
255      p2 *= maxc;
256      p3 *= maxc;
257
258      switch (spl->spl_data.depth) {
259      """))
260
261   for depth in depths:
262      value0 = depth.index(fmt)("spl->spl_data.buffer", "p0 + i")
263      value1 = depth.index(fmt)("spl->spl_data.buffer", "p1 + i")
264      value2 = depth.index(fmt)("spl->spl_data.buffer", "p2 + i")
265      value3 = depth.index(fmt)("spl->spl_data.buffer", "p3 + i")
266      # 4-point, cubic Hermite interpolation
267      # Code transcribed from "Polynomial Interpolators for High-Quality
268      # Resampling of Oversampled Audio" by Olli Niemitalo
269      # http://yehar.com/blog/?p=197
270      print(interp("""\
271         case #{depth.constant()}:
272         {
273            const float t = (float)spl->pos_bresenham_error / spl->step_denom;
274            signed int i;
275            for (i = 0; i < (signed int)maxc; i++) {
276               float x0 = #{value0};
277               float x1 = #{value1};
278               float x2 = #{value2};
279               float x3 = #{value3};
280               float c0 = x1;
281               float c1 = 0.5f * (x2 - x0);
282               float c2 = x0 - (2.5f * x1) + (2.0f * x2) - (0.5f * x3);
283               float c3 = (0.5f * (x3 - x0)) + (1.5f * (x1 - x2));
284               float s = (((((c3 * t) + c2) * t) + c1) * t) + c0;
285               samp_buf->f32[i] = s;
286            }
287         }
288         break;
289         """))
290
291   print(interp("""\
292      }
293      return samp_buf-> #{fmt} ;
294   }"""))
295
296if __name__ == "__main__":
297   print("// Warning: This file was created by make_resamplers.py - do not edit.")
298   print("// vim: set ft=c:")
299
300   make_point_interpolator("point_spl32", "f32")
301   make_point_interpolator("point_spl16", "s16")
302   make_linear_interpolator("linear_spl32", "f32")
303   make_linear_interpolator("linear_spl16", "s16")
304   make_cubic_interpolator("cubic_spl32", "f32")
305
306# vim: set sts=3 sw=3 et:
307