├── wled-mic ├── Assets.xcassets │ ├── Contents.json │ ├── AccentColor.colorset │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── wled_micApp.swift └── ContentView.swift ├── wled-mic.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata ├── xcuserdata │ └── altryne.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── project.pbxproj ├── wled-micTests └── wled_micTests.swift ├── wled-micUITests ├── wled_micUITestsLaunchTests.swift └── wled_micUITests.swift └── README.MD /wled-mic/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /wled-mic.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /wled-mic/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /wled-mic/wled_micApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // wled_micApp.swift 3 | // wled-mic 4 | // 5 | // Created by Alex Volkov on 8/20/25. 6 | // 7 | // 8 | //import SwiftUI 9 | // 10 | //@main 11 | //struct wled_micApp: App { 12 | // var body: some Scene { 13 | // WindowGroup { 14 | // ContentView() 15 | // } 16 | // } 17 | //} 18 | -------------------------------------------------------------------------------- /wled-micTests/wled_micTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // wled_micTests.swift 3 | // wled-micTests 4 | // 5 | // Created by Alex Volkov on 8/20/25. 6 | // 7 | 8 | import Testing 9 | @testable import wled_mic 10 | 11 | struct wled_micTests { 12 | 13 | @Test func example() async throws { 14 | // Write your test here and use APIs like `#expect(...)` to check expected conditions. 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /wled-mic.xcodeproj/xcuserdata/altryne.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | wled-mic.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /wled-mic/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "idiom" : "universal", 16 | "platform" : "ios", 17 | "size" : "1024x1024" 18 | }, 19 | { 20 | "appearances" : [ 21 | { 22 | "appearance" : "luminosity", 23 | "value" : "tinted" 24 | } 25 | ], 26 | "idiom" : "universal", 27 | "platform" : "ios", 28 | "size" : "1024x1024" 29 | } 30 | ], 31 | "info" : { 32 | "author" : "xcode", 33 | "version" : 1 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /wled-micUITests/wled_micUITestsLaunchTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // wled_micUITestsLaunchTests.swift 3 | // wled-micUITests 4 | // 5 | // Created by Alex Volkov on 8/20/25. 6 | // 7 | 8 | import XCTest 9 | 10 | final class wled_micUITestsLaunchTests: XCTestCase { 11 | 12 | override class var runsForEachTargetApplicationUIConfiguration: Bool { 13 | true 14 | } 15 | 16 | override func setUpWithError() throws { 17 | continueAfterFailure = false 18 | } 19 | 20 | @MainActor 21 | func testLaunch() throws { 22 | let app = XCUIApplication() 23 | app.launch() 24 | 25 | // Insert steps here to perform after app launch but before taking a screenshot, 26 | // such as logging into a test account or navigating somewhere in the app 27 | 28 | let attachment = XCTAttachment(screenshot: app.screenshot()) 29 | attachment.name = "Launch Screen" 30 | attachment.lifetime = .keepAlways 31 | add(attachment) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /wled-micUITests/wled_micUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // wled_micUITests.swift 3 | // wled-micUITests 4 | // 5 | // Created by Alex Volkov on 8/20/25. 6 | // 7 | 8 | import XCTest 9 | 10 | final class wled_micUITests: XCTestCase { 11 | 12 | override func setUpWithError() throws { 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | 15 | // In UI tests it is usually best to stop immediately when a failure occurs. 16 | continueAfterFailure = false 17 | 18 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 19 | } 20 | 21 | override func tearDownWithError() throws { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | @MainActor 26 | func testExample() throws { 27 | // UI tests must launch the application that they test. 28 | let app = XCUIApplication() 29 | app.launch() 30 | 31 | // Use XCTAssert and related functions to verify your tests produce the correct results. 32 | } 33 | 34 | @MainActor 35 | func testLaunchPerformance() throws { 36 | // This measures how long it takes to launch your application. 37 | measure(metrics: [XCTApplicationLaunchMetric()]) { 38 | XCUIApplication().launch() 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # 🎶 Feed My WLED — iOS Sound Reactive Mic App 2 | 3 | This project is an **iOS companion app** that turns your iPhone into a microphone source for **WLED SoundReactive**, sending **Audio Sync v2 UDP packets** directly to one or more WLED devices on your network. 4 | 5 | It’s especially useful for **ESP8266 devices** 🕹️ — since those don’t have official sound-reactive support in the main WLED builds. With this app, your phone does the heavy lifting (FFT, AGC, noise gate) and streams to the ESP, letting you still enjoy sound-reactive effects on hardware that can’t handle onboard DSP. 6 | 7 | --- 8 | 9 | ## 🔗 Related Projects 10 | 11 | - **WLED SoundReactive (WLED-mm fork):** [https://mm.kno.wled.ge/](https://mm.kno.wled.ge/) 12 | This is the modified WLED build that understands Audio Sync packets. You can flash it directly to your ESP8266 or ESP32 devices using the [WLED Web Flasher](https://install.wled.me/). 13 | 14 | --- 15 | 16 | ## 🚲 Example Use Case 17 | 18 | Perfect for portable setups like: 19 | - A bike with WLED strips (ESP8266/ESP32) 20 | - A hat or costume with LEDs 21 | - A travel router + USB power bank 22 | 23 | Run this app on your iPhone, and your lights pulse to the beat of your music 🎵 — no laptop required. 24 | 25 | --- 26 | 27 | ## 📱 iOS App Setup (Xcode) 28 | 29 | 1. **Clone / open project** in Xcode. 30 | 2. **Add permissions in Info.plist**: 31 | - `NSMicrophoneUsageDescription` → “Mic drives WLED sound-reactive effects.” 32 | - `NSLocalNetworkUsageDescription` → “Discover and send audio sync to WLED devices on the local network.” 33 | - `NSBonjourServices` (Array) → `_wled._tcp`, `_http._tcp` 34 | - `NSAppTransportSecurity > NSAllowsArbitraryLoadsInLocalNetworking` = YES 35 | 3. **Enable Background Audio**: 36 | - Target → Signing & Capabilities → `+ Capability` → **Background Modes** 37 | - Check **Audio, AirPlay, and Picture in Picture** 38 | 4. **Run on a real iPhone** (discovery doesn’t work in Simulator). 39 | 5. **Controls in the app**: 40 | - Modes: `Single` (direct IP), `Multicast` (239.0.0.1:11988), `Unicast` (fan-out to multiple devices) 41 | - **Discover**: uses Bonjour to find WLEDs (`_wled._tcp` / `_http._tcp`) — still experimental 🚧 42 | - **Add IP**: manually add devices if discovery doesn’t work on your network 43 | - **Noise Gate**: stops sending packets when silence is detected, so LEDs go completely dark 44 | - **Ping**: checks latency and alive status of devices 45 | - **Test Packet**: send a synthetic audio frame to verify your WLED reacts 46 | 47 | --- 48 | 49 | ## ⚙️ WLED Device Setup 50 | 51 | 1. Flash WLED-mm (SoundReactive build) using [the flasher](https://install.wled.me/). 52 | 2. In WLED: 53 | - **Settings → Usermods → Audio Reactive** → Enable 54 | - **Settings → Sync → Port = 11988, Mode = Receive** 55 | - For “relay” setups: check **Sync audio data with other WLEDs** 56 | 3. Choose a **sound-reactive effect** (e.g. Gravcenter, Gravfreq, Juggles). 57 | - Effects like GEQ always draw, but with the noise gate enabled in the app, you’ll see true silence. 58 | 59 | --- 60 | 61 | ## ⚡ Current Status 62 | 63 | - ✅ UDP v2 packet streaming (AGC, FFT, noise gate) 64 | - ✅ Multicast & Unicast modes 65 | - ✅ Bonjour discovery (under development — may not work on all routers yet) 66 | - ✅ Manual IP entry + Ping 67 | - ✅ Background audio (phone can stay locked) 68 | - 🚧 Discovery stability still being improved 69 | 70 | --- 71 | 72 | ## 👤 Author 73 | 74 | Made by [Alex Volkov](https://x.com/altryne) ([@altryne on X). 75 | 76 | --- 77 | 78 | ## 📜 License 79 | 80 | MIT -------------------------------------------------------------------------------- /wled-mic/ContentView.swift: -------------------------------------------------------------------------------- 1 | // ContentView.swift 2 | import SwiftUI 3 | import AVFoundation 4 | #if canImport(AVFAudio) 5 | import AVFAudio 6 | #endif 7 | import Network 8 | import Accelerate 9 | import UIKit 10 | 11 | // MARK: - Simple model 12 | struct Target: Identifiable, Hashable { 13 | let id = UUID() 14 | var host: String // IP or .local hostname 15 | var port: UInt16 // default 11988 16 | } 17 | 18 | // MARK: - Audio + Sender 19 | final class WLEDSender: ObservableObject { 20 | // Targets 21 | @Published var targets: [Target] = [ 22 | .init(host: "wled-mm.local", port: 11988) 23 | ] 24 | 25 | // UDP connections (one per target) 26 | private var conns: [UUID: NWConnection] = [:] 27 | private let queue = DispatchQueue(label: "wled.sender.queue") 28 | 29 | // Audio / DSP 30 | private let engine = AVAudioEngine() 31 | private let frameSize = 1024 32 | private var hann: [Float] = [] 33 | private var bandCenters: [Float] = Array(repeating: 0, count: 16) 34 | private var sampleRate: Double = 44100 35 | private var binEMA: [Float] = Array(repeating: 0, count: 16) 36 | private var frameCounter: UInt8 = 0 37 | private var lastUIUpdate: CFTimeInterval = 0 38 | 39 | // AGC → 0..255 40 | private var agcGain: Float = 1.0 41 | private let agcTarget: Float = 128.0 42 | private let agcAttack: Float = 0.20 43 | private let agcDecay: Float = 0.02 44 | private let agcFloor: Float = 0.05 45 | private let agcCeil: Float = 50.0 46 | 47 | // Noise gate (hysteresis + hold) 48 | @Published var gateEnabled: Bool = true 49 | @Published var gateOpenThresh: Float = 22 50 | @Published var gateCloseThresh: Float = 12 51 | @Published var gateHoldMs: Int = 250 52 | private var gateOpen = false 53 | private var gateLastAboveMs: Double = 0 54 | 55 | // UI state 56 | @Published var isRunning = false 57 | @Published var statusText = "Idle" 58 | @Published var uiLevel: Float = 0 59 | @Published var uiBins: [Float] = Array(repeating: 0, count: 16) 60 | 61 | init() { buildHann() } 62 | 63 | // MARK: Target management 64 | func addTarget(host: String, port: UInt16 = 11988) { 65 | let t = Target(host: host.trimmingCharacters(in: .whitespacesAndNewlines), port: port) 66 | guard !t.host.isEmpty else { return } 67 | targets.append(t) 68 | openConn(for: t) 69 | } 70 | 71 | func removeTargets(at offsets: IndexSet) { 72 | for i in offsets { 73 | let id = targets[i].id 74 | conns[id]?.cancel() 75 | conns.removeValue(forKey: id) 76 | } 77 | targets.remove(atOffsets: offsets) 78 | } 79 | 80 | private func openConn(for t: Target) { 81 | guard let p = NWEndpoint.Port(rawValue: t.port) else { return } 82 | let c = NWConnection(host: NWEndpoint.Host(t.host), port: p, using: .udp) 83 | c.start(queue: queue) 84 | conns[t.id] = c 85 | } 86 | 87 | private func rebuildConns() { 88 | conns.values.forEach { $0.cancel() } 89 | conns.removeAll() 90 | targets.forEach { openConn(for: $0) } 91 | } 92 | 93 | // MARK: Control 94 | func start() { 95 | guard !isRunning else { return } 96 | #if canImport(AVFAudio) 97 | if #available(iOS 17, *) { 98 | AVAudioApplication.requestRecordPermission { [weak self] ok in 99 | DispatchQueue.main.async { ok ? self?.configureAndStart() : (self?.statusText = "Mic permission denied") } 100 | } 101 | return 102 | } 103 | #endif 104 | AVAudioSession.sharedInstance().requestRecordPermission { [weak self] ok in 105 | DispatchQueue.main.async { ok ? self?.configureAndStart() : (self?.statusText = "Mic permission denied") } 106 | } 107 | } 108 | 109 | func stop() { 110 | guard isRunning else { return } 111 | engine.inputNode.removeTap(onBus: 0) 112 | engine.stop() 113 | isRunning = false 114 | statusText = "Stopped" 115 | } 116 | 117 | func sendTestPacket() { 118 | let bins = (0..<16).map { i -> UInt8 in 119 | let t = Float(i) / 15.0 120 | return UInt8(round(255.0 * sin(t * .pi))) 121 | } 122 | frameCounter &+= 1 123 | let pkt = buildV2(sampleRaw: 128, sampleSmth: 128, samplePeak: false, 124 | frameCounter: frameCounter, fftBins: bins, zeroCrossingCount: 0, 125 | FFT_Magnitude: 1000, FFT_MajorPeak: 440) 126 | fanout(pkt) 127 | } 128 | 129 | // MARK: Setup 130 | private func configureAndStart() { 131 | do { 132 | let s = AVAudioSession.sharedInstance() 133 | try s.setCategory(.playAndRecord, options: [.mixWithOthers]) 134 | try s.setMode(.measurement) 135 | try s.setActive(true) 136 | } catch { 137 | statusText = "AudioSession error: \(error.localizedDescription)"; return 138 | } 139 | 140 | rebuildConns() 141 | 142 | let input = engine.inputNode 143 | let format = input.inputFormat(forBus: 0) 144 | sampleRate = format.sampleRate 145 | buildHann(); buildBands() 146 | 147 | input.removeTap(onBus: 0) 148 | input.installTap(onBus: 0, bufferSize: AVAudioFrameCount(frameSize), format: format) { [weak self] buf, _ in 149 | self?.process(buffer: buf) 150 | } 151 | 152 | do { 153 | try engine.start() 154 | isRunning = true 155 | statusText = "Streaming to \(targets.count) device(s)…" 156 | } catch { 157 | statusText = "Engine error: \(error.localizedDescription)" 158 | } 159 | } 160 | 161 | // MARK: DSP 162 | private func buildHann() { 163 | var w = [Float](); w.reserveCapacity(frameSize) 164 | for i in 0.. Float in 175 | let t0 = Double(i)/16.0, t1 = Double(i+1)/16.0 176 | let lo = f0 * pow(f1/f0, t0), hi = f0 * pow(f1/f0, t1) 177 | return Float(sqrt(lo * hi)) 178 | } 179 | } 180 | 181 | private func process(buffer: AVAudioPCMBuffer) { 182 | guard let ch = buffer.floatChannelData?.pointee else { return } 183 | let n = Int(buffer.frameLength); if n == 0 { return } 184 | 185 | var x = [Float](repeating: 0, count: frameSize) 186 | let count = min(n, frameSize) 187 | x.withUnsafeMutableBufferPointer { dst in dst.baseAddress!.update(from: ch, count: count) } 188 | vDSP.multiply(x, hann, result: &x) 189 | 190 | // RMS 191 | var sum: Float = 0; vDSP_svesq(x, 1, &sum, vDSP_Length(frameSize)) 192 | let rms = sqrtf(sum / Float(frameSize)) 193 | 194 | // AGC → 0..255 195 | let instRaw = min(255.0, max(0.0, rms * 255.0 * agcGain)) 196 | let err = agcTarget - instRaw 197 | if err > 0 { agcGain = min(agcCeil, agcGain * (1.0 + agcAttack * err/255.0)) } 198 | else { agcGain = max(agcFloor, agcGain * (1.0 + agcDecay * err/255.0)) } 199 | 200 | // Gate 201 | var gateNow = gateOpen 202 | let nowMs = CACurrentMediaTime() * 1000.0 203 | if gateEnabled { 204 | if instRaw >= gateOpenThresh { gateNow = true; gateLastAboveMs = nowMs } 205 | else if gateNow, instRaw < gateCloseThresh, (nowMs - gateLastAboveMs) > Double(gateHoldMs) { gateNow = false } 206 | } else { gateNow = true } 207 | gateOpen = gateNow 208 | 209 | // Peak 210 | // (simple, internal use only) 211 | // Zero crossings 212 | var zc: UInt16 = 0 213 | for i in 1..= 0) || (a > 0 && b <= 0) { zc &+= 1 } 216 | } 217 | 218 | // 16-band via Goertzel 219 | let (binsActual, maxMag, maxFreq) = geq16(samples: x, fs: Float(sampleRate)) 220 | let magScaled = min(4096.0, max(0.0, Double(maxMag) * 600.0)) 221 | 222 | // Smooth for UI 223 | let smthOpen: Float = 0.2 * instRaw + 0.8 * uiLevel * 255.0 224 | 225 | // Choose payload 226 | let instToSend: Float 227 | let smthToSend: Float 228 | let binsToSend: [UInt8] 229 | if gateOpen { 230 | instToSend = instRaw 231 | smthToSend = smthOpen 232 | binsToSend = binsActual 233 | } else { 234 | instToSend = 0; smthToSend = 0 235 | binsToSend = [UInt8](repeating: 0, count: 16) 236 | } 237 | 238 | // Build + send 239 | frameCounter &+= 1 240 | let pkt = buildV2(sampleRaw: instToSend, sampleSmth: smthToSend, samplePeak: false, 241 | frameCounter: frameCounter, fftBins: binsToSend, zeroCrossingCount: zc, 242 | FFT_Magnitude: Float(magScaled), FFT_MajorPeak: maxFreq) 243 | fanout(pkt) 244 | 245 | // UI ~20fps 246 | let now = CACurrentMediaTime() 247 | if now - lastUIUpdate > 0.05 { 248 | lastUIUpdate = now 249 | let level = min(1, max(0, instRaw / 255.0)) 250 | let normBins = (gateOpen ? binsActual : [UInt8](repeating: 0, count: 16)).map { Float($0)/255.0 } 251 | DispatchQueue.main.async { [level, normBins] in 252 | self.uiLevel = level 253 | self.uiBins = normBins 254 | } 255 | } 256 | } 257 | 258 | private func geq16(samples x: [Float], fs: Float) -> ([UInt8], Float, Float) { 259 | var mags = [Float](repeating: 0, count: 16) 260 | let N = Float(x.count) 261 | var maxMag: Float = 0; var maxIdx = 0 262 | for (i, f) in bandCenters.enumerated() { 263 | let k = roundf(N * Float(f) / fs) 264 | let omega = 2.0 * Float.pi * k / N 265 | let coeff = 2.0 * cosf(omega) 266 | var s1: Float = 0, s2: Float = 0 267 | for n in 0.. maxMag { maxMag = mag; maxIdx = i } 275 | } 276 | var bins = [UInt8](repeating: 0, count: 16) 277 | let eps: Float = 1e-6 278 | for i in 0..<16 { 279 | let norm = (maxMag > eps) ? (mags[i]/maxMag) : 0 280 | binEMA[i] = 0.7*binEMA[i] + 0.3*norm 281 | bins[i] = UInt8(max(0, min(255, Int(round(binEMA[i]*255))))) 282 | } 283 | return (bins, maxMag, Float(bandCenters[maxIdx])) 284 | } 285 | 286 | // MARK: Packet + send 287 | private func buildV2(sampleRaw: Float, sampleSmth: Float, samplePeak: Bool, frameCounter: UInt8, 288 | fftBins: [UInt8], zeroCrossingCount: UInt16, FFT_Magnitude: Float, FFT_MajorPeak: Float) -> Data { 289 | var p = [UInt8](repeating: 0, count: 44) 290 | let hdr = Array("00002".utf8) 291 | for i in 0..> 8) & 0xFF) 300 | writeFloatLE(FFT_Magnitude, &p, 36) 301 | writeFloatLE(FFT_MajorPeak, &p, 40) 302 | return Data(p) 303 | } 304 | 305 | private func writeFloatLE(_ f: Float, _ buf: inout [UInt8], _ off: Int) { 306 | let le = f.bitPattern.littleEndian 307 | buf[off+0] = UInt8(le & 0xFF) 308 | buf[off+1] = UInt8((le >> 8) & 0xFF) 309 | buf[off+2] = UInt8((le >> 16) & 0xFF) 310 | buf[off+3] = UInt8((le >> 24) & 0xFF) 311 | } 312 | 313 | private func fanout(_ pkt: Data) { 314 | if conns.count != targets.count { rebuildConns() } // lazy fix if list changed 315 | for (i, t) in targets.enumerated() { 316 | let id = targets[i].id 317 | if conns[id] == nil { openConn(for: t) } 318 | conns[id]?.send(content: pkt, completion: .idempotent) 319 | } 320 | } 321 | } 322 | 323 | // MARK: - Bars 324 | struct BarView: View { 325 | var value: CGFloat 326 | var body: some View { 327 | GeometryReader { geo in 328 | let h = max(2, value * geo.size.height) 329 | ZStack(alignment: .bottom) { 330 | RoundedRectangle(cornerRadius: 3).stroke(lineWidth: 1) 331 | RoundedRectangle(cornerRadius: 3).frame(height: h) 332 | } 333 | } 334 | } 335 | } 336 | 337 | // MARK: - UI 338 | struct ContentView: View { 339 | @StateObject private var sender = WLEDSender() 340 | @State private var keepAwake = false 341 | 342 | @State private var newHost = "" 343 | @State private var newPort = "11988" 344 | 345 | var body: some View { 346 | VStack(spacing: 14) { 347 | Text("Feed My WLED — iOS").font(.title.bold()) 348 | 349 | // Add target 350 | HStack { 351 | TextField("wled-mm.local or 192.168.x.x", text: $newHost) 352 | .textFieldStyle(.roundedBorder) 353 | .keyboardType(.numbersAndPunctuation) 354 | .autocapitalization(.none) 355 | .disableAutocorrection(true) 356 | TextField("11988", text: $newPort) 357 | .frame(width: 80) 358 | .textFieldStyle(.roundedBorder) 359 | .keyboardType(.numberPad) 360 | Button("Add") { 361 | sender.addTarget(host: newHost, port: UInt16(newPort) ?? 11988) 362 | newHost = ""; newPort = "11988" 363 | } 364 | .buttonStyle(.borderedProminent) 365 | } 366 | .padding(.horizontal) 367 | 368 | // List of targets 369 | List { 370 | Section("Targets (\(sender.targets.count))") { 371 | ForEach(sender.targets) { t in 372 | HStack { 373 | Text(t.host).bold().lineLimit(1).truncationMode(.middle) 374 | Spacer() 375 | Text("\(t.port)").foregroundStyle(.secondary).monospaced() 376 | } 377 | } 378 | .onDelete(perform: sender.removeTargets) 379 | } 380 | } 381 | .frame(height: 180) 382 | 383 | // Noise gate 384 | GroupBox("Noise Gate") { 385 | VStack(spacing: 8) { 386 | Toggle("Enable", isOn: $sender.gateEnabled) 387 | HStack { 388 | Text("Open").frame(width: 60, alignment: .leading) 389 | Slider(value: $sender.gateOpenThresh, in: 0...255, step: 1) 390 | Text("\(Int(sender.gateOpenThresh))").monospaced() 391 | } 392 | HStack { 393 | Text("Close").frame(width: 60, alignment: .leading) 394 | Slider(value: $sender.gateCloseThresh, in: 0...255, step: 1) 395 | Text("\(Int(sender.gateCloseThresh))").monospaced() 396 | } 397 | HStack { 398 | Text("Hold").frame(width: 60, alignment: .leading) 399 | TextField("250", text: Binding( 400 | get: { String(sender.gateHoldMs) }, 401 | set: { sender.gateHoldMs = Int($0) ?? 250 } 402 | )) 403 | .textFieldStyle(.roundedBorder) 404 | .keyboardType(.numberPad) 405 | Text("ms").foregroundStyle(.secondary) 406 | } 407 | } 408 | } 409 | .padding(.horizontal) 410 | 411 | // Transport + test 412 | HStack { 413 | Button(sender.isRunning ? "Stop" : "Start") { 414 | sender.isRunning ? sender.stop() : sender.start() 415 | } 416 | .buttonStyle(.borderedProminent) 417 | 418 | Button("Send Test Packet") { sender.sendTestPacket() } 419 | .buttonStyle(.bordered) 420 | 421 | Toggle("Keep Screen Awake", isOn: $keepAwake) 422 | .onChange(of: keepAwake) { UIApplication.shared.isIdleTimerDisabled = $0 } 423 | } 424 | .padding(.horizontal) 425 | 426 | // Visualizers 427 | VStack(spacing: 8) { 428 | BarView(value: CGFloat(sender.uiLevel)) 429 | .frame(height: 70) 430 | .padding(.horizontal) 431 | 432 | HStack(alignment: .bottom, spacing: 4) { 433 | ForEach(0..<16, id: \.self) { i in 434 | BarView(value: CGFloat(min(1.0, max(0.0, sender.uiBins[i])))) 435 | } 436 | } 437 | .frame(height: 80) 438 | .padding(.horizontal) 439 | } 440 | 441 | Text(sender.statusText) 442 | .font(.footnote) 443 | .foregroundStyle(.secondary) 444 | 445 | Spacer(minLength: 8) 446 | } 447 | .padding(.top, 8) 448 | } 449 | } 450 | 451 | @main 452 | struct WLEDApp: App { 453 | var body: some Scene { WindowGroup { ContentView() } } 454 | } 455 | -------------------------------------------------------------------------------- /wled-mic.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 77; 7 | objects = { 8 | 9 | /* Begin PBXContainerItemProxy section */ 10 | 3FFF895D2E56172F0075EBE1 /* PBXContainerItemProxy */ = { 11 | isa = PBXContainerItemProxy; 12 | containerPortal = 3FFF89472E56172E0075EBE1 /* Project object */; 13 | proxyType = 1; 14 | remoteGlobalIDString = 3FFF894E2E56172E0075EBE1; 15 | remoteInfo = "wled-mic"; 16 | }; 17 | 3FFF89672E56172F0075EBE1 /* PBXContainerItemProxy */ = { 18 | isa = PBXContainerItemProxy; 19 | containerPortal = 3FFF89472E56172E0075EBE1 /* Project object */; 20 | proxyType = 1; 21 | remoteGlobalIDString = 3FFF894E2E56172E0075EBE1; 22 | remoteInfo = "wled-mic"; 23 | }; 24 | /* End PBXContainerItemProxy section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | 3FFF894F2E56172E0075EBE1 /* wled-mic.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "wled-mic.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 3FFF895C2E56172F0075EBE1 /* wled-micTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "wled-micTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 3FFF89662E56172F0075EBE1 /* wled-micUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "wled-micUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | /* End PBXFileReference section */ 31 | 32 | /* Begin PBXFileSystemSynchronizedRootGroup section */ 33 | 3FFF89512E56172E0075EBE1 /* wled-mic */ = { 34 | isa = PBXFileSystemSynchronizedRootGroup; 35 | path = "wled-mic"; 36 | sourceTree = ""; 37 | }; 38 | 3FFF895F2E56172F0075EBE1 /* wled-micTests */ = { 39 | isa = PBXFileSystemSynchronizedRootGroup; 40 | path = "wled-micTests"; 41 | sourceTree = ""; 42 | }; 43 | 3FFF89692E56172F0075EBE1 /* wled-micUITests */ = { 44 | isa = PBXFileSystemSynchronizedRootGroup; 45 | path = "wled-micUITests"; 46 | sourceTree = ""; 47 | }; 48 | /* End PBXFileSystemSynchronizedRootGroup section */ 49 | 50 | /* Begin PBXFrameworksBuildPhase section */ 51 | 3FFF894C2E56172E0075EBE1 /* Frameworks */ = { 52 | isa = PBXFrameworksBuildPhase; 53 | buildActionMask = 2147483647; 54 | files = ( 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | 3FFF89592E56172F0075EBE1 /* Frameworks */ = { 59 | isa = PBXFrameworksBuildPhase; 60 | buildActionMask = 2147483647; 61 | files = ( 62 | ); 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | 3FFF89632E56172F0075EBE1 /* Frameworks */ = { 66 | isa = PBXFrameworksBuildPhase; 67 | buildActionMask = 2147483647; 68 | files = ( 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | /* End PBXFrameworksBuildPhase section */ 73 | 74 | /* Begin PBXGroup section */ 75 | 3FFF89462E56172E0075EBE1 = { 76 | isa = PBXGroup; 77 | children = ( 78 | 3FFF89512E56172E0075EBE1 /* wled-mic */, 79 | 3FFF895F2E56172F0075EBE1 /* wled-micTests */, 80 | 3FFF89692E56172F0075EBE1 /* wled-micUITests */, 81 | 3FFF89502E56172E0075EBE1 /* Products */, 82 | ); 83 | sourceTree = ""; 84 | }; 85 | 3FFF89502E56172E0075EBE1 /* Products */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 3FFF894F2E56172E0075EBE1 /* wled-mic.app */, 89 | 3FFF895C2E56172F0075EBE1 /* wled-micTests.xctest */, 90 | 3FFF89662E56172F0075EBE1 /* wled-micUITests.xctest */, 91 | ); 92 | name = Products; 93 | sourceTree = ""; 94 | }; 95 | /* End PBXGroup section */ 96 | 97 | /* Begin PBXNativeTarget section */ 98 | 3FFF894E2E56172E0075EBE1 /* wled-mic */ = { 99 | isa = PBXNativeTarget; 100 | buildConfigurationList = 3FFF89702E56172F0075EBE1 /* Build configuration list for PBXNativeTarget "wled-mic" */; 101 | buildPhases = ( 102 | 3FFF894B2E56172E0075EBE1 /* Sources */, 103 | 3FFF894C2E56172E0075EBE1 /* Frameworks */, 104 | 3FFF894D2E56172E0075EBE1 /* Resources */, 105 | ); 106 | buildRules = ( 107 | ); 108 | dependencies = ( 109 | ); 110 | fileSystemSynchronizedGroups = ( 111 | 3FFF89512E56172E0075EBE1 /* wled-mic */, 112 | ); 113 | name = "wled-mic"; 114 | packageProductDependencies = ( 115 | ); 116 | productName = "wled-mic"; 117 | productReference = 3FFF894F2E56172E0075EBE1 /* wled-mic.app */; 118 | productType = "com.apple.product-type.application"; 119 | }; 120 | 3FFF895B2E56172F0075EBE1 /* wled-micTests */ = { 121 | isa = PBXNativeTarget; 122 | buildConfigurationList = 3FFF89732E56172F0075EBE1 /* Build configuration list for PBXNativeTarget "wled-micTests" */; 123 | buildPhases = ( 124 | 3FFF89582E56172F0075EBE1 /* Sources */, 125 | 3FFF89592E56172F0075EBE1 /* Frameworks */, 126 | 3FFF895A2E56172F0075EBE1 /* Resources */, 127 | ); 128 | buildRules = ( 129 | ); 130 | dependencies = ( 131 | 3FFF895E2E56172F0075EBE1 /* PBXTargetDependency */, 132 | ); 133 | fileSystemSynchronizedGroups = ( 134 | 3FFF895F2E56172F0075EBE1 /* wled-micTests */, 135 | ); 136 | name = "wled-micTests"; 137 | packageProductDependencies = ( 138 | ); 139 | productName = "wled-micTests"; 140 | productReference = 3FFF895C2E56172F0075EBE1 /* wled-micTests.xctest */; 141 | productType = "com.apple.product-type.bundle.unit-test"; 142 | }; 143 | 3FFF89652E56172F0075EBE1 /* wled-micUITests */ = { 144 | isa = PBXNativeTarget; 145 | buildConfigurationList = 3FFF89762E56172F0075EBE1 /* Build configuration list for PBXNativeTarget "wled-micUITests" */; 146 | buildPhases = ( 147 | 3FFF89622E56172F0075EBE1 /* Sources */, 148 | 3FFF89632E56172F0075EBE1 /* Frameworks */, 149 | 3FFF89642E56172F0075EBE1 /* Resources */, 150 | ); 151 | buildRules = ( 152 | ); 153 | dependencies = ( 154 | 3FFF89682E56172F0075EBE1 /* PBXTargetDependency */, 155 | ); 156 | fileSystemSynchronizedGroups = ( 157 | 3FFF89692E56172F0075EBE1 /* wled-micUITests */, 158 | ); 159 | name = "wled-micUITests"; 160 | packageProductDependencies = ( 161 | ); 162 | productName = "wled-micUITests"; 163 | productReference = 3FFF89662E56172F0075EBE1 /* wled-micUITests.xctest */; 164 | productType = "com.apple.product-type.bundle.ui-testing"; 165 | }; 166 | /* End PBXNativeTarget section */ 167 | 168 | /* Begin PBXProject section */ 169 | 3FFF89472E56172E0075EBE1 /* Project object */ = { 170 | isa = PBXProject; 171 | attributes = { 172 | BuildIndependentTargetsInParallel = 1; 173 | LastSwiftUpdateCheck = 1640; 174 | LastUpgradeCheck = 1640; 175 | TargetAttributes = { 176 | 3FFF894E2E56172E0075EBE1 = { 177 | CreatedOnToolsVersion = 16.4; 178 | }; 179 | 3FFF895B2E56172F0075EBE1 = { 180 | CreatedOnToolsVersion = 16.4; 181 | TestTargetID = 3FFF894E2E56172E0075EBE1; 182 | }; 183 | 3FFF89652E56172F0075EBE1 = { 184 | CreatedOnToolsVersion = 16.4; 185 | TestTargetID = 3FFF894E2E56172E0075EBE1; 186 | }; 187 | }; 188 | }; 189 | buildConfigurationList = 3FFF894A2E56172E0075EBE1 /* Build configuration list for PBXProject "wled-mic" */; 190 | developmentRegion = en; 191 | hasScannedForEncodings = 0; 192 | knownRegions = ( 193 | en, 194 | Base, 195 | ); 196 | mainGroup = 3FFF89462E56172E0075EBE1; 197 | minimizedProjectReferenceProxies = 1; 198 | preferredProjectObjectVersion = 77; 199 | productRefGroup = 3FFF89502E56172E0075EBE1 /* Products */; 200 | projectDirPath = ""; 201 | projectRoot = ""; 202 | targets = ( 203 | 3FFF894E2E56172E0075EBE1 /* wled-mic */, 204 | 3FFF895B2E56172F0075EBE1 /* wled-micTests */, 205 | 3FFF89652E56172F0075EBE1 /* wled-micUITests */, 206 | ); 207 | }; 208 | /* End PBXProject section */ 209 | 210 | /* Begin PBXResourcesBuildPhase section */ 211 | 3FFF894D2E56172E0075EBE1 /* Resources */ = { 212 | isa = PBXResourcesBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | ); 216 | runOnlyForDeploymentPostprocessing = 0; 217 | }; 218 | 3FFF895A2E56172F0075EBE1 /* Resources */ = { 219 | isa = PBXResourcesBuildPhase; 220 | buildActionMask = 2147483647; 221 | files = ( 222 | ); 223 | runOnlyForDeploymentPostprocessing = 0; 224 | }; 225 | 3FFF89642E56172F0075EBE1 /* Resources */ = { 226 | isa = PBXResourcesBuildPhase; 227 | buildActionMask = 2147483647; 228 | files = ( 229 | ); 230 | runOnlyForDeploymentPostprocessing = 0; 231 | }; 232 | /* End PBXResourcesBuildPhase section */ 233 | 234 | /* Begin PBXSourcesBuildPhase section */ 235 | 3FFF894B2E56172E0075EBE1 /* Sources */ = { 236 | isa = PBXSourcesBuildPhase; 237 | buildActionMask = 2147483647; 238 | files = ( 239 | ); 240 | runOnlyForDeploymentPostprocessing = 0; 241 | }; 242 | 3FFF89582E56172F0075EBE1 /* Sources */ = { 243 | isa = PBXSourcesBuildPhase; 244 | buildActionMask = 2147483647; 245 | files = ( 246 | ); 247 | runOnlyForDeploymentPostprocessing = 0; 248 | }; 249 | 3FFF89622E56172F0075EBE1 /* Sources */ = { 250 | isa = PBXSourcesBuildPhase; 251 | buildActionMask = 2147483647; 252 | files = ( 253 | ); 254 | runOnlyForDeploymentPostprocessing = 0; 255 | }; 256 | /* End PBXSourcesBuildPhase section */ 257 | 258 | /* Begin PBXTargetDependency section */ 259 | 3FFF895E2E56172F0075EBE1 /* PBXTargetDependency */ = { 260 | isa = PBXTargetDependency; 261 | target = 3FFF894E2E56172E0075EBE1 /* wled-mic */; 262 | targetProxy = 3FFF895D2E56172F0075EBE1 /* PBXContainerItemProxy */; 263 | }; 264 | 3FFF89682E56172F0075EBE1 /* PBXTargetDependency */ = { 265 | isa = PBXTargetDependency; 266 | target = 3FFF894E2E56172E0075EBE1 /* wled-mic */; 267 | targetProxy = 3FFF89672E56172F0075EBE1 /* PBXContainerItemProxy */; 268 | }; 269 | /* End PBXTargetDependency section */ 270 | 271 | /* Begin XCBuildConfiguration section */ 272 | 3FFF896E2E56172F0075EBE1 /* Debug */ = { 273 | isa = XCBuildConfiguration; 274 | buildSettings = { 275 | ALWAYS_SEARCH_USER_PATHS = NO; 276 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 277 | CLANG_ANALYZER_NONNULL = YES; 278 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 279 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 280 | CLANG_ENABLE_MODULES = YES; 281 | CLANG_ENABLE_OBJC_ARC = YES; 282 | CLANG_ENABLE_OBJC_WEAK = YES; 283 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 284 | CLANG_WARN_BOOL_CONVERSION = YES; 285 | CLANG_WARN_COMMA = YES; 286 | CLANG_WARN_CONSTANT_CONVERSION = YES; 287 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 288 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 289 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 290 | CLANG_WARN_EMPTY_BODY = YES; 291 | CLANG_WARN_ENUM_CONVERSION = YES; 292 | CLANG_WARN_INFINITE_RECURSION = YES; 293 | CLANG_WARN_INT_CONVERSION = YES; 294 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 295 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 296 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 297 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 298 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 299 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 300 | CLANG_WARN_STRICT_PROTOTYPES = YES; 301 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 302 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 303 | CLANG_WARN_UNREACHABLE_CODE = YES; 304 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 305 | COPY_PHASE_STRIP = NO; 306 | DEBUG_INFORMATION_FORMAT = dwarf; 307 | ENABLE_STRICT_OBJC_MSGSEND = YES; 308 | ENABLE_TESTABILITY = YES; 309 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 310 | GCC_C_LANGUAGE_STANDARD = gnu17; 311 | GCC_DYNAMIC_NO_PIC = NO; 312 | GCC_NO_COMMON_BLOCKS = YES; 313 | GCC_OPTIMIZATION_LEVEL = 0; 314 | GCC_PREPROCESSOR_DEFINITIONS = ( 315 | "DEBUG=1", 316 | "$(inherited)", 317 | ); 318 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 319 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 320 | GCC_WARN_UNDECLARED_SELECTOR = YES; 321 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 322 | GCC_WARN_UNUSED_FUNCTION = YES; 323 | GCC_WARN_UNUSED_VARIABLE = YES; 324 | IPHONEOS_DEPLOYMENT_TARGET = 18.5; 325 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 326 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 327 | MTL_FAST_MATH = YES; 328 | ONLY_ACTIVE_ARCH = YES; 329 | SDKROOT = iphoneos; 330 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; 331 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 332 | }; 333 | name = Debug; 334 | }; 335 | 3FFF896F2E56172F0075EBE1 /* Release */ = { 336 | isa = XCBuildConfiguration; 337 | buildSettings = { 338 | ALWAYS_SEARCH_USER_PATHS = NO; 339 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; 340 | CLANG_ANALYZER_NONNULL = YES; 341 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 342 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 343 | CLANG_ENABLE_MODULES = YES; 344 | CLANG_ENABLE_OBJC_ARC = YES; 345 | CLANG_ENABLE_OBJC_WEAK = YES; 346 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 347 | CLANG_WARN_BOOL_CONVERSION = YES; 348 | CLANG_WARN_COMMA = YES; 349 | CLANG_WARN_CONSTANT_CONVERSION = YES; 350 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 351 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 352 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 353 | CLANG_WARN_EMPTY_BODY = YES; 354 | CLANG_WARN_ENUM_CONVERSION = YES; 355 | CLANG_WARN_INFINITE_RECURSION = YES; 356 | CLANG_WARN_INT_CONVERSION = YES; 357 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 358 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 359 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 360 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 361 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 362 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 363 | CLANG_WARN_STRICT_PROTOTYPES = YES; 364 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 365 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 366 | CLANG_WARN_UNREACHABLE_CODE = YES; 367 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 368 | COPY_PHASE_STRIP = NO; 369 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 370 | ENABLE_NS_ASSERTIONS = NO; 371 | ENABLE_STRICT_OBJC_MSGSEND = YES; 372 | ENABLE_USER_SCRIPT_SANDBOXING = YES; 373 | GCC_C_LANGUAGE_STANDARD = gnu17; 374 | GCC_NO_COMMON_BLOCKS = YES; 375 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 376 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 377 | GCC_WARN_UNDECLARED_SELECTOR = YES; 378 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 379 | GCC_WARN_UNUSED_FUNCTION = YES; 380 | GCC_WARN_UNUSED_VARIABLE = YES; 381 | IPHONEOS_DEPLOYMENT_TARGET = 18.5; 382 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES; 383 | MTL_ENABLE_DEBUG_INFO = NO; 384 | MTL_FAST_MATH = YES; 385 | SDKROOT = iphoneos; 386 | SWIFT_COMPILATION_MODE = wholemodule; 387 | VALIDATE_PRODUCT = YES; 388 | }; 389 | name = Release; 390 | }; 391 | 3FFF89712E56172F0075EBE1 /* Debug */ = { 392 | isa = XCBuildConfiguration; 393 | buildSettings = { 394 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 395 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 396 | CODE_SIGN_STYLE = Automatic; 397 | CURRENT_PROJECT_VERSION = 1; 398 | ENABLE_PREVIEWS = YES; 399 | GENERATE_INFOPLIST_FILE = YES; 400 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 401 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 402 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 403 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 404 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 405 | LD_RUNPATH_SEARCH_PATHS = ( 406 | "$(inherited)", 407 | "@executable_path/Frameworks", 408 | ); 409 | MARKETING_VERSION = 1.0; 410 | PRODUCT_BUNDLE_IDENTIFIER = "me.alexw.wled-mic"; 411 | PRODUCT_NAME = "$(TARGET_NAME)"; 412 | SWIFT_EMIT_LOC_STRINGS = YES; 413 | SWIFT_VERSION = 5.0; 414 | TARGETED_DEVICE_FAMILY = "1,2"; 415 | }; 416 | name = Debug; 417 | }; 418 | 3FFF89722E56172F0075EBE1 /* Release */ = { 419 | isa = XCBuildConfiguration; 420 | buildSettings = { 421 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 422 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 423 | CODE_SIGN_STYLE = Automatic; 424 | CURRENT_PROJECT_VERSION = 1; 425 | ENABLE_PREVIEWS = YES; 426 | GENERATE_INFOPLIST_FILE = YES; 427 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; 428 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 429 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; 430 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 431 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 432 | LD_RUNPATH_SEARCH_PATHS = ( 433 | "$(inherited)", 434 | "@executable_path/Frameworks", 435 | ); 436 | MARKETING_VERSION = 1.0; 437 | PRODUCT_BUNDLE_IDENTIFIER = "me.alexw.wled-mic"; 438 | PRODUCT_NAME = "$(TARGET_NAME)"; 439 | SWIFT_EMIT_LOC_STRINGS = YES; 440 | SWIFT_VERSION = 5.0; 441 | TARGETED_DEVICE_FAMILY = "1,2"; 442 | }; 443 | name = Release; 444 | }; 445 | 3FFF89742E56172F0075EBE1 /* Debug */ = { 446 | isa = XCBuildConfiguration; 447 | buildSettings = { 448 | BUNDLE_LOADER = "$(TEST_HOST)"; 449 | CODE_SIGN_STYLE = Automatic; 450 | CURRENT_PROJECT_VERSION = 1; 451 | GENERATE_INFOPLIST_FILE = YES; 452 | IPHONEOS_DEPLOYMENT_TARGET = 18.5; 453 | MARKETING_VERSION = 1.0; 454 | PRODUCT_BUNDLE_IDENTIFIER = "me.alexw.wled-micTests"; 455 | PRODUCT_NAME = "$(TARGET_NAME)"; 456 | SWIFT_EMIT_LOC_STRINGS = NO; 457 | SWIFT_VERSION = 5.0; 458 | TARGETED_DEVICE_FAMILY = "1,2"; 459 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/wled-mic.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/wled-mic"; 460 | }; 461 | name = Debug; 462 | }; 463 | 3FFF89752E56172F0075EBE1 /* Release */ = { 464 | isa = XCBuildConfiguration; 465 | buildSettings = { 466 | BUNDLE_LOADER = "$(TEST_HOST)"; 467 | CODE_SIGN_STYLE = Automatic; 468 | CURRENT_PROJECT_VERSION = 1; 469 | GENERATE_INFOPLIST_FILE = YES; 470 | IPHONEOS_DEPLOYMENT_TARGET = 18.5; 471 | MARKETING_VERSION = 1.0; 472 | PRODUCT_BUNDLE_IDENTIFIER = "me.alexw.wled-micTests"; 473 | PRODUCT_NAME = "$(TARGET_NAME)"; 474 | SWIFT_EMIT_LOC_STRINGS = NO; 475 | SWIFT_VERSION = 5.0; 476 | TARGETED_DEVICE_FAMILY = "1,2"; 477 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/wled-mic.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/wled-mic"; 478 | }; 479 | name = Release; 480 | }; 481 | 3FFF89772E56172F0075EBE1 /* Debug */ = { 482 | isa = XCBuildConfiguration; 483 | buildSettings = { 484 | CODE_SIGN_STYLE = Automatic; 485 | CURRENT_PROJECT_VERSION = 1; 486 | GENERATE_INFOPLIST_FILE = YES; 487 | MARKETING_VERSION = 1.0; 488 | PRODUCT_BUNDLE_IDENTIFIER = "me.alexw.wled-micUITests"; 489 | PRODUCT_NAME = "$(TARGET_NAME)"; 490 | SWIFT_EMIT_LOC_STRINGS = NO; 491 | SWIFT_VERSION = 5.0; 492 | TARGETED_DEVICE_FAMILY = "1,2"; 493 | TEST_TARGET_NAME = "wled-mic"; 494 | }; 495 | name = Debug; 496 | }; 497 | 3FFF89782E56172F0075EBE1 /* Release */ = { 498 | isa = XCBuildConfiguration; 499 | buildSettings = { 500 | CODE_SIGN_STYLE = Automatic; 501 | CURRENT_PROJECT_VERSION = 1; 502 | GENERATE_INFOPLIST_FILE = YES; 503 | MARKETING_VERSION = 1.0; 504 | PRODUCT_BUNDLE_IDENTIFIER = "me.alexw.wled-micUITests"; 505 | PRODUCT_NAME = "$(TARGET_NAME)"; 506 | SWIFT_EMIT_LOC_STRINGS = NO; 507 | SWIFT_VERSION = 5.0; 508 | TARGETED_DEVICE_FAMILY = "1,2"; 509 | TEST_TARGET_NAME = "wled-mic"; 510 | }; 511 | name = Release; 512 | }; 513 | /* End XCBuildConfiguration section */ 514 | 515 | /* Begin XCConfigurationList section */ 516 | 3FFF894A2E56172E0075EBE1 /* Build configuration list for PBXProject "wled-mic" */ = { 517 | isa = XCConfigurationList; 518 | buildConfigurations = ( 519 | 3FFF896E2E56172F0075EBE1 /* Debug */, 520 | 3FFF896F2E56172F0075EBE1 /* Release */, 521 | ); 522 | defaultConfigurationIsVisible = 0; 523 | defaultConfigurationName = Release; 524 | }; 525 | 3FFF89702E56172F0075EBE1 /* Build configuration list for PBXNativeTarget "wled-mic" */ = { 526 | isa = XCConfigurationList; 527 | buildConfigurations = ( 528 | 3FFF89712E56172F0075EBE1 /* Debug */, 529 | 3FFF89722E56172F0075EBE1 /* Release */, 530 | ); 531 | defaultConfigurationIsVisible = 0; 532 | defaultConfigurationName = Release; 533 | }; 534 | 3FFF89732E56172F0075EBE1 /* Build configuration list for PBXNativeTarget "wled-micTests" */ = { 535 | isa = XCConfigurationList; 536 | buildConfigurations = ( 537 | 3FFF89742E56172F0075EBE1 /* Debug */, 538 | 3FFF89752E56172F0075EBE1 /* Release */, 539 | ); 540 | defaultConfigurationIsVisible = 0; 541 | defaultConfigurationName = Release; 542 | }; 543 | 3FFF89762E56172F0075EBE1 /* Build configuration list for PBXNativeTarget "wled-micUITests" */ = { 544 | isa = XCConfigurationList; 545 | buildConfigurations = ( 546 | 3FFF89772E56172F0075EBE1 /* Debug */, 547 | 3FFF89782E56172F0075EBE1 /* Release */, 548 | ); 549 | defaultConfigurationIsVisible = 0; 550 | defaultConfigurationName = Release; 551 | }; 552 | /* End XCConfigurationList section */ 553 | }; 554 | rootObject = 3FFF89472E56172E0075EBE1 /* Project object */; 555 | } 556 | --------------------------------------------------------------------------------