ShazamKit:音乐检测 "session.match(signature)" 未找到匹配且未抛出任何错误,很奇怪
ShazamKit: music detection "session.match(signature)" with no match found and no error was thrown, weird
今天,我创建了一个小应用程序来尝试 iOS 15 中的 ShazamKit 音乐检测功能。在 Youtube 上关注了一个 tutorial,我拥有 Apple 开发者会员资格并为此启用了 ShazamKit 服务应用标识符。
简而言之,我想从应用程序内的音频文件中使用 ShazamKit 检测歌曲元数据。
问题是尽管我已经成功生成了签名,但委托方法 didFind
和 didNotFindMatchFor
都没有触发。我认为如果至少没有找到匹配项,它应该在 didNotFindMatchFor
委托方法中给我一个错误,但它没有。
这是一个非常新的功能,我找不到太多相关的东西。感谢您的帮助。
更多信息:我确实找到了一些使用音频引擎的东西,但是使用麦克风的输出,如果用户用耳机听音乐,那将是不可能的。在我的例子中,我想使用文件本身,因为我的生产应用程序是一个音乐播放器,它在沙盒中存储了很多音频文件。
import ShazamKit
import UIKit
class ViewController: UIViewController {
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")
// ShazamKit is available from iOS 15
if #available(iOS 15, *) {
// session
let session = SHSession()
// delegate
session.delegate = self
do {
// get track
guard let url = Bundle.main.url(forResource: "Baby One More Time", withExtension: "mp3") else {
print("url is NULLLL")
return }
// create audio file
let file = try AVAudioFile(forReading: url)
let frameCapacity = AVAudioFrameCount(file.length / 26)
// Audio -> Buffer
guard let buffer = AVAudioPCMBuffer(pcmFormat: file.processingFormat, frameCapacity: frameCapacity) else {
print("Failed to create a buffer")
return }
// Read file into buffer
try file.read(into: buffer)
// SignatureGenerator
let generator = SHSignatureGenerator()
try generator.append(buffer, at: nil)
// create signature
let signature = generator.signature()
// try to match
session.match(signature)
} catch {
print(error)
}
} else {
// unavailable alert
}
}
}
extension ViewController: SHSessionDelegate {
func session(_ session: SHSession, didFind match: SHMatch) {
print("Match found!")
// get results
let items = match.mediaItems
items.forEach { item in
print(item.title ?? "title")
print(item.artist ?? "artist")
print(item.artworkURL?.absoluteURL ?? "artwork url")
}
}
func session(_ session: SHSession, didNotFindMatchFor signature: SHSignature, error: Error?) {
if let error = error {
print(error)
}
}
}
根据今天的测试和观察。我发现我们需要使用内置转换器(AVAudioConverter)将输入音频格式转换为 AVAudioFormat(standardFormatWithSampleRate: 44100, channels: 1)
。然后创建输出缓冲区,这次就识别到了音乐
我挑选了10+个音乐文件进行测试运行,除一个外,所有文件都可以检测到。有趣的是这个音乐文件可以被Shazam应用程序检测到,我不知道是什么原因,因为未检测到的音乐歌曲没有显示错误。
不管怎样,现在已经成功了。更新代码如下,它只是几个功能的组合用于测试目的,你应该将它们分成不同的功能用于生产。
@objc func recognizeSong(_ sender: UIButton) {
print("reco button tapped")
// ShazamKit is available from iOS 15
if #available(iOS 15, *) {
// session
let session = SHSession()
session.delegate = self
guard let url = Bundle.main.url(forResource: "You Belong With Me", withExtension: "mp3") else {
return
}
guard let audioFormat = AVAudioFormat(standardFormatWithSampleRate: 44100, channels: 1) else {
return
}
let generator = SHSignatureGenerator()
do {
let audioFile = try AVAudioFile(forReading: url)
guard let inputBuffer = AVAudioPCMBuffer(pcmFormat: audioFile.processingFormat, frameCapacity: 44100 * 10),
let outputBuffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: 44100 * 10) else {
return
}
// Read file into buffer
let inputBlock : AVAudioConverterInputBlock = { inNumPackets, outStatus in
do {
try audioFile.read(into: inputBuffer)
outStatus.pointee = .haveData
return inputBuffer
} catch {
if audioFile.framePosition >= audioFile.length {
outStatus.pointee = .endOfStream
return nil
} else {
outStatus.pointee = .noDataNow
return nil
}
}
}
guard let converter = AVAudioConverter(from: audioFile.processingFormat, to: audioFormat) else {
return
}
let status = converter.convert(to: outputBuffer, error: nil, withInputFrom: inputBlock)
if status == .error || status == .endOfStream {
return
}
try generator.append(outputBuffer, at: nil)
if status == .inputRanDry {
return
}
} catch {
print(error)
}
// create signature
let signature = generator.signature()
// try to match
session.match(signature)
} else {
// unavailable alert
}
}
}
参考:Apple forums
今天,我创建了一个小应用程序来尝试 iOS 15 中的 ShazamKit 音乐检测功能。在 Youtube 上关注了一个 tutorial,我拥有 Apple 开发者会员资格并为此启用了 ShazamKit 服务应用标识符。
简而言之,我想从应用程序内的音频文件中使用 ShazamKit 检测歌曲元数据。
问题是尽管我已经成功生成了签名,但委托方法 didFind
和 didNotFindMatchFor
都没有触发。我认为如果至少没有找到匹配项,它应该在 didNotFindMatchFor
委托方法中给我一个错误,但它没有。
这是一个非常新的功能,我找不到太多相关的东西。感谢您的帮助。
更多信息:我确实找到了一些使用音频引擎的东西,但是使用麦克风的输出,如果用户用耳机听音乐,那将是不可能的。在我的例子中,我想使用文件本身,因为我的生产应用程序是一个音乐播放器,它在沙盒中存储了很多音频文件。
import ShazamKit
import UIKit
class ViewController: UIViewController {
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")
// ShazamKit is available from iOS 15
if #available(iOS 15, *) {
// session
let session = SHSession()
// delegate
session.delegate = self
do {
// get track
guard let url = Bundle.main.url(forResource: "Baby One More Time", withExtension: "mp3") else {
print("url is NULLLL")
return }
// create audio file
let file = try AVAudioFile(forReading: url)
let frameCapacity = AVAudioFrameCount(file.length / 26)
// Audio -> Buffer
guard let buffer = AVAudioPCMBuffer(pcmFormat: file.processingFormat, frameCapacity: frameCapacity) else {
print("Failed to create a buffer")
return }
// Read file into buffer
try file.read(into: buffer)
// SignatureGenerator
let generator = SHSignatureGenerator()
try generator.append(buffer, at: nil)
// create signature
let signature = generator.signature()
// try to match
session.match(signature)
} catch {
print(error)
}
} else {
// unavailable alert
}
}
}
extension ViewController: SHSessionDelegate {
func session(_ session: SHSession, didFind match: SHMatch) {
print("Match found!")
// get results
let items = match.mediaItems
items.forEach { item in
print(item.title ?? "title")
print(item.artist ?? "artist")
print(item.artworkURL?.absoluteURL ?? "artwork url")
}
}
func session(_ session: SHSession, didNotFindMatchFor signature: SHSignature, error: Error?) {
if let error = error {
print(error)
}
}
}
根据今天的测试和观察。我发现我们需要使用内置转换器(AVAudioConverter)将输入音频格式转换为 AVAudioFormat(standardFormatWithSampleRate: 44100, channels: 1)
。然后创建输出缓冲区,这次就识别到了音乐
我挑选了10+个音乐文件进行测试运行,除一个外,所有文件都可以检测到。有趣的是这个音乐文件可以被Shazam应用程序检测到,我不知道是什么原因,因为未检测到的音乐歌曲没有显示错误。
不管怎样,现在已经成功了。更新代码如下,它只是几个功能的组合用于测试目的,你应该将它们分成不同的功能用于生产。
@objc func recognizeSong(_ sender: UIButton) {
print("reco button tapped")
// ShazamKit is available from iOS 15
if #available(iOS 15, *) {
// session
let session = SHSession()
session.delegate = self
guard let url = Bundle.main.url(forResource: "You Belong With Me", withExtension: "mp3") else {
return
}
guard let audioFormat = AVAudioFormat(standardFormatWithSampleRate: 44100, channels: 1) else {
return
}
let generator = SHSignatureGenerator()
do {
let audioFile = try AVAudioFile(forReading: url)
guard let inputBuffer = AVAudioPCMBuffer(pcmFormat: audioFile.processingFormat, frameCapacity: 44100 * 10),
let outputBuffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: 44100 * 10) else {
return
}
// Read file into buffer
let inputBlock : AVAudioConverterInputBlock = { inNumPackets, outStatus in
do {
try audioFile.read(into: inputBuffer)
outStatus.pointee = .haveData
return inputBuffer
} catch {
if audioFile.framePosition >= audioFile.length {
outStatus.pointee = .endOfStream
return nil
} else {
outStatus.pointee = .noDataNow
return nil
}
}
}
guard let converter = AVAudioConverter(from: audioFile.processingFormat, to: audioFormat) else {
return
}
let status = converter.convert(to: outputBuffer, error: nil, withInputFrom: inputBlock)
if status == .error || status == .endOfStream {
return
}
try generator.append(outputBuffer, at: nil)
if status == .inputRanDry {
return
}
} catch {
print(error)
}
// create signature
let signature = generator.signature()
// try to match
session.match(signature)
} else {
// unavailable alert
}
}
}
参考:Apple forums