Files
playdate-kit/Sources/PlaydateKit/Sound/Source/Classes/SamplePlayer.swift
T
javier c5037dd716 Swift 6.4 migration and exhancements (#1)
This PR contains the work done to update the library and the attached example project to use the Swift 6.4 computer as a minimum supported version and also, to use the latest features introduced in it.

Reviewed-on: #1
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
2026-09-18 13:15:08 +00:00

101 lines
3.9 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
internal import CPlaydate
extension Sound {
/// Plays an `AudioSample` from memory. Wraps `SamplePlayer`.
public final class SamplePlayer: Source {
private static var api: UnsafePointer<playdate_sound_sampleplayer> { Playdate.samplePlayerAPI.unsafelyUnwrapped }
var loopCallback: ((SamplePlayer) -> Void)?
private var retainedSample: AudioSample?
private var retainedRateModulator: SignalValue?
override init(pointer: OpaquePointer?, isOwned: Bool) {
super.init(pointer: pointer, isOwned: isOwned)
}
public convenience init() {
self.init(pointer: SamplePlayer.api.pointee.newPlayer.unsafelyUnwrapped().unsafelyUnwrapped,
isOwned: true)
}
public convenience init(path: String) throws(PlaydateError) {
self.init()
sample = try AudioSample(path: path)
}
deinit {
if isOwned {
SamplePlayer.api.pointee.freePlayer.unsafelyUnwrapped(pointer)
}
}
/// Retained by the player.
public var sample: AudioSample? {
get { retainedSample }
set {
retainedSample = newValue
SamplePlayer.api.pointee.setSample.unsafelyUnwrapped(pointer, newValue?.pointer)
}
}
/// Plays `repeat` times at `rate` (1 is normal); 0 loops forever, -1 ping-pongs.
@discardableResult
public func play(repeat repeatCount: Int = 1, rate: Float = 1) -> Bool {
SamplePlayer.api.pointee.play.unsafelyUnwrapped(pointer, Int32(repeatCount), rate) != 0
}
public func stop() {
SamplePlayer.api.pointee.stop.unsafelyUnwrapped(pointer)
}
public func setPaused(_ paused: Bool) {
SamplePlayer.api.pointee.setPaused.unsafelyUnwrapped(pointer, paused ? 1 : 0)
}
/// Length in seconds.
public var length: Float {
SamplePlayer.api.pointee.getLength.unsafelyUnwrapped(pointer)
}
/// Playback position, in seconds.
public var offset: Float {
get { SamplePlayer.api.pointee.getOffset.unsafelyUnwrapped(pointer) }
set { SamplePlayer.api.pointee.setOffset.unsafelyUnwrapped(pointer, newValue) }
}
/// Playback rate; 1 is normal. Negative plays backward (PCM only, not ADPCM).
public var rate: Float {
get { SamplePlayer.api.pointee.getRate.unsafelyUnwrapped(pointer) }
set { SamplePlayer.api.pointee.setRate.unsafelyUnwrapped(pointer, newValue) }
}
/// Restricts playback to `start``end`, in sample frames.
public func setPlayRange(start: Int, end: Int) {
SamplePlayer.api.pointee.setPlayRange.unsafelyUnwrapped(pointer, Int32(start), Int32(end))
}
/// Called each time playback loops; `nil` removes it.
public func setLoopCallback(_ callback: ((SamplePlayer) -> Void)?) {
loopCallback = callback
if callback != nil {
SamplePlayer.api.pointee.setLoopCallback.unsafelyUnwrapped(pointer, { _, userdata in
guard let userdata else { return }
let player = Unmanaged<SamplePlayer>.fromOpaque(userdata).takeUnretainedValue()
player.loopCallback?(player)
}, Unmanaged.passUnretained(self).toOpaque())
} else {
SamplePlayer.api.pointee.setLoopCallback.unsafelyUnwrapped(pointer, nil, nil)
}
}
/// A signal added to `rate`; `nil` clears it. The player retains it.
public var rateModulator: SignalValue? {
get { SignalValue.wrap(SamplePlayer.api.pointee.getRateModulator.unsafelyUnwrapped(pointer)) }
set {
retainedRateModulator = newValue
SamplePlayer.api.pointee.setRateModulator.unsafelyUnwrapped(pointer, newValue?.pointer)
}
}
}
}