Syncing the playing of audio files - html

I wish to play a list of words. Each word is represented by audio recordings of that word in three different dialects. All three audio files should start playing simultaneously, for each word. The code below works most of the time, but sometimes the three audio files will play out of sync for a given word. Why is this and how can I fix it?
UPDATE:
I think the issue is that the audio has to be fetched and stored on the client's machine in some sort of cache. This is my conclusion at least since I notice that problems are more prevalent when new words are added to the playlist. Can I cache all the audio for all words before I start playing?
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Player</title>
<script type='text/javascript'>//<![CDATA[
window.onload=function(){
var _dialects = ["c","m","u"];
var _playlist =
[
"ta_x",
"me_x",
"ag",
"rith",
"agus",
"ag",
"ceol",
];
var _players = new Array();
for (var i=0;i<_dialects.length;i++)
{
_players[i] = document.getElementById("audio_player_"+(i+1));
}
var _number_of_available_players = _dialects.length;
var _playlist_index = 0;
var _play_button = document.getElementById("play_button");
function play()
{
_playlist_index = 0;
_play_button.disabled = true;
next();
}
function next()
{
if(_number_of_available_players == _dialects.length)
{
if(_playlist_index < _playlist.length)
{
for (var i=0;i<_players.length;i++)
{
if (_players[i].canPlayType('audio/mpeg;'))
{
_players[i].src = "http://www.focloir.ie/media/ei/sounds/" + _playlist[_playlist_index] + "_"+_dialects[i]+".mp3";
}
else
{
_players[i].src = "http://www.focloir.ie/media/ei/sounds_ogg/" + _playlist[_playlist_index] + "_"+_dialects[i]+".ogg";
}
_players[i].play();
}
_playlist_index = _playlist_index + 1;
_number_of_available_players = 0;
}
else
{
_play_button.disabled = false;
}
}
}
function end()
{
_number_of_available_players = _number_of_available_players + 1
next();
}
for (var i=0;i<_players.length;i++)
{
_players[i].addEventListener('ended', end);
}
_play_button.addEventListener('click', play);
}//]]>
</script>
</head>
<body>
<audio id = "audio_player_1"></audio>
<audio id = "audio_player_2"></audio>
<audio id = "audio_player_3"></audio>
<button id = "play_button">Play</button>
</body>
</html>

Related

AmCharts4 - Unable to load file - external JSON file produced from SQL database

I'm struggling to open my json arranged data in AmCharts4. In my previous charts I used very simple script (chart.data = ;), which unfortunately does not work this time. So I'm using chart.dataSource.url function proposed by AmCharts documentation. When, I load example file found on web everything works fine, as soon as I switch to my file the chart is not able to load file. I'm not able to find a similar problem on web, therefore I would be very grateful for help.
Here is my example with working url and my not working file.
Thanks in advance:
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="https://www.amcharts.com/lib/4/core.js"></script>
<script src="https://www.amcharts.com/lib/4/charts.js"></script>
<script src="https://www.amcharts.com/lib/4/themes/animated.js"></script>
<style>
</style>
</head>
<body>
<div id="chartdiv"></div>
</body>
</html>
<!-- Styles -->
<style>
#chartdiv {
width: 100%;
height: 500px;
}
</style>
<!-- Resources -->
<script src="https://cdn.amcharts.com/lib/4/core.js"></script>
<script src="https://cdn.amcharts.com/lib/4/charts.js"></script>
<script src="https://cdn.amcharts.com/lib/4/themes/animated.js"></script>
<!-- Chart code -->
<script>
am4core.ready(function() {
// Themes begin
am4core.useTheme(am4themes_animated);
// Themes end
var chart = am4core.create('chartdiv', am4charts.XYChart)
// Modify chart's colors
chart.colors.list = [
am4core.color("#264B29"),
am4core.color("#94B255"),
am4core.color("#456C39"),
am4core.color("#C4D563"),
am4core.color("#698F47"),
am4core.color("#F9F871"),
];
chart.legend = new am4charts.Legend()
chart.legend.position = 'top'
chart.legend.paddingBottom = 20
chart.legend.labels.template.maxWidth = 95
var xAxis = chart.xAxes.push(new am4charts.CategoryAxis())
xAxis.dataFields.category = 'year'
xAxis.renderer.cellStartLocation = 0.1
xAxis.renderer.cellEndLocation = 0.9
xAxis.renderer.grid.template.location = 0;
var yAxis = chart.yAxes.push(new am4charts.ValueAxis());
function createSeries(value, name) {
var series = chart.series.push(new am4charts.ColumnSeries())
series.dataFields.valueY = value
series.dataFields.categoryX = 'year'
series.name = name
series.events.on("hidden", arrangeColumns);
series.events.on("shown", arrangeColumns);
var bullet = series.bullets.push(new am4charts.LabelBullet())
bullet.interactionsEnabled = false
bullet.dy = 30;
bullet.label.text = '{valueY}'
bullet.label.fill = am4core.color('#ffffff')
return series;
}
// Add data
//Working url
//chart.dataSource.url = "https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-160/sample_data_serial.json";
//My SQL produced JSON file is not working
chart.dataSource.url = "data/my-file.php";
chart.dataSource.adapter.add("parsedData", function(data) {
var newData = [];
data.forEach(function(dataItem) {
var newDataItem = {};
Object.keys(dataItem).forEach(function(key) {
if (typeof dataItem[key] === "object") {
newDataItem["_id"] = dataItem[key]["#id"];
dataItem[key]["Column"].forEach(function(dataItem) {
newDataItem[dataItem["#name"]] = dataItem["#id"];
});
} else {
newDataItem[key] = dataItem[key];
}
});
newData.push(newDataItem);
});
data = newData;
return data;
});
createSeries('cars', 'The First');
createSeries('motorcycles', 'The Second');
createSeries('bicycles', 'The Third');
//createSeries('bilanca_lsk_lst', 'T4');
function arrangeColumns() {
var series = chart.series.getIndex(0);
var w = 1 - xAxis.renderer.cellStartLocation - (1 - xAxis.renderer.cellEndLocation);
if (series.dataItems.length > 1) {
var x0 = xAxis.getX(series.dataItems.getIndex(0), "yearX");
var x1 = xAxis.getX(series.dataItems.getIndex(1), "yearX");
var delta = ((x1 - x0) / chart.series.length) * w;
if (am4core.isNumber(delta)) {
var middle = chart.series.length / 2;
var newIndex = 0;
chart.series.each(function(series) {
if (!series.isHidden && !series.isHiding) {
series.dummyData = newIndex;
newIndex++;
}
else {
series.dummyData = chart.series.indexOf(series);
}
})
var visibleCount = newIndex;
var newMiddle = visibleCount / 2;
chart.series.each(function(series) {
var trueIndex = chart.series.indexOf(series);
var newIndex = series.dummyData;
var dx = (newIndex - trueIndex + middle - newMiddle) * delta
series.animate({ property: "dx", to: dx }, series.interpolationDuration, series.interpolationEasing);
series.bulletsContainer.animate({ property: "dx", to: dx }, series.interpolationDuration, series.interpolationEasing);
})
}
}
}
});
// end am4core.ready()
</script>
I found a typing error in my-file.php
Anyhow, after I solved typing issue the chart.dataSource.url function still did not work, but It worked using next php include script.
chart.data = <?php include './data/my-file.php'; ?>;

How to play multiple audio files one by one in html 5?

I would like to play multiple audio files one by one. I have read the same question, but I have found the links to the original answer not working and question unanswered.
Original answer
<audio id="mySound" controls><source src="http://www.hvalur.org/audio/uploaded_files/ds_umkomuleysi_0_0.mp3" type="audio/mpeg"></audio>
<audio id="mySound1" ><source src="http://www.hvalur.org/audio/uploaded_files/ds_ljosmyndavel_0_0.mp3" type="audio/mpeg"></audio>
I want the first sound started by the user action (pressing the play button) and then the following sound(s) playing one by one.
The usage of this example will be practising of understanding of the telephone numbers read by native speaker.
I would do something like that:
var mySound = document.getElementById("mySound");
var mySound1 = document.getElementById("mySound1");
var mySound2 = document.getElementById("mySound2");
var mySound3 = document.getElementById("mySound3");
var mySoundArray = [mySound, mySound1, mySound2, mySound3];
var mySoundArrayPos = 0;
var myButton = document.getElementById("myButton");
myButton.addEventListener("click", function(event) {
mySound.play();
mySoundArrayPos = 0;
startSoundTimer();
});
const startSoundTimer = () => {
var mySoundTimer = setInterval(() => {
if (mySoundArray[mySoundArrayPos].currentTime >= mySoundArray[mySoundArrayPos].duration) {
if (mySoundArrayPos < mySoundArray.length -1) {
mySoundArrayPos += 1;
mySoundArray[mySoundArrayPos].play();
} else {
clearInterval(mySoundTimer);
}
}
}, 10);
};
I don't know if the 10ms are a bit too fast, but I think, it's legit.
Javascript
=============== `playaudio(i) {
var thisme = this;
var audio = new Audio(thisme.model.audioFiles[i].audioUrl);
audio.play();
audio.addEventListener("ended", function () {
i = i + 1;
console.log(i);
if (i < thisme.model.audioFiles.length) {
audio.src = thisme.model.audioFiles[i].audioUrl;
audio.play();
}
}, false);
}
}`
HTML
====`<span class='chunkAudio' (click)="playaudio(0)">
<img class='playbtn' src="{{model.playerImageUrl}}" />
</span>`

How to find classes that are not used in any CSS selector?

Consider the following HTML excerpt from a page:
<style type="text/css">
.existing-class {
background-color: #000;
}
</style>
<div class="existing-class non-existing-class"></div>
It has 2 classes applied. Here is the thing: non-existing-class is not defined anywhere in the CSS available in the page, however div is using it.
My question is: How can a developer programmatically detect elements in the page which are using classes that are not actually defined in the loaded CSS?
Okay, there you go ;)
Take a look at the script I have created, especially getUndefinedClasses function.
function httpGet(theUrl) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", theUrl, false ); // false for synchronous request
xmlHttp.send( null );
return xmlHttp.responseText;
}
function getAllCSSClasses(cssdata) {
var re = /\.(.+)\{/g;
var m;
let classes = [];
do {
m = re.exec(cssdata);
if (m) {
for(let key in m) {
if(
(typeof m[key] == "string") &&
(classes.indexOf(m[key]) == -1) &&
(m[key].indexOf(".") == -1)
)
classes.push(m[key].replace(/\s/g, " "));
}
}
} while (m);
return classes;
}
function getAllClasses() {
var csses = document.querySelectorAll('link[rel="stylesheet"]');
var classes = []
for (i = 0; i < csses.length; ++i) {
// let styledata = httpGet(csses[i].href);
var styledata = ".hi{ display: none; }";
var cclasses = getAllCSSClasses(styledata);
var classes = Object.assign([], classes, cclasses);
classes.concat(cclasses);
}
return classes;
}
function getHTMLUsedClasses() {
var elements = document.getElementsByTagName('*');
var unique = function (list, x) {
if (x != "" && list.indexOf(x) === -1) {
list.push(x);
}
return list;
};
var trim = function (x) { return x.trim(); };
var htmlclasses = [].reduce.call(elements, function (acc, e) {
return e.className.split(' ').map(trim).reduce(unique, acc);
}, []);
return htmlclasses;
}
function getUndefinedClasses(cssclasses, htmlclasses) {
var undefinedclasses = [];
for (let key in htmlclasses) {
if(cssclasses.indexOf(htmlclasses[key]) == -1 ) {
undefinedclasses.push(htmlclasses[key]);
}
}
return undefinedclasses;
}
var cssclasses = getAllClasses();
var htmlclasses = getHTMLUsedClasses();
console.log("Undefined classes : " + getUndefinedClasses(cssclasses, htmlclasses))
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<link rel="stylesheet" href="hi there">
</head>
<body>
<div class="hi"></div>
<div class="there"></div>
<div class="there_thier_333"></div>
</body>
</html>
What is done:
I get all the classnames from the css data, (you can pass the css
data by various means).
Then I get all the classes used in HTML elements, both of these are recorded in arrays.
Finally, I simply push the classes which were used by HTML Elements but not found in the cssclasses array which leaves you with the undefined classes in CSS.
(jsbin here needed)

How can i fix the setInterval part, is that even it?

I'm working on a game named Factory Tycoon and have spotted a bug which I can't resolve myself. Every second you get plus what your items per second is on your items but it is failing to do so. Feel free to test the code. Code:
<!DOCTYPE html>
<html>
<head>
<title>Factory Tycoon</title>
<script type="text/javascript">
var money = 1000;
var items = 0;
var itemsps = 1;
var dropper1Cost = 100;
var dropper1Audio = new Audio('Audio/dropper1Sound.mp3');
function addDropper() {
if (money <= dropper1Cost - 1) {
alert('Not Enough Money.')
}
if (money >= dropper1Cost) {
dropper1Audio.play()
itemsps += 1;
money -= dropper1Cost;
dropper1Cost += 100;
}
}
setInterval(function renderMoney() {
document.getElementById('money').innerHTML = "Money:" + money;
})
setInterval(function renderItemsProcessedPS() {
document.getElementById('items').innerHTML = "Items Processed:" + items;
})
setInterval(function renderItemsProcessedPS() {
document.getElementById('itemsps').innerHTML = "Items Processed Per Second:" + itemsps;
}, 1000)
</script>
</head>
<h4 id="money"></h4>
<h4 id="items"></h4>
<h4 id="itemsps"></h4>
<body>
<img src="Images/dropper1IMG.png" onclick="addDropper()">
</html>
There isn't much code as I've only just started to develop it tonight :).
There is a few issues in the code:
<h4> elements need to be inside the <body> element
the </body> end tag is missing
the setInterval is called before all elements had a chance to load, which will make it error, so I wrapped them in an init function and called that on body load
Side note, this code can be optimized with addEventListener for the load event etc., but here you have a start
<!DOCTYPE html>
<html>
<head>
<title>Factory Tycoon</title>
<style>
span { display: inline-block; padding: 5px; background: #ddd; }
</style>
<script type="text/javascript">
var money = 1000;
var items = 0;
var itemsps = 1;
var dropper1Cost = 100;
var dropper1Audio = new Audio('Audio/dropper1Sound.mp3');
function addDropper() {
if (money <= dropper1Cost - 1) {
alert('Not Enough Money.')
}
if (money >= dropper1Cost) {
dropper1Audio.play()
itemsps += 1;
money -= dropper1Cost;
dropper1Cost += 100;
}
}
function init() {
setInterval(function renderMoney() {
document.getElementById('money').innerHTML = "Money:" + money;
})
setInterval(function renderItemsProcessedPS() {
document.getElementById('items').innerHTML = "Items Processed:" + items;
})
setInterval(function renderItemsProcessedPS() {
document.getElementById('itemsps').innerHTML = "Items Processed Per Second:" + itemsps;
}, 1000)
}
</script>
</head>
<body onload="init();">
<h4 id="money"></h4>
<h4 id="items"></h4>
<h4 id="itemsps"></h4>
<span onclick="addDropper()">Click Me</span>
</body>
</html>

How to get info from background_page to popup?

I'm following the official Chrome Extension tutorial called Chritter where they fetch tweets from Twitter and place them into the extension. I'm trying to do similar except im trying to fetch items from an xml file.
My XML
<xml>
<item>
<title>Title 1</title>
<description>Description 1</description>
<duration>55:00</duration>
<published>28/01/2011</published>
</item>
<item>
<title>Title 2</title>
<description>Description 2</description>
<duration>55:00</duration>
<published>28/01/2011</published>
</item>
</xml>
background.html
<!--
To change this template, choose Tools | Templates
and open the template in the editor.
-->
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript">
var fetchFreq = 30000; // how often we fetch new items (30s)
var req; // request object
var unreadCount = 0; // how many unread items we have
var items; // all currently fetched items
getItems();
//setInterval(getItems, fetchFreq);
function getItems(){
req = new XMLHttpRequest();
req.open("GET", "http://urltoxml.com/xmlfile.xml", false);
req.onload = processItems;
req.send();
}
function processItems(){
xmlDoc = req.responseXML;
items = xmlDoc.getElementsByTagName("item");
unreadCount += items.length;
if (unreadCount > 0) {
chrome.browserAction.setBadgeBackgroundColor({
color: [255, 0, 0, 255]
});
chrome.browserAction.setBadgeText({text: '' + unreadCount});
}
items = xmlDoc.concat(items);
}
</script>
</head>
</html>
I don't know how to get the fetched items from the background.html and displayed onto the popup.html ?
popup.html
<html>
<head>
<link rel="stylesheet" href="popup.css" />
<script src="util.js"></script>
<script>
var bg; // background page
// timeline attributes
var timeline;
var template;
var title;
var link;
var description;
onload = setTimeout(init, 0); // workaround for http://crbug.com/24467
// initialize timeline template
function init() {
chrome.browserAction.setBadgeText({text: ''});
bg = chrome.extension.getBackgroundPage();
bg.unreadCount = 0;
timeline = document.getElementById('timeline');
template = xpath('//ol[#id="template"]/li', document);
title = xpath('//div[#class="text"]/span', title);
content = xpath('//div[#class="text"]/span', template);
update();
}
function update(){
// how to do this ?
// See Chritter example below with JSON,
// except i want to it with xml ?
}
</script>
</head>
<body>
<div id="body">
<ol id="timeline" />
</div>
<ol id="template">
<li>
<div class="text">
<a></a>
<span></span>
</div>
<div class="clear"></div>
</li>
</ol>
</body>
</html>
The way the Chritter extension does it only seems to work with JSON. Here is how they do it:
// update display
function update() {
var user;
var url;
var item;
for (var i in bg.tweets) {
user = bg.tweets[i].user;
url = 'http://twitter.com/' + user.screen_name;
// thumbnail
link.title = user.name;
link.href = openInNewTab(url);
image.src = user.profile_image_url;
image.alt = user.name;
// text
author.href = openInNewTab(url);
author.innerHTML = user.name;
content.innerHTML = linkify(bg.tweets[i].text);
// copy node and update
item = template.cloneNode(true);
timeline.appendChild(item);
}
}
Chritter background.html
<html>
<head>
<script type="text/javascript">
var fetchFreq = 30000; // how often we fetch new tweets (30s)
var req; // request object
var unreadCount = 0; // how many unread tweets we have
var tweets; // all currently fetched tweets
getTweets();
setInterval(getTweets, fetchFreq);
// fetch timeline from server
function getTweets() {
req = new XMLHttpRequest();
req.open('GET', 'http://twitter.com/statuses/public_timeline.json');
req.onload = processTweets;
req.send();
}
// process new batch of tweets
function processTweets() {
var res = JSON.parse(req.responseText);
unreadCount += res.length;
if (unreadCount > 0) {
chrome.browserAction.setBadgeBackgroundColor({
color: [255, 0, 0, 255]
});
chrome.browserAction.setBadgeText({text: '' + unreadCount});
}
tweets = res.concat(tweets);
}
</script>
</head>
</html>
Any help much appreciated! Thanks!
If you want to access items var from a background page then:
var items = chrome.extension.getBackgroundPage().items;
I am not sure what the exact question is, but the general practice is to store the data from background page into localstorage and then access this data from the popup page.
http://www.rajdeepd.com/articles/chrome/localstrg/LocalStorageSample.htm