質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.04%

【Swift4】動画撮影時に音声にイコライザーをかけて保存する方法を教えて下さい

受付中

回答 0

投稿

  • 評価
  • クリップ 1
  • VIEW 601

score 16

前提・実現したいこと

動画撮影時の入力音声にイコライザー処理を行った上で、カメラロールに保存したいです。

発生している問題・エラーメッセージ

通常の動画撮影と保存は問題なくできています。
イコライザー処理にはAVAudioEngineを使用しようと思っています。

動画撮影

入力された音声にイコライザーをかける

イコライザー処理がかかった状態で動画としてカメラロールに保存

上記を実装するためにはどのようにすればよいのでしょうか?
(そもそも上記の実装は可能なのでしょうか?)

何卒ご教示のほどよろしくお願いいたします。

該当のソースコード

import UIKit
import AVFoundation
import Photos

class ViewController: UIViewController, AVCaptureFileOutputRecordingDelegate {

    var session: AVCaptureSession!
    var videoDevice: AVCaptureDevice!
    var audioDevice: AVCaptureDevice!
    var fileOutput: AVCaptureFileOutput!
    var isRecording = false

    @IBOutlet weak var recordButton: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()

        session = AVCaptureSession()
        videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
        let videoInput = try! AVCaptureDeviceInput.init(device: videoDevice)
        session.addInput(videoInput)

        audioDevice = AVCaptureDevice.default(for: .audio)
        let audioInput = try! AVCaptureDeviceInput.init(device: audioDevice)
        session.addInput(audioInput)

        fileOutput = AVCaptureMovieFileOutput()
        session.addOutput(fileOutput)

        session.commitConfiguration()
        session.startRunning()

        setUpPreview()

    }

    func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
        PHPhotoLibrary.shared().performChanges({
            PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: outputFileURL)
        }) { (isCompleted, error) in
            if isCompleted {
                print("Saved")
            }
        }
    }

    func setUpPreview() {
        let previewLayer: AVCaptureVideoPreviewLayer!
        previewLayer = AVCaptureVideoPreviewLayer(session: session)
        previewLayer.frame = self.view.bounds
        previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
        self.view.layer.insertSublayer(previewLayer, at: 0)

    }

    @IBAction func tapRecordButton(_ sender: Any) {
        onClickRecordButton()
    }

    func startRecording() {
        let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
        let documentDirectry = paths[0] as String
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyyMMddHHmmSS"
        let filePath: String? = "\(documentDirectry)/video-\(formatter.string(from: Date())).mp4"
        let fileURL = NSURL(fileURLWithPath: filePath!)
        print("Start recording")
        fileOutput?.startRecording(to: fileURL as URL, recordingDelegate: self)
        recordButton.setImage(UIImage.init(named: "stop"), for: UIControl.State.normal)
    }

    func stopRecording() {
        print("Stop recording")
        fileOutput?.stopRecording()
        recordButton.setImage((UIImage.init(named: "start")), for: UIControl.State.normal)
    }

    func onClickRecordButton() {
        if !isRecording {
            startRecording()
            isRecording = true
        } else {
            stopRecording()
            isRecording = false
        }
    }

    // ここから下がイコライザーの部分
    var audioEngine = AVAudioEngine()
    func setup() {
        let audioSession = AVAudioSession.sharedInstance()
        do {
            try audioSession.setActive(true)
        } catch let error as NSError  {
            print("Error : \(error)")
        }

        let input = audioEngine.inputNode
        let mixer = audioEngine.mainMixerNode

        // Reverb
        let reverb = AVAudioUnitReverb()
        reverb.loadFactoryPreset(.largeRoom)
        audioEngine.attach(reverb)

        // Delay
        let delay = AVAudioUnitDelay()
        delay.delayTime = 1
        audioEngine.attach(delay)

        // EQ
        let eq = AVAudioUnitEQ()
        audioEngine.attach(eq)

        audioEngine.connect(input, to: reverb, format: input.inputFormat(forBus: 0))
        audioEngine.connect(reverb, to: delay, format: input.inputFormat(forBus: 0))
        audioEngine.connect(delay, to: eq, format: input.inputFormat(forBus: 0))
        audioEngine.connect(eq, to: mixer, format: input.inputFormat(forBus: 0))

        // Distortion
        let distortion = AVAudioUnitDistortion()
        distortion.loadFactoryPreset(.speechAlienChatter)
        audioEngine.attach(distortion)

        audioEngine.connect(input, to: distortion, format: input.inputFormat(forBus: 0))
        audioEngine.connect(distortion, to: mixer, format: input.inputFormat(forBus: 0))
    }
}

試したこと

上記のコードでは削除してありますが、イコライザ処理部分のブロックに動画と同じ保存先を指定したりなどもやってみたのですが、通常の動画としてしか保存されませんでした。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

まだ回答がついていません

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.04%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る