0

In AudioKit there is this method for AKPlayer:

@objc dynamic public func play(at audioTime: AVAudioTime?)

I want the same for stop method because I want to be able to stop the player at any time when the user hits the stop button. I am making a music app and I need to stop the sound in X time which is calculated based on BPM and etc.

Here is how I start my AKPlayer:

drums.play(at: AVAudioTime.now() + timeToClosestBeatGrid)

I want the same API with stop:

drums.stop(at: AVAudioTime.now() + timeToClosestBeatGrid) // this api doesnt exist :(((

I tried using endTime property by setting it but it does not seem to do anything...

How may I accomplish this?

PS: I am not looking for a Timer solution this is because a timer is not 100% accurate. I want my stop method to be 100% accurate just like play method

cs guy
  • 926
  • 2
  • 13
  • 33

2 Answers2

1

The most accurate way to schedule events in AudioKit is by using AKSequencer. The sequencer can be connected to a callback instrument, which is a node that passes the events to an user-defined function.

In your case, you would add an event at the time where you want the player to stop. In your callback function, you would stop the player as a response to that event.

This is an outline of what should be done:

  1. Create a track to contain the stop event, using AKSequencer's addTrack method. Connect this track to an AKCallbackInstrument. Please see this answer on how to connect an AKCallbackInstrument to an AKSequencer track.
  2. Add the stop event to the track, at the time position where you want the music to stop. As you will be interpreting the event yourself with a callback function, it doesn't really matter what type of event you use. You could simply use a Note On.
  3. In the callback function, stop the player when that event is received.

This is what your callback function would look like:

func stopCallback(status:UInt8, note:MIDINoteNumber, vel:MIDIVelocity) -> () {
    guard let status = AKMIDIStatus(byte: status),
        let type = status.type,
        type == .noteOn else { return }
    drums.stop()
}
vindur
  • 436
  • 4
  • 7
0

According to AudioKit documentation, you can try using the schedule(at:) method:

You can call this to schedule playback in the future or the player will call it when play() is called to load the audio data

After the play() method you should declare this schedule(at:) with an offset AVAudioTime.now() + timeToClosestBeatGrid and specify .dataPlayedBack as completion callback type, because this completion is called when (from docs)...

The buffer or file has finished playing

and now (in completion block) you can call drums.stop()

But... If the .stop() method should be called whenever the button is pressed, why not use some form of delay (Timer or DispatchQueue) with the value timeToClosestBeatGrid as the offset?

  • `why not use some form of delay (Timer or DispatchQueue) with the value timeToClosestBeatGrid as the offset?` the problem is the user can revoke the stop action if they press the button within `timeToClosestBeatGrid` amount of time. I made a custom Android Sound Engine and I had an API called stopAt and I am trying to basically copy the same logic here with AudioKit APIs. AKPlayer does not have `schedule(at:)` method AKAudioPlayer has it. Coming to the timer thing, I initially had that in mind but its a bit hard to manage all the timers if I need to revoke it – cs guy Mar 20 '21 at 01:47