I've created a voice recorder that plays back through the earpiece; however, I want to play back by speaker. I've added the following (Swift 4):
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.overrideOutputAudioPort(AVAudioSession.PortOverride.speaker)
} catch let error as NSError {
print("Audio Session error: \(error.localizedDescription)")
}
When I initialize the app, I am able to playback previous recordings by speaker; however, as soon as I record a new voice memo, the playback goes back to earpiece. I've looked up solutions but they all refer to setCategory which throws an error in Swift 4.
Does anyone know how to default recorded playbacks to speaker?
Here is my view controller in full:
import UIKit
import AVFoundation
class ViewController2: UIViewController, AVAudioRecorderDelegate, UITableViewDelegate, UITableViewDataSource {
// Setting up Table View
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberOfRecords
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = String(indexPath.row + 1)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let path = getDirectory().appendingPathComponent("\(indexPath.row + 1).m4a")
do {
audioPlayer = try AVAudioPlayer(contentsOf: path)
audioPlayer.play()
}
catch {
}
}
var audioPlayer : AVAudioPlayer!
var recordingSession : AVAudioSession!
var audioRecorder : AVAudioRecorder!
var numberOfRecords : Int = 0
@IBOutlet weak var buttonLabel: UIButton!
@IBOutlet weak var myTableView: UITableView!
@IBAction func record(_ sender: Any) {
// Check if we have an active recorder
if audioRecorder == nil {
numberOfRecords += 1
let filename = getDirectory().appendingPathComponent("\(numberOfRecords).m4a")
let settings = [AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 12000,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue]
// Start audio recording
do {
audioRecorder = try AVAudioRecorder(url: filename, settings: settings)
audioRecorder.delegate = self
audioRecorder.record()
//buttonLabel.setTitle("Stop", for: .normal)
}
catch {
displayAlert(title: "Oops!", message: "Recording failed")
}
}
else {
// Stop audio recording
audioRecorder.stop()
audioRecorder = nil
UserDefaults.standard.set(numberOfRecords, forKey: "myNumber")
myTableView.reloadData()
//buttonLabel.setTitle("Start", for: .normal)
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Setting up Recording session
recordingSession = AVAudioSession.sharedInstance()
if let number : Int = UserDefaults.standard.object(forKey: "myNumber") as? Int {
numberOfRecords = number
}
AVAudioSession.sharedInstance().requestRecordPermission { (hasPermission) in
if hasPermission {
print ("Accepted")
}
}
// Play speaker instead of earpiece
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.overrideOutputAudioPort(AVAudioSession.PortOverride.speaker)
} catch let error as NSError {
print("Audio Session error: \(error.localizedDescription)")
}
// Gesture recognizers
let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(swipeAction(swipe:)))
leftSwipe.direction = UISwipeGestureRecognizer.Direction.left
self.view.addGestureRecognizer(leftSwipe)
let rightSwipe = UISwipeGestureRecognizer(target: self, action: #selector(swipeAction(swipe:)))
rightSwipe.direction = UISwipeGestureRecognizer.Direction.right
self.view.addGestureRecognizer(rightSwipe)
let upSwipe = UISwipeGestureRecognizer(target: self, action: #selector(swipeAction(swipe:)))
upSwipe.direction = UISwipeGestureRecognizer.Direction.up
self.view.addGestureRecognizer(upSwipe)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Function that gets path to directory
func getDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentDirectory = paths[0]
return documentDirectory
}
// Function that displays an alert
func displayAlert(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "dismiss", style: .default, handler: nil))
present(alert, animated: true, completion: nil)
}
The do catch statement needs to be within the if and not else and not in the viewDidLoad () section.
// Setting up Table View
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberOfRecords
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = String(indexPath.row + 1)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let path = getDirectory().appendingPathComponent("\(indexPath.row + 1).m4a")
do {
audioPlayer = try AVAudioPlayer(contentsOf: path)
audioPlayer.play()
}
catch {
}
}
var audioPlayer : AVAudioPlayer!
var recordingSession : AVAudioSession!
var audioRecorder : AVAudioRecorder!
var numberOfRecords : Int = 0
@IBOutlet weak var buttonLabel: UIButton!
@IBOutlet weak var myTableView: UITableView!
@IBAction func record(_ sender: Any) {
// Check if we have an active recorder
if audioRecorder == nil {
numberOfRecords += 1
let filename = getDirectory().appendingPathComponent("\(numberOfRecords).m4a")
let settings = [AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 12000,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue]
// Start audio recording
do {
audioRecorder = try AVAudioRecorder(url: filename, settings: settings)
audioRecorder.delegate = self
audioRecorder.record()
//buttonLabel.setTitle("Stop", for: .normal)
}
catch {
displayAlert(title: "Oops!", message: "Recording failed")
}
// Play speaker instead of earpiece
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.overrideOutputAudioPort(AVAudioSessionPortOverride.speaker)
} catch let error as NSError {
print("Audio Session error: \(error.localizedDescription)")
}
}
else {
// Stop audio recording
audioRecorder.stop()
audioRecorder = nil
UserDefaults.standard.set(numberOfRecords, forKey: "myNumber")
myTableView.reloadData()
//buttonLabel.setTitle("Start", for: .normal)
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Setting up Recording session
recordingSession = AVAudioSession.sharedInstance()
if let number : Int = UserDefaults.standard.object(forKey: "myNumber") as? Int {
numberOfRecords = number
}
AVAudioSession.sharedInstance().requestRecordPermission { (hasPermission) in
if hasPermission {
print ("Accepted")
}
}
// Gesture recognizers
let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(swipeAction(swipe:)))
leftSwipe.direction = UISwipeGestureRecognizerDirection.left
self.view.addGestureRecognizer(leftSwipe)
let rightSwipe = UISwipeGestureRecognizer(target: self, action: #selector(swipeAction(swipe:)))
rightSwipe.direction = UISwipeGestureRecognizerDirection.right
self.view.addGestureRecognizer(rightSwipe)
let upSwipe = UISwipeGestureRecognizer(target: self, action: #selector(swipeAction(swipe:)))
upSwipe.direction = UISwipeGestureRecognizerDirection.up
self.view.addGestureRecognizer(upSwipe)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// Function that gets path to directory
func getDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentDirectory = paths[0]
return documentDirectory
}
// Function that displays an alert
func displayAlert(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "dismiss", style: .default, handler: nil))
present(alert, animated: true, completion: nil)
}