├── .gitignore ├── AudioUnitSamples.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── AudioUnitSamples.xcscheme ├── AudioUnitSamples ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── AudioUnitSamples.entitlements ├── Base.lproj │ └── LaunchScreen.storyboard ├── Common │ ├── AUGraphMixer.h │ ├── AUGraphMixer.mm │ ├── AUGraphRecorderPlayer.h │ ├── AUGraphRecorderPlayer.mm │ ├── AudioFileReader.h │ ├── AudioFileReader.mm │ ├── AudioFileWriter.h │ ├── AudioFileWriter.mm │ ├── AudioUnitPlayer.h │ ├── AudioUnitPlayer.mm │ ├── AudioUnitRecorder.h │ ├── AudioUnitRecorder.mm │ ├── AudioUnitRecorderAndPlayer.h │ ├── AudioUnitRecorderAndPlayer.mm │ ├── AudioUnitRecorderAndPlayerComplex.h │ ├── AudioUnitRecorderAndPlayerComplex.mm │ ├── CommonUtils.h │ └── CommonUtils.mm ├── Info.plist ├── SampleListViewController.h ├── SampleListViewController.m ├── SampleListViewController.xib ├── SamplesViewControllers │ ├── AUGraphRecordAndPlaySampleViewController.h │ ├── AUGraphRecordAndPlaySampleViewController.mm │ ├── AUGraphRecordAndPlaySampleViewController.xib │ ├── AudioUnitPlayerViewController.h │ ├── AudioUnitPlayerViewController.mm │ ├── AudioUnitPlayerViewController.xib │ ├── AudioUnitRecordOnlyViewController.h │ ├── AudioUnitRecordOnlyViewController.mm │ ├── AudioUnitRecordOnlyViewController.xib │ ├── BasicRecordAndPlaySampleViewController.h │ ├── BasicRecordAndPlaySampleViewController.mm │ ├── BasicRecordAndPlaySampleViewController.xib │ ├── ComplexRecordAndPlaySampleViewController.h │ ├── ComplexRecordAndPlaySampleViewController.mm │ └── ComplexRecordAndPlaySampleViewController.xib ├── ViewController.h ├── ViewController.m └── main.m ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by http://www.gitignore.io 2 | 3 | ### System ### 4 | .DS_Store 5 | 6 | ### Xcode ### 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.xcuserstate 21 | 22 | 23 | ### Objective-C ### 24 | # Xcode 25 | # 26 | build/ 27 | /Index 28 | *.pbxuser 29 | !default.pbxuser 30 | *.mode1v3 31 | !default.mode1v3 32 | *.mode2v3 33 | !default.mode2v3 34 | *.perspectivev3 35 | !default.perspectivev3 36 | xcuserdata 37 | *.xccheckout 38 | *.moved-aside 39 | DerivedData 40 | *.hmap 41 | *.ipa 42 | *.xcuserstate 43 | 44 | # CocoaPods 45 | # 46 | # We recommend against adding the Pods directory to your .gitignore. However 47 | # you should judge for yourself, the pros and cons are mentioned at: 48 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 49 | # 50 | Pods/ 51 | Podfile.lock 52 | ktv.xcworkspace 53 | 54 | ### AppCode ### 55 | .idea/ 56 | 57 | ### GTM ### 58 | /.gtm/ 59 | -------------------------------------------------------------------------------- /AudioUnitSamples.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D4003022279FED46002E9D53 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D4003021279FED46002E9D53 /* AppDelegate.m */; }; 11 | D4003028279FED46002E9D53 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D4003027279FED46002E9D53 /* ViewController.m */; }; 12 | D400302D279FED47002E9D53 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D400302C279FED47002E9D53 /* Assets.xcassets */; }; 13 | D4003030279FED47002E9D53 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D400302E279FED47002E9D53 /* LaunchScreen.storyboard */; }; 14 | D4003033279FED47002E9D53 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = D4003032279FED47002E9D53 /* main.m */; }; 15 | D400303C279FEDF3002E9D53 /* BasicRecordAndPlaySampleViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = D400303A279FEDF3002E9D53 /* BasicRecordAndPlaySampleViewController.mm */; }; 16 | D400303D279FEDF3002E9D53 /* BasicRecordAndPlaySampleViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D400303B279FEDF3002E9D53 /* BasicRecordAndPlaySampleViewController.xib */; }; 17 | D4003041279FEE60002E9D53 /* SampleListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D400303F279FEE60002E9D53 /* SampleListViewController.m */; }; 18 | D4003042279FEE60002E9D53 /* SampleListViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D4003040279FEE60002E9D53 /* SampleListViewController.xib */; }; 19 | D4003045279FF307002E9D53 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4003044279FF307002E9D53 /* AudioToolbox.framework */; }; 20 | D4003049279FFFA5002E9D53 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4003048279FFFA5002E9D53 /* AVFoundation.framework */; }; 21 | D40C2C6A27A15022002E0ABA /* AudioUnitRecordOnlyViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = D40C2C6827A15022002E0ABA /* AudioUnitRecordOnlyViewController.mm */; }; 22 | D40C2C6B27A15022002E0ABA /* AudioUnitRecordOnlyViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D40C2C6927A15022002E0ABA /* AudioUnitRecordOnlyViewController.xib */; }; 23 | D40C2C6E27A157C8002E0ABA /* CommonUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = D40C2C6D27A157C8002E0ABA /* CommonUtils.mm */; }; 24 | D40C2C7227A16668002E0ABA /* AudioFileWriter.mm in Sources */ = {isa = PBXBuildFile; fileRef = D40C2C7127A16668002E0ABA /* AudioFileWriter.mm */; }; 25 | D40C2C7527A1948D002E0ABA /* AudioUnitRecorder.mm in Sources */ = {isa = PBXBuildFile; fileRef = D40C2C7427A1948D002E0ABA /* AudioUnitRecorder.mm */; }; 26 | D40C2C7827A1990C002E0ABA /* AudioUnitPlayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = D40C2C7727A1990C002E0ABA /* AudioUnitPlayer.mm */; }; 27 | D40C2C7C27A19958002E0ABA /* AudioUnitPlayerViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = D40C2C7A27A19958002E0ABA /* AudioUnitPlayerViewController.mm */; }; 28 | D40C2C7D27A19958002E0ABA /* AudioUnitPlayerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D40C2C7B27A19958002E0ABA /* AudioUnitPlayerViewController.xib */; }; 29 | D40C2C8027A19BFC002E0ABA /* AudioFileReader.mm in Sources */ = {isa = PBXBuildFile; fileRef = D40C2C7F27A19BFC002E0ABA /* AudioFileReader.mm */; }; 30 | D40C2C8327A1B77B002E0ABA /* AudioUnitRecorderAndPlayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = D40C2C8227A1B77B002E0ABA /* AudioUnitRecorderAndPlayer.mm */; }; 31 | D4F8168927A27D4B0017C4B8 /* AudioUnitRecorderAndPlayerComplex.mm in Sources */ = {isa = PBXBuildFile; fileRef = D4F8168827A27D4B0017C4B8 /* AudioUnitRecorderAndPlayerComplex.mm */; }; 32 | D4F8168D27A27FDF0017C4B8 /* ComplexRecordAndPlaySampleViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D4F8168A27A27FDF0017C4B8 /* ComplexRecordAndPlaySampleViewController.xib */; }; 33 | D4F8168E27A27FDF0017C4B8 /* ComplexRecordAndPlaySampleViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = D4F8168C27A27FDF0017C4B8 /* ComplexRecordAndPlaySampleViewController.mm */; }; 34 | D4F8169127A377730017C4B8 /* AUGraphRecorderPlayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = D4F8169027A377730017C4B8 /* AUGraphRecorderPlayer.mm */; }; 35 | D4F8169527A462150017C4B8 /* AUGraphRecordAndPlaySampleViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D4F8169227A462150017C4B8 /* AUGraphRecordAndPlaySampleViewController.xib */; }; 36 | D4F8169627A462150017C4B8 /* AUGraphRecordAndPlaySampleViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = D4F8169327A462150017C4B8 /* AUGraphRecordAndPlaySampleViewController.mm */; }; 37 | D4F8169827A562220017C4B8 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4F8169727A562220017C4B8 /* CoreAudio.framework */; }; 38 | D4F8169B27A57FCA0017C4B8 /* AUGraphMixer.mm in Sources */ = {isa = PBXBuildFile; fileRef = D4F8169A27A57FCA0017C4B8 /* AUGraphMixer.mm */; }; 39 | /* End PBXBuildFile section */ 40 | 41 | /* Begin PBXFileReference section */ 42 | D400301D279FED46002E9D53 /* AudioUnitSamples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AudioUnitSamples.app; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | D4003020279FED46002E9D53 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 44 | D4003021279FED46002E9D53 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 45 | D4003026279FED46002E9D53 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 46 | D4003027279FED46002E9D53 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 47 | D400302C279FED47002E9D53 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 48 | D400302F279FED47002E9D53 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 49 | D4003031279FED47002E9D53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | D4003032279FED47002E9D53 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 51 | D4003039279FEDF3002E9D53 /* BasicRecordAndPlaySampleViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BasicRecordAndPlaySampleViewController.h; sourceTree = ""; }; 52 | D400303A279FEDF3002E9D53 /* BasicRecordAndPlaySampleViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = BasicRecordAndPlaySampleViewController.mm; sourceTree = ""; }; 53 | D400303B279FEDF3002E9D53 /* BasicRecordAndPlaySampleViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BasicRecordAndPlaySampleViewController.xib; sourceTree = ""; }; 54 | D400303E279FEE60002E9D53 /* SampleListViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SampleListViewController.h; sourceTree = ""; }; 55 | D400303F279FEE60002E9D53 /* SampleListViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleListViewController.m; sourceTree = ""; }; 56 | D4003040279FEE60002E9D53 /* SampleListViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SampleListViewController.xib; sourceTree = ""; }; 57 | D4003044279FF307002E9D53 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; 58 | D4003046279FF30C002E9D53 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; }; 59 | D4003048279FFFA5002E9D53 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; 60 | D40C2C6727A15022002E0ABA /* AudioUnitRecordOnlyViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AudioUnitRecordOnlyViewController.h; sourceTree = ""; }; 61 | D40C2C6827A15022002E0ABA /* AudioUnitRecordOnlyViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AudioUnitRecordOnlyViewController.mm; sourceTree = ""; }; 62 | D40C2C6927A15022002E0ABA /* AudioUnitRecordOnlyViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AudioUnitRecordOnlyViewController.xib; sourceTree = ""; }; 63 | D40C2C6C27A157C8002E0ABA /* CommonUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CommonUtils.h; sourceTree = ""; }; 64 | D40C2C6D27A157C8002E0ABA /* CommonUtils.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CommonUtils.mm; sourceTree = ""; }; 65 | D40C2C7027A16668002E0ABA /* AudioFileWriter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AudioFileWriter.h; sourceTree = ""; }; 66 | D40C2C7127A16668002E0ABA /* AudioFileWriter.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AudioFileWriter.mm; sourceTree = ""; }; 67 | D40C2C7327A1948D002E0ABA /* AudioUnitRecorder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AudioUnitRecorder.h; sourceTree = ""; }; 68 | D40C2C7427A1948D002E0ABA /* AudioUnitRecorder.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AudioUnitRecorder.mm; sourceTree = ""; }; 69 | D40C2C7627A1990C002E0ABA /* AudioUnitPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioUnitPlayer.h; sourceTree = ""; }; 70 | D40C2C7727A1990C002E0ABA /* AudioUnitPlayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AudioUnitPlayer.mm; sourceTree = ""; }; 71 | D40C2C7927A19958002E0ABA /* AudioUnitPlayerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AudioUnitPlayerViewController.h; sourceTree = ""; }; 72 | D40C2C7A27A19958002E0ABA /* AudioUnitPlayerViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AudioUnitPlayerViewController.mm; sourceTree = ""; }; 73 | D40C2C7B27A19958002E0ABA /* AudioUnitPlayerViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AudioUnitPlayerViewController.xib; sourceTree = ""; }; 74 | D40C2C7E27A19BFC002E0ABA /* AudioFileReader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AudioFileReader.h; sourceTree = ""; }; 75 | D40C2C7F27A19BFC002E0ABA /* AudioFileReader.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AudioFileReader.mm; sourceTree = ""; }; 76 | D40C2C8127A1B77B002E0ABA /* AudioUnitRecorderAndPlayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AudioUnitRecorderAndPlayer.h; sourceTree = ""; }; 77 | D40C2C8227A1B77B002E0ABA /* AudioUnitRecorderAndPlayer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AudioUnitRecorderAndPlayer.mm; sourceTree = ""; }; 78 | D410515E27A134ED00764794 /* AudioUnitSamples.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AudioUnitSamples.entitlements; sourceTree = ""; }; 79 | D4F8168727A27D4B0017C4B8 /* AudioUnitRecorderAndPlayerComplex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioUnitRecorderAndPlayerComplex.h; sourceTree = ""; }; 80 | D4F8168827A27D4B0017C4B8 /* AudioUnitRecorderAndPlayerComplex.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AudioUnitRecorderAndPlayerComplex.mm; sourceTree = ""; }; 81 | D4F8168A27A27FDF0017C4B8 /* ComplexRecordAndPlaySampleViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ComplexRecordAndPlaySampleViewController.xib; sourceTree = ""; }; 82 | D4F8168B27A27FDF0017C4B8 /* ComplexRecordAndPlaySampleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ComplexRecordAndPlaySampleViewController.h; sourceTree = ""; }; 83 | D4F8168C27A27FDF0017C4B8 /* ComplexRecordAndPlaySampleViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ComplexRecordAndPlaySampleViewController.mm; sourceTree = ""; }; 84 | D4F8168F27A377730017C4B8 /* AUGraphRecorderPlayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AUGraphRecorderPlayer.h; sourceTree = ""; }; 85 | D4F8169027A377730017C4B8 /* AUGraphRecorderPlayer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AUGraphRecorderPlayer.mm; sourceTree = ""; }; 86 | D4F8169227A462150017C4B8 /* AUGraphRecordAndPlaySampleViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AUGraphRecordAndPlaySampleViewController.xib; sourceTree = ""; }; 87 | D4F8169327A462150017C4B8 /* AUGraphRecordAndPlaySampleViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AUGraphRecordAndPlaySampleViewController.mm; sourceTree = ""; }; 88 | D4F8169427A462150017C4B8 /* AUGraphRecordAndPlaySampleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AUGraphRecordAndPlaySampleViewController.h; sourceTree = ""; }; 89 | D4F8169727A562220017C4B8 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; }; 90 | D4F8169927A57FCA0017C4B8 /* AUGraphMixer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AUGraphMixer.h; sourceTree = ""; }; 91 | D4F8169A27A57FCA0017C4B8 /* AUGraphMixer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AUGraphMixer.mm; sourceTree = ""; }; 92 | /* End PBXFileReference section */ 93 | 94 | /* Begin PBXFrameworksBuildPhase section */ 95 | D400301A279FED46002E9D53 /* Frameworks */ = { 96 | isa = PBXFrameworksBuildPhase; 97 | buildActionMask = 2147483647; 98 | files = ( 99 | D4F8169827A562220017C4B8 /* CoreAudio.framework in Frameworks */, 100 | D4003049279FFFA5002E9D53 /* AVFoundation.framework in Frameworks */, 101 | D4003045279FF307002E9D53 /* AudioToolbox.framework in Frameworks */, 102 | ); 103 | runOnlyForDeploymentPostprocessing = 0; 104 | }; 105 | /* End PBXFrameworksBuildPhase section */ 106 | 107 | /* Begin PBXGroup section */ 108 | D4003014279FED45002E9D53 = { 109 | isa = PBXGroup; 110 | children = ( 111 | D400301F279FED46002E9D53 /* AudioUnitSamples */, 112 | D400301E279FED46002E9D53 /* Products */, 113 | D4003043279FF307002E9D53 /* Frameworks */, 114 | ); 115 | sourceTree = ""; 116 | }; 117 | D400301E279FED46002E9D53 /* Products */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | D400301D279FED46002E9D53 /* AudioUnitSamples.app */, 121 | ); 122 | name = Products; 123 | sourceTree = ""; 124 | }; 125 | D400301F279FED46002E9D53 /* AudioUnitSamples */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | D40C2C6F27A16648002E0ABA /* Common */, 129 | D40C2C6627A14FDC002E0ABA /* SamplesViewControllers */, 130 | D410515E27A134ED00764794 /* AudioUnitSamples.entitlements */, 131 | D4003020279FED46002E9D53 /* AppDelegate.h */, 132 | D4003021279FED46002E9D53 /* AppDelegate.m */, 133 | D4003026279FED46002E9D53 /* ViewController.h */, 134 | D4003027279FED46002E9D53 /* ViewController.m */, 135 | D400303E279FEE60002E9D53 /* SampleListViewController.h */, 136 | D400303F279FEE60002E9D53 /* SampleListViewController.m */, 137 | D4003040279FEE60002E9D53 /* SampleListViewController.xib */, 138 | D400302C279FED47002E9D53 /* Assets.xcassets */, 139 | D400302E279FED47002E9D53 /* LaunchScreen.storyboard */, 140 | D4003031279FED47002E9D53 /* Info.plist */, 141 | D4003032279FED47002E9D53 /* main.m */, 142 | ); 143 | path = AudioUnitSamples; 144 | sourceTree = ""; 145 | }; 146 | D4003043279FF307002E9D53 /* Frameworks */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | D4F8169727A562220017C4B8 /* CoreAudio.framework */, 150 | D4003048279FFFA5002E9D53 /* AVFoundation.framework */, 151 | D4003046279FF30C002E9D53 /* AudioUnit.framework */, 152 | D4003044279FF307002E9D53 /* AudioToolbox.framework */, 153 | ); 154 | name = Frameworks; 155 | sourceTree = ""; 156 | }; 157 | D40C2C6627A14FDC002E0ABA /* SamplesViewControllers */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | D40C2C7927A19958002E0ABA /* AudioUnitPlayerViewController.h */, 161 | D40C2C7A27A19958002E0ABA /* AudioUnitPlayerViewController.mm */, 162 | D40C2C7B27A19958002E0ABA /* AudioUnitPlayerViewController.xib */, 163 | D40C2C6727A15022002E0ABA /* AudioUnitRecordOnlyViewController.h */, 164 | D40C2C6827A15022002E0ABA /* AudioUnitRecordOnlyViewController.mm */, 165 | D40C2C6927A15022002E0ABA /* AudioUnitRecordOnlyViewController.xib */, 166 | D4003039279FEDF3002E9D53 /* BasicRecordAndPlaySampleViewController.h */, 167 | D400303A279FEDF3002E9D53 /* BasicRecordAndPlaySampleViewController.mm */, 168 | D400303B279FEDF3002E9D53 /* BasicRecordAndPlaySampleViewController.xib */, 169 | D4F8168B27A27FDF0017C4B8 /* ComplexRecordAndPlaySampleViewController.h */, 170 | D4F8168C27A27FDF0017C4B8 /* ComplexRecordAndPlaySampleViewController.mm */, 171 | D4F8168A27A27FDF0017C4B8 /* ComplexRecordAndPlaySampleViewController.xib */, 172 | D4F8169427A462150017C4B8 /* AUGraphRecordAndPlaySampleViewController.h */, 173 | D4F8169327A462150017C4B8 /* AUGraphRecordAndPlaySampleViewController.mm */, 174 | D4F8169227A462150017C4B8 /* AUGraphRecordAndPlaySampleViewController.xib */, 175 | ); 176 | path = SamplesViewControllers; 177 | sourceTree = ""; 178 | }; 179 | D40C2C6F27A16648002E0ABA /* Common */ = { 180 | isa = PBXGroup; 181 | children = ( 182 | D40C2C6C27A157C8002E0ABA /* CommonUtils.h */, 183 | D40C2C6D27A157C8002E0ABA /* CommonUtils.mm */, 184 | D40C2C7027A16668002E0ABA /* AudioFileWriter.h */, 185 | D40C2C7127A16668002E0ABA /* AudioFileWriter.mm */, 186 | D40C2C7E27A19BFC002E0ABA /* AudioFileReader.h */, 187 | D40C2C7F27A19BFC002E0ABA /* AudioFileReader.mm */, 188 | D40C2C7627A1990C002E0ABA /* AudioUnitPlayer.h */, 189 | D40C2C7727A1990C002E0ABA /* AudioUnitPlayer.mm */, 190 | D40C2C7327A1948D002E0ABA /* AudioUnitRecorder.h */, 191 | D40C2C7427A1948D002E0ABA /* AudioUnitRecorder.mm */, 192 | D40C2C8127A1B77B002E0ABA /* AudioUnitRecorderAndPlayer.h */, 193 | D40C2C8227A1B77B002E0ABA /* AudioUnitRecorderAndPlayer.mm */, 194 | D4F8168727A27D4B0017C4B8 /* AudioUnitRecorderAndPlayerComplex.h */, 195 | D4F8168827A27D4B0017C4B8 /* AudioUnitRecorderAndPlayerComplex.mm */, 196 | D4F8168F27A377730017C4B8 /* AUGraphRecorderPlayer.h */, 197 | D4F8169027A377730017C4B8 /* AUGraphRecorderPlayer.mm */, 198 | D4F8169927A57FCA0017C4B8 /* AUGraphMixer.h */, 199 | D4F8169A27A57FCA0017C4B8 /* AUGraphMixer.mm */, 200 | ); 201 | path = Common; 202 | sourceTree = ""; 203 | }; 204 | /* End PBXGroup section */ 205 | 206 | /* Begin PBXNativeTarget section */ 207 | D400301C279FED46002E9D53 /* AudioUnitSamples */ = { 208 | isa = PBXNativeTarget; 209 | buildConfigurationList = D4003036279FED47002E9D53 /* Build configuration list for PBXNativeTarget "AudioUnitSamples" */; 210 | buildPhases = ( 211 | D4003019279FED46002E9D53 /* Sources */, 212 | D400301A279FED46002E9D53 /* Frameworks */, 213 | D400301B279FED46002E9D53 /* Resources */, 214 | ); 215 | buildRules = ( 216 | ); 217 | dependencies = ( 218 | ); 219 | name = AudioUnitSamples; 220 | productName = AudioUnitSamples; 221 | productReference = D400301D279FED46002E9D53 /* AudioUnitSamples.app */; 222 | productType = "com.apple.product-type.application"; 223 | }; 224 | /* End PBXNativeTarget section */ 225 | 226 | /* Begin PBXProject section */ 227 | D4003015279FED45002E9D53 /* Project object */ = { 228 | isa = PBXProject; 229 | attributes = { 230 | BuildIndependentTargetsInParallel = 1; 231 | LastUpgradeCheck = 1320; 232 | TargetAttributes = { 233 | D400301C279FED46002E9D53 = { 234 | CreatedOnToolsVersion = 13.2.1; 235 | }; 236 | }; 237 | }; 238 | buildConfigurationList = D4003018279FED45002E9D53 /* Build configuration list for PBXProject "AudioUnitSamples" */; 239 | compatibilityVersion = "Xcode 13.0"; 240 | developmentRegion = en; 241 | hasScannedForEncodings = 0; 242 | knownRegions = ( 243 | en, 244 | Base, 245 | ); 246 | mainGroup = D4003014279FED45002E9D53; 247 | productRefGroup = D400301E279FED46002E9D53 /* Products */; 248 | projectDirPath = ""; 249 | projectRoot = ""; 250 | targets = ( 251 | D400301C279FED46002E9D53 /* AudioUnitSamples */, 252 | ); 253 | }; 254 | /* End PBXProject section */ 255 | 256 | /* Begin PBXResourcesBuildPhase section */ 257 | D400301B279FED46002E9D53 /* Resources */ = { 258 | isa = PBXResourcesBuildPhase; 259 | buildActionMask = 2147483647; 260 | files = ( 261 | D40C2C7D27A19958002E0ABA /* AudioUnitPlayerViewController.xib in Resources */, 262 | D4003030279FED47002E9D53 /* LaunchScreen.storyboard in Resources */, 263 | D4F8168D27A27FDF0017C4B8 /* ComplexRecordAndPlaySampleViewController.xib in Resources */, 264 | D4003042279FEE60002E9D53 /* SampleListViewController.xib in Resources */, 265 | D400302D279FED47002E9D53 /* Assets.xcassets in Resources */, 266 | D400303D279FEDF3002E9D53 /* BasicRecordAndPlaySampleViewController.xib in Resources */, 267 | D4F8169527A462150017C4B8 /* AUGraphRecordAndPlaySampleViewController.xib in Resources */, 268 | D40C2C6B27A15022002E0ABA /* AudioUnitRecordOnlyViewController.xib in Resources */, 269 | ); 270 | runOnlyForDeploymentPostprocessing = 0; 271 | }; 272 | /* End PBXResourcesBuildPhase section */ 273 | 274 | /* Begin PBXSourcesBuildPhase section */ 275 | D4003019279FED46002E9D53 /* Sources */ = { 276 | isa = PBXSourcesBuildPhase; 277 | buildActionMask = 2147483647; 278 | files = ( 279 | D40C2C7527A1948D002E0ABA /* AudioUnitRecorder.mm in Sources */, 280 | D40C2C8027A19BFC002E0ABA /* AudioFileReader.mm in Sources */, 281 | D4F8168E27A27FDF0017C4B8 /* ComplexRecordAndPlaySampleViewController.mm in Sources */, 282 | D4F8169627A462150017C4B8 /* AUGraphRecordAndPlaySampleViewController.mm in Sources */, 283 | D4003041279FEE60002E9D53 /* SampleListViewController.m in Sources */, 284 | D40C2C6E27A157C8002E0ABA /* CommonUtils.mm in Sources */, 285 | D40C2C7C27A19958002E0ABA /* AudioUnitPlayerViewController.mm in Sources */, 286 | D40C2C7827A1990C002E0ABA /* AudioUnitPlayer.mm in Sources */, 287 | D4F8168927A27D4B0017C4B8 /* AudioUnitRecorderAndPlayerComplex.mm in Sources */, 288 | D40C2C8327A1B77B002E0ABA /* AudioUnitRecorderAndPlayer.mm in Sources */, 289 | D4F8169B27A57FCA0017C4B8 /* AUGraphMixer.mm in Sources */, 290 | D400303C279FEDF3002E9D53 /* BasicRecordAndPlaySampleViewController.mm in Sources */, 291 | D40C2C7227A16668002E0ABA /* AudioFileWriter.mm in Sources */, 292 | D40C2C6A27A15022002E0ABA /* AudioUnitRecordOnlyViewController.mm in Sources */, 293 | D4003028279FED46002E9D53 /* ViewController.m in Sources */, 294 | D4F8169127A377730017C4B8 /* AUGraphRecorderPlayer.mm in Sources */, 295 | D4003022279FED46002E9D53 /* AppDelegate.m in Sources */, 296 | D4003033279FED47002E9D53 /* main.m in Sources */, 297 | ); 298 | runOnlyForDeploymentPostprocessing = 0; 299 | }; 300 | /* End PBXSourcesBuildPhase section */ 301 | 302 | /* Begin PBXVariantGroup section */ 303 | D400302E279FED47002E9D53 /* LaunchScreen.storyboard */ = { 304 | isa = PBXVariantGroup; 305 | children = ( 306 | D400302F279FED47002E9D53 /* Base */, 307 | ); 308 | name = LaunchScreen.storyboard; 309 | sourceTree = ""; 310 | }; 311 | /* End PBXVariantGroup section */ 312 | 313 | /* Begin XCBuildConfiguration section */ 314 | D4003034279FED47002E9D53 /* Debug */ = { 315 | isa = XCBuildConfiguration; 316 | buildSettings = { 317 | ALWAYS_SEARCH_USER_PATHS = NO; 318 | CLANG_ANALYZER_NONNULL = YES; 319 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 320 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 321 | CLANG_CXX_LIBRARY = "libc++"; 322 | CLANG_ENABLE_MODULES = YES; 323 | CLANG_ENABLE_OBJC_ARC = YES; 324 | CLANG_ENABLE_OBJC_WEAK = YES; 325 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 326 | CLANG_WARN_BOOL_CONVERSION = YES; 327 | CLANG_WARN_COMMA = YES; 328 | CLANG_WARN_CONSTANT_CONVERSION = YES; 329 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 330 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 331 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 332 | CLANG_WARN_EMPTY_BODY = YES; 333 | CLANG_WARN_ENUM_CONVERSION = YES; 334 | CLANG_WARN_INFINITE_RECURSION = YES; 335 | CLANG_WARN_INT_CONVERSION = YES; 336 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 337 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 338 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 339 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 340 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 341 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 342 | CLANG_WARN_STRICT_PROTOTYPES = YES; 343 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 344 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 345 | CLANG_WARN_UNREACHABLE_CODE = YES; 346 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 347 | COPY_PHASE_STRIP = NO; 348 | DEBUG_INFORMATION_FORMAT = dwarf; 349 | ENABLE_STRICT_OBJC_MSGSEND = YES; 350 | ENABLE_TESTABILITY = YES; 351 | GCC_C_LANGUAGE_STANDARD = gnu11; 352 | GCC_DYNAMIC_NO_PIC = NO; 353 | GCC_NO_COMMON_BLOCKS = YES; 354 | GCC_OPTIMIZATION_LEVEL = 0; 355 | GCC_PREPROCESSOR_DEFINITIONS = ( 356 | "DEBUG=1", 357 | "$(inherited)", 358 | ); 359 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 360 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 361 | GCC_WARN_UNDECLARED_SELECTOR = YES; 362 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 363 | GCC_WARN_UNUSED_FUNCTION = YES; 364 | GCC_WARN_UNUSED_VARIABLE = YES; 365 | IPHONEOS_DEPLOYMENT_TARGET = 15.2; 366 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 367 | MTL_FAST_MATH = YES; 368 | ONLY_ACTIVE_ARCH = YES; 369 | SDKROOT = iphoneos; 370 | }; 371 | name = Debug; 372 | }; 373 | D4003035279FED47002E9D53 /* Release */ = { 374 | isa = XCBuildConfiguration; 375 | buildSettings = { 376 | ALWAYS_SEARCH_USER_PATHS = NO; 377 | CLANG_ANALYZER_NONNULL = YES; 378 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 379 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 380 | CLANG_CXX_LIBRARY = "libc++"; 381 | CLANG_ENABLE_MODULES = YES; 382 | CLANG_ENABLE_OBJC_ARC = YES; 383 | CLANG_ENABLE_OBJC_WEAK = YES; 384 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 385 | CLANG_WARN_BOOL_CONVERSION = YES; 386 | CLANG_WARN_COMMA = YES; 387 | CLANG_WARN_CONSTANT_CONVERSION = YES; 388 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 389 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 390 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 391 | CLANG_WARN_EMPTY_BODY = YES; 392 | CLANG_WARN_ENUM_CONVERSION = YES; 393 | CLANG_WARN_INFINITE_RECURSION = YES; 394 | CLANG_WARN_INT_CONVERSION = YES; 395 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 396 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 397 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 398 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 399 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 400 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 401 | CLANG_WARN_STRICT_PROTOTYPES = YES; 402 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 403 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 404 | CLANG_WARN_UNREACHABLE_CODE = YES; 405 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 406 | COPY_PHASE_STRIP = NO; 407 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 408 | ENABLE_NS_ASSERTIONS = NO; 409 | ENABLE_STRICT_OBJC_MSGSEND = YES; 410 | GCC_C_LANGUAGE_STANDARD = gnu11; 411 | GCC_NO_COMMON_BLOCKS = YES; 412 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 413 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 414 | GCC_WARN_UNDECLARED_SELECTOR = YES; 415 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 416 | GCC_WARN_UNUSED_FUNCTION = YES; 417 | GCC_WARN_UNUSED_VARIABLE = YES; 418 | IPHONEOS_DEPLOYMENT_TARGET = 15.2; 419 | MTL_ENABLE_DEBUG_INFO = NO; 420 | MTL_FAST_MATH = YES; 421 | SDKROOT = iphoneos; 422 | VALIDATE_PRODUCT = YES; 423 | }; 424 | name = Release; 425 | }; 426 | D4003037279FED47002E9D53 /* Debug */ = { 427 | isa = XCBuildConfiguration; 428 | buildSettings = { 429 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 430 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 431 | CODE_SIGN_ENTITLEMENTS = AudioUnitSamples/AudioUnitSamples.entitlements; 432 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; 433 | CODE_SIGN_STYLE = Automatic; 434 | CURRENT_PROJECT_VERSION = 1; 435 | DEVELOPMENT_TEAM = 584KQTRF3B; 436 | GENERATE_INFOPLIST_FILE = YES; 437 | INFOPLIST_FILE = AudioUnitSamples/Info.plist; 438 | INFOPLIST_KEY_NSMicrophoneUsageDescription = "开启麦克风可以随心所欲地进行直播"; 439 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 440 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 441 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 442 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 443 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 444 | LD_RUNPATH_SEARCH_PATHS = ( 445 | "$(inherited)", 446 | "@executable_path/Frameworks", 447 | ); 448 | MARKETING_VERSION = 1.0; 449 | PRODUCT_BUNDLE_IDENTIFIER = blue.joey.AudioUnitSamples; 450 | PRODUCT_NAME = "$(TARGET_NAME)"; 451 | SUPPORTS_MACCATALYST = NO; 452 | SWIFT_EMIT_LOC_STRINGS = YES; 453 | TARGETED_DEVICE_FAMILY = "1,2"; 454 | }; 455 | name = Debug; 456 | }; 457 | D4003038279FED47002E9D53 /* Release */ = { 458 | isa = XCBuildConfiguration; 459 | buildSettings = { 460 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 461 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 462 | CODE_SIGN_ENTITLEMENTS = AudioUnitSamples/AudioUnitSamples.entitlements; 463 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; 464 | CODE_SIGN_STYLE = Automatic; 465 | CURRENT_PROJECT_VERSION = 1; 466 | DEVELOPMENT_TEAM = 584KQTRF3B; 467 | GENERATE_INFOPLIST_FILE = YES; 468 | INFOPLIST_FILE = AudioUnitSamples/Info.plist; 469 | INFOPLIST_KEY_NSMicrophoneUsageDescription = "开启麦克风可以随心所欲地进行直播"; 470 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 471 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 472 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 473 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 474 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 475 | LD_RUNPATH_SEARCH_PATHS = ( 476 | "$(inherited)", 477 | "@executable_path/Frameworks", 478 | ); 479 | MARKETING_VERSION = 1.0; 480 | PRODUCT_BUNDLE_IDENTIFIER = blue.joey.AudioUnitSamples; 481 | PRODUCT_NAME = "$(TARGET_NAME)"; 482 | SUPPORTS_MACCATALYST = NO; 483 | SWIFT_EMIT_LOC_STRINGS = YES; 484 | TARGETED_DEVICE_FAMILY = "1,2"; 485 | }; 486 | name = Release; 487 | }; 488 | /* End XCBuildConfiguration section */ 489 | 490 | /* Begin XCConfigurationList section */ 491 | D4003018279FED45002E9D53 /* Build configuration list for PBXProject "AudioUnitSamples" */ = { 492 | isa = XCConfigurationList; 493 | buildConfigurations = ( 494 | D4003034279FED47002E9D53 /* Debug */, 495 | D4003035279FED47002E9D53 /* Release */, 496 | ); 497 | defaultConfigurationIsVisible = 0; 498 | defaultConfigurationName = Release; 499 | }; 500 | D4003036279FED47002E9D53 /* Build configuration list for PBXNativeTarget "AudioUnitSamples" */ = { 501 | isa = XCConfigurationList; 502 | buildConfigurations = ( 503 | D4003037279FED47002E9D53 /* Debug */, 504 | D4003038279FED47002E9D53 /* Release */, 505 | ); 506 | defaultConfigurationIsVisible = 0; 507 | defaultConfigurationName = Release; 508 | }; 509 | /* End XCConfigurationList section */ 510 | }; 511 | rootObject = D4003015279FED45002E9D53 /* Project object */; 512 | } 513 | -------------------------------------------------------------------------------- /AudioUnitSamples.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AudioUnitSamples.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /AudioUnitSamples.xcodeproj/xcshareddata/xcschemes/AudioUnitSamples.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 11 | 14 | 15 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 33 | 39 | 40 | 41 | 42 | 43 | 48 | 49 | 50 | 51 | 61 | 62 | 64 | 67 | 68 | 69 | 70 | 72 | 78 | 79 | 80 | 81 | 87 | 89 | 95 | 96 | 97 | 98 | 100 | 101 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /AudioUnitSamples/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // AudioUnitSamples 4 | // 5 | // Created by joey on 2022/1/25. 6 | // 7 | 8 | #import 9 | 10 | @interface AppDelegate : UIResponder 11 | 12 | @property (strong, nonatomic, nonnull) UIWindow *window; 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /AudioUnitSamples/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // AudioUnitSamples 4 | // 5 | // Created by joey on 2022/1/25. 6 | // 7 | 8 | #import "AppDelegate.h" 9 | #import "SampleListViewController.h" 10 | 11 | @interface AppDelegate () 12 | 13 | //@property (nonatomic, strong) UIWindow window; 14 | 15 | @end 16 | 17 | @implementation AppDelegate 18 | 19 | 20 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 21 | // Override point for customization after application launch. 22 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 23 | UINavigationController *rootVC = [[UINavigationController alloc] initWithRootViewController:[SampleListViewController new]]; 24 | self.window.rootViewController = rootVC; 25 | [self.window makeKeyAndVisible]; 26 | return YES; 27 | } 28 | 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /AudioUnitSamples/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /AudioUnitSamples/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /AudioUnitSamples/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /AudioUnitSamples/AudioUnitSamples.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /AudioUnitSamples/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 | -------------------------------------------------------------------------------- /AudioUnitSamples/Common/AUGraphMixer.h: -------------------------------------------------------------------------------- 1 | // 2 | // AUGraphMixer.h 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/29. 6 | // 7 | 8 | #import 9 | #import "CommonUtils.h" 10 | 11 | namespace samples { 12 | 13 | class AUGraphMixer { 14 | 15 | AUGraphMixer(AudioStreamBasicDescription format); 16 | 17 | 18 | }; 19 | 20 | } // namspace samples 21 | -------------------------------------------------------------------------------- /AudioUnitSamples/Common/AUGraphMixer.mm: -------------------------------------------------------------------------------- 1 | // 2 | // AUGraphMixer.m 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/29. 6 | // 7 | 8 | #import "AUGraphMixer.h" 9 | -------------------------------------------------------------------------------- /AudioUnitSamples/Common/AUGraphRecorderPlayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // AUGraphRecorderPlayer.h 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/28. 6 | // 7 | 8 | #import 9 | #import 10 | #import "AudioFileReader.h" 11 | #import "AudioFileWriter.h" 12 | 13 | namespace samples { 14 | 15 | class AUGraphRecorderPlayer { 16 | 17 | public: 18 | AUGraphRecorderPlayer(AudioStreamBasicDescription format, 19 | CFURLRef music_file_url, 20 | CFURLRef export_file_url); 21 | virtual ~AUGraphRecorderPlayer(); 22 | 23 | void InitializeGraph(); 24 | 25 | void Start(); 26 | 27 | void Stop(); 28 | 29 | bool IsRunning(); 30 | 31 | private: 32 | 33 | static OSStatus OnAskForVocalAudioBufferRenderCallback(void *inRefCon, 34 | AudioUnitRenderActionFlags *ioActionFlags, 35 | const AudioTimeStamp *inTimeStamp, 36 | UInt32 inBusNumber, 37 | UInt32 inNumberFrames, 38 | AudioBufferList *ioData); 39 | 40 | static OSStatus OnAskForMusicAudioBufferRenderCallback(void *inRefCon, 41 | AudioUnitRenderActionFlags *ioActionFlags, 42 | const AudioTimeStamp *inTimeStamp, 43 | UInt32 inBusNumber, 44 | UInt32 inNumberFrames, 45 | AudioBufferList *ioData); 46 | 47 | static OSStatus OnAskForExportMixedAudioBufferRenderCallback(void *inRefCon, 48 | AudioUnitRenderActionFlags *ioActionFlags, 49 | const AudioTimeStamp *inTimeStamp, 50 | UInt32 inBusNumber, 51 | UInt32 inNumberFrames, 52 | AudioBufferList *ioData); 53 | 54 | static OSStatus OnIOUnitAudioBufferIsAvailable(void *inRefCon, 55 | AudioUnitRenderActionFlags *ioActionFlags, 56 | const AudioTimeStamp *inTimeStamp, 57 | UInt32 inBusNumber, 58 | UInt32 inNumberFrames, 59 | AudioBufferList *ioData); 60 | 61 | static OSStatus OnIOUnitRenderNotify(void * inRefCon, 62 | AudioUnitRenderActionFlags *ioActionFlags, 63 | const AudioTimeStamp *inTimeStamp, 64 | UInt32 inBusNumber, 65 | UInt32 inNumberFrames, 66 | AudioBufferList * ioData); 67 | 68 | static OSStatus OnAskForExportVocalAudioBufferRenderCallback(void *inRefCon, 69 | AudioUnitRenderActionFlags *ioActionFlags, 70 | const AudioTimeStamp *inTimeStamp, 71 | UInt32 inBusNumber, 72 | UInt32 inNumberFrames, 73 | AudioBufferList *ioData); 74 | 75 | static OSStatus OnAskForExportMusicAudioBufferRenderCallback(void *inRefCon, 76 | AudioUnitRenderActionFlags *ioActionFlags, 77 | const AudioTimeStamp *inTimeStamp, 78 | UInt32 inBusNumber, 79 | UInt32 inNumberFrames, 80 | AudioBufferList *ioData); 81 | 82 | AudioStreamBasicDescription format_; 83 | AudioBufferList vocal_buffer_; 84 | AudioBufferList music_buffer_; 85 | 86 | std::unique_ptr music_file_reader_; 87 | std::unique_ptr export_file_writer_; 88 | 89 | dispatch_queue_t writer_serial_queue_; 90 | 91 | AUGraph graph_{nullptr}; 92 | 93 | AUNode io_node_; 94 | AudioUnit io_unit_; 95 | 96 | AUNode mixer_node_; 97 | AudioUnit mixer_unit_; 98 | 99 | AUNode music_player_node_; 100 | AudioUnit music_player_unit_; 101 | 102 | AUNode export_mixer_node_; 103 | AudioUnit export_mixer_unit_; 104 | }; 105 | 106 | } // namespace samples 107 | -------------------------------------------------------------------------------- /AudioUnitSamples/Common/AUGraphRecorderPlayer.mm: -------------------------------------------------------------------------------- 1 | // 2 | // AUGraphRecorderPlayer.m 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/28. 6 | // 7 | 8 | #import "AUGraphRecorderPlayer.h" 9 | #import "CommonUtils.h" 10 | 11 | namespace { 12 | 13 | // mixer 的 input scope 有多个 input element: 14 | // 我们定义 第 0 个为人声, 第 1 个为伴奏 15 | constexpr UInt32 kMixerVocalInputElementNumber = 0; 16 | constexpr UInt32 kMixerMusicInputElementNumber = 1; 17 | constexpr UInt32 kMixerExportMixerInputElementNumber = 2; 18 | 19 | 20 | // mixer 的 output scope 只有一个 element, number 自然为 0 21 | constexpr UInt32 kMixerUniqueOutputElementNumber = 0; 22 | }; 23 | 24 | namespace samples { 25 | 26 | AUGraphRecorderPlayer::AUGraphRecorderPlayer(AudioStreamBasicDescription format, 27 | CFURLRef music_file_url, 28 | CFURLRef export_file_url) : 29 | format_(format), 30 | music_file_reader_(std::make_unique(music_file_url, format)), 31 | export_file_writer_(std::make_unique(export_file_url, format)), 32 | writer_serial_queue_(dispatch_queue_create("writer_queue", DISPATCH_QUEUE_SERIAL)) { 33 | InitAudioBufferList(&vocal_buffer_); 34 | InitAudioBufferList(&music_buffer_); 35 | } 36 | 37 | AUGraphRecorderPlayer::~AUGraphRecorderPlayer() { 38 | if (IsRunning()) { 39 | Stop(); 40 | } 41 | DisposeAudioBufferList(&vocal_buffer_); 42 | DisposeAudioBufferList(&music_buffer_); 43 | } 44 | 45 | void AUGraphRecorderPlayer::InitializeGraph() { 46 | CheckHasError(NewAUGraph(&graph_), "New AUGraph"); 47 | 48 | // add io unit 49 | { 50 | AudioComponentDescription node_desc = { 51 | .componentType = kAudioUnitType_Output, 52 | .componentSubType = kAudioUnitSubType_RemoteIO, 53 | .componentManufacturer = kAudioUnitManufacturer_Apple, 54 | .componentFlags = kAudioComponentFlag_SandboxSafe, 55 | .componentFlagsMask = 0 56 | }; 57 | CheckHasError(AUGraphAddNode(graph_, &node_desc, &io_node_), "Add output node"); 58 | } 59 | 60 | // add mixer node 61 | { 62 | AudioComponentDescription node_desc = { 63 | .componentType = kAudioUnitType_Mixer, 64 | .componentSubType = kAudioUnitSubType_MultiChannelMixer, 65 | .componentManufacturer = kAudioUnitManufacturer_Apple, 66 | .componentFlags = kAudioComponentFlag_SandboxSafe, 67 | .componentFlagsMask = 0 68 | }; 69 | CheckHasError(AUGraphAddNode(graph_, &node_desc, &mixer_node_), "Add mixer node"); 70 | CheckHasError(AUGraphAddNode(graph_, &node_desc, &export_mixer_node_), "Add export mixer node"); 71 | } 72 | 73 | // connect mixer node:[output_scope]:output_element to io_unit:[input scope]:ouput_element 74 | // [] 为隐含含义, 因为肯定是连接前者的 output scope 到后者的 input scope, 这个是确定的, 75 | // 因为这两个 unit 的对应的 scope 对应的 element 都只有一个, 所以都是 0 76 | // mixer 的 output scope 对应的 element 只有一个, io unit 的 input scope 对应的 element 也只有一个 77 | CheckHasError(AUGraphConnectNodeInput(graph_, mixer_node_, kMixerUniqueOutputElementNumber, io_node_, 0), 78 | "Connect mixer_node_->output bus to io_node_->outputbus"); 79 | 80 | CheckHasError(AUGraphOpen(graph_), "open graph"); 81 | 82 | CheckHasError(AUGraphNodeInfo(graph_, io_node_, NULL, &io_unit_), "get io_unit_ from graph"); 83 | CheckHasError(AUGraphNodeInfo(graph_, mixer_node_, NULL, &mixer_unit_), "get mixer_unit_ from graph"); 84 | 85 | // config IO unit 86 | { 87 | UInt32 enable = 1; 88 | CheckHasError(AudioUnitSetProperty(io_unit_, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, 89 | &enable, sizeof(enable)), 90 | "enable IO unit Input"); 91 | 92 | CheckHasError(AudioUnitSetProperty(io_unit_, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, 93 | &format_, sizeof(AudioStreamBasicDescription)), 94 | "set format on io_unit_:1:output_scope"); 95 | 96 | AURenderCallbackStruct io_unit_input_callback { 97 | .inputProc = OnIOUnitAudioBufferIsAvailable, 98 | .inputProcRefCon = this 99 | }; 100 | CheckHasError(AudioUnitSetProperty(io_unit_, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 101 | 0, &io_unit_input_callback, sizeof(io_unit_input_callback)), "set input callback"); 102 | 103 | CheckHasError(AudioUnitAddRenderNotify(io_unit_, OnIOUnitRenderNotify, this), "add Render notify"); 104 | } 105 | 106 | // config mixer unit 107 | { 108 | UInt32 mixer_input_buses_num = 3; // can be 2 109 | // 这里的第 4 个参数 inElement 此处没有意义, 因为现在就是在设置 input element 的数量, 设置完了之后才有意义 110 | CheckHasError(AudioUnitSetProperty(mixer_unit_, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 111 | 0, &mixer_input_buses_num, sizeof(mixer_input_buses_num)), 112 | "set mixer input bus count"); 113 | 114 | // 指定 Input Scope 的 第 0 bus, 记住 mixer 的 input scope 有多个 bus, output scope 只有一个 bus 115 | CheckHasError(AudioUnitSetProperty(mixer_unit_, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 116 | kMixerVocalInputElementNumber, &format_, sizeof(AudioStreamBasicDescription)), 117 | "set format on mixex_unit_:0:input_scope"); 118 | 119 | // Note: 这里指定的 format 和音乐文件的实际格式必须是一致的 120 | CheckHasError(AudioUnitSetProperty(mixer_unit_, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 121 | kMixerMusicInputElementNumber, &format_, sizeof(AudioStreamBasicDescription)), 122 | "set format on mixex_unit_:0:input_scope"); 123 | CheckHasError(AudioUnitSetProperty(mixer_unit_, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 124 | kMixerExportMixerInputElementNumber, &format_, sizeof(AudioStreamBasicDescription)), 125 | "set format on mixex_unit_:0:input_scope"); 126 | // element 3 设置为静音 127 | AudioUnitParameterValue volume = 0; 128 | CheckHasError(AudioUnitSetParameter(mixer_unit_, kMultiChannelMixerParam_Volume, kAudioUnitScope_Input, 129 | kMixerExportMixerInputElementNumber, volume, 0), 130 | "set volume on mixer input element 2"); 131 | 132 | CheckHasError(AudioUnitSetProperty(mixer_unit_, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 133 | kMixerUniqueOutputElementNumber, &format_, sizeof(AudioStreamBasicDescription)), 134 | "set format on mixer_unit_:output_bus:output_scope"); 135 | 136 | AudioUnitParameterValue enable_mixer_input = 1; 137 | UInt32 in_buffer_offset_in_frames = 0; 138 | CheckHasError(AudioUnitSetParameter(mixer_unit_, kMultiChannelMixerParam_Enable, kAudioUnitScope_Input, 139 | kMixerVocalInputElementNumber, enable_mixer_input, in_buffer_offset_in_frames), 140 | "enable mixer input 0"); 141 | 142 | CheckHasError(AudioUnitSetParameter(mixer_unit_, kMultiChannelMixerParam_Enable, kAudioUnitScope_Input, 143 | kMixerMusicInputElementNumber, enable_mixer_input, in_buffer_offset_in_frames), 144 | "enable mixer input 0"); 145 | CheckHasError(AudioUnitSetParameter(mixer_unit_, kMultiChannelMixerParam_Enable, kAudioUnitScope_Input, 146 | kMixerExportMixerInputElementNumber, enable_mixer_input, in_buffer_offset_in_frames), 147 | "enable mixer input 0"); 148 | 149 | 150 | // 这里配置 element 0, 也就是人声的数据回调, 在这里回调里提供人声 sample 给 mixer 151 | AURenderCallbackStruct mixer_vocal_input_element0_render_callback { 152 | .inputProc = OnAskForVocalAudioBufferRenderCallback, 153 | .inputProcRefCon = this 154 | }; 155 | // Note: 这里的 SetNodeInputCallback 不等价于 AudioUnitSetProperty(unit, kAudioOutputUnitProperty_SetInputCallback, ...) 156 | // 而是等价于 AudioUnitSetProperty(unit, kAudioUnitProperty_SetRenderCallback, ...) 157 | CheckHasError(AUGraphSetNodeInputCallback(graph_, mixer_node_, kMixerVocalInputElementNumber, &mixer_vocal_input_element0_render_callback), 158 | "set mixer_vocal_input_element0 render callback"); 159 | 160 | // 同上, 这里配置 element 1, 也即是伴奏的数据回调 161 | AURenderCallbackStruct mixer_music_input_element1_render_callback { 162 | .inputProc = OnAskForMusicAudioBufferRenderCallback, 163 | .inputProcRefCon = this 164 | }; 165 | CheckHasError(AUGraphSetNodeInputCallback(graph_, mixer_node_, kMixerMusicInputElementNumber, &mixer_music_input_element1_render_callback), 166 | "set mixer_music_input_element1 render callback"); 167 | // 同上, 这里配置 element 2 168 | AURenderCallbackStruct mixer_export_mixer_input_element2_render_callback { 169 | .inputProc = OnAskForExportMixedAudioBufferRenderCallback, 170 | .inputProcRefCon = this 171 | }; 172 | CheckHasError(AUGraphSetNodeInputCallback(graph_, mixer_node_, kMixerExportMixerInputElementNumber, &mixer_export_mixer_input_element2_render_callback), 173 | "set mixer_export_mixer_input_element2 render callback"); 174 | } 175 | 176 | { 177 | // CheckHasError(AUGraphConnectNodeInput(graph_, export_mixer_node_, kMixerUniqueOutputElementNumber, 178 | // mixer_node_, kMixerExportMixerInputElementNumber), 179 | // "Connect export_mixer_node_->output bus to mixer_node_->input scope"); 180 | 181 | // CheckHasError(AUGraphOpen(graph_), "open graph"); 182 | 183 | CheckHasError(AUGraphNodeInfo(graph_, export_mixer_node_, NULL, &export_mixer_unit_), "get export_mixer_unit_ from graph"); 184 | 185 | } 186 | 187 | 188 | { 189 | UInt32 mixer_input_buses_num = 2; // can be 2 190 | // 这里的第 4 个参数 inElement 此处没有意义, 因为现在就是在设置 input element 的数量, 设置完了之后才有意义 191 | CheckHasError(AudioUnitSetProperty(export_mixer_unit_, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 192 | 0, &mixer_input_buses_num, sizeof(mixer_input_buses_num)), 193 | "set mixer input bus count"); 194 | 195 | // 指定 Input Scope 的 第 0 bus, 记住 mixer 的 input scope 有多个 bus, output scope 只有一个 bus 196 | CheckHasError(AudioUnitSetProperty(export_mixer_unit_, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 197 | kMixerVocalInputElementNumber, &format_, sizeof(AudioStreamBasicDescription)), 198 | "set format on mixex_unit_:0:input_scope"); 199 | 200 | // Note: 这里指定的 format 和音乐文件的实际格式必须是一致的 201 | CheckHasError(AudioUnitSetProperty(export_mixer_unit_, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 202 | kMixerMusicInputElementNumber, &format_, sizeof(AudioStreamBasicDescription)), 203 | "set format on mixex_unit_:0:input_scope"); 204 | 205 | 206 | CheckHasError(AudioUnitSetProperty(export_mixer_unit_, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 207 | kMixerUniqueOutputElementNumber, &format_, sizeof(AudioStreamBasicDescription)), 208 | "set format on mixer_unit_:output_bus:output_scope"); 209 | 210 | AudioUnitParameterValue enable_mixer_input = 1; 211 | UInt32 in_buffer_offset_in_frames = 0; 212 | CheckHasError(AudioUnitSetParameter(export_mixer_unit_, kMultiChannelMixerParam_Enable, kAudioUnitScope_Input, 213 | kMixerVocalInputElementNumber, enable_mixer_input, in_buffer_offset_in_frames), 214 | "enable mixer input 0"); 215 | 216 | CheckHasError(AudioUnitSetParameter(export_mixer_unit_, kMultiChannelMixerParam_Enable, kAudioUnitScope_Input, 217 | kMixerMusicInputElementNumber, enable_mixer_input, in_buffer_offset_in_frames), 218 | "enable mixer input 0"); 219 | 220 | // 这里配置 element 0, 也就是人声的数据回调, 在这里回调里提供人声 sample 给 mixer 221 | AURenderCallbackStruct mixer_vocal_input_element0_render_callback { 222 | .inputProc = OnAskForExportVocalAudioBufferRenderCallback, 223 | .inputProcRefCon = this 224 | }; 225 | // Note: 这里的 SetNodeInputCallback 不等价于 AudioUnitSetProperty(unit, kAudioOutputUnitProperty_SetInputCallback, ...) 226 | // 而是等价于 AudioUnitSetProperty(unit, kAudioUnitProperty_SetRenderCallback, ...) 227 | CheckHasError(AUGraphSetNodeInputCallback(graph_, export_mixer_node_, kMixerVocalInputElementNumber, &mixer_vocal_input_element0_render_callback), 228 | "set export mixer_vocal_input_element0 render callback"); 229 | 230 | // 同上, 这里配置 element 1, 也即是伴奏的数据回调 231 | AURenderCallbackStruct mixer_music_input_element1_render_callback { 232 | .inputProc = OnAskForExportMusicAudioBufferRenderCallback, 233 | .inputProcRefCon = this 234 | }; 235 | CheckHasError(AUGraphSetNodeInputCallback(graph_, export_mixer_node_, kMixerMusicInputElementNumber, &mixer_music_input_element1_render_callback), 236 | "set export mixer_music_input_element1 render callback"); 237 | 238 | } 239 | 240 | CheckHasError(AUGraphInitialize(graph_), "Initialize graph_"); 241 | 242 | CAShow(graph_); 243 | } 244 | 245 | void AUGraphRecorderPlayer::Start() { 246 | if (IsRunning()) { 247 | return; 248 | } 249 | music_file_reader_->OpenFile(); 250 | export_file_writer_->CreateFile(); 251 | CheckHasError(AUGraphStart(graph_), "start augraph"); 252 | } 253 | 254 | void AUGraphRecorderPlayer::Stop() { 255 | if (graph_ && IsRunning()) { 256 | CheckHasError(AUGraphStop(graph_), "stop augraph"); 257 | 258 | music_file_reader_->CloseFile(); 259 | export_file_writer_->CloseFile(); 260 | 261 | Boolean graphIsInitialized; 262 | if (!CheckHasError(AUGraphIsInitialized(graph_, &graphIsInitialized), "check AUGraphIsInitialized") 263 | && graphIsInitialized) { 264 | CheckHasError(AUGraphUninitialize(graph_), "AUGraphUninitialize"); 265 | } 266 | 267 | Boolean graphIsOpen; 268 | if (!CheckHasError(AUGraphIsOpen(graph_, &graphIsOpen), "check AUGraphIsOpen") && graphIsOpen) { 269 | CheckHasError(AUGraphClose(graph_), "AUGraphClose"); 270 | } 271 | 272 | CheckHasError(DisposeAUGraph(graph_), "DisposeAUGraph"); 273 | 274 | graph_ = NULL; 275 | } 276 | } 277 | 278 | bool AUGraphRecorderPlayer::IsRunning() { 279 | Boolean is_running = false; 280 | CheckHasError(AUGraphIsRunning(graph_, &is_running), "check is running"); 281 | return (bool)is_running; 282 | } 283 | 284 | OSStatus AUGraphRecorderPlayer::OnAskForVocalAudioBufferRenderCallback(void *inRefCon, 285 | AudioUnitRenderActionFlags *ioActionFlags, 286 | const AudioTimeStamp *inTimeStamp, 287 | UInt32 inBusNumber, 288 | UInt32 inNumberFrames, 289 | AudioBufferList *ioData) { 290 | AUGraphRecorderPlayer *THIS = static_cast(inRefCon); 291 | CopyAudioBufferListDatas(*ioData, THIS->vocal_buffer_); 292 | 293 | return noErr; 294 | } 295 | 296 | OSStatus AUGraphRecorderPlayer::OnAskForMusicAudioBufferRenderCallback(void *inRefCon, 297 | AudioUnitRenderActionFlags *ioActionFlags, 298 | const AudioTimeStamp *inTimeStamp, 299 | UInt32 inBusNumber, 300 | UInt32 inNumberFrames, 301 | AudioBufferList *ioData) { 302 | AUGraphRecorderPlayer *THIS = static_cast(inRefCon); 303 | bool eof = false; 304 | THIS->music_file_reader_->ReadAudioFrame(ioData->mBuffers[0].mDataByteSize, ioData->mBuffers[0].mData, eof); 305 | CopyAudioBufferListDatas(THIS->music_buffer_, *ioData); // music 306 | return noErr; 307 | } 308 | 309 | OSStatus AUGraphRecorderPlayer::OnAskForExportMixedAudioBufferRenderCallback(void *inRefCon, 310 | AudioUnitRenderActionFlags *ioActionFlags, 311 | const AudioTimeStamp *inTimeStamp, 312 | UInt32 inBusNumber, 313 | UInt32 inNumberFrames, 314 | AudioBufferList *ioData) { 315 | AUGraphRecorderPlayer *THIS = static_cast(inRefCon); 316 | OSStatus status = CheckErrorStatus(AudioUnitRender(THIS->export_mixer_unit_, ioActionFlags, inTimeStamp, 0, inNumberFrames, ioData), 317 | "OnAskForExportAudioBufferRenderCallback call"); 318 | // TODO(xueshi) post to cpp thread 319 | dispatch_async(THIS->writer_serial_queue_, ^{ 320 | THIS->export_file_writer_->WriteAudioPacket(ioData->mBuffers[0].mData, ioData->mBuffers[0].mDataByteSize); 321 | }); 322 | return status; 323 | } 324 | 325 | OSStatus AUGraphRecorderPlayer::OnIOUnitAudioBufferIsAvailable(void *inRefCon, 326 | AudioUnitRenderActionFlags *ioActionFlags, 327 | const AudioTimeStamp *inTimeStamp, 328 | UInt32 inBusNumber, 329 | UInt32 inNumberFrames, 330 | AudioBufferList *ioData) { 331 | AUGraphRecorderPlayer *THIS = static_cast(inRefCon); 332 | OSStatus ret = CheckErrorStatus(AudioUnitRender(THIS->io_unit_, ioActionFlags, inTimeStamp, 333 | inBusNumber, inNumberFrames, &THIS->vocal_buffer_), 334 | "render input callback"); 335 | return ret; 336 | } 337 | 338 | OSStatus AUGraphRecorderPlayer::OnIOUnitRenderNotify(void * inRefCon, 339 | AudioUnitRenderActionFlags * ioActionFlags, 340 | const AudioTimeStamp * inTimeStamp, 341 | UInt32 inBusNumber, 342 | UInt32 inNumberFrames, 343 | AudioBufferList * ioData) { 344 | AUGraphRecorderPlayer *rec_and_player = static_cast(inRefCon); 345 | AudioUnitRenderActionFlags flags = *ioActionFlags; 346 | if (flags & kAudioUnitRenderAction_PostRenderError) { 347 | OSStatus last_render_error = noErr; 348 | UInt32 size = sizeof(OSStatus); 349 | CheckHasError(AudioUnitGetProperty(rec_and_player->io_unit_, kAudioUnitProperty_LastRenderError, 350 | kAudioUnitScope_Global, kOutputBus, &last_render_error, &size), 351 | "get last render error"); 352 | CheckHasError(last_render_error, "last render error"); 353 | } 354 | return noErr; 355 | } 356 | 357 | ////// 358 | OSStatus AUGraphRecorderPlayer::OnAskForExportVocalAudioBufferRenderCallback(void *inRefCon, 359 | AudioUnitRenderActionFlags *ioActionFlags, 360 | const AudioTimeStamp *inTimeStamp, 361 | UInt32 inBusNumber, 362 | UInt32 inNumberFrames, 363 | AudioBufferList *ioData) { 364 | AUGraphRecorderPlayer *THIS = static_cast(inRefCon); 365 | CopyAudioBufferListDatas(*ioData, THIS->vocal_buffer_); 366 | // CopyAudioBufferListDatas(THIS->mixed_buffer_for_pushing_, THIS->vocal_buffer_); 367 | // 368 | // // 放在异步serial queue更好 369 | // THIS->export_file_writer_->WriteAudioPacket(THIS->mixed_buffer_for_pushing_.mBuffers[0].mData, 370 | // THIS->mixed_buffer_for_pushing_.mBuffers[0].mDataByteSize); 371 | 372 | return noErr; 373 | } 374 | 375 | OSStatus AUGraphRecorderPlayer::OnAskForExportMusicAudioBufferRenderCallback(void *inRefCon, 376 | AudioUnitRenderActionFlags *ioActionFlags, 377 | const AudioTimeStamp *inTimeStamp, 378 | UInt32 inBusNumber, 379 | UInt32 inNumberFrames, 380 | AudioBufferList *ioData) { 381 | AUGraphRecorderPlayer *THIS = static_cast(inRefCon); 382 | CopyAudioBufferListDatas(*ioData, THIS->music_buffer_); 383 | return noErr; 384 | } 385 | 386 | 387 | } // namespace samples 388 | -------------------------------------------------------------------------------- /AudioUnitSamples/Common/AudioFileReader.h: -------------------------------------------------------------------------------- 1 | // 2 | // AudioFileReader.h 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/26. 6 | // 7 | 8 | #import 9 | 10 | namespace samples { 11 | 12 | class AudioFileReader { 13 | 14 | public: 15 | AudioFileReader(CFURLRef file_path, AudioStreamBasicDescription format); 16 | 17 | void OpenFile(); 18 | 19 | void CloseFile(); 20 | 21 | void ReadAudioFrame(size_t size, void *data, bool& eof); 22 | 23 | private: 24 | AudioFileID file_id_; 25 | CFURLRef file_path_; 26 | 27 | SInt64 read_offset_{0}; 28 | 29 | AudioStreamBasicDescription format_; 30 | 31 | }; 32 | 33 | } // namespace smaples 34 | -------------------------------------------------------------------------------- /AudioUnitSamples/Common/AudioFileReader.mm: -------------------------------------------------------------------------------- 1 | // 2 | // AudioFileReader.m 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/26. 6 | // 7 | 8 | #import "AudioFileReader.h" 9 | #import "CommonUtils.h" 10 | 11 | namespace samples { 12 | 13 | AudioFileReader::AudioFileReader(CFURLRef file_path, 14 | AudioStreamBasicDescription format) 15 | : file_path_(file_path), format_(format) {} 16 | 17 | void AudioFileReader::OpenFile() { 18 | CheckHasError(AudioFileOpenURL(file_path_, kAudioFileReadPermission, 19 | kAudioFileWAVEType, &file_id_), 20 | "open wav file"); 21 | } 22 | 23 | void AudioFileReader::CloseFile() { 24 | CheckHasError(AudioFileClose(file_id_), "close wav file"); 25 | } 26 | 27 | void AudioFileReader::ReadAudioFrame(size_t size, void *data, bool& eof) { 28 | UInt32 read_size = static_cast(size); 29 | OSStatus status = CheckErrorStatus(AudioFileReadBytes(file_id_, 30 | false, 31 | read_offset_, 32 | &read_size, 33 | data), 34 | "read audio frame from file"); 35 | if (status == noErr) { 36 | read_offset_ += size; 37 | } 38 | 39 | if ((status == kAudioFileEndOfFileError) || (read_size < size)) { 40 | // eof, 返回静音数据 41 | memset(data, 0, size); 42 | eof = true; 43 | } 44 | } 45 | 46 | } // namespace samples 47 | -------------------------------------------------------------------------------- /AudioUnitSamples/Common/AudioFileWriter.h: -------------------------------------------------------------------------------- 1 | // 2 | // AudioFileWriter.h 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/26. 6 | // 7 | 8 | #import 9 | #import 10 | #include 11 | #include 12 | #import "CommonUtils.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | namespace samples { 17 | 18 | class AudioFileWriter { 19 | public: 20 | AudioFileWriter(CFURLRef file_ref, 21 | const AudioStreamBasicDescription& format); 22 | 23 | void CreateFile(); 24 | 25 | void WriteAudioPacket(const void *buffer, UInt32 size); 26 | 27 | void CloseFile(); 28 | 29 | private: 30 | CFURLRef file_ref_; 31 | AudioStreamBasicDescription format_; 32 | 33 | AudioFileID file_id_; 34 | size_t write_offset_{0}; 35 | 36 | }; 37 | 38 | } // namespace samples 39 | 40 | NS_ASSUME_NONNULL_END 41 | -------------------------------------------------------------------------------- /AudioUnitSamples/Common/AudioFileWriter.mm: -------------------------------------------------------------------------------- 1 | // 2 | // AudioFileWriter.m 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/26. 6 | // 7 | 8 | #import "AudioFileWriter.h" 9 | 10 | namespace samples { 11 | 12 | AudioFileWriter::AudioFileWriter(CFURLRef file_ref, 13 | const AudioStreamBasicDescription& format) 14 | : file_ref_(file_ref), format_(format) { 15 | } 16 | 17 | void AudioFileWriter::CreateFile() { 18 | std::cout << "======create file @ " << CFURLGetString(file_ref_) << std::endl; 19 | AudioFileFlags flags = kAudioFileFlags_DontPageAlignAudioData | kAudioFileFlags_EraseFile; 20 | CheckHasError(AudioFileCreateWithURL(file_ref_, kAudioFileWAVEType, &format_, flags, &file_id_), 21 | "Create audio file id"); 22 | } 23 | 24 | void AudioFileWriter::WriteAudioPacket(const void *buffer, UInt32 size) { 25 | if (!CheckHasError(AudioFileWriteBytes(file_id_, false, write_offset_, &size, buffer), "write bytes")) { 26 | write_offset_ += size; 27 | } 28 | } 29 | 30 | void AudioFileWriter::CloseFile() { 31 | std::cout << "======close file" << std::endl; 32 | CheckHasError(AudioFileClose(file_id_), "close file"); 33 | } 34 | 35 | } // namespace samples 36 | -------------------------------------------------------------------------------- /AudioUnitSamples/Common/AudioUnitPlayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // AudioUnitPlayer.h 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/26. 6 | // 7 | 8 | #import 9 | #include 10 | #include 11 | #include 12 | #include 13 | #import "CommonUtils.h" 14 | 15 | namespace samples { 16 | 17 | class AudioUnitPlayer { 18 | public: 19 | using OnAskForAudioBufferCallback = 20 | std::function; 21 | 22 | AudioUnitPlayer(AudioStreamBasicDescription format); 23 | 24 | virtual ~AudioUnitPlayer() { 25 | free(audio_buffer_list_.mBuffers[0].mData); 26 | } 27 | 28 | bool SetUpAudioUnit(); 29 | 30 | bool StartAudioUnit(); 31 | 32 | void StopAudioUnit(); 33 | 34 | void SetOnRecordAudioBufferCallback(OnAskForAudioBufferCallback callback) { 35 | std::cout << "======set audio unit wrapper OnRecordAudioBufferCallback" << std::endl; 36 | on_ask_audio_buffer_callback_ = callback; 37 | } 38 | 39 | static OSStatus OnAskingForMoreDataForPlayingRenderCallback(void * inRefCon, 40 | AudioUnitRenderActionFlags *ioActionFlags, 41 | const AudioTimeStamp *inTimeStamp, 42 | UInt32 inBusNumber, 43 | UInt32 inNumberFrames, 44 | AudioBufferList *ioData); 45 | 46 | static OSStatus ioUnitRenderNotify(void * inRefCon, 47 | AudioUnitRenderActionFlags * ioActionFlags, 48 | const AudioTimeStamp * inTimeStamp, 49 | UInt32 inBusNumber, 50 | UInt32 inNumberFrames, 51 | AudioBufferList * ioData); 52 | 53 | protected: 54 | 55 | AudioUnit io_unit_; 56 | AudioBufferList audio_buffer_list_; 57 | AudioStreamBasicDescription audio_format_; 58 | 59 | OnAskForAudioBufferCallback on_ask_audio_buffer_callback_{nullptr}; 60 | }; 61 | 62 | } // samples 63 | 64 | -------------------------------------------------------------------------------- /AudioUnitSamples/Common/AudioUnitPlayer.mm: -------------------------------------------------------------------------------- 1 | // 2 | // AudioUnitPlayer.m 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/26. 6 | // 7 | 8 | #import "AudioUnitPlayer.h" 9 | 10 | namespace samples { 11 | 12 | AudioUnitPlayer::AudioUnitPlayer(AudioStreamBasicDescription format) : 13 | audio_format_(format) { 14 | audio_buffer_list_.mNumberBuffers = 1; 15 | audio_buffer_list_.mBuffers[0].mNumberChannels = format.mChannelsPerFrame; 16 | // size_t bytes_per_frame = format.mChannelsPerFrame * format.mBytesPerFrame * format.mChannelsPerFrame; 17 | // size_t size = kPreferredSampleRate * kPreferredIOBufferDuration * bytes_per_frame; 18 | // TODO (xueshi) should be computed at runtime, not magic number 19 | audio_buffer_list_.mBuffers[0].mDataByteSize = 512; //size; 20 | audio_buffer_list_.mBuffers[0].mData = 21 | malloc(audio_buffer_list_.mBuffers[0].mDataByteSize); 22 | }; 23 | 24 | bool AudioUnitPlayer::SetUpAudioUnit() { 25 | std::cout << "======setup audio unit" << std::endl; 26 | // Create an audio component description to identify the Voice Processing 27 | // I/O audio unit. 28 | AudioComponentDescription io_unit_description; 29 | io_unit_description.componentType = kAudioUnitType_Output; 30 | io_unit_description.componentSubType = kAudioUnitSubType_RemoteIO; 31 | io_unit_description.componentManufacturer = kAudioUnitManufacturer_Apple; 32 | io_unit_description.componentFlags = 0; 33 | io_unit_description.componentFlagsMask = 0; 34 | 35 | // Obtain an audio unit instance given the description. 36 | AudioComponent io_unit_ref = 37 | AudioComponentFindNext(nullptr, &io_unit_description); 38 | 39 | // Create a Voice Processing IO audio unit. 40 | if (CheckHasError(AudioComponentInstanceNew(io_unit_ref, &io_unit_), 41 | "create io unit")) { 42 | io_unit_ = nullptr; 43 | return false; 44 | } 45 | 46 | // Enable input on the input scope of the input element. 47 | // 因为只播放, 所以不需要打开 input 48 | UInt32 enable_input = 0; 49 | if (CheckHasError(AudioUnitSetProperty(io_unit_, kAudioOutputUnitProperty_EnableIO, 50 | kAudioUnitScope_Input, kInputBus, &enable_input, 51 | sizeof(enable_input)), 52 | "set Property_EnableIO on inputbus : input scope")) { 53 | return false; 54 | } 55 | 56 | // Enable output on the output scope of the output element. 57 | UInt32 enable_output = 1; 58 | if (CheckHasError(AudioUnitSetProperty(io_unit_, kAudioOutputUnitProperty_EnableIO, 59 | kAudioUnitScope_Output, kOutputBus, 60 | &enable_output, sizeof(enable_output)), 61 | "set Property_EnableIO on kOutputBus : output scope")) { 62 | return false; 63 | } 64 | 65 | // Disable AU buffer allocation for the recorder, we allocate our own. 66 | // TODO(henrika): not sure that it actually saves resource to make this call. 67 | UInt32 flag = 0; 68 | if (CheckHasError(AudioUnitSetProperty( 69 | io_unit_, kAudioUnitProperty_ShouldAllocateBuffer, 70 | kAudioUnitScope_Output, kInputBus, &flag, sizeof(flag)), 71 | "set Property_ShouldAllocateBuffer on inputbus : output scope")) { 72 | return false; 73 | } 74 | 75 | AudioStreamBasicDescription format = audio_format_; 76 | UInt32 size = sizeof(format); 77 | 78 | // Set the format on the output scope of the input element/bus. 79 | if (CheckHasError(AudioUnitSetProperty(io_unit_, kAudioUnitProperty_StreamFormat, 80 | kAudioUnitScope_Output, kInputBus, &format, size), 81 | "set Property_StreamFormat on inputbus : output scope")) { 82 | return false; 83 | } 84 | 85 | // Set the format on the input scope of the output element/bus. 86 | if (CheckHasError(AudioUnitSetProperty(io_unit_, kAudioUnitProperty_StreamFormat, 87 | kAudioUnitScope_Input, kOutputBus, &format, size), 88 | "set Property_StreamFormat on outputbus : input scope")) { 89 | return false; 90 | } 91 | 92 | /// Render Callback 是 IO unit 的 outpus 回调我们, 请求要播放的数据的回调, 我们在这个 93 | /// 回调里, 填充满 ioData, 这部分数据将会被播放出来 94 | /// 如果想静音的话, flag 需要设置为 kAudioUnitRenderAction_OutputIsSilence, 并且把 95 | /// ioData 的数据全置为 0. 96 | AURenderCallbackStruct render_callback; 97 | render_callback.inputProc = OnAskingForMoreDataForPlayingRenderCallback; 98 | render_callback.inputProcRefCon = this; 99 | if (CheckHasError(AudioUnitSetProperty(io_unit_, 100 | kAudioUnitProperty_SetRenderCallback, 101 | kAudioUnitScope_Input, 102 | kOutputBus, 103 | &render_callback, 104 | sizeof(render_callback)), 105 | "set render callback on output bus: input scope")) { 106 | return false; 107 | } 108 | 109 | if (CheckHasError(AudioUnitAddRenderNotify(io_unit_, ioUnitRenderNotify, this), "add Render notify")) { 110 | return false; 111 | } 112 | 113 | 114 | // Initialize the Voice Processing I/O unit instance. 115 | // Calls to AudioUnitInitialize() can fail if called back-to-back on 116 | // different ADM instances. The error message in this case is -66635 which is 117 | // undocumented. Tests have shown that calling AudioUnitInitialize a second 118 | // time, after a short sleep, avoids this issue. 119 | // See webrtc:5166 for details. 120 | // int failed_initalize_attempts = 0; 121 | bool has_error = CheckHasError(AudioUnitInitialize(io_unit_), "Initialize IO unit"); 122 | while (has_error) { 123 | // RTCLogError(@"Failed to initialize the Voice Processing I/O unit. " 124 | // "Error=%ld.", 125 | // (long)result); 126 | // ++failed_initalize_attempts; 127 | // if (failed_initalize_attempts == kMaxNumberOfAudioUnitInitializeAttempts) { 128 | // // Max number of initialization attempts exceeded, hence abort. 129 | // RTCLogError(@"Too many initialization attempts."); 130 | // return false; 131 | // } 132 | // RTCLog(@"Pause 100ms and try audio unit initialization again..."); 133 | [NSThread sleepForTimeInterval:0.1f]; 134 | has_error = CheckHasError(AudioUnitInitialize(io_unit_), "Initialize IO unit"); 135 | } 136 | 137 | return has_error; 138 | } 139 | 140 | bool AudioUnitPlayer::StartAudioUnit() { 141 | std::cout << "======start audio unit" << std::endl; 142 | return AudioOutputUnitStart(io_unit_); 143 | } 144 | 145 | void AudioUnitPlayer::StopAudioUnit() { 146 | std::cout << "======stop audio unit" << std::endl; 147 | on_ask_audio_buffer_callback_ = nullptr; 148 | CheckHasError(AudioOutputUnitStop(io_unit_), "stop io unit"); 149 | CheckHasError(AudioUnitUninitialize(io_unit_), "deinit io unit"); 150 | CheckHasError(AudioComponentInstanceDispose(io_unit_), "dispose io unit"); 151 | } 152 | 153 | OSStatus AudioUnitPlayer::OnAskingForMoreDataForPlayingRenderCallback( 154 | void * inRefCon, 155 | AudioUnitRenderActionFlags *ioActionFlags, 156 | const AudioTimeStamp *inTimeStamp, 157 | UInt32 inBusNumber, 158 | UInt32 inNumberFrames, 159 | AudioBufferList *ioData) { 160 | AudioUnitPlayer *player = static_cast(inRefCon); 161 | bool eof = false; 162 | player->on_ask_audio_buffer_callback_(ioData->mBuffers[0].mData, 163 | ioData->mBuffers[0].mDataByteSize, 164 | eof); 165 | if (eof) { 166 | //... 167 | } 168 | // samples::AudioUnitPlayer *wrapper = static_cast(inRefCon); 169 | // OSStatus status = CheckErrorStatus(AudioUnitRender(wrapper->io_unit_, ioActionFlags, inTimeStamp, 170 | // inBusNumber, inNumberFrames, &wrapper->audio_buffer_list_), 171 | // "AudioUnitRender call"); 172 | // if (status == noErr && wrapper->on_record_callback_) { 173 | // wrapper->on_record_callback_(wrapper->audio_buffer_list_); 174 | // } 175 | return noErr; 176 | } 177 | 178 | OSStatus AudioUnitPlayer::ioUnitRenderNotify(void * inRefCon, 179 | AudioUnitRenderActionFlags * ioActionFlags, 180 | const AudioTimeStamp * inTimeStamp, 181 | UInt32 inBusNumber, 182 | UInt32 inNumberFrames, 183 | AudioBufferList * ioData) 184 | { 185 | // // !!! this method is timing sensitive, better not add any wasting time code here, even nslog 186 | // if (*ioActionFlags & kAudioUnitRenderAction_PostRender) { 187 | // } 188 | AudioUnitPlayer *player = static_cast(inRefCon); 189 | AudioUnitRenderActionFlags flags = *ioActionFlags; 190 | // @constant kAudioUnitRenderAction_PostRenderError 191 | // If this flag is set on the post-render call an error was returned by the 192 | // AUs render operation. In this case, the error can be retrieved through the 193 | // lastRenderError property and the audio data in ioData handed to the post-render 194 | // notification will be invalid. 195 | 196 | if (flags & kAudioUnitRenderAction_PostRenderError) { 197 | OSStatus ret; 198 | UInt32 size = sizeof(OSStatus); 199 | // OSStatus result = AudioUnitGetProperty(_audioUnit, 200 | // kAudioUnitProperty_LastRenderError, kAudioUnitScope_Global, 0, &ret, &size); 201 | // 202 | OSStatus status = AudioUnitGetProperty(player->io_unit_, kAudioUnitProperty_LastRenderError, kAudioUnitScope_Global, kOutputBus, &ret, &size); 203 | NSLog(@"======status: %@, ret: %@", @(status), @(ret)); 204 | } 205 | return noErr; 206 | } 207 | 208 | 209 | } // namespace samples 210 | -------------------------------------------------------------------------------- /AudioUnitSamples/Common/AudioUnitRecorder.h: -------------------------------------------------------------------------------- 1 | // 2 | // AudioUnitRecorder.h 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/26. 6 | // 7 | 8 | #import 9 | #include 10 | #include 11 | #include 12 | #include 13 | #import "CommonUtils.h" 14 | 15 | namespace samples { 16 | 17 | class AudioUnitRecorder { 18 | public: 19 | using OnRecordAudioBufferCallback = 20 | std::function; 21 | 22 | AudioUnitRecorder(AudioStreamBasicDescription format, CFURLRef record_file_url); 23 | 24 | virtual ~AudioUnitRecorder() { 25 | free(audio_buffer_list_.mBuffers[0].mData); 26 | } 27 | 28 | bool SetUpAudioUnit(); 29 | 30 | bool StartAudioUnit(); 31 | 32 | void StopAudioUnit(); 33 | 34 | void SetOnRecordAudioBufferCallback(OnRecordAudioBufferCallback callback) { 35 | std::cout << "======set audio unit wrapper OnRecordAudioBufferCallback" << std::endl; 36 | on_record_callback_ = callback; 37 | } 38 | 39 | static OSStatus OnRecordedDataIsAvailable(void * inRefCon, 40 | AudioUnitRenderActionFlags *ioActionFlags, 41 | const AudioTimeStamp *inTimeStamp, 42 | UInt32 inBusNumber, 43 | UInt32 inNumberFrames, 44 | AudioBufferList *ioData); 45 | 46 | protected: 47 | 48 | AudioUnit io_unit_; 49 | AudioFileID audio_file_; 50 | AudioBufferList audio_buffer_list_; 51 | AudioStreamBasicDescription audio_format_; 52 | 53 | OnRecordAudioBufferCallback on_record_callback_{nullptr}; 54 | }; 55 | 56 | } // samples 57 | 58 | -------------------------------------------------------------------------------- /AudioUnitSamples/Common/AudioUnitRecorder.mm: -------------------------------------------------------------------------------- 1 | // 2 | // AudioUnitRecorder.m 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/26. 6 | // 7 | 8 | #import "AudioUnitRecorder.h" 9 | 10 | namespace samples { 11 | 12 | AudioUnitRecorder::AudioUnitRecorder(AudioStreamBasicDescription format, 13 | CFURLRef record_file_url) : 14 | audio_format_(format) { 15 | audio_buffer_list_.mNumberBuffers = 1; 16 | audio_buffer_list_.mBuffers[0].mNumberChannels = format.mChannelsPerFrame; 17 | // size_t bytes_per_frame = format.mChannelsPerFrame * format.mBytesPerFrame * format.mChannelsPerFrame; 18 | // size_t size = kPreferredSampleRate * kPreferredIOBufferDuration * bytes_per_frame; 19 | // TODO (xueshi) should be computed at runtime, not magic number 20 | audio_buffer_list_.mBuffers[0].mDataByteSize = 512; //size; 21 | audio_buffer_list_.mBuffers[0].mData = 22 | malloc(audio_buffer_list_.mBuffers[0].mDataByteSize); 23 | }; 24 | 25 | bool AudioUnitRecorder::SetUpAudioUnit() { 26 | std::cout << "======setup audio unit" << std::endl; 27 | // Create an audio component description to identify the Voice Processing 28 | // I/O audio unit. 29 | AudioComponentDescription io_unit_description; 30 | io_unit_description.componentType = kAudioUnitType_Output; 31 | io_unit_description.componentSubType = kAudioUnitSubType_RemoteIO; 32 | io_unit_description.componentManufacturer = kAudioUnitManufacturer_Apple; 33 | io_unit_description.componentFlags = 0; 34 | io_unit_description.componentFlagsMask = 0; 35 | 36 | // Obtain an audio unit instance given the description. 37 | AudioComponent io_unit_ref = 38 | AudioComponentFindNext(nullptr, &io_unit_description); 39 | 40 | // Create a Voice Processing IO audio unit. 41 | if (CheckHasError(AudioComponentInstanceNew(io_unit_ref, &io_unit_), 42 | "create io unit")) { 43 | io_unit_ = nullptr; 44 | return false; 45 | } 46 | 47 | // Enable input on the input scope of the input element. 48 | UInt32 enable_input = 1; 49 | if (CheckHasError(AudioUnitSetProperty(io_unit_, kAudioOutputUnitProperty_EnableIO, 50 | kAudioUnitScope_Input, kInputBus, &enable_input, 51 | sizeof(enable_input)), 52 | "set Property_EnableIO on inputbus : input scope")) { 53 | return false; 54 | } 55 | 56 | // Enable output on the output scope of the output element. 57 | // 因为只录制, 所以关闭 output 58 | UInt32 enable_output = 0; 59 | if (CheckHasError(AudioUnitSetProperty(io_unit_, kAudioOutputUnitProperty_EnableIO, 60 | kAudioUnitScope_Output, kOutputBus, 61 | &enable_output, sizeof(enable_output)), 62 | "set Property_EnableIO on kOutputBus : output scope")) { 63 | return false; 64 | } 65 | 66 | // Disable AU buffer allocation for the recorder, we allocate our own. 67 | // TODO(henrika): not sure that it actually saves resource to make this call. 68 | UInt32 flag = 0; 69 | if (CheckHasError(AudioUnitSetProperty( 70 | io_unit_, kAudioUnitProperty_ShouldAllocateBuffer, 71 | kAudioUnitScope_Output, kInputBus, &flag, sizeof(flag)), 72 | "set Property_ShouldAllocateBuffer on inputbus : output scope")) { 73 | return false; 74 | } 75 | 76 | AudioStreamBasicDescription format = audio_format_; 77 | UInt32 size = sizeof(format); 78 | // Set the format on the output scope of the input element/bus. 79 | if (CheckHasError(AudioUnitSetProperty(io_unit_, kAudioUnitProperty_StreamFormat, 80 | kAudioUnitScope_Output, kInputBus, &format, size), 81 | "set Property_StreamFormat on inputbus : output scope")) { 82 | return false; 83 | } 84 | 85 | // Set the format on the input scope of the output element/bus. 86 | if (CheckHasError(AudioUnitSetProperty(io_unit_, kAudioUnitProperty_StreamFormat, 87 | kAudioUnitScope_Input, kOutputBus, &format, size), 88 | "set Property_StreamFormat on outputbus : input scope")) { 89 | return false; 90 | } 91 | 92 | // Specify the callback to be called by the I/O thread to us when input audio 93 | // is available. The recorded samples can then be obtained by calling the 94 | // AudioUnitRender() method. 95 | 96 | // input callback 是告诉我们已经采集到了数据, 需要我们使用 AudioUnitRender 从上游获取采集到的数据 97 | // 注意回调到 OnRecordedDataIsAvailable 的参数里, ioData 是 nullptr, 所以在调用 AudioUnitRender 98 | // 时, 不可使用回调里的 ioData 参数, 需要我们自己创建 99 | AURenderCallbackStruct input_callback; 100 | input_callback.inputProc = OnRecordedDataIsAvailable; 101 | input_callback.inputProcRefCon = this; 102 | if (CheckHasError(AudioUnitSetProperty(io_unit_, 103 | kAudioOutputUnitProperty_SetInputCallback, 104 | kAudioUnitScope_Global, kInputBus, 105 | &input_callback, sizeof(input_callback)), 106 | "set input callback on inputbus: global scope")) { 107 | return false; 108 | } 109 | 110 | // Initialize the Voice Processing I/O unit instance. 111 | // Calls to AudioUnitInitialize() can fail if called back-to-back on 112 | // different ADM instances. The error message in this case is -66635 which is 113 | // undocumented. Tests have shown that calling AudioUnitInitialize a second 114 | // time, after a short sleep, avoids this issue. 115 | // See webrtc:5166 for details. 116 | // int failed_initalize_attempts = 0; 117 | bool has_error = CheckHasError(AudioUnitInitialize(io_unit_), "Initialize IO unit"); 118 | while (has_error) { 119 | // RTCLogError(@"Failed to initialize the Voice Processing I/O unit. " 120 | // "Error=%ld.", 121 | // (long)result); 122 | // ++failed_initalize_attempts; 123 | // if (failed_initalize_attempts == kMaxNumberOfAudioUnitInitializeAttempts) { 124 | // // Max number of initialization attempts exceeded, hence abort. 125 | // RTCLogError(@"Too many initialization attempts."); 126 | // return false; 127 | // } 128 | // RTCLog(@"Pause 100ms and try audio unit initialization again..."); 129 | [NSThread sleepForTimeInterval:0.1f]; 130 | has_error = CheckHasError(AudioUnitInitialize(io_unit_), "Initialize IO unit"); 131 | } 132 | 133 | return has_error; 134 | } 135 | 136 | bool AudioUnitRecorder::StartAudioUnit() { 137 | std::cout << "======start audio unit" << std::endl; 138 | return AudioOutputUnitStart(io_unit_); 139 | } 140 | 141 | void AudioUnitRecorder::StopAudioUnit() { 142 | std::cout << "======stop audio unit" << std::endl; 143 | on_record_callback_ = nullptr; 144 | CheckHasError(AudioOutputUnitStop(io_unit_), "stop io unit"); 145 | CheckHasError(AudioUnitUninitialize(io_unit_), "deinit io unit"); 146 | CheckHasError(AudioComponentInstanceDispose(io_unit_), "dispose io unit"); 147 | } 148 | 149 | OSStatus AudioUnitRecorder::OnRecordedDataIsAvailable(void * inRefCon, 150 | AudioUnitRenderActionFlags *ioActionFlags, 151 | const AudioTimeStamp *inTimeStamp, 152 | UInt32 inBusNumber, 153 | UInt32 inNumberFrames, 154 | AudioBufferList *ioData) { 155 | samples::AudioUnitRecorder *wrapper = static_cast(inRefCon); 156 | OSStatus status = CheckErrorStatus(AudioUnitRender(wrapper->io_unit_, ioActionFlags, inTimeStamp, 157 | inBusNumber, inNumberFrames, &wrapper->audio_buffer_list_), 158 | "AudioUnitRender call"); 159 | if (status == noErr && wrapper->on_record_callback_) { 160 | wrapper->on_record_callback_(wrapper->audio_buffer_list_); 161 | } 162 | return status; 163 | } 164 | 165 | } // namespace samples 166 | -------------------------------------------------------------------------------- /AudioUnitSamples/Common/AudioUnitRecorderAndPlayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // AudioUnitRecorderAndPlayer.h 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/27. 6 | // 7 | 8 | #import 9 | 10 | namespace samples { 11 | 12 | class AudioUnitRecorderAndPlayer { 13 | public: 14 | AudioUnitRecorderAndPlayer(const AudioStreamBasicDescription& format); 15 | 16 | bool SetUpAudioUnit(); 17 | 18 | bool StartAudioUnit(); 19 | 20 | void StopAudioUnit(); 21 | 22 | private: 23 | static OSStatus OnIOUnitRenderNotify(void * inRefCon, 24 | AudioUnitRenderActionFlags *ioActionFlags, 25 | const AudioTimeStamp *inTimeStamp, 26 | UInt32 inBusNumber, 27 | UInt32 inNumberFrames, 28 | AudioBufferList * ioData); 29 | static OSStatus OnAskingForMoreDataForPlayingRenderCallback(void * inRefCon, 30 | AudioUnitRenderActionFlags *ioActionFlags, 31 | const AudioTimeStamp *inTimeStamp, 32 | UInt32 inBusNumber, 33 | UInt32 inNumberFrames, 34 | AudioBufferList * ioData); 35 | 36 | static OSStatus OnRecordedDataIsAvailable(void * inRefCon, 37 | AudioUnitRenderActionFlags *ioActionFlags, 38 | const AudioTimeStamp *inTimeStamp, 39 | UInt32 inBusNumber, 40 | UInt32 inNumberFrames, 41 | AudioBufferList * ioData); 42 | 43 | 44 | 45 | AudioStreamBasicDescription format_; 46 | AudioUnit io_unit_; 47 | AudioBufferList buffer_; 48 | 49 | }; 50 | 51 | } 52 | -------------------------------------------------------------------------------- /AudioUnitSamples/Common/AudioUnitRecorderAndPlayer.mm: -------------------------------------------------------------------------------- 1 | // 2 | // AudioUnitRecorderAndPlayer.m 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/27. 6 | // 7 | 8 | #import "AudioUnitRecorderAndPlayer.h" 9 | #include 10 | #import "CommonUtils.h" 11 | 12 | namespace samples { 13 | 14 | AudioUnitRecorderAndPlayer::AudioUnitRecorderAndPlayer(const AudioStreamBasicDescription& format) : format_(format) { 15 | buffer_.mNumberBuffers = 1; 16 | buffer_.mBuffers[0].mNumberChannels = 1; 17 | buffer_.mBuffers[0].mDataByteSize = 4096; 18 | buffer_.mBuffers[0].mData = malloc(buffer_.mBuffers[0].mDataByteSize); 19 | 20 | } 21 | 22 | bool AudioUnitRecorderAndPlayer::SetUpAudioUnit() { 23 | std::cout << "======setup audio unit" << std::endl; 24 | // Create an audio component description to identify the Voice Processing 25 | // I/O audio unit. 26 | AudioComponentDescription io_unit_description; 27 | io_unit_description.componentType = kAudioUnitType_Output; 28 | io_unit_description.componentSubType = kAudioUnitSubType_RemoteIO; 29 | io_unit_description.componentManufacturer = kAudioUnitManufacturer_Apple; 30 | io_unit_description.componentFlags = 0; 31 | io_unit_description.componentFlagsMask = 0; 32 | 33 | // Obtain an audio unit instance given the description. 34 | AudioComponent io_unit_ref = 35 | AudioComponentFindNext(nullptr, &io_unit_description); 36 | 37 | // Create a Voice Processing IO audio unit. 38 | if (CheckHasError(AudioComponentInstanceNew(io_unit_ref, &io_unit_), 39 | "create io unit")) { 40 | io_unit_ = nullptr; 41 | return false; 42 | } 43 | 44 | // Enable input on the input scope of the input element. 45 | UInt32 enable_input = 1; 46 | if (CheckHasError(AudioUnitSetProperty(io_unit_, kAudioOutputUnitProperty_EnableIO, 47 | kAudioUnitScope_Input, kInputBus, &enable_input, 48 | sizeof(enable_input)), 49 | "set Property_EnableIO on inputbus : input scope")) { 50 | return false; 51 | } 52 | 53 | // Enable output on the output scope of the output element. 54 | UInt32 enable_output = 1; 55 | if (CheckHasError(AudioUnitSetProperty(io_unit_, kAudioOutputUnitProperty_EnableIO, 56 | kAudioUnitScope_Output, kOutputBus, 57 | &enable_output, sizeof(enable_output)), 58 | "set Property_EnableIO on kOutputBus : output scope")) { 59 | return false; 60 | } 61 | 62 | // Disable AU buffer allocation for the recorder, we allocate our own. 63 | // TODO(henrika): not sure that it actually saves resource to make this call. 64 | UInt32 flag = 0; 65 | if (CheckHasError(AudioUnitSetProperty( 66 | io_unit_, kAudioUnitProperty_ShouldAllocateBuffer, 67 | kAudioUnitScope_Output, kInputBus, &flag, sizeof(flag)), 68 | "set Property_ShouldAllocateBuffer on inputbus : output scope")) { 69 | return false; 70 | } 71 | 72 | UInt32 size = sizeof(format_); 73 | // Set the format on the output scope of the input element/bus. 74 | if (CheckHasError(AudioUnitSetProperty(io_unit_, kAudioUnitProperty_StreamFormat, 75 | kAudioUnitScope_Output, kInputBus, &format_, size), 76 | "set Property_StreamFormat on inputbus : output scope")) { 77 | return false; 78 | } 79 | 80 | // Set the format on the input scope of the output element/bus. 81 | if (CheckHasError(AudioUnitSetProperty(io_unit_, kAudioUnitProperty_StreamFormat, 82 | kAudioUnitScope_Input, kOutputBus, &format_, size), 83 | "set Property_StreamFormat on outputbus : input scope")) { 84 | return false; 85 | } 86 | 87 | AURenderCallbackStruct render_callback; 88 | render_callback.inputProc = OnAskingForMoreDataForPlayingRenderCallback; 89 | render_callback.inputProcRefCon = this; 90 | if (CheckHasError(AudioUnitSetProperty(io_unit_, 91 | kAudioUnitProperty_SetRenderCallback, 92 | kAudioUnitScope_Input, 93 | kOutputBus, 94 | &render_callback, 95 | sizeof(render_callback)), 96 | "set render callback on output bus: input scope")) { 97 | return false; 98 | } 99 | 100 | 101 | // Specify the callback to be called by the I/O thread to us when input audio 102 | // is available. The recorded samples can then be obtained by calling the 103 | // AudioUnitRender() method. 104 | 105 | // input callback 是告诉我们已经采集到了数据, 需要我们使用 AudioUnitRender 从上游获取采集到的数据 106 | // 注意回调到 OnRecordedDataIsAvailable 的参数里, ioData 是 nullptr, 所以在调用 AudioUnitRender 107 | // 时, 不可使用回调里的 ioData 参数, 需要我们自己创建 108 | AURenderCallbackStruct input_callback; 109 | input_callback.inputProc = OnRecordedDataIsAvailable; 110 | input_callback.inputProcRefCon = this; 111 | if (CheckHasError(AudioUnitSetProperty(io_unit_, 112 | kAudioOutputUnitProperty_SetInputCallback, 113 | kAudioUnitScope_Global, kInputBus, 114 | &input_callback, sizeof(input_callback)), 115 | "set input callback on inputbus: global scope")) { 116 | return false; 117 | } 118 | 119 | // Initialize the Voice Processing I/O unit instance. 120 | // Calls to AudioUnitInitialize() can fail if called back-to-back on 121 | // different ADM instances. The error message in this case is -66635 which is 122 | // undocumented. Tests have shown that calling AudioUnitInitialize a second 123 | // time, after a short sleep, avoids this issue. 124 | // See webrtc:5166 for details. 125 | // int failed_initalize_attempts = 0; 126 | bool has_error = CheckHasError(AudioUnitInitialize(io_unit_), "Initialize IO unit"); 127 | while (has_error) { 128 | // RTCLogError(@"Failed to initialize the Voice Processing I/O unit. " 129 | // "Error=%ld.", 130 | // (long)result); 131 | // ++failed_initalize_attempts; 132 | // if (failed_initalize_attempts == kMaxNumberOfAudioUnitInitializeAttempts) { 133 | // // Max number of initialization attempts exceeded, hence abort. 134 | // RTCLogError(@"Too many initialization attempts."); 135 | // return false; 136 | // } 137 | // RTCLog(@"Pause 100ms and try audio unit initialization again..."); 138 | [NSThread sleepForTimeInterval:0.1f]; 139 | has_error = CheckHasError(AudioUnitInitialize(io_unit_), "Initialize IO unit"); 140 | } 141 | 142 | return true; 143 | } 144 | 145 | bool AudioUnitRecorderAndPlayer::StartAudioUnit() { 146 | OSStatus result = AudioOutputUnitStart(io_unit_); 147 | if (result != noErr) { 148 | NSLog(@"======error AudioOutputUnitStart(), %@", @(result)); 149 | return false; 150 | } 151 | return true; 152 | 153 | } 154 | 155 | void AudioUnitRecorderAndPlayer::StopAudioUnit() { 156 | CheckHasError(AudioOutputUnitStop(io_unit_), "stop io unit"); 157 | CheckHasError(AudioUnitUninitialize(io_unit_), "deinit io unit"); 158 | CheckHasError(AudioComponentInstanceDispose(io_unit_), "dispose io unit"); 159 | 160 | } 161 | 162 | 163 | OSStatus AudioUnitRecorderAndPlayer::OnIOUnitRenderNotify(void * inRefCon, 164 | AudioUnitRenderActionFlags * ioActionFlags, 165 | const AudioTimeStamp * inTimeStamp, 166 | UInt32 inBusNumber, 167 | UInt32 inNumberFrames, 168 | AudioBufferList * ioData) { 169 | AudioUnitRecorderAndPlayer *rec_and_player = static_cast(inRefCon); 170 | AudioUnitRenderActionFlags flags = *ioActionFlags; 171 | if (flags & kAudioUnitRenderAction_PostRenderError) { 172 | OSStatus ret = noErr; 173 | UInt32 size = sizeof(OSStatus); 174 | OSStatus status = AudioUnitGetProperty(rec_and_player->io_unit_, kAudioUnitProperty_LastRenderError, kAudioUnitScope_Global, kOutputBus, &ret, &size); 175 | NSLog(@"======PostRenderError, get status: %@, last render error ret: %@", @(status), @(ret)); 176 | } 177 | return noErr; 178 | } 179 | 180 | OSStatus AudioUnitRecorderAndPlayer::OnAskingForMoreDataForPlayingRenderCallback(void * inRefCon, 181 | AudioUnitRenderActionFlags * ioActionFlags, 182 | const AudioTimeStamp * inTimeStamp, 183 | UInt32 inBusNumber, 184 | UInt32 inNumberFrames, 185 | AudioBufferList * ioData) { 186 | AudioUnitRecorderAndPlayer *rec_and_player = static_cast(inRefCon); 187 | for (UInt32 i = 0; i< ioData->mNumberBuffers; ++i) { 188 | memcpy(ioData->mBuffers[i].mData, 189 | rec_and_player->buffer_.mBuffers[i].mData, 190 | rec_and_player->buffer_.mBuffers[i].mDataByteSize); 191 | } 192 | return noErr; 193 | } 194 | 195 | OSStatus AudioUnitRecorderAndPlayer::OnRecordedDataIsAvailable(void * inRefCon, 196 | AudioUnitRenderActionFlags * ioActionFlags, 197 | const AudioTimeStamp * inTimeStamp, 198 | UInt32 inBusNumber, 199 | UInt32 inNumberFrames, 200 | AudioBufferList * ioData) { 201 | AudioUnitRecorderAndPlayer *rec_and_player = static_cast(inRefCon); 202 | OSStatus status = AudioUnitRender(rec_and_player->io_unit_, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &rec_and_player->buffer_); 203 | if (status != noErr) { 204 | NSLog(@"======AudioUnitRender error: %@", @(status)); 205 | } 206 | return status; 207 | } 208 | } 209 | 210 | -------------------------------------------------------------------------------- /AudioUnitSamples/Common/AudioUnitRecorderAndPlayerComplex.h: -------------------------------------------------------------------------------- 1 | // 2 | // AudioUnitRecorderAndPlayerComplex.h 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/27. 6 | // 7 | 8 | #import 9 | #include 10 | #include 11 | 12 | namespace samples { 13 | 14 | class AudioUnitRecorderAndPlayerComplex { 15 | public: 16 | using OnAskForAudioBufferCallback = 17 | std::function; 18 | 19 | using OnRecordAudioBufferCallback = 20 | std::function; 21 | 22 | AudioUnitRecorderAndPlayerComplex(const AudioStreamBasicDescription& format); 23 | 24 | bool SetUpAudioUnit(); 25 | 26 | bool StartAudioUnit(); 27 | 28 | void StopAudioUnit(); 29 | 30 | void SetOnAskForAudioBufferCallback(OnAskForAudioBufferCallback callback) { 31 | ask_for_audio_buffer_callback_ = callback; 32 | } 33 | 34 | void SetOnRecordAudioBufferCallback(OnRecordAudioBufferCallback callback) { 35 | recorded_audio_buffer_callback_ = callback; 36 | } 37 | 38 | private: 39 | static OSStatus OnIOUnitRenderNotify(void * inRefCon, 40 | AudioUnitRenderActionFlags *ioActionFlags, 41 | const AudioTimeStamp *inTimeStamp, 42 | UInt32 inBusNumber, 43 | UInt32 inNumberFrames, 44 | AudioBufferList * ioData); 45 | static OSStatus OnAskingForMoreDataForPlayingRenderCallback(void * inRefCon, 46 | AudioUnitRenderActionFlags *ioActionFlags, 47 | const AudioTimeStamp *inTimeStamp, 48 | UInt32 inBusNumber, 49 | UInt32 inNumberFrames, 50 | AudioBufferList * ioData); 51 | 52 | static OSStatus OnRecordedDataIsAvailable(void * inRefCon, 53 | AudioUnitRenderActionFlags *ioActionFlags, 54 | const AudioTimeStamp *inTimeStamp, 55 | UInt32 inBusNumber, 56 | UInt32 inNumberFrames, 57 | AudioBufferList * ioData); 58 | 59 | 60 | 61 | AudioStreamBasicDescription format_; 62 | AudioUnit io_unit_; 63 | AudioBufferList buffer_; 64 | 65 | OnAskForAudioBufferCallback ask_for_audio_buffer_callback_; 66 | OnRecordAudioBufferCallback recorded_audio_buffer_callback_; 67 | }; 68 | 69 | } 70 | -------------------------------------------------------------------------------- /AudioUnitSamples/Common/AudioUnitRecorderAndPlayerComplex.mm: -------------------------------------------------------------------------------- 1 | // 2 | // AudioUnitRecorderAndPlayerComplex.m 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/27. 6 | // 7 | 8 | #import "AudioUnitRecorderAndPlayerComplex.h" 9 | #include 10 | #import "CommonUtils.h" 11 | 12 | namespace samples { 13 | 14 | AudioUnitRecorderAndPlayerComplex::AudioUnitRecorderAndPlayerComplex(const AudioStreamBasicDescription& format) : format_(format) { 15 | buffer_.mNumberBuffers = 1; 16 | buffer_.mBuffers[0].mNumberChannels = 1; 17 | buffer_.mBuffers[0].mDataByteSize = 4096; 18 | buffer_.mBuffers[0].mData = malloc(buffer_.mBuffers[0].mDataByteSize); 19 | } 20 | 21 | bool AudioUnitRecorderAndPlayerComplex::SetUpAudioUnit() { 22 | std::cout << "======setup audio unit" << std::endl; 23 | // Create an audio component description to identify the Voice Processing 24 | // I/O audio unit. 25 | AudioComponentDescription io_unit_description; 26 | io_unit_description.componentType = kAudioUnitType_Output; 27 | io_unit_description.componentSubType = kAudioUnitSubType_RemoteIO; 28 | io_unit_description.componentManufacturer = kAudioUnitManufacturer_Apple; 29 | io_unit_description.componentFlags = 0; 30 | io_unit_description.componentFlagsMask = 0; 31 | 32 | // Obtain an audio unit instance given the description. 33 | AudioComponent io_unit_ref = 34 | AudioComponentFindNext(nullptr, &io_unit_description); 35 | 36 | // Create a Voice Processing IO audio unit. 37 | if (CheckHasError(AudioComponentInstanceNew(io_unit_ref, &io_unit_), 38 | "create io unit")) { 39 | io_unit_ = nullptr; 40 | return false; 41 | } 42 | 43 | // Enable input on the input scope of the input element. 44 | UInt32 enable_input = 1; 45 | if (CheckHasError(AudioUnitSetProperty(io_unit_, kAudioOutputUnitProperty_EnableIO, 46 | kAudioUnitScope_Input, kInputBus, &enable_input, 47 | sizeof(enable_input)), 48 | "set Property_EnableIO on inputbus : input scope")) { 49 | return false; 50 | } 51 | 52 | // Enable output on the output scope of the output element. 53 | UInt32 enable_output = 1; 54 | if (CheckHasError(AudioUnitSetProperty(io_unit_, kAudioOutputUnitProperty_EnableIO, 55 | kAudioUnitScope_Output, kOutputBus, 56 | &enable_output, sizeof(enable_output)), 57 | "set Property_EnableIO on kOutputBus : output scope")) { 58 | return false; 59 | } 60 | 61 | // Disable AU buffer allocation for the recorder, we allocate our own. 62 | // TODO(henrika): not sure that it actually saves resource to make this call. 63 | UInt32 flag = 0; 64 | if (CheckHasError(AudioUnitSetProperty( 65 | io_unit_, kAudioUnitProperty_ShouldAllocateBuffer, 66 | kAudioUnitScope_Output, kInputBus, &flag, sizeof(flag)), 67 | "set Property_ShouldAllocateBuffer on inputbus : output scope")) { 68 | return false; 69 | } 70 | 71 | UInt32 size = sizeof(format_); 72 | // Set the format on the output scope of the input element/bus. 73 | if (CheckHasError(AudioUnitSetProperty(io_unit_, kAudioUnitProperty_StreamFormat, 74 | kAudioUnitScope_Output, kInputBus, &format_, size), 75 | "set Property_StreamFormat on inputbus : output scope")) { 76 | return false; 77 | } 78 | 79 | // Set the format on the input scope of the output element/bus. 80 | if (CheckHasError(AudioUnitSetProperty(io_unit_, kAudioUnitProperty_StreamFormat, 81 | kAudioUnitScope_Input, kOutputBus, &format_, size), 82 | "set Property_StreamFormat on outputbus : input scope")) { 83 | return false; 84 | } 85 | 86 | AURenderCallbackStruct render_callback; 87 | render_callback.inputProc = OnAskingForMoreDataForPlayingRenderCallback; 88 | render_callback.inputProcRefCon = this; 89 | if (CheckHasError(AudioUnitSetProperty(io_unit_, 90 | kAudioUnitProperty_SetRenderCallback, 91 | kAudioUnitScope_Input, 92 | kOutputBus, 93 | &render_callback, 94 | sizeof(render_callback)), 95 | "set render callback on output bus: input scope")) { 96 | return false; 97 | } 98 | 99 | 100 | // Specify the callback to be called by the I/O thread to us when input audio 101 | // is available. The recorded samples can then be obtained by calling the 102 | // AudioUnitRender() method. 103 | 104 | // input callback 是告诉我们已经采集到了数据, 需要我们使用 AudioUnitRender 从上游获取采集到的数据 105 | // 注意回调到 OnRecordedDataIsAvailable 的参数里, ioData 是 nullptr, 所以在调用 AudioUnitRender 106 | // 时, 不可使用回调里的 ioData 参数, 需要我们自己创建 107 | AURenderCallbackStruct input_callback; 108 | input_callback.inputProc = OnRecordedDataIsAvailable; 109 | input_callback.inputProcRefCon = this; 110 | if (CheckHasError(AudioUnitSetProperty(io_unit_, 111 | kAudioOutputUnitProperty_SetInputCallback, 112 | kAudioUnitScope_Global, kInputBus, 113 | &input_callback, sizeof(input_callback)), 114 | "set input callback on inputbus: global scope")) { 115 | return false; 116 | } 117 | 118 | // Initialize the Voice Processing I/O unit instance. 119 | // Calls to AudioUnitInitialize() can fail if called back-to-back on 120 | // different ADM instances. The error message in this case is -66635 which is 121 | // undocumented. Tests have shown that calling AudioUnitInitialize a second 122 | // time, after a short sleep, avoids this issue. 123 | // See webrtc:5166 for details. 124 | // int failed_initalize_attempts = 0; 125 | bool has_error = CheckHasError(AudioUnitInitialize(io_unit_), "Initialize IO unit"); 126 | while (has_error) { 127 | // RTCLogError(@"Failed to initialize the Voice Processing I/O unit. " 128 | // "Error=%ld.", 129 | // (long)result); 130 | // ++failed_initalize_attempts; 131 | // if (failed_initalize_attempts == kMaxNumberOfAudioUnitInitializeAttempts) { 132 | // // Max number of initialization attempts exceeded, hence abort. 133 | // RTCLogError(@"Too many initialization attempts."); 134 | // return false; 135 | // } 136 | // RTCLog(@"Pause 100ms and try audio unit initialization again..."); 137 | [NSThread sleepForTimeInterval:0.1f]; 138 | has_error = CheckHasError(AudioUnitInitialize(io_unit_), "Initialize IO unit"); 139 | } 140 | 141 | return true; 142 | } 143 | 144 | bool AudioUnitRecorderAndPlayerComplex::StartAudioUnit() { 145 | OSStatus result = AudioOutputUnitStart(io_unit_); 146 | if (result != noErr) { 147 | NSLog(@"======error AudioOutputUnitStart(), %@", @(result)); 148 | return false; 149 | } 150 | return true; 151 | 152 | } 153 | 154 | void AudioUnitRecorderAndPlayerComplex::StopAudioUnit() { 155 | CheckHasError(AudioOutputUnitStop(io_unit_), "stop io unit"); 156 | CheckHasError(AudioUnitUninitialize(io_unit_), "deinit io unit"); 157 | CheckHasError(AudioComponentInstanceDispose(io_unit_), "dispose io unit"); 158 | 159 | } 160 | 161 | 162 | OSStatus AudioUnitRecorderAndPlayerComplex::OnIOUnitRenderNotify(void * inRefCon, 163 | AudioUnitRenderActionFlags * ioActionFlags, 164 | const AudioTimeStamp * inTimeStamp, 165 | UInt32 inBusNumber, 166 | UInt32 inNumberFrames, 167 | AudioBufferList * ioData) { 168 | AudioUnitRecorderAndPlayerComplex *rec_and_player = static_cast(inRefCon); 169 | AudioUnitRenderActionFlags flags = *ioActionFlags; 170 | if (flags & kAudioUnitRenderAction_PostRenderError) { 171 | OSStatus ret = noErr; 172 | UInt32 size = sizeof(OSStatus); 173 | OSStatus status = AudioUnitGetProperty(rec_and_player->io_unit_, kAudioUnitProperty_LastRenderError, kAudioUnitScope_Global, kOutputBus, &ret, &size); 174 | NSLog(@"======PostRenderError, get status: %@, last render error ret: %@", @(status), @(ret)); 175 | } 176 | return noErr; 177 | } 178 | 179 | OSStatus AudioUnitRecorderAndPlayerComplex::OnAskingForMoreDataForPlayingRenderCallback(void * inRefCon, 180 | AudioUnitRenderActionFlags * ioActionFlags, 181 | const AudioTimeStamp * inTimeStamp, 182 | UInt32 inBusNumber, 183 | UInt32 inNumberFrames, 184 | AudioBufferList * ioData) { 185 | AudioUnitRecorderAndPlayerComplex *rec_and_player = static_cast(inRefCon); 186 | // for (UInt32 i = 0; i< ioData->mNumberBuffers; ++i) { 187 | // memcpy(ioData->mBuffers[i].mData, 188 | // rec_and_player->buffer_.mBuffers[i].mData, 189 | // rec_and_player->buffer_.mBuffers[i].mDataByteSize); 190 | // } 191 | if (rec_and_player->ask_for_audio_buffer_callback_) { 192 | rec_and_player->ask_for_audio_buffer_callback_(*ioData); 193 | } 194 | return noErr; 195 | } 196 | 197 | OSStatus AudioUnitRecorderAndPlayerComplex::OnRecordedDataIsAvailable(void * inRefCon, 198 | AudioUnitRenderActionFlags * ioActionFlags, 199 | const AudioTimeStamp * inTimeStamp, 200 | UInt32 inBusNumber, 201 | UInt32 inNumberFrames, 202 | AudioBufferList * ioData) { 203 | AudioUnitRecorderAndPlayerComplex *rec_and_player = static_cast(inRefCon); 204 | OSStatus status = AudioUnitRender(rec_and_player->io_unit_, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &rec_and_player->buffer_); 205 | if (status != noErr) { 206 | NSLog(@"======AudioUnitRender error: %@", @(status)); 207 | } else { 208 | if (rec_and_player->recorded_audio_buffer_callback_) { 209 | rec_and_player->recorded_audio_buffer_callback_(rec_and_player->buffer_); 210 | } 211 | } 212 | 213 | return status; 214 | } 215 | } 216 | 217 | -------------------------------------------------------------------------------- /AudioUnitSamples/Common/CommonUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // CommonUtils.h 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/26. 6 | // 7 | 8 | #import 9 | #include 10 | #include 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | // A VP I/O unit's bus 1 connects to input hardware (microphone). 15 | static const AudioUnitElement kInputBus = 1; 16 | // A VP I/O unit's bus 0 connects to output hardware (speaker). 17 | static const AudioUnitElement kOutputBus = 0; 18 | 19 | static const int kRTCAudioSessionPreferredNumberOfChannels = 1; 20 | static const int kBytesPerSample = 2; 21 | 22 | static const int kPreferredSampleRate = 48000; 23 | static const NSTimeInterval kPreferredIOBufferDuration = 0.005; // 5 ms 24 | 25 | /// return true if found error 26 | inline bool CheckHasError(OSStatus status, const std::string& operation) { 27 | if (status == noErr) { 28 | return false; 29 | } 30 | std::cout << "======error occurred @ " << operation << ", error code: " << status << std::endl; 31 | return true; 32 | } 33 | 34 | inline OSStatus CheckErrorStatus(OSStatus status, const std::string& operation) { 35 | if (status != noErr) { 36 | std::cout << "======error occurred @ " << operation << ", error code: " << status << std::endl; 37 | } 38 | return status; 39 | } 40 | 41 | void MixSInt16AudioSamples(SInt16 *dst, SInt16 *src, 42 | float dst_factor, float src_factor, 43 | size_t size_in_bytes); 44 | 45 | bool MixAudioBufferList(AudioBufferList& dst, const AudioBufferList& src, 46 | float dst_factor, float src_factor); 47 | 48 | void InitAudioBufferList(AudioBufferList* audio_buffer); 49 | 50 | void DisposeAudioBufferList(AudioBufferList* audio_buffer); 51 | 52 | bool CopyAudioBufferListDatas(AudioBufferList& dst, const AudioBufferList& src); 53 | 54 | @interface CommonUtils : NSObject 55 | 56 | + (AudioStreamBasicDescription)commonRecorderAudioFormat; 57 | 58 | + (BOOL)setupAudioSessionForRecordAndPlay; 59 | 60 | + (BOOL)setupAudioSessionForCategory:(AVAudioSessionCategory)category; 61 | 62 | @end 63 | 64 | NS_ASSUME_NONNULL_END 65 | -------------------------------------------------------------------------------- /AudioUnitSamples/Common/CommonUtils.mm: -------------------------------------------------------------------------------- 1 | // 2 | // CommonUtils.m 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/26. 6 | // 7 | 8 | #import "CommonUtils.h" 9 | 10 | void MixSInt16AudioSamples(SInt16 *dst, SInt16 *src, 11 | float dst_factor, float src_factor, 12 | size_t size_in_bytes) { 13 | for (size_t i = 0; i < size_in_bytes / sizeof(SInt16); i++) { 14 | *(dst + i) = static_cast(*(dst + i) * dst_factor) + 15 | static_cast(*(src + i) * src_factor); 16 | } 17 | } 18 | 19 | bool MixAudioBufferList(AudioBufferList& dst, const AudioBufferList& src, 20 | float dst_factor, float src_factor) { 21 | if (dst.mNumberBuffers != src.mNumberBuffers || 22 | (dst.mBuffers[0].mDataByteSize != src.mBuffers[0].mDataByteSize)) { 23 | return false; 24 | } 25 | SInt16 *_dst = static_cast(dst.mBuffers[0].mData); 26 | SInt16 *_src = static_cast(src.mBuffers[0].mData); 27 | MixSInt16AudioSamples(_dst, _src, dst_factor, src_factor, src.mBuffers[0].mDataByteSize); 28 | return true; 29 | } 30 | 31 | void InitAudioBufferList(AudioBufferList* audio_buffer) { 32 | UInt32 sizeInBytes = 512; 33 | audio_buffer->mNumberBuffers = 1; 34 | audio_buffer->mBuffers[0].mNumberChannels = [CommonUtils commonRecorderAudioFormat].mChannelsPerFrame; 35 | audio_buffer->mBuffers[0].mDataByteSize = sizeInBytes; 36 | audio_buffer->mBuffers[0].mData = malloc(sizeInBytes); 37 | memset(audio_buffer->mBuffers[0].mData, 0, sizeInBytes); 38 | } 39 | 40 | void DisposeAudioBufferList(AudioBufferList* audio_buffer) { 41 | for (size_t i = 0; i < audio_buffer->mNumberBuffers; i++) { 42 | if (audio_buffer->mBuffers[0].mData) { 43 | free(audio_buffer->mBuffers[0].mData); 44 | } 45 | } 46 | } 47 | 48 | bool CopyAudioBufferListDatas(AudioBufferList& dst, const AudioBufferList& src) { 49 | if (dst.mNumberBuffers != src.mNumberBuffers) { 50 | return false; 51 | } 52 | 53 | for (size_t i = 0; i < dst.mNumberBuffers; i++) { 54 | if (dst.mBuffers[i].mDataByteSize >= src.mBuffers[0].mDataByteSize) { 55 | memcpy(dst.mBuffers[i].mData, src.mBuffers[0].mData, src.mBuffers[0].mDataByteSize); 56 | } else { 57 | return false; 58 | } 59 | } 60 | 61 | return true; 62 | } 63 | 64 | @implementation CommonUtils 65 | 66 | + (AudioStreamBasicDescription)commonRecorderAudioFormat { 67 | AudioStreamBasicDescription format; 68 | format.mSampleRate = kPreferredSampleRate; 69 | format.mFormatID = kAudioFormatLinearPCM; 70 | format.mFormatFlags = 71 | kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; 72 | format.mBytesPerPacket = kBytesPerSample; 73 | format.mFramesPerPacket = 1; // uncompressed. 74 | format.mBytesPerFrame = kBytesPerSample; 75 | format.mChannelsPerFrame = kRTCAudioSessionPreferredNumberOfChannels; 76 | format.mBitsPerChannel = 8 * kBytesPerSample; 77 | return format; 78 | } 79 | 80 | + (BOOL)setupAudioSessionForRecordAndPlay { 81 | return [self setupAudioSessionForCategory:AVAudioSessionCategoryPlayAndRecord]; 82 | } 83 | 84 | + (BOOL)setupAudioSessionForCategory:(AVAudioSessionCategory)category { 85 | NSError *error = nullptr; 86 | [[AVAudioSession sharedInstance] setCategory:category error:&error]; 87 | if (error) { 88 | NSLog(@"======setCategory error:%@", error); 89 | return NO; 90 | } 91 | [[AVAudioSession sharedInstance] setPreferredIOBufferDuration:kPreferredIOBufferDuration error:&error]; 92 | if (error) { 93 | NSLog(@"======setPreferredIOBufferDuration error:%@", error); 94 | return NO; 95 | } 96 | 97 | NSLog(@"======acutally io buffer duration: %@", @([AVAudioSession sharedInstance].IOBufferDuration)); 98 | 99 | [[AVAudioSession sharedInstance] setPreferredSampleRate:kPreferredSampleRate error:&error]; 100 | if (error) { 101 | NSLog(@"======DEBUG %s %@ %@", __FUNCTION__, @"setPreferredSampleRate error", [error localizedDescription]); 102 | return NO; 103 | } 104 | 105 | // activate the audio session 106 | [[AVAudioSession sharedInstance] setActive:YES error:&error]; 107 | if (error) { 108 | NSLog(@"======DEBUG %s %@ %@", __FUNCTION__, @"setActive error", [error localizedDescription]); 109 | return NO; 110 | } 111 | 112 | return YES; 113 | } 114 | 115 | 116 | @end 117 | -------------------------------------------------------------------------------- /AudioUnitSamples/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | NSMicrophoneUsageDescription 25 | 开启麦克风可以随心所欲地进行直播 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /AudioUnitSamples/SampleListViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SampleListViewController.h 3 | // AudioUnitSamples 4 | // 5 | // Created by joey on 2022/1/25. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface SampleListViewController : UIViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /AudioUnitSamples/SampleListViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SampleListViewController.m 3 | // AudioUnitSamples 4 | // 5 | // Created by joey on 2022/1/25. 6 | // 7 | 8 | #import "SampleListViewController.h" 9 | #import "BasicRecordAndPlaySampleViewController.h" 10 | 11 | static NSString const * const kSampleListTableViewCellID = @"SampleListTableViewCell"; 12 | 13 | @interface SampleListViewController () 14 | 15 | @property (weak, nonatomic) IBOutlet UITableView *samplesTableView; 16 | @property (strong, nonatomic) NSArray* clazzes; 17 | 18 | @end 19 | 20 | @implementation SampleListViewController 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | self.clazzes = @[ 25 | NSClassFromString(@"AudioUnitPlayerViewController"), 26 | NSClassFromString(@"AudioUnitRecordOnlyViewController"), 27 | NSClassFromString(@"BasicRecordAndPlaySampleViewController"), 28 | NSClassFromString(@"ComplexRecordAndPlaySampleViewController"), 29 | NSClassFromString(@"AUGraphRecordAndPlaySampleViewController"), 30 | ]; 31 | [self.samplesTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:(NSString *)kSampleListTableViewCellID]; 32 | self.samplesTableView.delegate = self; 33 | self.samplesTableView.dataSource = self; 34 | [self.samplesTableView reloadData]; 35 | } 36 | 37 | - (IBAction)didTapbasicRecordAndPlay:(id)sender { 38 | [self.navigationController pushViewController:[BasicRecordAndPlaySampleViewController new] animated:YES]; 39 | 40 | } 41 | 42 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 43 | return self.clazzes.count; 44 | } 45 | 46 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 47 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:(NSString *)kSampleListTableViewCellID]; 48 | cell.textLabel.text = NSStringFromClass(self.clazzes[indexPath.row]); 49 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 50 | return cell; 51 | } 52 | 53 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 54 | UIViewController *sampleViewController = [self.clazzes[indexPath.row] new]; 55 | [self.navigationController pushViewController:sampleViewController animated:YES]; 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /AudioUnitSamples/SampleListViewController.xib: -------------------------------------------------------------------------------- 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 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /AudioUnitSamples/SamplesViewControllers/AUGraphRecordAndPlaySampleViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AUGraphRecordAndPlaySampleViewController.h 3 | // AudioUnitSamples 4 | // 5 | // Created by joey on 2022/1/25. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface AUGraphRecordAndPlaySampleViewController : UIViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /AudioUnitSamples/SamplesViewControllers/AUGraphRecordAndPlaySampleViewController.mm: -------------------------------------------------------------------------------- 1 | // 2 | // AUGraphRecordAndPlaySampleViewController.m 3 | // AudioUnitSamples 4 | // 5 | // Created by joey on 2022/1/25. 6 | // 7 | 8 | #import "AUGraphRecordAndPlaySampleViewController.h" 9 | #import 10 | #import 11 | #import 12 | #import "CommonUtils.h" 13 | #import "AudioUnitRecorderAndPlayerComplex.h" 14 | #import "AudioFileReader.h" 15 | #import "AudioFileWriter.h" 16 | #include 17 | #import "AUGraphRecorderPlayer.h" 18 | 19 | @interface AUGraphRecordAndPlaySampleViewController () { 20 | std::unique_ptr augraph_rec_player_; 21 | std::unique_ptr file_reader_; 22 | std::unique_ptr file_writer_; 23 | 24 | AudioBufferList vocal_audio_buffer_; // recorded pure vocal buffer 25 | AudioBufferList music_audio_buffer_; // music buffer read from local file 26 | 27 | AudioBufferList iounit_output_mixed_audio_buffer_; // music buffer + vocal buffer (if ear monitor is enabled) 28 | AudioBufferList export_mixed_audio_buffer_; // music + vocal 29 | } 30 | @property (nonatomic, strong) NSURL *musicFileURL; 31 | @property (nonatomic, strong) NSURL *exportFileURL; 32 | @property (nonatomic, assign) BOOL monitorEnabled; 33 | 34 | @end 35 | 36 | @implementation AUGraphRecordAndPlaySampleViewController 37 | 38 | - (void)dealloc { 39 | DisposeAudioBufferList(&vocal_audio_buffer_); 40 | DisposeAudioBufferList(&music_audio_buffer_); 41 | DisposeAudioBufferList(&iounit_output_mixed_audio_buffer_); 42 | DisposeAudioBufferList(&export_mixed_audio_buffer_); 43 | } 44 | 45 | - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { 46 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 47 | if (self) { 48 | InitAudioBufferList(&vocal_audio_buffer_); 49 | InitAudioBufferList(&music_audio_buffer_); 50 | InitAudioBufferList(&iounit_output_mixed_audio_buffer_); 51 | InitAudioBufferList(&export_mixed_audio_buffer_); 52 | _monitorEnabled = YES; 53 | } 54 | return self; 55 | } 56 | 57 | - (void)viewDidLoad { 58 | [super viewDidLoad]; 59 | 60 | NSURL *documentFolderPath = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject]; 61 | self.musicFileURL = [documentFolderPath URLByAppendingPathComponent:@"recorded_audio_num.wav"]; 62 | NSLog(@"======read file path: %@", self.musicFileURL.absoluteString); 63 | 64 | self.exportFileURL = [documentFolderPath URLByAppendingPathComponent:@"recorded_export_augraph.wav"]; 65 | } 66 | 67 | - (IBAction)didTapStartRecordButton:(id)sender { 68 | [CommonUtils setupAudioSessionForRecordAndPlay]; 69 | 70 | file_reader_ = std::make_unique( 71 | (__bridge CFURLRef)self.musicFileURL, 72 | [CommonUtils commonRecorderAudioFormat]); 73 | file_reader_->OpenFile(); 74 | 75 | file_writer_ = std::make_unique((__bridge CFURLRef)self.exportFileURL, 76 | [CommonUtils commonRecorderAudioFormat]); 77 | file_writer_->CreateFile(); 78 | 79 | augraph_rec_player_ = std::make_unique([CommonUtils commonRecorderAudioFormat], 80 | (__bridge CFURLRef)self.musicFileURL, 81 | (__bridge CFURLRef)self.exportFileURL); 82 | // audio_unit_rec_player_->SetOnRecordAudioBufferCallback([=](const AudioBufferList& audio_buffer) { 83 | // CopyAudioBufferListDatas(vocal_audio_buffer_, audio_buffer); 84 | // bool eof = false; 85 | // file_reader_->ReadAudioFrame(music_audio_buffer_.mBuffers[0].mDataByteSize, 86 | // music_audio_buffer_.mBuffers[0].mData, 87 | // eof); 88 | // CopyAudioBufferListDatas(iounit_output_mixed_audio_buffer_, music_audio_buffer_); 89 | // if (self.monitorEnabled) { 90 | // MixAudioBufferList(iounit_output_mixed_audio_buffer_, vocal_audio_buffer_, 0.5, 0.5); 91 | // } 92 | // }); 93 | 94 | // audio_unit_rec_player_->SetOnAskForAudioBufferCallback([=](AudioBufferList& audio_buffer) { 95 | // CopyAudioBufferListDatas(audio_buffer, iounit_output_mixed_audio_buffer_); 96 | // CopyAudioBufferListDatas(export_mixed_audio_buffer_, vocal_audio_buffer_); 97 | // MixAudioBufferList(export_mixed_audio_buffer_, music_audio_buffer_, 0.5, 0.5); 98 | // file_writer_->WriteAudioPacket(export_mixed_audio_buffer_.mBuffers[0].mData, 99 | // export_mixed_audio_buffer_.mBuffers[0].mDataByteSize); 100 | // }); 101 | // 102 | // audio_unit_rec_player_->SetUpAudioUnit(); 103 | // audio_unit_rec_player_->StartAudioUnit(); 104 | augraph_rec_player_->InitializeGraph(); 105 | augraph_rec_player_->Start(); 106 | } 107 | 108 | - (IBAction)didTapStopRecordButton:(id)sender { 109 | augraph_rec_player_->Stop(); 110 | file_reader_->CloseFile(); 111 | file_writer_->CloseFile(); 112 | } 113 | - (IBAction)monitorStateChanged:(UISwitch *)sender { 114 | self.monitorEnabled = sender.isOn; 115 | } 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /AudioUnitSamples/SamplesViewControllers/AUGraphRecordAndPlaySampleViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 30 | 38 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 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 | -------------------------------------------------------------------------------- /AudioUnitSamples/SamplesViewControllers/AudioUnitPlayerViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AudioUnitPlayerViewController.h 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/26. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface AudioUnitPlayerViewController : UIViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /AudioUnitSamples/SamplesViewControllers/AudioUnitPlayerViewController.mm: -------------------------------------------------------------------------------- 1 | // 2 | // AudioUnitPlayerViewController.m 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/26. 6 | // 7 | 8 | #import "AudioUnitPlayerViewController.h" 9 | #import "AudioFileReader.h" 10 | #import "AudioUnitPlayer.h" 11 | #import "CommonUtils.h" 12 | #include 13 | 14 | @interface AudioUnitPlayerViewController () { 15 | std::unique_ptr file_reader_; 16 | std::unique_ptr file_reader_english_; 17 | 18 | std::unique_ptr audio_unit_player_; 19 | AudioBufferList buffer_list_; 20 | } 21 | 22 | @property (nonatomic, strong) NSURL *audioFileURL; 23 | @property (nonatomic, strong) NSURL *audioEnglishFileURL; 24 | 25 | @end 26 | 27 | @implementation AudioUnitPlayerViewController 28 | 29 | - (void)dealloc { 30 | free(buffer_list_.mBuffers[0].mData); 31 | } 32 | 33 | - (void)viewDidLoad { 34 | [super viewDidLoad]; 35 | NSURL *documentFolderPath = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject]; 36 | self.audioFileURL = [documentFolderPath URLByAppendingPathComponent:@"recorded_audio_num.wav"]; 37 | NSLog(@"======read file path: %@", self.audioFileURL.absoluteString); 38 | 39 | self.audioEnglishFileURL = [documentFolderPath URLByAppendingPathComponent:@"recorded_audio_num_english.wav"]; 40 | 41 | [CommonUtils setupAudioSessionForCategory:AVAudioSessionCategoryPlayAndRecord]; 42 | 43 | buffer_list_.mNumberBuffers = 1; 44 | buffer_list_.mBuffers[0].mNumberChannels = [CommonUtils commonRecorderAudioFormat].mChannelsPerFrame; 45 | buffer_list_.mBuffers[0].mDataByteSize = 512; 46 | buffer_list_.mBuffers[0].mData = malloc(buffer_list_.mBuffers[0].mDataByteSize); 47 | 48 | // CFURLRef url = (__bridge CFURLRef)self.filePath; 49 | // AudioStreamBasicDescription format = [CommonUtils commonRecorderAudioFormat]; 50 | // wrapper_ = std::make_unique(format, url); 51 | // wrapper_->SetUpAudioUnit(); 52 | // 53 | // file_writer_ = std::make_unique(url, format); 54 | // file_writer_->CreateFile(); 55 | // 56 | // wrapper_->SetOnRecordAudioBufferCallback([=](const AudioBufferList& audio_buffer) { 57 | // file_writer_->WriteAudioPacket(audio_buffer.mBuffers[0].mData, audio_buffer.mBuffers[0].mDataByteSize); 58 | // }); 59 | // wrapper_->StartAudioUnit(); 60 | 61 | } 62 | 63 | - (IBAction)didTapStartPlayingButton:(id)sender { 64 | file_reader_ = std::make_unique( 65 | (__bridge CFURLRef)self.audioFileURL, 66 | [CommonUtils commonRecorderAudioFormat]); 67 | file_reader_->OpenFile(); 68 | 69 | file_reader_english_ = std::make_unique( 70 | (__bridge CFURLRef)self.audioEnglishFileURL, 71 | [CommonUtils commonRecorderAudioFormat]); 72 | file_reader_english_->OpenFile(); 73 | 74 | audio_unit_player_ = std::make_unique([CommonUtils commonRecorderAudioFormat]); 75 | audio_unit_player_->SetOnRecordAudioBufferCallback([=](void* data, size_t size, bool& eof) { 76 | self->file_reader_->ReadAudioFrame(size, data, eof); 77 | self->file_reader_english_->ReadAudioFrame(size, buffer_list_.mBuffers[0].mData, eof); 78 | SInt16 *data1 = static_cast(data); 79 | SInt16 *data2 = static_cast(buffer_list_.mBuffers[0].mData); 80 | MixSInt16AudioSamples(data1, data2, 0.5, 0.5, size); 81 | }); 82 | 83 | audio_unit_player_->SetUpAudioUnit(); 84 | audio_unit_player_->StartAudioUnit(); 85 | } 86 | 87 | - (IBAction)didTapStopPlayingButton:(id)sender { 88 | file_reader_->CloseFile(); 89 | file_reader_english_->CloseFile(); 90 | audio_unit_player_->StopAudioUnit(); 91 | } 92 | 93 | - (IBAction)didTapMuteButton:(id)sender { 94 | } 95 | 96 | /* 97 | #pragma mark - Navigation 98 | 99 | // In a storyboard-based application, you will often want to do a little preparation before navigation 100 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 101 | // Get the new view controller using [segue destinationViewController]. 102 | // Pass the selected object to the new view controller. 103 | } 104 | */ 105 | 106 | @end 107 | -------------------------------------------------------------------------------- /AudioUnitSamples/SamplesViewControllers/AudioUnitPlayerViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 30 | 38 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /AudioUnitSamples/SamplesViewControllers/AudioUnitRecordOnlyViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AudioUnitRecordOnlyViewController.h 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/26. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | 13 | /// 使用 AudioUnit 进行录制, 录制完成之后保存到文件中 14 | /// 使用 InputCallback 驱动, 调用 AudioUnitRender 获取到采集到的音频数据, 15 | /// 然后把这些数据保存到文件中 16 | @interface AudioUnitRecordOnlyViewController : UIViewController 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /AudioUnitSamples/SamplesViewControllers/AudioUnitRecordOnlyViewController.mm: -------------------------------------------------------------------------------- 1 | // 2 | // AudioUnitRecordOnlyViewController.m 3 | // AudioUnitSamples 4 | // 5 | // Created by Joey on 2022/1/26. 6 | // 7 | 8 | #import "AudioUnitRecordOnlyViewController.h" 9 | #import 10 | #include 11 | #include 12 | #include 13 | #import "CommonUtils.h" 14 | #import "AudioFileWriter.h" 15 | #import "AudioUnitRecorder.h" 16 | 17 | #pragma mark - AudioUnitRecordOnlyViewController 18 | @interface AudioUnitRecordOnlyViewController () { 19 | std::unique_ptr wrapper_; 20 | std::unique_ptr file_writer_; 21 | } 22 | 23 | @property (nonatomic, strong) NSURL *filePath; 24 | 25 | @end 26 | 27 | @implementation AudioUnitRecordOnlyViewController 28 | 29 | - (void)viewDidLoad { 30 | [super viewDidLoad]; 31 | 32 | NSURL *documentFolderPath = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject]; 33 | self.filePath = [documentFolderPath URLByAppendingPathComponent:@"recorded_audio.wav"]; 34 | NSLog(@"======file path: %@", self.filePath.absoluteString); 35 | 36 | [CommonUtils setupAudioSessionForRecordAndPlay]; 37 | } 38 | 39 | - (IBAction)didTapRecordButton:(id)sender { 40 | CFURLRef url = (__bridge CFURLRef)self.filePath; 41 | AudioStreamBasicDescription format = [CommonUtils commonRecorderAudioFormat]; 42 | wrapper_ = std::make_unique(format, url); 43 | wrapper_->SetUpAudioUnit(); 44 | 45 | file_writer_ = std::make_unique(url, format); 46 | file_writer_->CreateFile(); 47 | 48 | wrapper_->SetOnRecordAudioBufferCallback([=](const AudioBufferList& audio_buffer) { 49 | file_writer_->WriteAudioPacket(audio_buffer.mBuffers[0].mData, audio_buffer.mBuffers[0].mDataByteSize); 50 | }); 51 | wrapper_->StartAudioUnit(); 52 | } 53 | 54 | - (IBAction)didTapStopRecordingButton:(id)sender { 55 | wrapper_->StopAudioUnit(); 56 | file_writer_->CloseFile(); 57 | } 58 | 59 | @end 60 | 61 | 62 | -------------------------------------------------------------------------------- /AudioUnitSamples/SamplesViewControllers/AudioUnitRecordOnlyViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 30 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /AudioUnitSamples/SamplesViewControllers/BasicRecordAndPlaySampleViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BasicRecordAndPlaySampleViewController.h 3 | // AudioUnitSamples 4 | // 5 | // Created by joey on 2022/1/25. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface BasicRecordAndPlaySampleViewController : UIViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /AudioUnitSamples/SamplesViewControllers/BasicRecordAndPlaySampleViewController.mm: -------------------------------------------------------------------------------- 1 | // 2 | // BasicRecordAndPlaySampleViewController.m 3 | // AudioUnitSamples 4 | // 5 | // Created by joey on 2022/1/25. 6 | // 7 | 8 | #import "BasicRecordAndPlaySampleViewController.h" 9 | #import 10 | #import 11 | #import 12 | #import "CommonUtils.h" 13 | #import "AudioUnitRecorderAndPlayer.h" 14 | #include 15 | 16 | @interface BasicRecordAndPlaySampleViewController () { 17 | std::unique_ptr audio_unit_rec_player_; 18 | } 19 | 20 | @end 21 | 22 | @implementation BasicRecordAndPlaySampleViewController 23 | 24 | - (void)viewDidLoad { 25 | [super viewDidLoad]; 26 | } 27 | 28 | - (IBAction)didTapStartRecordButton:(id)sender { 29 | [CommonUtils setupAudioSessionForRecordAndPlay]; 30 | 31 | audio_unit_rec_player_ = std::make_unique([CommonUtils commonRecorderAudioFormat]); 32 | audio_unit_rec_player_->SetUpAudioUnit(); 33 | audio_unit_rec_player_->StartAudioUnit(); 34 | } 35 | 36 | - (IBAction)didTapStopRecordButton:(id)sender { 37 | audio_unit_rec_player_->StopAudioUnit(); 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /AudioUnitSamples/SamplesViewControllers/BasicRecordAndPlaySampleViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 30 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /AudioUnitSamples/SamplesViewControllers/ComplexRecordAndPlaySampleViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ComplexRecordAndPlaySampleViewController.h 3 | // AudioUnitSamples 4 | // 5 | // Created by joey on 2022/1/25. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface ComplexRecordAndPlaySampleViewController : UIViewController 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /AudioUnitSamples/SamplesViewControllers/ComplexRecordAndPlaySampleViewController.mm: -------------------------------------------------------------------------------- 1 | // 2 | // ComplexRecordAndPlaySampleViewController.m 3 | // AudioUnitSamples 4 | // 5 | // Created by joey on 2022/1/25. 6 | // 7 | 8 | #import "ComplexRecordAndPlaySampleViewController.h" 9 | #import 10 | #import 11 | #import 12 | #import "CommonUtils.h" 13 | #import "AudioUnitRecorderAndPlayerComplex.h" 14 | #import "AudioFileReader.h" 15 | #import "AudioFileWriter.h" 16 | #include 17 | 18 | @interface ComplexRecordAndPlaySampleViewController () { 19 | std::unique_ptr audio_unit_rec_player_; 20 | std::unique_ptr file_reader_; 21 | std::unique_ptr file_writer_; 22 | 23 | AudioBufferList vocal_audio_buffer_; // recorded pure vocal buffer 24 | AudioBufferList music_audio_buffer_; // music buffer read from local file 25 | 26 | AudioBufferList iounit_output_mixed_audio_buffer_; // music buffer + vocal buffer (if ear monitor is enabled) 27 | AudioBufferList export_mixed_audio_buffer_; // music + vocal 28 | } 29 | @property (nonatomic, strong) NSURL *musicFileURL; 30 | @property (nonatomic, strong) NSURL *exportFileURL; 31 | @property (nonatomic, assign) BOOL monitorEnabled; 32 | 33 | @end 34 | 35 | @implementation ComplexRecordAndPlaySampleViewController 36 | 37 | - (void)dealloc { 38 | DisposeAudioBufferList(&vocal_audio_buffer_); 39 | DisposeAudioBufferList(&music_audio_buffer_); 40 | DisposeAudioBufferList(&iounit_output_mixed_audio_buffer_); 41 | DisposeAudioBufferList(&export_mixed_audio_buffer_); 42 | } 43 | 44 | - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { 45 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 46 | if (self) { 47 | InitAudioBufferList(&vocal_audio_buffer_); 48 | InitAudioBufferList(&music_audio_buffer_); 49 | InitAudioBufferList(&iounit_output_mixed_audio_buffer_); 50 | InitAudioBufferList(&export_mixed_audio_buffer_); 51 | _monitorEnabled = YES; 52 | } 53 | return self; 54 | } 55 | 56 | - (void)viewDidLoad { 57 | [super viewDidLoad]; 58 | 59 | NSURL *documentFolderPath = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject]; 60 | self.musicFileURL = [documentFolderPath URLByAppendingPathComponent:@"recorded_audio_num.wav"]; 61 | NSLog(@"======read file path: %@", self.musicFileURL.absoluteString); 62 | 63 | self.exportFileURL = [documentFolderPath URLByAppendingPathComponent:@"recorded_export.wav"]; 64 | } 65 | 66 | - (IBAction)didTapStartRecordButton:(id)sender { 67 | [CommonUtils setupAudioSessionForRecordAndPlay]; 68 | 69 | file_reader_ = std::make_unique( 70 | (__bridge CFURLRef)self.musicFileURL, 71 | [CommonUtils commonRecorderAudioFormat]); 72 | file_reader_->OpenFile(); 73 | 74 | file_writer_ = std::make_unique((__bridge CFURLRef)self.exportFileURL, 75 | [CommonUtils commonRecorderAudioFormat]); 76 | file_writer_->CreateFile(); 77 | 78 | audio_unit_rec_player_ = std::make_unique([CommonUtils commonRecorderAudioFormat]); 79 | audio_unit_rec_player_->SetOnRecordAudioBufferCallback([=](const AudioBufferList& audio_buffer) { 80 | CopyAudioBufferListDatas(vocal_audio_buffer_, audio_buffer); 81 | bool eof = false; 82 | file_reader_->ReadAudioFrame(music_audio_buffer_.mBuffers[0].mDataByteSize, 83 | music_audio_buffer_.mBuffers[0].mData, 84 | eof); 85 | CopyAudioBufferListDatas(iounit_output_mixed_audio_buffer_, music_audio_buffer_); 86 | if (self.monitorEnabled) { 87 | MixAudioBufferList(iounit_output_mixed_audio_buffer_, vocal_audio_buffer_, 0.5, 0.5); 88 | } 89 | }); 90 | 91 | audio_unit_rec_player_->SetOnAskForAudioBufferCallback([=](AudioBufferList& audio_buffer) { 92 | CopyAudioBufferListDatas(audio_buffer, iounit_output_mixed_audio_buffer_); 93 | CopyAudioBufferListDatas(export_mixed_audio_buffer_, vocal_audio_buffer_); 94 | MixAudioBufferList(export_mixed_audio_buffer_, music_audio_buffer_, 0.5, 0.5); 95 | file_writer_->WriteAudioPacket(export_mixed_audio_buffer_.mBuffers[0].mData, 96 | export_mixed_audio_buffer_.mBuffers[0].mDataByteSize); 97 | }); 98 | 99 | audio_unit_rec_player_->SetUpAudioUnit(); 100 | audio_unit_rec_player_->StartAudioUnit(); 101 | } 102 | 103 | - (IBAction)didTapStopRecordButton:(id)sender { 104 | audio_unit_rec_player_->StopAudioUnit(); 105 | file_reader_->CloseFile(); 106 | file_writer_->CloseFile(); 107 | } 108 | - (IBAction)monitorStateChanged:(UISwitch *)sender { 109 | self.monitorEnabled = sender.isOn; 110 | } 111 | 112 | @end 113 | -------------------------------------------------------------------------------- /AudioUnitSamples/SamplesViewControllers/ComplexRecordAndPlaySampleViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 30 | 38 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 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 | -------------------------------------------------------------------------------- /AudioUnitSamples/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // AudioUnitSamples 4 | // 5 | // Created by joey on 2022/1/25. 6 | // 7 | 8 | #import 9 | 10 | @interface ViewController : UIViewController 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /AudioUnitSamples/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // AudioUnitSamples 4 | // 5 | // Created by joey on 2022/1/25. 6 | // 7 | 8 | #import "ViewController.h" 9 | 10 | @interface ViewController () 11 | 12 | @end 13 | 14 | @implementation ViewController 15 | 16 | - (void)viewDidLoad { 17 | [super viewDidLoad]; 18 | // Do any additional setup after loading the view. 19 | } 20 | 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /AudioUnitSamples/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // AudioUnitSamples 4 | // 5 | // Created by joey on 2022/1/25. 6 | // 7 | 8 | #import 9 | #import "AppDelegate.h" 10 | 11 | int main(int argc, char * argv[]) { 12 | NSString * appDelegateClassName; 13 | @autoreleasepool { 14 | // Setup code that might create autoreleased objects goes here. 15 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 16 | } 17 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 18 | } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Joey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AudioUnitSamples 2 | AudioUnit samples 3 | --------------------------------------------------------------------------------