diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 0bd4733..70e0b15 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -53,11 +53,25 @@ jobs:
hdiutil create -volname rtaudio -srcfolder dmg_temp -ov -format UDZO rtaudio.dmg
rm -rf dmg_temp
+ - name: Download and extract Sparkle
+ run: |
+ cd /tmp
+ curl -L https://github.com/sparkle-project/Sparkle/releases/download/2.9.0/Sparkle-2.9.0.tar.xz | tar xJ
+
+ - name: Generate Sparkle appcast
+ run: |
+ echo "${{ secrets.SPARKLE_PRIVATE_KEY }}" | /tmp/bin/generate_appcast \
+ --ed-key-file - \
+ -o appcast.xml \
+ ./rtaudio.dmg
+
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: rtaudio-release
- path: rtaudio.dmg
+ path: |
+ rtaudio.dmg
+ appcast.xml
retention-days: 1
release:
@@ -87,6 +101,17 @@ jobs:
# expose short SHA for use in the release name/tag
echo "short_sha=$(echo $GITHUB_SHA | cut -c1-7)" >> $GITHUB_OUTPUT
+ - name: Push appcast to rtaudio-updater repo
+ run: |
+ git clone https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository_owner }}/rtaudio-updater.git updater
+ cp artifacts/appcast.xml updater/appcast.xml
+ cd updater
+ git config user.email "action@github.com"
+ git config user.name "GitHub Action"
+ git add appcast.xml
+ git commit -m "Update appcast for release-${{ steps.commit_msg.outputs.short_sha }}" || true
+ git push
+
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
@@ -95,4 +120,6 @@ jobs:
target_commitish: ${{ github.sha }}
name: "🔖 Release #${{ steps.commit_msg.outputs.short_sha }}"
body: ${{ steps.commit_msg.outputs.msg }}
- files: artifacts/rtaudio.dmg
+ files: |
+ artifacts/rtaudio.dmg
+ artifacts/appcast.xml
diff --git a/rtaudio.xcodeproj/project.pbxproj b/rtaudio.xcodeproj/project.pbxproj
index a2ce719..ad41185 100644
--- a/rtaudio.xcodeproj/project.pbxproj
+++ b/rtaudio.xcodeproj/project.pbxproj
@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
+ 264F33942F64499800C1E2AB /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 264F33932F64499800C1E2AB /* Sparkle */; };
266C88832F605EEB00139B20 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 266C88822F605EEB00139B20 /* Accelerate.framework */; };
/* End PBXBuildFile section */
@@ -42,6 +43,7 @@
buildActionMask = 2147483647;
files = (
266C88832F605EEB00139B20 /* Accelerate.framework in Frameworks */,
+ 264F33942F64499800C1E2AB /* Sparkle in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -93,6 +95,7 @@
);
name = rtaudio;
packageProductDependencies = (
+ 264F33932F64499800C1E2AB /* Sparkle */,
);
productName = rtaudio;
productReference = 266C88662F605B8600139B20 /* rtaudio.app */;
@@ -123,6 +126,9 @@
);
mainGroup = 266C885D2F605B8600139B20;
minimizedProjectReferenceProxies = 1;
+ packageReferences = (
+ 264F33922F64499800C1E2AB /* XCRemoteSwiftPackageReference "Sparkle" */,
+ );
preferredProjectObjectVersion = 77;
productRefGroup = 266C88672F605B8600139B20 /* Products */;
projectDirPath = "";
@@ -399,6 +405,25 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
+
+/* Begin XCRemoteSwiftPackageReference section */
+ 264F33922F64499800C1E2AB /* XCRemoteSwiftPackageReference "Sparkle" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/sparkle-project/Sparkle";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 2.9.0;
+ };
+ };
+/* End XCRemoteSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+ 264F33932F64499800C1E2AB /* Sparkle */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 264F33922F64499800C1E2AB /* XCRemoteSwiftPackageReference "Sparkle" */;
+ productName = Sparkle;
+ };
+/* End XCSwiftPackageProductDependency section */
};
rootObject = 266C885E2F605B8600139B20 /* Project object */;
}
diff --git a/rtaudio.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/rtaudio.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
new file mode 100644
index 0000000..6e450d9
--- /dev/null
+++ b/rtaudio.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -0,0 +1,15 @@
+{
+ "originHash" : "e721da7f9826abdffcb6185e886155efa2514bd6234475f1afa893e29eb258d6",
+ "pins" : [
+ {
+ "identity" : "sparkle",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/sparkle-project/Sparkle",
+ "state" : {
+ "revision" : "21d8df80440b1ca3b65fa82e40782f1e5a9e6ba2",
+ "version" : "2.9.0"
+ }
+ }
+ ],
+ "version" : 3
+}
diff --git a/rtaudio/AppDelegate.swift b/rtaudio/AppDelegate.swift
index 353d2ee..2466fe6 100644
--- a/rtaudio/AppDelegate.swift
+++ b/rtaudio/AppDelegate.swift
@@ -9,6 +9,7 @@ import AVFAudio
import Cocoa
internal import MetalKit
import simd
+import Sparkle
func getAppleMusicArtwork() -> NSImage? {
let script = """
@@ -164,6 +165,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
var panel: NSPanel!
let audioTap = AudioTap()
var metalView: WaveformMTKView!
+ private var updaterController: SPUStandardUpdaterController!
let width: CGFloat = 135
let height: CGFloat = 60
@@ -186,7 +188,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
app.run()
}
+ @objc func checkForUpdates() {
+ updaterController.checkForUpdates(nil)
+ }
+
func applicationDidFinishLaunching(_ notification: Notification) {
+ updaterController = SPUStandardUpdaterController(
+ startingUpdater: true, updaterDelegate: nil, userDriverDelegate: nil)
// Create the actual app panel
panel = NSPanel(
contentRect: NSRect(x: 0, y: 0, width: width, height: height),
diff --git a/rtaudio/Info.plist b/rtaudio/Info.plist
index 3f9f95f..479816f 100644
--- a/rtaudio/Info.plist
+++ b/rtaudio/Info.plist
@@ -6,5 +6,10 @@
NSAudioCaptureUsageDescription
This app needs to capture system audio for displaying the waveform.
+ SUFeedURL
+
+ https://raw.githubusercontent.com/ZephyrCodesStuff/rtaudio-updater/refs/heads/main/appcast.xml
+ SUPublicEDKey
+ cqFRi1a5mwPMBGu47gW01S0FwPsQJapmeHQuq4cnXL0=
\ No newline at end of file
diff --git a/rtaudio/Views/WaveformMTKView.swift b/rtaudio/Views/WaveformMTKView.swift
index 2b328cb..26df63e 100644
--- a/rtaudio/Views/WaveformMTKView.swift
+++ b/rtaudio/Views/WaveformMTKView.swift
@@ -156,6 +156,15 @@ class WaveformMTKView: NSView, CAMetalDisplayLinkDelegate {
resetItem.target = NSApp.delegate
menu.addItem(resetItem)
+ menu.addItem(NSMenuItem.separator())
+ let updateItem = NSMenuItem(
+ title: "Check for Updates...",
+ action: #selector(AppDelegate.checkForUpdates),
+ keyEquivalent: ""
+ )
+ updateItem.target = NSApp.delegate
+ menu.addItem(updateItem)
+
menu.addItem(NSMenuItem.separator())
menu.addItem(
NSMenuItem(