├── .editorconfig ├── .gitignore ├── .travis.yml ├── Example ├── Podfile ├── Podfile.lock ├── Tests │ ├── 3722.mp3 │ ├── AudioTrackReader.swift │ ├── Info.plist │ └── Tests.swift ├── VoiceActivityDetector.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── VoiceActivityDetector-Example.xcscheme ├── VoiceActivityDetector.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── VoiceActivityDetector │ ├── AppDelegate.swift │ ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard │ ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Info.plist │ └── ViewController.swift ├── LICENSE ├── README.md ├── VoiceActivityDetector.podspec ├── VoiceActivityDetector ├── Assets │ └── .gitkeep └── Classes │ └── VoiceActivityDetector.swift └── _Pods.xcodeproj /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | 9 | [*.{swift,h,m}] 10 | indent_style = space 11 | indent_size = 2 12 | 13 | [*.md] 14 | indent_size = 2 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 26 | # Carthage/Checkouts 27 | 28 | Carthage/Build 29 | 30 | # We recommend against adding the Pods directory to your .gitignore. However 31 | # you should judge for yourself, the pros and cons are mentioned at: 32 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 33 | # 34 | # Note: if you ignore the Pods directory, make sure to uncomment 35 | # `pod install` in .travis.yml 36 | # 37 | Pods/ 38 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * https://www.objc.io/issues/6-build-tools/travis-ci/ 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode10.2 6 | language: objective-c 7 | # cache: cocoapods 8 | # podfile: Example/Podfile 9 | 10 | before_install: 11 | - gem install cocoapods 12 | - pod repo update 13 | - pod install --project-directory=Example 14 | 15 | before_script: 16 | - set -o pipefail 17 | - pod repo update 18 | 19 | script: 20 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/VoiceActivityDetector.xcworkspace -scheme VoiceActivityDetector-Example -sdk iphonesimulator12.2 -destination 'platform=iOS Simulator,name=iPhone 8,OS=12.2' ONLY_ACTIVE_ARCH=NO | xcpretty 21 | - pod lib lint 22 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'VoiceActivityDetector_Example' do 4 | pod 'VoiceActivityDetector', :path => '../' 5 | 6 | target 'VoiceActivityDetector_Tests' do 7 | inherit! :search_paths 8 | 9 | pod 'Quick', '~> 1.2.0' 10 | pod 'Nimble', '~> 7.0' 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - libfvad (0.1.0) 3 | - Nimble (7.3.4) 4 | - Quick (1.2.0) 5 | - VoiceActivityDetector (0.1.0): 6 | - libfvad (~> 0.1.0) 7 | 8 | DEPENDENCIES: 9 | - Nimble (~> 7.0) 10 | - Quick (~> 1.2.0) 11 | - VoiceActivityDetector (from `../`) 12 | 13 | SPEC REPOS: 14 | https://github.com/cocoapods/specs.git: 15 | - libfvad 16 | - Nimble 17 | - Quick 18 | 19 | EXTERNAL SOURCES: 20 | VoiceActivityDetector: 21 | :path: "../" 22 | 23 | SPEC CHECKSUMS: 24 | libfvad: e746a80f89355df888c454284141bbb41adfdc95 25 | Nimble: 051e3d8912d40138fa5591c78594f95fb172af37 26 | Quick: 58d203b1c5e27fff7229c4c1ae445ad7069a7a08 27 | VoiceActivityDetector: 4c5052ea5481dce390b5ab833444e6db96980d23 28 | 29 | PODFILE CHECKSUM: 859617957d9ae834b618523c0c642764aac522e2 30 | 31 | COCOAPODS: 1.7.4 32 | -------------------------------------------------------------------------------- /Example/Tests/3722.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reedom/VoiceActivityDetector/e639fc037f19f8c03ba33d1dfdfc42da914f11b4/Example/Tests/3722.mp3 -------------------------------------------------------------------------------- /Example/Tests/AudioTrackReader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AudioTrackReader.swift 3 | // VoiceActivityDetector 4 | // 5 | // Created by HANAI Tohru on 2019/07/12. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import AVFoundation 10 | 11 | public protocol AudioDataReader: class { 12 | var isActive: Bool { get } 13 | func next() -> CMSampleBuffer? 14 | } 15 | 16 | public class AudioTrackReader: AudioDataReader { 17 | private let reader: AVAssetReader 18 | private let readerOutput: AVAssetReaderTrackOutput 19 | public private(set) var isActive: Bool 20 | 21 | public convenience init(audioPath: String, timeRange: CMTimeRange?, settings: [String : Any]) throws { 22 | let url = URL(fileURLWithPath: audioPath) 23 | try self.init(audioURL: url, timeRange: timeRange, settings: settings) 24 | } 25 | 26 | public convenience init(audioURL: URL, timeRange: CMTimeRange?, settings: [String : Any]) throws { 27 | let asset = AVAsset(url: audioURL) 28 | guard let track = asset.tracks.first else { 29 | fatalError() 30 | } 31 | try self.init(track: track, timeRange: timeRange, settings: settings) 32 | } 33 | 34 | public init(track: AVAssetTrack, timeRange: CMTimeRange?, settings: [String : Any]) throws { 35 | reader = try AVAssetReader(asset: track.asset!) 36 | 37 | if let timeRange = timeRange { 38 | reader.timeRange = timeRange 39 | } 40 | 41 | readerOutput = AVAssetReaderTrackOutput(track: track, outputSettings: settings) 42 | guard reader.canAdd(readerOutput) else { 43 | fatalError() 44 | } 45 | reader.add(readerOutput) 46 | 47 | reader.startReading() 48 | isActive = true 49 | } 50 | 51 | public func next() -> CMSampleBuffer? { 52 | guard 53 | isActive, 54 | let sample = readerOutput.copyNextSampleBuffer(), 55 | CMSampleBufferIsValid(sample) 56 | else { 57 | isActive = false 58 | return nil 59 | } 60 | 61 | return sample 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Example/Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Tests/Tests.swift: -------------------------------------------------------------------------------- 1 | // https://github.com/Quick/Quick 2 | 3 | import Quick 4 | import Nimble 5 | import AVFoundation 6 | import VoiceActivityDetector 7 | 8 | class VoiceActivityDetectorSpec: QuickSpec { 9 | lazy var zeroData = Data(count: 2880) 10 | lazy var zero = zeroData.withUnsafeBytes { $0.baseAddress!.assumingMemoryBound(to: Int16.self) } 11 | 12 | override func spec() { 13 | describe("agressiveness") { 14 | it("should accept any of predefined value") { 15 | let detector = VoiceActivityDetector()! 16 | detector.agressiveness = .veryAggressive 17 | expect(detector.agressiveness) == .veryAggressive 18 | detector.agressiveness = .aggressive 19 | expect(detector.agressiveness) == .aggressive 20 | detector.agressiveness = .lowBitRate 21 | expect(detector.agressiveness) == .lowBitRate 22 | detector.agressiveness = .quality 23 | expect(detector.agressiveness) == .quality 24 | } 25 | } 26 | 27 | describe("sampleRate") { 28 | it("should accept known sample rates") { 29 | let detector = VoiceActivityDetector()! 30 | detector.sampleRate = 48000 31 | expect(detector.sampleRate) == 48000 32 | detector.sampleRate = 32000 33 | expect(detector.sampleRate) == 32000 34 | detector.sampleRate = 16000 35 | expect(detector.sampleRate) == 16000 36 | detector.sampleRate = 8000 37 | expect(detector.sampleRate) == 8000 38 | } 39 | 40 | #if targetEnvironment(simulator) 41 | it("should signal assert with unknown sample rates") { 42 | let detector = VoiceActivityDetector()! 43 | expect { detector.sampleRate = 8001 }.to(throwAssertion()) 44 | } 45 | #endif 46 | } 47 | 48 | describe("detect") { 49 | it("detects") { 50 | let detector = VoiceActivityDetector()! 51 | detector.sampleRate = 8000 52 | expect(detector.detect(frames: self.zero, count: 80)) == .inActiveVoice 53 | expect(detector.detect(frames: self.zero, lengthInMilliSec: 10)) == .inActiveVoice 54 | } 55 | 56 | #if targetEnvironment(simulator) 57 | it("should fail with inacceptable frame count") { 58 | let detector = VoiceActivityDetector()! 59 | expect { _ = detector.detect(frames: self.zero, count: 81) }.to(throwAssertion()) 60 | } 61 | #endif 62 | } 63 | 64 | describe("detect(sampleBuffer, ...)") { 65 | it("does") { 66 | let settings: [String : Any] = [ 67 | AVFormatIDKey: Int(kAudioFormatLinearPCM), 68 | AVLinearPCMBitDepthKey: 16, 69 | AVLinearPCMIsBigEndianKey: false, 70 | AVLinearPCMIsFloatKey: false, 71 | AVLinearPCMIsNonInterleaved: false, 72 | AVNumberOfChannelsKey: 1, 73 | AVSampleRateKey: 8000, 74 | ] 75 | let path = Bundle.main.path(forResource: "3722", ofType: "mp3")! 76 | let reader = try! AudioTrackReader(audioPath: path, timeRange: nil, settings: settings) 77 | 78 | CMSampleBufferInvalidate(reader.next()!) // skip the first iteration 79 | let sampleBuffer = reader.next()! 80 | 81 | expect(sampleBuffer).notTo(beNil()) 82 | 83 | let detector = VoiceActivityDetector()! 84 | guard let activities = detector.detect(sampleBuffer: sampleBuffer, byEachMilliSec: 10, duration: 30) else { 85 | fail() 86 | return 87 | } 88 | 89 | let presentationTimeStamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer) 90 | expect(presentationTimeStamp.seconds) > 0 91 | 92 | expect(activities.count) == 3 93 | expect(activities[2].timestamp) == 20 94 | expect(activities[2].presentationTimestamp.seconds) == presentationTimeStamp.seconds + 0.020 95 | CMSampleBufferInvalidate(sampleBuffer) 96 | } 97 | } 98 | } 99 | } 100 | 101 | -------------------------------------------------------------------------------- /Example/VoiceActivityDetector.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 076EA1A022D8D4BB008A61CC /* 3722.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 076EA19F22D8D4BB008A61CC /* 3722.mp3 */; }; 11 | 076EA1A122D8D4BB008A61CC /* 3722.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 076EA19F22D8D4BB008A61CC /* 3722.mp3 */; }; 12 | 076EA1A322D8D98F008A61CC /* AudioTrackReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 076EA1A222D8D98F008A61CC /* AudioTrackReader.swift */; }; 13 | 076EA1A422D8D98F008A61CC /* AudioTrackReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 076EA1A222D8D98F008A61CC /* AudioTrackReader.swift */; }; 14 | 23B8DCB2F9202A6FE149A770 /* Pods_VoiceActivityDetector_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 332B692F0BC4E701B4861634 /* Pods_VoiceActivityDetector_Tests.framework */; }; 15 | 2D4EDDC414D3B2BE6E1AC873 /* Pods_VoiceActivityDetector_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6E978FD693FFD07CE45D2EB4 /* Pods_VoiceActivityDetector_Example.framework */; }; 16 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 17 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; }; 18 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; }; 19 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 20 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 21 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXContainerItemProxy section */ 25 | 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */ = { 26 | isa = PBXContainerItemProxy; 27 | containerPortal = 607FACC81AFB9204008FA782 /* Project object */; 28 | proxyType = 1; 29 | remoteGlobalIDString = 607FACCF1AFB9204008FA782; 30 | remoteInfo = VoiceActivityDetector; 31 | }; 32 | /* End PBXContainerItemProxy section */ 33 | 34 | /* Begin PBXFileReference section */ 35 | 076EA19F22D8D4BB008A61CC /* 3722.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = 3722.mp3; sourceTree = ""; }; 36 | 076EA1A222D8D98F008A61CC /* AudioTrackReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioTrackReader.swift; sourceTree = ""; }; 37 | 0B60CC7736E8DD31F4C6BB44 /* Pods-VoiceActivityDetector_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VoiceActivityDetector_Tests.debug.xcconfig"; path = "Target Support Files/Pods-VoiceActivityDetector_Tests/Pods-VoiceActivityDetector_Tests.debug.xcconfig"; sourceTree = ""; }; 38 | 183EA7A9F709717463CEED3A /* Pods-VoiceActivityDetector_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VoiceActivityDetector_Example.release.xcconfig"; path = "Target Support Files/Pods-VoiceActivityDetector_Example/Pods-VoiceActivityDetector_Example.release.xcconfig"; sourceTree = ""; }; 39 | 2A749F9FE9174D69022AAA82 /* Pods-VoiceActivityDetector_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VoiceActivityDetector_Tests.release.xcconfig"; path = "Target Support Files/Pods-VoiceActivityDetector_Tests/Pods-VoiceActivityDetector_Tests.release.xcconfig"; sourceTree = ""; }; 40 | 332B692F0BC4E701B4861634 /* Pods_VoiceActivityDetector_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_VoiceActivityDetector_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 607FACD01AFB9204008FA782 /* VoiceActivityDetector_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VoiceActivityDetector_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 43 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 44 | 607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 45 | 607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 46 | 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 47 | 607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 48 | 607FACE51AFB9204008FA782 /* VoiceActivityDetector_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VoiceActivityDetector_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 49 | 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; 51 | 61A00C48A1420232188F85A5 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 52 | 662735B9FB48CFC51F9D8815 /* VoiceActivityDetector.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = VoiceActivityDetector.podspec; path = ../VoiceActivityDetector.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 53 | 6E978FD693FFD07CE45D2EB4 /* Pods_VoiceActivityDetector_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_VoiceActivityDetector_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 6ED6D9A209516773DEEEA66B /* Pods-VoiceActivityDetector_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VoiceActivityDetector_Example.debug.xcconfig"; path = "Target Support Files/Pods-VoiceActivityDetector_Example/Pods-VoiceActivityDetector_Example.debug.xcconfig"; sourceTree = ""; }; 55 | F1C409239B14EF740FFE580F /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 56 | /* End PBXFileReference section */ 57 | 58 | /* Begin PBXFrameworksBuildPhase section */ 59 | 607FACCD1AFB9204008FA782 /* Frameworks */ = { 60 | isa = PBXFrameworksBuildPhase; 61 | buildActionMask = 2147483647; 62 | files = ( 63 | 2D4EDDC414D3B2BE6E1AC873 /* Pods_VoiceActivityDetector_Example.framework in Frameworks */, 64 | ); 65 | runOnlyForDeploymentPostprocessing = 0; 66 | }; 67 | 607FACE21AFB9204008FA782 /* Frameworks */ = { 68 | isa = PBXFrameworksBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | 23B8DCB2F9202A6FE149A770 /* Pods_VoiceActivityDetector_Tests.framework in Frameworks */, 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | /* End PBXFrameworksBuildPhase section */ 76 | 77 | /* Begin PBXGroup section */ 78 | 18374B21B597443E0A6EE2A6 /* Pods */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 6ED6D9A209516773DEEEA66B /* Pods-VoiceActivityDetector_Example.debug.xcconfig */, 82 | 183EA7A9F709717463CEED3A /* Pods-VoiceActivityDetector_Example.release.xcconfig */, 83 | 0B60CC7736E8DD31F4C6BB44 /* Pods-VoiceActivityDetector_Tests.debug.xcconfig */, 84 | 2A749F9FE9174D69022AAA82 /* Pods-VoiceActivityDetector_Tests.release.xcconfig */, 85 | ); 86 | path = Pods; 87 | sourceTree = ""; 88 | }; 89 | 4E83482C57733A424534DA32 /* Frameworks */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | 6E978FD693FFD07CE45D2EB4 /* Pods_VoiceActivityDetector_Example.framework */, 93 | 332B692F0BC4E701B4861634 /* Pods_VoiceActivityDetector_Tests.framework */, 94 | ); 95 | name = Frameworks; 96 | sourceTree = ""; 97 | }; 98 | 607FACC71AFB9204008FA782 = { 99 | isa = PBXGroup; 100 | children = ( 101 | 607FACF51AFB993E008FA782 /* Podspec Metadata */, 102 | 607FACD21AFB9204008FA782 /* Example for VoiceActivityDetector */, 103 | 607FACE81AFB9204008FA782 /* Tests */, 104 | 607FACD11AFB9204008FA782 /* Products */, 105 | 18374B21B597443E0A6EE2A6 /* Pods */, 106 | 4E83482C57733A424534DA32 /* Frameworks */, 107 | ); 108 | sourceTree = ""; 109 | }; 110 | 607FACD11AFB9204008FA782 /* Products */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 607FACD01AFB9204008FA782 /* VoiceActivityDetector_Example.app */, 114 | 607FACE51AFB9204008FA782 /* VoiceActivityDetector_Tests.xctest */, 115 | ); 116 | name = Products; 117 | sourceTree = ""; 118 | }; 119 | 607FACD21AFB9204008FA782 /* Example for VoiceActivityDetector */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */, 123 | 607FACD71AFB9204008FA782 /* ViewController.swift */, 124 | 607FACD91AFB9204008FA782 /* Main.storyboard */, 125 | 607FACDC1AFB9204008FA782 /* Images.xcassets */, 126 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, 127 | 607FACD31AFB9204008FA782 /* Supporting Files */, 128 | ); 129 | name = "Example for VoiceActivityDetector"; 130 | path = VoiceActivityDetector; 131 | sourceTree = ""; 132 | }; 133 | 607FACD31AFB9204008FA782 /* Supporting Files */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 607FACD41AFB9204008FA782 /* Info.plist */, 137 | ); 138 | name = "Supporting Files"; 139 | sourceTree = ""; 140 | }; 141 | 607FACE81AFB9204008FA782 /* Tests */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | 076EA1A222D8D98F008A61CC /* AudioTrackReader.swift */, 145 | 076EA19F22D8D4BB008A61CC /* 3722.mp3 */, 146 | 607FACEB1AFB9204008FA782 /* Tests.swift */, 147 | 607FACE91AFB9204008FA782 /* Supporting Files */, 148 | ); 149 | path = Tests; 150 | sourceTree = ""; 151 | }; 152 | 607FACE91AFB9204008FA782 /* Supporting Files */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | 607FACEA1AFB9204008FA782 /* Info.plist */, 156 | ); 157 | name = "Supporting Files"; 158 | sourceTree = ""; 159 | }; 160 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | 662735B9FB48CFC51F9D8815 /* VoiceActivityDetector.podspec */, 164 | 61A00C48A1420232188F85A5 /* README.md */, 165 | F1C409239B14EF740FFE580F /* LICENSE */, 166 | ); 167 | name = "Podspec Metadata"; 168 | sourceTree = ""; 169 | }; 170 | /* End PBXGroup section */ 171 | 172 | /* Begin PBXNativeTarget section */ 173 | 607FACCF1AFB9204008FA782 /* VoiceActivityDetector_Example */ = { 174 | isa = PBXNativeTarget; 175 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "VoiceActivityDetector_Example" */; 176 | buildPhases = ( 177 | 7B660A898DB889ACC6ED7BD6 /* [CP] Check Pods Manifest.lock */, 178 | 607FACCC1AFB9204008FA782 /* Sources */, 179 | 607FACCD1AFB9204008FA782 /* Frameworks */, 180 | 607FACCE1AFB9204008FA782 /* Resources */, 181 | 7E95780EB2534BC7C026EC15 /* [CP] Embed Pods Frameworks */, 182 | ); 183 | buildRules = ( 184 | ); 185 | dependencies = ( 186 | ); 187 | name = VoiceActivityDetector_Example; 188 | productName = VoiceActivityDetector; 189 | productReference = 607FACD01AFB9204008FA782 /* VoiceActivityDetector_Example.app */; 190 | productType = "com.apple.product-type.application"; 191 | }; 192 | 607FACE41AFB9204008FA782 /* VoiceActivityDetector_Tests */ = { 193 | isa = PBXNativeTarget; 194 | buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "VoiceActivityDetector_Tests" */; 195 | buildPhases = ( 196 | A74D88808E6285A75142149C /* [CP] Check Pods Manifest.lock */, 197 | 607FACE11AFB9204008FA782 /* Sources */, 198 | 607FACE21AFB9204008FA782 /* Frameworks */, 199 | 607FACE31AFB9204008FA782 /* Resources */, 200 | 265F3DA040EF62FD1B950D18 /* [CP] Embed Pods Frameworks */, 201 | ); 202 | buildRules = ( 203 | ); 204 | dependencies = ( 205 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */, 206 | ); 207 | name = VoiceActivityDetector_Tests; 208 | productName = Tests; 209 | productReference = 607FACE51AFB9204008FA782 /* VoiceActivityDetector_Tests.xctest */; 210 | productType = "com.apple.product-type.bundle.unit-test"; 211 | }; 212 | /* End PBXNativeTarget section */ 213 | 214 | /* Begin PBXProject section */ 215 | 607FACC81AFB9204008FA782 /* Project object */ = { 216 | isa = PBXProject; 217 | attributes = { 218 | LastSwiftUpdateCheck = 0830; 219 | LastUpgradeCheck = 0830; 220 | ORGANIZATIONNAME = CocoaPods; 221 | TargetAttributes = { 222 | 607FACCF1AFB9204008FA782 = { 223 | CreatedOnToolsVersion = 6.3.1; 224 | DevelopmentTeam = R6HMM3C9D7; 225 | LastSwiftMigration = 0900; 226 | }; 227 | 607FACE41AFB9204008FA782 = { 228 | CreatedOnToolsVersion = 6.3.1; 229 | DevelopmentTeam = R6HMM3C9D7; 230 | LastSwiftMigration = 0900; 231 | TestTargetID = 607FACCF1AFB9204008FA782; 232 | }; 233 | }; 234 | }; 235 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "VoiceActivityDetector" */; 236 | compatibilityVersion = "Xcode 3.2"; 237 | developmentRegion = English; 238 | hasScannedForEncodings = 0; 239 | knownRegions = ( 240 | English, 241 | en, 242 | Base, 243 | ); 244 | mainGroup = 607FACC71AFB9204008FA782; 245 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */; 246 | projectDirPath = ""; 247 | projectRoot = ""; 248 | targets = ( 249 | 607FACCF1AFB9204008FA782 /* VoiceActivityDetector_Example */, 250 | 607FACE41AFB9204008FA782 /* VoiceActivityDetector_Tests */, 251 | ); 252 | }; 253 | /* End PBXProject section */ 254 | 255 | /* Begin PBXResourcesBuildPhase section */ 256 | 607FACCE1AFB9204008FA782 /* Resources */ = { 257 | isa = PBXResourcesBuildPhase; 258 | buildActionMask = 2147483647; 259 | files = ( 260 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */, 261 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */, 262 | 076EA1A022D8D4BB008A61CC /* 3722.mp3 in Resources */, 263 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */, 264 | ); 265 | runOnlyForDeploymentPostprocessing = 0; 266 | }; 267 | 607FACE31AFB9204008FA782 /* Resources */ = { 268 | isa = PBXResourcesBuildPhase; 269 | buildActionMask = 2147483647; 270 | files = ( 271 | 076EA1A122D8D4BB008A61CC /* 3722.mp3 in Resources */, 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | }; 275 | /* End PBXResourcesBuildPhase section */ 276 | 277 | /* Begin PBXShellScriptBuildPhase section */ 278 | 265F3DA040EF62FD1B950D18 /* [CP] Embed Pods Frameworks */ = { 279 | isa = PBXShellScriptBuildPhase; 280 | buildActionMask = 2147483647; 281 | files = ( 282 | ); 283 | inputPaths = ( 284 | "${PODS_ROOT}/Target Support Files/Pods-VoiceActivityDetector_Tests/Pods-VoiceActivityDetector_Tests-frameworks.sh", 285 | "${BUILT_PRODUCTS_DIR}/Nimble/Nimble.framework", 286 | "${BUILT_PRODUCTS_DIR}/Quick/Quick.framework", 287 | ); 288 | name = "[CP] Embed Pods Frameworks"; 289 | outputPaths = ( 290 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimble.framework", 291 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Quick.framework", 292 | ); 293 | runOnlyForDeploymentPostprocessing = 0; 294 | shellPath = /bin/sh; 295 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-VoiceActivityDetector_Tests/Pods-VoiceActivityDetector_Tests-frameworks.sh\"\n"; 296 | showEnvVarsInLog = 0; 297 | }; 298 | 7B660A898DB889ACC6ED7BD6 /* [CP] Check Pods Manifest.lock */ = { 299 | isa = PBXShellScriptBuildPhase; 300 | buildActionMask = 2147483647; 301 | files = ( 302 | ); 303 | inputFileListPaths = ( 304 | ); 305 | inputPaths = ( 306 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 307 | "${PODS_ROOT}/Manifest.lock", 308 | ); 309 | name = "[CP] Check Pods Manifest.lock"; 310 | outputFileListPaths = ( 311 | ); 312 | outputPaths = ( 313 | "$(DERIVED_FILE_DIR)/Pods-VoiceActivityDetector_Example-checkManifestLockResult.txt", 314 | ); 315 | runOnlyForDeploymentPostprocessing = 0; 316 | shellPath = /bin/sh; 317 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 318 | showEnvVarsInLog = 0; 319 | }; 320 | 7E95780EB2534BC7C026EC15 /* [CP] Embed Pods Frameworks */ = { 321 | isa = PBXShellScriptBuildPhase; 322 | buildActionMask = 2147483647; 323 | files = ( 324 | ); 325 | inputPaths = ( 326 | "${PODS_ROOT}/Target Support Files/Pods-VoiceActivityDetector_Example/Pods-VoiceActivityDetector_Example-frameworks.sh", 327 | "${BUILT_PRODUCTS_DIR}/VoiceActivityDetector/VoiceActivityDetector.framework", 328 | "${BUILT_PRODUCTS_DIR}/libfvad/libfvad.framework", 329 | ); 330 | name = "[CP] Embed Pods Frameworks"; 331 | outputPaths = ( 332 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/VoiceActivityDetector.framework", 333 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libfvad.framework", 334 | ); 335 | runOnlyForDeploymentPostprocessing = 0; 336 | shellPath = /bin/sh; 337 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-VoiceActivityDetector_Example/Pods-VoiceActivityDetector_Example-frameworks.sh\"\n"; 338 | showEnvVarsInLog = 0; 339 | }; 340 | A74D88808E6285A75142149C /* [CP] Check Pods Manifest.lock */ = { 341 | isa = PBXShellScriptBuildPhase; 342 | buildActionMask = 2147483647; 343 | files = ( 344 | ); 345 | inputFileListPaths = ( 346 | ); 347 | inputPaths = ( 348 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 349 | "${PODS_ROOT}/Manifest.lock", 350 | ); 351 | name = "[CP] Check Pods Manifest.lock"; 352 | outputFileListPaths = ( 353 | ); 354 | outputPaths = ( 355 | "$(DERIVED_FILE_DIR)/Pods-VoiceActivityDetector_Tests-checkManifestLockResult.txt", 356 | ); 357 | runOnlyForDeploymentPostprocessing = 0; 358 | shellPath = /bin/sh; 359 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 360 | showEnvVarsInLog = 0; 361 | }; 362 | /* End PBXShellScriptBuildPhase section */ 363 | 364 | /* Begin PBXSourcesBuildPhase section */ 365 | 607FACCC1AFB9204008FA782 /* Sources */ = { 366 | isa = PBXSourcesBuildPhase; 367 | buildActionMask = 2147483647; 368 | files = ( 369 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */, 370 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, 371 | 076EA1A322D8D98F008A61CC /* AudioTrackReader.swift in Sources */, 372 | ); 373 | runOnlyForDeploymentPostprocessing = 0; 374 | }; 375 | 607FACE11AFB9204008FA782 /* Sources */ = { 376 | isa = PBXSourcesBuildPhase; 377 | buildActionMask = 2147483647; 378 | files = ( 379 | 076EA1A422D8D98F008A61CC /* AudioTrackReader.swift in Sources */, 380 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */, 381 | ); 382 | runOnlyForDeploymentPostprocessing = 0; 383 | }; 384 | /* End PBXSourcesBuildPhase section */ 385 | 386 | /* Begin PBXTargetDependency section */ 387 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */ = { 388 | isa = PBXTargetDependency; 389 | target = 607FACCF1AFB9204008FA782 /* VoiceActivityDetector_Example */; 390 | targetProxy = 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */; 391 | }; 392 | /* End PBXTargetDependency section */ 393 | 394 | /* Begin PBXVariantGroup section */ 395 | 607FACD91AFB9204008FA782 /* Main.storyboard */ = { 396 | isa = PBXVariantGroup; 397 | children = ( 398 | 607FACDA1AFB9204008FA782 /* Base */, 399 | ); 400 | name = Main.storyboard; 401 | sourceTree = ""; 402 | }; 403 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = { 404 | isa = PBXVariantGroup; 405 | children = ( 406 | 607FACDF1AFB9204008FA782 /* Base */, 407 | ); 408 | name = LaunchScreen.xib; 409 | sourceTree = ""; 410 | }; 411 | /* End PBXVariantGroup section */ 412 | 413 | /* Begin XCBuildConfiguration section */ 414 | 607FACED1AFB9204008FA782 /* Debug */ = { 415 | isa = XCBuildConfiguration; 416 | buildSettings = { 417 | ALWAYS_SEARCH_USER_PATHS = NO; 418 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 419 | CLANG_CXX_LIBRARY = "libc++"; 420 | CLANG_ENABLE_MODULES = YES; 421 | CLANG_ENABLE_OBJC_ARC = YES; 422 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 423 | CLANG_WARN_BOOL_CONVERSION = YES; 424 | CLANG_WARN_COMMA = YES; 425 | CLANG_WARN_CONSTANT_CONVERSION = YES; 426 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 427 | CLANG_WARN_EMPTY_BODY = YES; 428 | CLANG_WARN_ENUM_CONVERSION = YES; 429 | CLANG_WARN_INFINITE_RECURSION = YES; 430 | CLANG_WARN_INT_CONVERSION = YES; 431 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 432 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 433 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 434 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 435 | CLANG_WARN_STRICT_PROTOTYPES = YES; 436 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 437 | CLANG_WARN_UNREACHABLE_CODE = YES; 438 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 439 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 440 | COPY_PHASE_STRIP = NO; 441 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 442 | ENABLE_STRICT_OBJC_MSGSEND = YES; 443 | ENABLE_TESTABILITY = YES; 444 | GCC_C_LANGUAGE_STANDARD = gnu99; 445 | GCC_DYNAMIC_NO_PIC = NO; 446 | GCC_NO_COMMON_BLOCKS = YES; 447 | GCC_OPTIMIZATION_LEVEL = 0; 448 | GCC_PREPROCESSOR_DEFINITIONS = ( 449 | "DEBUG=1", 450 | "$(inherited)", 451 | ); 452 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 453 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 454 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 455 | GCC_WARN_UNDECLARED_SELECTOR = YES; 456 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 457 | GCC_WARN_UNUSED_FUNCTION = YES; 458 | GCC_WARN_UNUSED_VARIABLE = YES; 459 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 460 | MTL_ENABLE_DEBUG_INFO = YES; 461 | ONLY_ACTIVE_ARCH = YES; 462 | SDKROOT = iphoneos; 463 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 464 | }; 465 | name = Debug; 466 | }; 467 | 607FACEE1AFB9204008FA782 /* Release */ = { 468 | isa = XCBuildConfiguration; 469 | buildSettings = { 470 | ALWAYS_SEARCH_USER_PATHS = NO; 471 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 472 | CLANG_CXX_LIBRARY = "libc++"; 473 | CLANG_ENABLE_MODULES = YES; 474 | CLANG_ENABLE_OBJC_ARC = YES; 475 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 476 | CLANG_WARN_BOOL_CONVERSION = YES; 477 | CLANG_WARN_COMMA = YES; 478 | CLANG_WARN_CONSTANT_CONVERSION = YES; 479 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 480 | CLANG_WARN_EMPTY_BODY = YES; 481 | CLANG_WARN_ENUM_CONVERSION = YES; 482 | CLANG_WARN_INFINITE_RECURSION = YES; 483 | CLANG_WARN_INT_CONVERSION = YES; 484 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 485 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 486 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 487 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 488 | CLANG_WARN_STRICT_PROTOTYPES = YES; 489 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 490 | CLANG_WARN_UNREACHABLE_CODE = YES; 491 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 492 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 493 | COPY_PHASE_STRIP = NO; 494 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 495 | ENABLE_NS_ASSERTIONS = NO; 496 | ENABLE_STRICT_OBJC_MSGSEND = YES; 497 | GCC_C_LANGUAGE_STANDARD = gnu99; 498 | GCC_NO_COMMON_BLOCKS = YES; 499 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 500 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 501 | GCC_WARN_UNDECLARED_SELECTOR = YES; 502 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 503 | GCC_WARN_UNUSED_FUNCTION = YES; 504 | GCC_WARN_UNUSED_VARIABLE = YES; 505 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 506 | MTL_ENABLE_DEBUG_INFO = NO; 507 | SDKROOT = iphoneos; 508 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 509 | VALIDATE_PRODUCT = YES; 510 | }; 511 | name = Release; 512 | }; 513 | 607FACF01AFB9204008FA782 /* Debug */ = { 514 | isa = XCBuildConfiguration; 515 | baseConfigurationReference = 6ED6D9A209516773DEEEA66B /* Pods-VoiceActivityDetector_Example.debug.xcconfig */; 516 | buildSettings = { 517 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 518 | DEVELOPMENT_TEAM = R6HMM3C9D7; 519 | INFOPLIST_FILE = VoiceActivityDetector/Info.plist; 520 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 521 | MODULE_NAME = ExampleApp; 522 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 523 | PRODUCT_NAME = "$(TARGET_NAME)"; 524 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 525 | SWIFT_VERSION = 5.0; 526 | }; 527 | name = Debug; 528 | }; 529 | 607FACF11AFB9204008FA782 /* Release */ = { 530 | isa = XCBuildConfiguration; 531 | baseConfigurationReference = 183EA7A9F709717463CEED3A /* Pods-VoiceActivityDetector_Example.release.xcconfig */; 532 | buildSettings = { 533 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 534 | DEVELOPMENT_TEAM = R6HMM3C9D7; 535 | INFOPLIST_FILE = VoiceActivityDetector/Info.plist; 536 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 537 | MODULE_NAME = ExampleApp; 538 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 539 | PRODUCT_NAME = "$(TARGET_NAME)"; 540 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 541 | SWIFT_VERSION = 5.0; 542 | }; 543 | name = Release; 544 | }; 545 | 607FACF31AFB9204008FA782 /* Debug */ = { 546 | isa = XCBuildConfiguration; 547 | baseConfigurationReference = 0B60CC7736E8DD31F4C6BB44 /* Pods-VoiceActivityDetector_Tests.debug.xcconfig */; 548 | buildSettings = { 549 | DEVELOPMENT_TEAM = R6HMM3C9D7; 550 | FRAMEWORK_SEARCH_PATHS = ( 551 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 552 | "$(inherited)", 553 | ); 554 | GCC_PREPROCESSOR_DEFINITIONS = ( 555 | "DEBUG=1", 556 | "$(inherited)", 557 | ); 558 | INFOPLIST_FILE = Tests/Info.plist; 559 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 560 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 561 | PRODUCT_NAME = "$(TARGET_NAME)"; 562 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 563 | SWIFT_VERSION = 4.0; 564 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VoiceActivityDetector_Example.app/VoiceActivityDetector_Example"; 565 | }; 566 | name = Debug; 567 | }; 568 | 607FACF41AFB9204008FA782 /* Release */ = { 569 | isa = XCBuildConfiguration; 570 | baseConfigurationReference = 2A749F9FE9174D69022AAA82 /* Pods-VoiceActivityDetector_Tests.release.xcconfig */; 571 | buildSettings = { 572 | DEVELOPMENT_TEAM = R6HMM3C9D7; 573 | FRAMEWORK_SEARCH_PATHS = ( 574 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 575 | "$(inherited)", 576 | ); 577 | INFOPLIST_FILE = Tests/Info.plist; 578 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 579 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 580 | PRODUCT_NAME = "$(TARGET_NAME)"; 581 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 582 | SWIFT_VERSION = 4.0; 583 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VoiceActivityDetector_Example.app/VoiceActivityDetector_Example"; 584 | }; 585 | name = Release; 586 | }; 587 | /* End XCBuildConfiguration section */ 588 | 589 | /* Begin XCConfigurationList section */ 590 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "VoiceActivityDetector" */ = { 591 | isa = XCConfigurationList; 592 | buildConfigurations = ( 593 | 607FACED1AFB9204008FA782 /* Debug */, 594 | 607FACEE1AFB9204008FA782 /* Release */, 595 | ); 596 | defaultConfigurationIsVisible = 0; 597 | defaultConfigurationName = Release; 598 | }; 599 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "VoiceActivityDetector_Example" */ = { 600 | isa = XCConfigurationList; 601 | buildConfigurations = ( 602 | 607FACF01AFB9204008FA782 /* Debug */, 603 | 607FACF11AFB9204008FA782 /* Release */, 604 | ); 605 | defaultConfigurationIsVisible = 0; 606 | defaultConfigurationName = Release; 607 | }; 608 | 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "VoiceActivityDetector_Tests" */ = { 609 | isa = XCConfigurationList; 610 | buildConfigurations = ( 611 | 607FACF31AFB9204008FA782 /* Debug */, 612 | 607FACF41AFB9204008FA782 /* Release */, 613 | ); 614 | defaultConfigurationIsVisible = 0; 615 | defaultConfigurationName = Release; 616 | }; 617 | /* End XCConfigurationList section */ 618 | }; 619 | rootObject = 607FACC81AFB9204008FA782 /* Project object */; 620 | } 621 | -------------------------------------------------------------------------------- /Example/VoiceActivityDetector.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/VoiceActivityDetector.xcodeproj/xcshareddata/xcschemes/VoiceActivityDetector-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 80 | 82 | 88 | 89 | 90 | 91 | 92 | 93 | 99 | 101 | 107 | 108 | 109 | 110 | 112 | 113 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /Example/VoiceActivityDetector.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/VoiceActivityDetector.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/VoiceActivityDetector/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // VoiceActivityDetector 4 | // 5 | // Created by reedom on 07/12/2019. 6 | // Copyright (c) 2019 reedom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | internal func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Example/VoiceActivityDetector/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Example/VoiceActivityDetector/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 41 | 47 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /Example/VoiceActivityDetector/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Example/VoiceActivityDetector/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | 38 | NSMicrophoneUsageDescription 39 | Capture mic to demonstrate Voice Active Detection 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/VoiceActivityDetector/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // VoiceActivityDetector 4 | // 5 | // Created by reedom on 07/12/2019. 6 | // Copyright (c) 2019 reedom. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AVFoundation.AVFAudio 11 | import VoiceActivityDetector 12 | 13 | class ViewController: UIViewController { 14 | @IBOutlet var toggleButton: UIButton! 15 | @IBOutlet var stateLabel: UILabel! 16 | @IBOutlet var agressivenessLabel: UILabel! 17 | 18 | let voiceActivityDetectDuration = 30 // ms 19 | let voiceActivityDetector = VoiceActivityDetector(agressiveness: .quality)! 20 | var voiceActivity: VoiceActivityDetector.VoiceActivity? { 21 | didSet { 22 | guard oldValue != voiceActivity else { return } 23 | 24 | if let voiceActivity = voiceActivity { 25 | switch voiceActivity { 26 | case .activeVoice: 27 | stateLabel.textColor = .white 28 | stateLabel.backgroundColor = .red 29 | stateLabel.text = "Active" 30 | case .inActiveVoice: 31 | stateLabel.textColor = .black 32 | stateLabel.backgroundColor = .green 33 | stateLabel.text = "inactive" 34 | } 35 | } else { 36 | stateLabel.text = "---" 37 | stateLabel.textColor = .black 38 | stateLabel.backgroundColor = .white 39 | } 40 | } 41 | } 42 | 43 | var isMicrophoneActive = false 44 | var audioBuffers = [AudioQueueBufferRef]() 45 | var audioQueue: AudioQueueRef? 46 | 47 | var audioStreamDescription = AudioStreamBasicDescription( 48 | mSampleRate: 8000, 49 | mFormatID: kAudioFormatLinearPCM, 50 | mFormatFlags: kAudioFormatFlagIsSignedInteger, 51 | mBytesPerPacket: 2, 52 | mFramesPerPacket: 1, 53 | mBytesPerFrame: 2, 54 | mChannelsPerFrame: 1, 55 | mBitsPerChannel: 16, 56 | mReserved: 0) 57 | 58 | override func viewDidLoad() { 59 | super.viewDidLoad() 60 | agressivenessLabel.text = voiceActivityDetector.agressiveness.description 61 | #if targetEnvironment(simulator) 62 | toggleButton.isEnabled = false 63 | #else 64 | setupAudioRecording() 65 | #endif 66 | } 67 | 68 | @IBAction func didTapToggleSession() { 69 | if isMicrophoneActive { 70 | deactivateMicrophone() 71 | voiceActivity = nil 72 | toggleButton.setTitle("Aactivate Mic", for: .normal) 73 | } else { 74 | activateMicrophone() 75 | if isMicrophoneActive { 76 | toggleButton.setTitle("Deactivate Mic", for: .normal) 77 | } 78 | } 79 | } 80 | 81 | @IBAction func didChangeAgressivenessValue(_ sender: UISlider) { 82 | let i = Int32(round(sender.value)) 83 | sender.value = Float(i) 84 | 85 | let agressiveness = VoiceActivityDetector.DetectionAggressiveness.init(rawValue: i)! 86 | voiceActivityDetector.agressiveness = agressiveness 87 | agressivenessLabel.text = agressiveness.description 88 | } 89 | 90 | override func didReceiveMemoryWarning() { 91 | super.didReceiveMemoryWarning() 92 | // Dispose of any resources that can be recreated. 93 | } 94 | } 95 | 96 | extension ViewController { 97 | func setupAudioRecording() { 98 | let callback: AudioQueueInputCallback = { ( 99 | inUserData: UnsafeMutableRawPointer?, 100 | inAQ: AudioQueueRef, 101 | inBuffer: AudioQueueBufferRef, 102 | inStartTime: UnsafePointer, 103 | inNumberPacketDescriptions: UInt32, 104 | inPacketDescs: UnsafePointer? 105 | ) in 106 | guard let inUserData = inUserData else { return } 107 | 108 | let myself = Unmanaged.fromOpaque(inUserData).takeUnretainedValue() 109 | guard myself.isMicrophoneActive else { return } 110 | 111 | myself.didReceivceSampleBuffer(buffer: inBuffer.pointee) 112 | 113 | let err = AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, nil) 114 | if (err != noErr) { 115 | NSLog("AudioQueueEnqueueBuffer failed with error (\(err))"); 116 | AudioQueueFreeBuffer(inAQ, inBuffer) 117 | } 118 | } 119 | 120 | let err = AudioQueueNewInput(&audioStreamDescription, 121 | callback, 122 | Unmanaged.passUnretained(self).toOpaque(), 123 | nil, nil, 0, &audioQueue) 124 | if err != noErr { 125 | fatalError("Unable to create new output audio queue (\(err))") 126 | } 127 | } 128 | 129 | func activateMicrophone() { 130 | guard let audioQueue = audioQueue else { return } 131 | 132 | let audioSession = AVAudioSession.sharedInstance() 133 | do { 134 | try audioSession.setCategory(AVAudioSession.Category.record) 135 | try audioSession.setActive(true) 136 | try audioSession.setPreferredSampleRate(audioStreamDescription.mSampleRate) 137 | 138 | enqueueBuffers() 139 | 140 | let err = AudioQueueStart(audioQueue, nil) 141 | if err == noErr { 142 | isMicrophoneActive = true 143 | } else { 144 | NSLog("AudioQueueStart failed with error (\(err))"); 145 | } 146 | } catch { 147 | print(error.localizedDescription) 148 | dequeueBuffers() 149 | } 150 | } 151 | 152 | func deactivateMicrophone() { 153 | isMicrophoneActive = false 154 | guard let audioQueue = audioQueue else { return } 155 | 156 | let err = AudioQueueStop(audioQueue, true) 157 | if err != noErr { 158 | NSLog("AudioQueueStop failed with error (\(err))"); 159 | } 160 | 161 | dequeueBuffers() 162 | } 163 | 164 | func enqueueBuffers() { 165 | guard let audioQueue = audioQueue else { return } 166 | 167 | let format = audioStreamDescription 168 | let bufferSize = UInt32(format.mSampleRate) * UInt32(format.mBytesPerFrame) / 1000 * UInt32(VoiceActivityDetector.Duration.msec30.rawValue) 169 | for _ in 0 ..< 3 { 170 | var buffer: AudioQueueBufferRef? 171 | var err = AudioQueueAllocateBuffer(audioQueue, bufferSize, &buffer) 172 | if (err != noErr) { 173 | NSLog("Failed to allocate buffer for audio recording (\(err))") 174 | continue 175 | } 176 | 177 | err = AudioQueueEnqueueBuffer(audioQueue, buffer!, 0, nil) 178 | if (err != noErr) { 179 | NSLog("Failed to enqueue buffer for audio recording (\(err))") 180 | } 181 | 182 | audioBuffers.append(buffer!) 183 | } 184 | } 185 | 186 | func dequeueBuffers() { 187 | guard let audioQueue = audioQueue else { return } 188 | while let buffer = audioBuffers.popLast() { 189 | AudioQueueFreeBuffer(audioQueue, buffer) 190 | } 191 | } 192 | } 193 | 194 | extension ViewController { 195 | 196 | func didReceivceSampleBuffer(buffer: AudioQueueBuffer) { 197 | let frames = buffer.mAudioData.assumingMemoryBound(to: Int16.self) 198 | var count = Int(buffer.mAudioDataByteSize) / MemoryLayout.size 199 | let detectorFrameUnit = Int(audioStreamDescription.mSampleRate) * VoiceActivityDetector.Duration.msec10.rawValue / 1000 200 | count = count - (count % detectorFrameUnit) 201 | guard 0 < count else { return } 202 | 203 | let voiceActivity = voiceActivityDetector.detect(frames: frames, count: count) 204 | DispatchQueue.main.async { 205 | self.voiceActivity = voiceActivity 206 | } 207 | } 208 | } 209 | 210 | extension VoiceActivityDetector.DetectionAggressiveness { 211 | var description: String { 212 | switch self { 213 | case .quality: 214 | return "quality" 215 | case .lowBitRate: 216 | return "lowBitRate" 217 | case .aggressive: 218 | return "aggressive" 219 | case .veryAggressive: 220 | return "veryAggressive" 221 | } 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 reedom 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebRTC based voice activity detection 2 | 3 | [![CI Status](https://img.shields.io/travis/reedom/VoiceActivityDetector.svg?style=flat)](https://travis-ci.org/reedom/VoiceActivityDetector) 4 | [![Version](https://img.shields.io/cocoapods/v/VoiceActivityDetector.svg?style=flat)](https://cocoapods.org/pods/VoiceActivityDetector) 5 | [![License](https://img.shields.io/cocoapods/l/VoiceActivityDetector.svg?style=flat)](https://cocoapods.org/pods/VoiceActivityDetector) 6 | [![Platform](https://img.shields.io/cocoapods/p/VoiceActivityDetector.svg?style=flat)](https://cocoapods.org/pods/VoiceActivityDetector) 7 | 8 | This is a Swift/Objective-C interface to the WebRTC Voice Activity Detector (VAD). 9 | 10 | A VAD classifies a piece of audio data as being voiced or unvoiced. It can be useful for telephony and speech recognition. 11 | 12 | The VAD that Google developed for the WebRTC project is reportedly one of the best available, being fast, modern and free. 13 | 14 | ## Sample data format 15 | 16 | The VAD engine simply work only with singed 16 bit, single channel PCM. 17 | 18 | Supported bitrates are: 19 | - 8000Hz 20 | - 16000Hz 21 | - 32000Hz 22 | - 48000Hz 23 | 24 | Note that internally all processing will be done 8000Hz. 25 | input data in higher sample rates will just be downsampled first. 26 | 27 | ## Usage 28 | 29 | ```swift 30 | import VoiceActivityDetector 31 | 32 | let voiceActivityDetector = VoiceActivityDetector(sampleRate: 8000, 33 | agressiveness: .veryAggressive) 34 | 35 | func didReceiveSampleBuffer(_ sampleBuffer: CMSampleBuffer) { 36 | // activities: [VoiceActivityDetector.VoiceActivityInfo]? 37 | let activities = voiceActivityDetector(sampleBuffer: sampleBuffer, byEachMilliSec: 10)! 38 | 39 | // ... 40 | } 41 | ``` 42 | 43 | For usage with a microphone, see [Example](Example/VoiceActivityDetector/ViewController.swift). 44 | And against an audio file, see [Test code](Example/Tests/Tests.swift). 45 | 46 | ## API 47 | 48 | ### Constructors 49 | 50 | ```swift 51 | init?() 52 | convenience init?(sampleRate: Int = 8000, agressiveness: DetectionAgressiveness = .quality) 53 | convenience init?(agressiveness: DetectionAgressiveness = .quality) { 54 | ``` 55 | 56 | Instanciate VoiceActivityDetector. 57 | 58 | ### Properties 59 | 60 | ```swift 61 | var agressiveness: DetectionAgressiveness 62 | ``` 63 | 64 | VAD operating "aggressiveness" mode. 65 | 66 | - `.quality` 67 | The default value; normal voice detection mode. Suitable for high bitrate, low-noise data. 68 | May classify noise as voice, too. 69 | - `.lowBitRate` 70 | Detection mode optimised for low-bitrate audio. 71 | - `.aggressive` 72 | Detection mode best suited for somewhat noisy, lower quality audio. 73 | - `.veryAggressive` 74 | Detection mode with lowest miss-rate. Works well for most inputs. 75 | 76 | ```swift 77 | var sampleRate: Int 78 | ``` 79 | 80 | Sample rate in Hz for VAD operations. 81 | Valid values are 8000, 16000, 32000 and 48000. The default is 8000. 82 | 83 | Note that internally all processing will be done 8000Hz. 84 | input data in higher sample rates will just be downsampled first. 85 | 86 | ### Functions 87 | 88 | ```swift 89 | func reset() 90 | ``` 91 | 92 | Reinitializes a VAD instance, clearing all state and resetting mode and 93 | sample rate to defaults. 94 | 95 | ```swift 96 | func detect(frames: UnsafePointer, count: Int) -> VoiceActivity 97 | ``` 98 | 99 | Calculates a VAD decision for an audio duration. 100 | 101 | `frames` is an array of signed 16-bit samples. 102 | `count` specifies count of frames. 103 | Since internal processor supports only counts of 10, 20 or 30 ms, 104 | so for example at 8 kHz, `count` must be either 80, 160 or 240. 105 | 106 | Returns a VAD decision. 107 | 108 | Under the hood, the VAD engine calculates energy powers in six frequency bands between 80-4KHz from signal data flow and guesses chance of voice activity state in a input duration. So, its decision should be more accurate by sequencial detection than one-shot or periodic ones. 109 | 110 | ```swift 111 | func detect(frames: UnsafePointer, lengthInMilliSec ms: Int) -> VoiceActivity 112 | ``` 113 | 114 | `ms` specifies processing duration in milliseconds. 115 | The should be either 10, 20 or 30 (ms). 116 | 117 | ```swift 118 | public func detect(sampleBuffer: CMSampleBuffer, 119 | byEachMilliSec ms: Int, 120 | offset: Int = 0, 121 | duration: Int? = nil) -> [VoiceActivityInfo]? { 122 | ``` 123 | Calculates VAD decisions among a sample buffer. 124 | 125 | `sampleBuffer` is an audio buffer to be inspected. 126 | `ms` specifies processing duration in milliseconds. 127 | `offset` controlls offset time in milliseconds from where to start VAD. 128 | `duration` controlls total VAD duration in milliseconds. 129 | 130 | Returns an array of VAD decision information. 131 | 132 | - `timestamp: Int` 133 | Elapse time from the beginning of the sample buffer, in milliseconds. 134 | - `presentationTimestamp: CMTime` 135 | This is `CMSampleBuffer.presentationTime` + `timestamp`, which may represent 136 | a timestamp in entire of a recording session. 137 | - `voiceActivity: VoiceActivity` 138 | a VAD decision. 139 | 140 | 141 | 142 | ## Example 143 | 144 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 145 | 146 | ## Installation 147 | 148 | VoiceActivityDetector is available through [CocoaPods](https://cocoapods.org). To install 149 | it, simply add the following line to your Podfile: 150 | 151 | ```ruby 152 | pod 'VoiceActivityDetector' 153 | ``` 154 | 155 | ## Author 156 | 157 | reedom, tohru@reedom.com 158 | 159 | ## License 160 | 161 | VoiceActivityDetector is available under the MIT license. See the LICENSE file for more info. 162 | -------------------------------------------------------------------------------- /VoiceActivityDetector.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint VoiceActivityDetector.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'VoiceActivityDetector' 11 | s.version = '0.2.0' 12 | s.summary = 'WebRTC based voice activity detection.' 13 | s.homepage = 'https://github.com/reedom/VoiceActivityDetector' 14 | s.license = { :type => 'MIT', :file => 'LICENSE' } 15 | s.author = { 'reedom' => 'tohru@reedom.com' } 16 | s.source = { :git => 'https://github.com/reedom/VoiceActivityDetector.git', :tag => s.version.to_s } 17 | s.ios.deployment_target = '8.0' 18 | s.swift_version = "5.0" 19 | s.source_files = 'VoiceActivityDetector/Classes/**/*' 20 | s.dependency 'libfvad', '~> 0.1.0' 21 | end 22 | -------------------------------------------------------------------------------- /VoiceActivityDetector/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reedom/VoiceActivityDetector/e639fc037f19f8c03ba33d1dfdfc42da914f11b4/VoiceActivityDetector/Assets/.gitkeep -------------------------------------------------------------------------------- /VoiceActivityDetector/Classes/VoiceActivityDetector.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VoiceActivityDetector.swift 3 | // VoiceActivityDetector 4 | // 5 | // Created by HANAI tohru on 07/12/2019. 6 | // Copyright (c) 2019 HANAI tohru. All rights reserved. 7 | // 8 | 9 | import AVFoundation 10 | import libfvad 11 | 12 | /// VoiceActivityDetector(VAD). 13 | /// 14 | /// `VoiceActivityDetector` uses the VAD engine of Google's WebRTC internally. 15 | public class VoiceActivityDetector { 16 | /// VAD operating "aggressiveness" mode. 17 | /// 18 | /// A more aggressive (higher mode) VAD is more restrictive in reporting speech. 19 | /// Put in other words the probability of being speech when the VAD returns 20 | /// `VoiceActivity.activeVoice` is increased with increasing mode. 21 | /// As a consequence also the missed detection rate goes up. 22 | public enum DetectionAggressiveness: Int32 { 23 | case quality = 0 24 | case lowBitRate = 1 25 | case aggressive = 2 26 | case veryAggressive = 3 27 | } 28 | 29 | /// VOD decision result. 30 | public enum VoiceActivity: Int32 { 31 | case inActiveVoice = 0 32 | case activeVoice = 1 33 | } 34 | 35 | /// Acceptable durations. 36 | public enum Duration: Int { 37 | case msec10 = 10 38 | case msec20 = 20 39 | case msec30 = 30 40 | } 41 | 42 | let inst: OpaquePointer 43 | var _detectionAggressiveness = DetectionAggressiveness.quality 44 | var _sampleRate: Int = 8000 45 | 46 | /// Creates and initializes a VAD instance. 47 | /// 48 | /// - Returns: `nil` in case of a memory allocation error. 49 | public init?() { 50 | guard let inst = fvad_new() else { return nil } 51 | self.inst = inst 52 | } 53 | 54 | /// Creates and initializes a VAD instance. 55 | /// 56 | /// - Parameter sampleRate: Sample rate in Hz for VAD operations. Supports only 8000|16000|32000|48000. 57 | /// - Parameter agressiveness: VAD operating "aggressiveness" mode. 58 | /// - Returns: `nil` in case of a memory allocation error. 59 | public convenience init?(sampleRate: Int = 8000, agressiveness: DetectionAggressiveness = .quality) { 60 | self.init() 61 | self.sampleRate = sampleRate 62 | self.agressiveness = agressiveness 63 | } 64 | 65 | /// Creates and initializes a VAD instance. 66 | /// 67 | /// - Parameter agressiveness: VAD operating "aggressiveness" mode. 68 | /// - Returns: `nil` in case of a memory allocation error. 69 | public convenience init?(agressiveness: DetectionAggressiveness = .quality) { 70 | self.init() 71 | self.agressiveness = agressiveness 72 | } 73 | 74 | deinit { 75 | // Frees the dynamic memory of a specified VAD instance. 76 | fvad_free(inst) 77 | } 78 | 79 | /// Reinitializes a VAD instance, clearing all state and resetting mode and 80 | /// sample rate to defaults. 81 | public func reset() { 82 | fvad_reset(inst) 83 | } 84 | 85 | /// VAD operating "aggressiveness" mode. 86 | public var agressiveness: DetectionAggressiveness { 87 | get { return _detectionAggressiveness } 88 | set { 89 | guard fvad_set_mode(inst, newValue.rawValue) == 0 else { 90 | fatalError("Invalid value: \(newValue.rawValue)") 91 | } 92 | _detectionAggressiveness = newValue 93 | } 94 | } 95 | 96 | /// Sample rate in Hz for VAD operations. 97 | /// 98 | /// Valid values are 8000, 16000, 32000 and 48000. The default is 8000. 99 | /// Note that internally all processing will be done 8000 Hz; input data in higher 100 | /// sample rates will just be downsampled first. 101 | public var sampleRate: Int { 102 | get { return _sampleRate } 103 | set { 104 | guard fvad_set_sample_rate(inst, Int32(newValue)) == 0 else { 105 | assertionFailure("Invalid value: \(newValue), should be 8000|16000|32000|48000") 106 | return 107 | } 108 | _sampleRate = newValue 109 | } 110 | } 111 | 112 | /// Calculates a VAD decision for an audio duration. 113 | /// 114 | /// - Parameter frames: Array of signed 16-bit samples. 115 | /// - Parameter count: Specify count of frames. 116 | /// Since internal processor supports only counts of 10, 20 or 30 ms, 117 | /// so for example at 8 kHz, `count` must be either 80, 160 or 240. 118 | /// - Returns: VAD decision. 119 | public func detect(frames: UnsafePointer, count: Int) -> VoiceActivity { 120 | switch fvad_process(inst, frames, count) { 121 | case 0: 122 | return .inActiveVoice 123 | case 1: 124 | return .activeVoice 125 | default: 126 | let validValues = [10, 20, 30] 127 | .map({ $0 * _sampleRate / 1000 }) 128 | .map({ String(describing: $0) }) 129 | .joined(separator: "|") 130 | assertionFailure("Invalid value \(count): should be \(validValues)") 131 | return .inActiveVoice 132 | } 133 | } 134 | 135 | /// Calculates a VAD decision for an audio duration. 136 | /// 137 | /// - Parameter frames: Array of signed 16-bit samples. 138 | /// - Parameter ms: Specify processing duration in milliseconds. 139 | /// The internal processor supports only 10, 20 or 30 ms. 140 | /// - Returns: VAD decision. 141 | public func detect(frames: UnsafePointer, lengthInMilliSec ms: Int) -> VoiceActivity { 142 | let count = ms * _sampleRate / 1000 143 | switch fvad_process(inst, frames, count) { 144 | case 0: 145 | return .inActiveVoice 146 | case 1: 147 | return .activeVoice 148 | default: 149 | assertionFailure("Invalid value \(ms): should be 10|20|30") 150 | return .inActiveVoice 151 | } 152 | } 153 | } 154 | 155 | extension VoiceActivityDetector { 156 | public struct VoiceActivityInfo { 157 | public let timestamp: Int 158 | public let presentationTimestamp: CMTime 159 | public let voiceActivity: VoiceActivity 160 | } 161 | 162 | /// Calculates VAD decisions among a sample buffer. 163 | /// 164 | /// - Parameter sampleBuffer: An audio buffer to be inspected. 165 | /// The data format should be signed 16-bit PCM 166 | /// and its sample rate should equals to `sampleRate`. 167 | /// - Parameter ms: Specify processing duration in milliseconds each. 168 | /// The internal processor supports only 10, 20 or 30 ms. 169 | /// - Parameter offset: Offset time in milliseconds from where to start VAD. 170 | /// - Parameter duration: Total VAD duration in milliseconds. 171 | /// - Returns: VAD decision information. 172 | public func detect(sampleBuffer: CMSampleBuffer, 173 | byEachMilliSec ms: Int, 174 | offset: Int = 0, 175 | duration: Int? = nil) -> [VoiceActivityInfo]? { 176 | guard let dataBuffer = CMSampleBufferGetDataBuffer(sampleBuffer) else { 177 | return nil 178 | } 179 | 180 | let sampleBytes = CMSampleBufferGetSampleSize(sampleBuffer, at: 0) 181 | assert(sampleBytes == 2, "Invalid sample format") 182 | 183 | let offsetBytes = (offset == 0) ? 0 : (offset * _sampleRate * sampleBytes / 1000) 184 | var bufferBytes = 0 185 | var bufferPointer: UnsafeMutablePointer? 186 | let ret = CMBlockBufferGetDataPointer(dataBuffer, 187 | atOffset: offsetBytes, 188 | lengthAtOffsetOut: &bufferBytes, 189 | totalLengthOut: nil, 190 | dataPointerOut: &bufferPointer) 191 | guard ret == kCMBlockBufferNoErr, bufferPointer != nil else { 192 | // FIXME how to pass the faileure information to the caller, an exception or Result<>? 193 | return nil 194 | } 195 | 196 | let sampleCount = bufferBytes / 2 197 | return bufferPointer!.withMemoryRebound(to: Int16.self, capacity: sampleCount) { (buffer) in 198 | let availableDuration = (sampleCount * 1000) / _sampleRate 199 | 200 | let times: Int 201 | if let duration = duration { 202 | times = min(duration, availableDuration) / ms 203 | } else { 204 | times = availableDuration / ms 205 | } 206 | 207 | let sampleCountPerUnit = ms * _sampleRate / 1000 208 | let timeDelta = CMTimeMake(value: Int64(ms), timescale: 1000) 209 | 210 | var timestamp = offset 211 | var presentationTimestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer) 212 | return (0 ..< times).map { i in 213 | let voiceActivity = detect(frames: buffer + i * sampleCountPerUnit, count: sampleCountPerUnit) 214 | let info = VoiceActivityInfo(timestamp: timestamp, 215 | presentationTimestamp: presentationTimestamp, 216 | voiceActivity: voiceActivity) 217 | timestamp += ms 218 | presentationTimestamp = CMTimeAdd(presentationTimestamp, timeDelta) 219 | return info 220 | } 221 | } 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj --------------------------------------------------------------------------------