I know that you can easily set the volume property of the music player, but I want to do it smoothly like Google Maps does when they use the voiceover for navigation instructions.
I was wondering what the best way to do this is.
Thanks!
I would try using a repeating NSTimer. Every time the timer fires you lower the volume a bit. When it reaches the target value you invalidate the timer.
Other ways of getting a repeated event (so that you can do something in stages gradually over time) are DISPATCH_SOURCE_TYPE_TIMER and CADisplayLink. But I think a timer is probably the simplest way to get started.
If you have a pre-existing sound that you're playing, a completely different solution is to apply a fadeout to it before you start playing it (and then just play it all at the same volume, because the sound itself fades out, do you see). AVFoundation gives you the tools to do that (e.g. setVolumeRampFromStartVolume:toEndVolume:timeRange:).
Related
I'm trying to capture synth pitch/volume/e.t.c changes along the duration of an AKSequence, i.e it records changes to AKMidiNoteData as its playing, Im thinking I should have a slider or wheel on my instrument ui and start capturing slider/wheel positions every 1/256 second or so, once a user has started manipulating them, is that the right way to go about this? Are there existing tutorials for this?
I'm working on an iOS application that works with different pieces of audio. Each piece of audio is tied to a separate button (similar to the kind of functionality you'd see in a soundboard app).
In a simple soundboard application, you've got an instance of a player object (AVAudioPlayer or an AVAudioEngine player node) that gets fired whenever a button is pressed.
If you push buttonOne, then sound1 plays.
If you push buttonOne again while sound1 is still playing, then the current instance is interrupted and "replaced" with a new instance of sound1 that starts over at the beginning.
If you push buttonOne, THEN push buttonTwo before sound1 is finished, the instance of sound1 is interrupted and replaced with sound2, again, played from the beginning.
Suppose you're trying to enable cross-fading between the two sounds. You can just create two player instances, load the first sound into player1 and the second into player2, and cross fade between them.
Building on that, suppose you're trying to allow different combinations of sounds to play at the same time. Either the idea that a large number of sounds (maybe the whole soundboard) can all play without interrupting each other. Or, perhaps, the ability to have multiple sounds playing at the same time, e.g. sound1 is a music bed, and sound2...soundXX are sound effects that should be able to play over the music bed without interrupting it.
QUESTIONS: what is the best design strategy for managing your player instances in this situation? Suppose you have a 5 x 5 grid of buttons in your soundboard. If, hypothetically, you should be able to play all 25 sounds at the same time, would that require you to init 25 player instances at setup? (this seems very anti-DRY and not particularly efficient). Or is there some way to dynamically manage the number of instances you need (maybe with a lazy variable?) so that additional instances are only generated as needed, e.g. when you have x number of sounds playing and you start another, an additional instance is generated to contain the newly added sound?
what is the best design strategy for managing instances of AVAudioPlayer in this situation
None. You should be using AVAudioEngine. That's exactly what it is, a sound board / patch kit.
Or is there some way to dynamically manage the number of instances you need
If I have 25 ordered instances of a thing where one possibility is no instance at all, that sounds like an array of Optionals.
Is where a way to smoothly seek playback to some position?
In other words, imagine that some track playing at 0:53 and I need it to play at 0:58 after 2 seconds. So I need it to smoothly increase rate (like using easing functions in animation) and smoothly decrease rate when playback time almost equal 0:58. (it should sound like rewind effect)
Simple seekToTime method is not an option since it just jumps to position.
Here is an illustration:
two________________________seconds_________________________to_seek
0:53__________________________________________________________0:58
|____________↑_________|__________=_________|__________↓__________|
rate=1______smoothly_rate=2.0_______________smoothly_rate=1_____rate=1
I've already tried to use easing methods to smoothly increase rate but looks like it's impossible - rate value changes right away to desired value without easing.
At least I know that it is actually possible. You can try Ampme app from Appstore. They're playing music synchronously on different devices and if there is some lag (buffering, etc) on one of the devices, it's playback smoothly corrects to right value without jumping.
Currently I'm using setRate:time:atHostTime: method and I think it's the right way, but I'm not able to make it work seeking smoothly.
When i call AudioOutputUnitStop, I hear an audible pop/click. Is there a way to prevent this? One solution I had in mind was to set a flag as soon as the user pauses. This flag would cause the audio to keep running but it would fade out. Then it would reset the playhead to 0.1s before where the audio was paused. Then, when the user hits play it would fade in in that tenth of a second. Is there an easier way?
EDIT:
I ended up implementing what I suggested and it works fine though I changed the delay to 0.02s to make it less noticeable. I actually don't call AudioOutputUnitStop at all anymore but I just fill the buffer with 0's when the user hits pause. There's less lag this way when starting to play again.
I am using the StageXL package for Sounds.
The problem that I have is that I am trying to mute the sounds that are currently playing. The only way that I could find to resolve this is to update all the Sounds currently playing SoundTransform volume to 0.
This is fine but this requires all SoundChannels to be stored somewhere. And without a completed event or something similar this collection will be ever increasing.
So is there a way to tell when a Sound finishes playing?
Use the SoundMixer.soundTransform property to change the global volume as simple as this:
SoundMixer.soundTransform = new SoundTransform(0.5);
This will change all sounds to half of the volume. Of course you can set it to 0.0 to mute all sounds.