ios - 如何在 iOS 中使用 CoreMotion 和 AVAudioPlayer 播放短音频文件?

标签 ios swift avfoundation avaudioplayer core-motion

当我在 X 轴上移动 iPhone 时(我正在使用 CoreMotionAVAudioPlayer),我试图播放短声音(1 到 4 秒)。我想为每个运动方向变化播放一种声音。

我写了下面的代码,但是当我移动 iPhone 时,声音播放了很多次,但移动方向没有改变。下面的 print 调用显示了许多 DownUp,例如 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(),而不考虑这些事件的线程安全性。

我建议至少重写此部分:

            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-打印播放。

此外,我还建议进行另外两项更改:保留操作队列(即使其成为属性而不是本地属性),并降低运动管理器更新的频率(通过设置较低的 accelerometerUpdateInterval)。

关于ios - 如何在 iOS 中使用 CoreMotion 和 AVAudioPlayer 播放短音频文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34597669/

相关文章:

ios - 如何区分两个本地通知

swift - Scenekit 物理 - 防止碰撞时旋转

ios - 错误域=AVFoundationErrorDomain 代码=-11821 "Cannot Decode"

ios - 代码 7 : AWS S3 Header file not found using Cocoa Pods

iphone - UITextField(有时)在输入时会使应用程序崩溃

ios - 在 macOS High Sierra 上获取默认 Realm 时崩溃

ios - 当音频设备添加到 AVCaptureSession 时,UIImpactFeedbackGenerator 不工作

ios - 如何在不使用 AudioSessionSetProperty 的情况下路由到 kAudioSessionProperty_OverrideCategoryEnableBluetoothInput

objective-c - 恢复上一个 View 及其 subview

ios - 仅收藏 View 滚动项目