J'ai un AVAudioPlayer jouant de l'audio (duh!)
L'audio est lancé lorsque l'utilisateur appuie sur un bouton. Quand ils le libèrent, je veux que l'audio disparaisse.
J'utilise Interface builder ... donc j'essaie de brancher une fonction sur "retouche à l'intérieur" qui atténue l'audio sur 1 seconde puis s'arrête.
Des idées?
Merci
Voici comment je le fais:
-(void)doVolumeFade
{
if (self.player.volume > 0.1) {
self.player.volume = self.player.volume - 0.1;
[self performSelector:@selector(doVolumeFade) withObject:nil afterDelay:0.1];
} else {
// Stop and get the sound ready for playing again
[self.player stop];
self.player.currentTime = 0;
[self.player prepareToPlay];
self.player.volume = 1.0;
}
}
Swift a une méthode AVAudioPlayer
que vous pouvez utiliser pour la décoloration qui a été incluse à partir de iOS 10.0
:
var audioPlayer = AVAudioPlayer()
...
audioPlayer.setVolume(0, fadeDuration: 3)
J'ai résolu ce problème en utilisant une sous-classe NSOperation afin que la décoloration du volume ne bloque pas le thread principal. Il permet également aux fondus d'être mis en file d'attente et oubliés. Ceci est particulièrement utile pour jouer des sons à un coup avec des effets de fondu d'entrée et de fondu car ils sont désalloués une fois le dernier fondu terminé.
// Example of MXAudioPlayerFadeOperation in NSOperationQueue
NSOperationQueue *audioFaderQueue = [[NSOperationQueue alloc] init];
[audioFaderQueue setMaxConcurrentOperationCount:1]; // Execute fades serially.
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"bg" ofType:@"mp3"]; // path to bg.mp3
AVAudioPlayer *player = [[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:filePath] error:NULL] autorelease];
[player setNumberOfLoops:-1];
[player setVolume:0.0];
// Note that delay is delay after last fade due to the Operation Queue working serially.
MXAudioPlayerFadeOperation *fadeIn = [[MXAudioPlayerFadeOperation alloc] initFadeWithAudioPlayer:player toVolume:1.0 overDuration:3.0];
[fadeIn setDelay:2.0];
MXAudioPlayerFadeOperation *fadeDown = [[MXAudioPlayerFadeOperation alloc] initFadeWithAudioPlayer:player toVolume:0.1 overDuration:3.0];
[fadeDown setDelay:0.0];
MXAudioPlayerFadeOperation *fadeUp = [[MXAudioPlayerFadeOperation alloc] initFadeWithAudioPlayer:player toVolume:1.0 overDuration:4.0];
[fadeUp setDelay:0.0];
MXAudioPlayerFadeOperation *fadeOut = [[MXAudioPlayerFadeOperation alloc] initFadeWithAudioPlayer:player toVolume:0.0 overDuration:3.0];
[fadeOut setDelay:2.0];
[audioFaderQueue addOperation:fadeIn]; // 2.0s - 5.0s
[audioFaderQueue addOperation:fadeDown]; // 5.0s - 8.0s
[audioFaderQueue addOperation:fadeUp]; // 8.0s - 12.0s
[audioFaderQueue addOperation:fadeOut]; // 14.0s - 17.0s
[fadeIn release];
[fadeDown release];
[fadeUp release];
[fadeOut release];
Pour le code de classe MXAudioPlayerFadeOperation, voir cet article .
J'ai fini par combiner une partie de la réponse et je l'ai convertie en Swift finissant dans cette méthode:
func fadeVolumeAndPause(){
if self.player?.volume > 0.1 {
self.player?.volume = self.player!.volume - 0.1
var dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC)))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
self.fadeVolumeAndPause()
})
} else {
self.player?.pause()
self.player?.volume = 1.0
}
}
Ce sont toutes de bonnes réponses, mais elles ne concernent pas la spécification du taux de fondu (ou l'application d'une courbe logarithmique au fondu, ce qui est parfois souhaitable), ni la spécification du nombre de dB de réduction par rapport à l'unité que vous évanouissez.
il s'agit d'une exception de l'une de mes applications, avec quelques "cloches et sifflets" supprimés, qui ne sont pas pertinents pour cette question.
prendre plaisir!
#define linearToDecibels(linear) (MIN(10,MAX(-100,20.0 * log10(linear))))
#define decibelsToLinear(decibels) (pow (10, (0.05 * decibels)))
#define fadeInfoId(n) [fadeInfo objectForKey:@#n]
#define fadeInfoObject(NSObject,n) ((NSObject*) fadeInfoId(n))
#define fadeInfoFloat(n) [fadeInfoId(n) floatValue]
#define useFadeInfoObject(n) * n = fadeInfoId(n)
#define useFadeInfoFloat(n) n = fadeInfoFloat(n)
#define setFadeInfoId(n,x) [fadeInfo setObject:x forKey:@#n]
#define setFadeInfoFloat(n,x) setFadeInfoId(n,[NSNumber numberWithFloat:x])
#define setFadeInfoFlag(n) setFadeInfoId(n,[NSNumber numberWithBool:YES])
#define saveFadeInfoId(n) setFadeInfoId(n,n)
#define saveFadeInfoFloat(n) setFadeInfoFloat(n,n)
#define fadeAVAudioPlayer_default nil
#define fadeAVAudioPlayer_linearFade @"linearFade"
#define fadeAVAudioPlayer_fadeToStop @"fadeToStop"
#define fadeAVAudioPlayer_linearFadeToStop @"linearFadeToStop"
-(void) fadeAVAudioPlayerTimerEvent:(NSTimer *) timer {
NSMutableDictionary *fadeInfo = timer.userInfo;
NSTimeInterval elapsed = 0 - [fadeInfoObject(NSDate,startTime) timeIntervalSinceNow];
NSTimeInterval useFadeInfoFloat(fadeTime);
float useFadeInfoFloat(fadeToLevel);
AVAudioPlayer useFadeInfoObject(player);
double linear;
if (elapsed>fadeTime) {
if (fadeInfoId(stopPlaybackAtFadeTime)) {
[player stop];
linear = fadeInfoFloat(fadeFromLevel);
} else {
linear = fadeToLevel;
}
[timer invalidate];
[fadeInfo release];
} else {
if (fadeInfoId(linearCurve)) {
float useFadeInfoFloat(fadeFromLevel);
float fadeDelta = fadeToLevel-fadeFromLevel;
linear = fadeFromLevel + (fadeDelta * (elapsed/fadeTime));
} else {
float useFadeInfoFloat(fadeToDB);
float useFadeInfoFloat(fadeFromDB);
float fadeDelta = fadeToDB-fadeFromDB;
float decibels = fadeFromDB + (fadeDelta * (elapsed/fadeTime));
linear = decibelsToLinear(decibels);
}
}
[player setVolume: linear];
//[self displayFaderLevelForMedia:player];
//[self updateMediaVolumeLabel:player];
}
-(void) fadeAVAudioPlayerLinear:(AVAudioPlayer *)player over:(NSTimeInterval) fadeTime fadeToLevel:(float) fadeToLevel fadeMode:(NSString*)fadeMode {
NSMutableDictionary *fadeInfo = [[NSMutableDictionary alloc ]init];
saveFadeInfoId(player);
float fadeFromLevel = player.volume;// to optimize macros put value in var, so we don't call method 3 times.
float fadeFromDB = linearToDecibels(fadeFromLevel);
float fadeToDB = linearToDecibels(fadeToLevel);
saveFadeInfoFloat(fadeFromLevel);
saveFadeInfoFloat(fadeToLevel);
saveFadeInfoFloat(fadeToDB);
saveFadeInfoFloat(fadeFromDB);
saveFadeInfoFloat(fadeTime);
setFadeInfoId(startTime,[NSDate date]);
if([fadeMode isEqualToString:fadeAVAudioPlayer_fadeToStop]||[fadeMode isEqualToString:fadeAVAudioPlayer_linearFadeToStop]){
setFadeInfoFlag(stopPlaybackAtFadeTime);
}
if([fadeMode isEqualToString:fadeAVAudioPlayer_linearFade]||[fadeMode isEqualToString:fadeAVAudioPlayer_linearFadeToStop]){
setFadeInfoFlag(linearCurve);
}
[NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(fadeAVAudioPlayerTimerEvent:) userInfo:fadeInfo repeats:YES];
}
-(void) fadeAVAudioPlayer:(AVAudioPlayer *)player over:(NSTimeInterval) fadeTime fadeToDB:(float) fadeToDB fadeMode:(NSString*)fadeMode {
[self fadeAVAudioPlayerLinear:player over:fadeTime fadeToLevel:decibelsToLinear(fadeToDB) fadeMode:fadeMode ];
}
-(void) fadeoutAVAudioPlayer:(AVAudioPlayer *)player {
[self fadeAVAudioPlayerLinear:player over:5.0 fadeToLevel:0 fadeMode:fadeAVAudioPlayer_default];
}
-(void) fadeinAVAudioPlayer:(AVAudioPlayer *)player {
[self fadeAVAudioPlayerLinear:player over:5.0 fadeToLevel:0 fadeMode:fadeAVAudioPlayer_default];
}
J'aime les réponses d'Ambroise Collon, donc j'ai voté mais Swift est typé statiquement de sorte que les méthodes performSelector
: doivent être abandonnées, peut-être qu'une alternative pourrait être l'envoi asynchrone (dans cette version j'ai ajouté aussi le volume de destination comme paramètre)
func dispatchDelay(delay:Double, closure:@escaping ()->()) {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delay, execute: closure)
}
extension AVAudioPlayer {
func fadeOut(vol:Float) {
if volume > vol {
//print("vol is : \(vol) and volume is: \(volume)")
dispatchDelay(delay: 0.1, closure: {
[weak self] in
guard let strongSelf = self else { return }
strongSelf.volume -= 0.01
strongSelf.fadeOut(vol: vol)
})
} else {
volume = vol
}
}
func fadeIn(vol:Float) {
if volume < vol {
dispatchDelay(delay: 0.1, closure: {
[weak self] in
guard let strongSelf = self else { return }
strongSelf.volume += 0.01
strongSelf.fadeIn(vol: vol)
})
} else {
volume = vol
}
}
}
Une extension pour Swift inspirée de la réponse la plus votée. Pour ceux d'entre vous qui aiment copier-coller :)
extension AVAudioPlayer {
func fadeOut() {
if volume > 0.1 {
// Fade
volume -= 0.1
perform(#selector(fadeOut), with: nil, afterDelay: 0.1)
} else {
// Stop and get the sound ready for playing again
stop()
prepareToPlay()
volume = 1
}
}
}
J'ai écrit une classe d'aide en Swift pour le fondu AvAudioPlayer
in and out. Vous pouvez utiliser la fonction de volume logarithmique pour un effet de fondu plus progressif.
let player = AVAudioPlayer(contentsOfURL: soundURL, error: nil)
let fader = iiFaderForAvAudioPlayer(player: player)
fader.fadeIn()
fader.fadeOut()
Voici une application de démonstration: https://github.com/evgenyneu/sound-fader-ios
Cela me semble être une descente d'utilisation d'un NSOperationQueue.
Voici donc ma solution:
-(void) fadeIn
{
if (self.currentPlayer.volume >= 1.0f) return;
else {
self.currentPlayer.volume+=0.10;
__weak typeof (self) weakSelf = self;
[NSThread sleepForTimeInterval:0.2f];
[self.fadingQueue addOperationWithBlock:^{
NSLog(@"fading in %.2f", self.currentPlayer.volume);
[weakSelf fadeIn];
}];
}
}
-(void) fadeOut
{
if (self.currentPlayer.volume <= 0.0f) return;
else {
self.currentPlayer.volume -=0.1;
__weak typeof (self) weakSelf = self;
[NSThread sleepForTimeInterval:0.2f];
[self.fadingQueue addOperationWithBlock:^{
NSLog(@"fading out %.2f", self.currentPlayer.volume);
[weakSelf fadeOut];
}];
}
}
Swift solution:
La réponse la mieux notée ici est excellente mais elle donne un effet de bégaiement car le pas de volume de 0,1 est trop. L'utilisation de 0,01 donne un effet de fondu plus doux à entendre.
En utilisant ce code, vous pouvez spécifier combien de temps vous voulez que la transition de fondu dure.
let fadeVolumeStep: Float = 0.01
let fadeTime = 0.5 // Fade time in seconds
var fadeVolumeStepTime: Double {
return fadeTime / Double(1.0 / fadeVolumeStep)
}
func fadeOut() {
guard let player = self.player else {
return
}
if !player.playing { return }
func fadeOutPlayer() {
if player.volume > fadeVolumeStep {
player.volume -= fadeVolumeStep
delay(time: fadeVolumeStepTime, closure: {
fadeOutPlayer()
})
} else {
player.stop()
player.currentTime = 0
player.prepareToPlay()
}
}
fadeOutPlayer()
}
func fadeIn() {
guard let player = self.player else {
return
}
if player.playing { return }
player.volume = 0
player.play()
func fadeInPlayer() {
if player.volume <= 1 - fadeVolumeStep {
player.volume += fadeVolumeStep
delay(time: fadeVolumeStepTime, closure: {
fadeInPlayer()
})
} else {
player.volume = 1
}
}
fadeInPlayer()
}
func delay(time delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
vous pouvez régler l'heure en utilisant la constante fadeTime
.
Que diriez-vous de ceci: (si le temps écoulé est négatif, atténuez le son, sinon fondu)
- (void) fadeInOutVolumeOverTime: (NSNumber *)time
{
#define fade_out_steps 0.1
float theVolume = player.volume;
NSTimeInterval theTime = [time doubleValue];
int sign = (theTime >= 0) ? 1 : -1;
// before we call this, if we are fading out, we save the volume
// so that we can restore back to that level in the fade in
if ((sign == 1) &&
((theVolume >= savedVolume) ||
(theTime == 0))) {
player.volume = savedVolume;
}
else if ((sign == -1) && (theVolume <= 0)) {
NSLog(@"fading");
[player pause];
[self performSelector:@selector(fadeInOutVolumeOverTime:) withObject:[NSNumber numberWithDouble:0] afterDelay:1.0];
}
else {
theTime *= fade_out_steps;
player.volume = theVolume + fade_out_steps * sign;
[self performSelector:@selector(fadeInOutVolumeOverTime:) withObject:time afterDelay:fabs(theTime)];
}
}
Dans Objective-C, essayez ceci:
NSURL *trackURL = [[NSURL alloc]initFileURLWithPath:@"path to audio track"];
// fade duration in seconds
NSTimeInterval fadeDuration = 0.3;
// duration of the audio track in seconds
NSTimeInterval audioTrackDuration = 5.0;
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:trackURL error:nil];
[audioPlayer play];
// first we set the volume to 1 - highest
[audioPlayer setVolume:1.0];
// then to 0 - lowest
[musicAudioPlayer setVolume:0 fadeDuration:audioTrackDuration - fadeDuration];
ne très bonne solution à ce problème est disponible auprès de Nick Lockwood à objc.io. Il utilise Core Animation pour modifier les volumes de temps, ce qui peut être utile si l'on a besoin de synchroniser les animations de l'interface utilisateur avec les changements de volume audio, y compris les animations interactives.