如何在 iOS 中使用 CoreMotion 和 AVAudioPlayer 播放短音频文件?
How to play a short audio file with CoreMotion and AVAudioPlayer in iOS?
当我在 X 轴上移动 iPhone 时(我正在使用 CoreMotion
和 AVAudioPlayer
),我试图播放较短的声音(1 到 4 秒)。我想为每个运动方向变化播放一个声音。
我写了下面的代码,但是当我移动iPhone时,声音播放了很多次,移动方向没有改变。下面的 print
调用显示了许多 Down
和 Up
,例如 Down Down Down Up Down Down Up Up...
。如果我评论两个 play
回调,print
显示我期望的交替:Down Up Down Up Down Up...
.
为什么AVAudioPlayer.play
在移动方向改变时被多次调用?
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()
}
您可能遇到线程问题。你是 运行 后台队列的更新(queue
,一个任意的 NSOperationQueue,顺便说一句,你也未能保留),但是你正在与 self.lastDirection
交谈并调用 self.play()
在同一个后台队列上,而不考虑这些活动的 thread-safety。
我建议至少重写此部分:
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
}
...更像这样:
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()
}
}
请注意,我做了两处更改:我已经跳转到整个 check-print-play-toggle 舞蹈的主线程,并且我颠倒了事件的顺序,以便 check-toggle-print-play.
我还建议另外两个更改:保留操作队列(即使其成为 属性 而不是本地),并减少运动管理器更新的频率(通过设置较低的 accelerometerUpdateInterval
).
当我在 X 轴上移动 iPhone 时(我正在使用 CoreMotion
和 AVAudioPlayer
),我试图播放较短的声音(1 到 4 秒)。我想为每个运动方向变化播放一个声音。
我写了下面的代码,但是当我移动iPhone时,声音播放了很多次,移动方向没有改变。下面的 print
调用显示了许多 Down
和 Up
,例如 Down Down Down Up Down Down Up Up...
。如果我评论两个 play
回调,print
显示我期望的交替:Down Up Down Up Down Up...
.
为什么AVAudioPlayer.play
在移动方向改变时被多次调用?
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()
}
您可能遇到线程问题。你是 运行 后台队列的更新(queue
,一个任意的 NSOperationQueue,顺便说一句,你也未能保留),但是你正在与 self.lastDirection
交谈并调用 self.play()
在同一个后台队列上,而不考虑这些活动的 thread-safety。
我建议至少重写此部分:
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
}
...更像这样:
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()
}
}
请注意,我做了两处更改:我已经跳转到整个 check-print-play-toggle 舞蹈的主线程,并且我颠倒了事件的顺序,以便 check-toggle-print-play.
我还建议另外两个更改:保留操作队列(即使其成为 属性 而不是本地),并减少运动管理器更新的频率(通过设置较低的 accelerometerUpdateInterval
).