Add C++ wrapper library

This commit adds libiir++, a C++ wrapper library which provides a true
object-oriented interface. The library has two primary objects, Coeff
(coefficient set) and Filter (filter instance) which are implemented
internally using the C objects.

The C++ implementation does produce additional copies of various bits
of information at setup time in order to better support querying the
structure of the filter at runtime. This is intended to be useful for a
future Python extension that will provide a filter experimentation lab.
This commit is contained in:
Laurence Withers 2014-07-07 13:27:12 +00:00
parent ccc5bc6738
commit 911b3eab3d
19 changed files with 969 additions and 0 deletions

1
config
View File

@ -33,4 +33,5 @@ source "scripts/paths"
# Project-specific variables below.
[ -z "${CC}" ] && CC="gcc"
[ -z "${CXX}" ] && CXX="g++"
[ -z "${CFLAGS}" ] && CFLAGS="-g -O2 -W -Wall"

1
src/libiir++/.params Normal file
View File

@ -0,0 +1 @@
lib c++ libiir++ iir++.h

View File

@ -0,0 +1,32 @@
/* libiir/src/libiir++/000_TopHeader.h
*
* Copyright: ©2014, Laurence Withers.
* Author: Laurence Withers <l@lwithers.me.uk>
* License: GPLv3
*/
#ifndef HEADER_libiirpp
#define HEADER_libiirpp
// standard includes, or includes needed for type declarations
#include <complex>
#include <vector>
// forward declares of opaque C structures (to avoid pulling in header)
extern "C" {
struct iir_coeff_t;
struct iir_filter_t;
}
/*! \defgroup libiirpp C++ library */
/*! \brief All %IIR library functions and classes.
\ingroup libiirpp
*/
namespace IIR {
/* options for text editors
vim: expandtab:ts=4:sw=4:syntax=cpp.doxygen
*/

View File

@ -0,0 +1,21 @@
/* libiir/src/libiir++/000_TopSource.cc
*
* Copyright: ©2014, Laurence Withers.
* Author: Laurence Withers <l@lwithers.me.uk>
* License: GPLv3
*/
#include "iir++.h"
#include "iir.h"
// iir.h includes <complex.h>, which #defines complex to _Complex. That's fine
// for the C header above, but will mess with our code definitions below.
#undef complex
// Below are all the includes used throughout the library.
namespace IIR {
/* options for text editors
vim: expandtab:ts=4:sw=4
*/

80
src/libiir++/100_Coeff.cc Normal file
View File

@ -0,0 +1,80 @@
/* libiir/src/libiir++/100_Coeff.cc
*
* Copyright: ©2014, Laurence Withers.
* Author: Laurence Withers <l@lwithers.me.uk>
* License: GPLv3
*/
Coeff::Coeff(int nc, const double* c, int nd, const double* d)
{
cc_ = iir_coeff_new(nc, c, nd, d);
}
Coeff::Coeff(const struct iir_coeff_t* cc)
{
cc_ = iir_coeff_copy(cc);
}
Coeff::Coeff(const Coeff& other)
{
cc_ = iir_coeff_copy(other.cc_);
}
Coeff::~Coeff()
{
iir_coeff_free(cc_);
}
int
Coeff::nc() const
{
return iir_coeff_get_nc(cc_);
}
double
Coeff::c(int idx) const
{
return iir_coeff_get_c(cc_, idx);
}
int
Coeff::nd() const
{
return iir_coeff_get_nd(cc_);
}
double
Coeff::d(int idx) const
{
return iir_coeff_get_d(cc_, idx);
}
std::complex<double>
Coeff::response(double freq) const
{
return std::complex<double>(iir_response_c(cc_, freq));
}
/* options for text editors
vim: expandtab:ts=4:sw=4:syntax=cpp.doxygen
*/

122
src/libiir++/100_Coeff.h Normal file
View File

@ -0,0 +1,122 @@
/* libiir/src/libiir++/100_Coeff.h
*
* Copyright: ©2014, Laurence Withers.
* Author: Laurence Withers <l@lwithers.me.uk>
* License: GPLv3
*/
/*! \defgroup libiirpp_coeff Coefficient handling
\ingroup libiirpp
A set of coefficients for a single stage %IIR filter is represented by an
instance of the \ref Coeff class. This class is often instantiated through
one of the common filter generator functions (see \ref libiirpp_common_filters).
*/
/*! \brief %IIR filter coefficient set
\ingroup libiirpp_coeff
An instance of this class represents the set of coefficients used to implement
a single-phase %IIR filter. See \ref iir_structure for more information.
*/
class Coeff {
public:
/*! \brief Constructor with raw coefficients.
\param nc Number of \a c coefficients, 1.
\param c Array of \a c coefficients.
\param nd Number of \a d coefficients, 1.
\param d Array of \a d coefficients.
*/
Coeff(int nc, const double* c, int nd, const double* d);
/*! \brief Constructor from C library object.
\param cc Pointer to existing coefficient object.
*/
Coeff(const struct iir_coeff_t* cc);
/*! \brief Copy constructor.
\param other Object to copy.
Performs a full deep copy of the set of coefficients from \a other.
*/
Coeff(const Coeff& other);
/*! \brief Destructor. */
virtual ~Coeff();
/*! \brief Count number of \a c coefficients.
\returns Number of \a c coefficients, 1.
*/
int nc() const;
/*! \brief Retrieve \a c coefficient by index.
\param idx Index of coefficient (0 \a idx < \ref nc()).
\returns Value of \a c coefficient at index \a idx.
*/
double c(int idx) const;
/*! \brief Count number of \a d coefficients.
\returns Number of \a d coefficients, 1.
*/
int nd() const;
/*! \brief Retrieve \a d coefficient by index.
\param idx Index of coefficient (0 \a idx < \ref nd()).
\returns Value of \a d coefficient at index \a idx.
*/
double d(int idx) const;
/*! \brief Evaluate response of filter at given frequency.
\param freq Frequency (0 \a freq 1).
\returns Complex response of filter at given frequency.
Returns the steady-state response of the filter to a pure input signal of
the given frequency. \a freq is expressed as a fraction of the intended
sampling rate (with 0.5 being the Nyquist frequency).
*/
std::complex<double> response(double freq) const;
private:
// allow the Filter class to access cc_ directly
friend class Filter;
// our internal representation is the C object
struct iir_coeff_t* cc_;
// disallow assignment; object is immutable
Coeff& operator=(const Coeff& other);
};
/* options for text editors
vim: expandtab:ts=4:sw=4:syntax=cpp.doxygen
*/

104
src/libiir++/200_Filter.cc Normal file
View File

@ -0,0 +1,104 @@
/* libiir/src/libiir++/200_Filter.cc
*
* Copyright: ©2014, Laurence Withers.
* Author: Laurence Withers <l@lwithers.me.uk>
* License: GPLv3
*/
Filter::Filter(const Coeff& coeff)
{
coeff_.push_back(new Coeff(coeff));
fi_ = iir_filter_new(coeff.cc_);
}
Filter::Filter(const Filter& other)
{
for(int pos = 0, max = other.coeff_.size(); pos < max; ++pos) {
Coeff* cc = new Coeff(*other.coeff_[pos]);
coeff_.push_back(cc);
if(!pos) fi_ = iir_filter_new(cc->cc_);
else iir_filter_chain(fi_, cc->cc_);
}
}
Filter::Filter(const char* desc)
{
fi_ = iir_parse(desc);
if(!fi_) return;
for(int pos = 0, max = iir_filter_coeff_sets(fi_); pos < max; ++pos) {
struct iir_coeff_t* c = iir_filter_coeff_set(fi_, pos);
coeff_.push_back(new Coeff(c));
iir_coeff_free(c);
}
}
Filter::~Filter()
{
for(int pos = 0, max = coeff_.size(); pos < max; ++pos) delete coeff_[pos];
iir_filter_free(fi_);
}
bool
Filter::valid() const
{
return fi_;
}
double
Filter::f(double x)
{
return iir_filter(fi_, x);
}
int
Filter::numCoeffSet() const
{
return coeff_.size();
}
const Coeff&
Filter::getCoeffSet(int idx) const
{
return *(coeff_[idx]);
}
void
Filter::chainCoeffSet(const Coeff& coeff)
{
coeff_.push_back(new Coeff(coeff));
iir_filter_chain(fi_, coeff.cc_);
}
std::complex<double>
Filter::response(double freq) const
{
return iir_response(fi_, freq);
}
/* options for text editors
vim: expandtab:ts=4:sw=4:syntax=cpp.doxygen
*/

152
src/libiir++/200_Filter.h Normal file
View File

@ -0,0 +1,152 @@
/* libiir/src/libiir++/200_Filter.h
*
* Copyright: ©2014, Laurence Withers.
* Author: Laurence Withers <l@lwithers.me.uk>
* License: GPLv3
*/
/*! \defgroup libiirpp_filter Basic IIR filtering
\ingroup libiirpp
An instance of the \ref Filter class represents the structure and state of an
%IIR filter. It may be built up with \ref Coeff instances or parsed from a
user-supplied string (see \ref string_desc).
*/
/*! \brief %IIR filter coefficient set
\ingroup libiirpp_filter
An instance of this class contains both a copy of the set of coefficients used
in each filter stage (of which there may be multiple), and all of the state
(i.e. previous inputs and outputs) associated with each stage.
*/
class Filter {
public:
/*! \brief Construct from coefficient set.
\param coeff Coefficient set.
This constructor builds a filter chain with one stage. Additional stages may
be added via \ref chainCoeffSet(). This constructor will always result in a
valid object.
*/
Filter(const Coeff& coeff);
/*! \brief Copy constructor.
\param other Filter to copy.
Copies the structure (but not the state) of the filter \a other. This
constructor will always result in a valid object.
*/
Filter(const Filter& other);
/*! \brief Construct from filter description string.
\param desc Description string.
This constructor will parse the description string \a desc (see
\ref string_desc for details on what is accepted) and build up a filter
as described.
If the parsing step fails, then \ref valid() will return false and the
object must not be used (no other functions except the destructor will be
safe to call).
*/
Filter(const char* desc);
/*! \brief Destructor. */
virtual ~Filter();
/*! \brief Determine whether object is valid.
\retval true if object is valid.
\retval false if object is not valid.
A Filter object might not be valid if it was constructed from a description
string that contained an error. In that case this function will return
\a false and the object should not be used (no other functions except the
destructor will be safe to call).
*/
bool valid() const;
/*! \brief %Filter a sample.
\param x Sample.
\returns %Filter output.
Injects a single sample into the filter and returns the output value.
*/
double f(double x);
/*! \brief Retrieve number of coefficient sets.
\returns Number of sets in use (1).
*/
int numCoeffSet() const;
/*! \brief Retrieve coefficient set.
\param idx Index of set (0 \a idx < \ref numCoeffSet()).
\returns Reference to coefficient set.
*/
const Coeff& getCoeffSet(int idx) const;
/*! \brief Add another set of coefficients to the filter chain.
\param coeff Coefficient set to add.
*/
void chainCoeffSet(const Coeff& coeff);
/*! \brief Evaluate response of filter at given frequency.
\param freq Frequency (0 \a freq 1).
\returns Complex response of filter at given frequency.
Returns the steady-state response of the filter to a pure input signal of
the given frequency. \a freq is expressed as a fraction of the intended
sampling rate (with 0.5 being the Nyquist frequency).
*/
std::complex<double> response(double freq) const;
private:
// one entry per set of coefficients, in order of execution
std::vector<const Coeff*> coeff_;
// the C object has both state and its own copy of the coefficients
struct iir_filter_t* fi_;
// disallow assignment
Filter& operator=(const Filter& other);
};
/* options for text editors
vim: expandtab:ts=4:sw=4:syntax=cpp.doxygen
*/

View File

@ -0,0 +1,58 @@
/* libiir/src/libiir++/300_common_filters.cc
*
* Copyright: ©2014, Laurence Withers.
* Author: Laurence Withers <l@lwithers.me.uk>
* License: GPLv3
*/
Coeff
ButterworthLowPass(int order, double gain, double corner)
{
struct iir_coeff_t* cc = iir_butterworth_lowpass(order, gain, corner);
Coeff ret(cc);
iir_coeff_free(cc);
return ret;
}
Coeff
ButterworthHighPass(int order, double gain, double corner)
{
struct iir_coeff_t* cc = iir_butterworth_highpass(order, gain, corner);
Coeff ret(cc);
iir_coeff_free(cc);
return ret;
}
Coeff
ButterworthBandPass(int order, double gain, double corner1, double corner2)
{
struct iir_coeff_t* cc = iir_butterworth_bandpass(order, gain, corner1,
corner2);
Coeff ret(cc);
iir_coeff_free(cc);
return ret;
}
Coeff
ButterworthBandStop(int order, double gain, double corner1, double corner2)
{
struct iir_coeff_t* cc = iir_butterworth_bandstop(order, gain, corner1,
corner2);
Coeff ret(cc);
iir_coeff_free(cc);
return ret;
}
/* options for text editors
vim: expandtab:ts=4:sw=4:syntax=cpp.doxygen
*/

View File

@ -0,0 +1,144 @@
/* libiir/src/libiir++/300_common_filters.h
*
* Copyright: ©2014, Laurence Withers.
* Author: Laurence Withers <l@lwithers.me.uk>
* License: GPLv3
*/
/*! \defgroup libiirpp_common_filters Common types of IIR filter
\ingroup libiirpp
Routines in this section can be used to build a coefficient set that matches a
common %IIR filter type. These can be passed to the \ref Filter class to build
up an actual instance of a filter.
\note It is common to build higher-order filters by chaining together several
lower-order filters. The functions in this section do not do that directly,
but are intended to build the individual components of a chain. See
\ref Filter::Filter(const char*) for an interface which performs this
automatically.
*/
/*!@{*/
/*! \brief nth-order Butterworth low-pass
\param order Order of filter (1).
\param gain Linear gain of filter.
\param corner Corner frequency expressed as a fraction of Nyquist
(0 \a corner 1).
\returns %IIR filter coefficient set.
Uses the Exstrom labs code to compute the coefficients of an nth-order (param
\a order) Butterworth-type low pass filter with gain \a gain and corner
frequency \a corner.
Note it is recommended to chain multiple filters together to build anything
greater than a 4th-order filter. This function won't do that directly for you.
\a gain will usually be set to be 1.0.
The corner frequency corner is expressed as a fraction of the sampling
frequency (which is of course not known by the %IIR code). It should lie between
0 (0Hz) and 1 (the Nyquist frequency, or ½ the sampling frequency).
*/
Coeff ButterworthLowPass(int order, double gain, double corner);
/*! \brief nth-order Butterworth high-pass
\param order Order of filter (1).
\param gain Linear gain of filter.
\param corner Corner frequency expressed as a fraction of Nyquist
(0 \a corner 1).
\returns %IIR filter coefficient set.
Uses the Exstrom labs code to compute the coefficients of an nth-order (param
\a order) Butterworth-type high pass filter with gain \a gain and corner
frequency \a corner.
Note it is recommended to chain multiple filters together to build anything
greater than a 4th-order filter. This function won't do that directly for you.
\a gain will usually be set to be 1.0.
The corner frequency corner is expressed as a fraction of the sampling
frequency (which is of course not known by the %IIR code). It should lie between
0 (0Hz) and 1 (the Nyquist frequency, or ½ the sampling frequency).
*/
Coeff ButterworthHighPass(int order, double gain, double corner);
/*! \brief nth-order Butterworth band-pass
\param order Order of filter (1).
\param gain Linear gain of filter.
\param corner1 Low corner frequency expressed as a fraction of Nyquist
(0 \a corner 1).
\param corner2 High corner frequency expressed as a fraction of Nyquist
(0 \a corner 1).
\returns %IIR filter coefficient set.
Uses the Exstrom labs code to compute the coefficients of an nth-order (param
\a order) Butterworth-type band pass filter with gain \a gain and corner
frequencies \a corner1 and \a corner2.
Note it is recommended to chain multiple filters together to build anything
greater than a 4th-order filter. This function won't do that directly for you.
\a gain will usually be set to be 1.0.
The corner frequencies \a corner1 and \a corner2 are expressed as a fraction of
the sampling frequency (which is of course not known by the %IIR code). They
should lie between 0 (0Hz) and 1 (the Nyquist frequency, or ½ the sampling
frequency), and \a corner2 should be greater than \a corner1.
*/
Coeff ButterworthBandPass(int order, double gain, double corner1,
double corner2);
/*! \brief nth-order Butterworth band-stop
\param order Order of filter (1).
\param gain Linear gain of filter.
\param corner1 Low corner frequency expressed as a fraction of Nyquist
(0 \a corner 1).
\param corner2 High corner frequency expressed as a fraction of Nyquist
(0 \a corner 1).
\returns %IIR filter coefficient set.
Uses the Exstrom labs code to compute the coefficients of an nth-order (param
\a order) Butterworth-type band stop filter with gain \a gain and corner
frequencies \a corner1 and \a corner2.
Note it is recommended to chain multiple filters together to build anything
greater than a 4th-order filter. This function won't do that directly for you.
\a gain will usually be set to be 1.0.
The corner frequencies \a corner1 and \a corner2 are expressed as a fraction of
the sampling frequency (which is of course not known by the %IIR code). They
should lie between 0 (0Hz) and 1 (the Nyquist frequency, or ½ the sampling
frequency), and \a corner2 should be greater than \a corner1.
*/
Coeff ButterworthBandStop(int order, double gain, double corner1,
double corner2);
/*!@}*/
/* options for text editors
vim: expandtab:ts=4:sw=4:syntax=cpp.doxygen
*/

View File

@ -0,0 +1,14 @@
/* libiir/src/libiir++/999_BottomHeader.h
*
* Copyright: ©2014, Laurence Withers.
* Author: Laurence Withers <l@lwithers.me.uk>
* License: GPLv3
*/
} // end namespace IIR
#endif
/* options for text editors
vim: expandtab:ts=4:sw=4:syntax=cpp.doxygen
*/

View File

@ -0,0 +1,12 @@
/* libiir/src/libiir++/999_BottomSource.cpp
*
* Copyright: ©2014, Laurence Withers.
* Author: Laurence Withers <l@lwithers.me.uk>
* License: GPLv3
*/
} // end namspace IIR
/* options for text editors
vim: expandtab:ts=4:sw=4
*/

View File

@ -0,0 +1 @@
source src/libiir++/build.lib

View File

@ -0,0 +1 @@
source src/libiir++/build.install-lib

View File

@ -0,0 +1,35 @@
build_target libiir++
# make paths (this is for Gentoo in particular)
build_dir_tree "${LIBDIR}" || return 1
build_dir_tree "${BINDIR}" || return 1
build_dir_tree "${INCLUDEDIR}" || return 1
# install library
echo "Installing libraries into '${LIBDIR}'"
install_file ${libiirpp} ${LIBDIR} 0755 || return 1
BASE="${libiirpp_BASE}.so"
MAJOR="${BASE}.${SOMAJOR}"
MICRO="${MAJOR}.${SOMICRO}"
install_symlink "${BASE}" "${MICRO}" "${LIBDIR}"
# install header
echo "Installing header file '${libiirpp_HEADER}' into ${INCLUDEDIR}"
install_header ${libiirpp_HEADER} ${INCLUDEDIR} 0644 || return 1
# install config script
echo "Installing config script into ${PKGCONFDIR}"
CONFFILE="${INSTALL_PREFIX}${BINDIR}/libiir++-config"
do_cmd rm -f "${CONFFILE}"
do_cmd_redir "${CONFFILE}" sed \
-e "s,@VERSION@,${VERSION}," \
-e "s,@LIB_DIR@,${LIBDIR}," \
-e "s,@INCLUDE_DIR@,${INCLUDEDIR}," \
-e "s,@DEP_CFLAGS@,${libiirpp_DEP_CFLAGS}," \
-e "s,@DEP_LIBS@,${libiirpp_DEP_LIBS}," \
src/libiir++/config-script
do_cmd chmod 0644 "${CONFFILE}"
print_success "Done"
# vim: syntax=sh:expandtab:ts=4:sw=4

58
src/libiir++/build.lib Normal file
View File

@ -0,0 +1,58 @@
# These are external variables, and shouldn't clash with anything else
# libiirpp
# libiirpp_BUILT
# libiirpp_HEADER
# libiirpp_BASE
# libiirpp_DEP_CFLAGS
# libiirpp_DEP_LIBS
build_target libiir
if [ -z ${libiirpp_BUILT} ]
then
libiirpp_BASE=libiir++
source src/libiir++/soversion
libiirpp="obj/${libiirpp_BASE}.so.${SOMAJOR}.${SOMICRO}"
libiirpp_DEP_CFLAGS=""
libiirpp_DEP_LIBS="${libiir}"
SO_EXTRA="${libiirpp_DEP_CFLAGS} ${libiirpp_DEP_LIBS} -lstdc++ -lc \
-D_GNU_SOURCE"
echo "Building library ${libiirpp}..."
do_cmd source src/libiir++/build.monolithic || return 1
MODIFIED=0
for test in ${MONOLITHIC_TESTS} ${HDR} ${SRC}
do
if [ ${test} -nt ${libiirpp} ]
then
MODIFIED=1
break
fi
done
if [ ${MODIFIED} -ne 0 ]
then
echo " Compiling"
SONAME="${libiirpp_BASE}.so.${SOMAJOR}"
do_cmd ${CXX} ${CFLAGS} -Iobj -shared -fpic -o "${libiirpp}" \
-Wl,-soname,${SONAME} \
${SRC} ${SO_EXTRA} || return 1
# make tests work
do_cmd ln -sf $(basename ${libiirpp}) obj/${SONAME} || return 1
print_success "Library built"
else
print_success "Library up to date"
fi
libiirpp_BUILT=1
libiirpp_HEADER=${HDR}
fi
# vim: syntax=sh:expandtab:ts=4:sw=4

View File

@ -0,0 +1,21 @@
# These are external variables, and shouldn't clash with anything else
# libiirpp_MONOLITHIC
SRC="obj/libiir++.cpp"
HDR="obj/iir++.h"
MONOLITHIC_TESTS="src/libiir++/build.lib src/libiir++/build.monolithic"
if [ -z "${libiirpp_MONOLITHIC}" ]
then
MONOLITHIC_SOURCE="$(find src/libiir++/ -name '*.h' | sort)"
make_monolithic ${HDR} Ch || return 1
MONOLITHIC_SOURCE="$(find src/libiir++/ -name '*.cc' | sort)"
make_monolithic ${SRC} C || return 1
libiirpp_MONOLITHIC=1
MONOLITHIC_DOC="${MONOLITHIC_DOC} ${HDR}"
fi
# vim: syntax=sh:expandtab:ts=4:sw=4

View File

@ -0,0 +1,97 @@
#!/bin/bash
# libiir/src/libiir++/config-script
#
# libiir++-config template. Variables are finalised at install time.
#
dep_cflags="@DEP_CFLAGS@"
dep_libs="@DEP_LIBS@"
include_dir="@INCLUDE_DIR@"
include_dir_set="no"
lib_dir="@LIB_DIR@"
lib_dir_set="no"
usage() {
cat <<EOF
Usage: libiir++-config [options]
Options:
[--version]
[--libs]
[--libdir[=DIR]]
[--cflags]
[--includedir[=DIR]]
EOF
exit $1
}
[ $# -eq 0 ] && usage 1 1>&2
while [ $# -gt 0 ]
do
case "$1" in
-*=*)
optarg="$(echo "$1" | sed 's/[-_a-zA-Z0-9]*=//')"
;;
*)
optarg=""
;;
esac
case "$1" in
--libdir=*)
lib_dir="${optarg}"
lib_dir_set="yes"
;;
--libdir)
echo_lib_dir="yes"
;;
--includedir=*)
include_dir="${optarg}"
include_dir_set="yes"
;;
--includedir)
echo_include_dir="yes"
;;
--version)
echo "@VERSION@"
exit 0
;;
--cflags)
[ "${include_dir}" != "/usr/include" ] && includes="-I${include_dir}"
echo_cflags="yes"
;;
--libs)
echo_libs="yes"
;;
*)
usage 1 1>&2
;;
esac
shift
done
[ "${echo_prefix}" == "yes" ] && echo "${prefix}"
[ "${echo_exec_prefix}" == "yes" ] && echo "${exec_prefix}"
[ "${echo_cflags}" == "yes" ] && echo "${dep_cflags} ${includes}"
[ "${echo_libs}" == "yes" ] && echo "${dep_libs} -L${lib_dir} -liir++"
true
# kate:

15
src/libiir++/soversion Normal file
View File

@ -0,0 +1,15 @@
# libiir/src/libiir++/soversion
#
# Copyright: ©2014, Laurence Withers.
# Author: Laurence Withers <l@lwithers.me.uk>
# License: GPLv3
#
# SOMAJOR is included in the library's soname, and needs to be bumped
# after a binary-incompatible release. It is a single integer.
SOMAJOR=0
# SOMICRO is bumped every time there is a binary-compatible release.
SOMICRO=0