]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - snd_mix.c
Fix name of CONFIG_VIDEO_CAPTURE macro.
[xonotic/darkplaces.git] / snd_mix.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22 #include "snd_main.h"
23
24 extern cvar_t snd_softclip;
25
26 static portable_sampleframe_t paintbuffer[PAINTBUFFER_SIZE];
27 static portable_sampleframe_t paintbuffer_unswapped[PAINTBUFFER_SIZE];
28
29 extern speakerlayout_t snd_speakerlayout; // for querying the listeners
30
31 #ifdef CONFIG_VIDEO_CAPTURE
32 static void S_CaptureAVISound(const portable_sampleframe_t *paintbuffer, size_t length)
33 {
34         size_t i;
35         unsigned int j;
36
37         if (!cls.capturevideo.active)
38                 return;
39
40         // undo whatever swapping the channel layout (swapstereo, ALSA) did
41         for(j = 0; j < snd_speakerlayout.channels; ++j)
42         {
43                 unsigned int j0 = snd_speakerlayout.listeners[j].channel_unswapped;
44                 for(i = 0; i < length; ++i)
45                         paintbuffer_unswapped[i].sample[j0] = paintbuffer[i].sample[j];
46         }
47
48         SCR_CaptureVideo_SoundFrame(paintbuffer_unswapped, length);
49 }
50 #endif
51
52 extern cvar_t snd_softclip;
53
54 static void S_SoftClipPaintBuffer(portable_sampleframe_t *painted_ptr, int nbframes, int width, int channels)
55 {
56         int i;
57
58         if((snd_softclip.integer == 1 && width <= 2) || snd_softclip.integer > 1)
59         {
60                 portable_sampleframe_t *p = painted_ptr;
61
62 #if 0
63 /* Soft clipping, the sound of a dream, thanks to Jon Wattes
64    post to Musicdsp.org */
65 #define SOFTCLIP(x) (x) = sin(bound(-M_PI/2, (x), M_PI/2)) * 0.25
66 #endif
67
68                 // let's do a simple limiter instead, seems to sound better
69                 static float maxvol = 0;
70                 maxvol = max(1.0f, maxvol * (1.0f - nbframes / (0.4f * snd_renderbuffer->format.speed)));
71 #define SOFTCLIP(x) if(fabs(x)>maxvol) maxvol=fabs(x); (x) /= maxvol;
72
73                 if (channels == 8)  // 7.1 surround
74                 {
75                         for (i = 0;i < nbframes;i++, p++)
76                         {
77                                 SOFTCLIP(p->sample[0]);
78                                 SOFTCLIP(p->sample[1]);
79                                 SOFTCLIP(p->sample[2]);
80                                 SOFTCLIP(p->sample[3]);
81                                 SOFTCLIP(p->sample[4]);
82                                 SOFTCLIP(p->sample[5]);
83                                 SOFTCLIP(p->sample[6]);
84                                 SOFTCLIP(p->sample[7]);
85                         }
86                 }
87                 else if (channels == 6)  // 5.1 surround
88                 {
89                         for (i = 0; i < nbframes; i++, p++)
90                         {
91                                 SOFTCLIP(p->sample[0]);
92                                 SOFTCLIP(p->sample[1]);
93                                 SOFTCLIP(p->sample[2]);
94                                 SOFTCLIP(p->sample[3]);
95                                 SOFTCLIP(p->sample[4]);
96                                 SOFTCLIP(p->sample[5]);
97                         }
98                 }
99                 else if (channels == 4)  // 4.0 surround
100                 {
101                         for (i = 0; i < nbframes; i++, p++)
102                         {
103                                 SOFTCLIP(p->sample[0]);
104                                 SOFTCLIP(p->sample[1]);
105                                 SOFTCLIP(p->sample[2]);
106                                 SOFTCLIP(p->sample[3]);
107                         }
108                 }
109                 else if (channels == 2)  // 2.0 stereo
110                 {
111                         for (i = 0; i < nbframes; i++, p++)
112                         {
113                                 SOFTCLIP(p->sample[0]);
114                                 SOFTCLIP(p->sample[1]);
115                         }
116                 }
117                 else if (channels == 1)  // 1.0 mono
118                 {
119                         for (i = 0; i < nbframes; i++, p++)
120                         {
121                                 SOFTCLIP(p->sample[0]);
122                         }
123                 }
124 #undef SOFTCLIP
125         }
126 }
127
128 static void S_ConvertPaintBuffer(portable_sampleframe_t *painted_ptr, void *rb_ptr, int nbframes, int width, int channels)
129 {
130         int i, val;
131
132         // FIXME: add 24bit and 32bit float formats
133         // FIXME: optimize with SSE intrinsics?
134         if (width == 2)  // 16bit
135         {
136                 short *snd_out = (short*)rb_ptr;
137                 if (channels == 8)  // 7.1 surround
138                 {
139                         for (i = 0;i < nbframes;i++, painted_ptr++)
140                         {
141                                 val = (int)(painted_ptr->sample[0] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
142                                 val = (int)(painted_ptr->sample[1] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
143                                 val = (int)(painted_ptr->sample[2] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
144                                 val = (int)(painted_ptr->sample[3] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
145                                 val = (int)(painted_ptr->sample[4] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
146                                 val = (int)(painted_ptr->sample[5] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
147                                 val = (int)(painted_ptr->sample[6] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
148                                 val = (int)(painted_ptr->sample[7] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
149                         }
150                 }
151                 else if (channels == 6)  // 5.1 surround
152                 {
153                         for (i = 0; i < nbframes; i++, painted_ptr++)
154                         {
155                                 val = (int)(painted_ptr->sample[0] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
156                                 val = (int)(painted_ptr->sample[1] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
157                                 val = (int)(painted_ptr->sample[2] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
158                                 val = (int)(painted_ptr->sample[3] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
159                                 val = (int)(painted_ptr->sample[4] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
160                                 val = (int)(painted_ptr->sample[5] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
161                         }
162                 }
163                 else if (channels == 4)  // 4.0 surround
164                 {
165                         for (i = 0; i < nbframes; i++, painted_ptr++)
166                         {
167                                 val = (int)(painted_ptr->sample[0] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
168                                 val = (int)(painted_ptr->sample[1] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
169                                 val = (int)(painted_ptr->sample[2] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
170                                 val = (int)(painted_ptr->sample[3] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
171                         }
172                 }
173                 else if (channels == 2)  // 2.0 stereo
174                 {
175                         for (i = 0; i < nbframes; i++, painted_ptr++)
176                         {
177                                 val = (int)(painted_ptr->sample[0] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
178                                 val = (int)(painted_ptr->sample[1] * 32768.0f);*snd_out++ = bound(-32768, val, 32767);
179                         }
180                 }
181                 else if (channels == 1)  // 1.0 mono
182                 {
183                         for (i = 0; i < nbframes; i++, painted_ptr++)
184                         {
185                                 val = (int)((painted_ptr->sample[0] + painted_ptr->sample[1]) * 16384.0f);*snd_out++ = bound(-32768, val, 32767);
186                         }
187                 }
188
189                 // noise is really really annoying
190                 if (cls.timedemo)
191                         memset(rb_ptr, 0, nbframes * channels * width);
192         }
193         else  // 8bit
194         {
195                 unsigned char *snd_out = (unsigned char*)rb_ptr;
196                 if (channels == 8)  // 7.1 surround
197                 {
198                         for (i = 0; i < nbframes; i++, painted_ptr++)
199                         {
200                                 val = (int)(painted_ptr->sample[0] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
201                                 val = (int)(painted_ptr->sample[1] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
202                                 val = (int)(painted_ptr->sample[2] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
203                                 val = (int)(painted_ptr->sample[3] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
204                                 val = (int)(painted_ptr->sample[4] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
205                                 val = (int)(painted_ptr->sample[5] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
206                                 val = (int)(painted_ptr->sample[6] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
207                                 val = (int)(painted_ptr->sample[7] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
208                         }
209                 }
210                 else if (channels == 6)  // 5.1 surround
211                 {
212                         for (i = 0; i < nbframes; i++, painted_ptr++)
213                         {
214                                 val = (int)(painted_ptr->sample[0] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
215                                 val = (int)(painted_ptr->sample[1] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
216                                 val = (int)(painted_ptr->sample[2] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
217                                 val = (int)(painted_ptr->sample[3] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
218                                 val = (int)(painted_ptr->sample[4] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
219                                 val = (int)(painted_ptr->sample[5] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
220                         }
221                 }
222                 else if (channels == 4)  // 4.0 surround
223                 {
224                         for (i = 0; i < nbframes; i++, painted_ptr++)
225                         {
226                                 val = (int)(painted_ptr->sample[0] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
227                                 val = (int)(painted_ptr->sample[1] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
228                                 val = (int)(painted_ptr->sample[2] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
229                                 val = (int)(painted_ptr->sample[3] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
230                         }
231                 }
232                 else if (channels == 2)  // 2.0 stereo
233                 {
234                         for (i = 0; i < nbframes; i++, painted_ptr++)
235                         {
236                                 val = (int)(painted_ptr->sample[0] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
237                                 val = (int)(painted_ptr->sample[1] * 128.0f) + 128; *snd_out++ = bound(0, val, 255);
238                         }
239                 }
240                 else if (channels == 1)  // 1.0 mono
241                 {
242                         for (i = 0;i < nbframes;i++, painted_ptr++)
243                         {
244                                 val = (int)((painted_ptr->sample[0] + painted_ptr->sample[1]) * 64.0f) + 128; *snd_out++ = bound(0, val, 255);
245                         }
246                 }
247
248                 // noise is really really annoying
249                 if (cls.timedemo)
250                         memset(rb_ptr, 128, nbframes * channels);
251         }
252 }
253
254
255 /*
256 ===============================================================================
257
258 CHANNEL MIXING
259
260 ===============================================================================
261 */
262
263 void S_MixToBuffer(void *stream, unsigned int bufferframes)
264 {
265         int channelindex;
266         channel_t *ch;
267         int totalmixframes;
268         unsigned char *outbytes = (unsigned char *) stream;
269         sfx_t *sfx;
270         portable_sampleframe_t *paint;
271         int wantframes;
272         int i;
273         int count;
274         int fetched;
275         int fetch;
276         int istartframe;
277         int iendframe;
278         int ilengthframes;
279         int totallength;
280         int loopstart;
281         int indexfrac;
282         int indexfracstep;
283 #define S_FETCHBUFFERSIZE 4096
284         float fetchsampleframes[S_FETCHBUFFERSIZE*2];
285         const float *fetchsampleframe;
286         float vol[SND_LISTENERS];
287         float lerp[2];
288         float sample[3];
289         double posd;
290         double speedd;
291         float maxvol;
292         qboolean looping;
293         qboolean silent;
294
295         // mix as many times as needed to fill the requested buffer
296         while (bufferframes)
297         {
298                 // limit to the size of the paint buffer
299                 totalmixframes = min(bufferframes, PAINTBUFFER_SIZE);
300
301                 // clear the paint buffer
302                 memset(paintbuffer, 0, totalmixframes * sizeof(paintbuffer[0]));
303
304                 // paint in the channels.
305                 // channels with zero volumes still advance in time but don't paint.
306                 ch = channels; // cppcheck complains here but it is wrong, channels is a channel_t[MAX_CHANNELS] and not an int
307                 for (channelindex = 0;channelindex < (int)total_channels;channelindex++, ch++)
308                 {
309                         sfx = ch->sfx;
310                         if (sfx == NULL)
311                                 continue;
312                         if (!S_LoadSound (sfx, true))
313                                 continue;
314                         if (ch->flags & CHANNELFLAG_PAUSED)
315                                 continue;
316                         if (!sfx->total_length)
317                                 continue;
318
319                         // copy the channel information to the stack for reference, otherwise the
320                         // values might change during a mix if the spatializer is updating them
321                         // (note: this still may get some old and some new values!)
322                         posd = ch->position;
323                         speedd = ch->mixspeed * sfx->format.speed / snd_renderbuffer->format.speed;
324                         for (i = 0;i < SND_LISTENERS;i++)
325                                 vol[i] = ch->volume[i];
326
327                         // check total volume level, because we can skip some code on silent sounds but other code must still run (position updates mainly)
328                         maxvol = 0;
329                         for (i = 0;i < SND_LISTENERS;i++)
330                                 if(vol[i] > maxvol)
331                                         maxvol = vol[i];
332                         switch(snd_renderbuffer->format.width)
333                         {
334                                 case 1: // 8bpp
335                                         silent = maxvol < (1.0f / (256.0f));
336                                         // so silent it has zero effect
337                                         break;
338                                 case 2: // 16bpp
339                                         silent = maxvol < (1.0f / (65536.0f));
340                                         // so silent it has zero effect
341                                         break;
342                                 default: // floating point
343                                         silent = maxvol < 1.0e-13f;
344                                         // 130 dB is difference between hearing
345                                         // threshold and a jackhammer from
346                                         // working distance.
347                                         // therefore, anyone who turns up
348                                         // volume so much they notice this
349                                         // cutoff, likely already has their
350                                         // ear-drums blown out anyway.
351                                         break;
352                         }
353
354                         // when doing prologic mixing, some channels invert one side
355                         if (ch->prologic_invert == -1)
356                                 vol[1] *= -1.0f;
357
358                         // get some sfx info in a consistent form
359                         totallength = sfx->total_length;
360                         loopstart = (int)sfx->loopstart < totallength ? (int)sfx->loopstart : ((ch->flags & CHANNELFLAG_FORCELOOP) ? 0 : totallength);
361                         looping = loopstart < totallength;
362
363                         // do the actual paint now (may skip work if silent)
364                         paint = paintbuffer;
365                         istartframe = 0;
366                         for (wantframes = totalmixframes;wantframes > 0;posd += count * speedd, wantframes -= count)
367                         {
368                                 // check if this is a delayed sound
369                                 if (posd < 0)
370                                 {
371                                         // for a delayed sound we have to eat into the delay first
372                                         count = (int)floor(-posd / speedd) + 1;
373                                         count = bound(1, count, wantframes);
374                                         // let the for loop iterator apply the skip
375                                         continue;
376                                 }
377
378                                 // compute a fetch size that won't overflow our buffer
379                                 count = wantframes;
380                                 for (;;)
381                                 {
382                                         istartframe = (int)floor(posd);
383                                         iendframe = (int)floor(posd + (count-1) * speedd);
384                                         ilengthframes = count > 1 ? (iendframe - istartframe + 2) : 2;
385                                         if (ilengthframes <= S_FETCHBUFFERSIZE)
386                                                 break;
387                                         // reduce count by 25% and try again
388                                         count -= count >> 2;
389                                 }
390
391                                 // zero whole fetch buffer for safety
392                                 // (floating point noise from uninitialized memory = HORRIBLE)
393                                 // otherwise we would only need to clear the excess
394                                 if (!silent)
395                                         memset(fetchsampleframes, 0, ilengthframes*sfx->format.channels*sizeof(fetchsampleframes[0]));
396
397                                 // if looping, do multiple fetches
398                                 fetched = 0;
399                                 for (;;)
400                                 {
401                                         fetch = min(ilengthframes - fetched, totallength - istartframe);
402                                         if (fetch > 0)
403                                         {
404                                                 if (!silent)
405                                                         sfx->fetcher->getsamplesfloat(ch, sfx, istartframe, fetch, fetchsampleframes + fetched*sfx->format.channels);
406                                                 istartframe += fetch;
407                                                 fetched += fetch;
408                                         }
409                                         if (istartframe == totallength && looping && fetched < ilengthframes)
410                                         {
411                                                 // loop and fetch some more
412                                                 posd += loopstart - totallength;
413                                                 istartframe = loopstart;
414                                         }
415                                         else
416                                         {
417                                                 break;
418                                         }
419                                 }
420
421                                 // set up our fixedpoint resampling variables (float to int conversions are expensive so do not do one per sampleframe)
422                                 fetchsampleframe = fetchsampleframes;
423                                 indexfrac = (int)floor((posd - floor(posd)) * 65536.0);
424                                 indexfracstep = (int)floor(speedd * 65536.0);
425                                 if (!silent)
426                                 {
427                                         if (sfx->format.channels == 2)
428                                         {
429                                                 // music is stereo
430 #if SND_LISTENERS != 8
431 #error the following code only supports up to 8 channels, update it
432 #endif
433                                                 if (snd_speakerlayout.channels > 2)
434                                                 {
435                                                         // surround mixing
436                                                         for (i = 0;i < count;i++, paint++)
437                                                         {
438                                                                 lerp[1] = indexfrac * (1.0f / 65536.0f);
439                                                                 lerp[0] = 1.0f - lerp[1];
440                                                                 sample[0] = fetchsampleframe[0] * lerp[0] + fetchsampleframe[2] * lerp[1];
441                                                                 sample[1] = fetchsampleframe[1] * lerp[0] + fetchsampleframe[3] * lerp[1];
442                                                                 sample[2] = (sample[0] + sample[1]) * 0.5f;
443                                                                 paint->sample[0] += sample[0] * vol[0];
444                                                                 paint->sample[1] += sample[1] * vol[1];
445                                                                 paint->sample[2] += sample[0] * vol[2];
446                                                                 paint->sample[3] += sample[1] * vol[3];
447                                                                 paint->sample[4] += sample[2] * vol[4];
448                                                                 paint->sample[5] += sample[2] * vol[5];
449                                                                 paint->sample[6] += sample[0] * vol[6];
450                                                                 paint->sample[7] += sample[1] * vol[7];
451                                                                 indexfrac += indexfracstep;
452                                                                 fetchsampleframe += 2 * (indexfrac >> 16);
453                                                                 indexfrac &= 0xFFFF;
454                                                         }
455                                                 }
456                                                 else
457                                                 {
458                                                         // stereo mixing
459                                                         for (i = 0;i < count;i++, paint++)
460                                                         {
461                                                                 lerp[1] = indexfrac * (1.0f / 65536.0f);
462                                                                 lerp[0] = 1.0f - lerp[1];
463                                                                 sample[0] = fetchsampleframe[0] * lerp[0] + fetchsampleframe[2] * lerp[1];
464                                                                 sample[1] = fetchsampleframe[1] * lerp[0] + fetchsampleframe[3] * lerp[1];
465                                                                 paint->sample[0] += sample[0] * vol[0];
466                                                                 paint->sample[1] += sample[1] * vol[1];
467                                                                 indexfrac += indexfracstep;
468                                                                 fetchsampleframe += 2 * (indexfrac >> 16);
469                                                                 indexfrac &= 0xFFFF;
470                                                         }
471                                                 }
472                                         }
473                                         else if (sfx->format.channels == 1)
474                                         {
475                                                 // most sounds are mono
476 #if SND_LISTENERS != 8
477 #error the following code only supports up to 8 channels, update it
478 #endif
479                                                 if (snd_speakerlayout.channels > 2)
480                                                 {
481                                                         // surround mixing
482                                                         for (i = 0;i < count;i++, paint++)
483                                                         {
484                                                                 lerp[1] = indexfrac * (1.0f / 65536.0f);
485                                                                 lerp[0] = 1.0f - lerp[1];
486                                                                 sample[0] = fetchsampleframe[0] * lerp[0] + fetchsampleframe[1] * lerp[1];
487                                                                 paint->sample[0] += sample[0] * vol[0];
488                                                                 paint->sample[1] += sample[0] * vol[1];
489                                                                 paint->sample[2] += sample[0] * vol[2];
490                                                                 paint->sample[3] += sample[0] * vol[3];
491                                                                 paint->sample[4] += sample[0] * vol[4];
492                                                                 paint->sample[5] += sample[0] * vol[5];
493                                                                 paint->sample[6] += sample[0] * vol[6];
494                                                                 paint->sample[7] += sample[0] * vol[7];
495                                                                 indexfrac += indexfracstep;
496                                                                 fetchsampleframe += (indexfrac >> 16);
497                                                                 indexfrac &= 0xFFFF;
498                                                         }
499                                                 }
500                                                 else
501                                                 {
502                                                         // stereo mixing
503                                                         for (i = 0;i < count;i++, paint++)
504                                                         {
505                                                                 lerp[1] = indexfrac * (1.0f / 65536.0f);
506                                                                 lerp[0] = 1.0f - lerp[1];
507                                                                 sample[0] = fetchsampleframe[0] * lerp[0] + fetchsampleframe[1] * lerp[1];
508                                                                 paint->sample[0] += sample[0] * vol[0];
509                                                                 paint->sample[1] += sample[0] * vol[1];
510                                                                 indexfrac += indexfracstep;
511                                                                 fetchsampleframe += (indexfrac >> 16);
512                                                                 indexfrac &= 0xFFFF;
513                                                         }
514                                                 }
515                                         }
516                                 }
517                         }
518                         ch->position = posd;
519                         if (!looping && istartframe == totallength)
520                                 S_StopChannel(ch - channels, false, false);
521                 }
522
523                 S_SoftClipPaintBuffer(paintbuffer, totalmixframes, snd_renderbuffer->format.width, snd_renderbuffer->format.channels);
524
525 #ifdef CONFIG_VIDEO_CAPTURE
526                 if (!snd_usethreadedmixing)
527                         S_CaptureAVISound(paintbuffer, totalmixframes);
528 #endif
529
530                 S_ConvertPaintBuffer(paintbuffer, outbytes, totalmixframes, snd_renderbuffer->format.width, snd_renderbuffer->format.channels);
531
532                 // advance the output pointer
533                 outbytes += totalmixframes * snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
534                 bufferframes -= totalmixframes;
535         }
536 }