Codebase list squeezelite / 495f115 output_pack.c
495f115

Tree @495f115 (Download .tar.gz)

output_pack.c @495f115raw · history · blame

/* 
 *  Squeezelite - lightweight headless squeezebox emulator
 *
 *  (c) Adrian Smith 2012-2015, triode1@btinternet.com
 *
 * This program 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, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program 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.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

// Scale and pack functions

#include "squeezelite.h"

#define MAX_SCALESAMPLE 0x7fffffffffffLL
#define MIN_SCALESAMPLE -MAX_SCALESAMPLE

// inlining these on windows prevents them being linkable...
#if !WIN
inline 
#endif
s32_t gain(s32_t gain, s32_t sample) {
	s64_t res = (s64_t)gain * (s64_t)sample;
	if (res > MAX_SCALESAMPLE) res = MAX_SCALESAMPLE;
	if (res < MIN_SCALESAMPLE) res = MIN_SCALESAMPLE;
	return (s32_t) (res >> 16);
}
#if !WIN
inline
#endif
s32_t to_gain(float f) {
	return (s32_t)(f * 65536.0F);
}

void _scale_and_pack_frames(void *outputptr, s32_t *inputptr, frames_t cnt, s32_t gainL, s32_t gainR, output_format format) {
	switch(format) {
	case S16_LE:
		{
			u32_t *optr = (u32_t *)(void *)outputptr;
#if SL_LITTLE_ENDIAN
			if (gainL == FIXED_ONE && gainR == FIXED_ONE) {
				while (cnt--) {
					*(optr++) = (*(inputptr) >> 16 & 0x0000ffff) | (*(inputptr + 1) & 0xffff0000);
					inputptr += 2;
				}
			} else {
				while (cnt--) {
					*(optr++) =  (gain(gainL, *(inputptr)) >> 16 & 0x0000ffff) | (gain(gainR, *(inputptr+1)) & 0xffff0000);
					inputptr += 2;
				}
			}
#else
			if (gainL == FIXED_ONE && gainR == FIXED_ONE) {
				while (cnt--) {
					s32_t lsample = *(inputptr++);
					s32_t rsample = *(inputptr++);
					*(optr++) = 
						(lsample & 0x00ff0000) << 8 | (lsample & 0xff000000) >> 8 |
						(rsample & 0x00ff0000) >> 8 | (rsample & 0xff000000) >> 24;
				}
			} else {
				while (cnt--) {
					s32_t lsample = gain(gainL, *(inputptr++));
					s32_t rsample = gain(gainR, *(inputptr++));
					*(optr++) = 
						(lsample & 0x00ff0000) << 8 | (lsample & 0xff000000) >> 8 |
						(rsample & 0x00ff0000) >> 8 | (rsample & 0xff000000) >> 24;
				}
			}
#endif
		}
		break;
	case S24_LE: 
		{
			u32_t *optr = (u32_t *)(void *)outputptr;
#if SL_LITTLE_ENDIAN
			if (gainL == FIXED_ONE && gainR == FIXED_ONE) {
				while (cnt--) {
					*(optr++) = *(inputptr++) >> 8;
					*(optr++) = *(inputptr++) >> 8;
				}
			} else {
				while (cnt--) {
					*(optr++) = gain(gainL, *(inputptr++)) >> 8;
					*(optr++) = gain(gainR, *(inputptr++)) >> 8;
				}
			}
#else
			if (gainL == FIXED_ONE && gainR == FIXED_ONE) {
				while (cnt--) {
					s32_t lsample = *(inputptr++);
					s32_t rsample = *(inputptr++);
					*(optr++) = 
						(lsample & 0xff000000) >> 16 | (lsample & 0x00ff0000) | (lsample & 0x0000ff00 << 16);
					*(optr++) = 
						(rsample & 0xff000000) >> 16 | (rsample & 0x00ff0000) | (rsample & 0x0000ff00 << 16);
				}
			} else {
				while (cnt--) {
					s32_t lsample = gain(gainL, *(inputptr++));
					s32_t rsample = gain(gainR, *(inputptr++));
					*(optr++) = 
						(lsample & 0xff000000) >> 16 | (lsample & 0x00ff0000) | (lsample & 0x0000ff00 << 16);
					*(optr++) = 
						(rsample & 0xff000000) >> 16 | (rsample & 0x00ff0000) | (rsample & 0x0000ff00 << 16);
				}
			}
#endif
		}
		break;
	case S24_3LE:
		{
			u8_t *optr = (u8_t *)(void *)outputptr;
			if (gainL == FIXED_ONE && gainR == FIXED_ONE) {
				while (cnt) {
					// attempt to do 32 bit memory accesses - move 2 frames at once: 16 bytes -> 12 bytes
					// falls through to exception case when not aligned or if less than 2 frames to move
					if (((uintptr_t)optr & 0x3) == 0 && cnt >= 2) {
						u32_t *o_ptr = (u32_t *)(void *)optr;
						while (cnt >= 2) {
							s32_t l1 = *(inputptr++); s32_t r1 = *(inputptr++);
							s32_t l2 = *(inputptr++); s32_t r2 = *(inputptr++);
#if SL_LITTLE_ENDIAN
							*(o_ptr++) = (l1 & 0xffffff00) >>  8 | (r1 & 0x0000ff00) << 16;
							*(o_ptr++) = (r1 & 0xffff0000) >> 16 | (l2 & 0x00ffff00) <<  8;
							*(o_ptr++) = (l2 & 0xff000000) >> 24 | (r2 & 0xffffff00);
#else
							*(o_ptr++) = (l1 & 0x0000ff00) << 16 | (l1 & 0x00ff0000) | (l1 & 0xff000000) >> 16 |
								(r1 & 0x0000ff00) >> 8; 
							*(o_ptr++) = (r1 & 0x00ff0000) <<  8 | (r1 & 0xff000000) >> 8 | (l2 & 0x0000ff00) |
								(l2 & 0x00ff0000) >> 16;
							*(o_ptr++) = (l2 & 0xff000000) | (r2 & 0x0000ff00) << 8 | (r2 & 0x00ff0000) >> 8 |
								(r2 & 0xff000000) >> 24;
#endif
							optr += 12;
							cnt  -=  2;
						}
					} else {
						s32_t lsample = *(inputptr++);
						s32_t rsample = *(inputptr++);
						*(optr++) = (lsample & 0x0000ff00) >>  8;
						*(optr++) = (lsample & 0x00ff0000) >> 16;
						*(optr++) = (lsample & 0xff000000) >> 24;
						*(optr++) = (rsample & 0x0000ff00) >>  8;
						*(optr++) = (rsample & 0x00ff0000) >> 16;
						*(optr++) = (rsample & 0xff000000) >> 24;
						cnt--;
					}
				}
			} else {
				while (cnt) {
					// attempt to do 32 bit memory accesses - move 2 frames at once: 16 bytes -> 12 bytes
					// falls through to exception case when not aligned or if less than 2 frames to move
					if (((uintptr_t)optr & 0x3) == 0 && cnt >= 2) {
						u32_t *o_ptr = (u32_t *)(void *)optr;
						while (cnt >= 2) {
							s32_t l1 = gain(gainL, *(inputptr++)); s32_t r1 = gain(gainR, *(inputptr++));
							s32_t l2 = gain(gainL, *(inputptr++)); s32_t r2 = gain(gainR, *(inputptr++));
#if SL_LITTLE_ENDIAN
							*(o_ptr++) = (l1 & 0xffffff00) >>  8 | (r1 & 0x0000ff00) << 16;
							*(o_ptr++) = (r1 & 0xffff0000) >> 16 | (l2 & 0x00ffff00) <<  8;
							*(o_ptr++) = (l2 & 0xff000000) >> 24 | (r2 & 0xffffff00);
#else
							*(o_ptr++) = (l1 & 0x0000ff00) << 16 | (l1 & 0x00ff0000) | (l1 & 0xff000000) >> 16 |
								(r1 & 0x0000ff00) >> 8; 
							*(o_ptr++) = (r1 & 0x00ff0000) <<  8 | (r1 & 0xff000000) >> 8 | (l2 & 0x0000ff00) |
								(l2 & 0x00ff0000) >> 16;
							*(o_ptr++) = (l2 & 0xff000000) | (r2 & 0x0000ff00) << 8 | (r2 & 0x00ff0000) >> 8 |
								(r2 & 0xff000000) >> 24;
#endif
							optr += 12;
							cnt  -=  2;
						}
					} else {
						s32_t lsample = gain(gainL, *(inputptr++));
						s32_t rsample = gain(gainR, *(inputptr++));
						*(optr++) = (lsample & 0x0000ff00) >>  8;
						*(optr++) = (lsample & 0x00ff0000) >> 16;
						*(optr++) = (lsample & 0xff000000) >> 24;
						*(optr++) = (rsample & 0x0000ff00) >>  8;
						*(optr++) = (rsample & 0x00ff0000) >> 16;
						*(optr++) = (rsample & 0xff000000) >> 24;
						cnt--;
					}
				}
			}
		}
		break;
	case S32_LE:
		{
			u32_t *optr = (u32_t *)(void *)outputptr;
#if SL_LITTLE_ENDIAN
			if (gainL == FIXED_ONE && gainR == FIXED_ONE) {
				memcpy(outputptr, inputptr, cnt * BYTES_PER_FRAME);
			} else {
				while (cnt--) {
					*(optr++) = gain(gainL, *(inputptr++));
					*(optr++) = gain(gainR, *(inputptr++));
				}
			}
#else
			if (gainL == FIXED_ONE && gainR == FIXED_ONE) {
				while (cnt--) {
					s32_t lsample = *(inputptr++);
					s32_t rsample = *(inputptr++);
					*(optr++) = 
						(lsample & 0xff000000) >> 24 | (lsample & 0x00ff0000) >> 8 |
						(lsample & 0x0000ff00) << 8  | (lsample & 0x000000ff) << 24;
					*(optr++) = 
						(rsample & 0xff000000) >> 24 | (rsample & 0x00ff0000) >> 8 |
						(rsample & 0x0000ff00) << 8  | (rsample & 0x000000ff) << 24;
				}
			} else {
				while (cnt--) {
					s32_t lsample = gain(gainL, *(inputptr++));
					s32_t rsample = gain(gainR, *(inputptr++));
					*(optr++) = 
						(lsample & 0xff000000) >> 24 | (lsample & 0x00ff0000) >> 8 |
						(lsample & 0x0000ff00) << 8  | (lsample & 0x000000ff) << 24;
					*(optr++) = 
						(rsample & 0xff000000) >> 24 | (rsample & 0x00ff0000) >> 8 |
						(rsample & 0x0000ff00) << 8  | (rsample & 0x000000ff) << 24;
				}
			}
#endif
		}
		break;
	default:
		break;
	}
}

#if !WIN
inline 
#endif
void _apply_cross(struct buffer *outputbuf, frames_t out_frames, s32_t cross_gain_in, s32_t cross_gain_out, s32_t **cross_ptr) {
	s32_t *ptr = (s32_t *)(void *)outputbuf->readp;
	frames_t count = out_frames * 2;
	while (count--) {
		if (*cross_ptr > (s32_t *)outputbuf->wrap) {
			*cross_ptr -= outputbuf->size / BYTES_PER_FRAME * 2;
		}
		*ptr = gain(cross_gain_out, *ptr) + gain(cross_gain_in, **cross_ptr);
		ptr++; (*cross_ptr)++;
	}
}

#if !WIN
inline 
#endif
void _apply_gain(struct buffer *outputbuf, frames_t count, s32_t gainL, s32_t gainR) {
	s32_t *ptrL = (s32_t *)(void *)outputbuf->readp;
	s32_t *ptrR = (s32_t *)(void *)outputbuf->readp + 1;
	while (count--) {
		*ptrL = gain(gainL, *ptrL);
		*ptrR = gain(gainR, *ptrR);
		ptrL += 2;
		ptrR += 2;
	}
}