Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
801 views
in Technique[技术] by (71.8m points)

swift: how to delete part of audio?

I'm creating a simple audio editing tool to trim and delete from an audio. I implemented the trim function and it is working fine. However I searched and tried to implement the delete function and here is my code:

func deleteExportAsset(_ asset: AVAsset, fileName: String, completeAudioTime: CGFloat) -> URL {
    print("(#function)")

    let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    let trimmedSoundFileURL = documentsDirectory.appendingPathComponent(fileName)
    print("saving to (trimmedSoundFileURL.absoluteString)")

    if FileManager.default.fileExists(atPath: trimmedSoundFileURL.absoluteString) {
        print("sound exists, removing (trimmedSoundFileURL.absoluteString)")
        do {
            if try trimmedSoundFileURL.checkResourceIsReachable() {
                print("is reachable")
            }

            try FileManager.default.removeItem(atPath: trimmedSoundFileURL.absoluteString)
        } catch {
            print("could not remove (trimmedSoundFileURL)")
            print(error.localizedDescription)
        }

    }

    print("creating export session for (asset)")

    if let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetAppleM4A) {
        exporter.outputFileType = AVFileType.m4a
        exporter.outputURL = trimmedSoundFileURL

        let timeRange1 = CMTimeRangeFromTimeToTime(CMTime(seconds: 0, preferredTimescale: 100), CMTime(seconds: endTimeOfRange1, preferredTimescale: 100))
        let timeRange2 = CMTimeRangeFromTimeToTime(CMTime(seconds: startTimeOfRange2)), preferredTimescale: 100), CMTime(seconds: Double(completeAudioTime), preferredTimescale: 100))
        exporter.timeRange = CMTimeRangeGetUnion(timeRange1, timeRange2)

        // do it
        exporter.exportAsynchronously(completionHandler: {
            print("export complete (exporter.status)")

            switch exporter.status {
            case  AVAssetExportSessionStatus.failed:

                if let e = exporter.error {
                    print("export failed (e)")
                }


            case AVAssetExportSessionStatus.cancelled:
                print("export cancelled (String(describing: exporter.error))")
            default:
                print("export complete")
            }
        })
    } else {
        print("cannot create AVAssetExportSession for asset (asset)")
    }

    return trimmedSoundFileURL
}

what I'm doing here is creating 2 Ranges. Range1 from 0 ->time1 and Range2 from time2->endOfAudio. (I want to delete from time1 -> time2)

then I'm creating the union between 2 ranges. however, nothing is happening to the audio. It is saved exactly like it was before this function.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

CMTimeRangeGetUnion returns another CMTimeRange, which is just a (start-)time and a duration. So there is nothing than can hold the two time ranges required to do what you are expecting. In extension, AVAssetExportSession has no API that takes a list of time ranges to export.

But there is a way to accomplish it. The idea is to create an editable copy of the asset, delete the time range, and then export the editable copy. AVMutableComposition does this:

// assuming 'asset', 'endTimeOfRange1' and 'startTimeOfRange2' from the question:

// create empty mutable composition
let composition: AVMutableComposition = AVMutableComposition()
// copy all of original asset into the mutable composition, effectively creating an editable copy
try composition.insertTimeRange( CMTimeRangeMake( kCMTimeZero, asset.duration), of: asset, at: kCMTimeZero)

// now edit as required, e.g. delete a time range
let startTime = CMTime(seconds: endTimeOfRange1, preferredTimescale: 100)
let endTime = CMTime(seconds: startTimeOfRange2, preferredTimescale: 100)
composition.removeTimeRange( CMTimeRangeFromTimeToTime( startTime, endTime))

// since AVMutableComposition is an AVAsset subclass, it can be exported with AVAssetExportSession (or played with an AVPlayer(Item))
if let exporter = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetAppleM4A) 
{
    // configure session and exportAsynchronously as above. 
    // You don't have to set the timeRange of the exportSession
}

Note that copying from the asset to the composition only modifies some in-memory structures defining which samples go where on the time line, but doesn't actually moves any media samples around. This is not done until exporting; as result, editing is (relatively) fast, and you have to keep the source file around at least until export is finished.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...