diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml index 9c0a192..6c160bd 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_linux.yml @@ -25,7 +25,7 @@ jobs: - name: Get ProjectM Git Hash id: git-hash - run: echo "hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + run: echo "hash=$(git rev-parse HEAD)v2" >> $GITHUB_OUTPUT - name: Cache Install id: cache-install @@ -42,7 +42,7 @@ jobs: if: steps.cache-install.outputs.cache-hit != 'true' - name: Configure Build - run: cmake -G "Ninja" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_SHARED_LIBS=ON + run: cmake -G "Ninja" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_SHARED_LIBS=ON -DENABLE_PLAYLIST=ON if: steps.cache-install.outputs.cache-hit != 'true' - name: Build Release @@ -88,6 +88,8 @@ jobs: - name: Configure Build run: cmake -G "Ninja" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_VERBOSE_MAKEFILE=YES -DprojectM4_DIR="artifacts/lib/cmake/projectM4" + env: + projectM4Playlist_DIR: "${{ github.workspace }}/artifacts/lib/cmake/projectM4Playlist" - name: Build Release id: build diff --git a/.github/workflows/build_osx.yml b/.github/workflows/build_osx.yml index 405f0a1..f657fc0 100644 --- a/.github/workflows/build_osx.yml +++ b/.github/workflows/build_osx.yml @@ -43,7 +43,7 @@ jobs: if: steps.cache-install.outputs.cache-hit != 'true' - name: Configure Build - run: cmake -G "Ninja" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_SHARED_LIBS=ON + run: cmake -G "Ninja" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_SHARED_LIBS=ON -DENABLE_PLAYLIST=ON if: steps.cache-install.outputs.cache-hit != 'true' - name: Build Release @@ -89,6 +89,8 @@ jobs: - name: Configure Build run: cmake -G "Ninja" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_VERBOSE_MAKEFILE=YES -DprojectM4_DIR="artifacts/lib/cmake/projectM4" + env: + projectM4Playlist_DIR: "${{ github.workspace }}/artifacts/lib/cmake/projectM4Playlist" - name: Build Release id: build diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index 885c230..503f428 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -34,7 +34,7 @@ jobs: - name: Get ProjectM Git Hash id: git-hash - run: echo "hash=$(git rev-parse HEAD)" >> $Env:GITHUB_OUTPUT + run: echo "hash=$(git rev-parse HEAD)v2" >> $Env:GITHUB_OUTPUT - name: Cache Install id: cache-install @@ -45,7 +45,7 @@ jobs: key: install-${{ runner.os }}-projectm-${{ steps.git-hash.outputs.hash }} - name: Configure Build - run: cmake -G "Visual Studio 17 2022" -A "X64" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_TOOLCHAIN_FILE="${Env:VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$:Debug>DLL" -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_SHARED_LIBS=ON + run: cmake -G "Visual Studio 17 2022" -A "X64" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_TOOLCHAIN_FILE="${Env:VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install" -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$:Debug>DLL" -DCMAKE_VERBOSE_MAKEFILE=YES -DBUILD_SHARED_LIBS=ON -DENABLE_PLAYLIST=ON if: steps.cache-install.outputs.cache-hit != 'true' - name: Build Release @@ -97,6 +97,8 @@ jobs: - name: Configure Build run: cmake -G "Visual Studio 17 2022" -A "X64" -S "${{ github.workspace }}" -B "${{ github.workspace }}/cmake-build" -DCMAKE_TOOLCHAIN_FILE="${Env:VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$:Debug>" -DCMAKE_VERBOSE_MAKEFILE=YES -DprojectM4_DIR="artifacts/lib/cmake/projectM4" + env: + projectM4Playlist_DIR: "${{ github.workspace }}/artifacts/lib/cmake/projectM4Playlist" - name: Build Release id: build diff --git a/.gitignore b/.gitignore index 6ec7632..7f299b1 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ build dist docs/TODO.md -test/output \ No newline at end of file +test/output +cmake-build-* +out/ diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..5fbd39d --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +gstprojectm \ No newline at end of file diff --git a/.idea/editor.xml b/.idea/editor.xml new file mode 100644 index 0000000..855412d --- /dev/null +++ b/.idea/editor.xml @@ -0,0 +1,103 @@ + + + + + \ No newline at end of file diff --git a/.idea/gst-projectm.iml b/.idea/gst-projectm.iml new file mode 100644 index 0000000..f08604b --- /dev/null +++ b/.idea/gst-projectm.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..0b76fe5 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..88facca --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/gstprojectm.xml b/.idea/runConfigurations/gstprojectm.xml new file mode 100644 index 0000000..84d73fa --- /dev/null +++ b/.idea/runConfigurations/gstprojectm.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e1bb4c..e41f87f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,8 @@ project(gstprojectm VERSION 0.0.1) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") -find_package(projectM4 4.1.0 REQUIRED) +find_package(projectM4 4.1.0 REQUIRED Playlist) + find_package(GStreamer REQUIRED COMPONENTS gstreamer-audio gstreamer-gl gstreamer-pbutils gstreamer-video) find_package(GLIB2 REQUIRED) @@ -61,6 +62,7 @@ endif() target_link_libraries(gstprojectm PRIVATE libprojectM::projectM + libprojectM::playlist PUBLIC ${GSTREAMER_LIBRARIES} ${GSTREAMER_BASE_LIBRARIES} diff --git a/README.md b/README.md index b63aafe..41a62b2 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@
+ ## Getting Started The documentation has been organized into distinct files, each dedicated to a specific platform. Within each file, you'll find detailed instructions covering the setup of prerequisites, the building process, installation steps, and guidance on utilizing the plugin on the respective platform. @@ -47,9 +48,35 @@ The documentation has been organized into distinct files, each dedicated to a sp - **[OSX](docs/OSX.md)** - **[Windows](docs/WINDOWS.md)** +Once the plugin has been installed, you can use it something like this: + +```shell +gst-launch pipewiresrc ! queue ! audioconvert ! projectm preset=/usr/local/share/projectM/presets preset-duration=5 ! video/x-raw,width=2048,height=1440,framerate=60/1 ! videoconvert ! xvimagesink sync=false +``` + +Or to convert an audio file to video: + +```shell +filesrc location=input.mp3 ! decodebin name=dec \ + decodebin ! tee name=t \ + t. ! queue ! audioconvert ! audioresample ! \ + capsfilter caps="audio/x-raw, format=F32LE, channels=2, rate=44100" ! avenc_aac bitrate=256000 ! queue ! mux. \ + t. ! queue ! audioconvert ! projectm preset=/usr/local/share/projectM/presets preset-duration=3 mesh-size=1024,576 ! \ + identity sync=false ! videoconvert ! videorate ! video/x-raw,framerate=60/1,width=3840,height=2160 ! \ + x264enc bitrate=35000 key-int-max=300 speed-preset=veryslow ! video/x-h264,stream-format=avc,alignment=au ! queue ! mux. \ + mp4mux name=mux ! filesink location=render.mp4; +``` + +Available options + +```shell +gst-inspect projectm +``` +

(back to top)

+ ## Contributing Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. @@ -66,6 +93,7 @@ Don't forget to give the project a star! Thanks again!

(back to top)

+ ## License Distributed under the LGPL-2.1 license. See `LICENSE` for more information. @@ -73,6 +101,7 @@ Distributed under the LGPL-2.1 license. See `LICENSE` for more information.

(back to top)

+ ## Support [![Discord][discord-shield]][discord-url] @@ -80,6 +109,7 @@ Distributed under the LGPL-2.1 license. See `LICENSE` for more information.

(back to top)

+ ## Contact Blaquewithaq (Discord: SoFloppy#1289) - [@anomievision](https://twitter.com/anomievision) - anomievision@gmail.com @@ -89,6 +119,7 @@ Blaquewithaq (Discord: SoFloppy#1289) - [@anomievision](https://twitter.com/anom + [contributors-shield]: https://img.shields.io/github/contributors/projectM-visualizer/gst-projectm.svg?style=for-the-badge [contributors-url]: https://github.com/projectM-visualizer/gst-projectm/graphs/contributors [forks-shield]: https://img.shields.io/github/forks/projectM-visualizer/gst-projectm.svg?style=for-the-badge @@ -104,4 +135,4 @@ Blaquewithaq (Discord: SoFloppy#1289) - [@anomievision](https://twitter.com/anom [crates-dl-shield]: https://img.shields.io/crates/d/gst-projectm?style=for-the-badge [crates-dl-url]: https://crates.io/crates/gst-projectm [discord-shield]: https://img.shields.io/discord/737206408482914387?style=for-the-badge -[discord-url]: https://discord.gg/7fQXN43n9W \ No newline at end of file +[discord-url]: https://discord.gg/7fQXN43n9W diff --git a/src/config.h b/src/config.h index 6d736d7..bc83e3b 100644 --- a/src/config.h +++ b/src/config.h @@ -11,7 +11,7 @@ G_BEGIN_DECLS #define PACKAGE "GstProjectM" #define PACKAGE_NAME "GstProjectM" -#define PACKAGE_VERSION "0.0.1" +#define PACKAGE_VERSION "0.0.2" #define PACKAGE_LICENSE "LGPL" #define PACKAGE_ORIGIN "https://github.com/projectM-visualizer/gst-projectm" @@ -30,8 +30,10 @@ G_BEGIN_DECLS #define DEFAULT_MESH_SIZE "48,32" #define DEFAULT_ASPECT_CORRECTION TRUE #define DEFAULT_EASTER_EGG 0.0 -#define DEFAULT_PRESET_LOCKED TRUE +#define DEFAULT_PRESET_LOCKED FALSE +#define DEFAULT_ENABLE_PLAYLIST TRUE +#define DEFAULT_SHUFFLE_PRESETS TRUE // depends on ENABLE_PLAYLIST G_END_DECLS -#endif /* __GST_PROJECTM_CONFIG_H__ */ \ No newline at end of file +#endif /* __GST_PROJECTM_CONFIG_H__ */ diff --git a/src/enums.h b/src/enums.h index 0d53bcc..863d677 100644 --- a/src/enums.h +++ b/src/enums.h @@ -23,8 +23,10 @@ enum { PROP_ASPECT_CORRECTION, PROP_EASTER_EGG, PROP_PRESET_LOCKED, + PROP_SHUFFLE_PRESETS, + PROP_ENABLE_PLAYLIST }; G_END_DECLS -#endif /* __GST_PROJECTM_ENUMS_H__ */ \ No newline at end of file +#endif /* __GST_PROJECTM_ENUMS_H__ */ diff --git a/src/plugin.c b/src/plugin.c index fc33776..125b2ed 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -95,6 +95,12 @@ void gst_projectm_set_property(GObject *object, guint property_id, case PROP_PRESET_LOCKED: plugin->preset_locked = g_value_get_boolean(value); break; + case PROP_ENABLE_PLAYLIST: + plugin->enable_playlist = g_value_get_boolean(value); + break; + case PROP_SHUFFLE_PRESETS: + plugin->shuffle_presets = g_value_get_boolean(value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; @@ -149,6 +155,12 @@ void gst_projectm_get_property(GObject *object, guint property_id, case PROP_PRESET_LOCKED: g_value_set_boolean(value, plugin->preset_locked); break; + case PROP_ENABLE_PLAYLIST: + g_value_set_boolean(value, plugin->enable_playlist); + break; + case PROP_SHUFFLE_PRESETS: + g_value_set_boolean(value, plugin->shuffle_presets); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; @@ -167,6 +179,8 @@ static void gst_projectm_init(GstProjectM *plugin) { plugin->hard_cut_sensitivity = DEFAULT_HARD_CUT_SENSITIVITY; plugin->soft_cut_duration = DEFAULT_SOFT_CUT_DURATION; plugin->preset_duration = DEFAULT_PRESET_DURATION; + plugin->enable_playlist = DEFAULT_ENABLE_PLAYLIST; + plugin->shuffle_presets = DEFAULT_SHUFFLE_PRESETS; const gchar *meshSizeStr = DEFAULT_MESH_SIZE; gint width, height; @@ -472,7 +486,7 @@ static void gst_projectm_class_init(GstProjectMClass *klass) { gobject_class, PROP_EASTER_EGG, g_param_spec_float( "easter-egg", "Easter Egg", - "DControls the activation of an Easter Egg feature. The value " + "Controls the activation of an Easter Egg feature. The value " "determines the likelihood of triggering the Easter Egg.", 0.0, 1.0, DEFAULT_EASTER_EGG, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); @@ -485,6 +499,23 @@ static void gst_projectm_class_init(GstProjectMClass *klass) { "remains on the current preset without automatic changes.", DEFAULT_PRESET_LOCKED, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property( + gobject_class, PROP_ENABLE_PLAYLIST, + g_param_spec_boolean( + "enable-playlist", "Enable Playlist", + "Enables or disables the playlist feature. When enabled, the " + "visualizer can switch between presets based on a provided playlist.", + DEFAULT_ENABLE_PLAYLIST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property( + gobject_class, PROP_SHUFFLE_PRESETS, + g_param_spec_boolean( + "shuffle-presets", "Shuffle Presets", + "Enables or disables preset shuffling. When enabled, the visualizer " + "randomly selects presets from the playlist if presets are provided " + "and not locked. Playlist must be enabled for this to take effect.", + DEFAULT_SHUFFLE_PRESETS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gobject_class->finalize = gst_projectm_finalize; scope_class->supported_gl_api = GST_GL_API_OPENGL3 | GST_GL_API_GLES2; diff --git a/src/plugin.h b/src/plugin.h index 43b3389..de1acff 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -29,6 +29,8 @@ struct _GstProjectM { gboolean aspect_correction; gfloat easter_egg; gboolean preset_locked; + gboolean enable_playlist; + gboolean shuffle_presets; GstProjectMPrivate *priv; }; @@ -62,4 +64,4 @@ static gboolean gst_projectm_setup(GstGLBaseAudioVisualizer *glav); G_END_DECLS -#endif /* __GST_PROJECTM_H__ */ \ No newline at end of file +#endif /* __GST_PROJECTM_H__ */ diff --git a/src/projectm.c b/src/projectm.c index 5b9b6a1..1bac137 100644 --- a/src/projectm.c +++ b/src/projectm.c @@ -4,6 +4,7 @@ #include +#include #include #include "plugin.h" @@ -14,6 +15,8 @@ GST_DEBUG_CATEGORY_STATIC(projectm_debug); projectm_handle projectm_init(GstProjectM *plugin) { projectm_handle handle = NULL; + projectm_playlist_handle playlist = NULL; + GST_DEBUG_CATEGORY_INIT(projectm_debug, "projectm", 0, "ProjectM"); GstAudioVisualizer *bscope = GST_AUDIO_VISUALIZER(plugin); @@ -31,6 +34,18 @@ projectm_handle projectm_init(GstProjectM *plugin) { GST_DEBUG_OBJECT(plugin, "Created projectM instance!"); } + if (plugin->enable_playlist) { + GST_DEBUG_OBJECT(plugin, "Playlist enabled"); + + // initialize preset playlist + playlist = projectm_playlist_create(handle); + projectm_playlist_set_shuffle(playlist, plugin->shuffle_presets); + // projectm_playlist_set_preset_switched_event_callback(_playlist, + // &ProjectMWrapper::PresetSwitchedEvent, static_cast(this)); + } else { + GST_DEBUG_OBJECT(plugin, "Playlist disabled"); + } + // Log properties GST_INFO_OBJECT( plugin, @@ -46,16 +61,23 @@ projectm_handle projectm_init(GstProjectM *plugin) { "mesh-size=(%lu, %lu)" "aspect-correction=%d, " "easter-egg=%f, " - "preset-locked=%d, ", + "preset-locked=%d, " + "enable-playlist=%d, " + "shuffle-presets=%d", plugin->preset_path, plugin->texture_dir_path, plugin->beat_sensitivity, plugin->hard_cut_duration, plugin->hard_cut_enabled, plugin->hard_cut_sensitivity, plugin->soft_cut_duration, plugin->preset_duration, plugin->mesh_width, plugin->mesh_height, - plugin->aspect_correction, plugin->easter_egg, plugin->preset_locked); + plugin->aspect_correction, plugin->easter_egg, plugin->preset_locked, + plugin->enable_playlist, plugin->shuffle_presets); // Load preset file if path is provided - if (plugin->preset_path != NULL) - projectm_load_preset_file(handle, plugin->preset_path, false); + if (plugin->preset_path != NULL) { + int added_count = + projectm_playlist_add_path(playlist, plugin->preset_path, true, false); + GST_INFO("Loaded preset path: %s, presets found: %d", plugin->preset_path, + added_count); + } // Set texture search path if directory path is provided if (plugin->texture_dir_path != NULL) { @@ -73,6 +95,11 @@ projectm_handle projectm_init(GstProjectM *plugin) { // Set preset duration, or set to in infinite duration if zero if (plugin->preset_duration > 0.0) { projectm_set_preset_duration(handle, plugin->preset_duration); + + // kick off the first preset + if (projectm_playlist_size(playlist) > 1 && !plugin->preset_locked) { + projectm_playlist_play_next(playlist, true); + } } else { projectm_set_preset_duration(handle, 999999.0); } @@ -97,4 +124,4 @@ projectm_handle projectm_init(GstProjectM *plugin) { // PROJECTM_STEREO); // projectm_opengl_render_frame(plugin->handle); -// } \ No newline at end of file +// }