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/
Related
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.
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.
I am new to the site and Swift so any feedback on question technique is gratefully received.
I am trying to make the value of a slider change both the audio that it being played and the text label (or image).
I have created my outlets:
#IBOutlet weak var audioSlider: UISlider!
#IBOutlet weak var audioValue: UILabel!
And my action:
#IBAction func audioSliderValueChanged(sender: UISlider) {
var currentValue = (sender.value)
audioValue.text = (StringInterpolationConvertible: audioValue))
All connected to one slider. I am unsure of how to approach this issue - would an if else work?
EDIT: Han Yong Code:
There was already a 'do' for other sounds within the app using AVFoundation. I imported Foundation (AVFoundation was already imported)
I have added:
#IBOutlet weak var audioSlider: UISlider!
#IBOutlet weak var audioValue: UILabel!
var audioSliderPlayer:AVAudioPlayer = AVAudioPlayer()
var currentTime: Double {
get {
return CMTimeGetSeconds(audioSliderPlayer.currentTime)
}
set {
let newTime = CMTimeMakeWithSeconds(newValue, 1)
audioSliderPlayer.seekToTime(newTime, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)
}
}
And added the following after the do function (within viewDidLoad):
{timeObserverToken = audioSliderPlayer.addPeriodicTimeObserverForInterval(interval, queue: dispatch_get_main_queue()) {
[weak self] time in
self?.timeSlider.value = Float(CMTimeGetSeconds(time))
}}
}
I have updated the action:
#IBAction func timeSliderDidChange(sender: UISlider) {
currentTime = Double(sender.value)
}
However, I am still having issues with errors. Also, how would I be able to specify which audio it plays dependent on value?
I guess you want the audioValue.text show the same value of slider, right?
Try this:
#IBAction func audioSliderValueChanged(sender: UISlider) {
var currentValue = (sender.value)
audioValue.text = "\(currentValue)"
}
If you use AVFoundation and AVPlayer, sample source that apple provide will be helpful.
In this source, assign var that has get and set method. In set method, update your label and player state. (Sample source snippet don't update label, but you can make it.)
var currentTime: Double {
get {
return CMTimeGetSeconds(player.currentTime())
}
set {
let newTime = CMTimeMakeWithSeconds(newValue, 1)
player.seekToTime(newTime, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)
}
}
If slider changed, it trigger variable's set method. Label and player status will be updated.
#IBAction func timeSliderDidChange(sender: UISlider) {
currentTime = Double(sender.value)
}
Add addPeriodicTimeObserverForInterval method to AVPlayer. It will be called every interval you set and trigger your variable's setter.
timeObserverToken = player.addPeriodicTimeObserverForInterval(interval, queue: dispatch_get_main_queue()) {
[weak self] time in
self?.timeSlider.value = Float(CMTimeGetSeconds(time))
}
I am trying to change the value of the speech rate property of my AVSpeechUtterance upon clicking a button in my app. I then want this value to carry over when I press the "speak" button.
If that explanation wasn't clear enough imagine this:
There are three buttons: one, two and three.
When the user presses one, the value of a the rate of an utterance changes (The same goes for the two and three).
Then after pushing one of the first three buttons, the user pushes another button that activates an utterance. This utterance will carry over the rate value and apply it to the speech.
Here is the code I have tried but doesn't work:
import UIKit
import AVFoundation
class SecondViewController: UIViewController {
#IBOutlet var minutesLabel: UITextField!
#IBOutlet var secondsField: UITextField!
func speak(sender: AnyObject) {
let speechUtterance = AVSpeechUtterance(string:exampleSpeech)
speechSynthesizer.speakUtterance(speechUtterance)
}
#IBOutlet var result: UILabel!
#IBAction func verySlow(sender: UIButton) {
let verySlowButtonPressed = true
let talkingSpeed = 90
let minutesValue : Double = (minutesLabel.text as NSString!).doubleValue
let secondsValue = (secondsField.text as NSString!).doubleValue
let secondsToMinutes = secondsValue / 60
let compiledTime = Double(minutesValue) + Double(secondsToMinutes)
let resultingWords = Double(compiledTime) * Double(talkingSpeed)
let resultCeiling = ceil(resultingWords)
result.text = "\(String(format: "%.0f", resultCeiling)) words"
if verySlowButtonPressed {
speechUtterance.rate = 0.25
speechUtterance.pitchMultiplier = 0.25
speechUtterance.volume = 0.75
}
}
#IBAction func speakButton(sender: AnyObject) {
speak(exampleSpeech)
}
In your speak function you are creating a new AVSpeechUtterance instance every time it's called. When your verySlow function is called, it's setting the rate, pitchMultiplier, and volume on what I'm presuming is a class property called speechUtterance. Since you're creating a new AVSpeechUtterance in speak each time it's called, those properties that you are setting in verySlow are not going to be set on the same instance.
One way to solve this would be to make rate, pitchMultiplier, and volume properties in your class, set those in your verySlow function, and then set them on speechUtterance after you create it in your speak function.
Something like:
var rate: Float?
var pitchMultiplier: Float?
var volume: Float?
func speak(sender: AnyObject) {
let speechUtterance = AVSpeechUtterance(string:exampleSpeech)
if let rate = self.rate {
speechUtterance.rate = rate
}
if let pitchMultiplier = self.pitchMultiplier {
speechUtterance.pitchMultiplier = pitchMultiplier
}
if let volume = self.volume {
speechUtterance.volume = volume
}
speechSynthesizer.speakUtterance(speechUtterance)
}
#IBAction func verySlow(sender: UIButton) {
// ... whatever other code you need here ...
self.rate = 0.25
self.pitchMultiplier = 0.25
self.volume = 0.75
}
Not sure but these are the value for speech rate, try using them. I hope it helps.
iOS 9
Very Slow -- 0.42
Slower -- 0.5
My Normal -- 0.53
Faster -- 0.56
Value are different for previous versions of iOS, please keep that in mind while implementing the solution.
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!")