Question

I basically have some source-code(not my own) in python that I would like to understand. It's an anti-aliased xor audio oscillator. I don't know python at all - but it's quite readable except for a few things:

Firstly - the full code:

f0 = 500.
fs = 44100.
T0 = f0/fs
P0 = fs/f0

t = arange(0,3*fs)
L = len(t)
imax = 2**16


# =============================================================================
# SIGNALS
# =============================================================================

# -----------------------------------------------------------------------------
#
def trivial_xor():
    s = zeros(L)
    sd1 = zeros(L)
    sd2 = zeros(L)
    s = zeros(L)
    w = 0.5
    p = 0
    for n in range(0,L):
        d1 = 2*p - 1
        if p < w:   d2 = 0
        else:           d2 = -0.5
        x1 = int(d1 * imax) & 0xFFFF
        x2 = int(d2 * imax) & 0xFFFF
        y = (x1 ^ x2) / float(imax)
        s[n] = 2*y - 1
        sd1[n] = d1
        sd2[n] = d2
        p += T0
        if p > 1: p -= 1
    return s

# -----------------------------------------------------------------------------
#
def trivial_xor_withSources():
    s = zeros(L)
    sd1 = zeros(L)
    sd2 = zeros(L)
    s = zeros(L)
    w = 0.5
    p = 0
    for n in range(0,L):
        d1 = 2*p - 1
        if p < w:   d2 = 0
        else:           d2 = -0.5
        x1 = int(d1 * imax) & 0xFFFF
        x2 = int(d2 * imax) & 0xFFFF
        y = (x1 ^ x2) / float(imax)
        s[n] = 2*y - 1
        sd1[n] = d1
        sd2[n] = d2
        p += T0
        if p > 1: p -= 1
    return s,sd1,sd2

# -----------------------------------------------------------------------------
#
def PTR1_xor():
    s = trivial_xor() - 2*T0
    #
    T1 = 2*T0
    P1 = 1/T1
    cdc = 1 + T1
    p0 = p1 = 0
    #
    for n in range(0,L):
        if p0 < 0.5:
            h = 0.5
            if p1 < T1:
                s[n] = p1*(2 - 2*h*P1) + 2*h - cdc
        elif p0 < 0.75:
            h = 0.5
            if p1 < T1:
                s[n] = p1*(2 - 2*h*P1) + 2*h - cdc + 1
        else:
            h = 1
            pp = p1 - 0.5
            if pp < T1:
                s[n] = pp*(2 - 2*h*P1) + 2*h - cdc
        #
        p0 += T0
        p1 += T1
        if p0 > 1:  p0 -= 1
        if p1 > 1:  p1 -= 1
    return s

It all seems pretty straight forward - except for what I assume to be the buffers, all I need to know is what is these function(s) in c++?

////////////////////////////////

t = arange(0,3*fs)
L = len(t)
imax = 2**16

////////////////////////////////

 def trivial_xor_withSources():
    s = zeros(L)
    sd1 = zeros(L)
    sd2 = zeros(L)
    s = zeros(L)
    w = 0.5
    p = 0
    for n in range(0,L):

I'm planning on using this in real time. The rest just look like simple math. Any help greatly appreciated!

Andrew

Was it helpful?

Solution

If you're trying to convert the code to C++, you can easily implement a (roughly) equivalent arange function:

#include <vector>

template<typename T>
std::vector<T> arange(T start, T stop, T step = 1) {
    std::vector<T> values;
    for (T value = start; value < stop; value += step)
        values.push_back(value);
    return values;
}

You could then use it like this:

auto t = arange<double>(0, 3*fs);
auto L = t.length();

The ** is exponentiation. You could call the pow function:

#include <cmath>
const double imax = pow(2., 16.);

But since you are dealing with constants anyway, you would be better off with:

const double imax = 65536.;

If you want to retain the expressive power of 2**16 and you don't want to incur the run-time cost of calling pow (perhaps you want to be able to change the exponent without having to manually recalculate the constant), you can use a constexpr function:

constexpr unsigned long power(unsigned long base, unsigned long exponent) {
    return exponent == 0 ? 1 : base * pow(base, exponent - 1);
}

const unsigned long imax = power(2, 16);

OTHER TIPS

Here is an explanation for all the non-trivials lines you outlined:

  • len(t) means "length of t", that is to say, the number of elements in array t.

  • 2**16 is "two to the power of 16" (1 << 16 in your C++ code).

  • for n in range(0,L) is equivalent to for (int n = 0; i < L; ++i)

  • arange and zeros are likely Numpy functions. You can find reference for them here and here.

Regarding the last point, probably there is some import statement you omitted from the code.

Quoting from the docs:

arange

Return evenly spaced values within a given interval.

Values are generated within the half-open interval [start, stop) (in other words, the interval including start but excluding stop).

The default step is 1, so t will be an array containing numbers [0, 1, 2, 3, ..., 3 * 44100 - 1] .

zeros

Return a new array of given shape and type, filled with zeros.

Default type for zeros is float, so s, sd1 and sd2 are initialized as arrays filled of 0.0, each having L elements.

Python: t = arange(0,3*fs)

C++: double t[] = {0.0,1.0,...,3*fs}; // will not compile of course

Python: L = len(t)

C++: int L = sizeof(t)/sizeof(*t); // where t is an array in the current scope

Python: s = zeros(L)

C++: double s[L] = {0}; // where L is a constant

Python: for n in range(0,L)

C++: for (int i=0; i<L; i++)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top