| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 | /*  *  Squeezelite - lightweight headless squeezebox emulator * *  (c) Adrian Smith 2012-2015, triode1@btinternet.com *      Ralph Irving 2015-2017, ralph_irving@hotmail.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/>. * */#include "squeezelite.h"#if BYTES_PER_FRAME == 4#define SHIFT 16#define OPTR_T	u16_t#else#define OPTR_T	u32_t	#define SHIFT 0#endifextern log_level loglevel;extern struct buffer *streambuf;extern struct buffer *outputbuf;extern struct streamstate stream;extern struct outputstate output;extern struct decodestate decode;extern struct processstate process;bool pcm_check_header = false;#define LOCK_S   mutex_lock(streambuf->mutex)#define UNLOCK_S mutex_unlock(streambuf->mutex)#define LOCK_O   mutex_lock(outputbuf->mutex)#define UNLOCK_O mutex_unlock(outputbuf->mutex)#if PROCESS#define LOCK_O_direct   if (decode.direct) mutex_lock(outputbuf->mutex)#define UNLOCK_O_direct if (decode.direct) mutex_unlock(outputbuf->mutex)#define LOCK_O_not_direct   if (!decode.direct) mutex_lock(outputbuf->mutex)#define UNLOCK_O_not_direct if (!decode.direct) mutex_unlock(outputbuf->mutex)#define IF_DIRECT(x)    if (decode.direct) { x }#define IF_PROCESS(x)   if (!decode.direct) { x }#else#define LOCK_O_direct   mutex_lock(outputbuf->mutex)#define UNLOCK_O_direct mutex_unlock(outputbuf->mutex)#define LOCK_O_not_direct#define UNLOCK_O_not_direct#define IF_DIRECT(x)    { x }#define IF_PROCESS(x)#endif#define MAX_DECODE_FRAMES 4096static u32_t sample_rates[] = {	11025, 22050, 32000, 44100, 48000, 8000, 12000, 16000, 24000, 96000, 88200, 176400, 192000, 352800, 384000, 705600, 768000};static u32_t sample_rate;static u32_t sample_size;static u32_t channels;static bool  bigendian;static bool  limit;static u32_t audio_left;static u32_t bytes_per_frame;typedef enum { UNKNOWN = 0, WAVE, AIFF } header_format;static void _check_header(void) {	u8_t *ptr = streambuf->readp;	unsigned bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));	header_format format = UNKNOWN;	// simple parsing of wav and aiff headers and get to samples	if (bytes > 12) {		if (!memcmp(ptr, "RIFF", 4) && !memcmp(ptr+8, "WAVE", 4)) {			LOG_INFO("WAVE");			format = WAVE;		} else if (!memcmp(ptr, "FORM", 4) && (!memcmp(ptr+8, "AIFF", 4) || !memcmp(ptr+8, "AIFC", 4))) {			LOG_INFO("AIFF");			format = AIFF;		}	}	if (format != UNKNOWN) {		ptr   += 12;		bytes -= 12;		while (bytes >= 8) {			char id[5];			unsigned len;			memcpy(id, ptr, 4);			id[4] = '\0';						if (format == WAVE) {				len = *(ptr+4) | *(ptr+5) << 8 | *(ptr+6) << 16| *(ptr+7) << 24;			} else {				len = *(ptr+4) << 24 | *(ptr+5) << 16 | *(ptr+6) << 8 | *(ptr+7);			}							LOG_INFO("header: %s len: %d", id, len);			if (format == WAVE && !memcmp(ptr, "data", 4)) {				ptr += 8;				_buf_inc_readp(streambuf, ptr - streambuf->readp);				audio_left = len;				if ((audio_left == 0xFFFFFFFF) || (audio_left == 0x7FFFEFFC)) {					LOG_INFO("wav audio size unknown: %u", audio_left);					limit = false;				} else {					LOG_INFO("wav audio size: %u", audio_left);					limit = true;				}				return;			}			if (format == AIFF && !memcmp(ptr, "SSND", 4) && bytes >= 16) {				unsigned offset = *(ptr+8) << 24 | *(ptr+9) << 16 | *(ptr+10) << 8 | *(ptr+11);				// following 4 bytes is blocksize - ignored				ptr += 8 + 8;				_buf_inc_readp(streambuf, ptr + offset - streambuf->readp);								// Reading from an upsampled stream, length could be wrong.				// Only use length in header for files.				if (stream.state == STREAMING_FILE) {					audio_left = len - 8 - offset;					LOG_INFO("aif audio size: %u", audio_left);					limit = true;				}				return;			}			if (format == WAVE && !memcmp(ptr, "fmt ", 4) && bytes >= 24) {				// override the server parsed values with our own				channels    = *(ptr+10) | *(ptr+11) << 8;				sample_rate = *(ptr+12) | *(ptr+13) << 8 | *(ptr+14) << 16 | *(ptr+15) << 24;				sample_size = (*(ptr+22) | *(ptr+23) << 8) / 8;				bigendian   = 0;				LOG_INFO("pcm size: %u rate: %u chan: %u bigendian: %u", sample_size, sample_rate, channels, bigendian);			}			if (format == AIFF && !memcmp(ptr, "COMM", 4) && bytes >= 26) {				int exponent;				// override the server parsed values with our own				channels    = *(ptr+8) << 8 | *(ptr+9);				sample_size = (*(ptr+14) << 8 | *(ptr+15)) / 8;				bigendian   = 1;				// sample rate is encoded as IEEE 80 bit extended format				// make some assumptions to simplify processing - only use first 32 bits of mantissa				exponent = ((*(ptr+16) & 0x7f) << 8 | *(ptr+17)) - 16383 - 31;				sample_rate  = *(ptr+18) << 24 | *(ptr+19) << 16 | *(ptr+20) << 8 | *(ptr+21);				while (exponent < 0) { sample_rate >>= 1; ++exponent; }				while (exponent > 0) { sample_rate <<= 1; --exponent; }				LOG_INFO("pcm size: %u rate: %u chan: %u bigendian: %u", sample_size, sample_rate, channels, bigendian);			}			if (bytes >= len + 8) {				ptr   += len + 8;				bytes -= (len + 8);			} else {				LOG_WARN("run out of data");				return;			}		}	} else {		LOG_WARN("unknown format - can't parse header");	}}static decode_state pcm_decode(void) {	unsigned bytes, in, out;	frames_t frames, count;	OPTR_T *optr;	u8_t  *iptr;	u8_t tmp[3*8];		LOCK_S;	if ( decode.new_stream && ( ( stream.state == STREAMING_FILE ) || pcm_check_header ) ) {		_check_header();	}	LOCK_O_direct;	bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));	IF_DIRECT(		out = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME;	);	IF_PROCESS(		out = process.max_in_frames;	);	if ((stream.state <= DISCONNECT && bytes < bytes_per_frame) || (limit && audio_left == 0)) {		UNLOCK_O_direct;		UNLOCK_S;		return DECODE_COMPLETE;	}	if (decode.new_stream) {		LOG_INFO("setting track_start");		LOCK_O_not_direct;		output.track_start = outputbuf->writep;		decode.new_stream = false;#if DSD		if (sample_size == 3 &&			is_stream_dop(((u8_t *)streambuf->readp) + (bigendian?0:2),						  ((u8_t *)streambuf->readp) + (bigendian?0:2) + sample_size,						  sample_size * channels, bytes / (sample_size * channels))) {			LOG_INFO("file contains DOP");			if (output.dsdfmt == DOP_S24_LE || output.dsdfmt == DOP_S24_3LE)				output.next_fmt = output.dsdfmt;			else				output.next_fmt = DOP;			output.next_sample_rate = sample_rate;			output.fade = FADE_INACTIVE;		} else {			output.next_sample_rate = decode_newstream(sample_rate, output.supported_rates);			output.next_fmt = PCM;			if (output.fade_mode) _checkfade(true);		}#else		output.next_sample_rate = decode_newstream(sample_rate, output.supported_rates);		if (output.fade_mode) _checkfade(true);#endif		UNLOCK_O_not_direct;		IF_PROCESS(			out = process.max_in_frames;		);		bytes_per_frame = channels * sample_size;	}	IF_DIRECT(		optr = (OPTR_T *)outputbuf->writep;	);	IF_PROCESS(		optr = (OPTR_T *)process.inbuf;	);	iptr = (u8_t *)streambuf->readp;	in = bytes / bytes_per_frame;	//  handle frame wrapping round end of streambuf	//  - only need if resizing of streambuf does not avoid this, could occur in localfile case	if (in == 0 && bytes > 0 && _buf_used(streambuf) >= bytes_per_frame) {		memcpy(tmp, iptr, bytes);		memcpy(tmp + bytes, streambuf->buf, bytes_per_frame - bytes);		iptr = tmp;		in = 1;	}	frames = min(in, out);	frames = min(frames, MAX_DECODE_FRAMES);	if (limit && frames * bytes_per_frame > audio_left) {		LOG_INFO("reached end of audio");		frames = audio_left / bytes_per_frame;	}		count = frames * channels;	if (channels == 2) {		if (sample_size == 1) {			while (count--) {				*optr++ = *iptr++ << (24-SHIFT);			}		} else if (sample_size == 2) {			if (bigendian) {#if BYTES_PER_FRAME == 4 && !SL_LITTLE_ENDIAN							// while loop below works as is, but memcpy is a win for that 16/16 typical case				memcpy(optr, iptr, count * BYTES_PER_FRAME / 2);#else								while (count--) {					*optr++ = *(iptr) << (24-SHIFT) | *(iptr+1) << (16-SHIFT);					iptr += 2;				}#endif							} else {#if BYTES_PER_FRAME == 4 && SL_LITTLE_ENDIAN							// while loop below works as is, but memcpy is a win for that 16/16 typical case				memcpy(optr, iptr, count * BYTES_PER_FRAME / 2);#else				while (count--) {					*optr++ = *(iptr) << (16-SHIFT) | *(iptr+1) << (24-SHIFT);					iptr += 2;				}#endif				}		} else if (sample_size == 3) {			if (bigendian) {				while (count--) {#if BYTES_PER_FRAME == 4									*optr++ = *(iptr) << 8 | *(iptr+1);#else										*optr++ = *(iptr) << 24 | *(iptr+1) << 16 | *(iptr+2) << 8;#endif						iptr += 3;				}			} else {				while (count--) {#if BYTES_PER_FRAME == 4														*optr++ = *(iptr+1) | *(iptr+2) << 8;#else					*optr++ = *(iptr) << 8 | *(iptr+1) << 16 | *(iptr+2) << 24;#endif						iptr += 3;				}			}		} else if (sample_size == 4) {			if (bigendian) {				while (count--) {#if BYTES_PER_FRAME == 4																			*optr++ = *(iptr) << 8 | *(iptr+1);#else					*optr++ = *(iptr) << 24 | *(iptr+1) << 16 | *(iptr+2) << 8 | *(iptr+3);#endif						iptr += 4;				}			} else {				while (count--) {#if BYTES_PER_FRAME == 4																								*optr++ = *(iptr+2) | *(iptr+3) << 8;#else					*optr++ = *(iptr) | *(iptr+1) << 8 | *(iptr+2) << 16 | *(iptr+3) << 24;#endif						iptr += 4;				}			}		}	} else if (channels == 1) {		if (sample_size == 1) {			while (count--) {				*optr = *iptr++ << (24-SHIFT);				*(optr+1) = *optr;				optr += 2;			}		} else if (sample_size == 2) {			if (bigendian) {				while (count--) {					*optr = *(iptr) << (24-SHIFT) | *(iptr+1) << (16-SHIFT);					*(optr+1) = *optr;					iptr += 2;					optr += 2;				}			} else {				while (count--) {					*optr = *(iptr) << (16-SHIFT) | *(iptr+1) << (24-SHIFT);					*(optr+1) = *optr;					iptr += 2;					optr += 2;				}			}		} else if (sample_size == 3) {			if (bigendian) {				while (count--) {#if BYTES_PER_FRAME == 4									*optr++ = *(iptr) << 8 | *(iptr+1);#else										*optr = *(iptr) << 24 | *(iptr+1) << 16 | *(iptr+2) << 8;#endif									*(optr+1) = *optr;					iptr += 3;					optr += 2;				}			} else {				while (count--) {#if BYTES_PER_FRAME == 4																			*optr++ = *(iptr+1) | *(iptr+2) << 8;#else										*optr = *(iptr) << 8 | *(iptr+1) << 16 | *(iptr+2) << 24;#endif									*(optr+1) = *optr;					iptr += 3;					optr += 2;				}			}		} else if (sample_size == 4) {			if (bigendian) {				while (count--) {#if BYTES_PER_FRAME == 4																			*optr++ = *(iptr) << 8 | *(iptr+1);#else										*optr++ = *(iptr) << 24 | *(iptr+1) << 16 | *(iptr+2) << 8 | *(iptr+3);#endif									*(optr+1) = *optr;					iptr += 4;					optr += 2;				}			} else {				while (count--) {#if BYTES_PER_FRAME == 4																								*optr++ = *(iptr+2) | *(iptr+3) << 8;#else										*optr++ = *(iptr) | *(iptr+1) << 8 | *(iptr+2) << 16 | *(iptr+3) << 24;#endif									*(optr+1) = *optr;					iptr += 4;					optr += 2;				}			}		}	} else {		LOG_ERROR("unsupported channels");	}		LOG_SDEBUG("decoded %u frames", frames);	_buf_inc_readp(streambuf, frames * bytes_per_frame);	if (limit) {		audio_left -= frames * bytes_per_frame;	}	IF_DIRECT(		_buf_inc_writep(outputbuf, frames * BYTES_PER_FRAME);	);	IF_PROCESS(		process.in_frames = frames;	);	UNLOCK_O_direct;	UNLOCK_S;	return DECODE_RUNNING;}static void pcm_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) {	sample_size = size - '0' + 1;	sample_rate = sample_rates[rate - '0'];	channels    = chan - '0';	bigendian   = (endianness == '0');	limit       = false;	LOG_INFO("pcm size: %u rate: %u chan: %u bigendian: %u", sample_size, sample_rate, channels, bigendian);	buf_adjust(streambuf, sample_size * channels);}static void pcm_close(void) {	buf_adjust(streambuf, 1);}struct codec *register_pcm(void) {	if ( pcm_check_header )	{		static struct codec ret = { 			'p',         // id			"wav,aif,pcm", // types			4096,        // min read			102400,      // min space			pcm_open,    // open			pcm_close,   // close			pcm_decode,  // decode		};		LOG_INFO("using pcm to decode wav,aif,pcm");		return &ret;	}	else	{		static struct codec ret = { 			'p',         // id			"aif,pcm", // types			4096,        // min read			102400,      // min space			pcm_open,    // open			pcm_close,   // close			pcm_decode,  // decode		};		LOG_INFO("using pcm to decode aif,pcm");		return &ret;	}	return NULL;}
 |