solenoid solenoid - 3 months ago 13
Swift Question

Reference sections of an audio file for playback using AVFoundation classes

Goal: I need to loop a changing, arbitrary slice of an audio file.

Current solutions:


  • First working solution used MPMusicPlayerController::systemMusicPlayer and Timer. This basically worked, but had a few issues. First, at some point in changing the location/duration of the slice, the UI would lock up (specifically, touch input). What was odd is it would happen ~1s after all of my code executed, but since I have no control over a lot of it, I eventually chalked it up to not being a workable solution.

  • Second solution is currently using the same basic set-up except I am using AVFoundation::AVAudioPlayer. I have only started on the implementation, but I am already noticing that that looping is fairly inconsistent.



I am currently at the point where the second solution is looping, but have not gotten to the point where I can test if the UI locks up.

Question: I was looking through the available methods in AVFoundation and had the thought: "Would it be possible to reference an exact chunk of an audio file and loop that, changing the chunk as needed?". What I cannot find, and I think it may be due to my lacking the nomenclature to properly search for it, is any examples of something like this.

I would like to use a built-in framework if possible.

Answer Source

After finally finding some good examples, I came up with:

let composition = AVMutableComposition()
var playerItem:AVPlayerItem!
var qPlayer:AVQueuePlayer!
var currentAudioTrack:AVAssetTrack!
var compAudioTrack:AVMutableCompositionTrack!
var uurl:URL!
var asset:AVURLAsset!

...

    do {
        try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, with: .mixWithOthers)
    } catch let error {
        print("i am error:", error)
    }
    compAudioTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())
    uurl = (sysMusicPlayer.nowPlayingItem?.assetURL)!
    asset = AVURLAsset(url: uurl)
    currentAudioTrack = asset.tracks(withMediaType: AVMediaTypeAudio)[0]
    assetTimeScale = currentAudioTrack.naturalTimeScale
    let a:CMTime = CMTime(seconds: 10, preferredTimescale: assetTimeScale)
    let b:CMTime = CMTime(seconds: 42, preferredTimescale: assetTimeScale)
    try! compAudioTrack.insertTimeRange(CMTimeRange(start: a, end: b), of: currentAudioTrack, at: CMTimeMake(0, 1))
    playerItem = AVPlayerItem(asset: composition)
    qPlayer = AVQueuePlayer(playerItem: playerItem)
    qPlayer.play()

This is the basic functionality, which lets me failr easily change the current section of song by

    compAudioTrack.segments.removeAll()
    try! compAudioTrack.insertTimeRange(timeRangeToInsert, of: currentAudioTrack, at: CMTimeMake(0, 1))
    qPlayer.insert(playerItem, after: nil)