├── Attenuator
├── Assets.xcassets
│ ├── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Attenuator.entitlements
├── ContentView.swift
├── AppDelegate.swift
├── Info.plist
└── Base.lproj
│ └── Main.storyboard
├── Attenuator.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcshareddata
│ └── xcschemes
│ │ ├── Attenuator.xcscheme
│ │ └── AttenuatorAU.xcscheme
└── project.pbxproj
├── AttenuatorAU
├── AttenuatorAU-Bridging-Header.h
├── AttenuatorAU.entitlements
├── DSP
│ ├── AttenuatorDSP.h
│ └── AttenuatorDSP.mm
├── UI
│ ├── MainUI.swift
│ ├── VUView.metal
│ ├── AudioUnitViewController.swift
│ ├── MainView.swift
│ └── VUView.swift
├── Audio Unit
│ ├── AttenuatorParameter.swift
│ └── AttenuatorAudioUnit.swift
└── Info.plist
└── .gitignore
/Attenuator/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Attenuator.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/AttenuatorAU/AttenuatorAU-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // AttenuatorAU-Bridging-Header.h
3 | // AttenuatorAU
4 | //
5 | // Created by Vlad Gorlov on 04.04.20.
6 | // Copyright © 2020 Vlad Gorlov. All rights reserved.
7 | //
8 |
9 | #import "AttenuatorDSP.h"
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
3 | .tmp
4 | .idea
5 | *.pbxuser
6 | !default.pbxuser
7 | *.mode1v3
8 | !default.mode1v3
9 | *.mode2v3
10 | !default.mode2v3
11 | *.perspectivev3
12 | !default.perspectivev3
13 | xcuserdata
14 | xcbaselines
15 | *.xccheckout
16 | *.moved-aside
17 | *.xcscmblueprint
18 |
--------------------------------------------------------------------------------
/Attenuator.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Attenuator/Attenuator.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/AttenuatorAU/AttenuatorAU.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Attenuator/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // Attenuator
4 | //
5 | // Created by Vlad Gorlov on 04.04.20.
6 | // Copyright © 2020 Vlad Gorlov. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct ContentView: View {
12 | var body: some View {
13 | Text("Hello, World!")
14 | .frame(maxWidth: .infinity, maxHeight: .infinity)
15 | }
16 | }
17 |
18 |
19 | struct ContentView_Previews: PreviewProvider {
20 | static var previews: some View {
21 | ContentView()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/AttenuatorAU/DSP/AttenuatorDSP.h:
--------------------------------------------------------------------------------
1 | //
2 | // AttenuatorDSP.h
3 | // AttenuatorAU
4 | //
5 | // Created by Vlad Gorlov on 05.04.20.
6 | // Copyright © 2020 Vlad Gorlov. All rights reserved.
7 | //
8 |
9 | #ifndef AttenuatorDSP_h
10 | #define AttenuatorDSP_h
11 |
12 | #import
13 |
14 | @interface AttenuatorDSP: NSObject
15 |
16 | @property (nonatomic) float paramGain;
17 | @property (nonatomic) bool isBypassed;
18 | @property (nonatomic) uint numberOfChannels;
19 |
20 | // Used by VU meter on UI side.
21 | @property (nonatomic) float maximumMagnitude;
22 |
23 | -(void)process:(AUAudioFrameCount)frameCount inBufferListPtr:(AudioBufferList*)inBufferListPtr outBufferListPtr:(AudioBufferList*)outBufferListPtr;
24 |
25 | @end
26 |
27 | #endif /* AttenuatorDSP_h */
28 |
--------------------------------------------------------------------------------
/AttenuatorAU/UI/MainUI.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainUI.swift
3 | // AttenuatorVST2UI
4 | //
5 | // Created by Vlad Gorlov on 30.03.20.
6 | // Copyright © 2020 Vlad Gorlov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Combine
11 | import SwiftUI
12 |
13 | struct MainUI: View {
14 |
15 | @EnvironmentObject var sliderData: SliderData
16 | @State var gain: Float = 100
17 |
18 | private var onChanged: (Float) -> Void
19 |
20 | init(onChanged: @escaping (Float) -> Void) {
21 | self.onChanged = onChanged
22 | }
23 |
24 | var body: some View {
25 | VStack {
26 | Slider(value: Binding(get: { self.gain }, set: {
27 | self.gain = $0
28 | self.onChanged($0)
29 | }), in: 0...100, step: 2)
30 | Text("Gain: \(Int(gain))")
31 | }.onReceive(sliderData.$gain, perform: { self.gain = $0 })
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/AttenuatorAU/UI/VUView.metal:
--------------------------------------------------------------------------------
1 | //
2 | // VUView.metal
3 | // AttenuatorAU
4 | //
5 | // Created by Vlad Gorlov on 05.04.20.
6 | // Copyright © 2020 Vlad Gorlov. All rights reserved.
7 | //
8 |
9 | #include
10 | using namespace metal;
11 |
12 | struct ColoredVertex {
13 | float4 position [[position]];
14 | float4 color;
15 | };
16 |
17 | vertex ColoredVertex
18 | vertex_line(uint vid [[vertex_id]],
19 | constant vector_float2 *positions [[buffer(0)]],
20 | constant vector_float4 *color [[buffer(1)]],
21 | constant vector_float2 *viewportSizePointer [[buffer(2)]]) {
22 |
23 | vector_float2 viewportSize = *viewportSizePointer;
24 | vector_float2 pixelSpacePosition = positions[vid].xy;
25 |
26 | ColoredVertex vert;
27 | vert.position = vector_float4(0.0, 0.0, 0.0, 1.0);
28 | vert.position.xy = (pixelSpacePosition / (viewportSize / 2.0)) - 1.0;
29 | vert.color = *color;
30 | return vert;
31 | }
32 |
33 | fragment float4
34 | fragment_line(ColoredVertex vert [[stage_in]]) {
35 | return vert.color;
36 | }
37 |
--------------------------------------------------------------------------------
/Attenuator/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "scale" : "1x",
6 | "size" : "16x16"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "scale" : "2x",
11 | "size" : "16x16"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "scale" : "1x",
16 | "size" : "32x32"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "scale" : "2x",
21 | "size" : "32x32"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "scale" : "1x",
26 | "size" : "128x128"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "scale" : "2x",
31 | "size" : "128x128"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "scale" : "1x",
36 | "size" : "256x256"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "scale" : "2x",
41 | "size" : "256x256"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "scale" : "1x",
46 | "size" : "512x512"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "scale" : "2x",
51 | "size" : "512x512"
52 | }
53 | ],
54 | "info" : {
55 | "author" : "xcode",
56 | "version" : 1
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Attenuator/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Attenuator
4 | //
5 | // Created by Vlad Gorlov on 04.04.20.
6 | // Copyright © 2020 Vlad Gorlov. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import SwiftUI
11 |
12 | @NSApplicationMain
13 | class AppDelegate: NSObject, NSApplicationDelegate {
14 |
15 | var window: NSWindow!
16 |
17 |
18 | func applicationDidFinishLaunching(_ aNotification: Notification) {
19 | // Create the SwiftUI view that provides the window contents.
20 | let contentView = ContentView()
21 |
22 | // Create the window and set the content view.
23 | window = NSWindow(
24 | contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
25 | styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
26 | backing: .buffered, defer: false)
27 | window.center()
28 | window.setFrameAutosaveName("Main Window")
29 | window.contentView = NSHostingView(rootView: contentView)
30 | window.makeKeyAndOrderFront(nil)
31 | }
32 |
33 | func applicationWillTerminate(_ aNotification: Notification) {
34 | // Insert code here to tear down your application
35 | }
36 |
37 |
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/Attenuator/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | Copyright © 2020 Vlad Gorlov. All rights reserved.
27 | NSMainStoryboardFile
28 | Main
29 | NSPrincipalClass
30 | NSApplication
31 | NSSupportsAutomaticTermination
32 |
33 | NSSupportsSuddenTermination
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/AttenuatorAU/Audio Unit/AttenuatorParameter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AttenuatorParameter.swift
3 | // AttenuatorAU
4 | //
5 | // Created by Vlad Gorlov on 05.04.20.
6 | // Copyright © 2020 Vlad Gorlov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import AudioUnit
11 |
12 | enum AttenuatorParameter: UInt64 {
13 |
14 | case gain = 1000
15 |
16 | static func fromRawValue(_ rawValue: UInt64) -> AttenuatorParameter {
17 | if let value = AttenuatorParameter(rawValue: rawValue) {
18 | return value
19 | }
20 | fatalError()
21 | }
22 |
23 | var parameterID: String {
24 | let prefix = "paramID:"
25 | switch self {
26 | case .gain: return prefix + "Gain"
27 | }
28 | }
29 |
30 | var name: String {
31 | switch self {
32 | case .gain: return "Gain"
33 | }
34 | }
35 |
36 | var min: AUValue {
37 | switch self {
38 | case .gain: return 0
39 | }
40 | }
41 |
42 | var max: AUValue {
43 | switch self {
44 | case .gain: return 1
45 | }
46 | }
47 |
48 | var defaultValue: AUValue {
49 | switch self {
50 | case .gain: return 1
51 | }
52 | }
53 |
54 | func stringFromValue(value: AUValue) -> String {
55 | switch self {
56 | case .gain: return "\(value)"
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/AttenuatorAU/DSP/AttenuatorDSP.mm:
--------------------------------------------------------------------------------
1 | //
2 | // AttenuatorDSP.mm
3 | // AttenuatorAU
4 | //
5 | // Created by Vlad Gorlov on 05.04.20.
6 | // Copyright © 2020 Vlad Gorlov. All rights reserved.
7 | //
8 |
9 | #include "AttenuatorDSP.h"
10 |
11 | @implementation AttenuatorDSP
12 |
13 | - (instancetype)init {
14 | self = [super init];
15 | if (self) {
16 | self.paramGain = 1;
17 | }
18 | return self;
19 | }
20 |
21 | - (void)process:(AUAudioFrameCount)frameCount inBufferListPtr:(AudioBufferList*)inBufferListPtr outBufferListPtr:(AudioBufferList*)outBufferListPtr {
22 |
23 | _maximumMagnitude = 0;
24 | for (int channel = 0; channel < _numberOfChannels; ++channel) {
25 | // Get pointer to immutable input buffer and mutable output buffer
26 | const float* inPtr = (float*)inBufferListPtr->mBuffers[channel].mData;
27 | float* outPtr = (float*)outBufferListPtr->mBuffers[channel].mData;
28 |
29 | // Perform per sample dsp on the incoming float `inPtr` before asigning it to `outPtr`
30 | for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
31 | float value = inPtr[frameIndex];
32 | if (!_isBypassed) {
33 | value *= _paramGain;
34 | }
35 | outPtr[frameIndex] = value;
36 | _maximumMagnitude = fmax(_maximumMagnitude, value);
37 | }
38 | }
39 | }
40 |
41 | @end
42 |
--------------------------------------------------------------------------------
/AttenuatorAU/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | AttenuatorAU
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSExtension
26 |
27 | NSExtensionAttributes
28 |
29 | AudioComponents
30 |
31 |
32 | description
33 | AttenuatorAU
34 | factoryFunction
35 | $(PRODUCT_MODULE_NAME).AudioUnitViewController
36 | manufacturer
37 | HOME
38 | name
39 | HOME: AttenuatorAU
40 | sandboxSafe
41 |
42 | subtype
43 | attr
44 | tags
45 |
46 | Effects
47 |
48 | type
49 | aufx
50 | version
51 | 67072
52 |
53 |
54 |
55 | NSExtensionPointIdentifier
56 | com.apple.AudioUnit-UI
57 | NSExtensionPrincipalClass
58 | $(PRODUCT_MODULE_NAME).AudioUnitViewController
59 |
60 | NSHumanReadableCopyright
61 | Copyright © 2020 Vlad Gorlov. All rights reserved.
62 |
63 |
64 |
--------------------------------------------------------------------------------
/AttenuatorAU/UI/AudioUnitViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AudioUnitViewController.swift
3 | // AttenuatorAU
4 | //
5 | // Created by Vlad Gorlov on 04.04.20.
6 | // Copyright © 2020 Vlad Gorlov. All rights reserved.
7 | //
8 |
9 | import CoreAudioKit
10 |
11 | public class AudioUnitViewController: AUViewController, AUAudioUnitFactory {
12 |
13 | private lazy var auView = MainView()
14 | var audioUnit: AttenuatorAudioUnit?
15 | private var parameterObserverToken: AUParameterObserverToken?
16 | private var isConfigured = false
17 |
18 | public override func loadView() {
19 | auView.needsLayout = true
20 | auView.layoutSubtreeIfNeeded()
21 | view = auView
22 | }
23 |
24 | public override func viewDidLoad() {
25 | super.viewDidLoad()
26 | setupViewIfNeeded()
27 | }
28 |
29 | public func createAudioUnit(with componentDescription: AudioComponentDescription) throws -> AUAudioUnit {
30 | let au = try AttenuatorAudioUnit(componentDescription: componentDescription, options: [])
31 | audioUnit = au
32 | DispatchQueue.main.async {
33 | self.setupViewIfNeeded()
34 | }
35 | return au
36 | }
37 |
38 | private func setupViewIfNeeded() {
39 | if !isConfigured, let au = audioUnit {
40 | isConfigured = true
41 | setupUI(au: au)
42 | }
43 | }
44 |
45 | private func setupUI(au: AttenuatorAudioUnit) {
46 | auView.setGain(au.parameterGain.value)
47 | parameterObserverToken = au.parameterTree?.token(byAddingParameterObserver: { address, value in
48 | DispatchQueue.main.async { [weak self] in
49 | let paramType = AttenuatorParameter.fromRawValue(address)
50 | switch paramType {
51 | case .gain:
52 | self?.auView.setGain(value)
53 | }
54 | }
55 | })
56 | auView.onDidChange = { [weak self] value in
57 | if let token = self?.parameterObserverToken {
58 | self?.audioUnit?.parameterGain?.setValue(value, originator: token)
59 | }
60 | }
61 | auView.onRender = { [weak self] in
62 | self?.audioUnit?.maximumMagnitude ?? 0
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/AttenuatorAU/UI/MainView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainView.swift
3 | // AttenuatorAU
4 | //
5 | // Created by Vlad Gorlov on 05.04.20.
6 | // Copyright © 2020 Vlad Gorlov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftUI
11 |
12 | final class SliderData: ObservableObject {
13 |
14 | @Published var gain: Float = 100
15 | }
16 |
17 | class MainView: NSView {
18 |
19 | private let sliderData = SliderData()
20 | private var vuView: VUView?
21 | private let stackView = NSStackView()
22 |
23 | var onDidChange: ((Float) -> Void)?
24 | var onRender: (() -> Float)?
25 |
26 | override init(frame frameRect: NSRect) {
27 | super.init(frame: frameRect)
28 | wantsLayer = true
29 | layer?.backgroundColor = NSColor.lightGray.cgColor
30 |
31 | do {
32 | let vuView = try VUView()
33 | stackView.addArrangedSubview(vuView)
34 | vuView.onRender = { [weak self] in
35 | return self?.onRender?() ?? 0
36 | }
37 | vuView.heightAnchor.constraint(equalToConstant: 30).isActive = true
38 | self.vuView = vuView
39 | } catch {
40 | assertionFailure()
41 | print(String(describing: error))
42 | }
43 |
44 | let view = NSHostingView(rootView: MainUI { [weak self] in
45 | let value = $0 / 100
46 | print("MainView> Value to Host: \(value)")
47 | self?.onDidChange?(value)
48 | }.environmentObject(sliderData))
49 | stackView.addArrangedSubview(view)
50 | view.widthAnchor.constraint(equalTo: stackView.widthAnchor).isActive = true
51 |
52 | stackView.orientation = .vertical
53 | stackView.translatesAutoresizingMaskIntoConstraints = false
54 | addSubview(stackView)
55 |
56 | leadingAnchor.constraint(equalTo: stackView.leadingAnchor).isActive = true
57 | trailingAnchor.constraint(equalTo: stackView.trailingAnchor).isActive = true
58 | topAnchor.constraint(equalTo: stackView.topAnchor).isActive = true
59 | bottomAnchor.constraint(equalTo: stackView.bottomAnchor).isActive = true
60 |
61 | widthAnchor.constraint(greaterThanOrEqualToConstant: 260).isActive = true
62 | }
63 |
64 | required dynamic init?(coder aDecoder: NSCoder) {
65 | fatalError()
66 | }
67 |
68 | func setGain(_ value: Float) {
69 | print("MainView> Value from Host: \(value)")
70 | sliderData.gain = 100 * value
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Attenuator.xcodeproj/xcshareddata/xcschemes/Attenuator.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
45 |
51 |
52 |
53 |
54 |
60 |
62 |
68 |
69 |
70 |
71 |
73 |
74 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/Attenuator.xcodeproj/xcshareddata/xcschemes/AttenuatorAU.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
6 |
9 |
10 |
16 |
22 |
23 |
24 |
30 |
36 |
37 |
38 |
39 |
40 |
45 |
46 |
47 |
48 |
59 |
63 |
64 |
65 |
71 |
72 |
73 |
74 |
81 |
83 |
89 |
90 |
91 |
92 |
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/AttenuatorAU/UI/VUView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VUView.swift
3 | // AttenuatorAU
4 | //
5 | // Created by Vlad Gorlov on 05.04.20.
6 | // Copyright © 2020 Vlad Gorlov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import MetalKit
11 |
12 | class VUView: MTKView {
13 |
14 | public enum Error: Swift.Error {
15 | case unableToInitialize(Any.Type)
16 | }
17 |
18 | private(set) var viewportSize = vector_float2(100, 100)
19 |
20 | private var metalDevice: MTLDevice!
21 | private var library: MTLLibrary!
22 | private var commandQueue: MTLCommandQueue!
23 | private var pipelineState: MTLRenderPipelineState!
24 |
25 | private var colorData = vector_float4(0, 0, 1, 1)
26 | private var verticesData = [vector_float2]()
27 | private var level: Float = 0
28 |
29 | var onRender: (() -> Float)?
30 |
31 | init(thisIsNeededToMakeSwiftCompilerHapy: Bool = true) throws {
32 | let device = MTLCreateSystemDefaultDevice()
33 | super.init(frame: .zero, device: device)
34 |
35 | // Clear color. See: https://forums.developer.apple.com/thread/26461
36 | clearColor = MTLClearColorMake(0, 0, 0, 0)
37 |
38 | if let device = device {
39 | metalDevice = device
40 | colorPixelFormat = MTLPixelFormat.bgra8Unorm // Actually it is default value
41 | delegate = self
42 | } else {
43 | throw Error.unableToInitialize(MTLDevice.self)
44 | }
45 |
46 | guard let url = Bundle(for: type(of: self)).url(forResource: "default", withExtension: "metallib") else {
47 | throw Error.unableToInitialize(URL.self)
48 | }
49 |
50 | library = try metalDevice.makeLibrary(filepath: url.path)
51 | guard let commandQueue = metalDevice.makeCommandQueue() else {
52 | throw Error.unableToInitialize(MTLCommandQueue.self)
53 | }
54 | self.commandQueue = commandQueue
55 |
56 | guard let vertexProgram = library.makeFunction(name: "vertex_line") else {
57 | throw Error.unableToInitialize(MTLFunction.self)
58 | }
59 | guard let fragmentProgram = library.makeFunction(name: "fragment_line") else {
60 | throw Error.unableToInitialize(MTLFunction.self)
61 | }
62 |
63 | let pipelineStateDescriptor = MTLRenderPipelineDescriptor()
64 | pipelineStateDescriptor.vertexFunction = vertexProgram
65 | pipelineStateDescriptor.fragmentFunction = fragmentProgram
66 | // Alternatively can be set from drawable.texture.pixelFormat
67 | pipelineStateDescriptor.colorAttachments[0].pixelFormat = colorPixelFormat
68 | pipelineState = try metalDevice.makeRenderPipelineState(descriptor: pipelineStateDescriptor)
69 | }
70 |
71 | required init(coder: NSCoder) {
72 | fatalError()
73 | }
74 | }
75 |
76 | extension VUView: MTKViewDelegate {
77 |
78 | func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
79 | viewportSize.x = Float(size.width)
80 | viewportSize.y = Float(size.height)
81 | }
82 |
83 | func draw(in view: MTKView) {
84 | if inLiveResize {
85 | return
86 | }
87 | if let drawable = currentDrawable, let descriptor = currentRenderPassDescriptor {
88 | autoreleasepool {
89 | do {
90 | try render(drawable: drawable, renderPassDescriptor: descriptor)
91 | } catch {
92 | print(String(describing: error))
93 | assertionFailure(String(describing: error))
94 | }
95 | }
96 | }
97 | }
98 |
99 | }
100 |
101 | extension VUView {
102 |
103 | func render(drawable: CAMetalDrawable, renderPassDescriptor: MTLRenderPassDescriptor) throws {
104 | guard let commandBuffer = commandQueue.makeCommandBuffer() else {
105 | throw Error.unableToInitialize(MTLCommandBuffer.self)
106 | }
107 |
108 | // Transparent Metal background. See: https://forums.developer.apple.com/thread/26461
109 | renderPassDescriptor.colorAttachments[0].loadAction = .clear
110 |
111 | guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else {
112 | throw Error.unableToInitialize(MTLRenderCommandEncoder.self)
113 | }
114 |
115 | do {
116 | renderEncoder.setRenderPipelineState(pipelineState)
117 |
118 | let width = Double(viewportSize.x)
119 | let height = Double(viewportSize.y)
120 | let viewPort = MTLViewport(originX: 0, originY: 0, width: width, height: height, znear: 0, zfar: 1)
121 | renderEncoder.setViewport(viewPort)
122 | try prepareEncoder(encoder: renderEncoder)
123 |
124 | renderEncoder.endEncoding()
125 |
126 | commandBuffer.present(drawable)
127 | commandBuffer.commit()
128 | } catch {
129 | renderEncoder.endEncoding()
130 | throw error
131 | }
132 | }
133 |
134 | func prepareEncoder(encoder: MTLRenderCommandEncoder) throws {
135 |
136 | verticesData.removeAll(keepingCapacity: true)
137 | level = onRender?() ?? 0
138 | if level <= 0 {
139 | return
140 | }
141 |
142 | let x = max(Float(viewportSize.x * level), 1)
143 | let vertices = makeRectangle(xMin: 0, xMax: x, yMin: 0, yMax: viewportSize.y)
144 | verticesData += vertices
145 |
146 | encoder.setVertexBytes(&verticesData, length: verticesData.count * MemoryLayout.stride, index: 0)
147 | encoder.setVertexBytes(&colorData, length: MemoryLayout.stride, index: 1)
148 | encoder.setVertexBytes(&viewportSize, length: MemoryLayout.stride, index: 2)
149 |
150 | encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: verticesData.count)
151 | }
152 |
153 | func makeRectangle(xMin: Float, xMax: Float, yMin: Float, yMax: Float) -> [vector_float2] {
154 | // Adding 2 triangles to represent recrtangle.
155 | return [vector_float2(xMin, yMin), vector_float2(xMin, yMax), vector_float2(xMax, yMax),
156 | vector_float2(xMin, yMin), vector_float2(xMax, yMax), vector_float2(xMax, yMin)]
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/AttenuatorAU/Audio Unit/AttenuatorAudioUnit.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AttenuatorAudioUnit.swift
3 | // AttenuatorAU
4 | //
5 | // Created by Vlad Gorlov on 05.04.20.
6 | // Copyright © 2020 Vlad Gorlov. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import AudioUnit
11 | import AVFoundation
12 |
13 | class AttenuatorAudioUnit: AUAudioUnit {
14 |
15 | public enum Error: Swift.Error {
16 | case statusError(OSStatus)
17 | case unableToInitialize(String)
18 | }
19 |
20 | private let maxNumberOfChannels: UInt32 = 8
21 | private let maxFramesToRender: UInt32 = 512
22 |
23 | private var _parameterTree: AUParameterTree!
24 | private(set) var parameterGain: AUParameter!
25 |
26 | private let dsp = AttenuatorDSP()
27 |
28 | private var inputBus: AUAudioUnitBus
29 | private var outputBus: AUAudioUnitBus
30 | private var outPCMBuffer: AVAudioPCMBuffer
31 |
32 | private var _inputBusses: AUAudioUnitBusArray!
33 | private var _outputBusses: AUAudioUnitBusArray!
34 |
35 | override init(componentDescription: AudioComponentDescription, options: AudioComponentInstantiationOptions) throws {
36 |
37 | guard let format = AVAudioFormat(standardFormatWithSampleRate: 44100, channels: 2) else {
38 | throw Error.unableToInitialize(String(describing: AVAudioFormat.self))
39 | }
40 | inputBus = try AUAudioUnitBus(format: format)
41 | inputBus.maximumChannelCount = maxNumberOfChannels
42 | outputBus = try AUAudioUnitBus(format: format)
43 | outputBus.maximumChannelCount = maxNumberOfChannels
44 |
45 | guard let pcmBuffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: maxFramesToRender) else {
46 | throw Error.unableToInitialize(String(describing: AVAudioPCMBuffer.self))
47 | }
48 | pcmBuffer.frameLength = maxFramesToRender
49 | outPCMBuffer = pcmBuffer
50 |
51 | dsp.numberOfChannels = format.channelCount
52 | dsp.paramGain = AttenuatorParameter.gain.defaultValue
53 |
54 | try super.init(componentDescription: componentDescription, options: options)
55 | self.maximumFramesToRender = maxFramesToRender
56 |
57 | _parameterTree = setUpParametersTree()
58 | _inputBusses = AUAudioUnitBusArray(audioUnit: self, busType: AUAudioUnitBusType.input, busses: [inputBus])
59 | _outputBusses = AUAudioUnitBusArray(audioUnit: self, busType: AUAudioUnitBusType.output, busses: [outputBus])
60 | }
61 |
62 | override var parameterTree: AUParameterTree? {
63 | get {
64 | return _parameterTree
65 | } set {
66 | fatalError()
67 | }
68 | }
69 |
70 | override var shouldBypassEffect: Bool {
71 | get {
72 | return dsp.isBypassed
73 | } set {
74 | dsp.isBypassed = newValue
75 | }
76 | }
77 |
78 | public override var inputBusses: AUAudioUnitBusArray {
79 | return _inputBusses
80 | }
81 |
82 | public override var outputBusses: AUAudioUnitBusArray {
83 | return _outputBusses
84 | }
85 |
86 | override func allocateRenderResources() throws {
87 | // Should be equal as we created it with the same format.
88 | if outputBus.format.channelCount != inputBus.format.channelCount {
89 | setRenderResourcesAllocated(false)
90 | throw Error.statusError(kAudioUnitErr_FailedInitialization)
91 | }
92 | try super.allocateRenderResources()
93 | guard let pcmBuffer = AVAudioPCMBuffer(pcmFormat: inputBus.format, frameCapacity: maximumFramesToRender) else {
94 | throw Error.unableToInitialize(String(describing: AVAudioPCMBuffer.self))
95 | }
96 | pcmBuffer.frameLength = maxFramesToRender
97 | outPCMBuffer = pcmBuffer
98 | dsp.numberOfChannels = outputBus.format.channelCount
99 | }
100 |
101 | override var internalRenderBlock: AUInternalRenderBlock {
102 | return { [weak self] _, timestamp, frameCount, outputBusNumber, outputData, _, pullInputBlock in
103 |
104 | guard let this = self else {
105 | return kAudioUnitErr_NoConnection
106 | }
107 | if frameCount > this.maximumFramesToRender {
108 | return kAudioUnitErr_TooManyFramesToProcess;
109 | }
110 |
111 | guard let pullInputBlock = pullInputBlock else {
112 | return kAudioUnitErr_NoConnection
113 | }
114 |
115 | var pullFlags: AudioUnitRenderActionFlags = []
116 |
117 | let inputData = this.outPCMBuffer.mutableAudioBufferList
118 | // Instead of `inputBusNumber` we can also pass `0`
119 | let status = pullInputBlock(&pullFlags, timestamp, frameCount, outputBusNumber, inputData)
120 |
121 | if status != noErr {
122 | return status
123 | }
124 |
125 | /*
126 | Important:
127 | If the caller passed non-null output pointers (outputData->mBuffers[x].mData), use those.
128 |
129 | If the caller passed null output buffer pointers, process in memory owned by the Audio Unit
130 | and modify the (outputData->mBuffers[x].mData) pointers to point to this owned memory.
131 | The Audio Unit is responsible for preserving the validity of this memory until the next call to render,
132 | or deallocateRenderResources is called.
133 |
134 | If your algorithm cannot process in-place, you will need to preallocate an output buffer
135 | and use it here.
136 |
137 | See the description of the canProcessInPlace property.
138 | */
139 | let inListPointer = UnsafeMutableAudioBufferListPointer(inputData)
140 | let outListPointer = UnsafeMutableAudioBufferListPointer(outputData)
141 | for indexOfBuffer in 0 ..< outListPointer.count {
142 | // Shpuld be equal by default.
143 | outListPointer[indexOfBuffer].mNumberChannels = inListPointer[indexOfBuffer].mNumberChannels
144 | outListPointer[indexOfBuffer].mDataByteSize = inListPointer[indexOfBuffer].mDataByteSize
145 | if outListPointer[indexOfBuffer].mData == nil {
146 | outListPointer[indexOfBuffer].mData = inListPointer[indexOfBuffer].mData
147 | }
148 | }
149 |
150 | this.dsp.process(frameCount, inBufferListPtr: inputData, outBufferListPtr: outputData)
151 |
152 | return status
153 | }
154 | }
155 |
156 | // MARK: -
157 |
158 | var maximumMagnitude: Float {
159 | return dsp.maximumMagnitude
160 | }
161 |
162 | // MARK: - Private
163 |
164 | private func setUpParametersTree() -> AUParameterTree {
165 | let pGain = AttenuatorParameter.gain
166 | parameterGain = AUParameterTree.createParameter(withIdentifier: pGain.parameterID,
167 | name: pGain.name, address: pGain.rawValue, min: pGain.min, max: pGain.max,
168 | unit: AudioUnitParameterUnit.linearGain, unitName: nil, flags: [],
169 | valueStrings: nil, dependentParameters: nil)
170 | parameterGain.value = pGain.defaultValue
171 | let tree = AUParameterTree.createTree(withChildren: [parameterGain])
172 | tree.implementorStringFromValueCallback = { param, value in
173 | guard let paramValue = value?.pointee else {
174 | return "-"
175 | }
176 | let param = AttenuatorParameter.fromRawValue(param.address)
177 | return param.stringFromValue(value: paramValue)
178 | }
179 | tree.implementorValueObserver = { [weak self] param, value in
180 | let param = AttenuatorParameter.fromRawValue(param.address)
181 | switch param {
182 | case .gain:
183 | self?.dsp.paramGain = value
184 | }
185 | }
186 | tree.implementorValueProvider = { [weak self] param in guard let s = self else { return AUValue() }
187 | let param = AttenuatorParameter.fromRawValue(param.address)
188 | switch param {
189 | case .gain:
190 | return s.dsp.paramGain;
191 | }
192 | }
193 | return tree
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/Attenuator.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 8042BF7D2439204C00D1477E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8042BF7C2439204C00D1477E /* AppDelegate.swift */; };
11 | 8042BF7F2439204C00D1477E /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8042BF7E2439204C00D1477E /* ContentView.swift */; };
12 | 8042BF812439204D00D1477E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8042BF802439204D00D1477E /* Assets.xcassets */; };
13 | 8042BF872439204D00D1477E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8042BF852439204D00D1477E /* Main.storyboard */; };
14 | 8042BFA82439220900D1477E /* AudioUnitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8042BFA72439220900D1477E /* AudioUnitViewController.swift */; };
15 | 8042BFB12439220900D1477E /* AttenuatorAU.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 8042BF932439220900D1477E /* AttenuatorAU.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
16 | 8042BFB92439498E00D1477E /* MainUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8042BFB82439498E00D1477E /* MainUI.swift */; };
17 | 8042BFBB24394A4A00D1477E /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8042BFBA24394A4A00D1477E /* MainView.swift */; };
18 | 8042BFBD24394B8900D1477E /* AttenuatorParameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8042BFBC24394B8900D1477E /* AttenuatorParameter.swift */; };
19 | 8042BFBF24394EB100D1477E /* AttenuatorAudioUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8042BFBE24394EB100D1477E /* AttenuatorAudioUnit.swift */; };
20 | 8042BFC52439C5D600D1477E /* AttenuatorDSP.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8042BFC42439C5D600D1477E /* AttenuatorDSP.mm */; };
21 | 8042BFC7243A0CAC00D1477E /* VUView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8042BFC6243A0CAC00D1477E /* VUView.swift */; };
22 | 8042BFC9243A144E00D1477E /* VUView.metal in Sources */ = {isa = PBXBuildFile; fileRef = 8042BFC8243A144E00D1477E /* VUView.metal */; };
23 | /* End PBXBuildFile section */
24 |
25 | /* Begin PBXContainerItemProxy section */
26 | 8042BFAF2439220900D1477E /* PBXContainerItemProxy */ = {
27 | isa = PBXContainerItemProxy;
28 | containerPortal = 8042BF712439204C00D1477E /* Project object */;
29 | proxyType = 1;
30 | remoteGlobalIDString = 8042BF922439220900D1477E;
31 | remoteInfo = AttenuatorAU;
32 | };
33 | /* End PBXContainerItemProxy section */
34 |
35 | /* Begin PBXCopyFilesBuildPhase section */
36 | 8042BFB52439220900D1477E /* Embed App Extensions */ = {
37 | isa = PBXCopyFilesBuildPhase;
38 | buildActionMask = 2147483647;
39 | dstPath = "";
40 | dstSubfolderSpec = 13;
41 | files = (
42 | 8042BFB12439220900D1477E /* AttenuatorAU.appex in Embed App Extensions */,
43 | );
44 | name = "Embed App Extensions";
45 | runOnlyForDeploymentPostprocessing = 0;
46 | };
47 | /* End PBXCopyFilesBuildPhase section */
48 |
49 | /* Begin PBXFileReference section */
50 | 8042BF792439204C00D1477E /* Attenuator.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Attenuator.app; sourceTree = BUILT_PRODUCTS_DIR; };
51 | 8042BF7C2439204C00D1477E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
52 | 8042BF7E2439204C00D1477E /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
53 | 8042BF802439204D00D1477E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
54 | 8042BF862439204D00D1477E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
55 | 8042BF882439204D00D1477E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
56 | 8042BF892439204D00D1477E /* Attenuator.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Attenuator.entitlements; sourceTree = ""; };
57 | 8042BF932439220900D1477E /* AttenuatorAU.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = AttenuatorAU.appex; sourceTree = BUILT_PRODUCTS_DIR; };
58 | 8042BFA72439220900D1477E /* AudioUnitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioUnitViewController.swift; sourceTree = ""; };
59 | 8042BFA92439220900D1477E /* AttenuatorAU-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AttenuatorAU-Bridging-Header.h"; sourceTree = ""; };
60 | 8042BFAD2439220900D1477E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
61 | 8042BFAE2439220900D1477E /* AttenuatorAU.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AttenuatorAU.entitlements; sourceTree = ""; };
62 | 8042BFB82439498E00D1477E /* MainUI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainUI.swift; sourceTree = ""; };
63 | 8042BFBA24394A4A00D1477E /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; };
64 | 8042BFBC24394B8900D1477E /* AttenuatorParameter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttenuatorParameter.swift; sourceTree = ""; };
65 | 8042BFBE24394EB100D1477E /* AttenuatorAudioUnit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttenuatorAudioUnit.swift; sourceTree = ""; };
66 | 8042BFC02439531900D1477E /* AttenuatorDSP.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AttenuatorDSP.h; sourceTree = ""; };
67 | 8042BFC42439C5D600D1477E /* AttenuatorDSP.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AttenuatorDSP.mm; sourceTree = ""; };
68 | 8042BFC6243A0CAC00D1477E /* VUView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VUView.swift; sourceTree = ""; };
69 | 8042BFC8243A144E00D1477E /* VUView.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = VUView.metal; sourceTree = ""; };
70 | /* End PBXFileReference section */
71 |
72 | /* Begin PBXFrameworksBuildPhase section */
73 | 8042BF762439204C00D1477E /* Frameworks */ = {
74 | isa = PBXFrameworksBuildPhase;
75 | buildActionMask = 2147483647;
76 | files = (
77 | );
78 | runOnlyForDeploymentPostprocessing = 0;
79 | };
80 | 8042BF902439220900D1477E /* Frameworks */ = {
81 | isa = PBXFrameworksBuildPhase;
82 | buildActionMask = 2147483647;
83 | files = (
84 | );
85 | runOnlyForDeploymentPostprocessing = 0;
86 | };
87 | /* End PBXFrameworksBuildPhase section */
88 |
89 | /* Begin PBXGroup section */
90 | 8042BF702439204C00D1477E = {
91 | isa = PBXGroup;
92 | children = (
93 | 8042BF7B2439204C00D1477E /* Attenuator */,
94 | 8042BF942439220900D1477E /* AttenuatorAU */,
95 | 8042BF7A2439204C00D1477E /* Products */,
96 | );
97 | sourceTree = "";
98 | };
99 | 8042BF7A2439204C00D1477E /* Products */ = {
100 | isa = PBXGroup;
101 | children = (
102 | 8042BF792439204C00D1477E /* Attenuator.app */,
103 | 8042BF932439220900D1477E /* AttenuatorAU.appex */,
104 | );
105 | name = Products;
106 | sourceTree = "";
107 | };
108 | 8042BF7B2439204C00D1477E /* Attenuator */ = {
109 | isa = PBXGroup;
110 | children = (
111 | 8042BF7C2439204C00D1477E /* AppDelegate.swift */,
112 | 8042BF7E2439204C00D1477E /* ContentView.swift */,
113 | 8042BF802439204D00D1477E /* Assets.xcassets */,
114 | 8042BF852439204D00D1477E /* Main.storyboard */,
115 | 8042BF882439204D00D1477E /* Info.plist */,
116 | 8042BF892439204D00D1477E /* Attenuator.entitlements */,
117 | );
118 | path = Attenuator;
119 | sourceTree = "";
120 | };
121 | 8042BF942439220900D1477E /* AttenuatorAU */ = {
122 | isa = PBXGroup;
123 | children = (
124 | 8042BFA92439220900D1477E /* AttenuatorAU-Bridging-Header.h */,
125 | 8042BFAD2439220900D1477E /* Info.plist */,
126 | 8042BFAE2439220900D1477E /* AttenuatorAU.entitlements */,
127 | 8042BF952439220900D1477E /* Audio Unit */,
128 | 8042BF992439220900D1477E /* DSP */,
129 | 8042BFA62439220900D1477E /* UI */,
130 | );
131 | path = AttenuatorAU;
132 | sourceTree = "";
133 | };
134 | 8042BF952439220900D1477E /* Audio Unit */ = {
135 | isa = PBXGroup;
136 | children = (
137 | 8042BFBE24394EB100D1477E /* AttenuatorAudioUnit.swift */,
138 | 8042BFBC24394B8900D1477E /* AttenuatorParameter.swift */,
139 | );
140 | path = "Audio Unit";
141 | sourceTree = "";
142 | };
143 | 8042BF992439220900D1477E /* DSP */ = {
144 | isa = PBXGroup;
145 | children = (
146 | 8042BFC02439531900D1477E /* AttenuatorDSP.h */,
147 | 8042BFC42439C5D600D1477E /* AttenuatorDSP.mm */,
148 | );
149 | path = DSP;
150 | sourceTree = "";
151 | };
152 | 8042BFA62439220900D1477E /* UI */ = {
153 | isa = PBXGroup;
154 | children = (
155 | 8042BFA72439220900D1477E /* AudioUnitViewController.swift */,
156 | 8042BFB82439498E00D1477E /* MainUI.swift */,
157 | 8042BFBA24394A4A00D1477E /* MainView.swift */,
158 | 8042BFC8243A144E00D1477E /* VUView.metal */,
159 | 8042BFC6243A0CAC00D1477E /* VUView.swift */,
160 | );
161 | path = UI;
162 | sourceTree = "";
163 | };
164 | /* End PBXGroup section */
165 |
166 | /* Begin PBXNativeTarget section */
167 | 8042BF782439204C00D1477E /* Attenuator */ = {
168 | isa = PBXNativeTarget;
169 | buildConfigurationList = 8042BF8C2439204D00D1477E /* Build configuration list for PBXNativeTarget "Attenuator" */;
170 | buildPhases = (
171 | 8042BF752439204C00D1477E /* Sources */,
172 | 8042BF762439204C00D1477E /* Frameworks */,
173 | 8042BF772439204C00D1477E /* Resources */,
174 | 8042BFB52439220900D1477E /* Embed App Extensions */,
175 | );
176 | buildRules = (
177 | );
178 | dependencies = (
179 | 8042BFB02439220900D1477E /* PBXTargetDependency */,
180 | );
181 | name = Attenuator;
182 | productName = Attenuator;
183 | productReference = 8042BF792439204C00D1477E /* Attenuator.app */;
184 | productType = "com.apple.product-type.application";
185 | };
186 | 8042BF922439220900D1477E /* AttenuatorAU */ = {
187 | isa = PBXNativeTarget;
188 | buildConfigurationList = 8042BFB22439220900D1477E /* Build configuration list for PBXNativeTarget "AttenuatorAU" */;
189 | buildPhases = (
190 | 8042BF8F2439220900D1477E /* Sources */,
191 | 8042BF902439220900D1477E /* Frameworks */,
192 | 8042BF912439220900D1477E /* Resources */,
193 | );
194 | buildRules = (
195 | );
196 | dependencies = (
197 | );
198 | name = AttenuatorAU;
199 | productName = AttenuatorAU;
200 | productReference = 8042BF932439220900D1477E /* AttenuatorAU.appex */;
201 | productType = "com.apple.product-type.app-extension";
202 | };
203 | /* End PBXNativeTarget section */
204 |
205 | /* Begin PBXProject section */
206 | 8042BF712439204C00D1477E /* Project object */ = {
207 | isa = PBXProject;
208 | attributes = {
209 | LastSwiftUpdateCheck = 1140;
210 | LastUpgradeCheck = 1140;
211 | ORGANIZATIONNAME = "Vlad Gorlov";
212 | TargetAttributes = {
213 | 8042BF782439204C00D1477E = {
214 | CreatedOnToolsVersion = 11.4;
215 | };
216 | 8042BF922439220900D1477E = {
217 | CreatedOnToolsVersion = 11.4;
218 | };
219 | };
220 | };
221 | buildConfigurationList = 8042BF742439204C00D1477E /* Build configuration list for PBXProject "Attenuator" */;
222 | compatibilityVersion = "Xcode 9.3";
223 | developmentRegion = en;
224 | hasScannedForEncodings = 0;
225 | knownRegions = (
226 | en,
227 | Base,
228 | );
229 | mainGroup = 8042BF702439204C00D1477E;
230 | productRefGroup = 8042BF7A2439204C00D1477E /* Products */;
231 | projectDirPath = "";
232 | projectRoot = "";
233 | targets = (
234 | 8042BF782439204C00D1477E /* Attenuator */,
235 | 8042BF922439220900D1477E /* AttenuatorAU */,
236 | );
237 | };
238 | /* End PBXProject section */
239 |
240 | /* Begin PBXResourcesBuildPhase section */
241 | 8042BF772439204C00D1477E /* Resources */ = {
242 | isa = PBXResourcesBuildPhase;
243 | buildActionMask = 2147483647;
244 | files = (
245 | 8042BF872439204D00D1477E /* Main.storyboard in Resources */,
246 | 8042BF812439204D00D1477E /* Assets.xcassets in Resources */,
247 | );
248 | runOnlyForDeploymentPostprocessing = 0;
249 | };
250 | 8042BF912439220900D1477E /* Resources */ = {
251 | isa = PBXResourcesBuildPhase;
252 | buildActionMask = 2147483647;
253 | files = (
254 | );
255 | runOnlyForDeploymentPostprocessing = 0;
256 | };
257 | /* End PBXResourcesBuildPhase section */
258 |
259 | /* Begin PBXSourcesBuildPhase section */
260 | 8042BF752439204C00D1477E /* Sources */ = {
261 | isa = PBXSourcesBuildPhase;
262 | buildActionMask = 2147483647;
263 | files = (
264 | 8042BF7F2439204C00D1477E /* ContentView.swift in Sources */,
265 | 8042BF7D2439204C00D1477E /* AppDelegate.swift in Sources */,
266 | );
267 | runOnlyForDeploymentPostprocessing = 0;
268 | };
269 | 8042BF8F2439220900D1477E /* Sources */ = {
270 | isa = PBXSourcesBuildPhase;
271 | buildActionMask = 2147483647;
272 | files = (
273 | 8042BFA82439220900D1477E /* AudioUnitViewController.swift in Sources */,
274 | 8042BFC7243A0CAC00D1477E /* VUView.swift in Sources */,
275 | 8042BFBF24394EB100D1477E /* AttenuatorAudioUnit.swift in Sources */,
276 | 8042BFC9243A144E00D1477E /* VUView.metal in Sources */,
277 | 8042BFB92439498E00D1477E /* MainUI.swift in Sources */,
278 | 8042BFC52439C5D600D1477E /* AttenuatorDSP.mm in Sources */,
279 | 8042BFBB24394A4A00D1477E /* MainView.swift in Sources */,
280 | 8042BFBD24394B8900D1477E /* AttenuatorParameter.swift in Sources */,
281 | );
282 | runOnlyForDeploymentPostprocessing = 0;
283 | };
284 | /* End PBXSourcesBuildPhase section */
285 |
286 | /* Begin PBXTargetDependency section */
287 | 8042BFB02439220900D1477E /* PBXTargetDependency */ = {
288 | isa = PBXTargetDependency;
289 | target = 8042BF922439220900D1477E /* AttenuatorAU */;
290 | targetProxy = 8042BFAF2439220900D1477E /* PBXContainerItemProxy */;
291 | };
292 | /* End PBXTargetDependency section */
293 |
294 | /* Begin PBXVariantGroup section */
295 | 8042BF852439204D00D1477E /* Main.storyboard */ = {
296 | isa = PBXVariantGroup;
297 | children = (
298 | 8042BF862439204D00D1477E /* Base */,
299 | );
300 | name = Main.storyboard;
301 | sourceTree = "";
302 | };
303 | /* End PBXVariantGroup section */
304 |
305 | /* Begin XCBuildConfiguration section */
306 | 8042BF8A2439204D00D1477E /* Debug */ = {
307 | isa = XCBuildConfiguration;
308 | buildSettings = {
309 | ALWAYS_SEARCH_USER_PATHS = NO;
310 | CLANG_ANALYZER_NONNULL = YES;
311 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
312 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
313 | CLANG_CXX_LIBRARY = "libc++";
314 | CLANG_ENABLE_MODULES = YES;
315 | CLANG_ENABLE_OBJC_ARC = YES;
316 | CLANG_ENABLE_OBJC_WEAK = YES;
317 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
318 | CLANG_WARN_BOOL_CONVERSION = YES;
319 | CLANG_WARN_COMMA = YES;
320 | CLANG_WARN_CONSTANT_CONVERSION = YES;
321 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
322 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
323 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
324 | CLANG_WARN_EMPTY_BODY = YES;
325 | CLANG_WARN_ENUM_CONVERSION = YES;
326 | CLANG_WARN_INFINITE_RECURSION = YES;
327 | CLANG_WARN_INT_CONVERSION = YES;
328 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
329 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
330 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
331 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
332 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
333 | CLANG_WARN_STRICT_PROTOTYPES = YES;
334 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
335 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
336 | CLANG_WARN_UNREACHABLE_CODE = YES;
337 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
338 | COPY_PHASE_STRIP = NO;
339 | DEBUG_INFORMATION_FORMAT = dwarf;
340 | ENABLE_STRICT_OBJC_MSGSEND = YES;
341 | ENABLE_TESTABILITY = YES;
342 | GCC_C_LANGUAGE_STANDARD = gnu11;
343 | GCC_DYNAMIC_NO_PIC = NO;
344 | GCC_NO_COMMON_BLOCKS = YES;
345 | GCC_OPTIMIZATION_LEVEL = 0;
346 | GCC_PREPROCESSOR_DEFINITIONS = (
347 | "DEBUG=1",
348 | "$(inherited)",
349 | );
350 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
351 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
352 | GCC_WARN_UNDECLARED_SELECTOR = YES;
353 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
354 | GCC_WARN_UNUSED_FUNCTION = YES;
355 | GCC_WARN_UNUSED_VARIABLE = YES;
356 | MACOSX_DEPLOYMENT_TARGET = 10.15;
357 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
358 | MTL_FAST_MATH = YES;
359 | ONLY_ACTIVE_ARCH = YES;
360 | SDKROOT = macosx;
361 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
362 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
363 | };
364 | name = Debug;
365 | };
366 | 8042BF8B2439204D00D1477E /* Release */ = {
367 | isa = XCBuildConfiguration;
368 | buildSettings = {
369 | ALWAYS_SEARCH_USER_PATHS = NO;
370 | CLANG_ANALYZER_NONNULL = YES;
371 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
372 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
373 | CLANG_CXX_LIBRARY = "libc++";
374 | CLANG_ENABLE_MODULES = YES;
375 | CLANG_ENABLE_OBJC_ARC = YES;
376 | CLANG_ENABLE_OBJC_WEAK = YES;
377 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
378 | CLANG_WARN_BOOL_CONVERSION = YES;
379 | CLANG_WARN_COMMA = YES;
380 | CLANG_WARN_CONSTANT_CONVERSION = YES;
381 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
382 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
383 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
384 | CLANG_WARN_EMPTY_BODY = YES;
385 | CLANG_WARN_ENUM_CONVERSION = YES;
386 | CLANG_WARN_INFINITE_RECURSION = YES;
387 | CLANG_WARN_INT_CONVERSION = YES;
388 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
389 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
390 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
391 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
392 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
393 | CLANG_WARN_STRICT_PROTOTYPES = YES;
394 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
395 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
396 | CLANG_WARN_UNREACHABLE_CODE = YES;
397 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
398 | COPY_PHASE_STRIP = NO;
399 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
400 | ENABLE_NS_ASSERTIONS = NO;
401 | ENABLE_STRICT_OBJC_MSGSEND = YES;
402 | GCC_C_LANGUAGE_STANDARD = gnu11;
403 | GCC_NO_COMMON_BLOCKS = YES;
404 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
405 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
406 | GCC_WARN_UNDECLARED_SELECTOR = YES;
407 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
408 | GCC_WARN_UNUSED_FUNCTION = YES;
409 | GCC_WARN_UNUSED_VARIABLE = YES;
410 | MACOSX_DEPLOYMENT_TARGET = 10.15;
411 | MTL_ENABLE_DEBUG_INFO = NO;
412 | MTL_FAST_MATH = YES;
413 | SDKROOT = macosx;
414 | SWIFT_COMPILATION_MODE = wholemodule;
415 | SWIFT_OPTIMIZATION_LEVEL = "-O";
416 | };
417 | name = Release;
418 | };
419 | 8042BF8D2439204D00D1477E /* Debug */ = {
420 | isa = XCBuildConfiguration;
421 | buildSettings = {
422 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
423 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
424 | CODE_SIGN_ENTITLEMENTS = Attenuator/Attenuator.entitlements;
425 | CODE_SIGN_STYLE = Automatic;
426 | COMBINE_HIDPI_IMAGES = YES;
427 | DEVELOPMENT_ASSET_PATHS = "";
428 | DEVELOPMENT_TEAM = TAGGGN65SW;
429 | ENABLE_HARDENED_RUNTIME = YES;
430 | ENABLE_PREVIEWS = YES;
431 | INFOPLIST_FILE = Attenuator/Info.plist;
432 | LD_RUNPATH_SEARCH_PATHS = (
433 | "$(inherited)",
434 | "@executable_path/../Frameworks",
435 | );
436 | MACOSX_DEPLOYMENT_TARGET = 10.15;
437 | PRODUCT_BUNDLE_IDENTIFIER = abc.example.Attenuator;
438 | PRODUCT_NAME = "$(TARGET_NAME)";
439 | SWIFT_VERSION = 5.0;
440 | };
441 | name = Debug;
442 | };
443 | 8042BF8E2439204D00D1477E /* Release */ = {
444 | isa = XCBuildConfiguration;
445 | buildSettings = {
446 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
447 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
448 | CODE_SIGN_ENTITLEMENTS = Attenuator/Attenuator.entitlements;
449 | CODE_SIGN_STYLE = Automatic;
450 | COMBINE_HIDPI_IMAGES = YES;
451 | DEVELOPMENT_ASSET_PATHS = "";
452 | DEVELOPMENT_TEAM = TAGGGN65SW;
453 | ENABLE_HARDENED_RUNTIME = YES;
454 | ENABLE_PREVIEWS = YES;
455 | INFOPLIST_FILE = Attenuator/Info.plist;
456 | LD_RUNPATH_SEARCH_PATHS = (
457 | "$(inherited)",
458 | "@executable_path/../Frameworks",
459 | );
460 | MACOSX_DEPLOYMENT_TARGET = 10.15;
461 | PRODUCT_BUNDLE_IDENTIFIER = abc.example.Attenuator;
462 | PRODUCT_NAME = "$(TARGET_NAME)";
463 | SWIFT_VERSION = 5.0;
464 | };
465 | name = Release;
466 | };
467 | 8042BFB32439220900D1477E /* Debug */ = {
468 | isa = XCBuildConfiguration;
469 | buildSettings = {
470 | CODE_SIGN_ENTITLEMENTS = AttenuatorAU/AttenuatorAU.entitlements;
471 | CODE_SIGN_STYLE = Automatic;
472 | DEVELOPMENT_TEAM = TAGGGN65SW;
473 | ENABLE_HARDENED_RUNTIME = YES;
474 | INFOPLIST_FILE = AttenuatorAU/Info.plist;
475 | LD_RUNPATH_SEARCH_PATHS = (
476 | "$(inherited)",
477 | "@executable_path/../Frameworks",
478 | "@executable_path/../../../../Frameworks",
479 | );
480 | PRODUCT_BUNDLE_IDENTIFIER = abc.example.Attenuator.AttenuatorAU;
481 | PRODUCT_NAME = "$(TARGET_NAME)";
482 | SKIP_INSTALL = YES;
483 | SWIFT_OBJC_BRIDGING_HEADER = "AttenuatorAU/AttenuatorAU-Bridging-Header.h";
484 | SWIFT_VERSION = 5.0;
485 | };
486 | name = Debug;
487 | };
488 | 8042BFB42439220900D1477E /* Release */ = {
489 | isa = XCBuildConfiguration;
490 | buildSettings = {
491 | CODE_SIGN_ENTITLEMENTS = AttenuatorAU/AttenuatorAU.entitlements;
492 | CODE_SIGN_STYLE = Automatic;
493 | DEVELOPMENT_TEAM = TAGGGN65SW;
494 | ENABLE_HARDENED_RUNTIME = YES;
495 | INFOPLIST_FILE = AttenuatorAU/Info.plist;
496 | LD_RUNPATH_SEARCH_PATHS = (
497 | "$(inherited)",
498 | "@executable_path/../Frameworks",
499 | "@executable_path/../../../../Frameworks",
500 | );
501 | PRODUCT_BUNDLE_IDENTIFIER = abc.example.Attenuator.AttenuatorAU;
502 | PRODUCT_NAME = "$(TARGET_NAME)";
503 | SKIP_INSTALL = YES;
504 | SWIFT_OBJC_BRIDGING_HEADER = "AttenuatorAU/AttenuatorAU-Bridging-Header.h";
505 | SWIFT_VERSION = 5.0;
506 | };
507 | name = Release;
508 | };
509 | /* End XCBuildConfiguration section */
510 |
511 | /* Begin XCConfigurationList section */
512 | 8042BF742439204C00D1477E /* Build configuration list for PBXProject "Attenuator" */ = {
513 | isa = XCConfigurationList;
514 | buildConfigurations = (
515 | 8042BF8A2439204D00D1477E /* Debug */,
516 | 8042BF8B2439204D00D1477E /* Release */,
517 | );
518 | defaultConfigurationIsVisible = 0;
519 | defaultConfigurationName = Release;
520 | };
521 | 8042BF8C2439204D00D1477E /* Build configuration list for PBXNativeTarget "Attenuator" */ = {
522 | isa = XCConfigurationList;
523 | buildConfigurations = (
524 | 8042BF8D2439204D00D1477E /* Debug */,
525 | 8042BF8E2439204D00D1477E /* Release */,
526 | );
527 | defaultConfigurationIsVisible = 0;
528 | defaultConfigurationName = Release;
529 | };
530 | 8042BFB22439220900D1477E /* Build configuration list for PBXNativeTarget "AttenuatorAU" */ = {
531 | isa = XCConfigurationList;
532 | buildConfigurations = (
533 | 8042BFB32439220900D1477E /* Debug */,
534 | 8042BFB42439220900D1477E /* Release */,
535 | );
536 | defaultConfigurationIsVisible = 0;
537 | defaultConfigurationName = Release;
538 | };
539 | /* End XCConfigurationList section */
540 | };
541 | rootObject = 8042BF712439204C00D1477E /* Project object */;
542 | }
543 |
--------------------------------------------------------------------------------
/Attenuator/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
--------------------------------------------------------------------------------