Apple System Sounds

Swift Language

AudioToolbox.AudioServices

Introduction

There are many ways to play audio on Apple devices.

Sometimes it’s confusing due to so many choices.

This is the first post in a series that will cover each way to play audio.

What’s probably the oldest method is to use the so-called “System Sounds” API which is part of AudioToolbox.AudioServices. Just import AVFoundation and you’ll be set.

That name is a bit deceiving because while it does mean the built-in bleeps and blorps known to Apple users, the developer can also register their own sound files to play. One restriction is that the sounds files need to be short – under 30 seconds in duration is recommended.

It’s good to know about this API because it was in wide use for game-like apps. If you want to play simple sounds (or even complex sounds), you probably would use AVFoundation instead.

Creating a system sound

What we need to play a system sound is a variable of type SystemSoundID (actually an Int32) that acts as an identifier for your sound. The C function AudioServicesCreateSystemSoundID will create the id from a file url. The actual file can be in your bundle, on the file system, or in iCloud. In this example I read from the bundle. If you are going to use your sounds as sound effects in a game (pew pew), this is probably what you will do.

Oh, look. Our old frenemy OSStatus from Core Audio and Core MIDI (andother Apple C APIs). Nothing special in my handling here. Grok the FourCC if you want. In the Github example I included an error function you can copy. Just be sure to check it’s not an error. And we also see the CFURL type from core foundation which is bridged to Swift’s URL. You just have to say so. (as shown)

Playing a system sound

Groovy, we have an id. Now what?

tl;dr Use function AudioServicesPlaySystemSound(Your SystemSoundID)

Here is one way. You want the “client” (e.g. your SwiftUI views/viewmodels) to have a simple way to play sounds without being bothered with the details. So a function named *playWhatever()* is a good way to go.

I initialize an instance variable to zero (it’s an Int32, remember?). Then lazily create the id using the previous function. Finally there’s the call to play it.

Not too horrible for a C API from Swift.
(Check out MIDI meta text messages in Core MIDI if you want to see horrible)

So, with all the above audio code in a struct or class separate from your UI code (cunningly named “audio” here) -maybe a view model, environment object, or instance variable – the UI have a very simply call to play the sound.

If you have a lot of sounds, perhaps creating an enum to organize them would be a good idea.

Finding the actual system sounds

The actual system sounds live on your device in directory /System/Library/Audio/UISounds. They are not available in the simulator! So it would be a good idea to check if you’re running on the simulator. The FileManager class can do all sorts of file things including listing the file in a directory. Imagine that.

You might want to try this:

The problem with that approach is it returns the files of only the specified directory. There are several subdirectories of system sounds.

So, I use the enumerate approach.

The system sounds are in “Core Audio Format”. They’re the only files I see in these directories, but out of paranoia I check their file extensions before adding them to the result collection. The example code has a few other FileManager frobs; sorting the list on specified file properties for example.

One more thing

There are other things that you can do with this API besides playing short sounds.

You can create “alerts” that do the appropriate thing on each device. For example, you can make the device vibrate after a Timer you coded is finished. An iPhone will, well, vibrate. My iPad responds to a vibrate alert just like my cat when I call him; he just stares at me and does nothing.

Note the word Alert in the function name.

If you want your iPhone to vibrate, you might want to look at the much more recent Haptic API.

The example project has several more examples.

Summary

You can use this API to play short sounds.

There are many audio things you cannot do though.

You cannot adjust the volume, or do looping, and you can play only one sound at a time.

If you want to do any of those things, try Core Audio, AVFoundation, or if you like sweating blood, Audio Units.

I will cover those alternatives in the next posts.

Resources

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.