AudioKit ios AKSequencer Not Restarting Playback Accurately - ios

I'm trying to use AudioKit to playback a sound on each beat of a measure(s). Although I've implemented the code from this similar question regarding callbacks via AudioKit, I can't seem to get the sequencer to update changes and playback properly. It will play once accurately, however after rewinding and changing the values it will only use the initial values (or not playback at all).
My intent is to create a struct of measures with beat values for each measure, then use MIDI and the callback to play different sounds dependent on how many measures/beats there are. Thanks!
import UIKit
import AudioKit
class ViewController: UIViewController {
let sequencer = AKSequencer()
let click = AKSynthSnare()
let callbackInst = AKCallbackInstrument()
// Create the struct that defines each line
struct Line {
var name: String
var measures: Int
var beatsPerMeasure: Int
func totalBeats() -> Int {
return (measures * beatsPerMeasure)
}
}
// Initialize intro line
var intro = Line(name: "Intro", measures: 0, beatsPerMeasure: 0)
// A function to create/update/playback the sequence on button press
func playBack() {
let metronomeTrack = sequencer.newTrack()
metronomeTrack?.setMIDIOutput(click.midiIn)
let callbackTrack = sequencer.newTrack()
callbackTrack?.setMIDIOutput(callbackInst.midiIn)
for steps in 0 ... Int(measuresRowOneValue) {
// this will trigger the sampler on the four down beats
metronomeTrack?.add(noteNumber: 60, velocity: 100, position: AKDuration(beats: Double(steps)), duration: AKDuration(beats: 0.5))
// set the midiNote number to the current beat number
callbackTrack?.add(noteNumber: MIDINoteNumber(steps), velocity: 100, position: AKDuration(beats: Double(steps)), duration: AKDuration(beats: 0.5))
// set the callback
callbackInst.callback = {status, noteNumber, velocity in
guard status == .noteOn else { return }
print("beat number: \(noteNumber + 1)")
}
}
}
#IBOutlet weak var rowOneLocationOne: UIImageView!
// Listener for UI display values
var measuresRowOneValue: Int = 0 {
didSet {
intro.measures = measuresRowOneValue
}
}
#IBAction func rowOnePlusButton(_ sender: UIButton) {
measuresRowOneValue += 1
}
#IBAction func rowOneMinusButton(_ sender: UIButton) {
measuresRowOneValue -= 1
}
#IBAction func playbackStart(_ sender: UIButton) {
playBack()
sequencer.play()
}
#IBAction func playbackStop(_ sender: UIButton) {
sequencer.stop()
}
#IBAction func playbackRestart(_ sender: UIButton) {
sequencer.rewind()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
AudioKit.output = click
try!AudioKit.start()
}
}

There are a few confusing things in your code, so I'm not sure if this is your only issue, but minimally, every time you change the length of your sequence, you will need to call setLength() followed by enableLooping. Basically, by default (i.e., unless you explicitly set the length) the length of the sequence will be the length of the longest track in the sequence. In your 'playback' method you are adding track on top of track without removing the old ones so it has no way of knowing how long you intend the sequence to be.
Your 'playback' method is doing two distinct things (neither of which involves playback). You might want to break it up. You could have a setup() to do the things that only ever need to be done once (create the tracks, set their outputs, set up the callback) and a rewriteSequence() methods that gets called when you want to re-write the tracks. This way you can reuse your existing tracks rather than continuously creating new ones.
var metronomeTrack: AKMusicTrack!
var callbackTrack: AKMusicTrack!
// call this just once at the start
func setup() {
metronomeTrack = sequencer.newTrack()
metronomeTrack?.setMIDIOutput(click.midiIn)
callbackTrack = sequencer.newTrack()
callbackTrack?.setMIDIOutput(callbackInst.midiIn)
callbackInst.callback = {status, noteNumber, velocity in
guard status == .noteOn else { return }
print("beat number: \(noteNumber + 1)")
}
}
// call this each time you want to change the sequence
func rewriteSequence() {
metronomeTrack?.clear()
callbackTrack?.clear()
for steps in 0 ... Int(measuresRowOneValue) {
metronomeTrack?.add(noteNumber: 60, velocity: 100, position: AKDuration(beats: Double(steps)), duration: AKDuration(beats: 0.5))
callbackTrack?.add(noteNumber: MIDINoteNumber(steps), velocity: 100, position: AKDuration(beats: Double(steps)), duration: AKDuration(beats: 0.5))
}
// This will make sure it loops correctly:
sequencer.setLength(AKDuration(beats: Double(measuresRowOneValue)))
sequencer.enableLooping()
}
I hope this helps.

Related

AudioKit 5 Amplitude Envelope no sound

I'm new here and new with music apps (and programming in general).
I'm trying to build a synth app using AudioKit 5 for my final project.
I made an oscillator and tried to add Amplitude Envelope but no sound coming out.
(If I put the oscillator in the output - there is a sound.)
I saw this question in the internet several times in different types but without any solution.
Does anyone know what is the problem?
And if not - do you have other solution for envelope?
The code:
import AudioKit
import SoundpipeAudioKit
import UIKit
class TryingViewController: UIViewController {
var osc = Oscillator(waveform: Table(.sine), frequency: 440, amplitude: 0.8)
var engine = AudioEngine()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func onBtn(_ sender: UIButton) {
let envelope = AmplitudeEnvelope(osc)
envelope.attackDuration = 0.1
envelope.decayDuration = 0.01
envelope.sustainLevel = 0.5
envelope.releaseDuration = 0.3
osc.start()
do {
try engine.start()
} catch {
print(error, "Field")
}
engine.output = envelope
envelope.start()
}
#IBAction func offBtn(_ sender: UIButton) {
osc.stop()
}
}
Edit:
I add this code and it works now, thanks to Aurelius Prochazk!
var isGateOpend = false
.
.
.
if isGateOpend{
envelope.closeGate()
isGateOpend = false
} else {
envelope.openGate()
isGateOpend = true
}
I still have a click, but I will open another question about it if I won't figure it out.
Thanks in advance!
The AmplitudeEnvelope is a "Gated" Node meaning that it responds to openGate and closeGate which should be used instead of start stop since those are at the node level rather than control level.

AKAmplitudeEnvelope is not working, just bypass the signal

Im trying to use AKAmplitudeEnvelope in the project, and it gives zero ADSR shaping. I wrote simple code to check this - the same result:
import Foundation
import AudioKit
class ViewController: UIViewController {
lazy var osc = AKOscillator(waveform: AKTable(.sine), frequency: 150, amplitude: 1)
lazy var envelope = AKAmplitudeEnvelope(osc)
override func viewDidLoad() {
super.viewDidLoad()
envelope.attackDuration = 0.01
envelope.decayDuration = 0.2
envelope.sustainLevel = 0.5
envelope.releaseDuration = 0.5
AudioKit.output = envelope
do {
try AudioKit.start()
} catch {
print(error)
}
osc.start()
}
#IBAction func startPressed(_ sender: UIButton) { //pressing the button
envelope.stop()
envelope.start()
}
}
So, when I press the button("startPressed" function) -just oscillator starting to sound permanently,
without any ADSR amplitude forming
It looks like you are still using AudioKit v4, but for v5, there's a cookbook full of examples, one of them being ADSR. https://github.com/AudioKit/Cookbook/

Computer AI turns in a UIViewController

In a swift game using UIKit I am writing, a human player will interact with UIKit UIButtons, GUI elements to take actions.
In the game, the player will play against AI players.
But here's the thing; the human player presses buttons and interacts and the AI player does not.
Given a simple UIViewController;
class SampleViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buyBtnPressed(_ sender: UIButton) {
print ("pressed")
}
}
So what I'm trying to ascertain is, how does the AI player itself take actions and handling turns within the context of the current view controller?
I believe the best way to do this is that there should be a loop that will wait until all players have completed their respective turns.
But where does this loop go? In the view did load?
If so, won't it eat up memory, or potentially lead (if not careful) to an endless loop?
I'm finding it hard to ascertain how an AI player can take actions within the given context of a UIViewController considering GUI elements are for human interaction.
I don't mean the AI should be animating pressing buttons or interacting with the screen, I mean; I have a UIViewController, it has a view did load; what is the strategy of implementing AI turns and whether or not this should be be achieved in a "game loop" in the View did load or can this be achieved in another way?
My question is; given the context of a UIViewController; how can I code the handling of an AI player taking turns and can this be achieved with a loop or another strategy?
Many thanks
edit: Code is now added
I have written out a turn base manager using Swift playgrounds, and 2 examples one using a UIViewController and another is a loop.
code now follows;
import Foundation
import GameplayKit
class Player {
var name: String
public private(set) var isAI: Bool = false
public private(set) var turnOrder: Int = 0
init(name: String, isAI: Bool?) {
self.name = name
if let hasAI = isAI {
self.isAI = hasAI
}
}
func setTurnOrderIndex(number: Int) {
self.turnOrder = number
}
}
let p1 = Player.init(name: "Bob", isAI: false)
let p2 = Player.init(name: "Alex", isAI: true)
protocol TurnOrderManagerDelegate: NSObjectProtocol {
func turnOrderWasSet()
}
protocol TurnDelegate: class {
func turnIsCompleted()
}
class Turn: NSObject {
weak var player: Player?
weak var delegate: TurnDelegate?
public private(set) var completed: Bool = false {
didSet {
delegate?.turnIsCompleted()
}
}
init(player:Player, delegate: TurnDelegate) {
self.player = player
self.delegate = delegate
}
func setAsComplete() {
self.completed = true
}
}
class TurnOrderManager: NSObject, TurnOrderManagerDelegate, TurnDelegate {
static var instance = TurnOrderManager()
public private(set) var turnOrderIndex: Int = 0
public private(set) var turnOrder: [Turn] = [Turn]() {
didSet {
self.turnOrderWasSet()
}
}
var playerOnTurn: Player? {
let turnObj = self.turnOrder[turnOrderIndex]
return (turnObj.player)
}
var allTurnsCompleted: Bool {
let filtered = turnOrder.filter { (turnObj:Turn) -> Bool in
return (turnObj.completed)
}.count
return (filtered == turnOrder.count)
}
func setTurnOrder(players:[Player]) {
if (self.turnOrder.count == 0) {
for playerObj in players {
let turnObj = Turn.init(player: playerObj, delegate: self)
self.turnOrder.append(turnObj)
}
}
}
func turnOrderWasSet() {
for (index, turnObj) in self.turnOrder.enumerated() {
turnObj.player?.setTurnOrderIndex(number: index)
}
}
func next() {
if (turnOrderIndex < (self.turnOrder.count - 1)) {
turnOrderIndex += 1
}
else {
turnOrderIndex = 0
}
}
internal func turnIsCompleted() {
print (" - turnIsCompleted")
TurnOrderManager.instance.next()
}
}
class GameModel {
var turnOrderManager: TurnOrderManager
init() {
self.turnOrderManager = TurnOrderManager.instance
self.turnOrderManager.setTurnOrder(players:[p1,p2])
}
// other game model stuff [...]
}
class Phase1State : GKState {
var gameModel: GameModel!
init(gameModel:GameModel) {
super.init()
self.gameModel = gameModel
}
override func isValidNextState(_ stateClass: AnyClass) -> Bool
{
return false
}
override func didEnter(from previousState: GKState?) {
}
override func willExit(to nextState: GKState) {
}
// MARK: - Action
func buy() {
let index = self.gameModel.turnOrderManager.turnOrderIndex
let turn = self.gameModel.turnOrderManager.turnOrder[index]
turn.setAsComplete()
}
}
class SomeViewController: UIViewController
{
var gameModel: GameModel?
weak var gamePhase: Phase1State?
var isPhaseComplete: Bool {
return self.gameModel?.turnOrderManager.allTurnsCompleted ?? false
}
override func viewDidLoad() {
super.viewDidLoad()
self.gameModel = GameModel.init()
self.gamePhase = Phase1State.init(gameModel: self.gameModel!)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func buyButtonPressed() {
self.gamePhase?.buy()
self.finishTurn()
}
func finishTurn() {
guard let turnIndex = self.gameModel?.turnOrderManager.turnOrderIndex else {
return
}
guard let turn = self.gameModel?.turnOrderManager.turnOrder[turnIndex] else {
return
}
turn.setAsComplete()
if (self.isPhaseComplete)
{
print ("All turns are completed")
}
else {
//self.gameModel?.turnOrderManager.next()
self.gamePhase?.buy()
guard let playerOnTurn = self.gameModel?.turnOrderManager.playerOnTurn else {
print ("No player is on turn")
return
}
print ("\(playerOnTurn.name) is on turn")
if (playerOnTurn.isAI)
{
self.gamePhase?.buy()
self.finishTurn()
}
}
}
}
// EXAMPLE 1 -- first attempt ...
let vc = SomeViewController()
vc.viewDidLoad()
vc.buyButtonPressed()
// EXAMPLE 2 -- another attempt ....
let gameModel: GameModel = GameModel.init()
let gamePhase = Phase1State.init(gameModel: gameModel)
// player then takes an action
while (gameModel.turnOrderManager.allTurnsCompleted == false)
{
let turnIndex = gameModel.turnOrderManager.turnOrderIndex
let turnObj = gameModel.turnOrderManager.turnOrder[turnIndex]
guard let playerOnTurn = turnObj.player else {
break
}
print ("Player \(playerOnTurn.name) is on turn")
gamePhase.buy()
}
print ("All turns are completed, advance to next phase")
The issue is;
On the finishTurn, it only seems to work if it relies on the first player in the index is a human player. If its not, I have no idea how to make it fire the buy action.
On the second example, I use a loop; but I'm concerned using a loop could end up just looping forever.
My query is therefore clarifyed, how can I ensure my view controller will fire actions for AI players when they don't press buttons and loop through each player and execute their respective turn.
Many thanks
Further edit:
I do not know if I should have the while (gameModel.turnOrderManager.allTurnsCompleted == false) loop inside my viewDidLoad() to act like a game loop.
There is no need to specifically use Sprite Kit for this. SpriteKit would be more to do with how the UI is made rather than how the logic of the game works.
However, I would recommend looking at GameplayKit. It's a framework that contains lots of built in game logic tools. Specifically you want something like the GKDecisionTree. There are a few WWDC videos about it too. GameplayKit can be used with SpriteKit, UIKit, SSceneKit or any other game engine that you decide to use (or not).
Also, the question you're asking is a very general question about game development. Having the computer "decide" to do something is quite a complex subject.
I'd also suggest having a quick watch of this video from AI & Games and other videos from that channel.
It'll give you an idea of how to approach your problem.
Session 609 and 608 from WWDC 2015 and 2016 are prob good :D
Regarding updating the AI.
Your AI should be event driven. You have the concept of "turns" and "players". There is a point in the game at which it becomes a "player's" "turn". (Even at the very beginning of the game it is either Player 1 or Player 2's turn.
At this time there are two possibilities. Either the player is an AI, or the player is a person.
As soon as this happens there should be some sort of trigger (like a function call or something) that tells the player its turn has started.
If that player is the AI then you need to start some sort of calculation (maybe with a built in delay to make it realistic) so that it decides what to do.
Look, I'm not sure on what kind of game you're making, buy you should probably learn SpriteKit, specially SKActions. With that, you can easily control the flow of events from your game.
With that said, how is your AI implementation? Based on your code, I would begin with something like this:
class AI {
enum Decision {
case doSomething
case doAnotherThing
case dontDoAnything
}
public func decide() -> Decision {
// Decide which action the AI will take...
return .doSomething // This return is just a example!
}
public func act(on : Decision) {
// Do whatever the AI needs based on a decision...
}
}
Then, in your ViewController:
class SampleViewController: UIViewController {
var ai = AI()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buyBtnPressed(_ sender: UIButton) {
print ("pressed")
ai.act(on: ai.decide())
}
}
I hope that helps!

How to switch on score to create an SKEmitterNode using SpriteKit?

I want to be able to toggle my SKEmitterNode (rain particles) off and on based on the score. But my update function gets called constantly, i.e. I end up with millions of particles on the screen with my current code below...how can I structure my code so that the rain particles will only get called once when a score is achieved?
class GameScene: SKScene, SKPhysicsContactDelegate {
func setUpRain() {
if let rainParticle = SKEmitterNode(fileNamed: "Rain") {
rainParticle.position = CGPointMake(frame.size.width, frame.size.height)
rainParticle.name = "rainParticle"
rainParticle.zPosition = Layer.Flash.rawValue
worldNode.addChild(rainParticle)
}
}
func makeItRain() {
let startRaining = SKAction.runBlock {
self.setUpRain()
}
runAction(startRaining, withKey: "Rain")
}
func stopRaining() {
removeActionForKey("Rain")
worldNode.enumerateChildNodesWithName("rainParticle", usingBlock: { node, stop in
node.removeFromParent()
})
}
}
class PlayingState: GKState {
unowned let scene: GameScene //used to gain access to our scene
override func updateWithDeltaTime(seconds: NSTimeInterval) {
scene.updateForegroundAndBackground()
scene.updateScore()
if scene.score > 2 {
scene.makeItRain()
}
if scene.score > 4 {
scene.stopRaining()
}
}
There's a few ways you can do this, but the simplest of these is to only call makeItRain() or stopRaining() once per toggle. What I mean by this is once makeItRain is called, it cannot be called again until stopRaining is called. This can be done with a boolean like so:
var rainToggle: Bool = false; //True = Raining
override func updateWithDeltaTime(seconds: NSTimeInterval) {
scene.updateForegroundAndBackground()
scene.updateScore()
if (scene.score > 4){
scene.stopRaining()
rainToggle = false;
}
else if (scene.score > 2 && !rainToggle) {
scene.makeItRain()
rainToggle = true;
}
}
This is only slightly inefficient since you are calling stopRaining() every frame for no reason, however it gets the job done and is easy to understand. Note also that I had to flip the order in which your if statements came (otherwise it wouldn't work).

Flow Control in Swift - AVAudioPlayer

I wrote an Iphone Swift app that plays a series of sounds in a random order using AVAudioPlayer-- for now a pop sound, a horn sound and a gong. It works when you hit the play button, except....
However, when I hit the stop button nothing happens- it doesn't respond (The stop does work if I have just one sound). I believe it is due to my flow control. If I did not put in the 'while soundPlayer.playing == true {}', the code would "fly" through the sound and not wait for it to finish.
How can I modify the code so the the sound plays to completion before going to the next sound? And also allow the stop button to be functional? See Code and screen shot below. (Actually Stack overflow will not allow me to post an image since I am so new)
//
// ViewController.swift
// InsultSchool2 Swift
//
// Created by JR on 12/3/14.
// Copyright (c) 2014 JR. All rights reserved.
//
import UIKit
import AVFoundation
//---------------------------------------------------
var soundPlayer:AVAudioPlayer = AVAudioPlayer()
// used to try to do some kind of flow control. May not be needed if better way is found.
var stopFlag:Bool = false
//-----------------------------------------------------
class ViewController: UIViewController {
#IBOutlet weak var valueSliderTime: UISlider!
#IBOutlet weak var valueLabelTime: UILabel!
#IBOutlet weak var valueStepperVolume: UIStepper!
#IBOutlet weak var valueLabelVolume: UILabel!
//------------------------------------------------------
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//Displays an initial Volume value in volume label on load
//Convert to an int. Otherwise you get a weird value when getting close to zero
//Multiply by 10 so the int works. Otherwise you would int'ing a number between 0.0 and 1.0.
// "\()" is a shorthand to convert whatever to a string
valueLabelVolume.text = "\(Int(valueStepperVolume.value * 10))"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//------------------------------------------------------
#IBAction func buttonStop(sender: UIBarButtonItem) {
NSLog("Enter Button Stop")
//?1 If a sound is not playing and stop is hit, then it crashes
//?2 the stop button does not work with the whlle loop below
soundPlayer.stop()
}
#IBAction func sliderTime(sender: UISlider) {
valueLabelTime.text = "\(Int(valueSliderTime.value))"
}
#IBAction func stepperVolume(sender: UIStepper) {
//Converted to an int. Otherwise you get a weird value when getting close to zero
valueLabelVolume.text = "\(Int(valueStepperVolume.value * 10))"
}
#IBAction func buttonPlay(sender: UIBarButtonItem) {
NSLog("Enter Button Start")
var soundArray:[String] = ["Sound0", "Sound1", "Sound2"]
// Randomizes a number to indicate which random sound to play in the array
/* Number is from 0 to number in the (). Don't add one or 0 will never play. Go one more than the numbers in the array. For example if you have 3 items in the array go to 3. THis will go from 0 to 2 (ie., 3 items)*/
// Reference----- var soundRandomNumber:Int = Int(arc4random_uniform(3))
var soundRandomNumber:Int
soundRandomNumber = Int(arc4random_uniform(3))
//Creates a random number to wait between sounds based on the slider value.
//arc4random requires a UInt32 (Unsigned is a positive number).
//_uniform is slightly more random than without the Uniform
//The documentation says to use Int otherwise.
println(Int(valueSliderTime.value))
NSLog("valueSliderTime.value")
var waitTimeRandomNumber = Int(arc4random_uniform(UInt32(valueSliderTime.value)))
println(waitTimeRandomNumber)
NSLog("waitTimeRandomNumber")
// Constructs a string with the random number for the URL
//var soundFile:String = soundArray[soundRandomNumber]
var soundFile:String
soundFile = soundArray[soundRandomNumber]
//Reference---- var soundURL = NSBundle.mainBundle().URLForResource(soundFile, withExtension:"mp3")
var soundURL:NSURL!
soundURL = NSBundle.mainBundle().URLForResource(soundFile, withExtension:"mp3")
soundPlayer = AVAudioPlayer(contentsOfURL: soundURL, error: nil)
//?3 How do I set up a loop or control that works until the stop button is pressed?
while stopFlag == false{
NSLog("inside while")
println(stopFlag)
//?4 Is the below correct? The actual volume does not seem to change though the .volume does
soundPlayer.volume = Float(valueStepperVolume.value)
println(Float(valueStepperVolume.value))
NSLog("Float(valueStepperVolume.value)")
println(soundPlayer.volume)
NSLog("soundPlayer.volume")
soundRandomNumber = Int(arc4random_uniform(3))
soundFile = soundArray[soundRandomNumber]
soundURL = NSBundle.mainBundle().URLForResource(soundFile, withExtension:"mp3")
soundPlayer = AVAudioPlayer(contentsOfURL: soundURL, error: nil)
soundPlayer.prepareToPlay()
soundPlayer.play()
//?5 How do I make the player not blow through the sound and wait until is finished
while soundPlayer.playing == true {
}
//?6 How can i make a random timer that waits for a random time before relooping?
waitTimeRandomNumber = Int(arc4random_uniform(UInt32(valueSliderTime.value)))
}// End of while loop
} //ends playButton IBAction
//?7 How to allow this app to play over other music in another player
}
use the repeat while statement instead to control the flow:
here is a link to apple's developers reference:
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html
repeat {
// move up or down for a snake or ladder
square += board[square]
// roll the dice
if ++diceRoll == 7 { diceRoll = 1 }
// move by the rolled amount
square += diceRoll
} while square < finalSquare
print("Game over!")

Resources