Skip to content

Commit

Permalink
Merge pull request #3 from hq9000/cython-vst-loader-2-create-package
Browse files Browse the repository at this point in the history
Cython vst loader 2 create package
  • Loading branch information
hq9000 authored Sep 15, 2020
2 parents fbacd40 + 062a10f commit 58736fa
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 30 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Build Cython module
run: |
cd cython_vst_loader; python setup.py build_ext --inplace
python setup.py build_ext --inplace
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
Expand Down
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
.idea
venv
venv
**/*.pyc

build/
*.c
*.so
__pycache__
5 changes: 5 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
include *.txt
recursive-include cython_vst_loader *.pyx
recursive-include tests *.py
recursive-include tests *.so
include Makefile
File renamed without changes.
109 changes: 108 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,109 @@
# cython-vst-loader
a cython-based loader for VST audio plugins proving a clean python object-oriented interface
a cython-based loader for VST audio plugins providing a clean python object-oriented interface

## Motivation
The purpose is to create a stable and simple wrapper for VST plugins to be used in higher-level projects, such as https://github.com/hq9000/py_headless_daw

## Installation

### Through pip
`pip install cython_vst_loader`

please note that there are requirements to be satisfied by your system in order for the above to succeed:

- a compiler is present (`gcc` in case of linux)
- `make` is installed
- header files for your python versions are installed (`apt install python3.7-dev` in case of Ubuntu)

### As a submodule
You can add this project as a git submodule to yours.

## Usage example

### Loading a plugin
The example below does the following:

- instantiates a plugin object by reading a `.so` vst plugin file.
- checks some plugin attributes

```python
from cython_vst_loader.vst_host import VstHost
from cython_vst_loader.vst_plugin import VstPlugin
from cython_vst_loader.vst_loader_wrapper import allocate_float_buffer, get_float_buffer_as_list, free_buffer, \
allocate_double_buffer, get_double_buffer_as_list
from cython_vst_loader.vst_event import VstNoteOnMidiEvent


sample_rate = 44100
buffer_size = 512

host = VstHost(sample_rate, buffer_size)

# Audio will be rendered into these buffers:
right_output = allocate_float_buffer(buffer_size, 1)
left_output = allocate_float_buffer(buffer_size, 1)

# `right_output` and `left_output` are integers which are, in fact,
# just pointers to float32 arrays cast to `int`

# These buffers are not managed by Python, and, therefore, are not garbage collected.
# use free_buffer to free up the memory

plugin_path = "/usr/bin/vst/amsynth-vst.x86_64-linux.so"
plugin = VstPlugin(plugin_path.encode('utf-8'), host)


# now we can work with this object representing a plugin instance:
assert(41 == plugin.get_num_parameters())
assert (b'amp_attack' == plugin.get_parameter_name(0))
assert (0.0 == plugin.get_parameter_value(0))
```

### Doing something useful with the plugin

The following example performs one cycle of rendering, involving both sending events and requesting to render audio.
```python
# 3: delta frames, at which frame(sample) in the current buffer the event occurs
# 85: the note number (85 = C# in the 7th octave)
# 100: velocity (0..128)
# 1: midi channel
event = VstNoteOnMidiEvent(3, 85, 100, 1)

plugin.process_events([event])
plugin.process_replacing([], [right_output, left_output], buffer_size)
# at this point, the buffers are expected to have some sound of the C# playing
```

### Freeing up buffers

```python
# when we are done, we free up the buffers
free_buffer(left_output)
free_buffer(right_output)
```

### Using numpy arrays as buffers

Although this library does not depend on numpy, you can use numpy arrays as buffers like so:

```python
import numpy as np

def numpy_array_to_pointer(numpy_array: np.ndarray) -> int:
if numpy_array.ndim != 1:
raise Exception('expected a 1d numpy array here')
pointer, _ = numpy_array.__array_interface__['data']
return pointer
```

the resulting value can be supplied to `VstPlugin.process_replacing` as a buffer

**note:** if buffers are created this way, they are managed by numpy and, therefore,
should not be freed manually

## Plugins used for testing

The following open source vst plugins were compiled for linux x86_64 and put into `tests/test_plugins` directory:
- https://github.com/amsynth/amsynth
- https://github.com/michaelwillis/dragonfly-reverb

Empty file added cython_vst_loader/__init__.py
Empty file.
27 changes: 0 additions & 27 deletions cython_vst_loader/setup.py

This file was deleted.

3 changes: 3 additions & 0 deletions release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
rm -rf dist/*.gz
python setup.py sdist
twine upload dist/*
Empty file added setup.cfg
Empty file.
66 changes: 66 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# from distutils.core import setup
import setuptools
from pathlib import Path
import os

from setuptools import Extension

USE_CYTHON = True

try:
# noinspection PyUnresolvedReferences
from Cython.Build import cythonize
except ImportError:
USE_CYTHON = False

this_directory = Path(__file__).parents[0]

os.system("make")

include_paths = [
this_directory.as_posix() + "/build/vstsdk/pluginterfaces/vst2.x"
]

if USE_CYTHON:
ext_modules = cythonize(
'cython_vst_loader/vst_loader_wrapper.pyx',
compiler_directives={'language_level': "3"}
)
else:
ext_modules = [
Extension("cython_vst_loader.vst_loader_wrapper", ["cython_vst_loader/vst_loader_wrapper.c"]),
]

# workaround for https://github.com/cython/cython/issues/1480
for module in ext_modules:
module.include_dirs = include_paths
module.extra_compile_args = [
"-Wno-unused-function"
]
with open(str(this_directory) + '/README.md', encoding='utf-8') as f:
long_description = f.read()

with open(str(this_directory) + '/version.txt', encoding='utf-8') as f:
version = f.read()

setuptools.setup(
ext_modules=ext_modules,
name='cython_vst_loader',
packages=['cython_vst_loader'],
version=version,
license='MIT',
description='a cython-based loader for VST audio plugins providing a clean python object-oriented interface',
long_description=long_description,
long_description_content_type='text/markdown',
author='Sergey Grechin', # Type in your name
author_email='[email protected]',
url='https://github.com/hq9000/cython-vst-loader',
keywords=['vst', 'plugin', 'cython'],
classifiers=[
'Development Status :: 4 - Beta',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
],
)
1 change: 1 addition & 0 deletions version.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.0.1

0 comments on commit 58736fa

Please sign in to comment.