GithubHelp home page GithubHelp logo

celtera / avendish Goto Github PK

View Code? Open in Web Editor NEW
409.0 14.0 12.0 5.89 MB

declarative polyamorous cross-system intermedia objects

License: Other

CMake 4.90% C++ 87.19% C 1.96% JavaScript 5.95%
audio maxmsp c-plus-plus reflection declarative multimedia intermedia ossia puredata pd vst

avendish's Introduction

avendish

declarative polyamorous cross-system intermedia objects

C++ Sponsor License Platforms GitHub Workflow Status

A zero-cost, compile-time, reflection-based, pure C++ solution to the quadratic glue MxN problem:

  • M host environments: DAW plug-in APIs, PureData, Max/MSP, etc.
  • N algorithms.
  • M x N binding code required.

Read the documentation to get started !

The original blog post contains the motivation and some implementation details.

If you use this work in an academic setting, please cite the paper:

@inproceedings{celerier2022rage,
  title={{Rage Against The Glue: Beyond Run-Time Media Frameworks with Modern C++}},
  author={Celerier, Jean-Micha\"el},
  booktitle={Proceedings of the International Computer Music Conference (ICMC)},
  year={2022},
  address={Limerick, Ireland}
}

Features

This library is a proof-of-concept (based on the Boost.PFR library and a couple of tricks), which showcases how with what little reflection modern C++ provides, it is possible to:

  • Automatically generate Python bindings for a C++ class (by leveraging pybind11).
  • Automatically generate OSC / OSCQuery bindings for a C++ class (through libossia).
  • Automatically generate user interfaces for C++ classes like e.g. Unity3D does (an example is provided with Qt and Nuklear).
  • Automatically generate audio plug-ins in a legacy API as an example, as well as in VST3 and Clap formats.
  • Automatically generate Max/MSP and PureData objects.
  • Automatically generate GPU draw and compute pipelines (for now only supported in the ossia/score bindings with the Qt RHI) to create GPU-based media processors with the same syntax.

And some more advanced features:

Design

Unlike many other reflection solutions in C++, this library has two properties:

  • It is entirely non-intrusive. The effects do not even need to include a single header. All the types are introspected from the content of the provided classes ; this library embraces protocols / traits / typeclass-based design mainly thanks to C++ concepts.

    • This is in order to enable at a later point easy targeting of bare-metal platforms.
    • This also ensure that implementations are not dependent of any "reflection" library, only of whatever facilities C++ compilers provide. This is done to provide a perfect separation of concern and make sure that the algorithms will still be easily useable in 20 years, like e.g. when done in Faust.
    • No macro are required to declare the types, etc. No specific function call is necessary. Just write the raw algorithm with its inputs and outputs, optionally add some metadata to make things nicer for the end-user in environments that support them (e.g. value ranges, etc) and you're done.
  • It is entirely done at compile-time through pure C++ mechanisms. No external code generator / scanner à la Qt's moc is used, no forked version of libclang is necessary, etc etc. Instead, it expects types to conform to various concepts corresponding to common multi-media use-cases.

    • As far as it's possible there is zero run-time cost over the equivalent C code. Everything that can be allocated statically, is. There is no run-time mapping of parameters to string, enums, etc. outside of what the host APIs do require.

The current catch is that types being introspected must be aggregates. This is not a very hard restriction in practice and allows for plenty of useful things.

The API is not as clean as it could be - the end-goal would be to have the meta-class and compile-time programming papers merged in the C++ standard in order to remove the remaining little boilerplate there is and open the use-cases & lift various dumb restrictions:

Committee if you hear us :)

Dependencies

  • A C++20 compiler (tested with clang-12 and gcc-11 ; GCC still has a couple bugs). MSVC does not implement the necessary features yet. If you don't have one you can use the ossia SDK which provides clang-12 & libc++ for Windows / Mac / Linux.
  • Boost.PFR : note that the library does not depend on the rest of Boost.
  • (optional) fmtlib. Provides nicer printing in some places.
  • (optional) pybind11.
  • (optional) Max SDK to make Max externals.
  • (optional) PureData headers to make Pd externals.
  • (optional) Steinberg© VST3 SDK to make VST3 plug-ins.
  • (optional) Clap SDK to make Clap plug-ins.
  • (optional) Qt & Verdigris to make QML UIs. Verdigris is a pure-C++ alternative to moc.
  • (optional) libossia to build ossia processors.

Examples

See https://github.com/celtera/avendish/tree/main/examples for a list of examples.

Here is an example of various Max/MSP & PureData objects generated from the examples folder ; the patches are available in https://github.com/celtera/avendish/tree/main/docs.

Max/MSP example

PureData example

Usage

The simplest way to get started is to take a look at the examples, and clone the template repository.

A recent enough clang version is provided for all platforms (check the CI scripts in .github/workflows/cmake).

Audio processor

A most basic avendish audio processor would look like this:

#pragma once
struct Distortion
{
  static consteval auto name() { return "Distortion"; }
  static consteval auto c_name() { return "disto"; }
  static consteval auto uuid() { return "dd4dd880-d525-44fb-9773-325c87b235c0"; }

  struct {
    struct {
      static consteval auto name() { return "Preamp"; }
      static consteval auto control() {
        struct {
          const float min = 0.001;
          const float max = 1000.;
          const float init = 1.;
        } c; return c;
      }

      float value{0.5};
    } preamp;

    struct {
      static consteval auto name() { return "Volume"; }
      float value{1.0};
    } volume;
  } inputs;

  void operator()(double** in, double** out, int frames)
  {
    const double preamp = inputs.preamp.value;
    const double volume = inputs.volume.value;

    for (int c = 0; c < channels; c++)
      for (int i = 0; i < frames; i++)
        out[c][i] = volume * std::tanh(in[c][i] * preamp);
  }
};
}

It will create for instance an audio plug-in with two parameters.

The next example will create a PureData object which:

  • can be initialized with either one float, two floats or a string,
  • can be sent a bamboozle message which will work with the arguments specified in the C++ function declaration
  • will perform an addition between its two inlets.
#pragma once
struct Addition
{
  static consteval auto name() { return "Addition"; }
  static consteval auto c_name() { return "avnd_addition"; }
  static consteval auto uuid() { return "36427eb1-b5f4-4735-a383-6164cb9b2572"; }

  struct {
    struct { float value; } a;
    struct { float value; } b;
  } inputs;

  struct {
    struct { float value; } out;
  } outputs;

  struct {
    struct {
      static consteval auto name() { return "member"; }
      static consteval auto func() { return &Messages::bamboozle; }
    } member;
  } messages;

  void bamboozle(float x, float y, const char* str)
  {
    inputs.a.value = x;
    inputs.b.value = y;
  }

  static constexpr std::tuple initialize{
      [] (Init& self, float a) { std::cout << "A: " << a << std::endl; }
    , [] (Init& self, const char* a, const char* b) { std::cout << "B: " << a << b << std::endl; }
  };

  void operator()()
  {
    outputs.out.value = inputs.a.value + inputs.b.value;
  }
};

A small library of helpers types and macros is provided to simplify the most common use-cases but is in no way mandatory.

Advanced features

As this library currently focuses on the "concept" of an audio effect processor or synthesizer, it provides various amenities tailored for that use case:

  • It's possible to write processors either in a mono, per-sample or multi-channel, per-buffer (and various variations) fashion:
void operator()(double** in, double** out, int frames) { /* my audio code */ }

float operator()(float in) { /* my audio code */ }

... etc ...

If a mono processor is written, the library will wrap it automatically in the case of a multichannel requirement from the host.

  • An example of automatically wrapping controls in std::atomic in order to provide thread-safe access to controls is implemented in one of the backends. Ultimately, this should be something configurable by the user: a Python binding should not have to pay the cost of atomic access, but an audio plug-in with an UI must be thread-safe.

Past travels

  • A first tentative in ossia score based on std::tuple and automatic deduction of function arguments.
  • vintage explored Boost.PFR for creating audio plug-ins
  • This was applied back on the API for ossia plug-ins.
  • And here we are :-)

Future directions (todo!)

Future directions (done!)

  • Support std::optional<T> as a way to indicate message-like input / output.
  • Continue porting the concepts developed in https://github.com/jcelerier/score-simple-api-2/ ; in particular for CPU-based image processing. Extend to e.g. Krita plug-ins.
  • Improve the handling of callbacks.
  • Have a basic UI story.
  • Implement concepts for GPU-based processing.

Licensing

The library is licensed under GPLv3+commercial. The concepts are licensed as permissively as possible (any of Boost license, public domain, BSD0, CC0, you name it) as it's mainly the concepts which enable interoperability. The end goal being that people who make, e.g. plug-in specs provide their own generators based on these concepts, to make sure that we have a thriving interoperable ecosystem.

avendish's People

Contributors

bltzr avatar carlschwan avatar grybouilli avatar jcelerier avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

avendish's Issues

Error trying to build avendish in windows 10

Hello,
I'm trying to compile the example you have at https://celtera.github.io/avendish/getting_started/compiling.html on windows 10. I've been working on it for more than a week and I've reached a point where I can't get out of it. I would like to know if someone has done the same and can give me a hand or I consider it impossible.
First I installed Anaconda, through it I got the python, pybind11, boost, etc. libraries. I downloaded from their respective github ossialib and clap. And I installed PureData, Qt and some other stuff.

From here, this is the process I follow, all the errors I find and how I solve them. Until I reach the point where I can't get out of it. I'm sorry to make this so long.

  • CMake: I managed to do it (with errors) with the following command line:

cmake -DCMAKE_C_COMPILER=G:/Repos/3daudio/llvm/bin/clang.exe -DCMAKE_CXX_COMPILER=G:/Repos/3daudio/llvm/bin/clang++.exe -DCMAKE_PREFIX_PATH="G:\Repos\3daudio\ossia;C:\Program Files\Pd\bin;C:\Program Files\Pd\src;G:\Repos\3daudio\clap-main;G:\Repos\3daudio\llvm;G:\Repos\3daudio\ninja;G:\Repos\3daudio\ossia\bin;G:\Repos\3daudio\libossia-master\src\ossia;G:\Repos\3daudio\qt\5.15.2\mingw81_64" -DCMAKE_BUILD_TYPE=Debug -S. -Bbuild_ninja -GNinja

CMake gives me the following error IMPORTED_IMPLIB not set for imported target "ossia::ossia" configuration which I get around by editing the ..\avendish-audio-processor-template_build_ninja_depsavendish-srcMakeLists.txt file and adding this set_target_properties(ossia::ossia PROPERTIES IMPORTED_IMPLIB "G:/Repos/3daudio/ossia/lib/static/ossia_x64.lib") and lauching again de CMake.

  • Then I launch the build with Ninja, and I get an error because it asks for an XCode MAC sdk. I just edit the build.ninja file, delete the standalone/MyProcessor_standalone.exe from the build all command.

  • I now repeat the build ninja command and I have a series of .h .hpp errors not found that I have not been able to resolve in any way.

[7/37] Building CXX object avnd_build/CMakeFiles/Avendish_clap_pch.dir/cmake_pch.hxx.pch FAILED: avnd_build/CMakeFiles/Avendish_clap_pch.dir/cmake_pch.hxx.pch G:\Repos\3daudio\llvm\bin\clang++.exe -DFMT_HEADER_ONLY=1 -D_LIBCPP_NO_EXCEPTIONS=1 -IG:/Repos/3daudio/avendish-audio-processor-template/build_ninja/avnd_build -IG:/Repos/3daudio/avendish-audio-processor-template/build_ninja/_deps/avendish-src -I/opt/libossia/include -IG:/Repos/3daudio/clap-main/include -IG:/Repos/3daudio/avendish-audio-processor-template/build_ninja/_deps/avendish-src/include -isystem C:/Users/Daniel/miniconda3/Library/include -g -fvisibility=internal -fvisibility-inlines-hidden -fcoroutines-ts -stdlib=libc++ -fno-stack-protector -fno-ident -fno-plt -ffunction-sections -fdata-sections -fno-exceptions -fno-rtti -fno-unwind-tables -fno-asynchronous-unwind-tables -std=gnu++20 -Winvalid-pch -fpch-instantiate-templates -Xclang -emit-pch -Xclang -include -Xclang G:/Repos/3daudio/avendish-audio-processor-template/build_ninja/avnd_build/CMakeFiles/Avendish_clap_pch.dir/cmake_pch.hxx -x c++-header -MD -MT avnd_build/CMakeFiles/Avendish_clap_pch.dir/cmake_pch.hxx.pch -MF avnd_build\CMakeFiles\Avendish_clap_pch.dir\cmake_pch.hxx.pch.d -o avnd_build/CMakeFiles/Avendish_clap_pch.dir/cmake_pch.hxx.pch -c G:/Repos/3daudio/avendish-audio-processor-template/build_ninja/avnd_build/CMakeFiles/Avendish_clap_pch.dir/cmake_pch.hxx.cxx In file included from <built-in>:1: In file included from G:/Repos/3daudio/avendish-audio-processor-template/build_ninja/avnd_build/CMakeFiles/Avendish_clap_pch.dir/cmake_pch.hxx:5: In file included from G:/Repos/3daudio/avendish-audio-processor-template/build_ninja/_deps/avendish-src/include/avnd/binding/clap/all.hpp:3: In file included from G:/Repos/3daudio/avendish-audio-processor-template/build_ninja/_deps/avendish-src/include/avnd/binding/clap/audio_effect.hpp:5: G:/Repos/3daudio/avendish-audio-processor-template/build_ninja/_deps/avendish-src/include/avnd/binding/clap/bus_info.hpp:9:10: fatal error: 'clap/all.h' file not found #include <clap/all.h>

[8/37] Building CXX object avnd_build/CMakeFiles/Avendish_ossia_pch.dir/cmake_pch.hxx.pch FAILED: avnd_build/CMakeFiles/Avendish_ossia_pch.dir/cmake_pch.hxx.pch G:\Repos\3daudio\llvm\bin\clang++.exe -DBOOST_ASIO_DISABLE_CONCEPTS=1 -DBOOST_ASIO_ENABLE_BUFFER_DEBUGGING -DBOOST_MATH_DISABLE_FLOAT128=1 -DBOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING -DBOOST_MULTI_INDEX_ENABLE_SAFE_MODE -DFMT_HEADER_ONLY=1 -DRAPIDJSON_HAS_STDSTRING=1 -DSERVUS_USE_DNSSD -DTINYSPLINE_DOUBLE_PRECISION -IG:/Repos/3daudio/avendish-audio-processor-template/build_ninja/avnd_build -IG:/Repos/3daudio/avendish-audio-processor-template/build_ninja/_deps/avendish-src -I/opt/libossia/include -IG:/Repos/3daudio/avendish-audio-processor-template/build_ninja/_deps/avendish-src/include -isystem G:/Repos/3daudio/ossia/include -isystem C:/Users/Daniel/miniconda3/Library/include -g -fvisibility=internal -fvisibility-inlines-hidden -fcoroutines-ts -stdlib=libc++ -fno-stack-protector -fno-ident -fno-plt -ffunction-sections -fdata-sections -std=gnu++20 -Winvalid-pch -fpch-instantiate-templates -Xclang -emit-pch -Xclang -include -Xclang G:/Repos/3daudio/avendish-audio-processor-template/build_ninja/avnd_build/CMakeFiles/Avendish_ossia_pch.dir/cmake_pch.hxx -x c++-header -MD -MT avnd_build/CMakeFiles/Avendish_ossia_pch.dir/cmake_pch.hxx.pch -MF avnd_build\CMakeFiles\Avendish_ossia_pch.dir\cmake_pch.hxx.pch.d -o avnd_build/CMakeFiles/Avendish_ossia_pch.dir/cmake_pch.hxx.pch -c G:/Repos/3daudio/avendish-audio-processor-template/build_ninja/avnd_build/CMakeFiles/Avendish_ossia_pch.dir/cmake_pch.hxx.cxx In file included from <built-in>:1: G:/Repos/3daudio/avendish-audio-processor-template/build_ninja/avnd_build/CMakeFiles/Avendish_ossia_pch.dir/cmake_pch.hxx:5:10: fatal error: 'ossia/prefix.hpp' file not found #include <ossia/prefix.hpp>

I understand that the clap/all.h and ossia/prefix files are not properly included. However, both have been copied correctly to their corresponding sub-folder within the ...\avendish-audio-processor-template\build_ninja_deps\avendish-src\include\avnd.

I have also tried to make the CMake for visual studio. I manage to get a solution (.sln file) but I have similar problems when trying to do the build.

Well, if you have read this far, thank you very much for the effort. If you can give me a hand, I'd appreciate it.

Best regards.

Concepts dependencies

My assumption was that the headers in avnd/concepts would be independent from the rest of the library, and that I would be able to e.g. #include <avnd/concepts/parameter.hpp from my project without drawing in any dependencies besides Avendish itself. However, when I tried to do so GCC complains that I don't have <boost/mp11/algorithm.hpp>, so I guess my assumption was mistaken.

Is this due to my misunderstanding, or is it an issue that the concepts depend on boost/mp11?

Dynamic ports

Maybe we can do it simply by specializing avnd::for_each_field(...) for the case of ranges?

Concepts Licensing and (Transitive) Includes

Considering a scenario where someone may wish to use avendish and release their source under a permissive license, or (aghast!) keep it closed source, I noticed that e.g. avnd/concepts/parameter.hpp with public domain licensing includes avnd/wrappers/widgets.hpp with a strong copy-left license; I realize that I am not sure then how to interpret the copyright status of the former. Doesn't including GPL code in your code mean you are obliged to release your code under the same license? Doesn't that mean that avnd/concepts/parameter.hpp, and anything else that includes avnd/wrappers/widgets.hpp or other GPL headers, is obliged to be licensed under the GPL? Is this a bug in avendish, or my understanding of the GPL?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.