简体   繁体   中英

Problems switching between playlists and setting timers in Swift iOS MPMediaPlayer app

I am building a music player app meant to mimic radio or internet streaming automation. I have already succeeded in pulling music from playlists and filtering by last date played so songs do not frequently repeat (a pet peeve about my iPod). I want it to schedule playing short station IDs every quarter hour from a separate playlist, then return to the current playlist or switch to a new one. The roadblocks I'm hitting are getting a repeating timer to wait until a song is over before playing an ID, and playing only one ID before switching back to music. A notification observer…

NotificationCenter.default.addObserver(self, selector:#selector(ViewController.quarterHour), name: NSNotification.Name.MPMusicPlayerControllerNowPlayingItemDidChange, object: nil)

…trips a timer…

self.timer2 = Timer(fireAt: date, interval: 1, target: self, selector: #selector(ViewController.runVoicer(_:)), userInfo: nil, repeats: true)
self.timer2.tolerance = 0.1
        

…that runs this function:

    @objc func quarterHour() {
        self.timer2 = Timer.scheduledTimer(timeInterval: 900, target: self, selector: #selector(ViewController.runVoicer(_:)), userInfo: nil, repeats: true)
        self.timer2.tolerance = 0.1
        }

The media query that selects from the Voicers playlist…

    @objc func runVoicer(_:AnyObject) {
        mp.pause()
        let queryVoice = MPMediaQuery.songs()
        let predicate = MPMediaPropertyPredicate(value: "Voicers",
                                                forProperty: MPMediaPlaylistPropertyName,
                                                comparisonType: .equalTo)
        queryVoice.addFilterPredicate(predicate)
        var collection = [MPMediaItem]()
        let unixDefault = Date(timeIntervalSince1970: 100)
        if let voicers = queryVoice.items {
            collection = voicers.sorted{$0.lastPlayedDate ?? unixDefault > $1.lastPlayedDate ?? unixDate}
        }
        let voiceCollection = MPMediaItemCollection(items: collection)
        mp.setQueue(with: voiceCollection)
            mp.play()
        fetchRockPlaylist()
    }

…not only interrupts a song when the timer goes off instead of using the “…NowPlayingItemDidChange” notification but also keeps playing instead returning to the function listed at bottom after one play, because I cannot land on any method for isolating the first item in the voiceCollection array.

Update: It didn't take long to realize the timer approach was never happening. The inherent latency of switching playlists on the fly meant hearing a second or two of the next song before the ID got going. After a few days of research, I achieved a breakthrough in a Playground of iterating through two arrays and inserting items from one into the other at regular intervals. So after rewriting the function to call up both playlists, and shuffling, sorting, and filtering the resulting arrays as needed, this loop placed IDs after every 3 songs…

let arrayCount = voiceCollection.count
var interR = 0
var indexV = 0
// for loop iterates through rock collection by 3, inserts item from voice collection
    for i in 0..<arrayCount {
       rockSet.insert(voiceCollection[indexV], at:interR)
       interR += 4
       indexV += 1
}

Now it runs exactly as I want. The one remaining issue is to make certain I have at least 3 times as many available songs as IDs, or I get an out of range error, but that's easily avoided. Just wanted to put this out there in case anyone else finds it useful, as there seemed to be no examples of it I could find anywhere else. Cheers…

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM