Updating a variable in a function driven by Timer - ios

I have the below function that works properly when a button is switched to activate it. I want to add a variable that gives each message it's current message number starting from 1. I've tried different methods of setting / updating the value but none of it helped with updating.
I'm very new to Swift / iOS development so I'm sure there is something I'm missing. What I do know is that the message prints to console repeatedly till the button is switched off and the Timer is what enables it to continuously run.
#IBOutlet weak var stateLabel: UILabel!
//Starts / Stops recording of sensor data via a switch
#IBAction func stateChange(_ sender: UISwitch) {
if sender.isOn == true {
startSensorData()
stateLabel.text = "Stop"
} else {
stopSensorData()
stateLabel.text = "Start"
}
}
func startSensorData() {
print("Start Capturing Sensor Data")
// Making sure sensors are available
if self.motionManager.isAccelerometerAvailable, self.motionManager.isGyroAvailable {
// Setting the frequency required for data session
self.motionManager.accelerometerUpdateInterval = 1.0 / 3.0
self.motionManager.gyroUpdateInterval = 1.0 / 3.0
// Start sensor updates
self.motionManager.startAccelerometerUpdates()
self.motionManager.startGyroUpdates()
// Configure a timer to fetch the data.
self.motionUpdateTimer = Timer.scheduledTimer(withTimeInterval: 1.0/3.0, repeats: true, block: { (timer1) in
// Get the motion data.
var loggingSample = 1
if let accelData = self.motionManager.accelerometerData, let gyroData = self.motionManager.gyroData {
let accelX = accelData.acceleration.x
let accelY = accelData.acceleration.y
let accelZ = accelData.acceleration.z
let gyroX = gyroData.rotationRate.x
let gyroY = gyroData.rotationRate.y
let gyroZ = gyroData.rotationRate.z
let message = "\(Date().timeIntervalSince1970),\(self.device_id),\(loggingSample),\(accelX),\(accelY),\(accelZ),\(gyroX),\(gyroY),\(gyroZ),Processing"
print(message)
loggingSample += 1
}
}
)}
}

You keep getting a value of 1 for loggingSample because you are using a local variable that gets created as 1 each time.
All you need to do is move the declaration of loggingSample to be outside the function so it is a class property.
Move the line:
var loggingSample = 1
outside the function so it is next to your outlets and other properties.

Related

Using same swift UIlabels for different function values

I am programming an iOS app using swift where i have 3 UILabels which will show data different sensors data into the same corresponding labels.
These are 3 labels which i am using.
#IBOutlet weak var xAccel: UILabel!
#IBOutlet weak var yAccel: UILabel!
#IBOutlet weak var zAccel: UILabel!
I am using UIsegmentedControl to change the display of data which is as follows.
#IBAction func AccelDidChange(_ sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 0:
myAccelerometer()
break
case 1:
myGyroscope()
break
default:
myAccelerometer()
}
Above used 2 functions are as follows
func myAccelerometer() {
// sets the time of each update
motion.accelerometerUpdateInterval = 0.1
//accessing the data from the accelerometer
motion.startAccelerometerUpdates(to: OperationQueue.current!) { (data, error) in
// can print the data on the console for testing purpose
//print(data as Any)
if let trueData = data {
self.view.reloadInputViews()
//setting different coordiantes to respective variables
let x = trueData.acceleration.x
let y = trueData.acceleration.y
let z = trueData.acceleration.z
//setting the variable values to label on UI
self.SensorName.text = "Accelerometer Data"
self.xAccel.text = "x : \(x)"
self.yAccel.text = "y : \(y)"
self.zAccel.text = "z : \(z)"
}
}
}
func myGyroscope() {
motion.gyroUpdateInterval = 0.1
motion.startGyroUpdates(to: OperationQueue.current!) { (data, error) in
if let trueData = data {
self.view.reloadInputViews()
//setting different coordiantes to respective variables
let x = trueData.rotationRate.x
let y = trueData.rotationRate.y
let z = trueData.rotationRate.z
//setting the variable values to label on UI
self.SensorName.text = "Gyroscope Data"
self.xAccel.text = "x: \(x)"
self.yAccel.text = "y: \(y)"
self.zAccel.text = "z: \(z)"
}
}
}
**
Problem is it keeps on displaying both the Accelerometer and Gyroscope data on UILabels at the same time instead of only showing the data of a particular sensor when tapped. I have tried to use the break option but still not working. If any one could point out the possible solution, that would be great. Thanks
**
EIDT -
Here is the output on screen where you can see the values fluctuates between different sensors. I only want readings from 1 sensor at a time.
https://imgur.com/a/21xW4au
#IBAction func AccelDidChange(_ sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 0:
motion.stopGyroUpdates()
myAccelerometer()
break
case 1:
motion.stopDeviceMotionUpdates()
myGyroscope()
break
default:
myAccelerometer()
}
}
You need to stop unneeded resource before the switch. Try this please

Clicks / Distortion in AudioKit

When I add a bunch (20-40) samples playing and overlapping eachother simultaneously sometimes it starts getting distorted and then some waving, oscillating, and clicking begins to happen. A similar sound happens when the samples are playing the the app crashes - sounds like an abrupt, crunchy halt.
Notice the waviness begins between 0:05 and 0:10; nasty clicks start around 0:15.
Listen Here
How can I make it smoother? I am spawning AKPlayer objects (from 4.1) that play 4-8 second .wav files. Those go into AKBoosters which go into AKMixers which go into the final AKMixer for output.
Edit:
Many PenAudioNodes get plugged into the mixer of the AudioReceiver singleton.
Here's my AudioReceiver singleton:
class AudioReceiver {
static var sharedInstance = AudioReceiver()
private var audioNodes = [UUID : AudioNode]()
private let mixer = AKMixer()
private let queue = DispatchQueue(label: "audio-queue")
//MARK: - Setup & Teardown
init() {
AudioKit.output = mixer //peakLimiter
AudioKit.start()
}
//MARK: - Public
func audioNodeBegan(_ message : AudioNodeMessage) {
queue.async {
var audioNode: AudioNode?
switch message.senderType {
case .pen:
audioNode = PenAudioNode()
case .home:
audioNode = LoopingAudioNode(with: AudioHelper.homeLoopFile())
default:
break
}
if let audioNode = audioNode {
self.audioNodes[message.senderId] = audioNode
self.mixer.connect(input: audioNode.output)
audioNode.start(message)
}
}
}
func audioNodeMoved(_ message : AudioNodeMessage) {
queue.async {
if let audioNode = self.audioNodes[message.senderId] {
audioNode.update(message)
}
}
}
func audioNodeEnded(_ message : AudioNodeMessage) {
queue.async {
if let audioNode = self.audioNodes[message.senderId] {
audioNode.stop(message)
}
self.audioNodes[message.senderId] = nil
}
}
}
Here's my PenAudioNode:
class PenAudioNode {
fileprivate var mixer: AKMixer?
fileprivate var playersBoosters = [AKPlayer : AKBooster]()
fileprivate var finalOutput: AKNode?
fileprivate let file: AKAudioFile = AudioHelper.randomBellSampleFile()
//MARK: - Setup & Teardown
init() {
mixer = AKMixer()
finalOutput = mixer!
}
}
extension PenAudioNode: AudioNode {
var output: AKNode {
return finalOutput!
}
func start(_ message: AudioNodeMessage) {
}
func update(_ message: AudioNodeMessage) {
if let velocity = message.velocity {
let newVolume = Swift.min((velocity / 50) + 0.1, 1)
mixer!.volume = newVolume
}
if let isClimactic = message.isClimactic, isClimactic {
let player = AKPlayer(audioFile: file)
player.completionHandler = { [weak self] in
self?.playerCompleted(player)
}
let booster = AKBooster(player)
playersBoosters[player] = booster
booster.rampTime = 1
booster.gain = 0
mixer!.connect(input: booster)
player.play()
booster.gain = 1
}
}
func stop(_ message: AudioNodeMessage) {
for (_, booster) in playersBoosters {
booster.gain = 0
}
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 1) {
self.mixer!.stop()
self.output.disconnectOutput()
}
}
private func playerCompleted(_ player: AKPlayer) {
playersBoosters.removeValue(forKey: player)
}
}
This sounds like you are not releasing objects and you are eventually overloading the audio engine with too many instances of processing nodes connected in the graph. In particular not releasing AKBoosters will cause an issue like this. I can't really tell what your code is doing, but if you are spawning objects without releasing them properly, it will lead to garbled audio.
You want to conserve objects as much as possible and make sure you are using the absolute minimum amount of AKNode based processing.
There are various ways to debug this, but you can start by printing out the current state of the AVAudioEngine:
AudioKit.engine.description
That will show how many nodes you have connected in the graph at any given moment.

Why is "print into console" much faster than ".text into label "

I´m trying to display the cadence and pace data of CMPedometer. When I run the application with my phone attached, it writes the output of the data immediately through the print("...") function into the console, but takes multiple turns until it displays the data in the UILabel.
How can I get the data as fast as possible so I can use them?
Best, Zack
import UIKit
import CoreMotion
class ViewController: UIViewController {
let pedometer = CMPedometer()
#IBOutlet weak var paceLabel: UILabel!
#IBOutlet weak var cadenceLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
guard CMPedometer.isCadenceAvailable() && CMPedometer.isPaceAvailable() else{
print("Pace and cadence data are not available")
return
}
let oneWeekAgo = NSDate(timeIntervalSinceNow: -(7 * 24 * 60 * 60))
pedometer.startUpdates(from: oneWeekAgo as Date) {data, error in
guard let pData = data , error == nil else{
return
}
//The current pace of the user, measured in seconds per meter. (1 step = 83cm?)
if let pace = pData.currentPace{
print("Pace = \(pace)")
self.paceLabel.text = "Pace = \(round(Double(pace))*10/10)"
}
//The rate at which steps are taken, measured in steps per second.
if let cadence = pData.currentCadence{
self.cadenceLabel.text = "Cadence = \(cadence))"
print("Cadence = \(cadence)")
}
}// -----------------oneWeekAgo
}// -----------------ViewDidLoad
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}//-------------------- UIViewController
The update block is called on a background thread and you need to update your UI on the main thread. Wrap the UI update calls in a dispatch back to the main thread:
Dispatch.main.async {
//The current pace of the user, measured in seconds per meter. (1 step = 83cm?)
if let pace = pData.currentPace{
print("Pace = \(pace)")
self.paceLabel.text = "Pace = \(round(Double(pace))*10/10)"
}
//The rate at which steps are taken, measured in steps per second.
if let cadence = pData.currentCadence{
self.cadenceLabel.text = "Cadence = \(cadence))"
print("Cadence = \(cadence)")
}
}
}

How to detect max dB Swift

I'm trying to detect dB on a iOS Device, however, I am new to AV audio foundation can't really get to figure it out. I have come across this post: iOS - Detect Blow into Mic and convert the results! (swift), but it is not working for me.
My current code is this:
import Foundation
import UIKit
import AVFoundation
import CoreAudio
class ViewController: UIViewController {
var recorder: AVAudioRecorder!
var levelTimer = NSTimer()
var lowPassResults: Double = 0.0
override func viewDidLoad() {
super.viewDidLoad()
//make an AudioSession, set it to PlayAndRecord and make it active
var audioSession:AVAudioSession = AVAudioSession.sharedInstance()
audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord, withOptions: nil)
audioSession.setActive(true, error: nil)
//set up the URL for the audio file
var documents: AnyObject = NSSearchPathForDirectoriesInDomains( NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0]
var str = documents.stringByAppendingPathComponent("recordTest.caf")
var url = NSURL.fileURLWithPath(str as String)
// make a dictionary to hold the recording settings so we can instantiate our AVAudioRecorder
var recordSettings: [NSObject : AnyObject] = [AVFormatIDKey:kAudioFormatAppleIMA4,
AVSampleRateKey:44100.0,
AVNumberOfChannelsKey:2,AVEncoderBitRateKey:12800,
AVLinearPCMBitDepthKey:16,
AVEncoderAudioQualityKey:AVAudioQuality.Max.rawValue
]
//declare a variable to store the returned error if we have a problem instantiating our AVAudioRecorder
var error: NSError?
//Instantiate an AVAudioRecorder
recorder = AVAudioRecorder(URL:url, settings: recordSettings, error: &error)
//If there's an error, print otherwise, run prepareToRecord and meteringEnabled to turn on metering (must be run in that order)
if let e = error {
print(e.localizedDescription)
} else {
recorder.prepareToRecord()
recorder.meteringEnabled = true
//start recording
recorder.record()
//instantiate a timer to be called with whatever frequency we want to grab metering values
self.levelTimer = NSTimer.scheduledTimerWithTimeInterval(0.02, target: self, selector: #selector(ViewController.levelTimerCallback), userInfo: nil, repeats: true)
}
}
//This selector/function is called every time our timer (levelTime) fires
func levelTimerCallback() {
//we have to update meters before we can get the metering values
recorder.updateMeters()
//print to the console if we are beyond a threshold value. Here I've used -7
if recorder.averagePowerForChannel(0) > -7 {
print("Dis be da level I'm hearin' you in dat mic ")
print(recorder.averagePowerForChannel(0))
print("Do the thing I want, mofo")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
i was currently building my app about movie making,and learned something about how to metering sound level in dB.
the origin data of recorder.averagePowerForChannel is not really dB level of the sound,it's provide a level range which is -160 - 0,so we need some modification to make this data more reasonable
so i was finding some thing that makes this data(value) convert to dB level data.
(Sorry about forgot where i was found it!)
here is the code
/**
Format dBFS to dB
- author: RÅGE_Devil_Jåmeson
- date: (2016-07-13) 20:07:03
- parameter dBFSValue: raw value of averagePowerOfChannel
- returns: formatted value
*/
func dBFS_convertTo_dB (dBFSValue: Float) -> Float
{
var level:Float = 0.0
let peak_bottom:Float = -60.0 // dBFS -> -160..0 so it can be -80 or -60
if dBFSValue < peak_bottom
{
level = 0.0
}
else if dBFSValue >= 0.0
{
level = 1.0
}
else
{
let root:Float = 2.0
let minAmp:Float = powf(10.0, 0.05 * peak_bottom)
let inverseAmpRange:Float = 1.0 / (1.0 - minAmp)
let amp:Float = powf(10.0, 0.05 * dBFSValue)
let adjAmp:Float = (amp - minAmp) * inverseAmpRange
level = powf(adjAmp, 1.0 / root)
}
return level
}
i was noticed that you are recording whit 2 channels so it will be little different with my code;
wish could help you out or give you some ideas :D
LAST UPDATE
Change coding language to swift

Creating multiple Countdown Timers in Swift

Okay so I'm trying to add a 2 seconds countdown timer in a label that activates whenever the user double taps the screen.
So upon doubletapping, the user would find a new label created with the number "2" then a second later "1" then "0".
I can do this perfectly for just 1 timer. When the user double taps to create a second timer, or more, the app crashes.
I found that this is due to the declaration of the variables being made in the class instead of in the function to allow the timer function to retrieve such declarations as the label.
If I found a way to make the timer function inside another function instead of under class it will work fine.
So here's the code that works only when 1 timer is being deployed.
Under Class:
var BTTimer = NSTimer ()
var BTCounter = 2
let BT = SKLabelNode
Under Function touchesBegan:
let B = SKSpriteNode(imageNamed: "B 110.png")
let Touch : UITouch! = touches.first
let TouchLocation = Touch.locationInNode(self)
let PreviousTouchLocation = Touch.previousLocationInNode(self)
let Player = childNodeWithName("Player") as! SKSpriteNode
let xPos = Player.position.x + (TouchLocation.x - PreviousTouchLocation.x)
let yPos = Player.position.y + (TouchLocation.y - PreviousTouchLocation.y)
B.position = CGPointMake(xPos, yPos)
B.zPosition = -2
self.addChild(B)
BT.fontSize = 27;
BT.fontColor = UIColor.redColor()
BT.position = CGPointMake(xPos - 9, yPos - 30)
BT.zPosition = -1
self.addChild(BT)
BT.text = String(BTCounter);
BTTimer = NSTimer.scheduledTimerWithTimeInterval(1, target:self, selector: #selector(GameScene.updateBCounter), userInfo: nil, repeats: true)
}
}
Now under another function of the same class:
func updateBCounter() {
if(BTCounter > 0) {
BTCounter-=1
BT.text = String(BTCounter)
}
else if(BTCounter == 0) {
//Remove B
//Place C
}
I wish to change this so that the user is capable of producing as many timers as he wishes to deploy.
You can update updateBCounter like this:
func updateBCounter(timer: NSTimer)
Your selector is changed to #selector(GameScene.updateBCounter(_:)
When you schedule the timer, you can set userInfo to any dictionary and the timer sent to updateBCounter will have it in its userInfo property.
I don't know exactly what you are trying to do, but those are the tools to do multiple timers at once calling the same selector, but knowing which one you are dealing with.
Get rid of the properties keeping track of things and make keys in the userInfo dictionary instead.
You could use any object as userInfo (not just a dictionary), so you could also do:
class TimerData {
var counter: Int
var l: SKLabelNode
}

Resources