Adopted Span and MutableSpan for buffers across the library to adapt to Swift 6.4.

This commit is contained in:
2026-09-18 12:06:21 +02:00
parent 89c8d7a04c
commit 4cb0f8f500
18 changed files with 127 additions and 83 deletions
@@ -1,8 +1,9 @@
extension Sound.Effect {
/// Processes up to `AUDIO_FRAMES_PER_CYCLE` sample frames in signed
/// Q8.24 format. `bufferActive` is `false` when the input buffer is
/// silent. Returns `true` if the effect produced output.
public typealias Processor = (_ left: UnsafeMutableBufferPointer<Int32>,
_ right: UnsafeMutableBufferPointer<Int32>?,
/// Q8.24 format. `right` is empty when the channel is mono.
/// `bufferActive` is `false` when the input buffer is silent. Returns
/// `true` if the effect produced output.
public typealias Processor = (_ left: inout MutableSpan<Int32>,
_ right: inout MutableSpan<Int32>,
_ bufferActive: Bool) -> Bool
}
@@ -30,9 +30,9 @@ extension Sound {
guard let effect, let left,
let userdata = effectAPI.pointee.getUserdata.unsafelyUnwrapped(effect) else { return 0 }
let box = Unmanaged<ProcessorBox>.fromOpaque(userdata).takeUnretainedValue()
let leftBuffer = UnsafeMutableBufferPointer(start: left, count: Int(nsamples))
let rightBuffer = right.map { UnsafeMutableBufferPointer(start: $0, count: Int(nsamples)) }
return box.processor(leftBuffer, rightBuffer, bufactive != 0) ? 1 : 0
var leftSpan = UnsafeMutableBufferPointer(start: left, count: Int(nsamples)).mutableSpan
var rightSpan = UnsafeMutableBufferPointer(start: right, count: right == nil ? 0 : Int(nsamples)).mutableSpan
return box.processor(&leftSpan, &rightSpan, bufactive != 0) ? 1 : 0
}, box.toOpaque()).unsafelyUnwrapped
isOwned = true
}
+4 -4
View File
@@ -51,14 +51,14 @@ extension Sound {
/// Sets a callback that records microphone input. Return `false` from the
/// callback to stop recording. Pass `nil` to stop recording immediately.
/// The buffer contains mono 16-bit samples.
/// The span contains mono 16-bit samples.
@discardableResult
public static func setMicCallback(source: MicSource = .autodetect,
_ callback: ((UnsafeMutableBufferPointer<Int16>) -> Bool)?) -> Bool {
_ callback: ((Span<Int16>) -> Bool)?) -> Bool {
micCallback = callback
if callback != nil {
return snd.pointee.setMicCallback.unsafelyUnwrapped({ _, buffer, length in
let samples = UnsafeMutableBufferPointer(start: buffer, count: Int(length))
let samples = UnsafeBufferPointer(start: buffer, count: Int(length)).span
return Sound.micCallback?(samples) == true ? 1 : 0
}, nil, CPlaydate.MicSource(CPlaydate.MicSource.RawValue(source.rawValue))) != 0
} else {
@@ -66,7 +66,7 @@ extension Sound {
}
}
nonisolated(unsafe) private static var micCallback: ((UnsafeMutableBufferPointer<Int16>) -> Bool)?
nonisolated(unsafe) private static var micCallback: ((Span<Int16>) -> Bool)?
/// Asks the user for permission to record from the microphone. `purpose`
/// is shown in the permission prompt. The completion receives whether
@@ -1,6 +1,6 @@
extension Sound.CallbackSource {
/// Fills the sample buffers and returns `true` if output was
/// produced. `right` is non-nil only for stereo sources.
public typealias Callback = (_ left: UnsafeMutableBufferPointer<Int16>,
_ right: UnsafeMutableBufferPointer<Int16>?) -> Bool
/// produced. `right` is empty for mono sources.
public typealias Callback = (_ left: inout MutableSpan<Int16>,
_ right: inout MutableSpan<Int16>) -> Bool
}
@@ -27,9 +27,9 @@ extension Sound {
UnsafeMutablePointer<Int16>?, Int32) -> Int32 = { context, left, right, length in
guard let context, let left else { return 0 }
let source = Unmanaged<CallbackSource>.fromOpaque(context).takeUnretainedValue()
let leftBuffer = UnsafeMutableBufferPointer(start: left, count: Int(length))
let rightBuffer = right.map { UnsafeMutableBufferPointer(start: $0, count: Int(length)) }
return source.callback(leftBuffer, rightBuffer) ? 1 : 0
var leftSpan = UnsafeMutableBufferPointer(start: left, count: Int(length)).mutableSpan
var rightSpan = UnsafeMutableBufferPointer(start: right, count: right == nil ? 0 : Int(length)).mutableSpan
return source.callback(&leftSpan, &rightSpan) ? 1 : 0
}
/// Attaches the C object created for this source.
@@ -7,7 +7,7 @@ extension Sound {
var loopCallback: ((FilePlayer) -> Void)?
var fadeCallback: ((FilePlayer) -> Void)?
var mp3DataSource: ((UnsafeMutableBufferPointer<UInt8>) -> Int)?
var mp3DataSource: ((inout MutableSpan<UInt8>) -> Int)?
private var retainedRateModulator: SignalValue?
override init(pointer: OpaquePointer?, isOwned: Bool) {
@@ -131,13 +131,13 @@ extension Sound {
/// fills the buffer and returns the number of bytes written; return 0
/// to signal the end of the stream.
public func setMP3StreamSource(bufferLength: Float,
_ dataSource: @escaping (UnsafeMutableBufferPointer<UInt8>) -> Int) {
_ dataSource: @escaping (inout MutableSpan<UInt8>) -> Int) {
mp3DataSource = dataSource
FilePlayer.api.pointee.setMP3StreamSource.unsafelyUnwrapped(pointer, { data, bytes, userdata in
guard let userdata, let data else { return 0 }
let player = Unmanaged<FilePlayer>.fromOpaque(userdata).takeUnretainedValue()
let buffer = UnsafeMutableBufferPointer(start: data, count: Int(bytes))
return Int32(player.mp3DataSource?(buffer) ?? 0)
var buffer = UnsafeMutableBufferPointer(start: data, count: Int(bytes)).mutableSpan
return Int32(player.mp3DataSource?(&buffer) ?? 0)
}, Unmanaged.passUnretained(self).toOpaque(), bufferLength)
}
@@ -75,9 +75,9 @@ extension Sound {
{ userdata, left, right, nsamples, rate, drate in
guard let userdata, let left else { return 0 }
let box = Unmanaged<GeneratorBox>.fromOpaque(userdata).takeUnretainedValue()
let leftBuffer = UnsafeMutableBufferPointer(start: left, count: Int(nsamples))
let rightBuffer = right.map { UnsafeMutableBufferPointer(start: $0, count: Int(nsamples)) }
return Int32(box.generator.render(leftBuffer, rightBuffer, rate, drate))
var leftSpan = UnsafeMutableBufferPointer(start: left, count: Int(nsamples)).mutableSpan
var rightSpan = UnsafeMutableBufferPointer(start: right, count: right == nil ? 0 : Int(nsamples)).mutableSpan
return Int32(box.generator.render(&leftSpan, &rightSpan, rate, drate))
},
{ userdata, note, velocity, length in
guard let userdata else { return }
@@ -2,11 +2,11 @@ extension Sound.Synth {
/// Custom generator callbacks. Samples are in signed Q8.24 format.
public struct Generator {
/// Renders up to 256 sample frames into `left` (and `right` for
/// stereo generators). `rate` is the per-frame phase step in
/// stereo generators; it is empty for mono ones). `rate` is the per-frame phase step in
/// Q0.32 format and `drate` its per-frame change. Returns the
/// number of frames rendered.
public var render: (_ left: UnsafeMutableBufferPointer<Int32>,
_ right: UnsafeMutableBufferPointer<Int32>?,
public var render: (_ left: inout MutableSpan<Int32>,
_ right: inout MutableSpan<Int32>,
_ rate: UInt32, _ drate: Int32) -> Int
/// Called when a note starts. `length` is -1 for indefinite notes.
public var noteOn: ((_ note: Sound.MIDINote, _ velocity: Float, _ length: Float) -> Void)?
@@ -17,8 +17,8 @@ extension Sound.Synth {
/// valid.
public var setParameter: ((_ parameter: Int, _ value: Float) -> Bool)?
public init(render: @escaping (_ left: UnsafeMutableBufferPointer<Int32>,
_ right: UnsafeMutableBufferPointer<Int32>?,
public init(render: @escaping (_ left: inout MutableSpan<Int32>,
_ right: inout MutableSpan<Int32>,
_ rate: UInt32, _ drate: Int32) -> Int,
noteOn: ((_ note: Sound.MIDINote, _ velocity: Float, _ length: Float) -> Void)? = nil,
release: ((_ stop: Bool) -> Void)? = nil,