Is it possible to load audio dynamically with JavaScript?

I have notes where for each note there are two audio files. I want to randomly choose one file and automatically play it using JS. Unfortunately, I haven’t been able to do so.

This is the first thing I tried:

let number = Math.floor(Math.random() * 2);
document.getElementById('question_audio').innerHTML =  (number == 0 ? `{{FirstAudio}}` : `{{SecondAudio}}`) ;

The example above results in two files playing regardless.

Then I tried to put both files to the back side of the card and triggering one of them ilke this:

let index = Math.floor(Math.random() * 2) ;
pycmd(`play:a:${index}`);

The example above kinda works but it doesn’t let me replay the audio because there’s no replay button, and on top of that it doesn’t work on Android.

So far I’m lost. If anyone knows how to do this, please respond.

1 Like
  1. You will want to go to the deck options, and uncheck ‘Automatically play audio’.
  2. Use the following script for the front:
let number = Math.floor(Math.random() * 2);
document.getElementById('question_audio').innerHTML =  (number == 0 ? `{{FirstAudio}}` : `{{SecondAudio}}`) ;
pycmd(`play:q:${number}`);

 
The way Anki’s sound playing mechanism works, is it first replaces all the {{Field}} with its values, then just looks for audio/video tags with a regex search in the resulting html. Because it finds both [sound:xx.mp3] in your html (inside the script tag), Anki thinks both files are displayed on the card and auto plays them all.

Thanks! Is it possible to avoid changing Deck Options settings? I’m going to share the deck online so having to tell each user to tweak their settings is not going to be the best way.
I could bundle the deck options with the deck itself (thankfully anki can do it) but it will prevent me from sharing the deck on AnkiWeb because as far as I know decks downloaded from AnkiWeb can not contain any additional settings.
Plus, the pycmd hack doesn’t work on Android.

Never used audio cards, but I find the way sound files are handled in Anki a bit weird. It gives the user almost no control. Why do it with Python when it could be done with HTML too?

Basic example

<audio src="sound.mp3" autoplay></audio>

That approach requires more work on the template side, but looking at the struggle here, I think it might be worth considering :thinking:


For reference: <audio>: The Embed Audio element - HTML: HyperText Markup Language | MDN

3 Likes

I think that’s your best bet here. You should be able to replace bunch of [sound:audio.mp3] to <audio src="audio.mp3" autoplay></audio> in the browser using ‘Find and Replace’ with regex.

You’ll need to have the entire tag in the field instead of doing a template like <audio src="{{audio}}> so the Check Media doesn’t delete your files.

3 Likes

It is true that HTMLAudioElement and HTMLVideoElement are more suitable for javascript control than [sound:...], but just like [sound:...], whether they can autoplay or not depends on the deck options. If the Don't play audio automatically option is enabled, autoplay attribute of <audio>/<video> will be ignored, and play() method of HTMLMediaElement will not work until the user interacts with the page, for example by clicking somewhere on the page.

See https://github.com/ankitects/anki/issues/1120#issuecomment-819204963

5 Likes

The <audio src="sound.mp3" autoplay></audio> solution works pretty well.

I want to add that with this method when you press “r” the audio won’t replay. To work around this, subscribing to keyup event should work.

document.onkeyup = (e) => {
    if (e.key == 'r') {
        document.getElementById('question_audio').play()
    }
};
1 Like

Hey there! I just wanted to check that I understand correctly: At the moment there is no easy (or difficult) method to access the audio file through javascript, is that correct?
One thing that I thought could be useful, esp. for learning things such as bird calls, is to visualise the sound. Which would be possible with various javascript packages. Just a thought :slight_smile:

You can access any file (including audio) inside collection.media, so long as you got its file name.

This has been demonstrated in this thread with the HTML <audio> tag. You can also use Audio() - Web APIs | MDN.

What is somewhat difficult however, is adding audio files in a non-standard way that doesn’t require a find & replace action. One would need to write a small add-on for that, I think.

3 Likes

To play all audio tags in loop

<script>
;(async function() {
  // AnkiMobile && AnkiDroid / Anki 2.1
  const elements = Array.from(document.querySelectorAll(".soundLink, .replacebutton"))
  const wait = t => new Promise(s => setTimeout(s, t))
  let index = 0
  do {
    const element = elements[index]
    element.click()
    await wait(2500)
    index++
    if (elems.length >= index) { index = 0 }
  } while(true)
})();
</script>

Not sure if relevant, but you can use controls to add controller to audio. Wanted to mention in case someone else finds useful.

<audio controls>
<source src="audio.mp3">
</audio>

Other variations

<audio controls autoplay muted>
1 Like