Peter Silie
Peter Silie

Reputation: 875

Playing a sound on macOS

I like to play a sound in my app. I found a lot of examples and also could compile and run them on Swift 3. But they are always for iOS. When imlementing this code in my app, AVAudioSession keeps undefined. What do I have to do? Is there something different for OSX?

import AVFoundation
...
    var audioPlayer = AVAudioPlayer()
    func PlaySound( ) {
        let alertSound = URL(fileURLWithPath: Bundle.main.path(forResource: "Sound", ofType: "mp3")!)
        do {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
        } catch _ {
        }
        do {
            try AVAudioSession.sharedInstance().setActive(true)
        } catch _ {
        }
        do {
            audioPlayer = try AVAudioPlayer(contentsOf: alertSound)
        } catch _{
        }
        audioPlayer.prepareToPlay()
        audioPlayer.play()
    }

Upvotes: 10

Views: 7224

Answers (4)

Christian Krueger
Christian Krueger

Reputation: 139

Even though it is little old-fashioned, you can simply enter the filename in the Attributes Inspector :

enter image description here

All you need to do first is to integrate a sound file into your project: enter image description here

Upvotes: 0

Samuel-IH
Samuel-IH

Reputation: 821

A modified version of El Tomato's answer, here's the swift 4.2 class that I use quite often for sound:

Usage:

    /// EXAMPLE 1
    //calling this will create a "bop.m4a" player in memory, and, play it immediately
    MakeSound.shared.playSound("bop.m4a")
    //calling it a second time has a lot less overhead because the player is already in memory
    MakeSound.shared.playSound("bop.m4a")

    ///EXAMPLE 2
    //simply calls the above many times
    MakeSound.shared.playSound(["bop.m4a", "beep.m4a", "bong.m4a"])

    // EXAMPLE 3
    //registers a shorthand, for quick calling
    MakeSound.shared.registerSound(fileName: "bop", fileExtension: "m4a", shortHand: "b")
    //plays using the shorthand, mostly handy because you define the actual file once,
    //and can change the one line of code without having to change every reference
    MakeSound.shared.playSound("b")

and the class:

class MakeSound {
private var players = [String : AVAudioPlayer]()
static var shared = MakeSound()

func playSound(fileName: String, fileExtension: String) {
    if registerSound(fileName: fileName, fileExtension: fileExtension, shortHand: fileName+"."+fileExtension) {
        playSound(fileName+"."+fileExtension)
    }
}

//thought about naming this "playsoundS", but i like it with the same name, makes it easier to type
func playSound(_ shortHands : [String]){
    for sh in shortHands{
        playSound(sh)
    }
}

///playSound("shorthand") OR playSound("mySound.mp3")
func playSound(_ shortHand : String) {
    if let player = players[shortHand]  {
        player.prepareToPlay()
        player.play()
    } else {
        if shortHand.contains(".") {
            //going to assume that coder will not send "." as a filepath
            //also going to assume that coder will not send "xyz." as filepath
            var comp = shortHand.components(separatedBy: ".")
            let ext = comp.last!
            comp.removeLast()
            if registerSound(fileName: comp.joined(separator: "."), fileExtension: ext, shortHand: shortHand) {
                playSound(shortHand)
            }
        } else {
            print("Sound request sent to makesound, no such shorthand, not a valid filename either.")
        }
    }
}

///registers a sound with makeSound, and returns a bool based on the success
@discardableResult func registerSound(fileName : String, fileExtension: String, shortHand: String) -> Bool {
    guard let url = Bundle.main.url(forResource: fileName, withExtension: fileExtension) else {
        print("Unable to register sound: \(shortHand). Filepath not valid.")
        return false
    }
    do {
        let player = try AVAudioPlayer(contentsOf: url)
        players[shortHand] = player
    } catch let error {
        print("Audioplayer \"\(shortHand)\" unable to initialize:\n" + error.localizedDescription)
        return false
    }
    return true
}

}

Now I will admit that NSSound is by far the easiest and most straight forward, yet, some of us prefer AVFoundation because we have a bit more control. (And such control is ironically removed in my class)

Upvotes: 1

El Tomato
El Tomato

Reputation: 6707

I've used the following function in conjunction with Swift 3 and a Cocoa project.

import AVFoundation

func playSound(file:String, ext:String) -> Void {
    let url = Bundle.main.url(forResource: file, withExtension: ext)!
    do {
        let player = try AVAudioPlayer(contentsOf: url)
        player.prepareToPlay()
        player.play()
    } catch let error {
        print(error.localizedDescription)
    }
}

// usage //

playSound(file: "sound", ext: "caf") // where the file name is sound.caf.

Make sure that the target membership checkbox is on when you select an audio file in your Xcode project.

Upvotes: 8

ixany
ixany

Reputation: 6040

Swift 3 / 4 / 4.2 / 5 - Easiest solution:

NSSound(named: "customSound")?.play()

Upvotes: 19

Related Questions