More build documentation.

Kai Blaschke 2022-02-17 18:13:26 +01:00
parent 72f1aadab3
commit 64b1099cf4
No known key found for this signature in database
GPG Key ID: B014B6811527389F
5 changed files with 954 additions and 9 deletions

23
Home.md

@ -16,15 +16,18 @@ The Wiki is organized into three main sections:
## projectM user manuals
If you're using one of the "official" projectM applications maintained by this project, you can find documentation for
those applications on the following pages. If you're unsure whether your application is actually affiliated with us,
please check the [applications list](projectM-Applications) first. Only those listed as "provided by the projectM team"
are covered here and can be supported by the maintainers. For all other applications, please refer to their respective
project pages or contact their authors directly for support inquiries.
If you're looking for manuals and information on one of the "official" projectM applications maintained by this project,
you can find documentation for those applications in their respective repository wikis:
- [Standalone and Steam applications](User-Manual-(Standalone))
- [EyeTune (Windows Store)](User-Manual-(EyeTune))
- [Music.app Plug-In](User-Manual-(Music.app-PlugIn))
- [SDL-based standalone and Steam applications](https://github.com/projectM-visualizer/frontend-sdl2/wiki)
- [Qt-based standalone UI](https://github.com/projectM-visualizer/frontend-qt/wiki)
- [EyeTune (Windows Store)](https://github.com/projectM-visualizer/frontend-uwp/wiki)
- [Apple Music.app plug-in](https://github.com/projectM-visualizer/frontend-music-plug-in/wiki)
If you're unsure whether your application is actually affiliated with us, please check
the [applications list](projectM-Applications) first. Only those listed as "provided by the projectM team" are covered
here and can be supported by the maintainers. For all other applications, please refer to their respective project pages
or contact their authors directly for support inquiries.
## Integrating projectM into applications
@ -35,7 +38,9 @@ Before starting, also make sure you have read the LGPL license and understood wh
legally use and distribute projectM with your application.
- [Download and build projectM for application development](Building-libprojectM)
- [Quickstart guide for integrating projectM into your application](Integration-Quickstart-Guide)
- [projectM API reference](API-Reference)
- [Build troubleshooting](Build-Troubleshooting)
- [Licensing obligations](projectM-Licensing)
## Contributing to projectM
@ -49,4 +54,4 @@ The pages below will help you get an overview of the projectM architecture, road
- [Getting started with projectM development](Getting-started-with-projectM) - Read this first if you're new to projectM
and/or OSS development in general.
- [Architectural overview](projectM-Architecture)
- [Coding and contribution guidelines](projectM-Developer-Guidelines)
- [Coding and contribution guidelines](projectM-Developer-Guidelines)

@ -0,0 +1,586 @@
# Building the projectM library
The following build instructions apply to the most recently released projectM sources.
### Important note on build tool changes in 4.0
Older projectM releases (up to 3.1.12, released in 2020) used autoconf/automake or native project files for building.
This is no longer supported and all build files have been replaced with CMake build scripts that can be used on all
platforms. If you need to build one of these old releases, please refer to the build instructions included in the
top-level dir of the respective source release. This page is only valid for releases of lipbprojectM 4.0 or later.
For more detailed instructions and other operating systems, [skip below the Linux quick start guide](#dependencies).
## Quick Start for Linux (Debian / Ubuntu)
### Install the build tools and dependencies
Mandatory packages:
```bash
sudo apt install build-essential cmake libgl1-mesa-dev mesa-common-dev libglm-dev
```
Optional packages for additional features:
```bash
sudo apt install libsdl2-dev # for building the integrated developer test UI
sudo apt install llvm-dev # for using the experimental LLVM Jit
sudo apt install ninja # To build projectM with Ninja instead of make
```
### Download the projectM sources
If you want to use a stable version of projectM, download the latest release from
the [Releases page on GitHub](https://github.com/projectM-visualizer/projectm/releases) and unpack it. You can then skip
to the next step.
If you prefer a bleeding-edge version or want to modify the code, clone the Git repository:
```bash
sudo apt install git # Probably already installed
git clone https://github.com/projectM-visualizer/projectm.git /path/to/local/repo
cd /path/to/local/repo
git fetch --all --tags
```
### Build and install projectM
Replace `/usr/local` with your preferred installation prefix.
#### Configure the project
```bash
sudo apt install cmake
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local ..
```
To generate Ninja scripts instead of Makefiles, add `-GNinja` to the above command.
#### Build and install
These commands will build projectM and install it to /usr/local or the configured installation prefix set in the step
before:
```bash
cmake --build . -- -j && sudo cmake --build . --target install
```
**Note**: You won't need to use `sudo` if the install prefix is writeable by your non-privileged user.
#### Test projectM
You can't directly run libprojectM on its own. For development testing, the old pre-4.0 SDL UI is still available and
can be enabled using `-DENABLE_SDL_UI=ON` via the CMake configuration command.
After building, you can run the SDL test UI directly from your build tree. It will not be installed alongside the
library and headers. All end-user applications are available in separate repositories.
## Dependencies
Depending on the OS/distribution and packaging system, libraries might be split into separate packages with binaries and
development files. To build projectM, both binaries and development files need to be installed.
#### General build dependencies for all platforms:
* A working build toolchain.
* [**CMake**](https://cmake.org/): Used to generate platform-specific build files.
* **OpenGL**: 3D graphics library. Used to render the visualizations.
* or **GLES3**: OpenGL libraries for embedded systems, version 3. Required to build projectM on Android devices,
Raspberry Pi, Emscripten and the Universal Windows Platform.
* [**glm**](https://github.com/g-truc/glm): OpenGL Mathematics library. Optional, will use a bundled version with
autotools or if not installed.
* [**SDL2**](https://github.com/libsdl-org/SDL): Simple Directmedia Layer. Version 2.0.5 or higher is required to build
the test UI.
* [**LLVM**](https://llvm.org/): Low-Level Virtual Machine. Optional and **experimental**, used to speed up preset
execution by leveraging the LLVM JIT compiler.
### Only relevant for Windows:
* [**vcpkg**](https://github.com/microsoft/vcpkg): C++ Library Manager for Windows. _Optional_, but recommended to
install the aforementioned library dependencies.
* [**GLEW**](http://glew.sourceforge.net/): The OpenGL Extension Wrangler Library. Only required if using CMake to
configure the build, the pre-created solutions use a bundled copy of GLEW.
NuGet is no longer supported.
## Building on Linux and macOS
### Installing dependencies
- Linux distributions will have packages available for most (if not all) required libraries. The package names and
commands to install them vary widely between distributions (and even versions of the same distribution). Please refer
to the documentation of your build OS on how to find and install the required libraries.
- On *BSD, install the appropriate Ports with `pkg install`.
- On macOS, using [Homebrew](https://brew.sh/) is the recommended way of installing any dependencies not supplied by
Xcode.
### Building with CMake
---
:exclamation: **IMPORTANT NOTE**: Currently, CMake build support is still in active development and considered
unfinished. It is working and produces running binaries, but there are still some features, build internals and whole
targets missing. While testing the CMake build files on any platform and feedback on this is strongly encouraged,
CMake-based builds should not yet be used in any production environment until this message is gone.
---
The steps documented below are a bare minimum quickstart guide on how to build and install the project. If you want to
configure the build to your needs, require more in-depth information about the build process and available tweaks, or on
how to use libprojectM in your own CMake-based projects, see [BUILDING-cmake.md](BUILDING-cmake.md).
Using CMake is the recommended and future-proof way of building projectM. CMake is a platform-independent tool that is
able to generate files for multiple build systems and toolsets while using only a single set of build instructions.
CMake support is still new and in development, but will replace the other project files (automake/autoconf scripts,
Visual Studio solutions and Xcode projects) in this repository once mature and stable.
Building the project with CMake requires two steps:
- Configure the build and generate project files.
- Build and install the project using the selected build tools.
**Note:** When building with CMake, the build directory should always be separate from the source directory. Generating
the build files directly inside the source tree is possible, but strongly discouraged. Using a subdirectory,
e.g. `cmake-build` inside the source directory is fine though.
This documentation only covers project-specific information. CMake is way too versatile and feature-rich to cover any
possible platform- and toolset-specific configuration details here. If you are not experienced in using CMake, please
first read the [official CMake documentation](https://cmake.org/cmake/help/latest/) (at least
the [User Interaction Guide](https://cmake.org/cmake/help/latest/guide/user-interaction/index.html)) for basic usage
instructions.
#### Configure the build
Configuring a non-debug build with default options and install prefix (`/usr/local`) can be done with these commands,
building in a subdirectory inside the source directory:
```shell
cd /path/to/source
mkdir cmake-build
cd cmake-build
cmake -DCMAKE_BUILD_TYPE=Release ..
```
CMake will check all required dependencies and display any errors. If configuration was successful, a summary of the
build configuration is printed and CMake should display a `Generating done` line. The project is now ready to build.
#### Compile and install the project
Depending on your generator choice, you can use your selected toolset as usual to build and install projectM:
- With `Unix Makefiles`, run `make && sudo make install`.
- With `Ninja`, run `ninja && sudo ninja install`.
- With `Xcode`, select the appropriate target and configuration in Xcode and build it, or `INSTALL` to install the
project.
You can also use CMake's build mode to run the selected toolset and build any specified target. CMake knows which
command to call and which parameters to pass, so the syntax works on all platforms with all generators. If you've
already set the top-level build directory as working directory, simply pass `.` as `/path/to/build/dir`:
```shell
cmake --build /path/to/build/dir --config Release
sudo cmake --build /path/to/build/dir --config Release --target install
```
If you don't need root permissions to install running the second command without `sudo` is sufficient.
If you want to provide arguments directly to the toolset command, add `--` at the end of the CMake command line followed
by any additional arguments. CMake will pass these *unchanged and unchecked* to the subcommand:
```shell
cmake --build /path/to/build/dir --config Release -- -j 4
```
## Building on Windows
To build the projectM library and the SDL-based standalone application, CMake must be used to create the project files
first. Using vcpkg to pull in the build dependencies is highly recommended, as CMake can't use NuGet (NuGet pulls in
dependencies using the project files, while CMake requires the libraries before creating the project files).
#### Installing the dependencies with vcpkg
As stated above, using vcpkg is the easiest way to get the required dependencies. First,
install [vcpkg from GitHub](https://github.com/microsoft/vcpkg) by following the official guide. Then install the
following packages for your desired architecture (called "triplet"):
- `glew`
- `sdl2`
The `glew` package will also pull in the `opengl` libraries.
Example to install the libraries for the x64 architecture, run from a Visual Studio command prompt:
```commandline
vcpkg install glew:x64-windows sdl2:x64-windows
```
#### Creating the Visual Studio solution
CMake provides separate generators for different Visual Studio versions. Newer CMake versions will support recent Visual
Studio releases, but may remove generators for older ones. To get a list of available generators from the command line,
use the `-G` switch without an argument. The CMake GUI will present you a dropdown list you can easily select from.
To set the build architecture in Visual Studio builds, use the `-A` switch and specify either `Win32` or `X64` as the
argument. If you want to build for both architectures, create separate build directories and configure them accordingly.
To make CMake aware of the installed vcpkg packages, simply use the provided toolchain file when configuring the
projectM build by
pointing [`CMAKE_TOOLCHAIN_FILE`](https://cmake.org/cmake/help/latest/variable/CMAKE_TOOLCHAIN_FILE.html) to it.
Here is a full command line example to create a Visual Studio 2019 solution for X64:
```commandline
cmake -G "Visual Studio 16 2019" -A "X64" -DCMAKE_TOOLCHAIN_FILE="<path to vcpkg>/scripts/buildsystems/vcpkg.cmake" -S "<path to source dir>" -B "<path to build dir>"
```
If you use the CMake GUI, check the "Specify toolchain file for cross-compiling" option in the first page of the
configuration assistant, then select the above `vcpkg.cmake` file on the second page.
Another option is to open the project folder in a recent Visual Studio version as a CMake project and configure CMake
using Visual Studio's JSON-based settings file.
#### Building the solution
To build the project, open the generated solution in Visual Studio and build it like any other solution. Each time the
CMake files are changed, Visual Studio will automatically regenerate the CMake build files and reload the solution
before continuing the build. Be aware that in old Visual Studio versions (2015 and earlier) the reload-and-continue
might not work properly.
You can also build the solution with msbuild via the command line, or use CMake's build wrapper to do that for you:
```commandline
cmake --build "<path to build dir>" --config Release
```
#### Using Ninja to build
The Ninja build system is shipped with Visual Studio since version 2019 and used by default if loading a CMake project
directly from within the IDE. Ninja can also be [installed separately](https://github.com/ninja-build/ninja/releases).
To configure the build directory for Ninja, pass `Ninja` or `Ninja Multi-Config` as the argument for the `-G` switch.
The difference between both generators is that the former uses `CMAKE_BUILD_TYPE` to specify the configuration (
e.g. `Debug` or `Release`) while the latter supports all configurations in a single build directory, specified during
build time.
The architecture is determined from the toolset, so make sure to run the commands in the correct Visual Studio command
prompt, e.g. "Native Tools for X64".
Configure and build for a single-configuration Release build with vcpkg:
```commandline
cmake -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="<path to vcpkg>/scripts/buildsystems/vcpkg.cmake" -S "<path to source dir>" -B "<path to build dir>"
cmake --build "<path to build dir>"
```
Same, but using the multi-configuration generator:
```commandline
cmake -G "Ninja Multi-Config" -DCMAKE_TOOLCHAIN_FILE="<path to vcpkg>/scripts/buildsystems/vcpkg.cmake" -S "<path to source dir>" -B "<path to build dir>"
cmake --build "<path to build dir>" --config Release
```
## Notes on other platforms and features
### Raspberry Pi (and other embedded systems)
* projectM is arch-independent, although there are some SSE2 enhancements for x86
* [Notes on running on raspberry pi](https://github.com/projectM-visualizer/projectm/issues/115)
### Build using NDK for Android
To build projectM using the Android SDK, please refer to the official NDK docs:
> https://developer.android.com/ndk/guides/cmake
It is highly recommended using the latest NDK and CMake >= 3.21 for building.
### LLVM JIT
There are some optimizations for parsing preset equations that leverage the LLVM JIT. You can try adding the CMake
option `-DENABLE_LLVM=ON` to enable them. They may not work with a newer version of
LLVM (https://github.com/projectM-visualizer/projectm/pull/360).
### Presets and textures
The libprojectM repository does no longer contain any additional assets like presets and textures. These have been
outsourced into [separate repositories](https://github.com/projectM-visualizer?q=presets-) to make it easier to select
certain preset packs to be packaged in applications and to reduce the size and scope of the libprojectM repository.
# Detailed CMake reference
If you require more in-depth information about the CMake build scripts, you will find more information below.
## Selecting a specific project file generator
To specify a CMake generator, use the `-G` switch, followed by the generator name. Some newer generators take an
additional architecture using the `-A` switch. To list all available generators available on your current platform,
leave out the generator name:
```shell
cmake -G
```
Additional information on the supported generators can be
found [in the CMake documentation](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html).
### Popular generators
By default, CMake will use the [`Unix Makefiles`](https://cmake.org/cmake/help/latest/generator/Unix%20Makefiles.html)
generator on Linux and macOS, which is a good choice and should work. Yet in some circumstances, you might want to
generate project files for a specific build tool or IDE:
```shell
cmake -G "Unix Makefiles" -S /path/to/source/dir -B /path/to/build/dir
```
A common alternative is the [`Ninja`](https://cmake.org/cmake/help/latest/generator/Ninja.html) generator, which
requires `ninja` to be installed. It is mostly a `make`
replacement with less overhead and should work equally well. It is supported on all major platforms, including Windows:
```shell
cmake -G Ninja -S /path/to/source/dir -B /path/to/build/dir
```
On macOS, CMake also supports the [`Xcode`](https://cmake.org/cmake/help/latest/generator/Xcode.html) generator. It will
create an `.xcodeproj` bundle which you can open in Xcode. It also adds support for automatic code signing, which might
be required if your application using projectM needs to be notarized for store deployment.
```shell
cmake -G Xcode -S /path/to/source/dir -B /path/to/build/dir
```
If you develop on Windows, you will possibly use Visual Studio. While recent visual Studio versions have CMake support
built-in, you can still pre-generate the solution and project files and open the `.sln` file from the build directory.
CMake provides a separate generator for each Visual Studio release. For Visual Studio 2019 you would use
the [`Visual Studio 16 2019`](https://cmake.org/cmake/help/latest/generator/Visual%20Studio%2016%202019.html) generator
and provide an additional architecture parameter:
```shell
cmake -G "Visual Studio 16 2019" -A "X64" -S /path/to/source/dir -B /path/to/build/dir
```
It is not possible to generate multi-arch solutions with CMake though. You need to create separate build directories and
use the respective `-A` switch for each.
## Project-specific configuration options
CMake has no built-in way of printing all available configuration options. You can either refer to the
top-level `CMakeLists.txt` which contains a block of `option` and `cmake_dependent_option` commands, or use one of the
available CMake UIs which will display the options after configuring the project once.
### Boolean switches
The following table also gives you an overview of the available options and their defaults. All options accept a boolean
value (`YES`/`NO`, `TRUE`/`FALSE`, `ON`/`OFF` or `1`/`0`) and can be provided on the configuration-phase command line
using the `-D` switch.
| CMake option | Default | Required dependencies | Description |
|-------------------------|---------|-----------------------|--------------------------------------------------------------------------------------------------|
| `ENABLE_DEBUG_PREFIX` | `ON` | | Adds `d` to the name of any binary file in debug builds. |
| `ENABLE_EMSCRIPTEN` | `OFF` | `GLES`, `Emscripten` | Build for the web using Emscripten. Automatically enabled when using the Emscripten build tools. |
| `ENABLE_SDL_UI` | `OFF` | `SDL2` | Builds and installs the SDL test UI. Also required for the tests. |
| `ENABLE_GLES` | `OFF` | `GLES` | Use OpenGL ES 3 profile for rendering instead of the Core profile. |
| `ENABLE_THREADING` | `ON` | `pthreads` | Enable multithreading support. If enabled, will cause an error if pthreads are not available. |
| `ENABLE_NATIVE_PRESETS` | `OFF` | | Builds and installs the binary (native) preset libraries. |
| `ENABLE_TESTING` | `OFF` | `SDL2` | Builds the unit tests. |
| `ENABLE_LLVM` | `OFF` | `LLVM` | Enables experimental LLVM JIT support. |
### Path options
There are also a few textual parameters that can be used to fine-tune the installation directories. Relative paths in
the following options are appended to the value
of [`CMAKE_INSTALL_PREFIX`](https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html) (which, on most UNIX
platforms, defaults to `/usr/local`):
| CMake option | Default | Description |
|-------------------------|------------------|------------------------------------------------------------------------|
| `PROJECTM_BIN_DIR` | `bin` | Directory where executables (e.g. the unit tests) are installed. |
| `PROJECTM_LIB_DIR` | `lib` | Directory where libprojectM is installed. |
| `PROJECTM_INCLUDE_DIR` | `include` | Directory where the libprojectM include files will be installed under. |
## Always perform out-of-tree builds!
Most classic IDEs and build systems directly make use of the source tree and create project files, temporary build
artifacts (e.g. object files) and the final binaries in the same directory structure as the source files. An advantage
of this approach is that you can find all compiled binaries side-by-side with their sources and generated headers are
already in the same directories as the source files including them. This approach has some drawbacks though:
- Only a single build configuration is supported as files are overwritten in-place.
- A lot of noise is created in the source directory, making it hard to distinguish between generated and original source
files.
- A very large `.gitignore` file is required to cover all unwanted files.
- Mistakes in the build scripts can overwrite source files, causing errors and destroy uncommitted work.
Some of these can be mitigated by providing additional targets (`make clean` and `make distclean`) or creating
subdirectories for Debug/Release build configurations.
While CMake also supports in-tree builds, it is "discouraged" in the official documentation, for the above reasons.
Building out-of-tree allows it to create multiple build directories with different configurations which do not influence
each other in any way. If a build directory contains unwanted artifacts, and you want to start fresh, simply delete and
recreate the whole directory - no work is lost.
This project follow this principle by treating the original source tree as read-only and avoiding potential conflicts:
- Everything under [`CMAKE_SOURCE_DIR`](https://cmake.org/cmake/help/latest/variable/CMAKE_SOURCE_DIR.html) must only be
read, never changed or written to.
- Everything under [`CMAKE_BINARY_DIR`](https://cmake.org/cmake/help/latest/variable/CMAKE_BINARY_DIR.html) is temporary
and related to the current build configuration.
- When generating configuration-dependent files,
use [`CMAKE_CONFIGURATION_TYPES`](https://cmake.org/cmake/help/latest/variable/CMAKE_CONFIGURATION_TYPES.html)
and [`CMAKE_BUILD_TYPE`](https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html) to create non-conflicting
files in the build tree.
While this project will not force you to build out-of-tree, there is no mechanism to clean up the generated files after
running cmake in-tree.
## CMake build directory layout
If you are new to CMake, the way of how CMake creates the build directory and where it creates the build targets might
be confusing. Here is a summary of what's in the build directory and how it is structured in general.
### Using files from the build tree
It is generally not good practice to directly take binaries and other files from the build tree for packaging, for
several reasons:
1. The directory structure is generated by CMake and depends on the generator used. The layout might change between
CMake versions, even for the same generator.
2. On platforms with RPATH support, CMake will store absolute paths in executables and shared libraries which point to
the absolute paths of any linked dependencies, either from the build tree or external libraries as well. These
binaries are not relocatable and will most certainly not work if run on any other computer (or even on the same after
deleting the build directory).
3. For some configurations, even Release build artifacts may contain debug symbols until they are installed.
It is fine to build and run executables from the build directory for development and debugging. For packaging, always
use the `install` target and copy files from there.
### Generated files
In the top-level build directory, CMake creates a few files that are present on any platform:
- `CMakeCache.txt`: This file contains all variables and build settings CMake needs to remember from the first
configuration run. This file can be edited on demand either manually or using a CMake UI to change any values. On the
next build, CMake will regenerate the project files if this file has been modified.
- `cmake_install.cmake`: Contains generated install-related settings.
- `install_manifest.txt`: After installing the project, this file contains a list with absolute filenames of all
installed files. It can be used for packaging or deleting installed files as CMake doesn't define an `uninstall`
target.
- The top-level project file for use with the selected build toolset, e.g. `Makefile`, `build.ninja`, `projectm.sln`
or `projectm.xcodeproj`, plus additional toolset-specific files.
The projectM build files generate additional files used in the build and install phases:
- `config.inp`: The default configuration file, by default installed to `<prefix>/share/projectM`.
- `libprojectM.pc`: Package configuration file for use with `pkgconfig`.
- `include/config.h`: A forced include file that contains macro definitions to enable or disable certain code features
based on the build configuration and availability of platform headers and features. Similar to the
autoheader-generated file.
### Subdirectory structure
The rest of the directory structure generally resembles the source tree. Source directories containing
a `CMakeLists.txt` file will also be created in the build tree with the same relative path. Each of these subdirectories
contains a `CMakeFiles` directory with CMake-internal data, generated project files for the select toolset, e.g.
makefiles and any temporary compile artifacts.
### Executable and library locations
Build targets - shared/static libraries and executables - are created in the same subdirectory in the build tree as
the `CMakeLists.txt` file that defines the target in the source tree (which, in most cases, resides in the same
directory as the source files). Depending on the generator used, the binaries are created directly in the directory for
single-configuration generators (like `Unix Makefiles` or `Ninja`) and in a subdirectory with the configuration name,
e.g. `Debug` or `Release`, for multi-configuration generators like `Xcode` or `Visual Studio 16 2019`.
You may also find additional files and symbolic links in the same location depending on the platform, e.g. `.pdb` files
on Windows.
## Using libprojectM in other CMake projects
See also: [Integration quickstart guide](Integration-Quickstart-Guide)
The built and installed static or shared projectM library can be used in other CMake-based projects to provide embedded
audio visualization using `find_package(libprojectM)`. There are also two additional ways:
- Importing the library CMake targets directly from the build tree without installation.
- Directly adding/compiling the libprojectM source tree into the embedding application.
The latter approach is not recommended for different technical reasons and should generally be avoided.
### Importing libprojectM targets from the build tree
This approach is useful for projects that either require more in-depth access to the projectM library files, especially
to headers that are not installed as part of the public API. This might cause issues if the internal headers change, but
gives a broader set of features and more control to the developer.
To use the targets, follow these steps:
- Configure the build directory as needed.
- Build the required library targets `projectM_static` and `projectM_shared` as needed, or simply everything.
- Include the file `src/libprojectM/projectM-exports.cmake` from the projectM build tree in your project.
All targets and their interface properties are now defined and can be used.
#### Example
```cmake
# libprojectM.a/.lib is already built.
set(PROJECTM_BUILD_DIR "/some/path/to/projectM/build")
include("${PROJECTM_BUILD_DIR}/src/libprojectM/projectM-exports.cmake")
add_executable(MyApp main.cpp)
target_link_libraries(MyApp PRIVATE libprojectM::static)
```
This will also add all projectM include directories to the target's source files, pointing to the respective projectM
source directories.
Look at the generated file in the build tree if you need more details about the available targets.
### Importing libprojectM targets from an installed version
This is the recommended way of importing libprojectM in your project. This project installs a set of CMake files
in `<PREFIX>/<LIBDIR>/cmake/libprojectM`, containing target definitions, version and dependency checks as well as any
additional libraries required for linking. Other projects then use CMake's `find_package` command to search for these
files in [different locations](https://cmake.org/cmake/help/latest/command/find_package.html#search-procedure).
In the case projectM libraries and headers are not installed in any system search path, you need to add either the
install prefix path (the top-level install dir) or the directory containing the libraries (the `lib` dir by default) to
the [`CMAKE_PREFIX_PATH`](https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html) list.
If the package was found, you can then link against one of these provided targets:
- `libprojectM::static` - The static library (`.a` or `.lib`).
- `libprojectM::shared` - The shared library (`.so`, `.dylib` or `.dll`).
Note that the availability of the targets depend on the platform and build configuration, so only one of those might be
available. You can check with `if(TARGET libprojectM::static)` and `if(TARGET libprojectM::shared)` to select the
available or preferred one.
Depending on how the package was built, targets might be available for multiple configurations or only `Release`. CMake
will automatically select the most appropriate one to link.
Include dirs, additional link dependencies and possible compiler options will be propagated to any target the library is
linked to.
#### Example
Links the shared library preferably, with fallback to static:
```cmake
find_package(libprojectM REQUIRED)
if (TARGET libprojectM::shared)
set(PROJECTM_LINK_TARGET libprojectM::shared)
else ()
# If this target is also unavailable, CMake will error out on target_link_libraries().
set(PROJECTM_LINK_TARGET libprojectM::static)
endif ()
add_executable(MyApp main.cpp)
target_link_libraries(MyApp PRIVATE ${PROJECTM_LINK_TARGET})
```

@ -0,0 +1,259 @@
# Integration Quickstart Guide
projectM is released as either a shared or static library and provides a C-based API for all exposed functionality. This
means that developers can use projectM in any application or with any programming language that supports calling native
C functions and provide a proper OpenGL rendering context, including the platform-specific drawing surface.
The quickstart guide explains how to get started with projectM in a C/C++ application. If you need to use it in other
languages, the best approach would be writing a small C-to-your-language layer, which will be very similar to using it
directly in a C application. Since there are many different programming languages out there, we cannot cover them all
No operating system specific details like creating an OpenGL context are handled in this guide. libprojectM and its API
are generally platform-agnostic and require the application to provide the rendering environment.
## Adding libprojectM to your application
The recommended way to integrate projectM into your application is by building libprojectM as a static or shared library
and use the public C API (by including `projectM.h`) to instantiate and control it. This is the recommended way to use
projectM. While the exposed functionality is limited to the public API libprojectM provides, you can easily update to
recent releases without effort. If libprojectM is linked as a shared library, updating is as easy as replacing the
library file - you don't even need to recompile your application.
If you need more control over projectM's internals, for example if you're building an advanced preset editor for which
you require access to projectM's internal parser or rendering code, you can also directly integrate the source files
into your application. Be aware that this comes with a more involved process of updating to newer libprojectM releases.
Also be aware that either statically linking libprojectM or directly including the sources into your codebase _requires_
your application to be released under the GPL or LGPL licenses.
This integration guide will only cover integration via the official C API and linking the static or shared library.
### Build the library
First, [download and build libprojectM](Building-libprojectM) and install the build results in a location where you can
access them easily.
### Add the libraries and include dirs
#### Using CMake
The recommended way to use libprojectM is by using CMake to build your application. In your CMakeLists.txt, you need to
find the libprojectM package and then link the correct library target to your executable or library:
```cmake
# After the project() command.
# In the case projectM is optional in your build, leave out the REQUIRED.
# If you need a specific libprojectM version, you can specify it after the package name,
find_package(libprojectM REQUIRED)
add_executable(MyApp
main.cpp
)
# Replace libprojectM::shared with libprojectM::static to link against the static library instead
target_link_libraries(MyApp
PRIVATE
libprojectM::shared
)
```
That's all. If CMake finds libprojectM, it'll link the correct library and also add any required include dirs and
compiler flags to your application's build configuration. To have CMake find your copy of libprojectM, you might need to
add it via `CMAKE_PREFIX_PATH`:
```shell
cmake -S /path/to/source \
-B /path/to/build \
-GNinja \
-DCMAKE_PREFIX_PATH=/path/to/libprojectM
```
If libprojectM is installed in a standard path your toolchain is configured to look into, you won't need it.
#### Using a different build system
If you can't or don't want to use CMake, you need to gather the library and include dirs on your own and add them
manually to your project configuration.
On Linux, libprojectM will also create a `libprojectM.pc` file for use with pkgconfig, which will be the tool of choice
for autotools-based builds for example.
Other build systems might provide libprojectM packages in the future, but the projectM developers are not planning on
creating or maintinaing those.
### Call projectM in your code
#### One word about memory allocation
Before we start using the API, it is important to know how the API works in regard to memory allocation. The C API is
technically a C wrapper around C++ code, to while you call C functions, they internally execute as C++ code in the
library. In addition to that, the library, if loaded as a shared/dynamic library, might have a different heap area than
your host application on some platforms.
To make sure all allocated memory is properly being disposed of after use, it must be freed in the same context where it
has been allocated: if your application reserved memory, it has to release it. If libprojectM reserved memory, it must
be released in the library code.
Currently, this only applies to either strings or the settings structure. As a rule of thumb, here is a quick reference:
- If a pointer (`char*` or `projectm_settings*`) is returned by an API function, _always_ use the
appropriate `projectm_free_xxx()` function if you're done using the data.
- If your code passes any self-allocated pointer to the API, it is safe to free the allocated memory immediately after
the call. Do _not_ use the `projectm_free_xxx()` functions on these pointers.
- It is safe to pass temporary pointers to any API call, e.g. using `std::string::c_str()` in an argument.
- You can use `projectm_alloc_string` to allocate memory for strings, but you _must_ then use `projectm_free_string` to
free it after use.
- If you call `projectm_free_settings`, all non-null `char*`members _must_ have been allocated
using `projectm_alloc_string`.
- Any data pointers passed to callbacks are only valid until the callback returns. If you need the data afterwards,.make
a copy. Your code _must not_ call `free` or `delete` on the passed data pointers inside the callback.
#### Create a new projectM instance
Now that you have your project files configured properly, you can immediately start using projectM. As stated above,
this guide assumes your application already takes care of creating a proper OpenGL rendering context. This must be done
before calling any projectM functions.
First, include the API header and create a new projectM instance:
```c
#include <libprojectM/projectM.h>
/* In your setup code: */
/* Create a projectM instance with default settings */
projectm_handle projectMHandle = projectm_create("", PROJECTM_FLAG_NONE);
if (!projectMHandle)
{
/* Something went wrong. */
}
```
The opaque handle returned by `projectm_create()` identifies your instance and must be used as the first parameter to
all API calls that have an `instance` parameter.
It is safe to include the header from both C and C++ files. It wraps `extern "C"` around declarations automatically.
#### Make sure to clean up the instance
Now is a good time to make sure the newly created instance is deleted after your application is done using it:
```c
/* In your shutdown code: */
projectm_destroy(projectMHandle);
projectMHandle = NULL;
```
If you make sure to set `projectMHandle` to `NULL` after destroying it, any further calls to `projectm_destroy()` will
simply be a no-op: it is safe to pass a null pointer.
**Note**: It is _not_ safe to pass `NULL` or already-destroyed instance handles in `instance` to any other API call!
Make sure your code doesn't do that.
#### Set the canvas size
Once your rendering context is ready and the dimensions of the target surface are known, you must provide the size to
projectM once after initializing, and again every time the surface was invalidated or changed size, e.g. after a windows
was resized, minimized and restored:
```c
/* Initialize these with your current surface dimensions. */
size_t renderWidth = 800;
size_t renderHeight = 600;
projectm_set_window_size(projectMHandle, renderWidth, renderHeight);
```
For performance reasons, make sure to only call `projectm_set_window_size()` if really needed, as this will currently
cause a full recreation of projectM's internal rendering pipeline, including shader compilation.
#### Render a frame
projectM is now ready to start rendering frames. This needs to be done in your application's rendering loop:
```c
projectm_render_frame(projectMHandle);
/* Swap buffers here */
```
libprojectM does not have any FPS limiting capabilities. This means you can render frames as fast as projectM can draw
them. In end-user applications, this might not be a good thing as it will fully utilize one or more CPU cores while the
display possibly cannot display frames at the same speed. By enabling VSync for buffer swaps, it will automatically
limit FPS to the refresh rate. You might further consider FPS limiting to a certain target framerate to safe resources
on the user's device.
#### Supplying audio data
With the above setup, the application will only render the default idle preset (the wandering "M" logo), but not do
anything fancy. To make it react to some audio playing, the application must pass audio sample data into the library.
Where the application sources the audio data is up to the actual implementation, e.g. capturing external audio via some
system API, directly decoding an audio file or using data from an underlying player application.
The API currently supports a few different data formats. All functions start with `projectm_pcm_add_`, followed by the
sample data type, the number of channels and the data structure type accepted to pass in the actual data. For best
performance and visuals, it is recommended to always use the `projectm_pcm_add_float_2ch_data()` function. It requires
your data to be in this format:
- 32 bit floating-point samples
- 2 channels (LR)
- A simple data pointer, pointing to the first data byte
- The number of samples
The actual sample frequency is not part of the interface, but projectM is optimized for 44.1 kHz, same as Milkdrop. It
will also work with other sample frequencies, but the beat detection and presets drawing spectrum data might not behave
as expected. Using 48 kHz is fine though as the difference is minimal.
The number of samples is the count of a full complement of data for all channels. This means if sample_count is 1, then
the data must at least contain 16 _bytes_ or 2 _floats_ (General
formula: `numBytes = sizeof(sample data type) * channels`).
Ideally, if the application is gathering audio data in a separate thread than the renderer, it _should not_ pass audio
data only while `projectm_render_frame` is running. libprojectM does not use mutexes internally to prevent mutual access
to the audio buffer. If there are race conditions, it won't cause crashes though, but may negatively impact the rendered
visuals.
With all that said, let's say your application has a wrapper function that gets properly formatted audio data as a basic
byte (`unsigned char*`) buffer:
```c
/* audio_data is passed in float/2ch format */
void add_audio_to_projectm(const unsigned char* audio_data, unsigned int length)
{
projectm_pcm_add_float_2ch_data(projectMHandle, (const float*)audio_data, length / sozeof(float) / 2 /* channels */);
}
```
Now projectM should start reacting to the audio.
#### Audio data caveats
The application does not need to care about how much data is stored inside projectM's internal buffer. If more data is
added than projectM can consume on each frame, it is implemented as a ring buffer and will simply be overwritten. Each
frame only renders the last added audio samples available in the buffer. This in turn means that if audio data is added
only sporadically in large batches, multiple frames will use the same audio data for rendering, effectively "freezing"
waveforms on screen.
Depending on the frequency and amount of audio data the application gets on each update (e.g. in callbacks from an
external audio driver), it might be necessary to spread out adding the data over multiple frames instead of adding it
all at once directly in the callback. Knowing the sample frequency and actual frame rendering time, the application can
calculate the number of new audio samples to pass to projectM before each frame. If audio data comes in faster than
frames are rendered, only the last few samples of the available audio data need to be passed. The application can query
projectM's internal sample buffer size with the `projectm_pcm_get_max_samples()` function for optimization.
In addition to all that, the application should make sure not to get too much behind the actually played audio, as it
might introduce visible lag between what the user hears and what projectM renders. As a rule of thumb, latencies over
35ms will potentially be noticeable by the user.
### What next?
Now that the application is able to render visualizations with projectM, it should also take care of configuring
additional features like adding presets, properly setting the different options projectM supports and handling user
input as required.
A good further reading is the [API reference](API-Reference) and looking at the available members in
the `projectm_settings` structure.
projectM also supports scanning a single directory recursively for presets, so your application doesn't need to do that.
For proper playlist management, rating support and faster loading times it is recommended to fill the preset playlist
using `projectm_add_preset_url()`. Future API extensions may add additional functionality for bulk playlist management.

@ -0,0 +1,95 @@
# Build Troubleshooting Q&A
On this page, you can find a list of common build issues you may encounter while building libprojectM and the frontends,
providing possible solutions.
If your problem isn't listed, and you can't find a solution on your own, please note that the projectM developers can
not provide technical support. If you think your build issue is caused by the projectM code or build system files (or
you are unsure if that is the case), please open a bug report in the respective repository. You can also ask for help in
the bug report channel or the [projectM Discord server](https://discord.gg/tpEuywB) and see if someone can help.
## CMake issues
### General advice
Things to check and try first:
- Always start with a fresh build directory if there is any issue, especially after checking out new code, switching
branches or updating your local copy after a while.
- If you don't want to delete your while build dir, at least try and delete the `CMakeCache.txt` in the top-level build
dir and re-run CMake's configuration phase.
- Make sure your C/C++ toolset and IDE is up-to-date and all components are properly set up.
- Use the latest stable CMake release. If only a very old version is available on your platform or shipped with your
IDE, building your own CMake binaries is really easy. There also are binary
releases [available on cmake.org](https://cmake.org/download/) for many platforms.
- Make sure the `cmake`/`cpack`/`ctest` commands are in your `PATH` variable.
### CMake can't find a working compiler
Depending on your OS and toolset, you might have to run CMake in a specific environment which defines paths for the
compiler and other tools:
- On Windows using Visual Studio, either run CMake from within VS (version 2019 or higher is recommended) or from the
developer command prompt for your target architecture which you can find in the Windows start menu.
- On macOS using Xcode, make sure you have the command line tools installed. If in doubt, run `xcode-select --install`
ina terminal with the user account you want to run CMake in. Run `xcode-select -p` to verify the tools are properly
installed. When using CMake to create Xcode projects, also make sure to use the latest CMake version.
- On Linux, make sure you have installed all required packages for compiling C/C++ code. Most distributions have meta
packages called "build-essential" or similar that will pull in compiler, linker, make and other build tools. Since
Linux is highly heterogeneous, refer to your distribution documentation on installing developer packages.
- If the above hints don't work, double-check your developer tools installation and
the [CMake generator](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html) used to create your project
files. The default generator might not be the correct one for your setup.
### CMake can't find a specific dependency
The projectM CMake scripts do not make any assumptions on where to look for build dependencies and solely rely on
CMake's built-in mechanisms to find the needed packages. The lookup process is mostly platform-independent and generally
requires packages and other files to be found in a specific set of directories:
- The [`CMAKE_PREFIX_PATH` variable](https://cmake.org/cmake/help/latest/variable/CMAKE_PREFIX_PATH.html) can be used to
add one or more directories to the build configuration in which CMake will first look into when searching for packages
and libraries. Add any paths separated by `;` for libraries that are not installed in the toolset/OS default search
paths.
- When using [vcpkg](https://vcpkg.io/) to build the dependencies on any platform, always
set [`CMAKE_TOOLCHAIN_FILE`](https://cmake.org/cmake/help/latest/variable/CMAKE_TOOLCHAIN_FILE.html)
to `<vcpkg-dir>/scripts/buildsystems/vcpkg.cmake`.
- In some cases, even if the dependency normally provides CMake files, pre-built packages might not contain them. In
this case, you need to either contact the author of the pre-built package or build the dependency yourself and add the
installation dir to `CMAKE_PREFIX_PATH`.
## Compilation issues
### General advice
Things to check and try first:
- Make sure you use the latest available compiler and SDK versions for your platform.
- Use a compiler that supports at least C++14.
- If you build a recent version of projectM, also try to use the most recent versions of the required dependencies, e.g.
SDL2.
- Make sure all libraries were built with the same compiler/SDK, for the same architecture and runtime libraries (e.g.
MSVC `/MD` and `/MT` flags).
- In newly written code, always use case-sensitive filenames in `#include` directives and other project files. Keep in
mind that most OS platforms have case-sensitive filesystems.
### There is some compiler error I don't understand
If you're only trying to build projectM, but are not a C/C++ developer, errors during compilation might not provide you
with enough information to identify the reason or fix the code. In this case, you can open an issue or ask on Discord to
get a possible solution. In some cases the error you're encountering might be a bug that needs to be solved in the
source, so it's important that you let us know about it. Please make sure you provide all relevant information, e.g. OS,
compiler and library versions etc. so we can reproduce the problem.
### An include file could not be found
- Make sure the filename casing in the `#include` directive is correct.
- Look at the compiler command line if all required include directories are present. If you can't see the compiler
commands, add `-DCMAKE_VERBOSE_MAKEFILE=ON` to CMake's configuration command to enable command output on all
generators.
- A third-party library might not be installed correctly or with an unexpected directory layout.
- If the missing include is a system or SDK header, check if your toolset is configured properly.
- Make sure the `#include` directive uses the correct quotes, e.g. `""` for project-internal headers and `<>` for
third-party or SDK headers. Modern compilers differentiate between those.
- Make sure include directories are provided via the correct compiler flag, e.g. `-i` for project-internal include paths
used with `#include "header.h"`and `-I` for third-party and SDK/system headers for use with `#include <header.h>`.