ca_debug_string: inPropertyData == NULL

I get an error in my debug window as per this post's title. I've looked around to try and understand what causes this, but haven't found an explaination, other than loosely related mySQL program errors about called methods not being able to use null (or zero) values. I couldn't see how this is realted to my swift program. I've had this app working when the user controls the length of the recording by tapping 'record', then 'stop resording'. I need to restrict the length of recordibng to 1 second, so I'm using the AVAudioRecorder class' record(forDuration:) function, setting the duration for 1 second.

I've only made this change, (lines 114 - 119), where the recording is now on a 1 second timer instead of waiting for the user to tap 'stop recording'. Now I get the error mentioned at the start of this post.

Even if you can't see the problem with my code, you'll be a great help if you know what the meaning behind the debug output is!


import UIKit
import AVFoundation
class RecordNoteViewController: UIViewController, AVAudioRecorderDelegate {
    var stackView: UIStackView!
    var recordNoteButton: UIButton!
    var playSampleButton: UIButton!
    var recordNoteSession: AVAudioSession!
    var noteSampler: AVAudioRecorder!
    var recordSampleResult: Bool = false
    var notePlayer: AVAudioPlayer!
    var selectedNote: String = ""
    var timer = Timer()
    /
   
    override func loadView() {
        super.loadView()
       
        view.backgroundColor = UIColor.gray
       
        stackView = UIStackView()
        stackView.spacing = 30
        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.distribution = UIStackViewDistribution.fillEqually
        stackView.alignment = .center
        stackView.axis = .vertical
        view.addSubview(stackView)
       
        stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    }
   
    override func viewDidLoad() {
        super.viewDidLoad()
       
        title = "Record \(selectedNote)"
        navigationItem.backBarButtonItem = UIBarButtonItem(title: "Record Note", style: .plain, target: nil, action: nil)
       
        recordNoteSession = AVAudioSession.sharedInstance()
       
        do {
            try recordNoteSession.setCategory(AVAudioSessionCategoryPlayAndRecord)
            try recordNoteSession.setActive(true)
            recordNoteSession.requestRecordPermission() { [unowned self] allowed in
                DispatchQueue.main.async {
                    if allowed {
                        self.loadRecordingUI()
                    } else {
                        self.loadFailUI()
                    }
                }
            }
        } catch {
            self.loadFailUI()
        }
    }
   
    func loadRecordingUI() {
        recordNoteButton = UIButton()
        recordNoteButton.translatesAutoresizingMaskIntoConstraints = false
        recordNoteButton.setTitle("Tap to Record", for: .normal)
        recordNoteButton.titleLabel?.font = UIFont.preferredFont(forTextStyle: .title1)
        recordNoteButton.addTarget(self, action: #selector(recordTapped), for: .touchUpInside)
        stackView.addArrangedSubview(recordNoteButton)
        playSampleButton = UIButton()
        playSampleButton.translatesAutoresizingMaskIntoConstraints = false
        playSampleButton.setTitle("Tap to Play", for: .normal)
        playSampleButton.isHidden = true
        playSampleButton.alpha = 0
        playSampleButton.titleLabel?.font = UIFont.preferredFont(forTextStyle: .title1)
        playSampleButton.addTarget(self, action: #selector(playSampleTapped), for: .touchUpInside)
        stackView.addArrangedSubview(playSampleButton)
    }
   
    func loadFailUI() {
        let failLabel = UILabel()
        failLabel.font = UIFont.preferredFont(forTextStyle: .headline)
        failLabel.text = "Record failed: please ensure the app has access to your microphone."
        failLabel.numberOfLines = 0
       
        stackView.addArrangedSubview(failLabel)
    }
   
    class func getDocumentsDirectory() -> URL {
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
        let documentsDirectory = paths[0]
        return documentsDirectory
    }
   
    class func getNoteSampleURL() -> URL {
        return getDocumentsDirectory().appendingPathComponent("note.m4a")
    }
   
    func startRecording() {
       
        view.backgroundColor = UIColor(red: 0.6, green: 0, blue: 0, alpha: 1)
       
        recordNoteButton.isEnabled = false
        recordNoteButton.setTitle("Recording...", for: .normal)
       
        let audioURL = RecordNoteViewController.getNoteSampleURL()
        print(audioURL.absoluteString)
       
        let settings = [
            AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
            AVSampleRateKey: 48000,
            AVNumberOfChannelsKey: 1,
            AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
        ]
       
        do {
            noteSampler = try AVAudioRecorder(url: audioURL, settings: settings)
            noteSampler.delegate = self
            recordSampleResult = noteSampler.record(forDuration: 1)
            if noteSampler.prepareToRecord() {
                if recordSampleResult == false {
                    finishRecording(success: false)
                }
            }
        } catch {
            finishRecording(success: false)
        }
        recordNoteButton.isEnabled = true
    }
   
    func finishRecording(success: Bool) {
        view.backgroundColor = UIColor(red: 0, green: 0.6, blue: 0, alpha: 1)
       
        recordNoteButton.isEnabled = true
        noteSampler.stop()
        noteSampler = nil
       
        if success {
            recordNoteButton.setTitle("Tap to Re-record", for: .normal)
            if playSampleButton.isHidden {
                UIView.animate(withDuration: 0.35) { [unowned self] in
                    self.playSampleButton.isHidden = false
                    self.playSampleButton.alpha = 1
                }
            }
            navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Analyze Note", style: .plain, target: self, action: #selector(analyzeNoteTapped))
        } else {
            recordNoteButton.setTitle("Tap to Record", for: .normal)
           
            let ac = UIAlertController(title: "Record failed", message: "There was a problem recording your note; please try again.", preferredStyle: .alert)
            ac.addAction(UIAlertAction(title: "OK", style: .default))
            present(ac, animated: true)
        }
    }
   
    @objc func analyzeNoteTapped() {
        let anvc = AnalyzeNoteViewController()
        anvc.selectedNote = selectedNote
        navigationController?.pushViewController(anvc, animated: true)
    }
   
    @objc func recordTapped() {
        if noteSampler == nil {
            startRecording()
            if !playSampleButton.isHidden {
                UIView.animate(withDuration: 0.35) { [unowned self] in
                    self.playSampleButton.isHidden = true
                    self.playSampleButton.alpha = 0
                }
            }
        }
    }
   
    func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
        if !flag {
            finishRecording(success: false)
        }
    }
   
    @objc func playSampleTapped() {
        let audioURL = RecordNoteViewController.getNoteSampleURL()
       
        do {
            notePlayer = try AVAudioPlayer(contentsOf: audioURL)
            notePlayer.play()
        } catch {
            let ac = UIAlertController(title: "Playback failed", message: "There was a problem playing your note; please try re-recording.", preferredStyle: .alert)
            ac.addAction(UIAlertAction(title: "OK", style: .default))
            present(ac, animated: true)
        }
    }
   
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        /
    }

Replies

Even if you can't see the problem with my code, you'll be a great help if you know what the meaning behind the debug output is!

ca_debug_string
is a debugging macro used by the Core Audio utility classes. You can get the latest public version of this code from the developer web site. It’s likely that one of the audio units your app is using (indirectly, via AVFoundation) is using these utility classes and has encountered a problem that’s caused it to log this message.

As to what this means for your code overall, I have, alas, no idea; audio isn’t really my specialism. For help with that you might try asking over in Media > AVFoundation (Audio).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"