From 216899ba3264ea0c8a65a4e7e034345e5f0fe271 Mon Sep 17 00:00:00 2001 From: pcbeard Date: Wed, 3 Apr 2024 23:45:33 -0700 Subject: [PATCH] Add higher-level finishCallback closure Also fixes a bug in .load(path:) that was returning false when file was successfully loaded. --- Sources/PlaydateKit/Core/Sound.swift | 43 ++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/Sources/PlaydateKit/Core/Sound.swift b/Sources/PlaydateKit/Core/Sound.swift index 1e759365..9551cac8 100644 --- a/Sources/PlaydateKit/Core/Sound.swift +++ b/Sources/PlaydateKit/Core/Sound.swift @@ -19,7 +19,13 @@ public enum Sound { pointer = fileplayer.newPlayer.unsafelyUnwrapped().unsafelyUnwrapped } - deinit { fileplayer.freePlayer.unsafelyUnwrapped(pointer) } + deinit { + fileplayer.freePlayer.unsafelyUnwrapped(pointer) + if let callbackData = _callbackData { + callbackData.deinitialize(count: 1) + callbackData.deallocate() + } + } // MARK: Public @@ -30,12 +36,12 @@ public enum Sound { /// Prepares player to stream the file at path. Returns `true` if the file exists, otherwise `false`. @discardableResult public func load(path: StaticString) -> Bool { - fileplayer.loadIntoPlayer(pointer, path.utf8Start) == 0 + fileplayer.loadIntoPlayer(pointer, path.utf8Start) == 1 } /// Prepares player to stream the file at path. Returns `true` if the file exists, otherwise `false`. @discardableResult public func load(path: UnsafePointer) -> Bool { - fileplayer.loadIntoPlayer(pointer, path) == 0 + fileplayer.loadIntoPlayer(pointer, path) == 1 } /// Starts playing the file player. If repeat is greater than one, it loops the given number of times. @@ -44,6 +50,37 @@ public enum Sound { @discardableResult public func play(repeat: Int32 = 1) -> Int32 { fileplayer.play.unsafelyUnwrapped(pointer, `repeat`) } + + private struct CallbackData { + var callback: (() -> Void)? + } + private var _callbackData: UnsafeMutablePointer? + + /// Installs a closure that will be called when playback has completed. This is implemented + /// by wrapping the closure in a struct that is stored in a dynamically allocated block, which + /// is passed as `userData` to `setFinishCallback` below. + public var finishCallback: (() -> Void)? { + get { + _callbackData?.pointee.callback + } + set { + if let callbackData = _callbackData { + callbackData.pointee.callback = newValue + } else if let newValue { + let callbackData: UnsafeMutablePointer = .allocate(capacity: 1) + callbackData.initialize(to: .init(callback: newValue)) + _callbackData = callbackData + setFinishCallback( + callback: { sourceSource, userdata in + if let callback = userdata?.assumingMemoryBound(to: CallbackData.self).pointee.callback { + callback() + } + }, + soundUserdata: callbackData + ) + } + } + } /// Sets a function to be called when playback has completed. public func setFinishCallback(