├── .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 | [](https://travis-ci.org/reedom/VoiceActivityDetector)
4 | [](https://cocoapods.org/pods/VoiceActivityDetector)
5 | [](https://cocoapods.org/pods/VoiceActivityDetector)
6 | [](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
--------------------------------------------------------------------------------