Luigi Jump Sound iPhone App

1 min read

Jack may be the only kid in the world who wanted to be… Luigi… not Mario… for Halloween. But hey, you be you man.

Watching him bounce around, I thought it’d be fun to make an app that played the classic mario “jump” sound effect when he jumped.

It was only 100 lines of code and some testing to get the sensitivity right. Basically I look for a large acceleration that is in the opposite direction of gravity.

//
//  AppDelegate.swift
//  Luigi
//
//  Created by Josh Wright on 10/25/20. MIT License.
//

import UIKit
import CoreMotion
import AVFoundation
import AppCenter
import AppCenterAnalytics
import AppCenterCrashes
import AppCenterDistribute

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    let motion = CMMotionManager();
    var timer:Timer?;
    var players: [AVAudioPlayer] = []
    var waitUntil:TimeInterval = 0
    var playerIndex:Int = -1
    var deathPlayer:AVAudioPlayer!;
    var stillInARow = 0;
    var hasJumpedSinceDeath = false

    func playSound() {
        DispatchQueue.main.async {
            let now = Date().timeIntervalSinceReferenceDate
            if (now < self.waitUntil) {
                return;
            }
            self.waitUntil = now + 0.5;
            self.playerIndex += 1
            let player = self.players[self.playerIndex % self.players.count]
            player.play()
            self.hasJumpedSinceDeath = true
        }
    }
    
    func loadPlayer(_ name:String) throws -> AVAudioPlayer {
        let url = Bundle.main.url(forResource: name, withExtension: "m4a")!
        let player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileType.mp3.rawValue)
        player.prepareToPlay()
        return player;
    }

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        MSDistribute.disableAutomaticCheckForUpdate()
        MSAppCenter.start("7274c718-2030-4561-ae1d-6f0d3c255665", withServices:[
          MSAnalytics.self,
          MSCrashes.self,
          MSDistribute.self
        ])
        
        do {
            try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
            try AVAudioSession.sharedInstance().setActive(true)
            for _ in (0..<4) {
                self.players.append(try loadPlayer("jump"))
            }
            self.deathPlayer = try loadPlayer("death")
        }catch let error {
            NSLog("ERROR: %@", [error .localizedDescription]);
        }
        
        self.motion.accelerometerUpdateInterval = 1.0 / 10.0  // 60 Hz
        self.motion.deviceMotionUpdateInterval = 1.0 / 10.0  // 60 Hz
        self.motion.startAccelerometerUpdates()
        self.motion.startDeviceMotionUpdates(to: OperationQueue()) { (m, e) in
            if let deviceMotion = self.motion.deviceMotion {
                let gravityVector = Vector3(x: CGFloat(deviceMotion.gravity.x),
                                            y: CGFloat(deviceMotion.gravity.y),
                                            z: CGFloat(deviceMotion.gravity.z))

                let userAccelerationVector = Vector3(x: CGFloat(deviceMotion.userAcceleration.x),
                                                     y: CGFloat(deviceMotion.userAcceleration.y),
                                                     z: CGFloat(deviceMotion.userAcceleration.z))
                
                let vals = [deviceMotion.userAcceleration.x, deviceMotion.userAcceleration.y, deviceMotion.userAcceleration.z, deviceMotion.rotationRate.x, deviceMotion.rotationRate.y, deviceMotion.rotationRate.z]
                let accelerationAmount = vals.reduce(0.0) {
                    return $0 + abs($1)/Double(vals.count)
                }
                
                // 3x under < .0022 -> chair
                // 3x under < .005  -> chest
                // 3x under < .04  -> moving
                if (accelerationAmount < 0.005 && self.hasJumpedSinceDeath) {
                    self.stillInARow += 1
                    if self.stillInARow >= 3 {
                        self.stillInARow = 0
                        self.deathPlayer.play()
                        self.hasJumpedSinceDeath = false
                    }
                }
                
                // NSLog("accelerationAmount: %f", accelerationAmount)

                // Acceleration to/from earth
                let zVector = gravityVector * userAccelerationVector
                let zAcceleration:CGFloat = zVector.length()
                if (zAcceleration > 1) {
                    self.playSound()
                }
            }
        }
        
       return true
    }
}

I installed it on an old phone and attached it to a belt under his suit. Here it is in action:

Then I added the “mario dies” sound effect when he lays down on the ground:

He loved it and his friends loved trying it too! If you’re interested, here’s the full project & source code:

Leave a Reply

Your email address will not be published. Required fields are marked *