0.5beta1 checkin
Support for AAC in MP4 file playback (makes several simplifying assumptions)
Remove glibc specific eventfd calls so compiles with ulibc
Add support for big endian processors
Add ablity to fix volume to 100% via LMS setting
Hide some more warnings when libraries not present
Adrian Smith
11 years ago
28 | 28 | |
29 | 29 | struct faad { |
30 | 30 | NeAACDecHandle hAac; |
31 | u8_t type; | |
31 | 32 | // faad symbols to be dynamically loaded |
32 | 33 | unsigned long (* NeAACDecGetCapabilities)(void); |
33 | 34 | NeAACDecConfigurationPtr (* NeAACDecGetCurrentConfiguration)(NeAACDecHandle); |
35 | 36 | NeAACDecHandle (* NeAACDecOpen)(void); |
36 | 37 | void (* NeAACDecClose)(NeAACDecHandle); |
37 | 38 | long (* NeAACDecInit)(NeAACDecHandle, unsigned char *, unsigned long, unsigned long *, unsigned char *); |
39 | char (* NeAACDecInit2)(NeAACDecHandle, unsigned char *pBuffer, unsigned long, unsigned long *, unsigned char *); | |
38 | 40 | void *(* NeAACDecDecode)(NeAACDecHandle, NeAACDecFrameInfo *, unsigned char *, unsigned long); |
39 | 41 | char *(* NeAACDecGetErrorMessage)(unsigned char); |
40 | 42 | }; |
56 | 58 | |
57 | 59 | typedef u_int32_t frames_t; |
58 | 60 | |
61 | // minimal code for mp4 file parsing to extract audio config and find media data | |
62 | ||
63 | // adapted from faad2/common/mp4ff | |
64 | u32_t mp4_desc_length(u8_t **buf) { | |
65 | u8_t b; | |
66 | u8_t num_bytes = 0; | |
67 | u32_t length = 0; | |
68 | ||
69 | do { | |
70 | b = **buf; | |
71 | *buf += 1; | |
72 | num_bytes++; | |
73 | length = (length << 7) | (b & 0x7f); | |
74 | } while ((b & 0x80) && num_bytes < 4); | |
75 | ||
76 | return length; | |
77 | } | |
78 | ||
79 | // read mp4 header to extract config data - assume this occurs at start of streambuf | |
80 | static int read_mp4_header(unsigned long *samplerate_p, unsigned char *channels_p) { | |
81 | size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); | |
82 | char type[5]; | |
83 | u32_t len; | |
84 | ||
85 | while (bytes >= 8) { | |
86 | len = unpackN((u32_t *)streambuf->readp); | |
87 | memcpy(type, streambuf->readp + 4, 4); | |
88 | type[4] = '\0'; | |
89 | ||
90 | // extract audio config from within esds and pass to DecInit2 | |
91 | if (!strcmp(type, "esds") && bytes > len) { | |
92 | u8_t *ptr = streambuf->readp + 12; | |
93 | if (*ptr++ == 0x03) { | |
94 | mp4_desc_length(&ptr); | |
95 | ptr += 4; | |
96 | } else { | |
97 | ptr += 3; | |
98 | } | |
99 | mp4_desc_length(&ptr); | |
100 | ptr += 13; | |
101 | if (*ptr++ != 0x05) { | |
102 | LOG_WARN("error parsing esds"); | |
103 | return -1; | |
104 | } | |
105 | unsigned config_len = mp4_desc_length(&ptr); | |
106 | if (a->NeAACDecInit2(a->hAac, ptr, config_len, samplerate_p, channels_p) != 0) { | |
107 | LOG_WARN("bad audio config"); | |
108 | return -1; | |
109 | } | |
110 | } | |
111 | ||
112 | // found media data, advance past header and return | |
113 | // currently assume audio samples are packed with no gaps into mdat from this point - we don't use stsz, stsc to find them | |
114 | if (!strcmp(type, "mdat")) { | |
115 | LOG_DEBUG("type: mdat"); | |
116 | _buf_inc_readp(streambuf, 8); | |
117 | if (len == 1) { | |
118 | _buf_inc_readp(streambuf, 8); | |
119 | } | |
120 | return 1; | |
121 | } | |
122 | ||
123 | // default to consuming entire box | |
124 | u32_t consume = len; | |
125 | ||
126 | // read into these boxes so reduce consume | |
127 | if (!strcmp(type, "moov") || !strcmp(type, "trak") || !strcmp(type, "mdia") || !strcmp(type, "minf") || !strcmp(type, "stbl")) { | |
128 | consume = 8; | |
129 | } | |
130 | if (!strcmp(type, "stsd")) consume = 16; | |
131 | if (!strcmp(type, "mp4a")) consume = 36; | |
132 | ||
133 | if (bytes > len) { | |
134 | LOG_DEBUG("type: %s len: %u consume: %u", type, len, consume); | |
135 | _buf_inc_readp(streambuf, consume); | |
136 | bytes -= consume; | |
137 | } else if (len > streambuf->size / 2) { | |
138 | LOG_WARN("type: %s len: %u - excessive length can't parse", type, len); | |
139 | return -1; | |
140 | } else { | |
141 | break; | |
142 | } | |
143 | } | |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
59 | 148 | static void faad_decode(void) { |
60 | 149 | LOCK_S; |
61 | 150 | size_t bytes_total = _buf_used(streambuf); |
62 | 151 | size_t bytes_wrap = min(bytes_total, _buf_cont_read(streambuf)); |
63 | 152 | |
64 | 153 | if (decode.new_stream) { |
65 | ||
66 | // find adts sync at start of header | |
67 | while (bytes_wrap >= 2 && (*(streambuf->readp) != 0xFF || (*(streambuf->readp + 1) & 0xF6) != 0xF0)) { | |
68 | _buf_inc_readp(streambuf, 1); | |
69 | bytes_total--; | |
70 | bytes_wrap--; | |
71 | } | |
72 | ||
73 | unsigned char channels; | |
74 | unsigned long samplerate; | |
75 | ||
76 | long n = a->NeAACDecInit(a->hAac, streambuf->readp, bytes_wrap, &samplerate, &channels); | |
77 | if (n < 0) { | |
78 | LOG_WARN("error initialising - ending stream"); | |
154 | int found = 0; | |
155 | static unsigned char channels; | |
156 | static unsigned long samplerate; | |
157 | ||
158 | if (a->type == '2') { | |
159 | ||
160 | LOG_INFO("opening atds stream"); | |
161 | ||
162 | while (bytes_wrap >= 2 && (*(streambuf->readp) != 0xFF || (*(streambuf->readp + 1) & 0xF6) != 0xF0)) { | |
163 | _buf_inc_readp(streambuf, 1); | |
164 | bytes_total--; | |
165 | bytes_wrap--; | |
166 | } | |
167 | ||
168 | long n = a->NeAACDecInit(a->hAac, streambuf->readp, bytes_wrap, &samplerate, &channels); | |
169 | if (n < 0) { | |
170 | found = -1; | |
171 | } else { | |
172 | _buf_inc_readp(streambuf, n); | |
173 | found = 1; | |
174 | } | |
175 | ||
176 | } else { | |
177 | ||
178 | LOG_INFO("opening mp4 stream"); | |
179 | ||
180 | found = read_mp4_header(&samplerate, &channels); | |
181 | } | |
182 | ||
183 | if (found == 1) { | |
184 | ||
185 | LOG_INFO("samplerate: %u channels: %u", samplerate, channels); | |
186 | bytes_total = _buf_used(streambuf); | |
187 | bytes_wrap = min(bytes_total, _buf_cont_read(streambuf)); | |
188 | ||
189 | LOCK_O; | |
190 | LOG_INFO("setting track_start"); | |
191 | output.next_sample_rate = samplerate; | |
192 | output.track_start = outputbuf->writep; | |
193 | decode.new_stream = false; | |
194 | UNLOCK_O; | |
195 | } | |
196 | ||
197 | if (found == -1) { | |
198 | ||
199 | LOG_WARN("error reading stream header"); | |
79 | 200 | UNLOCK_S; |
80 | 201 | LOCK_O; |
81 | 202 | decode.state = DECODE_ERROR; |
82 | 203 | UNLOCK_O; |
83 | 204 | return; |
84 | 205 | } |
85 | ||
86 | _buf_inc_readp(streambuf, n); | |
87 | bytes_total = _buf_used(streambuf); | |
88 | bytes_wrap = min(bytes_total, _buf_cont_read(streambuf)); | |
89 | ||
90 | LOG_INFO("setting track_start"); | |
91 | output.next_sample_rate = samplerate; | |
92 | output.track_start = outputbuf->writep; | |
93 | decode.new_stream = false; | |
94 | 206 | } |
95 | 207 | |
96 | 208 | NeAACDecFrameInfo info; |
97 | ||
98 | 209 | s32_t *iptr; |
99 | 210 | |
100 | 211 | if (bytes_wrap < WRAPBUF_LEN && bytes_total > WRAPBUF_LEN) { |
135 | 246 | |
136 | 247 | if (info.channels == 2) { |
137 | 248 | while (count--) { |
138 | *optr++ = *iptr++; | |
139 | *optr++ = *iptr++; | |
249 | *optr++ = *iptr++ << 8; | |
250 | *optr++ = *iptr++ << 8; | |
140 | 251 | } |
141 | 252 | } else if (info.channels == 1) { |
142 | 253 | while (count--) { |
143 | *optr++ = *iptr; | |
144 | *optr++ = *iptr++; | |
254 | *optr++ = *iptr << 8; | |
255 | *optr++ = *iptr++ << 8; | |
145 | 256 | } |
146 | 257 | } else { |
147 | 258 | LOG_WARN("unsupported number of channels"); |
153 | 264 | |
154 | 265 | UNLOCK_O; |
155 | 266 | |
156 | LOG_SDEBUG("wrote %u frames", info.samples); | |
267 | LOG_SDEBUG("wrote %u frames", info.samples / info.channels); | |
157 | 268 | } |
158 | 269 | |
159 | 270 | static void faad_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { |
160 | if (size == '2') { | |
161 | LOG_INFO("opening adts stream"); | |
162 | } else { | |
163 | LOG_ERROR("aac stream type %c not supported", size); | |
164 | LOCK_O; | |
165 | decode.state = DECODE_ERROR; | |
166 | UNLOCK_O; | |
167 | return; | |
168 | } | |
271 | a->type = size; | |
169 | 272 | |
170 | 273 | if (a->hAac) { |
171 | 274 | a->NeAACDecClose(a->hAac); |
174 | 277 | |
175 | 278 | NeAACDecConfigurationPtr conf = a->NeAACDecGetCurrentConfiguration(a->hAac); |
176 | 279 | |
177 | conf->outputFormat = FAAD_FMT_32BIT; | |
280 | conf->outputFormat = FAAD_FMT_24BIT; | |
178 | 281 | conf->downMatrix = 1; |
179 | 282 | |
180 | 283 | if (!a->NeAACDecSetConfiguration(a->hAac, conf)) { |
190 | 293 | static bool load_faad() { |
191 | 294 | void *handle = dlopen(LIBFAAD, RTLD_NOW); |
192 | 295 | if (!handle) { |
193 | LOG_WARN("dlerror: %s", dlerror()); | |
296 | LOG_INFO("dlerror: %s", dlerror()); | |
194 | 297 | return false; |
195 | 298 | } |
196 | 299 | |
203 | 306 | a->NeAACDecOpen = dlsym(handle, "NeAACDecOpen"); |
204 | 307 | a->NeAACDecClose = dlsym(handle, "NeAACDecClose"); |
205 | 308 | a->NeAACDecInit = dlsym(handle, "NeAACDecInit"); |
309 | a->NeAACDecInit2 = dlsym(handle, "NeAACDecInit2"); | |
206 | 310 | a->NeAACDecDecode = dlsym(handle, "NeAACDecDecode"); |
207 | 311 | a->NeAACDecGetErrorMessage = dlsym(handle, "NeAACDecGetErrorMessage"); |
208 | 312 |
157 | 157 | static bool load_flac() { |
158 | 158 | void *handle = dlopen(LIBFLAC, RTLD_NOW); |
159 | 159 | if (!handle) { |
160 | LOG_WARN("dlerror: %s", dlerror()); | |
160 | LOG_INFO("dlerror: %s", dlerror()); | |
161 | 161 | return false; |
162 | 162 | } |
163 | 163 |
170 | 170 | static bool load_mad() { |
171 | 171 | void *handle = dlopen(LIBMAD, RTLD_NOW); |
172 | 172 | if (!handle) { |
173 | LOG_WARN("dlerror: %s", dlerror()); | |
173 | LOG_INFO("dlerror: %s", dlerror()); | |
174 | 174 | return false; |
175 | 175 | } |
176 | 176 |
27 | 27 | |
28 | 28 | #define MAX_SILENCE_FRAMES 1024 |
29 | 29 | |
30 | // for mmap ouput we convert to LE formats on BE devices as it is likely hardware requires LE | |
30 | 31 | 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, |
31 | 32 | SND_PCM_FORMAT_UNKNOWN }; |
32 | 33 | |
33 | static snd_pcm_format_t fmts_writei[] = { SND_PCM_FORMAT_S32_LE, SND_PCM_FORMAT_UNKNOWN }; | |
34 | // for non mmap output we rely on ALSA to do the conversion and just open the device in native 32bit native endian | |
35 | static snd_pcm_format_t fmts_writei[] = { | |
36 | #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) | |
37 | SND_PCM_FORMAT_S32_LE, | |
38 | #else | |
39 | SND_PCM_FORMAT_S32_BE, | |
40 | #endif | |
41 | SND_PCM_FORMAT_UNKNOWN }; | |
34 | 42 | |
35 | 43 | typedef unsigned frames_t; |
36 | 44 | |
44 | 52 | } alsa; |
45 | 53 | |
46 | 54 | struct outputstate output; |
47 | ||
48 | #define FIXED_ONE 0x10000 | |
49 | 55 | |
50 | 56 | static inline s32_t gain(s32_t gain, s32_t sample) { |
51 | 57 | s64_t res = (s64_t)gain * (s64_t)sample; |
484 | 490 | switch(alsa.format) { |
485 | 491 | case SND_PCM_FORMAT_S16_LE: |
486 | 492 | { |
493 | #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) | |
487 | 494 | s16_t *optr = (s16_t *)(void *)outputptr; |
488 | 495 | if (gainL == FIXED_ONE && gainR == FIXED_ONE) { |
489 | 496 | while (cnt--) { |
496 | 503 | *(optr++) = gain(gainR, *(inputptr++)) >> 16; |
497 | 504 | } |
498 | 505 | } |
506 | #else | |
507 | u8_t *optr = (u8_t *)(void *)outputptr; | |
508 | if (gainL == FIXED_ONE && gainR == FIXED_ONE) { | |
509 | while (cnt--) { | |
510 | *(optr++) = (*(inputptr) & 0x00ff0000) >> 16; | |
511 | *(optr++) = (*(inputptr++) & 0xff000000) >> 24; | |
512 | *(optr++) = (*(inputptr) & 0x00ff0000) >> 16; | |
513 | *(optr++) = (*(inputptr++) & 0xff000000) >> 24; | |
514 | } | |
515 | } else { | |
516 | while (cnt--) { | |
517 | s32_t lsample = gain(gainL, *(inputptr++)); | |
518 | s32_t rsample = gain(gainR, *(inputptr++)); | |
519 | *(optr++) = (lsample & 0x00ff0000) >> 16; | |
520 | *(optr++) = (lsample & 0xff000000) >> 24; | |
521 | *(optr++) = (rsample & 0x00ff0000) >> 16; | |
522 | *(optr++) = (rsample & 0xff000000) >> 24; | |
523 | } | |
524 | } | |
525 | #endif | |
499 | 526 | } |
500 | 527 | break; |
501 | 528 | case SND_PCM_FORMAT_S24_LE: |
502 | 529 | { |
530 | #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) | |
503 | 531 | s32_t *optr = (s32_t *)(void *)outputptr; |
504 | 532 | if (gainL == FIXED_ONE && gainR == FIXED_ONE) { |
505 | 533 | while (cnt--) { |
512 | 540 | *(optr++) = gain(gainR, *(inputptr++)) >> 8; |
513 | 541 | } |
514 | 542 | } |
543 | #else | |
544 | u8_t *optr = (u8_t *)(void *)outputptr; | |
545 | if (gainL == FIXED_ONE && gainR == FIXED_ONE) { | |
546 | while (cnt--) { | |
547 | *(optr++) = (*(inputptr) & 0x0000ff00) >> 8; | |
548 | *(optr++) = (*(inputptr) & 0x00ff0000) >> 16; | |
549 | *(optr++) = (*(inputptr++) & 0xff000000) >> 24; | |
550 | *(optr++) = 0; | |
551 | *(optr++) = (*(inputptr) & 0x0000ff00) >> 8; | |
552 | *(optr++) = (*(inputptr) & 0x00ff0000) >> 16; | |
553 | *(optr++) = (*(inputptr++) & 0xff000000) >> 24; | |
554 | *(optr++) = 0; | |
555 | } | |
556 | } else { | |
557 | while (cnt--) { | |
558 | s32_t lsample = gain(gainL, *(inputptr++)); | |
559 | s32_t rsample = gain(gainR, *(inputptr++)); | |
560 | *(optr++) = (lsample & 0x0000ff00) >> 8; | |
561 | *(optr++) = (lsample & 0x00ff0000) >> 16; | |
562 | *(optr++) = (lsample & 0xff000000) >> 24; | |
563 | *(optr++) = 0; | |
564 | *(optr++) = (rsample & 0x0000ff00) >> 8; | |
565 | *(optr++) = (rsample & 0x00ff0000) >> 16; | |
566 | *(optr++) = (rsample & 0xff000000) >> 24; | |
567 | *(optr++) = 0; | |
568 | } | |
569 | } | |
570 | #endif | |
515 | 571 | } |
516 | 572 | break; |
517 | 573 | case SND_PCM_FORMAT_S24_3LE: |
526 | 582 | while (cnt >= 2) { |
527 | 583 | s32_t l1 = *(inputptr++); s32_t r1 = *(inputptr++); |
528 | 584 | s32_t l2 = *(inputptr++); s32_t r2 = *(inputptr++); |
585 | #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) | |
529 | 586 | *(o_ptr++) = (l1 & 0xffffff00) >> 8 | (r1 & 0x0000ff00) << 16; |
530 | 587 | *(o_ptr++) = (r1 & 0xffff0000) >> 16 | (l2 & 0x00ffff00) << 8; |
531 | 588 | *(o_ptr++) = (l2 & 0xff000000) >> 24 | (r2 & 0xffffff00); |
589 | #else | |
590 | *(o_ptr++) = (l1 & 0x0000ff00) << 16 | (l1 & 0x00ff0000) | (l1 & 0xff000000) >> 16 | | |
591 | (r1 & 0x0000ff00) >> 8; | |
592 | *(o_ptr++) = (r1 & 0x00ff0000) << 8 | (r1 & 0xff000000) >> 8 | (l2 & 0x0000ff00) | | |
593 | (l2 & 0x00ff0000) >> 16; | |
594 | *(o_ptr++) = (l2 & 0xff000000) | (r2 & 0x0000ff00) << 8 | (r2 & 0x00ff0000) >> 8 | | |
595 | (r2 & 0xff000000) >> 24; | |
596 | #endif | |
532 | 597 | optr += 12; |
533 | 598 | cnt -= 2; |
534 | 599 | } |
553 | 618 | while (cnt >= 2) { |
554 | 619 | s32_t l1 = gain(gainL, *(inputptr++)); s32_t r1 = gain(gainR, *(inputptr++)); |
555 | 620 | s32_t l2 = gain(gainL, *(inputptr++)); s32_t r2 = gain(gainR, *(inputptr++)); |
621 | #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) | |
556 | 622 | *(o_ptr++) = (l1 & 0xffffff00) >> 8 | (r1 & 0x0000ff00) << 16; |
557 | 623 | *(o_ptr++) = (r1 & 0xffff0000) >> 16 | (l2 & 0x00ffff00) << 8; |
558 | 624 | *(o_ptr++) = (l2 & 0xff000000) >> 24 | (r2 & 0xffffff00); |
625 | #else | |
626 | *(o_ptr++) = (l1 & 0x0000ff00) << 16 | (l1 & 0x00ff0000) | (l1 & 0xff000000) >> 16 | | |
627 | (r1 & 0x0000ff00) >> 8; | |
628 | *(o_ptr++) = (r1 & 0x00ff0000) << 8 | (r1 & 0xff000000) >> 8 | (l2 & 0x0000ff00) | | |
629 | (l2 & 0x00ff0000) >> 16; | |
630 | *(o_ptr++) = (l2 & 0xff000000) | (r2 & 0x0000ff00) << 8 | (r2 & 0x00ff0000) >> 8 | | |
631 | (r2 & 0xff000000) >> 24; | |
632 | #endif | |
559 | 633 | optr += 12; |
560 | 634 | cnt -= 2; |
561 | 635 | } |
576 | 650 | break; |
577 | 651 | case SND_PCM_FORMAT_S32_LE: |
578 | 652 | { |
653 | #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) | |
579 | 654 | s32_t *optr = (s32_t *)(void *)outputptr; |
580 | 655 | if (gainL == FIXED_ONE && gainR == FIXED_ONE) { |
581 | 656 | memcpy(outputptr, inputptr, cnt * BYTES_PER_FRAME); |
585 | 660 | *(optr++) = gain(gainR, *(inputptr++)); |
586 | 661 | } |
587 | 662 | } |
663 | #else | |
664 | u8_t *optr = (u8_t *)(void *)outputptr; | |
665 | if (gainL == FIXED_ONE && gainR == FIXED_ONE) { | |
666 | while (cnt--) { | |
667 | *(optr++) = (*(inputptr) & 0x000000ff); | |
668 | *(optr++) = (*(inputptr) & 0x0000ff00) >> 8; | |
669 | *(optr++) = (*(inputptr) & 0x00ff0000) >> 16; | |
670 | *(optr++) = (*(inputptr++) & 0xff000000) >> 24; | |
671 | *(optr++) = (*(inputptr) & 0x000000ff); | |
672 | *(optr++) = (*(inputptr) & 0x0000ff00) >> 8; | |
673 | *(optr++) = (*(inputptr) & 0x00ff0000) >> 16; | |
674 | *(optr++) = (*(inputptr++) & 0xff000000) >> 24; | |
675 | } | |
676 | } else { | |
677 | while (cnt--) { | |
678 | s32_t lsample = gain(gainL, *(inputptr++)); | |
679 | s32_t rsample = gain(gainR, *(inputptr++)); | |
680 | *(optr++) = (lsample & 0x000000ff); | |
681 | *(optr++) = (lsample & 0x0000ff00) >> 8; | |
682 | *(optr++) = (lsample & 0x00ff0000) >> 16; | |
683 | *(optr++) = (lsample & 0xff000000) >> 24; | |
684 | *(optr++) = (rsample & 0x000000ff); | |
685 | *(optr++) = (rsample & 0x0000ff00) >> 8; | |
686 | *(optr++) = (rsample & 0x00ff0000) >> 16; | |
687 | *(optr++) = (rsample & 0xff000000) >> 24; | |
688 | } | |
689 | } | |
690 | #endif | |
588 | 691 | } |
589 | 692 | break; |
590 | 693 | default: |
77 | 77 | } |
78 | 78 | } |
79 | 79 | |
80 | inline void packN(u32_t *dest, u32_t val) { | |
81 | u8_t *ptr = (u8_t *)dest; | |
82 | *(ptr) = (val >> 24) & 0xFF; *(ptr+1) = (val >> 16) & 0xFF; *(ptr+2) = (val >> 8) & 0xFF; *(ptr+3) = val & 0xFF; | |
83 | } | |
84 | ||
85 | inline void packn(u16_t *dest, u16_t val) { | |
86 | u8_t *ptr = (u8_t *)dest; | |
87 | *(ptr) = (val >> 8) & 0xFF; *(ptr+1) = val & 0xFF; | |
88 | } | |
89 | ||
90 | inline u32_t unpackN(u32_t *src) { | |
91 | u8_t *ptr = (u8_t *)src; | |
92 | return *(ptr) << 24 | *(ptr+1) << 16 | *(ptr+2) << 8 | *(ptr+3); | |
93 | } | |
94 | ||
95 | inline u16_t unpackn(u16_t *src) { | |
96 | u8_t *ptr = (u8_t *)src; | |
97 | return *(ptr) << 8 | *(ptr+1); | |
98 | } | |
99 | ||
100 | 80 | static void sendHELO(bool reconnect, const char *cap, u8_t mac[6]) { |
101 | const char *capbase = "Model=squeezelite,ModelName=SqueezeLite,AccuratePlayPoints=1,"; | |
81 | const char *capbase = "Model=squeezelite,ModelName=SqueezeLite,AccuratePlayPoints=1,HasDigitalOut=1,"; | |
102 | 82 | |
103 | 83 | struct HELO_packet pkt = { |
104 | 84 | .opcode = "HELO", |
310 | 290 | audg->gainL = unpackN(&audg->gainL); |
311 | 291 | audg->gainR = unpackN(&audg->gainR); |
312 | 292 | |
313 | LOG_INFO("audg gainL: %u gainR: %u", audg->gainL, audg->gainR); | |
293 | LOG_INFO("audg gainL: %u gainR: %u adjust: %u", audg->gainL, audg->gainR, audg->adjust); | |
314 | 294 | |
315 | 295 | LOCK_O; |
316 | output.gainL = audg->gainL; | |
317 | output.gainR = audg->gainR; | |
296 | output.gainL = audg->adjust ? audg->gainL : FIXED_ONE; | |
297 | output.gainR = audg->adjust ? audg->gainR : FIXED_ONE; | |
318 | 298 | UNLOCK_O; |
319 | 299 | } |
320 | 300 | |
393 | 373 | } |
394 | 374 | |
395 | 375 | if (pollinfo[1].revents) { |
396 | eventfd_t val; | |
397 | eventfd_read(efd, &val); | |
376 | uint64_t u; | |
377 | u = read(efd, &u, sizeof(uint64_t)); | |
398 | 378 | wake = true; |
399 | 379 | } |
400 | 380 | } |
501 | 481 | |
502 | 482 | // called from other threads to wake state machine above |
503 | 483 | void wake_controller(void) { |
504 | eventfd_write(efd, 1); | |
484 | uint64_t u = 1; | |
485 | u = write(efd, &u, sizeof(uint64_t)); | |
505 | 486 | } |
506 | 487 | |
507 | 488 | in_addr_t discover_server(void) { |
113 | 113 | char opcode[4]; |
114 | 114 | u32_t old_gainL; // unused |
115 | 115 | u32_t old_gainR; // unused |
116 | u8_t fixed_digital; // unused | |
116 | u8_t adjust; | |
117 | 117 | u8_t preamp; // unused |
118 | 118 | u32_t gainL; |
119 | 119 | u32_t gainR; |
34 | 34 | #include <sys/types.h> |
35 | 35 | #include <poll.h> |
36 | 36 | |
37 | #define VERSION "v0.4beta2" | |
37 | #define VERSION "v0.5beta1" | |
38 | 38 | |
39 | 39 | #define STREAMBUF_SIZE (2 * 1024 * 1024) |
40 | 40 | #define OUTPUTBUF_SIZE (44100 * 8 * 10) |
47 | 47 | |
48 | 48 | #define ALSA_BUFFER_TIME 20000 |
49 | 49 | #define ALSA_PERIOD_COUNT 4 |
50 | ||
51 | #define FIXED_ONE 0x10000 | |
50 | 52 | |
51 | 53 | typedef u_int8_t u8_t; |
52 | 54 | typedef u_int16_t u16_t; |
75 | 77 | // utils.c (non logging) |
76 | 78 | u32_t gettime_ms(void); |
77 | 79 | void get_mac(u8_t *mac); |
80 | inline void packN(u32_t *dest, u32_t val); | |
81 | inline void packn(u16_t *dest, u16_t val); | |
82 | inline u32_t unpackN(u32_t *src); | |
83 | inline u16_t unpackn(u16_t *src); | |
78 | 84 | |
79 | 85 | // buffer.c |
80 | 86 | struct buffer { |
83 | 83 | close(s); |
84 | 84 | } |
85 | 85 | |
86 | // pack/unpack to network byte order | |
87 | inline void packN(u32_t *dest, u32_t val) { | |
88 | u8_t *ptr = (u8_t *)dest; | |
89 | *(ptr) = (val >> 24) & 0xFF; *(ptr+1) = (val >> 16) & 0xFF; *(ptr+2) = (val >> 8) & 0xFF; *(ptr+3) = val & 0xFF; | |
90 | } | |
91 | ||
92 | inline void packn(u16_t *dest, u16_t val) { | |
93 | u8_t *ptr = (u8_t *)dest; | |
94 | *(ptr) = (val >> 8) & 0xFF; *(ptr+1) = val & 0xFF; | |
95 | } | |
96 | ||
97 | inline u32_t unpackN(u32_t *src) { | |
98 | u8_t *ptr = (u8_t *)src; | |
99 | return *(ptr) << 24 | *(ptr+1) << 16 | *(ptr+2) << 8 | *(ptr+3); | |
100 | } | |
101 | ||
102 | inline u16_t unpackn(u16_t *src) { | |
103 | u8_t *ptr = (u8_t *)src; | |
104 | return *(ptr) << 8 | *(ptr+1); | |
105 | } |