|
| 1 | +// |
| 2 | +// FaderGainBugs.swift |
| 3 | +// AudioKitEX |
| 4 | +// |
| 5 | +// Created by Taylor Holliday on 3/23/26. |
| 6 | +// |
| 7 | + |
| 8 | +import Testing |
| 9 | +import AudioKit |
| 10 | +import AudioKitEX |
| 11 | +import AVFoundation |
| 12 | +import Accelerate |
| 13 | +import CAudioKitEX |
| 14 | + |
| 15 | + |
| 16 | +/// Minimal repro: Fader.init calls AudioUnitSetParameter on an AU that hasn't been |
| 17 | +/// added to an engine, so the C-level AudioUnit doesn't recognise the parameter address |
| 18 | +/// and returns kAudioUnitErr_InvalidParameter (-10878). |
| 19 | +/// |
| 20 | +/// CheckError logs but doesn't throw, so we call the C API directly to prove |
| 21 | +/// the parameter write fails on an unconnected AudioUnit. |
| 22 | +@MainActor |
| 23 | +@Test |
| 24 | +func testFaderInitWithoutEngine() throws { |
| 25 | + let mixer = Mixer() |
| 26 | + let fader = Fader(mixer, gain: 1.0) |
| 27 | + |
| 28 | + // The Fader's underlying AudioUnit is not connected to any engine yet. |
| 29 | + // Prove that setting a parameter on it returns kAudioUnitErr_InvalidParameter. |
| 30 | + let avAudioUnit = fader.avAudioNode as! AVAudioUnit |
| 31 | + let au = avAudioUnit.audioUnit |
| 32 | + let rightGainAddress = akGetParameterAddress("FaderParameterRightGain") |
| 33 | + let status = AudioUnitSetParameter(au, AudioUnitParameterID(rightGainAddress), kAudioUnitScope_Global, 0, 1.0, 0) |
| 34 | + #expect(status == kAudioUnitErr_InvalidParameter, |
| 35 | + "Expected kAudioUnitErr_InvalidParameter (-10878) but got \(status)") |
| 36 | +} |
| 37 | + |
| 38 | +/// Prove that a non-default gain passed to Fader.init is silently lost at the |
| 39 | +/// C level. The Swift-side parameter.value holds 0.5, but after the node is |
| 40 | +/// connected to a running engine the DSP kernel still sees the default (1.0). |
| 41 | +@MainActor |
| 42 | +@Test |
| 43 | +func testFaderGainLostAfterConnection() throws { |
| 44 | + let engine = AudioEngine() |
| 45 | + let masterMixer = Mixer(name: "Master") |
| 46 | + engine.output = masterMixer |
| 47 | + |
| 48 | + let inputMixer = Mixer() |
| 49 | + let fader = Fader(inputMixer, gain: 0.5) |
| 50 | + |
| 51 | + // Swift side thinks gain is 0.5 |
| 52 | + #expect(fader.$leftGain.value == 0.5, "Swift-side leftGain should be 0.5") |
| 53 | + |
| 54 | + // Now connect to the running engine — this calls makeAVConnections |
| 55 | + masterMixer.addInput(fader) |
| 56 | + try engine.start() |
| 57 | + |
| 58 | + // Read back from both the Swift AUParameter and C-level AudioUnit |
| 59 | + let avAudioUnit = fader.avAudioNode as! AVAudioUnit |
| 60 | + let au = avAudioUnit.audioUnit |
| 61 | + let leftGainAddress = akGetParameterAddress("FaderParameterLeftGain") |
| 62 | + |
| 63 | + // Swift AUParameter thinks it's 0.5 |
| 64 | + let swiftValue = fader.$leftGain.value |
| 65 | + #expect(swiftValue == 0.5, "Swift-side leftGain should be 0.5 — got \(swiftValue)") |
| 66 | + |
| 67 | + // C-level AudioUnit does NOT have 0.5 |
| 68 | + var cValue: AudioUnitParameterValue = -1 |
| 69 | + AudioUnitGetParameter(au, AudioUnitParameterID(leftGainAddress), kAudioUnitScope_Global, 0, &cValue) |
| 70 | + #expect(cValue != 0.5, |
| 71 | + "C-level gain should NOT be 0.5 — the init-time set was lost (got \(cValue))") |
| 72 | + |
| 73 | + engine.stop() |
| 74 | +} |
0 commit comments