Saving plugin presets using fullState in Audio Units

Hi,

I'm having trouble saving user presets in the plugin for Audio Units. This works well for saving the user presets in the Host, but I get an error when trying to save them in the plugin.

I'm not using a parameter tree, but instead using the fullState's getter and setter for saving and retrieving a dictionary with the state.

With some simplified parameters it looks something like this:

var gain: Double = 0.0
var frequency: Double = 440.0
   
private var currentState: [String: Any] = [:]

override var fullState: [String: Any]? {
    get {
      // Save the current state
      currentState["gain"] = gain
      currentState["frequency"] = frequency

      // Return the preset state
      return ["myPresetKey": currentState]
    }
    set {
      // Extract the preset state
      currentState = newValue?["myPresetKey"] as? [String: Any] ?? [:]

      // Set the Audio Unit's properties
      gain = currentState["gain"] as? Double ?? 0.0
      frequency = currentState["frequency"] as? Double ?? 440.0
    }
 }

This works perfectly well for storing user presets when saved in the host. When trying to save them in the plugin to be able to reuse them across hosts, I get the following error in the interface: "Missing key in preset state map". Note that I am testing mostly in AUM.

I could not find any documentation for what the missing key is about and how can I get around this. Any ideas?

Replies

The error message disappears when you retreive the super.fullState data first and then add your dictionary to that one, see code below:


override var fullState: [String: Any]? {
        
        get {
            
            // save preset
   
            var state = super.fullState ?? [:]
            
            state["fullStateParams"] = [:] // your dictionary here
            
            return state
            
        }
        
        set {
             
            // load preset
            
            if let state:[String:Any] = newValue{
                
                if let stateFullParams:[String:[String:Any]] = state["fullStateParams"] as? [String:[String:Any]]{
                    
                   // do something with your dictionary
                    
                }
                
            }
            
        }
        
    }

Unfortunately this solution still doesn't solve the problem on how to save the preset inside the plugin.

Have you found a solution in the meantime?

Ok I think saving a preset inside the plugin goes something like this:

In order for the preset to be saved inside your AUv3 app container you'll need to override the currentPreset property in which the selected preset gets passed to the fullStateForDocument property, see code below:

override var supportsUserPresets: Bool{ get{ return true } }

private var _currentPreset: AUAudioUnitPreset?

override var currentPreset: AUAudioUnitPreset? {
    
    get { return _currentPreset }
    
    set {
        
        // If the newValue is nil, return.
        guard let preset = newValue else {
            
            _currentPreset = nil
            
            return
            
        }
        
        // Factory presets need to always have a number >= 0.
        if preset.number >= 0 {
            
            _currentPreset = preset
        
        // User presets are always negative.
        }else {
        
            // Attempt to restore the archived state for this user preset.
            do {
                    
                fullStateForDocument = try presetState(for: preset)                    
                
                // Set the currentPreset after successfully restoring the state.
                _currentPreset = preset
                
            } catch {
                
                print("Unable to restore set for preset \(preset.name)")
                
            }
            
        }
        
    }
    
}


override var fullState: [String: Any]? {
    
    get {
        
        // save preset
        
        var state = super.fullState ?? [:]
        
        state["fullStateParams"] = [:] // your dictionary here
        
        return state
        
    }
    
    set {
         
        // load preset
        
        if let state:[String:Any] = newValue{
            
            if let stateFullParams:[String:[String:Any]] = state["fullStateParams"] as? [String:[String:Any]]{
                
                loadPreset(array: stateFullParams) // do something with your dictionary
                
            }
            
        }
        
    }
    
}

More information can be found here:

https://developer.apple.com/videos/play/wwdc2019/509/

https://developer.apple.com/documentation/audiotoolbox/audio_unit_v3_plug-ins/incorporating_audio_effects_and_instruments

https://developer.apple.com/documentation/audiotoolbox/audio_unit_v3_plug-ins/creating_custom_audio_effects