### Setup AVAudioEngine for Audio Capture Source: https://github.com/helloooideeeeea/realtimecutvadlibrary/blob/main/_autodocs/integration-guide.md Set up the AVAudioEngine to capture audio input. This involves creating an audio format and installing a tap on the input node to receive audio buffers. ```swift func setupAudioEngine() { audioEngine = AVAudioEngine() let audioSession = AVAudioSession.sharedInstance() let sampleRate = audioSession.sampleRate let format = AVAudioFormat( commonFormat: .pcmFormatFloat32, sampleRate: sampleRate, channels: 1, interleaved: true ) guard let format = format else { print("Cannot create audio format") return } desiredFormat = format audioEngine?.inputNode.installTap( onBus: 0, bufferSize: 4800, format: format ) { [weak self] buffer, _ in self?.handleAudioBuffer(buffer) } audioEngine?.prepare() do { try audioEngine?.start() print("Audio engine started") } catch { print("Failed to start audio engine: \(error)") } } ``` -------------------------------- ### Minimal VADWrapper Setup Source: https://github.com/helloooideeeeea/realtimecutvadlibrary/blob/main/_autodocs/README.md Initialize VADWrapper, set the delegate, and configure the Silero model and sample rate. This is the basic setup for most applications. ```swift let vadManager = VADWrapper() vadManager.delegate = self vadManager.setSileroModel(.v5) vadManager.setSamplerate(.SAMPLERATE_16) ``` -------------------------------- ### Voice Memo Recorder Setup and VAD Integration Source: https://github.com/helloooideeeeea/realtimecutvadlibrary/blob/main/_autodocs/use-cases.md This Swift code sets up the audio session, initializes the VADManager with a Silero model and appropriate sample rate, and starts audio capture. It handles audio session configuration and VAD manager setup based on the device's sample rate. ```swift import UIKit import AVFoundation import RealTimeCutVADLibrary class VoiceMemoViewController: UIViewController, VADDelegate { var vadManager: VADWrapper? var audioEngine: AVAudioEngine? @IBOutlet weak var recordButton: UIButton! @IBOutlet weak var statusLabel: UILabel! @IBOutlet weak var durationLabel: UILabel! @IBOutlet weak var memoListTableView: UITableView! private var recordingStartTime: Date? private var memos: [URL] = [] private let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] override func viewDidLoad() { super.viewDidLoad() setupAudioSession() setupVAD() loadMemos() } func setupAudioSession() { let audioSession = AVAudioSession.sharedInstance() do { try audioSession.setCategory(.playAndRecord, options: .defaultToSpeaker) try audioSession.setActive(true, options: .notifyOthersOnDeactivation) } catch { showError("Audio session setup failed: \(error)") } } func setupVAD() { vadManager = VADWrapper() vadManager?.delegate = self vadManager?.setSileroModel(.v5) let audioSession = AVAudioSession.sharedInstance() switch audioSession.sampleRate { case 48000: vadManager?.setSamplerate(.SAMPLERATE_48) case 24000: vadManager?.setSamplerate(.SAMPLERATE_24) case 16000: vadManager?.setSamplerate(.SAMPLERATE_16) default: vadManager?.setSamplerate(.SAMPLERATE_8) } startAudioCapture() } func startAudioCapture() { audioEngine = AVAudioEngine() let audioSession = AVAudioSession.sharedInstance() let format = AVAudioFormat( commonFormat: .pcmFormatFloat32, sampleRate: audioSession.sampleRate, channels: 1, interleaved: true )! audioEngine?.inputNode.installTap(onBus: 0, bufferSize: 4800, format: format) { [weak self] buffer, _ in guard let self = self, let channelData = buffer.floatChannelData else { return } self.vadManager?.processAudioDataWithBuffer( channelData[0], count: UInt(buffer.frameLength) ) } do { try audioEngine?.start() updateUI(recording: false) } catch { showError("Audio engine start failed: \(error)") } } func voiceStarted() { DispatchQueue.main.async { self.recordingStartTime = Date() self.updateUI(recording: true) } } func voiceEndedWithWavData(_ wavData: NSData!) { guard let data = wavData else { return } DispatchQueue.main.async { let duration = Date().timeIntervalSince(self.recordingStartTime ?? Date()) let formatter = ISO8601DateFormatter() let timestamp = formatter.string(from: Date()) let fileURL = self.documentsURL.appendingPathComponent("\(timestamp).wav") do { try data.write(to: fileURL) self.memos.append(fileURL) self.memoListTableView.reloadData() self.showSuccess("Memo saved: \(duration.formatted())s") } catch { self.showError("Failed to save memo: \(error)") } self.updateUI(recording: false) } } func voiceDidContinueWithPCMFloatData(_ pcmFloatData: NSData!) { guard let recordingStart = recordingStartTime else { return } DispatchQueue.main.async { let elapsed = Date().timeIntervalSince(recordingStart) let minutes = Int(elapsed) / 60 let seconds = Int(elapsed) % 60 self.durationLabel.text = String(format: "%02d:%02d", minutes, seconds) } } func updateUI(recording: Bool) { recordButton.backgroundColor = recording ? .systemRed : .systemBlue statusLabel.text = recording ? "đŸŽ™ī¸ Recording..." : "Ready" statusLabel.textColor = recording ? .systemRed : .systemGreen } func loadMemos() { do { let contents = try FileManager.default.contentsOfDirectory(at: documentsURL, includingPropertiesForKeys: nil) memos = contents.filter { $0.pathExtension == "wav" } \ .sorted { $0.lastPathComponent > $1.lastPathComponent } } catch { print("Error loading memos: \(error)") } } func showError(_ message: String) { let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert) ``` -------------------------------- ### CocoaPods Installation Source: https://github.com/helloooideeeeea/realtimecutvadlibrary/blob/main/README.md Add this to your Podfile to integrate the library using CocoaPods. ```ruby pod 'RealTimeCutVADLibrary', '~> 1.0.14' ``` -------------------------------- ### Meeting Recorder View Controller Setup Source: https://github.com/helloooideeeeea/realtimecutvadlibrary/blob/main/_autodocs/use-cases.md Initializes the VAD manager, audio engine, and UI elements for the meeting recorder. Requires importing UIKit, AVFoundation, and the RealTimeCutVADLibrary. ```swift import UIKit import AVFoundation import RealTimeCutVADLibrary class MeetingRecorderViewController: UIViewController, VADDelegate { var vadManager: VADWrapper? var audioEngine: AVAudioEngine? @IBOutlet weak var recordButton: UIButton! @IBOutlet weak var statusLabel: UILabel! @IBOutlet weak var speakerCountLabel: UILabel! @IBOutlet weak var durationLabel: UILabel! private var isRecording = false private var recordingStartTime: Date? private var meetingSegments: [MeetingSegment] = [] private let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] private let segmentQueue = DispatchQueue(label: "com.example.meeting.segments") struct MeetingSegment { let startTime: Date let endTime: Date? let wavData: Data let speaker: Int // Speaker ID (detected by voice characteristics) } override func viewDidLoad() { super.viewDidLoad() // Corrected from viewDidLoad() to viewDidLoad() setupAudioSession() setupVAD() } func setupAudioSession() { let audioSession = AVAudioSession.sharedInstance() do { try audioSession.setCategory(.playAndRecord, options: .defaultToSpeaker) try audioSession.setActive(true) } catch { print("Audio session error: \(error)") } } func setupVAD() { vadManager = VADWrapper() vadManager?.delegate = self vadManager?.setSileroModel(.v5) let audioSession = AVAudioSession.sharedInstance() switch audioSession.sampleRate { case 48000: vadManager?.setSamplerate(.SAMPLERATE_48) case 24000: vadManager?.setSamplerate(.SAMPLERATE_24) default: vadManager?.setSamplerate(.SAMPLERATE_16) } // Optimized for meeting environment with multiple speakers vadManager?.setThresholdWithVadStartDetectionProbability( 0.75, vadEndDetectionProbability: 0.70, voiceStartVadTrueRatio: 0.6, voiceEndVadFalseRatio: 0.93, voiceStartFrameCount: 8, voiceEndFrameCount: 40 ) startAudioCapture() } func startAudioCapture() { audioEngine = AVAudioEngine() let format = AVAudioFormat( commonFormat: .pcmFormatFloat32, sampleRate: AVAudioSession.sharedInstance().sampleRate, channels: 1, interleaved: true )! audioEngine?.inputNode.installTap(onBus: 0, bufferSize: 4800, format: format) { [weak self] buffer, _ in guard let self = self, self.isRecording, let channelData = buffer.floatChannelData else { return } self.vadManager?.processAudioDataWithBuffer( channelData[0], count: UInt(buffer.frameLength) ) } try? audioEngine?.start() } @IBAction func recordButtonTapped(_ sender: UIButton) { if isRecording { stopRecording() } else { startRecording() } } func startRecording() { isRecording = true recordingStartTime = Date() recordButton.setTitle("âšī¸ Stop Recording", for: .normal) recordButton.backgroundColor = .systemRed statusLabel.text = "🔴 Recording..." updateDuration() } func stopRecording() { isRecording = false recordButton.setTitle("🔴 Start Recording", for: .normal) recordButton.backgroundColor = .systemGreen statusLabel.text = "Stopped" saveMeetingRecording() } func saveMeetingRecording() { let formatter = ISO8601DateFormatter() let timestamp = formatter.string(from: Date()) let meetingURL = documentsURL.appendingPathComponent("meeting_\(timestamp).json") segmentQueue.async { [weak self] in guard let self = self else { return } // Encode meeting segments as JSON var meetingData = [String: Any]() meetingData["timestamp"] = timestamp meetingData["segmentCount"] = self.meetingSegments.count meetingData["uniqueSpeakers"] = Set(self.meetingSegments.map { $0.speaker }).count do { let jsonData = try JSONSerialization.data(withJSONObject: meetingData) try jsonData.write(to: meetingURL) DispatchQueue.main.async { self.statusLabel.text = "✅ Meeting saved" } } catch { print("Failed to save meeting: \(error)") } } } func updateDuration() { // Implementation omitted for brevity } // VADDelegate method implementation would go here func vadDidDetectSpeaker(_ speaker: Int, vadProbability: Float, vadStart: Bool) { // Handle detected voice activity and speaker changes } func vadDidDetectVoiceActivity(_ vadProbability: Float, vadStart: Bool) { // Handle voice activity detection events } } ``` -------------------------------- ### Swift Package Manager Installation Source: https://github.com/helloooideeeeea/realtimecutvadlibrary/blob/main/README.md Add this to your Package.swift file to integrate the library using Swift Package Manager. ```swift .dependencies: [ .package(url: "https://github.com/helloooideeeeea/RealTimeCutVADLibrary.git", from: "1.0.14") ] ``` -------------------------------- ### Set Silero Model Version Example Source: https://github.com/helloooideeeeea/realtimecutvadlibrary/blob/main/_autodocs/api-reference/VADWrapper.md Example of how to set the Silero model version using the VADManager. Use version 5 for recommended accuracy. ```Swift // Use Silero model version 5 (recommended) vadManager.setSileroModel(.v5) // Or use version 4 // vadManager.setSileroModel(.v4) ``` -------------------------------- ### Start Recording with AVAudioRecorder Source: https://github.com/helloooideeeeea/realtimecutvadlibrary/blob/main/_autodocs/integration-guide.md Use this method for basic audio recording. Note that AVAudioRecorder does not provide real-time sample access, making it unsuitable for real-time VAD processing. ```swift class RecorderDelegate: NSObject, AVAudioRecorderDelegate { var vadManager: VADWrapper? func startRecording() { let audioSession = AVAudioSession.sharedInstance() try? audioSession.setCategory(.playAndRecord) let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let tempURL = documentsURL.appendingPathComponent("temp_recording.wav") let settings: [String: Any] = [ AVFormatIDKey: Int(kAudioFormatLinearPCM), AVSampleRateKey: 16000, AVNumberOfChannelsKey: 1, AVLinearPCMBitDepthKey: 16, AVLinearPCMIsNonInterleaved: false, AVLinearPCMIsFloatKey: false ] do { let recorder = try AVAudioRecorder(url: tempURL, settings: settings) recorder.delegate = self recorder.record() print("Recording started") } catch { print("Recording setup failed: \(error)") } } func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) { if flag { print("Recording completed successfully") } else { print("Recording failed") } } } ``` -------------------------------- ### Implement VADDelegate Callbacks Source: https://github.com/helloooideeeeea/realtimecutvadlibrary/blob/main/_autodocs/INDEX.md Provides example implementations for the required VADDelegate methods to handle voice events. ```swift func voiceStarted() { /* Handle voice start */ } func voiceEndedWithWavData(_ data: NSData) { /* Handle voice end */ } func voiceDidContinueWithPCMFloatData(_ data: NSData) { /* Handle streaming */ } ``` -------------------------------- ### Initialize and Use VADWrapper Source: https://github.com/helloooideeeeea/realtimecutvadlibrary/blob/main/_autodocs/api-reference/VADWrapper.md This snippet demonstrates how to initialize VADWrapper, set the Silero model and sample rate, and set up an AVAudioEngine to process audio data for voice activity detection. It includes handling voice started, ended, and continued events. ```swift import UIKit import AVFoundation import RealTimeCutVADLibrary class ViewController: UIViewController, VADDelegate { var vadManager: VADWrapper? var audioEngine: AVAudioEngine? override func viewDidLoad() { super.viewDidLoad() // Initialize VAD vadManager = VADWrapper() vadManager?.delegate = self vadManager?.setSileroModel(.v5) vadManager?.setSamplerate(.SAMPLERATE_48) // Setup audio capture let audioSession = AVAudioSession.sharedInstance() try? audioSession.setCategory(.playAndRecord) try? audioSession.setActive(true) audioEngine = AVAudioEngine() let format = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 48000, channels: 1, interleaved: true) audioEngine?.inputNode.installTap(onBus: 0, bufferSize: 4800, format: format) { buffer, _ in guard let channelData = buffer.floatChannelData else { return } self.vadManager?.processAudioDataWithBuffer(channelData[0], count: UInt(buffer.frameLength)) } try? audioEngine?.start() } func voiceStarted() { print("Voice detected!") } func voiceEndedWithWavData(_ wavData: NSData!) { print("Voice ended: \(wavData.count) bytes") } func voiceDidContinueWithPCMFloatData(_ pcmFloatData: NSData!) { print("Voice continues...") } } ``` -------------------------------- ### Configure VAD Sensitivity Thresholds Source: https://github.com/helloooideeeeea/realtimecutvadlibrary/blob/main/_autodocs/INDEX.md Illustrates setting custom thresholds for VAD start and end detection probabilities, and ratios. ```swift vad.setThresholdWithVadStartDetectionProbability( 0.7, vadEndDetectionProbability: 0.7, voiceStartVadTrueRatio: 0.5, voiceEndVadFalseRatio: 0.95, voiceStartFrameCount: 10, voiceEndFrameCount: 57 ) ``` -------------------------------- ### Deprecated Audio Processing Example Source: https://github.com/helloooideeeeea/realtimecutvadlibrary/blob/main/_autodocs/api-reference/VADWrapper.md Example demonstrating the deprecated usage of processing audio data with an array of NSNumber objects. Avoid using this method in new development. ```Swift // ❌ Do NOT use this method var audioDataArray: [NSNumber] = [] for i in 0.. 8192 { // ~256ms at 16kHz self.callSession?.sendAudioData(self.collectedPCMData) self.collectedPCMData.removeAll(keepingCapacity: true) } } } func startCall() { callSession = CallSession() callStartTime = Date() updateCallDuration() } func updateCallDuration() { guard let startTime = callStartTime else { return } let elapsed = Date().timeIntervalSince(startTime) let hours = Int(elapsed) / 3600 let minutes = Int(elapsed) / 60 % 60 let seconds = Int(elapsed) % 60 DispatchQueue.main.async { self.callDurationLabel.text = String(format: "%02d:%02d:%02d", hours, minutes, seconds) } } } ``` -------------------------------- ### Get Audio Session Properties Source: https://github.com/helloooideeeeea/realtimecutvadlibrary/blob/main/_autodocs/integration-guide.md Retrieve and print the current audio session's sample rate and number of output channels. This information is useful for configuring the VAD library to match the audio input. ```swift let sampleRate = audioSession.sampleRate let channels = audioSession.outputNumberOfChannels print("Sample Rate: \(sampleRate) Hz") print("Channels: \(channels)") ``` -------------------------------- ### Initialize VADWrapper and Set Parameters Source: https://github.com/helloooideeeeea/realtimecutvadlibrary/blob/main/_autodocs/INDEX.md Demonstrates the initialization of VADWrapper, setting the delegate, model, and sample rate. ```swift let vad = VADWrapper() vad.delegate = self vad.setSileroModel(.v5) vad.setSamplerate(.SAMPLERATE_48) ``` -------------------------------- ### Initialize VADWrapper and Configure Settings Source: https://github.com/helloooideeeeea/realtimecutvadlibrary/blob/main/_autodocs/configuration.md This snippet demonstrates the complete initialization process for VADWrapper, including setting the delegate, Silero model version, sample rate, and optional threshold customization. ```swift import RealTimeCutVADLibrary let vadManager = VADWrapper() // 1. Set delegate (required to receive callbacks) vadManager.delegate = self // 2. Set Silero model version (required) vadManager.setSileroModel(.v5) // 3. Set sample rate (required) vadManager.setSamplerate(.SAMPLERATE_48) // 4. (Optional) Customize thresholds if defaults don't suit your needs // vadManager.setThresholdWithVadStartDetectionProbability(...) // 5. Start processing audio // vadManager.processAudioDataWithBuffer(audioBuffer, count: bufferSize) ``` -------------------------------- ### Import RealTimeCutVADLibrary in Objective-C Source: https://github.com/helloooideeeeea/realtimecutvadlibrary/blob/main/_autodocs/INDEX.md Demonstrates how to import and initialize VADWrapper in Objective-C projects. ```objc #import VADWrapper *vad = [[VADWrapper alloc] init]; ``` -------------------------------- ### Clear Collected PCM Data on Voice Start Source: https://github.com/helloooideeeeea/realtimecutvadlibrary/blob/main/_autodocs/integration-guide.md Reset collected PCM data when voice starts to manage memory usage. This is crucial for applications that continuously process audio. ```swift func voiceStarted() { collectedPCMData = Data() } ``` -------------------------------- ### Initialize and Configure VAD Manager Source: https://github.com/helloooideeeeea/realtimecutvadlibrary/blob/main/README.md Initialize the VADManager, set its delegate, choose the Silero model version, and set the audio sample rate. Optional threshold configuration is also shown. ```swift import RealTimeCutVADLibrary class ViewController: UIViewController { var vadManager: VADWrapper? override func viewDidLoad() { super.viewDidLoad() // Initialize VAD Manager vadManager = VADWrapper() // Set VAD delegate to receive callbacks vadManager?.delegate = self // Set Silero model version (v4 or v5). Version v5 is recommended. vadManager?.setSileroModel(.v5) // Calling setVADThreshold is optional. If not called, the recommended default values will be used. // vadManager?.setThresholdWithVadStartDetectionProbability(0.7,0.7,0.5,0.95,10,57) // Set audio sample rate (8, 16, 24, or 48 kHz) vadManager?.setSamplerate(.SAMPLERATE_48) // Retrieve audio channel data from Microphone guard let channelData = buffer.floatChannelData else { return } // Extract frame length from the audio buffer let frameLength = UInt(buffer.frameLength) // Select the first channel as mono audio data let monoralData = channelData[0] // This is UnsafeMutablePointer // Send the audio data directly to VAD processing vadManager?.processAudioData(withBuffer: monoralData, count: frameLength) // ❌ Deprecated Usage. Do NOT use this method: Slow due to NSNumber conversion var monoralDataArray: [NSNumber] = [] for i in 0..