Couldn't able to hear audio via speaker on ios real device

This is my native module code implementation I'm getting base64 encoded string from server and passing this to my native module of pcm player to play audio

App.tsx

PcmPlayer.writeChunk(e.data);

PcmPlayer.swift

import AVFoundation

@objc(PcmPlayer)
class PcmPlayer: RCTEventEmitter {
  private var engine: AVAudioEngine?
  private var playerNode: AVAudioPlayerNode?
  private var format: AVAudioFormat?
  private var bufferQueue = [Data]()
  private var isPlaying = false
  private var hasEnded = false
  private var scheduledBufferCount = 0
  private let minBufferBytes = 50000
  private let pcmQueue = DispatchQueue(label: "pcm.queue")

  override init() {
    super.init()
  }

  override func supportedEvents() -> [String]! {
    return ["onStatus", "onMessage"]
  }

  @objc(initPlayer:channels:bitsPerSample:)
  func initPlayer(_ sampleRate: NSNumber,
                  channels: NSNumber,
                  bitsPerSample: NSNumber) {
    pcmQueue.async {
      self.stopInternal()
      let session = AVAudioSession.sharedInstance()
      do {
        try session.setCategory(.playback, mode: .default, options: [])
        try session.setActive(true, options: .notifyOthersOnDeactivation)
        try session.setMode(.default)
        print("🔈 Audio session active. Output route:", session.currentRoute.outputs)
      } catch {
        print("❌ Audio session setup failed:", error)
        return
      }

      self.engine = AVAudioEngine()
      self.playerNode = AVAudioPlayerNode()
      guard let engine = self.engine, let playerNode = self.playerNode else {
        print("❌ Engine or playerNode is nil")
        return
      }

      engine.attach(playerNode)
      self.format = AVAudioFormat(commonFormat: .pcmFormatFloat32,
                                  sampleRate: sampleRate.doubleValue,
                                  channels: AVAudioChannelCount(channels.uintValue),
                                  interleaved: false)

      guard let format = self.format else {
        print("❌ Failed to create AVAudioFormat")
        return
      }

      engine.connect(playerNode, to: engine.mainMixerNode, format: format)
      do {
        try engine.start()
        playerNode.play()
        engine.mainMixerNode.outputVolume = 1.0
        print("✅ AVAudioEngine started with format:", format)
      } catch {
        print("❌ Engine start failed:", error)
      }
      self.hasEnded = false
    }
  }


  @objc(writeChunk:)
  func writeChunk(_ base64Pcm: String) {
    pcmQueue.async {
      guard base64Pcm.count >= 10 else {
        print("⚠️ Skipping short base64 string")
        return
      }

      var padded = base64Pcm
      let mod4 = base64Pcm.count % 4
      if mod4 > 0 {
        padded += String(repeating: "=", count: 4 - mod4)
      }

      guard let data = Data(base64Encoded: padded, options: .ignoreUnknownCharacters) else {
        print("❌ Failed to decode base64")
        return
      }

      self.bufferQueue.append(data)
      print("📥 Received PCM chunk (\(data.count) bytes)")
      print("📥 writeChunk called. isPlaying=\(self.isPlaying), bufferQueue.count=\(self.bufferQueue.count)")

      if !self.isPlaying {
        self.isPlaying = true
        self.waitForBufferAndStartPlayback()
      } else if self.scheduledBufferCount == 0 {
        self.isPlaying = true
        self.waitForBufferAndStartPlayback()
      }
    }
  }

  private func waitForBufferAndStartPlayback() {
    DispatchQueue.global().async {
      while self.queueSize() < self.minBufferBytes && !self.hasEnded {
        Thread.sleep(forTimeInterval: 0.01)
      }
      self.writeLoop()
    }
  }

  private func writeLoop() {
    DispatchQueue.global().async {
      writeLoop: while self.isPlaying {
        if self.bufferQueue.isEmpty {
          for _ in 0..<100 {
            Thread.sleep(forTimeInterval: 0.01)
            if !self.bufferQueue.isEmpty { break }
          }

          if self.bufferQueue.isEmpty {
            print("🔇 No more data to play after waiting")
            self.isPlaying = false
            break writeLoop
          }
        }

        var data: Data?
        self.pcmQueue.sync {
          if !self.bufferQueue.isEmpty {
            data = self.bufferQueue.removeFirst()
          }
        }

        guard let chunk = data else {
          print("⚠️ No data to process")
          continue
        }

        if let buffer = self.pcmBufferFromData(chunk) {
          self.scheduledBufferCount += 1
          self.playerNode?.scheduleBuffer(buffer, completionHandler: {
            self.pcmQueue.async {
              self.scheduledBufferCount -= 1
              if self.bufferQueue.isEmpty && self.scheduledBufferCount == 0 {
                print("ℹ️ Playback idle - waiting for more data")
                self.isPlaying = false
              }
            }
          })
        }
      }
    }
  }

  private func pcmBufferFromData(_ data: Data) -> AVAudioPCMBuffer? {
    guard let format = self.format else { return nil }

    let frameCount = UInt32(data.count / 2)
    guard let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: frameCount) else {
      print("❌ Failed to create AVAudioPCMBuffer")
      return nil
    }

    buffer.frameLength = frameCount

    guard let floatChannelData = buffer.floatChannelData?[0] else {
      print("❌ floatChannelData is nil")
      return nil
    }

    data.withUnsafeBytes { (rawBuffer: UnsafeRawBufferPointer) in
      let int16Buffer = rawBuffer.bindMemory(to: Int16.self)
      let count = min(int16Buffer.count, Int(frameCount))
      for i in 0..<count {
        floatChannelData[i] = Float32(int16Buffer[i]) / Float32(Int16.max)
      }
    }

    return buffer
  }

  @objc(stopPlayer)
  func stopPlayer() {
    pcmQueue.async {
      self.stopInternal()
    }
  }

  private func stopInternal() {
    print("🛑 stopInternal called")
    self.playerNode?.stop()
    self.engine?.stop()
    self.engine?.reset()
    self.playerNode = nil
    self.engine = nil
    self.format = nil
    self.bufferQueue.removeAll()
    self.isPlaying = false
    self.hasEnded = true
    self.scheduledBufferCount = 0
  }

  @objc(canWrite:rejecter:)
  func canWrite(_ resolve: @escaping RCTPromiseResolveBlock,
                rejecter reject: RCTPromiseRejectBlock) {
    pcmQueue.async {
      resolve(self.bufferQueue.count < 20)
    }
  }

  @objc(flushPlayer:rejecter:)
  func flushPlayer(_ resolve: @escaping RCTPromiseResolveBlock,
                   rejecter reject: RCTPromiseRejectBlock) {
    pcmQueue.async {
      self.bufferQueue.removeAll()
      resolve(nil)
    }
  }

  @objc
  static override func requiresMainQueueSetup() -> Bool {
    return false
  }

  private func queueSize() -> Int {
    return pcmQueue.sync {
      return self.bufferQueue.reduce(0) { $0 + $1.count }
    }
  }
}

I couldn't able to hear any audio via my real iOS device also it is working fine on emulator.

Couldn't able to hear audio via speaker on ios real device
 
 
Q