├── .clang-format ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── COPYING ├── Common └── Source │ ├── AudioRingBuffer.hpp │ ├── ChannelMapper.hpp │ ├── ChannelSet.hpp │ ├── CoreDump.cpp │ ├── CoreDump.hpp │ ├── Defaults.cpp │ ├── Defaults.hpp │ ├── ImageDiff.hpp │ ├── KeyAndMouse.cpp │ ├── KeyAndMouse.hpp │ ├── KeyAndMouseCommon.hpp │ ├── Logger.cpp │ ├── Logger.hpp │ ├── MemoryFile.cpp │ ├── MemoryFile.hpp │ ├── Message.cpp │ ├── Message.hpp │ ├── Metrics.cpp │ ├── Metrics.hpp │ ├── Sentry.cpp │ ├── Sentry.hpp │ ├── ServerPlugin.hpp │ ├── ServiceReceiver.cpp │ ├── ServiceReceiver.hpp │ ├── SharedInstance.hpp │ ├── Signals.cpp │ ├── Signals.hpp │ ├── TraceReader.cpp │ ├── Tracer.cpp │ ├── Tracer.hpp │ ├── Utils.cpp │ ├── Utils.hpp │ ├── Version.hpp │ ├── WindowHelper.cpp │ ├── WindowHelper.hpp │ ├── WindowHelper.mm │ ├── WindowPositions.cpp │ ├── WindowPositions.hpp │ ├── json.hpp │ ├── mDNS.cpp │ ├── mDNS.hpp │ ├── mDNSConnector.cpp │ └── mDNSConnector.hpp ├── DEVELOP.md ├── Plugin ├── CMakeLists.txt └── Source │ ├── AudioStreamer.hpp │ ├── Client.cpp │ ├── Client.hpp │ ├── GenericEditor.cpp │ ├── GenericEditor.hpp │ ├── ImageReader.cpp │ ├── ImageReader.hpp │ ├── Images.cpp │ ├── Images.hpp │ ├── NewServerWindow.cpp │ ├── NewServerWindow.hpp │ ├── PluginButton.cpp │ ├── PluginButton.hpp │ ├── PluginEditor.cpp │ ├── PluginEditor.hpp │ ├── PluginProcessor.cpp │ ├── PluginProcessor.hpp │ ├── PluginSearchWindow.cpp │ ├── PluginSearchWindow.hpp │ ├── StatisticsWindow.cpp │ └── StatisticsWindow.hpp ├── PluginTray ├── CMakeLists.txt ├── Resources │ ├── icon.png │ ├── icon16.png │ └── icon64.png └── Source │ ├── App.cpp │ ├── App.hpp │ ├── Images.cpp │ ├── Images.hpp │ ├── PluginMonitor.cpp │ └── PluginMonitor.hpp ├── README.md ├── Server ├── CMakeLists.txt ├── Resources │ ├── icon.png │ ├── icon16.png │ └── icon64.png └── Source │ ├── App.cpp │ ├── App.hpp │ ├── AudioWorker.cpp │ ├── AudioWorker.hpp │ ├── CPUInfo.cpp │ ├── CPUInfo.hpp │ ├── Images.cpp │ ├── Images.hpp │ ├── MenuBarWindow.cpp │ ├── MenuBarWindow.hpp │ ├── ParameterValue.hpp │ ├── PluginListComponent.cpp │ ├── PluginListComponent.hpp │ ├── PluginListWindow.cpp │ ├── PluginListWindow.hpp │ ├── Processor.cpp │ ├── Processor.hpp │ ├── ProcessorChain.cpp │ ├── ProcessorChain.hpp │ ├── ProcessorClient.cpp │ ├── ProcessorClient.hpp │ ├── ProcessorWindow.cpp │ ├── ProcessorWindow.hpp │ ├── Sandbox.cpp │ ├── Sandbox.hpp │ ├── Screen.cpp │ ├── Screen.h │ ├── Screen.mm │ ├── ScreenHelper_linux.cpp │ ├── ScreenRecorder.cpp │ ├── ScreenRecorder.hpp │ ├── ScreenWorker.cpp │ ├── ScreenWorker.hpp │ ├── Server.cpp │ ├── Server.hpp │ ├── ServerSettings │ ├── DiagnosticsTab.cpp │ ├── DiagnosticsTab.hpp │ ├── MainTab.cpp │ ├── MainTab.hpp │ ├── PluginFormatsTab.cpp │ ├── PluginFormatsTab.hpp │ ├── ScreenCapturingTab.cpp │ ├── ScreenCapturingTab.hpp │ ├── StartupTab.cpp │ ├── StartupTab.hpp │ └── TabCommon.h │ ├── ServerSettingsWindow.cpp │ ├── ServerSettingsWindow.hpp │ ├── ServiceResponder.cpp │ ├── ServiceResponder.hpp │ ├── SplashWindow.hpp │ ├── StatisticsWindow.cpp │ ├── StatisticsWindow.hpp │ ├── Worker.cpp │ └── Worker.hpp ├── Tests ├── CMakeLists.txt └── Source │ ├── Main.cpp │ ├── Plugin │ └── AudioStreamerTest.hpp │ ├── Server │ ├── MultiMonoTest.hpp │ ├── ProcessorChainTest.hpp │ ├── SandboxPluginTest.hpp │ └── ScanPluginsTest.hpp │ └── TestsHelper.hpp ├── build.py ├── cmake ├── FindFFmpeg.cmake └── FindWebP.cmake ├── images ├── overview.jpg ├── plugin.jpg └── server.jpg ├── package ├── AudioGridderPlugin-10.7-x86_64.pkgproj ├── AudioGridderPlugin-arm64.pkgproj ├── AudioGridderPlugin-universal.pkgproj ├── AudioGridderPlugin-x86_64.pkgproj ├── AudioGridderPlugin.iss.in ├── AudioGridderServer-10.7-x86_64.pkgproj ├── AudioGridderServer-arm64.pkgproj ├── AudioGridderServer-universal.pkgproj ├── AudioGridderServer-x86_64.pkgproj ├── AudioGridderServer.iss.in ├── PlugIn.ico ├── VERSION.num ├── Version.hpp.in ├── archiveWin.bat.in ├── audiogridderserver.desktop ├── buildLinux.sh ├── buildMac.sh ├── buildTraceReader.sh ├── buildWin.bat ├── coresPerms.sh ├── createUniversalBinaries.sh ├── desktop.ini ├── format.sh ├── install-trayapp-linux.sh ├── install_linux_plugin.sh ├── install_linux_server.sh ├── license_MIT.txt ├── makeself-header.sh ├── makeself.sh └── setversion.sh └── vcpkg.json /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | ColumnLimit: 120 3 | IndentWidth: 4 4 | AccessModifierOffset: -2 5 | DerivePointerAlignment: false 6 | SortIncludes: false 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.user 2 | *~ 3 | .DS_Store 4 | JuceLibraryCode 5 | Builds 6 | package/build/* 7 | package/AudioGridder*.iss 8 | package/VERSION 9 | package/archiveWin.bat 10 | build-* 11 | debug-sym-* 12 | GPATH 13 | GRTAGS 14 | GTAGS 15 | .clangd 16 | compile_commands.json 17 | .projectile 18 | .dir-locals.el 19 | .cache 20 | .vscode 21 | TestsData 22 | suppress-win.txt 23 | 24 | #KC 25 | .idea 26 | cmake-build-* 27 | audiogridder-deps -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "JUCE"] 2 | path = JUCE 3 | url = https://github.com/apohl79/JUCE.git 4 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Andreas Pohl 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Common/Source/CoreDump.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef CoreDump_hpp 9 | #define CoreDump_hpp 10 | 11 | #include 12 | 13 | namespace e47 { 14 | namespace CoreDump { 15 | 16 | void initialize(const String& appName, const String& filePrefix, bool showMessage); 17 | 18 | } // namespace CoreDump 19 | 20 | } // namespace e47 21 | 22 | #endif /* CoreDump_hpp */ 23 | -------------------------------------------------------------------------------- /Common/Source/Defaults.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #include "Defaults.hpp" 9 | 10 | #if JUCE_WINDOWS 11 | #include 12 | #endif 13 | 14 | namespace e47 { 15 | namespace Defaults { 16 | 17 | bool unixDomainSocketsSupported() noexcept { 18 | #ifdef JUCE_WINDOWS 19 | if (auto* hndl = GetModuleHandleW(L"ntdll.dll")) { 20 | using RtlGetVersionPtr = LONG(WINAPI*)(PRTL_OSVERSIONINFOW); 21 | if (auto* rtlGetVersion = (RtlGetVersionPtr)GetProcAddress(hndl, "RtlGetVersion")) { 22 | RTL_OSVERSIONINFOW osInfo = {}; 23 | osInfo.dwOSVersionInfoSize = sizeof(osInfo); 24 | if (rtlGetVersion(&osInfo) == 0) { 25 | return osInfo.dwBuildNumber >= 17134; 26 | } 27 | } 28 | } 29 | return false; 30 | #else 31 | return true; 32 | #endif 33 | } 34 | 35 | } // namespace Defaults 36 | } // namespace e47 37 | -------------------------------------------------------------------------------- /Common/Source/ImageDiff.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef ImageDiff_hpp 9 | #define ImageDiff_hpp 10 | 11 | namespace e47 { 12 | 13 | namespace ImageDiff { 14 | 15 | static bool operator==(const PixelARGB& lhs, const PixelARGB& rhs) { 16 | return lhs.getNativeARGB() == rhs.getNativeARGB(); 17 | } 18 | 19 | static bool operator!=(const PixelARGB& lhs, const PixelARGB& rhs) { return !(lhs == rhs); } 20 | 21 | using PerPixelFn = std::function; 22 | 23 | inline uint64_t getDelta(const uint8_t* imgFrom, const uint8_t* imgTo, uint8_t* imgDelta, int width, int height, 24 | PerPixelFn fn = nullptr) { 25 | uint64_t count = 0; 26 | auto* pxFrom = reinterpret_cast(imgFrom); 27 | auto* pxTo = reinterpret_cast(imgTo); 28 | auto* pxDelta = reinterpret_cast(imgDelta); 29 | for (int y = 0; y < height; y++) { 30 | for (int x = 0; x < width; x++) { 31 | auto p = (size_t)(y * width + x); 32 | if (pxFrom[p] != pxTo[p]) { 33 | count++; 34 | pxDelta[p].set(pxTo[p]); 35 | pxDelta[p].setAlpha(255); 36 | } else { 37 | pxDelta[p] = {0, 0, 0, 0}; 38 | } 39 | if (fn) { 40 | fn(pxTo[p]); 41 | } 42 | } 43 | } 44 | return count; 45 | } 46 | 47 | inline uint64_t getDelta(const Image& imgFrom, const Image& imgTo, const Image& imgDelta, PerPixelFn fn = nullptr) { 48 | if (imgFrom.getBounds() == imgTo.getBounds() && imgDelta.getBounds() == imgTo.getBounds()) { 49 | int width = imgTo.getWidth(); 50 | int height = imgTo.getHeight(); 51 | const Image::BitmapData bdFrom(imgFrom, 0, 0, width, height); 52 | const Image::BitmapData bdTo(imgTo, 0, 0, width, height); 53 | Image::BitmapData bdDelta(imgDelta, 0, 0, width, height); 54 | return getDelta(bdFrom.data, bdTo.data, bdDelta.data, width, height, fn); 55 | } 56 | return 0; 57 | } 58 | 59 | inline uint64_t applyDelta(uint8_t* imgDst, const uint8_t* imgDelta, int width, int height) { 60 | uint64_t count = 0; 61 | auto* pxDst = reinterpret_cast(imgDst); 62 | auto* pxDelta = reinterpret_cast(imgDelta); 63 | for (int y = 0; y < height; y++) { 64 | for (int x = 0; x < width; x++) { 65 | auto p = (size_t)(y * width + x); 66 | if (pxDelta[p].getAlpha() == 255) { 67 | pxDst[p].set(pxDelta[p]); 68 | count++; 69 | } 70 | } 71 | } 72 | return count; 73 | } 74 | 75 | inline uint64_t applyDelta(Image& imgDst, const Image& imgDelta) { 76 | if (imgDelta.getBounds() == imgDst.getBounds()) { 77 | int width = imgDelta.getWidth(); 78 | int height = imgDelta.getHeight(); 79 | const Image::BitmapData bdDelta(imgDelta, 0, 0, width, height); 80 | Image::BitmapData bdDst(imgDst, 0, 0, width, height); 81 | return applyDelta(bdDst.data, bdDelta.data, width, height); 82 | } 83 | return 0; 84 | } 85 | 86 | inline float getBrightness(const PixelARGB& px) { 87 | auto col = Colour::fromRGBA(px.getRed(), px.getGreen(), px.getBlue(), px.getAlpha()); 88 | return col.getFloatRed() / 3 + col.getFloatGreen() / 3 + col.getFloatBlue() / 3; 89 | } 90 | 91 | inline float getBrightness(const uint8_t* img, int width, int height) { 92 | auto* px = reinterpret_cast(img); 93 | float brightness = 0; 94 | for (int y = 0; y < height; y++) { 95 | for (int x = 0; x < width; x++) { 96 | auto p = (size_t)(y * width + x); 97 | brightness += getBrightness(px[p]); 98 | } 99 | } 100 | return brightness; 101 | } 102 | 103 | inline float getBrightness(const Image& img) { 104 | int width = img.getWidth(); 105 | int height = img.getHeight(); 106 | const Image::BitmapData bd(img, 0, 0, width, height); 107 | return getBrightness(bd.data, width, height); 108 | } 109 | 110 | } // namespace ImageDiff 111 | 112 | } // namespace e47 113 | 114 | #endif 115 | -------------------------------------------------------------------------------- /Common/Source/KeyAndMouse.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef KeyAndMouse_hpp 9 | #define KeyAndMouse_hpp 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "KeyAndMouseCommon.hpp" 16 | 17 | namespace e47 { 18 | 19 | static inline bool isShiftKey(uint16_t kc) { return kc == 0x38; } 20 | static inline bool isControlKey(uint16_t kc) { return kc == 0x3B; } 21 | static inline bool isAltKey(uint16_t kc) { return kc == 0x3A; } 22 | static inline bool isCopyKey(uint16_t kc) { return kc == COPYKEY; } 23 | static inline bool isPasteKey(uint16_t kc) { return kc == PASTEKEY; } 24 | static inline bool isCutKey(uint16_t kc) { return kc == CUTKEY; } 25 | static inline bool isSelectAllKey(uint16_t kc) { return kc == SELECTALLKEY; } 26 | 27 | void setShiftKey(uint64_t& flags); 28 | void setControlKey(uint64_t& flags); 29 | void setAltKey(uint64_t& flags); 30 | void setCopyKeys(uint16_t& key, uint64_t& flags); 31 | void setPasteKeys(uint16_t& key, uint64_t& flags); 32 | void setCutKeys(uint16_t& key, uint64_t& flags); 33 | void setSelectAllKeys(uint16_t& key, uint64_t& flags); 34 | 35 | void mouseEvent(MouseEvType t, float x, float y, uint64_t flags = 0); 36 | void mouseScrollEvent(float x, float y, float deltaX, float deltaY, bool isSmooth); 37 | void keyEventDown(uint16_t keyCode, uint64_t flags = 0, bool currentProcessOnly = false, void* nativeHandle = nullptr); 38 | void keyEventUp(uint16_t keyCode, uint64_t flags = 0, bool currentProcessOnly = false, void* nativeHandle = nullptr); 39 | 40 | } // namespace e47 41 | 42 | #endif /* KeyAndMouse_hpp */ 43 | -------------------------------------------------------------------------------- /Common/Source/KeyAndMouseCommon.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef KeyAndMouseCommon_hpp 9 | #define KeyAndMouseCommon_hpp 10 | 11 | #include 12 | #include 13 | 14 | enum MouseEvType : uint8_t { 15 | MOVE, 16 | LEFT_DOWN, 17 | LEFT_UP, 18 | LEFT_DRAG, 19 | RIGHT_DOWN, 20 | RIGHT_UP, 21 | RIGHT_DRAG, 22 | OTHER_DOWN, 23 | OTHER_UP, 24 | OTHER_DRAG, 25 | WHEEL, 26 | DBL_CLICK 27 | }; 28 | 29 | const uint16_t COPYKEY = 0xF0; 30 | const uint16_t PASTEKEY = 0xF1; 31 | const uint16_t CUTKEY = 0xF2; 32 | const uint16_t SELECTALLKEY = 0xF3; 33 | const uint16_t NOKEY = 0xFF; 34 | 35 | const std::unordered_map VKeyMap = { 36 | {"A", 0x00}, {"S", 0x01}, {"D", 0x02}, 37 | {"F", 0x03}, {"H", 0x04}, {"G", 0x05}, 38 | {"Z", 0x06}, {"X", 0x07}, {"C", 0x08}, 39 | {"V", 0x09}, {"B", 0x0B}, {"Q", 0x0C}, 40 | {"W", 0x0D}, {"E", 0x0E}, {"R", 0x0F}, 41 | {"Y", 0x10}, {"T", 0x11}, {"1", 0x12}, 42 | {"2", 0x13}, {"3", 0x14}, {"4", 0x15}, 43 | {"6", 0x16}, {"5", 0x17}, {"=", 0x18}, 44 | {"9", 0x19}, {"7", 0x1A}, {"-", 0x1B}, 45 | {"8", 0x1C}, {"0", 0x1D}, {"{", 0x1E}, 46 | {"O", 0x1F}, {"U", 0x20}, {"}", 0x21}, 47 | {"I", 0x22}, {"P", 0x23}, {"L", 0x25}, 48 | {"J", 0x26}, {"'", 0x27}, {"K", 0x28}, 49 | {";", 0x29}, {"\\", 0x2A}, {",", 0x2B}, 50 | {"/", 0x2C}, {"N", 0x2D}, {"M", 0x2E}, 51 | {".", 0x2F}, {"`", 0x32}, {"Return", 0x24}, 52 | {"Tab", 0x30}, {"Space", 0x31}, {"Backspace", 0x33}, 53 | {"Escape", 0x35}, {"Command", 0x37}, {"Shift", 0x38}, 54 | {"Option", 0x3A}, {"Control", 0x3B}, {"F17", 0x40}, 55 | {"F18", 0x4F}, {"F19", 0x50}, {"F20", 0x5A}, 56 | {"F5", 0x60}, {"F6", 0x65}, {"F11", 0x67}, 57 | {"F13", 0x69}, {"F16", 0x6A}, {"F14", 0x6B}, 58 | {"F10", 0x6D}, {"F12", 0x6F}, {"F15", 0x71}, 59 | {"Home", 0x73}, {"PageUp", 0x74}, {"Delete", 0x75}, 60 | {"F4", 0x76}, {"End", 0x77}, {"F2", 0x78}, 61 | {"PageDown", 0x79}, {"F1", 0x7A}, {"LeftArrow", 0x7B}, 62 | {"RightArrow", 0x7C}, {"DownArrow", 0x7D}, {"UpArrow", 0x7E}, 63 | {"Numpad0", 0x52}, {"Numpad1", 0x53}, {"Numpad2", 0x54}, 64 | {"Numpad3", 0x55}, {"Numpad4", 0x56}, {"Numpad5", 0x57}, 65 | {"Numpad6", 0x58}, {"Numpad7", 0x59}, {"Numpad8", 0x5A}, 66 | {"Numpad9", 0x5B}, {"Numpad*", 0x43}, {"Numpad/", 0x4B}, 67 | {"Numpad+", 0x45}, {"Numpad-", 0x4E}, {"Numpad=", 0x51}, 68 | {"Numpad.", 0x41}, {"NumpadClear", 0x47}, {"Copy", COPYKEY}, 69 | {"Paste", PASTEKEY}, {"Cut", CUTKEY}, {"SelectAll", SELECTALLKEY}}; 70 | 71 | static inline uint16_t getKeyCode(std::string s) { 72 | auto it = VKeyMap.find(s); 73 | if (it != VKeyMap.end()) { 74 | return (uint16_t)it->second; 75 | } 76 | return NOKEY; 77 | } 78 | 79 | static inline std::string getKeyName(uint16_t code) { 80 | for (auto& p : VKeyMap) { 81 | if (p.second == code) { 82 | return p.first; 83 | } 84 | } 85 | return ""; 86 | } 87 | 88 | #endif /* KeyAndMouseCommon_hpp */ 89 | -------------------------------------------------------------------------------- /Common/Source/Logger.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef Logger_hpp 9 | #define Logger_hpp 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace e47 { 16 | 17 | class Logger : public Thread { 18 | public: 19 | Logger(const String& appName, const String& filePrefix, bool linkLatest = true); 20 | ~Logger() override; 21 | void run() override; 22 | 23 | static void log(String msg); 24 | 25 | static void initialize(const String& appName, const String& filePrefix, const String& configFile, 26 | bool linkLatest = true, bool logDirectly = false); 27 | 28 | static void initialize() { 29 | initialize({}, {}, {}, true, true); 30 | setLogToErr(true); 31 | } 32 | 33 | static void deleteFileAtFinish(); 34 | static std::shared_ptr getInstance(); 35 | static void cleanup(); 36 | 37 | static bool isEnabled() { return m_enabled; } 38 | static void setEnabled(bool b); 39 | static File getLogFile(); 40 | static void setLogToErr(bool b); 41 | static void setLogDirectly(bool b); 42 | 43 | private: 44 | File m_file; 45 | std::ofstream m_outstream; 46 | bool m_deleteFile = false; 47 | bool m_logDirectly = false; 48 | std::queue m_msgQ[2]; 49 | size_t m_msgQIdx = 0; 50 | std::mutex m_mtx; 51 | std::condition_variable m_cv; 52 | 53 | bool m_logToErr = false; 54 | bool m_debugger = false; 55 | 56 | void logToQueue(String msg); 57 | void logMsg(const String& msg); 58 | 59 | static std::shared_ptr m_inst; 60 | static std::mutex m_instMtx; 61 | static size_t m_instRefCount; 62 | 63 | static std::atomic_bool m_enabled; 64 | }; 65 | 66 | class LogTag { 67 | public: 68 | LogTag(const String& name) : m_tagId((uint64)this), m_tagName(name) {} 69 | LogTag(const LogTag& other) : m_tagId(other.m_tagId), m_tagName(other.m_tagName), m_tagExtra(other.m_tagExtra) {} 70 | virtual ~LogTag() {} 71 | 72 | LogTag& operator=(const LogTag& rhs) { 73 | if (this != &rhs) { 74 | m_tagId = rhs.m_tagId; 75 | m_tagName = rhs.m_tagName; 76 | m_tagExtra = rhs.m_tagExtra; 77 | } 78 | return *this; 79 | } 80 | 81 | static inline String getStrWithLeadingZero(int n, int digits = 2) { 82 | String s = ""; 83 | while (--digits > 0) { 84 | if (n < pow(10, digits)) { 85 | s << "0"; 86 | } 87 | } 88 | s << n; 89 | return s; 90 | } 91 | 92 | static inline String getTimeStr() { 93 | auto now = Time::getCurrentTime(); 94 | auto H = getStrWithLeadingZero(now.getHours()); 95 | auto M = getStrWithLeadingZero(now.getMinutes()); 96 | auto S = getStrWithLeadingZero(now.getSeconds()); 97 | auto m = getStrWithLeadingZero(now.getMilliseconds(), 3); 98 | String ret = ""; 99 | ret << H << ":" << M << ":" << S << "." << m; 100 | return ret; 101 | } 102 | 103 | static inline String getTaggedStr(const String& name, const String& ptr, const String& extra, bool withTime) { 104 | String tag = ""; 105 | if (withTime) { 106 | tag << getTimeStr() << "|"; 107 | } 108 | tag << name << "|" << ptr; 109 | if (extra.isNotEmpty()) { 110 | tag << "|" << extra; 111 | } 112 | return tag; 113 | } 114 | 115 | void setLogTagExtra(const String& s) { m_tagExtra = s; } 116 | void setLogTagName(const String& s) { m_tagName = s; } 117 | const String& getLogTagName() const { return m_tagName; } 118 | const String& getLogTagExtra() const { return m_tagExtra; } 119 | uint64 getTagId() const { return m_tagId; } 120 | 121 | const LogTag* getLogTagSource() const { return this; } 122 | String getLogTag() const { 123 | return m_tagId == 0 ? "" : getTaggedStr(m_tagName, String::toHexString(m_tagId), m_tagExtra, true); 124 | } 125 | String getLogTagNoTime() const { 126 | return m_tagId == 0 ? "" : getTaggedStr(m_tagName, String::toHexString(m_tagId), m_tagExtra, false); 127 | } 128 | 129 | protected: 130 | uint64 m_tagId = 0; 131 | String m_tagName; 132 | String m_tagExtra; 133 | }; 134 | 135 | class LogTagDelegate : public LogTag { 136 | public: 137 | LogTagDelegate(const LogTag* src = nullptr) : LogTag("unset") { setLogTagSource(src); } 138 | void setLogTagSource(const LogTag* src) { 139 | if (nullptr != src) { 140 | m_tagId = src->getTagId(); 141 | m_tagName = src->getLogTagName(); 142 | m_tagExtra = src->getLogTagExtra(); 143 | } 144 | } 145 | }; 146 | 147 | } // namespace e47 148 | 149 | #endif /* Logger_hpp */ 150 | -------------------------------------------------------------------------------- /Common/Source/MemoryFile.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #include "MemoryFile.hpp" 9 | 10 | #ifndef JUCE_WINDOWS 11 | #include 12 | #include 13 | #include 14 | #endif 15 | 16 | namespace e47 { 17 | 18 | MemoryFile::MemoryFile(LogTag* tag, const String& path, size_t size) 19 | : LogTagDelegate(tag), m_file(path), m_size(size) {} 20 | 21 | MemoryFile::MemoryFile(LogTag* tag, const File& file, size_t size) : LogTagDelegate(tag), m_file(file), m_size(size) {} 22 | 23 | MemoryFile::~MemoryFile() { close(); } 24 | 25 | void MemoryFile::open(bool overwriteIfExists) { 26 | if (isOpen()) { 27 | logln("file already opened"); 28 | return; 29 | } 30 | void* m = nullptr; 31 | #ifdef JUCE_WINDOWS 32 | m_fd = CreateFileA(m_file.getFullPathName().getCharPointer(), (GENERIC_READ | GENERIC_WRITE), 33 | (FILE_SHARE_READ | FILE_SHARE_WRITE), NULL, overwriteIfExists ? CREATE_ALWAYS : OPEN_ALWAYS, 34 | FILE_ATTRIBUTE_TEMPORARY, NULL); 35 | if (m_fd == INVALID_HANDLE_VALUE) { 36 | logln("CreateFileA failed: " << getLastErrorStr()); 37 | return; 38 | } 39 | LONG sizeh = (m_size >> (sizeof(LONG) * 8)); 40 | LONG sizel = (m_size & 0xffffffff); 41 | auto res = SetFilePointer(m_fd, sizel, &sizeh, FILE_BEGIN); 42 | if (INVALID_SET_FILE_POINTER == res) { 43 | logln("SetFilePointer failed: " << getLastErrorStr()); 44 | return; 45 | } 46 | if (!SetEndOfFile(m_fd)) { 47 | logln("SetEndOfFile failed: " << getLastErrorStr()); 48 | return; 49 | } 50 | m_mapped_hndl = CreateFileMappingA(m_fd, NULL, PAGE_READWRITE, 0, 0, NULL); 51 | if (NULL == m_mapped_hndl) { 52 | logln("CreateFileMappingA failed: " << getLastErrorStr()); 53 | return; 54 | } 55 | m = MapViewOfFileEx(m_mapped_hndl, FILE_MAP_WRITE, 0, 0, 0, NULL); 56 | if (NULL == m) { 57 | logln("MapViewOfFileEx failed: " << getLastErrorStr()); 58 | return; 59 | } 60 | #else 61 | auto openFlags = O_CREAT | O_RDWR; 62 | if (overwriteIfExists) { 63 | openFlags |= O_TRUNC; 64 | } 65 | m_fd = ::open(m_file.getFullPathName().getCharPointer(), openFlags, S_IRWXU); 66 | if (m_fd < 0) { 67 | logln("open failed: " << strerror(errno)); 68 | return; 69 | } 70 | if (ftruncate(m_fd, (off_t)m_size)) { 71 | logln("ftruncate failed: " << strerror(errno)); 72 | return; 73 | } 74 | m = mmap(nullptr, m_size, PROT_WRITE | PROT_READ, MAP_SHARED, m_fd, 0); 75 | if (MAP_FAILED == m) { 76 | logln("mmap failed: " << strerror(errno)); 77 | return; 78 | } 79 | #endif 80 | m_data = static_cast(m); 81 | } 82 | 83 | void MemoryFile::close() { 84 | if (!isOpen()) { 85 | return; 86 | } 87 | #ifdef JUCE_WINDOWS 88 | UnmapViewOfFile(m_data); 89 | CloseHandle(m_mapped_hndl); 90 | m_mapped_hndl = NULL; 91 | CloseHandle(m_fd); 92 | m_fd = NULL; 93 | #else 94 | munmap(m_data, m_size); 95 | ::close(m_fd); 96 | m_fd = -1; 97 | #endif 98 | m_data = nullptr; 99 | } 100 | 101 | } // namespace e47 102 | -------------------------------------------------------------------------------- /Common/Source/MemoryFile.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef __MEMORYFILE_H_ 9 | #define __MEMORYFILE_H_ 10 | 11 | #include 12 | 13 | #ifdef JUCE_WINDOWS 14 | #include 15 | #endif 16 | 17 | #include "Utils.hpp" 18 | 19 | namespace e47 { 20 | 21 | class MemoryFile : LogTagDelegate { 22 | public: 23 | MemoryFile() {} 24 | MemoryFile(LogTag* tag, const String& path, size_t size); 25 | MemoryFile(LogTag* tag, const File& file, size_t size); 26 | MemoryFile(const MemoryFile& other) 27 | : LogTagDelegate(other.getLogTagSource()), 28 | m_file(other.m_file), 29 | m_fd(other.m_fd), 30 | #ifdef JUCE_WINDOWS 31 | m_mapped_hndl(other.m_mapped_hndl), 32 | #endif 33 | m_data(other.m_data), 34 | m_size(other.m_size) { 35 | } 36 | ~MemoryFile(); 37 | 38 | MemoryFile& operator=(const MemoryFile& rhs) { 39 | if (this != &rhs) { 40 | m_file = rhs.m_file; 41 | m_fd = rhs.m_fd; 42 | #ifdef JUCE_WINDOWS 43 | m_mapped_hndl = rhs.m_mapped_hndl; 44 | #endif 45 | m_data = rhs.m_data; 46 | m_size = rhs.m_size; 47 | } 48 | return *this; 49 | } 50 | 51 | bool exists() const { return m_file.exists(); } 52 | bool isOpen() const { return nullptr != m_data; } 53 | void open(bool overwriteIfExists = false); 54 | void close(); 55 | void deleteFile() const { m_file.deleteFile(); } 56 | File getFile() const { return m_file; } 57 | 58 | char* data() { return m_data; } 59 | size_t size() { return m_size; } 60 | 61 | private: 62 | File m_file; 63 | #ifdef JUCE_WINDOWS 64 | HANDLE m_fd = NULL; 65 | HANDLE m_mapped_hndl = NULL; 66 | #else 67 | int m_fd = -1; 68 | #endif 69 | char* m_data = nullptr; 70 | size_t m_size = 0; 71 | }; 72 | 73 | } // namespace e47 74 | 75 | #endif // __MEMORYFILE_H_ 76 | -------------------------------------------------------------------------------- /Common/Source/Sentry.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #include "Sentry.hpp" 9 | 10 | #include "Sentry.hpp" 11 | #include "Defaults.hpp" 12 | #include "Utils.hpp" 13 | #include "Version.hpp" 14 | 15 | #ifndef AG_SENTRY_ENABLED 16 | #define AG_SENTRY_ENABLED 0 17 | #endif 18 | 19 | #if AG_SENTRY_ENABLED 20 | #define SENTRY_BUILD_STATIC 21 | #include 22 | #endif 23 | 24 | namespace e47 { 25 | namespace Sentry { 26 | 27 | #if AG_SENTRY_ENABLED 28 | static std::atomic_bool l_sentryEnabled{true}; 29 | static std::atomic_bool l_sentryInitialized{false}; 30 | #endif 31 | 32 | void initialize() { 33 | #if AG_SENTRY_ENABLED 34 | auto crashpadPath = Defaults::getSentryCrashpadPath(); 35 | setLogTagStatic("sentry"); 36 | if (juce_isRunningUnderDebugger()) { 37 | logln("not initializing sentry: debugger detected"); 38 | } else if (l_sentryEnabled && crashpadPath.isNotEmpty() && !l_sentryInitialized.exchange(true)) { 39 | logln("initializing crash reporting..."); 40 | sentry_options_t* options = sentry_options_new(); 41 | sentry_options_set_dsn(options, AG_SENTRY_DSN); 42 | sentry_options_set_handler_path(options, crashpadPath.toRawUTF8()); 43 | sentry_options_set_database_path(options, Defaults::getSentryDbPath().toRawUTF8()); 44 | 45 | if (String(AUDIOGRIDDER_VERSION) != "dev-build") { 46 | StringArray parts = StringArray::fromTokens(AUDIOGRIDDER_VERSION, "-", {}); 47 | StringArray version = StringArray::fromTokens(AUDIOGRIDDER_VERSION_NUM, ".", {}); 48 | String rel = "release_"; 49 | rel << version[0] << "_" << version[1] << "_" << version[2]; 50 | if (parts.size() > 1) { 51 | rel << "_" << parts[1]; 52 | } 53 | sentry_options_set_release(options, rel.toRawUTF8()); 54 | } 55 | 56 | if (Logger::isEnabled()) { 57 | auto logfile = Logger::getLogFile().getFullPathName(); 58 | if (logfile.isNotEmpty()) { 59 | logln(" attaching logfile: " << Logger::getLogFile().getFileName()); 60 | sentry_options_add_attachment(options, logfile.toRawUTF8()); 61 | } 62 | } 63 | 64 | if (Tracer::isEnabled()) { 65 | auto tracefile = Tracer::getTraceFile().getFullPathName(); 66 | if (tracefile.isNotEmpty()) { 67 | logln(" attaching tracefile: " << Tracer::getTraceFile().getFileName()); 68 | sentry_options_add_attachment(options, tracefile.toRawUTF8()); 69 | } 70 | } 71 | 72 | sentry_init(options); 73 | } 74 | #endif 75 | } 76 | 77 | void cleanup() { 78 | #if AG_SENTRY_ENABLED 79 | if (l_sentryInitialized.exchange(false)) { 80 | sentry_close(); 81 | } 82 | #endif 83 | } 84 | 85 | void setEnabled(bool b) { 86 | #if AG_SENTRY_ENABLED 87 | l_sentryEnabled = b; 88 | #else 89 | ignoreUnused(b); 90 | #endif 91 | } 92 | 93 | bool isEnabled() { 94 | #if AG_SENTRY_ENABLED 95 | return l_sentryEnabled; 96 | #else 97 | return false; 98 | #endif 99 | } 100 | 101 | } // namespace Sentry 102 | } // namespace e47 103 | -------------------------------------------------------------------------------- /Common/Source/Sentry.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef _SENTRY_HPP_ 9 | #define _SENTRY_HPP_ 10 | 11 | namespace e47 { 12 | namespace Sentry { 13 | 14 | void initialize(); 15 | void cleanup(); 16 | void setEnabled(bool b); 17 | bool isEnabled(); 18 | 19 | } // namespace Sentry 20 | } // namespace e47 21 | 22 | #endif // _SENTRY_HPP_ 23 | -------------------------------------------------------------------------------- /Common/Source/ServerPlugin.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef ServerPlugin_hpp 9 | #define ServerPlugin_hpp 10 | 11 | #include 12 | 13 | #include "Utils.hpp" 14 | 15 | using json = nlohmann::json; 16 | 17 | namespace e47 { 18 | 19 | class ServerPlugin { 20 | public: 21 | ServerPlugin() noexcept {} 22 | 23 | ServerPlugin(const String& name, const String& company, const String& id, const String& idDeprecated, 24 | const String& type, const String& category, bool isInstrument, const StringArray& layouts) noexcept 25 | : m_name(name), 26 | m_company(company), 27 | m_id(id), 28 | m_idDeprecated(idDeprecated), 29 | m_type(type), 30 | m_category(category), 31 | m_isInstrument(isInstrument), 32 | m_layouts(layouts) { 33 | if (m_id.isEmpty()) { 34 | m_id = m_idDeprecated; 35 | } 36 | if (m_category.isEmpty()) { 37 | m_category = "Unknown"; 38 | } 39 | } 40 | 41 | ServerPlugin(const ServerPlugin& other) noexcept { 42 | m_name = other.m_name; 43 | m_company = other.m_company; 44 | m_id = other.m_id; 45 | m_idDeprecated = other.m_idDeprecated; 46 | m_type = other.m_type; 47 | m_category = other.m_category; 48 | m_isInstrument = other.m_isInstrument; 49 | m_layouts = other.m_layouts; 50 | } 51 | 52 | ServerPlugin& operator=(const ServerPlugin& other) noexcept { 53 | m_name = other.m_name; 54 | m_company = other.m_company; 55 | m_id = other.m_id; 56 | m_idDeprecated = other.m_idDeprecated; 57 | m_type = other.m_type; 58 | m_category = other.m_category; 59 | m_isInstrument = other.m_isInstrument; 60 | m_layouts = other.m_layouts; 61 | return *this; 62 | } 63 | 64 | friend bool operator==(const ServerPlugin& lhs, const ServerPlugin& rhs) { 65 | return lhs.m_name == rhs.m_name && lhs.m_company == rhs.m_company && lhs.m_id == rhs.m_id && 66 | lhs.m_type == rhs.m_type && lhs.m_category == rhs.m_category && lhs.m_isInstrument == rhs.m_isInstrument; 67 | } 68 | 69 | const String& getName() const { return m_name; } 70 | const String& getCompany() const { return m_company; } 71 | const String& getId() const { return m_id; } 72 | const String& getIdDeprecated() const { return m_idDeprecated; } 73 | const String& getType() const { return m_type; } 74 | const String& getCategory() const { return m_category; } 75 | bool isInstrument() const { return m_isInstrument; } 76 | const StringArray& getLayouts() const { return m_layouts; } 77 | 78 | static ServerPlugin fromString(const String& s) { 79 | try { 80 | auto j = json::parse(s.toStdString()); 81 | return fromJson(j); 82 | } catch (json::parse_error&) { 83 | auto parts = StringArray::fromTokens(s, ";", ""); 84 | return ServerPlugin(parts[0], parts[1], parts[2], parts[2], parts[3], parts[4], false, {}); 85 | } 86 | } 87 | 88 | static ServerPlugin fromJson(const json& j) { 89 | ServerPlugin plug(jsonGetValue(j, "name", String()), jsonGetValue(j, "company", String()), 90 | jsonGetValue(j, "id2", String()), jsonGetValue(j, "id", String()), 91 | jsonGetValue(j, "type", String()), jsonGetValue(j, "category", String()), 92 | jsonGetValue(j, "isInstrument", false), {}); 93 | if (jsonHasValue(j, "layouts")) { 94 | for (auto& jlayout : j["layouts"]) { 95 | plug.m_layouts.add(jlayout.get()); 96 | } 97 | } 98 | return plug; 99 | } 100 | 101 | String toString() const { 102 | json j; 103 | j["name"] = m_name.toStdString(); 104 | j["company"] = m_company.toStdString(); 105 | j["id"] = m_id.toStdString(); 106 | j["idDeprecated"] = m_idDeprecated.toStdString(); 107 | j["type"] = m_type.toStdString(); 108 | j["category"] = m_category.toStdString(); 109 | j["isInstrument"] = m_isInstrument; 110 | auto jlayouts = json::array(); 111 | for (auto& l : m_layouts) { 112 | jlayouts.push_back(l.toStdString()); 113 | } 114 | j["layouts"] = jlayouts; 115 | return j.dump(); 116 | } 117 | 118 | private: 119 | String m_name; 120 | String m_company; 121 | String m_id; 122 | String m_idDeprecated; 123 | String m_type; 124 | String m_category; 125 | bool m_isInstrument; 126 | StringArray m_layouts; 127 | }; 128 | 129 | struct MenuLevel { 130 | enum Type { NONE, FORMAT, CATEGORY, COMPANY, PLUGIN }; 131 | Type type = NONE; 132 | std::unique_ptr> entryMap; 133 | std::unique_ptr> subMap; 134 | }; 135 | 136 | } // namespace e47 137 | 138 | #endif /* ServerPlugin_hpp */ 139 | -------------------------------------------------------------------------------- /Common/Source/ServiceReceiver.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | * 7 | * Based on https://github.com/mjansson/mdns 8 | */ 9 | 10 | #ifndef ServiceReceiver_hpp 11 | #define ServiceReceiver_hpp 12 | 13 | #include 14 | #include "mDNS.hpp" 15 | #include "Utils.hpp" 16 | 17 | namespace e47 { 18 | 19 | class ServiceReceiver : public Thread, public LogTag { 20 | public: 21 | ServiceReceiver() : Thread("ServiceReceiver"), LogTag("mdns") { startThread(); } 22 | ~ServiceReceiver() override { 23 | logln("stopping receiver"); 24 | stopThread(1500); 25 | } 26 | 27 | void run() override; 28 | 29 | int handleRecord(int sock, const struct sockaddr* from, size_t addrlen, mdns_entry_type_t entry, uint16_t query_id, 30 | uint16_t rtype, uint16_t rclass, uint32_t ttl, const void* data, size_t size, size_t name_offset, 31 | size_t name_length, size_t record_offset, size_t record_length, void* user_data); 32 | 33 | static void initialize(uint64 id, std::function fn = nullptr); 34 | static std::shared_ptr getInstance(); 35 | static void cleanup(uint64 id); 36 | 37 | static Array getServers(); 38 | static String hostToName(const String& host); 39 | static ServerInfo lookupServerInfo(const String& host); 40 | static ServerInfo lookupServerInfo(const Uuid& uuid); 41 | 42 | private: 43 | static std::shared_ptr m_inst; 44 | static std::mutex m_instMtx; 45 | static size_t m_instRefCount; 46 | 47 | char m_entryBuffer[1024]; 48 | mdns_record_txt_t m_txtBuffer[512]; 49 | int m_curId; 50 | int m_curPort; 51 | String m_curName; 52 | String m_curUuid; 53 | float m_curLoad; 54 | bool m_curLocalMode; 55 | String m_curVersion; 56 | Array m_currentResult; 57 | 58 | Array m_servers; 59 | std::mutex m_serversMtx; 60 | std::unordered_map m_lastReachableChecks; 61 | 62 | HashMap> m_updateFn; 63 | 64 | Array getServersInternal(); 65 | bool updateServers(); 66 | 67 | bool isReachable(const ServerInfo& srv); 68 | }; 69 | 70 | } // namespace e47 71 | #endif /* ServiceReceiver_hpp */ 72 | -------------------------------------------------------------------------------- /Common/Source/SharedInstance.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef SharedInstance_hpp 9 | #define SharedInstance_hpp 10 | 11 | #include 12 | #include 13 | 14 | namespace e47 { 15 | 16 | template 17 | class SharedInstance { 18 | public: 19 | virtual ~SharedInstance() {} 20 | 21 | static void initialize(std::function)> onInit = nullptr) { 22 | std::lock_guard lock(m_instMtx); 23 | if (nullptr == m_inst) { 24 | m_inst = std::make_shared(); 25 | if (nullptr != onInit) { 26 | onInit(m_inst); 27 | } 28 | } 29 | m_instRefCount++; 30 | } 31 | 32 | static void cleanup(std::function)> onCleanup = nullptr) { 33 | std::lock_guard lock(m_instMtx); 34 | m_instRefCount--; 35 | if (m_instRefCount == 0) { 36 | if (nullptr != onCleanup) { 37 | onCleanup(m_inst); 38 | } 39 | m_inst.reset(); 40 | } 41 | } 42 | 43 | static std::shared_ptr getInstance() { 44 | std::lock_guard lock(m_instMtx); 45 | return m_inst; 46 | } 47 | 48 | static size_t getRefCount() { 49 | std::lock_guard lock(m_instMtx); 50 | return m_instRefCount; 51 | } 52 | 53 | protected: 54 | static std::mutex& getInstanceMtx() { return m_instMtx; } 55 | 56 | private: 57 | static std::shared_ptr m_inst; 58 | static std::mutex m_instMtx; 59 | static size_t m_instRefCount; 60 | }; 61 | 62 | template 63 | std::shared_ptr SharedInstance::m_inst; 64 | 65 | template 66 | std::mutex SharedInstance::m_instMtx; 67 | 68 | template 69 | size_t SharedInstance::m_instRefCount = 0; 70 | 71 | } // namespace e47 72 | 73 | #endif /* SharedInstance_hpp */ 74 | -------------------------------------------------------------------------------- /Common/Source/Signals.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #include "Signals.hpp" 9 | #include "Utils.hpp" 10 | 11 | #ifdef JUCE_WINDOWS 12 | #include 13 | #include 14 | #include 15 | #else 16 | #include 17 | #endif 18 | 19 | #include 20 | 21 | namespace e47 { 22 | namespace Signals { 23 | 24 | setLogTagStatic("signals"); 25 | 26 | typedef void (*sigAction)(int); 27 | 28 | void signalHandler(int signum) { 29 | traceScope(); 30 | switch (signum) { 31 | case SIGABRT: 32 | traceln("SIGABRT"); 33 | break; 34 | case SIGSEGV: 35 | traceln("SIGSEGV"); 36 | break; 37 | case SIGFPE: 38 | traceln("SIGFPE"); 39 | break; 40 | default: 41 | traceln("signum=" << signum); 42 | return; 43 | } 44 | #ifdef JUCE_WINDOWS 45 | RaiseException(0, 0, 0, NULL); 46 | #else 47 | void* callstack[128]; 48 | int frames = backtrace(callstack, 128); 49 | char** strs = backtrace_symbols(callstack, frames); 50 | for (int i = 0; i < frames; ++i) { 51 | traceln(strs[i]); 52 | } 53 | free(strs); 54 | #endif 55 | } 56 | 57 | void initialize() { 58 | // signal(SIGABRT, signalHandler); 59 | // signal(SIGFPE, signalHandler); 60 | #ifdef JUCE_WINDOWS 61 | // signal(SIGSEGV, signalHandler); 62 | #else 63 | signal(SIGPIPE, SIG_IGN); 64 | #endif 65 | } 66 | 67 | } // namespace Signals 68 | } // namespace e47 69 | -------------------------------------------------------------------------------- /Common/Source/Signals.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef Signals_hpp 9 | #define Signals_hpp 10 | 11 | namespace e47 { 12 | namespace Signals { 13 | 14 | void initialize(); 15 | 16 | } // namespace Signals 17 | } // namespace e47 18 | 19 | #endif /* Signals_hpp */ 20 | -------------------------------------------------------------------------------- /Common/Source/Tracer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #include "Tracer.hpp" 9 | #include "Utils.hpp" 10 | #include "SharedInstance.hpp" 11 | #include "Defaults.hpp" 12 | #include "MemoryFile.hpp" 13 | 14 | namespace e47 { 15 | namespace Tracer { 16 | 17 | #define NUM_OF_TRACE_RECORDS 25000 // ~5MB 18 | 19 | std::atomic_bool l_tracerEnabled{false}; 20 | std::atomic_uint64_t l_index{0}; 21 | MemoryFile l_file; 22 | bool l_deleteFile = false; 23 | 24 | struct Inst : SharedInstance {}; 25 | 26 | setLogTagStatic("tracer"); 27 | 28 | #define TRACE_STRCPY(dst, src) \ 29 | do { \ 30 | int len = jmin((int)sizeof(dst) - 1, src.length()); \ 31 | strncpy(dst, src.getCharPointer(), (size_t)len); \ 32 | dst[len] = 0; \ 33 | } while (0) 34 | 35 | Scope::Scope(const LogTag* t, const String& f, int l, const String& ff) { 36 | if (l_tracerEnabled) { 37 | enabled = true; 38 | tagId = t->getTagId(); 39 | tagName = t->getLogTagName(); 40 | tagExtra = t->getLogTagExtra(); 41 | file = f; 42 | line = l; 43 | func = ff; 44 | start = Time::getHighResolutionTicks(); 45 | traceMessage(tagId, tagName, tagExtra, file, line, func, ">> enter"); 46 | } 47 | } 48 | 49 | Scope::Scope(const LogTagDelegate* t, const String& f, int l, const String& ff) 50 | : Scope(t->getLogTagSource(), f, l, ff) {} 51 | 52 | void initialize(const String& appName, const String& filePrefix, bool linkLatest) { 53 | Inst::initialize([&](auto) { 54 | auto f = File(Defaults::getLogFileName(appName, filePrefix, ".trace")).getNonexistentSibling(); 55 | l_file = MemoryFile(getLogTagSource(), f, NUM_OF_TRACE_RECORDS * sizeof(TraceRecord)); 56 | // create dir if needed 57 | auto d = f.getParentDirectory(); 58 | if (!d.exists()) { 59 | d.createDirectory(); 60 | } 61 | if (linkLatest) { 62 | // create a latest link 63 | auto latestLnk = File(Defaults::getLogFileName(appName, filePrefix, ".trace", true)); 64 | latestLnk.deleteFile(); 65 | f.createSymbolicLink(latestLnk, true); 66 | } 67 | // cleanup 68 | cleanDirectory(d.getFullPathName(), filePrefix, ".trace", 5); 69 | }); 70 | } 71 | 72 | void cleanup() { 73 | Inst::cleanup([](auto) { 74 | l_tracerEnabled = false; 75 | Thread::sleep(50); 76 | l_file.close(); 77 | if (l_deleteFile) { 78 | l_file.deleteFile(); 79 | } 80 | }); 81 | } 82 | 83 | void deleteFileAtFinish() { l_deleteFile = true; } 84 | 85 | File getTraceFile() { return l_file.getFile(); } 86 | 87 | void setEnabled(bool b) { 88 | if (b && !l_file.isOpen()) { 89 | l_file.open(true); 90 | } 91 | l_tracerEnabled = b; 92 | } 93 | 94 | bool isEnabled() { return l_tracerEnabled; } 95 | 96 | TraceRecord* getRecord() { 97 | if (l_file.isOpen()) { 98 | auto offset = (l_index.fetch_add(1) % NUM_OF_TRACE_RECORDS) * sizeof(TraceRecord); 99 | return reinterpret_cast(l_file.data() + offset); 100 | } 101 | return nullptr; 102 | } 103 | 104 | void traceMessage(const LogTag* tag, const String& file, int line, const String& func, const String& msg) { 105 | if (l_tracerEnabled) { 106 | traceMessage(tag->getTagId(), tag->getLogTagName(), tag->getLogTagExtra(), file, line, func, msg); 107 | } 108 | } 109 | 110 | void traceMessage(uint64 tagId, const String& tagName, const String& tagExtra, const String& file, int line, 111 | const String& func, const String& msg) { 112 | if (l_tracerEnabled) { 113 | auto thread = Thread::getCurrentThread(); 114 | String threadTag = "unknown"; 115 | if (nullptr != thread) { 116 | threadTag = thread->getThreadName(); 117 | } else { 118 | auto mm = MessageManager::getInstanceWithoutCreating(); 119 | if (nullptr != mm && mm->isThisTheMessageThread()) { 120 | threadTag = "message_thread"; 121 | } 122 | } 123 | auto* rec = getRecord(); 124 | if (nullptr != rec) { 125 | rec->time = Time::getMillisecondCounterHiRes(); 126 | rec->threadId = (uint64)Thread::getCurrentThreadId(); 127 | rec->tagId = tagId; 128 | rec->line = line; 129 | TRACE_STRCPY(rec->threadName, threadTag); 130 | TRACE_STRCPY(rec->tagName, tagName); 131 | TRACE_STRCPY(rec->tagExtra, tagExtra); 132 | TRACE_STRCPY(rec->file, File::createFileWithoutCheckingPath(file).getFileName()); 133 | TRACE_STRCPY(rec->func, func); 134 | TRACE_STRCPY(rec->msg, msg); 135 | } else { 136 | l_tracerEnabled = false; 137 | logln("failed to get trace record"); 138 | } 139 | } 140 | } 141 | 142 | } // namespace Tracer 143 | } // namespace e47 144 | -------------------------------------------------------------------------------- /Common/Source/Tracer.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef ThreadTracer_hpp 9 | #define ThreadTracer_hpp 10 | 11 | #include 12 | 13 | namespace e47 { 14 | 15 | class LogTag; 16 | class LogTagDelegate; 17 | 18 | namespace Tracer { 19 | 20 | void traceMessage(const LogTag* tag, const String& file, int line, const String& func, const String& msg); 21 | void traceMessage(uint64 tagId, const String& tagName, const String& tagExtra, const String& file, int line, 22 | const String& func, const String& msg); 23 | 24 | void initialize(const String& appName, const String& filePrefix, bool linkLatest = true); 25 | void cleanup(); 26 | void deleteFileAtFinish(); 27 | File getTraceFile(); 28 | 29 | void setEnabled(bool b); 30 | bool isEnabled(); 31 | 32 | struct Scope { 33 | bool enabled = false; 34 | uint64 tagId; 35 | String tagName; 36 | String tagExtra; 37 | String file; 38 | int line; 39 | String func; 40 | int64 start; 41 | 42 | Scope(const LogTag* t, const String& f, int l, const String& ff); 43 | Scope(const LogTagDelegate* t, const String& f, int l, const String& ff); 44 | ~Scope() { 45 | if (enabled) { 46 | auto end = Time::getHighResolutionTicks(); 47 | double ms = Time::highResolutionTicksToSeconds(end - start) * 1000; 48 | traceMessage(tagId, tagName, tagExtra, file, line, func, "<< exit (took " + String(ms) + "ms)"); 49 | } 50 | } 51 | }; 52 | 53 | struct TraceRecord { 54 | double time; 55 | uint64 threadId; 56 | char threadName[16]; 57 | uint64 tagId; 58 | char tagName[16]; 59 | char tagExtra[32]; 60 | char file[32]; 61 | int line; 62 | char func[32]; 63 | char msg[64]; 64 | }; 65 | 66 | } // namespace Tracer 67 | } // namespace e47 68 | 69 | #endif /* ThreadTracer_hpp */ 70 | -------------------------------------------------------------------------------- /Common/Source/Utils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #include 9 | 10 | #include "Utils.hpp" 11 | 12 | #ifdef JUCE_WINDOWS 13 | #include 14 | #endif 15 | 16 | namespace e47 { 17 | 18 | String getLastErrorStr() { 19 | #ifdef JUCE_WINDOWS 20 | DWORD err = GetLastError(); 21 | LPSTR lpMsgBuf; 22 | FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, 23 | err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL); 24 | return String(lpMsgBuf); 25 | #else 26 | std::vector buf(512); 27 | ignoreUnused(strerror_r(errno, buf.data(), buf.size())); 28 | return String(buf.data()); 29 | #endif 30 | } 31 | 32 | void windowToFront(Component* c) { 33 | setLogTagStatic("utils"); 34 | traceScope(); 35 | if (nullptr != c && !c->isAlwaysOnTop()) { 36 | #ifndef JUCE_LINUX 37 | c->setAlwaysOnTop(true); 38 | #endif 39 | c->toFront(true); 40 | #ifndef JUCE_LINUX 41 | Timer::callAfterDelay(100, [c] { c->setAlwaysOnTop(false); }); 42 | #endif 43 | } 44 | } 45 | 46 | } // namespace e47 47 | -------------------------------------------------------------------------------- /Common/Source/Version.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef Version_hpp 9 | #define Version_hpp 10 | 11 | #define AUDIOGRIDDER_VERSION "dev-build" 12 | #define AUDIOGRIDDER_VERSIONW L"dev-build" 13 | #define AUDIOGRIDDER_VERSION_NUM "1.2.0" 14 | #define AUDIOGRIDDER_VERSION_NUMW L"1.2.0" 15 | #define AUDIOGRIDDER_BUILD_DATE "" 16 | 17 | #endif /* Version_hpp */ 18 | -------------------------------------------------------------------------------- /Common/Source/WindowHelper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #include "WindowHelper.hpp" 9 | 10 | #if !JUCE_MAC 11 | 12 | #ifdef JUCE_WINDOWS 13 | #include 14 | #endif 15 | 16 | namespace e47 { 17 | namespace WindowHelper { 18 | 19 | juce::Rectangle getWindowScreenBounds(juce::Component* c) { 20 | #ifdef JUCE_WINDOWS 21 | if (HWND hwnd = (HWND)c->getWindowHandle()) { 22 | RECT rect; 23 | if (HWND hwndA = GetAncestor(hwnd, GA_ROOT)) { 24 | if (GetWindowRect(hwndA, &rect)) { 25 | // scaling 26 | HDC hDC = GetDC(0); 27 | float dpi = (GetDeviceCaps(hDC, LOGPIXELSX) + GetDeviceCaps(hDC, LOGPIXELSY)) / 2.0f; 28 | ReleaseDC(0, hDC); 29 | float sf = 96 / dpi; 30 | long left = lroundf(sf * rect.left); 31 | long top = lroundf(sf * rect.top); 32 | long right = lroundf(sf * rect.right); 33 | long bottom = lroundf(sf * rect.bottom); 34 | return {left, top, right - left, bottom - top}; 35 | } 36 | } 37 | } 38 | #else 39 | ignoreUnused(c); 40 | #endif 41 | return {}; 42 | } 43 | 44 | } // namespace WindowHelper 45 | } // namespace e47 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /Common/Source/WindowHelper.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef _WINDOWHELPER_HPP_ 9 | #define _WINDOWHELPER_HPP_ 10 | 11 | #include 12 | 13 | namespace e47 { 14 | namespace WindowHelper { 15 | 16 | juce::Rectangle getWindowScreenBounds(juce::Component* c); 17 | 18 | } 19 | } // namespace e47 20 | 21 | #endif // _WINDOWHELPER_HPP_ 22 | -------------------------------------------------------------------------------- /Common/Source/WindowHelper.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifdef __APPLE__ 9 | 10 | #include 11 | 12 | #include "WindowHelper.hpp" 13 | 14 | #include 15 | 16 | namespace e47 { 17 | namespace WindowHelper { 18 | 19 | juce::Rectangle getWindowScreenBounds(juce::Component* c) { 20 | if (auto* view = (NSView*)c->getWindowHandle()) { 21 | if (auto* window = [view window]) { 22 | auto windowRect = flippedScreenRect([window frame]); 23 | return juce::Rectangle(windowRect.origin.x, windowRect.origin.y, windowRect.size.width, 24 | windowRect.size.height) 25 | .toNearestInt(); 26 | } 27 | } 28 | return {}; 29 | } 30 | 31 | } 32 | } // namespace e47 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /Common/Source/WindowPositions.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #include "WindowPositions.hpp" 9 | #include "Defaults.hpp" 10 | 11 | #if AG_SERVER 12 | #include "App.hpp" 13 | #include "Server.hpp" 14 | #endif 15 | 16 | namespace e47 { 17 | 18 | WindowPositions::WindowPositions() : LogTag("winpos") { 19 | #if AG_SERVER 20 | if (auto srv = getApp()->getServer()) { 21 | m_file = MemoryFile( 22 | this, Defaults::getConfigFileName(Defaults::WindowPositionsServer, {{"id", String(srv->getId())}}), 23 | sizeof(WindowPositions::Positions)); 24 | } 25 | #else 26 | m_file = MemoryFile(this, Defaults::getConfigFileName(Defaults::WindowPositionsPlugin), 27 | sizeof(WindowPositions::Positions)); 28 | #endif 29 | m_file.open(); 30 | if (m_file.isOpen()) { 31 | m_positions = reinterpret_cast(m_file.data()); 32 | logln("opened window positions file " << m_file.getFile().getFullPathName()); 33 | } 34 | } 35 | 36 | WindowPositions::Position WindowPositions::getPosition(WindowPositions::PositionType t, const Position& def) const { 37 | Position ret; 38 | if (nullptr != m_positions) { 39 | switch (t) { 40 | case ServerSettings: 41 | ret = m_positions->ServerSettings; 42 | break; 43 | case ServerStats: 44 | ret = m_positions->ServerStats; 45 | break; 46 | case ServerPlugins: 47 | ret = m_positions->ServerPlugins; 48 | break; 49 | case PluginMonFx: 50 | ret = m_positions->PluginMonFx; 51 | break; 52 | case PluginMonInst: 53 | ret = m_positions->PluginMonInst; 54 | break; 55 | case PluginMonMidi: 56 | ret = m_positions->PluginMonMidi; 57 | break; 58 | case PluginStatsFx: 59 | ret = m_positions->PluginStatsFx; 60 | break; 61 | case PluginStatsInst: 62 | ret = m_positions->PluginStatsInst; 63 | break; 64 | case PluginStatsMidi: 65 | ret = m_positions->PluginStatsMidi; 66 | break; 67 | } 68 | } 69 | if (!ret.isEmpty()) { 70 | ret.setHeight(def.getHeight()); 71 | ret.setWidth(def.getWidth()); 72 | } 73 | return ret.isEmpty() ? def : ret; 74 | } 75 | 76 | void WindowPositions::setPosition(WindowPositions::PositionType t, WindowPositions::Position p) { 77 | if (nullptr != m_positions) { 78 | switch (t) { 79 | case ServerSettings: 80 | m_positions->ServerSettings = p; 81 | break; 82 | case ServerStats: 83 | m_positions->ServerStats = p; 84 | break; 85 | case ServerPlugins: 86 | m_positions->ServerPlugins = p; 87 | break; 88 | case PluginMonFx: 89 | m_positions->PluginMonFx = p; 90 | break; 91 | case PluginMonInst: 92 | m_positions->PluginMonInst = p; 93 | break; 94 | case PluginMonMidi: 95 | m_positions->PluginMonMidi = p; 96 | break; 97 | case PluginStatsFx: 98 | m_positions->PluginStatsFx = p; 99 | break; 100 | case PluginStatsInst: 101 | m_positions->PluginStatsInst = p; 102 | break; 103 | case PluginStatsMidi: 104 | m_positions->PluginStatsMidi = p; 105 | break; 106 | } 107 | } 108 | } 109 | 110 | WindowPositions::Position WindowPositions::get(WindowPositions::PositionType t, const WindowPositions::Position& def) { 111 | auto inst = getInstance(); 112 | if (nullptr != inst) { 113 | return inst->getPosition(t, def); 114 | } 115 | return def; 116 | } 117 | 118 | void WindowPositions::set(WindowPositions::PositionType t, WindowPositions::Position p) { 119 | auto inst = getInstance(); 120 | if (nullptr != inst) { 121 | inst->setPosition(t, p); 122 | } 123 | } 124 | 125 | } // namespace e47 126 | -------------------------------------------------------------------------------- /Common/Source/WindowPositions.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef __WINDOWPOSITIONS_H_ 9 | #define __WINDOWPOSITIONS_H_ 10 | 11 | #include 12 | 13 | #include "Utils.hpp" 14 | #include "SharedInstance.hpp" 15 | #include "MemoryFile.hpp" 16 | 17 | namespace e47 { 18 | 19 | class WindowPositions : public LogTag, public SharedInstance { 20 | public: 21 | WindowPositions(); 22 | 23 | using Position = juce::Rectangle; 24 | 25 | struct Positions { 26 | Position ServerSettings; 27 | Position ServerStats; 28 | Position ServerPlugins; 29 | Position PluginMonFx; 30 | Position PluginMonInst; 31 | Position PluginMonMidi; 32 | Position PluginStatsFx; 33 | Position PluginStatsInst; 34 | Position PluginStatsMidi; 35 | }; 36 | 37 | enum PositionType { 38 | ServerSettings, 39 | ServerStats, 40 | ServerPlugins, 41 | PluginMonFx, 42 | PluginMonInst, 43 | PluginMonMidi, 44 | PluginStatsFx, 45 | PluginStatsInst, 46 | PluginStatsMidi 47 | }; 48 | 49 | Position getPosition(PositionType t, const Position& def) const; 50 | void setPosition(PositionType t, Position p); 51 | 52 | static Position get(PositionType t, const Position& def); 53 | static void set(PositionType t, Position p); 54 | 55 | private: 56 | MemoryFile m_file; 57 | Positions* m_positions = nullptr; 58 | }; 59 | 60 | } // namespace e47 61 | 62 | #endif // __WINDOWPOSITIONS_H_ 63 | -------------------------------------------------------------------------------- /Common/Source/mDNSConnector.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | * 7 | * Based on https://github.com/mjansson/mdns 8 | */ 9 | 10 | #ifndef mDNSConnector_hpp 11 | #define mDNSConnector_hpp 12 | 13 | #include 14 | #include "Utils.hpp" 15 | #include "mDNS.hpp" 16 | 17 | #define MDNS_TO_JUCE_STRING(s) String(s.str, s.length) 18 | 19 | namespace e47 { 20 | 21 | class mDNSConnector : public LogTagDelegate { 22 | public: 23 | mDNSConnector(LogTag* tag) : LogTagDelegate(tag) { m_buffer = malloc(m_bufferSize); } 24 | ~mDNSConnector() { free(m_buffer); } 25 | 26 | int openClientSockets(int maxSockets, int port); 27 | int openServiceSockets(int maxSockets); 28 | 29 | // server side, wait for requests 30 | void readQueries(mdns_record_callback_fn callback, void* userData = nullptr); 31 | 32 | // client side, send/read queries 33 | void sendQuery(const String& service); 34 | void readResponses(mdns_record_callback_fn callback, void* userData = nullptr); 35 | 36 | void close(); 37 | 38 | static String getHostName(); 39 | static String ipv4ToString(const struct sockaddr_in* addr, size_t addrlen, bool noPort = false); 40 | static String ipv6ToString(const struct sockaddr_in6* addr, size_t addrlen, bool noPort = false); 41 | static String ipToString(const struct sockaddr* addr, size_t addrlen, bool noPort = false); 42 | 43 | // const Array getSockets() const { return m_sockets; } 44 | uint32_t getAddr4() const { return m_addr4; } 45 | const uint8_t* getAddr6() const { return m_addr6; } 46 | 47 | private: 48 | Array m_sockets; 49 | bool m_hasIPv4; 50 | bool m_hasIPv6; 51 | uint32_t m_addr4; 52 | uint8_t m_addr6[16]; 53 | void* m_buffer; 54 | size_t m_bufferSize = 2048; 55 | 56 | enum ReadType { SERVICE, QUERY }; 57 | void readRecords(ReadType type, mdns_record_callback_fn callback, void* userData = nullptr); 58 | }; 59 | 60 | } // namespace e47 61 | 62 | #endif /* mDNSConnector_hpp */ 63 | -------------------------------------------------------------------------------- /DEVELOP.md: -------------------------------------------------------------------------------- 1 | # Developer Guide 2 | 3 | Help is very much welcome and so are pull requests! 4 | 5 | ## Building 6 | 7 | Here is how you can build AudioGridder. Note that building the server is only 8 | supported on macOS and Windows. 9 | 10 | ``` 11 | # Checkout the code 12 | git clone --recurse-submodules https://github.com/apohl79/audiogridder.git 13 | cd audiogridder 14 | 15 | # Checkout the dependencies 16 | git clone https://github.com/apohl79/audiogridder-deps.git 17 | 18 | # Configure and compile 19 | python3 build.py conf --disable-signing 20 | python3 build.py build 21 | ``` 22 | 23 | ### Building with Visual Studio Code on Linux 24 | - Install juce dependencies, build essentials along with g++ 25 | - Download and install VSCode 26 | - Install Microsoft C/C++ extension pack and CMake tools along with ninja build 27 | - Open AudioGridder folder and import. All configuration will be set automatically. 28 | - Select build type, target and build 29 | 30 | ## Coding conventions 31 | 32 | Please follow the existing coding style (*m_* notation for class member 33 | variables, cammel case for class, member and variable names, curly braces for 34 | single statement control blocks, etc). Also before submitting a pull request, 35 | make sure you are running the code formater. 36 | 37 | ``` 38 | python3 build.py format 39 | ``` 40 | 41 | Thanks for your contributions! 42 | -------------------------------------------------------------------------------- /Plugin/Source/GenericEditor.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | 12 | #include "PluginProcessor.hpp" 13 | #include "Tracer.hpp" 14 | 15 | namespace e47 { 16 | 17 | class GenericEditor : public Component, public LogTag { 18 | public: 19 | GenericEditor(PluginProcessor& processor); 20 | ~GenericEditor() override; 21 | 22 | void paint(juce::Graphics&) override; 23 | void resized() override; 24 | void updateParamValue(int paramIdx); 25 | 26 | private: 27 | PluginProcessor& m_processor; 28 | 29 | Array> m_labels; 30 | Array> m_components; 31 | struct OnClick : MouseListener, LogTagDelegate { 32 | std::function func; 33 | 34 | OnClick(LogTag* tag, std::function f) : LogTagDelegate(tag), func(f) {} 35 | 36 | void mouseUp(const MouseEvent& ev) override { 37 | traceScope(); 38 | if (func && ev.mouseDownTime < ev.eventTime) { 39 | func(); 40 | } 41 | } 42 | }; 43 | Array> m_clickHandlers; 44 | 45 | struct GestureTracker : MouseListener, LogTagDelegate { 46 | int idx, channel; 47 | bool isTracking = false; 48 | PluginProcessor& processor; 49 | 50 | GestureTracker(GenericEditor* e, int i, int c) 51 | : LogTagDelegate(e), idx(i), channel(c), processor(e->m_processor) {} 52 | 53 | void mouseDown(const MouseEvent&) override { 54 | traceScope(); 55 | isTracking = true; 56 | processor.updateParameterGestureTracking(processor.getActivePlugin(), channel, idx, true); 57 | } 58 | 59 | void mouseUp(const MouseEvent&) override { 60 | traceScope(); 61 | isTracking = false; 62 | processor.updateParameterGestureTracking(processor.getActivePlugin(), channel, idx, false); 63 | } 64 | }; 65 | Array> m_gestureTrackers; 66 | 67 | Client::Parameter& getParameter(int paramIdx); 68 | Component* getComponent(int paramIdx); 69 | 70 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(GenericEditor) 71 | }; 72 | 73 | } // namespace e47 74 | -------------------------------------------------------------------------------- /Plugin/Source/ImageReader.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | * 7 | * Based on https://github.com/mjansson/mdns 8 | */ 9 | 10 | #ifndef ImageReader_hpp 11 | #define ImageReader_hpp 12 | 13 | #include 14 | #include "Utils.hpp" 15 | #include "ImageDiff.hpp" 16 | 17 | JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE("-Wsign-conversion", "-Wconversion") 18 | JUCE_BEGIN_IGNORE_WARNINGS_MSVC(4244) 19 | extern "C" { 20 | #include "libavcodec/avcodec.h" 21 | #include "libavdevice/avdevice.h" 22 | #include "libavutil/imgutils.h" 23 | #include "libswscale/swscale.h" 24 | } 25 | JUCE_END_IGNORE_WARNINGS_GCC_LIKE 26 | JUCE_END_IGNORE_WARNINGS_MSVC 27 | 28 | #ifdef JUCE_WINDOWS 29 | #pragma comment(lib, "strmiids.lib") 30 | #pragma comment(lib, "mfplat.lib") 31 | #pragma comment(lib, "secur32.lib") 32 | #pragma comment(lib, "bcrypt.lib") 33 | #pragma comment(lib, "mfuuid.lib") 34 | #endif 35 | 36 | namespace e47 { 37 | 38 | class ImageReader : public LogTagDelegate { 39 | public: 40 | ImageReader(); 41 | 42 | std::shared_ptr read(const char* data, size_t size, int width, int height, int widthPadded, int heightPadded, 43 | double scale); 44 | 45 | private: 46 | std::shared_ptr m_image; 47 | 48 | int m_width = 0; 49 | int m_height = 0; 50 | int m_widthPadded = 0; 51 | int m_heightPadded = 0; 52 | double m_scale = 1; 53 | const AVCodec* m_inputCodec = nullptr; 54 | AVCodecContext* m_inputCodecCtx = nullptr; 55 | AVFrame* m_inputFrame = nullptr; 56 | AVFrame* m_outputFrame = nullptr; 57 | uint8_t* m_outputFrameBuf = nullptr; 58 | AVPacket* m_inputPacket = nullptr; 59 | SwsContext* m_swsCtx = nullptr; 60 | 61 | bool initCodec(); 62 | void closeCodec(); 63 | }; 64 | 65 | } // namespace e47 66 | 67 | #endif /* ImageReader_hpp */ 68 | -------------------------------------------------------------------------------- /Plugin/Source/Images.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef Images_hpp 9 | #define Images_hpp 10 | 11 | #include 12 | 13 | class Images { 14 | public: 15 | static const char* server_png; 16 | static const int server_pngSize; 17 | static const char* settings_png; 18 | static const int settings_pngSize; 19 | static const char* logo_png; 20 | static const int logo_pngSize; 21 | static const char* cpu_png; 22 | static const int cpu_pngSize; 23 | static const char* pluginlogo_png; 24 | static const int pluginlogo_pngSize; 25 | }; 26 | 27 | #endif // Images_hpp 28 | -------------------------------------------------------------------------------- /Plugin/Source/NewServerWindow.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #include "NewServerWindow.hpp" 9 | 10 | #include 11 | 12 | namespace e47 { 13 | 14 | NewServerWindow::NewServerWindow(float x, float y) : TopLevelWindow("New Server", true) { 15 | setBounds((int)lroundf(x), (int)lroundf(y), 196, 70); 16 | 17 | addChildAndSetID(&m_server, "server"); 18 | m_server.setBounds(5, 5, 188, 25); 19 | 20 | addChildAndSetID(&m_cancel, "cancel"); 21 | m_cancel.setBounds(5, 35, 90, 25); 22 | m_cancel.setButtonText("Cancel"); 23 | m_cancel.addListener(this); 24 | 25 | addChildAndSetID(&m_ok, "ok"); 26 | m_ok.setBounds(100, 35, 90, 25); 27 | m_ok.setButtonText("Add"); 28 | m_ok.addListener(this); 29 | 30 | setVisible(true); 31 | } 32 | 33 | NewServerWindow::~NewServerWindow() {} 34 | 35 | void NewServerWindow::paint(Graphics& g) { 36 | g.fillAll(getLookAndFeel().findColour(ResizableWindow::backgroundColourId)); // clear the background 37 | } 38 | 39 | void NewServerWindow::buttonClicked(Button* button) { 40 | if (button->getButtonText() == "Add" && m_onOk != nullptr) { 41 | m_onOk(m_server.getTextValue().toString()); 42 | } 43 | delete this; 44 | } 45 | 46 | void NewServerWindow::activeWindowStatusChanged() { 47 | TopLevelWindow::activeWindowStatusChanged(); 48 | if (!isActiveWindow()) { 49 | delete this; 50 | } 51 | } 52 | 53 | } // namespace e47 54 | -------------------------------------------------------------------------------- /Plugin/Source/NewServerWindow.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef NewServerWindow_hpp 9 | 10 | #include 11 | 12 | namespace e47 { 13 | 14 | class NewServerWindow : public TopLevelWindow, public TextButton::Listener { 15 | public: 16 | NewServerWindow(float x, float y); 17 | ~NewServerWindow() override; 18 | 19 | void paint(Graphics&) override; 20 | 21 | virtual void buttonClicked(Button* button) override; 22 | 23 | using OkFuction = std::function; 24 | void onOk(OkFuction f) { m_onOk = f; } 25 | 26 | void activeWindowStatusChanged() override; 27 | 28 | private: 29 | TextEditor m_server; 30 | TextButton m_ok; 31 | TextButton m_cancel; 32 | 33 | OkFuction m_onOk; 34 | 35 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NewServerWindow) 36 | }; 37 | 38 | } // namespace e47 39 | 40 | #endif // NewServerWindow_hpp 41 | -------------------------------------------------------------------------------- /Plugin/Source/PluginButton.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef PluginButton_hpp 9 | #define PluginButton_hpp 10 | 11 | #include 12 | 13 | namespace e47 { 14 | 15 | class PluginButton : public TextButton { 16 | public: 17 | PluginButton(const String& id, const String& name, bool extraButtons = true); 18 | virtual ~PluginButton() override {} 19 | 20 | enum AreaType { MAIN, BYPASS, MOVE_DOWN, MOVE_UP, DELETE }; 21 | 22 | class Listener { 23 | public: 24 | virtual ~Listener() {} 25 | virtual void buttonClicked(Button* button, const ModifierKeys& modifiers, PluginButton::AreaType area) = 0; 26 | }; 27 | 28 | void setOnClickWithModListener(Listener* p) { m_listener = p; } 29 | void setActive(bool b) { m_active = b; } 30 | 31 | const String& getPluginId() const { return m_id; } 32 | 33 | void mouseUp(const MouseEvent& event) override; 34 | void mouseMove(const MouseEvent& event) override; 35 | 36 | AreaType getAreaType() const; 37 | 38 | void setEnabled(bool b) { 39 | m_enabled = b; 40 | repaint(); 41 | } 42 | bool isEnabled() const { return m_enabled; } 43 | 44 | protected: 45 | void paintButton(Graphics& g, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) override; 46 | void clicked(const ModifierKeys& modifiers) override; 47 | void drawText(Graphics& g, int left, int right); 48 | 49 | // Avoid hidden overload warning 50 | using Button::clicked; 51 | 52 | private: 53 | Listener* m_listener = nullptr; 54 | bool m_active = false; 55 | bool m_enabled = true; 56 | String m_id; 57 | bool m_withExtraButtons = true; 58 | Rectangle m_bypassArea, m_moveUpArea, m_moveDownArea, m_deleteArea; 59 | Point m_lastMousePosition; 60 | 61 | inline bool isWithinArea(const Rectangle& area, const Point& point) const { 62 | return point.getX() >= area.getX() && point.getX() <= area.getRight(); 63 | } 64 | }; 65 | 66 | } // namespace e47 67 | 68 | #endif /* PluginButton_hpp */ 69 | -------------------------------------------------------------------------------- /Plugin/Source/StatisticsWindow.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef StatisticsWindow_hpp 9 | #define StatisticsWindow_hpp 10 | 11 | #include 12 | 13 | #include "Utils.hpp" 14 | #include "SharedInstance.hpp" 15 | 16 | namespace e47 { 17 | 18 | class PluginEditor; 19 | 20 | class StatisticsWindow : public DocumentWindow, public LogTag { 21 | public: 22 | StatisticsWindow(); 23 | ~StatisticsWindow() override; 24 | 25 | void closeButtonPressed() override; 26 | 27 | class HirozontalLine : public Component { 28 | public: 29 | HirozontalLine(juce::Rectangle bounds) { setBounds(bounds); } 30 | void paint(Graphics& g) override; 31 | }; 32 | 33 | static void initialize(); 34 | static void cleanup(); 35 | static void show(); 36 | static void hide(); 37 | 38 | private: 39 | std::vector> m_components; 40 | Label m_totalClients, m_audioRPS, m_audioPTavg, m_audioPTmin, m_audioPTmax, m_audioPT95th, m_audioBytesOut, 41 | m_audioBytesIn; 42 | 43 | static std::unique_ptr m_inst; 44 | 45 | class Updater : public Thread, public LogTagDelegate { 46 | public: 47 | Updater(LogTag* tag) : Thread("StatsUpdater"), LogTagDelegate(tag) { 48 | traceScope(); 49 | initAsyncFunctors(); 50 | } 51 | 52 | ~Updater() override { 53 | traceScope(); 54 | stopAsyncFunctors(); 55 | } 56 | 57 | void set(std::function fn) { m_fn = fn; } 58 | 59 | void run() override { 60 | traceScope(); 61 | while (!threadShouldExit()) { 62 | runOnMsgThreadAsync([this] { m_fn(); }); 63 | // Relax 64 | sleepExitAware(1000); 65 | } 66 | } 67 | 68 | private: 69 | std::function m_fn; 70 | 71 | ENABLE_ASYNC_FUNCTORS(); 72 | }; 73 | Updater m_updater; 74 | 75 | void addLabel(const String& txt, juce::Rectangle bounds); 76 | 77 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(StatisticsWindow) 78 | }; 79 | 80 | } // namespace e47 81 | 82 | #endif /* StatisticsWindow_hpp */ 83 | -------------------------------------------------------------------------------- /PluginTray/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | project(AUDIOGRIDDER_PLUGIN_TRAY VERSION 1.0.0) 4 | 5 | set(AG_ICON_BIG "${CMAKE_CURRENT_SOURCE_DIR}/Resources/icon.png") 6 | set(AG_ICON_SMALL "${CMAKE_CURRENT_SOURCE_DIR}/Resources/icon.png") 7 | if(CMAKE_SYSTEM_NAME STREQUAL "Windows") 8 | set(AG_ICON_BIG "${CMAKE_CURRENT_SOURCE_DIR}/Resources/icon64.png") 9 | set(AG_ICON_SMALL "${CMAKE_CURRENT_SOURCE_DIR}/Resources/icon16.png") 10 | endif() 11 | 12 | juce_add_gui_app(AudioGridderPluginTray 13 | VERSION "${AG_VERSION}" 14 | PRODUCT_NAME "AudioGridderPluginTray" 15 | COMPANY_NAME "e47" 16 | COMPANY_COPYRIGHT "2020 Andreas Pohl" 17 | COMPANY_WEBSITE "https://www.audiogridder.com" 18 | PLUGINHOST_AU TRUE 19 | ICON_BIG "${AG_ICON_BIG}" 20 | ICON_SMALL "${AG_ICON_SMALL}") 21 | 22 | juce_generate_juce_header(AudioGridderPluginTray) 23 | 24 | aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/Source AG_SOURCES_PLUGIN_TRAY) 25 | 26 | target_sources(AudioGridderPluginTray PRIVATE ${AG_SOURCES_PLUGIN_TRAY} ${AG_SOURCES_COMMON}) 27 | 28 | target_compile_definitions(AudioGridderPluginTray PRIVATE 29 | AG_PLUGIN_TRAY 30 | AG_SENTRY_ENABLED=${AG_SENTRY_ENABLED} 31 | AG_SENTRY_DSN="${AG_SENTRY_DSN}" 32 | AG_SENTRY_CRASHPAD_PATH="${AG_SENTRY_CRASHPAD_PATH}" 33 | JUCE_WEB_BROWSER=0 34 | JUCE_USE_CURL=0 35 | JUCE_MODAL_LOOPS_PERMITTED=1 36 | JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING 37 | JUCE_DISABLE_ASSERTIONS 38 | JUCE_APPLICATION_NAME_STRING="$" 39 | JUCE_APPLICATION_VERSION_STRING="$") 40 | 41 | target_compile_features(AudioGridderPluginTray PRIVATE cxx_std_14) 42 | 43 | juce_add_binary_data(AudioGridderPluginTrayData SOURCES 44 | Resources/icon.png 45 | Resources/icon16.png 46 | Resources/icon64.png) 47 | 48 | target_link_libraries(AudioGridderPluginTray PRIVATE 49 | AudioGridderPluginTrayData 50 | juce::juce_graphics 51 | juce::juce_gui_extra 52 | juce::juce_recommended_config_flags 53 | juce::juce_recommended_lto_flags 54 | juce::juce_recommended_warning_flags 55 | ${SENTRY_LIBRARIES}) 56 | 57 | if(CMAKE_SYSTEM_NAME STREQUAL "Linux") 58 | target_link_libraries(AudioGridderPluginTray PRIVATE X11 Xtst) 59 | endif() 60 | 61 | ag_strip(AudioGridderPluginTray Tray) 62 | 63 | if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") 64 | ag_bundle_add_crashpad(AudioGridderPluginTray) 65 | ag_bundle_sign(AudioGridderPluginTray) 66 | if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND AG_ENABLE_DEBUG_COPY_STEP) 67 | ag_bundle_copy(AudioGridderPluginTray /Applications/Debug) 68 | endif() 69 | endif() 70 | -------------------------------------------------------------------------------- /PluginTray/Resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apohl79/audiogridder/76dd9a006a86110b9245bc44f7a6c78f3d6a5c9f/PluginTray/Resources/icon.png -------------------------------------------------------------------------------- /PluginTray/Resources/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apohl79/audiogridder/76dd9a006a86110b9245bc44f7a6c78f3d6a5c9f/PluginTray/Resources/icon16.png -------------------------------------------------------------------------------- /PluginTray/Resources/icon64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apohl79/audiogridder/76dd9a006a86110b9245bc44f7a6c78f3d6a5c9f/PluginTray/Resources/icon64.png -------------------------------------------------------------------------------- /PluginTray/Source/Images.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef Images_hpp 9 | #define Images_hpp 10 | 11 | #include 12 | 13 | class Images { 14 | public: 15 | static const char* tray_png; 16 | static const int tray_pngSize; 17 | static const char* wintray_png; 18 | static const int wintray_pngSize; 19 | static const char* logo_png; 20 | static const int logo_pngSize; 21 | }; 22 | 23 | #endif // Images_hpp 24 | -------------------------------------------------------------------------------- /PluginTray/Source/PluginMonitor.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef __PLUGINMONITOR_H_ 9 | #define __PLUGINMONITOR_H_ 10 | 11 | #include 12 | 13 | #include "SharedInstance.hpp" 14 | #include "Utils.hpp" 15 | #include "Defaults.hpp" 16 | 17 | namespace e47 { 18 | 19 | class App; 20 | class PluginMonitor; 21 | 22 | class PluginMonitorWindow : public TopLevelWindow, public LogTagDelegate { 23 | public: 24 | PluginMonitorWindow(PluginMonitor* mon, App* app); 25 | ~PluginMonitorWindow() override; 26 | 27 | void paint(Graphics& g) override { 28 | g.fillAll(getLookAndFeel().findColour(ResizableWindow::backgroundColourId)); // clear the background 29 | } 30 | 31 | void mouseUp(const MouseEvent& event) override; 32 | 33 | void update(); 34 | 35 | private: 36 | class PluginMonitorComponent : public Component { 37 | public: 38 | void paint(Graphics& g) override { 39 | g.fillAll(getLookAndFeel().findColour(ResizableWindow::backgroundColourId)); // clear the background 40 | } 41 | friend class PluginMonitorWindow; 42 | }; 43 | 44 | class Channel : public Component { 45 | public: 46 | Channel(juce::Rectangle bounds, Colour col) : m_col(col) { 47 | setBounds(bounds); 48 | if (m_col.isTransparent()) { 49 | m_col = Colours::white.withAlpha(0.1f); 50 | } 51 | } 52 | void paint(Graphics& g) override; 53 | 54 | private: 55 | Colour m_col; 56 | }; 57 | 58 | class Status : public Component { 59 | public: 60 | Status() {} 61 | Status(juce::Rectangle bounds, bool connected, bool loadedPluginsOk) { 62 | setBounds(bounds); 63 | setColor(connected, loadedPluginsOk); 64 | } 65 | 66 | void setColor(bool connected, bool loadedPluginsOk) { 67 | m_col = connected 68 | ? loadedPluginsOk ? Colour(Defaults::PLUGIN_OK_COLOR) : Colour(Defaults::PLUGIN_NOTLOADED_COLOR) 69 | : Colour(Defaults::PLUGIN_NOTCONNECTED_COLOR); 70 | } 71 | 72 | void paint(Graphics& g) override; 73 | 74 | private: 75 | Colour m_col; 76 | }; 77 | 78 | class HirozontalLine : public Component { 79 | public: 80 | HirozontalLine(juce::Rectangle bounds, bool bold) : m_bold(bold) { setBounds(bounds); } 81 | void paint(Graphics& g) override; 82 | 83 | private: 84 | bool m_bold; 85 | }; 86 | 87 | PluginMonitor* m_mon; 88 | App* m_app; 89 | PluginMonitorComponent m_main; 90 | Viewport m_viewPort; 91 | ImageComponent m_logo; 92 | Label m_title; 93 | Status m_legendOk, m_legendNotConnected, m_legendNotLoaded; 94 | Label m_legendOkLbl, m_legendNotConnectedLbl, m_legendNotLoadedLbl; 95 | int m_totalWidth = 665; 96 | int m_totalHeight = 32; 97 | int m_legendWidth = 230; 98 | int m_channelColWidth = 20; 99 | int m_channelColIdx = 0; 100 | int m_channelNameWidth = 100; 101 | int m_channelNameIdx = 1; 102 | int m_bufferWidth = 30; 103 | int m_bufferAvgIdx = 5; 104 | int m_buffer95thIdx = 6; 105 | int m_readErrWidth = 50; 106 | int m_readErrIdx = 7; 107 | int m_perfProcessWidth = 65; 108 | int m_perfProcessIdx = 8; 109 | std::vector> m_components; 110 | TooltipWindow m_tooltipWindow; 111 | 112 | void addLabel(const String& txt, const String& tooltip, juce::Rectangle bounds, 113 | Justification just = Justification::topLeft, Colour col = Colours::white, float alpha = 0.6f); 114 | int getConditionalWidth(); 115 | void updatePosition(); 116 | }; 117 | 118 | class PluginMonitor : public LogTag, public Timer { 119 | public: 120 | bool showChannelName = true; 121 | bool showChannelColor = true; 122 | bool showBufferAvg = false; 123 | bool showBuffer95th = false; 124 | bool showReadErrors = true; 125 | bool showPerfProcess = false; 126 | bool windowAutoShow = true; 127 | bool windowAlwaysShow = false; 128 | bool windowActive = false; 129 | 130 | PluginMonitor(App* app) : LogTag("monitor"), m_app(app) { startTimer(100); } 131 | ~PluginMonitor() override {} 132 | 133 | void hideWindow() { 134 | windowAlwaysShow = false; 135 | m_hideCounter = 0; 136 | m_window.reset(); 137 | } 138 | 139 | void refresh() { m_needsUpdate = true; } 140 | 141 | void timerCallback() override; 142 | 143 | private: 144 | App* m_app; 145 | std::unique_ptr m_window; 146 | std::atomic_bool m_needsUpdate{false}; 147 | std::atomic_int m_hideCounter; 148 | 149 | void update(); 150 | }; 151 | 152 | } // namespace e47 153 | 154 | #endif // __PLUGINMONITOR_H_ 155 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AudioGridder 2 | 3 | AudioGridder is a network bridge for audio and MIDI that allows for offloading 4 | the DSP processing of audio plugins to remote computers running macOS or 5 | Windows. This can come in handy when mixing complex projects or running CPU 6 | intensive instruments for instance. AudioGridder comes with a plugin and a 7 | server and supports VST2, VST3 and AudioUnit plugin formats. Plugins can be 8 | hosted and accessed across the network: simply run the AudioGridder server on a 9 | remote machine and connect your DAW using the AudioGridder plugin. This allows 10 | you to add remote insert chains or instruments into your DAW's signal paths. The 11 | DSP code of the loaded remote plugins will be executed on the remote machine and 12 | the remote plugin UI's will be streamed over the wire. With AudioGridder you get 13 | an experience very close to hosting the plugins directly in your DAW but not 14 | using your local CPU. 15 | 16 | For more information and intstallation instructions, please visit 17 | [https://audiogridder.com](https://audiogridder.com). 18 | 19 |

20 | 21 |

22 | 23 | # Help / Bugs / Ideas 24 | 25 | Please report bugs, discuss ideas or ask questions in the 26 | [discussions](https://github.com/apohl79/audiogridder/discussions) area! 27 | Issues will only be created as a result of a discussion going forward. 28 | 29 | :point_right: **Note: Please do NOT create issues. Please create discussion threads.** 30 | 31 | :exclamation: You have to follow [the bug reporting guide](https://audiogridder.com/bug-reports/) when reporting bugs! 32 | 33 | # Installation 34 | 35 | Please find the latest installers in the 36 | [download](https://audiogridder.com/download/) section. 37 | 38 | ## macOS Homebrew 39 | 40 | On macOS you can install AudioGridder via homebrew: 41 | 42 | ``` 43 | brew install audiogridder-plugin 44 | ``` 45 | ``` 46 | brew install audiogridder-server 47 | ``` 48 | 49 | # Features 50 | 51 | - VST2 / VST3 / AAX / AudioUnit (macOS only) 52 | - Effect & Instrument plugins 53 | - Latency compensation 54 | - 32/64 bit float processing 55 | - Audio over network 56 | - Midi over network 57 | - Unlimited remote effect plugin chains 58 | - Streaming of plugin UIs 59 | - Local control of remote plugin UI's 60 | - Generic Plugin Parameter Editor 61 | - Automation 62 | 63 | # Compatibility 64 | 65 | - Server: macOS 10.7+, Windows 7+ 66 | - Plugin: macOS 10.7+, Windows 7+, Linux 67 | - AudioGridder is 64bit only 68 | - The server supports AudioUnit (macOS only) and VST2/VST3 plugins 69 | - The plugin is available as VST2/VST3, AAX and AudioUnit (macOS only) 70 | 71 | # Donations 72 | 73 | AudioGridder is free. If you use it, please consider supporting the project: 74 | 75 | [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=MF9TGYY8P8GG4) 76 | [![donorbox](https://d1iczxrky3cnb2.cloudfront.net/button-small-blue.png)](https://donorbox.org/audiogridder?default_interval=o) 77 | 78 | # Contributing 79 | 80 | Pull requests are welcome! Please follow the 81 | [development guide](https://github.com/apohl79/audiogridder/blob/master/DEVELOP.md) 82 | if you'd like to contribute to the project and to get started. 83 | -------------------------------------------------------------------------------- /Server/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | project(AUDIOGRIDDER_SERVER VERSION 1.0.0) 4 | 5 | set(AG_ICON_BIG "${CMAKE_CURRENT_SOURCE_DIR}/Resources/icon.png") 6 | set(AG_ICON_SMALL "${CMAKE_CURRENT_SOURCE_DIR}/Resources/icon.png") 7 | if(CMAKE_SYSTEM_NAME STREQUAL "Windows") 8 | set(AG_ICON_BIG "${CMAKE_CURRENT_SOURCE_DIR}/Resources/icon64.png") 9 | set(AG_ICON_SMALL "${CMAKE_CURRENT_SOURCE_DIR}/Resources/icon16.png") 10 | endif() 11 | 12 | juce_add_gui_app(AudioGridderServer 13 | VERSION "${AG_VERSION}" 14 | PRODUCT_NAME "AudioGridderServer" 15 | COMPANY_NAME "e47" 16 | COMPANY_COPYRIGHT "2020-2022 Andreas Pohl" 17 | COMPANY_WEBSITE "https://www.audiogridder.com" 18 | PLUGINHOST_AU TRUE 19 | ICON_BIG "${AG_ICON_BIG}" 20 | ICON_SMALL "${AG_ICON_SMALL}") 21 | 22 | juce_generate_juce_header(AudioGridderServer) 23 | 24 | aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/Source AG_SOURCES_SERVER) 25 | aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/Source/ServerSettings AG_SOURCES_SERVER_SETTINGS) 26 | 27 | if(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") 28 | list(REMOVE_ITEM AG_SOURCES_SERVER "${CMAKE_CURRENT_SOURCE_DIR}/Source/Screen.mm") 29 | endif() 30 | 31 | if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") 32 | list(REMOVE_ITEM AG_SOURCES_SERVER "${CMAKE_CURRENT_SOURCE_DIR}/Source/ScreenHelper_linux.cpp") 33 | endif() 34 | 35 | target_sources(AudioGridderServer PRIVATE ${AG_SOURCES_SERVER} ${AG_SOURCES_SERVER_SETTINGS} ${AG_SOURCES_COMMON}) 36 | 37 | set(AG_PLUGINHOST_VST 0) 38 | if(AG_SDKS_ROOT) 39 | set(AG_PLUGINHOST_VST 1) 40 | endif() 41 | 42 | target_compile_definitions(AudioGridderServer PRIVATE 43 | AG_SERVER 44 | AG_SENTRY_ENABLED=${AG_SENTRY_ENABLED} 45 | AG_SENTRY_DSN="${AG_SENTRY_DSN}" 46 | AG_SENTRY_CRASHPAD_PATH="${AG_SENTRY_CRASHPAD_PATH}" 47 | JUCE_PLUGINHOST_VST3=1 48 | JUCE_PLUGINHOST_VST=${AG_PLUGINHOST_VST} 49 | JUCE_PLUGINHOST_LV2=1 50 | JUCE_WEB_BROWSER=0 51 | JUCE_USE_CURL=0 52 | JUCE_MODAL_LOOPS_PERMITTED=1 53 | JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING 54 | JUCE_DISABLE_ASSERTIONS 55 | JUCE_APPLICATION_NAME_STRING="$" 56 | JUCE_APPLICATION_VERSION_STRING="$" 57 | JUCE_DISPLAY_SPLASH_SCREEN=0) 58 | 59 | target_compile_features(AudioGridderServer PRIVATE cxx_std_14) 60 | 61 | juce_add_binary_data(AudioGridderServerData SOURCES 62 | Resources/icon.png 63 | Resources/icon16.png 64 | Resources/icon64.png) 65 | 66 | target_link_libraries(AudioGridderServer PRIVATE 67 | AudioGridderServerData 68 | juce::juce_audio_basics 69 | juce::juce_audio_processors 70 | juce::juce_audio_formats 71 | juce::juce_graphics 72 | juce::juce_gui_extra 73 | juce::juce_recommended_config_flags 74 | juce::juce_recommended_lto_flags 75 | juce::juce_recommended_warning_flags 76 | ${FFMPEG_LIBRARIES} 77 | ${WEBP_LIBRARIES} 78 | ${SENTRY_LIBRARIES}) 79 | 80 | if(CMAKE_SYSTEM_NAME STREQUAL "Linux") 81 | target_link_libraries(AudioGridderServer PRIVATE X11 Xtst xcb xcb-shm xcb-xfixes xcb-shape) 82 | target_compile_definitions(AudioGridderServer PRIVATE JUCE_LINUX=1) 83 | endif() 84 | 85 | ag_strip(AudioGridderServer Server) 86 | 87 | if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") 88 | target_link_libraries(AudioGridderServer 89 | PRIVATE 90 | "-framework AVFoundation" 91 | "-framework CoreMedia" 92 | "-framework OpenGL" 93 | "-framework Security") 94 | if(AG_MACOS_TARGET STRGREATER_EQUAL 10.8) 95 | target_link_libraries(AudioGridderServer PRIVATE "-framework VideoToolbox") 96 | endif() 97 | ag_bundle_add_crashpad(AudioGridderServer) 98 | ag_bundle_sign(AudioGridderServer) 99 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 100 | if(AG_ENABLE_ASAN) 101 | target_compile_options(AudioGridderServer PRIVATE -fsanitize=address -fno-omit-frame-pointer) 102 | target_link_options(AudioGridderServer PRIVATE -fsanitize=address) 103 | endif() 104 | if(AG_ENABLE_DEBUG_COPY_STEP) 105 | ag_bundle_copy(AudioGridderServer /Applications/Debug) 106 | endif() 107 | endif() 108 | endif() 109 | -------------------------------------------------------------------------------- /Server/Resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apohl79/audiogridder/76dd9a006a86110b9245bc44f7a6c78f3d6a5c9f/Server/Resources/icon.png -------------------------------------------------------------------------------- /Server/Resources/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apohl79/audiogridder/76dd9a006a86110b9245bc44f7a6c78f3d6a5c9f/Server/Resources/icon16.png -------------------------------------------------------------------------------- /Server/Resources/icon64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apohl79/audiogridder/76dd9a006a86110b9245bc44f7a6c78f3d6a5c9f/Server/Resources/icon64.png -------------------------------------------------------------------------------- /Server/Source/AudioWorker.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef AudioWorker_hpp 9 | #define AudioWorker_hpp 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "ProcessorChain.hpp" 16 | #include "Message.hpp" 17 | #include "Utils.hpp" 18 | #include "ChannelMapper.hpp" 19 | 20 | namespace e47 { 21 | 22 | struct audio_chunk_hdr_t { 23 | int channels; 24 | int samples; 25 | bool isDouble; 26 | }; 27 | 28 | class ProcessorChain; 29 | 30 | class AudioWorker : public Thread, public LogTagDelegate { 31 | public: 32 | AudioWorker(LogTag* tag); 33 | virtual ~AudioWorker() override; 34 | 35 | void init(std::unique_ptr s, HandshakeRequest cfg); 36 | 37 | void run() override; 38 | void shutdown(); 39 | void clear(); 40 | 41 | bool isOk() { 42 | std::lock_guard lock(m_mtx); 43 | if (nullptr == m_socket) { 44 | m_error = "socket is nullptr"; 45 | m_wasOk = false; 46 | } else if (!m_socket->isConnected()) { 47 | m_error = "socket is not connected"; 48 | m_wasOk = false; 49 | } else { 50 | m_wasOk = true; 51 | } 52 | return m_wasOk; 53 | } 54 | 55 | bool isOkNoLock() const { return m_wasOk; } 56 | 57 | int getChannelsIn() const { return m_channelsIn; } 58 | int getChannelsOut() const { return m_channelsOut; } 59 | int getChannelsSC() const { return m_channelsSC; } 60 | 61 | bool addPlugin(const String& id, const String& settings, const String& layout, uint64 monoChannels, String& err); 62 | void delPlugin(int idx); 63 | void exchangePlugins(int idxA, int idxB); 64 | std::shared_ptr getProcessor(int idx) const { return m_chain->getProcessor(idx); } 65 | int getSize() const { return static_cast(m_chain->getSize()); } 66 | int getLatencySamples() const { return m_chain->getLatencySamples(); } 67 | void update() { m_chain->update(); } 68 | bool isSidechainDisabled() const { return m_chain->isSidechainDisabled(); } 69 | 70 | float getParameterValue(int idx, int channel, int paramIdx) { 71 | return m_chain->getParameterValue(idx, channel, paramIdx); 72 | } 73 | 74 | struct ComparablePluginDescription : PluginDescription { 75 | ComparablePluginDescription(const PluginDescription& other) : PluginDescription(other) {} 76 | bool operator==(const ComparablePluginDescription& other) const { return isDuplicateOf(other); } 77 | }; 78 | 79 | using RecentsListType = Array; 80 | String getRecentsList(String host) const; 81 | void addToRecentsList(const String& id, const String& host); 82 | 83 | private: 84 | std::mutex m_mtx; 85 | std::atomic_bool m_wasOk{true}; 86 | std::unique_ptr m_socket; 87 | String m_error; 88 | int m_channelsIn; 89 | int m_channelsOut; 90 | int m_channelsSC; 91 | ChannelSet m_activeChannels; 92 | ChannelMapper m_channelMapper; 93 | double m_sampleRate; 94 | int m_samplesPerBlock; 95 | bool m_doublePrecision; 96 | std::shared_ptr m_chain; 97 | static std::unordered_map m_recents; 98 | static std::mutex m_recentsMtx; 99 | 100 | AudioBuffer m_procBufferF; 101 | AudioBuffer m_procBufferD; 102 | 103 | bool waitForData(); 104 | 105 | template 106 | AudioBuffer* getProcBuffer() { 107 | return &m_procBufferF; 108 | } 109 | 110 | template 111 | void processBlock(AudioBuffer& buffer, MidiBuffer& midi); 112 | 113 | ENABLE_ASYNC_FUNCTORS(); 114 | }; 115 | 116 | } // namespace e47 117 | 118 | #endif /* AudioWorker_hpp */ 119 | -------------------------------------------------------------------------------- /Server/Source/CPUInfo.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef CPUInfo_hpp 9 | #define CPUInfo_hpp 10 | 11 | #include 12 | 13 | #include "SharedInstance.hpp" 14 | #include "Utils.hpp" 15 | 16 | namespace e47 { 17 | 18 | class CPUInfo : public Thread, public LogTag, public SharedInstance { 19 | public: 20 | CPUInfo() : Thread("CPUInfo"), LogTag("cpuinfo") { startThread(); } 21 | ~CPUInfo() override { stopThread(-1); } 22 | 23 | void run() override; 24 | 25 | static float getUsage() { return m_usage; } 26 | 27 | private: 28 | static std::atomic m_usage; 29 | }; 30 | 31 | } // namespace e47 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /Server/Source/Images.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef Images_hpp 9 | #define Images_hpp 10 | 11 | #include 12 | 13 | class Images { 14 | public: 15 | static const char* serverinv_png; 16 | static const int serverinv_pngSize; 17 | static const char* logotxt_png; 18 | static const int logotxt_pngSize; 19 | static const char* logo_png; 20 | static const int logo_pngSize; 21 | static const char* logowintray_png; 22 | static const int logowintray_pngSize; 23 | static const char* logowintraylight_png; 24 | static const int logowintraylight_pngSize; 25 | }; 26 | 27 | #endif // Images_hpp 28 | -------------------------------------------------------------------------------- /Server/Source/MenuBarWindow.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #include "MenuBarWindow.hpp" 9 | #include "App.hpp" 10 | #include "Images.hpp" 11 | 12 | namespace e47 { 13 | 14 | MenuBarWindow::MenuBarWindow(App* app) 15 | : DocumentWindow(ProjectInfo::projectName, Colours::lightgrey, DocumentWindow::closeButton), m_app(app) { 16 | PopupMenu m; 17 | m.addItem("About AudioGridder", [app] { 18 | app->showSplashWindow([app](bool isInfo) { 19 | if (isInfo) { 20 | URL("https://audiogridder.com").launchInDefaultBrowser(); 21 | } 22 | app->hideSplashWindow(); 23 | }); 24 | #ifdef JUCE_WINDOWS 25 | String info = L"\xa9 2020-2023 Andreas Pohl, https://audiogridder.com"; 26 | #else 27 | String info = L"© 2020-2023 Andreas Pohl, https://audiogridder.com"; 28 | #endif 29 | app->setSplashInfo(info); 30 | }); 31 | const char* logoNoMac = Images::logowintray_png; 32 | int logoNoMacSize = Images::logowintray_pngSize; 33 | #ifdef JUCE_WINDOWS 34 | bool lightTheme = 35 | WindowsRegistry::getValue( 36 | "HKEY_CURRENT_" 37 | "USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize\\SystemUsesLightTheme", 38 | "1") == "1"; 39 | if (lightTheme) { 40 | logoNoMac = Images::logowintraylight_png; 41 | logoNoMacSize = Images::logowintraylight_pngSize; 42 | } 43 | #endif 44 | setIconImage(ImageCache::getFromMemory(logoNoMac, logoNoMacSize), 45 | ImageCache::getFromMemory(Images::logo_png, Images::logo_pngSize)); 46 | #ifdef JUCE_MAC 47 | app->setMacMainMenu(app, &m); 48 | #endif 49 | } 50 | 51 | MenuBarWindow::~MenuBarWindow() { 52 | #ifdef JUCE_MAC 53 | m_app->setMacMainMenu(nullptr); 54 | #endif 55 | } 56 | 57 | void MenuBarWindow::mouseUp(const MouseEvent& /* event */) { 58 | auto menu = m_app->getMenuForIndex(0, "Tray"); 59 | menu.addSeparator(); 60 | menu.addItem("About AudioGridder", [this] { 61 | m_app->showSplashWindow([this](bool isInfo) { 62 | if (isInfo) { 63 | URL("https://audiogridder.com").launchInDefaultBrowser(); 64 | } 65 | m_app->hideSplashWindow(); 66 | }); 67 | #ifdef JUCE_WINDOWS 68 | String info = L"\xa9 2020-2023 Andreas Pohl, https://audiogridder.com"; 69 | #else 70 | String info = L"© 2020-2023 Andreas Pohl, https://audiogridder.com"; 71 | #endif 72 | m_app->setSplashInfo(info); 73 | }); 74 | menu.addItem("Restart", [this] { m_app->prepareShutdown(App::EXIT_RESTART); }); 75 | menu.addItem("Quit", [this] { m_app->prepareShutdown(); }); 76 | #ifdef JUCE_MAC 77 | showDropdownMenu(menu); 78 | #else 79 | menu.show(); 80 | #endif 81 | } 82 | 83 | } // namespace e47 84 | -------------------------------------------------------------------------------- /Server/Source/MenuBarWindow.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef _MENUBARWINDOW_HPP_ 9 | #define _MENUBARWINDOW_HPP_ 10 | 11 | #include 12 | 13 | namespace e47 { 14 | 15 | class App; 16 | 17 | class MenuBarWindow : public DocumentWindow, public SystemTrayIconComponent { 18 | public: 19 | MenuBarWindow(App* app); 20 | ~MenuBarWindow() override; 21 | 22 | void mouseUp(const MouseEvent&) override; 23 | 24 | private: 25 | App* m_app; 26 | }; 27 | 28 | } // namespace e47 29 | 30 | #endif // _MENUBARWINDOW_HPP_ 31 | -------------------------------------------------------------------------------- /Server/Source/ParameterValue.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef _PARAMETERVALUE_HPP_ 9 | #define _PARAMETERVALUE_HPP_ 10 | 11 | namespace e47 { 12 | namespace Srv { 13 | 14 | struct ParameterValue { 15 | int paramIdx; 16 | float value; 17 | int channel = 0; 18 | }; 19 | 20 | } // namespace Srv 21 | 22 | } // namespace e47 23 | 24 | #endif // _PARAMETERVALUE_HPP_ 25 | -------------------------------------------------------------------------------- /Server/Source/PluginListComponent.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | * 7 | * Heavily based on juce_audio_processors/scanning/juce_PluginListComponent.h 8 | */ 9 | 10 | #ifndef PluginListComponent_hpp 11 | #define PluginListComponent_hpp 12 | 13 | #include 14 | #include 15 | 16 | namespace e47 { 17 | 18 | class PluginListComponent : public Component, public FileDragAndDropTarget, private ChangeListener { 19 | public: 20 | PluginListComponent(AudioPluginFormatManager& formatManager, KnownPluginList& listToRepresent, 21 | std::set& exList, const File& deadMansPedalFile); 22 | ~PluginListComponent() override; 23 | 24 | PopupMenu createMenuForRow(int rowNumber); 25 | 26 | private: 27 | AudioPluginFormatManager& m_formatManager; 28 | KnownPluginList& m_list; 29 | std::set& m_excludeList; 30 | File m_deadMansPedalFile; 31 | TableListBox m_table; 32 | String m_dialogTitle, m_dialogText; 33 | 34 | class TableModel; 35 | std::unique_ptr m_tableModel; 36 | 37 | void updateList(); 38 | void removeMissingPlugins(); 39 | void removePluginItems(const std::vector indexes); 40 | void addPluginItems(const std::vector indexes); 41 | void rescanPluginItems(const std::vector indexes); 42 | 43 | void resized() override; 44 | bool isInterestedInFileDrag(const StringArray&) override { return false; } 45 | void filesDropped(const StringArray&, int, int) override {} 46 | void changeListenerCallback(ChangeBroadcaster*) override; 47 | 48 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginListComponent) 49 | }; 50 | 51 | } // namespace e47 52 | 53 | #endif // PluginListComponent_hpp 54 | -------------------------------------------------------------------------------- /Server/Source/PluginListWindow.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #include "App.hpp" 9 | #include "PluginListWindow.hpp" 10 | #include "Server.hpp" 11 | #include "WindowPositions.hpp" 12 | 13 | namespace e47 { 14 | 15 | PluginListWindow::PluginListWindow(App* app, KnownPluginList& list, const String& deadMansPedalFile) 16 | : DocumentWindow("Available Plugins", 17 | LookAndFeel::getDefaultLookAndFeel().findColour(ResizableWindow::backgroundColourId), 18 | DocumentWindow::closeButton), 19 | m_app(app), 20 | m_pluginlist(list), 21 | m_deadMansPedalFile(deadMansPedalFile) { 22 | if (auto srv = m_app->getServer()) { 23 | setUsingNativeTitleBar(true); 24 | m_plugmgr.addDefaultFormats(); 25 | setContentOwned(new PluginListComponent(m_plugmgr, m_pluginlist, srv->getExcludeList(), m_deadMansPedalFile), 26 | true); 27 | 28 | setResizable(true, false); 29 | centreWithSize(700, 600); 30 | setBounds(WindowPositions::get(WindowPositions::ServerPlugins, getBounds())); 31 | 32 | setVisible(true); 33 | windowToFront(this); 34 | } 35 | } 36 | 37 | PluginListWindow::~PluginListWindow() { 38 | WindowPositions::set(WindowPositions::ServerPlugins, getBounds()); 39 | clearContentComponent(); 40 | } 41 | 42 | void PluginListWindow::closeButtonPressed() { m_app->hidePluginList(); } 43 | 44 | } // namespace e47 45 | -------------------------------------------------------------------------------- /Server/Source/PluginListWindow.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef PluginListWindow_hpp 9 | #define PluginListWindow_hpp 10 | 11 | #include 12 | 13 | #include "PluginListComponent.hpp" 14 | 15 | namespace e47 { 16 | 17 | class App; 18 | 19 | class PluginListWindow : public DocumentWindow { 20 | public: 21 | PluginListWindow(App* app, KnownPluginList& list, const String& deadMansPedalFile); 22 | ~PluginListWindow() override; 23 | 24 | void closeButtonPressed() override; 25 | 26 | private: 27 | App* m_app; 28 | AudioPluginFormatManager m_plugmgr; 29 | KnownPluginList& m_pluginlist; 30 | File m_deadMansPedalFile; 31 | 32 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginListWindow) 33 | }; 34 | 35 | } // namespace e47 36 | 37 | #endif /* PluginListWindow_hpp */ 38 | -------------------------------------------------------------------------------- /Server/Source/ProcessorChain.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef ProcessorChain_hpp 9 | #define ProcessorChain_hpp 10 | 11 | #include 12 | 13 | #include "Utils.hpp" 14 | #include "Defaults.hpp" 15 | #include "Message.hpp" 16 | 17 | namespace e47 { 18 | 19 | class Processor; 20 | 21 | class ProcessorChain : public AudioProcessor, public LogTagDelegate { 22 | public: 23 | class PlayHead : public AudioPlayHead { 24 | public: 25 | PlayHead(AudioPlayHead::PositionInfo* posInfo) : m_posInfo(posInfo) {} 26 | Optional getPosition() const override { return makeOptional(*m_posInfo); } 27 | 28 | private: 29 | AudioPlayHead::PositionInfo* m_posInfo; 30 | }; 31 | 32 | ProcessorChain(const LogTag* tag, const BusesProperties& props, const HandshakeRequest& cfg) 33 | : AudioProcessor(props), LogTagDelegate(tag), m_cfg(cfg) {} 34 | 35 | static BusesProperties createBussesProperties(int in, int out, int sc) { 36 | setLogTagStatic("processorchain"); 37 | traceScope(); 38 | auto props = BusesProperties().withOutput("Output", AudioChannelSet::discreteChannels(out), false); 39 | if (in > 0) { 40 | props = props.withInput("Input", AudioChannelSet::discreteChannels(in), false); 41 | } 42 | if (sc > 0) { 43 | props = props.withInput("Sidechain", AudioChannelSet::discreteChannels(sc), false); 44 | } 45 | return props; 46 | } 47 | 48 | const HandshakeRequest& getConfig() const { return m_cfg; } 49 | 50 | bool isSidechainDisabled() const { return m_sidechainDisabled; } 51 | 52 | void prepareToPlay(double sampleRate, int maximumExpectedSamplesPerBlock) override; 53 | void releaseResources() override; 54 | void setPlayHead(AudioPlayHead* ph) override; 55 | void processBlock(AudioBuffer& buffer, MidiBuffer& midiMessages) override; 56 | void processBlock(AudioBuffer& buffer, MidiBuffer& midiMessages) override; 57 | const String getName() const override { return "ProcessorChain"; } 58 | double getTailLengthSeconds() const override; 59 | bool supportsDoublePrecisionProcessing() const override; 60 | bool isBusesLayoutSupported(const BusesLayout& /*layouts*/) const override { return true; } 61 | 62 | bool updateChannels(int channelsIn, int channelsOut, int channelsSC); 63 | int getExtraChannels(); 64 | 65 | bool acceptsMidi() const override { return false; } 66 | bool producesMidi() const override { return false; } 67 | AudioProcessorEditor* createEditor() override { return nullptr; } 68 | bool hasEditor() const override { return false; } 69 | int getNumPrograms() override { return 0; } 70 | int getCurrentProgram() override { return 0; } 71 | void setCurrentProgram(int /* index */) override {} 72 | const String getProgramName(int /* index */) override { return ""; } 73 | void changeProgramName(int /* index */, const String& /* newName */) override {} 74 | void getStateInformation(juce::MemoryBlock& /* destData */) override {} 75 | void setStateInformation(const void* /* data */, int /* sizeInBytes */) override {} 76 | 77 | bool initPluginInstance(Processor* proc, const String& layout, String& err); 78 | bool addPluginProcessor(const String& id, const String& settings, const String& layout, uint64 monoChannels, 79 | String& err); 80 | void addProcessor(std::shared_ptr processor); 81 | size_t getSize() const { return m_processors.size(); } 82 | std::shared_ptr getProcessor(int index); 83 | 84 | void delProcessor(int idx); 85 | void exchangeProcessors(int idxA, int idxB); 86 | float getParameterValue(int idx, int channel, int paramIdx); 87 | void update(); 88 | void clear(); 89 | String toString(); 90 | 91 | private: 92 | std::vector> m_processors; 93 | std::mutex m_processorsMtx; 94 | 95 | std::atomic_bool m_supportsDoublePrecision{true}; 96 | std::atomic m_tailSecs{0.0}; 97 | 98 | HandshakeRequest m_cfg; 99 | 100 | int m_extraChannels = 0; 101 | bool m_hasSidechain = false; 102 | bool m_sidechainDisabled = false; 103 | 104 | template 105 | void processBlockInternal(AudioBuffer& buffer, MidiBuffer& midiMessages); 106 | 107 | template 108 | void preProcessBlocks(Processor* proc); 109 | 110 | bool setProcessorBusesLayout(Processor* proc, const String& targetOutputLayout); 111 | 112 | void updateNoLock(); 113 | }; 114 | 115 | } // namespace e47 116 | 117 | #endif /* ProcessorChain_hpp */ 118 | -------------------------------------------------------------------------------- /Server/Source/ProcessorClient.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef _PROCESSORCLIENT_HPP_ 9 | #define _PROCESSORCLIENT_HPP_ 10 | 11 | #include 12 | 13 | #include "Utils.hpp" 14 | #include "Message.hpp" 15 | #include "ParameterValue.hpp" 16 | #include "ChannelMapper.hpp" 17 | #include "ChannelSet.hpp" 18 | 19 | namespace e47 { 20 | 21 | class ProcessorClient : public Thread, public LogTag { 22 | public: 23 | ProcessorClient(const String& id, HandshakeRequest cfg) 24 | : Thread("ProcessorClient"), 25 | LogTag("processorclient"), 26 | m_port(getWorkerPort()), 27 | m_id(id), 28 | m_cfg(cfg), 29 | m_activeChannels(cfg.activeChannels, cfg.channelsIn > 0), 30 | m_channelMapper(this) { 31 | m_activeChannels.setNumChannels(cfg.channelsIn + cfg.channelsSC, cfg.channelsOut); 32 | m_channelMapper.createPluginMapping(m_activeChannels); 33 | } 34 | ~ProcessorClient() override; 35 | 36 | bool init(); 37 | void shutdown(); 38 | bool isOk(); 39 | const String& getError() const { return m_error; } 40 | 41 | void run() override; 42 | 43 | std::function onParamValueChange; 44 | std::function onParamGestureChange; 45 | std::function&)> onKeysFromSandbox; 46 | std::function onStatusChange; 47 | 48 | bool load(const String& settings, const String& layout, uint64 monoChannels, String& err); 49 | void unload(); 50 | 51 | bool isLoaded() const { return m_loaded; } 52 | 53 | const String getName(); 54 | bool hasEditor(); 55 | void showEditor(int channel, int x, int y); 56 | void hideEditor(); 57 | bool supportsDoublePrecisionProcessing(); 58 | bool isSuspended(); 59 | double getTailLengthSeconds(); 60 | void getStateInformation(String&); 61 | void setStateInformation(const String&); 62 | void setPlayHead(AudioPlayHead*); 63 | const json& getParameters(); 64 | int getNumPrograms(); 65 | const String getProgramName(int); 66 | void setCurrentProgram(int); 67 | void suspendProcessing(bool); 68 | void suspendProcessingRemoteOnly(bool); 69 | int getTotalNumOutputChannels(); 70 | int getLatencySamples(); 71 | void processBlock(AudioBuffer& buffer, MidiBuffer& midiMessages); 72 | void processBlock(AudioBuffer& buffer, MidiBuffer& midiMessages); 73 | juce::Rectangle getScreenBounds(); 74 | void setParameterValue(int channel, int paramIdx, float value); 75 | float getParameterValue(int channel, int paramIdx); 76 | std::vector getAllParameterValues(); 77 | void setMonoChannels(uint64 channels); 78 | int getChannelInstances() const { return m_lastChannelInstances; } 79 | 80 | private: 81 | int m_port; 82 | String m_id; 83 | HandshakeRequest m_cfg; 84 | ChildProcess m_process; 85 | std::unique_ptr m_sockCmdIn, m_sockCmdOut, m_sockAudio; 86 | std::mutex m_cmdMtx, m_audioMtx; 87 | std::shared_ptr m_bytesOutMeter, m_bytesInMeter; 88 | String m_error; 89 | 90 | bool m_loaded = false; 91 | String m_name; 92 | StringArray m_presets; 93 | json m_parameters; 94 | int m_latency = 0; 95 | bool m_hasEditor = false; 96 | bool m_scDisabled = false; 97 | bool m_supportsDoublePrecision = true; 98 | double m_tailSeconds = 0.0; 99 | int m_numOutputChannels = 0; 100 | AudioPlayHead* m_playhead = nullptr; 101 | std::atomic_bool m_suspended{false}; 102 | String m_lastSettings; 103 | String m_lastLayout; 104 | uint64 m_lastMonoChannels; 105 | juce::Rectangle m_lastScreenBounds; 106 | int m_lastChannelInstances = 0; 107 | 108 | ChannelSet m_activeChannels; 109 | ChannelMapper m_channelMapper; 110 | 111 | static std::unordered_set m_workerPorts; 112 | static std::mutex m_workerPortsMtx; 113 | 114 | static int getWorkerPort(); 115 | static void removeWorkerPort(int port); 116 | 117 | bool startSandbox(); 118 | bool connectSandbox(); 119 | 120 | void setAndLogError(const String& e) { 121 | m_error = e; 122 | logln(e); 123 | } 124 | 125 | template 126 | void processBlockInternal(AudioBuffer& buffer, MidiBuffer& midiMessages); 127 | 128 | void handleMessage(std::shared_ptr> msg); 129 | void handleMessage(std::shared_ptr> msg); 130 | void handleMessage(std::shared_ptr> msg); 131 | void handleMessage(std::shared_ptr> msg); 132 | }; 133 | 134 | } // namespace e47 135 | 136 | #endif // _PROCESSORCLIENT_HPP_ 137 | -------------------------------------------------------------------------------- /Server/Source/ProcessorWindow.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef _PROCESSORWINDOW_HPP_ 9 | #define _PROCESSORWINDOW_HPP_ 10 | 11 | #include 12 | 13 | #include "ScreenRecorder.hpp" 14 | #include "Utils.hpp" 15 | 16 | namespace e47 { 17 | 18 | class Processor; 19 | 20 | class ProcessorWindow : public DocumentWindow, private Timer, public LogTag { 21 | public: 22 | using CaptureCallbackNative = std::function image, int width, int height)>; 23 | using CaptureCallbackFFmpeg = ScreenRecorder::CaptureCallback; 24 | 25 | ProcessorWindow(std::shared_ptr proc, Thread::ThreadID tid, CaptureCallbackNative func, 26 | std::function onHide, int x, int y); 27 | ProcessorWindow(std::shared_ptr proc, Thread::ThreadID tid, CaptureCallbackFFmpeg func, 28 | std::function onHide, int x, int y); 29 | ~ProcessorWindow() override; 30 | 31 | void closeButtonPressed() override; 32 | BorderSize getBorderThickness() override { return {}; } 33 | 34 | void forgetEditor(); 35 | juce::Rectangle getScreenCaptureRect(); 36 | void updateScreenCaptureArea(); 37 | void startCapturing(); 38 | void stopCapturing(); 39 | void resized() override; 40 | void setVisible(bool b) override; 41 | bool isShowingPlugin() const { return m_isShowing; } 42 | bool hasEditor() const { return nullptr != m_editor; } 43 | void move(int x, int y); 44 | void toTop(); 45 | 46 | CaptureCallbackFFmpeg getCaptureCallbackFFmpeg() const { return m_callbackFFmpeg; } 47 | CaptureCallbackNative getCaptureCallbackNative() const { return m_callbackNative; } 48 | std::function getOnHide() const { return m_onHide; } 49 | 50 | Thread::ThreadID getTid() const { return m_tid; } 51 | 52 | private: 53 | std::shared_ptr m_processor; 54 | Thread::ThreadID m_tid; 55 | AudioProcessorEditor* m_editor = nullptr; 56 | CaptureCallbackNative m_callbackNative; 57 | CaptureCallbackFFmpeg m_callbackFFmpeg; 58 | std::function m_onHide; 59 | juce::Rectangle m_screenCaptureRect, m_totalRect; 60 | int m_startCapturingRetry; 61 | bool m_isShowing = false; 62 | 63 | void createEditor(); 64 | void captureWindow(); 65 | 66 | void timerCallback() override { captureWindow(); } 67 | 68 | bool isFullyVisible() const; 69 | 70 | ENABLE_ASYNC_FUNCTORS(); 71 | 72 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ProcessorWindow) 73 | }; 74 | 75 | } // namespace e47 76 | 77 | #endif // _PROCESSORWINDOW_HPP_ 78 | -------------------------------------------------------------------------------- /Server/Source/Sandbox.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #include "Sandbox.hpp" 9 | #include "Server.hpp" 10 | 11 | namespace e47 { 12 | 13 | SandboxPeer::SandboxPeer(Server& server) : LogTagDelegate(&server), m_server(server) { initAsyncFunctors(); } 14 | SandboxPeer::~SandboxPeer() { stopAsyncFunctors(); } 15 | 16 | bool SandboxPeer::send(const SandboxMessage& msg, ResponseCallback callback, bool shouldBlock) { 17 | traceScope(); 18 | MemoryBlock block; 19 | msg.serialize(block); 20 | auto hash = msg.id.hash(); 21 | bool ret = true; 22 | bool* pret = shouldBlock ? &ret : nullptr; 23 | auto fn = [this, block, hash, callback, pret] { 24 | traceScope(); 25 | bool result = sendMessage(block); 26 | if (result && callback) { 27 | m_callbacks.set(hash, callback); 28 | } 29 | if (nullptr != pret) { 30 | *pret = result; 31 | } 32 | }; 33 | if (shouldBlock) { 34 | runOnMsgThreadSync(fn); 35 | } else { 36 | runOnMsgThreadAsync(fn); 37 | } 38 | return ret; 39 | } 40 | 41 | void SandboxPeer::read(const MemoryBlock& data) { 42 | traceScope(); 43 | try { 44 | auto j = json::parse(data.begin(), data.end()); 45 | SandboxMessage msg(j["type"].get(), j["data"], j["uuid"].get()); 46 | auto idhash = msg.id.hash(); 47 | if (m_callbacks.contains(idhash)) { 48 | m_callbacks[idhash](msg); 49 | m_callbacks.remove(idhash); 50 | } else { 51 | handleMessage(msg); 52 | } 53 | } catch (json::parse_error& e) { 54 | logln("failed to parse json message from sandbox: " << e.what()); 55 | } 56 | } 57 | 58 | SandboxMaster::SandboxMaster(Server& server, const String& i) : SandboxPeer(server), id(i) {} 59 | 60 | void SandboxMaster::handleConnectionLost() { 61 | traceScope(); 62 | if (m_terminated) { 63 | return; 64 | } 65 | m_server.handleDisconnectFromSandbox(*this); 66 | } 67 | 68 | void SandboxMaster::handleMessage(const SandboxMessage& msg) { 69 | traceScope(); 70 | if (m_terminated) { 71 | return; 72 | } 73 | if (msg.type == SandboxMessage::SANDBOX_PORT) { 74 | int port = msg.data["port"].get(); 75 | logln("received port " << port << " from sandbox " << id); 76 | if (onPortReceived) { 77 | onPortReceived(port); 78 | } 79 | } else { 80 | m_server.handleMessageFromSandbox(*this, msg); 81 | } 82 | } 83 | 84 | SandboxSlave::SandboxSlave(Server& server) : SandboxPeer(server) {} 85 | 86 | void SandboxSlave::handleConnectionLost() { 87 | if (m_terminated) { 88 | return; 89 | } 90 | m_server.handleDisconnectedFromMaster(); 91 | } 92 | 93 | void SandboxSlave::handleConnectionMade() { 94 | if (m_terminated) { 95 | return; 96 | } 97 | m_server.handleConnectedToMaster(); 98 | } 99 | 100 | void SandboxSlave::handleMessage(const SandboxMessage& msg) { 101 | if (m_terminated) { 102 | return; 103 | } 104 | m_server.handleMessageFromMaster(msg); 105 | } 106 | 107 | } // namespace e47 108 | -------------------------------------------------------------------------------- /Server/Source/Sandbox.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef Sandbox_hpp 9 | #define Sandbox_hpp 10 | 11 | #include 12 | 13 | #include "Utils.hpp" 14 | #include "Message.hpp" 15 | 16 | namespace e47 { 17 | 18 | class Server; 19 | 20 | class SandboxPeer : public LogTagDelegate { 21 | public: 22 | SandboxPeer(Server& server); 23 | ~SandboxPeer(); 24 | 25 | using ResponseCallback = std::function; 26 | 27 | bool send(const SandboxMessage& msg, ResponseCallback callback = nullptr, bool shouldBlock = false); 28 | void read(const MemoryBlock& data); 29 | 30 | void terminate() { m_terminated = true; } 31 | 32 | protected: 33 | Server& m_server; 34 | std::atomic_bool m_terminated{false}; 35 | 36 | virtual bool sendMessage(const MemoryBlock&) = 0; 37 | virtual void handleMessage(const SandboxMessage&) = 0; 38 | 39 | private: 40 | HashMap m_callbacks; 41 | 42 | ENABLE_ASYNC_FUNCTORS(); 43 | }; 44 | 45 | struct SandboxMaster : ChildProcessCoordinator, SandboxPeer { 46 | SandboxMaster(Server& server, const String& id); 47 | 48 | String id; 49 | std::function onPortReceived; 50 | 51 | // ChildProcessMaster 52 | void handleConnectionLost() override; 53 | void handleMessageFromSlave(const MemoryBlock& data) override { read(data); } 54 | 55 | protected: 56 | // SandboxPeer 57 | bool sendMessage(const MemoryBlock& data) override { return sendMessageToWorker(data); } 58 | void handleMessage(const SandboxMessage&) override; 59 | }; 60 | 61 | struct SandboxSlave : ChildProcessWorker, SandboxPeer { 62 | SandboxSlave(Server& server); 63 | 64 | // ChildProcessSlave 65 | void handleConnectionMade() override; 66 | void handleConnectionLost() override; 67 | void handleMessageFromMaster(const MemoryBlock& data) override { read(data); } 68 | 69 | protected: 70 | // SandboxPeer 71 | bool sendMessage(const MemoryBlock& data) override { return sendMessageToCoordinator(data); } 72 | void handleMessage(const SandboxMessage&) override; 73 | }; 74 | 75 | } // namespace e47 76 | 77 | #endif // Sandbox_hpp 78 | -------------------------------------------------------------------------------- /Server/Source/Screen.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #include "Screen.h" 9 | 10 | #ifdef JUCE_WINDOWS 11 | #include 12 | 13 | namespace e47 { 14 | 15 | std::shared_ptr captureScreenNative(juce::Rectangle rect) { 16 | HDC hDC = GetDC(0); 17 | float dpi = (GetDeviceCaps(hDC, LOGPIXELSX) + GetDeviceCaps(hDC, LOGPIXELSY)) / 2.0f; 18 | float scaleFactor = dpi / 96; 19 | int x = GetSystemMetrics(SM_XVIRTUALSCREEN) + rect.getX(); 20 | int y = GetSystemMetrics(SM_YVIRTUALSCREEN) + rect.getY(); 21 | int w = (int)roundl(rect.getWidth() * scaleFactor); 22 | int h = (int)roundl(rect.getHeight() * scaleFactor); 23 | HDC cDC = CreateCompatibleDC(hDC); 24 | HBITMAP bmap = CreateCompatibleBitmap(hDC, w, h); 25 | HGDIOBJ oldObj = SelectObject(cDC, bmap); 26 | auto ret = std::make_shared(juce::Image::ARGB, w, h, false); 27 | 28 | if (BitBlt(cDC, 0, 0, w, h, hDC, x, y, SRCCOPY)) { 29 | BITMAPINFO bmi = {0}; 30 | bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 31 | bmi.bmiHeader.biWidth = w; 32 | bmi.bmiHeader.biHeight = -h; 33 | bmi.bmiHeader.biPlanes = 1; 34 | bmi.bmiHeader.biBitCount = 32; 35 | bmi.bmiHeader.biCompression = BI_RGB; 36 | bmi.bmiHeader.biSizeImage = 0; 37 | 38 | juce::Image::BitmapData bd(*ret, 0, 0, w, h); 39 | 40 | if (!GetDIBits(hDC, bmap, 0, h, bd.data, &bmi, DIB_RGB_COLORS)) { 41 | ret.reset(); 42 | } 43 | } 44 | 45 | SelectObject(cDC, oldObj); 46 | DeleteObject(bmap); 47 | DeleteDC(cDC); 48 | ReleaseDC(0, hDC); 49 | 50 | if (scaleFactor != 1.0) { 51 | auto rescaled = std::make_shared(ret->rescaled(rect.getWidth(), rect.getHeight())); 52 | return rescaled; 53 | } 54 | return ret; 55 | } 56 | 57 | } // namespace e47 58 | 59 | #endif 60 | 61 | #if defined(JUCE_LINUX) 62 | namespace e47 { 63 | 64 | int getScreenShotData(unsigned char** buffer, int x, int y, unsigned int w, unsigned int h); 65 | 66 | std::shared_ptr captureScreenNative(juce::Rectangle rect) { 67 | unsigned int w = (unsigned int)rect.getWidth(); 68 | unsigned int h = (unsigned int)rect.getHeight(); 69 | int x = rect.getX(), y = rect.getY(); 70 | // ALOCATE buffer for copying image data 71 | auto ret = std::make_shared(juce::Image::ARGB, w, h, false); 72 | juce::Image::BitmapData bd(*ret, 0, 0, (int)w, (int)h); 73 | 74 | /* GET Image */ 75 | getScreenShotData(&bd.data, x, y, w, h); 76 | return ret; 77 | } 78 | 79 | } // namespace e47 80 | #endif 81 | -------------------------------------------------------------------------------- /Server/Source/Screen.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef Screen_h 9 | #define Screen_h 10 | 11 | #include 12 | 13 | using namespace juce; 14 | 15 | namespace e47 { 16 | 17 | std::shared_ptr captureScreenNative(Rectangle rect); 18 | 19 | #ifdef JUCE_MAC 20 | int getCaptureDeviceIndex(); 21 | void askForScreenRecordingPermission(); 22 | bool askForAccessibilityPermission(); 23 | #endif 24 | 25 | } // namespace e47 26 | 27 | #endif /* Screen_h */ 28 | -------------------------------------------------------------------------------- /Server/Source/Screen.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifdef __APPLE__ 9 | 10 | #import 11 | 12 | #include "Screen.h" 13 | #include "Utils.hpp" 14 | #include 15 | #include 16 | 17 | 18 | namespace e47 { 19 | 20 | std::shared_ptr captureScreenNative(Rectangle rect) { 21 | setLogTagStatic("screen"); 22 | auto r = CGRectMake(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); 23 | auto imgref = CGWindowListCreateImage(r, kCGWindowListOptionAll, kCGNullWindowID, kCGWindowImageNominalResolution); 24 | if (nullptr != imgref) { 25 | NSBitmapImageRep* rep = [[NSBitmapImageRep alloc] initWithCGImage:imgref]; 26 | if (nullptr != rep) { 27 | NSDictionary *props = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:1.0] 28 | forKey:NSImageCompressionFactor]; 29 | NSData* data = [rep representationUsingType:NSJPEGFileType properties:props]; 30 | std::shared_ptr ret; 31 | if (nullptr != data) { 32 | ret = std::make_shared(JPEGImageFormat::loadFrom([data bytes], [data length])); 33 | ret->duplicateIfShared(); 34 | } else { 35 | logln("representationUsingType failed"); 36 | } 37 | [rep release]; 38 | CGImageRelease(imgref); 39 | return ret; 40 | } else { 41 | logln("initWithCGImage failed"); 42 | } 43 | } else { 44 | logln("CGWindowListCreateImage failed"); 45 | } 46 | return nullptr; 47 | } 48 | 49 | int getCaptureDeviceIndex() { 50 | setLogTagStatic("screen"); 51 | NSArray* devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; 52 | NSArray* devicesMuxed = [AVCaptureDevice devicesWithMediaType:AVMediaTypeMuxed]; 53 | uint64_t numVideoDevices = [devices count] + [devicesMuxed count]; 54 | uint32_t numScreens = 0; 55 | CGGetActiveDisplayList(0, nullptr, &numScreens); 56 | int ret = -1; 57 | logln("iterating over available capture devices..."); 58 | if (numScreens > 0) { 59 | CGDirectDisplayID screens[32]; 60 | if (numScreens > 32) { 61 | numScreens = 32; 62 | } 63 | CGGetActiveDisplayList(numScreens, screens, &numScreens); 64 | for (uint32_t i = 0; i < numScreens; i++) { 65 | auto idx = numVideoDevices + i; 66 | String desc = " found device with index " + String(idx); 67 | if (ret == -1) { 68 | ret = (int)idx; 69 | desc << " (selected)"; 70 | } 71 | logln(desc); 72 | } 73 | } 74 | return ret; 75 | } 76 | 77 | void askForScreenRecordingPermission() { 78 | #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 79 | CGDisplayStreamRef stream = 80 | CGDisplayStreamCreate(CGMainDisplayID(), 1, 1, kCVPixelFormatType_32BGRA, nil, 81 | ^(CGDisplayStreamFrameStatus, uint64_t, IOSurfaceRef, CGDisplayStreamUpdateRef){ 82 | }); 83 | if (stream) { 84 | CFRelease(stream); 85 | } 86 | #endif 87 | } 88 | 89 | bool askForAccessibilityPermission() { 90 | return AXIsProcessTrusted(); 91 | } 92 | 93 | } 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /Server/Source/ScreenHelper_linux.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #ifdef JUCE_LINUX 3 | #include 4 | #include 5 | namespace e47 { 6 | static Display* display = NULL; 7 | 8 | int getScreenShotData(unsigned char** buffer, int x, int y, unsigned int width, unsigned int height) { 9 | int ret = 0; 10 | if (!display) { 11 | display = XOpenDisplay(NULL); 12 | } 13 | 14 | if (!display) { 15 | // MSG("Fatal: Failed to open X display"); 16 | return -1; 17 | } 18 | 19 | Window win = DefaultRootWindow(display); 20 | XImage* img = XGetImage(display, win, x, y, width, height, AllPlanes, ZPixmap); 21 | memcpy(*buffer, img->data, width * height * 4); 22 | XDestroyImage(img); 23 | return ret; 24 | } 25 | 26 | void closeDisplayHandle() { 27 | if (display) { 28 | XCloseDisplay(display); 29 | } 30 | } 31 | 32 | } // namespace e47 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /Server/Source/ScreenRecorder.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef ScreenRecorder_hpp 9 | #define ScreenRecorder_hpp 10 | 11 | #include 12 | 13 | #include "Utils.hpp" 14 | #include "SharedInstance.hpp" 15 | 16 | #include 17 | 18 | JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE("-Wsign-conversion", "-Wconversion") 19 | JUCE_BEGIN_IGNORE_WARNINGS_MSVC(4244) 20 | extern "C" { 21 | #include "libavcodec/avcodec.h" 22 | #include "libavdevice/avdevice.h" 23 | #include "libavutil/imgutils.h" 24 | #include "libswscale/swscale.h" 25 | } 26 | JUCE_END_IGNORE_WARNINGS_MSVC 27 | JUCE_END_IGNORE_WARNINGS_GCC_LIKE 28 | 29 | #ifdef JUCE_WINDOWS 30 | #pragma comment(lib, "strmiids.lib") 31 | #pragma comment(lib, "mfplat.lib") 32 | #pragma comment(lib, "secur32.lib") 33 | #pragma comment(lib, "bcrypt.lib") 34 | #pragma comment(lib, "mfuuid.lib") 35 | #endif 36 | 37 | namespace e47 { 38 | 39 | class ScreenRecorder : public LogTag, public SharedInstance { 40 | public: 41 | using CaptureCallback = std::function; 43 | using ErrorCallback = std::function; 44 | 45 | enum EncoderMode { WEBP, MJPEG }; 46 | 47 | ScreenRecorder(); 48 | ~ScreenRecorder(); 49 | 50 | void start(juce::Rectangle rect, CaptureCallback callbackFn, ErrorCallback errorFn); 51 | void resume(juce::Rectangle rect = {}); 52 | void stop(); 53 | 54 | bool isRecording() const { return m_capture; } 55 | 56 | enum EncoderQuality : int { ENC_QUALITY_LOW = 0, ENC_QUALITY_MEDIUM = 1, ENC_QUALITY_HIGH = 2 }; 57 | 58 | static void initialize(EncoderMode encMode = WEBP, EncoderQuality quality = ENC_QUALITY_MEDIUM); 59 | 60 | private: 61 | static String m_inputFmtName; 62 | static String m_inputStreamUrl; 63 | static const AVInputFormat* m_inputFmt; 64 | static AVFormatContext* m_captureFmtCtx; 65 | const AVCodec* m_captureCodec = nullptr; 66 | AVCodecContext* m_captureCodecCtx = nullptr; 67 | AVFrame* m_captureFrame = nullptr; 68 | AVFrame* m_cropFrame = nullptr; 69 | AVPacket* m_capturePacket = nullptr; 70 | AVStream* m_captureStream = nullptr; 71 | int m_captureStreamIndex = -1; 72 | 73 | static const AVCodec* m_outputCodec; 74 | AVCodecContext* m_outputCodecCtx = nullptr; 75 | AVFrame* m_outputFrame = nullptr; 76 | uint8_t* m_outputFrameBuf = nullptr; 77 | AVPacket* m_outputPacket = nullptr; 78 | 79 | static bool m_initialized; 80 | 81 | SwsContext* m_swsCtx = nullptr; 82 | 83 | juce::Rectangle m_captureRect; 84 | int m_pxSize = 0; 85 | int m_scaledWith = 0; 86 | int m_scaledHeight = 0; 87 | 88 | static EncoderMode m_encMode; 89 | static double m_scale; 90 | static int m_quality; 91 | static bool m_downScale; 92 | 93 | std::unique_ptr m_thread; 94 | std::atomic_bool m_threadRunning{false}; 95 | std::atomic_bool m_capture{false}; 96 | std::mutex m_startStopMtx; 97 | 98 | CaptureCallback m_captureCallback; 99 | ErrorCallback m_errorCallback; 100 | 101 | bool prepareInput(); 102 | bool prepareOutput(); 103 | 104 | void cleanupInput(); 105 | void cleanupOutput(); 106 | 107 | void record(); 108 | 109 | inline void logError(const String& err) { 110 | if (nullptr != m_errorCallback) { 111 | m_errorCallback(err); 112 | } 113 | logln(err); 114 | } 115 | }; 116 | 117 | } // namespace e47 118 | #endif /* ScreenRecorder_hpp */ 119 | -------------------------------------------------------------------------------- /Server/Source/ScreenWorker.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef ScreenWorker_hpp 9 | #define ScreenWorker_hpp 10 | 11 | #include 12 | #include 13 | 14 | #include "Utils.hpp" 15 | #include "ProcessorChain.hpp" 16 | 17 | namespace e47 { 18 | 19 | class ScreenWorker : public Thread, public LogTagDelegate { 20 | public: 21 | ScreenWorker(LogTag* tag); 22 | virtual ~ScreenWorker(); 23 | 24 | void init(std::unique_ptr s); 25 | 26 | bool isOk() { 27 | std::lock_guard lock(m_mtx); 28 | if (nullptr == m_socket) { 29 | m_error = "socket is nullptr"; 30 | m_wasOk = false; 31 | } else if (!m_socket->isConnected()) { 32 | m_error = "socket is not connected"; 33 | m_wasOk = false; 34 | } else { 35 | m_wasOk = true; 36 | } 37 | return m_wasOk; 38 | } 39 | 40 | bool isOkNoLock() const { return m_wasOk; } 41 | 42 | void run(); 43 | void runNative(); 44 | void runFFmpeg(); 45 | void shutdown(); 46 | 47 | void showEditor(Thread::ThreadID tid, std::shared_ptr proc, int channel, int x, int y, 48 | std::function onHide); 49 | void hideEditor(); 50 | 51 | private: 52 | std::mutex m_mtx; 53 | std::atomic_bool m_wasOk{true}; 54 | std::unique_ptr m_socket; 55 | String m_error; 56 | 57 | // Native capturing 58 | std::shared_ptr m_currentImage, m_lastImage, m_diffImage; 59 | // FFmpeg capturing 60 | std::vector m_imageBuf; 61 | 62 | int m_width, m_widthPadded; 63 | int m_height, m_heightPadded; 64 | double m_scale; 65 | bool m_updated = false; 66 | uint16 m_imgCounter = 0; 67 | std::mutex m_currentImageLock; 68 | std::condition_variable m_currentImageCv; 69 | 70 | std::atomic_bool m_visible{false}; 71 | Processor* m_currentProc; 72 | Thread::ThreadID m_currentTid = nullptr; 73 | int m_currentChannel = 0; 74 | 75 | ENABLE_ASYNC_FUNCTORS(); 76 | }; 77 | 78 | } // namespace e47 79 | 80 | #endif /* ScreenWorker_hpp */ 81 | -------------------------------------------------------------------------------- /Server/Source/ServerSettings/DiagnosticsTab.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Kieran Coulter 6 | */ 7 | 8 | #include "DiagnosticsTab.hpp" 9 | #include "Logger.hpp" 10 | #include "Tracer.hpp" 11 | 12 | namespace e47 { 13 | 14 | DiagnosticsTab::DiagnosticsTab(bool crashReporting) { 15 | int row = 0; 16 | 17 | m_loggerLbl.setText("Logging:", NotificationType::dontSendNotification); 18 | m_loggerLbl.setBounds(getLabelBounds(row)); 19 | addAndMakeVisible(m_loggerLbl); 20 | 21 | m_logger.setBounds(getCheckBoxBounds(row)); 22 | m_logger.setToggleState(Logger::isEnabled(), NotificationType::dontSendNotification); 23 | addAndMakeVisible(m_logger); 24 | 25 | row++; 26 | 27 | m_tracerLbl.setText("Tracing (please enable to report issues):", NotificationType::dontSendNotification); 28 | m_tracerLbl.setBounds(getLabelBounds(row)); 29 | addAndMakeVisible(m_tracerLbl); 30 | 31 | m_tracer.setBounds(getCheckBoxBounds(row)); 32 | m_tracer.setToggleState(Tracer::isEnabled(), NotificationType::dontSendNotification); 33 | addAndMakeVisible(m_tracer); 34 | 35 | row++; 36 | 37 | m_crashReportingLbl.setText("Send crash reports (please enable if you have issues!):", 38 | NotificationType::dontSendNotification); 39 | m_crashReportingLbl.setBounds(getLabelBounds(row)); 40 | addAndMakeVisible(m_crashReportingLbl); 41 | 42 | m_crashReporting.setBounds(getCheckBoxBounds(row)); 43 | m_crashReporting.setToggleState(crashReporting, NotificationType::dontSendNotification); 44 | addAndMakeVisible(m_crashReporting); 45 | } 46 | 47 | void DiagnosticsTab::paint(Graphics& g) { 48 | auto bgColour = LookAndFeel::getDefaultLookAndFeel().findColour(ResizableWindow::backgroundColourId); 49 | g.setColour(bgColour); 50 | } 51 | 52 | void DiagnosticsTab::resized() {} 53 | 54 | } // namespace e47 -------------------------------------------------------------------------------- /Server/Source/ServerSettings/DiagnosticsTab.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Kieran Coulter 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | #include "TabCommon.h" 12 | 13 | namespace e47 { 14 | 15 | class DiagnosticsTab : public juce::Component 16 | { 17 | public: 18 | DiagnosticsTab(bool crashReporting); 19 | void paint (Graphics& g) override; 20 | void resized() override; 21 | bool getTracerEnabled() { return m_tracer.getToggleState(); } 22 | bool getLoggerEnabled() { return m_logger.getToggleState(); } 23 | bool getCrashReportingEnabled() { return m_crashReporting.getToggleState(); } 24 | private: 25 | ToggleButton m_tracer, m_logger, m_crashReporting; 26 | Label m_tracerLbl, m_loggerLbl, m_crashReportingLbl; 27 | }; 28 | 29 | } // namespace e47 30 | -------------------------------------------------------------------------------- /Server/Source/ServerSettings/MainTab.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Kieran Coulter 6 | */ 7 | 8 | #include "MainTab.hpp" 9 | 10 | namespace e47 { 11 | 12 | MainTab::MainTab(MainSettings mainSettings) { 13 | int row = 0; 14 | 15 | m_nameLabel.setText("Server Name:", NotificationType::dontSendNotification); 16 | m_nameLabel.setBounds(getLabelBounds(row)); 17 | addAndMakeVisible(m_nameLabel); 18 | 19 | m_nameText.setText(mainSettings.name); 20 | m_nameText.setBounds(getWideFieldBounds(row)); 21 | addAndMakeVisible(m_nameText); 22 | 23 | row++; 24 | 25 | m_idLabel.setText("Server ID:", NotificationType::dontSendNotification); 26 | m_idLabel.setBounds(getLabelBounds(row)); 27 | addAndMakeVisible(m_idLabel); 28 | 29 | String idStr(mainSettings.id); 30 | m_idTextLabel.setText(idStr, NotificationType::dontSendNotification); 31 | m_idTextLabel.setBounds(getFieldBounds(row)); 32 | m_idTextLabel.setJustificationType(Justification::right); 33 | addAndMakeVisible(m_idTextLabel); 34 | 35 | row++; 36 | 37 | String tooltip; 38 | tooltip << "Chain Isolation: Each AG plugin chain created by an AG plugin will run in a dedicated process." 39 | << newLine << newLine 40 | << "Plugin Isolation: Each plugin loaded into an AG plugin chain will run in a dedicated process."; 41 | m_sandboxLabel.setText("Sandbox Mode:", NotificationType::dontSendNotification); 42 | m_sandboxLabel.setBounds(getLabelBounds(row)); 43 | m_sandboxLabel.setTooltip(tooltip); 44 | addAndMakeVisible(m_sandboxLabel); 45 | 46 | m_sandboxMode.setBounds(getWideFieldBounds(row)); 47 | m_sandboxMode.addItem("Disabled", 1); 48 | m_sandboxMode.addItem("Chain Isolation", 2); 49 | m_sandboxMode.addItem("Plugin Isolation", 3); 50 | m_sandboxMode.setSelectedItemIndex(mainSettings.mode); 51 | m_sandboxMode.setTooltip(tooltip); 52 | addAndMakeVisible(m_sandboxMode); 53 | } 54 | 55 | void MainTab::paint(Graphics& g) { 56 | auto bgColour = LookAndFeel::getDefaultLookAndFeel().findColour(ResizableWindow::backgroundColourId); 57 | g.setColour(bgColour); 58 | } 59 | 60 | void MainTab::resized() {} 61 | 62 | } // namespace e47 -------------------------------------------------------------------------------- /Server/Source/ServerSettings/MainTab.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Kieran Coulter 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | #include "TabCommon.h" 12 | #include "Utils.hpp" 13 | 14 | namespace e47 { 15 | 16 | class MainTab : public juce::Component { 17 | public: 18 | MainTab(MainSettings mainSettings); 19 | void paint(Graphics& g) override; 20 | void resized() override; 21 | String getNameText() { return m_nameText.getText(); } 22 | String getIdText() { return m_idTextLabel.getText(); } 23 | int getSandboxSelectedIndex() { return m_sandboxMode.getSelectedItemIndex(); } 24 | 25 | private: 26 | Label m_nameLabel, m_idLabel, m_sandboxLabel, m_idTextLabel; 27 | TextEditor m_nameText; 28 | ComboBox m_sandboxMode; 29 | }; 30 | 31 | } // namespace e47 32 | -------------------------------------------------------------------------------- /Server/Source/ServerSettings/PluginFormatsTab.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Kieran Coulter 6 | */ 7 | 8 | #include "PluginFormatsTab.hpp" 9 | 10 | namespace e47 { 11 | 12 | PluginFormatsTab::PluginFormatsTab(FormatSettings formatSettings) { 13 | int row = 0; 14 | String tmpStr; 15 | 16 | #ifdef JUCE_MAC 17 | m_auLabel.setText("AudioUnit Support:", NotificationType::dontSendNotification); 18 | m_auLabel.setBounds(getLabelBounds(row)); 19 | addAndMakeVisible(m_auLabel); 20 | 21 | m_auSupport.setBounds(getCheckBoxBounds(row)); 22 | m_auSupport.setToggleState(formatSettings.au, NotificationType::dontSendNotification); 23 | addAndMakeVisible(m_auSupport); 24 | 25 | row++; 26 | #endif 27 | 28 | m_vst3Label.setText("VST3 Support:", NotificationType::dontSendNotification); 29 | m_vst3Label.setBounds(getLabelBounds(row)); 30 | addAndMakeVisible(m_vst3Label); 31 | 32 | m_vst3Support.setBounds(getCheckBoxBounds(row)); 33 | m_vst3Support.setToggleState(formatSettings.vst3, NotificationType::dontSendNotification); 34 | addAndMakeVisible(m_vst3Support); 35 | 36 | row++; 37 | 38 | tmpStr = "VST3 Custom Folders"; 39 | tmpStr << newLine << "(one folder per line):"; 40 | m_vst3CustomLabel.setText(tmpStr, NotificationType::dontSendNotification); 41 | m_vst3CustomLabel.setBounds(getLabelBounds(row)); 42 | addAndMakeVisible(m_vst3CustomLabel); 43 | 44 | m_vst3Folders.setBounds(getLargeFieldBounds(row)); 45 | m_vst3Folders.setMultiLine(true, false); 46 | m_vst3Folders.setReturnKeyStartsNewLine(true); 47 | addAndMakeVisible(m_vst3Folders); 48 | 49 | tmpStr = ""; 50 | for (auto& folder : formatSettings.vst3Folders) { 51 | tmpStr << folder << newLine; 52 | } 53 | m_vst3Folders.setText(tmpStr); 54 | 55 | row += largeFieldRows; 56 | 57 | m_vst2Label.setText("VST2 Support:", NotificationType::dontSendNotification); 58 | m_vst2Label.setBounds(getLabelBounds(row)); 59 | addAndMakeVisible(m_vst2Label); 60 | 61 | m_vst2Support.setBounds(getCheckBoxBounds(row)); 62 | m_vst2Support.setToggleState(formatSettings.vst2, NotificationType::dontSendNotification); 63 | addAndMakeVisible(m_vst2Support); 64 | 65 | row++; 66 | 67 | tmpStr = "VST2 Custom Folders"; 68 | tmpStr << newLine << "(one folder per line):"; 69 | m_vst2CustomLabel.setText(tmpStr, NotificationType::dontSendNotification); 70 | m_vst2CustomLabel.setBounds(getLabelBounds(row)); 71 | addAndMakeVisible(m_vst2CustomLabel); 72 | 73 | m_vst2Folders.setBounds(getLargeFieldBounds(row)); 74 | m_vst2Folders.setMultiLine(true, false); 75 | m_vst2Folders.setReturnKeyStartsNewLine(true); 76 | addChildAndSetID(&m_vst2Folders, "vst2fold"); 77 | 78 | tmpStr = ""; 79 | for (auto& folder : formatSettings.vst2Folders) { 80 | tmpStr << folder << newLine; 81 | } 82 | m_vst2Folders.setText(tmpStr); 83 | 84 | row += largeFieldRows; 85 | 86 | String tooltip = "If you select this, only custom folders will be scanned."; 87 | m_vst2CustomOnlyLabel.setText("Do not include VST standard folders:", NotificationType::dontSendNotification); 88 | m_vst2CustomOnlyLabel.setBounds(getLabelBounds(row)); 89 | m_vst2CustomOnlyLabel.setTooltip(tooltip); 90 | addAndMakeVisible(m_vst2CustomOnlyLabel); 91 | 92 | m_vstNoStandardFolders.setBounds(getCheckBoxBounds(row)); 93 | m_vstNoStandardFolders.setToggleState(formatSettings.vst2NoStandard, NotificationType::dontSendNotification); 94 | m_vstNoStandardFolders.setTooltip(tooltip); 95 | addAndMakeVisible(m_vstNoStandardFolders); 96 | 97 | row++; 98 | 99 | m_lv2Label.setText("LV2 Support:", NotificationType::dontSendNotification); 100 | m_lv2Label.setBounds(getLabelBounds(row)); 101 | addAndMakeVisible(m_lv2Label); 102 | 103 | m_lv2Support.setBounds(getCheckBoxBounds(row)); 104 | m_lv2Support.setToggleState(formatSettings.lv2, NotificationType::dontSendNotification); 105 | addAndMakeVisible(m_lv2Support); 106 | 107 | row++; 108 | 109 | tmpStr = "LV2 Custom Folders"; 110 | tmpStr << newLine << "(one folder per line):"; 111 | m_lv2CustomLabel.setText(tmpStr, NotificationType::dontSendNotification); 112 | m_lv2CustomLabel.setBounds(getLabelBounds(row)); 113 | addAndMakeVisible(m_lv2CustomLabel); 114 | 115 | m_lv2Folders.setBounds(getLargeFieldBounds(row)); 116 | m_lv2Folders.setMultiLine(true, false); 117 | m_lv2Folders.setReturnKeyStartsNewLine(true); 118 | addAndMakeVisible(m_lv2Folders); 119 | 120 | tmpStr = ""; 121 | for (auto& folder : formatSettings.lv2Folders) { 122 | tmpStr << folder << newLine; 123 | } 124 | m_lv2Folders.setText(tmpStr); 125 | } 126 | 127 | void PluginFormatsTab::paint(Graphics& g) { 128 | auto bgColour = LookAndFeel::getDefaultLookAndFeel().findColour(ResizableWindow::backgroundColourId); 129 | g.setColour(bgColour); 130 | } 131 | 132 | void PluginFormatsTab::resized() {} 133 | 134 | } // namespace e47 -------------------------------------------------------------------------------- /Server/Source/ServerSettings/PluginFormatsTab.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Kieran Coulter 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | #include "TabCommon.h" 12 | #include "Server.hpp" 13 | 14 | namespace e47 { 15 | 16 | class PluginFormatsTab : public juce::Component 17 | { 18 | public: 19 | PluginFormatsTab(FormatSettings formatSettings); 20 | void paint (Graphics& g) override; 21 | void resized() override; 22 | bool getAuSupport() { return m_auSupport.getToggleState(); } 23 | bool getVst3Support() { return m_vst3Support.getToggleState(); } 24 | bool getVst2Support() { return m_vst2Support.getToggleState(); } 25 | bool getLv2Support() { return m_lv2Support.getToggleState(); } 26 | bool getVstNoStandardFolders() { return m_vstNoStandardFolders.getToggleState(); } 27 | String getVst2FoldersText() { return m_vst2Folders.getText(); } 28 | String getVst3FoldersText() { return m_vst3Folders.getText(); } 29 | String getLv2FoldersText() { return m_lv2Folders.getText(); } 30 | private: 31 | ToggleButton m_auSupport, m_vst2Support, m_vst3Support, m_lv2Support, m_vstNoStandardFolders; 32 | TextEditor m_vst2Folders, m_vst3Folders, m_lv2Folders; 33 | Label m_auLabel, m_vst3Label, m_vst3CustomLabel, m_vst2Label, m_vst2CustomLabel, m_vst2CustomOnlyLabel, 34 | m_lv2Label, m_lv2CustomLabel; 35 | }; 36 | 37 | } // namespace e47 -------------------------------------------------------------------------------- /Server/Source/ServerSettings/ScreenCapturingTab.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Kieran Coulter 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | #include "TabCommon.h" 12 | 13 | namespace e47 { 14 | 15 | class ScreenCapturingTab : public juce::Component 16 | { 17 | public: 18 | ScreenCapturingTab(CaptureSettings captureSettings); 19 | void paint (Graphics& g) override; 20 | void resized() override; 21 | int getModeSelectedId() { return m_screenCapturingMode.getSelectedId(); } 22 | int getQualitySelectedId() { return m_screenCapturingQuality.getSelectedId(); } 23 | bool getWindowsOnTopEnabled() { return m_pluginWindowsOnTop.getToggleState(); } 24 | bool getDiffDetectionEnabled() { return m_screenDiffDetection.getToggleState(); } 25 | String getJpgQualityText() { return m_screenJpgQuality.getText(); } 26 | String getMouseOffsetXYText() { return m_screenMouseOffsetXY.getText(); } 27 | private: 28 | ComboBox m_screenCapturingMode, m_screenCapturingQuality; 29 | ToggleButton m_pluginWindowsOnTop, m_screenDiffDetection; 30 | TextEditor m_screenJpgQuality, m_screenMouseOffsetXY; 31 | Label m_screenCapturingModeLbl, m_screenCapturingQualityLbl, m_pluginWindowsOnTopLbl, 32 | m_screenDiffDetectionLbl, m_screenJpgQualityLbl, m_screenMouseOffsetXYLbl; 33 | }; 34 | 35 | } // namespace e47 36 | -------------------------------------------------------------------------------- /Server/Source/ServerSettings/StartupTab.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Kieran Coulter 6 | */ 7 | 8 | #include "StartupTab.hpp" 9 | #include "Defaults.hpp" 10 | 11 | namespace e47 { 12 | 13 | StartupTab::StartupTab(bool scanForPlugins) { 14 | String tooltip; 15 | int row = 0; 16 | 17 | tooltip << "Enter the IDs of servers that you want to start automatically. An ID must be a number in the range of " 18 | "0-31. Example: 0,1,4-8" 19 | << newLine << newLine << "Note: You have to restart manually for taking changes into effect."; 20 | 21 | m_autoStartLbl.setText("Autostart servers with IDs:", NotificationType::dontSendNotification); 22 | m_autoStartLbl.setTooltip(tooltip); 23 | m_autoStartLbl.setBounds(getLabelBounds(row)); 24 | addAndMakeVisible(m_autoStartLbl); 25 | 26 | auto cfg = configParseFile(Defaults::getConfigFileName(Defaults::ConfigServerStartup)); 27 | if (jsonHasValue(cfg, "IDs")) { 28 | m_idText.setText(jsonGetValue(cfg, "IDs", String())); 29 | } 30 | m_idText.setInputFilter(new TextEditor::LengthAndCharacterRestriction(103, "0123456789-,"), true); 31 | m_idText.setBounds(getWideFieldBounds(row)); 32 | m_idText.setTooltip(tooltip); 33 | addAndMakeVisible(m_idText); 34 | 35 | row++; 36 | 37 | m_scanForPluginsLbl.setText("Scan for Plugins at Startup:", NotificationType::dontSendNotification); 38 | m_scanForPluginsLbl.setBounds(getLabelBounds(row)); 39 | addAndMakeVisible(m_scanForPluginsLbl); 40 | 41 | m_scanForPlugins.setBounds(getCheckBoxBounds(row)); 42 | m_scanForPlugins.setToggleState(scanForPlugins, NotificationType::dontSendNotification); 43 | addAndMakeVisible(m_scanForPlugins); 44 | } 45 | 46 | void StartupTab::paint(Graphics& g) { 47 | auto bgColour = LookAndFeel::getDefaultLookAndFeel().findColour(ResizableWindow::backgroundColourId); 48 | g.setColour(bgColour); 49 | } 50 | 51 | void StartupTab::resized() {} 52 | 53 | } // namespace e47 -------------------------------------------------------------------------------- /Server/Source/ServerSettings/StartupTab.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Kieran Coulter 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | #include "TabCommon.h" 12 | 13 | namespace e47 { 14 | 15 | class StartupTab : public juce::Component 16 | { 17 | public: 18 | StartupTab(bool scanForPlugins); 19 | void paint (Graphics& g) override; 20 | void resized() override; 21 | bool getScanForPlugins() { return m_scanForPlugins.getToggleState(); } 22 | private: 23 | ToggleButton m_scanForPlugins; 24 | TextEditor m_idText; 25 | Label m_autoStartLbl, m_scanForPluginsLbl; 26 | }; 27 | 28 | } // namespace e47 29 | -------------------------------------------------------------------------------- /Server/Source/ServerSettings/TabCommon.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Kieran Coulter 6 | */ 7 | 8 | #pragma once 9 | 10 | static int totalWidth = 600; 11 | 12 | #ifdef JUCE_LINUX 13 | static int totalHeight = 100; 14 | static int extraBorderTB = 20; 15 | #else 16 | static int totalHeight = 80; 17 | static int extraBorderTB = 0; 18 | #endif 19 | 20 | static int borderLR = 15; // left/right border 21 | static int borderTB = 15; // top/bottom border 22 | static int rowHeight = 30; 23 | static int fieldWidth = 50; 24 | static int wideFieldWidth = 250; 25 | static int fieldHeight = 25; 26 | static int labelWidth = 350; 27 | static int labelHeight = 35; 28 | static int headerHeight = 18; 29 | static int checkBoxWidth = 25; 30 | static int checkBoxHeight = 25; 31 | static int largeFieldRows = 2; 32 | static int largeFieldWidth = 250; 33 | static int largeFieldHeight = largeFieldRows * rowHeight - 10; 34 | 35 | static auto getLabelBounds = [](int r) { 36 | return juce::Rectangle(borderLR, extraBorderTB + borderTB + r * rowHeight, labelWidth, labelHeight); 37 | }; 38 | static auto getFieldBounds = [](int r) { 39 | return juce::Rectangle(totalWidth - fieldWidth - borderLR, extraBorderTB + borderTB + r * rowHeight + 3, 40 | fieldWidth, fieldHeight); 41 | }; 42 | static auto getWideFieldBounds = [](int r) { 43 | return juce::Rectangle(totalWidth - wideFieldWidth - borderLR, extraBorderTB + borderTB + r * rowHeight + 3, 44 | wideFieldWidth, fieldHeight); 45 | }; 46 | static auto getCheckBoxBounds = [](int r) { 47 | return juce::Rectangle(totalWidth - checkBoxWidth - borderLR, extraBorderTB + borderTB + r * rowHeight + 3, 48 | checkBoxWidth, checkBoxHeight); 49 | }; 50 | static auto getLargeFieldBounds = [](int r) { 51 | return juce::Rectangle(totalWidth - largeFieldWidth - borderLR, extraBorderTB + borderTB + r * rowHeight + 3, 52 | largeFieldWidth, largeFieldHeight); 53 | }; 54 | static auto getHeaderBounds = [](int r) { 55 | return juce::Rectangle(borderLR, extraBorderTB + borderTB + r * rowHeight + 7, totalWidth - borderLR * 2, 56 | headerHeight); 57 | }; 58 | 59 | struct MainSettings { 60 | const String& name; 61 | int id; 62 | int mode; 63 | }; 64 | 65 | struct FormatSettings { 66 | bool au; 67 | bool vst3; 68 | bool vst2; 69 | bool vst2NoStandard; 70 | bool lv2; 71 | const StringArray& vst3Folders; 72 | const StringArray& vst2Folders; 73 | const StringArray& lv2Folders; 74 | }; 75 | 76 | struct CaptureSettings { 77 | bool capOff; 78 | bool localMode; 79 | bool capFFmpeg; 80 | bool diffDetect; 81 | bool winOnTop; 82 | int FFmpegQuality; 83 | int offsetX; 84 | int offsetY; 85 | float screenQuality; 86 | }; -------------------------------------------------------------------------------- /Server/Source/ServerSettingsWindow.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef ServerSettingsWindow_hpp 9 | #define ServerSettingsWindow_hpp 10 | 11 | #include 12 | 13 | #include "Utils.hpp" 14 | #include "ServerSettings/MainTab.hpp" 15 | #include "ServerSettings/PluginFormatsTab.hpp" 16 | #include "ServerSettings/ScreenCapturingTab.hpp" 17 | #include "ServerSettings/StartupTab.hpp" 18 | #include "ServerSettings/DiagnosticsTab.hpp" 19 | 20 | namespace e47 { 21 | 22 | class App; 23 | 24 | class ServerSettingsWindow : public DocumentWindow, public LogTag { 25 | public: 26 | explicit ServerSettingsWindow(App* app); 27 | ~ServerSettingsWindow() override; 28 | 29 | void closeButtonPressed() override; 30 | 31 | private: 32 | App* m_app; 33 | 34 | TextButton m_saveButton; 35 | 36 | #if OLD_LAYOUT 37 | std::vector> m_components; 38 | TextEditor m_idText, m_nameText, m_screenJpgQuality, m_vst2Folders, m_vst3Folders, m_lv2Folders, 39 | m_screenMouseOffsetXY; 40 | ToggleButton m_auSupport, m_vst3Support, m_vst2Support, m_lv2Support, m_screenDiffDetection, m_scanForPlugins, 41 | m_tracer, m_logger, m_vstNoStandardFolders, m_pluginWindowsOnTop, m_crashReporting; 42 | Label m_screenJpgQualityLbl, m_screenDiffDetectionLbl, m_screenCapturingQualityLbl, m_pluginWindowsOnTopLbl; 43 | ComboBox m_screenCapturingMode, m_screenCapturingQuality, m_sandboxMode; 44 | TooltipWindow m_tooltipWindow; 45 | #else 46 | TabbedComponent m_tabbedComponent; 47 | MainTab m_mainTab; 48 | PluginFormatsTab m_pluginFormatsTab; 49 | ScreenCapturingTab m_screenCapturingTab; 50 | StartupTab m_startupTab; 51 | DiagnosticsTab m_diagnosticsTab; 52 | #endif 53 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ServerSettingsWindow) 54 | }; 55 | 56 | } // namespace e47 57 | 58 | #endif /* ServerSettingsWindow_hpp */ 59 | -------------------------------------------------------------------------------- /Server/Source/ServiceResponder.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | * 7 | * Based on https://github.com/mjansson/mdns 8 | */ 9 | 10 | #ifndef ServiceResponder_hpp 11 | #define ServiceResponder_hpp 12 | 13 | #include 14 | 15 | #include "Utils.hpp" 16 | #include "mDNSConnector.hpp" 17 | 18 | namespace e47 { 19 | 20 | class ServiceResponder : public Thread, public LogTag { 21 | public: 22 | static std::unique_ptr m_inst; 23 | 24 | ServiceResponder(int port, int id, const String& hostname, Uuid uuid, bool localMode); 25 | ~ServiceResponder() override; 26 | 27 | void run() override; 28 | 29 | int handleRecord(int sock, const struct sockaddr* from, size_t addrlen, mdns_entry_type_t entry, uint16_t query_id, 30 | uint16_t rtype, uint16_t rclass, uint32_t ttl, const void* data, size_t size, size_t name_offset, 31 | size_t name_length, size_t record_offset, size_t record_length, void* user_data); 32 | 33 | static void initialize(int port, int id, const String& hostname, Uuid uuid, bool localMode); 34 | static void cleanup(); 35 | static void setHostName(const String& hostname); 36 | static const String& getHostName(); 37 | 38 | private: 39 | int m_port; 40 | int m_id; 41 | String m_hostname; 42 | Uuid m_uuid; 43 | bool m_localMode; 44 | mDNSConnector m_connector; 45 | 46 | char m_sendBuffer[1024]; 47 | char m_nameBuffer[1024]; 48 | }; 49 | 50 | } // namespace e47 51 | 52 | #endif /* ServiceResponder_hpp */ 53 | -------------------------------------------------------------------------------- /Server/Source/StatisticsWindow.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef StatisticsWindow_hpp 9 | #define StatisticsWindow_hpp 10 | 11 | #include 12 | 13 | #include "Utils.hpp" 14 | 15 | namespace e47 { 16 | 17 | class App; 18 | 19 | class StatisticsWindow : public DocumentWindow, public LogTag { 20 | public: 21 | StatisticsWindow(App* app); 22 | ~StatisticsWindow() override; 23 | 24 | void closeButtonPressed() override; 25 | 26 | class HirozontalLine : public Component { 27 | public: 28 | HirozontalLine(juce::Rectangle bounds) { setBounds(bounds); } 29 | void paint(Graphics& g) override; 30 | }; 31 | 32 | private: 33 | App* m_app; 34 | std::vector> m_components; 35 | Label m_cpu, m_totalWorkers, m_activeWorkers, m_plugins, m_audioRPS, m_audioPTavg, m_audioPTmin, m_audioPTmax, 36 | m_audioPT95th, m_audioBytesOut, m_audioBytesIn; 37 | bool m_sandboxing; 38 | 39 | class Updater : public Thread, public LogTagDelegate { 40 | public: 41 | Updater(LogTag* tag) : Thread("StatsUpdater"), LogTagDelegate(tag) { 42 | traceScope(); 43 | initAsyncFunctors(); 44 | } 45 | 46 | ~Updater() override { 47 | traceScope(); 48 | stopAsyncFunctors(); 49 | } 50 | 51 | void set(std::function fn) { m_fn = fn; } 52 | 53 | void run() override { 54 | traceScope(); 55 | while (!threadShouldExit()) { 56 | runOnMsgThreadAsync([this] { m_fn(); }); 57 | // Relax 58 | sleepExitAware(1000); 59 | } 60 | } 61 | 62 | private: 63 | std::function m_fn; 64 | 65 | ENABLE_ASYNC_FUNCTORS(); 66 | }; 67 | Updater m_updater; 68 | 69 | void addLabel(const String& txt, juce::Rectangle bounds); 70 | 71 | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(StatisticsWindow) 72 | }; 73 | 74 | } // namespace e47 75 | 76 | #endif /* StatisticsWindow_hpp */ 77 | -------------------------------------------------------------------------------- /Server/Source/Worker.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef Worker_hpp 9 | #define Worker_hpp 10 | 11 | #include 12 | #include 13 | 14 | #include "AudioWorker.hpp" 15 | #include "Message.hpp" 16 | #include "ScreenWorker.hpp" 17 | #include "Utils.hpp" 18 | 19 | namespace e47 { 20 | 21 | class Server; 22 | 23 | class Worker : public Thread, public LogTag { 24 | public: 25 | static std::atomic_uint32_t count; 26 | static std::atomic_uint32_t runCount; 27 | 28 | Worker(std::shared_ptr masterSocket, const HandshakeRequest& cfg, int sandboxModeRuntime = 0); 29 | 30 | ~Worker() override; 31 | void run() override; 32 | 33 | void shutdown(); 34 | 35 | void handleMessage(std::shared_ptr> msg); 36 | void handleMessage(std::shared_ptr> msg); 37 | void handleMessage(std::shared_ptr> msg); 38 | void handleMessage(std::shared_ptr> msg); 39 | void handleMessage(std::shared_ptr> msg, bool fromMaster = false); 40 | void handleMessage(std::shared_ptr> msg); 41 | void handleMessage(std::shared_ptr> msg); 42 | void handleMessage(std::shared_ptr> msg); 43 | void handleMessage(std::shared_ptr> msg); 44 | void handleMessage(std::shared_ptr> msg); 45 | void handleMessage(std::shared_ptr> msg); 46 | void handleMessage(std::shared_ptr> msg); 47 | void handleMessage(std::shared_ptr> msg); 48 | void handleMessage(std::shared_ptr> msg); 49 | void handleMessage(std::shared_ptr> msg); 50 | void handleMessage(std::shared_ptr> msg); 51 | void handleMessage(std::shared_ptr> msg); 52 | void handleMessage(std::shared_ptr> msg); 53 | void handleMessage(std::shared_ptr> msg); 54 | void handleMessage(std::shared_ptr> msg); 55 | void handleMessage(std::shared_ptr> msg); 56 | void handleMessage(std::shared_ptr> msg); 57 | void handleMessage(std::shared_ptr> msg); 58 | void handleMessage(std::shared_ptr> msg); 59 | void handleMessage(std::shared_ptr> msg); 60 | 61 | private: 62 | std::shared_ptr m_masterSocket; 63 | std::unique_ptr m_cmdIn; 64 | std::unique_ptr m_cmdOut; 65 | std::mutex m_cmdOutMtx; 66 | HandshakeRequest m_cfg; 67 | std::shared_ptr m_audio; 68 | std::shared_ptr m_screen; 69 | std::atomic_int m_activeEditorIdx{-1}; 70 | MessageFactory m_msgFactory; 71 | bool m_noPluginListFilter = false; 72 | int m_sandboxModeRuntime = 0; 73 | 74 | struct KeyWatcher : KeyListener { 75 | Worker* worker; 76 | KeyWatcher(Worker* w) : worker(w) {} 77 | bool keyPressed(const KeyPress& kp, Component*); 78 | }; 79 | 80 | struct ClipboardTracker : Timer { 81 | String current; 82 | Worker* worker; 83 | 84 | ClipboardTracker(Worker* w) : worker(w) {} 85 | ~ClipboardTracker() override { stopTimer(); } 86 | 87 | void start() { startTimer(200); } 88 | void stop() { stopTimer(); } 89 | 90 | void timerCallback() override { 91 | auto val = SystemClipboard::getTextFromClipboard(); 92 | if (val != current) { 93 | current = val; 94 | worker->sendClipboard(val); 95 | } 96 | } 97 | }; 98 | 99 | std::unique_ptr m_keyWatcher; 100 | std::unique_ptr m_clipboardTracker; 101 | 102 | void sendKeys(const std::vector& keysToPress); 103 | void sendClipboard(const String& val); 104 | void sendParamValueChange(int idx, int channel, int paramIdx, float val); 105 | void sendParamGestureChange(int idx, int channel, int paramIdx, bool guestureIsStarting); 106 | void sendStatusChange(int idx, bool ok, const String& err); 107 | void sendHideEditor(int idx); 108 | void sendError(const String& error); 109 | 110 | ENABLE_ASYNC_FUNCTORS(); 111 | }; 112 | 113 | } // namespace e47 114 | 115 | #endif /* Worker_hpp */ 116 | -------------------------------------------------------------------------------- /Tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | project(AUDIOGRIDDER_TESTS VERSION 1.0.0) 4 | 5 | aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/Source AG_SOURCES_TESTS) 6 | aux_source_directory(${CMAKE_SOURCE_DIR}/Common/Source AG_SOURCES_COMMON) 7 | aux_source_directory(${CMAKE_SOURCE_DIR}/Server/Source AG_SOURCES_SERVER) 8 | aux_source_directory(${CMAKE_SOURCE_DIR}/Plugin/Source AG_SOURCES_PLUGIN) 9 | list(REMOVE_ITEM AG_SOURCES_COMMON "${CMAKE_SOURCE_DIR}/Common/Source/TraceReader.cpp") 10 | 11 | include_directories(${CMAKE_SOURCE_DIR}) 12 | include_directories(${CMAKE_SOURCE_DIR}/Tests/Source) 13 | include_directories(${CMAKE_SOURCE_DIR}/Server/Source) 14 | #include_directories(${CMAKE_CURRENT_SOURCE_DIR}/Source/Server) 15 | 16 | macro(ag_add_testrunner type) 17 | set(BINNAME "testrunner-${type}") 18 | message(STATUS "Target ${BINNAME} enabled.") 19 | juce_add_console_app(${BINNAME} PLUGINHOST_AU TRUE) 20 | juce_generate_juce_header(${BINNAME}) 21 | 22 | target_sources(${BINNAME} PRIVATE 23 | ${AG_SOURCES_TESTS} 24 | ${AG_SOURCES_COMMON}) 25 | 26 | target_compile_features(${BINNAME} PRIVATE cxx_std_14) 27 | 28 | target_compile_definitions(${BINNAME} 29 | PRIVATE 30 | JUCE_USE_CURL=0 31 | JUCE_WEB_BROWSER=0 32 | JUCE_USE_OGGVORBIS=1 33 | JUCE_MODAL_LOOPS_PERMITTED=1 34 | PLUGIN_DIR="${CMAKE_BINARY_DIR}/lib" 35 | AG_VERSION="${AG_VERSION}" 36 | AG_UNIT_TESTS 37 | AG_TESTS_DATA="${CMAKE_SOURCE_DIR}/TestsData" 38 | BOOST_ALL_NO_LIB) 39 | 40 | if(${type} STREQUAL "server") 41 | target_sources(${BINNAME} PRIVATE ${AG_SOURCES_SERVER}) 42 | target_compile_definitions(${BINNAME} PRIVATE 43 | AG_UNIT_TEST_SERVER 44 | AG_SERVER 45 | JUCE_PLUGINHOST_VST3=1 46 | JUCE_PLUGINHOST_VST=1) 47 | elseif(${type} STREQUAL "fx") 48 | target_sources(${BINNAME} PRIVATE ${AG_SOURCES_PLUGIN}) 49 | target_compile_definitions(${BINNAME} PRIVATE 50 | AG_UNIT_TEST_PLUGIN_FX 51 | AG_PLUGIN) 52 | endif() 53 | 54 | set(LINK_LIBRARIES 55 | ${FFMPEG_LIBRARIES} 56 | ${WEBP_LIBRARIES} 57 | juce::juce_core 58 | juce::juce_gui_basics 59 | juce::juce_gui_extra 60 | juce::juce_graphics 61 | juce::juce_events 62 | juce::juce_audio_basics 63 | juce::juce_audio_formats 64 | juce::juce_audio_processors 65 | juce::juce_recommended_config_flags 66 | juce::juce_recommended_warning_flags 67 | juce::juce_recommended_lto_flags) 68 | 69 | if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") 70 | list(APPEND LINK_LIBRARIES "-framework AVFoundation -framework CoreMedia") 71 | if(AG_MACOS_TARGET STRGREATER_EQUAL 10.8) 72 | list(APPEND LINK_LIBRARIES "-framework VideoToolbox") 73 | endif() 74 | endif() 75 | 76 | if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 77 | list(INSERT LINK_LIBRARIES 0 "-Wl,--start-group") 78 | list(APPEND LINK_LIBRARIES "-Wl,--end-group") 79 | endif() 80 | 81 | target_link_libraries(${BINNAME} 82 | PRIVATE 83 | ${LINK_LIBRARIES}) 84 | 85 | endmacro() 86 | 87 | ag_add_testrunner(server) 88 | ag_add_testrunner(fx) 89 | -------------------------------------------------------------------------------- /Tests/Source/Main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 SyncDNA Inc. 3 | * 4 | * Author: Andreas Pohl 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "Utils.hpp" 12 | 13 | #ifdef AG_UNIT_TEST_SERVER 14 | #include "Server/ScanPluginsTest.hpp" 15 | #include "Server/ProcessorChainTest.hpp" 16 | #include "Server/SandboxPluginTest.hpp" 17 | #include "Server/MultiMonoTest.hpp" 18 | #endif 19 | 20 | #ifdef AG_UNIT_TEST_PLUGIN_FX 21 | #include "Plugin/AudioStreamerTest.hpp" 22 | #endif 23 | 24 | namespace e47 { 25 | 26 | class ConsoleUnitTestRunner : public UnitTestRunner { 27 | void logMessage(const String& msg) override { 28 | setLogTagStatic("testrunner"); 29 | logln(msg); 30 | } 31 | }; 32 | 33 | class ConsoleApp : public JUCEApplicationBase { 34 | public: 35 | ConsoleApp() : testsThread(nullptr, "TestRunner") {} 36 | ~ConsoleApp() override {} 37 | 38 | void initialise(const String&) override { 39 | Logger::initialize(); 40 | Tracer::initialize("Tests", "tests_"); 41 | Tracer::setEnabled(true); 42 | 43 | int runs = 1; 44 | 45 | auto args = getCommandLineParameterArray(); 46 | for (int i = 0; i < args.size(); i++) { 47 | if (args[i] == "-runs" && i + 1 < args.size()) { 48 | runs = args[i + 1].getIntValue(); 49 | i++; 50 | } 51 | } 52 | 53 | testsThread.fn = [this, runs] { 54 | setLogTagStatic("testrunner"); 55 | 56 | int tests = 0; 57 | int fails = 0; 58 | int remainingRuns = runs; 59 | 60 | do { 61 | ConsoleUnitTestRunner runner; 62 | runner.setAssertOnFailure(true); 63 | 64 | std::mutex mtx; 65 | std::condition_variable cv; 66 | 67 | FnThread timeoutThread( 68 | [&] { 69 | while (!Thread::currentThreadShouldExit()) { 70 | std::unique_lock lock(mtx); 71 | if (cv.wait_for(lock, 5min) == std::cv_status::timeout) { 72 | logln("test timeout"); 73 | raise(SIGABRT); 74 | } 75 | } 76 | }, 77 | "TimeoutThread", true); 78 | 79 | auto resetTimeout = [&] { 80 | std::lock_guard lock(mtx); 81 | cv.notify_one(); 82 | }; 83 | 84 | for (auto& test : UnitTest::getAllTests()) { 85 | runner.runTests({test}); 86 | resetTimeout(); 87 | for (int i = 0; i < runner.getNumResults(); i++) { 88 | tests++; 89 | if (runner.getResult(i)->failures > 0) { 90 | fails++; 91 | } 92 | } 93 | } 94 | 95 | timeoutThread.signalThreadShouldExit(); 96 | resetTimeout(); 97 | 98 | logln("Summary: " << (tests - fails) << " / " << tests << " test groups completed successfully"); 99 | } while (--remainingRuns > 0 && fails == 0); 100 | 101 | if (fails > 0) { 102 | setApplicationReturnValue(1); 103 | } 104 | 105 | quit(); 106 | }; 107 | testsThread.startThread(); 108 | } 109 | 110 | const String getApplicationName() override { return "sdna_test_runner"; } 111 | const String getApplicationVersion() override { return ""; } 112 | bool moreThanOneInstanceAllowed() override { return false; } 113 | void anotherInstanceStarted(const String&) override {} 114 | 115 | void suspended() override {} 116 | void resumed() override {} 117 | void systemRequestedQuit() override {} 118 | void unhandledException(const std::exception*, const String&, int) override {} 119 | 120 | void shutdown() override { 121 | testsThread.stopThread(-1); 122 | Tracer::cleanup(); 123 | Logger::cleanup(); 124 | } 125 | 126 | private: 127 | FnThread testsThread; 128 | }; 129 | 130 | } // namespace e47 131 | 132 | START_JUCE_APPLICATION(e47::ConsoleApp) 133 | -------------------------------------------------------------------------------- /Tests/Source/Server/MultiMonoTest.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef _MULTIMONOTEST_HPP_ 9 | #define _MULTIMONOTEST_HPP_ 10 | 11 | #include 12 | 13 | #include "TestsHelper.hpp" 14 | #include "Server.hpp" 15 | #include "ProcessorChain.hpp" 16 | #include "Processor.hpp" 17 | #include "ChannelSet.hpp" 18 | 19 | namespace e47 { 20 | 21 | class MultiMonoTest : UnitTest { 22 | public: 23 | MultiMonoTest() : UnitTest("MultiMono") {} 24 | 25 | void runTest() override { 26 | beginTest("Setup"); 27 | 28 | double sampleRate = 48000.0; 29 | int blockSize = 512, chIn = 2, chOut = 2, chSc = 2; 30 | 31 | LogTag testTag("test"); 32 | String err; 33 | 34 | auto pc = std::make_unique(&testTag, ProcessorChain::createBussesProperties(chIn, chOut, chSc), 35 | HandshakeRequest()); 36 | pc->updateChannels(chIn, chOut, chSc); 37 | pc->prepareToPlay(sampleRate, blockSize); 38 | 39 | KnownPluginList pl; 40 | json playouts; 41 | Server::loadKnownPluginList(pl, playouts, 999); 42 | 43 | String id = "VST3-66155f87"; 44 | auto desc = Processor::findPluginDescritpion(id, pl); 45 | auto proc = std::make_shared(*pc, id, sampleRate, blockSize, false); 46 | expect(proc->load("|", "Multi-Mono", 0, err, desc.get()), "Load failed: " + err); 47 | pc->addProcessor(proc); 48 | expect(proc->getLatencySamples() == 60); 49 | expect(pc->getLatencySamples() == 60); 50 | 51 | TestsHelper::TestPlayHead phead; 52 | pc->setPlayHead(&phead); 53 | 54 | ChannelSet cs(0, 0, 2); 55 | MidiBuffer midi; 56 | AudioBuffer buf(chIn + chSc, blockSize); 57 | 58 | beginTest("All channels on"); 59 | 60 | setBufferSamples(buf, 0.5f); 61 | pc->processBlock(buf, midi); 62 | checkBufferSamples2(buf, 0.0f, 0, 2, 0, 60); 63 | checkBufferSamples2(buf, 0.5f, 0, 2, 60, blockSize - 60); 64 | 65 | buf.clear(); 66 | pc->processBlock(buf, midi); 67 | checkBufferSamples2(buf, 0.5f, 0, 2, 0, 60); // leftover 68 | checkBufferSamples2(buf, 0.0f, 0, 2, 60, blockSize - 60); 69 | 70 | beginTest("Right OFF"); 71 | 72 | cs.setOutputActive(0); 73 | proc->setMonoChannels(cs.toInt()); 74 | 75 | setBufferSamples(buf, 0.5f); 76 | pc->processBlock(buf, midi); 77 | checkBufferSamples2(buf, 0.0f, 0, 2, 0, 60); 78 | checkBufferSamples2(buf, 0.5f, 0, 2, 60, blockSize - 60); 79 | 80 | buf.clear(); 81 | pc->processBlock(buf, midi); 82 | checkBufferSamples2(buf, 0.5f, 0, 2, 0, 60); // leftover 83 | checkBufferSamples2(buf, 0.0f, 0, 2, 60, blockSize - 60); 84 | 85 | beginTest("Left OFF"); 86 | 87 | cs.setOutputRangeActive(false); 88 | cs.setOutputActive(1); 89 | proc->setMonoChannels(cs.toInt()); 90 | 91 | setBufferSamples(buf, 0.5f); 92 | pc->processBlock(buf, midi); 93 | checkBufferSamples2(buf, 0.0f, 0, 2, 0, 60); 94 | checkBufferSamples2(buf, 0.5f, 0, 2, 60, blockSize - 60); 95 | 96 | buf.clear(); 97 | pc->processBlock(buf, midi); 98 | checkBufferSamples2(buf, 0.5f, 0, 2, 0, 60); // leftover 99 | checkBufferSamples2(buf, 0.0f, 0, 2, 60, blockSize - 60); 100 | 101 | pc->delProcessor(0); 102 | pc->releaseResources(); 103 | } 104 | }; 105 | 106 | static MultiMonoTest multiMonoTest; 107 | 108 | } // namespace e47 109 | 110 | #endif // _MULTIMONOTEST_HPP_ 111 | -------------------------------------------------------------------------------- /Tests/Source/Server/ProcessorChainTest.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef _PROCESSORCHAINTEST_HPP_ 9 | #define _PROCESSORCHAINTEST_HPP_ 10 | 11 | #include 12 | 13 | #include "TestsHelper.hpp" 14 | #include "Server.hpp" 15 | #include "ProcessorChain.hpp" 16 | #include "Processor.hpp" 17 | 18 | namespace e47 { 19 | 20 | class ProcessorChainTest : UnitTest { 21 | public: 22 | ProcessorChainTest() : UnitTest("ProcessorChain") {} 23 | 24 | void runTest() override { 25 | runTestBasic(); 26 | runLoadPlugins(); 27 | } 28 | 29 | void runTestBasic() { 30 | beginTest("Basic tests"); 31 | 32 | int chIn = 2, chOut = 2, chSc = 0; 33 | 34 | LogTag testTag("test"); 35 | 36 | auto pc = std::make_unique(&testTag, ProcessorChain::createBussesProperties(chIn, chOut, chSc), 37 | HandshakeRequest()); 38 | expect(pc->updateChannels(chIn, chOut, chSc)); 39 | pc.reset(); 40 | 41 | chIn = chOut = 64; 42 | pc = std::make_unique(&testTag, ProcessorChain::createBussesProperties(chIn, chOut, chSc), 43 | HandshakeRequest()); 44 | expect(pc->updateChannels(chIn, chOut, chSc)); 45 | pc.reset(); 46 | 47 | chSc = 2; 48 | pc = std::make_unique(&testTag, ProcessorChain::createBussesProperties(chIn, chOut, chSc), 49 | HandshakeRequest()); 50 | expect(pc->updateChannels(chIn, chOut, chSc)); 51 | pc.reset(); 52 | 53 | chIn = chOut = chSc = 2; 54 | pc = std::make_unique(&testTag, ProcessorChain::createBussesProperties(chIn, chOut, chSc), 55 | HandshakeRequest()); 56 | expect(pc->updateChannels(chIn, chOut, chSc)); 57 | } 58 | 59 | void runLoadPlugins() { 60 | beginTest("Load plugins"); 61 | 62 | double sampleRate = 48000.0; 63 | int blockSize = 512, chIn = 2, chOut = 2, chSc = 2; 64 | 65 | LogTag testTag("test"); 66 | 67 | auto pc = std::make_unique(&testTag, ProcessorChain::createBussesProperties(chIn, chOut, chSc), 68 | HandshakeRequest()); 69 | pc->updateChannels(chIn, chOut, chSc); 70 | pc->prepareToPlay(sampleRate, blockSize); 71 | 72 | KnownPluginList pl; 73 | json playouts; 74 | Server::loadKnownPluginList(pl, playouts, 999); 75 | 76 | for (auto desc : pl.getTypes()) { 77 | auto id = Processor::createPluginID(desc); 78 | logMessage("Loading " + desc.descriptiveName + " with ID " + id); 79 | auto proc = std::make_shared(*pc, id, sampleRate, blockSize, false); 80 | String err; 81 | expect(proc->load({}, {}, 0, err, &desc), "Load failed: " + err); 82 | pc->addProcessor(std::move(proc)); 83 | } 84 | 85 | expect(pc->getSize() == (size_t)pl.getNumTypes()); 86 | 87 | while (pc->getSize() > 0) { 88 | pc->delProcessor(0); 89 | } 90 | } 91 | }; 92 | 93 | static ProcessorChainTest processorChainTest; 94 | 95 | } // namespace e47 96 | 97 | #endif // _PROCESSORCHAINTEST_HPP_ 98 | -------------------------------------------------------------------------------- /Tests/Source/Server/SandboxPluginTest.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef _SANDBOXPLUGINTEST_HPP_ 9 | #define _SANDBOXPLUGINTEST_HPP_ 10 | 11 | #include 12 | 13 | #include "TestsHelper.hpp" 14 | #include "Defaults.hpp" 15 | #include "Server.hpp" 16 | #include "Processor.hpp" 17 | #include "ProcessorChain.hpp" 18 | #include "ChannelSet.hpp" 19 | 20 | namespace e47 { 21 | 22 | class SandboxPluginTest : UnitTest { 23 | public: 24 | SandboxPluginTest() : UnitTest("Sandbox (Plugin Isolation)") {} 25 | 26 | void runTest() override { 27 | logMessage("Setting up server config"); 28 | auto serverConfig = Defaults::getConfigFileName(Defaults::ConfigServer, {{"id", "999"}}); 29 | configWriteFile(serverConfig, {{"ID", 999}, 30 | {"NAME", "Test"}, 31 | {"CrashReporting", false}, 32 | {"SandboxMode", Server::SANDBOX_PLUGIN}, 33 | {"Tracer", true}}); 34 | 35 | beginTest("Load plugins"); 36 | 37 | double sampleRate = 48000.0; 38 | int blockSize = 512, chIn = 2, chOut = 2, chSc = 2; 39 | ChannelSet activeChannels; 40 | activeChannels.setNumChannels(chIn + chSc, chOut); 41 | activeChannels.setRangeActive(); 42 | HandshakeRequest cfg = {AG_PROTOCOL_VERSION, chIn, chOut, chSc, sampleRate, blockSize, false, 0, 0, 0, 43 | activeChannels.toInt(), 0}; 44 | 45 | LogTag testTag("test"); 46 | 47 | auto pc = 48 | std::make_unique(&testTag, ProcessorChain::createBussesProperties(chIn, chOut, chSc), cfg); 49 | pc->setProcessingPrecision(AudioProcessor::singlePrecision); 50 | pc->updateChannels(chIn, chOut, chSc); 51 | pc->prepareToPlay(sampleRate, blockSize); 52 | 53 | KnownPluginList pl; 54 | json playouts; 55 | Server::loadKnownPluginList(pl, playouts, 999); 56 | 57 | for (auto desc : pl.getTypes()) { 58 | auto id = Processor::createPluginID(desc); 59 | logMessage("Loading " + desc.descriptiveName + " with ID " + id); 60 | auto proc = std::make_shared(*pc, id, sampleRate, blockSize, true); 61 | String err; 62 | expect(proc->load({}, {}, 0, err, &desc), "Load failed: " + err); 63 | expect(proc->isClient()); 64 | expect(proc->isLoaded()); 65 | pc->addProcessor(proc); 66 | // bypass the plugin in the sandbox so we can do some audio tests 67 | proc->getClient()->suspendProcessingRemoteOnly(true); 68 | } 69 | 70 | expect(pc->getSize() == (size_t)pl.getNumTypes()); 71 | 72 | beginTest("Send audio"); 73 | 74 | int latency = pc->getLatencySamples(); 75 | 76 | TestsHelper::TestPlayHead phead; 77 | pc->setPlayHead(&phead); 78 | 79 | AudioBuffer buf(chIn + chSc, blockSize); 80 | setBufferSamples(buf, 0.5f); 81 | MidiBuffer midi; 82 | pc->processBlock(buf, midi); 83 | 84 | if (latency == 0) { 85 | checkBufferSamples(buf, 0.5f); 86 | } else { 87 | checkBufferSamples2(buf, 0.0f, 0, buf.getNumChannels(), 0, latency); 88 | checkBufferSamples2(buf, 0.5f, 0, buf.getNumChannels(), latency, buf.getNumSamples() - latency); 89 | } 90 | 91 | beginTest("Unload plugins"); 92 | 93 | while (pc->getSize() > 0) { 94 | logMessage("Unloading " + pc->getProcessor(0)->getName()); 95 | pc->delProcessor(0); 96 | } 97 | 98 | pc->releaseResources(); 99 | } 100 | }; 101 | 102 | static SandboxPluginTest sandboxPluginTest; 103 | 104 | } // namespace e47 105 | 106 | #endif // _SANDBOXPLUGINTEST_HPP_ 107 | -------------------------------------------------------------------------------- /Tests/Source/Server/ScanPluginsTest.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef _SCANPLUGINSTEST_HPP_ 9 | #define _SCANPLUGINSTEST_HPP_ 10 | 11 | #include 12 | 13 | #include "TestsHelper.hpp" 14 | #include "Server.hpp" 15 | #include "Utils.hpp" 16 | 17 | namespace e47 { 18 | 19 | class ScanPluginsTest : UnitTest { 20 | public: 21 | ScanPluginsTest() : UnitTest("Scan Plugins") {} 22 | 23 | void runTest() override { 24 | beginTest("Scan"); 25 | 26 | std::vector vst2plugins, vst3plugins; 27 | auto datadir = TestsHelper::getTestsDataDir(); 28 | 29 | #if JUCE_MAC 30 | vst2plugins.push_back(datadir.getChildFile("dreverb_1.0_mac_86_64") 31 | .getChildFile("VST2") 32 | .getChildFile("DReverb.vst") 33 | .getFullPathName()); 34 | vst3plugins.push_back(datadir.getChildFile("dreverb_1.0_mac_86_64") 35 | .getChildFile("VST3") 36 | .getChildFile("DReverb.vst3") 37 | .getFullPathName()); 38 | #elif JUCE_WINDOWS 39 | vst2plugins.push_back(datadir.getChildFile("dreverb_1.0_win_86_64") 40 | .getChildFile("VST2") 41 | .getChildFile("DReverb.dll") 42 | .getFullPathName()); 43 | vst3plugins.push_back(datadir.getChildFile("dreverb_1.0_win_86_64") 44 | .getChildFile("VST3") 45 | .getChildFile("DReverb.vst3") 46 | .getFullPathName()); 47 | #endif 48 | vst3plugins.push_back(datadir.getChildFile("2RuleSynth.vst3").getFullPathName()); 49 | vst3plugins.push_back(datadir.getChildFile("LoudMax.vst3").getFullPathName()); 50 | 51 | for (auto& p : vst2plugins) { 52 | runOnMsgThreadSync([&] { expect(Server::scanPlugin(p, "VST", 999)); }); 53 | } 54 | 55 | for (auto& p : vst3plugins) { 56 | runOnMsgThreadSync([&] { expect(Server::scanPlugin(p, "VST3", 999)); }); 57 | } 58 | } 59 | }; 60 | 61 | static ScanPluginsTest scanPluginsTest; 62 | 63 | } // namespace e47 64 | 65 | #endif // _SCANPLUGINSTEST_HPP_ 66 | -------------------------------------------------------------------------------- /Tests/Source/TestsHelper.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2022 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef _TESTSHELPER_HPP_ 9 | #define _TESTSHELPER_HPP_ 10 | 11 | #include 12 | 13 | #ifndef AG_TESTS_DATA 14 | #define AG_TESTS_DATA "" 15 | #endif 16 | 17 | namespace e47 { 18 | namespace TestsHelper { 19 | 20 | inline File getTestsDataDir() { 21 | #if JUCE_MAC 22 | return File(AG_TESTS_DATA).getChildFile("macos"); 23 | #elif JUCE_WINDOWS 24 | return File(AG_TESTS_DATA).getChildFile("windows"); 25 | #else 26 | return {}; 27 | #endif 28 | } 29 | 30 | struct TestPlayHead : AudioPlayHead { 31 | AudioPlayHead::PositionInfo posInfo; 32 | TestPlayHead() { posInfo.setTimeInSamples(makeOptional(0)); } 33 | Optional getPosition() const { return makeOptional(posInfo); } 34 | }; 35 | 36 | #define setBufferSamples(b, v) \ 37 | do { \ 38 | for (int c = 0; c < b.getNumChannels(); c++) { \ 39 | for (int s = 0; s < b.getNumSamples(); s++) { \ 40 | b.setSample(c, s, v); \ 41 | } \ 42 | } \ 43 | } while (0) 44 | 45 | #define checkBufferSamples(b, v) \ 46 | do { \ 47 | bool __fail = false; \ 48 | for (int __c = 0; __c < b.getNumChannels() && !__fail; __c++) { \ 49 | for (int __s = 0; __s < b.getNumSamples() && !__fail; __s++) { \ 50 | auto __x = b.getSample(0, __s); \ 51 | __fail = __x != v; \ 52 | expect(!__fail, "sample at channel " + String(__c) + ", position " + String(__s) + " should be " + \ 53 | String(v) + " but is " + String(__x)); \ 54 | } \ 55 | } \ 56 | } while (0) 57 | 58 | #define checkBufferSamples2(b, v, ch, numChannels, sample, numSamples) \ 59 | do { \ 60 | bool __fail = false; \ 61 | for (int __c = ch; __c < ch + numChannels && !__fail; __c++) { \ 62 | for (int __s = sample; __s < sample + numSamples && !__fail; __s++) { \ 63 | auto __x = b.getSample(0, __s); \ 64 | __fail = abs(__x - v) > 0.1; \ 65 | expect(!__fail, "sample at channel " + String(__c) + ", position " + String(__s) + " should be " + \ 66 | String(v) + " but is " + String(__x)); \ 67 | } \ 68 | } \ 69 | } while (0) 70 | 71 | } // namespace TestsHelper 72 | } // namespace e47 73 | 74 | #endif // _TESTSHELPER_HPP_ 75 | -------------------------------------------------------------------------------- /cmake/FindFFmpeg.cmake: -------------------------------------------------------------------------------- 1 | # Macro to find header and lib directories 2 | # Based on: https://sources.debian.org/src/acoustid-fingerprinter/0.6-6/cmake/modules/FindFFmpeg.cmake/ 3 | # example: FFMPEG_FIND(AVFORMAT avformat avformat.h) 4 | macro(FFMPEG_FIND varname shortname headername) 5 | 6 | find_path(FFMPEG_${varname}_INCLUDE_DIRS lib${shortname}/${headername} 7 | PATHS 8 | ${FFMPEG_ROOT}/include 9 | $ENV{FFMPEG_DIR}/include 10 | /usr/include/x86_64-linux-gnu 11 | /usr/local/include 12 | /usr/include/ 13 | NO_DEFAULT_PATH 14 | DOC "Location of FFMPEG Headers") 15 | 16 | if(AG_ENABLE_DYNAMIC_LINKING) 17 | set(LIB_NAMES lib${shortname}.dylib lib${shortname}.so ${shortname}.dll) 18 | else() 19 | set(LIB_NAMES lib${shortname}.a ${shortname}.lib) 20 | endif() 21 | 22 | find_library(FFMPEG_${varname}_LIBRARIES 23 | NAMES ${LIB_NAMES} 24 | PATHS 25 | ${FFMPEG_ROOT}/lib 26 | $ENV{FFMPEG_DIR}/lib 27 | /usr/lib/x86_64-linux-gnu 28 | /usr/local/lib 29 | /usr/lib 30 | NO_DEFAULT_PATH 31 | DOC "Location of FFMPEG Libs") 32 | 33 | if(FFMPEG_${varname}_LIBRARIES AND FFMPEG_${varname}_INCLUDE_DIRS) 34 | set(FFMPEG_${varname}_FOUND 1) 35 | message(STATUS "Found ${shortname}") 36 | message(STATUS " includes : ${FFMPEG_${varname}_INCLUDE_DIRS}") 37 | message(STATUS " libraries: ${FFMPEG_${varname}_LIBRARIES}") 38 | else() 39 | message(STATUS "Could not find ${shortname}") 40 | endif() 41 | 42 | endmacro(FFMPEG_FIND) 43 | 44 | set(FFMPEG_ROOT "$ENV{FFMPEG_DIR}" CACHE PATH "Location of FFMPEG") 45 | 46 | FFMPEG_FIND(LIBAVFORMAT avformat avformat.h) 47 | FFMPEG_FIND(LIBAVDEVICE avdevice avdevice.h) 48 | FFMPEG_FIND(LIBAVCODEC avcodec avcodec.h) 49 | FFMPEG_FIND(LIBAVUTIL avutil avutil.h) 50 | FFMPEG_FIND(LIBAVFILTER avfilter avfilter.h) 51 | FFMPEG_FIND(LIBSWSCALE swscale swscale.h) 52 | FFMPEG_FIND(LIBSWRESAMPLE swresample swresample.h) 53 | 54 | set(FFMPEG_FOUND "NO") 55 | if (FFMPEG_LIBAVFORMAT_FOUND AND 56 | FFMPEG_LIBAVDEVICE_FOUND AND 57 | FFMPEG_LIBAVCODEC_FOUND AND 58 | FFMPEG_LIBAVUTIL_FOUND AND 59 | FFMPEG_LIBAVFILTER_FOUND AND 60 | FFMPEG_LIBSWSCALE_FOUND AND 61 | FFMPEG_LIBSWRESAMPLE_FOUND) 62 | 63 | set(FFMPEG_FOUND "YES") 64 | set(FFMPEG_INCLUDE_DIRS ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS}) 65 | set(FFMPEG_LIBRARY_DIRS ${FFMPEG_LIBAVFORMAT_LIBRARY_DIRS}) 66 | set(FFMPEG_LIBRARIES 67 | ${FFMPEG_LIBAVFORMAT_LIBRARIES} 68 | ${FFMPEG_LIBAVDEVICE_LIBRARIES} 69 | ${FFMPEG_LIBAVCODEC_LIBRARIES} 70 | ${FFMPEG_LIBAVUTIL_LIBRARIES} 71 | ${FFMPEG_LIBAVFILTER_LIBRARIES} 72 | ${FFMPEG_LIBSWSCALE_LIBRARIES} 73 | ${FFMPEG_LIBSWRESAMPLE_LIBRARIES}) 74 | else() 75 | message(STATUS "Could not find FFMPEG") 76 | endif() 77 | -------------------------------------------------------------------------------- /cmake/FindWebP.cmake: -------------------------------------------------------------------------------- 1 | # Macro to find header and lib directories 2 | # Based on: https://sources.debian.org/src/acoustid-fingerprinter/0.6-6/cmake/modules/FindFFmpeg.cmake/ 3 | # example: FFMPEG_FIND(AVFORMAT avformat avformat.h) 4 | macro(WEBP_FIND varname shortname headername) 5 | 6 | find_path(WEBP_${varname}_INCLUDE_DIRS webp/${headername} 7 | PATHS 8 | ${FFMPEG_ROOT}/include 9 | $ENV{FFMPEG_DIR}/include 10 | /usr/include/x86_64-linux-gnu 11 | /usr/local/include 12 | /usr/include/ 13 | NO_DEFAULT_PATH 14 | DOC "Location of FFMPEG Headers") 15 | 16 | if(AG_ENABLE_DYNAMIC_LINKING) 17 | set(LIB_NAMES lib${shortname}.dylib lib${shortname}.so ${shortname}.dll) 18 | else() 19 | set(LIB_NAMES lib${shortname}.a ${shortname}.lib) 20 | endif() 21 | 22 | find_library(WEBP_${varname}_LIBRARIES 23 | NAMES ${LIB_NAMES} 24 | PATHS 25 | ${FFMPEG_ROOT}/lib 26 | $ENV{FFMPEG_DIR}/lib 27 | /usr/lib/x86_64-linux-gnu 28 | /usr/local/lib 29 | /usr/lib 30 | NO_DEFAULT_PATH 31 | DOC "Location of FFMPEG Libs") 32 | 33 | if(WEBP_${varname}_LIBRARIES AND WEBP_${varname}_INCLUDE_DIRS) 34 | set(WEBP_${varname}_FOUND 1) 35 | message(STATUS "Found ${shortname}") 36 | message(STATUS " includes : ${WEBP_${varname}_INCLUDE_DIRS}") 37 | message(STATUS " libraries: ${WEBP_${varname}_LIBRARIES}") 38 | else() 39 | message(STATUS "Could not find ${shortname}") 40 | endif() 41 | 42 | endmacro(WEBP_FIND) 43 | 44 | set(WEBP_ROOT "$ENV{WEBP_DIR}" CACHE PATH "Location of WebP") 45 | 46 | WEBP_FIND(LIBWEBP webp encode.h) 47 | WEBP_FIND(LIBWEBPMUX webpmux mux.h) 48 | 49 | set(WEBP_FOUND "NO") 50 | if (WEBP_LIBWEBP_FOUND AND WEBP_LIBWEBPMUX_FOUND) 51 | set(WEBP_FOUND "YES") 52 | set(WEBP_INCLUDE_DIRS ${WEBP_LIBWEBP_INCLUDE_DIRS}) 53 | set(WEBP_LIBRARY_DIRS ${WEBP_LIBWEBP_LIBRARY_DIRS}) 54 | set(WEBP_LIBRARIES 55 | ${WEBP_LIBWEBP_LIBRARIES} 56 | ${WEBP_LIBWEBPMUX_LIBRARIES}) 57 | else() 58 | message(STATUS "Could not find WebP") 59 | endif() 60 | -------------------------------------------------------------------------------- /images/overview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apohl79/audiogridder/76dd9a006a86110b9245bc44f7a6c78f3d6a5c9f/images/overview.jpg -------------------------------------------------------------------------------- /images/plugin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apohl79/audiogridder/76dd9a006a86110b9245bc44f7a6c78f3d6a5c9f/images/plugin.jpg -------------------------------------------------------------------------------- /images/server.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apohl79/audiogridder/76dd9a006a86110b9245bc44f7a6c78f3d6a5c9f/images/server.jpg -------------------------------------------------------------------------------- /package/AudioGridderServer.iss.in: -------------------------------------------------------------------------------- 1 | [Setup] 2 | AppName=AudioGridder 3 | AppVersion=#STR_VER# 4 | AppPublisher="Andreas Pohl, e47" 5 | AppPublisherURL="https://audiogridder.com" 6 | DefaultDirName={commonpf64}\AudioGridderServer 7 | DefaultGroupName=AudioGridder 8 | OutputBaseFilename=AudioGridderServer_#STR_VER# 9 | 10 | [Files] 11 | Source: "..\build-windows-x86_64\bin\AudioGridderServer.exe"; DestDir: "{app}"; Flags: ignoreversion 12 | Source: "..\build-windows-x86_64\bin\crashpad_handler.exe"; DestDir: "{app}"; Flags: ignoreversion 13 | 14 | [Icons] 15 | Name: "{autoprograms}\AudioGridderServer"; Filename: "{app}\AudioGridderServer.exe" 16 | 17 | [Code] 18 | function NextButtonClick(PageId: Integer): Boolean; 19 | var 20 | Running: Boolean; 21 | FindRec: TFindRec; 22 | FileName: String; 23 | begin 24 | Result := True; 25 | if (PageId = wpReady) then begin 26 | FileName := ExpandConstant('{userappdata}\AudioGridder\audiogridderserver*.running'); 27 | if FindFirst(FileName, FindRec) then 28 | try 29 | Running := FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY = 0; 30 | finally 31 | FindClose(FindRec); 32 | end; 33 | if (Running) then begin 34 | MsgBox('AudioGridder Server seems to be running. Please terminate it before you continue!', mbError, MB_OK); 35 | end; 36 | end; 37 | end; 38 | -------------------------------------------------------------------------------- /package/PlugIn.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apohl79/audiogridder/76dd9a006a86110b9245bc44f7a6c78f3d6a5c9f/package/PlugIn.ico -------------------------------------------------------------------------------- /package/VERSION.num: -------------------------------------------------------------------------------- 1 | 1.2.0 2 | -------------------------------------------------------------------------------- /package/Version.hpp.in: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020 Andreas Pohl 3 | * Licensed under MIT (https://github.com/apohl79/audiogridder/blob/master/COPYING) 4 | * 5 | * Author: Andreas Pohl 6 | */ 7 | 8 | #ifndef Version_hpp 9 | #define Version_hpp 10 | 11 | #define AUDIOGRIDDER_VERSION "#STR_VER#" 12 | #define AUDIOGRIDDER_VERSIONW L"#STR_VER#" 13 | #define AUDIOGRIDDER_VERSION_NUM "#NUM_VER#" 14 | #define AUDIOGRIDDER_VERSION_NUMW L"#NUM_VER#" 15 | #define AUDIOGRIDDER_BUILD_DATE "#STR_BUILD_DATE#" 16 | 17 | #endif /* Version_hpp */ 18 | -------------------------------------------------------------------------------- /package/archiveWin.bat.in: -------------------------------------------------------------------------------- 1 | @echo archiving files... 2 | copy /B ..\build-win-10-x86_64\Server\AudioGridderServer_artefacts\RelWithDebInfo\AudioGridderServer.exe ..\..\Archive\Builds\#STR_VER#\win 3 | copy /B ..\build-win-10-x86_64\Server\AudioGridderServer_artefacts\RelWithDebInfo\AudioGridderServer.pdb ..\..\Archive\Builds\#STR_VER#\win 4 | copy /B ..\build-win-10-x86_64\PluginTray\AudioGridderPluginTray_artefacts\RelWithDebInfo\AudioGridderPluginTray.exe ..\..\Archive\Builds\#STR_VER#\win 5 | copy /B ..\build-win-10-x86_64\PluginTray\AudioGridderPluginTray_artefacts\RelWithDebInfo\AudioGridderPluginTray.pdb ..\..\Archive\Builds\#STR_VER#\win 6 | 7 | copy /B ..\build-win-10-x86_64\Plugin\AudioGridderFx_artefacts\RelWithDebInfo\VST3\AudioGridder.vst3\Contents\x86_64-win\AudioGridder.vst3 ..\..\Archive\Builds\#STR_VER#\win 8 | copy /B ..\build-win-10-x86_64\Plugin\AudioGridderFx_artefacts\RelWithDebInfo\VST3\AudioGridder.vst3\Contents\x86_64-win\AudioGridder.pdb ..\..\Archive\Builds\#STR_VER#\win\vst3 9 | copy /B ..\build-win-10-x86_64\Plugin\AudioGridderFx_artefacts\RelWithDebInfo\VST\AudioGridder.dll ..\..\Archive\Builds\#STR_VER#\win 10 | copy /B ..\build-win-10-x86_64\Plugin\AudioGridderFx_artefacts\RelWithDebInfo\VST\AudioGridder.pdb ..\..\Archive\Builds\#STR_VER#\win\vst 11 | xcopy /E /I ..\build-win-10-x86_64\Plugin\AudioGridderFx_artefacts\RelWithDebInfo\AAX ..\..\Archive\Builds\#STR_VER#\win 12 | 13 | copy /B ..\build-win-10-x86_64\Plugin\AudioGridderInst_artefacts\RelWithDebInfo\VST3\AudioGridderInst.vst3\Contents\x86_64-win\AudioGridderInst.vst3 ..\..\Archive\Builds\#STR_VER#\win 14 | copy /B ..\build-win-10-x86_64\Plugin\AudioGridderInst_artefacts\RelWithDebInfo\VST3\AudioGridderInst.vst3\Contents\x86_64-win\AudioGridderInst.pdb ..\..\Archive\Builds\#STR_VER#\win\vst3 15 | copy /B ..\build-win-10-x86_64\Plugin\AudioGridderInst_artefacts\RelWithDebInfo\VST\AudioGridderInst.dll ..\..\Archive\Builds\#STR_VER#\win 16 | copy /B ..\build-win-10-x86_64\Plugin\AudioGridderInst_artefacts\RelWithDebInfo\VST\AudioGridderInst.pdb ..\..\Archive\Builds\#STR_VER#\win\vst 17 | xcopy /E /I ..\build-win-10-x86_64\Plugin\AudioGridderInst_artefacts\RelWithDebInfo\AAX ..\..\Archive\Builds\#STR_VER#\win 18 | 19 | copy /B ..\build-win-10-x86_64\Plugin\AudioGridderMidi_artefacts\RelWithDebInfo\VST3\AudioGridderMidi.vst3\Contents\x86_64-win\AudioGridderMidi.vst3 ..\..\Archive\Builds\#STR_VER#\win 20 | copy /B ..\build-win-10-x86_64\Plugin\AudioGridderMidi_artefacts\RelWithDebInfo\VST3\AudioGridderMidi.vst3\Contents\x86_64-win\AudioGridderMidi.pdb ..\..\Archive\Builds\#STR_VER#\win\vst3 21 | copy /B ..\build-win-10-x86_64\Plugin\AudioGridderMidi_artefacts\RelWithDebInfo\VST\AudioGridderMidi.dll ..\..\Archive\Builds\#STR_VER#\win 22 | copy /B ..\build-win-10-x86_64\Plugin\AudioGridderMidi_artefacts\RelWithDebInfo\VST\AudioGridderMidi.pdb ..\..\Archive\Builds\#STR_VER#\win\vst 23 | xcopy /E /I ..\build-win-10-x86_64\Plugin\AudioGridderMidi_artefacts\RelWithDebInfo\AAX ..\..\Archive\Builds\#STR_VER#\win 24 | 25 | @echo compressing package... 26 | cd build 27 | del AudioGridder_#STR_VER#-Windows-Installers.zip 28 | powershell "Compress-Archive -Path AudioGridderPlugin_#STR_VER#.exe,AudioGridderServer_#STR_VER#.exe -DestinationPath AudioGridder_#STR_VER#-Windows-Installers.zip" 29 | 30 | @echo cleaning up... 31 | del AudioGridderPlugin_#STR_VER#.exe AudioGridderServer_#STR_VER#.exe 32 | -------------------------------------------------------------------------------- /package/audiogridderserver.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Version=1.0 3 | Name=AudioGridder Server 4 | Comment=AudioGridder Server Backend 5 | Exec=/usr/local/bin/AudioGridderServer 6 | Icon=/usr/local/share/audiogridder/icon64.png 7 | Type=Application 8 | Terminal=false 9 | Categories=Audio; 10 | Keywords=Audio; 11 | -------------------------------------------------------------------------------- /package/buildLinux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -rf build-linux-x86_64 4 | cmake -B build-linux-x86_64 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DFFMPEG_ROOT=/mnt/audio/ag-deps-linux-x86_64 5 | cmake --build build-linux-x86_64 -j6 6 | 7 | VERSION=$(cat package/VERSION) 8 | 9 | mkdir -p package/build/vst 10 | mkdir -p package/build/vst3 11 | mkdir -p package/build/tray 12 | 13 | cp build-linux-x86_64/Plugin/AudioGridderFx_artefacts/RelWithDebInfo/VST/libAudioGridder.so package/build/vst/AudioGridder.so 14 | cp build-linux-x86_64/Plugin/AudioGridderInst_artefacts/RelWithDebInfo/VST/libAudioGridderInst.so package/build/vst/AudioGridderInst.so 15 | cp build-linux-x86_64/Plugin/AudioGridderMidi_artefacts/RelWithDebInfo/VST/libAudioGridderMidi.so package/build/vst/AudioGridderMidi.so 16 | cp -r build-linux-x86_64/Plugin/AudioGridderFx_artefacts/RelWithDebInfo/VST3/AudioGridder.vst3 package/build/vst3/ 17 | cp -r build-linux-x86_64/Plugin/AudioGridderInst_artefacts/RelWithDebInfo/VST3/AudioGridderInst.vst3 package/build/vst3/ 18 | cp -r build-linux-x86_64/Plugin/AudioGridderMidi_artefacts/RelWithDebInfo/VST3/AudioGridderMidi.vst3 package/build/vst3/ 19 | cp build-linux-x86_64/PluginTray/AudioGridderPluginTray_artefacts/RelWithDebInfo/AudioGridderPluginTray package/build/tray/ 20 | 21 | cp package/build/vst/* ../Archive/Builds/$VERSION/linux 22 | cp -r package/build/vst3/* ../Archive/Builds/$VERSION/linux 23 | cp -r package/build/tray/* ../Archive/Builds/$VERSION/linux 24 | 25 | cd package/build 26 | zip -r AudioGridder_$VERSION-Linux.zip vst vst3 tray 27 | zip -j AudioGridder_$VERSION-Linux.zip ../install-trayapp-linux.sh 28 | rm -rf vst vst3 tray 29 | -------------------------------------------------------------------------------- /package/buildMac.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VERSION=$(cat package/VERSION) 4 | 5 | function package() { 6 | PROJECT=$1 7 | PKG=$2 8 | TARGET=$3 9 | packagesbuild --package-version "$VERSION" $PROJECT 10 | mv $PKG $TARGET 11 | echo "Created $TARGET" 12 | } 13 | 14 | function build() { 15 | os=$1 16 | arch=$2 17 | target=$3 18 | toolchain=$4 19 | dev=$5 20 | builddir=build-$os-$target-$arch 21 | buildtype=RelWithDebInfo 22 | 23 | if [ $dev -gt 0 ]; then 24 | builddir=build-dev 25 | buildtype=Debug 26 | fi 27 | 28 | echo "setting toolchain..." 29 | toolchain_bak="$(xcode-select -p)" 30 | sudo xcode-select -s $toolchain 31 | 32 | rm -rf $builddir 33 | 34 | cmake -B $builddir -DCMAKE_BUILD_TYPE=$buildtype -DAG_DEPS_ROOT=$HOME/audio/ag-deps-$os-$target-$arch -DCMAKE_OSX_ARCHITECTURES=$arch -DAG_MACOS_TARGET=$target 35 | cmake --build $builddir -j12 36 | 37 | if [ -n "$AG_ENABLE_SENTRY" ]; then 38 | echo "copying crashpad..." 39 | cp $HOME/audio/ag-deps-$os-$target-$arch/bin/crashpad_handler $builddir/bin 40 | fi 41 | 42 | echo "restoring toolchain..." 43 | sudo xcode-select -s $toolchain_bak 44 | 45 | if [ $dev -eq 0 ]; then 46 | 47 | if [ -n "$(which packagesbuild)" ]; then 48 | echo 49 | 50 | macos_target="" 51 | if [ "$target" == "10.7" ]; then 52 | macos_target="-$target" 53 | fi 54 | 55 | package package/AudioGridderPlugin$macos_target-$arch.pkgproj package/build/AudioGridderPlugin.pkg package/build/AudioGridderPlugin_${VERSION}_macOS$macos_target-$arch.pkg 56 | package package/AudioGridderServer$macos_target-$arch.pkgproj package/build/AudioGridderServer.pkg package/build/AudioGridderServer_${VERSION}_macOS$macos_target-$arch.pkg 57 | fi 58 | 59 | rsync -a build-$os-$target-$arch/bin/ ../Archive/Builds/$VERSION/$os$macos_target-$arch/ 60 | rsync -a build-$os-$target-$arch/lib/ ../Archive/Builds/$VERSION/$os$macos_target-$arch/ 61 | fi 62 | } 63 | 64 | if [ "$1" == "dev" ]; then 65 | echo 66 | echo "--- DEV BUILD ---" 67 | echo 68 | # macOS 10.8 X86_64 69 | build macos x86_64 10.8 /Library/Developer/10/CommandLineTools 1 70 | else 71 | # macOS 10.8 X86_64 72 | build macos x86_64 10.8 /Library/Developer/10/CommandLineTools 0 73 | 74 | # macOS 10.7 X86_64 75 | build macos x86_64 10.7 /Library/Developer/10/CommandLineTools 0 76 | 77 | # macOS 11.1 ARM64 78 | build macos arm64 11.1 /Library/Developer/CommandLineTools 0 79 | 80 | package/createUniversalBinaries.sh 81 | rsync -a build-macos-universal/bin/ ../Archive/Builds/$VERSION/macos-universal/ 82 | rsync -a build-macos-universal/lib/ ../Archive/Builds/$VERSION/macos-universal/ 83 | 84 | package package/AudioGridderPlugin-universal.pkgproj package/build/AudioGridderPlugin.pkg package/build/AudioGridderPlugin_${VERSION}_macOS-universal.pkg 85 | package package/AudioGridderServer-universal.pkgproj package/build/AudioGridderServer.pkg package/build/AudioGridderServer_${VERSION}_macOS-universal.pkg 86 | 87 | cd package/build 88 | zip AudioGridder_$VERSION-MacOS-Installers.zip AudioGridderPlugin_$VERSION_*.pkg AudioGridderServer_$VERSION_*.pkg 89 | rm AudioGridderPlugin_$VERSION_*.pkg AudioGridderServer_$VERSION_*.pkg 90 | cd - 91 | fi 92 | -------------------------------------------------------------------------------- /package/buildTraceReader.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang++ -g -std=c++14 -O3 -lboost_program_options -o package/build/tracereader Common/Source/TraceReader.cpp 3 | -------------------------------------------------------------------------------- /package/buildWin.bat: -------------------------------------------------------------------------------- 1 | cd .. 2 | del /F /S /Q build-win-10-x86_64 3 | cmake -B build-win-10-x86_64 -A x64 -DFFMPEG_ROOT=z:/ag-deps-win-x86_64 4 | cmake --build build-win-10-x86_64 --config RelWithDebInfo 5 | 6 | cd package 7 | "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" /Obuild AudioGridderPlugin.iss 8 | "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" /Obuild AudioGridderServer.iss 9 | 10 | call archiveWin.bat 11 | -------------------------------------------------------------------------------- /package/coresPerms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ -d /cores ]; then 3 | chmod o+w /cores 4 | fi 5 | -------------------------------------------------------------------------------- /package/createUniversalBinaries.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SRC_X86=build-macos-10.8-x86_64 4 | SRC_ARM=build-macos-11.1-arm64 5 | TARGET=build-macos-universal 6 | 7 | if [ ! -d $TARGET ]; then 8 | mkdir -p $TARGET 9 | fi 10 | 11 | function runlipo() { 12 | FILE=$1 13 | echo $FILE 14 | FILE_ARM=$(echo $FILE | sed "s,$TARGET,$SRC_ARM,") 15 | lipo -create $FILE $FILE_ARM -output $FILE 16 | } 17 | 18 | function create() { 19 | if [ -d $TARGET/$1 ]; then 20 | rm -rf $TARGET/$1 21 | fi 22 | mkdir -p $TARGET/$1 23 | rsync -a $SRC_X86/$1/ $TARGET/$1/ 24 | for FILE in $(find $TARGET/$1 -type f | grep MacOS | grep -v __Pace_Eden); do 25 | runlipo $FILE 26 | done 27 | } 28 | 29 | sudo xcode-select -s /Library/Developer/CommandLineTools 30 | 31 | echo "creating universal binaries..." 32 | create bin 33 | create lib 34 | runlipo $TARGET/bin/crashpad_handler 35 | -------------------------------------------------------------------------------- /package/desktop.ini: -------------------------------------------------------------------------------- 1 | [.ShellClassInfo] 2 | IconResource=PlugIn.ico,0 3 | ;For compatibility with Windows XP 4 | IconFile=PlugIn.ico 5 | IconIndex=0 6 | -------------------------------------------------------------------------------- /package/format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function format() { 4 | if [ "$(basename $f)" != "json.hpp" ]; then 5 | dos2unix $f 6 | clang-format --verbose -i $f 7 | fi 8 | } 9 | 10 | if [ -n "$1" ] && [ -f $1 ]; then 11 | f=$1 12 | format $f 13 | exit 14 | fi 15 | 16 | DIR=. 17 | if [ ! -d $DIR/Plugin ]; then 18 | DIR=.. 19 | fi 20 | if [ -n "$1" ]; then 21 | DIR=$1 22 | fi 23 | 24 | for f in $(find $DIR/Plugin/Source -name "*.[ch]pp"); do 25 | format $f 26 | done 27 | for f in $(find $DIR/PluginTray/Source -name "*.[ch]pp"); do 28 | format $f 29 | done 30 | for f in $(find $DIR/Server/Source -name "*.[ch]pp"); do 31 | format $f 32 | done 33 | for f in $(find $DIR/Common/Source -name "*.[ch]pp"); do 34 | format $f 35 | done 36 | for f in $(find $DIR/Tests/Source -name "*.[ch]pp"); do 37 | format $f 38 | done 39 | -------------------------------------------------------------------------------- /package/install-trayapp-linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | install bin/AudioGridderPluginTray /usr/local/bin 3 | install bin/crashpad_handler /usr/local/bin 4 | -------------------------------------------------------------------------------- /package/install_linux_plugin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function check_dir() { 4 | d=$1 5 | sudo=$2 6 | if [ ! -d $d ]; then 7 | $sudo mkdir -p $d 8 | fi 9 | } 10 | 11 | function inst() { 12 | f=$1 13 | t=$2 14 | sudo=$3 15 | TARGET=$t/$(basename $f) 16 | echo "installing $TARGET" 17 | if [ -d $f ]; then 18 | if [ -d "$t/$TARGET" ]; then 19 | $sudo rm -rf "$t/$TARGET" 20 | fi 21 | $sudo cp -r $f $t/ 22 | else 23 | $sudo install $f $TARGET 24 | fi 25 | } 26 | 27 | echo 28 | echo "AudioGridder Plugin Installer" 29 | echo "=============================" 30 | echo 31 | echo "Version: #version#" 32 | echo 33 | 34 | VST_DIR_DEFAULT="$HOME/.vst" 35 | VST3_DIR_DEFAULT="$HOME/.vst3" 36 | 37 | SHARE_DIR="/usr/local/share/audiogridder" 38 | 39 | echo -n "VST Target Directory [$VST_DIR_DEFAULT]: " 40 | read -r VST_DIR 41 | if [ -z "$VST_DIR" ]; then 42 | VST_DIR=$VST_DIR_DEFAULT 43 | fi 44 | 45 | echo -n "VST3 Target Directory [$VST3_DIR_DEFAULT]: " 46 | read -r VST3_DIR 47 | if [ -z "$VST3_DIR" ]; then 48 | VST3_DIR=$VST3_DIR_DEFAULT 49 | fi 50 | 51 | echo 52 | 53 | VST_DIR=$(echo "echo $VST_DIR" | bash) 54 | VST3_DIR=$(echo "echo $VST3_DIR" | bash) 55 | 56 | check_dir $VST_DIR 57 | check_dir $VST3_DIR 58 | check_dir $SHARE_DIR sudo 59 | 60 | for f in ./vst/*; do 61 | inst $f $VST_DIR 62 | done 63 | 64 | for f in ./vst3/*; do 65 | inst $f $VST3_DIR 66 | done 67 | 68 | inst bin/AudioGridderPluginTray $SHARE_DIR sudo 69 | inst bin/crashpad_handler $SHARE_DIR sudo 70 | -------------------------------------------------------------------------------- /package/install_linux_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function check_dir() { 4 | d=$1 5 | sudo=$2 6 | if [ ! -d $d ]; then 7 | $sudo mkdir -p $d 8 | fi 9 | } 10 | 11 | function inst() { 12 | f=$1 13 | t=$2 14 | sudo=$3 15 | TARGET=$t/$(basename $f) 16 | echo "installing $TARGET" 17 | if [ -d $f ]; then 18 | if [ -d "$t/$TARGET" ]; then 19 | $sudo rm -rf "$t/$TARGET" 20 | fi 21 | $sudo cp -r $f $t/ 22 | else 23 | $sudo install $f $TARGET 24 | fi 25 | } 26 | 27 | echo 28 | echo "AudioGridder Server Installer" 29 | echo "=============================" 30 | echo 31 | echo "Version: #version#" 32 | echo 33 | 34 | BIN_DIR="/usr/local/bin" 35 | SHARE_DIR="/usr/local/share/audiogridder" 36 | APPS_DIR="/usr/local/share/applications" 37 | 38 | check_dir $BIN_DIR sudo 39 | check_dir $SHARE_DIR sudo 40 | check_dir $APPS_DIR sudo 41 | 42 | inst bin/AudioGridderServer $BIN_DIR sudo 43 | inst bin/crashpad_handler $SHARE_DIR sudo 44 | inst resources/icon64.png $SHARE_DIR sudo 45 | inst resources/audiogridderserver.desktop $APPS_DIR sudo 46 | -------------------------------------------------------------------------------- /package/license_MIT.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020-2023 Andreas Pohl 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /package/setversion.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DEFAULT_VERSION="1.2.0" 4 | DEFAULT_VERSION_STR="dev-build" 5 | 6 | if [ "$1" = "-h" ]; then 7 | echo "Usage: $0 " 8 | exit 1 9 | fi 10 | 11 | if [ "$1" == "-cleardate" ]; then 12 | DATE="" 13 | shift 14 | else 15 | DATE=$(date) 16 | fi 17 | 18 | if [ "$1" == "-setdate" ]; then 19 | shift 20 | DATE_ORG=$DATE 21 | echo -n "Please set the build date [$DATE]: " 22 | read DATE 23 | if [ -z "$DATE" ]; then 24 | DATE=$DATE_ORG 25 | fi 26 | fi 27 | 28 | TAG_BUILD=0 29 | 30 | if [ "$1" == "-tag" ]; then 31 | TAG_BUILD=1 32 | shift 33 | fi 34 | 35 | NUM_VER=$1 36 | STR_VER=$2 37 | 38 | if [ -z "$NUM_VER" ]; then 39 | NUM_VER=$DEFAULT_VERSION 40 | fi 41 | 42 | if [ -z "$STR_VER" ]; then 43 | STR_VER=$DEFAULT_VERSION_STR 44 | fi 45 | 46 | echo "Setting version to: $NUM_VER $STR_VER" 47 | 48 | cat package/Version.hpp.in | sed "s/#STR_VER#/$STR_VER/" | sed "s/#STR_BUILD_DATE#/$DATE/" | sed "s/#NUM_VER#/$NUM_VER/" > Common/Source/Version.hpp 49 | cat package/AudioGridderPlugin.iss.in | sed "s/#STR_VER#/$STR_VER/" > package/AudioGridderPlugin.iss 50 | cat package/AudioGridderServer.iss.in | sed "s/#STR_VER#/$STR_VER/" > package/AudioGridderServer.iss 51 | cat package/archiveWin.bat.in | sed "s/#STR_VER#/$STR_VER/g" > package/archiveWin.bat 52 | 53 | echo $STR_VER > package/VERSION 54 | echo $NUM_VER > package/VERSION.num 55 | 56 | if [ "$STR_VER" != "dev-build" ]; then 57 | #mkdir -p ../Archive/Builds/$STR_VER/win/vst 58 | #mkdir -p ../Archive/Builds/$STR_VER/win/vst3 59 | #mkdir -p ../Archive/Builds/$STR_VER/macos-x86_64 60 | #mkdir -p ../Archive/Builds/$STR_VER/macos-10.7-x86_64 61 | #mkdir -p ../Archive/Builds/$STR_VER/macos-arm64 62 | #mkdir -p ../Archive/Builds/$STR_VER/macos-universal 63 | #mkdir -p ../Archive/Builds/$STR_VER/linux 64 | 65 | if [ $TAG_BUILD -gt 0 ]; then 66 | GIT_TAG=$(echo "release_$STR_VER" | tr '[:upper:]' '[:lower:]' | sed 's/\./_/g' | sed 's/-/_/g') 67 | git tag $GIT_TAG 68 | fi 69 | fi 70 | -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "audiogridder", 3 | "version-string" : "1.0.0", 4 | "builtin-baseline" : "64ca152891d6ab135c6c27881e7eb0ac2fa15bba", 5 | "dependencies" : [ { 6 | "name" : "boost", 7 | "version>=" : "1.83.0#1" 8 | }, { 9 | "name" : "libwebp", 10 | "version>=" : "1.3.2" 11 | }, 12 | "ffmpeg" 13 | ], 14 | "overrides": [ 15 | { 16 | "name": "ffmpeg", 17 | "version": "6.0" 18 | } 19 | ] 20 | } --------------------------------------------------------------------------------