diff --git a/faad.c b/faad.c index a56b021..4c3a758 100644 --- a/faad.c +++ b/faad.c @@ -29,6 +29,7 @@ struct faad { NeAACDecHandle hAac; + u8_t type; // faad symbols to be dynamically loaded unsigned long (* NeAACDecGetCapabilities)(void); NeAACDecConfigurationPtr (* NeAACDecGetCurrentConfiguration)(NeAACDecHandle); @@ -36,6 +37,7 @@ NeAACDecHandle (* NeAACDecOpen)(void); void (* NeAACDecClose)(NeAACDecHandle); long (* NeAACDecInit)(NeAACDecHandle, unsigned char *, unsigned long, unsigned long *, unsigned char *); + char (* NeAACDecInit2)(NeAACDecHandle, unsigned char *pBuffer, unsigned long, unsigned long *, unsigned char *); void *(* NeAACDecDecode)(NeAACDecHandle, NeAACDecFrameInfo *, unsigned char *, unsigned long); char *(* NeAACDecGetErrorMessage)(unsigned char); }; @@ -57,45 +59,154 @@ typedef u_int32_t frames_t; +// minimal code for mp4 file parsing to extract audio config and find media data + +// adapted from faad2/common/mp4ff +u32_t mp4_desc_length(u8_t **buf) { + u8_t b; + u8_t num_bytes = 0; + u32_t length = 0; + + do { + b = **buf; + *buf += 1; + num_bytes++; + length = (length << 7) | (b & 0x7f); + } while ((b & 0x80) && num_bytes < 4); + + return length; +} + +// read mp4 header to extract config data - assume this occurs at start of streambuf +static int read_mp4_header(unsigned long *samplerate_p, unsigned char *channels_p) { + size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); + char type[5]; + u32_t len; + + while (bytes >= 8) { + len = unpackN((u32_t *)streambuf->readp); + memcpy(type, streambuf->readp + 4, 4); + type[4] = '\0'; + + // extract audio config from within esds and pass to DecInit2 + if (!strcmp(type, "esds") && bytes > len) { + u8_t *ptr = streambuf->readp + 12; + if (*ptr++ == 0x03) { + mp4_desc_length(&ptr); + ptr += 4; + } else { + ptr += 3; + } + mp4_desc_length(&ptr); + ptr += 13; + if (*ptr++ != 0x05) { + LOG_WARN("error parsing esds"); + return -1; + } + unsigned config_len = mp4_desc_length(&ptr); + if (a->NeAACDecInit2(a->hAac, ptr, config_len, samplerate_p, channels_p) != 0) { + LOG_WARN("bad audio config"); + return -1; + } + } + + // found media data, advance past header and return + // currently assume audio samples are packed with no gaps into mdat from this point - we don't use stsz, stsc to find them + if (!strcmp(type, "mdat")) { + LOG_DEBUG("type: mdat"); + _buf_inc_readp(streambuf, 8); + if (len == 1) { + _buf_inc_readp(streambuf, 8); + } + return 1; + } + + // default to consuming entire box + u32_t consume = len; + + // read into these boxes so reduce consume + if (!strcmp(type, "moov") || !strcmp(type, "trak") || !strcmp(type, "mdia") || !strcmp(type, "minf") || !strcmp(type, "stbl")) { + consume = 8; + } + if (!strcmp(type, "stsd")) consume = 16; + if (!strcmp(type, "mp4a")) consume = 36; + + if (bytes > len) { + LOG_DEBUG("type: %s len: %u consume: %u", type, len, consume); + _buf_inc_readp(streambuf, consume); + bytes -= consume; + } else if (len > streambuf->size / 2) { + LOG_WARN("type: %s len: %u - excessive length can't parse", type, len); + return -1; + } else { + break; + } + } + + return 0; +} + static void faad_decode(void) { LOCK_S; size_t bytes_total = _buf_used(streambuf); size_t bytes_wrap = min(bytes_total, _buf_cont_read(streambuf)); if (decode.new_stream) { - - // find adts sync at start of header - while (bytes_wrap >= 2 && (*(streambuf->readp) != 0xFF || (*(streambuf->readp + 1) & 0xF6) != 0xF0)) { - _buf_inc_readp(streambuf, 1); - bytes_total--; - bytes_wrap--; - } - - unsigned char channels; - unsigned long samplerate; - - long n = a->NeAACDecInit(a->hAac, streambuf->readp, bytes_wrap, &samplerate, &channels); - if (n < 0) { - LOG_WARN("error initialising - ending stream"); + int found = 0; + static unsigned char channels; + static unsigned long samplerate; + + if (a->type == '2') { + + LOG_INFO("opening atds stream"); + + while (bytes_wrap >= 2 && (*(streambuf->readp) != 0xFF || (*(streambuf->readp + 1) & 0xF6) != 0xF0)) { + _buf_inc_readp(streambuf, 1); + bytes_total--; + bytes_wrap--; + } + + long n = a->NeAACDecInit(a->hAac, streambuf->readp, bytes_wrap, &samplerate, &channels); + if (n < 0) { + found = -1; + } else { + _buf_inc_readp(streambuf, n); + found = 1; + } + + } else { + + LOG_INFO("opening mp4 stream"); + + found = read_mp4_header(&samplerate, &channels); + } + + if (found == 1) { + + LOG_INFO("samplerate: %u channels: %u", samplerate, channels); + bytes_total = _buf_used(streambuf); + bytes_wrap = min(bytes_total, _buf_cont_read(streambuf)); + + LOCK_O; + LOG_INFO("setting track_start"); + output.next_sample_rate = samplerate; + output.track_start = outputbuf->writep; + decode.new_stream = false; + UNLOCK_O; + } + + if (found == -1) { + + LOG_WARN("error reading stream header"); UNLOCK_S; LOCK_O; decode.state = DECODE_ERROR; UNLOCK_O; return; } - - _buf_inc_readp(streambuf, n); - bytes_total = _buf_used(streambuf); - bytes_wrap = min(bytes_total, _buf_cont_read(streambuf)); - - LOG_INFO("setting track_start"); - output.next_sample_rate = samplerate; - output.track_start = outputbuf->writep; - decode.new_stream = false; } NeAACDecFrameInfo info; - s32_t *iptr; if (bytes_wrap < WRAPBUF_LEN && bytes_total > WRAPBUF_LEN) { @@ -136,13 +247,13 @@ if (info.channels == 2) { while (count--) { - *optr++ = *iptr++; - *optr++ = *iptr++; + *optr++ = *iptr++ << 8; + *optr++ = *iptr++ << 8; } } else if (info.channels == 1) { while (count--) { - *optr++ = *iptr; - *optr++ = *iptr++; + *optr++ = *iptr << 8; + *optr++ = *iptr++ << 8; } } else { LOG_WARN("unsupported number of channels"); @@ -154,19 +265,11 @@ UNLOCK_O; - LOG_SDEBUG("wrote %u frames", info.samples); + LOG_SDEBUG("wrote %u frames", info.samples / info.channels); } static void faad_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { - if (size == '2') { - LOG_INFO("opening adts stream"); - } else { - LOG_ERROR("aac stream type %c not supported", size); - LOCK_O; - decode.state = DECODE_ERROR; - UNLOCK_O; - return; - } + a->type = size; if (a->hAac) { a->NeAACDecClose(a->hAac); @@ -175,7 +278,7 @@ NeAACDecConfigurationPtr conf = a->NeAACDecGetCurrentConfiguration(a->hAac); - conf->outputFormat = FAAD_FMT_32BIT; + conf->outputFormat = FAAD_FMT_24BIT; conf->downMatrix = 1; if (!a->NeAACDecSetConfiguration(a->hAac, conf)) { @@ -191,7 +294,7 @@ static bool load_faad() { void *handle = dlopen(LIBFAAD, RTLD_NOW); if (!handle) { - LOG_WARN("dlerror: %s", dlerror()); + LOG_INFO("dlerror: %s", dlerror()); return false; } @@ -204,6 +307,7 @@ a->NeAACDecOpen = dlsym(handle, "NeAACDecOpen"); a->NeAACDecClose = dlsym(handle, "NeAACDecClose"); a->NeAACDecInit = dlsym(handle, "NeAACDecInit"); + a->NeAACDecInit2 = dlsym(handle, "NeAACDecInit2"); a->NeAACDecDecode = dlsym(handle, "NeAACDecDecode"); a->NeAACDecGetErrorMessage = dlsym(handle, "NeAACDecGetErrorMessage"); diff --git a/flac.c b/flac.c index fd100d7..d6216bc 100644 --- a/flac.c +++ b/flac.c @@ -158,7 +158,7 @@ static bool load_flac() { void *handle = dlopen(LIBFLAC, RTLD_NOW); if (!handle) { - LOG_WARN("dlerror: %s", dlerror()); + LOG_INFO("dlerror: %s", dlerror()); return false; } diff --git a/mad.c b/mad.c index 8cdb33c..0fb2e34 100644 --- a/mad.c +++ b/mad.c @@ -171,7 +171,7 @@ static bool load_mad() { void *handle = dlopen(LIBMAD, RTLD_NOW); if (!handle) { - LOG_WARN("dlerror: %s", dlerror()); + LOG_INFO("dlerror: %s", dlerror()); return false; } diff --git a/output.c b/output.c index d989c6e..e1ba609 100644 --- a/output.c +++ b/output.c @@ -28,10 +28,18 @@ #define MAX_SILENCE_FRAMES 1024 +// for mmap ouput we convert to LE formats on BE devices as it is likely hardware requires LE static snd_pcm_format_t fmts_mmap[] = { SND_PCM_FORMAT_S32_LE, SND_PCM_FORMAT_S24_LE, SND_PCM_FORMAT_S24_3LE, SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_UNKNOWN }; -static snd_pcm_format_t fmts_writei[] = { SND_PCM_FORMAT_S32_LE, SND_PCM_FORMAT_UNKNOWN }; +// for non mmap output we rely on ALSA to do the conversion and just open the device in native 32bit native endian +static snd_pcm_format_t fmts_writei[] = { +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + SND_PCM_FORMAT_S32_LE, +#else + SND_PCM_FORMAT_S32_BE, +#endif + SND_PCM_FORMAT_UNKNOWN }; typedef unsigned frames_t; @@ -45,8 +53,6 @@ } alsa; struct outputstate output; - -#define FIXED_ONE 0x10000 static inline s32_t gain(s32_t gain, s32_t sample) { s64_t res = (s64_t)gain * (s64_t)sample; @@ -485,6 +491,7 @@ switch(alsa.format) { case SND_PCM_FORMAT_S16_LE: { +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) s16_t *optr = (s16_t *)(void *)outputptr; if (gainL == FIXED_ONE && gainR == FIXED_ONE) { while (cnt--) { @@ -497,10 +504,31 @@ *(optr++) = gain(gainR, *(inputptr++)) >> 16; } } +#else + u8_t *optr = (u8_t *)(void *)outputptr; + if (gainL == FIXED_ONE && gainR == FIXED_ONE) { + while (cnt--) { + *(optr++) = (*(inputptr) & 0x00ff0000) >> 16; + *(optr++) = (*(inputptr++) & 0xff000000) >> 24; + *(optr++) = (*(inputptr) & 0x00ff0000) >> 16; + *(optr++) = (*(inputptr++) & 0xff000000) >> 24; + } + } else { + while (cnt--) { + s32_t lsample = gain(gainL, *(inputptr++)); + s32_t rsample = gain(gainR, *(inputptr++)); + *(optr++) = (lsample & 0x00ff0000) >> 16; + *(optr++) = (lsample & 0xff000000) >> 24; + *(optr++) = (rsample & 0x00ff0000) >> 16; + *(optr++) = (rsample & 0xff000000) >> 24; + } + } +#endif } break; case SND_PCM_FORMAT_S24_LE: { +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) s32_t *optr = (s32_t *)(void *)outputptr; if (gainL == FIXED_ONE && gainR == FIXED_ONE) { while (cnt--) { @@ -513,6 +541,34 @@ *(optr++) = gain(gainR, *(inputptr++)) >> 8; } } +#else + u8_t *optr = (u8_t *)(void *)outputptr; + if (gainL == FIXED_ONE && gainR == FIXED_ONE) { + while (cnt--) { + *(optr++) = (*(inputptr) & 0x0000ff00) >> 8; + *(optr++) = (*(inputptr) & 0x00ff0000) >> 16; + *(optr++) = (*(inputptr++) & 0xff000000) >> 24; + *(optr++) = 0; + *(optr++) = (*(inputptr) & 0x0000ff00) >> 8; + *(optr++) = (*(inputptr) & 0x00ff0000) >> 16; + *(optr++) = (*(inputptr++) & 0xff000000) >> 24; + *(optr++) = 0; + } + } else { + while (cnt--) { + 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++) = 0; + *(optr++) = (rsample & 0x0000ff00) >> 8; + *(optr++) = (rsample & 0x00ff0000) >> 16; + *(optr++) = (rsample & 0xff000000) >> 24; + *(optr++) = 0; + } + } +#endif } break; case SND_PCM_FORMAT_S24_3LE: @@ -527,9 +583,18 @@ while (cnt >= 2) { s32_t l1 = *(inputptr++); s32_t r1 = *(inputptr++); s32_t l2 = *(inputptr++); s32_t r2 = *(inputptr++); +#if (__BYTE_ORDER__ == __ORDER_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; } @@ -554,9 +619,18 @@ 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 (__BYTE_ORDER__ == __ORDER_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; } @@ -577,6 +651,7 @@ break; case SND_PCM_FORMAT_S32_LE: { +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) s32_t *optr = (s32_t *)(void *)outputptr; if (gainL == FIXED_ONE && gainR == FIXED_ONE) { memcpy(outputptr, inputptr, cnt * BYTES_PER_FRAME); @@ -586,6 +661,34 @@ *(optr++) = gain(gainR, *(inputptr++)); } } +#else + u8_t *optr = (u8_t *)(void *)outputptr; + if (gainL == FIXED_ONE && gainR == FIXED_ONE) { + while (cnt--) { + *(optr++) = (*(inputptr) & 0x000000ff); + *(optr++) = (*(inputptr) & 0x0000ff00) >> 8; + *(optr++) = (*(inputptr) & 0x00ff0000) >> 16; + *(optr++) = (*(inputptr++) & 0xff000000) >> 24; + *(optr++) = (*(inputptr) & 0x000000ff); + *(optr++) = (*(inputptr) & 0x0000ff00) >> 8; + *(optr++) = (*(inputptr) & 0x00ff0000) >> 16; + *(optr++) = (*(inputptr++) & 0xff000000) >> 24; + } + } else { + while (cnt--) { + s32_t lsample = gain(gainL, *(inputptr++)); + s32_t rsample = gain(gainR, *(inputptr++)); + *(optr++) = (lsample & 0x000000ff); + *(optr++) = (lsample & 0x0000ff00) >> 8; + *(optr++) = (lsample & 0x00ff0000) >> 16; + *(optr++) = (lsample & 0xff000000) >> 24; + *(optr++) = (rsample & 0x000000ff); + *(optr++) = (rsample & 0x0000ff00) >> 8; + *(optr++) = (rsample & 0x00ff0000) >> 16; + *(optr++) = (rsample & 0xff000000) >> 24; + } + } +#endif } break; default: diff --git a/slimproto.c b/slimproto.c index abf4650..6a5100b 100644 --- a/slimproto.c +++ b/slimproto.c @@ -78,28 +78,8 @@ } } -inline void packN(u32_t *dest, u32_t val) { - u8_t *ptr = (u8_t *)dest; - *(ptr) = (val >> 24) & 0xFF; *(ptr+1) = (val >> 16) & 0xFF; *(ptr+2) = (val >> 8) & 0xFF; *(ptr+3) = val & 0xFF; -} - -inline void packn(u16_t *dest, u16_t val) { - u8_t *ptr = (u8_t *)dest; - *(ptr) = (val >> 8) & 0xFF; *(ptr+1) = val & 0xFF; -} - -inline u32_t unpackN(u32_t *src) { - u8_t *ptr = (u8_t *)src; - return *(ptr) << 24 | *(ptr+1) << 16 | *(ptr+2) << 8 | *(ptr+3); -} - -inline u16_t unpackn(u16_t *src) { - u8_t *ptr = (u8_t *)src; - return *(ptr) << 8 | *(ptr+1); -} - static void sendHELO(bool reconnect, const char *cap, u8_t mac[6]) { - const char *capbase = "Model=squeezelite,ModelName=SqueezeLite,AccuratePlayPoints=1,"; + const char *capbase = "Model=squeezelite,ModelName=SqueezeLite,AccuratePlayPoints=1,HasDigitalOut=1,"; struct HELO_packet pkt = { .opcode = "HELO", @@ -311,11 +291,11 @@ audg->gainL = unpackN(&audg->gainL); audg->gainR = unpackN(&audg->gainR); - LOG_INFO("audg gainL: %u gainR: %u", audg->gainL, audg->gainR); + LOG_INFO("audg gainL: %u gainR: %u adjust: %u", audg->gainL, audg->gainR, audg->adjust); LOCK_O; - output.gainL = audg->gainL; - output.gainR = audg->gainR; + output.gainL = audg->adjust ? audg->gainL : FIXED_ONE; + output.gainR = audg->adjust ? audg->gainR : FIXED_ONE; UNLOCK_O; } @@ -394,8 +374,8 @@ } if (pollinfo[1].revents) { - eventfd_t val; - eventfd_read(efd, &val); + uint64_t u; + u = read(efd, &u, sizeof(uint64_t)); wake = true; } } @@ -502,7 +482,8 @@ // called from other threads to wake state machine above void wake_controller(void) { - eventfd_write(efd, 1); + uint64_t u = 1; + u = write(efd, &u, sizeof(uint64_t)); } in_addr_t discover_server(void) { diff --git a/slimproto.h b/slimproto.h index 6f0a09c..197b070 100644 --- a/slimproto.h +++ b/slimproto.h @@ -114,7 +114,7 @@ char opcode[4]; u32_t old_gainL; // unused u32_t old_gainR; // unused - u8_t fixed_digital; // unused + u8_t adjust; u8_t preamp; // unused u32_t gainL; u32_t gainR; diff --git a/squeezelite.h b/squeezelite.h index 8ff7d5d..d16b19e 100644 --- a/squeezelite.h +++ b/squeezelite.h @@ -35,7 +35,7 @@ #include #include -#define VERSION "v0.4beta2" +#define VERSION "v0.5beta1" #define STREAMBUF_SIZE (2 * 1024 * 1024) #define OUTPUTBUF_SIZE (44100 * 8 * 10) @@ -48,6 +48,8 @@ #define ALSA_BUFFER_TIME 20000 #define ALSA_PERIOD_COUNT 4 + +#define FIXED_ONE 0x10000 typedef u_int8_t u8_t; typedef u_int16_t u16_t; @@ -76,6 +78,10 @@ // utils.c (non logging) u32_t gettime_ms(void); void get_mac(u8_t *mac); +inline void packN(u32_t *dest, u32_t val); +inline void packn(u16_t *dest, u16_t val); +inline u32_t unpackN(u32_t *src); +inline u16_t unpackn(u16_t *src); // buffer.c struct buffer { diff --git a/utils.c b/utils.c index a04238c..38be6cc 100644 --- a/utils.c +++ b/utils.c @@ -84,3 +84,23 @@ close(s); } +// pack/unpack to network byte order +inline void packN(u32_t *dest, u32_t val) { + u8_t *ptr = (u8_t *)dest; + *(ptr) = (val >> 24) & 0xFF; *(ptr+1) = (val >> 16) & 0xFF; *(ptr+2) = (val >> 8) & 0xFF; *(ptr+3) = val & 0xFF; +} + +inline void packn(u16_t *dest, u16_t val) { + u8_t *ptr = (u8_t *)dest; + *(ptr) = (val >> 8) & 0xFF; *(ptr+1) = val & 0xFF; +} + +inline u32_t unpackN(u32_t *src) { + u8_t *ptr = (u8_t *)src; + return *(ptr) << 24 | *(ptr+1) << 16 | *(ptr+2) << 8 | *(ptr+3); +} + +inline u16_t unpackn(u16_t *src) { + u8_t *ptr = (u8_t *)src; + return *(ptr) << 8 | *(ptr+1); +} diff --git a/vorbis.c b/vorbis.c index db48767..93707b6 100644 --- a/vorbis.c +++ b/vorbis.c @@ -184,7 +184,7 @@ if (handle) { tremor = true; } else { - LOG_WARN("dlerror: %s", dlerror()); + LOG_INFO("dlerror: %s", dlerror()); return false; } }