├── AudioPlotDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── sunyazhou.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── sunyazhou.xcuserdatad │ └── xcschemes │ ├── AudioPlotDemo.xcscheme │ └── xcschememanagement.plist ├── AudioPlotDemo.xcworkspace ├── contents.xcworkspacedata └── xcuserdata │ └── sunyazhou.xcuserdatad │ └── UserInterfaceState.xcuserstate ├── AudioPlotDemo ├── 17yeasold.mp3 ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── ViewController.h ├── ViewController.m └── main.m ├── KSYAudioPlotView.podspec ├── KSYAudioPlotView ├── KSYAudioDisplayLink.h ├── KSYAudioDisplayLink.m ├── KSYAudioFile.h ├── KSYAudioFile.m ├── KSYAudioPlot.h ├── KSYAudioPlotView.h ├── KSYAudioPlotView.m ├── KSYAudioUtilities.h ├── KSYAudioUtilities.m ├── KSYPlotCircularBuffer.c └── KSYPlotCircularBuffer.h ├── Podfile ├── Podfile.lock ├── Pods ├── Manifest.lock ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── sunyazhou.xcuserdatad │ │ └── xcschemes │ │ ├── Pods-AudioPlotDemo.xcscheme │ │ └── xcschememanagement.plist └── Target Support Files │ └── Pods-AudioPlotDemo │ ├── Pods-AudioPlotDemo-acknowledgements.markdown │ ├── Pods-AudioPlotDemo-acknowledgements.plist │ ├── Pods-AudioPlotDemo-dummy.m │ ├── Pods-AudioPlotDemo-frameworks.sh │ ├── Pods-AudioPlotDemo-resources.sh │ ├── Pods-AudioPlotDemo.debug.xcconfig │ └── Pods-AudioPlotDemo.release.xcconfig ├── README.md └── ksyAudioPlotView.gif /AudioPlotDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3F8269011EF4C4DD0087BB4C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F8269001EF4C4DD0087BB4C /* main.m */; }; 11 | 3F8269041EF4C4DD0087BB4C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F8269031EF4C4DD0087BB4C /* AppDelegate.m */; }; 12 | 3F8269071EF4C4DD0087BB4C /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F8269061EF4C4DD0087BB4C /* ViewController.m */; }; 13 | 3F82690A1EF4C4DD0087BB4C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3F8269081EF4C4DD0087BB4C /* Main.storyboard */; }; 14 | 3F82690C1EF4C4DD0087BB4C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3F82690B1EF4C4DD0087BB4C /* Assets.xcassets */; }; 15 | 3F82690F1EF4C4DD0087BB4C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3F82690D1EF4C4DD0087BB4C /* LaunchScreen.storyboard */; }; 16 | 3F8269171EF4C6990087BB4C /* 17yeasold.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 3F8269161EF4C6990087BB4C /* 17yeasold.mp3 */; }; 17 | 3F8269191EF4C7810087BB4C /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F8269181EF4C7810087BB4C /* Accelerate.framework */; }; 18 | 3F82691B1EF4C7870087BB4C /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F82691A1EF4C7870087BB4C /* AVFoundation.framework */; }; 19 | 3F82691D1EF4C78D0087BB4C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F82691C1EF4C78D0087BB4C /* Foundation.framework */; }; 20 | 3F82691F1EF4C7940087BB4C /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F82691E1EF4C7940087BB4C /* QuartzCore.framework */; }; 21 | 3F8269211EF4C7990087BB4C /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F8269201EF4C7990087BB4C /* CoreGraphics.framework */; }; 22 | 3F8269231EF4C7A20087BB4C /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F8269221EF4C7A20087BB4C /* AudioToolbox.framework */; }; 23 | 3F8269251EF4C7AB0087BB4C /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3F8269241EF4C7AB0087BB4C /* OpenGLES.framework */; }; 24 | 3F82695A1EF4D4CF0087BB4C /* KSYAudioDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F8269501EF4D4CF0087BB4C /* KSYAudioDisplayLink.m */; }; 25 | 3F82695B1EF4D4CF0087BB4C /* KSYAudioFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F8269521EF4D4CF0087BB4C /* KSYAudioFile.m */; }; 26 | 3F82695C1EF4D4CF0087BB4C /* KSYAudioPlotView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F8269551EF4D4CF0087BB4C /* KSYAudioPlotView.m */; }; 27 | 3F82695D1EF4D4CF0087BB4C /* KSYAudioUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F8269571EF4D4CF0087BB4C /* KSYAudioUtilities.m */; }; 28 | 3F82695E1EF4D4CF0087BB4C /* KSYPlotCircularBuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 3F8269581EF4D4CF0087BB4C /* KSYPlotCircularBuffer.c */; }; 29 | E81681CB1A48A37AD436641D /* libPods-AudioPlotDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6C39A49AD86ED9F9E766AE41 /* libPods-AudioPlotDemo.a */; }; 30 | /* End PBXBuildFile section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 3F8268FC1EF4C4DD0087BB4C /* AudioPlotDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AudioPlotDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 3F8269001EF4C4DD0087BB4C /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 35 | 3F8269021EF4C4DD0087BB4C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 36 | 3F8269031EF4C4DD0087BB4C /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 37 | 3F8269051EF4C4DD0087BB4C /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 38 | 3F8269061EF4C4DD0087BB4C /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 39 | 3F8269091EF4C4DD0087BB4C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 40 | 3F82690B1EF4C4DD0087BB4C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 41 | 3F82690E1EF4C4DD0087BB4C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 42 | 3F8269101EF4C4DD0087BB4C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 43 | 3F8269161EF4C6990087BB4C /* 17yeasold.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = 17yeasold.mp3; sourceTree = ""; }; 44 | 3F8269181EF4C7810087BB4C /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; 45 | 3F82691A1EF4C7870087BB4C /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; 46 | 3F82691C1EF4C78D0087BB4C /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 47 | 3F82691E1EF4C7940087BB4C /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 48 | 3F8269201EF4C7990087BB4C /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 49 | 3F8269221EF4C7A20087BB4C /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; 50 | 3F8269241EF4C7AB0087BB4C /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; 51 | 3F82694F1EF4D4CF0087BB4C /* KSYAudioDisplayLink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSYAudioDisplayLink.h; sourceTree = ""; }; 52 | 3F8269501EF4D4CF0087BB4C /* KSYAudioDisplayLink.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSYAudioDisplayLink.m; sourceTree = ""; }; 53 | 3F8269511EF4D4CF0087BB4C /* KSYAudioFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSYAudioFile.h; sourceTree = ""; }; 54 | 3F8269521EF4D4CF0087BB4C /* KSYAudioFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSYAudioFile.m; sourceTree = ""; }; 55 | 3F8269531EF4D4CF0087BB4C /* KSYAudioPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSYAudioPlot.h; sourceTree = ""; }; 56 | 3F8269541EF4D4CF0087BB4C /* KSYAudioPlotView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSYAudioPlotView.h; sourceTree = ""; }; 57 | 3F8269551EF4D4CF0087BB4C /* KSYAudioPlotView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSYAudioPlotView.m; sourceTree = ""; }; 58 | 3F8269561EF4D4CF0087BB4C /* KSYAudioUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSYAudioUtilities.h; sourceTree = ""; }; 59 | 3F8269571EF4D4CF0087BB4C /* KSYAudioUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KSYAudioUtilities.m; sourceTree = ""; }; 60 | 3F8269581EF4D4CF0087BB4C /* KSYPlotCircularBuffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = KSYPlotCircularBuffer.c; sourceTree = ""; }; 61 | 3F8269591EF4D4CF0087BB4C /* KSYPlotCircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KSYPlotCircularBuffer.h; sourceTree = ""; }; 62 | 5FCC3353F40B3149E265F3C3 /* Pods-AudioPlotDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AudioPlotDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AudioPlotDemo/Pods-AudioPlotDemo.debug.xcconfig"; sourceTree = ""; }; 63 | 6C39A49AD86ED9F9E766AE41 /* libPods-AudioPlotDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AudioPlotDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | BC787CAC2D2AB63FCD86C48B /* Pods-AudioPlotDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AudioPlotDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-AudioPlotDemo/Pods-AudioPlotDemo.release.xcconfig"; sourceTree = ""; }; 65 | /* End PBXFileReference section */ 66 | 67 | /* Begin PBXFrameworksBuildPhase section */ 68 | 3F8268F91EF4C4DD0087BB4C /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 2147483647; 71 | files = ( 72 | 3F8269251EF4C7AB0087BB4C /* OpenGLES.framework in Frameworks */, 73 | 3F8269231EF4C7A20087BB4C /* AudioToolbox.framework in Frameworks */, 74 | 3F8269211EF4C7990087BB4C /* CoreGraphics.framework in Frameworks */, 75 | 3F82691F1EF4C7940087BB4C /* QuartzCore.framework in Frameworks */, 76 | 3F82691D1EF4C78D0087BB4C /* Foundation.framework in Frameworks */, 77 | 3F82691B1EF4C7870087BB4C /* AVFoundation.framework in Frameworks */, 78 | 3F8269191EF4C7810087BB4C /* Accelerate.framework in Frameworks */, 79 | E81681CB1A48A37AD436641D /* libPods-AudioPlotDemo.a in Frameworks */, 80 | ); 81 | runOnlyForDeploymentPostprocessing = 0; 82 | }; 83 | /* End PBXFrameworksBuildPhase section */ 84 | 85 | /* Begin PBXGroup section */ 86 | 3F8268F31EF4C4DD0087BB4C = { 87 | isa = PBXGroup; 88 | children = ( 89 | 3F8268FE1EF4C4DD0087BB4C /* AudioPlotDemo */, 90 | 3F8268FD1EF4C4DD0087BB4C /* Products */, 91 | 58327A6DF9D6703E1FC8850E /* Frameworks */, 92 | 9CED0C92F73AE97FD4A18733 /* Pods */, 93 | ); 94 | sourceTree = ""; 95 | }; 96 | 3F8268FD1EF4C4DD0087BB4C /* Products */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 3F8268FC1EF4C4DD0087BB4C /* AudioPlotDemo.app */, 100 | ); 101 | name = Products; 102 | sourceTree = ""; 103 | }; 104 | 3F8268FE1EF4C4DD0087BB4C /* AudioPlotDemo */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 3F82694E1EF4D4CF0087BB4C /* KSYAudioPlotView */, 108 | 3F8269161EF4C6990087BB4C /* 17yeasold.mp3 */, 109 | 3F8269021EF4C4DD0087BB4C /* AppDelegate.h */, 110 | 3F8269031EF4C4DD0087BB4C /* AppDelegate.m */, 111 | 3F8269051EF4C4DD0087BB4C /* ViewController.h */, 112 | 3F8269061EF4C4DD0087BB4C /* ViewController.m */, 113 | 3F8269081EF4C4DD0087BB4C /* Main.storyboard */, 114 | 3F82690B1EF4C4DD0087BB4C /* Assets.xcassets */, 115 | 3F82690D1EF4C4DD0087BB4C /* LaunchScreen.storyboard */, 116 | 3F8269101EF4C4DD0087BB4C /* Info.plist */, 117 | 3F8268FF1EF4C4DD0087BB4C /* Supporting Files */, 118 | ); 119 | path = AudioPlotDemo; 120 | sourceTree = ""; 121 | }; 122 | 3F8268FF1EF4C4DD0087BB4C /* Supporting Files */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 3F8269001EF4C4DD0087BB4C /* main.m */, 126 | ); 127 | name = "Supporting Files"; 128 | sourceTree = ""; 129 | }; 130 | 3F82694E1EF4D4CF0087BB4C /* KSYAudioPlotView */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 3F82694F1EF4D4CF0087BB4C /* KSYAudioDisplayLink.h */, 134 | 3F8269501EF4D4CF0087BB4C /* KSYAudioDisplayLink.m */, 135 | 3F8269511EF4D4CF0087BB4C /* KSYAudioFile.h */, 136 | 3F8269521EF4D4CF0087BB4C /* KSYAudioFile.m */, 137 | 3F8269531EF4D4CF0087BB4C /* KSYAudioPlot.h */, 138 | 3F8269541EF4D4CF0087BB4C /* KSYAudioPlotView.h */, 139 | 3F8269551EF4D4CF0087BB4C /* KSYAudioPlotView.m */, 140 | 3F8269561EF4D4CF0087BB4C /* KSYAudioUtilities.h */, 141 | 3F8269571EF4D4CF0087BB4C /* KSYAudioUtilities.m */, 142 | 3F8269581EF4D4CF0087BB4C /* KSYPlotCircularBuffer.c */, 143 | 3F8269591EF4D4CF0087BB4C /* KSYPlotCircularBuffer.h */, 144 | ); 145 | path = KSYAudioPlotView; 146 | sourceTree = SOURCE_ROOT; 147 | }; 148 | 58327A6DF9D6703E1FC8850E /* Frameworks */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | 3F8269241EF4C7AB0087BB4C /* OpenGLES.framework */, 152 | 3F8269221EF4C7A20087BB4C /* AudioToolbox.framework */, 153 | 3F8269201EF4C7990087BB4C /* CoreGraphics.framework */, 154 | 3F82691E1EF4C7940087BB4C /* QuartzCore.framework */, 155 | 3F82691C1EF4C78D0087BB4C /* Foundation.framework */, 156 | 3F82691A1EF4C7870087BB4C /* AVFoundation.framework */, 157 | 3F8269181EF4C7810087BB4C /* Accelerate.framework */, 158 | 6C39A49AD86ED9F9E766AE41 /* libPods-AudioPlotDemo.a */, 159 | ); 160 | name = Frameworks; 161 | sourceTree = ""; 162 | }; 163 | 9CED0C92F73AE97FD4A18733 /* Pods */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | 5FCC3353F40B3149E265F3C3 /* Pods-AudioPlotDemo.debug.xcconfig */, 167 | BC787CAC2D2AB63FCD86C48B /* Pods-AudioPlotDemo.release.xcconfig */, 168 | ); 169 | name = Pods; 170 | sourceTree = ""; 171 | }; 172 | /* End PBXGroup section */ 173 | 174 | /* Begin PBXNativeTarget section */ 175 | 3F8268FB1EF4C4DD0087BB4C /* AudioPlotDemo */ = { 176 | isa = PBXNativeTarget; 177 | buildConfigurationList = 3F8269131EF4C4DD0087BB4C /* Build configuration list for PBXNativeTarget "AudioPlotDemo" */; 178 | buildPhases = ( 179 | 7CB164B0DCAFB92A09AEC675 /* [CP] Check Pods Manifest.lock */, 180 | 3F8268F81EF4C4DD0087BB4C /* Sources */, 181 | 3F8268F91EF4C4DD0087BB4C /* Frameworks */, 182 | 3F8268FA1EF4C4DD0087BB4C /* Resources */, 183 | 8B55A8DD2660D1C38CD00E87 /* [CP] Embed Pods Frameworks */, 184 | 4C7FFA6516DACEB0EB35BF83 /* [CP] Copy Pods Resources */, 185 | ); 186 | buildRules = ( 187 | ); 188 | dependencies = ( 189 | ); 190 | name = AudioPlotDemo; 191 | productName = AudioPlotDemo; 192 | productReference = 3F8268FC1EF4C4DD0087BB4C /* AudioPlotDemo.app */; 193 | productType = "com.apple.product-type.application"; 194 | }; 195 | /* End PBXNativeTarget section */ 196 | 197 | /* Begin PBXProject section */ 198 | 3F8268F41EF4C4DD0087BB4C /* Project object */ = { 199 | isa = PBXProject; 200 | attributes = { 201 | LastUpgradeCheck = 0830; 202 | ORGANIZATIONNAME = Ksyun; 203 | TargetAttributes = { 204 | 3F8268FB1EF4C4DD0087BB4C = { 205 | CreatedOnToolsVersion = 8.3.3; 206 | DevelopmentTeam = ZGJ54Q7R45; 207 | ProvisioningStyle = Manual; 208 | }; 209 | }; 210 | }; 211 | buildConfigurationList = 3F8268F71EF4C4DD0087BB4C /* Build configuration list for PBXProject "AudioPlotDemo" */; 212 | compatibilityVersion = "Xcode 3.2"; 213 | developmentRegion = English; 214 | hasScannedForEncodings = 0; 215 | knownRegions = ( 216 | en, 217 | Base, 218 | ); 219 | mainGroup = 3F8268F31EF4C4DD0087BB4C; 220 | productRefGroup = 3F8268FD1EF4C4DD0087BB4C /* Products */; 221 | projectDirPath = ""; 222 | projectRoot = ""; 223 | targets = ( 224 | 3F8268FB1EF4C4DD0087BB4C /* AudioPlotDemo */, 225 | ); 226 | }; 227 | /* End PBXProject section */ 228 | 229 | /* Begin PBXResourcesBuildPhase section */ 230 | 3F8268FA1EF4C4DD0087BB4C /* Resources */ = { 231 | isa = PBXResourcesBuildPhase; 232 | buildActionMask = 2147483647; 233 | files = ( 234 | 3F8269171EF4C6990087BB4C /* 17yeasold.mp3 in Resources */, 235 | 3F82690F1EF4C4DD0087BB4C /* LaunchScreen.storyboard in Resources */, 236 | 3F82690C1EF4C4DD0087BB4C /* Assets.xcassets in Resources */, 237 | 3F82690A1EF4C4DD0087BB4C /* Main.storyboard in Resources */, 238 | ); 239 | runOnlyForDeploymentPostprocessing = 0; 240 | }; 241 | /* End PBXResourcesBuildPhase section */ 242 | 243 | /* Begin PBXShellScriptBuildPhase section */ 244 | 4C7FFA6516DACEB0EB35BF83 /* [CP] Copy Pods Resources */ = { 245 | isa = PBXShellScriptBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | ); 249 | inputPaths = ( 250 | ); 251 | name = "[CP] Copy Pods Resources"; 252 | outputPaths = ( 253 | ); 254 | runOnlyForDeploymentPostprocessing = 0; 255 | shellPath = /bin/sh; 256 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AudioPlotDemo/Pods-AudioPlotDemo-resources.sh\"\n"; 257 | showEnvVarsInLog = 0; 258 | }; 259 | 7CB164B0DCAFB92A09AEC675 /* [CP] Check Pods Manifest.lock */ = { 260 | isa = PBXShellScriptBuildPhase; 261 | buildActionMask = 2147483647; 262 | files = ( 263 | ); 264 | inputPaths = ( 265 | ); 266 | name = "[CP] Check Pods Manifest.lock"; 267 | outputPaths = ( 268 | ); 269 | runOnlyForDeploymentPostprocessing = 0; 270 | shellPath = /bin/sh; 271 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; 272 | showEnvVarsInLog = 0; 273 | }; 274 | 8B55A8DD2660D1C38CD00E87 /* [CP] Embed Pods Frameworks */ = { 275 | isa = PBXShellScriptBuildPhase; 276 | buildActionMask = 2147483647; 277 | files = ( 278 | ); 279 | inputPaths = ( 280 | ); 281 | name = "[CP] Embed Pods Frameworks"; 282 | outputPaths = ( 283 | ); 284 | runOnlyForDeploymentPostprocessing = 0; 285 | shellPath = /bin/sh; 286 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AudioPlotDemo/Pods-AudioPlotDemo-frameworks.sh\"\n"; 287 | showEnvVarsInLog = 0; 288 | }; 289 | /* End PBXShellScriptBuildPhase section */ 290 | 291 | /* Begin PBXSourcesBuildPhase section */ 292 | 3F8268F81EF4C4DD0087BB4C /* Sources */ = { 293 | isa = PBXSourcesBuildPhase; 294 | buildActionMask = 2147483647; 295 | files = ( 296 | 3F82695C1EF4D4CF0087BB4C /* KSYAudioPlotView.m in Sources */, 297 | 3F8269071EF4C4DD0087BB4C /* ViewController.m in Sources */, 298 | 3F82695A1EF4D4CF0087BB4C /* KSYAudioDisplayLink.m in Sources */, 299 | 3F82695B1EF4D4CF0087BB4C /* KSYAudioFile.m in Sources */, 300 | 3F8269041EF4C4DD0087BB4C /* AppDelegate.m in Sources */, 301 | 3F82695E1EF4D4CF0087BB4C /* KSYPlotCircularBuffer.c in Sources */, 302 | 3F8269011EF4C4DD0087BB4C /* main.m in Sources */, 303 | 3F82695D1EF4D4CF0087BB4C /* KSYAudioUtilities.m in Sources */, 304 | ); 305 | runOnlyForDeploymentPostprocessing = 0; 306 | }; 307 | /* End PBXSourcesBuildPhase section */ 308 | 309 | /* Begin PBXVariantGroup section */ 310 | 3F8269081EF4C4DD0087BB4C /* Main.storyboard */ = { 311 | isa = PBXVariantGroup; 312 | children = ( 313 | 3F8269091EF4C4DD0087BB4C /* Base */, 314 | ); 315 | name = Main.storyboard; 316 | sourceTree = ""; 317 | }; 318 | 3F82690D1EF4C4DD0087BB4C /* LaunchScreen.storyboard */ = { 319 | isa = PBXVariantGroup; 320 | children = ( 321 | 3F82690E1EF4C4DD0087BB4C /* Base */, 322 | ); 323 | name = LaunchScreen.storyboard; 324 | sourceTree = ""; 325 | }; 326 | /* End PBXVariantGroup section */ 327 | 328 | /* Begin XCBuildConfiguration section */ 329 | 3F8269111EF4C4DD0087BB4C /* Debug */ = { 330 | isa = XCBuildConfiguration; 331 | buildSettings = { 332 | ALWAYS_SEARCH_USER_PATHS = NO; 333 | CLANG_ANALYZER_NONNULL = YES; 334 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 335 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 336 | CLANG_CXX_LIBRARY = "libc++"; 337 | CLANG_ENABLE_MODULES = YES; 338 | CLANG_ENABLE_OBJC_ARC = YES; 339 | CLANG_WARN_BOOL_CONVERSION = YES; 340 | CLANG_WARN_CONSTANT_CONVERSION = YES; 341 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 342 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 343 | CLANG_WARN_EMPTY_BODY = YES; 344 | CLANG_WARN_ENUM_CONVERSION = YES; 345 | CLANG_WARN_INFINITE_RECURSION = YES; 346 | CLANG_WARN_INT_CONVERSION = YES; 347 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 348 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 349 | CLANG_WARN_UNREACHABLE_CODE = YES; 350 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 351 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 352 | COPY_PHASE_STRIP = NO; 353 | DEBUG_INFORMATION_FORMAT = dwarf; 354 | ENABLE_STRICT_OBJC_MSGSEND = YES; 355 | ENABLE_TESTABILITY = YES; 356 | GCC_C_LANGUAGE_STANDARD = gnu99; 357 | GCC_DYNAMIC_NO_PIC = NO; 358 | GCC_NO_COMMON_BLOCKS = YES; 359 | GCC_OPTIMIZATION_LEVEL = 0; 360 | GCC_PREPROCESSOR_DEFINITIONS = ( 361 | "DEBUG=1", 362 | "$(inherited)", 363 | ); 364 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 365 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 366 | GCC_WARN_UNDECLARED_SELECTOR = YES; 367 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 368 | GCC_WARN_UNUSED_FUNCTION = YES; 369 | GCC_WARN_UNUSED_VARIABLE = YES; 370 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 371 | MTL_ENABLE_DEBUG_INFO = YES; 372 | ONLY_ACTIVE_ARCH = YES; 373 | SDKROOT = iphoneos; 374 | }; 375 | name = Debug; 376 | }; 377 | 3F8269121EF4C4DD0087BB4C /* Release */ = { 378 | isa = XCBuildConfiguration; 379 | buildSettings = { 380 | ALWAYS_SEARCH_USER_PATHS = NO; 381 | CLANG_ANALYZER_NONNULL = YES; 382 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 383 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 384 | CLANG_CXX_LIBRARY = "libc++"; 385 | CLANG_ENABLE_MODULES = YES; 386 | CLANG_ENABLE_OBJC_ARC = YES; 387 | CLANG_WARN_BOOL_CONVERSION = YES; 388 | CLANG_WARN_CONSTANT_CONVERSION = 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_OBJC_ROOT_CLASS = YES_ERROR; 396 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 397 | CLANG_WARN_UNREACHABLE_CODE = YES; 398 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 399 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 400 | COPY_PHASE_STRIP = NO; 401 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 402 | ENABLE_NS_ASSERTIONS = NO; 403 | ENABLE_STRICT_OBJC_MSGSEND = YES; 404 | GCC_C_LANGUAGE_STANDARD = gnu99; 405 | GCC_NO_COMMON_BLOCKS = YES; 406 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 407 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 408 | GCC_WARN_UNDECLARED_SELECTOR = YES; 409 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 410 | GCC_WARN_UNUSED_FUNCTION = YES; 411 | GCC_WARN_UNUSED_VARIABLE = YES; 412 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 413 | MTL_ENABLE_DEBUG_INFO = NO; 414 | SDKROOT = iphoneos; 415 | VALIDATE_PRODUCT = YES; 416 | }; 417 | name = Release; 418 | }; 419 | 3F8269141EF4C4DD0087BB4C /* Debug */ = { 420 | isa = XCBuildConfiguration; 421 | baseConfigurationReference = 5FCC3353F40B3149E265F3C3 /* Pods-AudioPlotDemo.debug.xcconfig */; 422 | buildSettings = { 423 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 424 | DEVELOPMENT_TEAM = ZGJ54Q7R45; 425 | INFOPLIST_FILE = AudioPlotDemo/Info.plist; 426 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 427 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 428 | ONLY_ACTIVE_ARCH = YES; 429 | PRODUCT_BUNDLE_IDENTIFIER = com.kingsoft.AudioPlotDemo; 430 | PRODUCT_NAME = "$(TARGET_NAME)"; 431 | PROVISIONING_PROFILE = "ab59afe7-c387-485a-a9bc-8ed2b2516693"; 432 | PROVISIONING_PROFILE_SPECIFIER = ksc_wildcard_dev_sunyazhou20170522110817; 433 | }; 434 | name = Debug; 435 | }; 436 | 3F8269151EF4C4DD0087BB4C /* Release */ = { 437 | isa = XCBuildConfiguration; 438 | baseConfigurationReference = BC787CAC2D2AB63FCD86C48B /* Pods-AudioPlotDemo.release.xcconfig */; 439 | buildSettings = { 440 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 441 | DEVELOPMENT_TEAM = ZGJ54Q7R45; 442 | INFOPLIST_FILE = AudioPlotDemo/Info.plist; 443 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 444 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 445 | ONLY_ACTIVE_ARCH = YES; 446 | PRODUCT_BUNDLE_IDENTIFIER = com.kingsoft.AudioPlotDemo; 447 | PRODUCT_NAME = "$(TARGET_NAME)"; 448 | PROVISIONING_PROFILE = "ab59afe7-c387-485a-a9bc-8ed2b2516693"; 449 | PROVISIONING_PROFILE_SPECIFIER = ksc_wildcard_dev_sunyazhou20170522110817; 450 | }; 451 | name = Release; 452 | }; 453 | /* End XCBuildConfiguration section */ 454 | 455 | /* Begin XCConfigurationList section */ 456 | 3F8268F71EF4C4DD0087BB4C /* Build configuration list for PBXProject "AudioPlotDemo" */ = { 457 | isa = XCConfigurationList; 458 | buildConfigurations = ( 459 | 3F8269111EF4C4DD0087BB4C /* Debug */, 460 | 3F8269121EF4C4DD0087BB4C /* Release */, 461 | ); 462 | defaultConfigurationIsVisible = 0; 463 | defaultConfigurationName = Release; 464 | }; 465 | 3F8269131EF4C4DD0087BB4C /* Build configuration list for PBXNativeTarget "AudioPlotDemo" */ = { 466 | isa = XCConfigurationList; 467 | buildConfigurations = ( 468 | 3F8269141EF4C4DD0087BB4C /* Debug */, 469 | 3F8269151EF4C4DD0087BB4C /* Release */, 470 | ); 471 | defaultConfigurationIsVisible = 0; 472 | defaultConfigurationName = Release; 473 | }; 474 | /* End XCConfigurationList section */ 475 | }; 476 | rootObject = 3F8268F41EF4C4DD0087BB4C /* Project object */; 477 | } 478 | -------------------------------------------------------------------------------- /AudioPlotDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /AudioPlotDemo.xcodeproj/project.xcworkspace/xcuserdata/sunyazhou.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/AudioPlotView/e3ab33850c54be1373d234b5d4c1001ca7cf6db9/AudioPlotDemo.xcodeproj/project.xcworkspace/xcuserdata/sunyazhou.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /AudioPlotDemo.xcodeproj/xcuserdata/sunyazhou.xcuserdatad/xcschemes/AudioPlotDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /AudioPlotDemo.xcodeproj/xcuserdata/sunyazhou.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | AudioPlotDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 3F8268FB1EF4C4DD0087BB4C 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /AudioPlotDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /AudioPlotDemo.xcworkspace/xcuserdata/sunyazhou.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/AudioPlotView/e3ab33850c54be1373d234b5d4c1001ca7cf6db9/AudioPlotDemo.xcworkspace/xcuserdata/sunyazhou.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /AudioPlotDemo/17yeasold.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/AudioPlotView/e3ab33850c54be1373d234b5d4c1001ca7cf6db9/AudioPlotDemo/17yeasold.mp3 -------------------------------------------------------------------------------- /AudioPlotDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // AudioPlotDemo 4 | // 5 | // Created by sunyazhou on 2017/6/17. 6 | // Copyright © 2017年 Ksyun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /AudioPlotDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // AudioPlotDemo 4 | // 5 | // Created by sunyazhou on 2017/6/17. 6 | // Copyright © 2017年 Ksyun. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /AudioPlotDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /AudioPlotDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /AudioPlotDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /AudioPlotDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /AudioPlotDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // AudioPlotDemo 4 | // 5 | // Created by sunyazhou on 2017/6/17. 6 | // Copyright © 2017年 Ksyun. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /AudioPlotDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // AudioPlotDemo 4 | // 5 | // Created by sunyazhou on 2017/6/17. 6 | // Copyright © 2017年 Ksyun. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | #import "KSYAudioPlotView.h" 12 | #import "KSYAudioFile.h" 13 | 14 | #define kAudioFileDefault [[NSBundle mainBundle] pathForResource:@"17yeasold" ofType:@"mp3"] 15 | 16 | @interface ViewController () 17 | @property (weak, nonatomic) IBOutlet KSYAudioPlotView *audioPlot; 18 | // 19 | // An EZAudioFile that will be used to load the audio file at the file path specified 20 | // 21 | @property (nonatomic, strong) KSYAudioFile *audioFile; 22 | 23 | //------------------------------------------------------------------------------ 24 | 25 | // 26 | // An EZAudioPlayer that will be used for playback 27 | // 28 | //@property (nonatomic, strong) EZAudioPlayer *player; 29 | @end 30 | 31 | @implementation ViewController 32 | 33 | - (void)viewDidLoad { 34 | [super viewDidLoad]; 35 | 36 | // 37 | // Customizing the audio plot's look 38 | // 39 | 40 | // 41 | // Background color 42 | // 43 | self.audioPlot.backgroundColor = [UIColor colorWithRed: 0.169 green: 0.643 blue: 0.675 alpha: 1]; 44 | 45 | // 46 | // Waveform color 47 | // 48 | self.audioPlot.color = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0]; 49 | 50 | // 51 | // Plot type 52 | // 53 | self.audioPlot.plotType = KSYPlotTypeBuffer; 54 | 55 | // 56 | // Fill 57 | // 58 | self.audioPlot.shouldFill = YES; 59 | 60 | // 61 | // Mirror 62 | // 63 | self.audioPlot.shouldMirror = YES; 64 | 65 | // 66 | // No need to optimze for realtime 67 | // 68 | self.audioPlot.shouldOptimizeForRealtimePlot = NO; 69 | 70 | // 71 | // Customize the layer with a shadow for fun 72 | // 73 | self.audioPlot.waveformLayer.shadowOffset = CGSizeMake(0.0, 1.0); 74 | self.audioPlot.waveformLayer.shadowRadius = 0.0; 75 | self.audioPlot.waveformLayer.shadowColor = [UIColor colorWithRed: 0.069 green: 0.543 blue: 0.575 alpha: 1].CGColor; 76 | self.audioPlot.waveformLayer.shadowOpacity = 5.0; 77 | self.audioPlot.waveformLayer.lineWidth = 3; 78 | 79 | // 80 | // Load in the sample file 81 | // 82 | [self openFileWithFilePathURL:[NSURL fileURLWithPath:kAudioFileDefault]]; 83 | 84 | 85 | } 86 | 87 | #pragma mark - Action Extensions 88 | //------------------------------------------------------------------------------ 89 | 90 | - (void)openFileWithFilePathURL:(NSURL*)filePathURL 91 | { 92 | self.audioFile = [KSYAudioFile audioFileWithURL:filePathURL]; 93 | 94 | 95 | // 96 | // Plot the whole waveform 97 | // 98 | self.audioPlot.plotType = KSYPlotTypeBuffer; 99 | self.audioPlot.shouldFill = YES; 100 | self.audioPlot.shouldMirror = YES; 101 | 102 | // 103 | // Get the audio data from the audio file 104 | // 105 | __weak typeof (self) weakSelf = self; 106 | [self.audioFile getWaveformDataWithCompletionBlock:^(float **waveformData, 107 | int length) 108 | { 109 | [weakSelf.audioPlot updateBuffer:waveformData[0] 110 | withBufferSize:length]; 111 | }]; 112 | } 113 | 114 | - (void)didReceiveMemoryWarning { 115 | [super didReceiveMemoryWarning]; 116 | // Dispose of any resources that can be recreated. 117 | } 118 | 119 | 120 | @end 121 | -------------------------------------------------------------------------------- /AudioPlotDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // AudioPlotDemo 4 | // 5 | // Created by sunyazhou on 2017/6/17. 6 | // Copyright © 2017年 Ksyun. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /KSYAudioPlotView.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint KSYAudioPlotView.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = "KSYAudioPlotView" 11 | s.version = "1.0.3" 12 | s.summary = "Audio Waveform view" 13 | s.description = <<-DESC 14 | 金山云音频视波图UI组件 15 | DESC 16 | s.homepage = "https://github.com/ksvc/AudioPlotView" 17 | s.license = {:type => 'Proprietary', :text => <<-LICENSE 18 | Copyright 2017 kingsoft Ltd. All rights reserved. 19 | LICENSE 20 | } 21 | 22 | s.author = { "zengfanping" => "zengfanping@kingsoft.com" } 23 | s.ios.deployment_target = '8.0' 24 | # s.osx.deployment_target = '10.8' 25 | s.source = { :git => "https://github.com/ksvc/AudioPlotView.git", 26 | :tag => 'v'+s.version.to_s } 27 | s.ios.frameworks = 'AudioToolbox','AVFoundation','GLKit', 'Accelerate' 28 | # s.osx.frameworks = 'AudioToolbox','AudioUnit','CoreAudio','QuartzCore','OpenGL','GLKit', 'Accelerate' 29 | s.requires_arc = true; 30 | s.source_files = 'KSYAudioPlotView/*.{h,m,c}' 31 | end 32 | -------------------------------------------------------------------------------- /KSYAudioPlotView/KSYAudioDisplayLink.h: -------------------------------------------------------------------------------- 1 | // 2 | // KSYAudioDisplayLink.h 3 | // EditDemo 4 | // 5 | // Created by sunyazhou on 2017/6/15. 6 | // Copyright © 2017年 Kingsoft, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @class KSYAudioDisplayLink; 13 | 14 | //------------------------------------------------------------------------------ 15 | #pragma mark - KSYAudioDisplayLinkDelegate 16 | //------------------------------------------------------------------------------ 17 | 18 | /** 19 | The KSYAudioDisplayLinkDelegate provides a means for an KSYAudioDisplayLink instance to notify a receiver when it should redraw itself. 20 | */ 21 | @protocol KSYAudioDisplayLinkDelegate 22 | 23 | @required 24 | /** 25 | Required method for an KSYAudioDisplayLinkDelegate to implement. This fires at the screen's display rate (typically 60 fps). 26 | @param displayLink An KSYAudioDisplayLink instance used by a receiver to draw itself at the screen's refresh rate. 27 | */ 28 | - (void)displayLinkNeedsDisplay:(KSYAudioDisplayLink *)displayLink; 29 | 30 | @end 31 | 32 | //------------------------------------------------------------------------------ 33 | #pragma mark - KSYAudioDisplayLink 34 | //------------------------------------------------------------------------------ 35 | 36 | /** 37 | The KSYAudioDisplayLink provides a cross-platform (iOS and Mac) abstraction over the CADisplayLink for iOS and CVDisplayLink for Mac. The purpose of this class is to provide an accurate timer for views that need to redraw themselves at 60 fps. This class is used by the KSYAudioPlot and, eventually, the KSYAudioPlotGL to provide a timer mechanism to draw real-time plots. 38 | */ 39 | 40 | @interface KSYAudioDisplayLink : NSObject 41 | //------------------------------------------------------------------------------ 42 | #pragma mark - Class Methods 43 | //------------------------------------------------------------------------------ 44 | 45 | /** 46 | Class method to create an KSYAudioDisplayLink. The caller should implement the KSYAudioDisplayLinkDelegate protocol to receive the `displayLinkNeedsDisplay:` delegate method to know when to redraw itself. 47 | @param delegate An instance that implements the KSYAudioDisplayLinkDelegate protocol. 48 | @return An instance of the KSYAudioDisplayLink. 49 | */ 50 | + (instancetype)displayLinkWithDelegate:(id)delegate; 51 | 52 | //------------------------------------------------------------------------------ 53 | #pragma mark - Properties 54 | //------------------------------------------------------------------------------ 55 | 56 | /** 57 | The KSYAudioDisplayLinkDelegate for which to receive the redraw calls. 58 | */ 59 | @property (nonatomic, weak) id delegate; 60 | 61 | //------------------------------------------------------------------------------ 62 | #pragma mark - Instance Methods 63 | //------------------------------------------------------------------------------ 64 | 65 | /** 66 | Method to start the display link and provide the `displayLinkNeedsDisplay:` calls to the `delegate` 67 | */ 68 | - (void)start; 69 | 70 | /** 71 | Method to stop the display link from providing the `displayLinkNeedsDisplay:` calls to the `delegate` 72 | */ 73 | - (void)stop; 74 | 75 | //------------------------------------------------------------------------------ 76 | 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /KSYAudioPlotView/KSYAudioDisplayLink.m: -------------------------------------------------------------------------------- 1 | // 2 | // KSYAudioDisplayLink.m 3 | // EditDemo 4 | // 5 | // Created by sunyazhou on 2017/6/15. 6 | // Copyright © 2017年 Kingsoft, Inc. All rights reserved. 7 | // 8 | 9 | #import "KSYAudioDisplayLink.h" 10 | 11 | //------------------------------------------------------------------------------ 12 | #pragma mark - CVDisplayLink Callback (Declaration) 13 | //------------------------------------------------------------------------------ 14 | 15 | #if TARGET_OS_IPHONE 16 | #elif TARGET_OS_MAC 17 | static CVReturn KSYAudioDisplayLinkCallback(CVDisplayLinkRef displayLinkRef, 18 | const CVTimeStamp *now, 19 | const CVTimeStamp *outputTime, 20 | CVOptionFlags flagsIn, 21 | CVOptionFlags *flagsOut, 22 | void *displayLinkContext); 23 | #endif 24 | 25 | //------------------------------------------------------------------------------ 26 | #pragma mark - KSYAudioDisplayLink (Interface Extension) 27 | //------------------------------------------------------------------------------ 28 | 29 | @interface KSYAudioDisplayLink () 30 | #if TARGET_OS_IPHONE 31 | @property (nonatomic, strong) CADisplayLink *displayLink; 32 | #elif TARGET_OS_MAC 33 | @property (nonatomic, assign) CVDisplayLinkRef displayLink; 34 | #endif 35 | @property (nonatomic, assign) BOOL stopped; 36 | @end 37 | 38 | //------------------------------------------------------------------------------ 39 | #pragma mark - KSYAudioDisplayLink (Implementation) 40 | //------------------------------------------------------------------------------ 41 | 42 | @implementation KSYAudioDisplayLink 43 | 44 | //------------------------------------------------------------------------------ 45 | #pragma mark - Dealloc 46 | //------------------------------------------------------------------------------ 47 | 48 | - (void)dealloc 49 | { 50 | #if TARGET_OS_IPHONE 51 | [self.displayLink invalidate]; 52 | #elif TARGET_OS_MAC 53 | CVDisplayLinkStop(self.displayLink); 54 | CVDisplayLinkRelease(self.displayLink); 55 | self.displayLink = nil; 56 | #endif 57 | } 58 | 59 | //------------------------------------------------------------------------------ 60 | #pragma mark - Class Initialization 61 | //------------------------------------------------------------------------------ 62 | 63 | + (instancetype)displayLinkWithDelegate:(id)delegate 64 | { 65 | KSYAudioDisplayLink *displayLink = [[self alloc] init]; 66 | displayLink.delegate = delegate; 67 | return displayLink; 68 | } 69 | 70 | //------------------------------------------------------------------------------ 71 | #pragma mark - Initialization 72 | //------------------------------------------------------------------------------ 73 | 74 | - (instancetype) init 75 | { 76 | self = [super init]; 77 | if (self) 78 | { 79 | [self setup]; 80 | } 81 | return self; 82 | } 83 | 84 | //------------------------------------------------------------------------------ 85 | #pragma mark - Setup 86 | //------------------------------------------------------------------------------ 87 | 88 | - (void)setup 89 | { 90 | self.stopped = YES; 91 | #if TARGET_OS_IPHONE 92 | self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)]; 93 | [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 94 | #elif TARGET_OS_MAC 95 | CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink); 96 | CVDisplayLinkSetOutputCallback(self.displayLink, 97 | KSYAudioDisplayLinkCallback, 98 | (__bridge void *)(self)); 99 | CVDisplayLinkStart(self.displayLink); 100 | #endif 101 | } 102 | 103 | //------------------------------------------------------------------------------ 104 | #pragma mark - Actions 105 | //------------------------------------------------------------------------------ 106 | 107 | - (void)start 108 | { 109 | #if TARGET_OS_IPHONE 110 | self.displayLink.paused = NO; 111 | #elif TARGET_OS_MAC 112 | CVDisplayLinkStart(self.displayLink); 113 | #endif 114 | self.stopped = NO; 115 | } 116 | 117 | //------------------------------------------------------------------------------ 118 | 119 | - (void)stop 120 | { 121 | #if TARGET_OS_IPHONE 122 | self.displayLink.paused = YES; 123 | #elif TARGET_OS_MAC 124 | CVDisplayLinkStop(self.displayLink); 125 | #endif 126 | self.stopped = YES; 127 | } 128 | 129 | //------------------------------------------------------------------------------ 130 | 131 | - (void)update 132 | { 133 | if (!self.stopped) 134 | { 135 | if ([self.delegate respondsToSelector:@selector(displayLinkNeedsDisplay:)]) 136 | { 137 | [self.delegate displayLinkNeedsDisplay:self]; 138 | } 139 | } 140 | } 141 | 142 | //------------------------------------------------------------------------------ 143 | 144 | @end 145 | 146 | //------------------------------------------------------------------------------ 147 | #pragma mark - CVDisplayLink Callback (Implementation) 148 | //------------------------------------------------------------------------------ 149 | 150 | #if TARGET_OS_IPHONE 151 | #elif TARGET_OS_MAC 152 | static CVReturn KSYAudioDisplayLinkCallback(CVDisplayLinkRef displayLinkRef, 153 | const CVTimeStamp *now, 154 | const CVTimeStamp *outputTime, 155 | CVOptionFlags flagsIn, 156 | CVOptionFlags *flagsOut, 157 | void *displayLinkContext) 158 | { 159 | KSYAudioDisplayLink *displayLink = (__bridge KSYAudioDisplayLink*)displayLinkContext; 160 | [displayLink update]; 161 | return kCVReturnSuccess; 162 | } 163 | #endif 164 | -------------------------------------------------------------------------------- /KSYAudioPlotView/KSYAudioFile.h: -------------------------------------------------------------------------------- 1 | // 2 | // KSYAudioFile.h 3 | // EditDemo 4 | // 5 | // Created by sunyazhou on 2017/6/15. 6 | // Copyright © 2017年 Kingsoft, Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | //------------------------------------------------------------------------------ 13 | @class KSYAudioFile; 14 | @class KSYAudioFloatData; 15 | //------------------------------------------------------------------------------ 16 | #pragma mark - Blocks 17 | //------------------------------------------------------------------------------ 18 | /** 19 | A block used when returning back the waveform data. The waveform data itself will be an array of float arrays, one for each channel, and the length indicates the total length of each float array. 20 | @param waveformData An array of float arrays, each representing a channel of audio data from the file 21 | @param length An int representing the length of each channel of float audio data 22 | */ 23 | typedef void (^KSYAudioWaveformDataCompletionBlock)(float **waveformData, int length); 24 | 25 | //------------------------------------------------------------------------------ 26 | #pragma mark - KSYAudioFileDelegate 27 | //------------------------------------------------------------------------------ 28 | /** 29 | The KSYAudioFileDelegate provides event callbacks for the KSYAudioFile object. These type of events are triggered by reads and seeks on the file and gives feedback such as the audio data read as a float array for visualizations and the new seek position for UI updating. 30 | */ 31 | @protocol KSYAudioFileDelegate 32 | 33 | @optional 34 | /** 35 | Triggered from the KSYAudioFile function `readFrames:audioBufferList:bufferSize:eof:` to notify the delegate of the read audio data as a float array instead of a buffer list. Common use case of this would be to visualize the float data using an audio plot or audio data dependent OpenGL sketch. 36 | @param audioFile The instance of the KSYAudioFile that triggered the event. 37 | @param buffer A float array of float arrays holding the audio data. buffer[0] would be the left channel's float array while buffer[1] would be the right channel's float array in a stereo file. 38 | @param bufferSize The length of the buffers float arrays 39 | @param numberOfChannels The number of channels. 2 for stereo, 1 for mono. 40 | */ 41 | - (void) audioFile:(KSYAudioFile *)audioFile 42 | readAudio:(float **)buffer 43 | withBufferSize:(UInt32)bufferSize 44 | withNumberOfChannels:(UInt32)numberOfChannels; 45 | 46 | //------------------------------------------------------------------------------ 47 | 48 | /** 49 | Occurs when the audio file's internal seek position has been updated by the KSYAudioFile functions `readFrames:audioBufferList:bufferSize:eof:` or `audioFile:updatedPosition:`. As of 0.8.0 this is the preferred method of listening for position updates on the audio file since a user may want the pull the currentTime, formattedCurrentTime, or the frame index from the KSYAudioFile instance provided. 50 | @param audioFile The instance of the KSYAudio in which the change occured. 51 | */ 52 | - (void)audioFileUpdatedPosition:(KSYAudioFile *)audioFile; 53 | 54 | //------------------------------------------------------------------------------ 55 | 56 | /** 57 | Occurs when the audio file's internal seek position has been updated by the KSYAudioFile functions `readFrames:audioBufferList:bufferSize:eof:` or `audioFile:updatedPosition:`. 58 | @param audioFile The instance of the KSYAudio in which the change occured 59 | @param framePosition The new frame index as a 64-bit signed integer 60 | @deprecated This property is deprecated starting in version 0.8.0. 61 | @note Please use `audioFileUpdatedPosition:` property instead. 62 | */ 63 | - (void)audioFile:(KSYAudioFile *)audioFile 64 | updatedPosition:(SInt64)framePosition __attribute__((deprecated)); 65 | 66 | @end 67 | 68 | //------------------------------------------------------------------------------ 69 | #pragma mark - KSYAudioFile 70 | //------------------------------------------------------------------------------ 71 | /** 72 | The KSYAudioFile provides a lightweight and intuitive way to asynchronously interact with audio files. These interactions included reading audio data, seeking within an audio file, getting information about the file, and pulling the waveform data for visualizing the contents of the audio file. The KSYAudioFileDelegate provides event callbacks for when reads, seeks, and various updates happen within the audio file to allow the caller to interact with the action in meaningful ways. Common use cases here could be to read the audio file's data as AudioBufferList structures for output (see KSYOutput) and visualizing the audio file's data as a float array using an audio plot (see KSYAudioPlot). 73 | */ 74 | @interface KSYAudioFile : NSObject 75 | 76 | //------------------------------------------------------------------------------ 77 | #pragma mark - Properties 78 | //------------------------------------------------------------------------------ 79 | 80 | /** 81 | A KSYAudioFileDelegate for the audio file that is used to return events such as new seek positions within the file and the read audio data as a float array. 82 | */ 83 | @property (nonatomic, weak) id delegate; 84 | 85 | //------------------------------------------------------------------------------ 86 | #pragma mark - Initialization 87 | //------------------------------------------------------------------------------ 88 | /** 89 | @name Initialization 90 | */ 91 | 92 | /** 93 | Creates a new instance of the KSYAudioFile using a file path URL. 94 | @param url The file path reference of the audio file as an NSURL. 95 | @return The newly created KSYAudioFile instance. nil if the file path does not exist. 96 | */ 97 | - (instancetype)initWithURL:(NSURL *)url; 98 | 99 | /** 100 | Creates a new instance of the KSYAudioFile using a file path URL with a delegate conforming to the KSYAudioFileDelegate protocol. 101 | @param delegate The audio file delegate that receives events specified by the KSYAudioFileDelegate protocol 102 | @param url The file path reference of the audio file as an NSURL. 103 | @return The newly created KSYAudioFile instance. 104 | */ 105 | - (instancetype)initWithURL:(NSURL *)url 106 | delegate:(id)delegate; 107 | 108 | //------------------------------------------------------------------------------ 109 | 110 | /** 111 | Creates a new instance of the KSYAudioFile using a file path URL with a delegate conforming to the KSYAudioFileDelegate protocol and a client format. 112 | @param url The file path reference of the audio file as an NSURL. 113 | @param delegate The audio file delegate that receives events specified by the KSYAudioFileDelegate protocol 114 | @param clientFormat An AudioStreamBasicDescription that will be used as the client format on the audio file. For instance, the audio file might be in a 22.5 kHz sample rate format in its file format, but your app wants to read the samples at a sample rate of 44.1 kHz so it can iterate with other components (like a audio processing graph) without any weird playback effects. If this initializer is not used then a non-interleaved float format will be assumed. 115 | @return The newly created KSYAudioFile instance. 116 | */ 117 | - (instancetype)initWithURL:(NSURL *)url 118 | delegate:(id)delegate 119 | clientFormat:(AudioStreamBasicDescription)clientFormat; 120 | 121 | //------------------------------------------------------------------------------ 122 | #pragma mark - Class Initializers 123 | //------------------------------------------------------------------------------ 124 | /** 125 | @name Class Initializers 126 | */ 127 | 128 | /** 129 | Class method that creates a new instance of the KSYAudioFile using a file path URL. 130 | @param url The file path reference of the audio file as an NSURL. 131 | @return The newly created KSYAudioFile instance. 132 | */ 133 | + (instancetype)audioFileWithURL:(NSURL *)url; 134 | 135 | //------------------------------------------------------------------------------ 136 | 137 | /** 138 | Class method that creates a new instance of the KSYAudioFile using a file path URL with a delegate conforming to the KSYAudioFileDelegate protocol. 139 | @param url The file path reference of the audio file as an NSURL. 140 | @param delegate The audio file delegate that receives events specified by the KSYAudioFileDelegate protocol 141 | @return The newly created KSYAudioFile instance. 142 | */ 143 | + (instancetype)audioFileWithURL:(NSURL *)url 144 | delegate:(id)delegate; 145 | 146 | //------------------------------------------------------------------------------ 147 | 148 | /** 149 | Class method that creates a new instance of the KSYAudioFile using a file path URL with a delegate conforming to the KSYAudioFileDelegate protocol and a client format. 150 | @param url The file path reference of the audio file as an NSURL. 151 | @param delegate The audio file delegate that receives events specified by the KSYAudioFileDelegate protocol 152 | @param clientFormat An AudioStreamBasicDescription that will be used as the client format on the audio file. For instance, the audio file might be in a 22.5 kHz sample rate, interleaved MP3 file format, but your app wants to read linear PCM samples at a sample rate of 44.1 kHz so it can be read in the context of other components sharing a common stream format (like a audio processing graph). If this initializer is not used then the `defaultClientFormat` will be used as teh default value for the client format. 153 | @return The newly created KSYAudioFile instance. 154 | */ 155 | + (instancetype)audioFileWithURL:(NSURL *)url 156 | delegate:(id)delegate 157 | clientFormat:(AudioStreamBasicDescription)clientFormat; 158 | 159 | //------------------------------------------------------------------------------ 160 | #pragma mark - Class Methods 161 | //------------------------------------------------------------------------------ 162 | /** 163 | @name Class Methods 164 | */ 165 | 166 | /** 167 | A class method that subclasses can override to specify the default client format that will be used to read audio data from this file. A client format is different from the file format in that it is the format of the other components interacting with this file. For instance, the file on disk could be a 22.5 kHz, float format, but we might have an audio processing graph that has a 44.1 kHz, signed integer format that we'd like to interact with. The client format lets us set that 44.1 kHz format on the audio file to properly read samples from it with any interpolation or format conversion that must take place done automatically within the KSYAudioFile `readFrames:audioBufferList:bufferSize:eof:` method. Default is stereo, non-interleaved, 44.1 kHz. 168 | @return An AudioStreamBasicDescription that serves as the audio file's client format. 169 | */ 170 | + (AudioStreamBasicDescription)defaultClientFormat; 171 | 172 | //------------------------------------------------------------------------------ 173 | 174 | /** 175 | A class method that subclasses can override to specify the default sample rate that will be used in the `defaultClientFormat` method. Default is 44100.0 (44.1 kHz). 176 | @return A Float64 representing the sample rate that should be used in the default client format. 177 | */ 178 | + (Float64)defaultClientFormatSampleRate; 179 | 180 | //------------------------------------------------------------------------------ 181 | 182 | /** 183 | Provides an array of the supported audio files types. Each audio file type is provided as a string, i.e. @"caf". Useful for filtering lists of files in an open panel to only the types allowed. 184 | @return An array of NSString objects representing the represented file types. 185 | */ 186 | + (NSArray *)supportedAudioFileTypes; 187 | 188 | //------------------------------------------------------------------------------ 189 | #pragma mark - Events 190 | //------------------------------------------------------------------------------ 191 | /** 192 | @name Reading From The Audio File 193 | */ 194 | 195 | /** 196 | Reads a specified number of frames from the audio file. In addition, this will notify the KSYAudioFileDelegate (if specified) of the read data as a float array with the audioFile:readAudio:withBufferSize:withNumberOfChannels: event and the new seek position within the file with the audioFile:updatedPosition: event. 197 | @param frames The number of frames to read from the file. 198 | @param audioBufferList An allocated AudioBufferList structure in which to store the read audio data 199 | @param bufferSize A pointer to a UInt32 in which to store the read buffersize 200 | @param eof A pointer to a BOOL in which to store whether the read operation reached the end of the audio file. 201 | */ 202 | //- (void)readFrames:(UInt32)frames 203 | // audioBufferList:(AudioBufferList *)audioBufferList 204 | // bufferSize:(UInt32 *)bufferSize 205 | // eof:(BOOL *)eof; 206 | 207 | //------------------------------------------------------------------------------ 208 | 209 | /** 210 | @name Seeking Through The Audio File 211 | */ 212 | 213 | /** 214 | Seeks through an audio file to a specified frame. This will notify the KSYAudioFileDelegate (if specified) with the audioFile:updatedPosition: function. 215 | @param frame The new frame position to seek to as a SInt64. 216 | */ 217 | - (void)seekToFrame:(SInt64)frame; 218 | 219 | //------------------------------------------------------------------------------ 220 | #pragma mark - Getters 221 | //------------------------------------------------------------------------------ 222 | /** 223 | @name Getting Information About The Audio File 224 | */ 225 | 226 | /** 227 | Provides the common AudioStreamBasicDescription that will be used for in-app interaction. The file's format will be converted to this format and then sent back as either a float array or a `AudioBufferList` pointer. For instance, the file on disk could be a 22.5 kHz, float format, but we might have an audio processing graph that has a 44.1 kHz, signed integer format that we'd like to interact with. The client format lets us set that 44.1 kHz format on the audio file to properly read samples from it with any interpolation or format conversion that must take place done automatically within the KSYAudioFile `readFrames:audioBufferList:bufferSize:eof:` method. Default is stereo, non-interleaved, 44.1 kHz. 228 | @warning This must be a linear PCM format! 229 | @return An AudioStreamBasicDescription structure describing the format of the audio file. 230 | */ 231 | @property (readwrite) AudioStreamBasicDescription clientFormat; 232 | 233 | //------------------------------------------------------------------------------ 234 | 235 | /** 236 | Provides the current offset in the audio file as an NSTimeInterval (i.e. in seconds). When setting this it will determine the correct frame offset and perform a `seekToFrame` to the new time offset. 237 | @warning Make sure the new current time offset is less than the `duration` or you will receive an invalid seek assertion. 238 | */ 239 | @property (nonatomic, readwrite) NSTimeInterval currentTime; 240 | 241 | //------------------------------------------------------------------------------ 242 | 243 | /** 244 | Provides the duration of the audio file in seconds. 245 | */ 246 | @property (readonly) NSTimeInterval duration; 247 | 248 | //------------------------------------------------------------------------------ 249 | 250 | /** 251 | Provides the AudioStreamBasicDescription structure containing the format of the file. 252 | @return An AudioStreamBasicDescription structure describing the format of the audio file. 253 | */ 254 | @property (readonly) AudioStreamBasicDescription fileFormat; 255 | 256 | //------------------------------------------------------------------------------ 257 | 258 | /** 259 | Provides the current time as an NSString with the time format MM:SS. 260 | */ 261 | @property (readonly) NSString *formattedCurrentTime; 262 | 263 | //------------------------------------------------------------------------------ 264 | 265 | /** 266 | Provides the duration as an NSString with the time format MM:SS. 267 | */ 268 | @property (readonly) NSString *formattedDuration; 269 | 270 | //------------------------------------------------------------------------------ 271 | 272 | /** 273 | Provides the frame index (a.k.a the seek positon) within the audio file as SInt64. This can be helpful when seeking through the audio file. 274 | @return The current frame index within the audio file as a SInt64. 275 | */ 276 | @property (readonly) SInt64 frameIndex; 277 | 278 | //------------------------------------------------------------------------------ 279 | 280 | /** 281 | Provides a dictionary containing the metadata (ID3) tags that are included in the header for the audio file. Typically this contains stuff like artist, title, release year, etc. 282 | @return An NSDictionary containing the metadata for the audio file. 283 | */ 284 | @property (readonly) NSDictionary *metadata; 285 | 286 | //------------------------------------------------------------------------------ 287 | 288 | /** 289 | Provides the total duration of the audio file in seconds. 290 | @deprecated This property is deprecated starting in version 0.3.0. 291 | @note Please use `duration` property instead. 292 | @return The total duration of the audio file as a Float32. 293 | */ 294 | @property (readonly) NSTimeInterval totalDuration __attribute__((deprecated)); 295 | 296 | //------------------------------------------------------------------------------ 297 | 298 | /** 299 | Provides the total frame count of the audio file in the client format. 300 | @return The total number of frames in the audio file in the AudioStreamBasicDescription representing the client format as a SInt64. 301 | */ 302 | @property (readonly) SInt64 totalClientFrames; 303 | 304 | //------------------------------------------------------------------------------ 305 | 306 | /** 307 | Provides the total frame count of the audio file in the file format. 308 | @return The total number of frames in the audio file in the AudioStreamBasicDescription representing the file format as a SInt64. 309 | */ 310 | @property (readonly) SInt64 totalFrames; 311 | 312 | //------------------------------------------------------------------------------ 313 | 314 | /** 315 | Provides the NSURL for the audio file. 316 | @return An NSURL representing the path of the KSYAudioFile instance. 317 | */ 318 | @property (nonatomic, copy, readonly) NSURL *url; 319 | 320 | //------------------------------------------------------------------------------ 321 | #pragma mark - Helpers 322 | //------------------------------------------------------------------------------ 323 | 324 | /** 325 | Synchronously pulls the waveform amplitude data into a float array for the receiver. This returns a waveform with a default resolution of 1024, meaning there are 1024 data points to plot the waveform. 326 | @param numberOfPoints A UInt32 representing the number of data points you need. The higher the number of points the more detailed the waveform will be. 327 | @return A KSYAudioFloatData instance containing the audio data for all channels of the audio. 328 | */ 329 | - (KSYAudioFloatData *)getWaveformData; 330 | 331 | //------------------------------------------------------------------------------ 332 | 333 | /** 334 | Synchronously pulls the waveform amplitude data into a float array for the receiver. 335 | @param numberOfPoints A UInt32 representing the number of data points you need. The higher the number of points the more detailed the waveform will be. 336 | @return A KSYAudioFloatData instance containing the audio data for all channels of the audio. 337 | */ 338 | - (KSYAudioFloatData *)getWaveformDataWithNumberOfPoints:(UInt32)numberOfPoints; 339 | 340 | //------------------------------------------------------------------------------ 341 | 342 | /** 343 | Asynchronously pulls the waveform amplitude data into a float array for the receiver. This returns a waveform with a default resolution of 1024, meaning there are 1024 data points to plot the waveform. 344 | @param completion A KSYAudioWaveformDataCompletionBlock that executes when the waveform data has been extracted. Provides a `KSYAudioFloatData` instance containing the waveform data for all audio channels. 345 | */ 346 | - (void)getWaveformDataWithCompletionBlock:(KSYAudioWaveformDataCompletionBlock)completion; 347 | 348 | //------------------------------------------------------------------------------ 349 | 350 | /** 351 | Asynchronously pulls the waveform amplitude data into a float array for the receiver. 352 | @param numberOfPoints A UInt32 representing the number of data points you need. The higher the number of points the more detailed the waveform will be. 353 | @param completion A KSYAudioWaveformDataCompletionBlock that executes when the waveform data has been extracted. Provides a `KSYAudioFloatData` instance containing the waveform data for all audio channels. 354 | */ 355 | - (void)getWaveformDataWithNumberOfPoints:(UInt32)numberOfPoints 356 | completion:(KSYAudioWaveformDataCompletionBlock)completion; 357 | 358 | //------------------------------------------------------------------------------ 359 | 360 | @end 361 | 362 | 363 | //------------------------------------------------------------------------------ AudioData模型相关 364 | 365 | 366 | @interface KSYAudioFloatData : NSObject 367 | //------------------------------------------------------------------------------ 368 | 369 | + (instancetype)dataWithNumberOfChannels:(int)numberOfChannels 370 | buffers:(float **)buffers 371 | bufferSize:(UInt32)bufferSize; 372 | 373 | //------------------------------------------------------------------------------ 374 | 375 | @property (nonatomic, assign, readonly) int numberOfChannels; 376 | @property (nonatomic, assign, readonly) float **buffers; 377 | @property (nonatomic, assign, readonly) UInt32 bufferSize; 378 | 379 | //------------------------------------------------------------------------------ 380 | 381 | - (float *)bufferForChannel:(int)channel; 382 | 383 | //------------------------------------------------------------------------------ 384 | @end 385 | -------------------------------------------------------------------------------- /KSYAudioPlotView/KSYAudioFile.m: -------------------------------------------------------------------------------- 1 | // 2 | // KSYAudioFile.m 3 | // EditDemo 4 | // 5 | // Created by sunyazhou on 2017/6/15. 6 | // Copyright © 2017年 Kingsoft, Inc. All rights reserved. 7 | // 8 | 9 | #import "KSYAudioFile.h" 10 | #import "KSYAudioUtilities.h" 11 | #include 12 | // constants 13 | static UInt32 KSYAudioFileWaveformDefaultResolution = 1024; 14 | static NSString *KSYAudioFileWaveformDataQueueIdentifier = @"com.KSYaudio.waveformQueue"; 15 | 16 | //------------------------------------------------------------------------------ 17 | 18 | typedef struct 19 | { 20 | AudioFileID audioFileID; 21 | AudioStreamBasicDescription clientFormat; 22 | NSTimeInterval duration; 23 | ExtAudioFileRef extAudioFileRef; 24 | AudioStreamBasicDescription fileFormat; 25 | SInt64 frames; 26 | CFURLRef sourceURL; 27 | } KSYAudioFileInfo; 28 | 29 | //------------------------------------------------------------------------------ 30 | #pragma mark - KSYAudioFile 31 | //------------------------------------------------------------------------------ 32 | 33 | @interface KSYAudioFile () 34 | 35 | @property (nonatomic) float **floatData; 36 | @property (nonatomic) KSYAudioFileInfo *info; 37 | @property (nonatomic) pthread_mutex_t lock; 38 | @property (nonatomic) dispatch_queue_t waveformQueue; 39 | @end 40 | 41 | @implementation KSYAudioFile 42 | //------------------------------------------------------------------------------ 43 | #pragma mark - Dealloc 44 | //------------------------------------------------------------------------------ 45 | 46 | - (void)dealloc 47 | { 48 | pthread_mutex_destroy(&_lock); 49 | [KSYAudioUtilities freeFloatBuffers:self.floatData numberOfChannels:self.clientFormat.mChannelsPerFrame]; 50 | [KSYAudioUtilities checkResult:ExtAudioFileDispose(self.info->extAudioFileRef) operation:"Failed to dispose of ext audio file"]; 51 | free(self.info); 52 | } 53 | 54 | //------------------------------------------------------------------------------ 55 | #pragma mark - Initialization 56 | //------------------------------------------------------------------------------ 57 | 58 | - (instancetype)init 59 | { 60 | self = [super init]; 61 | if (self) 62 | { 63 | self.info = (KSYAudioFileInfo *)malloc(sizeof(KSYAudioFileInfo)); 64 | _floatData = NULL; 65 | pthread_mutex_init(&_lock, NULL); 66 | _waveformQueue = dispatch_queue_create(KSYAudioFileWaveformDataQueueIdentifier.UTF8String, DISPATCH_QUEUE_PRIORITY_DEFAULT); 67 | } 68 | return self; 69 | } 70 | 71 | //------------------------------------------------------------------------------ 72 | 73 | - (instancetype)initWithURL:(NSURL *)url 74 | { 75 | return [self initWithURL:url delegate:nil]; 76 | } 77 | 78 | //------------------------------------------------------------------------------ 79 | 80 | - (instancetype)initWithURL:(NSURL *)url 81 | delegate:(id)delegate 82 | { 83 | return [self initWithURL:url 84 | delegate:delegate 85 | clientFormat:[self.class defaultClientFormat]]; 86 | } 87 | 88 | //------------------------------------------------------------------------------ 89 | 90 | - (instancetype)initWithURL:(NSURL *)url 91 | delegate:(id)delegate 92 | clientFormat:(AudioStreamBasicDescription)clientFormat 93 | { 94 | self = [self init]; 95 | if (self) 96 | { 97 | self.info->sourceURL = (__bridge CFURLRef)(url); 98 | self.info->clientFormat = clientFormat; 99 | self.delegate = delegate; 100 | if (![self setup]) 101 | { 102 | return nil; 103 | } 104 | } 105 | return self; 106 | } 107 | 108 | //------------------------------------------------------------------------------ 109 | #pragma mark - Class Initializers 110 | //------------------------------------------------------------------------------ 111 | 112 | + (instancetype)audioFileWithURL:(NSURL *)url 113 | { 114 | return [[self alloc] initWithURL:url]; 115 | } 116 | 117 | //------------------------------------------------------------------------------ 118 | 119 | + (instancetype)audioFileWithURL:(NSURL *)url 120 | delegate:(id)delegate 121 | { 122 | return [[self alloc] initWithURL:url delegate:delegate]; 123 | } 124 | 125 | //------------------------------------------------------------------------------ 126 | 127 | + (instancetype)audioFileWithURL:(NSURL *)url 128 | delegate:(id)delegate 129 | clientFormat:(AudioStreamBasicDescription)clientFormat 130 | { 131 | return [[self alloc] initWithURL:url 132 | delegate:delegate 133 | clientFormat:clientFormat]; 134 | } 135 | 136 | //------------------------------------------------------------------------------ 137 | #pragma mark - NSCopying 138 | //------------------------------------------------------------------------------ 139 | 140 | - (id)copyWithZone:(NSZone *)zone 141 | { 142 | return [KSYAudioFile audioFileWithURL:self.url]; 143 | } 144 | 145 | //------------------------------------------------------------------------------ 146 | #pragma mark - Class Methods 147 | //------------------------------------------------------------------------------ 148 | 149 | + (AudioStreamBasicDescription)defaultClientFormat 150 | { 151 | return [KSYAudioUtilities stereoFloatNonInterleavedFormatWithSampleRate:[self defaultClientFormatSampleRate]]; 152 | } 153 | 154 | //------------------------------------------------------------------------------ 155 | 156 | + (Float64)defaultClientFormatSampleRate 157 | { 158 | return 44100.0f; 159 | } 160 | 161 | //------------------------------------------------------------------------------ 162 | 163 | + (NSArray *)supportedAudioFileTypes 164 | { 165 | return @ 166 | [ 167 | @"aac", 168 | @"caf", 169 | @"aif", 170 | @"aiff", 171 | @"aifc", 172 | @"mp3", 173 | @"mp4", 174 | @"m4a", 175 | @"snd", 176 | @"au", 177 | @"sd2", 178 | @"wav" 179 | ]; 180 | } 181 | 182 | //------------------------------------------------------------------------------ 183 | #pragma mark - Setup 184 | //------------------------------------------------------------------------------ 185 | 186 | - (BOOL)setup 187 | { 188 | // 189 | // Try to open the file, bail if the file could not be opened 190 | // 191 | BOOL success = [self openAudioFile]; 192 | if (!success) 193 | { 194 | return success; 195 | } 196 | 197 | // 198 | // Set the client format 199 | // 200 | self.clientFormat = self.info->clientFormat; 201 | 202 | return YES; 203 | } 204 | 205 | //------------------------------------------------------------------------------ 206 | #pragma mark - Creating/Opening Audio File 207 | //------------------------------------------------------------------------------ 208 | 209 | - (BOOL)openAudioFile 210 | { 211 | // 212 | // Need a source url 213 | // 214 | NSAssert(self.info->sourceURL, @"KSYAudioFile cannot be created without a source url!"); 215 | 216 | // 217 | // Determine if the file actually exists 218 | // 219 | CFURLRef url = self.info->sourceURL; 220 | NSURL *fileURL = (__bridge NSURL *)(url); 221 | BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:fileURL.path]; 222 | 223 | // 224 | // Create an ExtAudioFileRef for the file handle 225 | // 226 | if (fileExists) 227 | { 228 | [KSYAudioUtilities checkResult:ExtAudioFileOpenURL(url, &self.info->extAudioFileRef) 229 | operation:"Failed to create ExtAudioFileRef"]; 230 | } 231 | else 232 | { 233 | return NO; 234 | } 235 | 236 | // 237 | // Get the underlying AudioFileID 238 | // 239 | UInt32 propSize = sizeof(self.info->audioFileID); 240 | [KSYAudioUtilities checkResult:ExtAudioFileGetProperty(self.info->extAudioFileRef, 241 | kExtAudioFileProperty_AudioFile, 242 | &propSize, 243 | &self.info->audioFileID) 244 | operation:"Failed to get underlying AudioFileID"]; 245 | 246 | // 247 | // Store the file format 248 | // 249 | propSize = sizeof(self.info->fileFormat); 250 | [KSYAudioUtilities checkResult:ExtAudioFileGetProperty(self.info->extAudioFileRef, 251 | kExtAudioFileProperty_FileDataFormat, 252 | &propSize, 253 | &self.info->fileFormat) 254 | operation:"Failed to get file audio format on existing audio file"]; 255 | 256 | // 257 | // Get the total frames and duration 258 | // 259 | propSize = sizeof(SInt64); 260 | [KSYAudioUtilities checkResult:ExtAudioFileGetProperty(self.info->extAudioFileRef, 261 | kExtAudioFileProperty_FileLengthFrames, 262 | &propSize, 263 | &self.info->frames) 264 | operation:"Failed to get total frames"]; 265 | self.info->duration = (NSTimeInterval) self.info->frames / self.info->fileFormat.mSampleRate; 266 | 267 | return YES; 268 | } 269 | 270 | //------------------------------------------------------------------------------ 271 | #pragma mark - Events 272 | //------------------------------------------------------------------------------ 273 | 274 | - (void)seekToFrame:(SInt64)frame 275 | { 276 | if (pthread_mutex_trylock(&_lock) == 0) 277 | { 278 | [KSYAudioUtilities checkResult:ExtAudioFileSeek(self.info->extAudioFileRef, 279 | frame) 280 | operation:"Failed to seek frame position within audio file"]; 281 | 282 | pthread_mutex_unlock(&_lock); 283 | 284 | // 285 | // Notify delegate 286 | // 287 | if ([self.delegate respondsToSelector:@selector(audioFileUpdatedPosition:)]) 288 | { 289 | [self.delegate audioFileUpdatedPosition:self]; 290 | } 291 | 292 | // 293 | // Deprecated, but supported until 1.0 294 | // 295 | #pragma GCC diagnostic push 296 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 297 | if ([self.delegate respondsToSelector:@selector(audioFile:updatedPosition:)]) 298 | { 299 | [self.delegate audioFile:self updatedPosition:[self frameIndex]]; 300 | } 301 | #pragma GCC diagnostic pop 302 | } 303 | } 304 | 305 | //------------------------------------------------------------------------------ 306 | #pragma mark - Getters 307 | //------------------------------------------------------------------------------ 308 | 309 | - (AudioStreamBasicDescription)floatFormat 310 | { 311 | return [KSYAudioUtilities stereoFloatNonInterleavedFormatWithSampleRate:44100.0f]; 312 | } 313 | 314 | //------------------------------------------------------------------------------ 315 | 316 | - (KSYAudioFloatData *)getWaveformData 317 | { 318 | return [self getWaveformDataWithNumberOfPoints:KSYAudioFileWaveformDefaultResolution]; 319 | } 320 | 321 | //------------------------------------------------------------------------------ 322 | 323 | - (KSYAudioFloatData *)getWaveformDataWithNumberOfPoints:(UInt32)numberOfPoints 324 | { 325 | KSYAudioFloatData *waveformData; 326 | if (pthread_mutex_trylock(&_lock) == 0) 327 | { 328 | // store current frame 329 | SInt64 currentFrame = self.frameIndex; 330 | BOOL interleaved = [KSYAudioUtilities isInterleaved:self.clientFormat]; 331 | UInt32 channels = self.clientFormat.mChannelsPerFrame; 332 | if (channels == 0) 333 | { 334 | // prevent division by zero 335 | pthread_mutex_unlock(&_lock); 336 | return nil; 337 | } 338 | float **data = (float **)malloc( sizeof(float*) * channels ); 339 | for (int i = 0; i < channels; i++) 340 | { 341 | data[i] = (float *)malloc( sizeof(float) * numberOfPoints ); 342 | } 343 | 344 | // seek to 0 345 | [KSYAudioUtilities checkResult:ExtAudioFileSeek(self.info->extAudioFileRef, 346 | 0) 347 | operation:"Failed to seek frame position within audio file"]; 348 | 349 | // calculate the required number of frames per buffer 350 | SInt64 framesPerBuffer = ((SInt64) self.totalClientFrames / numberOfPoints); 351 | SInt64 framesPerChannel = framesPerBuffer / channels; 352 | 353 | // allocate an audio buffer list 354 | AudioBufferList *audioBufferList = [KSYAudioUtilities audioBufferListWithNumberOfFrames:(UInt32)framesPerBuffer 355 | numberOfChannels:self.info->clientFormat.mChannelsPerFrame 356 | interleaved:interleaved]; 357 | 358 | // read through file and calculate rms at each point 359 | for (SInt64 i = 0; i < numberOfPoints; i++) 360 | { 361 | UInt32 bufferSize = (UInt32) framesPerBuffer; 362 | [KSYAudioUtilities checkResult:ExtAudioFileRead(self.info->extAudioFileRef, 363 | &bufferSize, 364 | audioBufferList) 365 | operation:"Failed to read audio data from file waveform"]; 366 | if (interleaved) 367 | { 368 | float *buffer = (float *)audioBufferList->mBuffers[0].mData; 369 | for (int channel = 0; channel < channels; channel++) 370 | { 371 | float channelData[framesPerChannel]; 372 | for (int frame = 0; frame < framesPerChannel; frame++) 373 | { 374 | channelData[frame] = buffer[frame * channels + channel]; 375 | } 376 | float rms = [KSYAudioUtilities RMS:channelData length:(UInt32)framesPerChannel]; 377 | data[channel][i] = rms; 378 | } 379 | } 380 | else 381 | { 382 | for (int channel = 0; channel < channels; channel++) 383 | { 384 | float *channelData = audioBufferList->mBuffers[channel].mData; 385 | float rms = [KSYAudioUtilities RMS:channelData length:bufferSize]; 386 | data[channel][i] = rms; 387 | } 388 | } 389 | } 390 | 391 | // clean up 392 | [KSYAudioUtilities freeBufferList:audioBufferList]; 393 | 394 | // seek back to previous position 395 | [KSYAudioUtilities checkResult:ExtAudioFileSeek(self.info->extAudioFileRef, 396 | currentFrame) 397 | operation:"Failed to seek frame position within audio file"]; 398 | 399 | pthread_mutex_unlock(&_lock); 400 | 401 | waveformData = [KSYAudioFloatData dataWithNumberOfChannels:channels 402 | buffers:(float **)data 403 | bufferSize:numberOfPoints]; 404 | 405 | // cleanup 406 | for (int i = 0; i < channels; i++) 407 | { 408 | free(data[i]); 409 | } 410 | free(data); 411 | } 412 | return waveformData; 413 | } 414 | 415 | //------------------------------------------------------------------------------ 416 | 417 | - (void)getWaveformDataWithCompletionBlock:(KSYAudioWaveformDataCompletionBlock)waveformDataCompletionBlock 418 | { 419 | [self getWaveformDataWithNumberOfPoints:KSYAudioFileWaveformDefaultResolution 420 | completion:waveformDataCompletionBlock]; 421 | } 422 | 423 | //------------------------------------------------------------------------------ 424 | 425 | - (void)getWaveformDataWithNumberOfPoints:(UInt32)numberOfPoints 426 | completion:(KSYAudioWaveformDataCompletionBlock)completion 427 | { 428 | if (!completion) 429 | { 430 | return; 431 | } 432 | 433 | // async get waveform data 434 | __weak KSYAudioFile *weakSelf = self; 435 | dispatch_async(self.waveformQueue, ^{ 436 | KSYAudioFloatData *waveformData = [weakSelf getWaveformDataWithNumberOfPoints:numberOfPoints]; 437 | dispatch_async(dispatch_get_main_queue(), ^{ 438 | completion(waveformData.buffers, waveformData.bufferSize); 439 | }); 440 | }); 441 | } 442 | 443 | //------------------------------------------------------------------------------ 444 | 445 | - (AudioStreamBasicDescription)clientFormat 446 | { 447 | return self.info->clientFormat; 448 | } 449 | 450 | //------------------------------------------------------------------------------ 451 | 452 | - (NSTimeInterval)currentTime 453 | { 454 | return [KSYAudioUtilities MAP:(float)[self frameIndex] 455 | leftMin:0.0f 456 | leftMax:(float)[self totalFrames] 457 | rightMin:0.0f 458 | rightMax:[self duration]]; 459 | } 460 | 461 | //------------------------------------------------------------------------------ 462 | 463 | - (NSTimeInterval)duration 464 | { 465 | return self.info->duration; 466 | } 467 | 468 | //------------------------------------------------------------------------------ 469 | 470 | - (AudioStreamBasicDescription)fileFormat 471 | { 472 | return self.info->fileFormat; 473 | } 474 | 475 | //------------------------------------------------------------------------------ 476 | 477 | - (NSString *)formattedCurrentTime 478 | { 479 | return [KSYAudioUtilities displayTimeStringFromSeconds:[self currentTime]]; 480 | } 481 | 482 | //------------------------------------------------------------------------------ 483 | 484 | - (NSString *)formattedDuration 485 | { 486 | return [KSYAudioUtilities displayTimeStringFromSeconds:[self duration]]; 487 | } 488 | 489 | //------------------------------------------------------------------------------ 490 | 491 | - (SInt64)frameIndex 492 | { 493 | SInt64 frameIndex; 494 | [KSYAudioUtilities checkResult:ExtAudioFileTell(self.info->extAudioFileRef, &frameIndex) 495 | operation:"Failed to get frame index"]; 496 | return frameIndex; 497 | } 498 | 499 | //------------------------------------------------------------------------------ 500 | 501 | - (NSDictionary *)metadata 502 | { 503 | // get size of metadata property (dictionary) 504 | UInt32 propSize = sizeof(self.info->audioFileID); 505 | CFDictionaryRef metadata; 506 | UInt32 writable; 507 | [KSYAudioUtilities checkResult:AudioFileGetPropertyInfo(self.info->audioFileID, 508 | kAudioFilePropertyInfoDictionary, 509 | &propSize, 510 | &writable) 511 | operation:"Failed to get the size of the metadata dictionary"]; 512 | 513 | // pull metadata 514 | [KSYAudioUtilities checkResult:AudioFileGetProperty(self.info->audioFileID, 515 | kAudioFilePropertyInfoDictionary, 516 | &propSize, 517 | &metadata) 518 | operation:"Failed to get metadata dictionary"]; 519 | 520 | // cast to NSDictionary 521 | return (__bridge NSDictionary*)metadata; 522 | } 523 | 524 | //------------------------------------------------------------------------------ 525 | 526 | - (NSTimeInterval)totalDuration 527 | { 528 | return self.info->duration; 529 | } 530 | 531 | //------------------------------------------------------------------------------ 532 | 533 | - (SInt64)totalClientFrames 534 | { 535 | SInt64 totalFrames = [self totalFrames]; 536 | AudioStreamBasicDescription clientFormat = self.info->clientFormat; 537 | AudioStreamBasicDescription fileFormat = self.info->fileFormat; 538 | BOOL sameSampleRate = clientFormat.mSampleRate == fileFormat.mSampleRate; 539 | if (!sameSampleRate) 540 | { 541 | totalFrames = self.info->duration * clientFormat.mSampleRate; 542 | } 543 | return totalFrames; 544 | } 545 | 546 | //------------------------------------------------------------------------------ 547 | 548 | - (SInt64)totalFrames 549 | { 550 | return self.info->frames; 551 | } 552 | 553 | //------------------------------------------------------------------------------ 554 | 555 | - (NSURL *)url 556 | { 557 | return (__bridge NSURL*)self.info->sourceURL; 558 | } 559 | 560 | //------------------------------------------------------------------------------ 561 | #pragma mark - Setters 562 | //------------------------------------------------------------------------------ 563 | 564 | - (void)setClientFormat:(AudioStreamBasicDescription)clientFormat 565 | { 566 | // 567 | // Clear any float data currently cached 568 | // 569 | if (self.floatData) 570 | { 571 | self.floatData = nil; 572 | } 573 | 574 | // 575 | // Client format can only be linear PCM! 576 | // 577 | NSAssert([KSYAudioUtilities isLinearPCM:clientFormat], @"Client format must be linear PCM"); 578 | 579 | // 580 | // Store the client format 581 | // 582 | self.info->clientFormat = clientFormat; 583 | 584 | // 585 | // Set the client format on the ExtAudioFileRef 586 | // 587 | [KSYAudioUtilities checkResult:ExtAudioFileSetProperty(self.info->extAudioFileRef, 588 | kExtAudioFileProperty_ClientDataFormat, 589 | sizeof(clientFormat), 590 | &clientFormat) 591 | operation:"Couldn't set client data format on file"]; 592 | 593 | // 594 | // Create a new float converter using the client format as the input format 595 | 596 | // 597 | // Determine how big our float buffers need to be to hold a buffer of float 598 | // data for the audio received callback. 599 | // 600 | UInt32 maxPacketSize; 601 | UInt32 propSize = sizeof(maxPacketSize); 602 | [KSYAudioUtilities checkResult:ExtAudioFileGetProperty(self.info->extAudioFileRef, 603 | kExtAudioFileProperty_ClientMaxPacketSize, 604 | &propSize, 605 | &maxPacketSize) 606 | operation:"Failed to get max packet size"]; 607 | 608 | self.floatData = [KSYAudioUtilities floatBuffersWithNumberOfFrames:1024 609 | numberOfChannels:self.clientFormat.mChannelsPerFrame]; 610 | } 611 | 612 | //------------------------------------------------------------------------------ 613 | 614 | - (void)setCurrentTime:(NSTimeInterval)currentTime 615 | { 616 | NSAssert(currentTime < [self duration], @"Invalid seek operation, expected current time to be less than duration"); 617 | SInt64 frame = [KSYAudioUtilities MAP:currentTime 618 | leftMin:0.0f 619 | leftMax:[self duration] 620 | rightMin:0.0f 621 | rightMax:[self totalFrames]]; 622 | [self seekToFrame:frame]; 623 | } 624 | 625 | //------------------------------------------------------------------------------ 626 | #pragma mark - Description 627 | //------------------------------------------------------------------------------ 628 | 629 | - (NSString *)description 630 | { 631 | return [NSString stringWithFormat:@"%@ {\n" 632 | " url: %@,\n" 633 | " duration: %f,\n" 634 | " totalFrames: %lld,\n" 635 | " metadata: %@,\n" 636 | " fileFormat: { %@ },\n" 637 | " clientFormat: { %@ } \n" 638 | "}", 639 | [super description], 640 | [self url], 641 | [self duration], 642 | [self totalFrames], 643 | [self metadata], 644 | [KSYAudioUtilities stringForAudioStreamBasicDescription:[self fileFormat]], 645 | [KSYAudioUtilities stringForAudioStreamBasicDescription:[self clientFormat]]]; 646 | } 647 | 648 | //------------------------------------------------------------------------------ 649 | @end 650 | 651 | 652 | //------------------------------------------------------------------------------ AudioData模型相关 653 | #pragma mark - KSYAudioFloatData 654 | //------------------------------------------------------------------------------ 655 | 656 | @interface KSYAudioFloatData () 657 | @property (nonatomic, assign, readwrite) int numberOfChannels; 658 | @property (nonatomic, assign, readwrite) float **buffers; 659 | @property (nonatomic, assign, readwrite) UInt32 bufferSize; 660 | @end 661 | 662 | //------------------------------------------------------------------------------ 663 | 664 | @implementation KSYAudioFloatData 665 | 666 | //------------------------------------------------------------------------------ 667 | 668 | - (void)dealloc 669 | { 670 | [KSYAudioUtilities freeFloatBuffers:self.buffers 671 | numberOfChannels:self.numberOfChannels]; 672 | } 673 | 674 | //------------------------------------------------------------------------------ 675 | 676 | + (instancetype)dataWithNumberOfChannels:(int)numberOfChannels 677 | buffers:(float **)buffers 678 | bufferSize:(UInt32)bufferSize 679 | { 680 | id data = [[self alloc] init]; 681 | size_t size = sizeof(float) * bufferSize; 682 | float **buffersCopy = [KSYAudioUtilities floatBuffersWithNumberOfFrames:bufferSize 683 | numberOfChannels:numberOfChannels]; 684 | for (int i = 0; i < numberOfChannels; i++) 685 | { 686 | memcpy(buffersCopy[i], buffers[i], size); 687 | } 688 | ((KSYAudioFloatData *)data).buffers = buffersCopy; 689 | ((KSYAudioFloatData *)data).bufferSize = bufferSize; 690 | ((KSYAudioFloatData *)data).numberOfChannels = numberOfChannels; 691 | return data; 692 | } 693 | 694 | //------------------------------------------------------------------------------ 695 | 696 | - (float *)bufferForChannel:(int)channel 697 | { 698 | float *buffer = NULL; 699 | if (channel < self.numberOfChannels) 700 | { 701 | buffer = self.buffers[channel]; 702 | } 703 | return buffer; 704 | } 705 | 706 | //------------------------------------------------------------------------------ 707 | 708 | @end 709 | 710 | -------------------------------------------------------------------------------- /KSYAudioPlotView/KSYAudioPlot.h: -------------------------------------------------------------------------------- 1 | // 2 | // KSYAudioPlot.h 3 | // AudioPlotDemo 4 | // 5 | // Created by sunyazhou on 2017/6/17. 6 | // Copyright © 2017年 Ksyun. All rights reserved. 7 | // 8 | 9 | #ifndef KSYAudioPlot_h 10 | #define KSYAudioPlot_h 11 | 12 | #import "KSYAudioPlotView.h" 13 | #import "KSYAudioFile.h" 14 | #import "KSYPlotCircularBuffer.h" 15 | #import "KSYAudioUtilities.h" 16 | #import "KSYAudioDisplayLink.h" 17 | #endif /* KSYAudioPlot_h */ 18 | -------------------------------------------------------------------------------- /KSYAudioPlotView/KSYAudioPlotView.h: -------------------------------------------------------------------------------- 1 | // 2 | // KSYAudioPlotView.h 3 | // 4 | // Created by sunyazhou on 2017/6/16. 5 | // Copyright © 2017年 Kingsoft, Inc. All rights reserved. 6 | // 7 | 8 | #import 9 | //#import "KSYPlot.h" 10 | #import "KSYAudioDisplayLink.h" 11 | 12 | #import "KSYAudioUtilities.h" 13 | 14 | //------------------------------------------------------------------------------ 15 | #pragma mark - Enumerations 16 | //------------------------------------------------------------------------------ 17 | 18 | ///----------------------------------------------------------- 19 | /// @name Plot Types 20 | ///----------------------------------------------------------- 21 | 22 | /** 23 | The types of plots that can be displayed in the view using the data. 24 | */ 25 | typedef NS_ENUM(NSInteger, KSYPlotType) 26 | { 27 | /** 28 | Plot that displays only the samples of the current buffer 29 | */ 30 | KSYPlotTypeBuffer, 31 | 32 | /** 33 | Plot that displays a rolling history of values using the RMS calculated for each incoming buffer 34 | */ 35 | KSYPlotTypeRolling 36 | }; 37 | 38 | 39 | //@class KSYAudio; 40 | 41 | //------------------------------------------------------------------------------ 42 | #pragma mark - Constants 43 | //------------------------------------------------------------------------------ 44 | 45 | /** 46 | The default value used for the maximum rolling history buffer length of any KSYAudioPlot. 47 | @deprecated This constant is deprecated starting in version 0.2.0. 48 | @note Please use KSYAudioPlotDefaultMaxHistoryBufferLength instead. 49 | */ 50 | FOUNDATION_EXPORT UInt32 const kKSYAudioPlotMaxHistoryBufferLength __attribute__((deprecated)); 51 | 52 | /** 53 | The default value used for the default rolling history buffer length of any KSYAudioPlot. 54 | @deprecated This constant is deprecated starting in version 0.2.0. 55 | @note Please use KSYAudioPlotDefaultHistoryBufferLength instead. 56 | */ 57 | FOUNDATION_EXPORT UInt32 const kKSYAudioPlotDefaultHistoryBufferLength __attribute__((deprecated)); 58 | 59 | /** 60 | The default value used for the default rolling history buffer length of any KSYAudioPlot. 61 | */ 62 | FOUNDATION_EXPORT UInt32 const KSYAudioPlotDefaultHistoryBufferLength; 63 | 64 | /** 65 | The default value used for the maximum rolling history buffer length of any KSYAudioPlot. 66 | */ 67 | FOUNDATION_EXPORT UInt32 const KSYAudioPlotDefaultMaxHistoryBufferLength; 68 | 69 | //------------------------------------------------------------------------------ 70 | #pragma mark - KSYAudioPlotWaveformLayer 71 | //------------------------------------------------------------------------------ 72 | 73 | /** 74 | The KSYAudioPlotWaveformLayer is a lightweight subclass of the CAShapeLayer that allows implicit animations on the `path` key. 75 | */ 76 | @interface KSYAudioPlotWaveformLayer : CAShapeLayer 77 | @end 78 | 79 | //------------------------------------------------------------------------------ 80 | #pragma mark - KSYAudioPlot 81 | //------------------------------------------------------------------------------ 82 | //@interface KSYAudioPlot : KSYPlot 83 | #if TARGET_OS_IPHONE 84 | #import 85 | @interface KSYAudioPlotView : UIView 86 | #elif TARGET_OS_MAC 87 | #import 88 | @interface KSYAudioPlotView : NSView 89 | #endif 90 | 91 | //------------------------------------------------------------------------------ 92 | #pragma mark - Properties 93 | //------------------------------------------------------------------------------ 94 | 95 | ///----------------------------------------------------------- 96 | /// @name Customizing The Plot's Appearance 97 | ///----------------------------------------------------------- 98 | /** 99 | The default background color of the plot. For iOS the color is specified as a UIColor while for OSX the color is an NSColor. The default value on both platforms is black. 100 | */ 101 | #if TARGET_OS_IPHONE 102 | @property (nonatomic, strong) IBInspectable UIColor *backgroundColor; 103 | #elif TARGET_OS_MAC 104 | @property (nonatomic, strong) IBInspectable NSColor *backgroundColor; 105 | #endif 106 | 107 | /** 108 | The default color of the plot's data (i.e. waveform, y-axis values). For iOS the color is specified as a UIColor while for OSX the color is an NSColor. The default value on both platforms is red. 109 | */ 110 | #if TARGET_OS_IPHONE 111 | @property (nonatomic, strong) IBInspectable UIColor *color; 112 | #elif TARGET_OS_MAC 113 | @property (nonatomic, strong) IBInspectable NSColor *color; 114 | #endif 115 | 116 | /** 117 | The plot's gain value, which controls the scale of the y-axis values. The default value of the gain is 1.0f and should always be greater than 0.0f. 118 | */ 119 | @property (nonatomic, assign) IBInspectable float gain; 120 | 121 | /** 122 | The type of plot as specified by the `KSYPlotType` enumeration (i.e. a buffer or rolling plot type). 123 | */ 124 | @property (nonatomic, assign) IBInspectable KSYPlotType plotType; 125 | 126 | /** 127 | A boolean indicating whether or not to fill in the graph. A value of YES will make a filled graph (filling in the space between the x-axis and the y-value), while a value of NO will create a stroked graph (connecting the points along the y-axis). 128 | */ 129 | @property (nonatomic, assign) IBInspectable BOOL shouldFill; 130 | 131 | /** 132 | A boolean indicating whether the graph should be rotated along the x-axis to give a mirrored reflection. This is typical for audio plots to produce the classic waveform look. A value of YES will produce a mirrored reflection of the y-values about the x-axis, while a value of NO will only plot the y-values. 133 | */ 134 | @property (nonatomic, assign) IBInspectable BOOL shouldMirror; 135 | 136 | /** 137 | A BOOL that allows optimizing the audio plot's drawing for real-time displays. Since the update function may be updating the plot's data very quickly (over 60 frames per second) this property will throttle the drawing calls to be 60 frames per second (or whatever the screen rate is). Specifically, it disables implicit path change animations on the `waveformLayer` and sets up a display link to render 60 fps (audio updating the plot at 44.1 kHz causes it to re-render 86 fps - far greater than what is needed for a visual display). 138 | */ 139 | @property (nonatomic, assign) BOOL shouldOptimizeForRealtimePlot; 140 | 141 | //------------------------------------------------------------------------------ 142 | 143 | /** 144 | A BOOL indicating whether the plot should center itself vertically. 145 | */ 146 | @property (nonatomic, assign) BOOL shouldCenterYAxis; 147 | 148 | //------------------------------------------------------------------------------ 149 | 150 | /** 151 | An KSYAudioPlotWaveformLayer that is used to render the actual waveform. By switching the drawing code to Core Animation layers in version 0.2.0 most work, specifically the compositing step, is now done on the GPU. Hence, multiple KSYAudioPlot instances can be used simultaneously with very low CPU overhead so these are now practical for table and collection views. 152 | */ 153 | @property (nonatomic, strong) KSYAudioPlotWaveformLayer *waveformLayer; 154 | 155 | //------------------------------------------------------------------------------ 156 | #pragma mark - Adjust Resolution 157 | //------------------------------------------------------------------------------ 158 | 159 | ///----------------------------------------------------------- 160 | /// @name Adjusting The Resolution 161 | ///----------------------------------------------------------- 162 | 163 | /** 164 | Sets the length of the rolling history buffer (i.e. the number of points in the rolling plot's buffer). Can grow or shrink the display up to the maximum size specified by the `maximumRollingHistoryLength` method. Will return the actual set value, which will be either the given value if smaller than the `maximumRollingHistoryLength` or `maximumRollingHistoryLength` if a larger value is attempted to be set. 165 | @param historyLength The new length of the rolling history buffer. 166 | @return The new value equal to the historyLength or the `maximumRollingHistoryLength`. 167 | */ 168 | -(int)setRollingHistoryLength:(int)historyLength; 169 | 170 | //------------------------------------------------------------------------------ 171 | 172 | /** 173 | Provides the length of the rolling history buffer (i.e. the number of points in the rolling plot's buffer). 174 | * @return An int representing the length of the rolling history buffer 175 | */ 176 | -(int)rollingHistoryLength; 177 | 178 | //------------------------------------------------------------------------------ 179 | #pragma mark - Subclass Methods 180 | //------------------------------------------------------------------------------ 181 | 182 | ///----------------------------------------------------------- 183 | /// @name Subclass Methods 184 | ///----------------------------------------------------------- 185 | 186 | /** 187 | Main method that handles converting the points created from the `updatedBuffer:withBufferSize:` method into a CGPathRef to store in the `waveformLayer`. In this method you can create any path you'd like using the point array (for instance, maybe mapping the points to a circle instead of the standard 2D plane). 188 | @param points An array of CGPoint structures, with the x values ranging from 0 - (pointCount - 1) and y values containing the last audio data's buffer. 189 | @param pointCount A UInt32 of the length of the point array. 190 | @param rect An KSYRect (CGRect on iOS or NSRect on OSX) that the path should be created relative to. 191 | @return A CGPathRef that is the path you'd like to store on the `waveformLayer` to visualize the audio data. 192 | */ 193 | - (CGPathRef)createPathWithPoints:(CGPoint *)points 194 | pointCount:(UInt32)pointCount 195 | inRect:(KSYRect)rect; 196 | 197 | //------------------------------------------------------------------------------ 198 | 199 | /** 200 | Provides the default length of the rolling history buffer when the plot is initialized. Default is `KSYAudioPlotDefaultHistoryBufferLength` constant. 201 | @return An int describing the initial length of the rolling history buffer. 202 | */ 203 | - (int)defaultRollingHistoryLength; 204 | 205 | //------------------------------------------------------------------------------ 206 | 207 | /** 208 | Called after the view has been created. Subclasses should use to add any additional methods needed instead of overriding the init methods. 209 | */ 210 | - (void)setupPlot; 211 | 212 | //------------------------------------------------------------------------------ 213 | 214 | /** 215 | Provides the default number of points that will be used to initialize the graph's points data structure that holds. Essentially the plot starts off as a flat line of this many points. Default is 100. 216 | @return An int describing the initial number of points the plot should have when flat lined. 217 | */ 218 | - (int)initialPointCount; 219 | 220 | //------------------------------------------------------------------------------ 221 | 222 | /** 223 | Provides the default maximum rolling history length - that is, the maximum amount of points the `setRollingHistoryLength:` method may be set to. If a length higher than this is set then the plot will likely crash because the appropriate resources are only allocated once during the plot's initialization step. Defualt is `KSYAudioPlotDefaultMaxHistoryBufferLength` constant. 224 | @return An int describing the maximum length of the absolute rolling history buffer. 225 | */ 226 | - (int)maximumRollingHistoryLength; 227 | 228 | //------------------------------------------------------------------------------ 229 | 230 | /** 231 | Method to cause the waveform layer's path to get recreated and redrawn on screen using the last buffer of data provided. This is the equivalent to the drawRect: method used to normally subclass a view's drawing. This normally don't need to be overrode though - a better approach would be to override the `createPathWithPoints:pointCount:inRect:` method. 232 | */ 233 | - (void)redraw; 234 | 235 | //------------------------------------------------------------------------------ 236 | 237 | /** 238 | Main method used to copy the sample data from the source buffer and update the 239 | plot. Subclasses can overwrite this method for custom behavior. 240 | @param data A float array of the sample data. Subclasses should copy this data to a separate array to avoid threading issues. 241 | @param length The length of the float array as an int. 242 | */ 243 | -(void)setSampleData:(float *)data length:(int)length; 244 | 245 | //------------------------------------------------------------------------------ 246 | 247 | #pragma mark - Clearing 248 | //------------------------------------------------------------------------------ 249 | 250 | ///----------------------------------------------------------- 251 | /// @name Clearing The Plot 252 | ///----------------------------------------------------------- 253 | 254 | /** 255 | Clears all data from the audio plot (includes both KSYPlotTypeBuffer and KSYPlotTypeRolling) 256 | */ 257 | -(void)clear; 258 | 259 | //------------------------------------------------------------------------------ 260 | #pragma mark - Get Samples 261 | //------------------------------------------------------------------------------ 262 | 263 | ///----------------------------------------------------------- 264 | /// @name Updating The Plot 265 | ///----------------------------------------------------------- 266 | 267 | /** 268 | Updates the plot with the new buffer data and tells the view to redraw itself. Caller will provide a float array with the values they expect to see on the y-axis. The plot will internally handle mapping the x-axis and y-axis to the current view port, any interpolation for fills effects, and mirroring. 269 | @param buffer A float array of values to map to the y-axis. 270 | @param bufferSize The size of the float array that will be mapped to the y-axis. 271 | @warning The bufferSize is expected to be the same, constant value once initial triggered. For plots using OpenGL a vertex buffer object will be allocated with a maximum buffersize of (2 * the initial given buffer size) to account for any interpolation necessary for filling in the graph. Updates use the glBufferSubData(...) function, which will crash if the buffersize exceeds the initial maximum allocated size. 272 | */ 273 | -(void)updateBuffer:(float *)buffer withBufferSize:(UInt32)bufferSize; 274 | @end 275 | 276 | @interface KSYAudioPlotView () 277 | @property (nonatomic, strong) KSYAudioDisplayLink *displayLink; 278 | @property (nonatomic, assign) KSYPlotHistoryInfo *historyInfo; 279 | @property (nonatomic, assign) CGPoint *points; 280 | @property (nonatomic, assign) UInt32 pointCount; 281 | @end 282 | -------------------------------------------------------------------------------- /KSYAudioPlotView/KSYAudioPlotView.m: -------------------------------------------------------------------------------- 1 | // 2 | // KSYAudioPlotView.m 3 | // EditDemo 4 | // 5 | // Created by sunyazhou on 2017/6/16. 6 | // Copyright © 2017年 Kingsoft, Inc. All rights reserved. 7 | // 8 | 9 | #import "KSYAudioPlotView.h" 10 | 11 | 12 | //------------------------------------------------------------------------------ 13 | #pragma mark - Constants 14 | //------------------------------------------------------------------------------ 15 | 16 | UInt32 const kKSYAudioPlotMaxHistoryBufferLength = 8192; 17 | UInt32 const kKSYAudioPlotDefaultHistoryBufferLength = 512; 18 | UInt32 const KSYAudioPlotDefaultHistoryBufferLength = 512; 19 | UInt32 const KSYAudioPlotDefaultMaxHistoryBufferLength = 8192; 20 | 21 | //------------------------------------------------------------------------------ 22 | #pragma mark - KSYAudioPlot (Implementation) 23 | //------------------------------------------------------------------------------ 24 | 25 | @implementation KSYAudioPlotView 26 | 27 | //------------------------------------------------------------------------------ 28 | #pragma mark - Dealloc 29 | //------------------------------------------------------------------------------ 30 | 31 | - (void)dealloc 32 | { 33 | [KSYAudioUtilities freeHistoryInfo:self.historyInfo]; 34 | free(self.points); 35 | } 36 | 37 | //------------------------------------------------------------------------------ 38 | #pragma mark - Initialization 39 | //------------------------------------------------------------------------------ 40 | 41 | - (id)init 42 | { 43 | self = [super init]; 44 | if (self) 45 | { 46 | [self initPlot]; 47 | } 48 | return self; 49 | } 50 | 51 | - (id)initWithCoder:(NSCoder *)aDecoder 52 | { 53 | self = [super initWithCoder:aDecoder]; 54 | if (self) 55 | { 56 | [self initPlot]; 57 | } 58 | return self; 59 | } 60 | 61 | #if TARGET_OS_IPHONE 62 | - (id)initWithFrame:(CGRect)frameRect 63 | #elif TARGET_OS_MAC 64 | - (id)initWithFrame:(NSRect)frameRect 65 | #endif 66 | { 67 | self = [super initWithFrame:frameRect]; 68 | if (self) 69 | { 70 | [self initPlot]; 71 | } 72 | return self; 73 | } 74 | 75 | #if TARGET_OS_IPHONE 76 | - (void)layoutSubviews 77 | { 78 | [super layoutSubviews]; 79 | [CATransaction begin]; 80 | [CATransaction setDisableActions:YES]; 81 | self.waveformLayer.frame = self.bounds; 82 | [self redraw]; 83 | [CATransaction commit]; 84 | } 85 | #elif TARGET_OS_MAC 86 | - (void)layout 87 | { 88 | [super layout]; 89 | [CATransaction begin]; 90 | [CATransaction setDisableActions:YES]; 91 | self.waveformLayer.frame = self.bounds; 92 | [self redraw]; 93 | [CATransaction commit]; 94 | } 95 | #endif 96 | 97 | - (void)initPlot 98 | { 99 | self.shouldCenterYAxis = YES; 100 | self.shouldOptimizeForRealtimePlot = YES; 101 | self.gain = 1.0; 102 | self.plotType = KSYPlotTypeBuffer; 103 | self.shouldMirror = NO; 104 | self.shouldFill = NO; 105 | 106 | // Setup history window 107 | [self resetHistoryBuffers]; 108 | 109 | self.waveformLayer = [KSYAudioPlotWaveformLayer layer]; 110 | self.waveformLayer.frame = self.bounds; 111 | self.waveformLayer.lineWidth = 1.0f; 112 | self.waveformLayer.fillColor = nil; 113 | self.waveformLayer.backgroundColor = nil; 114 | self.waveformLayer.opaque = YES; 115 | 116 | #if TARGET_OS_IPHONE 117 | self.color = [UIColor colorWithHue:0 saturation:1.0 brightness:1.0 alpha:1.0]; 118 | #elif TARGET_OS_MAC 119 | self.color = [NSColor colorWithCalibratedHue:0 saturation:1.0 brightness:1.0 alpha:1.0]; 120 | self.wantsLayer = YES; 121 | self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay; 122 | #endif 123 | self.backgroundColor = nil; 124 | [self.layer insertSublayer:self.waveformLayer atIndex:0]; 125 | 126 | // 127 | // Allow subclass to initialize plot 128 | // 129 | [self setupPlot]; 130 | 131 | self.points = calloc(KSYAudioPlotDefaultMaxHistoryBufferLength, sizeof(CGPoint)); 132 | self.pointCount = [self initialPointCount]; 133 | [self redraw]; 134 | } 135 | 136 | //------------------------------------------------------------------------------ 137 | 138 | - (void)setupPlot 139 | { 140 | // 141 | // Override in subclass 142 | // 143 | } 144 | 145 | //------------------------------------------------------------------------------ 146 | #pragma mark - Setup 147 | //------------------------------------------------------------------------------ 148 | 149 | - (void)resetHistoryBuffers 150 | { 151 | // 152 | // Clear any existing data 153 | // 154 | if (self.historyInfo) 155 | { 156 | [KSYAudioUtilities freeHistoryInfo:self.historyInfo]; 157 | } 158 | 159 | self.historyInfo = [KSYAudioUtilities historyInfoWithDefaultLength:[self defaultRollingHistoryLength] 160 | maximumLength:[self maximumRollingHistoryLength]]; 161 | } 162 | 163 | //------------------------------------------------------------------------------ 164 | #pragma mark - Setters 165 | //------------------------------------------------------------------------------ 166 | 167 | - (void)setBackgroundColor:(id)backgroundColor 168 | { 169 | [super setBackgroundColor:backgroundColor]; 170 | self.layer.backgroundColor = [backgroundColor CGColor]; 171 | } 172 | 173 | //------------------------------------------------------------------------------ 174 | 175 | - (void)setColor:(id)color 176 | { 177 | _color = color; 178 | // [super setColor:color]; 179 | self.waveformLayer.strokeColor = [color CGColor]; 180 | if (self.shouldFill) 181 | { 182 | self.waveformLayer.fillColor = [color CGColor]; 183 | } 184 | } 185 | 186 | //------------------------------------------------------------------------------ 187 | 188 | - (void)setShouldOptimizeForRealtimePlot:(BOOL)shouldOptimizeForRealtimePlot 189 | { 190 | _shouldOptimizeForRealtimePlot = shouldOptimizeForRealtimePlot; 191 | if (shouldOptimizeForRealtimePlot && !self.displayLink) 192 | { 193 | self.displayLink = [KSYAudioDisplayLink displayLinkWithDelegate:self]; 194 | [self.displayLink start]; 195 | } 196 | else 197 | { 198 | [self.displayLink stop]; 199 | self.displayLink = nil; 200 | } 201 | } 202 | 203 | //------------------------------------------------------------------------------ 204 | 205 | - (void)setShouldFill:(BOOL)shouldFill 206 | { 207 | _shouldFill = shouldFill; 208 | // [super setShouldFill:shouldFill]; 209 | self.waveformLayer.fillColor = shouldFill ? [self.color CGColor] : nil; 210 | } 211 | 212 | //------------------------------------------------------------------------------ 213 | #pragma mark - Drawing 214 | //------------------------------------------------------------------------------ 215 | 216 | - (void)clear 217 | { 218 | if (self.pointCount > 0) 219 | { 220 | [self resetHistoryBuffers]; 221 | float data[self.pointCount]; 222 | memset(data, 0, self.pointCount * sizeof(float)); 223 | [self setSampleData:data length:self.pointCount]; 224 | [self redraw]; 225 | } 226 | } 227 | 228 | //------------------------------------------------------------------------------ 229 | 230 | - (void)redraw 231 | { 232 | KSYRect frame = [self.waveformLayer frame]; 233 | CGPathRef path = [self createPathWithPoints:self.points 234 | pointCount:self.pointCount 235 | inRect:frame]; 236 | if (self.shouldOptimizeForRealtimePlot) 237 | { 238 | [CATransaction begin]; 239 | [CATransaction setDisableActions:YES]; 240 | self.waveformLayer.path = path; 241 | [CATransaction commit]; 242 | } 243 | else 244 | { 245 | self.waveformLayer.path = path; 246 | } 247 | CGPathRelease(path); 248 | } 249 | 250 | //------------------------------------------------------------------------------ 251 | 252 | - (CGPathRef)createPathWithPoints:(CGPoint *)points 253 | pointCount:(UInt32)pointCount 254 | inRect:(KSYRect)rect 255 | { 256 | CGMutablePathRef path = NULL; 257 | if (pointCount > 0) 258 | { 259 | path = CGPathCreateMutable(); 260 | double xscale = (rect.size.width) / ((float)self.pointCount); 261 | double halfHeight = floor(rect.size.height / 2.0); 262 | int deviceOriginFlipped = [self isDeviceOriginFlipped] ? -1 : 1; 263 | CGAffineTransform xf = CGAffineTransformIdentity; 264 | CGFloat translateY = 0.0f; 265 | if (!self.shouldCenterYAxis) 266 | { 267 | #if TARGET_OS_IPHONE 268 | translateY = CGRectGetHeight(rect); 269 | #elif TARGET_OS_MAC 270 | translateY = 0.0f; 271 | #endif 272 | } 273 | else 274 | { 275 | translateY = halfHeight + rect.origin.y; 276 | } 277 | xf = CGAffineTransformTranslate(xf, 0.0, translateY); 278 | double yScaleFactor = halfHeight; 279 | if (!self.shouldCenterYAxis) 280 | { 281 | yScaleFactor = 2.0 * halfHeight; 282 | } 283 | xf = CGAffineTransformScale(xf, xscale, deviceOriginFlipped * yScaleFactor); 284 | CGPathAddLines(path, &xf, self.points, self.pointCount); 285 | if (self.shouldMirror) 286 | { 287 | xf = CGAffineTransformScale(xf, 1.0f, -1.0f); 288 | CGPathAddLines(path, &xf, self.points, self.pointCount); 289 | } 290 | if (self.shouldFill) 291 | { 292 | CGPathCloseSubpath(path); 293 | } 294 | } 295 | return path; 296 | } 297 | 298 | //------------------------------------------------------------------------------ 299 | #pragma mark - Update 300 | //------------------------------------------------------------------------------ 301 | 302 | - (void)updateBuffer:(float *)buffer withBufferSize:(UInt32)bufferSize 303 | { 304 | // append the buffer to the history 305 | [KSYAudioUtilities appendBufferRMS:buffer 306 | withBufferSize:bufferSize 307 | toHistoryInfo:self.historyInfo]; 308 | 309 | // copy samples 310 | switch (self.plotType) 311 | { 312 | case KSYPlotTypeBuffer: 313 | [self setSampleData:buffer 314 | length:bufferSize]; 315 | break; 316 | case KSYPlotTypeRolling: 317 | 318 | [self setSampleData:self.historyInfo->buffer 319 | length:self.historyInfo->bufferSize]; 320 | break; 321 | default: 322 | break; 323 | } 324 | 325 | // update drawing 326 | if (!self.shouldOptimizeForRealtimePlot) 327 | { 328 | [self redraw]; 329 | } 330 | } 331 | 332 | //------------------------------------------------------------------------------ 333 | 334 | - (void)setSampleData:(float *)data length:(int)length 335 | { 336 | CGPoint *points = self.points; 337 | for (int i = 0; i < length; i++) 338 | { 339 | points[i].x = i; 340 | points[i].y = data[i] * self.gain; 341 | } 342 | points[0].y = points[length - 1].y = 0.0f; 343 | self.pointCount = length; 344 | } 345 | 346 | //------------------------------------------------------------------------------ 347 | #pragma mark - Adjusting History Resolution 348 | //------------------------------------------------------------------------------ 349 | 350 | - (int)rollingHistoryLength 351 | { 352 | return self.historyInfo->bufferSize; 353 | } 354 | 355 | //------------------------------------------------------------------------------ 356 | 357 | - (int)setRollingHistoryLength:(int)historyLength 358 | { 359 | self.historyInfo->bufferSize = MIN(KSYAudioPlotDefaultMaxHistoryBufferLength, historyLength); 360 | return self.historyInfo->bufferSize; 361 | } 362 | 363 | //------------------------------------------------------------------------------ 364 | #pragma mark - Subclass 365 | //------------------------------------------------------------------------------ 366 | 367 | - (int)defaultRollingHistoryLength 368 | { 369 | return KSYAudioPlotDefaultHistoryBufferLength; 370 | } 371 | 372 | //------------------------------------------------------------------------------ 373 | 374 | - (int)initialPointCount 375 | { 376 | return 100; 377 | } 378 | 379 | //------------------------------------------------------------------------------ 380 | 381 | - (int)maximumRollingHistoryLength 382 | { 383 | return KSYAudioPlotDefaultMaxHistoryBufferLength; 384 | } 385 | 386 | //------------------------------------------------------------------------------ 387 | #pragma mark - Utility 388 | //------------------------------------------------------------------------------ 389 | 390 | - (BOOL)isDeviceOriginFlipped 391 | { 392 | BOOL isDeviceOriginFlipped = NO; 393 | #if TARGET_OS_IPHONE 394 | isDeviceOriginFlipped = YES; 395 | #elif TARGET_OS_MAC 396 | #endif 397 | return isDeviceOriginFlipped; 398 | } 399 | 400 | //------------------------------------------------------------------------------ 401 | #pragma mark - KSYAudioDisplayLinkDelegate 402 | //------------------------------------------------------------------------------ 403 | 404 | - (void)displayLinkNeedsDisplay:(KSYAudioDisplayLink *)displayLink 405 | { 406 | [self redraw]; 407 | } 408 | 409 | //------------------------------------------------------------------------------ 410 | 411 | @end 412 | 413 | ////------------------------------------------------------------------------------ 414 | #pragma mark - KSYAudioPlotWaveformLayer (Implementation) 415 | ////------------------------------------------------------------------------------ 416 | 417 | @implementation KSYAudioPlotWaveformLayer 418 | 419 | - (id)actionForKey:(NSString *)event 420 | { 421 | if ([event isEqualToString:@"path"]) 422 | { 423 | if ([CATransaction disableActions]) 424 | { 425 | return nil; 426 | } 427 | else 428 | { 429 | CABasicAnimation *animation = [CABasicAnimation animation]; 430 | animation.timingFunction = [CATransaction animationTimingFunction]; 431 | animation.duration = [CATransaction animationDuration]; 432 | return animation; 433 | } 434 | return nil; 435 | } 436 | return [super actionForKey:event]; 437 | } 438 | 439 | @end 440 | 441 | -------------------------------------------------------------------------------- /KSYAudioPlotView/KSYAudioUtilities.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import "KSYPlotCircularBuffer.h" 5 | #if TARGET_OS_IPHONE 6 | #import 7 | #elif TARGET_OS_MAC 8 | #endif 9 | 10 | //------------------------------------------------------------------------------ 11 | #pragma mark - Data Structures 12 | //------------------------------------------------------------------------------ 13 | 14 | /** 15 | A data structure that holds information about audio data over time. It contains a circular buffer to incrementally write the audio data to and a scratch buffer to hold a window of audio data relative to the whole circular buffer. In use, this will provide a way to continuously append data while having an adjustable viewable window described by the bufferSize. 16 | */ 17 | typedef struct 18 | { 19 | float *buffer; 20 | int bufferSize; 21 | KSYPlotCircularBuffer circularBuffer; 22 | } KSYPlotHistoryInfo; 23 | 24 | //------------------------------------------------------------------------------ 25 | 26 | /** 27 | A data structure that holds information about a node in the context of an AUGraph. 28 | */ 29 | typedef struct 30 | { 31 | AudioUnit audioUnit; 32 | AUNode node; 33 | } KSYAudioNodeInfo; 34 | 35 | //------------------------------------------------------------------------------ 36 | #pragma mark - Types 37 | //------------------------------------------------------------------------------ 38 | 39 | #if TARGET_OS_IPHONE 40 | typedef CGRect KSYRect; 41 | #elif TARGET_OS_MAC 42 | typedef NSRect KSYRect; 43 | #endif 44 | 45 | //------------------------------------------------------------------------------ 46 | #pragma mark - KSYAudioUtilities 47 | //------------------------------------------------------------------------------ 48 | 49 | /** 50 | The KSYAudioUtilities class provides a set of class-level utility methods used throughout KSYAudio to handle common operations such as allocating audio buffers and structures, creating various types of AudioStreamBasicDescription structures, string helpers for formatting and debugging, various math utilities, a very handy check result function (used everywhere!), and helpers for dealing with circular buffers. These were previously on the KSYAudio class, but as of the 0.1.0 release have been moved here so the whole KSYAudio is not needed when using only certain modules. 51 | */ 52 | @interface KSYAudioUtilities : NSObject 53 | 54 | //------------------------------------------------------------------------------ 55 | #pragma mark - Debugging 56 | //------------------------------------------------------------------------------ 57 | 58 | ///----------------------------------------------------------- 59 | /// @name Debugging KSYAudio 60 | ///----------------------------------------------------------- 61 | 62 | /** 63 | Globally sets whether or not the program should exit if a `checkResult:operation:` operation fails. Currently the behavior on KSYAudio is to quit if a `checkResult:operation:` fails, but this is not desirable in any production environment. Internally there are a lot of `checkResult:operation:` operations used on all the core classes. This should only ever be set to NO in production environments since a `checkResult:operation:` failing means something breaking has likely happened. 64 | @param shouldExitOnCheckResultFail A BOOL indicating whether or not the running program should exist due to a `checkResult:operation:` fail. 65 | */ 66 | + (void)setShouldExitOnCheckResultFail:(BOOL)shouldExitOnCheckResultFail; 67 | 68 | //------------------------------------------------------------------------------ 69 | 70 | /** 71 | Provides a flag indicating whether or not the program will exit if a `checkResult:operation:` fails. 72 | @return A BOOL indicating whether or not the program will exit if a `checkResult:operation:` fails. 73 | */ 74 | + (BOOL)shouldExitOnCheckResultFail; 75 | 76 | //------------------------------------------------------------------------------ 77 | #pragma mark - AudioBufferList Utility 78 | //------------------------------------------------------------------------------ 79 | 80 | ///----------------------------------------------------------- 81 | /// @name AudioBufferList Utility 82 | ///----------------------------------------------------------- 83 | 84 | /** 85 | Allocates an AudioBufferList structure. Make sure to call freeBufferList when done using AudioBufferList or it will leak. 86 | @param frames The number of frames that will be stored within each audio buffer 87 | @param channels The number of channels (e.g. 2 for stereo, 1 for mono, etc.) 88 | @param interleaved Whether the samples will be interleaved (if not it will be assumed to be non-interleaved and each channel will have an AudioBuffer allocated) 89 | @return An AudioBufferList struct that has been allocated in memory 90 | */ 91 | + (AudioBufferList *)audioBufferListWithNumberOfFrames:(UInt32)frames 92 | numberOfChannels:(UInt32)channels 93 | interleaved:(BOOL)interleaved; 94 | 95 | //------------------------------------------------------------------------------ 96 | 97 | /** 98 | Allocates an array of float arrays given the number of frames needed to store in each float array. 99 | @param frames A UInt32 representing the number of frames to store in each float buffer 100 | @param channels A UInt32 representing the number of channels (i.e. the number of float arrays to allocate) 101 | @return An array of float arrays, each the length of the number of frames specified 102 | */ 103 | + (float **)floatBuffersWithNumberOfFrames:(UInt32)frames 104 | numberOfChannels:(UInt32)channels; 105 | 106 | //------------------------------------------------------------------------------ 107 | 108 | /** 109 | Deallocates an AudioBufferList structure from memory. 110 | @param bufferList A pointer to the buffer list you would like to free 111 | */ 112 | + (void)freeBufferList:(AudioBufferList *)bufferList; 113 | 114 | //------------------------------------------------------------------------------ 115 | 116 | /** 117 | Deallocates an array of float buffers 118 | @param buffers An array of float arrays 119 | @param channels A UInt32 representing the number of channels (i.e. the number of float arrays to deallocate) 120 | */ 121 | + (void)freeFloatBuffers:(float **)buffers numberOfChannels:(UInt32)channels; 122 | 123 | //------------------------------------------------------------------------------ 124 | #pragma mark - AudioStreamBasicDescription Utilties 125 | //------------------------------------------------------------------------------ 126 | 127 | ///----------------------------------------------------------- 128 | /// @name Creating An AudioStreamBasicDescription 129 | ///----------------------------------------------------------- 130 | 131 | /** 132 | Creates a signed-integer, interleaved AudioStreamBasicDescription for the number of channels specified for an AIFF format. 133 | @param channels The desired number of channels 134 | @param sampleRate A float representing the sample rate. 135 | @return A new AudioStreamBasicDescription with the specified format. 136 | */ 137 | + (AudioStreamBasicDescription)AIFFFormatWithNumberOfChannels:(UInt32)channels 138 | sampleRate:(float)sampleRate; 139 | 140 | //------------------------------------------------------------------------------ 141 | 142 | /** 143 | Creates an AudioStreamBasicDescription for the iLBC narrow band speech codec. 144 | @param sampleRate A float representing the sample rate. 145 | @return A new AudioStreamBasicDescription with the specified format. 146 | */ 147 | + (AudioStreamBasicDescription)iLBCFormatWithSampleRate:(float)sampleRate; 148 | 149 | //------------------------------------------------------------------------------ 150 | 151 | /** 152 | Creates a float-based, non-interleaved AudioStreamBasicDescription for the number of channels specified. 153 | @param channels A UInt32 representing the number of channels. 154 | @param sampleRate A float representing the sample rate. 155 | @return A float-based AudioStreamBasicDescription with the number of channels specified. 156 | */ 157 | + (AudioStreamBasicDescription)floatFormatWithNumberOfChannels:(UInt32)channels 158 | sampleRate:(float)sampleRate; 159 | 160 | //------------------------------------------------------------------------------ 161 | 162 | /** 163 | Creates an AudioStreamBasicDescription for an M4A AAC format. 164 | @param channels The desired number of channels 165 | @param sampleRate A float representing the sample rate. 166 | @return A new AudioStreamBasicDescription with the specified format. 167 | */ 168 | + (AudioStreamBasicDescription)M4AFormatWithNumberOfChannels:(UInt32)channels 169 | sampleRate:(float)sampleRate; 170 | 171 | //------------------------------------------------------------------------------ 172 | 173 | /** 174 | Creates a single-channel, float-based AudioStreamBasicDescription. 175 | @param sampleRate A float representing the sample rate. 176 | @return A new AudioStreamBasicDescription with the specified format. 177 | */ 178 | + (AudioStreamBasicDescription)monoFloatFormatWithSampleRate:(float)sampleRate; 179 | 180 | //------------------------------------------------------------------------------ 181 | 182 | /** 183 | Creates a single-channel, float-based AudioStreamBasicDescription (as of 0.0.6 this is the same as `monoFloatFormatWithSampleRate:`). 184 | @param sampleRate A float representing the sample rate. 185 | @return A new AudioStreamBasicDescription with the specified format. 186 | */ 187 | + (AudioStreamBasicDescription)monoCanonicalFormatWithSampleRate:(float)sampleRate; 188 | 189 | //------------------------------------------------------------------------------ 190 | 191 | /** 192 | Creates a two-channel, non-interleaved, float-based AudioStreamBasicDescription (as of 0.0.6 this is the same as `stereoFloatNonInterleavedFormatWithSampleRate:`). 193 | @param sampleRate A float representing the sample rate. 194 | @return A new AudioStreamBasicDescription with the specified format. 195 | */ 196 | + (AudioStreamBasicDescription)stereoCanonicalNonInterleavedFormatWithSampleRate:(float)sampleRate; 197 | 198 | //------------------------------------------------------------------------------ 199 | 200 | /** 201 | Creates a two-channel, interleaved, float-based AudioStreamBasicDescription. 202 | @param sampleRate A float representing the sample rate. 203 | @return A new AudioStreamBasicDescription with the specified format. 204 | */ 205 | + (AudioStreamBasicDescription)stereoFloatInterleavedFormatWithSampleRate:(float)sampleRate; 206 | 207 | //------------------------------------------------------------------------------ 208 | 209 | /** 210 | Creates a two-channel, non-interleaved, float-based AudioStreamBasicDescription. 211 | @param sampleRate A float representing the sample rate. 212 | @return A new AudioStreamBasicDescription with the specified format. 213 | */ 214 | + (AudioStreamBasicDescription)stereoFloatNonInterleavedFormatWithSampleRate:(float)sampleRate; 215 | 216 | //------------------------------------------------------------------------------ 217 | // @name AudioStreamBasicDescription Helper Functions 218 | //------------------------------------------------------------------------------ 219 | 220 | /** 221 | Checks an AudioStreamBasicDescription to see if it is a float-based format (as opposed to a signed integer based format). 222 | @param asbd A valid AudioStreamBasicDescription 223 | @return A BOOL indicating whether or not the AudioStreamBasicDescription is a float format. 224 | */ 225 | + (BOOL)isFloatFormat:(AudioStreamBasicDescription)asbd; 226 | 227 | //------------------------------------------------------------------------------ 228 | 229 | /** 230 | Checks an AudioStreamBasicDescription to check for an interleaved flag (samples are 231 | stored in one buffer one after another instead of two (or n channels) parallel buffers 232 | @param asbd A valid AudioStreamBasicDescription 233 | @return A BOOL indicating whether or not the AudioStreamBasicDescription is interleaved 234 | */ 235 | + (BOOL)isInterleaved:(AudioStreamBasicDescription)asbd; 236 | 237 | //------------------------------------------------------------------------------ 238 | 239 | /** 240 | Checks an AudioStreamBasicDescription to see if it is a linear PCM format (uncompressed, 241 | 1 frame per packet) 242 | @param asbd A valid AudioStreamBasicDescription 243 | @return A BOOL indicating whether or not the AudioStreamBasicDescription is linear PCM. 244 | */ 245 | + (BOOL)isLinearPCM:(AudioStreamBasicDescription)asbd; 246 | 247 | ///----------------------------------------------------------- 248 | /// @name AudioStreamBasicDescription Utilities 249 | ///----------------------------------------------------------- 250 | 251 | /** 252 | Nicely logs out the contents of an AudioStreamBasicDescription struct 253 | @param asbd The AudioStreamBasicDescription struct with content to print out 254 | */ 255 | + (void)printASBD:(AudioStreamBasicDescription)asbd; 256 | 257 | //------------------------------------------------------------------------------ 258 | 259 | /** 260 | Converts seconds into a string formatted as MM:SS 261 | @param seconds An NSTimeInterval representing the number of seconds 262 | @return An NSString instance formatted as MM:SS from the seconds provided. 263 | */ 264 | + (NSString *)displayTimeStringFromSeconds:(NSTimeInterval)seconds; 265 | 266 | //------------------------------------------------------------------------------ 267 | 268 | /** 269 | Creates a string to use when logging out the contents of an AudioStreamBasicDescription 270 | @param asbd A valid AudioStreamBasicDescription struct. 271 | @return An NSString representing the contents of the AudioStreamBasicDescription. 272 | */ 273 | + (NSString *)stringForAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd; 274 | 275 | //------------------------------------------------------------------------------ 276 | 277 | /** 278 | Just a wrapper around the setCanonical function provided in the Core Audio Utility C++ class. 279 | @param asbd The AudioStreamBasicDescription structure to modify 280 | @param nChannels The number of expected channels on the description 281 | @param interleaved A flag indicating whether the stereo samples should be interleaved in the buffer 282 | */ 283 | + (void)setCanonicalAudioStreamBasicDescription:(AudioStreamBasicDescription*)asbd 284 | numberOfChannels:(UInt32)nChannels 285 | interleaved:(BOOL)interleaved; 286 | 287 | //------------------------------------------------------------------------------ 288 | #pragma mark - Math Utilities 289 | //------------------------------------------------------------------------------ 290 | 291 | ///----------------------------------------------------------- 292 | /// @name Math Utilities 293 | ///----------------------------------------------------------- 294 | 295 | /** 296 | Appends an array of values to a history buffer and performs an internal shift to add the values to the tail and removes the same number of values from the head. 297 | @param buffer A float array of values to append to the tail of the history buffer 298 | @param bufferLength The length of the float array being appended to the history buffer 299 | @param scrollHistory The target history buffer in which to append the values 300 | @param scrollHistoryLength The length of the target history buffer 301 | */ 302 | + (void)appendBufferAndShift:(float*)buffer 303 | withBufferSize:(int)bufferLength 304 | toScrollHistory:(float*)scrollHistory 305 | withScrollHistorySize:(int)scrollHistoryLength; 306 | 307 | //------------------------------------------------------------------------------ 308 | 309 | /** 310 | Appends a value to a history buffer and performs an internal shift to add the value to the tail and remove the 0th value. 311 | @param value The float value to append to the history array 312 | @param scrollHistory The target history buffer in which to append the values 313 | @param scrollHistoryLength The length of the target history buffer 314 | */ 315 | +(void) appendValue:(float)value 316 | toScrollHistory:(float*)scrollHistory 317 | withScrollHistorySize:(int)scrollHistoryLength; 318 | 319 | //------------------------------------------------------------------------------ 320 | 321 | /** 322 | Maps a value from one coordinate system into another one. Takes in the current value to map, the minimum and maximum values of the first coordinate system, and the minimum and maximum values of the second coordinate system and calculates the mapped value in the second coordinate system's constraints. 323 | @param value The value expressed in the first coordinate system 324 | @param leftMin The minimum of the first coordinate system 325 | @param leftMax The maximum of the first coordinate system 326 | @param rightMin The minimum of the second coordindate system 327 | @param rightMax The maximum of the second coordinate system 328 | @return The mapped value in terms of the second coordinate system 329 | */ 330 | + (float)MAP:(float)value 331 | leftMin:(float)leftMin 332 | leftMax:(float)leftMax 333 | rightMin:(float)rightMin 334 | rightMax:(float)rightMax; 335 | 336 | //------------------------------------------------------------------------------ 337 | 338 | /** 339 | Calculates the root mean squared for a buffer. 340 | @param buffer A float buffer array of values whose root mean squared to calculate 341 | @param bufferSize The size of the float buffer 342 | @return The root mean squared of the buffer 343 | */ 344 | + (float)RMS:(float*)buffer length:(int)bufferSize; 345 | 346 | //------------------------------------------------------------------------------ 347 | 348 | /** 349 | Calculate the sign function sgn(x) = 350 | { -1 , x < 0, 351 | { 0 , x = 0, 352 | { 1 , x > 0 353 | @param value The float value for which to use as x 354 | @return The float sign value 355 | */ 356 | + (float)SGN:(float)value; 357 | 358 | //------------------------------------------------------------------------------ 359 | #pragma mark - Music Utilities 360 | //------------------------------------------------------------------------------ 361 | 362 | + (NSString *)noteNameStringForFrequency:(float)frequency 363 | includeOctave:(BOOL)includeOctave; 364 | 365 | //------------------------------------------------------------------------------ 366 | #pragma mark - OSStatus Utility 367 | //------------------------------------------------------------------------------ 368 | 369 | ///----------------------------------------------------------- 370 | /// @name OSStatus Utility 371 | ///----------------------------------------------------------- 372 | 373 | /** 374 | Basic check result function useful for checking each step of the audio setup process 375 | @param result The OSStatus representing the result of an operation 376 | @param operation A string (const char, not NSString) describing the operation taking place (will print if fails) 377 | */ 378 | + (void)checkResult:(OSStatus)result operation:(const char *)operation; 379 | 380 | //------------------------------------------------------------------------------ 381 | 382 | /** 383 | Provides a string representation of the often cryptic Core Audio error codes 384 | @param code A UInt32 representing an error code 385 | @return An NSString with a human readable version of the error code. 386 | */ 387 | + (NSString *)stringFromUInt32Code:(UInt32)code; 388 | 389 | //------------------------------------------------------------------------------ 390 | #pragma mark - Color Utility 391 | //------------------------------------------------------------------------------ 392 | 393 | ///----------------------------------------------------------- 394 | /// @name Color Utility 395 | ///----------------------------------------------------------- 396 | 397 | /** 398 | Helper function to get the color components from a CGColorRef in the RGBA colorspace. 399 | @param color A CGColorRef that represents a color. 400 | @param red A pointer to a CGFloat to hold the value of the red component. This value will be between 0 and 1. 401 | @param green A pointer to a CGFloat to hold the value of the green component. This value will be between 0 and 1. 402 | @param blue A pointer to a CGFloat to hold the value of the blue component. This value will be between 0 and 1. 403 | @param alpha A pointer to a CGFloat to hold the value of the alpha component. This value will be between 0 and 1. 404 | */ 405 | + (void)getColorComponentsFromCGColor:(CGColorRef)color 406 | red:(CGFloat *)red 407 | green:(CGFloat *)green 408 | blue:(CGFloat *)blue 409 | alpha:(CGFloat *)alpha; 410 | 411 | //------------------------------------------------------------------------------ 412 | #pragma mark - Plot Utility 413 | //------------------------------------------------------------------------------ 414 | 415 | ///----------------------------------------------------------- 416 | /// @name Plot Utility 417 | ///----------------------------------------------------------- 418 | 419 | /** 420 | Given a buffer representing a window of float history data this append the RMS of a buffer of incoming float data...This will likely be deprecated in a future version of KSYAudio for a circular buffer based approach. 421 | @param scrollHistory An array of float arrays being used to hold the history values for each channel. 422 | @param scrollHistoryLength An int representing the length of the history window. 423 | @param index An int pointer to the index of the current read index of the history buffer. 424 | @param buffer A float array representing the incoming audio data. 425 | @param bufferSize An int representing the length of the incoming audio data. 426 | @param isChanging A BOOL pointer representing whether the resolution (length of the history window) is currently changing. 427 | */ 428 | + (void)updateScrollHistory:(float **)scrollHistory 429 | withLength:(int)scrollHistoryLength 430 | atIndex:(int *)index 431 | withBuffer:(float *)buffer 432 | withBufferSize:(int)bufferSize 433 | isResolutionChanging:(BOOL *)isChanging; 434 | 435 | //------------------------------------------------------------------------------ 436 | #pragma mark - KSYPlotCircularBuffer Utility 437 | //------------------------------------------------------------------------------ 438 | 439 | ///----------------------------------------------------------- 440 | /// @name KSYPlotCircularBuffer Utility 441 | ///----------------------------------------------------------- 442 | 443 | /** 444 | Appends the data from the audio buffer list to the circular buffer 445 | @param circularBuffer Pointer to the instance of the KSYPlotCircularBuffer to add the audio data to 446 | @param audioBufferList Pointer to the instance of the AudioBufferList with the audio data 447 | */ 448 | + (void)appendDataToCircularBuffer:(KSYPlotCircularBuffer*)circularBuffer 449 | fromAudioBufferList:(AudioBufferList*)audioBufferList; 450 | 451 | //------------------------------------------------------------------------------ 452 | 453 | /** 454 | Initializes the circular buffer (just a wrapper around the C method) 455 | @param circularBuffer Pointer to an instance of the KSYPlotCircularBuffer 456 | @param size The length of the KSYPlotCircularBuffer (usually 1024) 457 | */ 458 | + (void)circularBuffer:(KSYPlotCircularBuffer*)circularBuffer 459 | withSize:(int)size; 460 | 461 | //------------------------------------------------------------------------------ 462 | 463 | /** 464 | Frees a circular buffer 465 | @param circularBuffer Pointer to the circular buffer to clear 466 | */ 467 | + (void)freeCircularBuffer:(KSYPlotCircularBuffer*)circularBuffer; 468 | 469 | //------------------------------------------------------------------------------ 470 | #pragma mark - KSYPlotHistoryInfo Utility 471 | //------------------------------------------------------------------------------ 472 | 473 | /** 474 | Calculates the RMS of a float array containing audio data and appends it to the tail of a KSYPlotHistoryInfo data structure. Thread-safe. 475 | @param buffer A float array containing the incoming audio buffer to append to the history buffer 476 | @param bufferSize A UInt32 representing the length of the incoming audio buffer 477 | @param historyInfo A pointer to a KSYPlotHistoryInfo structure to use for managing the history buffers 478 | */ 479 | + (void)appendBufferRMS:(float *)buffer 480 | withBufferSize:(UInt32)bufferSize 481 | toHistoryInfo:(KSYPlotHistoryInfo *)historyInfo; 482 | 483 | //------------------------------------------------------------------------------ 484 | 485 | /** 486 | Appends a buffer of audio data to the tail of a KSYPlotHistoryInfo data structure. Thread-safe. 487 | @param buffer A float array containing the incoming audio buffer to append to the history buffer 488 | @param bufferSize A UInt32 representing the length of the incoming audio buffer 489 | @param historyInfo A pointer to a KSYPlotHistoryInfo structure to use for managing the history buffers 490 | */ 491 | + (void)appendBuffer:(float *)buffer 492 | withBufferSize:(UInt32)bufferSize 493 | toHistoryInfo:(KSYPlotHistoryInfo *)historyInfo; 494 | 495 | //------------------------------------------------------------------------------ 496 | 497 | /** 498 | Zeroes out a KSYPlotHistoryInfo data structure without freeing the resources. 499 | @param historyInfo A pointer to a KSYPlotHistoryInfo data structure 500 | */ 501 | + (void)clearHistoryInfo:(KSYPlotHistoryInfo *)historyInfo; 502 | 503 | //------------------------------------------------------------------------------ 504 | 505 | /** 506 | Frees a KSYPlotHistoryInfo data structure 507 | @param historyInfo A pointer to a KSYPlotHistoryInfo data structure 508 | */ 509 | + (void)freeHistoryInfo:(KSYPlotHistoryInfo *)historyInfo; 510 | 511 | //------------------------------------------------------------------------------ 512 | 513 | /** 514 | Creates an KSYPlotHistoryInfo data structure with a default length for the window buffer and a maximum length capacity for the internal circular buffer that holds all the audio data. 515 | @param defaultLength An int representing the default length (i.e. the number of points that will be displayed on screen) of the history window. 516 | @param maximumLength An int representing the default maximum length that is the absolute maximum amount of values that can be held in the history's circular buffer. 517 | @return A pointer to the KSYPlotHistoryInfo created. The caller is responsible for freeing this structure using the `freeHistoryInfo` method above. 518 | */ 519 | + (KSYPlotHistoryInfo *)historyInfoWithDefaultLength:(int)defaultLength 520 | maximumLength:(int)maximumLength; 521 | 522 | //------------------------------------------------------------------------------ 523 | 524 | @end 525 | -------------------------------------------------------------------------------- /KSYAudioPlotView/KSYPlotCircularBuffer.c: -------------------------------------------------------------------------------- 1 | // 2 | // KSYPlotCircularBuffer.c 3 | // EditDemo 4 | // 5 | // Created by sunyazhou on 2017/6/16. 6 | // Copyright © 2017年 Kingsoft, Inc. All rights reserved. 7 | // 8 | 9 | #include "KSYPlotCircularBuffer.h" 10 | #include 11 | #include 12 | #include 13 | 14 | #define reportResult(result,operation) (_reportResult((result),(operation),strrchr(__FILE__, '/')+1,__LINE__)) 15 | static inline bool _reportResult(kern_return_t result, const char *operation, const char* file, int line) { 16 | if ( result != ERR_SUCCESS ) { 17 | printf("%s:%d: %s: %s\n", file, line, operation, mach_error_string(result)); 18 | return false; 19 | } 20 | return true; 21 | } 22 | 23 | bool _KSYPlotCircularBufferInit(KSYPlotCircularBuffer *buffer, int32_t length, size_t structSize) { 24 | 25 | assert(length > 0); 26 | 27 | if ( structSize != sizeof(KSYPlotCircularBuffer) ) { 28 | fprintf(stderr, "KSYPlotCircularBuffer: Header version mismatch. Check for old versions of KSYPlotCircularBuffer in your project\n"); 29 | abort(); 30 | } 31 | 32 | // Keep trying until we get our buffer, needed to handle race conditions 33 | int retries = 3; 34 | while ( true ) { 35 | 36 | buffer->length = (int32_t)round_page(length); // We need whole page sizes 37 | 38 | // Temporarily allocate twice the length, so we have the contiguous address space to 39 | // support a second instance of the buffer directly after 40 | vm_address_t bufferAddress; 41 | kern_return_t result = vm_allocate(mach_task_self(), 42 | &bufferAddress, 43 | buffer->length * 2, 44 | VM_FLAGS_ANYWHERE); // allocate anywhere it'll fit 45 | if ( result != ERR_SUCCESS ) { 46 | if ( retries-- == 0 ) { 47 | reportResult(result, "Buffer allocation"); 48 | return false; 49 | } 50 | // Try again if we fail 51 | continue; 52 | } 53 | 54 | // Now replace the second half of the allocation with a virtual copy of the first half. Deallocate the second half... 55 | result = vm_deallocate(mach_task_self(), 56 | bufferAddress + buffer->length, 57 | buffer->length); 58 | if ( result != ERR_SUCCESS ) { 59 | if ( retries-- == 0 ) { 60 | reportResult(result, "Buffer deallocation"); 61 | return false; 62 | } 63 | // If this fails somehow, deallocate the whole region and try again 64 | vm_deallocate(mach_task_self(), bufferAddress, buffer->length); 65 | continue; 66 | } 67 | 68 | // Re-map the buffer to the address space immediately after the buffer 69 | vm_address_t virtualAddress = bufferAddress + buffer->length; 70 | vm_prot_t cur_prot, max_prot; 71 | result = vm_remap(mach_task_self(), 72 | &virtualAddress, // mirror target 73 | buffer->length, // size of mirror 74 | 0, // auto alignment 75 | 0, // force remapping to virtualAddress 76 | mach_task_self(), // same task 77 | bufferAddress, // mirror source 78 | 0, // MAP READ-WRITE, NOT COPY 79 | &cur_prot, // unused protection struct 80 | &max_prot, // unused protection struct 81 | VM_INHERIT_DEFAULT); 82 | if ( result != ERR_SUCCESS ) { 83 | if ( retries-- == 0 ) { 84 | reportResult(result, "Remap buffer memory"); 85 | return false; 86 | } 87 | // If this remap failed, we hit a race condition, so deallocate and try again 88 | vm_deallocate(mach_task_self(), bufferAddress, buffer->length); 89 | continue; 90 | } 91 | 92 | if ( virtualAddress != bufferAddress+buffer->length ) { 93 | // If the memory is not contiguous, clean up both allocated buffers and try again 94 | if ( retries-- == 0 ) { 95 | printf("Couldn't map buffer memory to end of buffer\n"); 96 | return false; 97 | } 98 | 99 | vm_deallocate(mach_task_self(), virtualAddress, buffer->length); 100 | vm_deallocate(mach_task_self(), bufferAddress, buffer->length); 101 | continue; 102 | } 103 | 104 | buffer->buffer = (void*)bufferAddress; 105 | buffer->fillCount = 0; 106 | buffer->head = buffer->tail = 0; 107 | buffer->atomic = true; 108 | 109 | return true; 110 | } 111 | return false; 112 | } 113 | 114 | void KSYPlotCircularBufferCleanup(KSYPlotCircularBuffer *buffer) { 115 | vm_deallocate(mach_task_self(), (vm_address_t)buffer->buffer, buffer->length * 2); 116 | memset(buffer, 0, sizeof(KSYPlotCircularBuffer)); 117 | } 118 | 119 | void KSYPlotCircularBufferClear(KSYPlotCircularBuffer *buffer) { 120 | int32_t fillCount; 121 | if ( KSYPlotCircularBufferTail(buffer, &fillCount) ) { 122 | KSYPlotCircularBufferConsume(buffer, fillCount); 123 | } 124 | } 125 | 126 | void KSYPlotCircularBufferSetAtomic(KSYPlotCircularBuffer *buffer, bool atomic) { 127 | buffer->atomic = atomic; 128 | } 129 | -------------------------------------------------------------------------------- /KSYAudioPlotView/KSYPlotCircularBuffer.h: -------------------------------------------------------------------------------- 1 | // 2 | // KSYPlotCircularBuffer.h 3 | // EditDemo 4 | // 5 | // Created by sunyazhou on 2017/6/16. 6 | // Copyright © 2017年 Kingsoft, Inc. All rights reserved. 7 | // 8 | 9 | #ifndef KSYPlotCircularBuffer_h 10 | #define KSYPlotCircularBuffer_h 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | typedef struct { 21 | void *buffer; 22 | int32_t length; 23 | int32_t tail; 24 | int32_t head; 25 | volatile int32_t fillCount; 26 | bool atomic; 27 | } KSYPlotCircularBuffer; 28 | 29 | /*! 30 | * Initialise buffer 31 | * 32 | * Note that the length is advisory only: Because of the way the 33 | * memory mirroring technique works, the true buffer length will 34 | * be multiples of the device page size (e.g. 4096 bytes) 35 | * 36 | * @param buffer Circular buffer 37 | * @param length Length of buffer 38 | */ 39 | #define KSYPlotCircularBufferInit(buffer, length) \ 40 | _KSYPlotCircularBufferInit(buffer, length, sizeof(*buffer)) 41 | bool _KSYPlotCircularBufferInit(KSYPlotCircularBuffer *buffer, int32_t length, size_t structSize); 42 | 43 | /*! 44 | * Cleanup buffer 45 | * 46 | * Releases buffer resources. 47 | */ 48 | void KSYPlotCircularBufferCleanup(KSYPlotCircularBuffer *buffer); 49 | 50 | /*! 51 | * Clear buffer 52 | * 53 | * Resets buffer to original, empty state. 54 | * 55 | * This is safe for use by consumer while producer is accessing 56 | * buffer. 57 | */ 58 | void KSYPlotCircularBufferClear(KSYPlotCircularBuffer *buffer); 59 | 60 | /*! 61 | * Set the atomicity 62 | * 63 | * If you set the atomiticy to false using this method, the buffer will 64 | * not use atomic operations. This can be used to give the compiler a little 65 | * more optimisation opportunities when the buffer is only used on one thread. 66 | * 67 | * Important note: Only set this to false if you know what you're doing! 68 | * 69 | * The default value is true (the buffer will use atomic operations) 70 | * 71 | * @param buffer Circular buffer 72 | * @param atomic Whether the buffer is atomic (default true) 73 | */ 74 | void KSYPlotCircularBufferSetAtomic(KSYPlotCircularBuffer *buffer, bool atomic); 75 | 76 | // Reading (consuming) 77 | 78 | /*! 79 | * Access end of buffer 80 | * 81 | * This gives you a pointer to the end of the buffer, ready 82 | * for reading, and the number of available bytes to read. 83 | * 84 | * @param buffer Circular buffer 85 | * @param availableBytes On ouKSYPlotut, the number of bytes ready for reading 86 | * @return Pointer to the first bytes ready for reading, or NULL if buffer is empty 87 | */ 88 | static __inline__ __attribute__((always_inline)) void* KSYPlotCircularBufferTail(KSYPlotCircularBuffer *buffer, int32_t* availableBytes) { 89 | *availableBytes = buffer->fillCount; 90 | if ( *availableBytes == 0 ) return NULL; 91 | return (void*)((char*)buffer->buffer + buffer->tail); 92 | } 93 | 94 | /*! 95 | * Consume bytes in buffer 96 | * 97 | * This frees up the just-read bytes, ready for writing again. 98 | * 99 | * @param buffer Circular buffer 100 | * @param amount Number of bytes to consume 101 | */ 102 | static __inline__ __attribute__((always_inline)) void KSYPlotCircularBufferConsume(KSYPlotCircularBuffer *buffer, int32_t amount) { 103 | buffer->tail = (buffer->tail + amount) % buffer->length; 104 | if ( buffer->atomic ) { 105 | OSAtomicAdd32Barrier(-amount, &buffer->fillCount); 106 | } else { 107 | buffer->fillCount -= amount; 108 | } 109 | assert(buffer->fillCount >= 0); 110 | } 111 | 112 | /*! 113 | * Access front of buffer 114 | * 115 | * This gives you a pointer to the front of the buffer, ready 116 | * for writing, and the number of available bytes to write. 117 | * 118 | * @param buffer Circular buffer 119 | * @param availableBytes On ouKSYPlotut, the number of bytes ready for writing 120 | * @return Pointer to the first bytes ready for writing, or NULL if buffer is full 121 | */ 122 | static __inline__ __attribute__((always_inline)) void* KSYPlotCircularBufferHead(KSYPlotCircularBuffer *buffer, int32_t* availableBytes) { 123 | *availableBytes = (buffer->length - buffer->fillCount); 124 | if ( *availableBytes == 0 ) return NULL; 125 | return (void*)((char*)buffer->buffer + buffer->head); 126 | } 127 | 128 | // Writing (producing) 129 | 130 | /*! 131 | * Produce bytes in buffer 132 | * 133 | * This marks the given section of the buffer ready for reading. 134 | * 135 | * @param buffer Circular buffer 136 | * @param amount Number of bytes to produce 137 | */ 138 | static __inline__ __attribute__((always_inline)) void KSYPlotCircularBufferProduce(KSYPlotCircularBuffer *buffer, int32_t amount) { 139 | buffer->head = (buffer->head + amount) % buffer->length; 140 | if ( buffer->atomic ) { 141 | OSAtomicAdd32Barrier(amount, &buffer->fillCount); 142 | } else { 143 | buffer->fillCount += amount; 144 | } 145 | assert(buffer->fillCount <= buffer->length); 146 | } 147 | 148 | /*! 149 | * Helper routine to copy bytes to buffer 150 | * 151 | * This copies the given bytes to the buffer, and marks them ready for reading. 152 | * 153 | * @param buffer Circular buffer 154 | * @param src Source buffer 155 | * @param len Number of bytes in source buffer 156 | * @return true if bytes copied, false if there was insufficient space 157 | */ 158 | static __inline__ __attribute__((always_inline)) bool KSYPlotCircularBufferProduceBytes(KSYPlotCircularBuffer *buffer, const void* src, int32_t len) { 159 | int32_t space; 160 | void *ptr = KSYPlotCircularBufferHead(buffer, &space); 161 | if ( space < len ) return false; 162 | memcpy(ptr, src, len); 163 | KSYPlotCircularBufferProduce(buffer, len); 164 | return true; 165 | } 166 | 167 | /*! 168 | * Deprecated method 169 | */ 170 | static __inline__ __attribute__((always_inline)) __deprecated_msg("use KSYPlotCircularBufferSetAtomic(false) and KSYPlotCircularBufferConsume instead") 171 | void KSYPlotCircularBufferConsumeNoBarrier(KSYPlotCircularBuffer *buffer, int32_t amount) { 172 | buffer->tail = (buffer->tail + amount) % buffer->length; 173 | buffer->fillCount -= amount; 174 | assert(buffer->fillCount >= 0); 175 | } 176 | 177 | /*! 178 | * Deprecated method 179 | */ 180 | static __inline__ __attribute__((always_inline)) __deprecated_msg("use KSYPlotCircularBufferSetAtomic(false) and KSYPlotCircularBufferProduce instead") 181 | void KSYPlotCircularBufferProduceNoBarrier(KSYPlotCircularBuffer *buffer, int32_t amount) { 182 | buffer->head = (buffer->head + amount) % buffer->length; 183 | buffer->fillCount += amount; 184 | assert(buffer->fillCount <= buffer->length); 185 | } 186 | 187 | #ifdef __cplusplus 188 | } 189 | #endif 190 | 191 | 192 | #endif /* KSYPlotCircularBuffer_h */ 193 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | platform :ios, '8.0' 3 | 4 | target 'AudioPlotDemo' do 5 | # Uncomment the next line if you're using Swift or would like to use dynamic frameworks 6 | # use_frameworks! 7 | 8 | # Pods for AudioPlotDemo 9 | 10 | end 11 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODFILE CHECKSUM: 24f9dc7e7b332561ac0a69779303f7345632007d 2 | 3 | COCOAPODS: 1.2.1 4 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODFILE CHECKSUM: 24f9dc7e7b332561ac0a69779303f7345632007d 2 | 3 | COCOAPODS: 1.2.1 4 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3A2DB08C137AF1A7C3A47CBDD12F5B8F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6604A7D69453B4569E4E4827FB9155A9 /* Foundation.framework */; }; 11 | 536ECA0A903C2678059B0DD9576FA0A4 /* Pods-AudioPlotDemo-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 819E718B2303B202207F32BE5DC16DFB /* Pods-AudioPlotDemo-dummy.m */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXFileReference section */ 15 | 085309B3AA33C9F97340443B8B0212D6 /* Pods-AudioPlotDemo-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-AudioPlotDemo-resources.sh"; sourceTree = ""; }; 16 | 1ED6E04A42D12150B6EF8397DE5B169C /* Pods-AudioPlotDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-AudioPlotDemo.release.xcconfig"; sourceTree = ""; }; 17 | 37D2A350EC94D1EF9A7AC72AFD4A9947 /* Pods-AudioPlotDemo-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-AudioPlotDemo-acknowledgements.markdown"; sourceTree = ""; }; 18 | 44DB1B5213EA86485FC833560335E181 /* Pods-AudioPlotDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-AudioPlotDemo.debug.xcconfig"; sourceTree = ""; }; 19 | 6604A7D69453B4569E4E4827FB9155A9 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 20 | 819E718B2303B202207F32BE5DC16DFB /* Pods-AudioPlotDemo-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-AudioPlotDemo-dummy.m"; sourceTree = ""; }; 21 | 923AEEA0B7B9BCA555839A885A8D996C /* libPods-AudioPlotDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libPods-AudioPlotDemo.a"; path = "libPods-AudioPlotDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 23 | BB7651049AF11CFB2F4F444150F41A61 /* Pods-AudioPlotDemo-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-AudioPlotDemo-acknowledgements.plist"; sourceTree = ""; }; 24 | EEFB9203893B95414978B559C629C3D0 /* Pods-AudioPlotDemo-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-AudioPlotDemo-frameworks.sh"; sourceTree = ""; }; 25 | /* End PBXFileReference section */ 26 | 27 | /* Begin PBXFrameworksBuildPhase section */ 28 | D1B93715EB628F747B26143E6EA2290C /* Frameworks */ = { 29 | isa = PBXFrameworksBuildPhase; 30 | buildActionMask = 2147483647; 31 | files = ( 32 | 3A2DB08C137AF1A7C3A47CBDD12F5B8F /* Foundation.framework in Frameworks */, 33 | ); 34 | runOnlyForDeploymentPostprocessing = 0; 35 | }; 36 | /* End PBXFrameworksBuildPhase section */ 37 | 38 | /* Begin PBXGroup section */ 39 | 333D75A431F1758FB51861FBDAE16B34 /* Targets Support Files */ = { 40 | isa = PBXGroup; 41 | children = ( 42 | 6C0A33EFA56935DFD5A0907602535610 /* Pods-AudioPlotDemo */, 43 | ); 44 | name = "Targets Support Files"; 45 | sourceTree = ""; 46 | }; 47 | 6C0A33EFA56935DFD5A0907602535610 /* Pods-AudioPlotDemo */ = { 48 | isa = PBXGroup; 49 | children = ( 50 | 37D2A350EC94D1EF9A7AC72AFD4A9947 /* Pods-AudioPlotDemo-acknowledgements.markdown */, 51 | BB7651049AF11CFB2F4F444150F41A61 /* Pods-AudioPlotDemo-acknowledgements.plist */, 52 | 819E718B2303B202207F32BE5DC16DFB /* Pods-AudioPlotDemo-dummy.m */, 53 | EEFB9203893B95414978B559C629C3D0 /* Pods-AudioPlotDemo-frameworks.sh */, 54 | 085309B3AA33C9F97340443B8B0212D6 /* Pods-AudioPlotDemo-resources.sh */, 55 | 44DB1B5213EA86485FC833560335E181 /* Pods-AudioPlotDemo.debug.xcconfig */, 56 | 1ED6E04A42D12150B6EF8397DE5B169C /* Pods-AudioPlotDemo.release.xcconfig */, 57 | ); 58 | name = "Pods-AudioPlotDemo"; 59 | path = "Target Support Files/Pods-AudioPlotDemo"; 60 | sourceTree = ""; 61 | }; 62 | 7DB346D0F39D3F0E887471402A8071AB = { 63 | isa = PBXGroup; 64 | children = ( 65 | 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */, 66 | BC3CA7F9E30CC8F7E2DD044DD34432FC /* Frameworks */, 67 | 8B651B5C1A08F6D872D126CCE566C61C /* Products */, 68 | 333D75A431F1758FB51861FBDAE16B34 /* Targets Support Files */, 69 | ); 70 | sourceTree = ""; 71 | }; 72 | 8B651B5C1A08F6D872D126CCE566C61C /* Products */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 923AEEA0B7B9BCA555839A885A8D996C /* libPods-AudioPlotDemo.a */, 76 | ); 77 | name = Products; 78 | sourceTree = ""; 79 | }; 80 | BC3CA7F9E30CC8F7E2DD044DD34432FC /* Frameworks */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | D35AF013A5F0BAD4F32504907A52519E /* iOS */, 84 | ); 85 | name = Frameworks; 86 | sourceTree = ""; 87 | }; 88 | D35AF013A5F0BAD4F32504907A52519E /* iOS */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 6604A7D69453B4569E4E4827FB9155A9 /* Foundation.framework */, 92 | ); 93 | name = iOS; 94 | sourceTree = ""; 95 | }; 96 | /* End PBXGroup section */ 97 | 98 | /* Begin PBXNativeTarget section */ 99 | AFBBD8FFC09037490FD9EAD5472B6485 /* Pods-AudioPlotDemo */ = { 100 | isa = PBXNativeTarget; 101 | buildConfigurationList = 55BA5ED55B7BAA6B57DFB0A73A300248 /* Build configuration list for PBXNativeTarget "Pods-AudioPlotDemo" */; 102 | buildPhases = ( 103 | 391C9C4F07EA5001089080C3CDDB0B66 /* Sources */, 104 | D1B93715EB628F747B26143E6EA2290C /* Frameworks */, 105 | ); 106 | buildRules = ( 107 | ); 108 | dependencies = ( 109 | ); 110 | name = "Pods-AudioPlotDemo"; 111 | productName = "Pods-AudioPlotDemo"; 112 | productReference = 923AEEA0B7B9BCA555839A885A8D996C /* libPods-AudioPlotDemo.a */; 113 | productType = "com.apple.product-type.library.static"; 114 | }; 115 | /* End PBXNativeTarget section */ 116 | 117 | /* Begin PBXProject section */ 118 | D41D8CD98F00B204E9800998ECF8427E /* Project object */ = { 119 | isa = PBXProject; 120 | attributes = { 121 | LastSwiftUpdateCheck = 0830; 122 | LastUpgradeCheck = 0700; 123 | }; 124 | buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */; 125 | compatibilityVersion = "Xcode 3.2"; 126 | developmentRegion = English; 127 | hasScannedForEncodings = 0; 128 | knownRegions = ( 129 | en, 130 | ); 131 | mainGroup = 7DB346D0F39D3F0E887471402A8071AB; 132 | productRefGroup = 8B651B5C1A08F6D872D126CCE566C61C /* Products */; 133 | projectDirPath = ""; 134 | projectRoot = ""; 135 | targets = ( 136 | AFBBD8FFC09037490FD9EAD5472B6485 /* Pods-AudioPlotDemo */, 137 | ); 138 | }; 139 | /* End PBXProject section */ 140 | 141 | /* Begin PBXSourcesBuildPhase section */ 142 | 391C9C4F07EA5001089080C3CDDB0B66 /* Sources */ = { 143 | isa = PBXSourcesBuildPhase; 144 | buildActionMask = 2147483647; 145 | files = ( 146 | 536ECA0A903C2678059B0DD9576FA0A4 /* Pods-AudioPlotDemo-dummy.m in Sources */, 147 | ); 148 | runOnlyForDeploymentPostprocessing = 0; 149 | }; 150 | /* End PBXSourcesBuildPhase section */ 151 | 152 | /* Begin XCBuildConfiguration section */ 153 | 12987D06BDAC955421495A30B0BFB797 /* Debug */ = { 154 | isa = XCBuildConfiguration; 155 | baseConfigurationReference = 44DB1B5213EA86485FC833560335E181 /* Pods-AudioPlotDemo.debug.xcconfig */; 156 | buildSettings = { 157 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 158 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 159 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 160 | DEBUG_INFORMATION_FORMAT = dwarf; 161 | ENABLE_STRICT_OBJC_MSGSEND = YES; 162 | GCC_NO_COMMON_BLOCKS = YES; 163 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 164 | MACH_O_TYPE = staticlib; 165 | MTL_ENABLE_DEBUG_INFO = YES; 166 | OTHER_LDFLAGS = ""; 167 | OTHER_LIBTOOLFLAGS = ""; 168 | PODS_ROOT = "$(SRCROOT)"; 169 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 170 | PRODUCT_NAME = "$(TARGET_NAME)"; 171 | SDKROOT = iphoneos; 172 | SKIP_INSTALL = YES; 173 | }; 174 | name = Debug; 175 | }; 176 | 17EB2009B7CA99A2FD82755E1F9F35DF /* Release */ = { 177 | isa = XCBuildConfiguration; 178 | baseConfigurationReference = 1ED6E04A42D12150B6EF8397DE5B169C /* Pods-AudioPlotDemo.release.xcconfig */; 179 | buildSettings = { 180 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 181 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 182 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 183 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 184 | ENABLE_STRICT_OBJC_MSGSEND = YES; 185 | GCC_NO_COMMON_BLOCKS = YES; 186 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 187 | MACH_O_TYPE = staticlib; 188 | MTL_ENABLE_DEBUG_INFO = NO; 189 | OTHER_LDFLAGS = ""; 190 | OTHER_LIBTOOLFLAGS = ""; 191 | PODS_ROOT = "$(SRCROOT)"; 192 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 193 | PRODUCT_NAME = "$(TARGET_NAME)"; 194 | SDKROOT = iphoneos; 195 | SKIP_INSTALL = YES; 196 | }; 197 | name = Release; 198 | }; 199 | 34FE9531DA9AF2820790339988D5FF41 /* Release */ = { 200 | isa = XCBuildConfiguration; 201 | buildSettings = { 202 | ALWAYS_SEARCH_USER_PATHS = NO; 203 | CLANG_ANALYZER_NONNULL = YES; 204 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES; 205 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 206 | CLANG_CXX_LIBRARY = "libc++"; 207 | CLANG_ENABLE_MODULES = YES; 208 | CLANG_ENABLE_OBJC_ARC = YES; 209 | CLANG_WARN_BOOL_CONVERSION = YES; 210 | CLANG_WARN_CONSTANT_CONVERSION = YES; 211 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; 212 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 213 | CLANG_WARN_EMPTY_BODY = YES; 214 | CLANG_WARN_ENUM_CONVERSION = YES; 215 | CLANG_WARN_INFINITE_RECURSION = YES; 216 | CLANG_WARN_INT_CONVERSION = YES; 217 | CLANG_WARN_OBJC_ROOT_CLASS = YES; 218 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 219 | CLANG_WARN_UNREACHABLE_CODE = YES; 220 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 221 | CODE_SIGNING_REQUIRED = NO; 222 | COPY_PHASE_STRIP = YES; 223 | ENABLE_NS_ASSERTIONS = NO; 224 | GCC_C_LANGUAGE_STANDARD = gnu99; 225 | GCC_PREPROCESSOR_DEFINITIONS = ( 226 | "POD_CONFIGURATION_RELEASE=1", 227 | "$(inherited)", 228 | ); 229 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 230 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 231 | GCC_WARN_UNDECLARED_SELECTOR = YES; 232 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 233 | GCC_WARN_UNUSED_FUNCTION = YES; 234 | GCC_WARN_UNUSED_VARIABLE = YES; 235 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 236 | PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; 237 | STRIP_INSTALLED_PRODUCT = NO; 238 | SYMROOT = "${SRCROOT}/../build"; 239 | VALIDATE_PRODUCT = YES; 240 | }; 241 | name = Release; 242 | }; 243 | C104F7F091290C3D1E248192F07FE689 /* Debug */ = { 244 | isa = XCBuildConfiguration; 245 | buildSettings = { 246 | ALWAYS_SEARCH_USER_PATHS = NO; 247 | CLANG_ANALYZER_NONNULL = YES; 248 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES; 249 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 250 | CLANG_CXX_LIBRARY = "libc++"; 251 | CLANG_ENABLE_MODULES = YES; 252 | CLANG_ENABLE_OBJC_ARC = YES; 253 | CLANG_WARN_BOOL_CONVERSION = YES; 254 | CLANG_WARN_CONSTANT_CONVERSION = YES; 255 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; 256 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 257 | CLANG_WARN_EMPTY_BODY = YES; 258 | CLANG_WARN_ENUM_CONVERSION = YES; 259 | CLANG_WARN_INFINITE_RECURSION = YES; 260 | CLANG_WARN_INT_CONVERSION = YES; 261 | CLANG_WARN_OBJC_ROOT_CLASS = YES; 262 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 263 | CLANG_WARN_UNREACHABLE_CODE = YES; 264 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 265 | CODE_SIGNING_REQUIRED = NO; 266 | COPY_PHASE_STRIP = NO; 267 | ENABLE_TESTABILITY = YES; 268 | GCC_C_LANGUAGE_STANDARD = gnu99; 269 | GCC_DYNAMIC_NO_PIC = NO; 270 | GCC_OPTIMIZATION_LEVEL = 0; 271 | GCC_PREPROCESSOR_DEFINITIONS = ( 272 | "POD_CONFIGURATION_DEBUG=1", 273 | "DEBUG=1", 274 | "$(inherited)", 275 | ); 276 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 277 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 278 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 279 | GCC_WARN_UNDECLARED_SELECTOR = YES; 280 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 281 | GCC_WARN_UNUSED_FUNCTION = YES; 282 | GCC_WARN_UNUSED_VARIABLE = YES; 283 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 284 | ONLY_ACTIVE_ARCH = YES; 285 | PROVISIONING_PROFILE_SPECIFIER = NO_SIGNING/; 286 | STRIP_INSTALLED_PRODUCT = NO; 287 | SYMROOT = "${SRCROOT}/../build"; 288 | }; 289 | name = Debug; 290 | }; 291 | /* End XCBuildConfiguration section */ 292 | 293 | /* Begin XCConfigurationList section */ 294 | 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = { 295 | isa = XCConfigurationList; 296 | buildConfigurations = ( 297 | C104F7F091290C3D1E248192F07FE689 /* Debug */, 298 | 34FE9531DA9AF2820790339988D5FF41 /* Release */, 299 | ); 300 | defaultConfigurationIsVisible = 0; 301 | defaultConfigurationName = Release; 302 | }; 303 | 55BA5ED55B7BAA6B57DFB0A73A300248 /* Build configuration list for PBXNativeTarget "Pods-AudioPlotDemo" */ = { 304 | isa = XCConfigurationList; 305 | buildConfigurations = ( 306 | 12987D06BDAC955421495A30B0BFB797 /* Debug */, 307 | 17EB2009B7CA99A2FD82755E1F9F35DF /* Release */, 308 | ); 309 | defaultConfigurationIsVisible = 0; 310 | defaultConfigurationName = Release; 311 | }; 312 | /* End XCConfigurationList section */ 313 | }; 314 | rootObject = D41D8CD98F00B204E9800998ECF8427E /* Project object */; 315 | } 316 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/sunyazhou.xcuserdatad/xcschemes/Pods-AudioPlotDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/sunyazhou.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Pods-AudioPlotDemo.xcscheme 8 | 9 | isShown 10 | 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | AFBBD8FFC09037490FD9EAD5472B6485 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-AudioPlotDemo/Pods-AudioPlotDemo-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-AudioPlotDemo/Pods-AudioPlotDemo-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-AudioPlotDemo/Pods-AudioPlotDemo-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_AudioPlotDemo : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_AudioPlotDemo 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-AudioPlotDemo/Pods-AudioPlotDemo-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 63 | 64 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 65 | code_sign_cmd="$code_sign_cmd &" 66 | fi 67 | echo "$code_sign_cmd" 68 | eval "$code_sign_cmd" 69 | fi 70 | } 71 | 72 | # Strip invalid architectures 73 | strip_invalid_archs() { 74 | binary="$1" 75 | # Get architectures for current file 76 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 77 | stripped="" 78 | for arch in $archs; do 79 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 80 | # Strip non-valid architectures in-place 81 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 82 | stripped="$stripped $arch" 83 | fi 84 | done 85 | if [[ "$stripped" ]]; then 86 | echo "Stripped $binary of architectures:$stripped" 87 | fi 88 | } 89 | 90 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 91 | wait 92 | fi 93 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-AudioPlotDemo/Pods-AudioPlotDemo-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | 3) 22 | TARGET_DEVICE_ARGS="--target-device tv" 23 | ;; 24 | 4) 25 | TARGET_DEVICE_ARGS="--target-device watch" 26 | ;; 27 | *) 28 | TARGET_DEVICE_ARGS="--target-device mac" 29 | ;; 30 | esac 31 | 32 | install_resource() 33 | { 34 | if [[ "$1" = /* ]] ; then 35 | RESOURCE_PATH="$1" 36 | else 37 | RESOURCE_PATH="${PODS_ROOT}/$1" 38 | fi 39 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 40 | cat << EOM 41 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 42 | EOM 43 | exit 1 44 | fi 45 | case $RESOURCE_PATH in 46 | *.storyboard) 47 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 48 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 49 | ;; 50 | *.xib) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.framework) 55 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 56 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 57 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 58 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 59 | ;; 60 | *.xcdatamodel) 61 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 62 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 63 | ;; 64 | *.xcdatamodeld) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 67 | ;; 68 | *.xcmappingmodel) 69 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 70 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 71 | ;; 72 | *.xcassets) 73 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 74 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 75 | ;; 76 | *) 77 | echo "$RESOURCE_PATH" 78 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 79 | ;; 80 | esac 81 | } 82 | 83 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 85 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 86 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 87 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | fi 89 | rm -f "$RESOURCES_TO_COPY" 90 | 91 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 92 | then 93 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 94 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 95 | while read line; do 96 | if [[ $line != "${PODS_ROOT}*" ]]; then 97 | XCASSET_FILES+=("$line") 98 | fi 99 | done <<<"$OTHER_XCASSETS" 100 | 101 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 102 | fi 103 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-AudioPlotDemo/Pods-AudioPlotDemo.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" 3 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" 4 | OTHER_LDFLAGS = $(inherited) -ObjC 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-AudioPlotDemo/Pods-AudioPlotDemo.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" 3 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" 4 | OTHER_LDFLAGS = $(inherited) -ObjC 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AudioPlotView 2 | 3 |
Source Type: Open Source
4 | 5 | 6 | ## 音频波形图 7 | 8 | 这是金山云开发短视频时候用到解析本地音频文件的流信息形成视波图 9 | 10 | ![](https://raw.githubusercontent.com/ksvc/AudioPlotView/master/ksyAudioPlotView.gif) 11 | 12 | 13 | 14 | 安装 15 | 16 | ``` sh 17 | pod 'KSYAudioPlotView' 18 | ``` 19 | 20 | 21 | -------------------------------------------------------------------------------- /ksyAudioPlotView.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ksvc/AudioPlotView/e3ab33850c54be1373d234b5d4c1001ca7cf6db9/ksyAudioPlotView.gif --------------------------------------------------------------------------------