     }
     public func startImmediately() { //skips the offset
         //this starts the recurring timer
-        //print("starting fade \((finalVolume > initialVolume ? "up" : "down"))")
+        let millis = NSDate().timeIntervalSince1970*1000
+        print("starting fade \(millis) \(directionString)")
         stepCounter = 0
         
-        if fadeTimer.valid {
-            fadeTimer.invalidate()
-        }
-        fadeTimer = NSTimer.scheduledTimerWithTimeInterval(controlRate,
-                                                           target: self,
-                                                           selector: #selector(updateFade),
-                                                           userInfo: nil,
-                                                           repeats: true)
-    }
-    
-    @objc func updateFade(timer: NSTimer) {
+        if cadUpdateTimer != nil{
+            cadUpdateTimer?.invalidate()
+        }
+        if numberOfSteps == 0{ //if no steps to run, go ahead and set the volume early
+            output!.gain = finalVolume
+        }
+        cadUpdateTimer = CADisplayLink(target: self, selector: #selector(updateFadeFromCADTimer))
+        cadUpdateTimer?.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSRunLoopCommonModes)
+        }
+    
+    @objc func updateFadeFromCADTimer() {
         let direction: Double = (initialVolume > finalVolume ? 1.0 : -1.0)
-        
-        if stepCount == 0{
+        let millis = NSDate().timeIntervalSince1970*1000
+        print("updatingFade fade \(millis) - \(stepCounter) \(directionString)")
+        if numberOfSteps == 0{
             endFade()
-        }else if stepCounter <= stepCount {
-            let controlAmount: Double = Double(stepCounter) / Double(stepCount) //normalized 0-1 value
+        }else if stepCounter <= numberOfSteps {
+            let controlAmount: Double = Double(stepCounter) / Double(numberOfSteps) //normalized 0-1 value
             var scaledControlAmount: Double = 0.0
             
             switch curveType {

        self.curvature = curvature
        self.duration = duration
        self.offset = offset
        stepCount = Int(floor(duration / controlRate))
        self.output = output
    }
    
    func scheduleFade(fireTime: Double) {
        //this schedules a fade to start after fireTime
        fadeScheduleTimer = NSTimer.scheduledTimerWithTimeInterval(
            fireTime + offset,
            target: self,
            selector: #selector(scheduleFadeTimer),
            userInfo: nil,
            repeats: false)
        //print("scheduled for \(fireTime) @ \(duration) seconds")
        output!.gain = initialVolume
    }
    @objc func scheduleFadeTimer(timer: NSTimer) {
        startImmediately()
    }
    
    public func start() { //starts the fade WITH the offset

        }
        return curvePoints
    }
    
    func scheduleCADTimer(seconds: Double) {
        if seconds == 0 {
            startImmediately()
        }else{
            let delayRate = (seconds / cadTimerRate)
            let frameDelay = Int(floor(delayRate))
            cadDelayTimer = CADisplayLink(target: self, selector: #selector(startFadeFromCADTimer))
            cadDelayTimer?.frameInterval = frameDelay
            cadDelayTimer?.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSRunLoopCommonModes)
        }
    }
    
    @objc func startFadeFromCADTimer() {
        let millis = NSDate().timeIntervalSince1970*1000
        print("startFadeFromCADTimer fired \(millis) - \(cadTimerIncrement) \(directionString)")
        //runs twice, once when scheduled and again at next frame, so we have to skip the first one and run the second.
        if cadTimerIncrement > 0 {
            cadDelayTimer?.invalidate()
            startImmediately()
        }
        cadTimerIncrement += 1
    }
}
