<commit_before>// Copyright AudioKit. All Rights Reserved. Revision History at http://github.com/AudioKit/AudioKit/

import AVFoundation
import CAudioKit

public struct AKNodeParameterDef {
    public var identifier: String
    public var name: String
    public var address: AUParameterAddress
    public var range: ClosedRange<AUValue>
    public var unit: AudioUnitParameterUnit
    public var flags: AudioUnitParameterOptions
    public init(identifier: String,
                name: String,
                address: AUParameterAddress,
                range: ClosedRange<AUValue>,
                unit: AudioUnitParameterUnit,
                flags: AudioUnitParameterOptions) {
        self.identifier = identifier
        self.name = name
        self.address = address
        self.range = range
        self.unit = unit
        self.flags = flags
    }
}

/// AKNodeParameter wraps AUParameter in a user-friendly interface and adds some AudioKit-specific functionality.
/// New version for use with Parameter property wrapper.
public class AKNodeParameter {

    private var dsp: AKDSPRef?
    private var avAudioUnit: AVAudioUnit!

    public private(set) var parameter: AUParameter?

    // MARK: Parameter properties

    public var value: AUValue = 0 {
        didSet {
            guard let min = parameter?.minValue, let max = parameter?.maxValue else { return }
            value = (min...max).clamp(value)
            if value == oldValue { return }
            parameter?.value = value
        }
    }

    public var boolValue: Bool {
        get { value > 0.5 }
        set { value = newValue ? 1.0 : 0.0 }
    }

    public var minValue: AUValue {
        parameter?.minValue ?? 0
    }

    public var maxValue: AUValue {
        parameter?.maxValue ?? 1
    }

    public var range: ClosedRange<AUValue> {
        (parameter?.minValue ?? 0) ... (parameter?.maxValue ?? 1)
    }

    // MARK: Automation

    private var renderObserverToken: Int?

    /// Begin automation of the parameter.
    ///
    /// If `startTime` is nil, the automation will be scheduled as soon as possible.
    ///
    /// - Parameter events: automation curve
    /// - Parameter startTime: optional time to start automation
    public func automate(events: [AKAutomationEvent], startTime: AVAudioTime? = nil) {

        var lastRenderTime = avAudioUnit.lastRenderTime ?? AVAudioTime(sampleTime: 0, atRate: AKSettings.sampleRate)

        if !lastRenderTime.isSampleTimeValid {
            lastRenderTime = AVAudioTime(sampleTime: 0, atRate: AKSettings.sampleRate)
        }

        var lastTime = startTime ?? lastRenderTime
        guard let parameter = parameter else { return }

        if lastTime.isHostTimeValid {
            // Convert to sample time.
            let lastTimeSeconds = AVAudioTime.seconds(forHostTime: lastRenderTime.hostTime)
            let startTimeSeconds = AVAudioTime.seconds(forHostTime: lastTime.hostTime)

            lastTime = lastRenderTime.offset(seconds: (startTimeSeconds - lastTimeSeconds))
        }

        assert(lastTime.isSampleTimeValid)
        stopAutomation()

        events.withUnsafeBufferPointer { automationPtr in

            guard let automationBaseAddress = automationPtr.baseAddress else { return }

            guard let observer = AKParameterAutomationGetRenderObserver(parameter.address,
                                                                  avAudioUnit.auAudioUnit.scheduleParameterBlock,
                                                                  Float(AKSettings.sampleRate),
                                                                  Float(lastTime.sampleTime),
                                                                  automationBaseAddress,
                                                                  events.count) else { return }

            renderObserverToken = avAudioUnit.auAudioUnit.token(byAddingRenderObserver: observer)
        }

    }

    /// Automate to a new value using a ramp.
    public func ramp(to value: AUValue, duration: AUValue) {
        automate(events: [AKAutomationEvent(targetValue: value, startTime: 0, rampDuration: duration)])
    }

    public func stopAutomation() {

        if let token = renderObserverToken {
            avAudioUnit.auAudioUnit.removeRenderObserver(token)
        }

    }

    private var parameterObserverToken: AUParameterObserverToken?

    /// Records automation for this parameter.
    /// - Parameter callback: Called on the main queue for each parameter event.
    public func recordAutomation(callback: @escaping (AUParameterAutomationEvent) -> Void) {

        guard let parameter = parameter else { return }
        parameterObserverToken = parameter.token(byAddingParameterAutomationObserver: { (numberEvents, events) in

            for index in 0..<numberEvents {
                let event = events[index]

                // Dispatching to main thread avoids the restrictions
                // required of parameter automation observers.
                DispatchQueue.main.async {<commit_msg>Don't need a render observer for a single ramp.<commit_after>// Copyright AudioKit. All Rights Reserved. Revision History at http://github.com/AudioKit/AudioKit/

import AVFoundation
import CAudioKit

public struct AKNodeParameterDef {
    public var identifier: String
    public var name: String
    public var address: AUParameterAddress
    public var range: ClosedRange<AUValue>
    public var unit: AudioUnitParameterUnit
    public var flags: AudioUnitParameterOptions
    public init(identifier: String,
                name: String,
                address: AUParameterAddress,
                range: ClosedRange<AUValue>,
                unit: AudioUnitParameterUnit,
                flags: AudioUnitParameterOptions) {
        self.identifier = identifier
        self.name = name
        self.address = address
        self.range = range
        self.unit = unit
        self.flags = flags
    }
}

/// AKNodeParameter wraps AUParameter in a user-friendly interface and adds some AudioKit-specific functionality.
/// New version for use with Parameter property wrapper.
public class AKNodeParameter {

    private var dsp: AKDSPRef?
    private var avAudioUnit: AVAudioUnit!

    public private(set) var parameter: AUParameter?

    // MARK: Parameter properties

    public var value: AUValue = 0 {
        didSet {
            guard let min = parameter?.minValue, let max = parameter?.maxValue else { return }
            value = (min...max).clamp(value)
            if value == oldValue { return }
            parameter?.value = value
        }
    }

    public var boolValue: Bool {
        get { value > 0.5 }
        set { value = newValue ? 1.0 : 0.0 }
    }

    public var minValue: AUValue {
        parameter?.minValue ?? 0
    }

    public var maxValue: AUValue {
        parameter?.maxValue ?? 1
    }

    public var range: ClosedRange<AUValue> {
        (parameter?.minValue ?? 0) ... (parameter?.maxValue ?? 1)
    }

    // MARK: Automation

    private var renderObserverToken: Int?

    /// Begin automation of the parameter.
    ///
    /// If `startTime` is nil, the automation will be scheduled as soon as possible.
    ///
    /// - Parameter events: automation curve
    /// - Parameter startTime: optional time to start automation
    public func automate(events: [AKAutomationEvent], startTime: AVAudioTime? = nil) {

        var lastRenderTime = avAudioUnit.lastRenderTime ?? AVAudioTime(sampleTime: 0, atRate: AKSettings.sampleRate)

        if !lastRenderTime.isSampleTimeValid {
            lastRenderTime = AVAudioTime(sampleTime: 0, atRate: AKSettings.sampleRate)
        }

        var lastTime = startTime ?? lastRenderTime
        guard let parameter = parameter else { return }

        if lastTime.isHostTimeValid {
            // Convert to sample time.
            let lastTimeSeconds = AVAudioTime.seconds(forHostTime: lastRenderTime.hostTime)
            let startTimeSeconds = AVAudioTime.seconds(forHostTime: lastTime.hostTime)

            lastTime = lastRenderTime.offset(seconds: (startTimeSeconds - lastTimeSeconds))
        }

        assert(lastTime.isSampleTimeValid)
        stopAutomation()

        events.withUnsafeBufferPointer { automationPtr in

            guard let automationBaseAddress = automationPtr.baseAddress else { return }

            guard let observer = AKParameterAutomationGetRenderObserver(parameter.address,
                                                                  avAudioUnit.auAudioUnit.scheduleParameterBlock,
                                                                  Float(AKSettings.sampleRate),
                                                                  Float(lastTime.sampleTime),
                                                                  automationBaseAddress,
                                                                  events.count) else { return }

            renderObserverToken = avAudioUnit.auAudioUnit.token(byAddingRenderObserver: observer)
        }

    }

    /// Automate to a new value using a ramp.
    public func ramp(to value: AUValue, duration: Double) {
        guard let parameter = parameter else { return }
        let paramBlock = avAudioUnit.auAudioUnit.scheduleParameterBlock
        paramBlock(AUEventSampleTimeImmediate,
                   AUAudioFrameCount(duration * AKSettings.sampleRate),
                   parameter.address,
                   value)
    }

    public func stopAutomation() {

        if let token = renderObserverToken {
            avAudioUnit.auAudioUnit.removeRenderObserver(token)
        }

    }

    private var parameterObserverToken: AUParameterObserverToken?

    /// Records automation for this parameter.
    /// - Parameter callback: Called on the main queue for each parameter event.
    public func recordAutomation(callback: @escaping (AUParameterAutomationEvent) -> Void) {

        guard let parameter = parameter else { return }
        parameterObserverToken = parameter.token(byAddingParameterAutomationObserver: { (numberEvents, events) in

            for index in 0..<numberEvents {
                let event = events[index]

                // Dispatching to main thread avoids the restrictions
                // required of parameter automation observers.
                DispatchQueue.main.async {
{'fraction_non_alphanumeric': 0.07164869029275808, 'fraction_numerical': 0.0027927580893682587, 'mean_word_length': 2.042777615001465, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 20, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2356995', 'n_tokens_mistral': 2545, 'n_tokens_neox': 2385, 'n_words': 727}