A reusable JUCE module for license validation across all Cave Acoustics plugins.
- API-based license validation with Cave Acoustics server
- Machine ID binding to prevent license sharing
- Grace period for network outages (7 days default)
- Automatic periodic revalidation
- License deactivation support
- Demo mode with audio interruption
- Cross-platform support (macOS, Windows, Linux)
# Add the CaveLicensing module
juce_add_module(../../modules/CaveLicensing)
# Link to SharedCode
target_link_libraries(SharedCode
INTERFACE
CaveLicensing
# ... other modules
)
# Define licensing configuration
target_compile_definitions(SharedCode
INTERFACE
CAVE_PRODUCT_ID="MYPRODUCT"
CAVE_PRODUCT_NAME="My Product"
CAVE_COMPANY_NAME="Cave Acoustics"
CAVE_API_URL="https://caveacoustics.com/api"
CAVE_PRODUCT_SLUG="my-product"
)// In PluginProcessor.h
#include <CaveLicensing/CaveLicensing.h>
class MyProcessor : public juce::AudioProcessor
{
// ...
cave::CaveLicenseManager licenseManager { CAVE_PRODUCT_ID, CAVE_API_URL, CAVE_PRODUCT_SLUG };
cave::CaveDemoProcessor demoModeProcessor;
};MyProcessor::MyProcessor()
{
licenseManager.initialize();
}void MyProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer&)
{
if (!licenseManager.isLicensed())
{
demoModeProcessor.process(buffer);
if (demoModeProcessor.isInSilencePeriod())
{
buffer.clear();
return;
}
}
// Normal processing...
}void MyEditor::showActivationPanel()
{
if (processorRef.isLicensed())
return;
activationPanel = std::make_unique<cave::CaveLicensePanel>(
processorRef.getLicenseManager(),
CAVE_PRODUCT_NAME
);
activationPanel->onActivationSuccess = [this] {
updateLicenseButton();
};
activationPanel->showAsOverlay(this);
}Main license management class with API-based validation.
// Create with product configuration
CaveLicenseManager licenseManager(CAVE_PRODUCT_ID, CAVE_API_URL, CAVE_PRODUCT_SLUG);
licenseManager.initialize();
// Check license status (thread-safe, can call from audio thread)
if (licenseManager.isLicensed()) {
// Full functionality
}
// Activate with a license key
if (licenseManager.activateLicense("CAVE-XXXX-XXXX-XXXX-XXXX")) {
// Success
}
// Deactivate (frees up an activation slot)
licenseManager.deactivateLicense();
// Error handling
if (!licenseManager.isLicensed()) {
auto error = licenseManager.getUserFriendlyError();
auto hints = licenseManager.getTroubleshootingHints();
}
// Configure revalidation and grace period
licenseManager.setRevalidationIntervalDays(7); // default
licenseManager.setGracePeriodDays(7); // defaultHTTP client for Cave Acoustics license API. Used internally by CaveLicenseManager.
CaveApiClient client(CAVE_API_URL, CAVE_PRODUCT_SLUG);
// Authorize a license
auto response = client.authorize(licenseKey, machineId, machineName);
if (response.success) {
// response.activationId, response.remainingActivations
}
// Deauthorize
client.deauthorize(licenseKey, machineId);
// Check connectivity
if (client.testConnection()) {
// Server reachable
}Audio processor that periodically mutes output in demo mode.
- 3 minutes of normal playback
- 30 seconds of silence
- 500ms smooth cosine fades
demoModeProcessor.prepare(sampleRate);
demoModeProcessor.process(buffer);
if (demoModeProcessor.isInSilencePeriod()) {
// Currently muted
}Modal UI component for license activation.
auto panel = std::make_unique<CaveLicensePanel>(licenseManager, "My Product");
panel->onActivationSuccess = [this]() { /* update UI */ };
panel->showAsOverlay(parentComponent);Static configuration for license storage locations.
// Get storage locations
auto dir = CaveLicenseConfig::getLicenseDirectory("MYPRODUCT");
auto file = CaveLicenseConfig::getLicenseFile("MYPRODUCT");License keys follow the format: CAVE-XXXX-XXXX-XXXX-XXXX
Keys are validated against the Cave Acoustics API server. Upon successful activation, the license is stored locally with an activation timestamp for periodic revalidation.
Licenses are automatically revalidated with the server every 7 days (configurable). This ensures revoked licenses are detected promptly.
If the server is unreachable during revalidation, a 7-day grace period applies. During this time, previously validated licenses continue to work. After the grace period expires, the plugin reverts to demo mode until connectivity is restored.
The module provides detailed error information:
// Check error type
if (licenseManager.hadNetworkError()) {
// Connectivity issue - may use grace period
} else {
// Server rejection (invalid key, revoked, etc.)
}
// Get user-friendly messages
auto error = licenseManager.getUserFriendlyError();
auto hints = licenseManager.getTroubleshootingHints();Licenses are stored in:
- macOS:
~/Library/Application Support/Cave Acoustics/<ProductID>/license.xml - Windows:
%APPDATA%/Cave Acoustics/<ProductID>/license.xml - Linux:
~/.local/share/Cave Acoustics/<ProductID>/license.xml
- juce_core
- juce_cryptography
- juce_gui_basics
- juce_audio_basics
Proprietary - Cave Acoustics