Integrating audio and accelerometer for Swift 2.0? (Using coremotion) - ios

import CoreMotion
var ButtonAudio4URL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("Swing", ofType: "wav")!)
var ButtonAudioPlayer4 = AVAudioPlayer()
do {
try ButtonAudioPlayer4 = AVAudioPlayer(contentsOfURL: ButtonAudio4URL)
ButtonAudioPlayer4.prepareToPlay()
} catch {
print("audioPlayer failure")
}
func play() {
ButtonAudioPlayer4.currentTime = 0
ButtonAudioPlayer4.play()
}
func play2() {
ButtonAudioPlayer4.currentTime = 0
ButtonAudioPlayer4.play()
}
func play3() {
ButtonAudioPlayer4.currentTime = 0
ButtonAudioPlayer4.play()
}
func stop() {
ButtonAudioPlayer4.currentTime = 0
ButtonAudioPlayer4.stop()
}
func stop2() {
ButtonAudioPlayer4.currentTime = 0
ButtonAudioPlayer4.stop()
}
func stop3() {
ButtonAudioPlayer4.currentTime = 0
ButtonAudioPlayer4.stop()
}
lastDirection = 0 //Idle accel
threshold = 1.0 //Minimum positive(right) accel
nthreshold = -1.0 //Minimum negative(left) accel
if motionManager.accelerometerAvailable {
let queue = NSOperationQueue()
motionManager.startAccelerometerUpdatesToQueue(queue, withHandler: {
data, error in
guard let data = data else{
return
}
// Get the acceleration
let xAccel = data.acceleration.x //X accel
let yAccel = data.acceleration.y //Y accel
let zAccel = data.acceleration.z //Z accel
let xPositive = xAccel > 0 //Positive(right) x accel
let xNegative = xAccel < 0 //Negative(left) x accel
let yPositive = yAccel > 0 //Positive(up) y accel
let yNegative = yAccel < 0 //Negative(down) y accel
let zPositive = zAccel > 0 //Positive(front) z accel
let zNegative = zAccel < 0 //Negative(back) z accel
// Run if the acceleration is higher than theshold
if abs(xAccel) > self.threshold {
//If moved right
dispatch_async(dispatch_get_main_queue()) {
if self.lastDirection != 1 && xPositive {
self.lastDirection = 1
print("Up")
self.play()
} else if self.lastDirection != -1 && !xPositive {
self.lastDirection = -1
print("Down")
self.play()
}
}
}
// Run if the acceleration is higher than ntheshold
if abs(xAccel) < self.nthreshold {
//If moved left
dispatch_async(dispatch_get_main_queue()) {
if self.lastDirection != 1 && xNegative {
self.lastDirection = 1
print("Up")
self.play2()
} else if self.lastDirection != -1 && !xNegative {
self.lastDirection = -1
print("Down")
self.play2()
}
}
}
// Run if the acceleration is higher than theshold
if abs(yAccel) > self.threshold {
//If moved up
dispatch_async(dispatch_get_main_queue()) {
if self.lastDirection != 1 && yPositive {
self.lastDirection = 1
print("Up")
self.play()
} else if self.lastDirection != -1 && !yPositive {
self.lastDirection = -1
print("Down")
self.play()
}
}
}
// Run if the acceleration is higher than ntheshold
if abs(yAccel) < self.nthreshold {
//If moved left
dispatch_async(dispatch_get_main_queue()) {
if self.lastDirection != 1 && yNegative {
self.lastDirection = 1
print("Up")
self.play2()
} else if self.lastDirection != -1 && !yNegative {
self.lastDirection = -1
print("Down")
self.play2()
}
}
}
// Run if the acceleration is higher than theshold
if abs(zAccel) > self.threshold {
//If moved front
dispatch_async(dispatch_get_main_queue()) {
if self.lastDirection != 1 && zPositive {
self.lastDirection = 1
print("Up")
self.play()
} else if self.lastDirection != -1 && !zPositive {
self.lastDirection = -1
print("Down")
self.play()
}
}
}
// Run if the acceleration is higher than theshold
if abs(zAccel) < self.nthreshold {
//If moved back
dispatch_async(dispatch_get_main_queue()) {
if self.lastDirection != 1 && zNegative {
self.lastDirection = 1
print("Up")
self.play2()
} else if self.lastDirection != -1 && !zNegative {
self.lastDirection = -1
print("Down")
self.play2()
}
}
}
})
}
Hello,
I have built an iOS app with Xcode 7.2, Swift 2.0, for i0S 9.2
I would like to add a feature where when the user waves their device in the air, a sound will be played. My problem is that, although the sound plays, it suddenly stops, and is replayed when moved again in the process of playing the sound. I would like the sound to just be played once, without any overlapping or stopping. I have included all the code related to CoreMotion, and excluded all others.
Thank you.

If I do understand your purpose, you probably would like to have a boolean barrier to prevent duplicated playing.
Ex:
var isPlaying1 = false
func play() {
if self.isPlaying1 == true {
return
}
ButtonAudioPlayer4.currentTime = 0
ButtonAudioPlayer4.play()
self.isPlaying1 = true
}
func stop() {
if self.isPlaying1 == false {
return
}
ButtonAudioPlayer4.currentTime = 0
ButtonAudioPlayer4.stop()
self.isPlaying1 = false
}

Related

How to detect apple watch position using accelerometer and Gravity in swift?

I have creating an application for apple watch. The Logic is, when the user rise their hand and tap a button from app. At that time I will fetch the accelerometer values. And whenever user rise their hand and meet the captured position, I have to send message to iPhone.
For me am getting the values correctly But, It will always give the values based on accelerometer. Which means user doesn't rise the hand but accelerometer values matched. So values will send to mobile.
func startUpadateAccelerometer() {
self.motionManager.accelerometerUpdateInterval = 1.0 / 10.0
self.motionManager.startAccelerometerUpdates(to: OperationQueue()) { (accelerometerData, error) -> Void in
guard accelerometerData != nil else
{
print("There was an error: \(String(describing: error))")
return
}
DispatchQueue.main.async {
if(self.can_reset){
let differenceX : Bool = self.validateButtom(currentValue: accelerometerData!.acceleration.x, inititalValue: self.gravityReference.x)
let differenceY : Bool = self.validateButtom(currentValue: accelerometerData!.acceleration.y, inititalValue: self.gravityReference.y)
if(differenceX && differenceY && self.gravityOffsetDifference(currentValue: accelerometerData!.acceleration.x, referenceValue: self.gravityReference.x ) && self.gravityOffsetDifference(currentValue: accelerometerData!.acceleration.y, referenceValue: self.gravityReference.y)){
WKInterfaceDevice().play(.success)
// self.addLog(_logStr: EventsTypes.Achievements1.rawValue)
self.logString += String(format: "X: %0.3f Y: %0.3f Z: %0.3f \n", accelerometerData!.acceleration.x,accelerometerData!.acceleration.y,accelerometerData!.acceleration.z)
self.m_XYZValueLbl.setText(self.logString)
self.is_RechedZeroPos = true
self.session?.sendMessage(["msg" : "\(self.logString)"], replyHandler: nil) { (error) in
NSLog("%#", "Error sending message: \(error)")
}
} else {
if(self.checkAchievements2_3(deviceMotionData: accelerometerData!.acceleration) == true) {
if self.is_RechedZeroPos == true {
self.addLog(_logStr: EventsTypes.Achievements2.rawValue)
self.is_RechedZeroPos = false
} else {
self.addLog(_logStr: EventsTypes.Achievements3.rawValue)
}
}
}
} else {
self.gravityReference = accelerometerData!.acceleration
//self.logString = String(format: "Reference Acceleration %0.3f %0.3f %0.3f \n", self.gravityReference.x,self.gravityReference.y,self.gravityReference.z)
self.can_reset = true
}
}
}
}
func validateButtom(currentValue : Double , inititalValue : Double) -> Bool {
if( currentValue == 0 && inititalValue == 0) {
return true
} else if( currentValue < 0 && inititalValue < 0) {
return true
} else if( currentValue > 0 && inititalValue > 0) {
return true
} else {
return false
}
}
func gravityOffsetDifference(currentValue : Double , referenceValue: Double) -> Bool {
var difference : Double!
if (fabs(currentValue) <= fabs(referenceValue)) {
difference = fabs(referenceValue) - fabs(currentValue)
} else {
difference = fabs(currentValue) - fabs(referenceValue)
}
if (difference <= gravityOffset ) {
return true
} else {
return false
}
}
Please guide me to get the values only when the user captured the position.
Accelerometers measure changes in velocity along the x, y, and z axes
Fetching accelerometer data
let motion = CMMotionManager()
func startAccelerometers() {
// Make sure the accelerometer hardware is available.
if self.motion.isAccelerometerAvailable {
self.motion.accelerometerUpdateInterval = 1.0 / 60.0 // 60 Hz
self.motion.startAccelerometerUpdates()
// Configure a timer to fetch the data.
self.timer = Timer(fire: Date(), interval: (1.0/60.0),
repeats: true, block: { (timer) in
// Get the accelerometer data.
if let data = self.motion.accelerometerData {
let x = data.acceleration.x
let y = data.acceleration.y
let z = data.acceleration.z
// Use the accelerometer data in your app.
}
})
// Add the timer to the current run loop.
RunLoop.current.add(self.timer!, forMode: .defaultRunLoopMode)
}
}
Important
If your app relies on the presence of accelerometer hardware, configure the UIRequiredDeviceCapabilities key of its Info.plist file with the accelerometer value. For more information about the meaning of this key, see Information Property List Key Reference.

How do I run a function inside an action in Sprite Kit?

I have the following code for a game I am making. In one of the parts, I have
let spawn = SKAction.run {
self.createCars()
}
but for some reason I keep getting an error saying "Value of type '(NSObject -> () -> GameScene' has no member 'createCars' even though createCars is a function defined lower in my code. Why is this happening and what can I do to fix this?
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var lanecounter:CGFloat = 0
var score:Int = 0
var player:SKSpriteNode?
let scoreLabel = SKLabelNode()
let highScoreLabel = SKLabelNode()
var direction:Int?
let noCategory:UInt32 = 0
let carCategory:UInt32 = 0b1
let playerCategory:UInt32 = 0b1 << 1
let pointCategory:UInt32 = 0b1 << 2
var died:Bool?
var restartBTN = SKSpriteNode()
let moveLeft:SKAction = SKAction.moveBy(x: -100, y: 0, duration: 0.065)
let moveRight:SKAction = SKAction.moveBy(x: 100, y: 0, duration: 0.065)
let moveDown:SKAction = SKAction.moveTo(y: -800, duration: 1.1)
let spawn = SKAction.run {
self.createCars()
}
let delay = SKAction.wait(forDuration: 4)
func createCars(){
let middlePoints = SKSpriteNode()
var openLane:CGFloat = 0
middlePoints.size = CGSize(width: 100, height: 10)
middlePoints.physicsBody = SKPhysicsBody(rectangleOf: middlePoints.size)
//middlePoints.color = SKColor.purple
middlePoints.physicsBody?.categoryBitMask = pointCategory
middlePoints.physicsBody?.collisionBitMask = playerCategory
middlePoints.physicsBody?.contactTestBitMask = playerCategory
var randInt:Int = 0
randInt = Int(arc4random_uniform(3))
if lanecounter == -2 {
if randInt == 0 || randInt == 1 {
openLane = -1
}
if randInt == 2 {
openLane = -2
}
}
if lanecounter == 2 {
if randInt == 0 || randInt == 1 {
openLane = 1
}
if randInt == 2 {
openLane = 2
}
}
if lanecounter == -1 || lanecounter == 0 || lanecounter == 1 {
if randInt == 0 {
openLane = lanecounter - 1
}
if randInt == 1 {
openLane = lanecounter
}
if randInt == 2 {
openLane = lanecounter + 1
}
}
//print("lanecounter is", lanecounter)
//print("open lane is", openLane)
if openLane != -2 {
spawnCar(xCord: -2)
}
if openLane != -1 {
spawnCar(xCord: -1)
}
if openLane != 0 {
spawnCar(xCord: 0)
}
if openLane != 1 {
spawnCar(xCord: 1)
}
if openLane != 2 {
spawnCar(xCord: 2)
}
middlePoints.position = CGPoint(x: (openLane*100), y: self.frame.height)
self.addChild(middlePoints)
middlePoints.run(moveDown)
}
}
Minimal, Complete, and Verifiable
First of all, we need a Minimal, Complete, and Verifiable example like this:
class GameScene: SKScene {
let spawn = SKAction.run {
self.createCars()
}
func createCars() { }
}
error: value of type '(NSObject) -> () -> GameScene' has no member 'createCars'
Problem
The problem happens because you can't populate a property on the same line it is defined using self (infact self doesn't exist yet).
Solution
However you can use a lazy property wich will be evaluated only when called (and at that time self will be available).
class GameScene: SKScene {
lazy var spawn: SKAction = {
SKAction.run { self.createCars() }
}()
func createCars() { }
}

Make PanGesture becoming to slider

I made a PanGuesture as a slider, Here blow is the responds func:
func respondsToPenGesture(sender: UIPanGestureRecognizer) {
if (sender.state == UIGestureRecognizerState.Began) {
if noFilterEffectButton.selected == false {
startPanLocation = sender.locationInView(self.newEffectView)
persentageNumber.text = String(startNumber)
persentageNumber.hidden = false
}
} else if (sender.state == UIGestureRecognizerState.Changed) {
let stopLocation = sender.locationInView(self.newEffectView)
let abscissaChange = stopLocation.x - startPanLocation!.x
if newEffectView.hidden == false {
if abs(abscissaChange) > 0 {
if noFilterEffectButton.selected == false{
if let effectCurrent = self.currentEffect {
effectCurrent.adjustParams(CGFloat(abscissaChange/6))
}
}
}
if noFilterEffectButton.selected == false {
var printChangingNumber = startNumber + Int(abscissaChange)
if printChangingNumber > 100 {
printChangingNumber = 100
} else if printChangingNumber < 0 {
printChangingNumber = 0
}
persentageNumber.text = String(printChangingNumber)
}
}
} else if (sender.state == UIGestureRecognizerState.Ended) {
let stopLocation = sender.locationInView(self.newEffectView)
let abscissaChange = stopLocation.x - startPanLocation!.x
startNumber = startNumber + Int(abscissaChange)
if startNumber > 100 {
startNumber = 100
} else if startNumber < 0 {
startNumber = 0
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.5*Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
self.persentageNumber.text = String(self.startNumber)
self.persentageNumber.hidden = true
}
}
}
percentageNumber is a label show the changes on the screen. And the adjustParams() function:
func adjustParams(value: CGFloat) {
newValue = value + newValue
self.adjust = newValue
print("AdjustNumber")
print(adjust)
}
And I override this function for exactly one effect:
override func adjustParams(value: CGFloat) {
super.adjustParams(value)
lastAutValueChange = lastAutValueChange + value/200
lastIntensityChange = lastIntensityChange + value/200
var currentAutValue = lastAutValueChange
var currentIntValue = lastIntensityChange
currentIntValue = currentIntValue < -1.0 ? -1.0 : currentIntValue
//value = value > 1.0 ? 1.0 : value
//autValue = value < -1.0 ? -1.0 : autValue
if (currentIntValue >= 0) {
currentIntValue = 1.0
} else {
currentIntValue += 1
}
if (currentAutValue <= 0) {
currentAutValue = 0
} else {
if currentAutValue > 1 {
currentAutValue = 1
}
}
print(currentIntValue)
print(currentAutValue)
self.autoTuneModule.setIntensity(Float(currentIntValue))
self.audioUnit.finalMix = Double(currentAutValue)
}
The test answer is not matchable for the exactly changes and the percentNumber shows on the screen. How could I change my functions to make them matchable for each others?
(By the way, I want to do something like the slider in Prisma, if there is something like it, please let me know about it).

Maths formula of cleaner way for bounding box logic?

Below I have written some bounding box logic which is a bit verbose. Is there a way to make it more readable, so the check looks cleaner and more concise?
func validatePosition(position:SCNVector3, forNode node:SCNNode) -> SCNVector3 {
var newPosition = position
var maxVector = SCNVector3Zero
var minVector = SCNVector3Zero
let success = self.actionDelegate?.getBoundingBox(&minVector, max: &maxVector)
guard success == true else {
return newPosition
}
if newPosition.x < minVector.x && newPosition.x < 0 {
newPosition.x = minVector.x
}
if newPosition.y < minVector.y && newPosition.y < 0 {
newPosition.y = minVector.y
}
if newPosition.z < minVector.z && newPosition.z < 0 {
newPosition.z = minVector.z
}
if newPosition.x > maxVector.x && newPosition.x > 0 {
newPosition.x = maxVector.x
}
if newPosition.y > maxVector.y && newPosition.y > 0 {
newPosition.y = maxVector.y
}
if newPosition.z > maxVector.z && newPosition.z > 0 {
newPosition.z = maxVector.z
}
return newPosition
}
Try this:
extension ClosedInterval {
func clamp(value : Bound) -> Bound {
return self.start > value ? self.start
: self.end < value ? self.end
: value
}
}
extension SCNVector3 {
func clamp(min min:SCNVector3, max: SCNVector3) -> SCNVector3 {
let x = (min.x...max.x).clamp(self.x)
let y = (min.y...max.y).clamp(self.y)
let z = (min.z...max.z).clamp(self.z)
return SCNVector3(x, y, z)
}
}
func validatePosition(position:SCNVector3, forNode node:SCNNode) -> SCNVector3 {
var newPosition = position
var maxVector = SCNVector3Zero
var minVector = SCNVector3Zero
let success = self.actionDelegate?.getBoundingBox(&minVector, max: &maxVector)
guard success == true else {
return newPosition
}
newPosition = position.clamp(min: minVector, max: maxVector)
return newPosition
}

How to play a short audio file with CoreMotion and AVAudioPlayer in iOS?

I'm trying to play short sounds (1 to 4 seconds) when I move the iPhone on the X axis (I'm using CoreMotion and AVAudioPlayer). I want to play one sound for each movement direction change.
I wrote the code below, but when I move the iPhone, the sound is played many times without the movement direction change. The print calls below show many Down and Up such as Down Down Down Up Down Down Up Up.... If I comment both play callbacks, the print shows the alternation that I expect: Down Up Down Up Down Up....
Why the AVAudioPlayer.play is called more than one time when the movement direction changes?
override func viewDidLoad() {
super.viewDidLoad()
// Audio
audioURL = NSBundle.mainBundle().URLForResource("shortSound", withExtension: "wav")!
do {
try audioPlayer = AVAudioPlayer(contentsOfURL: audioURL)
audioPlayer.prepareToPlay()
} catch {
print("audioPlayer failure")
}
// Sensor
lastDirection = 0
threshold = 2.1
motionManager = CMMotionManager()
if motionManager.accelerometerAvailable {
let queue = NSOperationQueue()
motionManager.startAccelerometerUpdatesToQueue(queue, withHandler: {
data, error in
guard let data = data else{
return
}
// Get the acceleration
let xAccel = data.acceleration.x
let xPositive = xAccel > 0
// Run if the acceleration is higher than theshold
if abs(xAccel) > self.threshold {
// Run only if the direction is changed
if self.lastDirection != 1 && xPositive {
print("Up")
self.play()
self.lastDirection = 1
} else if self.lastDirection != -1 && !xPositive {
print("Down")
self.play()
self.lastDirection = -1
}
}
})
}
}
func play() {
audioPlayer.currentTime = 0
audioPlayer.play()
}
You probably have a threading problem. You are running the updates on a background queue (queue, an arbitrary NSOperationQueue, which by the way you are also failing to retain), but then you are talking to self.lastDirection and calling self.play() on that same background queue without regard to the thread-safety of those activities.
I would suggest at the very least rewriting this section:
if self.lastDirection != 1 && xPositive {
print("Up")
self.play()
self.lastDirection = 1
} else if self.lastDirection != -1 && !xPositive {
print("Down")
self.play()
self.lastDirection = -1
}
...more like this:
dispatch_async(dispatch_get_main_queue()) {
if self.lastDirection != 1 && xPositive {
self.lastDirection = 1
print("Up")
self.play()
} else if self.lastDirection != -1 && !xPositive {
self.lastDirection = -1
print("Down")
self.play()
}
}
Note that I've made two changes: I've stepped out to the main thread for the entire check-print-play-toggle dance, and I've reversed the order of events so that it goes check-toggle-print-play.
Also I would suggest two other changes: retain the operation queue (i.e. make it a property instead of a local), and reduce the frequency of motion manager updates (by setting a lower accelerometerUpdateInterval).

Resources