12
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Swiftでの音声ファイルトリミング(Crop)と結合(Concat)

Last updated at Posted at 2017-02-28
  • 録音した音声を保存してから指定秒数でトリミング
  • 数個の音声ファイルを結合

これら2つの機能を組み合わせて使いたかったので、
その際のメモ兼備忘録。

前提

AVAudioSessionとAVAudioRecorderを使ってデバイス内に音声ファイルが保存されていること。

トリミング(Crop)

音声ファイルの末尾から規定秒数分を切り出すケース

まずは切り出し開始位置を決める。
(audioRecorderは録音中のAVAudioRecorderオブジェクト)

let cropTime:TimeInterval = 10 // 10秒分切り出す
let recordedTime = audioRecorder.currentTime
audioRecorder.stop()

切り出し位置が決まったら、Export用にコードを実装する。
(cropTimeより短い時間のファイルなら何もしない)

if recordedTime > cropTime {
    let croppedFileSaveURL = NSURL(fileURLWithPath: "fineName.caf")
    let trimStartTime = recordedTime - cropTime
    // arg1 / arg2 = CMTimeらしいので、とりあえず1で除算
    // 本当はもっと厳密にやったほうが良いかも
    let startTime = CMTimeMake(Int64(trimStartTime), 1)
    let endTime = CMTimeMake(Int64(recordedTime), 1)
    // 開始時間、終了時間からCropするTimeRangeを作成
    let exportTimeRange = CMTimeRangeFromTimeToTime(startTime, endTime)

    // AssetにInputとなるファイルのUrlをセット
    let asset = AVAsset(url: audioRecorder.url)
    // cafファイルとしてExportする
    let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetPassthrough)
    exporter?.outputFileType = AVFileTypeCoreAudioFormat
    exporter?.timeRange = exportTimeRange
    exporter?.outputURL = croppedFileSaveURL

    // Export
    exporter!.exportAsynchronously(completionHandler: {
        switch exporter!.status {
        case .completed:
            print("Crop Success! Url -> \(croppedFileSaveURL)")
        case .failed, .cancelled:
            print("error = \(exporter?.error)")
        default:
            print("error = \(exporter?.error)")
        }
    })
}

結合(Concat)

複数の音声ファイルを1つのファイルに結合するケース

let audioFileURLs = [fileA_Url, fileB_Url, fileC_Url]
var nextStartTime = kCMTimeZero
let composition = AVMutableComposition()
let track = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid)

// 結合するファイル毎に、timerangeを作りtrackにinsertする
for url in audioFileURLs {
    let asset = AVURLAsset(url: url as URL)
    if let assetTrack = asset.tracks(withMediaType: AVMediaTypeAudio).first {
        let timeRange = CMTimeRange(start: kCMTimeZero, duration: asset.duration)
        do {
            try track.insertTimeRange(timeRange, of: assetTrack, at: nextSoundStartTime)
            nextSoundStartTime = CMTimeAdd(nextSoundStartTime, timeRange.duration)
        } catch {
            print("concatenateError : \(error)")
        }
    }
}

if let exportSession = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetPassthrough) {

    let concatFileSaveURL = NSURL(fileURLWithPath: "fineName.caf")

    exportSession.outputFileType = AVFileTypeCoreAudioFormat
    exportSession.outputURL = concatFileSaveURL as URL

    exportSession.exportAsynchronously(completionHandler: {
        switch exportSession.status {
        case .completed:
            print("Concat Success! Url -> \(concatFileSaveURL)")
        case .failed, .cancelled:
            print("error = \(exporter?.error)")
        default:
            print("error = \(exporter?.error)")
    })
}
12
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?