0

今天,我创建了一个小应用程序来尝试在 iOS 15 中的 ShazamKit 音乐检测能力。按照 Youtube 上的教程进行操作,并且我拥有 Apple 开发者会员资格,并为此应用程序标识符启用了 ShazamKit 服务。

简而言之,我想用 ShazamKit 从应用程序内的音频文件中检测歌曲元数据。

问题是,尽管我已经成功生成了签名,但委托方法didFinddidNotFindMatchFor没有触发。didNotFindMatchFor如果至少没有找到匹配项,我认为它应该在委托方法中给我一个错误,但事实并非如此。

这是一个相当新的功能,我找不到太多相关的东西。感谢任何帮助。

更多信息:我确实找到了一些使用 audioEngine 的东西,但是使用麦克风的输出,如果用户用耳机听音乐,那将是不可能的。就我而言,我想使用文件本身,因为我的生产应用程序是一个音乐播放器,它在沙盒中存储了很多音频文件。

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)
        }
    }
}
4

1 回答 1

1

根据今天的测试和观察。我发现我们需要AVAudioFormat(standardFormatWithSampleRate: 44100, channels: 1)使用内置转换器(AVAudioConverter)将输入音频格式转换为。然后创建输出缓冲区,这次识别音乐。

我选择了 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
        }
    }
}

参考:苹果论坛

于 2021-10-10T06:54:59.330 回答