Switch to .pkg/.exe/.deb installers with Azure Trusted Signing

Replaces the zip-based ci/build.sh flow with installer outputs (Inno Setup
on Windows, productbuild on macOS, cpack DEB on Linux) signed via Microsoft
Trusted Signing on Windows and Apple Developer ID on macOS.

Plugin code: factory wavetables and presets now ship as files via the
installer rather than embedded BinaryData. PluginProcessor implements
getProgramDirectory/getFactoryProgramDirectories so gin scans factory
content from systemResourceRoot. First-run migration copies any presets
from the legacy com.socalabs/Wavetable/programs path into the new user
dir under Library/Audio/Presets/SocaLabs/Wavetable.

Bumps gin submodule to match Identity for getFactoryProgramDirectories
support.
This commit is contained in:
Roland Rabien 2026-04-29 13:23:23 -07:00
commit 130e626ea6
21 changed files with 879 additions and 262 deletions

View file

@ -3,7 +3,7 @@ on:
push:
branches:
- '**'
concurrency:
group: build-${{ github.ref || github.run_id }}
@ -11,33 +11,58 @@ jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
timeout-minutes: 30
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
include:
- name: Windows
os: [windows-latest]
os: windows-latest
- name: Linux
os: [ubuntu-latest]
os: ubuntu-latest
- name: macOS
os: [macos-latest]
os: macos-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: "Run script"
- name: Install Linux dependencies
if: matrix.name == 'Linux'
run: |
./ci/build.sh
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
ninja-build clang g++ \
libasound2-dev libjack-jackd2-dev ladspa-sdk libcurl4-openssl-dev \
libfreetype-dev libfontconfig1-dev \
libx11-dev libxcomposite-dev libxcursor-dev libxext-dev \
libxinerama-dev libxrandr-dev libxrender-dev \
libwebkit2gtk-4.1-dev libglu1-mesa-dev mesa-common-dev
shell: bash
- name: Install Inno Setup (Windows)
if: matrix.name == 'Windows'
run: choco install innosetup --no-progress -y
shell: bash
- name: Build installer
run: ./Installer/build.sh
shell: bash
env:
# macOS signing / notarization
APPLICATION: ${{ secrets.APPLICATION }}
INSTALLER: ${{ secrets.INSTALLER }}
APPLE_PASS: ${{ secrets.APPLE_PASS }}
APPLE_USER: ${{ secrets.APPLE_USER }}
APIKEY: ${{ secrets.APIKEY }}
INSTALLER: ${{ secrets.INSTALLER }}
APPLE_USER: ${{ secrets.APPLE_USER }}
APPLE_PASS: ${{ secrets.APPLE_PASS }}
# Windows Trusted Signing (Azure)
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
- name: Upload Artifact
uses: actions/upload-artifact@v4
if: always()
with:
name: Binaries ${{ matrix.name }}
path: ci/bin/*.zip
retention-days: 30
path: bin/*
retention-days: 30

View file

@ -8,45 +8,58 @@ jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
timeout-minutes: 30
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
include:
- name: Windows
os: [windows-latest]
os: windows-latest
- name: Linux
os: [ubuntu-latest]
os: ubuntu-latest
- name: macOS
os: [macos-latest]
os: macos-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: "Build"
- name: Install Linux dependencies
if: matrix.name == 'Linux'
run: |
./ci/build.sh
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
ninja-build clang g++ \
libasound2-dev libjack-jackd2-dev ladspa-sdk libcurl4-openssl-dev \
libfreetype-dev libfontconfig1-dev \
libx11-dev libxcomposite-dev libxcursor-dev libxext-dev \
libxinerama-dev libxrandr-dev libxrender-dev \
libwebkit2gtk-4.1-dev libglu1-mesa-dev mesa-common-dev
shell: bash
- name: Install Inno Setup (Windows)
if: matrix.name == 'Windows'
run: choco install innosetup --no-progress -y
shell: bash
- name: Build installer
run: ./Installer/build.sh
shell: bash
env:
APPLICATION: ${{ secrets.APPLICATION }}
INSTALLER: ${{ secrets.INSTALLER }}
APPLE_PASS: ${{ secrets.APPLE_PASS }}
APPLE_USER: ${{ secrets.APPLE_USER }}
APIKEY: ${{ secrets.APIKEY }}
- name: "Upload"
run: |
./ci/upload.sh
shell: bash
env:
APPLICATION: ${{ secrets.APPLICATION }}
INSTALLER: ${{ secrets.INSTALLER }}
APPLE_PASS: ${{ secrets.APPLE_PASS }}
APPLE_USER: ${{ secrets.APPLE_USER }}
APIKEY: ${{ secrets.APIKEY }}
INSTALLER: ${{ secrets.INSTALLER }}
APPLE_USER: ${{ secrets.APPLE_USER }}
APPLE_PASS: ${{ secrets.APPLE_PASS }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
- name: Upload Artifact
uses: actions/upload-artifact@v4
if: always()
with:
name: Binaries ${{ matrix.name }}
path: ci/bin/*.zip
path: bin/*
retention-days: 30
release:
@ -54,7 +67,6 @@ jobs:
needs: build
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Download Artifacts
@ -64,3 +76,4 @@ jobs:
shell: bash
env:
GH_TOKEN: ${{ secrets.ACCESS_TOKEN }}
APIKEY: ${{ secrets.APIKEY }}

4
.gitignore vendored
View file

@ -33,6 +33,8 @@
xcuserdata
Builds
JuceLibraryCode
ci/bin
bin
Installer/macOS/bin
Installer/win/bin
.DS_Store
.claude

View file

@ -13,7 +13,10 @@ set (PLUGIN_CODE Wave)
set (CMAKE_POLICY_DEFAULT_CMP0077 NEW)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set (CMAKE_SUPPRESS_REGENERATION true)
set (CMAKE_SKIP_INSTALL_RULES YES)
# Skip install rules everywhere except Linux, where CPack/DEB needs them.
if (NOT (UNIX AND NOT APPLE))
set (CMAKE_SKIP_INSTALL_RULES YES)
endif()
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
set_property (GLOBAL PROPERTY DEBUG_CONFIGURATIONS "Debug")
@ -96,9 +99,7 @@ set_property (TARGET melatonin_inspector APPEND PROPERTY LABELS melatonin)
set_property (DIRECTORY APPEND PROPERTY LABELS Assets)
file (GLOB_RECURSE binary_files CONFIGURE_DEPENDS
"./plugin/Resources/layout.json"
"./plugin/Resources/WavetablesFLAC/*.wt2048"
"./plugin/Resources/Presets/*.xml")
"./plugin/Resources/layout.json")
juce_add_binary_data (${PLUGIN_NAME}_Assets SOURCES ${binary_files})
@ -242,10 +243,57 @@ if(UNIX AND NOT APPLE)
endif()
if(WIN32)
set (dest "Program Files")
else()
set (dest "Applications")
endif()
#
# Install + CPack (Linux only — macOS uses pkgbuild/productbuild, Windows uses Inno Setup)
#
if (UNIX AND NOT APPLE)
set (_artefact_dir "${CMAKE_BINARY_DIR}/${PLUGIN_NAME}_artefacts/$<CONFIG>")
install (TARGETS ${PLUGIN_NAME} DESTINATION "${dest}")
install (FILES "${_artefact_dir}/VST/lib${PLUGIN_NAME}.so"
DESTINATION lib/vst
RENAME "${PLUGIN_NAME}.so"
COMPONENT VST)
install (DIRECTORY "${_artefact_dir}/VST3/${PLUGIN_NAME}.vst3"
DESTINATION lib/vst3
COMPONENT VST3
USE_SOURCE_PERMISSIONS)
install (DIRECTORY "${_artefact_dir}/LV2/${PLUGIN_NAME}.lv2"
DESTINATION lib/lv2
COMPONENT LV2
USE_SOURCE_PERMISSIONS)
install (FILES "${_artefact_dir}/CLAP/${PLUGIN_NAME}.clap"
DESTINATION lib/clap
COMPONENT CLAP)
install (DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/plugin/Resources/Presets/"
DESTINATION share/SocaLabs/${PLUGIN_NAME}/Presets
COMPONENT Resources
FILES_MATCHING PATTERN "*.xml")
install (DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/plugin/Resources/WavetablesFLAC/"
DESTINATION share/SocaLabs/${PLUGIN_NAME}/Wavetables
COMPONENT Resources
FILES_MATCHING PATTERN "*.wt2048")
set (CPACK_PACKAGE_NAME "wavetable")
set (CPACK_PACKAGE_VERSION "${PLUGIN_VERSION}")
set (CPACK_PACKAGE_VENDOR "SocaLabs")
set (CPACK_PACKAGE_HOMEPAGE_URL "https://socalabs.com/")
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Wavetable synthesizer plugin")
set (CPACK_PACKAGE_CONTACT "support@socalabs.com")
set (CPACK_PACKAGING_INSTALL_PREFIX "/usr")
set (CPACK_GENERATOR "DEB")
set (CPACK_DEB_COMPONENT_INSTALL OFF)
set (CPACK_COMPONENTS_ALL VST VST3 LV2 CLAP Resources)
set (CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE)
set (CPACK_DEBIAN_PACKAGE_MAINTAINER "Roland Rabien <support@socalabs.com>")
set (CPACK_DEBIAN_PACKAGE_SECTION "sound")
set (CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
set (CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set (CPACK_DEBIAN_FILE_NAME "Wavetable.deb")
include (CPack)
endif()

127
Installer/EULA.rtf Normal file
View file

@ -0,0 +1,127 @@
{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang4105\deflangfe4105{\fonttbl{\f0\fswiss\fprq2\fcharset0 Calibri Light;}}
{\colortbl ;\red82\green82\blue82;\red0\green0\blue0;}
{\*\listtable
{\list\listhybrid
{\listlevel\levelnfc0\leveljc0\levelstartat1{\leveltext\'02\'00.;}{\levelnumbers\'01;}\jclisttab\tx720}
{\listlevel\levelnfc4\leveljc0\levelstartat1{\leveltext\'02\'01.;}{\levelnumbers\'01;}\jclisttab\tx720}\listid1 }}
{\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}}
{\*\generator Riched20 10.0.18362}{\*\mmathPr\mnaryLim0\mdispDef1\mwrapIndent1440 }\viewkind4\uc1
\pard\widctlpar\expndtw-10\kerning28\f0\fs56 End-User Licence Agreement\par
\pard\widctlpar\sa720\qj\cf1\expndtw0\kerning0\fs21\line READ THE FOLLOWING LICENCE AGREEMENT CAREFULLY! IT CONTAINS VERY IMPORTANT INFORMATION ABOUT YOUR RIGHTS AND OBLIGATIONS, AS WELL AS LIMITATIONS AND EXCLUSIONS THAT MAY APPLY TO YOU. THIS DOCUMENT CONTAINS A DISPUTE RESOLUTION CLAUSE. BY CLICKING ON THE "ACCEPT" BUTTON, YOU ARE CONSENTING TO BE BOUND BY AND ARE BECOMING A PARTY TO THIS AGREEMENT. IF YOU DO NOT AGREE TO ALL OF THE TERMS OF THIS AGREEMENT, CLICK THE "DO NOT ACCEPT" BUTTON OR LEAVE THE WEBSITE.\par
\pard\widctlpar\sa120\qj\cf2\b\fs33 1. Definitions\par
\pard
{\pntext\f0 1.\tab}{\*\pn\pnlvlbody\pnf0\pnindent720\pnstart1\pndec{\pntxta.}}
\widctlpar\fi-360\li360\qj\cf1\b0\fs21 "We", "Our" and "Us" means SocaLabs.\par
{\pntext\f0 2.\tab}"You" and "Your" means the person or entity who is being licenced to use the Licenced Software.\par
{\pntext\f0 3.\tab}"Agreement" means this Software Licence Agreement between Us and You;\par
{\pntext\f0 4.\tab}"Licenced Materials" means the Licenced Software and the Licenced Library;\par
{\pntext\f0 5.\tab}"Licenced Software" means certain commercial software products being provided to You under this Agreement, including executable program modules thereof, as well as related documentation, and computer readable media;\par
{\pntext\f0 6.\tab}"Sub-Licenced Software" means certain third party owned software components being provided under this Agreement that are required to properly enable or operate the Licenced Software; and\par
{\pntext\f0 7.\tab}"Licenced Library" means any audio waveforms, such as melodies, music, instrument recordings, or other audible information, provided by Us for use with the Licenced Software.\par
\pard\widctlpar\li360\qj\par
\par
\pard\widctlpar\sa120\qj\cf2\b\fs33 2. Software Licence, Rights and Restrictions\par
\pard
{\listtext\f0 1.\tab}\jclisttab\tx720\ls1\widctlpar\fi-360\li360\qj\cf1\b0\fs21 Subject to the provisions contained in this Agreement, We hereby grant to You a personal, revocable, non-exclusive licence (the "Licence") to access, read, use and download one copy of the Licenced Materials solely for the purposes provided under paragraph 3. This is not a transfer of title.\par
{\listtext\f0 2.\tab}Under the Licence you may not and shall not, without Our express written authorization:\par
\pard
{\listtext\f0 a.\tab}\jclisttab\tx720\ls1\ilvl1\widctlpar\fi-360\li1080\qj own title, or transfer title to the Licenced Materials to another party;\par
{\listtext\f0 b.\tab}transfer the Licence;\par
{\listtext\f0 c.\tab}remove any copyright or other proprietary notations from the Licenced Materials;\par
{\listtext\f0 d.\tab}distribute, or sublicense or otherwise provide copies of or access to the Licenced Materials to any third party;\par
{\listtext\f0 e.\tab}pledge, hypothecate, alienate or otherwise encumber the Licenced Materials to any third party;\par
{\listtext\f0 f.\tab}create derivative works of, reverse engineer, decompile, disassemble, adapt, translate, transmit, arrange, modify, copy, bundle, sell, sublicense, export, merge, transfer, adapt, loan, rent, lease, assign, share, outsource, host, publish, make available to any person or otherwise use, either directly or indirectly, the Licenced Materials;\par
{\listtext\f0 g.\tab}permit, allow or do anything that would infringe or otherwise prejudice the proprietary rights of Us or Our licensors;\par
{\listtext\f0 h.\tab}circumvent, remove, alter or otherwise impair any technological protection measures, digital rights management information, or other copy-protection mechanisms used in or by the Licenced Materials; or\par
{\listtext\f0 i.\tab}include, use, re-sample, mix, process, isolate or embed any content from the Licenced Library in any virtual instrument, audio sampling system, audio playback system or library of any kind, other than those provided or authorized by Us.\par
\pard
{\listtext\f0 3.\tab}\jclisttab\tx720\ls1\widctlpar\fi-360\li360\qj We are not obligated to provide any authorization referred to in paragraph 2(2), We reserve the right to charge a fee for the grant of such authorization, and We may cancel such authorization at Our sole and unfettered discretion by providing notice to You of such cancellation.\par
{\listtext\f0 4.\tab}The restrictions set out in this Agreement, including the restrictions listed at paragraph 2(2), shall not apply to the limited extent the restrictions are prohibited by applicable law.\par
{\listtext\f0 5.\tab}We will have the right to inspect and enforce the restrictions and covenants contained in this Agreement at Your sole expense, and You hereby agree to promptly notify Us of any known violations of such restrictions.\par
{\listtext\f0 6.\tab}You agree to protect the Licenced Materials from unauthorized use, reproduction, distribution or publication in electronic or physical form.\par
{\listtext\f0 7.\tab}Upon execution of this Agreement, We will:\par
\pard
{\listtext\f0 a.\tab}\jclisttab\tx720\ls1\ilvl1\widctlpar\fi-360\li1080\qj permit You to download a copy of the Licenced Materials for Your use under this Agreement; and\par
{\listtext\f0 b.\tab}permit You to download updates to the Licenced Software as We consider needed. We are not obligated to provide updates for the Licenced Software.\par
\pard\widctlpar\li1080\qj\par
\par
\pard\widctlpar\sa120\qj\cf2\b\fs33 3. Usage Conditions\par
\pard
{\pntext\f0 1.\tab}{\*\pn\pnlvlbody\pnf0\pnindent720\pnstart1\pndec{\pntxta.}}
\widctlpar\fi-360\li360\qj\cf1\b0\fs21 You are authorized to use the Licenced Materials only for personal use. Your personal use may be commercial.\par
{\pntext\f0 2.\tab}You may not rent out to others equipment on which the Licenced Software is installed or by which the Licenced Library is accessible without obtaining a written rental licence from Us that expressly authorizes such use. We reserve the right to charge a fee for a rental licence.\par
{\pntext\f0 3.\tab}You may install the Licenced Software on as many devices as required for Your personal use provided such devices are used only by You.\par
\pard\widctlpar\qj\par
\pard\widctlpar\li360\qj\par
\pard\widctlpar\sa120\qj\cf2\b\fs33 4. Ownership and Intellectual Property Rights\par
\pard
{\pntext\f0 1.\tab}{\*\pn\pnlvlbody\pnf0\pnindent720\pnstart1\pndec{\pntxta.}}
\widctlpar\fi-360\li360\qj\cf1\b0\fs21 All copyright in the Licenced Software, including any documentation, media, packaging and illustrations, but excluding the Sublicensed Software, is owned by Us. You do not acquire title to any copyright in the Licenced Materials or Sublicensed Software under this Agreement.\par
{\pntext\f0 2.\tab}Certain logos, product names and trade-marks owned by Us or by third parties may be contained within the Licenced Materials or in documentation, media, packaging and illustrations relating to the Licensed Software. You do not acquire title to any such logos, product names or trade-marks under this Agreement.\par
\pard\widctlpar\li360\qj\par
\par
\pard\widctlpar\sa120\qj\cf2\b\fs33 5. Termination\par
\pard
{\pntext\f0 1.\tab}{\*\pn\pnlvlbody\pnf0\pnindent720\pnstart1\pndec{\pntxta.}}
\widctlpar\fi-360\li360\qj\cf1\b0\fs21 This Agreement is effective until terminated by Us, with or without cause, in Our sole and unfettered discretion. We may terminate this Agreement without notice to You if You fail to comply with any of its terms. Any such termination by Us shall be in addition to and without prejudice to such rights and remedies as may be available to Us, including injunction and other equitable remedies.\par
{\pntext\f0 2.\tab}The disclaimers, limitations on liability, ownership, termination, interpretation, Your warranty and the indemnity provisions of this Agreement shall survive the termination or expiry of this Agreement.\par
{\pntext\f0 3.\tab}This Agreement will automatically terminate if You violate or assist in the violation of any of the restrictions of paragraph 2(2).\par
{\pntext\f0 4.\tab}On the termination of this Agreement, you must destroy any copies of the Licenced Materials in your possession whether in electronic or printed format.\par
\pard\widctlpar\li360\qj\par
\par
\pard\widctlpar\sa120\qj\cf2\b\fs33 6. Indemnification\par
\pard\widctlpar\sa720\qj\cf1\b0\fs21 You agree to indemnify, defend and hold Us and Our partners, lawyers, staff, affiliates, successors and assigns (collectively, \ldblquote Affiliated Parties\rdblquote ) harmless from any liability, loss, claim and expense, including reasonable legal fees, related to Your violation of this Agreement or use of the Licenced Materials.\par
\pard\widctlpar\sa120\qj\cf2\b\fs33 7. Disclaimer and Limitation of Liability\par
\pard
{\pntext\f0 1.\tab}{\*\pn\pnlvlbody\pnf0\pnindent720\pnstart1\pndec{\pntxta.}}
\widctlpar\fi-360\li360\qj\cf1\b0\fs21 THE LICENCED MATERIALS ARE PROVIDED "AS IS", "AS AVAILABLE" AND ALL WARRANTIES, EXPRESS OR IMPLIED, ARE DISCLAIMED (INCLUDING BUT NOT LIMITED TO THE DISCLAIMER OF ANY IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE). THE LICENCED MATERIALS MAY CONTAIN BUGS, ERRORS, PROBLEMS OR OTHER LIMITATIONS. WE AND OUR AFFILIATED PARTIES HAVE NO LIABILITY WHATSOEVER FOR YOUR USE OF ANY OF THE MATERIALS. IN PARTICULAR, BUT NOT AS A LIMITATION THEREOF, WE AND THE AFFILIATED PARTIES ARE NOT LIABLE FOR ANY INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING DAMAGES FOR LOSS OF BUSINESS, LOSS OF PROFITS, LITIGATION OR THE LIKE), WHETHER BASED ON BREACH OF CONTRACT, BREACH OF WARRANTY, TORT (INCLUDING NEGLIGENCE), PRODUCT LIABILITY OR OTHERWISE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THE NEGATION OF DAMAGES SET FORTH ABOVE ARE FUNDAMENTAL ELEMENTS OF THE BASIS OF THE BARGAIN BETWEEN YOU AND US. THE LICENCED MATERIALS WOULD NOT BE PROVIDED WITHOUT SUCH LIMITATIONS.\par
{\pntext\f0 2.\tab}All responsibility or liability for any damages caused by the Licenced Materials, including, without limitation, damages caused by computer viruses or other malicious code contained within the Licenced Software is disclaimed.\par
\pard\widctlpar\li360\qj\par
\par
\pard\widctlpar\sa120\qj\cf2\b\fs33 8. Use of Information\par
\pard\widctlpar\sa720\qj\cf1\b0\fs21 We reserve the right, and You authorize Us, to the use and assignment of all information regarding Your use of the Licenced Materials and all information provided by You in any manner consistent with Our Privacy Policy.\par
\pard\widctlpar\sa120\qj\cf2\b\fs33 9. Miscellaneous\par
\pard
{\pntext\f0 1.\tab}{\*\pn\pnlvlbody\pnf0\pnindent720\pnstart1\pndec{\pntxta.}}
\widctlpar\fi-360\li360\qj\cf1\b0\fs21 This Agreement shall be treated as though it were executed and performed in the province of British Columbia, Canada, and shall be governed by and construed in accordance with the laws of the province of British Columbia (without regard to conflict of law principles).\par
{\pntext\f0 2.\tab}Any of Your causes of action with respect to the Licenced Materials must be instituted within six (6) months after the cause of action arose or be forever waived and barred. All actions shall be subject to the limitations set forth in paragraph 7 of this Agreement.\par
{\pntext\f0 3.\tab}The language in this Agreement shall be interpreted as to its fair meaning and not construed strictly for or against either party. The division of this Agreement into sections and the insertion of headings are for convenience of reference only and shall not affect the construction or interpretation of this Agreement.\par
{\pntext\f0 4.\tab}All legal proceedings arising out of or in connection with this Agreement shall be brought solely in Vancouver, British Columbia. You expressly submit to the exclusive jurisdiction of said courts and consent to extra-territorial service of process.\par
{\pntext\f0 5.\tab}Should any part of this Agreement be held invalid or unenforceable, that portion shall be construed consistent with applicable law and the remaining portions shall remain in full force and effect.\par
{\pntext\f0 6.\tab}Failure of Us to enforce any provision of this Agreement shall not be deemed a waiver of such provision nor of the right to enforce such provision.\par
{\pntext\f0 7.\tab}You agree to review this Agreement prior to downloading, copying, installing or using the Licenced Materials. This Agreement may be amended by Us from time to time without specific advance notice to You. The latest Agreement will be provided with updates to the Licenced Software, and You should review the Agreement prior to using the Licenced Materials.\par
{\pntext\f0 8.\tab}This Agreement, as modified from time to time as described above, and including the policies incorporated by reference, constitutes the entire and only agreement between You and Us and supersedes all prior or contemporaneous agreements, representations, warranties and understandings with respect to the Licenced Materials.\par
{\pntext\f0 9.\tab}To the extent that anything in or associated with the Licenced Materials is in conflict or inconsistent with this Agreement, this Agreement shall take precedence.\par
\pard\widctlpar\sa160\sl252\slmult1\cf0\fs22\par
}

259
Installer/build.sh Executable file
View file

@ -0,0 +1,259 @@
#!/bin/bash -e
set -x
#
# Resolve repo root
#
cd "$(dirname "$0")"
cd ..
PROJECT_ROOT=$(pwd)
PLUGIN="Wavetable"
VENDOR="SocaLabs"
BUNDLE_BASE="com.socalabs.wavetable"
if [ "$(uname)" = "Darwin" ]; then
PLATFORM="macOS"
elif [ "$(expr substr "$(uname -s)" 1 5)" = "Linux" ]; then
PLATFORM="linux"
else
PLATFORM="win"
fi
VERSION=$(cat "$PROJECT_ROOT/VERSION")
#
# Reset staging
#
rm -Rf "$PROJECT_ROOT/Installer/$PLATFORM/bin"
mkdir -p "$PROJECT_ROOT/Installer/$PLATFORM/bin"
rm -Rf "$PROJECT_ROOT/bin"
mkdir -p "$PROJECT_ROOT/bin"
############################################################
# macOS — pkgbuild + productbuild
############################################################
if [ "$PLATFORM" = "macOS" ]; then
TEAM_ID="${TEAM_ID:-3FS7DJDG38}"
DEV_APP_ID="${DEV_APP_ID:-Developer ID Application: Roland Rabien (${TEAM_ID})}"
DEV_INST_ID="${DEV_INST_ID:-Developer ID Installer: Roland Rabien (${TEAM_ID})}"
#
# Bootstrap a temp keychain from base64-encoded p12 secrets when running in CI.
# APPLICATION = base64 of Developer ID Application .p12
# INSTALLER = base64 of Developer ID Installer .p12
#
if [ -n "${APPLICATION:-}" ] && [ -n "${INSTALLER:-}" ]; then
KC_PASS="${KEYCHAIN_PASSWORD:-nr4aGPyz}"
P12_PASS="${P12_PASSWORD:-aym9PKWB}"
security create-keychain -p "$KC_PASS" Keys.keychain || true
echo "$APPLICATION" | base64 -D -o /tmp/Application.p12
echo "$INSTALLER" | base64 -D -o /tmp/Installer.p12
security import /tmp/Application.p12 -t agg -k Keys.keychain -P "$P12_PASS" -A -T /usr/bin/codesign
security import /tmp/Installer.p12 -t agg -k Keys.keychain -P "$P12_PASS" -A -T /usr/bin/productsign
security list-keychains -s Keys.keychain
security default-keychain -s Keys.keychain
security unlock-keychain -p "$KC_PASS" Keys.keychain
security set-keychain-settings -l -u -t 13600 Keys.keychain
security set-key-partition-list -S apple-tool:,apple: -s -k "$KC_PASS" Keys.keychain
fi
ART_DIR="$PROJECT_ROOT/Builds/xcode/${PLUGIN}_artefacts/Release"
cd "$PROJECT_ROOT"
cmake --preset xcode
cmake --build --preset xcode --config Release
STAGE="$PROJECT_ROOT/Installer/macOS/bin/stage"
PKG_DIR="$PROJECT_ROOT/Installer/macOS/bin/pkgs"
rm -Rf "$STAGE" "$PKG_DIR"
mkdir -p "$STAGE/vst" "$STAGE/vst3" "$STAGE/au" "$STAGE/clap"
mkdir -p "$STAGE/resources/Library/Audio/Presets/$VENDOR/$PLUGIN"
mkdir -p "$PKG_DIR"
cp -RL "$ART_DIR/VST/$PLUGIN.vst" "$STAGE/vst/"
cp -RL "$ART_DIR/VST3/$PLUGIN.vst3" "$STAGE/vst3/"
cp -RL "$ART_DIR/AU/$PLUGIN.component" "$STAGE/au/"
cp -RL "$ART_DIR/CLAP/$PLUGIN.clap" "$STAGE/clap/"
# Resources component: factory Wavetables and Presets
cp -R "$PROJECT_ROOT/plugin/Resources/Presets" "$STAGE/resources/Library/Audio/Presets/$VENDOR/$PLUGIN/Presets"
cp -R "$PROJECT_ROOT/plugin/Resources/WavetablesFLAC" "$STAGE/resources/Library/Audio/Presets/$VENDOR/$PLUGIN/Wavetables"
find "$STAGE/resources" -name ".DS_Store" -delete
if security find-identity -v -p codesigning | grep -q "$DEV_APP_ID"; then
codesign -s "$DEV_APP_ID" --options=runtime --timestamp --force -v "$STAGE/vst/$PLUGIN.vst"
codesign -s "$DEV_APP_ID" --options=runtime --timestamp --force -v "$STAGE/vst3/$PLUGIN.vst3"
codesign -s "$DEV_APP_ID" --options=runtime --timestamp --force -v "$STAGE/au/$PLUGIN.component"
codesign -s "$DEV_APP_ID" --options=runtime --timestamp --force -v "$STAGE/clap/$PLUGIN.clap"
else
echo "Skipping codesign — Developer ID Application not in keychain"
fi
# Component packages
pkgbuild --root "$STAGE/vst" \
--install-location "/Library/Audio/Plug-Ins/VST" \
--identifier "${BUNDLE_BASE}.vst.pkg" \
--version "$VERSION" \
"$PKG_DIR/vst.pkg"
pkgbuild --root "$STAGE/vst3" \
--install-location "/Library/Audio/Plug-Ins/VST3" \
--identifier "${BUNDLE_BASE}.vst3.pkg" \
--version "$VERSION" \
"$PKG_DIR/vst3.pkg"
pkgbuild --root "$STAGE/au" \
--install-location "/Library/Audio/Plug-Ins/Components" \
--identifier "${BUNDLE_BASE}.au.pkg" \
--version "$VERSION" \
"$PKG_DIR/au.pkg"
pkgbuild --root "$STAGE/clap" \
--install-location "/Library/Audio/Plug-Ins/CLAP" \
--identifier "${BUNDLE_BASE}.clap.pkg" \
--version "$VERSION" \
"$PKG_DIR/clap.pkg"
pkgbuild --root "$STAGE/resources" \
--install-location "/" \
--identifier "${BUNDLE_BASE}.resources.pkg" \
--version "$VERSION" \
--scripts "$PROJECT_ROOT/Installer/macOS/scripts" \
"$PKG_DIR/resources.pkg"
# productbuild — combine into one signed installer
cp "$PROJECT_ROOT/Installer/EULA.rtf" "$PKG_DIR/EULA.rtf"
cp "$PROJECT_ROOT/Installer/macOS/welcome.txt" "$PKG_DIR/welcome.txt"
PKG_OUT="$PROJECT_ROOT/Installer/macOS/bin/${PLUGIN}.pkg"
productbuild --distribution "$PROJECT_ROOT/Installer/macOS/distribution.xml" \
--package-path "$PKG_DIR" \
--resources "$PKG_DIR" \
--version "$VERSION" \
"$PKG_OUT.unsigned"
if security find-identity -v | grep -q "$DEV_INST_ID"; then
productsign --sign "$DEV_INST_ID" "$PKG_OUT.unsigned" "$PKG_OUT"
rm "$PKG_OUT.unsigned"
else
echo "Skipping productsign — Developer ID Installer not in keychain"
mv "$PKG_OUT.unsigned" "$PKG_OUT"
fi
if [ -n "${APPLE_USER:-}" ] && [ -n "${APPLE_PASS:-}" ]; then
xcrun notarytool submit --verbose \
--apple-id "$APPLE_USER" --password "$APPLE_PASS" --team-id "$TEAM_ID" \
--wait --timeout 30m "$PKG_OUT"
xcrun stapler staple "$PKG_OUT"
else
echo "Skipping notarization — APPLE_USER / APPLE_PASS not set"
fi
cp "$PKG_OUT" "$PROJECT_ROOT/bin/"
# Symbols zip
cd "$ART_DIR"
zip -r "$PROJECT_ROOT/bin/Symbols_Mac.zip" \
AU/$PLUGIN.component.dSYM \
VST/$PLUGIN.vst.dSYM \
VST3/$PLUGIN.vst3.dSYM \
CLAP/$PLUGIN.clap.dSYM 2>/dev/null || true
############################################################
# Linux — cpack DEB (VST + VST3 + LV2 + CLAP + Resources)
############################################################
elif [ "$PLATFORM" = "linux" ]; then
cd "$PROJECT_ROOT"
cmake --preset ninja-gcc
cmake --build --preset ninja-gcc --config Release
cd "$PROJECT_ROOT/Builds/ninja-gcc"
cpack -G DEB -C Release
cp "$PROJECT_ROOT/Builds/ninja-gcc/"*.deb "$PROJECT_ROOT/bin/"
############################################################
# Windows — Inno Setup + Azure Trusted Signing
############################################################
else
cd "$PROJECT_ROOT"
cmake --preset vs
cmake --build --preset vs --config Release
ART_DIR="$PROJECT_ROOT/Builds/vs/${PLUGIN}_artefacts/Release"
STAGE="$PROJECT_ROOT/Installer/win/bin"
rm -Rf "$STAGE"
mkdir -p "$STAGE/VST" "$STAGE/VST3" "$STAGE/CLAP"
cp -R "$ART_DIR/VST/$PLUGIN.dll" "$STAGE/VST/"
cp -R "$ART_DIR/VST3/$PLUGIN.vst3" "$STAGE/VST3/"
cp -R "$ART_DIR/CLAP/$PLUGIN.clap" "$STAGE/CLAP/"
#
# Sign binaries via Microsoft Trusted Signing.
# Required env: AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET.
# Cert is referenced via Installer/win/metadata.json.
#
uuid_re='^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'
WIN_SIGN=0
if [ -n "${AZURE_TENANT_ID:-}" ] || [ -n "${AZURE_CLIENT_ID:-}" ] || [ -n "${AZURE_CLIENT_SECRET:-}" ]; then
if [[ ! "${AZURE_TENANT_ID:-}" =~ $uuid_re ]]; then
echo "ERROR: AZURE_TENANT_ID is not a valid GUID — set the secret on the repo or unset all three to skip signing."; exit 1
elif [[ ! "${AZURE_CLIENT_ID:-}" =~ $uuid_re ]]; then
echo "ERROR: AZURE_CLIENT_ID is not a valid GUID — set the secret on the repo or unset all three to skip signing."; exit 1
elif [ -z "${AZURE_CLIENT_SECRET:-}" ]; then
echo "ERROR: AZURE_CLIENT_SECRET is empty — set the secret on the repo or unset all three to skip signing."; exit 1
else
WIN_SIGN=1
fi
fi
if [ "$WIN_SIGN" = "1" ]; then
# Locate signtool — the version dir under Windows Kits varies by runner image.
SIGNTOOL=$(ls -1 "/c/Program Files (x86)/Windows Kits/10/bin/"*/x64/signtool.exe 2>/dev/null | sort | tail -1)
if [ -z "$SIGNTOOL" ]; then
echo "signtool.exe not found in Windows Kits"; exit 1
fi
TOOLS_DIR="$STAGE/_signingtools"
mkdir -p "$TOOLS_DIR"
nuget install Microsoft.Trusted.Signing.Client -Version 1.0.86 \
-OutputDirectory "$TOOLS_DIR" -ExcludeVersion -NonInteractive
DLIB="$TOOLS_DIR/Microsoft.Trusted.Signing.Client/bin/x64/Azure.CodeSigning.Dlib.dll"
METADATA="$PROJECT_ROOT/Installer/win/metadata.json"
sign_file () {
"$SIGNTOOL" sign -v -fd SHA256 \
-tr "http://timestamp.acs.microsoft.com" -td SHA256 \
-dlib "$DLIB" -dmdf "$METADATA" "$1"
}
sign_file "$STAGE/VST/$PLUGIN.dll"
sign_file "$STAGE/VST3/$PLUGIN.vst3/Contents/x86_64-win/$PLUGIN.vst3"
sign_file "$STAGE/CLAP/$PLUGIN.clap"
else
echo "Skipping Windows binary signing — Azure secrets not set"
fi
# Build installer .exe
cd "$PROJECT_ROOT/Installer/win"
ISCC="/c/Program Files (x86)/Inno Setup 6/ISCC.exe"
if [ ! -f "$ISCC" ]; then
ISCC="/c/Program Files/Inno Setup 6/ISCC.exe"
fi
"$ISCC" "$PROJECT_ROOT/Installer/win/${PLUGIN}.iss"
EXE_OUT="$PROJECT_ROOT/Installer/win/bin/${PLUGIN}.exe"
# Sign installer
if [ "$WIN_SIGN" = "1" ]; then
sign_file "$EXE_OUT"
fi
cp "$EXE_OUT" "$PROJECT_ROOT/bin/"
fi

70
Installer/eula.md Normal file
View file

@ -0,0 +1,70 @@
# End-User Licence Agreement #
READ THE FOLLOWING LICENCE AGREEMENT CAREFULLY! IT CONTAINS VERY IMPORTANT INFORMATION ABOUT YOUR RIGHTS AND OBLIGATIONS, AS WELL AS LIMITATIONS AND EXCLUSIONS THAT MAY APPLY TO YOU. THIS DOCUMENT CONTAINS A DISPUTE RESOLUTION CLAUSE. BY CLICKING ON THE "ACCEPT" BUTTON, YOU ARE CONSENTING TO BE BOUND BY AND ARE BECOMING A PARTY TO THIS AGREEMENT. IF YOU DO NOT AGREE TO ALL OF THE TERMS OF THIS AGREEMENT, CLICK THE "DO NOT ACCEPT" BUTTON OR LEAVE THE WEBSITE.
## 1. Definitions ##
1. "We", "Our" and "Us" means SocaLabs.
2. "You" and "Your" means the person or entity who is being licenced to use the Licenced Software.
3. "Agreement" means this Software Licence Agreement between Us and You;
4. "Licenced Materials" means the Licenced Software and the Licenced Library;
5. "Licenced Software" means certain commercial software products being provided to You under this Agreement, including executable program modules thereof, as well as related documentation, and computer readable media;
6. "Sub-Licenced Software" means certain third party owned software components being provided under this Agreement that are required to properly enable or operate the Licenced Software; and
7. "Licenced Library" means any audio waveforms, such as melodies, music, instrument recordings, or other audible information, provided by Us for use with the Licenced Software.
## 2. Software Licence, SamplePack Licence, Rights and Restrictions ##
1. Subject to the provisions contained in this Agreement, We hereby grant to You a personal, revocable, non-exclusive licence (the "Licence") to access, read, use and download one copy of the Licenced Materials solely for the purposes provided under paragraph 3. This is not a transfer of title.
2. Under the Licence you may not and shall not, without Our express written authorization:
1. own title, or transfer title to the Licenced Materials to another party;
2. transfer the Licence;
3. remove any copyright or other proprietary notations from the Licenced Materials;
4. distribute, or sublicense or otherwise provide copies of or access to the Licenced Materials to any third party;
5. pledge, hypothecate, alienate or otherwise encumber the Licenced Materials to any third party;
6. create derivative works of, reverse engineer, decompile, disassemble, adapt, translate, transmit, arrange, modify, copy, bundle, sell, sublicense, export, merge, transfer, adapt, loan, rent, lease, assign, share, outsource, host, publish, make available to any person or otherwise use, either directly or indirectly, the Licenced Materials;
7. permit, allow or do anything that would infringe or otherwise prejudice the proprietary rights of Us or Our licensors;
8. circumvent, remove, alter or otherwise impair any technological protection measures, digital rights management information, or other copy-protection mechanisms used in or by the Licenced Materials; or
9. include, use, re-sample, mix, process, isolate or embed any content from the Licenced Library in any virtual instrument, audio sampling system, audio playback system or library of any kind, other than those provided or authorized by Us.
3. We are not obligated to provide any authorization referred to in paragraph 2(2), We reserve the right to charge a fee for the grant of such authorization, and We may cancel such authorization at Our sole and unfettered discretion by providing notice to You of such cancellation.
4. The restrictions set out in this Agreement, including the restrictions listed at paragraph 2(2), shall not apply to the limited extent the restrictions are prohibited by applicable law.
5. We will have the right to inspect and enforce the restrictions and covenants contained in this Agreement at Your sole expense, and You hereby agree to promptly notify Us of any known violations of such restrictions.
6. You agree to protect the Licenced Materials from unauthorized use, reproduction, distribution or publication in electronic or physical form.
7. Upon execution of this Agreement, We will:
1. permit You to download a copy of the Licenced Materials for Your use under this Agreement; and
2. permit You to download updates to the Licenced Software as We consider needed. We are not obligated to provide updates for the Licenced Software.
## 3. Usage Conditions ##
1. You are authorized to use the Licenced Materials only for personal use.
2. You may not rent out to others equipment on which the Licenced Software is installed or by which the Licenced Library is accessible without obtaining a written rental licence from Us that expressly authorizes such use. We reserve the right to charge a fee for a rental licence.
3. You may install the Licenced Software on as many devices as required for Your personal use provided such devices are used only by You.
## 4. Ownership and Intellectual Property Rights ##
1. All copyright in the Licenced Software, including any documentation, media, packaging and illustrations, but excluding the Sublicenced Software, is owned by Us. You do not acquire title to any copyright in the Licenced Materials or Sublicenced Software under this Agreement.
2. Certain logos, product names and trade-marks owned by Us or by third parties may be contained within the Licenced Materials or in documentation, media, packaging and illustrations relating to the Licensed Software. You do not acquire title to any such logos, product names or trade-marks under this Agreement.
## 5. Termination ##
1. This Agreement is effective until terminated by Us, with or without cause, in Our sole and unfettered discretion. We may terminate this Agreement without notice to You if You fail to comply with any of its terms. Any such termination by Us shall be in addition to and without prejudice to such rights and remedies as may be available to Us, including injunction and other equitable remedies.
2. The disclaimers, limitations on liability, ownership, termination, interpretation, Your warranty and the indemnity provisions of this Agreement shall survive the termination or expiry of this Agreement.
3. This Agreement will automatically terminate if You violate or assist in the violation of any of the restrictions of paragraph 2(2).
4. On the termination of this Agreement, you must destroy any copies of the Licenced Materials in your possession whether in electronic or printed format.
## 6. Indemnification ##
You agree to indemnify, defend and hold Us and Our partners, lawyers, staff, affiliates, successors and assigns (collectively, "Affiliated Parties") harmless from any liability, loss, claim and expense, including reasonable legal fees, related to Your violation of this Agreement or use of the Licenced Materials.
## 7. Disclaimer and Limitation of Liability ##
1. THE LICENCED MATERIALS ARE PROVIDED "AS IS", "AS AVAILABLE" AND ALL WARRANTIES, EXPRESS OR IMPLIED, ARE DISCLAIMED (INCLUDING BUT NOT LIMITED TO THE DISCLAIMER OF ANY IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE). THE LICENCED MATERIALS MAY CONTAIN BUGS, ERRORS, PROBLEMS OR OTHER LIMITATIONS. WE AND OUR AFFILIATED PARTIES HAVE NO LIABILITY WHATSOEVER FOR YOUR USE OF ANY OF THE MATERIALS. IN PARTICULAR, BUT NOT AS A LIMITATION THEREOF, WE AND THE AFFILIATED PARTIES ARE NOT LIABLE FOR ANY INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING DAMAGES FOR LOSS OF BUSINESS, LOSS OF PROFITS, LITIGATION OR THE LIKE), WHETHER BASED ON BREACH OF CONTRACT, BREACH OF WARRANTY, TORT (INCLUDING NEGLIGENCE), PRODUCT LIABILITY OR OTHERWISE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THE NEGATION OF DAMAGES SET FORTH ABOVE ARE FUNDAMENTAL ELEMENTS OF THE BASIS OF THE BARGAIN BETWEEN YOU AND US. THE LICENCED MATERIALS WOULD NOT BE PROVIDED WITHOUT SUCH LIMITATIONS.
2. All responsibility or liability for any damages caused by the Licenced Materials, including, without limitation, damages caused by computer viruses or other malicious code contained within the Licenced Software is disclaimed.
## 8. Use of Information ##
We reserve the right, and You authorize Us, to the use and assignment of all information regarding Your use of the Licenced Materials and all information provided by You in any manner consistent with Our Privacy Policy.
## 9. Miscellaneous ##
1. This Agreement shall be treated as though it were executed and performed in the province of British Columbia, Canada, and shall be governed by and construed in accordance with the laws of the province of British Columbia (without regard to conflict of law principles).
2. Any of Your causes of action with respect to the Licenced Materials must be instituted within six (6) months after the cause of action arose or be forever waived and barred. All actions shall be subject to the limitations set forth in paragraph 7 of this Agreement.
3. The language in this Agreement shall be interpreted as to its fair meaning and not construed strictly for or against either party. The division of this Agreement into sections and the insertion of headings are for convenience of reference only and shall not affect the construction or interpretation of this Agreement.
4. All legal proceedings arising out of or in connection with this Agreement shall be brought solely in Vancouver, British Columbia. You expressly submit to the exclusive jurisdiction of said courts and consent to extra-territorial service of process.
5. Should any part of this Agreement be held invalid or unenforceable, that portion shall be construed consistent with applicable law and the remaining portions shall remain in full force and effect.
6. Failure of Us to enforce any provision of this Agreement shall not be deemed a waiver of such provision nor of the right to enforce such provision.
7. You agree to review this Agreement prior to downloading, copying, installing or using the Licenced Materials. This Agreement may be amended by Us from time to time without specific advance notice to You. The latest Agreement will be provided with updates to the Licenced Software, and You should review the Agreement prior to using the Licenced Materials.
8. This Agreement, as modified from time to time as described above, and including the policies incorporated by reference, constitutes the entire and only agreement between You and Us and supersedes all prior or contemporaneous agreements, representations, warranties and understandings with respect to the Licenced Materials.
9. To the extent that anything in or associated with the Licenced Materials is in conflict or inconsistent with this Agreement, this Agreement shall take precedence.

View file

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<installer-gui-script minSpecVersion="2">
<title>Wavetable</title>
<organization>com.socalabs</organization>
<domains enable_anywhere="false" enable_currentUserHome="false" enable_localSystem="true"/>
<options customize="always" require-scripts="false" rootVolumeOnly="true"/>
<license file="EULA.rtf"/>
<welcome file="welcome.txt" mime-type="text/plain"/>
<choices-outline>
<line choice="com.socalabs.wavetable.vst"/>
<line choice="com.socalabs.wavetable.vst3"/>
<line choice="com.socalabs.wavetable.au"/>
<line choice="com.socalabs.wavetable.clap"/>
<line choice="com.socalabs.wavetable.resources"/>
</choices-outline>
<choice id="com.socalabs.wavetable.vst"
title="VST"
description="Install the VST plug-in to /Library/Audio/Plug-Ins/VST"
visible="true">
<pkg-ref id="com.socalabs.wavetable.vst.pkg"/>
</choice>
<choice id="com.socalabs.wavetable.vst3"
title="VST3"
description="Install the VST3 plug-in to /Library/Audio/Plug-Ins/VST3"
visible="true">
<pkg-ref id="com.socalabs.wavetable.vst3.pkg"/>
</choice>
<choice id="com.socalabs.wavetable.au"
title="Audio Unit"
description="Install the Audio Unit plug-in to /Library/Audio/Plug-Ins/Components"
visible="true">
<pkg-ref id="com.socalabs.wavetable.au.pkg"/>
</choice>
<choice id="com.socalabs.wavetable.clap"
title="CLAP"
description="Install the CLAP plug-in to /Library/Audio/Plug-Ins/CLAP"
visible="true">
<pkg-ref id="com.socalabs.wavetable.clap.pkg"/>
</choice>
<choice id="com.socalabs.wavetable.resources"
title="Factory Wavetables &amp; Presets"
description="Install factory content to /Library/Audio/Presets/SocaLabs/Wavetable"
visible="true"
enabled="false"
selected="true">
<pkg-ref id="com.socalabs.wavetable.resources.pkg"/>
</choice>
<pkg-ref id="com.socalabs.wavetable.vst.pkg" version="0">vst.pkg</pkg-ref>
<pkg-ref id="com.socalabs.wavetable.vst3.pkg" version="0">vst3.pkg</pkg-ref>
<pkg-ref id="com.socalabs.wavetable.au.pkg" version="0">au.pkg</pkg-ref>
<pkg-ref id="com.socalabs.wavetable.clap.pkg" version="0">clap.pkg</pkg-ref>
<pkg-ref id="com.socalabs.wavetable.resources.pkg" version="0">resources.pkg</pkg-ref>
</installer-gui-script>

View file

@ -0,0 +1,6 @@
#!/bin/sh
# Force the AU host registrar to rescan so the freshly installed Component is
# visible without a logout/reboot.
killall -9 AudioComponentRegistrar >/dev/null 2>&1
exit 0

View file

@ -0,0 +1,14 @@
#!/bin/sh
# Remove any prior install of Wavetable so the new bundle takes its place cleanly.
rm -Rf "/Library/Audio/Plug-Ins/VST/Wavetable.vst"
rm -Rf "/Library/Audio/Plug-Ins/VST3/Wavetable.vst3"
rm -Rf "/Library/Audio/Plug-Ins/Components/Wavetable.component"
rm -Rf "/Library/Audio/Plug-Ins/CLAP/Wavetable.clap"
# Wipe the previous shipped factory content so removed wavetables/presets from
# the old version don't linger. User content lives under ~/Library and is left alone.
rm -Rf "/Library/Audio/Presets/SocaLabs/Wavetable/Presets"
rm -Rf "/Library/Audio/Presets/SocaLabs/Wavetable/Wavetables"
exit 0

View file

@ -0,0 +1,17 @@
Wavetable by SocaLabs
=====================
This installer places the Wavetable plug-in in your system audio plug-in
folders along with the factory wavetable bank and factory presets.
Components:
• VST /Library/Audio/Plug-Ins/VST
• VST3 /Library/Audio/Plug-Ins/VST3
• Audio Unit /Library/Audio/Plug-Ins/Components
• CLAP /Library/Audio/Plug-Ins/CLAP
• Factory content /Library/Audio/Presets/SocaLabs/Wavetable
User-saved presets live under
~/Library/Audio/Presets/SocaLabs/Wavetable
and are not touched by this installer.

View file

@ -0,0 +1,75 @@
; Wavetable installer (Inno Setup)
#define MyAppName "Wavetable"
#define MyAppCompany "SocaLabs"
#define MyAppPublisher "SocaLabs"
#define MyAppCopyright "2026 SocaLabs"
#define MyAppURL "https://socalabs.com/"
#define MyAppVersion GetStringFileInfo("bin\VST3\Wavetable.vst3\Contents\x86_64-win\Wavetable.vst3", "ProductVersion")
#define MyDefaultDirName "{commoncf64}\VST3"
[Setup]
AppID={{FB36B27A-9328-4438-8612-3B4E73021845}
AppName={#MyAppCompany} {#MyAppName} {#MyAppVersion}
AppVerName={#MyAppCompany} {#MyAppName} {#MyAppVersion}
AppVersion={#MyAppVersion}
AppCopyright={#MyAppCopyright}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={#MyDefaultDirName}
DisableProgramGroupPage=yes
OutputDir=.\bin
OutputBaseFilename=Wavetable
Compression=lzma/ultra
SolidCompression=true
ShowLanguageDialog=auto
LicenseFile=..\EULA.rtf
InternalCompressLevel=ultra
MinVersion=0,6.1.7600
FlatComponentsList=false
AppendDefaultDirName=false
AlwaysShowDirOnReadyPage=yes
DirExistsWarning=no
DisableDirPage=yes
DisableWelcomePage=no
DisableReadyPage=no
DisableReadyMemo=no
ArchitecturesAllowed=x64compatible
ArchitecturesInstallIn64BitMode=x64compatible
VersionInfoVersion={#MyAppVersion}
VersionInfoCompany={#MyAppPublisher}
VersionInfoCopyright={#MyAppCopyright}
VersionInfoProductName={#MyAppCompany} {#MyAppName} {#MyAppVersion} (64-bit)
VersionInfoProductVersion={#MyAppVersion}
VersionInfoProductTextVersion={#MyAppVersion}
UsePreviousGroup=False
Uninstallable=no
PrivilegesRequired=admin
[Languages]
Name: english; MessagesFile: compiler:Default.isl
[Components]
Name: "vst"; Description: "VST plug-in"; Types: full custom; Flags: checkablealone
Name: "vst3"; Description: "VST3 plug-in"; Types: full custom; Flags: checkablealone
Name: "clap"; Description: "CLAP plug-in"; Types: full custom; Flags: checkablealone
Name: "resources"; Description: "Factory wavetables and presets"; Types: full custom; Flags: fixed
[InstallDelete]
Type: files; Name: "{commoncf64}\VST\Wavetable.dll"; Components: vst
Type: filesandordirs; Name: "{commoncf64}\VST3\Wavetable.vst3"; Components: vst3
Type: files; Name: "{commoncf64}\CLAP\Wavetable.clap"; Components: clap
Type: filesandordirs; Name: "{commonappdata}\SocaLabs\Wavetable\Presets"; Components: resources
Type: filesandordirs; Name: "{commonappdata}\SocaLabs\Wavetable\Wavetables"; Components: resources
[Files]
; Plug-in formats
Source: "bin\VST\Wavetable.dll"; DestDir: "{commoncf64}\VST"; Flags: ignoreversion overwritereadonly; Components: vst
Source: "bin\VST3\Wavetable.vst3\*"; DestDir: "{commoncf64}\VST3\Wavetable.vst3\"; Flags: ignoreversion overwritereadonly recursesubdirs; Components: vst3
Source: "bin\CLAP\Wavetable.clap"; DestDir: "{commoncf64}\CLAP"; Flags: ignoreversion overwritereadonly; Components: clap
; Factory content (Wavetables, Presets) → C:\ProgramData\SocaLabs\Wavetable\
Source: "..\..\plugin\Resources\Presets\*.xml"; DestDir: "{commonappdata}\SocaLabs\Wavetable\Presets\"; Flags: ignoreversion; Components: resources
Source: "..\..\plugin\Resources\WavetablesFLAC\*.wt2048"; DestDir: "{commonappdata}\SocaLabs\Wavetable\Wavetables\"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: resources

View file

@ -0,0 +1,5 @@
{
"Endpoint": "https://wus.codesigning.azure.net/",
"CodeSigningAccountName": "Signing2025",
"CertificateProfileName": "rabien"
}

View file

@ -1,148 +0,0 @@
#!/bin/bash -e
set +x
PLUGIN="Wavetable"
# linux specific stiff
if [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
sudo apt-get update
sudo apt-get install clang git ninja-build ladspa-sdk freeglut3-dev g++ libasound2-dev libcurl4-openssl-dev libfreetype6-dev libjack-jackd2-dev libx11-dev libxcomposite-dev libxcursor-dev libxinerama-dev libxrandr-dev mesa-common-dev webkit2gtk-4.0 juce-tools xvfb
fi
# mac specific stuff
if [ "$(uname)" == "Darwin" ]; then
# Create a temp keychain
if [ -n "$GITHUB_ACTIONS" ]; then
if [ -n "$APPLICATION" ]; then
echo "Create a keychain"
security create-keychain -p nr4aGPyz Keys.keychain
echo $APPLICATION | base64 -D -o /tmp/Application.p12
echo $INSTALLER | base64 -D -o /tmp/Installer.p12
security import /tmp/Application.p12 -t agg -k Keys.keychain -P aym9PKWB -A -T /usr/bin/codesign
security import /tmp/Installer.p12 -t agg -k Keys.keychain -P aym9PKWB -A -T /usr/bin/codesign
security list-keychains -s Keys.keychain
security default-keychain -s Keys.keychain
security unlock-keychain -p nr4aGPyz Keys.keychain
security set-keychain-settings -l -u -t 13600 Keys.keychain
security set-key-partition-list -S apple-tool:,apple: -s -k nr4aGPyz Keys.keychain
fi
DEV_APP_ID="Developer ID Application: Roland Rabien (3FS7DJDG38)"
DEV_INST_ID="Developer ID Installer: Roland Rabien (3FS7DJDG38)"
fi
fi
ROOT=$(cd "$(dirname "$0")/.."; pwd)
cd "$ROOT"
echo "$ROOT"
BRANCH=${GITHUB_REF##*/}
echo "$BRANCH"
cd "$ROOT/ci"
rm -Rf bin
mkdir bin
# Build mac version
if [ "$(uname)" == "Darwin" ]; then
cd "$ROOT"
cmake --preset xcode
cmake --build --preset xcode --config Release
mkdir -p "$ROOT/ci/bin/au"
mkdir -p "$ROOT/ci/bin/vst"
mkdir -p "$ROOT/ci/bin/vst3"
mkdir -p "$ROOT/ci/bin/clap"
cp -R "$ROOT/Builds/xcode/${PLUGIN}_artefacts/Release/AU/$PLUGIN.component" "$ROOT/ci/bin/au"
cp -R "$ROOT/Builds/xcode/${PLUGIN}_artefacts/Release/VST/$PLUGIN.vst" "$ROOT/ci/bin/vst"
cp -R "$ROOT/Builds/xcode/${PLUGIN}_artefacts/Release/VST3/$PLUGIN.vst3" "$ROOT/ci/bin/vst3"
cp -R "$ROOT/Builds/xcode/${PLUGIN}_artefacts/Release/CLAP/$PLUGIN.clap" "$ROOT/ci/bin/clap"
cd "$ROOT/ci/bin"
if [ -n "$APPLICATION" ]; then
codesign -s "$DEV_APP_ID" -v vst/$PLUGIN.vst --options=runtime --timestamp --force
codesign -s "$DEV_APP_ID" -v vst3/$PLUGIN.vst3 --options=runtime --timestamp --force
codesign -s "$DEV_APP_ID" -v au/$PLUGIN.component --options=runtime --timestamp --force
codesign -s "$DEV_APP_ID" -v clap/$PLUGIN.clap --options=runtime --timestamp --force
else
echo "Not signing"
fi
# Notarize
cd "$ROOT/ci/bin"
if [[ -n "$APPLE_USER" ]]; then
zip -r ${PLUGIN}_Mac.zip vst/$PLUGIN.vst vst3/$PLUGIN.vst3 au/$PLUGIN.component clap/$PLUGIN.clap
xcrun notarytool submit --verbose --apple-id "$APPLE_USER" --password "$APPLE_PASS" --team-id "3FS7DJDG38" --wait --timeout 30m ${PLUGIN}_Mac.zip
rm ${PLUGIN}_Mac.zip
xcrun stapler staple vst/$PLUGIN.vst
xcrun stapler staple vst3/$PLUGIN.vst3
xcrun stapler staple au/$PLUGIN.component
xcrun stapler staple clap/$PLUGIN.clap
else
echo "Not notarizing"
fi
zip -r ${PLUGIN}_Mac.zip vst/$PLUGIN.vst vst3/$PLUGIN.vst3 au/$PLUGIN.component clap/$PLUGIN.clap
if [ "$BRANCH" = "release" ]; then
curl -F "files=@${PLUGIN}_Mac.zip" "https://socalabs.com/files/set.php?key=$APIKEY"
fi
# Build linux version
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
cd "$ROOT"
cmake --preset ninja-gcc
cmake --build --preset ninja-gcc --config Release
mkdir -p "$ROOT/ci/bin/lv2"
mkdir -p "$ROOT/ci/bin/vst"
mkdir -p "$ROOT/ci/bin/vst3"
mkdir -p "$ROOT/ci/bin/clap"
cp -R "$ROOT/Builds/ninja-gcc/${PLUGIN}_artefacts/Release/LV2/$PLUGIN.lv2" "$ROOT/ci/bin/lv2"
cp -R "$ROOT/Builds/ninja-gcc/${PLUGIN}_artefacts/Release/VST/lib$PLUGIN.so" "$ROOT/ci/bin/vst/$PLUGIN.so"
cp -R "$ROOT/Builds/ninja-gcc/${PLUGIN}_artefacts/Release/VST3/$PLUGIN.vst3" "$ROOT/ci/bin/vst3"
cp -R "$ROOT/Builds/ninja-gcc/${PLUGIN}_artefacts/Release/CLAP/$PLUGIN.clap" "$ROOT/ci/bin/clap"
cd "$ROOT/ci/bin"
# Strip debug symbols
strip vst/$PLUGIN.so
strip vst3/$PLUGIN.vst3/Contents/x86_64-linux/$PLUGIN.so
strip lv2/$PLUGIN.lv2/lib$PLUGIN.so
strip clap/$PLUGIN.clap
# Upload
cd "$ROOT/ci/bin"
zip -r ${PLUGIN}_Linux.zip vst/$PLUGIN.so vst3/$PLUGIN.vst3 lv2/$PLUGIN.lv2 clap/$PLUGIN.clap
if [ "$BRANCH" = "release" ]; then
curl -F "files=@${PLUGIN}_Linux.zip" "https://socalabs.com/files/set.php?key=$APIKEY"
fi
# Build Win version
elif [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then
cd "$ROOT"
cmake --preset vs
cmake --build --preset vs --config Release
mkdir -p "$ROOT/ci/bin/vst"
mkdir -p "$ROOT/ci/bin/vst3"
mkdir -p "$ROOT/ci/bin/clap"
cp -R "$ROOT/Builds/vs/${PLUGIN}_artefacts/Release/VST/$PLUGIN.dll" "$ROOT/ci/bin/vst"
cp -R "$ROOT/Builds/vs/${PLUGIN}_artefacts/Release/VST3/$PLUGIN.vst3" "$ROOT/ci/bin/vst3"
cp -R "$ROOT/Builds/vs/${PLUGIN}_artefacts/Release/CLAP/$PLUGIN.clap" "$ROOT/ci/bin/clap"
cd "$ROOT/ci/bin"
7z a ${PLUGIN}_Win.zip vst/$PLUGIN.dll vst3/$PLUGIN.vst3 clap/$PLUGIN.clap
if [ "$BRANCH" = "release" ]; then
curl -F "files=@${PLUGIN}_Win.zip" "https://socalabs.com/files/set.php?key=$APIKEY"
fi
fi

View file

@ -1,16 +0,0 @@
#!/bin/bash -e
ROOT=$(cd "$(dirname "$0")/.."; pwd)
cd "$ROOT"
export PATH=$PATH:"/c/Program Files/CMake/bin"
if [ "$(uname)" == "Darwin" ]; then
TOOLCHAIN="xcode"
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
TOOLCHAIN="ninja-gcc"
elif [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then
TOOLCHAIN="vs"
fi
cmake --preset $TOOLCHAIN -D BUILD_EXTRAS=OFF -D JUCE_COPY_PLUGIN_AFTER_BUILD=ON

View file

@ -1,26 +0,0 @@
#!/bin/bash -e
set +x
PLUGIN="Wavetable"
ROOT=$(cd "$(dirname "$0")/.."; pwd)
cd "$ROOT"
echo "$ROOT"
# Upload mac version
if [ "$(uname)" == "Darwin" ]; then
cd "$ROOT/ci/bin"
curl -F "files=@${PLUGIN}_Mac.zip" "https://socalabs.com/files/set.php?key=$APIKEY"
fi
# Upload linux version
if [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
cd "$ROOT/ci/bin"
curl -F "files=@${PLUGIN}_Linux.zip" "https://socalabs.com/files/set.php?key=$APIKEY"
fi
# Upload Win version
if [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then
cd "$ROOT/ci/bin"
curl -F "files=@${PLUGIN}_Win.zip" "https://socalabs.com/files/set.php?key=$APIKEY"
fi

@ -1 +1 @@
Subproject commit f26a6a3b8dc96c1de9b6011390ccc80308c85126
Subproject commit 7781d8dafb59e6e4b7837ff8acb113fcae7291ea

View file

@ -143,12 +143,7 @@ public:
}
else if (e.originalComponent == &h && e.mouseWasClicked() && e.x >= prevButton.getRight() && e.x <= nextButton.getX())
{
juce::StringArray tables;
for (auto i = 0; i < BinaryData::namedResourceListSize; i++)
if (juce::String (BinaryData::originalFilenames[i]).endsWith (".wt2048"))
tables.add (juce::String (BinaryData::originalFilenames[i]).upToLastOccurrenceOf (".wt2048", false, false));
tables.sortNatural();
auto tables = proc.getWavetableNames();
std::map<juce::String, juce::PopupMenu> menus;

View file

@ -507,6 +507,49 @@ void extractWavetables()
}
#endif
//==============================================================================
namespace
{
juce::File userResourceRoot()
{
#if JUCE_MAC
return juce::File::getSpecialLocation (juce::File::userHomeDirectory)
.getChildFile ("Library/Audio/Presets/SocaLabs/Wavetable");
#elif JUCE_WINDOWS
return juce::File::getSpecialLocation (juce::File::userApplicationDataDirectory)
.getChildFile ("SocaLabs/Wavetable");
#else
return juce::File::getSpecialLocation (juce::File::userApplicationDataDirectory)
.getChildFile ("SocaLabs/Wavetable");
#endif
}
juce::File systemResourceRoot()
{
#if JUCE_MAC
return juce::File ("/Library/Audio/Presets/SocaLabs/Wavetable");
#elif JUCE_WINDOWS
return juce::File::getSpecialLocation (juce::File::commonApplicationDataDirectory)
.getChildFile ("SocaLabs/Wavetable");
#else
return juce::File ("/usr/share/SocaLabs/Wavetable");
#endif
}
// Pre-installer location used when factory presets were embedded and gin's
// default getProgramDirectory() chose a "programs" folder under devId.
juce::File legacyUserProgramDirectory()
{
#if JUCE_MAC
return juce::File::getSpecialLocation (juce::File::userApplicationDataDirectory)
.getChildFile ("Application Support/com.socalabs/Wavetable/programs");
#else
return juce::File::getSpecialLocation (juce::File::userApplicationDataDirectory)
.getChildFile ("com.socalabs/Wavetable/programs");
#endif
}
}
//==============================================================================
static gin::ProcessorOptions createProcessorOptions()
{
@ -520,12 +563,20 @@ WavetableAudioProcessor::WavetableAudioProcessor()
fireAmp (FXBaseCallback ([this] { return gin::Processor::getSampleRate(); })),
grindAmp (FXBaseCallback ([this] { return gin::Processor::getSampleRate(); }))
{
{
auto sz = 0;
for (auto i = 0; i < BinaryData::namedResourceListSize; i++)
if (juce::String (BinaryData::originalFilenames[i]).endsWith (".xml"))
if (auto data = BinaryData::getNamedResource (BinaryData::namedResourceList[i], sz))
extractProgram (BinaryData::originalFilenames[i], data, sz);
// One-time migration of any user presets from the pre-installer location.
// Factory presets now live in systemResourceRoot()/Presets and are surfaced
// via getFactoryProgramDirectories(). User saves go to userResourceRoot()/Presets.
{
auto oldDir = legacyUserProgramDirectory();
auto newDir = userResourceRoot().getChildFile ("Presets");
if (oldDir.isDirectory()
&& newDir.findChildFiles (juce::File::findFiles, false, "*.xml").isEmpty())
{
if (! newDir.isDirectory())
newDir.createDirectory();
for (auto f : oldDir.findChildFiles (juce::File::findFiles, false, "*.xml"))
f.copyFileTo (newDir.getChildFile (f.getFileName()));
}
}
mtsClient = MTS_RegisterClient();
@ -592,14 +643,12 @@ void WavetableAudioProcessor::reloadWavetables()
{
auto loadMemory = [&] (const juce::String& name) -> juce::MemoryBlock
{
for (auto i = 0; i < BinaryData::namedResourceListSize; i++)
auto file = systemResourceRoot().getChildFile ("Wavetables").getChildFile (name + ".wt2048");
if (file.existsAsFile())
{
if ((name + ".wt2048").equalsIgnoreCase (BinaryData::originalFilenames[i]))
{
int sz = 0;
if (auto data = BinaryData::getNamedResource (BinaryData::namedResourceList[i], sz))
return juce::MemoryBlock (data, sz);
}
juce::MemoryBlock mb;
if (file.loadFileAsData (mb))
return mb;
}
return {};
};
@ -651,12 +700,7 @@ void WavetableAudioProcessor::incWavetable (int osc, int delta)
else
userTable2.reset();
juce::StringArray tables;
for (auto i = 0; i < BinaryData::namedResourceListSize; i++)
if (juce::String (BinaryData::originalFilenames[i]).endsWith (".wt2048"))
tables.add (juce::String (BinaryData::originalFilenames[i]).upToLastOccurrenceOf (".wt2048", false, false));
tables.sortNatural();
auto tables = getWavetableNames();
auto idx = tables.indexOf (table.toString());
@ -725,6 +769,34 @@ void WavetableAudioProcessor::updateState()
state.setProperty ("wt2Data", userTable2.toBase64Encoding(), nullptr);
}
//==============================================================================
juce::File WavetableAudioProcessor::getProgramDirectory()
{
auto dir = userResourceRoot().getChildFile ("Presets");
if (! dir.isDirectory())
dir.createDirectory();
return dir;
}
juce::Array<juce::File> WavetableAudioProcessor::getFactoryProgramDirectories()
{
return { systemResourceRoot().getChildFile ("Presets") };
}
juce::StringArray WavetableAudioProcessor::getWavetableNames() const
{
juce::StringArray tables;
auto wtDir = systemResourceRoot().getChildFile ("Wavetables");
if (wtDir.isDirectory())
for (auto f : wtDir.findChildFiles (juce::File::findFiles, false, "*.wt2048"))
tables.add (f.getFileNameWithoutExtension());
tables.sortNatural();
return tables;
}
//==============================================================================
void WavetableAudioProcessor::setupModMatrix()
{

View file

@ -24,6 +24,9 @@ public:
bool supportsMPE() const override { return true; }
juce::File getProgramDirectory() override;
juce::Array<juce::File> getFactoryProgramDirectories() override;
void stateUpdated() override;
void updateState() override;
@ -52,6 +55,7 @@ public:
void reloadWavetables();
void incWavetable (int osc, int delta);
bool loadUserWavetable (int osc, const juce::File& f, int sz);
juce::StringArray getWavetableNames() const;
void applyEffects (juce::AudioSampleBuffer& buffer);
void applyEffect (juce::AudioSampleBuffer& buffer, int fxId);

View file

@ -23,4 +23,19 @@ fi
echo "$NOTES" > /tmp/release_notes.txt
gh release create "$VER" --title "$VER" -F /tmp/release_notes.txt ./Binaries\ Linux/*.zip ./Binaries\ Windows/*.zip ./Binaries\ macOS/*.zip
ASSETS=(
"./Binaries Linux"/*.deb
"./Binaries Windows"/*.exe
"./Binaries macOS"/*.pkg
)
if [ -f "./Binaries macOS/Symbols_Mac.zip" ]; then
ASSETS+=("./Binaries macOS/Symbols_Mac.zip")
fi
gh release create "$VER" --title "$VER" -F /tmp/release_notes.txt "${ASSETS[@]}"
for f in "./Binaries Linux"/*.deb \
"./Binaries Windows"/*.exe \
"./Binaries macOS"/*.pkg; do
curl -F "files=@${f}" "https://socalabs.com/files/set.php?key=$APIKEY"
done