首页 > 解决方案 > 如何录制音频并将其保存到沙盒?

问题描述

我正在开发记录用户音频的应用程序,并在表格视图的另一个屏幕上显示从此应用程序制作的所有早期录音。单击特定行时,必须播放录音。我如何实现这一目标,是否有任何资源可以帮助我解决这个问题?

我的代码当前保存录音并在同一屏幕上播放。但是,新的录音会覆盖以前的录音,并且只有一个录音会保存到文件管理器中。我已将“隐私 - 需要使用麦克风”添加到 plist。音频已成功录制和播放。

import UIKit
import AVFoundation
import MobileCoreServices



class ViewController: UIViewController, AVAudioRecorderDelegate, AVAudioPlayerDelegate{

@IBOutlet var recordingTimeLabel: UILabel!
@IBOutlet var record_btn_ref: UIButton!
@IBOutlet var play_btn_ref: UIButton!

//Variables:
var audioRecorder: AVAudioRecorder!
var audioPlayer : AVAudioPlayer!
var meterTimer:Timer!
var isAudioRecordingGranted: Bool!
var isRecording = false
var isPlaying = false


override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    //Check recording permission:

    check_record_permission()

    //Add right bar button:

    navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Documents", style: .plain, target: self, action: #selector(OpenDoc))

}
//Button action to start recording:
@IBAction func start_recording(_ sender: UIButton)
{
    //If already recording:
    if(isRecording)
    {
        //Stop recording:
        finishAudioRecording(success: true)
        //Set the title back to "Record":
        record_btn_ref.setTitle("Record", for: .normal)
        //Enable the play button:
        play_btn_ref.isEnabled = true
        //Set the value of the variable "isRecording" to false
        isRecording = false
    }
        //If audio was not being recorded:
    else
    {
        //Setup the recorder:
        setup_recorder()
        //Start recording:
        audioRecorder.record()
        //Update label every 1 sec:
        meterTimer = Timer.scheduledTimer(timeInterval: 0.1, target:self, selector:#selector(self.updateAudioMeter(timer:)), userInfo:nil, repeats:true)
        //Set the title of the label to "Stop":
        record_btn_ref.setTitle("Stop", for: .normal)
        //Enable the play button:
        play_btn_ref.isEnabled = false
        //Set "isRecording" to true:
        isRecording = true
    }
}


//Button action for play/pause:
@IBAction func play_recording(_ sender: Any)
{
    //If audio is already being played (i.e. it should pause on being clicked again):
    if(isPlaying)
    {
        //Stop audio player
        audioPlayer.stop()
        //Enable record button:
        record_btn_ref.isEnabled = true
        //Set the title to "Play"
        play_btn_ref.setTitle("Play", for: .normal)
        //Set value of "isPlaying" to false:
        isPlaying = false
    }
        //It is not playing (i.e. it should play when button is clicked)
    else
    {
        //If file path exists:
        if FileManager.default.fileExists(atPath: getFileUrl().path)
        {
            //Disable the record button:
            record_btn_ref.isEnabled = false
            //Set the title of the button to "Pause":
            play_btn_ref.setTitle("Pause", for: .normal)
            //Prepare to play:
            prepare_play()
            //Implement play method of audioPlayer:
            audioPlayer.play()
            //Set variable "isPlaying" to true:
            isPlaying = true
        }
            //If file path doesn't exist:
        else
        {
            display_alert(msg_title: "Error", msg_desc: "Audio file is missing.", action_title: "OK")
        }
    }
}


//Function that checks permission to record:

func check_record_permission()
{
    //Switch record permission instances:

    switch AVAudioSession.sharedInstance().recordPermission {
    //Case granted:
    case AVAudioSessionRecordPermission.granted:
        isAudioRecordingGranted = true
        break
    //Case denied:
    case AVAudioSessionRecordPermission.denied:
        isAudioRecordingGranted = false
        break
    //Case not determined, in which case ask for permission:
    case AVAudioSessionRecordPermission.undetermined:
        AVAudioSession.sharedInstance().requestRecordPermission({ (allowed) in
            if allowed {
                self.isAudioRecordingGranted = true
            } else {
                self.isAudioRecordingGranted = false
            }
        })
        break
    //Default case:
    default:
        break
    }
}


//Function that gets the directory path:
func getDocumentsDirectory() -> URL
{
    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    let documentsDirectory = paths[0]
    return documentsDirectory
}

//Function that gets the URL file path:
func getFileUrl() -> URL
{
    let filename = "myRecording.m4a"
    let filePath = getDocumentsDirectory().appendingPathComponent(filename)

    return filePath
}

//Function that sets up the recorder:
func setup_recorder()
{
    if isAudioRecordingGranted
    {
        let session = AVAudioSession.sharedInstance()
        do
        {
            try session.setCategory(AVAudioSession.Category.playAndRecord, options: .defaultToSpeaker)
            try session.setActive(true)
            let settings = [
                AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
                AVSampleRateKey: 44100,
                AVNumberOfChannelsKey: 2,
                AVEncoderAudioQualityKey:AVAudioQuality.high.rawValue
            ]
            audioRecorder = try AVAudioRecorder(url: getFileUrl(), settings: settings)
            audioRecorder.delegate = self
            audioRecorder.isMeteringEnabled = true
            audioRecorder.prepareToRecord()
        }
        catch let error {
            display_alert(msg_title: "Error", msg_desc: error.localizedDescription, action_title: "OK")
        }
    }
    else
    {
        display_alert(msg_title: "Error", msg_desc: "Don't have access to use your microphone.", action_title: "OK")
    }
}



//Objective C function to update text of the timer label:
@objc func updateAudioMeter(timer: Timer)
{
    if audioRecorder.isRecording
    {
        let hr = Int((audioRecorder.currentTime / 60) / 60)
        let min = Int(audioRecorder.currentTime / 60)
        let sec = Int(audioRecorder.currentTime.truncatingRemainder(dividingBy: 60))
        let totalTimeString = String(format: "%02d:%02d:%02d", hr, min, sec)
        recordingTimeLabel.text = totalTimeString
        audioRecorder.updateMeters()
    }
}

//Function for finish audio recording:
func finishAudioRecording(success: Bool)
{
    //If recording was successful:
    if success
    {
        audioRecorder.stop()
        audioRecorder = nil
        meterTimer.invalidate()
        print("recorded successfully.")


    }
        //If recording was not successful:
    else
    {
        display_alert(msg_title: "Error", msg_desc: "Recording failed.", action_title: "OK")
    }
}


//Prepare to play:
func prepare_play()
{
    do
    {
        audioPlayer = try AVAudioPlayer(contentsOf: getFileUrl())
        audioPlayer.delegate = self
        audioPlayer.prepareToPlay()
    }
    catch{
        print("Error")
    }
}




//Function for audio record did finish recording:

func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool)
{
    if !flag
    {
        finishAudioRecording(success: false)
    }
    play_btn_ref.isEnabled = true



}

//If recorded audio was played:

func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool)
{
    record_btn_ref.isEnabled = true
    play_btn_ref.setTitle("Play", for: .normal)
}

//Function to display alerts:

func display_alert(msg_title : String , msg_desc : String ,action_title : String)
{
    let ac = UIAlertController(title: msg_title, message: msg_desc, preferredStyle: .alert)
    ac.addAction(UIAlertAction(title: action_title, style: .default)
    {
        (result : UIAlertAction) -> Void in
        _ = self.navigationController?.popViewController(animated: true)
    })
    present(ac, animated: true)
}




@objc func OpenDoc()
{
    let documentPicker = UIDocumentPickerViewController(documentTypes: [kUTTypeMPEG4Audio as String], in: .import)
    documentPicker.delegate = self as? UIDocumentPickerDelegate
    documentPicker.allowsMultipleSelection = false
    present(documentPicker, animated: true, completion: nil)
}

}

标签: iosswiftaudiosandbox

解决方案


我的代码当前保存录音并在同一屏幕上播放。但是,新的录音会覆盖以前的录音,并且只有一个录音会保存到文件管理器中。

据我了解,您在此功能中为录音提供了相同的名称func getFileUrl()。让每个记录具有不同名称的最简单方法是为其添加时间戳,例如使用Date()timeIntervalSince1970.

func getFileUrl() -> URL
{
    let currentTime = Date().timeIntervalSince1970
    let filename = "myRecording-\(currentTime).m4a"
    let filePath = getDocumentsDirectory().appendingPathComponent(filename)

    return filePath
}

接下来,您的play_recording函数应该从另一个屏幕上的 TableView 获取录音的 URL 作为参数。


推荐阅读