0

First off, I just want to say thanks to the team at AudioKit for shedding some light on some difficult problems through their code. I have a few questions.

1: It does not appear the the AKAudioPlayer class applies on-the-spot fades if a player is stopped before reaching the end of the file/buffer. Is there another place in the AudioKit library where this is handled?

2: Does anybody know if the AVAudioMixer node’s volume can be adjusted in real time? E.G. can I make adjustments every 1/441 ms to follow the curve of my fade envelope? There is also the AVAudioUnitEQ with its globalGain property.

3: Is it possible to write to an AVAudioPCMBuffer’s floatChannelData after it has been scheduled, and while it is being played?

I’m writing a sampler app with AVFoundation. When it came time to tackle the problem of applying fades to loaded audio files within AVAudioPlayerNodes my first plan was to adjust the volume of the mixer node attached to my player node(s) in real time. This did not seem to have any sort of effect. It is entirely possible that my timing was off when doing this.

When I finally looked at the AKAudioPlayer class, I realized that one could adjust the actual buffer associated with an audio file. After a day or two of debugging, I was able to adapt the code from the AKAudioPlayer class into my PadModel class, with a few minor differences, and it works great.

However, I’m still getting those nasty little clicks whenever I stop one of my Pads from playing before the end of the file because the fades I apply are only in place at the start and the end of the file/buffer.

As far as my first question is concerned, in looking through the AKAudioPlayer class, it appears that the only fades applied to the buffer occur at the beginning and end of the buffer. The stop() method does not appear to apply any sort of on-the-spot fade to the buffer.

In my mind, the only way to have a fade out happen once a stop event happens is to apply it after said stop event, correct?

I have tried doing this, playing a 10 ms long faded-out buffer consisting of the buffer 10 ms after the stop position immediately after I call stop on my player node. It does not have the desired affect. I did not have much confidence in this scheme from the onset, but it seemed worth a try.

To be clear, once my stop() method is called, before actually stopping the the player node, I allocate the 10 ms fade buffer, read into the buffer at the position it is currently at, for the number of frames my fade buffer consists of. I then apply the envelope to the recently allocated fade out buffer, just as it is done in fadeBuffer() method in the AKAudioPlayer class. At this point I finally call stop() on the playing node, then schedule and play the fade out buffer.

Obviously there is going to be a discontinuity between stopping the buffer and playing the fade out buffer, e.g. by the time I apply the fade to the fade out buffer, the stop frame position I assigned to a local variable will no longer be valid, etc. And indeed, once I let off a pad, the sound that is played can only be described as discontinuous.

The only other solution to the problem I can think of strikes me as a daunting task, which would be to continually apply the fade envelope in realtime to the samples immediately ahead of the current play position as the buffer is being played. I currently do not believe I have the coding chops to pull this off.

Anyway, I looked through all the questions on S.O. concerned with AudioKit and this particular subject did not seem to come up. So anybodies thoughts on the matter would be greatly appreciated. Thanks in advance!

If anybody wants to look at my code, the PadModel class starts on line 223 of this file:

https://github.com/mike-normal13/pad/blob/master/Pad.swift

Budge
  • 91
  • 7

1 Answers1

1

AudioKit is lacking in a fade-to-stop method. I would suggest requesting the feature as it is a worth while endeavor. If you are using AVAudioUnitSampler, I believe you can set ADSR values to achieve the fading effect, but not in a very straightforward way. You have to create a preset using AULab, figure out how to get the release to work, then import it into your project.

dave234
  • 4,793
  • 1
  • 14
  • 29
  • Thanks! I'm going to take a crack at without the AVAudioUnitSampler. The timing and threading is going to be rough to get right. Assuming the floatChannelData buffer can be written to while the buffer is being played, this should be dooable. – Budge Oct 09 '17 at 23:32
  • Well I will offer a partial answer here. An AVAudioPCM buffer can indeed be written to while it is being played. I still do no know the answer to the second question. Although in my sampler App, I have a volume control set up that is very responsive. So maybe applying a fade via AVAudioMixer node's volume might actually work if you could get the timing right. My current solution of applying a fade just ahead of the current play position is not working because I have not figured out how to apply the fade fast enough to keep up with the current play position of the buffer. I will revisit this. – Budge Oct 12 '17 at 05:21
  • I was able to get a partially satisfactory result by turning down the volume with a timer to the attached mixer node as soon as I touch up. Unfortunately, the end click is not completely eliminated. From what I can tell, even if you have the volume turned all the way down on a mixer node, when you call stop() on the player node there may or may not be a click. The stop clicks I’m hearing now are greatly reduced, but not completely eliminated. At some point I will try using an EQ node instead. – Budge Nov 09 '17 at 22:45