Edited at

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

More than 1 year has passed since last update.


  • 録音した音声を保存してから指定秒数でトリミング

  • 数個の音声ファイルを結合

これら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)")
})
}