add opus codec (i2s default IO changed!)

This commit is contained in:
philippe44
2019-08-01 23:00:33 -07:00
parent 54b39d7d09
commit 2af82edce4
22 changed files with 6065 additions and 39 deletions

View File

@@ -2,7 +2,7 @@
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
CFLAGS += -O3 -DLINKALL -DLOOPBACK -DNO_FAAD -DRESAMPLE16 -DEMBEDDED -DTREMOR_ONLY -DBYTES_PER_FRAME=4 \
CFLAGS += -O3 -DLINKALL -DLOOPBACK -DTAS575x -DNO_FAAD -DRESAMPLE16 -DEMBEDDED -DTREMOR_ONLY -DBYTES_PER_FRAME=4 \
-I$(COMPONENT_PATH)/../codecs/inc \
-I$(COMPONENT_PATH)/../codecs/inc/mad \
-I$(COMPONENT_PATH)/../codecs/inc/alac \
@@ -10,7 +10,9 @@ CFLAGS += -O3 -DLINKALL -DLOOPBACK -DNO_FAAD -DRESAMPLE16 -DEMBEDDED -DTREMOR_ON
-I$(COMPONENT_PATH)/../codecs/inc/vorbis \
-I$(COMPONENT_PATH)/../codecs/inc/soxr \
-I$(COMPONENT_PATH)/../codecs/inc/resample16 \
-I$(COMPONENT_PATH)/../tools
-I$(COMPONENT_PATH)/../tools \
-I$(COMPONENT_PATH)/../codecs/inc/opus \
-I$(COMPONENT_PATH)/../codecs/inc/opusfile
# -I$(COMPONENT_PATH)/../codecs/inc/faad2

View File

@@ -180,6 +180,9 @@ void decode_init(log_level level, const char *include_codecs, const char *exclud
if (!strstr(exclude_codecs, "ogg") && (!include_codecs || (order_codecs = strstr(include_codecs, "ogg"))))
sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_vorbis());
if (!strstr(exclude_codecs, "ops") && (!include_codecs || (order_codecs = strstr(include_codecs, "ops"))))
sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_opus());
if (!strstr(exclude_codecs, "flac") && (!include_codecs || (order_codecs = strstr(include_codecs, "flac"))))
sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_flac());

View File

@@ -218,6 +218,9 @@ static void usage(const char *argv0) {
#if USE_SSL
" SSL"
#endif
#if NO_SSLSYM
" NO_SSLSYM"
#endif
#if LINKALL
" LINKALL"
#endif
@@ -702,7 +705,7 @@ int main(int argc, char **argv) {
signal(SIGHUP, sighandler);
#endif
#if USE_SSL && !LINKALL
#if USE_SSL && !LINKALL && !NO_SSLSYM
ssl_loaded = load_ssl_symbols();
#endif
@@ -837,7 +840,7 @@ int main(int argc, char **argv) {
}
#endif
#if USE_SSL && !LINKALL
#if USE_SSL && !LINKALL && !NO_SSLSYM
free_ssl_symbols();
#endif

View File

@@ -0,0 +1,317 @@
/*
* Squeezelite - lightweight headless squeezebox emulator
*
* (c) Adrian Smith 2012-2015, triode1@btinternet.com
* Ralph Irving 2015-2017, ralph_irving@hotmail.com
* Philippe 2018-2019, philippe_44@outlook.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"
/*
* with some low-end CPU, the decode call takes a fair bit of time and if the outputbuf is locked during that
* period, the output_thread (or equivalent) will be locked although there is plenty of samples available.
* Normally, with PRIO_INHERIT, that thread should increase decoder priority and get the lock quickly but it
* seems that when the streambuf has plenty of data, the decode thread grabs the CPU to much, even it the output
* thread has a higher priority. Using an interim buffer where opus decoder writes the output is not great from
* an efficiency (one extra memory copy) point of view, but it allows the lock to not be kept for too long
*/
#define FRAME_BUF 2048
#if BYTES_PER_FRAME == 4
#define ALIGN(n) (n)
#else
#define ALIGN(n) (n << 16)
#endif
#include <opusfile.h>
struct opus {
struct OggOpusFile *of;
bool opened;
#if FRAME_BUF
u8_t *write_buf;
#endif
#if !LINKALL
// opus symbols to be dynamically loaded
void (*op_free)(OggOpusFile *_of);
int (*op_read)(OggOpusFile *_of, opus_int16 *_pcm, int _buf_size, int *_li);
const OpusHead* (*op_head)(OggOpusFile *_of, int _li);
OggOpusFile* (*op_open_callbacks) (void *_source, OpusFileCallbacks *_cb, unsigned char *_initial_data, size_t _initial_bytes, int *_error);
#endif
};
static struct opus *u;
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;
#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
#if LINKALL
#define OP(h, fn, ...) (op_ ## fn)(__VA_ARGS__)
#else
#define OP(h, fn, ...) (h)->op_ ## fn(__VA_ARGS__)
#endif
// called with mutex locked within vorbis_decode to avoid locking O before S
static int _read_cb(void *datasource, char *ptr, int size) {
size_t bytes;
LOCK_S;
bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
bytes = min(bytes, size);
memcpy(ptr, streambuf->readp, bytes);
_buf_inc_readp(streambuf, bytes);
UNLOCK_S;
return bytes;
}
static decode_state opus_decompress(void) {
frames_t frames;
int n;
static int channels;
u8_t *write_buf;
LOCK_S;
if (stream.state <= DISCONNECT && !_buf_used(streambuf)) {
UNLOCK_S;
return DECODE_COMPLETE;
}
UNLOCK_S;
if (decode.new_stream) {
struct OpusFileCallbacks cbs;
const struct OpusHead *info;
int err;
cbs.read = (op_read_func) _read_cb;
cbs.seek = NULL; cbs.tell = NULL; cbs.close = NULL;
if ((u->of = OP(u, open_callbacks, streambuf, &cbs, NULL, 0, &err)) == NULL) {
LOG_WARN("open_callbacks error: %d", err);
return DECODE_COMPLETE;
}
u->opened = true;
info = OP(u, head, u->of, -1);
LOCK_O;
output.next_sample_rate = 48000;
IF_DSD( output.next_fmt = PCM; )
output.track_start = outputbuf->writep;
if (output.fade_mode) _checkfade(true);
decode.new_stream = false;
UNLOCK_O;
channels = info->channel_count;
LOG_INFO("setting track_start");
}
#if !FRAME_BUF
LOCK_O_direct;
#endif
#if FRAME_BUF
IF_DIRECT(
frames = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME;
write_buf = u->write_buf;
);
#else
IF_DIRECT(
frames = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME;
write_buf = outputbuf->writep;
);
#endif
IF_PROCESS(
frames = process.max_in_frames;
write_buf = process.inbuf;
);
#if FRAME_BUF
frames = min(frames, FRAME_BUF);
#endif
// write the decoded frames into outputbuf then unpack them (they are 16 bits)
n = OP(u, read, u->of, (opus_int16*) write_buf, frames * channels, NULL);
#if FRAME_BUF
LOCK_O_direct;
#endif
if (n > 0) {
frames_t count;
s16_t *iptr;
ISAMPLE_T *optr;
frames = n;
count = frames * channels;
iptr = (s16_t *)write_buf + count;
optr = (ISAMPLE_T *) outputbuf->writep + frames * 2;
if (channels == 2) {
#if BYTES_PER_FRAME == 4
memcpy(outputbuf->writep, write_buf, frames * BYTES_PER_FRAME);
#else
while (count--) {
*--optr = *--iptr << 16;
}
#endif
} else if (channels == 1) {
while (count--) {
*--optr = ALIGN(*--iptr);
*--optr = ALIGN(*iptr);
}
}
IF_DIRECT(
_buf_inc_writep(outputbuf, frames * BYTES_PER_FRAME);
);
IF_PROCESS(
process.in_frames = frames;
);
LOG_SDEBUG("wrote %u frames", frames);
} else if (n == 0) {
LOG_INFO("end of stream");
UNLOCK_O_direct;
return DECODE_COMPLETE;
} else if (n == OP_HOLE) {
// recoverable hole in stream, seen when skipping
LOG_DEBUG("hole in stream");
} else {
LOG_INFO("op_read error: %d", n);
UNLOCK_O_direct;
return DECODE_COMPLETE;
}
UNLOCK_O_direct;
return DECODE_RUNNING;
}
static void opus_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) {
if (!u->of) {
#if FRAME_BUF
u->write_buf = malloc(FRAME_BUF * BYTES_PER_FRAME);
#endif
}
u->opened = false;
}
static void opus_close(void) {
u->opened = false;
OP(u, free, u->of);
#if FRAME_BUF
free(u->write_buf);
u->write_buf = NULL;
#endif
u->of = NULL;
}
static bool load_opus(void) {
#if !LINKALL
void *handle = dlopen(LIBOPUS, RTLD_NOW);
char *err;
if (!handle) {
LOG_INFO("dlerror: %s", dlerror());
return false;
}
u->op_free = dlsym(handle, "op_free");
u->op_read = dlsym(handle, "op_read");
u->op_head = dlsym(handle, "op_head");
u->op_open_callbacks = dlsym(handle, "op_open_callbacks");
if ((err = dlerror()) != NULL) {
LOG_INFO("dlerror: %s", err);
return false;
}
LOG_INFO("loaded "LIBOPUS);
#endif
return true;
}
struct codec *register_opus(void) {
static struct codec ret = {
'u', // id
"ops", // types
4096, // min read
20480, // min space
opus_open, // open
opus_close, // close
opus_decompress, // decode
};
u = malloc(sizeof(struct opus));
if (!u) {
return NULL;
}
u->of = NULL;
u->opened = false;
if (!load_opus()) {
return NULL;
}
LOG_INFO("using opus to decode ops");
return &ret;
}

View File

@@ -387,7 +387,7 @@ void output_init_common(log_level level, const char *device, unsigned output_buf
output.supported_rates[i] = rates[i];
}
}
else
else {
#else
{
#endif

View File

@@ -131,9 +131,9 @@ static const struct tas575x_cmd_s tas575x_init_sequence[] = {
static const i2c_config_t i2c_config = {
.mode = I2C_MODE_MASTER,
.sda_io_num = 19,
.sda_io_num = 27,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = 18,
.scl_io_num = 26,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 100000,
};

View File

@@ -122,7 +122,7 @@ static void sendHELO(bool reconnect, const char *fixed_cap, const char *var_cap,
struct HELO_packet pkt;
#if USE_SSL
#if !LINKALL
#if !LINKALL && !NO_SSLSYM
if (ssl_loaded) base_cap = SSL_CAP "," BASE_CAP;
else base_cap = BASE_CAP;
#endif
@@ -769,11 +769,6 @@ in_addr_t discover_server(char *default_server) {
char *buf;
struct pollfd pollinfo;
unsigned port;
int attempts = 0;
#if EMBEDDED
attempts = 5;
#endif
int disc_sock = socket(AF_INET, SOCK_DGRAM, 0);
@@ -810,7 +805,7 @@ in_addr_t discover_server(char *default_server) {
server_addr(default_server, &s.sin_addr.s_addr, &port);
}
} while (s.sin_addr.s_addr == 0 && running && (!attempts || --attempts));
} while (s.sin_addr.s_addr == 0 && running);
closesocket(disc_sock);
@@ -895,7 +890,7 @@ void slimproto(log_level level, char *server, u8_t mac[6], const char *name, con
new_server = 0;
while (running && slimproto_ip) {
while (running) {
if (new_server) {
previous_server = slimproto_ip;

View File

@@ -180,6 +180,12 @@
#define USE_SSL 0
#endif
#if defined (NO_SSLSYM)
#undef NO_SSLSYM
#define NO_SSLSYM 1
#else
#define NO_SSLSYM 0
#endif
#if !LINKALL
@@ -190,6 +196,7 @@
#define LIBMAD "libmad.so.0"
#define LIBMPG "libmpg123.so.0"
#define LIBVORBIS "libvorbisfile.so.3"
#define LIBOPUS "libopusfile.so.0"
#define LIBTREMOR "libvorbisidec.so.1"
#define LIBFAAD "libfaad.so.2"
#define LIBAVUTIL "libavutil.so.%d"
@@ -205,6 +212,7 @@
#define LIBMPG "libmpg123.0.dylib"
#define LIBVORBIS "libvorbisfile.3.dylib"
#define LIBTREMOR "libvorbisidec.1.dylib"
#define LIBOPUS "libopusfile.0.dylib"
#define LIBFAAD "libfaad.2.dylib"
#define LIBAVUTIL "libavutil.%d.dylib"
#define LIBAVCODEC "libavcodec.%d.dylib"
@@ -217,6 +225,7 @@
#define LIBMAD "libmad-0.dll"
#define LIBMPG "libmpg123-0.dll"
#define LIBVORBIS "libvorbisfile.dll"
#define LIBOPUS "libopusfile-0.dll"
#define LIBTREMOR "libvorbisidec.dll"
#define LIBFAAD "libfaad2.dll"
#define LIBAVUTIL "avutil-%d.dll"
@@ -226,15 +235,17 @@
#endif
#if FREEBSD
#define LIBFLAC "libFLAC.so.11"
#define LIBMAD "libmad.so.2"
#define LIBFLAC "libFLAC.so.8"
#define LIBMAD "libmad.so.0"
#define LIBMPG "libmpg123.so.0"
#define LIBVORBIS "libvorbisfile.so.6"
#define LIBVORBIS "libvorbisfile.so.3"
#define LIBTREMOR "libvorbisidec.so.1"
#define LIBOPUS "libopusfile.so.1"
#define LIBFAAD "libfaad.so.2"
#define LIBAVUTIL "libavutil.so.%d"
#define LIBAVCODEC "libavcodec.so.%d"
#define LIBAVFORMAT "libavformat.so.%d"
#define LIBSOXR "libsoxr.so.0"
#endif
#endif // !LINKALL
@@ -773,6 +784,7 @@ struct codec *register_helixaac(void);
struct codec *register_dsd(void);
struct codec *register_alac(void);
struct codec *register_ff(const char *codec);
struct codec *register_opus(void);
//gpio.c
#if GPIO
@@ -799,7 +811,7 @@ void ir_close(void);
#endif
// sslsym.c
#if USE_SSL && !LINKALL
#if USE_SSL && !LINKALL && !NO_SSLSYM
bool load_ssl_symbols(void);
void free_ssl_symbols(void);
bool ssl_loaded;

View File

@@ -377,7 +377,7 @@ void stream_init(log_level level, unsigned stream_buf_size) {
}
#if USE_SSL
#if !LINKALL
#if !LINKALL && !NO_SSLSYM
if (ssl_loaded) {
#endif
SSL_library_init();
@@ -387,7 +387,7 @@ void stream_init(log_level level, unsigned stream_buf_size) {
exit(0);
}
SSL_CTX_set_options(SSLctx, SSL_OP_NO_SSLv2);
#if !LINKALL
#if !LINKALL && !NO_SSLSYM
}
#endif
ssl = NULL;

View File

@@ -113,15 +113,15 @@ static size_t _read_cb(void *ptr, size_t size, size_t nmemb, void *datasource) {
size_t bytes;
LOCK_S;
bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
bytes = min(bytes, size * nmemb);
memcpy(ptr, streambuf->readp, bytes);
_buf_inc_readp(streambuf, bytes);
UNLOCK_S;
return bytes / size;
}
@@ -137,7 +137,7 @@ static decode_state vorbis_decode(void) {
u8_t *write_buf;
LOCK_S;
if (stream.state <= DISCONNECT && !_buf_used(streambuf)) {
UNLOCK_S;
return DECODE_COMPLETE;
@@ -151,7 +151,7 @@ static decode_state vorbis_decode(void) {
struct vorbis_info *info;
cbs.read_func = _read_cb;
if (TREMOR(v)) {
cbs.seek_func = _seek_cb; cbs.close_func = _close_cb; cbs.tell_func = _tell_cb;
} else {
@@ -162,13 +162,13 @@ static decode_state vorbis_decode(void) {
LOG_WARN("open_callbacks error: %d", err);
return DECODE_COMPLETE;
}
v->opened = true;
info = OV(v, info, v->vf, -1);
LOG_INFO("setting track_start");
LOCK_O;
output.next_sample_rate = decode_newstream(info->rate, output.supported_rates);
output.next_sample_rate = decode_newstream(info->rate, output.supported_rates);
IF_DSD( output.next_fmt = PCM; )
output.track_start = outputbuf->writep;
if (output.fade_mode) _checkfade(true);
@@ -186,15 +186,18 @@ static decode_state vorbis_decode(void) {
#if !FRAME_BUF
LOCK_O_direct;
#endif
#if FRAME_BUF
IF_DIRECT(
frames = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME;
#if FRAME_BUF
write_buf = v->write_buf;
#else
write_buf = outputbuf->writep;
#endif
);
#else
IF_DIRECT(
frames = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME;
write_buf = outputbuf->writep;
);
#endif
IF_PROCESS(
frames = process.max_in_frames;
write_buf = process.inbuf;
@@ -244,7 +247,7 @@ static decode_state vorbis_decode(void) {
while (count--) {
*--optr = *--iptr << 16;
}
#endif
#endif
} else if (channels == 1) {
while (count--) {
*--optr = ALIGN(*--iptr);
@@ -271,7 +274,7 @@ static decode_state vorbis_decode(void) {
// recoverable hole in stream, seen when skipping
LOG_DEBUG("hole in stream");
} else {
LOG_INFO("ov_read error: %d", n);