├── AudioRecorder.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ ├── apple.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ ├── AudioRecorder.xcscheme │ │ └── xcschememanagement.plist │ └── xiaokai.zhan.xcuserdatad │ └── xcschemes │ ├── AudioRecorder.xcscheme │ └── xcschememanagement.plist ├── AudioRecorder ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── AudioRecorder.h ├── AudioRecorder.m ├── AudioSession │ ├── AVAudioSession+RouteUtils.h │ ├── AVAudioSession+RouteUtils.m │ ├── ELAudioSession.h │ └── ELAudioSession.m ├── Base.lproj │ └── LaunchScreen.storyboard ├── Info.plist ├── ViewController.h ├── ViewController.m ├── ViewController.xib ├── main.m └── utils │ ├── CommonUtil.h │ └── CommonUtil.m ├── AudioRecorderTests ├── AudioRecorderTests.m └── Info.plist └── AudioRecorderUITests ├── AudioRecorderUITests.m └── Info.plist /AudioRecorder.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 188EA2481E55D4FE00E858A4 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 188EA2471E55D4FE00E858A4 /* main.m */; }; 11 | 188EA24B1E55D4FE00E858A4 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 188EA24A1E55D4FE00E858A4 /* AppDelegate.m */; }; 12 | 188EA24E1E55D4FE00E858A4 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 188EA24D1E55D4FE00E858A4 /* ViewController.m */; }; 13 | 188EA2531E55D4FE00E858A4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 188EA2521E55D4FE00E858A4 /* Assets.xcassets */; }; 14 | 188EA2561E55D4FE00E858A4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 188EA2541E55D4FE00E858A4 /* LaunchScreen.storyboard */; }; 15 | 188EA2611E55D4FF00E858A4 /* AudioRecorderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 188EA2601E55D4FF00E858A4 /* AudioRecorderTests.m */; }; 16 | 188EA26C1E55D4FF00E858A4 /* AudioRecorderUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 188EA26B1E55D4FF00E858A4 /* AudioRecorderUITests.m */; }; 17 | 188EA27A1E55D57300E858A4 /* ViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 188EA2791E55D57300E858A4 /* ViewController.xib */; }; 18 | 188EA2C31E55DC1500E858A4 /* CommonUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = 188EA2C21E55DC1500E858A4 /* CommonUtil.m */; }; 19 | 18A9E72D1E5C79F2002069FA /* AVAudioSession+RouteUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 18A9E72A1E5C79F2002069FA /* AVAudioSession+RouteUtils.m */; }; 20 | 18A9E72E1E5C79F2002069FA /* ELAudioSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 18A9E72C1E5C79F2002069FA /* ELAudioSession.m */; }; 21 | 18A9E7311E5C7A5D002069FA /* AudioRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 18A9E7301E5C7A5D002069FA /* AudioRecorder.m */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXContainerItemProxy section */ 25 | 188EA25D1E55D4FF00E858A4 /* PBXContainerItemProxy */ = { 26 | isa = PBXContainerItemProxy; 27 | containerPortal = 188EA23B1E55D4FE00E858A4 /* Project object */; 28 | proxyType = 1; 29 | remoteGlobalIDString = 188EA2421E55D4FE00E858A4; 30 | remoteInfo = AudioRecorder; 31 | }; 32 | 188EA2681E55D4FF00E858A4 /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = 188EA23B1E55D4FE00E858A4 /* Project object */; 35 | proxyType = 1; 36 | remoteGlobalIDString = 188EA2421E55D4FE00E858A4; 37 | remoteInfo = AudioRecorder; 38 | }; 39 | /* End PBXContainerItemProxy section */ 40 | 41 | /* Begin PBXFileReference section */ 42 | 188EA2431E55D4FE00E858A4 /* AudioRecorder.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AudioRecorder.app; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 188EA2471E55D4FE00E858A4 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 44 | 188EA2491E55D4FE00E858A4 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 45 | 188EA24A1E55D4FE00E858A4 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 46 | 188EA24C1E55D4FE00E858A4 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 47 | 188EA24D1E55D4FE00E858A4 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 48 | 188EA2521E55D4FE00E858A4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 49 | 188EA2551E55D4FE00E858A4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 50 | 188EA2571E55D4FE00E858A4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 51 | 188EA25C1E55D4FF00E858A4 /* AudioRecorderTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AudioRecorderTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | 188EA2601E55D4FF00E858A4 /* AudioRecorderTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AudioRecorderTests.m; sourceTree = ""; }; 53 | 188EA2621E55D4FF00E858A4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54 | 188EA2671E55D4FF00E858A4 /* AudioRecorderUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AudioRecorderUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 188EA26B1E55D4FF00E858A4 /* AudioRecorderUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AudioRecorderUITests.m; sourceTree = ""; }; 56 | 188EA26D1E55D4FF00E858A4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 57 | 188EA2791E55D57300E858A4 /* ViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ViewController.xib; sourceTree = ""; }; 58 | 188EA2C11E55DC1500E858A4 /* CommonUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonUtil.h; sourceTree = ""; }; 59 | 188EA2C21E55DC1500E858A4 /* CommonUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CommonUtil.m; sourceTree = ""; }; 60 | 18A9E7291E5C79F2002069FA /* AVAudioSession+RouteUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "AVAudioSession+RouteUtils.h"; sourceTree = ""; }; 61 | 18A9E72A1E5C79F2002069FA /* AVAudioSession+RouteUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "AVAudioSession+RouteUtils.m"; sourceTree = ""; }; 62 | 18A9E72B1E5C79F2002069FA /* ELAudioSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELAudioSession.h; sourceTree = ""; }; 63 | 18A9E72C1E5C79F2002069FA /* ELAudioSession.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELAudioSession.m; sourceTree = ""; }; 64 | 18A9E72F1E5C7A5D002069FA /* AudioRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioRecorder.h; sourceTree = ""; }; 65 | 18A9E7301E5C7A5D002069FA /* AudioRecorder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioRecorder.m; sourceTree = ""; }; 66 | /* End PBXFileReference section */ 67 | 68 | /* Begin PBXFrameworksBuildPhase section */ 69 | 188EA2401E55D4FE00E858A4 /* Frameworks */ = { 70 | isa = PBXFrameworksBuildPhase; 71 | buildActionMask = 2147483647; 72 | files = ( 73 | ); 74 | runOnlyForDeploymentPostprocessing = 0; 75 | }; 76 | 188EA2591E55D4FF00E858A4 /* Frameworks */ = { 77 | isa = PBXFrameworksBuildPhase; 78 | buildActionMask = 2147483647; 79 | files = ( 80 | ); 81 | runOnlyForDeploymentPostprocessing = 0; 82 | }; 83 | 188EA2641E55D4FF00E858A4 /* Frameworks */ = { 84 | isa = PBXFrameworksBuildPhase; 85 | buildActionMask = 2147483647; 86 | files = ( 87 | ); 88 | runOnlyForDeploymentPostprocessing = 0; 89 | }; 90 | /* End PBXFrameworksBuildPhase section */ 91 | 92 | /* Begin PBXGroup section */ 93 | 188EA23A1E55D4FE00E858A4 = { 94 | isa = PBXGroup; 95 | children = ( 96 | 188EA2451E55D4FE00E858A4 /* AudioRecorder */, 97 | 188EA25F1E55D4FF00E858A4 /* AudioRecorderTests */, 98 | 188EA26A1E55D4FF00E858A4 /* AudioRecorderUITests */, 99 | 188EA2441E55D4FE00E858A4 /* Products */, 100 | ); 101 | sourceTree = ""; 102 | }; 103 | 188EA2441E55D4FE00E858A4 /* Products */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 188EA2431E55D4FE00E858A4 /* AudioRecorder.app */, 107 | 188EA25C1E55D4FF00E858A4 /* AudioRecorderTests.xctest */, 108 | 188EA2671E55D4FF00E858A4 /* AudioRecorderUITests.xctest */, 109 | ); 110 | name = Products; 111 | sourceTree = ""; 112 | }; 113 | 188EA2451E55D4FE00E858A4 /* AudioRecorder */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 18A9E7281E5C79F2002069FA /* AudioSession */, 117 | 188EA2C01E55DC1500E858A4 /* utils */, 118 | 188EA2491E55D4FE00E858A4 /* AppDelegate.h */, 119 | 188EA24A1E55D4FE00E858A4 /* AppDelegate.m */, 120 | 188EA24C1E55D4FE00E858A4 /* ViewController.h */, 121 | 188EA24D1E55D4FE00E858A4 /* ViewController.m */, 122 | 188EA2521E55D4FE00E858A4 /* Assets.xcassets */, 123 | 188EA2541E55D4FE00E858A4 /* LaunchScreen.storyboard */, 124 | 188EA2571E55D4FE00E858A4 /* Info.plist */, 125 | 188EA2461E55D4FE00E858A4 /* Supporting Files */, 126 | 188EA2791E55D57300E858A4 /* ViewController.xib */, 127 | 18A9E72F1E5C7A5D002069FA /* AudioRecorder.h */, 128 | 18A9E7301E5C7A5D002069FA /* AudioRecorder.m */, 129 | ); 130 | path = AudioRecorder; 131 | sourceTree = ""; 132 | }; 133 | 188EA2461E55D4FE00E858A4 /* Supporting Files */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 188EA2471E55D4FE00E858A4 /* main.m */, 137 | ); 138 | name = "Supporting Files"; 139 | sourceTree = ""; 140 | }; 141 | 188EA25F1E55D4FF00E858A4 /* AudioRecorderTests */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | 188EA2601E55D4FF00E858A4 /* AudioRecorderTests.m */, 145 | 188EA2621E55D4FF00E858A4 /* Info.plist */, 146 | ); 147 | path = AudioRecorderTests; 148 | sourceTree = ""; 149 | }; 150 | 188EA26A1E55D4FF00E858A4 /* AudioRecorderUITests */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | 188EA26B1E55D4FF00E858A4 /* AudioRecorderUITests.m */, 154 | 188EA26D1E55D4FF00E858A4 /* Info.plist */, 155 | ); 156 | path = AudioRecorderUITests; 157 | sourceTree = ""; 158 | }; 159 | 188EA2C01E55DC1500E858A4 /* utils */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 188EA2C11E55DC1500E858A4 /* CommonUtil.h */, 163 | 188EA2C21E55DC1500E858A4 /* CommonUtil.m */, 164 | ); 165 | path = utils; 166 | sourceTree = ""; 167 | }; 168 | 18A9E7281E5C79F2002069FA /* AudioSession */ = { 169 | isa = PBXGroup; 170 | children = ( 171 | 18A9E7291E5C79F2002069FA /* AVAudioSession+RouteUtils.h */, 172 | 18A9E72A1E5C79F2002069FA /* AVAudioSession+RouteUtils.m */, 173 | 18A9E72B1E5C79F2002069FA /* ELAudioSession.h */, 174 | 18A9E72C1E5C79F2002069FA /* ELAudioSession.m */, 175 | ); 176 | path = AudioSession; 177 | sourceTree = ""; 178 | }; 179 | /* End PBXGroup section */ 180 | 181 | /* Begin PBXNativeTarget section */ 182 | 188EA2421E55D4FE00E858A4 /* AudioRecorder */ = { 183 | isa = PBXNativeTarget; 184 | buildConfigurationList = 188EA2701E55D4FF00E858A4 /* Build configuration list for PBXNativeTarget "AudioRecorder" */; 185 | buildPhases = ( 186 | 188EA23F1E55D4FE00E858A4 /* Sources */, 187 | 188EA2401E55D4FE00E858A4 /* Frameworks */, 188 | 188EA2411E55D4FE00E858A4 /* Resources */, 189 | ); 190 | buildRules = ( 191 | ); 192 | dependencies = ( 193 | ); 194 | name = AudioRecorder; 195 | productName = AudioRecorder; 196 | productReference = 188EA2431E55D4FE00E858A4 /* AudioRecorder.app */; 197 | productType = "com.apple.product-type.application"; 198 | }; 199 | 188EA25B1E55D4FF00E858A4 /* AudioRecorderTests */ = { 200 | isa = PBXNativeTarget; 201 | buildConfigurationList = 188EA2731E55D4FF00E858A4 /* Build configuration list for PBXNativeTarget "AudioRecorderTests" */; 202 | buildPhases = ( 203 | 188EA2581E55D4FF00E858A4 /* Sources */, 204 | 188EA2591E55D4FF00E858A4 /* Frameworks */, 205 | 188EA25A1E55D4FF00E858A4 /* Resources */, 206 | ); 207 | buildRules = ( 208 | ); 209 | dependencies = ( 210 | 188EA25E1E55D4FF00E858A4 /* PBXTargetDependency */, 211 | ); 212 | name = AudioRecorderTests; 213 | productName = AudioRecorderTests; 214 | productReference = 188EA25C1E55D4FF00E858A4 /* AudioRecorderTests.xctest */; 215 | productType = "com.apple.product-type.bundle.unit-test"; 216 | }; 217 | 188EA2661E55D4FF00E858A4 /* AudioRecorderUITests */ = { 218 | isa = PBXNativeTarget; 219 | buildConfigurationList = 188EA2761E55D4FF00E858A4 /* Build configuration list for PBXNativeTarget "AudioRecorderUITests" */; 220 | buildPhases = ( 221 | 188EA2631E55D4FF00E858A4 /* Sources */, 222 | 188EA2641E55D4FF00E858A4 /* Frameworks */, 223 | 188EA2651E55D4FF00E858A4 /* Resources */, 224 | ); 225 | buildRules = ( 226 | ); 227 | dependencies = ( 228 | 188EA2691E55D4FF00E858A4 /* PBXTargetDependency */, 229 | ); 230 | name = AudioRecorderUITests; 231 | productName = AudioRecorderUITests; 232 | productReference = 188EA2671E55D4FF00E858A4 /* AudioRecorderUITests.xctest */; 233 | productType = "com.apple.product-type.bundle.ui-testing"; 234 | }; 235 | /* End PBXNativeTarget section */ 236 | 237 | /* Begin PBXProject section */ 238 | 188EA23B1E55D4FE00E858A4 /* Project object */ = { 239 | isa = PBXProject; 240 | attributes = { 241 | LastUpgradeCheck = 0820; 242 | ORGANIZATIONNAME = xiaokai.zhan; 243 | TargetAttributes = { 244 | 188EA2421E55D4FE00E858A4 = { 245 | CreatedOnToolsVersion = 8.2.1; 246 | DevelopmentTeam = MY5GTD35UJ; 247 | ProvisioningStyle = Automatic; 248 | }; 249 | 188EA25B1E55D4FF00E858A4 = { 250 | CreatedOnToolsVersion = 8.2.1; 251 | DevelopmentTeam = MY5GTD35UJ; 252 | ProvisioningStyle = Automatic; 253 | TestTargetID = 188EA2421E55D4FE00E858A4; 254 | }; 255 | 188EA2661E55D4FF00E858A4 = { 256 | CreatedOnToolsVersion = 8.2.1; 257 | DevelopmentTeam = MY5GTD35UJ; 258 | ProvisioningStyle = Automatic; 259 | TestTargetID = 188EA2421E55D4FE00E858A4; 260 | }; 261 | }; 262 | }; 263 | buildConfigurationList = 188EA23E1E55D4FE00E858A4 /* Build configuration list for PBXProject "AudioRecorder" */; 264 | compatibilityVersion = "Xcode 3.2"; 265 | developmentRegion = English; 266 | hasScannedForEncodings = 0; 267 | knownRegions = ( 268 | en, 269 | Base, 270 | ); 271 | mainGroup = 188EA23A1E55D4FE00E858A4; 272 | productRefGroup = 188EA2441E55D4FE00E858A4 /* Products */; 273 | projectDirPath = ""; 274 | projectRoot = ""; 275 | targets = ( 276 | 188EA2421E55D4FE00E858A4 /* AudioRecorder */, 277 | 188EA25B1E55D4FF00E858A4 /* AudioRecorderTests */, 278 | 188EA2661E55D4FF00E858A4 /* AudioRecorderUITests */, 279 | ); 280 | }; 281 | /* End PBXProject section */ 282 | 283 | /* Begin PBXResourcesBuildPhase section */ 284 | 188EA2411E55D4FE00E858A4 /* Resources */ = { 285 | isa = PBXResourcesBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | 188EA2561E55D4FE00E858A4 /* LaunchScreen.storyboard in Resources */, 289 | 188EA27A1E55D57300E858A4 /* ViewController.xib in Resources */, 290 | 188EA2531E55D4FE00E858A4 /* Assets.xcassets in Resources */, 291 | ); 292 | runOnlyForDeploymentPostprocessing = 0; 293 | }; 294 | 188EA25A1E55D4FF00E858A4 /* Resources */ = { 295 | isa = PBXResourcesBuildPhase; 296 | buildActionMask = 2147483647; 297 | files = ( 298 | ); 299 | runOnlyForDeploymentPostprocessing = 0; 300 | }; 301 | 188EA2651E55D4FF00E858A4 /* Resources */ = { 302 | isa = PBXResourcesBuildPhase; 303 | buildActionMask = 2147483647; 304 | files = ( 305 | ); 306 | runOnlyForDeploymentPostprocessing = 0; 307 | }; 308 | /* End PBXResourcesBuildPhase section */ 309 | 310 | /* Begin PBXSourcesBuildPhase section */ 311 | 188EA23F1E55D4FE00E858A4 /* Sources */ = { 312 | isa = PBXSourcesBuildPhase; 313 | buildActionMask = 2147483647; 314 | files = ( 315 | 18A9E72E1E5C79F2002069FA /* ELAudioSession.m in Sources */, 316 | 188EA24E1E55D4FE00E858A4 /* ViewController.m in Sources */, 317 | 188EA24B1E55D4FE00E858A4 /* AppDelegate.m in Sources */, 318 | 18A9E7311E5C7A5D002069FA /* AudioRecorder.m in Sources */, 319 | 18A9E72D1E5C79F2002069FA /* AVAudioSession+RouteUtils.m in Sources */, 320 | 188EA2C31E55DC1500E858A4 /* CommonUtil.m in Sources */, 321 | 188EA2481E55D4FE00E858A4 /* main.m in Sources */, 322 | ); 323 | runOnlyForDeploymentPostprocessing = 0; 324 | }; 325 | 188EA2581E55D4FF00E858A4 /* Sources */ = { 326 | isa = PBXSourcesBuildPhase; 327 | buildActionMask = 2147483647; 328 | files = ( 329 | 188EA2611E55D4FF00E858A4 /* AudioRecorderTests.m in Sources */, 330 | ); 331 | runOnlyForDeploymentPostprocessing = 0; 332 | }; 333 | 188EA2631E55D4FF00E858A4 /* Sources */ = { 334 | isa = PBXSourcesBuildPhase; 335 | buildActionMask = 2147483647; 336 | files = ( 337 | 188EA26C1E55D4FF00E858A4 /* AudioRecorderUITests.m in Sources */, 338 | ); 339 | runOnlyForDeploymentPostprocessing = 0; 340 | }; 341 | /* End PBXSourcesBuildPhase section */ 342 | 343 | /* Begin PBXTargetDependency section */ 344 | 188EA25E1E55D4FF00E858A4 /* PBXTargetDependency */ = { 345 | isa = PBXTargetDependency; 346 | target = 188EA2421E55D4FE00E858A4 /* AudioRecorder */; 347 | targetProxy = 188EA25D1E55D4FF00E858A4 /* PBXContainerItemProxy */; 348 | }; 349 | 188EA2691E55D4FF00E858A4 /* PBXTargetDependency */ = { 350 | isa = PBXTargetDependency; 351 | target = 188EA2421E55D4FE00E858A4 /* AudioRecorder */; 352 | targetProxy = 188EA2681E55D4FF00E858A4 /* PBXContainerItemProxy */; 353 | }; 354 | /* End PBXTargetDependency section */ 355 | 356 | /* Begin PBXVariantGroup section */ 357 | 188EA2541E55D4FE00E858A4 /* LaunchScreen.storyboard */ = { 358 | isa = PBXVariantGroup; 359 | children = ( 360 | 188EA2551E55D4FE00E858A4 /* Base */, 361 | ); 362 | name = LaunchScreen.storyboard; 363 | sourceTree = ""; 364 | }; 365 | /* End PBXVariantGroup section */ 366 | 367 | /* Begin XCBuildConfiguration section */ 368 | 188EA26E1E55D4FF00E858A4 /* Debug */ = { 369 | isa = XCBuildConfiguration; 370 | buildSettings = { 371 | ALWAYS_SEARCH_USER_PATHS = NO; 372 | CLANG_ANALYZER_NONNULL = YES; 373 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 374 | CLANG_CXX_LIBRARY = "libc++"; 375 | CLANG_ENABLE_MODULES = YES; 376 | CLANG_ENABLE_OBJC_ARC = YES; 377 | CLANG_WARN_BOOL_CONVERSION = YES; 378 | CLANG_WARN_CONSTANT_CONVERSION = YES; 379 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 380 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 381 | CLANG_WARN_EMPTY_BODY = YES; 382 | CLANG_WARN_ENUM_CONVERSION = YES; 383 | CLANG_WARN_INFINITE_RECURSION = YES; 384 | CLANG_WARN_INT_CONVERSION = YES; 385 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 386 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 387 | CLANG_WARN_UNREACHABLE_CODE = YES; 388 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 389 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 390 | COPY_PHASE_STRIP = NO; 391 | DEBUG_INFORMATION_FORMAT = dwarf; 392 | ENABLE_STRICT_OBJC_MSGSEND = YES; 393 | ENABLE_TESTABILITY = YES; 394 | GCC_C_LANGUAGE_STANDARD = gnu99; 395 | GCC_DYNAMIC_NO_PIC = NO; 396 | GCC_NO_COMMON_BLOCKS = YES; 397 | GCC_OPTIMIZATION_LEVEL = 0; 398 | GCC_PREPROCESSOR_DEFINITIONS = ( 399 | "DEBUG=1", 400 | "$(inherited)", 401 | ); 402 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 403 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 404 | GCC_WARN_UNDECLARED_SELECTOR = YES; 405 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 406 | GCC_WARN_UNUSED_FUNCTION = YES; 407 | GCC_WARN_UNUSED_VARIABLE = YES; 408 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 409 | MTL_ENABLE_DEBUG_INFO = YES; 410 | ONLY_ACTIVE_ARCH = YES; 411 | SDKROOT = iphoneos; 412 | }; 413 | name = Debug; 414 | }; 415 | 188EA26F1E55D4FF00E858A4 /* Release */ = { 416 | isa = XCBuildConfiguration; 417 | buildSettings = { 418 | ALWAYS_SEARCH_USER_PATHS = NO; 419 | CLANG_ANALYZER_NONNULL = YES; 420 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 421 | CLANG_CXX_LIBRARY = "libc++"; 422 | CLANG_ENABLE_MODULES = YES; 423 | CLANG_ENABLE_OBJC_ARC = YES; 424 | CLANG_WARN_BOOL_CONVERSION = YES; 425 | CLANG_WARN_CONSTANT_CONVERSION = YES; 426 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 427 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 428 | CLANG_WARN_EMPTY_BODY = YES; 429 | CLANG_WARN_ENUM_CONVERSION = YES; 430 | CLANG_WARN_INFINITE_RECURSION = YES; 431 | CLANG_WARN_INT_CONVERSION = YES; 432 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 433 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 434 | CLANG_WARN_UNREACHABLE_CODE = YES; 435 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 436 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 437 | COPY_PHASE_STRIP = NO; 438 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 439 | ENABLE_NS_ASSERTIONS = NO; 440 | ENABLE_STRICT_OBJC_MSGSEND = YES; 441 | GCC_C_LANGUAGE_STANDARD = gnu99; 442 | GCC_NO_COMMON_BLOCKS = YES; 443 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 444 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 445 | GCC_WARN_UNDECLARED_SELECTOR = YES; 446 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 447 | GCC_WARN_UNUSED_FUNCTION = YES; 448 | GCC_WARN_UNUSED_VARIABLE = YES; 449 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 450 | MTL_ENABLE_DEBUG_INFO = NO; 451 | SDKROOT = iphoneos; 452 | VALIDATE_PRODUCT = YES; 453 | }; 454 | name = Release; 455 | }; 456 | 188EA2711E55D4FF00E858A4 /* Debug */ = { 457 | isa = XCBuildConfiguration; 458 | buildSettings = { 459 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 460 | DEVELOPMENT_TEAM = MY5GTD35UJ; 461 | INFOPLIST_FILE = AudioRecorder/Info.plist; 462 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 463 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 464 | PRODUCT_BUNDLE_IDENTIFIER = com.phuket.tour.audiotoolbox.encoder.AudioRecorder; 465 | PRODUCT_NAME = "$(TARGET_NAME)"; 466 | }; 467 | name = Debug; 468 | }; 469 | 188EA2721E55D4FF00E858A4 /* Release */ = { 470 | isa = XCBuildConfiguration; 471 | buildSettings = { 472 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 473 | DEVELOPMENT_TEAM = MY5GTD35UJ; 474 | INFOPLIST_FILE = AudioRecorder/Info.plist; 475 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 476 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 477 | PRODUCT_BUNDLE_IDENTIFIER = com.phuket.tour.audiotoolbox.encoder.AudioRecorder; 478 | PRODUCT_NAME = "$(TARGET_NAME)"; 479 | }; 480 | name = Release; 481 | }; 482 | 188EA2741E55D4FF00E858A4 /* Debug */ = { 483 | isa = XCBuildConfiguration; 484 | buildSettings = { 485 | BUNDLE_LOADER = "$(TEST_HOST)"; 486 | DEVELOPMENT_TEAM = MY5GTD35UJ; 487 | INFOPLIST_FILE = AudioRecorderTests/Info.plist; 488 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 489 | PRODUCT_BUNDLE_IDENTIFIER = com.phuket.tour.audiotoolbox.encoder.AudioRecorderTests; 490 | PRODUCT_NAME = "$(TARGET_NAME)"; 491 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AudioRecorder.app/AudioRecorder"; 492 | }; 493 | name = Debug; 494 | }; 495 | 188EA2751E55D4FF00E858A4 /* Release */ = { 496 | isa = XCBuildConfiguration; 497 | buildSettings = { 498 | BUNDLE_LOADER = "$(TEST_HOST)"; 499 | DEVELOPMENT_TEAM = MY5GTD35UJ; 500 | INFOPLIST_FILE = AudioRecorderTests/Info.plist; 501 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 502 | PRODUCT_BUNDLE_IDENTIFIER = com.phuket.tour.audiotoolbox.encoder.AudioRecorderTests; 503 | PRODUCT_NAME = "$(TARGET_NAME)"; 504 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AudioRecorder.app/AudioRecorder"; 505 | }; 506 | name = Release; 507 | }; 508 | 188EA2771E55D4FF00E858A4 /* Debug */ = { 509 | isa = XCBuildConfiguration; 510 | buildSettings = { 511 | DEVELOPMENT_TEAM = MY5GTD35UJ; 512 | INFOPLIST_FILE = AudioRecorderUITests/Info.plist; 513 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 514 | PRODUCT_BUNDLE_IDENTIFIER = com.phuket.tour.audiotoolbox.encoder.AudioRecorderUITests; 515 | PRODUCT_NAME = "$(TARGET_NAME)"; 516 | TEST_TARGET_NAME = AudioRecorder; 517 | }; 518 | name = Debug; 519 | }; 520 | 188EA2781E55D4FF00E858A4 /* Release */ = { 521 | isa = XCBuildConfiguration; 522 | buildSettings = { 523 | DEVELOPMENT_TEAM = MY5GTD35UJ; 524 | INFOPLIST_FILE = AudioRecorderUITests/Info.plist; 525 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 526 | PRODUCT_BUNDLE_IDENTIFIER = com.phuket.tour.audiotoolbox.encoder.AudioRecorderUITests; 527 | PRODUCT_NAME = "$(TARGET_NAME)"; 528 | TEST_TARGET_NAME = AudioRecorder; 529 | }; 530 | name = Release; 531 | }; 532 | /* End XCBuildConfiguration section */ 533 | 534 | /* Begin XCConfigurationList section */ 535 | 188EA23E1E55D4FE00E858A4 /* Build configuration list for PBXProject "AudioRecorder" */ = { 536 | isa = XCConfigurationList; 537 | buildConfigurations = ( 538 | 188EA26E1E55D4FF00E858A4 /* Debug */, 539 | 188EA26F1E55D4FF00E858A4 /* Release */, 540 | ); 541 | defaultConfigurationIsVisible = 0; 542 | defaultConfigurationName = Release; 543 | }; 544 | 188EA2701E55D4FF00E858A4 /* Build configuration list for PBXNativeTarget "AudioRecorder" */ = { 545 | isa = XCConfigurationList; 546 | buildConfigurations = ( 547 | 188EA2711E55D4FF00E858A4 /* Debug */, 548 | 188EA2721E55D4FF00E858A4 /* Release */, 549 | ); 550 | defaultConfigurationIsVisible = 0; 551 | defaultConfigurationName = Release; 552 | }; 553 | 188EA2731E55D4FF00E858A4 /* Build configuration list for PBXNativeTarget "AudioRecorderTests" */ = { 554 | isa = XCConfigurationList; 555 | buildConfigurations = ( 556 | 188EA2741E55D4FF00E858A4 /* Debug */, 557 | 188EA2751E55D4FF00E858A4 /* Release */, 558 | ); 559 | defaultConfigurationIsVisible = 0; 560 | defaultConfigurationName = Release; 561 | }; 562 | 188EA2761E55D4FF00E858A4 /* Build configuration list for PBXNativeTarget "AudioRecorderUITests" */ = { 563 | isa = XCConfigurationList; 564 | buildConfigurations = ( 565 | 188EA2771E55D4FF00E858A4 /* Debug */, 566 | 188EA2781E55D4FF00E858A4 /* Release */, 567 | ); 568 | defaultConfigurationIsVisible = 0; 569 | defaultConfigurationName = Release; 570 | }; 571 | /* End XCConfigurationList section */ 572 | }; 573 | rootObject = 188EA23B1E55D4FE00E858A4 /* Project object */; 574 | } 575 | -------------------------------------------------------------------------------- /AudioRecorder.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AudioRecorder.xcodeproj/xcuserdata/apple.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /AudioRecorder.xcodeproj/xcuserdata/apple.xcuserdatad/xcschemes/AudioRecorder.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /AudioRecorder.xcodeproj/xcuserdata/apple.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | AudioRecorder.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 188EA2421E55D4FE00E858A4 16 | 17 | primary 18 | 19 | 20 | 188EA25B1E55D4FF00E858A4 21 | 22 | primary 23 | 24 | 25 | 188EA2661E55D4FF00E858A4 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /AudioRecorder.xcodeproj/xcuserdata/xiaokai.zhan.xcuserdatad/xcschemes/AudioRecorder.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /AudioRecorder.xcodeproj/xcuserdata/xiaokai.zhan.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | AudioRecorder.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 188EA2421E55D4FE00E858A4 16 | 17 | primary 18 | 19 | 20 | 188EA25B1E55D4FF00E858A4 21 | 22 | primary 23 | 24 | 25 | 188EA2661E55D4FF00E858A4 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /AudioRecorder/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // AudioRecorder 4 | // 5 | // Created by apple on 2017/2/16. 6 | // Copyright © 2017年 xiaokai.zhan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /AudioRecorder/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // AudioRecorder 4 | // 5 | // Created by apple on 2017/2/16. 6 | // Copyright © 2017年 xiaokai.zhan. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "ViewController.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 21 | UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:[[ViewController alloc] initWithNibName:@"ViewController" bundle:nil]]; 22 | self.window.rootViewController = navigationController; 23 | [self.window makeKeyAndVisible]; 24 | return YES; 25 | } 26 | 27 | 28 | - (void)applicationWillResignActive:(UIApplication *)application { 29 | // 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. 30 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 31 | } 32 | 33 | 34 | - (void)applicationDidEnterBackground:(UIApplication *)application { 35 | // 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. 36 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 37 | } 38 | 39 | 40 | - (void)applicationWillEnterForeground:(UIApplication *)application { 41 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 42 | } 43 | 44 | 45 | - (void)applicationDidBecomeActive:(UIApplication *)application { 46 | // 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. 47 | } 48 | 49 | 50 | - (void)applicationWillTerminate:(UIApplication *)application { 51 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 52 | } 53 | 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /AudioRecorder/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /AudioRecorder/AudioRecorder.h: -------------------------------------------------------------------------------- 1 | // 2 | // AudioRecorder.h 3 | // AudioRecorder 4 | // 5 | // Created by apple on 2017/2/21. 6 | // Copyright © 2017年 xiaokai.zhan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AudioRecorder : NSObject 12 | 13 | - (id) initWithPath:(NSString*) path; 14 | 15 | - (void)start; 16 | 17 | - (void)stop; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /AudioRecorder/AudioRecorder.m: -------------------------------------------------------------------------------- 1 | // 2 | // AudioRecorder.m 3 | // AudioRecorder 4 | // 5 | // Created by apple on 2017/2/21. 6 | // Copyright © 2017年 xiaokai.zhan. All rights reserved. 7 | // 8 | 9 | /** 10 | * Setup AudioSession 11 | * 1: Category 12 | * 2: Set Listener 13 | * Interrupt Listener 14 | * AudioRoute Change Listener 15 | * Hardwate output Volume Listener 16 | * 3: Set IO BufferDuration 17 | * 4: Active AudioSession 18 | * 19 | * Setup AudioUnit 20 | * 1:Build AudioComponentDescription To Build AudioUnit Instance 21 | * 2:Build AudioStreamBasicDescription To Set AudioUnit Property 22 | * 3:Connect Node Or Set RenderCallback For AudioUnit 23 | * 4:Initialize AudioUnit 24 | * 5:Initialize AudioUnit 25 | * 6:AudioOutputUnitStart 26 | * 27 | **/ 28 | #import "AudioRecorder.h" 29 | #import "ELAudioSession.h" 30 | 31 | 32 | static const AudioUnitElement inputElement = 1; 33 | 34 | @interface AudioRecorder() 35 | @property(nonatomic, assign) AUGraph auGraph; 36 | @property(nonatomic, assign) AUNode ioNode; 37 | @property(nonatomic, assign) AudioUnit ioUnit; 38 | @property(nonatomic, assign) AUNode mixerNode; 39 | @property(nonatomic, assign) AudioUnit mixerUnit; 40 | @property(nonatomic, assign) AUNode convertNode; 41 | @property(nonatomic, assign) AudioUnit convertUnit; 42 | @property(nonatomic, assign) Float64 sampleRate; 43 | 44 | @end 45 | 46 | @implementation AudioRecorder 47 | { 48 | NSString* _destinationFilePath; 49 | ExtAudioFileRef finalAudioFile; 50 | } 51 | 52 | - (id) initWithPath:(NSString*) path { 53 | self = [super init]; 54 | if(self) { 55 | _sampleRate = 44100.0; 56 | _destinationFilePath = path; 57 | [[ELAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord]; 58 | [[ELAudioSession sharedInstance] setPreferredSampleRate:_sampleRate]; 59 | [[ELAudioSession sharedInstance] setActive:YES]; 60 | [[ELAudioSession sharedInstance] addRouteChangeListener]; 61 | [self addAudioSessionInterruptedObserver]; 62 | [self createAudioUnitGraph]; 63 | } 64 | return self; 65 | } 66 | 67 | 68 | - (void)createAudioUnitGraph 69 | { 70 | OSStatus status = NewAUGraph(&_auGraph); 71 | CheckStatus(status, @"Could not create a new AUGraph", YES); 72 | [self addAudioUnitNodes]; 73 | status = AUGraphOpen(_auGraph); 74 | CheckStatus(status, @"Could not open AUGraph", YES); 75 | [self getUnitsFromNodes]; 76 | [self setAudioUnitProperties]; 77 | [self makeNodeConnections]; 78 | CAShow(_auGraph); 79 | status = AUGraphInitialize(_auGraph); 80 | CheckStatus(status, @"Could not initialize AUGraph", YES); 81 | } 82 | 83 | - (void)addAudioUnitNodes 84 | { 85 | OSStatus status = noErr; 86 | AudioComponentDescription ioDescription; 87 | bzero(&ioDescription, sizeof(ioDescription)); 88 | ioDescription.componentManufacturer = kAudioUnitManufacturer_Apple; 89 | ioDescription.componentType = kAudioUnitType_Output; 90 | ioDescription.componentSubType = kAudioUnitSubType_RemoteIO; 91 | status = AUGraphAddNode(_auGraph, &ioDescription, &_ioNode); 92 | CheckStatus(status, @"Could not add I/O node to AUGraph", YES); 93 | 94 | AudioComponentDescription converterDescription; 95 | bzero(&converterDescription, sizeof(converterDescription)); 96 | converterDescription.componentType = kAudioUnitType_FormatConverter; 97 | converterDescription.componentSubType = kAudioUnitSubType_AUConverter; 98 | converterDescription.componentManufacturer = kAudioUnitManufacturer_Apple; 99 | status = AUGraphAddNode(_auGraph, &converterDescription, &_convertNode); 100 | CheckStatus(status, @"Could not add Converter node to AUGraph", YES); 101 | 102 | AudioComponentDescription mixerDescription; 103 | bzero(&mixerDescription, sizeof(mixerDescription)); 104 | mixerDescription.componentManufacturer = kAudioUnitManufacturer_Apple; 105 | mixerDescription.componentType = kAudioUnitType_Mixer; 106 | mixerDescription.componentSubType = kAudioUnitSubType_MultiChannelMixer; 107 | status = AUGraphAddNode(_auGraph, &mixerDescription, &_mixerNode); 108 | CheckStatus(status, @"Could not add mixer node to AUGraph", YES); 109 | } 110 | 111 | - (void)getUnitsFromNodes 112 | { 113 | OSStatus status = noErr; 114 | status = AUGraphNodeInfo(_auGraph, _ioNode, NULL, &_ioUnit); 115 | CheckStatus(status, @"Could not retrieve node info for I/O node", YES); 116 | status = AUGraphNodeInfo(_auGraph, _convertNode, NULL, &_convertUnit); 117 | CheckStatus(status, @"Could not retrieve node info for convert node", YES); 118 | status = AUGraphNodeInfo(_auGraph, _mixerNode, NULL, &_mixerUnit); 119 | CheckStatus(status, @"Could not retrieve node info for mixer node", YES); 120 | } 121 | 122 | - (void)setAudioUnitProperties 123 | { 124 | OSStatus status = noErr; 125 | AudioStreamBasicDescription stereoStreamFormat = [self noninterleavedPCMFormatWithChannels:2]; 126 | status = AudioUnitSetProperty(_ioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, inputElement, 127 | &stereoStreamFormat, sizeof(stereoStreamFormat)); 128 | CheckStatus(status, @"Could not set stream format on I/O unit output scope", YES); 129 | UInt32 enableIO = 1; 130 | status = AudioUnitSetProperty(_ioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, inputElement, 131 | &enableIO, sizeof(enableIO)); 132 | CheckStatus(status, @"Could not enable I/O on I/O unit input scope", YES); 133 | UInt32 mixerElementCount = 1; 134 | status = AudioUnitSetProperty(_mixerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, 135 | &mixerElementCount, sizeof(mixerElementCount)); 136 | CheckStatus(status, @"Could not set element count on mixer unit input scope", YES); 137 | status = AudioUnitSetProperty(_mixerUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, 138 | &_sampleRate, sizeof(_sampleRate)); 139 | CheckStatus(status, @"Could not set sample rate on mixer unit output scope", YES); 140 | 141 | UInt32 maximumFramesPerSlice = 4096; 142 | AudioUnitSetProperty ( 143 | _ioUnit, 144 | kAudioUnitProperty_MaximumFramesPerSlice, 145 | kAudioUnitScope_Global, 146 | 0, 147 | &maximumFramesPerSlice, 148 | sizeof (maximumFramesPerSlice) 149 | ); 150 | 151 | 152 | 153 | UInt32 bytesPerSample = sizeof (AudioUnitSampleType); 154 | AudioStreamBasicDescription _clientFormat32float; 155 | _clientFormat32float.mFormatID = kAudioFormatLinearPCM; 156 | _clientFormat32float.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; 157 | _clientFormat32float.mBytesPerPacket = bytesPerSample; 158 | _clientFormat32float.mFramesPerPacket = 1; 159 | _clientFormat32float.mBytesPerFrame = bytesPerSample; 160 | _clientFormat32float.mChannelsPerFrame = 2; 161 | _clientFormat32float.mBitsPerChannel = 8 * bytesPerSample; 162 | _clientFormat32float.mSampleRate = _sampleRate; 163 | AudioUnitSetProperty(_mixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &_clientFormat32float, sizeof(_clientFormat32float)); 164 | AudioUnitSetProperty(_ioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &_clientFormat32float, sizeof(_clientFormat32float)); 165 | AudioUnitSetProperty(_convertUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &stereoStreamFormat, sizeof(stereoStreamFormat)); 166 | AudioUnitSetProperty(_convertUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &_clientFormat32float, sizeof(_clientFormat32float)); 167 | } 168 | 169 | - (void)prepareFinalWriteFile{ 170 | AudioStreamBasicDescription destinationFormat; 171 | memset(&destinationFormat, 0, sizeof(destinationFormat)); 172 | 173 | destinationFormat.mFormatID = kAudioFormatLinearPCM; 174 | destinationFormat.mSampleRate = _sampleRate; 175 | // if we want pcm, default to signed 16-bit little-endian 176 | destinationFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; 177 | destinationFormat.mBitsPerChannel = 16; 178 | destinationFormat.mChannelsPerFrame = 2; 179 | destinationFormat.mBytesPerPacket = destinationFormat.mBytesPerFrame = (destinationFormat.mBitsPerChannel / 8) * destinationFormat.mChannelsPerFrame; 180 | destinationFormat.mFramesPerPacket = 1; 181 | 182 | UInt32 size = sizeof(destinationFormat); 183 | OSStatus result = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &destinationFormat); 184 | 185 | if(result) printf("AudioFormatGetProperty %d \n", (int)result); 186 | CFURLRef destinationURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, 187 | (CFStringRef)_destinationFilePath, 188 | kCFURLPOSIXPathStyle, 189 | false); 190 | 191 | // specify codec Saving the output in .m4a format 192 | result = ExtAudioFileCreateWithURL(destinationURL, 193 | kAudioFileCAFType, 194 | &destinationFormat, 195 | NULL, 196 | kAudioFileFlags_EraseFile, 197 | &finalAudioFile); 198 | if(result) printf("ExtAudioFileCreateWithURL %d \n", (int)result); 199 | CFRelease(destinationURL); 200 | 201 | // // This is a very important part and easiest way to set the ASBD for the File with correct format. 202 | AudioStreamBasicDescription clientFormat; 203 | UInt32 fSize = sizeof (clientFormat); 204 | memset(&clientFormat, 0, sizeof(clientFormat)); 205 | // get the audio data format from the Output Unit 206 | CheckStatus(AudioUnitGetProperty(_mixerUnit, 207 | kAudioUnitProperty_StreamFormat, 208 | kAudioUnitScope_Output, 209 | 0, 210 | &clientFormat, 211 | &fSize),@"AudioUnitGetProperty on failed", YES); 212 | 213 | // set the audio data format of mixer Unit 214 | CheckStatus(ExtAudioFileSetProperty(finalAudioFile, 215 | kExtAudioFileProperty_ClientDataFormat, 216 | sizeof(clientFormat), 217 | &clientFormat), 218 | @"ExtAudioFileSetProperty kExtAudioFileProperty_ClientDataFormat failed", YES); 219 | 220 | 221 | // specify codec 222 | UInt32 codec = kAppleHardwareAudioCodecManufacturer; 223 | CheckStatus(ExtAudioFileSetProperty(finalAudioFile, 224 | kExtAudioFileProperty_CodecManufacturer, 225 | sizeof(codec), 226 | &codec),@"ExtAudioFileSetProperty on extAudioFile Faild", YES); 227 | 228 | CheckStatus(ExtAudioFileWriteAsync(finalAudioFile, 0, NULL),@"ExtAudioFileWriteAsync Failed", YES); 229 | } 230 | 231 | static OSStatus renderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) 232 | { 233 | OSStatus result = noErr; 234 | __unsafe_unretained AudioRecorder *THIS = (__bridge AudioRecorder *)inRefCon; 235 | AudioUnitRender(THIS->_mixerUnit, ioActionFlags, inTimeStamp, 0, inNumberFrames, ioData); 236 | result = ExtAudioFileWriteAsync(THIS->finalAudioFile, inNumberFrames, ioData); 237 | return result; 238 | } 239 | 240 | - (void)makeNodeConnections 241 | { 242 | OSStatus status = noErr; 243 | status = AUGraphConnectNodeInput(_auGraph, _ioNode, 1, _convertNode, 0); 244 | CheckStatus(status, @"Could not connect I/O node input to convert node input", YES); 245 | status = AUGraphConnectNodeInput(_auGraph, _convertNode, 0, _mixerNode, 0); 246 | CheckStatus(status, @"Could not connect I/O node input to mixer node input", YES); 247 | AURenderCallbackStruct finalRenderProc; 248 | finalRenderProc.inputProc = &renderCallback; 249 | finalRenderProc.inputProcRefCon = (__bridge void *)self; 250 | status = AUGraphSetNodeInputCallback(_auGraph, _ioNode, 0, &finalRenderProc); 251 | CheckStatus(status, @"Could not set InputCallback For IONode", YES); 252 | 253 | // status = AUGraphConnectNodeInput(_auGraph, _mixerNode, 0, _ioNode, 0); 254 | // CheckStatus(status, @"Could not connect mixer node output to I/O node input", YES); 255 | } 256 | 257 | - (void)dealloc 258 | { 259 | [self destroyAudioUnitGraph]; 260 | } 261 | 262 | - (AudioStreamBasicDescription)noninterleavedPCMFormatWithChannels:(UInt32)channels 263 | { 264 | UInt32 bytesPerSample = sizeof(AudioUnitSampleType); 265 | 266 | AudioStreamBasicDescription asbd; 267 | bzero(&asbd, sizeof(asbd)); 268 | asbd.mSampleRate = _sampleRate; 269 | asbd.mFormatID = kAudioFormatLinearPCM; 270 | // asbd.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; 271 | asbd.mFormatFlags = kAudioFormatFlagsAudioUnitCanonical | kAudioFormatFlagIsNonInterleaved; 272 | asbd.mBitsPerChannel = 8 * bytesPerSample; 273 | asbd.mBytesPerFrame = bytesPerSample; 274 | asbd.mBytesPerPacket = bytesPerSample; 275 | asbd.mFramesPerPacket = 1; 276 | asbd.mChannelsPerFrame = channels; 277 | 278 | return asbd; 279 | } 280 | 281 | - (void)destroyAudioUnitGraph 282 | { 283 | AUGraphStop(_auGraph); 284 | AUGraphUninitialize(_auGraph); 285 | AUGraphClose(_auGraph); 286 | AUGraphRemoveNode(_auGraph, _mixerNode); 287 | AUGraphRemoveNode(_auGraph, _ioNode); 288 | DisposeAUGraph(_auGraph); 289 | _ioUnit = NULL; 290 | _mixerUnit = NULL; 291 | _mixerNode = 0; 292 | _ioNode = 0; 293 | _auGraph = NULL; 294 | } 295 | 296 | - (void)start 297 | { 298 | [self prepareFinalWriteFile]; 299 | OSStatus status = AUGraphStart(_auGraph); 300 | CheckStatus(status, @"Could not start AUGraph", YES); 301 | } 302 | 303 | - (void)stop 304 | { 305 | OSStatus status = AUGraphStop(_auGraph); 306 | CheckStatus(status, @"Could not stop AUGraph", YES); 307 | ExtAudioFileDispose(finalAudioFile); 308 | } 309 | 310 | // AudioSession 被打断的通知 311 | - (void)addAudioSessionInterruptedObserver 312 | { 313 | [self removeAudioSessionInterruptedObserver]; 314 | [[NSNotificationCenter defaultCenter] addObserver:self 315 | selector:@selector(onNotificationAudioInterrupted:) 316 | name:AVAudioSessionInterruptionNotification 317 | object:[AVAudioSession sharedInstance]]; 318 | } 319 | 320 | - (void)removeAudioSessionInterruptedObserver 321 | { 322 | [[NSNotificationCenter defaultCenter] removeObserver:self 323 | name:AVAudioSessionInterruptionNotification 324 | object:nil]; 325 | } 326 | 327 | - (void)onNotificationAudioInterrupted:(NSNotification *)sender { 328 | AVAudioSessionInterruptionType interruptionType = [[[sender userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] unsignedIntegerValue]; 329 | switch (interruptionType) { 330 | case AVAudioSessionInterruptionTypeBegan: 331 | [self stop]; 332 | break; 333 | case AVAudioSessionInterruptionTypeEnded: 334 | [self start]; 335 | break; 336 | default: 337 | break; 338 | } 339 | } 340 | 341 | static void CheckStatus(OSStatus status, NSString *message, BOOL fatal) 342 | { 343 | if(status != noErr) 344 | { 345 | char fourCC[16]; 346 | *(UInt32 *)fourCC = CFSwapInt32HostToBig(status); 347 | fourCC[4] = '\0'; 348 | 349 | if(isprint(fourCC[0]) && isprint(fourCC[1]) && isprint(fourCC[2]) && isprint(fourCC[3])) 350 | NSLog(@"%@: %s", message, fourCC); 351 | else 352 | NSLog(@"%@: %d", message, (int)status); 353 | 354 | if(fatal) 355 | exit(-1); 356 | } 357 | } 358 | @end 359 | -------------------------------------------------------------------------------- /AudioRecorder/AudioSession/AVAudioSession+RouteUtils.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface AVAudioSession (RouteUtils) 4 | 5 | - (BOOL)usingBlueTooth; 6 | 7 | - (BOOL)usingWiredMicrophone; 8 | 9 | - (BOOL)shouldShowEarphoneAlert; 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /AudioRecorder/AudioSession/AVAudioSession+RouteUtils.m: -------------------------------------------------------------------------------- 1 | #import "AVAudioSession+RouteUtils.h" 2 | 3 | 4 | // /* input port types */ 5 | // AVF_EXPORT NSString *const AVAudioSessionPortLineIn NS_AVAILABLE_IOS(6_0); /* Line level input on a dock connector */ 6 | // AVF_EXPORT NSString *const AVAudioSessionPortBuiltInMic NS_AVAILABLE_IOS(6_0); /* Built-in microphone on an iOS device */ 7 | // AVF_EXPORT NSString *const AVAudioSessionPortHeadsetMic NS_AVAILABLE_IOS(6_0); /* Microphone on a wired headset. Headset refers to an 8 | // 9 | // /* output port types */ 10 | // AVF_EXPORT NSString *const AVAudioSessionPortLineOut NS_AVAILABLE_IOS(6_0); /* Line level output on a dock connector */ 11 | // AVF_EXPORT NSString *const AVAudioSessionPortHeadphones NS_AVAILABLE_IOS(6_0); /* Headphone or headset output */ 12 | // AVF_EXPORT NSString *const AVAudioSessionPortBluetoothA2DP NS_AVAILABLE_IOS(6_0); /* Output on a Bluetooth A2DP device */ 13 | // AVF_EXPORT NSString *const AVAudioSessionPortBuiltInReceiver NS_AVAILABLE_IOS(6_0); /* The speaker you hold to your ear when on a phone call */ 14 | // AVF_EXPORT NSString *const AVAudioSessionPortBuiltInSpeaker NS_AVAILABLE_IOS(6_0); /* Built-in speaker on an iOS device */ 15 | // AVF_EXPORT NSString *const AVAudioSessionPortHDMI NS_AVAILABLE_IOS(6_0); /* Output via High-Definition Multimedia Interface */ 16 | // AVF_EXPORT NSString *const AVAudioSessionPortAirPlay NS_AVAILABLE_IOS(6_0); /* Output on a remote Air Play device */ 17 | // AVF_EXPORT NSString *const AVAudioSessionPortBluetoothLE NS_AVAILABLE_IOS(7_0); /* Output on a Bluetooth Low Energy device */ 18 | // 19 | // /* port types that refer to either input or output */ 20 | // AVF_EXPORT NSString *const AVAudioSessionPortBluetoothHFP NS_AVAILABLE_IOS(6_0); /* Input or output on a Bluetooth Hands-Free Profile device */ 21 | // AVF_EXPORT NSString *const AVAudioSessionPortUSBAudio NS_AVAILABLE_IOS(6_0); /* Input or output on a Universal Serial Bus device */ 22 | // AVF_EXPORT NSString *const AVAudioSessionPortCarAudio NS_AVAILABLE_IOS(7_0); /* Input or output via Car Audio */ 23 | 24 | 25 | @implementation AVAudioSession (RouteUtils) 26 | 27 | - (BOOL)usingBlueTooth { 28 | NSArray *inputs = self.currentRoute.inputs; 29 | NSArray *blueToothInputRoutes = @[AVAudioSessionPortBluetoothHFP]; 30 | for (AVAudioSessionPortDescription *description in inputs) { 31 | if ([blueToothInputRoutes containsObject:description.portType]) { 32 | return YES; 33 | } 34 | } 35 | 36 | NSArray *outputs = self.currentRoute.outputs; 37 | NSArray *blueToothOutputRoutes = @[AVAudioSessionPortBluetoothHFP, AVAudioSessionPortBluetoothA2DP, AVAudioSessionPortBluetoothLE]; 38 | for (AVAudioSessionPortDescription *description in outputs) { 39 | if ([blueToothOutputRoutes containsObject:description.portType]) { 40 | return YES; 41 | } 42 | } 43 | 44 | return NO; 45 | } 46 | 47 | - (BOOL)usingWiredMicrophone { 48 | NSArray *inputs = self.currentRoute.inputs; 49 | NSArray *headSetInputRoutes = @[AVAudioSessionPortHeadsetMic]; 50 | for (AVAudioSessionPortDescription *description in inputs) { 51 | if ([headSetInputRoutes containsObject:description.portType]) { 52 | return YES; 53 | } 54 | } 55 | 56 | NSArray *outputs = self.currentRoute.outputs; 57 | NSArray *headSetOutputRoutes = @[AVAudioSessionPortHeadphones, AVAudioSessionPortUSBAudio]; 58 | for (AVAudioSessionPortDescription *description in outputs) { 59 | if ([headSetOutputRoutes containsObject:description.portType]) { 60 | return YES; 61 | } 62 | } 63 | return NO; 64 | } 65 | 66 | - (BOOL)shouldShowEarphoneAlert 67 | { 68 | // 用户如果没有带耳机,则应该提出提示,目前采用保守策略,即尽量减少alert弹出,所以,我们认为只要不是用手机内置的听筒或者喇叭作为声音外放的,都认为用户带了耳机 69 | NSArray *outputs = self.currentRoute.outputs; 70 | NSArray *headSetOutputRoutes = @[AVAudioSessionPortBuiltInReceiver, AVAudioSessionPortBuiltInSpeaker]; 71 | for (AVAudioSessionPortDescription *description in outputs) { 72 | if ([headSetOutputRoutes containsObject:description.portType]) { 73 | return YES; 74 | } 75 | } 76 | return NO; 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /AudioRecorder/AudioSession/ELAudioSession.h: -------------------------------------------------------------------------------- 1 | // 2 | // ELAudioSession.h 3 | // video_player 4 | // 5 | // Created by apple on 16/9/5. 6 | // Copyright © 2016年 xiaokai.zhan. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | extern const NSTimeInterval AUSAudioSessionLatency_Background; 13 | extern const NSTimeInterval AUSAudioSessionLatency_Default; 14 | extern const NSTimeInterval AUSAudioSessionLatency_LowLatency; 15 | 16 | @interface ELAudioSession : NSObject 17 | 18 | + (ELAudioSession *)sharedInstance; 19 | 20 | @property(nonatomic, strong) AVAudioSession *audioSession; // Underlying system audio session 21 | @property(nonatomic, assign) Float64 preferredSampleRate; 22 | @property(nonatomic, assign, readonly) Float64 currentSampleRate; 23 | @property(nonatomic, assign) NSTimeInterval preferredLatency; 24 | @property(nonatomic, assign) BOOL active; 25 | @property(nonatomic, strong) NSString *category; 26 | 27 | - (void)addRouteChangeListener; 28 | @end 29 | -------------------------------------------------------------------------------- /AudioRecorder/AudioSession/ELAudioSession.m: -------------------------------------------------------------------------------- 1 | // 2 | // ELAudioSession.m 3 | // video_player 4 | // 5 | // Created by apple on 16/9/5. 6 | // Copyright © 2016年 xiaokai.zhan. All rights reserved. 7 | // 8 | 9 | #import "ELAudioSession.h" 10 | #import "AVAudioSession+RouteUtils.h" 11 | 12 | const NSTimeInterval AUSAudioSessionLatency_Background = 0.0929; 13 | const NSTimeInterval AUSAudioSessionLatency_Default = 0.0232; 14 | const NSTimeInterval AUSAudioSessionLatency_LowLatency = 0.0058; 15 | 16 | @implementation ELAudioSession 17 | 18 | + (ELAudioSession *)sharedInstance 19 | { 20 | static ELAudioSession *instance = NULL; 21 | static dispatch_once_t onceToken; 22 | dispatch_once(&onceToken, ^{ 23 | instance = [[ELAudioSession alloc] init]; 24 | }); 25 | return instance; 26 | } 27 | 28 | - (id)init 29 | { 30 | if((self = [super init])) 31 | { 32 | _preferredSampleRate = _currentSampleRate = 44100.0; 33 | _audioSession = [AVAudioSession sharedInstance]; 34 | } 35 | return self; 36 | } 37 | 38 | - (void)setCategory:(NSString *)category 39 | { 40 | _category = category; 41 | 42 | NSError *error = nil; 43 | if(![self.audioSession setCategory:_category error:&error]) 44 | NSLog(@"Could note set category on audio session: %@", error.localizedDescription); 45 | } 46 | 47 | - (void)setActive:(BOOL)active 48 | { 49 | _active = active; 50 | 51 | NSError *error = nil; 52 | 53 | if(![self.audioSession setPreferredSampleRate:self.preferredSampleRate error:&error]) 54 | NSLog(@"Error when setting sample rate on audio session: %@", error.localizedDescription); 55 | 56 | if(![self.audioSession setActive:_active error:&error]) 57 | NSLog(@"Error when setting active state of audio session: %@", error.localizedDescription); 58 | 59 | _currentSampleRate = [self.audioSession sampleRate]; 60 | } 61 | 62 | - (void)setPreferredLatency:(NSTimeInterval)preferredLatency 63 | { 64 | _preferredLatency = preferredLatency; 65 | 66 | NSError *error = nil; 67 | if(![self.audioSession setPreferredIOBufferDuration:_preferredLatency error:&error]) 68 | NSLog(@"Error when setting preferred I/O buffer duration"); 69 | } 70 | 71 | - (void)addRouteChangeListener 72 | { 73 | [[NSNotificationCenter defaultCenter] addObserver:self 74 | selector:@selector(onNotificationAudioRouteChange:) 75 | name:AVAudioSessionRouteChangeNotification 76 | object:nil]; 77 | [self adjustOnRouteChange]; 78 | } 79 | 80 | #pragma mark - notification observer 81 | 82 | - (void)onNotificationAudioRouteChange:(NSNotification *)sender { 83 | [self adjustOnRouteChange]; 84 | } 85 | 86 | - (void)adjustOnRouteChange 87 | { 88 | AVAudioSessionRouteDescription *currentRoute = [[AVAudioSession sharedInstance] currentRoute]; 89 | if (currentRoute) { 90 | if ([[AVAudioSession sharedInstance] usingWiredMicrophone]) { 91 | } else { 92 | if (![[AVAudioSession sharedInstance] usingBlueTooth]) { 93 | [[AVAudioSession sharedInstance] overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil]; 94 | } 95 | } 96 | } 97 | } 98 | @end 99 | -------------------------------------------------------------------------------- /AudioRecorder/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /AudioRecorder/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSMicrophoneUsageDescription 6 | microphoneDesciption 7 | CFBundleDevelopmentRegion 8 | en 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /AudioRecorder/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // AudioRecorder 4 | // 5 | // Created by apple on 2017/2/16. 6 | // Copyright © 2017年 xiaokai.zhan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /AudioRecorder/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // AudioRecorder 4 | // 5 | // Created by apple on 2017/2/16. 6 | // Copyright © 2017年 xiaokai.zhan. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "AudioRecorder.h" 11 | #import "CommonUtil.h" 12 | 13 | @interface ViewController () 14 | 15 | @end 16 | 17 | @implementation ViewController 18 | { 19 | AudioRecorder* _recorder; 20 | } 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | // Do any additional setup after loading the view, typically from a nib. 24 | } 25 | 26 | - (IBAction)record:(id)sender { 27 | NSLog(@"Forward To Recorder Page..."); 28 | NSString* filePath = [CommonUtil documentsPath:@"recorder.caf"]; 29 | _recorder = [[AudioRecorder alloc] initWithPath:filePath]; 30 | [_recorder start]; 31 | } 32 | 33 | - (IBAction)stop:(id)sender { 34 | if(_recorder) { 35 | [_recorder stop]; 36 | } 37 | } 38 | 39 | - (void)didReceiveMemoryWarning { 40 | [super didReceiveMemoryWarning]; 41 | // Dispose of any resources that can be recreated. 42 | } 43 | 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /AudioRecorder/ViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 29 | 37 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /AudioRecorder/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // AudioRecorder 4 | // 5 | // Created by apple on 2017/2/16. 6 | // Copyright © 2017年 xiaokai.zhan. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /AudioRecorder/utils/CommonUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // CommonUtil.h 3 | // video_player 4 | // 5 | // Created by apple on 16/8/25. 6 | // Copyright © 2016年 xiaokai.zhan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CommonUtil : NSObject 12 | 13 | +(NSString *)bundlePath:(NSString *)fileName; 14 | 15 | +(NSString *)documentsPath:(NSString *)fileName; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /AudioRecorder/utils/CommonUtil.m: -------------------------------------------------------------------------------- 1 | // 2 | // CommonUtil.m 3 | // video_player 4 | // 5 | // Created by apple on 16/8/25. 6 | // Copyright © 2016年 xiaokai.zhan. All rights reserved. 7 | // 8 | 9 | #import "CommonUtil.h" 10 | 11 | @implementation CommonUtil 12 | 13 | +(NSString *)bundlePath:(NSString *)fileName { 14 | return [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:fileName]; 15 | } 16 | 17 | +(NSString *)documentsPath:(NSString *)fileName { 18 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 19 | NSString *documentsDirectory = [paths objectAtIndex:0]; 20 | return [documentsDirectory stringByAppendingPathComponent:fileName]; 21 | } 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /AudioRecorderTests/AudioRecorderTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // AudioRecorderTests.m 3 | // AudioRecorderTests 4 | // 5 | // Created by apple on 2017/2/16. 6 | // Copyright © 2017年 xiaokai.zhan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AudioRecorderTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation AudioRecorderTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /AudioRecorderTests/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 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /AudioRecorderUITests/AudioRecorderUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // AudioRecorderUITests.m 3 | // AudioRecorderUITests 4 | // 5 | // Created by apple on 2017/2/16. 6 | // Copyright © 2017年 xiaokai.zhan. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AudioRecorderUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation AudioRecorderUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /AudioRecorderUITests/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 | CFBundleVersion 20 | 1 21 | 22 | 23 | --------------------------------------------------------------------------------