├── LICENSE ├── MetalAudioShaders.podspec ├── MetalAudioShaders.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── MetalAudioShaders ├── CocoaPodsBundledResourcePlaceholder ├── Conv1d │ ├── MASConv1dDescriptor.h │ ├── MASConv1dDescriptor.m │ ├── MASConv1dFactory.h │ ├── MASConv1dFactory.m │ ├── MASConv1dKernel.h │ ├── MASConv1dKernel.m │ └── conv1d.metal ├── Info.plist ├── MASBaseKernel.h ├── MASBaseKernel.m ├── MASCommandEncoderExtension.h ├── MASCommandEncoderExtension.m ├── MASShapedBuffer.h ├── MASShapedBuffer.m ├── MetalAudioShaders.h ├── Spectrogram │ ├── MASSpectrogramDescriptor.h │ ├── MASSpectrogramDescriptor.m │ ├── MASSpectrogramFactory.h │ ├── MASSpectrogramFactory.m │ ├── MASSpectrogramKernel.h │ ├── MASSpectrogramKernel.m │ └── spectrogram.metal └── half.h └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Oleksii Moiseenko 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /MetalAudioShaders.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'MetalAudioShaders' 3 | s.version = '0.1.3' 4 | s.summary = 'MAS' 5 | s.description = "MPS like shaders for audio processing. Spectrogram. Conv1d" 6 | 7 | s.homepage = 'https://github.com/techpro-studio/MetalAudioShaders' 8 | s.license = { :type => 'MIT', :file => 'LICENSE' } 9 | s.author = { 'Oleksii Moiseenko' => 'oleksiimoiseenko@gmail.com' } 10 | s.source = { :git => 'https://github.com/techpro-studio/MetalAudioShaders.git', :tag => s.version } 11 | s.requires_arc = true 12 | 13 | s.ios.deployment_target = '11.0' 14 | s.osx.deployment_target = '10.13' 15 | 16 | s.source_files = 'MetalAudioShaders/**/*.{h,m,metal}' 17 | s.exclude_files = "MetalAudioShaders/*.plist" 18 | 19 | s.ios.pod_target_xcconfig = { 'METAL_LIBRARY_OUTPUT_DIR' => '${TARGET_BUILD_DIR}/MetalAudioShaders.bundle/' } 20 | s.osx.pod_target_xcconfig = { 'METAL_LIBRARY_OUTPUT_DIR' => '${TARGET_BUILD_DIR}/MetalAudioShaders.bundle/Contents/Resources/' } 21 | s.resource_bundle = { 'MetalAudioShaders' => ['MetalAudioShaders/CocoaPodsBundledResourcePlaceholder'] } 22 | 23 | s.weak_frameworks = 'MetalPerformanceShaders', 'Metal' 24 | 25 | end 26 | -------------------------------------------------------------------------------- /MetalAudioShaders.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | FA3DA1A324F6634700A33ED4 /* conv1d.metal in Sources */ = {isa = PBXBuildFile; fileRef = FAD21B2E24F63F9300AE8539 /* conv1d.metal */; }; 11 | FA3DA1A424F6634D00A33ED4 /* spectrogram.metal in Sources */ = {isa = PBXBuildFile; fileRef = FAD21B2A24F63F9300AE8539 /* spectrogram.metal */; }; 12 | FAA330A824F646CF000A60F1 /* MetalAudioShaders.h in Headers */ = {isa = PBXBuildFile; fileRef = FAA330A724F646CF000A60F1 /* MetalAudioShaders.h */; }; 13 | FAD21B3B24F63F9300AE8539 /* MASBaseKernel.m in Sources */ = {isa = PBXBuildFile; fileRef = FAD21B2624F63F9300AE8539 /* MASBaseKernel.m */; }; 14 | FAD21B3C24F63F9300AE8539 /* MASConv1dFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = FAD21B2724F63F9300AE8539 /* MASConv1dFactory.m */; }; 15 | FAD21B3D24F63F9300AE8539 /* MASConv1dKernel.m in Sources */ = {isa = PBXBuildFile; fileRef = FAD21B2824F63F9300AE8539 /* MASConv1dKernel.m */; }; 16 | FAD21B3E24F63F9300AE8539 /* MASSpectrogramFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = FAD21B2924F63F9300AE8539 /* MASSpectrogramFactory.m */; }; 17 | FAD21B4024F63F9300AE8539 /* MASShapedBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = FAD21B2B24F63F9300AE8539 /* MASShapedBuffer.m */; }; 18 | FAD21B4124F63F9300AE8539 /* MASConv1dDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = FAD21B2C24F63F9300AE8539 /* MASConv1dDescriptor.m */; }; 19 | FAD21B4224F63F9300AE8539 /* MASBaseKernel.h in Headers */ = {isa = PBXBuildFile; fileRef = FAD21B2D24F63F9300AE8539 /* MASBaseKernel.h */; }; 20 | FAD21B4424F63F9300AE8539 /* MASConv1dFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = FAD21B2F24F63F9300AE8539 /* MASConv1dFactory.h */; }; 21 | FAD21B4524F63F9300AE8539 /* MASConv1dKernel.h in Headers */ = {isa = PBXBuildFile; fileRef = FAD21B3024F63F9300AE8539 /* MASConv1dKernel.h */; }; 22 | FAD21B4624F63F9300AE8539 /* MASSpectrogramDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = FAD21B3124F63F9300AE8539 /* MASSpectrogramDescriptor.h */; }; 23 | FAD21B4724F63F9300AE8539 /* MASCommandEncoderExtension.h in Headers */ = {isa = PBXBuildFile; fileRef = FAD21B3224F63F9300AE8539 /* MASCommandEncoderExtension.h */; }; 24 | FAD21B4824F63F9300AE8539 /* half.h in Headers */ = {isa = PBXBuildFile; fileRef = FAD21B3324F63F9300AE8539 /* half.h */; }; 25 | FAD21B4924F63F9300AE8539 /* MASSpectrogramFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = FAD21B3424F63F9300AE8539 /* MASSpectrogramFactory.h */; }; 26 | FAD21B4A24F63F9300AE8539 /* MASSpectrogramDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = FAD21B3524F63F9300AE8539 /* MASSpectrogramDescriptor.m */; }; 27 | FAD21B4B24F63F9300AE8539 /* MASSpectrogramKernel.h in Headers */ = {isa = PBXBuildFile; fileRef = FAD21B3624F63F9300AE8539 /* MASSpectrogramKernel.h */; }; 28 | FAD21B4C24F63F9300AE8539 /* MASSpectrogramKernel.m in Sources */ = {isa = PBXBuildFile; fileRef = FAD21B3724F63F9300AE8539 /* MASSpectrogramKernel.m */; }; 29 | FAD21B4D24F63F9300AE8539 /* MASCommandEncoderExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = FAD21B3824F63F9300AE8539 /* MASCommandEncoderExtension.m */; }; 30 | FAD21B4E24F63F9300AE8539 /* MASConv1dDescriptor.h in Headers */ = {isa = PBXBuildFile; fileRef = FAD21B3924F63F9300AE8539 /* MASConv1dDescriptor.h */; }; 31 | FAD21B4F24F63F9300AE8539 /* MASShapedBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = FAD21B3A24F63F9300AE8539 /* MASShapedBuffer.h */; }; 32 | /* End PBXBuildFile section */ 33 | 34 | /* Begin PBXFileReference section */ 35 | FA6FE0D924E546F800C2C3D3 /* MetalAudioShaders.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MetalAudioShaders.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | FA6FE0DD24E546F800C2C3D3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 37 | FAA330A724F646CF000A60F1 /* MetalAudioShaders.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MetalAudioShaders.h; sourceTree = ""; }; 38 | FAD21B2624F63F9300AE8539 /* MASBaseKernel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MASBaseKernel.m; sourceTree = ""; }; 39 | FAD21B2724F63F9300AE8539 /* MASConv1dFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MASConv1dFactory.m; sourceTree = ""; }; 40 | FAD21B2824F63F9300AE8539 /* MASConv1dKernel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MASConv1dKernel.m; sourceTree = ""; }; 41 | FAD21B2924F63F9300AE8539 /* MASSpectrogramFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MASSpectrogramFactory.m; sourceTree = ""; }; 42 | FAD21B2A24F63F9300AE8539 /* spectrogram.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = spectrogram.metal; sourceTree = ""; }; 43 | FAD21B2B24F63F9300AE8539 /* MASShapedBuffer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MASShapedBuffer.m; sourceTree = ""; }; 44 | FAD21B2C24F63F9300AE8539 /* MASConv1dDescriptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MASConv1dDescriptor.m; sourceTree = ""; }; 45 | FAD21B2D24F63F9300AE8539 /* MASBaseKernel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MASBaseKernel.h; sourceTree = ""; }; 46 | FAD21B2E24F63F9300AE8539 /* conv1d.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = conv1d.metal; sourceTree = ""; }; 47 | FAD21B2F24F63F9300AE8539 /* MASConv1dFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MASConv1dFactory.h; sourceTree = ""; }; 48 | FAD21B3024F63F9300AE8539 /* MASConv1dKernel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MASConv1dKernel.h; sourceTree = ""; }; 49 | FAD21B3124F63F9300AE8539 /* MASSpectrogramDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MASSpectrogramDescriptor.h; sourceTree = ""; }; 50 | FAD21B3224F63F9300AE8539 /* MASCommandEncoderExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MASCommandEncoderExtension.h; sourceTree = ""; }; 51 | FAD21B3324F63F9300AE8539 /* half.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = half.h; sourceTree = ""; }; 52 | FAD21B3424F63F9300AE8539 /* MASSpectrogramFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MASSpectrogramFactory.h; sourceTree = ""; }; 53 | FAD21B3524F63F9300AE8539 /* MASSpectrogramDescriptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MASSpectrogramDescriptor.m; sourceTree = ""; }; 54 | FAD21B3624F63F9300AE8539 /* MASSpectrogramKernel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MASSpectrogramKernel.h; sourceTree = ""; }; 55 | FAD21B3724F63F9300AE8539 /* MASSpectrogramKernel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MASSpectrogramKernel.m; sourceTree = ""; }; 56 | FAD21B3824F63F9300AE8539 /* MASCommandEncoderExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MASCommandEncoderExtension.m; sourceTree = ""; }; 57 | FAD21B3924F63F9300AE8539 /* MASConv1dDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MASConv1dDescriptor.h; sourceTree = ""; }; 58 | FAD21B3A24F63F9300AE8539 /* MASShapedBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MASShapedBuffer.h; sourceTree = ""; }; 59 | /* End PBXFileReference section */ 60 | 61 | /* Begin PBXFrameworksBuildPhase section */ 62 | FA6FE0D624E546F800C2C3D3 /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | /* End PBXFrameworksBuildPhase section */ 70 | 71 | /* Begin PBXGroup section */ 72 | FA6FE0CF24E546F800C2C3D3 = { 73 | isa = PBXGroup; 74 | children = ( 75 | FA6FE0DB24E546F800C2C3D3 /* MetalAudioShaders */, 76 | FA6FE0DA24E546F800C2C3D3 /* Products */, 77 | ); 78 | sourceTree = ""; 79 | }; 80 | FA6FE0DA24E546F800C2C3D3 /* Products */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | FA6FE0D924E546F800C2C3D3 /* MetalAudioShaders.framework */, 84 | ); 85 | name = Products; 86 | sourceTree = ""; 87 | }; 88 | FA6FE0DB24E546F800C2C3D3 /* MetalAudioShaders */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | FAD21B5124F63FC000AE8539 /* Spectrogram */, 92 | FAD21B5024F63FB000AE8539 /* Conv1d */, 93 | FAD21B3324F63F9300AE8539 /* half.h */, 94 | FAD21B2D24F63F9300AE8539 /* MASBaseKernel.h */, 95 | FAD21B2624F63F9300AE8539 /* MASBaseKernel.m */, 96 | FAD21B3224F63F9300AE8539 /* MASCommandEncoderExtension.h */, 97 | FAD21B3824F63F9300AE8539 /* MASCommandEncoderExtension.m */, 98 | FAD21B3A24F63F9300AE8539 /* MASShapedBuffer.h */, 99 | FAD21B2B24F63F9300AE8539 /* MASShapedBuffer.m */, 100 | FA6FE0DD24E546F800C2C3D3 /* Info.plist */, 101 | FAA330A724F646CF000A60F1 /* MetalAudioShaders.h */, 102 | ); 103 | path = MetalAudioShaders; 104 | sourceTree = ""; 105 | }; 106 | FAD21B5024F63FB000AE8539 /* Conv1d */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | FAD21B2E24F63F9300AE8539 /* conv1d.metal */, 110 | FAD21B3924F63F9300AE8539 /* MASConv1dDescriptor.h */, 111 | FAD21B2C24F63F9300AE8539 /* MASConv1dDescriptor.m */, 112 | FAD21B2F24F63F9300AE8539 /* MASConv1dFactory.h */, 113 | FAD21B2724F63F9300AE8539 /* MASConv1dFactory.m */, 114 | FAD21B3024F63F9300AE8539 /* MASConv1dKernel.h */, 115 | FAD21B2824F63F9300AE8539 /* MASConv1dKernel.m */, 116 | ); 117 | path = Conv1d; 118 | sourceTree = ""; 119 | }; 120 | FAD21B5124F63FC000AE8539 /* Spectrogram */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | FAD21B2A24F63F9300AE8539 /* spectrogram.metal */, 124 | FAD21B3124F63F9300AE8539 /* MASSpectrogramDescriptor.h */, 125 | FAD21B3524F63F9300AE8539 /* MASSpectrogramDescriptor.m */, 126 | FAD21B3424F63F9300AE8539 /* MASSpectrogramFactory.h */, 127 | FAD21B2924F63F9300AE8539 /* MASSpectrogramFactory.m */, 128 | FAD21B3624F63F9300AE8539 /* MASSpectrogramKernel.h */, 129 | FAD21B3724F63F9300AE8539 /* MASSpectrogramKernel.m */, 130 | ); 131 | path = Spectrogram; 132 | sourceTree = ""; 133 | }; 134 | /* End PBXGroup section */ 135 | 136 | /* Begin PBXHeadersBuildPhase section */ 137 | FA6FE0D424E546F800C2C3D3 /* Headers */ = { 138 | isa = PBXHeadersBuildPhase; 139 | buildActionMask = 2147483647; 140 | files = ( 141 | FAD21B4524F63F9300AE8539 /* MASConv1dKernel.h in Headers */, 142 | FAA330A824F646CF000A60F1 /* MetalAudioShaders.h in Headers */, 143 | FAD21B4F24F63F9300AE8539 /* MASShapedBuffer.h in Headers */, 144 | FAD21B4424F63F9300AE8539 /* MASConv1dFactory.h in Headers */, 145 | FAD21B4924F63F9300AE8539 /* MASSpectrogramFactory.h in Headers */, 146 | FAD21B4224F63F9300AE8539 /* MASBaseKernel.h in Headers */, 147 | FAD21B4824F63F9300AE8539 /* half.h in Headers */, 148 | FAD21B4E24F63F9300AE8539 /* MASConv1dDescriptor.h in Headers */, 149 | FAD21B4B24F63F9300AE8539 /* MASSpectrogramKernel.h in Headers */, 150 | FAD21B4624F63F9300AE8539 /* MASSpectrogramDescriptor.h in Headers */, 151 | FAD21B4724F63F9300AE8539 /* MASCommandEncoderExtension.h in Headers */, 152 | ); 153 | runOnlyForDeploymentPostprocessing = 0; 154 | }; 155 | /* End PBXHeadersBuildPhase section */ 156 | 157 | /* Begin PBXNativeTarget section */ 158 | FA6FE0D824E546F800C2C3D3 /* MetalAudioShaders */ = { 159 | isa = PBXNativeTarget; 160 | buildConfigurationList = FA6FE0E124E546F800C2C3D3 /* Build configuration list for PBXNativeTarget "MetalAudioShaders" */; 161 | buildPhases = ( 162 | FA6FE0D424E546F800C2C3D3 /* Headers */, 163 | FA6FE0D524E546F800C2C3D3 /* Sources */, 164 | FA6FE0D624E546F800C2C3D3 /* Frameworks */, 165 | FA6FE0D724E546F800C2C3D3 /* Resources */, 166 | ); 167 | buildRules = ( 168 | ); 169 | dependencies = ( 170 | ); 171 | name = MetalAudioShaders; 172 | productName = MetalAudioShaders; 173 | productReference = FA6FE0D924E546F800C2C3D3 /* MetalAudioShaders.framework */; 174 | productType = "com.apple.product-type.framework"; 175 | }; 176 | /* End PBXNativeTarget section */ 177 | 178 | /* Begin PBXProject section */ 179 | FA6FE0D024E546F800C2C3D3 /* Project object */ = { 180 | isa = PBXProject; 181 | attributes = { 182 | LastUpgradeCheck = 1200; 183 | TargetAttributes = { 184 | FA6FE0D824E546F800C2C3D3 = { 185 | CreatedOnToolsVersion = 12.0; 186 | }; 187 | }; 188 | }; 189 | buildConfigurationList = FA6FE0D324E546F800C2C3D3 /* Build configuration list for PBXProject "MetalAudioShaders" */; 190 | compatibilityVersion = "Xcode 9.3"; 191 | developmentRegion = en; 192 | hasScannedForEncodings = 0; 193 | knownRegions = ( 194 | en, 195 | Base, 196 | ); 197 | mainGroup = FA6FE0CF24E546F800C2C3D3; 198 | productRefGroup = FA6FE0DA24E546F800C2C3D3 /* Products */; 199 | projectDirPath = ""; 200 | projectRoot = ""; 201 | targets = ( 202 | FA6FE0D824E546F800C2C3D3 /* MetalAudioShaders */, 203 | ); 204 | }; 205 | /* End PBXProject section */ 206 | 207 | /* Begin PBXResourcesBuildPhase section */ 208 | FA6FE0D724E546F800C2C3D3 /* Resources */ = { 209 | isa = PBXResourcesBuildPhase; 210 | buildActionMask = 2147483647; 211 | files = ( 212 | ); 213 | runOnlyForDeploymentPostprocessing = 0; 214 | }; 215 | /* End PBXResourcesBuildPhase section */ 216 | 217 | /* Begin PBXSourcesBuildPhase section */ 218 | FA6FE0D524E546F800C2C3D3 /* Sources */ = { 219 | isa = PBXSourcesBuildPhase; 220 | buildActionMask = 2147483647; 221 | files = ( 222 | FA3DA1A324F6634700A33ED4 /* conv1d.metal in Sources */, 223 | FAD21B4D24F63F9300AE8539 /* MASCommandEncoderExtension.m in Sources */, 224 | FAD21B3D24F63F9300AE8539 /* MASConv1dKernel.m in Sources */, 225 | FAD21B4C24F63F9300AE8539 /* MASSpectrogramKernel.m in Sources */, 226 | FAD21B4124F63F9300AE8539 /* MASConv1dDescriptor.m in Sources */, 227 | FAD21B4A24F63F9300AE8539 /* MASSpectrogramDescriptor.m in Sources */, 228 | FAD21B3B24F63F9300AE8539 /* MASBaseKernel.m in Sources */, 229 | FAD21B3E24F63F9300AE8539 /* MASSpectrogramFactory.m in Sources */, 230 | FAD21B4024F63F9300AE8539 /* MASShapedBuffer.m in Sources */, 231 | FAD21B3C24F63F9300AE8539 /* MASConv1dFactory.m in Sources */, 232 | FA3DA1A424F6634D00A33ED4 /* spectrogram.metal in Sources */, 233 | ); 234 | runOnlyForDeploymentPostprocessing = 0; 235 | }; 236 | /* End PBXSourcesBuildPhase section */ 237 | 238 | /* Begin XCBuildConfiguration section */ 239 | FA6FE0DF24E546F800C2C3D3 /* Debug */ = { 240 | isa = XCBuildConfiguration; 241 | buildSettings = { 242 | ALWAYS_SEARCH_USER_PATHS = NO; 243 | CLANG_ANALYZER_NONNULL = YES; 244 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 245 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 246 | CLANG_CXX_LIBRARY = "libc++"; 247 | CLANG_ENABLE_MODULES = YES; 248 | CLANG_ENABLE_OBJC_ARC = YES; 249 | CLANG_ENABLE_OBJC_WEAK = YES; 250 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 251 | CLANG_WARN_BOOL_CONVERSION = YES; 252 | CLANG_WARN_COMMA = YES; 253 | CLANG_WARN_CONSTANT_CONVERSION = YES; 254 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 255 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 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_NON_LITERAL_NULL_CONVERSION = YES; 262 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 263 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 264 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 265 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 266 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 267 | CLANG_WARN_STRICT_PROTOTYPES = YES; 268 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 269 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 270 | CLANG_WARN_UNREACHABLE_CODE = YES; 271 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 272 | COPY_PHASE_STRIP = NO; 273 | CURRENT_PROJECT_VERSION = 1; 274 | DEBUG_INFORMATION_FORMAT = dwarf; 275 | ENABLE_STRICT_OBJC_MSGSEND = YES; 276 | ENABLE_TESTABILITY = YES; 277 | GCC_C_LANGUAGE_STANDARD = gnu11; 278 | GCC_DYNAMIC_NO_PIC = NO; 279 | GCC_NO_COMMON_BLOCKS = YES; 280 | GCC_OPTIMIZATION_LEVEL = 0; 281 | GCC_PREPROCESSOR_DEFINITIONS = ( 282 | "DEBUG=1", 283 | "$(inherited)", 284 | ); 285 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 286 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 287 | GCC_WARN_UNDECLARED_SELECTOR = YES; 288 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 289 | GCC_WARN_UNUSED_FUNCTION = YES; 290 | GCC_WARN_UNUSED_VARIABLE = YES; 291 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 292 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 293 | MTL_FAST_MATH = YES; 294 | ONLY_ACTIVE_ARCH = YES; 295 | SDKROOT = iphoneos; 296 | VERSIONING_SYSTEM = "apple-generic"; 297 | VERSION_INFO_PREFIX = ""; 298 | }; 299 | name = Debug; 300 | }; 301 | FA6FE0E024E546F800C2C3D3 /* Release */ = { 302 | isa = XCBuildConfiguration; 303 | buildSettings = { 304 | ALWAYS_SEARCH_USER_PATHS = NO; 305 | CLANG_ANALYZER_NONNULL = YES; 306 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 307 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 308 | CLANG_CXX_LIBRARY = "libc++"; 309 | CLANG_ENABLE_MODULES = YES; 310 | CLANG_ENABLE_OBJC_ARC = YES; 311 | CLANG_ENABLE_OBJC_WEAK = YES; 312 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 313 | CLANG_WARN_BOOL_CONVERSION = YES; 314 | CLANG_WARN_COMMA = YES; 315 | CLANG_WARN_CONSTANT_CONVERSION = YES; 316 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 317 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 318 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 319 | CLANG_WARN_EMPTY_BODY = YES; 320 | CLANG_WARN_ENUM_CONVERSION = YES; 321 | CLANG_WARN_INFINITE_RECURSION = YES; 322 | CLANG_WARN_INT_CONVERSION = YES; 323 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 324 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 325 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 326 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 327 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 328 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 329 | CLANG_WARN_STRICT_PROTOTYPES = YES; 330 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 331 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 332 | CLANG_WARN_UNREACHABLE_CODE = YES; 333 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 334 | COPY_PHASE_STRIP = NO; 335 | CURRENT_PROJECT_VERSION = 1; 336 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 337 | ENABLE_NS_ASSERTIONS = NO; 338 | ENABLE_STRICT_OBJC_MSGSEND = YES; 339 | GCC_C_LANGUAGE_STANDARD = gnu11; 340 | GCC_NO_COMMON_BLOCKS = YES; 341 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 342 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 343 | GCC_WARN_UNDECLARED_SELECTOR = YES; 344 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 345 | GCC_WARN_UNUSED_FUNCTION = YES; 346 | GCC_WARN_UNUSED_VARIABLE = YES; 347 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 348 | MTL_ENABLE_DEBUG_INFO = NO; 349 | MTL_FAST_MATH = YES; 350 | SDKROOT = iphoneos; 351 | VALIDATE_PRODUCT = YES; 352 | VERSIONING_SYSTEM = "apple-generic"; 353 | VERSION_INFO_PREFIX = ""; 354 | }; 355 | name = Release; 356 | }; 357 | FA6FE0E224E546F800C2C3D3 /* Debug */ = { 358 | isa = XCBuildConfiguration; 359 | buildSettings = { 360 | CODE_SIGN_STYLE = Automatic; 361 | DEFINES_MODULE = YES; 362 | DEVELOPMENT_TEAM = 5X83K7Q543; 363 | DYLIB_COMPATIBILITY_VERSION = 1; 364 | DYLIB_CURRENT_VERSION = 1; 365 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 366 | INFOPLIST_FILE = MetalAudioShaders/Info.plist; 367 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 368 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 369 | LD_RUNPATH_SEARCH_PATHS = ( 370 | "$(inherited)", 371 | "@executable_path/Frameworks", 372 | "@loader_path/Frameworks", 373 | ); 374 | PRODUCT_BUNDLE_IDENTIFIER = studio.techpro.MetalAudioShaders; 375 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 376 | SDKROOT = iphoneos; 377 | SKIP_INSTALL = YES; 378 | TARGETED_DEVICE_FAMILY = "1,2"; 379 | }; 380 | name = Debug; 381 | }; 382 | FA6FE0E324E546F800C2C3D3 /* Release */ = { 383 | isa = XCBuildConfiguration; 384 | buildSettings = { 385 | CODE_SIGN_STYLE = Automatic; 386 | DEFINES_MODULE = YES; 387 | DEVELOPMENT_TEAM = 5X83K7Q543; 388 | DYLIB_COMPATIBILITY_VERSION = 1; 389 | DYLIB_CURRENT_VERSION = 1; 390 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 391 | INFOPLIST_FILE = MetalAudioShaders/Info.plist; 392 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 393 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 394 | LD_RUNPATH_SEARCH_PATHS = ( 395 | "$(inherited)", 396 | "@executable_path/Frameworks", 397 | "@loader_path/Frameworks", 398 | ); 399 | PRODUCT_BUNDLE_IDENTIFIER = studio.techpro.MetalAudioShaders; 400 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 401 | SDKROOT = iphoneos; 402 | SKIP_INSTALL = YES; 403 | TARGETED_DEVICE_FAMILY = "1,2"; 404 | }; 405 | name = Release; 406 | }; 407 | /* End XCBuildConfiguration section */ 408 | 409 | /* Begin XCConfigurationList section */ 410 | FA6FE0D324E546F800C2C3D3 /* Build configuration list for PBXProject "MetalAudioShaders" */ = { 411 | isa = XCConfigurationList; 412 | buildConfigurations = ( 413 | FA6FE0DF24E546F800C2C3D3 /* Debug */, 414 | FA6FE0E024E546F800C2C3D3 /* Release */, 415 | ); 416 | defaultConfigurationIsVisible = 0; 417 | defaultConfigurationName = Release; 418 | }; 419 | FA6FE0E124E546F800C2C3D3 /* Build configuration list for PBXNativeTarget "MetalAudioShaders" */ = { 420 | isa = XCConfigurationList; 421 | buildConfigurations = ( 422 | FA6FE0E224E546F800C2C3D3 /* Debug */, 423 | FA6FE0E324E546F800C2C3D3 /* Release */, 424 | ); 425 | defaultConfigurationIsVisible = 0; 426 | defaultConfigurationName = Release; 427 | }; 428 | /* End XCConfigurationList section */ 429 | }; 430 | rootObject = FA6FE0D024E546F800C2C3D3 /* Project object */; 431 | } 432 | -------------------------------------------------------------------------------- /MetalAudioShaders.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MetalAudioShaders.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MetalAudioShaders/CocoaPodsBundledResourcePlaceholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techpro-studio/MetalAudioShaders/2c0a79e26d717895e81cd692128a8c7c0c10b173/MetalAudioShaders/CocoaPodsBundledResourcePlaceholder -------------------------------------------------------------------------------- /MetalAudioShaders/Conv1d/MASConv1dDescriptor.h: -------------------------------------------------------------------------------- 1 | // 2 | // Conv1DDescriptor.h 3 | // audio_test 4 | // 5 | // Created by Alex on 06.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MASShapedBuffer.h" 11 | #import 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | 16 | @interface MASConv1dDescriptor : NSObject 17 | 18 | @property (nonatomic, assign) unsigned short kernelSize; 19 | @property (nonatomic, assign) unsigned short inputFeatureChannels; 20 | @property (nonatomic, assign) unsigned short outputFeatureChannels; 21 | @property (nonatomic, assign) unsigned short stride; 22 | @property (nonatomic, assign) unsigned short inputSize; 23 | @property (nonatomic, readonly) unsigned short outputSize; 24 | @property (nonatomic, assign) BOOL useSinglePrecision; 25 | 26 | -(instancetype) initWithKernelSize: (unsigned short) kernelSize 27 | inputFeatureChannels: (unsigned short) inputFeatureChannels 28 | outputFeatureChannels: (unsigned short) outputFeatureChannels 29 | stride: (unsigned short) stride 30 | inputSize: (unsigned short) inputSize 31 | useSinglePrecision: (BOOL) useSinglePrecision; 32 | 33 | 34 | 35 | 36 | @end 37 | 38 | NS_ASSUME_NONNULL_END 39 | -------------------------------------------------------------------------------- /MetalAudioShaders/Conv1d/MASConv1dDescriptor.m: -------------------------------------------------------------------------------- 1 | // 2 | // Conv1DDescriptor.m 3 | // audio_test 4 | // 5 | // Created by Alex on 06.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #import "MASConv1dDescriptor.h" 10 | #import "half.h" 11 | 12 | @implementation MASConv1dDescriptor 13 | 14 | -(instancetype) initWithKernelSize: (unsigned short) kernelSize 15 | inputFeatureChannels: (unsigned short) inputFeatureChannels 16 | outputFeatureChannels: (unsigned short) outputFeatureChannels 17 | stride: (unsigned short) stride 18 | inputSize: (unsigned short) inputSize 19 | useSinglePrecision: (BOOL) useSinglePrecision 20 | { 21 | self = [super init]; 22 | if (self) { 23 | self.kernelSize = kernelSize; 24 | self.inputFeatureChannels = inputFeatureChannels; 25 | self.outputFeatureChannels = outputFeatureChannels; 26 | self.stride = stride; 27 | self.inputSize = inputSize; 28 | self.useSinglePrecision = useSinglePrecision; 29 | self->_outputSize = ((inputSize - (kernelSize - stride)) / stride) ; 30 | } 31 | return self; 32 | } 33 | 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /MetalAudioShaders/Conv1d/MASConv1dFactory.h: -------------------------------------------------------------------------------- 1 | // 2 | // Conv1DFactory.h 3 | // audio_test 4 | // 5 | // Created by Alex on 24.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "MASShapedBuffer.h" 12 | #import "MASConv1dDescriptor.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface MASConv1dFactory : NSObject 17 | 18 | +(MASShapedBuffer *) createWeightsFor: (MASConv1dDescriptor *)descriptor withFile: (NSString * _Nullable) path; 19 | +(MASShapedBuffer *) createBiasesFor: (MASConv1dDescriptor *)descriptor withFile: (NSString * _Nullable) path; 20 | 21 | +(MPSMatrixDescriptor *) outputMatrixDescriptorFor: (MASConv1dDescriptor *)descriptor; 22 | 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /MetalAudioShaders/Conv1d/MASConv1dFactory.m: -------------------------------------------------------------------------------- 1 | // 2 | // Conv1DFactory.m 3 | // audio_test 4 | // 5 | // Created by Alex on 24.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #import "MASConv1dFactory.h" 10 | #import "half.h" 11 | 12 | @implementation MASConv1dFactory 13 | 14 | +(unsigned short) typeSizeFor: (MASConv1dDescriptor *)descriptor 15 | { 16 | return descriptor.useSinglePrecision ? sizeof(float) : sizeof(half); 17 | } 18 | 19 | +(MASShapedBuffer *) fillBuffer:(MASShapedBuffer *) buffer withFile: (NSString *) filePath 20 | { 21 | if (filePath == nil){ 22 | return buffer; 23 | } 24 | NSData *data = [NSData dataWithContentsOfFile:filePath]; 25 | if (data == nil) { 26 | return buffer; 27 | } 28 | memcpy([buffer buffer], [data bytes], [buffer bufferLength]); 29 | return buffer; 30 | } 31 | 32 | +(MASShapedBuffer *)createWeightsFor: (MASConv1dDescriptor *)descriptor withFile: (NSString * _Nullable) path 33 | { 34 | return [self fillBuffer: [[MASShapedBuffer alloc] initWithShape:@[@(descriptor.kernelSize), @(descriptor.inputFeatureChannels), @(descriptor.outputFeatureChannels)] andTypeSize: [self typeSizeFor: descriptor]] withFile:path]; 35 | } 36 | 37 | +(MASShapedBuffer *)createBiasesFor: (MASConv1dDescriptor *)descriptor withFile: (NSString * _Nullable) path 38 | { 39 | return [self fillBuffer: [[MASShapedBuffer alloc] initWithShape:@[@(descriptor.outputFeatureChannels)] andTypeSize:[self typeSizeFor: descriptor]] withFile:path]; 40 | } 41 | 42 | +(MPSMatrixDescriptor *)outputMatrixDescriptorFor: (MASConv1dDescriptor *)descriptor 43 | { 44 | return [MPSMatrixDescriptor 45 | matrixDescriptorWithRows:descriptor.outputFeatureChannels 46 | columns:descriptor.outputSize 47 | rowBytes:descriptor.outputSize * [self typeSizeFor: descriptor] 48 | dataType: descriptor.useSinglePrecision ? MPSDataTypeFloat32 : MPSDataTypeFloat16]; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /MetalAudioShaders/Conv1d/MASConv1dKernel.h: -------------------------------------------------------------------------------- 1 | // 2 | // MPSConv1DKernel.h 3 | // audio_test 4 | // 5 | // Created by Alex on 05.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MASConv1dDescriptor.h" 11 | #import 12 | #import "MASBaseKernel.h" 13 | #import "MASShapedBuffer.h" 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | @interface MASConv1dKernel: MASBaseKernel 18 | 19 | -(instancetype) initWithDevice: (id) device 20 | andDescriptor: (MASConv1dDescriptor *) descriptor 21 | weights: (MASShapedBuffer *) weights 22 | biases: (MASShapedBuffer *) biases; 23 | 24 | -(void) encodeToCommandBuffer: (id) buffer 25 | inputMatrix: (MPSMatrix * _Nonnull) inputMatrix 26 | resultMatrix: (MPSMatrix * _Nonnull) resultMatrix; 27 | 28 | @end 29 | 30 | NS_ASSUME_NONNULL_END 31 | -------------------------------------------------------------------------------- /MetalAudioShaders/Conv1d/MASConv1dKernel.m: -------------------------------------------------------------------------------- 1 | // 2 | // MPSConv1DKernel.m 3 | // audio_test 4 | // 5 | // Created by Alex on 05.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #import "MASConv1dKernel.h" 10 | #import "half.h" 11 | #import "MASCommandEncoderExtension.h" 12 | 13 | 14 | @implementation MASConv1dKernel 15 | { 16 | MASConv1dDescriptor *descriptor; 17 | id weights; 18 | id biases; 19 | } 20 | 21 | - (instancetype)initWithDevice: (id) device 22 | andDescriptor: (MASConv1dDescriptor *) descriptor 23 | weights: (MASShapedBuffer *) weights 24 | biases: (MASShapedBuffer *) biases; 25 | { 26 | self = [super initWithDevice:device functionName: descriptor.useSinglePrecision 27 | ? @"conv1d_float" : @"conv1d_half"]; 28 | if (self) { 29 | NSUInteger weightsCount = descriptor.inputFeatureChannels * descriptor.outputFeatureChannels * descriptor.kernelSize; 30 | self -> descriptor = descriptor; 31 | self -> weights = [self makeMTLBuffer: weightsCount fromShaped:weights]; 32 | self -> biases = [self makeMTLBuffer: descriptor.outputFeatureChannels fromShaped:biases]; 33 | } 34 | return self; 35 | } 36 | 37 | -(id) makeMTLBuffer: (NSUInteger) count fromShaped:(MASShapedBuffer*) shapedBuffer 38 | { 39 | NSUInteger size = (descriptor.useSinglePrecision ? sizeof(float) : sizeof(half)) * count; 40 | id buffer = [self.device newBufferWithLength: size options: MTLResourceStorageModeShared]; 41 | memcpy([buffer contents], [shapedBuffer buffer], size); 42 | return buffer; 43 | } 44 | 45 | -(void) encodeToCommandBuffer: (id) buffer 46 | inputMatrix: (MPSMatrix * _Nonnull) inputMatrix 47 | resultMatrix: (MPSMatrix * _Nonnull) resultMatrix 48 | { 49 | __auto_type commandEncoder = [buffer computeCommandEncoder]; 50 | if (commandEncoder == nil){ 51 | NSLog(@"Empty command encoder"); 52 | return; 53 | } 54 | 55 | unsigned short config[6] = { 56 | descriptor.kernelSize, 57 | descriptor.inputFeatureChannels, 58 | descriptor.outputFeatureChannels, 59 | descriptor.stride, 60 | descriptor.inputSize, 61 | descriptor.outputSize 62 | }; 63 | 64 | [commandEncoder setComputePipelineState: self.pipelineState]; 65 | [commandEncoder setBytes: &config length: 6 * sizeof(unsigned short) atIndex: 0]; 66 | [commandEncoder setBuffer: inputMatrix.data offset: 0 atIndex: 1]; 67 | [commandEncoder setBuffer: resultMatrix.data offset: 0 atIndex: 2]; 68 | [commandEncoder setBuffer: weights offset: 0 atIndex: 3]; 69 | [commandEncoder setBuffer: biases offset: 0 atIndex: 4]; 70 | 71 | [MASCommandEncoderExtension dispatchMatrix: resultMatrix inCommandEncoder: commandEncoder with:self.pipelineState]; 72 | 73 | [commandEncoder endEncoding]; 74 | } 75 | 76 | - (void)describe 77 | { 78 | NSLog(@"Conv1D Kernel size(%d) : Input(size: %d, features: %d) -> Output(size: %d, feautures: %d)", descriptor.kernelSize, descriptor.inputSize, descriptor.inputFeatureChannels, descriptor.outputSize, descriptor.outputFeatureChannels); 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /MetalAudioShaders/Conv1d/conv1d.metal: -------------------------------------------------------------------------------- 1 | // 2 | // conv1d.metal 3 | // audio_test 4 | // 5 | // Created by Alex on 06.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct Config { 13 | ushort kernelSize; 14 | ushort inputFeatureChannels; 15 | ushort outputFeatureChannels; 16 | ushort stride; 17 | ushort inputSize; 18 | ushort outputSize; 19 | }; 20 | 21 | template 22 | T compute_conv1d_kernel(constant T* rowPtr, constant T *weightsPtr, ushort kernelSize) 23 | { 24 | T sum = 0.0h; 25 | ushort iterations = kernelSize / 4; 26 | for (ushort i = 0; i < iterations; ++i) 27 | { 28 | vec row = ((constant vec*) rowPtr)[i]; 29 | vec weights = ((constant vec*) weightsPtr)[i]; 30 | sum += dot(row, weights); 31 | } 32 | ushort left = kernelSize % 4; 33 | for (ushort i = 0; i < left; ++i) 34 | { 35 | sum += rowPtr[iterations * 4 + i] * weightsPtr[iterations * 4 + i]; 36 | } 37 | return sum; 38 | } 39 | 40 | template 41 | void compute_conv1d(constant Config & config, constant T* inputBuffer, device T* output, constant T* weights, constant T* biasTerms, ushort2 index) 42 | { 43 | if (index.y >= config.outputFeatureChannels || index.x >= config.outputSize) { 44 | return; 45 | } 46 | uint outputFeatureWeightsOffset = config.kernelSize * config.inputFeatureChannels * index.y; 47 | constant T* outputFeatureWeights = weights + outputFeatureWeightsOffset; 48 | 49 | ushort inputRowOffset = index.x * config.stride; 50 | 51 | T result = 0.0h; 52 | 53 | for (ushort i = 0; i < config.inputFeatureChannels; ++i) 54 | { 55 | constant T* rowPtr = inputBuffer + (i * config.inputSize) + inputRowOffset; 56 | constant T* weightsPtr = outputFeatureWeights + (i * config.kernelSize); 57 | result += compute_conv1d_kernel(rowPtr, weightsPtr, config.kernelSize); 58 | } 59 | 60 | uint outputIndex = (index.y * config.outputSize) + index.x; 61 | 62 | result += biasTerms[index.y]; 63 | 64 | output[outputIndex] = result; 65 | } 66 | 67 | 68 | kernel void conv1d_half(constant Config & config, constant half* inputBuffer [[buffer(1)]], device half* output [[buffer(2)]], constant half* weights [[buffer(3)]], constant half* biasTerms [[buffer(4)]], ushort2 index[[thread_position_in_grid]]) 69 | { 70 | compute_conv1d(config, inputBuffer, output, weights, biasTerms, index); 71 | } 72 | 73 | 74 | kernel void conv1d_float(constant Config & config, constant float* inputBuffer [[buffer(1)]], device float* output [[buffer(2)]], constant float* weights [[buffer(3)]], constant float* biasTerms [[buffer(4)]], ushort2 index[[thread_position_in_grid]]) 75 | { 76 | compute_conv1d(config, inputBuffer, output, weights, biasTerms, index); 77 | } 78 | 79 | 80 | -------------------------------------------------------------------------------- /MetalAudioShaders/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /MetalAudioShaders/MASBaseKernel.h: -------------------------------------------------------------------------------- 1 | // 2 | // BaseKernel.h 3 | // audio_test 4 | // 5 | // Created by Alex on 09.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface MASBaseKernel : NSObject 16 | 17 | @property (readonly, nonatomic) id pipelineState; 18 | 19 | @property (readonly, nonatomic) id device; 20 | 21 | -(instancetype) initWithDevice: (id) device functionName: (NSString *) functionName; 22 | 23 | -(void) describe; 24 | 25 | @end 26 | 27 | NS_ASSUME_NONNULL_END 28 | -------------------------------------------------------------------------------- /MetalAudioShaders/MASBaseKernel.m: -------------------------------------------------------------------------------- 1 | // 2 | // BaseKernel.m 3 | // audio_test 4 | // 5 | // Created by Alex on 09.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #import "MASBaseKernel.h" 10 | #import 11 | 12 | @interface MASBaseKernel () 13 | 14 | @property (readwrite, nonatomic) id device; 15 | 16 | @property (readwrite, nonatomic) id pipelineState; 17 | 18 | @end 19 | 20 | 21 | @implementation MASBaseKernel 22 | 23 | -(instancetype)initWithDevice:(id)device functionName:(nonnull NSString *)functionName 24 | { 25 | self = [super init]; 26 | if (self) { 27 | self.device = device; 28 | 29 | NSError *error; 30 | 31 | __auto_type libURL = [self libraryURL]; 32 | 33 | __auto_type library = [device newLibraryWithURL:libURL error:&error]; 34 | 35 | if (library == nil) { 36 | @throw [NSException exceptionWithName:@"no library" reason:@"library not found" userInfo:nil]; 37 | } 38 | 39 | __auto_type function = [library newFunctionWithName:functionName]; 40 | 41 | if (function == nil) { 42 | @throw [NSException exceptionWithName:@"no function" reason:[NSString stringWithFormat:@"Function with name '%@' not found", functionName] userInfo:nil]; 43 | } 44 | 45 | 46 | self.pipelineState = [device newComputePipelineStateWithFunction:function error:&error]; 47 | 48 | if (error != nil) { 49 | @throw [NSException exceptionWithName:@"Pipeline state failed" reason:error.localizedDescription userInfo:nil]; 50 | } 51 | } 52 | return self; 53 | } 54 | 55 | 56 | -(NSURL *) libraryURL 57 | { 58 | NSBundle* bundle = [NSBundle bundleForClass: MASBaseKernel.class]; 59 | NSURL* bundleURL = [[bundle resourceURL] URLByAppendingPathComponent: @"MetalAudioShaders.bundle"]; 60 | __auto_type currentBundle = [NSBundle bundleWithURL: bundleURL]; 61 | __auto_type libURL = [currentBundle URLForResource:@"default" withExtension:@"metallib"]; 62 | return libURL; 63 | } 64 | 65 | -(void) describe 66 | { 67 | NSLog(@"Base kernel"); 68 | } 69 | 70 | @end 71 | 72 | -------------------------------------------------------------------------------- /MetalAudioShaders/MASCommandEncoderExtension.h: -------------------------------------------------------------------------------- 1 | // 2 | // MatrixKernel.h 3 | // audio_test 4 | // 5 | // Created by Alex on 10.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #import "MASBaseKernel.h" 10 | #import 11 | 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface MASCommandEncoderExtension: NSObject 16 | 17 | +(void)dispatchMatrix: (MPSMatrix *)matrix 18 | inCommandEncoder: (id) commandEncoder 19 | with: (id) pipelineState; 20 | 21 | 22 | @end 23 | 24 | NS_ASSUME_NONNULL_END 25 | -------------------------------------------------------------------------------- /MetalAudioShaders/MASCommandEncoderExtension.m: -------------------------------------------------------------------------------- 1 | // 2 | // MatrixKernel.m 3 | // audio_test 4 | // 5 | // Created by Alex on 10.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #import "MASCommandEncoderExtension.h" 10 | 11 | @implementation MASCommandEncoderExtension 12 | 13 | +(void)dispatchMatrix: (MPSMatrix *)matrix 14 | inCommandEncoder: (id) commandEncoder 15 | with: (id) pipelineState 16 | { 17 | __auto_type width = pipelineState.threadExecutionWidth; 18 | __auto_type height = pipelineState.maxTotalThreadsPerThreadgroup / width; 19 | 20 | MTLSize threadGroupSize = MTLSizeMake(width, height, 1); 21 | MTLSize threadGroups = MTLSizeMake( 22 | (matrix.columns + width - 1) / width, 23 | (matrix.rows + height - 1) / height, 24 | 1 25 | ); 26 | 27 | [commandEncoder dispatchThreadgroups:threadGroups threadsPerThreadgroup: threadGroupSize]; 28 | } 29 | 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /MetalAudioShaders/MASShapedBuffer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Tensor.h 3 | // audio_test 4 | // 5 | // Created by Alex on 09.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface MASShapedBuffer : NSObject 14 | 15 | -(instancetype) initWithShape: (NSArray *) shape andTypeSize: (unsigned short) typeSize; 16 | 17 | -(NSArray *) shape; 18 | 19 | -(NSUInteger) bufferLength; 20 | 21 | -(void *) buffer; 22 | 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /MetalAudioShaders/MASShapedBuffer.m: -------------------------------------------------------------------------------- 1 | // 2 | // Tensor.m 3 | // audio_test 4 | // 5 | // Created by Alex on 09.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #import "MASShapedBuffer.h" 10 | 11 | @implementation MASShapedBuffer 12 | { 13 | NSArray *shape; 14 | NSUInteger _bufferLength; 15 | void *ptr; 16 | } 17 | 18 | -(NSArray *)shape 19 | { 20 | return shape; 21 | } 22 | 23 | -(void *) buffer 24 | { 25 | return ptr; 26 | } 27 | 28 | -(instancetype)initWithShape: (NSArray *) shape 29 | andTypeSize: (unsigned short) typeSize 30 | { 31 | self = [super init]; 32 | if (self) { 33 | self -> shape = shape; 34 | size_t size = typeSize; 35 | for (NSNumber *item in shape) 36 | { 37 | size *= item.unsignedIntValue; 38 | } 39 | _bufferLength = size; 40 | ptr = malloc(size); 41 | 42 | } 43 | return self; 44 | } 45 | 46 | - (void)dealloc 47 | { 48 | free(ptr); 49 | } 50 | 51 | -(NSUInteger)bufferLength 52 | { 53 | return _bufferLength; 54 | } 55 | 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /MetalAudioShaders/MetalAudioShaders.h: -------------------------------------------------------------------------------- 1 | // 2 | // MetalAudioShaders.h 3 | // MetalAudioShaders 4 | // 5 | // Created by Alex on 26.08.2020. 6 | // 7 | 8 | #ifndef MetalAudioShaders_h 9 | #define MetalAudioShaders_h 10 | 11 | 12 | #import "MASCommandEncoderExtension.h" 13 | #import "MASConv1dKernel.h" 14 | #import "MASConv1dFactory.h" 15 | #import "MASSpectrogramKernel.h" 16 | #import "MASSpectrogramFactory.h" 17 | #import "MASShapedBuffer.h" 18 | 19 | #endif /* MetalAudioShaders_h */ 20 | -------------------------------------------------------------------------------- /MetalAudioShaders/Spectrogram/MASSpectrogramDescriptor.h: -------------------------------------------------------------------------------- 1 | // 2 | // SpectrogramDescriptor.h 3 | // audio_test 4 | // 5 | // Created by Alex on 09.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MASShapedBuffer.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | typedef enum WindowType: NSUInteger { 15 | WindowTypeHamming = 1, 16 | WindowTypeHann = 2, 17 | WindowTypeBlackMan = 3, 18 | } WindowType; 19 | 20 | 21 | 22 | @interface MASSpectrogramDescriptor : NSObject 23 | 24 | @property (nonatomic, readonly) unsigned short nfft; 25 | 26 | @property (nonatomic, readonly) unsigned short noverlap; 27 | 28 | @property (nonatomic, assign) unsigned short fftNormalizationFactor; 29 | 30 | @property (nonatomic, readonly) NSUInteger inputSize; 31 | 32 | @property (nonatomic, readonly) BOOL isComplex; 33 | 34 | @property (nonatomic, readonly) BOOL useSinglePrecision; 35 | 36 | @property (nonatomic, retain) MASShapedBuffer *window; 37 | 38 | // Size of column or number of rows 39 | @property (nonatomic, readonly) unsigned short outputFeatureChannels; 40 | // Size of row or number of columns 41 | @property (nonatomic, readonly) unsigned short outputSize; 42 | 43 | @property (nonatomic, readonly) unsigned short step; 44 | 45 | -(instancetype) initWithNfft: (unsigned short) nfft 46 | noverlap: (unsigned short) noverlap 47 | inputSize: (NSUInteger) inputSize 48 | isComplex: (BOOL) isComplex 49 | useSinglePrecision: (BOOL) useSinglePrecision; 50 | 51 | 52 | -(void) setWindowWithType: (WindowType) type; 53 | 54 | 55 | @end 56 | 57 | NS_ASSUME_NONNULL_END 58 | -------------------------------------------------------------------------------- /MetalAudioShaders/Spectrogram/MASSpectrogramDescriptor.m: -------------------------------------------------------------------------------- 1 | // 2 | // SpectrogramDescriptor.m 3 | // audio_test 4 | // 5 | // Created by Alex on 09.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #import "MASSpectrogramDescriptor.h" 10 | #import "half.h" 11 | 12 | @implementation MASSpectrogramDescriptor 13 | 14 | 15 | -(instancetype)initWithNfft:(unsigned short)nfft 16 | noverlap:(unsigned short)noverlap 17 | inputSize:(NSUInteger)inputSize 18 | isComplex:(BOOL)isComplex 19 | useSinglePrecision:(BOOL)useSinglePrecision 20 | { 21 | self = [super init]; 22 | if (self) { 23 | _nfft = nfft; 24 | _noverlap = noverlap; 25 | _inputSize = inputSize; 26 | _isComplex = isComplex; 27 | _fftNormalizationFactor = 1.0f; 28 | _useSinglePrecision = useSinglePrecision; 29 | _step = nfft - noverlap; 30 | _outputSize = (inputSize - noverlap) / _step; 31 | _outputFeatureChannels = isComplex ? nfft : (nfft / 2) + 1; 32 | _window = [[MASShapedBuffer alloc] initWithShape:@[@(_nfft)] andTypeSize:[self typeSize]]; 33 | [self setNoWindow]; 34 | } 35 | return self; 36 | } 37 | 38 | -(void) copyToWindow: (float *) buffer 39 | { 40 | if (_useSinglePrecision){ 41 | memcpy([_window buffer], buffer, sizeof(float) * _nfft); 42 | } else { 43 | vector_float_to_half(buffer, [_window buffer], sizeof(half) * _nfft); 44 | } 45 | } 46 | 47 | -(void) setNoWindow 48 | { 49 | float ones[_nfft]; 50 | for (int i = 0; i < _nfft; ++i) 51 | ones[i] = 1.0f; 52 | [self copyToWindow:ones]; 53 | } 54 | 55 | -(unsigned short) typeSize 56 | { 57 | return self.useSinglePrecision ? sizeof(float) : sizeof(half); 58 | } 59 | 60 | - (void)setWindowWithType:(WindowType)type 61 | { 62 | float window[_nfft]; 63 | switch (type) { 64 | case WindowTypeHann: 65 | vDSP_hann_window(window, _nfft, 0); 66 | break; 67 | case WindowTypeHamming: 68 | vDSP_hamm_window(window, _nfft, 0); 69 | break; 70 | case WindowTypeBlackMan: 71 | vDSP_blkman_window(window, _nfft, 0); 72 | break; 73 | } 74 | [self copyToWindow: window]; 75 | } 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /MetalAudioShaders/Spectrogram/MASSpectrogramFactory.h: -------------------------------------------------------------------------------- 1 | // 2 | // SpectrogramFactory.h 3 | // audio_test 4 | // 5 | // Created by Alex on 24.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "MASSpectrogramDescriptor.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface MASSpectrogramFactory : NSObject 16 | 17 | +(MPSMatrixDescriptor*) outputMatrixDescriptor: (MASSpectrogramDescriptor *) descriptor; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /MetalAudioShaders/Spectrogram/MASSpectrogramFactory.m: -------------------------------------------------------------------------------- 1 | // 2 | // SpectrogramFactory.m 3 | // audio_test 4 | // 5 | // Created by Alex on 24.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #import "MASSpectrogramFactory.h" 10 | #import "half.h" 11 | 12 | @implementation MASSpectrogramFactory 13 | 14 | +(unsigned short) typeSizeFor: (MASSpectrogramDescriptor *)descriptor 15 | { 16 | return descriptor.useSinglePrecision ? sizeof(float) : sizeof(half); 17 | } 18 | 19 | + (MPSMatrixDescriptor *)outputMatrixDescriptor:(MASSpectrogramDescriptor *)descriptor 20 | { 21 | return [MPSMatrixDescriptor 22 | matrixDescriptorWithRows:descriptor.outputFeatureChannels 23 | columns: descriptor.outputSize 24 | rowBytes: descriptor.outputSize * [self typeSizeFor: descriptor] 25 | dataType: descriptor.useSinglePrecision ? MPSDataTypeFloat32 : MPSDataTypeFloat16]; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /MetalAudioShaders/Spectrogram/MASSpectrogramKernel.h: -------------------------------------------------------------------------------- 1 | // 2 | // SpectrogramKernel.h 3 | // audio_test 4 | // 5 | // Created by Alex on 09.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #import "MASBaseKernel.h" 10 | #import 11 | #import 12 | #import "MASSpectrogramDescriptor.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface MASSpectrogramKernel : MASBaseKernel 17 | 18 | -(instancetype) initWithDevice: (id) device 19 | andDescriptor: (MASSpectrogramDescriptor *) descriptor; 20 | 21 | -(void) encodeToCommandBuffer: (id) buffer 22 | inputVector: (MPSVector * _Nonnull) inputVector 23 | resultMatrix: (MPSMatrix * _Nonnull) resultMatrix; 24 | 25 | @end 26 | 27 | NS_ASSUME_NONNULL_END 28 | -------------------------------------------------------------------------------- /MetalAudioShaders/Spectrogram/MASSpectrogramKernel.m: -------------------------------------------------------------------------------- 1 | // 2 | // SpectrogramKernel.m 3 | // audio_test 4 | // 5 | // Created by Alex on 09.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #import "MASSpectrogramKernel.h" 10 | #import "MASCommandEncoderExtension.h" 11 | #import 12 | 13 | 14 | typedef struct { 15 | unsigned short outputFeatureChannels; 16 | unsigned short outputSize; 17 | unsigned short nfft; 18 | unsigned short step; 19 | float normalizationFactor; 20 | } Config; 21 | 22 | @implementation MASSpectrogramKernel 23 | { 24 | MASSpectrogramDescriptor *descriptor; 25 | } 26 | 27 | - (instancetype)initWithDevice:(id)device 28 | andDescriptor:(MASSpectrogramDescriptor *)descriptor 29 | { 30 | NSString* functionName = [NSString stringWithFormat:@"spectrogram_%@_%@", descriptor.isComplex ? @"complex" : @"real", descriptor.useSinglePrecision ? @"float" : @"half"]; 31 | self = [super initWithDevice:device functionName: functionName]; 32 | if (self) { 33 | self -> descriptor = descriptor; 34 | } 35 | return self; 36 | } 37 | 38 | -(void) encodeToCommandBuffer: (id) buffer 39 | inputVector: (MPSVector * _Nonnull) inputVector 40 | resultMatrix: (MPSMatrix * _Nonnull) resultMatrix 41 | { 42 | __auto_type commandEncoder = [buffer computeCommandEncoder]; 43 | if (commandEncoder == nil){ 44 | NSLog(@"Empty command encoder"); 45 | return; 46 | } 47 | 48 | 49 | Config config = { 50 | descriptor.outputFeatureChannels, 51 | descriptor.outputSize, 52 | descriptor.nfft, 53 | descriptor.step, 54 | descriptor.fftNormalizationFactor 55 | }; 56 | 57 | [commandEncoder setComputePipelineState: self.pipelineState]; 58 | [commandEncoder setBytes: &config length:sizeof(Config) atIndex: 0]; 59 | [commandEncoder setBytes: descriptor.window.buffer length: descriptor.window.bufferLength atIndex:1]; 60 | [commandEncoder setBuffer: inputVector.data offset: 0 atIndex: 2]; 61 | [commandEncoder setBuffer: resultMatrix.data offset: 0 atIndex: 3]; 62 | 63 | 64 | [MASCommandEncoderExtension dispatchMatrix: resultMatrix inCommandEncoder: commandEncoder with: self.pipelineState]; 65 | 66 | [commandEncoder endEncoding]; 67 | } 68 | 69 | -(void)describe 70 | { 71 | NSLog(@"Spectrogram Input(size: %lu) -> Output(size: %d, feautures: %d)", (unsigned long)descriptor.inputSize, descriptor.outputSize, descriptor.outputFeatureChannels); 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /MetalAudioShaders/Spectrogram/spectrogram.metal: -------------------------------------------------------------------------------- 1 | // 2 | // new_shaders.metal 3 | // audio_test 4 | // 5 | // Created by Alex on 06.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #include 10 | using namespace metal; 11 | 12 | struct Config { 13 | ushort outputFeatureChannels; 14 | ushort outputSize; 15 | ushort nfft; 16 | ushort step; 17 | float normalizationFactor; 18 | }; 19 | 20 | template 21 | constexpr T pi(){ 22 | if (is_same::value){ 23 | return M_PI_H; 24 | } else { 25 | return M_PI_F; 26 | } 27 | } 28 | 29 | template 30 | constexpr T min_value(){ 31 | if (is_same::value){ 32 | return 5.9605e-8h; 33 | } else { 34 | return 1.5849e-13f; 35 | } 36 | } 37 | 38 | 39 | 40 | 41 | template 42 | vec real_dft_step(constant T *input, constant T* window, ushort k, ushort nfft){ 43 | vec value {0.h, 0.h}; 44 | for (int n = 0; n < nfft; ++n) { 45 | T windowFactor = window[n]; 46 | T angle = 2 * pi() * n * k / nfft; 47 | value += { *(input + n) * windowFactor * cos(angle), -1 * *(input + n) * windowFactor * sin(angle) }; 48 | } 49 | return value; 50 | } 51 | 52 | template 53 | vec complex_dft_step(constant vec *input, constant T* window, ushort k, ushort nfft){ 54 | vec value {0, 0}; 55 | for (int n = 0; n < nfft; ++n) { 56 | T windowFactor = window[n]; 57 | T angle = 2 * pi() * n * k / nfft; 58 | vec inputN = *(input + n); 59 | value += { inputN[0] * windowFactor * cos(angle) + inputN[1] * windowFactor * sin(angle) , -1 * inputN[0] * windowFactor * sin(angle) + inputN[1] * windowFactor * cos(angle) }; 60 | } 61 | return value; 62 | } 63 | 64 | template 65 | T calculate_magnitude(vec complex) { 66 | T magnitude = sqrt(complex[0] * complex[0] + complex[1] * complex[1]); 67 | magnitude += min_value(); 68 | T result = 10.0 * log10(magnitude); 69 | return result; 70 | } 71 | 72 | template 73 | void spectrogram_real(constant Config& config, constant T* window, constant T *input, device T *output, ushort2 index[[thread_position_in_grid]]) 74 | { 75 | if (index.x >= config.outputSize || index.y >= config.outputFeatureChannels) { return; } 76 | constant T *input_begin = input + index.x * config.step; 77 | vec dft = real_dft_step(input_begin, window, index.y, config.nfft); 78 | dft *= (T) config.normalizationFactor; 79 | uint outputIndex = index.y * config.outputSize + index.x; 80 | output[outputIndex] = calculate_magnitude(dft); 81 | } 82 | 83 | template 84 | void spectrogram_complex(constant Config& config, constant T* window, constant vec *input, device T *output, ushort2 index[[thread_position_in_grid]]) 85 | { 86 | if (index.x >= config.outputSize || index.y >= config.outputFeatureChannels) { return; } 87 | constant vec *input_begin = input + index.x * config.step; 88 | vec dft = complex_dft_step(input_begin, window, index.y, config.nfft); 89 | dft *= (T) config.normalizationFactor; 90 | uint outputIndex = index.y * config.outputSize + index.x; 91 | output[outputIndex] = calculate_magnitude(dft); 92 | } 93 | 94 | kernel void spectrogram_real_half(constant Config& config, constant half* window, constant half *input, device half *output, ushort2 index[[thread_position_in_grid]]) 95 | { 96 | spectrogram_real(config, window, input, output, index); 97 | } 98 | 99 | kernel void spectrogram_real_float(constant Config& config, constant float* window, constant float *input, device float *output, ushort2 index[[thread_position_in_grid]]) 100 | { 101 | spectrogram_real(config, window, input, output, index); 102 | } 103 | 104 | kernel void spectrogram_complex_half(constant Config& config, constant half* window, constant half2 *input, device half *output, ushort2 index[[thread_position_in_grid]]) 105 | { 106 | spectrogram_complex(config, window, input, output, index); 107 | } 108 | 109 | kernel void spectrogram_complex_float(constant Config& config, constant float* window, constant float2 *input, device float *output, ushort2 index[[thread_position_in_grid]]) 110 | { 111 | spectrogram_complex(config, window, input, output, index); 112 | } 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /MetalAudioShaders/half.h: -------------------------------------------------------------------------------- 1 | // 2 | // half.h 3 | // audio_test 4 | // 5 | // Created by Alex on 07.08.2020. 6 | // Copyright © 2020 Alex. All rights reserved. 7 | // 8 | 9 | #ifndef half_h 10 | #define half_h 11 | 12 | #include 13 | 14 | typedef unsigned short half; 15 | 16 | static inline void vector_half_to_float(half* src, float *dst, unsigned long size) 17 | { 18 | vImage_Buffer inputBuff = { src, 1, size, sizeof(half) }; 19 | vImage_Buffer outputBuff = { dst, 1, size, sizeof(float) }; 20 | vImageConvert_Planar16FtoPlanarF(&inputBuff, &outputBuff, 0); 21 | } 22 | 23 | static inline void vector_float_to_half(float* src, half *dst, unsigned long size) 24 | { 25 | vImage_Buffer inputBuff = { src, 1, size, sizeof(float) }; 26 | vImage_Buffer outputBuff = { dst, 1, size, sizeof(half) }; 27 | vImageConvert_PlanarFtoPlanar16F(&inputBuff, &outputBuff, 0); 28 | } 29 | 30 | 31 | #endif /* half_h */ 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MetalAudioShaders 2 | [![Version](https://img.shields.io/cocoapods/v/MetalAudioShaders.svg?style=flat)](https://cocoapods.org/pods/MetalAudioShaders) 3 | [![License](https://img.shields.io/cocoapods/l/MetalAudioShaders.svg?style=flat)](https://cocoapods.org/pods/MetalAudioShaders) 4 | [![Platform](https://img.shields.io/cocoapods/p/MetalAudioShaders.svg?style=flat)](https://cocoapods.org/pods/MetalAudioShaders) 5 | 6 | ## Example 7 | 8 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 9 | 10 | ## Requirements 11 | 12 | ## Installation 13 | 14 | MetalAudioShaders is available through [CocoaPods](https://cocoapods.org). To install 15 | it, simply add the following line to your Podfile: 16 | 17 | ```ruby 18 | pod 'MetalAudioShaders' 19 | ``` 20 | 21 | ## Author 22 | 23 | Oleksii Moiseenko, alex@techpro.studio 24 | 25 | ## License 26 | 27 | MetalAudioShaders is available under the MIT license. See the LICENSE file for more info. 28 | --------------------------------------------------------------------------------