Adopted Span and MutableSpan for buffers across the library to adapt to Swift 6.4.
This commit is contained in:
@@ -31,9 +31,10 @@ extension File {
|
|||||||
|
|
||||||
/// Reads up to `buffer.count` bytes into `buffer`. Returns the number
|
/// Reads up to `buffer.count` bytes into `buffer`. Returns the number
|
||||||
/// of bytes read; 0 indicates end of file.
|
/// of bytes read; 0 indicates end of file.
|
||||||
public func read(into buffer: UnsafeMutableRawBufferPointer) throws(PlaydateError) -> Int {
|
public func read(into buffer: inout MutableSpan<UInt8>) throws(PlaydateError) -> Int {
|
||||||
let result = fileAPI.pointee.read.unsafelyUnwrapped(
|
let result = buffer.withUnsafeMutableBufferPointer { buffer in
|
||||||
pointer, buffer.baseAddress, UInt32(buffer.count))
|
fileAPI.pointee.read.unsafelyUnwrapped(pointer, buffer.baseAddress, UInt32(buffer.count))
|
||||||
|
}
|
||||||
if result < 0 { throw lastFileError() }
|
if result < 0 { throw lastFileError() }
|
||||||
return Int(result)
|
return Int(result)
|
||||||
}
|
}
|
||||||
@@ -49,11 +50,12 @@ extension File {
|
|||||||
return bytes
|
return bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes the buffer to the file. Returns the number of bytes written.
|
/// Writes the bytes to the file. Returns the number of bytes written.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func write(_ buffer: UnsafeRawBufferPointer) throws(PlaydateError) -> Int {
|
public func write(_ bytes: Span<UInt8>) throws(PlaydateError) -> Int {
|
||||||
let result = fileAPI.pointee.write.unsafelyUnwrapped(
|
let result = bytes.withUnsafeBufferPointer { buffer in
|
||||||
pointer, buffer.baseAddress, UInt32(buffer.count))
|
fileAPI.pointee.write.unsafelyUnwrapped(pointer, buffer.baseAddress, UInt32(buffer.count))
|
||||||
|
}
|
||||||
if result < 0 { throw lastFileError() }
|
if result < 0 { throw lastFileError() }
|
||||||
return Int(result)
|
return Int(result)
|
||||||
}
|
}
|
||||||
@@ -61,11 +63,9 @@ extension File {
|
|||||||
/// Writes the bytes to the file. Returns the number of bytes written.
|
/// Writes the bytes to the file. Returns the number of bytes written.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func write(_ bytes: [UInt8]) throws(PlaydateError) -> Int {
|
public func write(_ bytes: [UInt8]) throws(PlaydateError) -> Int {
|
||||||
let result = bytes.withUnsafeBytes { buffer in
|
try bytes.withUnsafeBufferPointer { buffer throws(PlaydateError) in
|
||||||
fileAPI.pointee.write.unsafelyUnwrapped(pointer, buffer.baseAddress, UInt32(buffer.count))
|
try write(buffer.span)
|
||||||
}
|
}
|
||||||
if result < 0 { throw lastFileError() }
|
|
||||||
return Int(result)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes the string's UTF-8 to the file. Returns the bytes written.
|
/// Writes the string's UTF-8 to the file. Returns the bytes written.
|
||||||
|
|||||||
@@ -39,14 +39,42 @@ extension Graphics {
|
|||||||
|
|
||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
|
|
||||||
/// The bitmap's dimensions, row stride, and raw storage.
|
/// The bitmap's dimensions and row stride.
|
||||||
public var data: Data {
|
public var data: Data {
|
||||||
|
let raw = rawData()
|
||||||
|
return Data(width: raw.width, height: raw.height, rowBytes: raw.rowBytes,
|
||||||
|
hasMask: raw.mask != nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls `body` with the bitmap's pixel data: one bit per pixel,
|
||||||
|
/// `height` rows of `rowBytes` bytes each.
|
||||||
|
public func withPixelData<Result, Failure: Error>(
|
||||||
|
_ body: (inout MutableSpan<UInt8>) throws(Failure) -> Result
|
||||||
|
) throws(Failure) -> Result {
|
||||||
|
let raw = rawData()
|
||||||
|
var span = UnsafeMutableBufferPointer(
|
||||||
|
start: raw.data, count: raw.data == nil ? 0 : raw.height * raw.rowBytes).mutableSpan
|
||||||
|
return try body(&span)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls `body` with the bitmap's mask data, laid out like the pixel
|
||||||
|
/// data. Returns `nil` if the bitmap has no mask.
|
||||||
|
public func withMaskData<Result, Failure: Error>(
|
||||||
|
_ body: (inout MutableSpan<UInt8>) throws(Failure) -> Result
|
||||||
|
) throws(Failure) -> Result? {
|
||||||
|
let raw = rawData()
|
||||||
|
guard let mask = raw.mask else { return nil }
|
||||||
|
var span = UnsafeMutableBufferPointer(start: mask, count: raw.height * raw.rowBytes).mutableSpan
|
||||||
|
return try body(&span)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func rawData() -> (width: Int, height: Int, rowBytes: Int,
|
||||||
|
mask: UnsafeMutablePointer<UInt8>?, data: UnsafeMutablePointer<UInt8>?) {
|
||||||
var width: Int32 = 0, height: Int32 = 0, rowBytes: Int32 = 0
|
var width: Int32 = 0, height: Int32 = 0, rowBytes: Int32 = 0
|
||||||
var mask: UnsafeMutablePointer<UInt8>?
|
var mask: UnsafeMutablePointer<UInt8>?
|
||||||
var data: UnsafeMutablePointer<UInt8>?
|
var data: UnsafeMutablePointer<UInt8>?
|
||||||
gfx.pointee.getBitmapData.unsafelyUnwrapped(pointer, &width, &height, &rowBytes, &mask, &data)
|
gfx.pointee.getBitmapData.unsafelyUnwrapped(pointer, &width, &height, &rowBytes, &mask, &data)
|
||||||
return Data(width: Int(width), height: Int(height), rowBytes: Int(rowBytes),
|
return (Int(width), Int(height), Int(rowBytes), mask, data)
|
||||||
mask: mask, data: data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cached dimensions, so `width`/`height` don't pay a full
|
/// Cached dimensions, so `width`/`height` don't pay a full
|
||||||
@@ -56,8 +84,8 @@ extension Graphics {
|
|||||||
|
|
||||||
private var size: (width: Int, height: Int) {
|
private var size: (width: Int, height: Int) {
|
||||||
if let cachedSize { return cachedSize }
|
if let cachedSize { return cachedSize }
|
||||||
let data = self.data
|
let raw = rawData()
|
||||||
let size = (data.width, data.height)
|
let size = (raw.width, raw.height)
|
||||||
cachedSize = size
|
cachedSize = size
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,11 @@ extension Graphics {
|
|||||||
|
|
||||||
/// Creates a font from the contents of a .pft file already in memory.
|
/// Creates a font from the contents of a .pft file already in memory.
|
||||||
/// The bytes are copied and retained for the font's lifetime.
|
/// The bytes are copied and retained for the font's lifetime.
|
||||||
public convenience init?(data: UnsafeRawBufferPointer, wide: Bool = false) {
|
public convenience init?(data: Span<UInt8>, wide: Bool = false) {
|
||||||
let copy = UnsafeMutableRawPointer.allocate(byteCount: data.count, alignment: 4)
|
let copy = UnsafeMutableRawPointer.allocate(byteCount: data.count, alignment: 4)
|
||||||
copy.copyMemory(from: data.baseAddress.unsafelyUnwrapped, byteCount: data.count)
|
data.withUnsafeBytes { bytes in
|
||||||
|
copy.copyMemory(from: bytes.baseAddress.unsafelyUnwrapped, byteCount: bytes.count)
|
||||||
|
}
|
||||||
let fontData = OpaquePointer(copy)
|
let fontData = OpaquePointer(copy)
|
||||||
guard let pointer = gfx.pointee.makeFontFromData.unsafelyUnwrapped(
|
guard let pointer = gfx.pointee.makeFontFromData.unsafelyUnwrapped(
|
||||||
fontData, wide ? 1 : 0, Int32(data.count)) else {
|
fontData, wide ? 1 : 0, Int32(data.count)) else {
|
||||||
|
|||||||
@@ -279,15 +279,25 @@ extension Graphics {
|
|||||||
|
|
||||||
// MARK: - Framebuffer
|
// MARK: - Framebuffer
|
||||||
|
|
||||||
/// The current working framebuffer. Rows are `rowSize` bytes.
|
/// Calls `body` with the current working framebuffer: `rows` rows of
|
||||||
|
/// `rowSize` bytes each. Returns `nil` if there is no framebuffer.
|
||||||
/// Call `markUpdatedRows(from:to:)` after writing directly.
|
/// Call `markUpdatedRows(from:to:)` after writing directly.
|
||||||
public static var frame: UnsafeMutablePointer<UInt8>? {
|
public static func withFrame<Result, Failure: Error>(
|
||||||
gfx.pointee.getFrame.unsafelyUnwrapped()
|
_ body: (inout MutableSpan<UInt8>) throws(Failure) -> Result
|
||||||
|
) throws(Failure) -> Result? {
|
||||||
|
guard let frame = gfx.pointee.getFrame.unsafelyUnwrapped() else { return nil }
|
||||||
|
var span = UnsafeMutableBufferPointer(start: frame, count: rows * rowSize).mutableSpan
|
||||||
|
return try body(&span)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The framebuffer currently shown on the display. Rows are `rowSize` bytes.
|
/// Calls `body` with the framebuffer currently shown on the display:
|
||||||
public static var displayFrame: UnsafeMutablePointer<UInt8>? {
|
/// `rows` rows of `rowSize` bytes each. Returns `nil` if there is no
|
||||||
gfx.pointee.getDisplayFrame.unsafelyUnwrapped()
|
/// framebuffer.
|
||||||
|
public static func withDisplayFrame<Result, Failure: Error>(
|
||||||
|
_ body: (Span<UInt8>) throws(Failure) -> Result
|
||||||
|
) throws(Failure) -> Result? {
|
||||||
|
guard let frame = gfx.pointee.getDisplayFrame.unsafelyUnwrapped() else { return nil }
|
||||||
|
return try body(UnsafeBufferPointer(start: frame, count: rows * rowSize).span)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A bitmap view of the display framebuffer. Simulator only; `nil` on device.
|
/// A bitmap view of the display framebuffer. Simulator only; `nil` on device.
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
extension Graphics.Bitmap {
|
extension Graphics.Bitmap {
|
||||||
/// The bitmap's dimensions, row stride, and raw pixel/mask storage.
|
/// The bitmap's dimensions and row stride. Access the pixels themselves
|
||||||
/// The pointers are owned by the bitmap.
|
/// with `withPixelData(_:)` and `withMaskData(_:)`.
|
||||||
public struct Data {
|
public struct Data: Sendable {
|
||||||
/// The bitmap's width, in pixels.
|
/// The bitmap's width, in pixels.
|
||||||
public let width: Int
|
public let width: Int
|
||||||
/// The bitmap's height, in pixels.
|
/// The bitmap's height, in pixels.
|
||||||
public let height: Int
|
public let height: Int
|
||||||
/// The stride of one row of pixel data, in bytes.
|
/// The stride of one row of pixel (and mask) data, in bytes.
|
||||||
public let rowBytes: Int
|
public let rowBytes: Int
|
||||||
/// The bitmap's mask data, or `nil` if it has no mask. One bit per
|
/// Whether the bitmap has a mask.
|
||||||
/// pixel; rows are `rowBytes` wide.
|
public let hasMask: Bool
|
||||||
public let mask: UnsafeMutablePointer<UInt8>?
|
|
||||||
/// The bitmap's pixel data. One bit per pixel; rows are `rowBytes`
|
|
||||||
/// wide.
|
|
||||||
public let data: UnsafeMutablePointer<UInt8>?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,9 +126,9 @@ extension JSON {
|
|||||||
reader.read = { userdata, buffer, size in
|
reader.read = { userdata, buffer, size in
|
||||||
guard let userdata, let buffer else { return -1 }
|
guard let userdata, let buffer else { return -1 }
|
||||||
let file = Unmanaged<File.Handle>.fromOpaque(userdata).takeUnretainedValue()
|
let file = Unmanaged<File.Handle>.fromOpaque(userdata).takeUnretainedValue()
|
||||||
let destination = UnsafeMutableRawBufferPointer(start: buffer, count: Int(size))
|
var destination = UnsafeMutableBufferPointer(start: buffer, count: Int(size)).mutableSpan
|
||||||
do {
|
do {
|
||||||
let count = try file.read(into: destination)
|
let count = try file.read(into: &destination)
|
||||||
return count > 0 ? Int32(count) : -1
|
return count > 0 ? Int32(count) : -1
|
||||||
} catch {
|
} catch {
|
||||||
return -1
|
return -1
|
||||||
|
|||||||
@@ -152,9 +152,10 @@ extension Network {
|
|||||||
|
|
||||||
/// Reads up to `buffer.count` response bytes. Returns the number of
|
/// Reads up to `buffer.count` response bytes. Returns the number of
|
||||||
/// bytes read.
|
/// bytes read.
|
||||||
public func read(into buffer: UnsafeMutableRawBufferPointer) throws(NetError) -> Int {
|
public func read(into buffer: inout MutableSpan<UInt8>) throws(NetError) -> Int {
|
||||||
let result = httpAPI.pointee.read.unsafelyUnwrapped(pointer, buffer.baseAddress,
|
let result = buffer.withUnsafeMutableBufferPointer { buffer in
|
||||||
UInt32(buffer.count))
|
httpAPI.pointee.read.unsafelyUnwrapped(pointer, buffer.baseAddress, UInt32(buffer.count))
|
||||||
|
}
|
||||||
if result < 0 {
|
if result < 0 {
|
||||||
throw NetError(rawValue: result) ?? .unknown
|
throw NetError(rawValue: result) ?? .unknown
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,8 +112,10 @@ extension Network {
|
|||||||
|
|
||||||
/// Reads up to `buffer.count` bytes, waiting up to the read timeout.
|
/// Reads up to `buffer.count` bytes, waiting up to the read timeout.
|
||||||
/// Returns the number of bytes read.
|
/// Returns the number of bytes read.
|
||||||
public func read(into buffer: UnsafeMutableRawBufferPointer) throws(NetError) -> Int {
|
public func read(into buffer: inout MutableSpan<UInt8>) throws(NetError) -> Int {
|
||||||
let result = tcpAPI.pointee.read.unsafelyUnwrapped(pointer, buffer.baseAddress, buffer.count)
|
let result = buffer.withUnsafeMutableBufferPointer { buffer in
|
||||||
|
tcpAPI.pointee.read.unsafelyUnwrapped(pointer, buffer.baseAddress, buffer.count)
|
||||||
|
}
|
||||||
if result < 0 {
|
if result < 0 {
|
||||||
throw NetError(rawValue: result) ?? .unknown
|
throw NetError(rawValue: result) ?? .unknown
|
||||||
}
|
}
|
||||||
@@ -133,11 +135,13 @@ extension Network {
|
|||||||
return bytes
|
return bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes the buffer to the connection. Returns the number of bytes
|
/// Writes the bytes to the connection. Returns the number of bytes
|
||||||
/// accepted.
|
/// accepted.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func write(_ buffer: UnsafeRawBufferPointer) throws(NetError) -> Int {
|
public func write(_ bytes: Span<UInt8>) throws(NetError) -> Int {
|
||||||
let result = tcpAPI.pointee.write.unsafelyUnwrapped(pointer, buffer.baseAddress, buffer.count)
|
let result = bytes.withUnsafeBufferPointer { buffer in
|
||||||
|
tcpAPI.pointee.write.unsafelyUnwrapped(pointer, buffer.baseAddress, buffer.count)
|
||||||
|
}
|
||||||
if result < 0 {
|
if result < 0 {
|
||||||
throw NetError(rawValue: result) ?? .unknown
|
throw NetError(rawValue: result) ?? .unknown
|
||||||
}
|
}
|
||||||
@@ -148,13 +152,9 @@ extension Network {
|
|||||||
/// accepted.
|
/// accepted.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func write(_ bytes: [UInt8]) throws(NetError) -> Int {
|
public func write(_ bytes: [UInt8]) throws(NetError) -> Int {
|
||||||
let result = bytes.withUnsafeBytes { buffer in
|
try bytes.withUnsafeBufferPointer { buffer throws(NetError) in
|
||||||
tcpAPI.pointee.write.unsafelyUnwrapped(pointer, buffer.baseAddress, buffer.count)
|
try write(buffer.span)
|
||||||
}
|
}
|
||||||
if result < 0 {
|
|
||||||
throw NetError(rawValue: result) ?? .unknown
|
|
||||||
}
|
|
||||||
return Int(result)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
extension Sound.Effect {
|
extension Sound.Effect {
|
||||||
/// Processes up to `AUDIO_FRAMES_PER_CYCLE` sample frames in signed
|
/// Processes up to `AUDIO_FRAMES_PER_CYCLE` sample frames in signed
|
||||||
/// Q8.24 format. `bufferActive` is `false` when the input buffer is
|
/// Q8.24 format. `right` is empty when the channel is mono.
|
||||||
/// silent. Returns `true` if the effect produced output.
|
/// `bufferActive` is `false` when the input buffer is silent. Returns
|
||||||
public typealias Processor = (_ left: UnsafeMutableBufferPointer<Int32>,
|
/// `true` if the effect produced output.
|
||||||
_ right: UnsafeMutableBufferPointer<Int32>?,
|
public typealias Processor = (_ left: inout MutableSpan<Int32>,
|
||||||
|
_ right: inout MutableSpan<Int32>,
|
||||||
_ bufferActive: Bool) -> Bool
|
_ bufferActive: Bool) -> Bool
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ extension Sound {
|
|||||||
guard let effect, let left,
|
guard let effect, let left,
|
||||||
let userdata = effectAPI.pointee.getUserdata.unsafelyUnwrapped(effect) else { return 0 }
|
let userdata = effectAPI.pointee.getUserdata.unsafelyUnwrapped(effect) else { return 0 }
|
||||||
let box = Unmanaged<ProcessorBox>.fromOpaque(userdata).takeUnretainedValue()
|
let box = Unmanaged<ProcessorBox>.fromOpaque(userdata).takeUnretainedValue()
|
||||||
let leftBuffer = UnsafeMutableBufferPointer(start: left, count: Int(nsamples))
|
var leftSpan = UnsafeMutableBufferPointer(start: left, count: Int(nsamples)).mutableSpan
|
||||||
let rightBuffer = right.map { UnsafeMutableBufferPointer(start: $0, count: Int(nsamples)) }
|
var rightSpan = UnsafeMutableBufferPointer(start: right, count: right == nil ? 0 : Int(nsamples)).mutableSpan
|
||||||
return box.processor(leftBuffer, rightBuffer, bufactive != 0) ? 1 : 0
|
return box.processor(&leftSpan, &rightSpan, bufactive != 0) ? 1 : 0
|
||||||
}, box.toOpaque()).unsafelyUnwrapped
|
}, box.toOpaque()).unsafelyUnwrapped
|
||||||
isOwned = true
|
isOwned = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,14 +51,14 @@ extension Sound {
|
|||||||
|
|
||||||
/// Sets a callback that records microphone input. Return `false` from the
|
/// Sets a callback that records microphone input. Return `false` from the
|
||||||
/// callback to stop recording. Pass `nil` to stop recording immediately.
|
/// 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
|
@discardableResult
|
||||||
public static func setMicCallback(source: MicSource = .autodetect,
|
public static func setMicCallback(source: MicSource = .autodetect,
|
||||||
_ callback: ((UnsafeMutableBufferPointer<Int16>) -> Bool)?) -> Bool {
|
_ callback: ((Span<Int16>) -> Bool)?) -> Bool {
|
||||||
micCallback = callback
|
micCallback = callback
|
||||||
if callback != nil {
|
if callback != nil {
|
||||||
return snd.pointee.setMicCallback.unsafelyUnwrapped({ _, buffer, length in
|
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
|
return Sound.micCallback?(samples) == true ? 1 : 0
|
||||||
}, nil, CPlaydate.MicSource(CPlaydate.MicSource.RawValue(source.rawValue))) != 0
|
}, nil, CPlaydate.MicSource(CPlaydate.MicSource.RawValue(source.rawValue))) != 0
|
||||||
} else {
|
} 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`
|
/// Asks the user for permission to record from the microphone. `purpose`
|
||||||
/// is shown in the permission prompt. The completion receives whether
|
/// is shown in the permission prompt. The completion receives whether
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
extension Sound.CallbackSource {
|
extension Sound.CallbackSource {
|
||||||
/// Fills the sample buffers and returns `true` if output was
|
/// Fills the sample buffers and returns `true` if output was
|
||||||
/// produced. `right` is non-nil only for stereo sources.
|
/// produced. `right` is empty for mono sources.
|
||||||
public typealias Callback = (_ left: UnsafeMutableBufferPointer<Int16>,
|
public typealias Callback = (_ left: inout MutableSpan<Int16>,
|
||||||
_ right: UnsafeMutableBufferPointer<Int16>?) -> Bool
|
_ right: inout MutableSpan<Int16>) -> Bool
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ extension Sound {
|
|||||||
UnsafeMutablePointer<Int16>?, Int32) -> Int32 = { context, left, right, length in
|
UnsafeMutablePointer<Int16>?, Int32) -> Int32 = { context, left, right, length in
|
||||||
guard let context, let left else { return 0 }
|
guard let context, let left else { return 0 }
|
||||||
let source = Unmanaged<CallbackSource>.fromOpaque(context).takeUnretainedValue()
|
let source = Unmanaged<CallbackSource>.fromOpaque(context).takeUnretainedValue()
|
||||||
let leftBuffer = UnsafeMutableBufferPointer(start: left, count: Int(length))
|
var leftSpan = UnsafeMutableBufferPointer(start: left, count: Int(length)).mutableSpan
|
||||||
let rightBuffer = right.map { UnsafeMutableBufferPointer(start: $0, count: Int(length)) }
|
var rightSpan = UnsafeMutableBufferPointer(start: right, count: right == nil ? 0 : Int(length)).mutableSpan
|
||||||
return source.callback(leftBuffer, rightBuffer) ? 1 : 0
|
return source.callback(&leftSpan, &rightSpan) ? 1 : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attaches the C object created for this source.
|
/// Attaches the C object created for this source.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ extension Sound {
|
|||||||
|
|
||||||
var loopCallback: ((FilePlayer) -> Void)?
|
var loopCallback: ((FilePlayer) -> Void)?
|
||||||
var fadeCallback: ((FilePlayer) -> Void)?
|
var fadeCallback: ((FilePlayer) -> Void)?
|
||||||
var mp3DataSource: ((UnsafeMutableBufferPointer<UInt8>) -> Int)?
|
var mp3DataSource: ((inout MutableSpan<UInt8>) -> Int)?
|
||||||
private var retainedRateModulator: SignalValue?
|
private var retainedRateModulator: SignalValue?
|
||||||
|
|
||||||
override init(pointer: OpaquePointer?, isOwned: Bool) {
|
override init(pointer: OpaquePointer?, isOwned: Bool) {
|
||||||
@@ -131,13 +131,13 @@ extension Sound {
|
|||||||
/// fills the buffer and returns the number of bytes written; return 0
|
/// fills the buffer and returns the number of bytes written; return 0
|
||||||
/// to signal the end of the stream.
|
/// to signal the end of the stream.
|
||||||
public func setMP3StreamSource(bufferLength: Float,
|
public func setMP3StreamSource(bufferLength: Float,
|
||||||
_ dataSource: @escaping (UnsafeMutableBufferPointer<UInt8>) -> Int) {
|
_ dataSource: @escaping (inout MutableSpan<UInt8>) -> Int) {
|
||||||
mp3DataSource = dataSource
|
mp3DataSource = dataSource
|
||||||
FilePlayer.api.pointee.setMP3StreamSource.unsafelyUnwrapped(pointer, { data, bytes, userdata in
|
FilePlayer.api.pointee.setMP3StreamSource.unsafelyUnwrapped(pointer, { data, bytes, userdata in
|
||||||
guard let userdata, let data else { return 0 }
|
guard let userdata, let data else { return 0 }
|
||||||
let player = Unmanaged<FilePlayer>.fromOpaque(userdata).takeUnretainedValue()
|
let player = Unmanaged<FilePlayer>.fromOpaque(userdata).takeUnretainedValue()
|
||||||
let buffer = UnsafeMutableBufferPointer(start: data, count: Int(bytes))
|
var buffer = UnsafeMutableBufferPointer(start: data, count: Int(bytes)).mutableSpan
|
||||||
return Int32(player.mp3DataSource?(buffer) ?? 0)
|
return Int32(player.mp3DataSource?(&buffer) ?? 0)
|
||||||
}, Unmanaged.passUnretained(self).toOpaque(), bufferLength)
|
}, Unmanaged.passUnretained(self).toOpaque(), bufferLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,9 +75,9 @@ extension Sound {
|
|||||||
{ userdata, left, right, nsamples, rate, drate in
|
{ userdata, left, right, nsamples, rate, drate in
|
||||||
guard let userdata, let left else { return 0 }
|
guard let userdata, let left else { return 0 }
|
||||||
let box = Unmanaged<GeneratorBox>.fromOpaque(userdata).takeUnretainedValue()
|
let box = Unmanaged<GeneratorBox>.fromOpaque(userdata).takeUnretainedValue()
|
||||||
let leftBuffer = UnsafeMutableBufferPointer(start: left, count: Int(nsamples))
|
var leftSpan = UnsafeMutableBufferPointer(start: left, count: Int(nsamples)).mutableSpan
|
||||||
let rightBuffer = right.map { UnsafeMutableBufferPointer(start: $0, count: Int(nsamples)) }
|
var rightSpan = UnsafeMutableBufferPointer(start: right, count: right == nil ? 0 : Int(nsamples)).mutableSpan
|
||||||
return Int32(box.generator.render(leftBuffer, rightBuffer, rate, drate))
|
return Int32(box.generator.render(&leftSpan, &rightSpan, rate, drate))
|
||||||
},
|
},
|
||||||
{ userdata, note, velocity, length in
|
{ userdata, note, velocity, length in
|
||||||
guard let userdata else { return }
|
guard let userdata else { return }
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ extension Sound.Synth {
|
|||||||
/// Custom generator callbacks. Samples are in signed Q8.24 format.
|
/// Custom generator callbacks. Samples are in signed Q8.24 format.
|
||||||
public struct Generator {
|
public struct Generator {
|
||||||
/// Renders up to 256 sample frames into `left` (and `right` for
|
/// 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
|
/// Q0.32 format and `drate` its per-frame change. Returns the
|
||||||
/// number of frames rendered.
|
/// number of frames rendered.
|
||||||
public var render: (_ left: UnsafeMutableBufferPointer<Int32>,
|
public var render: (_ left: inout MutableSpan<Int32>,
|
||||||
_ right: UnsafeMutableBufferPointer<Int32>?,
|
_ right: inout MutableSpan<Int32>,
|
||||||
_ rate: UInt32, _ drate: Int32) -> Int
|
_ rate: UInt32, _ drate: Int32) -> Int
|
||||||
/// Called when a note starts. `length` is -1 for indefinite notes.
|
/// Called when a note starts. `length` is -1 for indefinite notes.
|
||||||
public var noteOn: ((_ note: Sound.MIDINote, _ velocity: Float, _ length: Float) -> Void)?
|
public var noteOn: ((_ note: Sound.MIDINote, _ velocity: Float, _ length: Float) -> Void)?
|
||||||
@@ -17,8 +17,8 @@ extension Sound.Synth {
|
|||||||
/// valid.
|
/// valid.
|
||||||
public var setParameter: ((_ parameter: Int, _ value: Float) -> Bool)?
|
public var setParameter: ((_ parameter: Int, _ value: Float) -> Bool)?
|
||||||
|
|
||||||
public init(render: @escaping (_ left: UnsafeMutableBufferPointer<Int32>,
|
public init(render: @escaping (_ left: inout MutableSpan<Int32>,
|
||||||
_ right: UnsafeMutableBufferPointer<Int32>?,
|
_ right: inout MutableSpan<Int32>,
|
||||||
_ rate: UInt32, _ drate: Int32) -> Int,
|
_ rate: UInt32, _ drate: Int32) -> Int,
|
||||||
noteOn: ((_ note: Sound.MIDINote, _ velocity: Float, _ length: Float) -> Void)? = nil,
|
noteOn: ((_ note: Sound.MIDINote, _ velocity: Float, _ length: Float) -> Void)? = nil,
|
||||||
release: ((_ stop: Bool) -> Void)? = nil,
|
release: ((_ stop: Bool) -> Void)? = nil,
|
||||||
|
|||||||
@@ -307,8 +307,12 @@ extension System {
|
|||||||
/// Sends data over the mirror connection. Returns `false` if mirroring is
|
/// Sends data over the mirror connection. Returns `false` if mirroring is
|
||||||
/// not active or the send fails.
|
/// not active or the send fails.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public static func sendMirrorData(command: UInt8, data: UnsafeMutableRawBufferPointer) -> Bool {
|
public static func sendMirrorData(command: UInt8, data: Span<UInt8>) -> Bool {
|
||||||
api.pointee.sendMirrorData.unsafelyUnwrapped(command, data.baseAddress, Int32(data.count))
|
data.withUnsafeBufferPointer { buffer in
|
||||||
|
// The C API takes a non-const pointer but only reads the data.
|
||||||
|
api.pointee.sendMirrorData.unsafelyUnwrapped(
|
||||||
|
command, UnsafeMutableRawPointer(mutating: buffer.baseAddress), Int32(buffer.count))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// OS, language, and pdx version information.
|
/// OS, language, and pdx version information.
|
||||||
|
|||||||
@@ -146,7 +146,8 @@ struct WrapperTests {
|
|||||||
var produced = 0
|
var produced = 0
|
||||||
let source = Sound.addSource(stereo: false) { left, right in
|
let source = Sound.addSource(stereo: false) { left, right in
|
||||||
produced += left.count
|
produced += left.count
|
||||||
#expect(right == nil)
|
let isMono = right.isEmpty // #expect cannot capture a span
|
||||||
|
#expect(isMono)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
#expect(Sound.CallbackSource.live.count == baseline + 1)
|
#expect(Sound.CallbackSource.live.count == baseline + 1)
|
||||||
@@ -280,7 +281,8 @@ struct WrapperTests {
|
|||||||
let effect = Sound.Effect(processor: { left, right, _ in
|
let effect = Sound.Effect(processor: { left, right, _ in
|
||||||
_ = token
|
_ = token
|
||||||
processed += left.count
|
processed += left.count
|
||||||
#expect(right == nil)
|
let isMono = right.isEmpty // #expect cannot capture a span
|
||||||
|
#expect(isMono)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user