bug fixes

This commit is contained in:
Dallas Groot 2026-04-10 17:05:12 -07:00
parent caea96547a
commit 7d448e79de
2 changed files with 34 additions and 11 deletions

View file

@ -1173,7 +1173,7 @@ class AudioPlayer: NSObject, ObservableObject {
isUsingOfflineVis = false
offlineVisProgress = 0
offlineVisFPS = VisualizerSettings.shared.effectiveFPS
let points = VisualizerSettings.shared.numberOfPoints
let points = VisualizerSettings.shared.nowPlaying.numberOfPoints
let cutoff = VisualizerSettings.shared.frequencyCutoff
Task {

View file

@ -1610,12 +1610,32 @@ class ShazamRecognizer: NSObject, ObservableObject, SHSessionDelegate {
// MARK: - MTAudioProcessingTap
private func installTap(on playerItem: AVPlayerItem) -> Bool {
guard let audioTrack = playerItem.asset.tracks(withMediaType: .audio).first else {
// loadTracks is async (tracks(withMediaType:) deprecated iOS 16)
// We load synchronously from the asset's already-loaded track list if available,
// falling back to a blocking load. Since this is called off the main thread in
// a Task, a brief synchronous wait is acceptable.
let asset = playerItem.asset
var audioTrack: AVAssetTrack?
// Fast path: tracks already in memory (common for items that are playing)
let loadedTracks = asset.tracks(withMediaType: .audio)
if let first = loadedTracks.first {
audioTrack = first
} else {
// Async load bridge with a semaphore so we stay synchronous from the caller's view
let sema = DispatchSemaphore(value: 0)
Task {
audioTrack = try? await asset.loadTracks(withMediaType: .audio).first
sema.signal()
}
sema.wait()
}
guard let audioTrack else {
DebugLogger.shared.log("Shazam: no audio track on playerItem", category: "Audio")
return false
}
// Store self as opaque pointer in tap storage
var callbacks = MTAudioProcessingTapCallbacks(
version: kMTAudioProcessingTapCallbacksVersion_0,
clientInfo: UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()),
@ -1624,15 +1644,15 @@ class ShazamRecognizer: NSObject, ObservableObject, SHSessionDelegate {
},
finalize: nil,
prepare: { tap, maxFrames, processingFormat in
// Capture the source format so we can build the converter on first buffer
let storage = Unmanaged<ShazamRecognizer>
.fromOpaque(MTAudioProcessingTapGetStorage(tap))
.takeUnretainedValue()
let format = AVAudioFormat(streamDescription: processingFormat)
storage.analysisQueue.async {
storage.sourceFormat = format
if let src = format, let dst = storage.targetFormat as AVAudioFormat? {
storage.converter = try? AVAudioConverter(from: src, to: dst)
// AVAudioConverter.init does not throw no try? needed
if let src = format {
storage.converter = AVAudioConverter(from: src, to: storage.targetFormat)
}
}
},
@ -1640,19 +1660,22 @@ class ShazamRecognizer: NSObject, ObservableObject, SHSessionDelegate {
process: shazamTapProcess
)
var newTap: Unmanaged<MTAudioProcessingTap>?
var rawTap: Unmanaged<MTAudioProcessingTap>?
let status = MTAudioProcessingTapCreate(
kCFAllocatorDefault, &callbacks,
kMTAudioProcessingTapCreationFlag_PostEffects, &newTap
kMTAudioProcessingTapCreationFlag_PostEffects, &rawTap
)
guard status == noErr, let newTap else {
guard status == noErr, let rawTap else {
DebugLogger.shared.log("Shazam: MTAudioProcessingTapCreate failed \(status)", category: "Audio")
return false
}
tap = newTap
// takeRetainedValue transfers ownership (ARC takes over) assign to our ivar
let tapValue = rawTap.takeRetainedValue()
tap = Unmanaged.passRetained(tapValue) // re-wrap for cleanup later
let inputParams = AVMutableAudioMixInputParameters(track: audioTrack)
inputParams.audioTapProcessor = newTap.takeRetainedValue()
inputParams.audioTapProcessor = tapValue // passes the already-retained value
let mix = AVMutableAudioMix()
mix.inputParameters = [inputParams]