Skip to content

Instantly share code, notes, and snippets.

@slyoldfox
Created November 22, 2022 14:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save slyoldfox/6c1c97bd43d97ffa428904523d3f63cf to your computer and use it in GitHub Desktop.
Save slyoldfox/6c1c97bd43d97ffa428904523d3f63cf to your computer and use it in GitHub Desktop.
Patch to make speex work on baresip 2.9.0
diff --git a/cmake/FindSPEEX.cmake b/cmake/FindSPEEX.cmake
new file mode 100644
index 00000000..966f52d8
--- /dev/null
+++ b/cmake/FindSPEEX.cmake
@@ -0,0 +1,32 @@
+find_package(PkgConfig QUIET)
+pkg_check_modules(PC_LIBSPEEX QUIET libspeex)
+
+find_path(SPEEX_INCLUDE_DIR
+ NAMES speex/speex.h
+ HINTS
+ "${SPEEX_INCLUDE_DIRS}"
+ "${SPEEX_HINTS}/include"
+ PATHS /usr/local/include /usr/include
+)
+
+find_library(SPEEX_LIBRARY
+ NAMES speex
+ HINTS
+ "${SPEEX_LIBRARY_DIRS}"
+ "${SPEEX_HINTS}/lib"
+ PATHS /usr/local/lib /usr/lib
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(SPEEX DEFAULT_MSG SPEEX_LIBRARY
+ SPEEX_INCLUDE_DIR)
+
+if(SPEEX_FOUND)
+ set( SPEEX_INCLUDE_DIRS ${SPEEX_INCLUDE_DIR} )
+ set( SPEEX_LIBRARIES ${SPEEX_LIBRARY} )
+else()
+ set( SPEEX_INCLUDE_DIRS )
+ set( SPEEX_LIBRARIES )
+endif()
+
+mark_as_advanced( SPEEX_LIBRARIES SPEEX_INCLUDE_DIRS )
diff --git a/cmake/modules.cmake b/cmake/modules.cmake
index e7367e59..56a75d26 100644
--- a/cmake/modules.cmake
+++ b/cmake/modules.cmake
@@ -23,6 +23,7 @@ find_package(PULSE)
find_package(SDL)
find_package(SNDFILE)
find_package(SPANDSP)
+find_package(SPEEX)
find_package(V4L2)
find_package(VPX)
find_package(WEBRTC_AEC)
diff --git a/modules/speex/CMakeLists.txt b/modules/speex/CMakeLists.txt
new file mode 100644
index 00000000..f19ebc8a
--- /dev/null
+++ b/modules/speex/CMakeLists.txt
@@ -0,0 +1,13 @@
+project(speex)
+
+set(SRCS speex.c)
+if(STATIC)
+ add_library(${PROJECT_NAME} OBJECT ${SRCS})
+else()
+ add_library(${PROJECT_NAME} MODULE ${SRCS})
+endif()
+#target_include_directories(${PROJECT_NAME} PUBLIC "/home/hass/speex-install/include")
+#target_link_libraries(${PROJECT_NAME} PRIVATE "/home/hass/speex-install/lib")
+#target_link_libraries(${PROJECT_NAME} PRIVATE "home/hass/speex-install/lib")
+target_include_directories(${PROJECT_NAME} PRIVATE ${SPEEX_INCLUDE_DIRS})
+target_link_libraries(${PROJECT_NAME} PRIVATE ${SPEEX_LIBRARIES})
diff --git a/modules/speex/module.mk b/modules/speex/module.mk
new file mode 100644
index 00000000..81c8b187
--- /dev/null
+++ b/modules/speex/module.mk
@@ -0,0 +1,12 @@
+#
+# module.mk
+#
+# Copyright (C) 2010 Creytiv.com
+#
+
+MOD := speex
+$(MOD)_SRCS += speex.c
+$(MOD)_LFLAGS += -lspeex
+$(MOD)_CFLAGS += -Wno-strict-prototypes
+
+include mk/mod.mk
diff --git a/modules/speex/speex.c b/modules/speex/speex.c
new file mode 100644
index 00000000..5295d1a6
--- /dev/null
+++ b/modules/speex/speex.c
@@ -0,0 +1,525 @@
+/**
+ * @file speex.c Speex audio codec
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <stdlib.h>
+#include <speex/speex.h>
+#include <speex/speex_stereo.h>
+#include <speex/speex_callbacks.h>
+#include <re.h>
+#include <baresip.h>
+
+
+/**
+ * @defgroup speex speex
+ *
+ * The Speex audio codec
+ *
+ * NOTE: The Speex codec has been obsoleted by Opus.
+ */
+
+
+enum {
+ MIN_FRAME_SIZE = 43,
+ SPEEX_PTIME = 20,
+};
+
+enum {NR_CODECS = 6};
+
+
+struct auenc_state {
+ void *enc;
+ SpeexBits bits;
+
+ uint32_t frame_size; /* Number of sample-frames */
+ uint8_t channels;
+};
+
+
+struct audec_state {
+ void *dec;
+ SpeexBits bits;
+ SpeexStereoState stereo;
+ SpeexCallback callback;
+
+ uint32_t frame_size; /* Number of sample-frames */
+ uint8_t channels;
+};
+
+
+static char speex_fmtp_nb[128];
+static char speex_fmtp_wb[128];
+
+
+/** Speex configuration */
+static struct {
+ int quality;
+ int complexity;
+ int enhancement;
+ int mode_nb;
+ int mode_wb;
+ int vbr;
+ int vad;
+} sconf = {
+ 3, /* 0-10 */
+ 2, /* 0-10 */
+ 0, /* 0 or 1 */
+ 3, /* 1-6 */
+ 6, /* 1-6 */
+ 0, /* 0 or 1 */
+ 0 /* 0 or 1 */
+};
+
+
+static void encode_destructor(void *arg)
+{
+ struct auenc_state *st = arg;
+
+ speex_bits_destroy(&st->bits);
+ speex_encoder_destroy(st->enc);
+}
+
+
+static void decode_destructor(void *arg)
+{
+ struct audec_state *st = arg;
+
+ speex_bits_destroy(&st->bits);
+ speex_decoder_destroy(st->dec);
+}
+
+
+static void encoder_config(void *st)
+{
+ int ret;
+
+ ret = speex_encoder_ctl(st, SPEEX_SET_QUALITY, &sconf.quality);
+ if (ret) {
+ warning("speex: SPEEX_SET_QUALITY: %d\n", ret);
+ }
+
+ ret = speex_encoder_ctl(st, SPEEX_SET_COMPLEXITY, &sconf.complexity);
+ if (ret) {
+ warning("speex: SPEEX_SET_COMPLEXITY: %d\n", ret);
+ }
+
+ ret = speex_encoder_ctl(st, SPEEX_SET_VBR, &sconf.vbr);
+ if (ret) {
+ warning("speex: SPEEX_SET_VBR: %d\n", ret);
+ }
+
+ ret = speex_encoder_ctl(st, SPEEX_SET_VAD, &sconf.vad);
+ if (ret) {
+ warning("speex: SPEEX_SET_VAD: %d\n", ret);
+ }
+}
+
+
+static void decoder_config(void *st)
+{
+ int ret;
+
+ ret = speex_decoder_ctl(st, SPEEX_SET_ENH, &sconf.enhancement);
+ if (ret) {
+ warning("speex: SPEEX_SET_ENH: %d\n", ret);
+ }
+}
+
+
+static int decode_param(struct auenc_state *st, const struct pl *name,
+ const struct pl *val)
+{
+ int ret;
+
+ /* mode: List supported Speex decoding modes. The valid modes are
+ different for narrowband and wideband, and are defined as follows:
+
+ {1,2,3,4,5,6,any}
+ */
+ if (0 == pl_strcasecmp(name, "mode")) {
+ struct pl v;
+ int mode;
+
+ /* parameter is quoted */
+ if (re_regex(val->p, val->l, "\"[^\"]+\"", &v))
+ v = *val;
+
+ if (0 == pl_strcasecmp(&v, "any"))
+ return 0;
+
+ mode = pl_u32(&v);
+
+ ret = speex_encoder_ctl(st->enc, SPEEX_SET_MODE, &mode);
+ if (ret) {
+ warning("speex: SPEEX_SET_MODE: ret=%d\n", ret);
+ }
+ }
+ /* vbr: variable bit rate - either 'on' 'off' or 'vad' */
+ else if (0 == pl_strcasecmp(name, "vbr")) {
+ int vbr = 0, vad = 0;
+
+ if (0 == pl_strcasecmp(val, "on"))
+ vbr = 1;
+ else if (0 == pl_strcasecmp(val, "off"))
+ vbr = 0;
+ else if (0 == pl_strcasecmp(val, "vad"))
+ vad = 1;
+ else {
+ warning("speex: invalid vbr value %r\n", val);
+ }
+
+ debug("speex: setting VBR=%d VAD=%d\n", vbr, vad);
+ ret = speex_encoder_ctl(st->enc, SPEEX_SET_VBR, &vbr);
+ if (ret) {
+ warning("speex: SPEEX_SET_VBR: ret=%d\n", ret);
+ }
+ ret = speex_encoder_ctl(st->enc, SPEEX_SET_VAD, &vad);
+ if (ret) {
+ warning("speex: SPEEX_SET_VAD: ret=%d\n", ret);
+ }
+ }
+ else if (0 == pl_strcasecmp(name, "cng")) {
+ int dtx = 0;
+
+ if (0 == pl_strcasecmp(val, "on"))
+ dtx = 0;
+ else if (0 == pl_strcasecmp(val, "off"))
+ dtx = 1;
+
+ ret = speex_encoder_ctl(st->enc, SPEEX_SET_DTX, &dtx);
+ if (ret) {
+ warning("speex: SPEEX_SET_DTX: ret=%d\n", ret);
+ }
+ }
+ else {
+ debug("speex: unknown Speex param: %r=%r\n", name, val);
+ }
+
+ return 0;
+}
+
+
+static void param_handler(const struct pl *name, const struct pl *val,
+ void *arg)
+{
+ struct auenc_state *st = arg;
+
+ decode_param(st, name, val);
+}
+
+
+static const SpeexMode *resolve_mode(uint32_t srate)
+{
+ switch (srate) {
+
+ default:
+ case 8000: return &speex_nb_mode;
+ case 16000: return &speex_wb_mode;
+ case 32000: return &speex_uwb_mode;
+ }
+}
+
+
+static int encode_update(struct auenc_state **aesp, const struct aucodec *ac,
+ struct auenc_param *prm, const char *fmtp)
+{
+ struct auenc_state *st;
+ int ret, err = 0;
+
+ if (!aesp || !ac || !prm)
+ return EINVAL;
+ //if (prm->ptime != SPEEX_PTIME)
+// return EPROTO;
+ if (*aesp)
+ return 0;
+
+ st = mem_zalloc(sizeof(*st), encode_destructor);
+ if (!st)
+ return ENOMEM;
+
+ st->frame_size = ac->srate * SPEEX_PTIME / 1000;
+ st->channels = ac->ch;
+
+ /* Encoder */
+ st->enc = speex_encoder_init(resolve_mode(ac->srate));
+ if (!st->enc) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ speex_bits_init(&st->bits);
+
+ encoder_config(st->enc);
+
+ ret = speex_encoder_ctl(st->enc, SPEEX_GET_FRAME_SIZE,
+ &st->frame_size);
+ if (ret) {
+ warning("speex: SPEEX_GET_FRAME_SIZE: %d\n", ret);
+ }
+
+ if (str_isset(fmtp)) {
+ struct pl params;
+
+ pl_set_str(&params, fmtp);
+
+ fmt_param_apply(&params, param_handler, st);
+ }
+
+ out:
+ if (err)
+ mem_deref(st);
+ else
+ *aesp = st;
+
+ return err;
+}
+
+
+static int decode_update(struct audec_state **adsp,
+ const struct aucodec *ac, const char *fmtp)
+{
+ struct audec_state *st;
+ int err = 0;
+ (void)fmtp;
+
+ if (!adsp || !ac)
+ return EINVAL;
+ if (*adsp)
+ return 0;
+
+ st = mem_zalloc(sizeof(*st), decode_destructor);
+ if (!st)
+ return ENOMEM;
+
+ st->frame_size = ac->srate * SPEEX_PTIME / 1000;
+ st->channels = ac->ch;
+
+ /* Decoder */
+ st->dec = speex_decoder_init(resolve_mode(ac->srate));
+ if (!st->dec) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ speex_bits_init(&st->bits);
+
+ if (2 == st->channels) {
+
+ /* Stereo. */
+ st->stereo.balance = 1;
+ st->stereo.e_ratio = .5f;
+ st->stereo.smooth_left = 1;
+ st->stereo.smooth_right = 1;
+
+ st->callback.callback_id = SPEEX_INBAND_STEREO;
+ st->callback.func = speex_std_stereo_request_handler;
+ st->callback.data = &st->stereo;
+ speex_decoder_ctl(st->dec, SPEEX_SET_HANDLER,
+ &st->callback);
+ }
+
+ decoder_config(st->dec);
+
+ out:
+ if (err)
+ mem_deref(st);
+ else
+ *adsp = st;
+
+ return err;
+}
+
+
+static int encode( struct auenc_state *st,
+ bool *marker, uint8_t *buf, size_t *len,
+ int fmt, const void *sampv, size_t sampc )
+{
+ const size_t n = st->channels * st->frame_size;
+ int ret, r;
+ (void)marker;
+ (void)fmt;
+
+ if (*len < 128)
+ return ENOMEM;
+
+ /* VAD */
+ if (!sampv || !sampc) {
+ /* 5 zeros interpreted by Speex as silence (submode 0) */
+ speex_bits_pack(&st->bits, 0, 5);
+ goto out;
+ }
+
+ /* Handle multiple Speex frames in one RTP packet */
+ while (sampc > 0) {
+
+ /* Assume stereo */
+ if (2 == st->channels) {
+ speex_encode_stereo_int((int16_t *)sampv,
+ st->frame_size, &st->bits);
+ }
+
+ ret = speex_encode_int(st->enc, (int16_t *)sampv, &st->bits);
+ if (1 != ret) {
+ warning("speex: speex_encode_int: ret=%d\n", ret);
+ }
+
+ sampc -= n;
+ sampv += n;
+ }
+
+ out:
+ /* Terminate bit stream */
+ speex_bits_pack(&st->bits, 15, 5);
+
+ r = speex_bits_write(&st->bits, (char *)buf, (int)*len);
+ *len = r;
+
+ speex_bits_reset(&st->bits);
+
+ return 0;
+}
+
+
+static int decode(struct audec_state *st,
+ int fmt, void *sampv, size_t *sampc,
+ bool marker, const uint8_t *buf, size_t len)
+{
+ const size_t n = st->channels * st->frame_size;
+ size_t i = 0;
+ (void)fmt;
+ (void)marker;
+
+ /* Read into bit-stream */
+ speex_bits_read_from(&st->bits, (char *)buf, (int)len);
+
+ /* Handle multiple Speex frames in one RTP packet */
+ while (speex_bits_remaining(&st->bits) >= MIN_FRAME_SIZE) {
+ int ret;
+
+ if (*sampc < n)
+ return ENOMEM;
+
+ ret = speex_decode_int(st->dec, &st->bits,
+ (int16_t *)&sampv[i]);
+ if (ret < 0) {
+ if (-1 == ret) {
+ }
+ else if (-2 == ret) {
+ warning("speex: decode: corrupt stream\n");
+ }
+ else {
+ warning("speex: decode: speex_decode_int:"
+ " ret=%d\n", ret);
+ }
+ break;
+ }
+
+ /* Transforms a mono frame into a stereo frame
+ using intensity stereo info */
+ if (2 == st->channels) {
+ speex_decode_stereo_int((int16_t *)&sampv[i],
+ st->frame_size,
+ &st->stereo);
+ }
+
+ i += n;
+ *sampc -= n;
+ }
+
+ *sampc = i;
+
+ return 0;
+}
+
+
+static int pkloss(struct audec_state *st,
+ int fmt, void *sampv, size_t *sampc,
+ const uint8_t *buf, size_t len)
+{
+ const size_t n = st->channels * st->frame_size;
+ (void)len;
+ (void)buf;
+ (void)fmt;
+
+ if (*sampc < n)
+ return ENOMEM;
+
+ /* Silence */
+ speex_decode_int(st->dec, NULL, sampv);
+ *sampc = n;
+
+ return 0;
+}
+
+
+static void config_parse(struct conf *conf)
+{
+ uint32_t v;
+
+ if (0 == conf_get_u32(conf, "speex_quality", &v))
+ sconf.quality = v;
+ if (0 == conf_get_u32(conf, "speex_complexity", &v))
+ sconf.complexity = v;
+ if (0 == conf_get_u32(conf, "speex_enhancement", &v))
+ sconf.enhancement = v;
+ if (0 == conf_get_u32(conf, "speex_mode_nb", &v))
+ sconf.mode_nb = v;
+ if (0 == conf_get_u32(conf, "speex_mode_wb", &v))
+ sconf.mode_wb = v;
+ if (0 == conf_get_u32(conf, "speex_vbr", &v))
+ sconf.vbr = v;
+ if (0 == conf_get_u32(conf, "speex_vad", &v))
+ sconf.vad = v;
+}
+
+
+static struct aucodec speexv[NR_CODECS] = {
+/* Stereo Speex */
+{LE_INIT, 0, "speex", 32000, 32000, 2, 2, 20, speex_fmtp_wb, encode_update, encode, decode_update, decode, pkloss,0,0},
+{LE_INIT, 0, "speex", 16000, 16000, 2, 2, 20, speex_fmtp_wb, encode_update, encode, decode_update, decode, pkloss,0,0},
+{LE_INIT, 0, "speex", 8000, 8000, 2, 2, 20, speex_fmtp_wb, encode_update, encode, decode_update, decode, pkloss,0,0},
+/* Standard Speex */
+{LE_INIT, 0, "speex", 32000, 32000, 1, 1, 20, speex_fmtp_wb, encode_update, encode, decode_update, decode, pkloss,0,0},
+{LE_INIT, 0, "speex", 16000, 16000, 1, 1, 20, speex_fmtp_wb, encode_update, encode, decode_update, decode, pkloss,0,0},
+{LE_INIT, 0, "speex", 8000, 8000, 1, 1, 20, speex_fmtp_wb, encode_update, encode, decode_update, decode, pkloss,0,0},
+};
+
+
+static int speex_init(void)
+{
+ size_t i;
+ struct list *aucodecl = baresip_aucodecl();
+
+ config_parse(conf_cur());
+
+ (void)re_snprintf(speex_fmtp_nb, sizeof(speex_fmtp_nb),
+ "mode=\"%d\";vbr=%s;cng=on", sconf.mode_nb,
+ sconf.vad ? "vad" : (sconf.vbr ? "on" : "off"));
+
+ (void)re_snprintf(speex_fmtp_wb, sizeof(speex_fmtp_wb),
+ "mode=\"%d\";vbr=%s;cng=on", sconf.mode_wb,
+ sconf.vad ? "vad" : (sconf.vbr ? "on" : "off"));
+
+ for (i=0; i<NR_CODECS; i++)
+ aucodec_register(aucodecl, &speexv[i]);
+
+ return 0;
+}
+
+
+static int speex_close(void)
+{
+ size_t i;
+ for (i=0; i<NR_CODECS; i++)
+ aucodec_unregister(&speexv[i]);
+ return 0;
+}
+
+
+EXPORT_SYM const struct mod_export DECL_EXPORTS(speex) = {
+ "speex",
+ "codec",
+ speex_init,
+ speex_close
+};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment