Tenemos que hacer clic en la tarjeta para reproducir el audio.
Primero coloca los archivos de audio mp3 dentro del directorio www/audio
Como este es un proyecto de demostración, basta con colocar el archivo b.mp3 en esa carpeta.

Método 1‑Reproducción con HTML5
Reproduce el audio mediante HTML5 Audio compatible con WebView, no requiere plugins
Modifica el contenido de main.js de la siguiente forma
// Datos de consonantes del vietnamita
const consonants = [
{char: "b", pronounce: "/ɓ/", desc: "Consonante oclusiva bilabial implosiva"},
{char: "c", pronounce: "/k/", desc: "Consonante oclusiva velar"},
{char: "ch", pronounce: "/tɕ/", desc: "Consonante africada palatal"},
{char: "d", pronounce: "/ɗ/", desc: "Consonante oclusiva alveolar implosiva"},
{char: "đ", pronounce: "/ɗ/", desc: "Consonante implosiva alveolar"},
{char: "g", pronounce: "/ɣ/", desc: "Consonante fricativa velar"},
{char: "gh", pronounce: "/ɣ/", desc: "Se usa antes de e, i"},
{char: "h", pronounce: "/h/", desc: "Consonante fricativa glotal"},
{char: "k", pronounce: "/k/", desc: "Consonante oclusiva velar sorda"},
{char: "kh", pronounce: "/x/", desc: "Consonante fricativa velar sorda"},
{char: "l", pronounce: "/l/", desc: "Consonante lateral alveolar"},
{char: "m", pronounce: "/m/", desc: "Consonante nasal bilabial"},
{char: "n", pronounce: "/n/", desc: "Consonante nasal alveolar"},
{char: "ng", pronounce: "/ŋ/", desc: "Consonante nasal velar"},
{char: "ngh", pronounce: "/ŋ/", desc: "Se usa antes de e,i"},
{char: "nh", pronounce: "/ɲ/", desc: "Consonante nasal palatal"},
{char: "p", pronounce: "/p/", desc: "Consonante oclusiva bilabial no aspirada"},
{char: "ph", pronounce: "/f/", desc: "Consonante fricativa labiodental"},
{char: "q", pronounce: "/kw/", desc: "Siempre va acompañada de u"},
{char: "r", pronounce: "/r/", desc: "Consonante vibrante alveolar"},
{char: "s", pronounce: "/s/", desc: "Consonante fricativa alveolar sorda"},
{char: "t", pronounce: "/t/", desc: "Consonante oclusiva alveolar sorda"},
{char: "th", pronounce: "/θ/", desc: "Consonante fricativa dental"},
{char: "v", pronounce: "/v/", desc: "Consonante fricativa labiodental sonora"},
{char: "x", pronounce: "/s/", desc: "Consonante fricativa alveolar sorda"}
];
let favList = JSON.parse(localStorage.getItem("vn_consonant_fav")) || [];
let currentAudio = null; // Objeto de audio global, para detener la reproducción anterior
const gridEl = document.getElementById("consonant-grid");
const favGridEl = document.getElementById("favorite-grid");
const modal = document.getElementById("modal");
const modalChar = document.getElementById("modal-char");
const modalPro = document.getElementById("modal-pronounce");
const modalDesc = document.getElementById("modal-desc");
const btnFav = document.getElementById("btn-fav");
const closeModal = document.getElementById("close-modal");
let currentItem = null;
// ====================== FUNCIÓN PARA REPRODUCIR MP3 ======================
function playAudio(charName) {
// Detener el audio que se esté reproduciendo
if(currentAudio){
currentAudio.pause();
currentAudio.currentTime = 0;
}
// Crear objeto de audio
currentAudio = new Audio(`audio/${charName}.mp3`);
currentAudio.volume = 1;
currentAudio.play().catch(err=>{
console.log("No se pudo reproducir el archivo: ", charName, err);
alert(`No se encuentra el archivo de audio: ${charName}.mp3`);
});
}
// Renderizar tarjetas de consonantes
function renderCards(){
gridEl.innerHTML = "";
consonants.forEach(item=>{
const div = document.createElement("div");
div.className = "bg-cardBg rounded-lg py-5 text-center card-shadow active:scale-95 transition-transform cursor-pointer";
div.innerHTML = `<span class="text-2xl font-bold text-gray-800">${item.char}</span>`;
div.onclick = ()=> {
playAudio(item.char); // Al pulsar la tarjeta reproduce el mp3
openModal(item);
};
gridEl.appendChild(div);
})
}
// Renderizar página de favoritos
function renderFav(){
favGridEl.innerHTML = "";
if(favList.length === 0){
favGridEl.innerHTML = `<p class="col-span-3 text-center text-gray-400 py-10">Todavía no hay elementos favoritos</p>`;
return;
}
favList.forEach(item=>{
const div = document.createElement("div");
div.className = "bg-cardBg rounded-lg py-5 text-center card-shadow active:scale-95 transition-transform cursor-pointer";
div.innerHTML = `<span class="text-2xl font-bold text-gray-800">${item.char}</span>`;
div.onclick = ()=> {
playAudio(item.char);
openModal(item);
};
favGridEl.appendChild(div);
})
}
// Abrir ventana emergente con detalles
function openModal(item){
currentItem = item;
modalChar.innerText = item.char;
modalPro.innerText = item.pronounce;
modalDesc.innerText = item.desc;
updateFavButton();
modal.classList.remove("hidden");
}
// Cerrar ventana emergente
closeModal.onclick = ()=>{
modal.classList.add("hidden");
}
modal.onclick = (e)=>{
if(e.target === modal) modal.classList.add("hidden");
}
// Botón para agregar / quitar de favoritos
function updateFavButton(){
const exist = favList.find(x=>x.char === currentItem.char);
btnFav.innerText = exist ? "Quitar de favoritos" : "Agregar a favoritos";
}
btnFav.onclick = ()=>{
const idx = favList.findIndex(x=>x.char === currentItem.char);
if(idx >=0) favList.splice(idx,1);
else favList.push(currentItem);
localStorage.setItem("vn_consonant_fav", JSON.stringify(favList));
updateFavButton();
renderFav();
}
// Pestañas para cambiar de página
const tabBtns = document.querySelectorAll(".tab-btn");
const pages = document.querySelectorAll(".page");
tabBtns.forEach(btn=>{
btn.onclick = ()=>{
const targetId = btn.dataset.target;
tabBtns.forEach(b=>{
b.classList.remove("tab-active");
b.querySelector("svg").classList.add("text-gray-500");
})
btn.classList.add("tab-active");
btn.querySelector("svg").classList.remove("text-gray-500");
pages.forEach(p=>p.classList.add("hidden"));
document.getElementById(targetId).classList.remove("hidden");
}
})
document.addEventListener("DOMContentLoaded", ()=>{
renderCards();
renderFav();
})
document.addEventListener("deviceready", ()=>{
console.log("Cordova listo");
}, false);Lenguaje del código: JavaScript (javascript)
Método 2‑Reproducción mediante plugin
Reproducir audio usando el plugin de cordova cordova-plugin-media
Instalar el plugin
cordova plugin add cordova-plugin-media
Salida de ejecución
D:\cordovademo\hello>cordova plugin add cordova-plugin-media
Installing "cordova-plugin-media" for android
Installing "cordova-plugin-file" for android
Subproject Path: CordovaLib
Subproject Path: app
The Android Persistent storage location now defaults to "Internal". Please check this plugin's README to see if your application needs any changes in its config.xml.
If this is a new application no changes are required.
If this is an update to an existing application that did not specify an "AndroidPersistentFileLocation" you may need to add:
"<preference name="AndroidPersistentFileLocation" value="Compatibility" />"
to config.xml in order for the application to find previously stored files.
Adding cordova-plugin-media to package.jsonLenguaje del código: HTML, XML (xml)
Reemplazar la función de reproducción de audio
let mediaPlayer = null;
function playAudio(charName) {
// Detener archivo anterior
if(mediaPlayer){
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
// Importante: Ruta www/audio/ para Cordova Android
const src = `audio/${charName}.mp3`;
mediaPlayer = new Media(src,
()=>{
// Liberar recursos tras finalizar reproducción
mediaPlayer.release();
mediaPlayer = null;
},
(err)=>{
console.error("Error al reproducir audio", err);
}
);
mediaPlayer.play();
}Lenguaje del código: JavaScript (javascript)
Sustituye este código por la función correspondiente dentro del archivo main.js original
// Datos de consonantes del vietnamita
const consonants = [
{char: "b", pronounce: "/ɓ/", desc: "Consonante oclusiva bilabial implosiva"},
{char: "c", pronounce: "/k/", desc: "Consonante oclusiva velar"},
{char: "ch", pronounce: "/tɕ/", desc: "Consonante africada palatal"},
{char: "d", pronounce: "/ɗ/", desc: "Consonante oclusiva alveolar implosiva"},
{char: "đ", pronounce: "/ɗ/", desc: "Consonante implosiva alveolar"},
{char: "g", pronounce: "/ɣ/", desc: "Consonante fricativa velar"},
{char: "gh", pronounce: "/ɣ/", desc: "Se usa antes de e, i"},
{char: "h", pronounce: "/h/", desc: "Consonante fricativa glotal"},
{char: "k", pronounce: "/k/", desc: "Consonante oclusiva velar sorda"},
{char: "kh", pronounce: "/x/", desc: "Consonante fricativa velar sorda"},
{char: "l", pronounce: "/l/", desc: "Consonante lateral alveolar"},
{char: "m", pronounce: "/m/", desc: "Consonante nasal bilabial"},
{char: "n", pronounce: "/n/", desc: "Consonante nasal alveolar"},
{char: "ng", pronounce: "/ŋ/", desc: "Consonante nasal velar"},
{char: "ngh", pronounce: "/ŋ/", desc: "Se usa antes de e,i"},
{char: "nh", pronounce: "/ɲ/", desc: "Consonante nasal palatal"},
{char: "p", pronounce: "/p/", desc: "Consonante oclusiva bilabial no aspirada"},
{char: "ph", pronounce: "/f/", desc: "Consonante fricativa labiodental"},
{char: "q", pronounce: "/kw/", desc: "Siempre va acompañada de u"},
{char: "r", pronounce: "/r/", desc: "Consonante vibrante alveolar"},
{char: "s", pronounce: "/s/", desc: "Consonante fricativa alveolar sorda"},
{char: "t", pronounce: "/t/", desc: "Consonante oclusiva alveolar sorda"},
{char: "th", pronounce: "/θ/", desc: "Consonante fricativa dental"},
{char: "v", pronounce: "/v/", desc: "Consonante fricativa labiodental sonora"},
{char: "x", pronounce: "/s/", desc: "Consonante fricativa alveolar sorda"}
];
let favList = JSON.parse(localStorage.getItem("vn_consonant_fav")) || [];
let currentAudio = null; // Objeto de audio global, para detener la reproducción anterior
const gridEl = document.getElementById("consonant-grid");
const favGridEl = document.getElementById("favorite-grid");
const modal = document.getElementById("modal");
const modalChar = document.getElementById("modal-char");
const modalPro = document.getElementById("modal-pronounce");
const modalDesc = document.getElementById("modal-desc");
const btnFav = document.getElementById("btn-fav");
const closeModal = document.getElementById("close-modal");
let currentItem = null;
// ====================== FUNCIÓN PARA REPRODUCIR MP3 ======================
let mediaPlayer = null;
function playAudio(charName) {
// Detener archivo anterior
if(mediaPlayer){
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
// Importante: Ruta www/audio/ para Cordova Android
const src = `audio/${charName}.mp3`;
mediaPlayer = new Media(src,
()=>{
// Liberar recursos tras finalizar reproducción
mediaPlayer.release();
mediaPlayer = null;
},
(err)=>{
console.error("Error al reproducir audio", err);
}
);
mediaPlayer.play();
}
// Renderizar tarjetas de consonantes
function renderCards(){
gridEl.innerHTML = "";
consonants.forEach(item=>{
const div = document.createElement("div");
div.className = "bg-cardBg rounded-lg py-5 text-center card-shadow active:scale-95 transition-transform cursor-pointer";
div.innerHTML = `<span class="text-2xl font-bold text-gray-800">${item.char}</span>`;
div.onclick = ()=> {
playAudio(item.char); // Al pulsar la tarjeta reproduce el mp3
openModal(item);
};
gridEl.appendChild(div);
})
}
// Renderizar página de favoritos
function renderFav(){
favGridEl.innerHTML = "";
if(favList.length === 0){
favGridEl.innerHTML = `<p class="col-span-3 text-center text-gray-400 py-10">Todavía no hay elementos favoritos</p>`;
return;
}
favList.forEach(item=>{
const div = document.createElement("div");
div.className = "bg-cardBg rounded-lg py-5 text-center card-shadow active:scale-95 transition-transform cursor-pointer";
div.innerHTML = `<span class="text-2xl font-bold text-gray-800">${item.char}</span>`;
div.onclick = ()=> {
playAudio(item.char);
openModal(item);
};
favGridEl.appendChild(div);
})
}
// Abrir ventana emergente con detalles
function openModal(item){
currentItem = item;
modalChar.innerText = item.char;
modalPro.innerText = item.pronounce;
modalDesc.innerText = item.desc;
updateFavButton();
modal.classList.remove("hidden");
}
// Cerrar ventana emergente
closeModal.onclick = ()=>{
modal.classList.add("hidden");
}
modal.onclick = (e)=>{
if(e.target === modal) modal.classList.add("hidden");
}
// Botón para agregar / quitar de favoritos
function updateFavButton(){
const exist = favList.find(x=>x.char === currentItem.char);
btnFav.innerText = exist ? "Quitar de favoritos" : "Agregar a favoritos";
}
btnFav.onclick = ()=>{
const idx = favList.findIndex(x=>x.char === currentItem.char);
if(idx >=0) favList.splice(idx,1);
else favList.push(currentItem);
localStorage.setItem("vn_consonant_fav", JSON.stringify(favList));
updateFavButton();
renderFav();
}
// Pestañas para cambiar de página
const tabBtns = document.querySelectorAll(".tab-btn");
const pages = document.querySelectorAll(".page");
tabBtns.forEach(btn=>{
btn.onclick = ()=>{
const targetId = btn.dataset.target;
tabBtns.forEach(b=>{
b.classList.remove("tab-active");
b.querySelector("svg").classList.add("text-gray-500");
})
btn.classList.add("tab-active");
btn.querySelector("svg").classList.remove("text-gray-500");
pages.forEach(p=>p.classList.add("hidden"));
document.getElementById(targetId).classList.remove("hidden");
}
})
document.addEventListener("DOMContentLoaded", ()=>{
renderCards();
renderFav();
})
document.addEventListener("deviceready", ()=>{
console.log("Cordova listo");
}, false);Lenguaje del código: JavaScript (javascript)
Reproducir el audio