pluginTester.cpp (5361B)
1 #include <chrono> 2 3 #include "fakeAudioDevice.h" 4 #include "pluginHost.h" 5 #include "logger.h" 6 #include "baseLib/commandline.h" 7 #include "baseLib/filesystem.h" 8 9 class JuceAppLifetimeObjects 10 { 11 public: 12 JuceAppLifetimeObjects() 13 { 14 MessageManager::getInstance(); 15 } 16 ~JuceAppLifetimeObjects() 17 { 18 DeletedAtShutdown::deleteAll(); 19 MessageManager::deleteInstance(); 20 } 21 private: 22 JUCE_DECLARE_NON_COPYABLE(JuceAppLifetimeObjects) 23 JUCE_DECLARE_NON_MOVEABLE(JuceAppLifetimeObjects) 24 }; 25 26 int main(const int _argc, char* _argv[]) 27 { 28 baseLib::CommandLine cmdLine(_argc, _argv); 29 30 StdoutLogger logger; 31 32 auto error = [](const String& _msg) -> int 33 { 34 Logger::writeToLog("Error: " + _msg); 35 Logger::writeToLog("Usage:\n" 36 "pluginTester -plugin <pathToPlugin> [-seconds n -blocks n -blocksize n -samplerate x -forever]"); 37 return 1; 38 }; 39 try 40 { 41 ConsoleApplication app; 42 43 std::string pluginPathName = cmdLine.get("plugin"); 44 45 if (pluginPathName.empty()) 46 { 47 return error("No plugin specified"); 48 } 49 50 { 51 // juce wants the folder for a VST3/LV2 plugin instead of the actual file 52 const auto lowercase = baseLib::filesystem::lowercase(pluginPathName); 53 54 auto start = lowercase.find(".vst3"); 55 if (start == std::string::npos) 56 start = lowercase.find(".lv2"); 57 if (start == std::string::npos) 58 start = lowercase.find(".component"); 59 if (start == std::string::npos) 60 start = lowercase.find(".vst"); 61 62 if (start != std::string::npos) 63 { 64 auto slash = pluginPathName.find_first_of("\\/", start); 65 66 if (slash != std::string::npos) 67 pluginPathName = pluginPathName.substr(0, slash); 68 } 69 } 70 71 JuceAppLifetimeObjects jalto; 72 73 CommandLinePluginHost pluginHost; 74 75 const auto& formatManager = pluginHost.getFormatManager(); 76 77 PluginDescription desc; 78 79 for (int i = 0; i < formatManager.getNumFormats(); ++i) 80 { 81 auto* format = formatManager.getFormat(i); 82 83 if (!format) 84 continue; 85 86 Logger::writeToLog("Attempt to load plugin as type " + format->getName()); 87 88 KnownPluginList plugins; 89 90 OwnedArray<PluginDescription> typesFound; 91 plugins.scanAndAddFile(pluginPathName, true,typesFound, *format); 92 93 const auto types = plugins.getTypes(); 94 95 if (types.isEmpty()) 96 continue; 97 98 desc = types.getFirst(); 99 break; 100 } 101 102 if (desc.fileOrIdentifier.isEmpty()) 103 return error("Failed to find plugin " + pluginPathName); 104 105 if (!pluginHost.loadPlugin(desc)) 106 return error("Failed to load plugin " + pluginPathName); 107 108 FakeAudioIODevice audioDevice; 109 110 const uint32_t numIns = pluginHost.getCurrentProcessor()->getTotalNumInputChannels(); 111 const uint32_t numOuts = pluginHost.getCurrentProcessor()->getTotalNumOutputChannels(); 112 113 const auto blocksize = cmdLine.getInt("blocksize", 512); 114 const auto samplerate = cmdLine.getFloat("samplerate", 48000.0f); 115 116 auto res = audioDevice.open(numIns, numOuts, samplerate, blocksize); 117 118 if (res.isNotEmpty()) 119 return error("Failed to open audio device: " + res); 120 121 audioDevice.start(&pluginHost); 122 123 const auto forever = cmdLine.contains("forever"); 124 125 if (forever) 126 { 127 uint64_t blockCount = 0; 128 uint64_t sr = static_cast<uint64_t>(samplerate); 129 130 uint64_t lastMinutes = 0; 131 132 using Clock = std::chrono::high_resolution_clock; 133 134 const auto tBegin = Clock::now(); 135 136 while (true) 137 { 138 audioDevice.processAudio(); 139 ++blockCount; 140 141 auto formatDuration = [](const uint64_t _seconds) -> std::string 142 { 143 char temp[64]; 144 const auto minutes = _seconds / 60; 145 const auto hours = minutes / 60; 146 const auto s = _seconds - minutes * 60; 147 const auto m = minutes - hours * 60; 148 (void)snprintf(temp, sizeof(temp), "%02uh %02um %02us", static_cast<uint32_t>(hours), static_cast<uint32_t>(m), static_cast<uint32_t>(s)); 149 return temp; 150 }; 151 152 const auto totalSeconds = blockCount * blocksize / sr; 153 const auto minutes = totalSeconds / 60; 154 155 if (minutes != lastMinutes) 156 { 157 const auto t2 = Clock::now(); 158 const auto duration = std::chrono::duration_cast<std::chrono::seconds>(t2 - tBegin).count(); 159 160 const auto speed = static_cast<double>(totalSeconds) * 100.0 / static_cast<double>(duration); 161 162 char temp[64]; 163 (void)snprintf(temp, sizeof(temp), "Processed %s, elapsed %s, speed %2.2f%%", formatDuration(totalSeconds).c_str(), formatDuration(duration).c_str(), speed); 164 Logger::writeToLog(temp); 165 lastMinutes = minutes; 166 } 167 } 168 } 169 170 const auto seconds = cmdLine.getInt("seconds", 0); 171 auto blocks = cmdLine.getInt("blocks", 0); 172 173 if (blocks && seconds) 174 return error("Cannot specify both blocks and seconds"); 175 176 if (seconds) 177 { 178 blocks = static_cast<int>(samplerate) / blocksize * seconds; 179 if (blocks == 0) 180 blocks = 1; 181 } 182 183 int lastPercent = -1; 184 185 char temp[64]; 186 187 for (int i=0; i<blocks; ++i) 188 { 189 audioDevice.processAudio(); 190 191 const auto percent = i * 100 / blocks; 192 193 if (percent == lastPercent) 194 continue; 195 lastPercent = percent; 196 197 (void)snprintf(temp, sizeof(temp), "Progress: %d%% (%d/%d blocks)", percent, i, blocks); 198 Logger::writeToLog(temp); 199 } 200 201 (void)snprintf(temp, sizeof(temp), "Progress: %d%% (%d/%d blocks)", 100, blocks, blocks); 202 Logger::writeToLog(temp); 203 204 return 0; 205 } 206 catch (const std::exception& e) 207 { 208 juce::Logger::writeToLog(e.what()); 209 return 1; 210 } 211 }