Enhancing Audio Recording Mastery: Part I — Mono Mode

Bilal Bakhrom
5 min readNov 20, 2023

--

In this article, I’ll guide you through the fascinating process of recording audio using the AVFoundation framework in the Swift programming language.

Many of Apple’s cutting-edge devices come equipped with multiple microphones, and the AVAudioSession empowers you to handpick the specific device microphone for recording. By configuring the preferred polar pattern, you can seamlessly capture mono or stereo audio from these built-in wonders. Stay tuned for the upcoming article, where I’ll write about the stereo pattern.

Now, let’s explore the three polar patterns that enable you to tailor your audio recording to mono. The term “data source” here refers to an audio input source:

  • Cardioid: This pattern sensitive to sound from the direction of the data source and is nearly insensitive to sound from the opposite direction.
  • Subcardioid: This pattern most sensitive to sound from the direction of the data source and is less sensitive to sound from the opposite direction.
  • Omnidirectional: A data source that’s equally sensitive to sound from any direction.

It’s crucial to note that each microphone may possess a specific orientation or directionality. To describe these orientations, we use AVAudioSession.Orientation constants, outlining the directions a particular microphone or audio data source can point concerning the device’s natural orientation.

Now, let’s immerse ourselves in the coding realm. Prior to setting up the audio recorder or determining the preferred input, we must set the audio session’s category and mode, and subsequently, activate the session:

/// Configures the audio session for recording and playback.
///
/// - Throws: An `AudioSessionError` if the configuration fails.
private func configureAudioSession() throws {
do {
// Get the instance of audio session.
let audioSession = AVAudioSession.sharedInstance()

// Set the audio session category to record, allowing default to speaker and Bluetooth.
try audioSession.setCategory(.record, options: [.defaultToSpeaker, .allowBluetooth])

// Activate the audio session.
try audioSession.setActive(true)
} catch {
// If an error occurs during configuration, throw an appropriate error.
throw AudioSessionError.configurationFailed
}
}

If you’re eager to both record and play back your recordings, simply set the category to .playAndRecord. To ensure a clean and comprehensible code, I’ve built custom errors using the AudioSessionError and RecorderError enums.

Now, let’s move on to setting the built-in microphone:

/// Sets the built-in microphone as the preferred input.
///
/// - Note: You must set a preferred input port only after setting
/// the audio session’s category and mode and activating the session.
///
/// - Throws: An `AudioSessionError` if the device does not have
/// a built-in microphone or if setting the built-in microphone as
/// the preferred input fails.
private func enableBuiltInMicrophone() throws {
// Get the instance of audio session.
let audioSession = AVAudioSession.sharedInstance()

// Get the audio inputs.
let availableInputs = audioSession.availableInputs

// Find the available input that corresponds to the built-in microphone.
guard let builtInMicInput = availableInputs?.first(where: { $0.portType == .builtInMic }) else {
// If no built-in microphone is found, throw an error.
throw AudioSessionError.missingBuiltInMicrophone
}

do {
// Set the built-in microphone as the preferred input.
try audioSession.setPreferredInput(builtInMicInput)
} catch {
// If an error occurs while setting the preferred input, throw an appropriate error.
throw AudioSessionError.unableToSetBuiltInMicrophone
}
}

Now, we’re prepared to initialize the audio recorder. We achieve this by usingAVAudioRecorder:

/// The AVAudioRecorder instance for handling audio recording.
private var recorder: AVAudioRecorder!

/// The file name for the audio recording.
private let recordingFileName = "recording.aac"

/// Sets up the audio recorder with the necessary configurations.
private func setupAudioRecorder() throws {
let tempDir = FileManager.default.temporaryDirectory
let fileURL = tempDir.appendingPathComponent(recordingFileName)

do {
let audioSettings: [String: Any] = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVLinearPCMIsNonInterleaved: false,
AVSampleRateKey: 44_100.0,
AVNumberOfChannelsKey: 1,
AVLinearPCMBitDepthKey: 16,
AVEncoderAudioQualityKey: AVAudioQuality.max.rawValue
]
recorder = try AVAudioRecorder(url: fileURL, settings: audioSettings)
} catch {
throw RecorderError.unableToCreateAudioRecorder
}

recorder.delegate = self
recorder.prepareToRecord()
}

Let’s break down the purpose of each element in the audioSettings dictionary:

AVFormatIDKey — This key determines the audio file format.

  • In simpler terms: It specifies the type or format of the audio file, for example, MPEG-4 AAC.

AVLinearPCMIsNonInterleaved — This key decides whether audio data is stored in an interleaved or non-interleaved manner.

  • In simpler terms: It defines how the pieces of audio information are organized, either in a specific order for efficiency (interleaved) or not.

AVSampleRateKey— This key sets the number of audio samples captured per second.

  • In simpler terms: It defines the rhythm of the audio by determining how many “snapshots” of sound are taken every second.

AVNumberOfChannelsKey— This key determines the number of audio channels used for recording.

  • In simpler terms: It specifies whether the recording will capture sound in stereo (two channels) or mono (one channel).

AVLinearPCMBitDepthKey— This key sets the number of bits of information in each audio sample.

  • In simpler terms: It defines the amount of detail captured in each piece of sound, with a higher bit depth representing more detailed information.

AVEncoderAudioQualityKey— This key sets the desired audio quality for the recording.

  • In simpler terms: It determines how good the recorded audio should sound, with higher values indicating better quality but potentially larger file sizes.

Now, we’re set to dive into the audio recording process. To streamline things, I’ll create a new class and guide you through the creation of methods for a simplified experience.

public class AudioRecorderManager: NSObject, AVAudioRecorderDelegate {
// MARK: - Properties
...
private var recorder: AVAudioRecorder!
...

// MARK: - Initialization

override init() {
super.init()

do {
try configureAudioSession()
try enableBuiltInMicrophone()
try setupAudioRecorder()
} catch {
// If any errors occur during initialization,
// terminate the app with a fatalError.
fatalError("Error: \(error)")
}
}

// MARK: - Recorder Control

public func record() {
guard state != .recording else { return }

recorder.record()
state = .recording
}

public func stop() {
recorder.stop()
state = .stopped
}

// MARK: - AVAudioRecorderDelegate

public func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
// Move the recorded audio file to the documents directory.
let destURL = FileManager.default.urlInDocumentsDirectory(named: recordingFileName)
try? FileManager.default.removeItem(at: destURL)
try? FileManager.default.moveItem(at: recorder.url, to: destURL)

// Prepare for a new record.
recorder.prepareToRecord()
state = .stopped
}

// MARK: - Audio Session and Recorder Configuration

private func configureAudioSession() throws {...}

private func enableBuiltInMicrophone() throws {...}

private func setupAudioRecorder() throws {...}
}

Absolutely, managing the recording is a breeze with the record(), pause(), and stop() methods. That concludes the essential aspects for now in this article.

In the next article, prepare to explore the intriguing universe of stereo audio recording. To achieve stereo, we’ll simultaneously capture audio from all the device microphones, taking your understanding to the next level!

Check the second part:
Enhancing Audio Recording Mastery: Part II — Stereo Mode

--

--

Bilal Bakhrom

Seasoned iOS Developer with a knack for creating awesome apps. Ready to bring my coding superpowers to your team's success party! 🚀