Conditional Audio Playback

Hey everyone,

I’m looking for tips on how to make my Anki cards better, especially those that focus on synonyms.
A few years ago, I broke the “minimum information principle”, because I was struggling with nearly perfect synonyms where the choice depends on the speaker’s preference. If I had only learned one word, I wouldn’t understand someone using the other.

Here’s what I do for synonyms:

  • I change the note type from Basic (and reversed) to just Basic.
  • On the front, I keep the main word as it is.
  • On the back, I add the synonyms in this format:
    Word → Synonym_1, Synonym_2

To make sure I recognize the synonyms independently, I also created two extra cards:

  • Synonym_1 → Word
  • Synonym_2 → Word

Additionally, I’ve added a “ding” sound to the card template to signal that I’m dealing with synonyms and need to recall two or more words. The sound is played using this code:
<div class="nobutton">[sound:_ding.ogg]</div>

However, this method has some drawbacks.
The “ding” sound plays unnecessarily about 66% of the time, or even more if there are additional synonyms, which has led me to become desensitized to it over time - I barely notice it anymore.
I rely on audio recordings for both the front and back fields and don’t look at the screen while studying. (I can’t apply any visual clues)

Here’s my question:
Is there a way to make the audio (the “ding” sound) play only if there are more than two audio tags in the back field? Maybe through a JavaScript solution or something similar?

I’d prefer not to create another note type because my note types have become a chaotic mess over the years, and managing them is overwhelming. I’d rather stick with my current setup unless there’s no other option.

Thanks in advance for your help!

If all your notes meet the following conditions:

  • Synonym_1 and Synonym_2 either contain a single audio file (with or without text) or are completely empty.
  • There’s no situation where Synonym_1 is empty but Synonym_2 contains something.

Then the following should work:

{{#Synonym_2}}
    <div class="nobutton">[sound:_ding.ogg]</div>
{{/Synonym_2}}

Don’t tell anybody, but I do that too! Language learners gotta do what we gotta do!

Do you have each of the synonyms and audio files in separate fields? If not, that will definitely make it easier to work this out.

Hey

Thanks so much for the suggestions and help so far!
I’ve been trying hard to implement your solutions, but I keep hitting a wall.
Let me give you a real-world example to explain my setup better:

In my decks, I have multiple fields. Two of the relevant ones are:

  • Back: (Field)
acabar<br>
terminar
  • B_Audio: (Field)
[sound:acabar_spnsh.ogg]<br>
[sound:terminar_spnsh.ogg]

In this case, I’d like to hear the “ding” sound since there are multiple synonyms.

Now, for an imaginary sibling card example:

  • Back: (Field)
    finish
  • B_Audio: (Field)
    [sound:finish_spnsh.ogg]

In this case, there’s only one word, so the “ding” sound shouldn’t play.

I don’t have separate fields for each synonym because there can be an arbitrary number of synonyms on a single card. Instead, they’re grouped together in the same field, which I imagine complicates things a bit.

Thanks for any help or tips! I really appreciate it!

1 Like

If your B_Audio field always looks like described above (having a [sound:*.*] followed by <br> followed by [sound:*.*]) then you could probably use js to achieve your goal. Something in the lines of the following would work:

<script>
	// use multiline string
	var audio_field = `
	{{B_Audio}};
	`
	// count how many <br> there are. Also see: https://stackoverflow.com/a/4009768
	var count = (audio_field.match(/<br>/g) || []).length;
	
	// if there is more than one count, then there must be a synonym
	if (count > 1) {
		console.log("play ding");
		
		var audio = new Audio('_ding.mp3')
		audio.play();
	}
</script>
1 Like

Thanks for your response and for providing the script!

I gave it a shot, but unfortunately, it didn’t work for me.
I already have a JavaScript section on my front template, so I was wondering if I can have multiple tags, or I need to merge them?
Just to rule things out, I tried removing the other JavaScript I had, but it still didn’t work. (=there was no “ding” sound)

I did notice one thing though: this section “if there is more than one count, then there must be a synonym” isn’t true for my setup. In my case, if there’s even a single <br> tag in the B_Audio field, it means there’s a synonym. (as I don’t use line breaks if there’s just a single sound.)

For testing, I didn’t modify your script at all and picked a card with three synonyms (so two line breaks in B_Audio) to ensure it should trigger the ding sound. However, nothing played.

I’m not a JavaScript expert—far from it—but if I understood your script correctly, I should remove [sound:_ding.ogg] from my template in order for it to work. I assumed the script itself would handle playing the sound without needing additional audio tags.
If that’s not how it’s supposed to work, let me know! :slightly_smiling_face:

I can confirm that the {{B_Audio}} field is valid (no typo) and it sometimes has <br> tags.

1 Like

I assumed you have a file named _ding.mp3 in your collection folder. If not, then rename the part accordingly. In your case you seem to have an .ogg instead of a .mp3, so you have to change the file accordingly.

My bad. In that case it should be if (count > 0).

You can have mutliple tags and you can also merge them, you decide.

Yes, it does once you reference the correct file as stated above.

1 Like

Thanks so much for your reply and clarifications!

It was pure inattention on my part that I didn’t notice the wrong file extension. :person_facepalming:t2:
I was so focused on the logic of the equation - thinking about how to make it “equals and greater to 1” - that I couldn’t think outside the box and realize that “greater than 0” is essentially the same thing. That’s on me!

I’ve now marked your reply as the solution because it does work quite well, and I really like it. I’m super grateful for your help! :blush:

I do have one follow-up question, though: is it possible to make the ding sound play in sequence with the other audio files, rather than simultaneously? My original template had the ding sound play first, followed by the question audio, but now they overlap.

If this is possible but too complex to implement, I’m happy to leave things as they are now. I can still hear the question, and worst case, I can always press the replay button if needed.
I am already impressed and thankful for your method - it’s a huge improvement!

2 Likes

Right, I didn’t even consider this.

It is possible, but not with the way ankis [sound:some_file.mp3] tags work (or if it is, then I couldn’t figure it out).

You can try the following code, which should work as intended. It will include different looking audio players though. You could hide them or style them back, I think Danika_Dakika experimented in another thread with that.

Here’s the code:

{{Front}}

<div id="audio_container">
</div>

<script>
	// Stores custom sound in format "[custom_sound:A.mp3]<br>[custom_sound:B.mp3]"
	var audio_field = "{{B_Audio}}";
	// Count how many <br> there are. If at least one is found, then the
	// card also has synonyms. Also see: https://stackoverflow.com/a/4009768
	var count = (audio_field.match(/<br>/g) || []).length;
	// the name of the audio file, which should be played when a synonym is found.
	var audio_synonym = "_ding.ogg";
	// Tmp variable. Stores the sound in an array to make processing it easier.
	var sounds_array;
	// stores the name of all audio names found in audio_field. If there is
	// a synonym, then audio_synonym is prepended.
	var sounds_src_array = [];
	
	
	// At least one "<br>" found --> there is a synonym
	if (count > 0) {
		sounds_src_array.push(audio_synonym);
	}
	
	
	// Loop over all sound tags. This finds the names of all the other audio files
	// and stores them in another array.
	sounds_array = audio_field.split("<br>");
	for (var i = 0; i < sounds_array.length; i++) {
		sounds_src_array.push(sounds_array[i].replace(/\[custom_sound:(.+\..+)\]/g, '$1'));
	}
	
	
	// finally play all the audio files.
	
	// contains all audio elements
	var audio_container = document.getElementById("audio_container");
	
	// used to get the amount of audio sources that should be played
	audio_playback_elements = []
	
	for (var i = 0; i < sounds_src_array.length; i++) {
		// creates audio element
		var audio_playback_element = document.createElement("audio");
		// sets id for audio element
		audio_playback_element.setAttribute("id", "myAudio" + i);
		// adds audio element to audio container
		audio_container.appendChild(audio_playback_element);
		// set values for the audio element
		audio_playback_element.controls = true;
		audio_playback_element.src = sounds_src_array[i];
		audio_playback_element.defaultMuted = false;
		audio_playback_element.muted = false;
		audio_playback_element.loop = false;
		
		// add element to elements
		audio_playback_elements.push(audio_playback_element);
	}
	
	// the code below will automatically play the next audio, until every audio
	// had been played.
	var index = -1;
	
	function play_audio() {
		if (index < audio_playback_elements.length) {
			index++;
			audio_playback_elements[index].addEventListener("ended", play_audio);
			audio_playback_elements[index].play();
		}
		
		return;
	}
	
	play_audio();
</script>

It will only work if you change every [sound:some_file.mp3] in your field to [custom_sound:some_file.mp3], so you’d have to change that first.

Edit: One thing you should certainly test is if the audio files are included during sync / in backups. If not, maybe prepending a _ to the audio file name already solves it.

Thanks again for all your help!
I just synced my deck and tested it on multiple AnkiDroid devices, but unfortunately, it’s not working as intended.

When there are synonyms (multiple audio tags in the B_Audio field), AnkiDroid ends up playing the B_Audio field instead of _ding.ogg. The ding sound doesn’t even play - it just plays the first audio file in the B_Audio field (not both).
It’s not working correctly during the question phase.

This is the code I’ve been using:

<script>
	// use multiline string
	var audio_field = `
	{{B_Audio}};
	`
	// count how many <br> there are. Also see: https://stackoverflow.com/a/4009768
	var count = (audio_field.match(/<br>/g) || []).length;
	
	// if there is more than one count, then there must be a synonym
	if (count > 0) {
		console.log("play ding");
		
		var audio = new Audio('_ding.ogg')
		audio.play();
	}
</script>

This works well on Anki Desktop (I don’t own AnkiMobile, so I can’t test compatibility there), but AnkiDroid seems to handle it differently. Do you know what might be causing this interference?

I also tried your latest script with “custom_sound”, but sadly, that didn’t work either.
It even collapsed my card templates.

I really appreciate all the effort you’ve put into this, but I think we should abandon this second attempt. It’s clearly very complex, and I don’t want to take up so much of your time, especially when the result is so minute.
I’m already super grateful for what you’ve done, and your solution has helped me on Anki Desktop! I wish we could figure out what’s wrong with AnkiDroid. :face_with_diagonal_mouth:

I’m back with an update. I’m not sure what happened.
Yesterday, everything seemed to work properly.
I didn’t actually study the cards, I just tested them in Anki’s browser preview.

Today, I started learning on multiple versions of AnkiDroid.
Just to move forward with my studies, I tried reviewing the cards I had buried earlier, because AnkiDroid revealed the backside audio during the question phase, on Anki Desktop. When I unburied those cards, I noticed a similar issue.

  • On Anki Desktop, it plays everything: the front audio, the ding sound, and all the backside audio during the question phase.
  • On AnkiDroid, it only plays the front audio and the first audio file from the B_Audio field during the question phase, skipping the ding sound entirely.

Looking back, I’m not even sure if the behavior on Anki Desktop wasn’t the same yesterday. I probably didn’t notice it because “Back Side Only” was checked in the preview mode while I was testing.

That’s odd. I only use anki on desktop though and have no experience with how AnkiDroid handles audio. Maybe AnkiDroid can’t play audio this way?

Interesting. It works fine for me on desktop. Just to make sure: What do you mean with “it collapsed”?

With one of my scripts or without them?

Yes, the first script played everything simultaneasly. I fixed that in the second script. If you do not want to play every audio in succession at all, then that should be possible to achieve as well.
Edit: I think I misunderstood you here. I thought you wanted to play all those audio files and have the ding right before playing them. Do you mean you just want to play the ding sound when you see the question and on the backside you have the audio for synonyms that anki should handle itself?

I’m guessing this is with the first script. Maybe AnkiDroid stops / mutes audio if multiple audio files are triggered.

Maybe someone over here knows how to get something like this working on AnkiDroid. I unfortunately cannot experiement with it outside of anki desktop.

I realized I made a mistake; I didn’t notice that you had provided a full template, not just the script. The first “collapse” issue I mentioned was resolved by removing the extraneous {{Front}} tag, so that part’s sorted.

However, the sounds are still mixed up, and I think there’s a misunderstanding we should clear up. I also renamed my audio tag to [custom_sound:good_evening.ogg] (as per your suggestion), but it wasn’t played, it was only displayed. For these reasons, I prefer the first, simpler approach. It doesn’t require replacing tags, it’s easier to maintain, and it’s less likely to break across different devices. Plus, as I mentioned, I can’t really code or interpret complex scripts, so simpler is better for me! :slightly_smiling_face:

The issue with the shorter version is that it plays everything, including the backside audio, which obviously isn’t ideal. I’ve been looking at the code, but I still can’t figure out what causes this.

Here’s an example of what I expect versus what happens:

  • Front side: I have a word (e.g., [sound:front_audio.ogg])
  • Back side: I have two synonyms with audio:
[sound:back_audio_1.ogg]<br>
[sound:back_audio_2.ogg]
  1. I should hear the ding sound (_ding.ogg) because there are two synonyms on the backside. (The ding tells me I need to recall multiple answers.)
  2. I shouldn’t hear any audio from the backside until I hit “Show Answer.

Instead of this,
I hear [sound:front_audio.ogg] simultaneously with _ding.ogg (which I can get used to).
I also hear [sound:back_audio_1.ogg] and [sound:back_audio_2.ogg] even though the backside hasn’t been revealed yet. This makes it less of a challenge since I’m hearing the answers before I have a chance to recall them.
When I hit “Show Answer,” the _ding.ogg sound plays again, which is unnecessary, and then the two backside audios play as expected. (_ding.ogg is on the front side)

1 Like

@CrayonLearn The [sound] tag seems to cause the audio to play even when inserted in a template literal in a Javascript script. You could try to store in the B_Audio field only the names of the audio files, without the [sound] tag, and then use Javascript to play them.

1 Like

Thanks for your detailed description, it really helps!

The issue is that I need to read the back audio field for the script to work. But if I do something like this:

var audio_field = `
{{Back}};
`

then anki will substitute the {{Back}} field automatically with

<a class="replay-button soundLink" href=# onclick="pycmd('play:q:2'); return false;">
    <svg class="playImage" viewBox="0 0 64 64" version="1.1">
        <circle cx="32" cy="32" r="29" />
        <path d="M56.502,32.301l-37.502,20.101l0.329,-40.804l37.173,20.703Z" />
    </svg>
</a>;

And this triggers the autoplay. Notice that the pycmd('play:q:2'); part changes with every audio added. It starts with 0 and increments +1 for every audio that will be autoplayed.

Ideally you have some other field that doesn’t include audio, but still shows us if there are synonyms. That would solve this problem.

If it was possible to just get the text you see in your field (e.g. [sound:t.mp3]) then this task would be pretty trivial. But anki changes the [sound:t.mp3] behind the scenes for you, so that audio actually works. Also see this:

So I guess my follow up question is: Do you have a field that doesn’t include audio that we could read to know if there are synonyms? Or would it be okay to create one? It doesn’t need to be displayed on the card and could include a single letter (y for yes, n for no).

1 Like

I was thinking something like:

<script>
  if (`{{B_Audio}}`.split("<br>").length > 1) {
    new Audio("_ding.ogg").play();
}
</script>

in the front, and:

<script>
(async function () {
  function playAudio(path) {
    return new Promise(async (resolve) => {
      const audio = new Audio(path);
      await audio.play();
      setTimeout(resolve, audio.duration * 1000);
    });
  }
  for (const audioFile of `{{B_Audio}}`.split("<br>")) {
    await playAudio(audioFile);
  }
})();
</script>

in the back

The fact that {{B_Audio}} even exists on the front triggers the autoplay already, due to the substitution I mentioned above. So this doesn’t work either, unfortunately.

1 Like

You’re correct, but if the OP only stores the name of the files, without the [sound] tag and one per line, it should work

1 Like

My solution causes a big problem, though: if you don’t use the [sound] tag, Anki will think the audio file is not necessary when you use the Check Media tool. In addition, I’ve just remembered that it’s possible to disable the autoplay from the deck options screen (Gear Icon at the end of the deck’s row in the Decks screen → OptionsAudio → check Don't play audio automatically)

1 Like

Good points. In that case a simple field like I proposed above (a yes or no field) would be the best way to continue with this. That way the only autoplay that cannot be disabled from the settings is from the bing sound and anki will recognize the media.