38 lines
1.8 KiB
Swift
38 lines
1.8 KiB
Swift
// C callbacks use an unretained `user` context. Client destruction joins callback threads,
|
|
// and transient event pointers are copied before the callback returns.
|
|
|
|
import VoiceCatC
|
|
import Foundation
|
|
|
|
/// Internal: builds the `vc_callbacks` struct wired to VoiceCatClient's C function pointers.
|
|
/// The `user` context is an Unmanaged-passUnretained pointer to the client — resolved back
|
|
/// to the client inside `onEvent`/`onLevel` below.
|
|
internal enum Callbacks {
|
|
/// The `on_event` C function pointer. Non-capturing @convention(c) closure — resolves
|
|
/// the VoiceCatClient from `user` and enqueues a safe copy of the event.
|
|
static let onEvent: @convention(c) (
|
|
UnsafeMutableRawPointer?, UnsafePointer<vc_event>?
|
|
) -> Void = { user, ev in
|
|
guard let user, let ev else { return }
|
|
let client = Unmanaged<VoiceCatClient>.fromOpaque(user).takeUnretainedValue()
|
|
// Copy the event (including text) to a Swift value NOW — the raw vc_event is
|
|
// invalid after this callback returns.
|
|
client.enqueueEvent(VoiceCatEvent.from(ev.pointee))
|
|
}
|
|
|
|
/// The `on_level` C function pointer. Coalesces to "latest sample per stream_id"
|
|
/// (intermediate values are visually irrelevant — same as C#'s ConcurrentDictionary).
|
|
static let onLevel: @convention(c) (
|
|
UnsafeMutableRawPointer?, UInt32, Float
|
|
) -> Void = { user, streamId, rms in
|
|
guard let user else { return }
|
|
let client = Unmanaged<VoiceCatClient>.fromOpaque(user).takeUnretainedValue()
|
|
client.enqueueLevel(streamId, rms)
|
|
}
|
|
|
|
/// Construct the vc_callbacks struct for a given client.
|
|
static func make(user: UnsafeMutableRawPointer) -> vc_callbacks {
|
|
vc_callbacks(on_event: onEvent, on_level: onLevel, user: user)
|
|
}
|
|
}
|