Can't record on iPad: Error Domain=NSOSStatusErrorDomain Code=1718449215 "(null)" - ios

I am running on iPad iOS 9.3.4 (latest version as of this writing).
I am running this code:
let settings = [
AVFormatIDKey: NSNumber(unsignedInt: kAudioFormatMPEG4AAC),
AVSampleRateKey: 44100.0,
AVNumberOfChannelsKey: 2 as NSNumber,
AVEncoderAudioQualityKey: AVAudioQuality.High.rawValue
]
do {
audioRecorder = try AVAudioRecorder(URL: audioURL, settings: settings)
audioRecorder.delegate = self
audioRecorder.record()
} catch let error as NSError{
print(error.description)
}
I am catching this error:
Error Domain=NSOSStatusErrorDomain Code=1718449215 "(null)"
When I try to use AVAudioRecorder with Objective-C - I am able to record with no problems. The problem seems to only occur with Swift and only on a device - no issue in simulator.
If I switch out kAudioFormatMPEG4AAC with kAudioFormatLinearPCM, I am able to record - but when I try to play back the recording nothing plays - seems like it hasn't recorded well.
Has anyone been able to record with AVAudioRecorder in Swift lately and have the recording play back on a real iPad? I would like to just have that code.

Output file path extension must be in sync with AVFormatIDKey
For .wav
let recordSettings:[String:Any] = [AVFormatIDKey:kAudioFormatLinearPCM,
AVEncoderAudioQualityKey:AVAudioQuality.max.rawValue,
AVEncoderBitRateKey:320000,
AVNumberOfChannelsKey:2,
AVSampleRateKey:44100.0 ] as [String : Any]
For .m4a
let recordSettings:[String:Any] = [AVFormatIDKey:kAudioFormatAppleLossless,
AVEncoderAudioQualityKey:AVAudioQuality.max.rawValue,
AVEncoderBitRateKey:320000,
AVNumberOfChannelsKey:2,
AVSampleRateKey:44100.0 ] as [String : Any]

Looks like I never set the recording session as being active. I wish the error description was better though.
override init() {
super.init()
recordingSession = AVAudioSession.sharedInstance()
do {
try recordingSession.setCategory(AVAudioSessionCategoryPlayAndRecord)
try recordingSession.setActive(true)
recordingSession.requestRecordPermission() { (allowed: Bool) -> Void in
dispatch_async(dispatch_get_main_queue()) {
if allowed {
// success
} else {
// TBD: Show a message to the user that they need to give permission in settings app to proceed
}
}
}
} catch {
// TBD: Show a message to the user that they need to give permission in settings app to proceed
}
}

Related

Sound volume decreasing for no apparent reason

I have an iOS app using SwiftUI. It handles a few sound files and performs some audio recording. This is the function doing the recording work:
func recordAudio(to file: String, for duration: TimeInterval) {
let audioSession:AVAudioSession = AVAudioSession.sharedInstance()
do {try audioSession.setCategory(.playAndRecord, mode: .default)
try audioSession.setActive(true)
let audioFilename = getDocumentsDirectory().appendingPathComponent(file+".m4a"),
audioURL = URL(fileURLWithPath: audioFilename),
settings = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 44100,
AVNumberOfChannelsKey: 2,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
audioRecorder = try AVAudioRecorder(url: audioURL, settings: settings)
audioRecorder.delegate = self
audioRecorder.record(forDuration: TimeInterval(2.0))
} catch let error as NSError {
print("Failed -- Recording !!! -> \(error)")
}
}
At this point, it basically works, but there is a strange behaviour that I neither understand nor like.
Here is the problem:
When I start the app and play a sound file, the volume is right for my taste.
Then without ever adjusting the volume I perform some recording (using the function above).
Finally after the recording is done, I go back to the file I played just before and play it again; the volume has mysteriously gone down, without me knowing why.
Is there something in my function that could explain that?
Or some other cause that someone could think of?
If I restart the app, the volume automatically goes back to normal.
For information, I am using iOS 14.4.2 and Xcode 12.4.
The audio session will be a decreased volume during playback after recording in .playAndRecord mode. After recording, explicitly set to something like .playback to get the volume you're expecting.

AudioKit empty file using renderToFile with AKSequencer

I'm trying to use AudioKit.renderToFile() to export short MIDI passages to audio (m4a):
// renderSequencer is an instance of AKSequencer
self.renderSequencer.loadMIDIFile(fromURL: midiURL)
Conductor.sharedInstance.setInstrument(renderItem.soundID, forOfflineRender: true)
// we only have one track with note content
for track in self.renderSequencer.tracks {
if track.isNotEmpty {
track.setMIDIOutput(Conductor.sharedInstance.midiIn)
}
}
let audioCacheDir = self.module.stateManager.audioCacheDirectory
// strip name off midi file
let midiFileName = String(midiURL.lastPathComponent.split(separator: ".")[0])
audioFileName = midiFileName
audioFileURL = audioCacheDir.appendingPathComponent("\(midiFileName).m4a")
if let audioFileURL = audioFileURL {
let settings = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 44100,
AVNumberOfChannelsKey: 2,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
let audioFile: AVAudioFile = try! AVAudioFile(forWriting: audioFileURL, settings: settings)
// get time in seconds of audio file (with 4-beat tail)
var duration: Float64 = 0.0
MusicSequenceGetSecondsForBeats(seq, (16.0 + 4), &duration)
// render sequence
do { try AudioKit.renderToFile(audioFile, duration: duration) {
self.renderSequencer.setRate(60.0)
self.renderSequencer.play()
}
} catch { print("Error performing offline file render!") }
}
This does produce an audio file of the expected duration, but it is silent. I've also tried logging from my MIDI output and can see that the events "played" from inside the preload closure are actually being sent/handled.
Mostly, I suppose, I'm curious to know whether this is actually expected to work. I've seen a couple of posts suggesting that renderToFile from MIDI is not supported (while others have suggested they have it working).
I did, btw, also post an issue on the audiokit GitHub.

AVAudioRecorder settings empty after constructor call

I'm trying to record audio using the microphone and AVAudioRecorder.
It works on iOS 8 but my code does not work anymore on iOS 9.
The recordSettings dictionary is set properly, then I give it to the
AVAudioRecorder:URL:settings constructor.
But, just after, recorder.settings is empty, an assertion failure is thrown
let recordSettings: [String: AnyObject] = [
AVNumberOfChannelsKey: NSNumber(integer: 2),
AVFormatIDKey: NSNumber(integer: Int(kAudioFormatMPEG4AAC)),
AVEncoderBitRateKey: NSNumber(integer: 64)]
var recorder: AVAudioRecorder!
do {
recorder = try AVAudioRecorder(URL: tempURL, settings:recordSettings) // recordSettings.count = 3
assert(recorder.settings.count != 0, "Audio Recorder does not provide settings") // assertion failure threw
} catch let error as NSError {
print("error when intitializing recorder: \(error)")
return
}
Anyone can help me ? Is it a bug ?
EDIT : In my entire code I did not test recorder.settings just after. I did instantiate recorder like my code above, then I did that :
recorder.delegate = self
recorder.prepareToRecord()
recorder.meteringEnabled = true
And it crashes in this line :
for i in 1...(recorder.settings[AVNumberOfChannelsKey] as! Int) {
...
}
It crashes because recorder.settings[AVNumberOfChannelsKey] is nil
I'm not sure why you're checking the settings property, but
from the AVAudioRecorder header file, on the settings property:
these settings are fully valid only when prepareToRecord has been called
so you must call prepareToRecord() first BUT it will fail/return false, because your bitrate is way too low! Its unit is bits per second, not kilobits per second:
AVEncoderBitRateKey: NSNumber(integer: 64000)
This worked on iOS 8 because your too-low bitrate was simply discarded. Looks like it became an error in iOS 9.

swift2 AVAudioRecorder

I am trying the following code using swift 2, which should be fine in swift 1.
class NewSoundViewController: UIViewController {
required init(coder aDecoder: NSCoder) {
let audioURL = NSURL.fileURLWithPathComponents([
NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0],
"MyAudio.m4a"
])
do {
let session = AVAudioSession.sharedInstance()
try session.setCategory(AVAudioSessionCategoryPlayAndRecord)
} catch {
print("Session errors.")
}
do {
let recordSettings: [String: AnyObject] = [
AVFormatIDKey: kAudioFormatMPEG4AAC,
AVSampleRateKey: 44100.0,
AVNumberOfChannelsKey: 2,
]
self.audioRecorder = try AVAudioRecorder(URL: audioURL!, settings: recordSettings)
self.audioRecorder.meteringEnabled = true
self.audioRecorder.prepareToRecord()
} catch let error as NSError{
print(error.description)
} catch {
print("Other errors")
}
super.init(coder: aDecoder)
}
I got compiling error
Type 'AudioFormatID' does not conform to protocol 'AnyObject'`
at the line AVFormatIDKey: kAudioFormatMPEG4AAC,.
If I comment out the line, I passed build, but got a runtime error
Error Domain=NSOSStatusErrorDomain Code=1718449215 "The operation couldn’t be completed. (OSStatus error 1718449215.)"
I also tried AVFormatIDKey: NSNumber(unsignedInt: kAudioFormatMPEG4AAC),, and got runtime error. Xcode seemed go to debug mode It red highlighted self.audioRecorder = try AVAudioRecorder(URL: audioURL!, settings: recordSettings) and said
Thread 1: EXC_BAD_ACCESS(code=1, address=0x0)
Can anyone please help me?
I also tried AVFormatIDKey: NSNumber(unsignedInt: kAudioFormatMPEG4AAC)
Well, that is the correct thing to say. Basically you are asking two questions here; the compiler error you've already solved. Now you're having a runtime error, but that's completely different matter.
As for the runtime error, it's probably just a figment of trying to test on the Simulator. I ran your code on the device (after fixing the line in question so that it would compile) and it's fine.
EDIT In a comment, you revealed that you tested this on a device running iOS 8.3. That's the problem! You need to test on a device, and it needs to be a device running iOS 9. Then you'll find that your code runs without crashing.
I had the same issue with the settings. AVFormatIDKey was not accepted with the error mentioned in the original post.
The recordSettings needs to be explicitly casted in Swift 2.
These are my settings for recording that works. If I just skipped the AVFormatIDKey, the microphone only worked for a fraction of a second.
let recordSettings = [AVSampleRateKey : NSNumber(float: Float(44100.0)),
AVFormatIDKey : NSNumber(int: Int32(kAudioFormatAppleLossless)),
AVNumberOfChannelsKey : NSNumber(int: 1),
AVEncoderAudioQualityKey : NSNumber(int: Int32(AVAudioQuality.Medium.rawValue)),
AVEncoderBitRateKey : NSNumber(int: Int32(320000))]
You don't need to upgrade the device to iOS 9. I built and ran this for iOS 8.4
I had a similar issue and it turns out that when I updated from swift 1.2 to 2.0 the signature of "settings" changed to a non optional [String : AnyObject]
//Swift 1.2
AVAudioRecorder(URL: audioURL!, settings: nil)
if I pass an empty dictionary it doesn't crash for me anymore and works as before.
//Swift 2.0
AVAudioRecorder(URL: audioURL!, settings: [:])

Stream audio from microphone via bluetooth to another iPhone

I am trying to take incoming microphone audio and stream it to another iPhone. Basically a phone call but via bluetooth. I have the audio coming in via AVAudioRecorder:
func startRecording() {
audioRecorder = nil
let audioSession:AVAudioSession = AVAudioSession.sharedInstance()
audioSession.setCategory(AVAudioSessionCategoryRecord, error: nil)
var recordSettings:NSMutableDictionary = NSMutableDictionary(capacity: 10)
recordSettings.setObject(NSNumber(integerLiteral: kAudioFormatLinearPCM), forKey: AVFormatIDKey)
recordSettings.setObject(NSNumber(float: 44100.0), forKey: AVSampleRateKey)
recordSettings.setObject(NSNumber(int: 2), forKey: AVNumberOfChannelsKey)
recordSettings.setObject(NSNumber(int: 16), forKey: AVLinearPCMBitDepthKey)
recordSettings.setObject(NSNumber(bool: false), forKey: AVLinearPCMIsBigEndianKey)
recordSettings.setObject(NSNumber(bool: false), forKey: AVLinearPCMIsFloatKey)
soundPath = documentsDirectory.stringByAppendingPathComponent("record.caf")
refURL = NSURL(fileURLWithPath: soundPath as String)
var error:NSError?
audioRecorder = AVAudioRecorder(URL: refURL, settings: recordSettings as [NSObject : AnyObject], error: &error)
if audioRecorder.prepareToRecord() == true {
audioRecorder.meteringEnabled = true
audioRecorder.record()
} else {
println(error?.localizedDescription)
}
}
Then I tried using StreamReader from HERE - StreamReader from #martin-r
Using:
if let aStreamReader = StreamReader(path: documentsDirectory.stringByAppendingPathComponent("record.caf")) {
while let line = aStreamReader.nextLine() {
let dataz = line.dataUsingEncoding(NSUTF8StringEncoding)
println (line)
Then send the data to another device using:
self.appDelegate.mpcDelegate.session.sendData(data: NSData!, toPeers: [AnyObject]!, withMode: MCSessionSendDataMode, error: NSErrorPointer )
I convert line to NSData, then using a dispatch_after 0.5 seconds running constantly, I send it to another device via bluetooth.
It does not seem to work and I don't think this is a practical way of doing it. I have done numerous searches and haven't seen much on streaming data via bluetooth. The key word streaming (understandably) sends me to pages about server streaming.
My question is, how can I take audio from a microphone and send it to another iPhone via bluetooth? I have the bluetooth part all set up and it works great. My question is very similar to THIS except with iPhones and Swift - I want to have a phone call via bluetooth.
Thank you in advance.
To simultaneously record and redirect output you need to use the category AVAudioSessionCategoryMultiRoute.
Here's the link to the categories list:
https://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVAudioSession_ClassReference/#//apple_ref/doc/constant_group/Audio_Session_Categories
If all else fails you can use a pre-made solution:
http://audiob.us
It has an API that lets you integrate audio streaming from one app to another:
https://developer.audiob.us/
It supports multiple output endpoints.
Hope this helps.

Resources