Shazamkit - Exception 'required condition is false: IsFormatSampleRateAndChannelCountValid(format)'

I'm trying to expose my native shazamkit code to the host react native app. The implementation works fine in a separate swift project but it fails when I try to integrate it into a React Native app.

Exception 'required condition is false: IsFormatSampleRateAndChannelCountValid(format)' was thrown while invoking exposed on target ShazamIOS with params (
    1682,
    1683
)
callstack: (
	0   CoreFoundation                      0x00007ff80049b761 __exceptionPreprocess + 242
	1   libobjc.A.dylib                     0x00007ff800063904 objc_exception_throw + 48
	2   CoreFoundation                      0x00007ff80049b56b +[NSException raise:format:] + 0
	3   AVFAudio                            0x00007ff846197929 _Z19AVAE_RaiseExceptionP8NSStringz + 156
	4   AVFAudio                            0x00007ff8461f2e90 _ZN17AUGraphNodeBaseV318CreateRecordingTapEmjP13AVAudioFormatU13block_pointerFvP16AVAudioPCMBufferP11AVAudioTimeE + 766
	5   AVFAudio                            0x00007ff84625f703 -[AVAudioNode installTapOnBus:bufferSize:format:block:] + 1456
	6   muse                                0x000000010a313dd0 $s4muse9ShazamIOSC6record33_35CC2309E4CA22278DC49D01D96C376ALLyyF + 496
	7   muse                                0x000000010a313210 $s4muse9ShazamIOSC5startyyF + 288
	8   muse                                0x000000010a312d03 $s4muse9ShazamIOSC7exposed_6rejectyyypSgXE_ySSSg_AGs5Error_pSgtXEtF + 83
	9   muse                                0x000000010a312e47 $s4muse9ShazamIOSC7exposed_6rejectyyypSgXE_ySSSg_AGs5Error_pSgtXEtFTo + 103
	10  CoreFoundation                      0x00007ff8004a238c __invoking___ + 140
	11  CoreFoundation                      0x00007ff80049f6b3 -[NSInvocation invoke] + 302
	12  CoreFoundation                      0x00007ff80049f923 -[NSInvocation invokeWithTarget:] + 70
	13  muse                                0x000000010a9210ef -[RCTModuleMethod invokeWithBridge:module:arguments:] + 2495
	14  muse                                0x000000010a925cb4 _ZN8facebook5reactL11invokeInnerEP9RCTBridgeP13RCTModuleDatajRKN5folly7dynamicEiN12_GLOBAL__N_117SchedulingContextE + 2036
	15  muse                                0x000000010a925305 _ZZN8facebook5react15RCTNativeModule6invokeEjON5folly7dynamicEiENK3$_0clEv + 133
	16  muse                                0x000000010a925279 ___ZN8facebook5react15RCTNativeModule6invokeEjON5folly7dynamicEi_block_invoke + 25
	17  libdispatch.dylib                   0x000000010e577747 _dispatch_call_block_and_release + 12
	18  libdispatch.dylib                   0x000000010e5789f7 _dispatch_client_callout + 8
	19  libdispatch.dylib                   0x000000010e5808c9 _dispatch_lane_serial_drain + 1127
	20  libdispatch.dylib                   0x000000010e581665 _dispatch_lane_invoke + 441
	21  libdispatch.dylib                   0x000000010e58e76e _dispatch_root_queue_drain_deferred_wlh + 318
	22  libdispatch.dylib                   0x000000010e58db69 _dispatch_workloop_worker_thread + 590
	23  libsystem_pthread.dylib             0x000000010da67b84 _pthread_wqthread + 327
	24  libsystem_pthread.dylib             0x000000010da66acf start_wqthread + 15
)
RCTFatal
facebook::react::invokeInner(RCTBridge*, RCTModuleData*, unsigned int, folly::dynamic const&, int, (anonymous namespace)::SchedulingContext)
facebook::react::RCTNativeModule::invoke(unsigned int, folly::dynamic&&, int)::$_0::operator()() const
invocation function for block in facebook::react::RCTNativeModule::invoke(unsigned int, folly::dynamic&&, int)

This is my swift file, error happens in the record function.

import Foundation
import ShazamKit

@objc(ShazamIOS)
class ShazamIOS : NSObject {

  @Published var matching: Bool = false
  @Published var mediaItem: SHMatchedMediaItem?
  @Published var error: Error? {
      didSet {
          hasError = error != nil
      }
  }
  @Published var hasError: Bool = false
  
  private lazy var audioSession: AVAudioSession = .sharedInstance()
  private lazy var session: SHSession = .init()
  private lazy var audioEngine: AVAudioEngine = .init()
  private lazy var inputNode = self.audioEngine.inputNode
  private lazy var bus: AVAudioNodeBus = 0
  
  override init() {
      super.init()
      session.delegate = self
  }

  @objc
  func exposed(_ resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock){
    start()
    resolve("ios code executed")
  }

  func start() {
      switch audioSession.recordPermission {
      case .granted:
          self.record()
      case .denied:
          DispatchQueue.main.async {
              self.error = ShazamError.recordDenied
          }
      case .undetermined:
          audioSession.requestRecordPermission { granted in
              DispatchQueue.main.async {
                  if granted {
                      self.record()
                  }
                  else {
                      self.error = ShazamError.recordDenied
                  }
              }
          }
      @unknown default:
          DispatchQueue.main.async {
              self.error = ShazamError.unknown
          }
      }
  }

  private func record() {
      do {
          self.matching = true
          let format = self.inputNode.outputFormat(forBus: bus)
          self.inputNode.installTap(onBus: bus, bufferSize: 8192, format: format) { [weak self] (buffer, time) in
              self?.session.matchStreamingBuffer(buffer, at: time)
          }
          self.audioEngine.prepare()
          try self.audioEngine.start()
      }
      catch {
          self.error = error
      }
  }

  func stop() {
      self.audioEngine.stop()
      self.inputNode.removeTap(onBus: bus)
      self.matching = false
  }

  @objc
  static func requiresMainQueueSetup() -> Bool {
    return true;
  }
}

extension ShazamIOS: SHSessionDelegate {

    func session(_ session: SHSession, didFind match: SHMatch) {
        DispatchQueue.main.async { [self] in
            if let mediaItem = match.mediaItems.first {
                self.mediaItem = mediaItem
                self.stop()
            }
        }
    }

    func session(_ session: SHSession, didNotFindMatchFor signature: SHSignature, error: Error?) {
        DispatchQueue.main.async {[self] in
            self.error = error
            self.stop()
        }
    }
}

objC file

#import <Foundation/Foundation.h>
#import "React/RCTBridgeModule.h"

@interface RCT_EXTERN_MODULE(ShazamIOS, NSObject);
RCT_EXTERN_METHOD(exposed:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
@end

how I consume the exposed function in RN.

  const {ShazamModule, ShazamIOS} = NativeModules;

  const onPressIOSButton = () => {
    ShazamIOS.exposed().then(result => console.log(result)).catch(e => console.log(e.message, e.code));
  };
Answered by Engineer in 788571022

Based on the error message and stack trace, an exception is being raised due to a mismatching format passed to installTap(onBus:bufferSize:format:block:). The format parameter is optional, so you can also pass a nil format in the above call.

To make your implementation robust, you should register for the AVAudioEngineConfigurationChange notification and correctly handle any changes to the audio input or output hardware's channel count or sample rate.

Please also see the Matching audio using the built-in microphone article. The configureAudioEngine function connects the engine's input node to a mixer node, and installs a tap on that mixer node to make sure the audio format is compatible with ShazamKit. In general, the audio format of some microphones and input sources may not be compatible with ShazamKit.

Based on the error message and stack trace, an exception is being raised due to a mismatching format passed to installTap(onBus:bufferSize:format:block:). The format parameter is optional, so you can also pass a nil format in the above call.

To make your implementation robust, you should register for the AVAudioEngineConfigurationChange notification and correctly handle any changes to the audio input or output hardware's channel count or sample rate.

Please also see the Matching audio using the built-in microphone article. The configureAudioEngine function connects the engine's input node to a mixer node, and installs a tap on that mixer node to make sure the audio format is compatible with ShazamKit. In general, the audio format of some microphones and input sources may not be compatible with ShazamKit.

Hi there, I didn't use ShazamKit, just audio & mixer nodes but I got the same crash required condition is false: IsFormatSampleRateAndChannelCountValid(format) I follow your article https://developer.apple.com/documentation/shazamkit/matching-audio-using-the-built-in-microphone but still didn't work. The problem is that issue only reported on crashlytics from my user and I can't reproduce on my device.

 Fatal Exception: com.apple.coreaudio.avfaudio
0  CoreFoundation                 0x2d5fc __exceptionPreprocess
1  libobjc.A.dylib                0x31244 objc_exception_throw
2  CoreFoundation                 0x17a6e0 -[NSException initWithCoder:]
3  AVFAudio                       0x634d8 AUGraphNodeBaseV3::CreateRecordingTap(unsigned long, unsigned int, AVAudioFormat*, void (AVAudioPCMBuffer*, AVAudioTime*) block_pointer)
4  AVFAudio                       0x112624 AVAudioEngineImpl::InstallTapOnNode(AVAudioNode*, unsigned long, unsigned int, AVAudioFormat*, void (AVAudioPCMBuffer*, AVAudioTime*) block_pointer)
5  AVFAudio                       0xe7fa0 -[AVAudioNode installTapOnBus:bufferSize:format:block:]
6  Runner                         0x1b2f8 closure #1 in SpeechController.initializeAudioInputEngine() + 1269 (SpeechController.swift:1269)
7  Runner                         0x11c28 partial apply for thunk for @callee_guaranteed () -> () + 573 (AssetController.swift:573)
8  Runner                         0x15160 thunk for @escaping @callee_guaranteed () -> () (<compiler-generated>)
9  libdispatch.dylib              0x3fa8 _dispatch_client_callout
10 libdispatch.dylib              0x137fc _dispatch_lane_barrier_sync_invoke_and_complete
11 Runner                         0x1aec8 SpeechController.initializeAudioInputEngine() + 1235 (SpeechController.swift:1235)
12 Runner                         0x1f750 SpeechController.applicationWillEnterForeground() + 1923 (SpeechController.swift:1923)
13 Runner                         0x13e6c AppDelegate.applicationWillEnterForeground(_:) + 133 (AppDelegate.swift:133)
14 Runner                         0x13ee0 @objc AppDelegate.applicationWillEnterForeground(_:) (<compiler-generated>)
15 UIKitCore                      0x3f24d0 -[UIApplication _sendWillEnterForegroundCallbacks]
16 UIKitCore                      0x3eeb84 __101-[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:]_block_invoke_2
17 UIKitCore                      0x24f5e4 _UIScenePerformActionsWithLifecycleActionMask
18 UIKitCore                      0x3ef00c __101-[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:]_block_invoke
19 UIKitCore                      0x3eed1c -[_UISceneLifecycleMultiplexer _performBlock:withApplicationOfDeactivationReasons:fromReasons:]
20 UIKitCore                      0x1b5834 -[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:]
21 UIKitCore                      0x1b5554 -[_UISceneLifecycleMultiplexer uiScene:transitionedFromState:withTransitionContext:]
22 UIKitCore                      0x1b500c __186-[_UIWindowSceneFBSSceneTransitionContextDrivenLifecycleSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:]_block_invoke
23 UIKitCore                      0x1b41b4 +[BSAnimationSettings(UIKit) tryAnimatingWithSettings:fromCurrentState:actions:completion:]
24 UIKitCore                      0x1b37ec _UISceneSettingsDiffActionPerformChangesWithTransitionContextAndCompletion
25 UIKitCore                      0x1b349c -[_UIWindowSceneFBSSceneTransitionContextDrivenLifecycleSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:]
26 UIKitCore                      0x24f394 __64-[UIScene scene:didUpdateWithDiff:transitionContext:completion:]_block_invoke.229
27 UIKitCore                      0x24ece4 -[UIScene _emitSceneSettingsUpdateResponseForCompletion:afterSceneUpdateWork:]
28 UIKitCore                      0x246708 -[UIScene scene:didUpdateWithDiff:transitionContext:completion:]
29 UIKitCore                      0x245ee8 -[UIApplicationSceneClientAgent scene:handleEvent:withCompletion:]
30 FrontBoardServices             0xc290 __76-[FBSScene updater:didUpdateSettings:withDiff:transitionContext:completion:]_block_invoke.146
31 FrontBoardServices             0x9834 -[FBSScene _callOutQueue_coalesceClientSettingsUpdates:]
32 FrontBoardServices             0x7090 -[FBSScene updater:didUpdateSettings:withDiff:transitionContext:completion:]
33 FrontBoardServices             0x1f868 __94-[FBSWorkspaceScenesClient _queue_updateScene:withSettings:diff:transitionContext:completion:]_block_invoke_2
34 FrontBoardServices             0x1f4c8 -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:]
35 FrontBoardServices             0xa510 __94-[FBSWorkspaceScenesClient _queue_updateScene:withSettings:diff:transitionContext:completion:]_block_invoke
36 libdispatch.dylib              0x3fa8 _dispatch_client_callout
37 libdispatch.dylib              0x79f0 _dispatch_block_invoke_direct

Here is my code:

var engine = AVAudioEngine()

  let mixer = AVAudioMixerNode()

    let soundNode = AVAudioPlayerNode()

    let speechNode = AVAudioPlayerNode()



engine.attach(mixer)

        engine.connect(mixer, to: engine.outputNode, format: nil)



        // Add the sound effects and looping node players

        engine.attach(soundNode)

        engine.connect(soundNode, to: mixer, format: nil)

        let speechFormat = AVAudioFormat.init(

            standardFormatWithSampleRate: 32000, channels: 1)

        engine.attach(speechNode)

        engine.connect(speechNode, to: mixer, format: speechFormat)



 let audioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory( AVAudioSession.Category.playAndRecord, mode: mode, options: [.defaultToSpeaker, .allowBluetooth])

try audioSession.setPreferredSampleRate(micSampleRate)
try audioSession.setActive(true)
try engine.inputNode.setVoiceProcessingEnabled(true)
try engine.outputNode.setVoiceProcessingEnabled(true)
engine.inputNode.removeTap(onBus: kBus)

            // buffer needs to be 100ms for good latency

            engine.inputNode.installTap(

                onBus: kBus,

                bufferSize: UInt32(inputFormat!.sampleRate * kBufferDuration),

                format: inputFormat!

            ) { buffer, _ in
                guard self.microphoneEnabled, !self.appInBackground else {
                self.recognize(buffer: buffer)
            }

Can you look at is there anything wrong with my code? Thank you!

Shazamkit - Exception 'required condition is false: IsFormatSampleRateAndChannelCountValid(format)'
 
 
Q