| 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
 
- #endif
 
- extern 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 4096
 
- static 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;
 
- }
 
 
  |