You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native集成最新ShazamKit遇SHMatcher错误求解决方案及示例

React Native集成ShazamKit实现音乐识别问题解决

问题描述

我已经为React Native创建了桥接,但难以理解ShazamKit文档,希望获取在React Native应用中使用最新版ShazamKit实现麦克风音乐识别的思路、帮助或源码。我尝试了以下Swift桥接代码,却出现错误:Cannot find 'SHMatcher' in scope。

原代码:

// my swift file
import Foundation
import ShazamKit
import AVFoundation
import React

@objc(ShazamBridge)
class ShazamBridge: NSObject {
    private let audioEngine = AVAudioEngine()
    private let session = SHSession()
    private var signatureGenerator = SHSignatureGenerator()

    @objc func startListening(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        let inputNode = audioEngine.inputNode
        let recordingFormat = inputNode.outputFormat(forBus: 0)
        inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { (buffer: AVAudioPCMBuffer, when: AVAudioTime) in
            self.signatureGenerator.append(buffer, at: nil)
        }

        audioEngine.prepare()
        do {
            try audioEngine.start()
        } catch {
            reject("Error", "Audio engine could not start", error)
        }

        resolve("Listening started")
    }

    @objc func stopListeningAndIdentify(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        audioEngine.stop()
        audioEngine.inputNode.removeTap(onBus: 0)

        let matcher = SHMatcher()
        let signature = signatureGenerator.signature()
        matcher.addSignature(signature)

        session.match(matcher, completion: { (matches, error) in
            if let error = error {
                reject("Error", "Failed to match audio", error)
            } else if let match = matches?.first {
                resolve(match.mediaItem.title)
            } else {
                resolve(nil)
            }
        })
    }
}

错误原因

最新版ShazamKit中,SHMatcher已被废弃,无需创建该实例。直接使用SHSession的match(_ signature: SHSignature, completion: ...)方法即可完成音频匹配。

修正后的完整代码

import Foundation
import ShazamKit
import AVFoundation
import React

@objc(ShazamBridge)
class ShazamBridge: NSObject {
    private let audioEngine = AVAudioEngine()
    private let session = SHSession()
    private var signatureGenerator = SHSignatureGenerator()

    @objc func startListening(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        // 检查麦克风权限
        switch AVAudioSession.sharedInstance().recordPermission {
        case .granted:
            setupAudioTap(resolve: resolve, reject: reject)
        case .denied:
            reject("PERMISSION_DENIED", "麦克风权限已被拒绝", nil)
        case .undetermined:
            AVAudioSession.sharedInstance().requestRecordPermission { granted in
                DispatchQueue.main.async {
                    if granted {
                        self.setupAudioTap(resolve: resolve, reject: reject)
                    } else {
                        reject("PERMISSION_DENIED", "麦克风权限请求被拒绝", nil)
                    }
                }
            }
        @unknown default:
            reject("PERMISSION_ERROR", "权限状态未知", nil)
        }
    }

    private func setupAudioTap(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {
        let inputNode = audioEngine.inputNode
        let recordingFormat = inputNode.outputFormat(forBus: 0)
        
        // 移除已存在的tap,避免重复添加
        inputNode.removeTap(onBus: 0)
        
        inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { [weak self] buffer, when in
            self?.signatureGenerator.append(buffer, at: when)
        }

        audioEngine.prepare()
        do {
            try audioEngine.start()
            resolve("Listening started")
        } catch {
            reject("AUDIO_ENGINE_ERROR", "音频引擎启动失败", error)
        }
    }

    @objc func stopListeningAndIdentify(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
        audioEngine.stop()
        audioEngine.inputNode.removeTap(onBus: 0)

        let signature = signatureGenerator.signature()
        // 重置签名生成器,以便下次使用
        signatureGenerator = SHSignatureGenerator()

        session.match(signature) { matches, error in
            DispatchQueue.main.async {
                if let error = error {
                    reject("MATCH_ERROR", "音频匹配失败", error)
                } else if let match = matches?.first {
                    resolve([
                        "title": match.mediaItem.title ?? "未知标题",
                        "artist": match.mediaItem.artist ?? "未知艺术家",
                        "album": match.mediaItem.albumTitle ?? "未知专辑"
                    ])
                } else {
                    resolve(nil)
                }
            }
        }
    }

    // 暴露给React Native的模块配置
    @objc static func requiresMainQueueSetup() -> Bool {
        return true
    }
}

关键说明

  • 权限配置:在iOS项目的Info.plist中添加以下权限描述:
    <key>NSMicrophoneUsageDescription</key>
    <string>需要访问麦克风以识别音乐</string>
    <key>NSShazamKitUsageDescription</key>
    <string>需要使用ShazamKit识别音乐</string>
    
  • 音频引擎管理:添加了麦克风权限检查,避免无权限时崩溃;每次启动监听前移除旧的tap,防止重复添加导致的错误
  • 签名重置:每次匹配完成后重置SHSignatureGenerator,确保下次监听生成新的签名
  • 结果返回:扩展了返回数据,包含标题、艺术家、专辑信息,更实用;所有回调切换到主队列,符合React Native的线程要求
  • 模块配置:实现requiresMainQueueSetup方法,确保模块在主队列初始化,避免线程问题

内容的提问来源于stack exchange,提问作者amirhosein

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 14:17:48