Python-specific Notes

The following are brief notes on Python-specific binding features and how the Libaudioverse API maps to Python.

Comparing Objects

Libaudioverse objects are thin proxies over handles, with their global state stored elsewhere in the Libaudioverse module. Consequently, you need to use ==, not is. is will work sometimes, but no guarantee is made that two node objects in different variables are the same proxy instance.

Properties

Properties are bound as Python properties, set up so that you can simply set them as normal: sine.frequency = 32.5. In addition, the objects representing numeric properties overload all operators expected for their types. The value attribute also exists on all properties. It is most useful to get the value of other property types (namely string and integer properties set from enums).

Some old code uses the .value suffix for all operations on the right-hand side of assignment, as well as on the left-hand side of compound assignment operators. For example, sine.frequency = sine.frequency.value + 2 or sine.frequency.value += 2. This is no longer necessary because numeric properties now behave like numbers.

Note the following important points:

  • Properties do not implement __hash__ because there is no sensible definition that can satisfy all cases.
  • Storing the property proxy object keeps the node it came from alive. If you need to store values of properties, it is much more efficient to extract the value. x = sine.frequency keeps the sine node alive at least until x dies, but x = sine.frequency.value doesn’t.
  • If you store a property object, changing it (i.e. assignment, *=, calling reset) will affect the node.

Callbacks

As you read the following section, note that callbacks need to be fast. The following could be abstracted more, but not without significant performance penalties. this is the only functionality which requires you to deal directly with ctypes.

Callbacks are bound as node.set_XXX_callback for setting and node.get_XXX_callback from getting. The getter will always return the same callable as passed to the setter.

Python holds a strong reference to the last set callback object at least until Libaudioverse deletes the handle. You can therefore use any callable safely. Libaudioverse does not guarantee when the callable will be deleted, so it is possible for large objects to be kept alive.

In the reference, nodes with callbacks document the funcctionality of the callback with the setter and show the full C signature. For example, the callback used with the graph listener is void (LavHandle nodeHandle, unsigned int frames, unsigned int channels, float * buffer, void * userdata). Libaudioverse performs the following transformations for you:

  • Any arguments which are of type LavHandle are turned into appropriate instances of the appropriate classes.
  • If the last argument is a void* to userdata, it is not visible to you and is abstracted over by the bindings.
  • All other arguments are passed as-is using appropriate ctypes types.

Arguments to the callback are always passed as the first n positional arguments. Setter functions also allow you to provide additional_args and additional_kwargs for convenience. These arguments are added *after*the arguments from Libaudioverse.

If the callback returns void, you need to return None. Otherwise, you need to return something that can be converted to the appropriate ctypes type. Most callbacks do not expect you to return anything. It is more common to require you to write to a buffer of audio data.

Atomicity and Simulation Locking

instead of binding Lav_simulationLock and Lav_simulationUnlock directly, the simulation is a context manager. Using a with statement will make everything inside it inaudible until the with statement ends. They may be nested safely.

For example:

with my_simulation:
    # some stuff

Note that all the concerns in the language-agnostic manual apply. If you use the simulation as a context manager on more than one thread, it behaves exactly like a lock; furthermore, all Libaudioverse functions that use the simulation or objects created from it will block on any thread but the one currently inside the critical section.

name Conversion

The names for Libaudioverse objects can be seen in the API reference. This section is provided in order to aid in translating examples in C to Python. This module follows PEP8 in the external API. The code decidedly does not, as it is generated from custom tools.

Python class names are generated by taking the Lav_OBJTYPE_FOOBAR enum constants, stripping the Lav_OBJTYPE_ prefix, and converting the rest to camelcase.

Enumerations are generated in a similar manner as objects: strip the Lav_ prefix and convert the rest to camelcase. The members have their common prefix stripped and are then converted to lowercase, in accordance with the PEP8 class member standards.