Beads audio library in Python mode

Hi ! I am starting to learn Beads audio library, wonderful book by Evan Merz but in Java.
The beads library works fine in Processing.py up to some points where the Java does not seem obvious to be translated in Python.
------> Has anybody tried to translate in Python the Merz book, or better the 11 Lessons ?
I am stuck with FM synthesis, having to translate a CUSTOM FUNCTION (much used in the book) :

modulator = new WavePlayer(ac, 5, Buffer.SINE);
// this is a custom function
// custom functions are a bit like custom Unit Generators (custom Beads)
// but they only override the calculate function
Function frequencyModulation = new Function(modulator)
{
  public float calculate() {
    // return x[0], which is the original value of the modulator signal (a sine wave)
    // multiplied by 50 to make the sine vary between -50 and 50
    // then add 200, so that it varies from 150 to 250
    return (x[0] * 50.0) + 200.0;            // <---- works fine !
  }
};

carrier = new WavePlayer(ac, frequencyModulation, Buffer.SINE);

----------- I tried this but without luck :

class My_Function(Function) :
    def calculate(self) :
        return x[0]*50+200               # <---- x is not defined !!!

modulator = WavePlayer(ac,5,Buffer.SINE)
frequencyModulation = My_Function(modulator)
carrier = WavePlayer(ac,frequencyModulation,Buffer.SINE)

Any help ?
1 Like

Please format your code :blush:

It consist on these two steps:

  1. In your code editor (PDE, VS code, Eclipse, etc) ensure you execute the beautifier function. This function automatically indents your code. Auto-indenting makes your code easier to read and helps catching bugs due to mismatch parenthesis, for instance. In the PDE, you use the key combination: ctrl+t
  2. You copy and paste your code in the forum. Then you select the code and you hit the formatting button aka. the button with this symbol: </>

That’s it! Please notice you do not create a new post in case you need to format something you already posted. You can edit your post, copy the code to the PDE, indent the code properly there and then past it back here, format the code and >> save << the edits.

Extra info:

Formatting your code makes everybody’s life easier, your code looks much better plus it ensures your code integrity is not affected by the forum’s formatting (Do you know the forum processes markup code?) Please visit the sticky posts or the FAQ section/post to learn about this, other advantages and super powers you can get in this brand new forum.

Kf

OK Thanks. I thought your editor was verbatim-like…

That is pretty mysterious (to me, at least).

You don’t include headers, so I’m not sure – is Function imported from a standard Java library, or is it a custom method of Beads? To make a Python equivalent, first need to track down what exactly x[] is in the original Java and where it is coming from…

…ah, I should have just looked it up.

Function is a custom class in Beads – specifically, it extends UGen, and it holds x[].

Here is the source:

…and the documentation:


(…so, it is NOT: https://docs.oracle.com/javase/8/docs/api/java/util/function/Function.html )

However, that float[] field is protected! :exploding_head:
BeadsProject.net/doc/net/beadsproject/beads/ugens/Function.html#x

And Jython can’t access non-public members from Java classes! :cold_face:

I’ve had to use reflection in order to access that Function::x[] protected field. :telescope:
And then stored it as an alias field named xx[] right after! :crazy_face:

"""
 Lesson03_FMSynthesis (v1.1)
 Py Mode: GoToLoop (2018-Sep-04)
 Discourse.Processing.org/t/beads-audio-library-in-python-mode/3077/6
"""

add_library('beads')

from java.util import Arrays

FG, BG = 0xffFF66CC, 0xff000000
FREQ = 5.0

def setup():
    size(512, 512)
    noSmooth()
    loadPixels()

    global ac
    ac = AudioContext()

    modulator = WavePlayer(ac, FREQ, Buffer.SINE)
    carrier = WavePlayer(ac, FreqControl(modulator), Buffer.SINE)

    gain = Gain(ac, 1, .1)
    gain.addInput(carrier)

    ac.out.addInput(gain)
    ac.start()


def draw():
    Arrays.fill(pixels, BG)

    siz = ac.bufferSize
    gain = ac.out

    w = width
    h = height

    cy = h >> 1
    res = len(pixels) - 1

    for i in range(w):
        vOff = int(cy * (1 + gain.getValue(0, i * siz / w)))
        pixels[min(res, vOff * h + i)] = FG

    updatePixels()


def keyPressed(): key == ESC and ac.stop()


class FreqControl(Function):
    MULT, AUGMENT = 50.0, 200.0

    def __init__(f, *args, **kw):
        super(FreqControl, f).__init__(*args, **kw)

        f.mul = kw.get('mul', f.MULT)
        f.aug = kw.get('aug', f.AUGMENT)

        fieldX = Function.getDeclaredField('x')
        fieldX.accessible = True
        f.xx = fieldX.get(f)


    def calculate(f): return f.xx[0] * f.mul + f.aug
1 Like

Are you saying Jython can’t extend Java classes? This is meant to be extended, hence why it’s protected.

Not at all. You can see in my solution that I extend the abstract class Function as class Funct. :face_with_monocle:

Can I?! Never used Python / Jython - have contributed to Beads! :smile: But if Jython can’t extend a Java class and access its protected members, it can’t really extend a Java class!

Class Function is already abstract. Thus it already needs to be extended. :face_with_monocle:

Therefore, there’s no need at all for its field x[] to be non-public there. :zipper_mouth_face:

The Java non-public members are inherited, of course. :coffee:

Only they can’t be accessed directly. Need reflection hacks! :robot:

Your inheritance definition is too strict methinks. :flushed:

As a fix to make Python Mode users happy, the abstract class Function should add a public getter for its protected field x[]: :star_struck:

public float getX(final int idx) {
  return x[idx];
}

Er, definitely not! This is the entire reason that protected exists. The fault here is in Jython not Beads.

Hi ! Any of you is in charge of Beads ? Who and when this getter will be available for Python mode users ?
Thanks.

I can’t find the Lesson 03 Python file in the standard distribution which contains only Lesson 01 and 04 ???

Only “Java Mode” got the complete set of 11 examples. :coffee:
My version was converted to “Python Mode” from the “Java Mode” version: :snake:
https://github.com/orsjb/beads/blob/master/packages/Processing/tutorial/Lesson03_FMSynthesis/Lesson03_FMSynthesis.pde

Why should it be up to Beads to work around issues with Jython?!

You’d be better following up on the Beads mailing list - Redirecting to Google Groups

That would be kind to have a complete set of Beads Lessons in Python mode for those who do not program in Java (and your trick is a bit too clever as it requires Java knowledge of protected variables).
Python Beads deserves more than the present number of users, notably for teaching, it’s a good job.
Thanks for the rest of us.

Jython treats any Java’s non-public members as if they were full-force private, regardless whether they have protected or “Package” more liberal access levels. :male_detective:

Although reflection techniques are “clever”, these 3 statements aren’t that hard to grasp IMO: :curling_stone:

fieldX = Function.getDeclaredField('x')
fieldX.accessible = True
self.xx = fieldX.get(self)

Believe me, if that field x[] were private instead of the less restrictive protected, the Java version would have a much bigger boilerplate for us to code: :shield:

import beads.*;
import java.lang.reflect.Field;

class FreqControl extends Function {
  static final float MULT = 50.0, AUGMENT = 200.0;
  final float[] xx;

  @SafeVarargs FreqControl(final UGen... inputs) {
    super(inputs);

    try {
      final Field fieldX = Function.class.getDeclaredField("x");
      fieldX.setAccessible(true);
      xx = (float[]) fieldX.get(this);
    }

    catch (final ReflectiveOperationException cause) {
      throw new RuntimeException(cause);
    }
  }

  @Override float calculate() {
    return xx[0] * MULT + AUGMENT;
  }
}

You should try convincing them to either change the field Function::x[] 's access level to public or include a public getter method for it: :persevere:

Never used Beads library before; but you can always post your Java to Python Mode conversion attempts here in the forum. :sunglasses:

It’s not meant to be public! It’s meant to be private to subclasses, aka. protected.

Beads hasn’t seen much development in a while, but the mailing list I linked earlier is probably the best place to discuss this.

I’ve merely listed the 2 possible easiest options to “fix” the issue for Processing’s Python Mode, regardless of their technical merit. :snake: Which 1 to pick is another matter! :stuck_out_tongue: