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.