6

If I wish to change the opacity of a <div> irregularly over time, I can use CSS animation and @keyframes:

.myDiv {
  animation: myAnimation 10s linear;
}

@keyframes myAnimation {

    0% {opacity: 1;}
   80% {opacity: 1;}
   81% {opacity: 0.99;}
  100% {opacity: 0;}

}

}

But what if I want to change the volume of an <audio class="my-audio"> element irregularly over time in an identical manner?

I know I can begin with:

var myAudio = document.getElementsByClassName('my-audio')[0];
myAudio.volume = 1.0;

But how can I change the volume to 0.99, when 81% of the sound clip has played and fade uniformly down to a volume to 0.0, when 100% of the sound clip has played?

Is there a javascript syntax for this kind of aural transition or am I looking for something which doesn't exist (yet)?

Rounin
  • 27,134
  • 9
  • 83
  • 108
  • You can implement one yourself using `setTimeout` or `setInterval`. – ibrahim mahrir Nov 29 '17 at 20:19
  • This can't be done with CSS because `volume` is an object property, not a CSS property. It can be done via JavaScript using `setTimeout()` and you will have to do the math yourself to decide how high the volume should be and when. – Scott Marcus Nov 29 '17 at 20:21
  • 1
    https://stackoverflow.com/questions/7451508/html5-audio-playback-with-fade-in-and-fade-out – sinisake Nov 29 '17 at 20:22
  • You can get the `currentTime` property of you audio element and there is a timeupdate event. Together with the `duration` property you can do the math you need and on the right timing start setting lower values to the `volume` property. – DSCH Nov 29 '17 at 20:29
  • I would create a new audio file for that. – Walle Cyril Nov 29 '17 at 20:59

3 Answers3

1

I guarantee there is a better way of doing this, both performance wise and simplicity. However, here is my attempt of solving this case if I understood your question correct:

var myAudio = document.getElementsByClassName('my-audio')[0];

myAudio.onloadedmetadata = function() {
  myAudio.ontimeupdate = function() {
    fadeVolume(kFrame)
  };
};

var kFrame = {
  0: 0,
  10: .10,
  20: .20,
  30: .30,
  40: .40,
  50: .50,
  60: 0
}

function fadeVolume(settings) {
  if (settings) {
    var keys = Object.keys(settings);
    var duration = myAudio.duration;
    var currentTime = myAudio.currentTime;
    var percentage = (currentTime / duration) * 100;

    keys.forEach(key => {
      if (key <= percentage) {
        myAudio.volume = settings[key];
      }
    })
  }
}
<audio class="my-audio" controls>
  <source src="https://www.w3schools.com/tags/mov_bbb.mp4" type="audio/ogg">
</audio>
Sølve T.
  • 4,159
  • 1
  • 20
  • 31
  • I like your approach here, @Sølve - using an `object` (or an `array`) gives great control over raising and lowering the volume at different stages in the audio. – Rounin Dec 01 '17 at 16:56
1

Here's what I came up with in the end:

  1. The script determines the duration of the audio element, after which it calculates the point during the audio at which the fade needs to start.
  2. A setInterval commences which monitors how much of the audio has played by repeatedly checking the currentTime property.
  3. At the start-point of the fade a setInterval commences which repeatedly reduces the volume of the audio, until it reaches zero.

Working Example:

var myAudio = document.getElementsByTagName('audio')[0];
var myAudioVolume = 1.0;
var myAudioDuration;
var myAudioPlayInterval;
var myAudioCheckInterval;
var myAudioDuration1Percent;
var myAudioFadeStart = 30; // The percentage of playback before the fade starts


setTimeout(function(){
    myAudioDuration = myAudio.duration;
    myAudioDuration1Percent = (myAudio.duration / 100); 
}, 200);


function myAudioFadeVolume() {

    myAudioFader = setInterval(function(){
        myAudioVolume = myAudio.volume;

        myAudioVolume = myAudioVolume - (1 / myAudioFadeStart);
        console.log('myAudio volume is now ' + myAudioVolume);
        myAudio.volume = myAudioVolume;

        if (myAudioVolume < 0.05) {
            
            myAudio.volume = 0;
            clearInterval(myAudioFader);
        }

    }, (myAudioDuration1Percent * 1000));
}

function myAudioFindFadeStart() {

    myAudioCheckInterval = setInterval(function(){

        if (myAudio.currentTime > (myAudioDuration1Percent * myAudioFadeStart)) {

            myAudioFadeVolume();
            clearInterval(myAudioCheckInterval);
        }
    }, 10);
}

setTimeout(function(){
    myAudio.play();
    myAudioFindFadeStart();
}, 1500);
<audio class="my-audio">
  <source src="https://www.w3schools.com/tags/mov_bbb.mp4" type="audio/ogg">
</audio>
Rounin
  • 27,134
  • 9
  • 83
  • 108
1

Here is my solution:

audio = new Audio(`example.ogg`);
for (let i = 0; i < 10; i ++) {
   setTimeout(() => {
      audio.volume = 1 - i * 0.1;
      if (i === 9) audio.pause();
   }, i * 200); // 2 sec; change this number to raise or lower the duration
}
lendoo
  • 332
  • 4
  • 5