iOS 15中ShazamKit调用session.match(signature)无匹配且代理方法未触发问题求助
解决ShazamKit匹配无回调的问题
我仔细看了你的代码和问题描述,核心问题其实出在SHSession的生命周期上,另外还有几个细节可以优化,帮你一步步解决:
1. 最关键的问题:SHSession被提前释放了
你在recognizeSong方法里创建的SHSession是局部变量,当方法执行完毕后,ARC会立即释放这个session实例——这就导致后续的匹配请求还没完成,session就不存在了,自然不会触发didFind或didNotFindMatchFor代理方法。
解决方案:把SHSession改成ViewController的实例属性,让它的生命周期和VC保持一致:
class ViewController: UIViewController { // 改成实例变量,确保生命周期足够长 private lazy var session: SHSession = { let session = SHSession() session.delegate = self return session }() // 其他代码不变... }
然后在recognizeSong里直接用这个self.session,不要再重新创建局部的session。
2. 音频缓冲处理的优化
你计算frameCapacity的方式file.length / 26可能会导致读取的音频片段不完整,生成的signature有效时长不足,影响匹配结果。建议直接使用音频文件的完整长度来创建buffer:
// 替换原来的frameCapacity计算 let frameCapacity = AVAudioFrameCount(file.length) guard let buffer = AVAudioPCMBuffer(pcmFormat: file.processingFormat, frameCapacity: frameCapacity) else { print("Failed to create a buffer") return } // 读取完整音频到buffer try file.read(into: buffer)
另外,生成signature后可以打印它的时长,确认是否有效:
let signature = generator.signature() print("Signature duration: \(signature.duration)") // 应该和音频文件时长接近 session.match(signature)
3. 完善代理方法的日志输出
即使没有匹配到歌曲,didNotFindMatchFor也应该输出日志,方便你确认方法是否被调用:
func session(_ session: SHSession, didNotFindMatchFor signature: SHSignature, error: Error?) { if let error = error { print("Match failed with error: \(error.localizedDescription)") } else { print("No matching song found, no error returned") } }
4. 额外注意事项
- 确保你的苹果开发者账号已经正确启用ShazamKit服务,并且App ID的配置和你测试用的App一致;
- 测试用的音频文件要确保是完整的、音质清晰的歌曲片段(至少几秒以上,太短的话ShazamKit无法识别);
- 因为你是处理沙盒内的音频文件,不需要申请麦克风权限,这点没问题,完全符合你司音乐播放器的需求。
修改后的完整代码示例:
import ShazamKit import UIKit import AVFoundation class ViewController: UIViewController { private lazy var session: SHSession = { let session = SHSession() session.delegate = self return session }() lazy var recoButton: UIButton = { let button = UIButton(frame: CGRect(x: 0, y: 0, width: 120, height: 60)) button.layer.cornerRadius = 8 button.backgroundColor = .brown button.setTitle("Recognize", for: .normal) button.addTarget(self, action: #selector(recognizeSong), for: .touchUpInside) return button }() override func viewDidLoad() { super.viewDidLoad() view.addSubview(recoButton) recoButton.center = view.center } @objc func recognizeSong(_ sender: UIButton) { print("reco button tapped") if #available(iOS 15, *) { do { guard let url = Bundle.main.url(forResource: "Baby One More Time", withExtension: "mp3") else { print("Audio file not found") return } let file = try AVAudioFile(forReading: url) let frameCapacity = AVAudioFrameCount(file.length) guard let buffer = AVAudioPCMBuffer(pcmFormat: file.processingFormat, frameCapacity: frameCapacity) else { print("Failed to create audio buffer") return } try file.read(into: buffer) let generator = SHSignatureGenerator() try generator.append(buffer, at: nil) let signature = generator.signature() print("Generated signature with duration: \(signature.duration)") session.match(signature) } catch { print("Error during recognition: \(error.localizedDescription)") } } else { let alert = UIAlertController(title: "Unsupported", message: "ShazamKit requires iOS 15 or later", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default)) present(alert, animated: true) } } } extension ViewController: SHSessionDelegate { func session(_ session: SHSession, didFind match: SHMatch) { print("Match found!") let items = match.mediaItems items.forEach { item in print("Title: \(item.title ?? "Unknown Title")") print("Artist: \(item.artist ?? "Unknown Artist")") print("Artwork URL: \(item.artworkURL?.absoluteString ?? "No Artwork")") } } func session(_ session: SHSession, didNotFindMatchFor signature: SHSignature, error: Error?) { if let error = error { print("Match failed with error: \(error.localizedDescription)") } else { print("No matching song found in Shazam's database") } } }
按照上面的修改,你应该能正常触发代理方法,无论是匹配成功还是失败都会有对应的日志输出。
内容的提问来源于stack exchange,提问作者Zhou Haibo
相关产品推荐
相关产品推荐

