From e2d265f0387e06856e2985ae12f327fac25112b7 Mon Sep 17 00:00:00 2001 From: theIDinside Date: Sat, 23 Oct 2021 21:22:49 +0200 Subject: [PATCH] Issue#130 (#546) * Fixes issue projectM-visualizer/projectm#130 Adds functionality of writing a rendered frame to file as requested by the issue https://github.com/projectM-visualizer/projectm/issues/130. Keyhandler.cpp has been updated to handle the keydown of 'd', which triggers writeNextFrameToFile to be set to true, which means at the end of Renderer::FinishPass1 we write the texture contents to file. Renderer::debugWriteMainTextureToFile() const contains the functionality of writing the contents of the main texture. * Addition to commit that fixes projectM-visualizer/projectm#130 Safe file write, making sure we don't overflow while creating the filename. If c++20 features was enabled, this could look so, so much better. * Added keybinding to README doc * Formatting Fixed snake_case to camelCase Changed delete to delete[] Turned magic constant to a named variable --- README.md | 131 +++++++++++++++----------- src/libprojectM/KeyHandler.cpp | 3 + src/libprojectM/Renderer/Renderer.cpp | 43 +++++++++ src/libprojectM/Renderer/Renderer.hpp | 12 ++- 4 files changed, 130 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 12101e4e6..ceeb4b95a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ [![Build Status](https://travis-ci.org/projectM-visualizer/projectm.svg?branch=master)](https://travis-ci.org/projectM-visualizer/projectm) - ![Logo](https://github.com/projectM-visualizer/projectm/raw/master/web/logo.png) ## projectM - The most advanced open-source music visualizer @@ -12,54 +11,67 @@ projectM is an open-source project that reimplements the esteemed [Winamp Milkdr Its purpose in life is to read an audio input and to produce mesmerizing visuals, detecting tempo, and rendering advanced equations into a limitless array of user-contributed visualizations. ### Available For + ##### Windows -* [Standalone](https://github.com/projectM-visualizer/projectm/releases) ([latest build](https://ci.appveyor.com/project/revmischa/projectm/build/artifacts)) - (Requires the latest [Visual C++ redistributable](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads)) -* [Steam](https://store.steampowered.com/app/1358800/projectM_Music_Visualizer/) -* [Windows Store](https://www.microsoft.com/store/apps/9NDCVH0VCWJN) + +- [Standalone](https://github.com/projectM-visualizer/projectm/releases) ([latest build](https://ci.appveyor.com/project/revmischa/projectm/build/artifacts)) - (Requires the latest [Visual C++ redistributable](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads)) +- [Steam](https://store.steampowered.com/app/1358800/projectM_Music_Visualizer/) +- [Windows Store](https://www.microsoft.com/store/apps/9NDCVH0VCWJN) + #### macOS -* [Standalone](https://github.com/projectM-visualizer/projectm/releases) -* [Steam](https://store.steampowered.com/app/1358800/projectM_Music_Visualizer/) -* [Music.app Plugin](https://github.com/projectM-visualizer/projectm/releases/) -* [Brew](https://formulae.brew.sh/formula/projectm) + +- [Standalone](https://github.com/projectM-visualizer/projectm/releases) +- [Steam](https://store.steampowered.com/app/1358800/projectM_Music_Visualizer/) +- [Music.app Plugin](https://github.com/projectM-visualizer/projectm/releases/) +- [Brew](https://formulae.brew.sh/formula/projectm) + #### Linux -* [Steam](https://store.steampowered.com/app/1358800/projectM_Music_Visualizer/) -* Check your repository for a binary release. + +- [Steam](https://store.steampowered.com/app/1358800/projectM_Music_Visualizer/) +- Check your repository for a binary release. + #### Android -* [Google Play](https://play.google.com/store/apps/details?id=com.psperl.prjM) + +- [Google Play](https://play.google.com/store/apps/details?id=com.psperl.prjM) + #### Xbox / Windows Phone -* [Windows Store](https://www.microsoft.com/store/apps/9NDCVH0VCWJN) + +- [Windows Store](https://www.microsoft.com/store/apps/9NDCVH0VCWJN) + #### Other -* [Source code](https://github.com/projectM-visualizer/projectm/) -* [Qt5](https://www.qt.io/)-based [PulseAudio](https://www.freedesktop.org/wiki/Software/PulseAudio/) and JACK desktop apps in [source code](https://github.com/projectM-visualizer/projectm/) -* [ALSA, XMMS, Winamp, JACK](https://sourceforge.net/projects/projectm/files/) (source, unmaintained) + +- [Source code](https://github.com/projectM-visualizer/projectm/) +- [Qt5](https://www.qt.io/)-based [PulseAudio](https://www.freedesktop.org/wiki/Software/PulseAudio/) and JACK desktop apps in [source code](https://github.com/projectM-visualizer/projectm/) +- [ALSA, XMMS, Winamp, JACK](https://sourceforge.net/projects/projectm/files/) (source, unmaintained) ### Discord chat + [Chat with us on Discord.](https://discord.gg/tpEuywB) ### Demo Video + [![](http://img.youtube.com/vi/2dSam8zwSFw/0.jpg)](http://www.youtube.com/watch?v=2dSam8zwSFw "Demo") - ### Presets + The preset files define the visualizations via pixel shaders and Milkdrop-style equations and parameters. Included with projectM are the bltc201, Milkdrop 1 and 2, projectM, tryptonaut and yin collections. You can grab these presets [here](http://spiegelmc.com/pub/projectm_presets.zip). You can also download an enormous 41,000 preset pack of presets [here](https://mischa.lol/projectM/presets_community.zip) (123MB zipped). - ### Also Featured In + [![Kodi](https://github.com/projectM-visualizer/projectm/raw/master/web/kodi.png) Kodi (formerly XBMC)](https://kodi.tv/) [![Helix](https://github.com/projectM-visualizer/projectm/raw/master/web/helix.jpg) Helix](http://ghostfiregames.com/helixhome.html) - [![Silverjuke](https://github.com/projectM-visualizer/projectm/raw/master/web/silverjuke.png) Silverjuke (FOSS Jukebox)](https://www.silverjuke.net) [ Silent Radiance Distance Disco](https://silentradiance.com) - -*** +--- ## Screenshots + ![Screenshot](https://github.com/projectM-visualizer/projectm/raw/master/src/projectM-MusicPlugin/projectM%20screenshots/Screen%20Shot%202014-08-25%20at%2012.31.20%20AM.png) ![Screenshot](https://github.com/projectM-visualizer/projectm/raw/master/src/projectM-MusicPlugin/projectM%20screenshots/Screen%20Shot%202014-08-25%20at%2012.33.50%20AM.png) @@ -77,58 +89,66 @@ You can also download an enormous 41,000 preset pack of presets [here](https://m ![Screenshot](https://github.com/projectM-visualizer/projectm/raw/master/src/projectM-MusicPlugin/projectM%20screenshots/Screen%20Shot%202014-08-25%20at%2012.31.07%20AM.png) ![Screenshot](https://silentradiance.com/demos/projectM_vr/projectm_vr.png) -*** + +--- ## Architecture -* [Article](https://lwn.net/Articles/750152/) + +- [Article](https://lwn.net/Articles/750152/) # Building from source + See [BUILDING.md](BUILDING.md) # Keyboard Controls: -* Up: increase beat sensitivity (max 5) -* Down: decrease beat sensitivity (min 0) -* Y: toggle shuffle enabled -* R: jump to random preset -* N or P: next or previous preset (hard transition) -* Shift-N or Shift-P: next or previous preset (soft transition) -* L: lock current preset -* H or F1: show help (if supported) -* M: Open preset navigation menu (if supported) -* F3: show preset (if supported) -* F4: show stats (if supported) -* F5: show FPS (if supported) +- Up: increase beat sensitivity (max 5) +- Down: decrease beat sensitivity (min 0) +- Y: toggle shuffle enabled +- R: jump to random preset +- N or P: next or previous preset (hard transition) +- Shift-N or Shift-P: next or previous preset (soft transition) +- L: lock current preset +- D: Save current frame to file + +- H or F1: show help (if supported) +- M: Open preset navigation menu (if supported) +- F3: show preset (if supported) +- F4: show stats (if supported) +- F5: show FPS (if supported) #### Only ProjectM SDL: -* Cmd/Ctrl-Q: *q*uit -* Cmd/Ctrl-I: select next audio *i*nput device -* Cmd/Ctrl-S: *s*tretch monitors -* Cmd/Ctrl-M: change *m*onitor -* Cmd/Ctrl-F: toggle *f*ull screen -* Mouse Scroll Up / Down: next or previous preset (hard transition) -* Return: search for preset (RETURN or ESCAPE to exit search) -* Space: lock current preset +- Cmd/Ctrl-Q: *q*uit +- Cmd/Ctrl-I: select next audio *i*nput device +- Cmd/Ctrl-S: *s*tretch monitors +- Cmd/Ctrl-M: change *m*onitor +- Cmd/Ctrl-F: toggle *f*ull screen +- Mouse Scroll Up / Down: next or previous preset (hard transition) +- Return: search for preset (RETURN or ESCAPE to exit search) +- Space: lock current preset # Using the library + At its core projectM is a library, [libprojectM](src/libprojectM). This library is responsible for parsing presets, analyzing audio PCM data with beat detection and FFT, applying the preset to the audio feature data and rendering the resulting output with openGL. It can render to an OpenGL context or a texture. To look at a simple example way of using the library see the [libSDL2 sample code](src/projectM-sdl/projectM_SDL_main.cpp). There are many other applications that make use of libprojectM and that can be found in the [src](src/) directory. -*** +--- # Todo -* Steal cool stuff from the recently-released Milkdrop source. -* Finish [emscripten support](https://github.com/projectM-visualizer/projectm/pull/307) for building to wasm/webGL for the web. -* Update the [various implementations using libprojectM](src). -* Update downstream projects with new versions. -*** +- Steal cool stuff from the recently-released Milkdrop source. +- Finish [emscripten support](https://github.com/projectM-visualizer/projectm/pull/307) for building to wasm/webGL for the web. +- Update the [various implementations using libprojectM](src). +- Update downstream projects with new versions. + +--- ## Help + Report issues on [GitHub](https://github.com/projectM-visualizer/projectm/issues/new) [Chat with us on Discord.](https://discord.gg/tpEuywB). @@ -136,16 +156,19 @@ Report issues on [GitHub](https://github.com/projectM-visualizer/projectm/issues If you would like to help improve this project, either with documentation, code, porting, hardware or anything else please let us know! We gladly accept pull requests and issues. ## Maintainers + If you maintain packages of libprojectM, we are happy to work with you! Please note well: -* The main focus of this project is libprojectM. It's a library that only really depends on OpenGL. The other applications are more like examples and demos. -* Most of the applications (e.g. `src/projectM-*`) are likely outdated and of less utility than the core library. If you desire to use them or depend on them, please file an issue so we can help update them. -* The "canonical" application for actually viewing the visualizations is now projectM-sdl, based on libSDL2 because it supports audio input and is completely cross-platform. -* This is an open source project! If you don't like something, feel free to contribute improvements! -* Yes, you are looking at the official version. This is not a fork. + +- The main focus of this project is libprojectM. It's a library that only really depends on OpenGL. The other applications are more like examples and demos. +- Most of the applications (e.g. `src/projectM-*`) are likely outdated and of less utility than the core library. If you desire to use them or depend on them, please file an issue so we can help update them. +- The "canonical" application for actually viewing the visualizations is now projectM-sdl, based on libSDL2 because it supports audio input and is completely cross-platform. +- This is an open source project! If you don't like something, feel free to contribute improvements! +- Yes, you are looking at the official version. This is not a fork. ## Authors + [Authors](https://github.com/projectM-visualizer/projectm/raw/master/AUTHORS.txt) - ## License + [LGPL](https://github.com/projectM-visualizer/projectm/raw/master/LICENSE.txt) diff --git a/src/libprojectM/KeyHandler.cpp b/src/libprojectM/KeyHandler.cpp index ad03135b3..37823afb0 100755 --- a/src/libprojectM/KeyHandler.cpp +++ b/src/libprojectM/KeyHandler.cpp @@ -311,6 +311,9 @@ void projectM::default_key_handler( projectMEvent event, projectMKeycode keycode renderer->studio = !renderer->studio; case PROJECTM_K_i: break; + case PROJECTM_K_d: // d stands for write DEBUG output. + renderer->writeNextFrameToFile = true; + break; case PROJECTM_K_RETURN: renderer->toggleSearchText(); if (renderer->showsearch) { diff --git a/src/libprojectM/Renderer/Renderer.cpp b/src/libprojectM/Renderer/Renderer.cpp index a0c72a644..8bb623db5 100644 --- a/src/libprojectM/Renderer/Renderer.cpp +++ b/src/libprojectM/Renderer/Renderer.cpp @@ -7,6 +7,9 @@ #include "MilkdropWaveform.hpp" #include #include +#include +#include +#include #include #include #include "omptl/omptl" @@ -16,6 +19,7 @@ #include #include #include +#include "stb_image_write.h" using namespace std::chrono; @@ -411,6 +415,11 @@ void Renderer::FinishPass1() draw_title_to_texture(); textureManager->updateMainTexture(); + if(writeNextFrameToFile) { + debugWriteMainTextureToFile(); + writeNextFrameToFile = false; + } + } void Renderer::Pass2(const Pipeline& pipeline, const PipelineContext& pipelineContext) @@ -839,6 +848,40 @@ void Renderer::deleteSearchText() } } + + +void Renderer::debugWriteMainTextureToFile() const { + GLuint fbo; + auto mainTexture = textureManager->getMainTexture(); + + const auto safeWriteFile = [](const auto frameNumber, auto data, const auto width, const auto height) { + static const std::string prefix{"frame_texture_contents-"}; + const auto prefixLen = prefix.size(); + constexpr auto fileNameMaxLength = 150; + constexpr auto fileExtensionLength = 4; + char fileNameBuffer[fileNameMaxLength]; + std::memcpy(fileNameBuffer, prefix.data(), prefixLen); + auto t = std::time(nullptr); + auto tm = *std::localtime(&t); + const auto bytesWritten = std::strftime(fileNameBuffer + prefixLen, fileNameMaxLength - prefixLen, "%Y-%m-%d-%H:%M:%S", &tm); + const auto offset = prefixLen + bytesWritten; + const auto spaceLeft = fileNameMaxLength - offset; + std::snprintf(fileNameBuffer + offset, spaceLeft - fileExtensionLength, "%d.bmp", frameNumber); + stbi_write_bmp( fileNameBuffer, width, height, 4, data); + }; + + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mainTexture->texID, 0); + auto dataSize = mainTexture->width * mainTexture->height * 4; + GLubyte* pixels = new GLubyte[dataSize]; + glReadPixels(0, 0, mainTexture->width, mainTexture->height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); + safeWriteFile(totalframes, pixels, mainTexture->width, mainTexture->height); + delete[] pixels; +} + void Renderer::setToastMessage(const std::string& theValue) { // Initialize counters diff --git a/src/libprojectM/Renderer/Renderer.hpp b/src/libprojectM/Renderer/Renderer.hpp index cd93b5917..2c5199346 100644 --- a/src/libprojectM/Renderer/Renderer.hpp +++ b/src/libprojectM/Renderer/Renderer.hpp @@ -86,6 +86,7 @@ public: bool correction; bool noSwitch; + bool writeNextFrameToFile; struct preset { int id; @@ -123,7 +124,7 @@ public: Renderer(int width, int height, int gx, int gy, BeatDetect *_beatDetect, std::string presetURL, std::string title_fontURL, std::string menu_fontURL, const std::string& datadir = ""); ~Renderer(); - + void RenderFrame(const Pipeline &pipeline, const PipelineContext &pipelineContext); void RenderFrameOnlyPass1(const Pipeline &pipeline, const PipelineContext &pipelineContext); void RenderFrameOnlyPass2(const Pipeline &pipeline, const PipelineContext &pipelineContext,int xoffset,int yoffset,int eye); @@ -146,7 +147,7 @@ public: } void setHelpText(const std::string& theValue) { - m_helpText = theValue; + m_helpText = theValue; } std::string helpText() const { @@ -154,7 +155,7 @@ public: } void setFPS(const int &theValue) { - m_fps = std::to_string(theValue); + m_fps = std::to_string(theValue); } std::string fps() const { @@ -172,13 +173,14 @@ public: void touchDestroy(float x, float y); void touchDestroyAll(); bool touchedWaveform(float x, float y, std::size_t i); - + void setToastMessage(const std::string& theValue); std::string getSearchText() const; void setSearchText(const std::string& theValue); void resetSearchText(); void deleteSearchText(); - + /// Writes the contents of current mainTexture in TextureManager to a bmp file + void debugWriteMainTextureToFile() const; std::string toastMessage() const { return m_toastMessage; }