I'm new to AudioKit and am using v5. The oscillator frequency ramp doesn't seem to work as expected. The following example has 2 successive ramps. First it ramps 440-880, but then sounds like it goes 880-660 instead of 440-660.
If I comment out osc.$frequency.ramp(to: 660.0, duration: 1.0), it ramps 440-880, then ramps extremely quickly to 440 but gets there.
Seems like something isn't resetting after the ramp.
(The sleeps are only for testing.)
Is this a bug? Is there something else I should be doing? Any insight would be much appreciated! Thanks!
import Cocoa
import AudioKit
class Test {
let akEngine = AudioEngine()
let osc = Oscillator()
func setup() {
osc.amplitude = 0.1
akEngine.output = osc
do {
try akEngine.start()
} catch {
print("Couldn't start AudioEngine.")
}
osc.frequency = 440.0
osc.start()
osc.$frequency.ramp(to: 880.0, duration: 1.0)
sleep(2)
osc.stop()
sleep(1)
osc.frequency = 440.0
osc.start()
osc.$frequency.ramp(to: 660.0, duration: 1.0)
sleep(2)
osc.stop()
}
}
This probably deserves an explanation, or maybe it should be fixed to work as you have it above, but once you start automating changes, you should stick with the automation syntax and not jump out to plain setting of values. For you this means just replacing osc.frequency = 440 with osc.$frequency.ramp(to: 440.0, duration: 0.0) and I believe you will get the effect you want.
Setting a parameter (osc.frequency = 440.0) causes a tiny ramp to be used to avoid zippering (equivalent to osc.$frequency.ramp(to: 440.0, duration:epsilon)). The test code immediately applies another ramp, overriding the zippering ramp.
If you insert a sleep after your second osc.start(), you'll hear the quick zipper ramp back to 440.
Related
If I use AKOscillator only for specific purpose, should I anyway use Envelopes classes to avoide
amplitude click when I start/stop oscillator?
Or there any other more light methods?
One "light" method is to set your parameter ramp to a non zero value, start your amplitude at zero, and then set your amplitude. Ramping is the same value for all parameters, though, so depending on if you want your frequency to change at a different ramp, you may want to change the ramp again after it has reached the amplitude you want.
Here's an example playground:
import AudioKitPlaygrounds
import AudioKit
let oscillator = AKOscillator(waveform: AKTable(.sine), amplitude: 0)
oscillator.rampDuration = 0.2
AudioKit.output = oscillator
try AudioKit.start()
oscillator.start()
oscillator.amplitude = 1.0
sleep(1)
oscillator.amplitude = 0
I used your code and it did not help, but I found out that this 'click' appears on the end, when oscillator stops. so if even rampDuration is 0.0, there is not 'click' at the start, the only 'click' is on the end. Here is my code (it is inside IOS app):
class ViewController: UIViewController {
var osc = AKOscillator(waveform: AKTable(.sine), amplitude: 0)
#IBAction func buttonTapped(_ sender: UIButton) { //when button in App is pressed
osc.rampDuration = 0.2
AudioKit.output = osc
osc.frequency = Double.random(in: 100.0...1000.0)
try? AudioKit.start()
osc.start()
osc.amplitude = 0.5
osc.rampDuration = 0.0 //to avoid frequency glide effect
sleep(1)
//osc.rampDuration - I tried to change rampDuration before oscillator stop, but it
//did not help
osc.stop() //here is amplitude 'click' appears
try? AudioKit.stop()
}
So, as I supposed I have to use envelopes anyway?
In a game I'm developing with SpriteKit, I want certain objects to appear and shrink. I already know how to scale them down, and I'm achieving this using the following code:
myNode.run(SKAction.scale(to: 0, duration: 3))
However, the shrinking happens 'linearly'. Is there a way to make it shrink exponentially faster? Or at least that it starts slowly and at the last second it shrinks twice as fast?
Sorry I could not test this out, I do not have a compiler. It is properly not the best way to do it, but I gave it a shot:
func delay(_ delay:Double, closure:#escaping ()->()) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
let totalRunTime = 1
var add = 0.0
var scale = 1
var done = false
while !done{
delay(add, closure: {
add += 0.1
scale = 1 - (add ^2)
myNode.run(SKAction.scale(to: scale, duration: totalRunTime / 10))
})
if add == 0{
done = true
}
}
Edit: When I look at my code I may see a bug: maybe you need to switch the exponential formula from scale to duration to make it work, I can not test it now :(
Have a look at the Sprite Kit Utils from Ray Wenderlich. It's quite a useful library and also provides easing functions for movement, scale, and rotate actions.
(For reference, have a look at the different easing functions demonstrated on easings.net)
Hope that helps!
I am trying to playback audiofiles using with the sequencer in AudioKit framework.
AudioKit.output = sampler
AudioKit.start()
sampler.enableMIDI(midi.client,name: "sampler")
// sequencer start
let seq = AKSequencer()
seq.setLength(AKDuration(beats:Double(4)))
seq.enableLooping()
let pattern = seq.newTrack()
pattern?.setMIDIOutput(sampler.midiIn)
pattern!.add(noteNumber: 48, velocity: 127, position: AKDuration(beats:Double(1)), duration: AKDuration(beats:Double(0.2)), channel: 0)
pattern!.add(noteNumber: 48, velocity: 127, position: AKDuration(beats:Double(1)), duration: AKDuration(beats:Double(0.2)), channel: 0)
pattern!.add(noteNumber: 48, velocity: 127, position: AKDuration(beats:Double(2)), duration: AKDuration(beats:Double(0.2)), channel: 0)
pattern!.setLoopInfo(AKDuration( beats:Double(4) ), numberOfLoops: 80)
seq.play()
I got to the point where the AKMidiSampler will only play sine waves but not the right sample as described here
So as it turns out it is not possible to create sequences "on the fly" so i started to look for workarounds and found SelectorClock Its a workaround from the AudioKit Developers. Sadly this is not working anymore.. many of the class definitions and their properties changed.
Maybe I am not up to date and this is fixed already.. if not I'm sure there must be a go to solution to this issue.
Turn on the background capability to your target.
Choose audio.
Without this, you get just sine waves.
If you want to be completely independent from using AKSequencer you can try the following:
let metro = AKMetronome()
metro.tempo = 120.0
metro.frequency1 = 0
metro.frequency2 = 0
metro.callback = {
// your code e.g.: trigger a AKSamplePlayer() which should have been defined earlier in your code:
sample.play()
}
AudioKit.output = AKMixer(metro, sample)
try! AudioKit.start()
metro.start()
I haven‘t tested this piece of code since I am on my phone right now but it should work. I have this concept running on my iPhone 6s and it works very well. I also tried to replace AKMetronome() with my own class but I haven‘t figured out every single aspect of the sporth parameter yet. I basically want to get rid of initiating any metronome sound ( which is already set to zero in sporth so shouldn‘t produce any noise ) in the first place.. I‘ll let you know in case I‘ll achieve that.
I'm using Swift 3.0 and Xcode 8.2.1, testing on an iPhone 6s running iOS 10.2.
I'm simply attempting to rotate an SKCameraNode at a rate that is slowing down (I.E. it has both an angular velocity and acceleration).
This is my current solution, and yes I know it's weird:
cam.run(
SKAction.repeatForever(
SKAction.sequence([
SKAction.run
{
self.cam.run(SKAction.rotate(toAngle: 0.0, duration: 0.7, shortestUnitArc: true))
},
SKAction.wait(forDuration: 0.1)])))
The camera's zRotation starts at some non-zero place, and then this is called. It then calls the SKAction.rotate part every 0.1 seconds. It's not shown here, but I have it terminate after 3 seconds.
This provides the exact effect that I want, because every time the SKAction is called, the zRotation is closer to its 0.0, but the time it needs to take to get there stays at 0.7, so the rate at which it approaches 0.0 slows down.
HOWEVER, 1 of 3 things happen from this:
It works perfectly as intended
It feels like it stops and continues every 0.1 seconds
The camera just immediately and completely stops functioning as a camera as soon as the SKAction is called, and the player is just stuck looking at the same spot till the cameras are switched.
I tried reducing the waitForDuration from 0.1 to 0.01 but then it always does option 3.
Is there some sort of "rules of execution" that I'm not following when it comes to using cameras?
Thanks!
You can use a single rotate(toAngle:) action that decelerates when the camera's zRotation is close to the ending angle and setting the timingMode property to .easeOut. For example,
cam.zRotation = CGFloat.pi
let action = SKAction.rotate(toAngle: 0, duration: duration, shortestUnitArc: true)
// Slows when zRotation is near 0
action.timingMode = .easeOut
cam.run(action)
Alternatively, you can define your own timing function to customize the ease-out behavior by replacing timingMode statement with
action.timingFunction = {
time in
// Custom ease-out, modify this as needed
return 1-pow(1-time, 5)
}
Moreover, you can use the following to calculate the action's duration so the angular velocity and acceleration are consistent regardless of the starting angle (i.e., the amount of rotation)
let duration = TimeInterval(normalizedArcFromZero(angle:cam.zRotation)) * 3
func normalizedArcFromZero(angle:CGFloat) -> CGFloat {
let diff = angle.mod(dividingBy:CGFloat.pi*2)
let arc = CGFloat.pi - (diff + CGFloat.pi).mod(dividingBy:2 * CGFloat.pi)
return abs(arc)/CGFloat.pi
}
Lastly, since the above requires a modulo function that performs a "floored" division (instead of Swift's truncatingRemainder), you'll need to add this to your project
extension CGFloat {
func mod(dividingBy x:CGFloat) -> CGFloat {
return self - floor(self/x) * x
}
}
I am creating a very simple game using Swift and SpriteKit and I am moving a ball on the screen using the accelerometer data (acceleration x,y).
I would say the code works fine but I have noticed that sometimes (often right when I open the app) the accelerometer data is not correct and delayed for few seconds.
Why is that happening?
I am using the following code to read the accelerometer data:
if motionManager.accelerometerAvailable == true {
motionManager.startAccelerometerUpdatesToQueue(NSOperationQueue.currentQueue(), withHandler:{
data, error in
self.accX = CGFloat(data.acceleration.x)
self.accY = CGFloat(data.acceleration.y)
})
}
And the function update to apply some impulse to the ball:
override func update(currentTime: CFTimeInterval) {
var impulse = CGVectorMake(accX, accY)
var obj = childNodeWithName("ball") as SKSpriteNode
obj.physicsBody?.applyImpulse(impulse)
}
Am i missing something?
Thank you
With any accelerometer data, it is a good idea to run it through a filter to smooth out any irregular spikes. Here is my favorite:
double filteredAcceleration[3];
memset(filteredAcceleration, 0, sizeof(filteredAcceleration));
CMAccelerometerData *newestAccel = motionManager.accelerometerData;
filteredAcceleration[0] = (filteredAcceleration[0]*(1.0-alpha)) + (newestAccel.acceleration.x*alpha);
filteredAcceleration[1] = (filteredAcceleration[1]*(1.0-alpha)) + (newestAccel.acceleration.y*alpha);
filteredAcceleration[2] = (filteredAcceleration[2]*(1.0-alpha)) + (newestAccel.acceleration.z*alpha);
alpha can be any value from 0 to 1. The closer to 1 the more responsive it will be, the closer to zero the more smooth it will be. My favorite value on the iPhone is 0.2 It is a good compromise for smooth yet responsive for a game like doodle jump, or possibly moving a ball around.
I don't know why the accelerometer data is incorrect/delayed on startup, my guess would be that the hardware has to wake up and calibrate itself, but regardless of the why, if you implement a filter, it will smooth out these irregularities, and they won't be nearly as noticeable.
I have given priority to both functions and the issue seems fixed.
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {
// do some task
dispatch_async(dispatch_get_main_queue()) {
// code with priority
}
}