How to use the loop if the track was not started from the beginning (with buffering type = .always in AKPlayer )

时光总嘲笑我的痴心妄想 提交于 2020-07-22 03:53:26

问题


I run several AKPlayer with different files and the same length - the first one starts from the beginning, the others start by the button relative to the time of the first. Files are seamless, worth buffering type = .always, loop = true. If AKPlayer did not start from the beginning, then only that part of the track from which the track began to play to the end, and when the loop occurs, starts StartTime is not a zero value. It is necessary that when a loop is played all the tracks from the beginning. With AKWaveTable, everything works adequately, but there are no pan and pitch settings in the player.

Here is a sample code. In the original, I use an array of players and in a loop I upload all the files to my players. I’m doing something like drummachine - several audio files are playing, and I can turn on / off other files in parallel with respect to the time the player was first started. Each file is the same in duration. When I do the same in AKWaveTable, then everything works correctly, but in the future it does not suit me. In this example, I run the playPlayer1 method first and after a while I run playPlayer2. When Player1 starts the loop from the beginning, then Player2 starts the loop from the previous currentTime (for example, from the middle of the file) and its length becomes equal forever (endTime - currentTime)

 var player1: AKPlayer!
    var player2: AKPlayer!
    var playersMixer: AKMixer!

    init() {
        do {
            let file1 = try AKAudioFile(readFileName: "Audio/file1.m4a")
            let file2 = try AKAudioFile(readFileName: "Audio/file2.m4a")

            player1.load(audioFile: file1)
            player2.load(audioFile: file2)
        } catch {
            print(error.localizedDescription)
        }

        player1.buffering = .always
        player1.isLooping = true
        player2.buffering = .always
        player2.isLooping = true

        playersMixer = AKMixer (player1,player2)
        AudioKit.output = playersMixer
        do {
           try AudioKit.start()
        } catch {
            print(error.localizedDescription)
        }
    }
    func playPlayer1() {
        player1.play()
    }
    func playPlayer2() {
        player2.play(from: currentTime)
    }
    var currentTime: Double {
        get {
            if player1.isPlaying {
                return player1.currentTime
            }
            return 0
        }
    }

回答1:


I think that's a missing feature in AudioKit or a bug. Meanwhile, the current version does use the startTime for the loop points as we can see in the source code (https://github.com/AudioKit/AudioKit/blob/master/AudioKit/Common/Nodes/Playback/Players/Player/AKPlayer.swift)

Meanwhile, there's a solution I found and hope it's useful for other readers:

                    player.isLooping = false
                    player.buffering = .dynamic
                    player.completionHandler = {
                        player.isLooping = true
                        player.buffering = .always
                        player.play(from: 0, to: player.duration)
                    }
                    player.play(from: masterPlayer.currentTime)

What I'm doing is to create a player and set .isLooping and .buffering falsely and call .play to run it as a one-time, or a single shot play! A .completionHandler callback is called on complete, where I set .isLooping and .buffering positively. Finally, call .play setting the desired loop points using the properties from and to from self. The masterPlayer refers to a separate player that is used as a reference to get the current play position, I know it's easy to understand but just to avoid assumptions.

EDIT:

After going throw tests, I found an issue that I've reported and deleted before (Is synchronised looping supported for AKPlayers that are multiples in their duration?) and had to reopen, so hopefully, I get an answer and will help understand if it's supposed to work or not.

Also forced player to detach and created a new one:

                        player.completionHandler = {
                            player.detach()
                            let player = self.createPlayer(audioFile: audioFile)
                            self.connectPlayerToMixer(player)
                            player.isLooping = true
                            player.buffering = .always
                            player.startTime = 0
                            player.endTime = audioFile.duration
                            player.play(at: AVAudioTime.now())
                        }

I've now tested AKWaveTable and hopefully, I did it correctly but it also fails just after the recording that is twice the size of previews recording, for example:

  • Loop1 > 1 2 3 4
  • Loop2 > 1 2 3 4
  • Loop3 > 1 2 3 4 5 6 7 8
  • Loop4 > 1 2 3 4
  • Loop5 > 5 6 7 8 (recorded as 1 2 3 4 5 6 7 8, but plays back as 5 6 7 8)

I'll have to go back and test the original AKNodeRecorder to see if the behaviour is similar, but for the moment I'll try another tests with AKWaveTable.

    • After some time testing * *

Here's the audio recording describing the issue above:

https://drive.google.com/open?id=1zxIJgFFvTwGsve11RFpc-_Z94gEEzql7

After looking at the problem for some time, and all the research I found what seems to be a solution. By shifting the procedure that is used! Instead of setting the player to start along the current time, should instead schedule it to start from the start time. This because the start time is what is used for the loop start and endpoints! To learn a bit more follow the link to another StackOverflow post I've posted before (Solution is presented here (Is synchronised looping supported for AKPlayers that are multiples in their duration?), thanks!



来源:https://stackoverflow.com/questions/57764856/how-to-use-the-loop-if-the-track-was-not-started-from-the-beginning-with-buffer

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!