I have an array:
var videoSources = ["0000.mp4", "0015.mp4", "0030.mp4", "0045.mp4", "0100.mp4"];
I would play videos based on current time; mp4 are recorded every 15minutes;
if time = 0044 play 0030.mp4
So I need something like a pattern [0-15] to find and play correct video.
Here function to play video at the moment:
var currentIndex = 0;
// listener function changes src
function myNewSrc() {
var myVideo = document.getElementsByTagName('video')[0];
myVideo.src = videoSources[currentIndex];
myVideo.load();
}
I'd suggest parseing the file name to an integer and then finding if a minute mark is within the range of the file:
var videoSources =
["0000.mp4",
"0015.mp4",
"0030.mp4",
"0045.mp4",
"0100.mp4"]
var getTimestamp = (ts, sources) =>
sources.find(name => {
const startStr = name.split(".")[0]; // e.g.: "0015"
const startMin = parseInt(startStr, 10); // e.g.: 15
const endMin = startMin + 15;
return ts >= startMin && ts < endMin;
});
console.log(getTimestamp(44, videoSources));
If the ranges aren't guaranteed to be 15 minutes, you could do a "look ahead" to find the end of a range. In my current example, I've hardcoded it to be 15 minutes, and expect the file name to be the start of the file.
Edit: to support hours and minutes
var videoSources =
["0000.mp4",
"0015.mp4",
"0030.mp4",
"0045.mp4",
"0100.mp4",
"1145.mp4",
"1230.mp4",
"1245.mp4"]
var getTimestamp = (h, m, sources) =>
sources
.sort()
.find(name => {
const hours = parseInt(name.slice(0, 2), 10);
const minutesStart = parseInt(name.slice(2, 4), 10);
const minutesEnd = minutesStart + 15;
return h <= hours && m >= minutesStart && m < minutesEnd;
});
console.log(getTimestamp(12, 44, videoSources));
You could map your times array and time to numbers and then use findIndex method to return index of correct time element from array.
var videoSources = ["0000.mp4", "0015.mp4", "0030.mp4", "0045.mp4", "0100.mp4"]
const getIndex = (arr, time) => {
time = parseInt(time);
arr = arr.map(e => parseInt(e.split('.')[0]));
return arr.findIndex((e, i) => time >= e && !arr[i + 1] || time < arr[i + 1]);
}
console.log(getIndex(videoSources, "0044"))
console.log(getIndex(videoSources, "0015"))
console.log(getIndex(videoSources, "0099"))
console.log(getIndex(videoSources, "0120"))
Related
I'm trying to calculate the normalized cpu percentage for my node process. My goal is to get it to match the output that I have in my htop for the pid but I'm unable to do so. My code is below along with my htop output.
import { cpuUsage } from "node:process";
import { cpus } from "os";
function basicCpuUsage() {
const startTime = process.hrtime();
const startUsage = cpuUsage();
const numCpus = cpus().length;
const add = 1 + 1; // make cpu do work?
const usageDiff = cpuUsage(startUsage); // get diff time from start
const endTime = process.hrtime(startTime); // total amount of time that has elapsed
const usageMS = (usageDiff.user + usageDiff.system) / 1e3;
const totalMS = endTime[0] * 1e3 + endTime[1] / 1e6;
const cpuPercent = (usageMS / totalMS) * 100;
const normTotal = usageMS / numCpus; // average usage time per cpu
const normPercent = (normTotal / totalMS) * 100;
console.log({
cpuPercent: cpuPercent.toFixed(2),
normPercent: normPercent.toFixed(2),
});
}
process.title = "CPU Test";
const { pid } = process;
console.log({ pid });
const title = setInterval(() => {
basicCpuUsage();
}, 1000);
Here is my output, as you can see my code cpu output does no match my htop cpu output. Which part of my calculation is incorrect? I was thinking this might have to do with my setInterval function call, but not sure. I am attempting to create a long running process where I can view the cpu usage.
Turns out I was making an incorrect calculation. I was dividing my totalTimeMS twice when I should have done it once. Additionally I moved the currentUsage and currentTime to the outside of the function.
Below is the correct calculation:
import { cpus } from "os";
let currentUsage = process.cpuUsage();
let currentTime = process.hrtime();
function basicCpuUsage() {
const numCpus = cpus().length;
const usageDiff = process.cpuUsage(currentUsage); // get diff time from start
const endTime = process.hrtime(currentTime); // total amount of time that has elapsed
currentUsage = process.cpuUsage()
currentTime = process.hrtime();
const usageMS = (usageDiff.user + usageDiff.system) / 1e3;
const totalMS = endTime[0] * 1e3 + endTime[1] / 1e6;
const cpuPercent = (usageMS / totalMS) * 100;
const normPercent = (usageMS / totalMS / numCpus) * 100; // average usage time per cpu
console.log({
cpuPercent: cpuPercent.toFixed(2),
normPercent: normPercent.toFixed(2),
});
}
process.title = "CPU Test";
setInterval(() => {
for (let i = 0; i < 25; i++) {
const add = 1 + 1;
}
basicCpuUsage();
}, 5000);
I have a very simple js git code that doesn't seem to work. It sometimes pushes random commits, but for most of the commits, it doesn't work. Here is the code:
const jsonfile = require("jsonfile");
const crypto = require('crypto');
const fs = require('fs');
const chalk = require("chalk");
const ora = require("ora");
const boxen = require("boxen");
const simpleGit = require("simple-git");
const FILE_PATH = "./data.json";
function daysIntoYear(){
let date = new Date();
return (Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) - Date.UTC(date.getFullYear(), 0, 0)) / 24 / 60 / 60 / 1000;
}
const makeCommit = (DATE, n) => {
if (n === 0) return simpleGit().push();
const data = {
date: DATE,
iter: n
};
d = new Date(DATE);
var curr_date = d.getDate();
var curr_month = d.getMonth() + 1;
var curr_year = d.getFullYear();
// Log for debugging purposes
// console.log(curr_month + "-" + curr_date + "-" + curr_year);
jsonfile.writeFile(FILE_PATH, data, () => {
simpleGit()
.add([FILE_PATH])
.commit(crypto.randomBytes(20).toString('hex'), ['--date=' + d])
.push();
});
makeCommit(DATE, --n)
};
function getRandomCommits () {
let result = crypto.randomInt(0, 101)
if (result <= 30) {
return crypto.randomInt(0, 6)
} else if (result <= 45) {
return crypto.randomInt(0, 14)
} else {
return crypto.randomInt(0, 25)
}
}
function commitsAndContributions () {
let iter = daysIntoYear();
let result = [];
for (let i = 0; i < iter; i++) {
result.push({
date: new Date(new Date().getFullYear(), 0, i),
commits: getRandomCommits()
})
}
result.splice(0, 1)
return result;
}
function startCommits () {
let data = commitsAndContributions();
const spinner = ora("Generating your GitHub activity\n").start();
fs.writeFileSync('./commits.json', JSON.stringify(data, null, 4))
for (let i = 0; i < data.length; i++) {
makeCommit(data[i].date, data[i].commits);
}
spinner.succeed();
console.log(
boxen(
`${chalk.green("Success!")} ${
data.length
} commits have been created.`,
{ borderColor: "yellow", padding: 1, align: "center" }
)
);
}
startCommits()
Data.json is an actual file, commits.json is also a file, and this is linked to a github repo. When I run the script:
jsonfile.writeFile('./data.json', new Date(), () => {
simpleGit()
.add(['./data.json'])
.commit(crypto.randomBytes(20).toString('hex'), ['--date=' + new Date()])
.push();
});
I can see the random commit get pushed. However, it doesn't work when I run the main script. Is there a reason for this?
Edit: As far as I can tell, it pushes one commit, and ignores the rest. Is there some sort of ratelimit going on?
The loop in startCommits runs synchronously (ie: doesn't wait for the commit/push to finish before carrying on to the next iteration). You will therefore likely see a success message before anything more than the first commit has been started.
In this example the makeCommit and startCommits functions are changed to be async with each entry in the data array processed in full before the next one is begun:
const {writeFile} = require('fs/promises');
const makeCommit = async (DATE, n) => {
if (n === 0) return simpleGit().push();
const data = {
date: DATE,
iter: n
};
d = new Date(DATE);
await writeFile(FILE_PATH, 'utf8', JSON.stringify(data));
await simpleGit()
.add([FILE_PATH])
.commit(crypto.randomBytes(20).toString('hex'), ['--date=' + d])
.push();
});
makeCommit(DATE, --n)
};
async function startCommits () {
let data = commitsAndContributions();
const spinner = ora("Generating your GitHub activity\n").start();
fs.writeFileSync('./commits.json', JSON.stringify(data, null, 4))
for (const entry of data) {
await makeCommit(entry.date, entry.commits);
}
spinner.succeed();
}
I have an array of objects. Each object contains a Date attribute.
I need to return an array of objects with date attributes. however, objects that have date attributes that are consecutive days (1 day apart), need to be within their own array.
example input:
const inputArray = [ObjectwDate, ObjectwDate, ObjectwDate, ObjectwDate, ObjectwDate, ObjectwDate, ObjectwDate, ObjectwDate];
example expected output:
// example output
const finalArray = [ObjectwDate, ObjectwDate, [ObjectwDate, ObjectwDate, ObjectwDate], ObjectwDate, [ObjectwDate, ObjectwDate]];
I'm really lost on how to get this to work in all cases. So far I have something similar to the following:
const rangeArray = [];
const finalArray = [];
// sort input array for oldest date
const format = inputArray.sort((a, b) => (
new Date(a.date) - new Date(b.date)
));
format.sort((aDate, bDate) => {
if (differenceInDays(bDate.date, aDate.date) === 1) {
rangeArray.push(aDate, bDate);
}
finalArray .push(bDate);
});
I feel as though this is not the most efficient way to solve my problem. I'm wondering if there is a better way to loop through the array, and put all objects with consecutive dates in their own array.
Any help would be appreciated.
EDIT:
I am using date-fns to calculate the difference in days:
https://date-fns.org/v1.29.0/docs/differenceInDays
After you sor your array, it should be fairly straight foward from there. Iterate through and push consecutives into an array. When you encounter a non-consec, push the current array into an array and start with a new one. If the array that you're pushing has a length of one, just push the item inside. Will provide sample code shortly
UPDATE:
Something along these lines, but still have to do something for the very last value, because there is no i + 1. The code below isn't functional :D
function GroupConsecutiveDates(arrayOfDates)
{
let outerArray = [];
let innerArray = [];
for(let i = 1; i < arrayOfDates.length; i++)
{
let currentDate = arrayOfDates[i];
let nextDate = arrayOfDates[i + 1];
let dateDifference = currentDate - nextDate;
if(dateDifference > 1)
{
switch(innerArray.length)
{
case 0:
break;
case 1:
outerArray.push(innerArray[0]);
default:
outerArray.push(innerArray);
}
innerArray = [currentDate];
continue;
}
innerArray.push(currentDate);
}
function randomDate(start, end) {
return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime()));
}
let dates = [];
for (let i = 0; i < 20; i++)
{
dates.push(randomDate(new Date(2018, 1, 5), new Date()));
}
dates = dates.sort((a, b) => a - b);
function GetDateDifference(date1, date2)
{
var timeDiff = Math.abs(date2.getTime() - date1.getTime());
var diffDays = Math.ceil(timeDiff / (1000 * 3600 * 24));
return diffDays;
}
function CollectNearDates(arr)
{
if(arr.length <= 1) return arr
let outer = [];
let inner = [arr[0]];
let previousDate = inner[0];
for(let i = 1; i < arr.length; i++)
{
let currentDate = dates[i];
let difference = GetDateDifference(previousDate, currentDate);
if(difference <= 1)
{
inner.push(previousDate);
previousDate = currentDate;
continue;
}
switch(inner.length)
{
case 0:
break;
case 1:
outer.push(inner[0]);
break;
default:
outer.push(inner);
}
previousDate = currentDate;
inner = [currentDate]
}
if(inner.length === 1) outer.push(previousDate);
else inner.push(previousDate);
return outer;
}
console.log(CollectNearDates(dates))
Coniser a variable list = ["A", "B",...] as list of strings. I want to use a Javascript programm that picks three strings from this list once a day and writes it into a HTML field.
Currently I use
function getRandom(arr, n) {
var result = new Array(n),
len = arr.length,
taken = new Array(len);
if (n > len)
throw new RangeError("getRandom: more elements taken than available");
while (n--) {
var x = Math.floor(Math.random() * len);
result[n] = arr[x in taken ? taken[x] : x];
taken[x] = --len in taken ? taken[len] : len;
}
return result;
}
smallList = getRandom(list, 3);
var htmlTags = [
"tag1",
"tag2",
"tag3"
];
for (var i = 0; i < htmlTags.length; i++) {
document.getElementById(htmlTags[i]).innerHTML = smallList[i];
}
Now this list gets new entries every time I refresh the website. Is there a way that smallList is only set once a day/hour/min/ in a pedriod of time only using javascript?
So you want to:
Pick three values from your list and show them on your web page
Each day, pick three new values to show for the whole day
Everyone who visits the page should see the same values regardless of client
As others have suggested, it would be a better candidate for a server-side task than client-side.
For example, you might have a server page which checks for the existence of a value stored in cache. The cache would be set to 24 hours. If the cache is not available, then a new cache object is created and given a half-life of 24 hours. Inside the cache, you could also store the values you wish to retrieve.
Then, you could retrieve the cache and output the values. The particular implementation of the cache would depend on your server-side language.
OKAY: Via Stored Values (COOKIE, SESSION, LOCAL STORAGE, MEMORY):
per user you'd have to use a cookie, session, or write to local storage in the browser.
for all users you'd have to use a server variable somewhere like a database, file, or memory.
you'd set the value to expire in a day and regenerate if expired. this is the answer you will get from most people because it is the only way they know how to solve this, single set value polled from all locations.
BETTER: Via Deterministic Pseudo-Random Number Generator Seeded with Date:
or if you are really ambitious and don't want to rely on a value that you set somewhere, you could use or write a:
deterministic pseudo-random number generator that you seed off of the date. Since deterministic pseudo-random generators produce reproducible "randoms" from the same seed, seeding the date gives you a unique seed per day, hence a unique random per day.
function RC4(seed) {
this.s = new Array(256);
this.i = 0;
this.j = 0;
for (var i = 0; i < 256; i++) {
this.s[i] = i;
}
if (seed) {
this.mix(seed);
}
};
RC4.getStringBytes = function(string) {
var output = [];
for (var i = 0; i < string.length; i++) {
var c = string.charCodeAt(i);
var bytes = [];
do {
bytes.push(c & 0xFF);
c = c >> 8;
} while (c > 0);
output = output.concat(bytes.reverse());
}
return output;
};
RC4.prototype._swap = function(i, j) {
var tmp = this.s[i];
this.s[i] = this.s[j];
this.s[j] = tmp;
};
RC4.prototype.mix = function(seed) {
var input = RC4.getStringBytes(seed);
var j = 0;
for (var i = 0; i < this.s.length; i++) {
j += this.s[i] + input[i % input.length];
j %= 256;
this._swap(i, j);
}
};
RC4.prototype.next = function() {
this.i = (this.i + 1) % 256;
this.j = (this.j + this.s[this.i]) % 256;
this._swap(this.i, this.j);
return this.s[(this.s[this.i] + this.s[this.j]) % 256];
};
function RNG(seed) {
if (seed == null) {
seed = '' + Math.random() + Date.now();
} else if (typeof seed === "function") {
// Use it as a uniform number generator
this.uniform = seed;
this.nextByte = function() {
return ~~(this.uniform() * 256);
};
seed = null;
} else if (Object.prototype.toString.call(seed) !== "[object String]") {
seed = JSON.stringify(seed);
}
this._normal = null;
if (seed) {
this._state = new RC4(seed);
} else {
this._state = null;
}
}
RNG.prototype.nextByte = function() {
return this._state.next();
};
RNG.prototype.uniform = function() {
var BYTES = 7; // 56 bits to make a 53-bit double
var output = 0;
for (var i = 0; i < BYTES; i++) {
output *= 256;
output += this.nextByte();
}
return output / (Math.pow(2, BYTES * 8) - 1);
};
RNG.prototype.random = function(n, m) {
if (n == null) {
return this.uniform();
} else if (m == null) {
m = n;
n = 0;
}
return n + Math.floor(this.uniform() * (m - n));
};
RNG.$ = new RNG();
Date.prototype.yyyymmdd = function() {
var mm = this.getMonth() + 1; // getMonth() is zero-based
var dd = this.getDate();
return [this.getFullYear(), !mm[1] && '0', mm, !dd[1] && '0', dd].join(''); // padding
};
// Using the Date like so will give you the same random between 40 and 50 for the same day
var rng = new RNG((new Date).yyyymmdd()); rng.random(40, 50);
// Test with dates
var rng = new RNG('20180301'); rng.random(40, 50);
var rng = new RNG('20180302'); rng.random(40, 50);
var rng = new RNG('20180301'); rng.random(40, 50);
Store the list in localStorage or a Cookie. Also store the timestamp.
Use setTimeout(function(){...}, n) to examine the timestamp and update the values as needed.
If the page refreshes or is loaded anew, then perform the check on what is stored. If nothing exists, create your list and set the timestamp. If data does exist, then compare the timestamp and update the list as needed.
If you need the list to be consistent across users, then everything needs to be stored, examined and calculated on the server-side.
localStorage.savedData = {
timestamp: new Date(),
dataList: ['a','b','c']
}
To get the values from localStorage:
// you don't have to create variables, you can just use localStorage.[property] to get compare any value
let ts = localStorage.timestamp; // Date object
let dl = localStorage.dataList; // Array of values
For more information on localStorage see (or search the web) -> https://www.w3schools.com/html/html5_webstorage.asp
I'm interested in using the JavaScript WebAudioAPI to detect song beats, and then render them in a canvas.
I can handle the canvas part, but I'm not a big audio guy and really don't understand how to make a beat detector in JavaScript.
I've tried following this article but cannot, for the life of me, connect the dots between each function to make a functional program.
I know I should show you some code but honestly I don't have any, all my attempts have failed miserably and the relevant code it's in the previously mentioned article.
Anyways I'd really appreciate some guidance, or even better a demo of how to actually detect song beats with the WebAudioAPI.
Thanks!
The main thing to understand about the referenced article by Joe Sullivan is that even though it gives a lot of source code, it's far from final and complete code. To reach a working solution you will still need both some coding and debugging skills.
This answer draws most of its code from the referenced article, original licensing applies where appropriate.
Below is a naïve sample implementation for using the functions described by the above article, you still need to figure out correct thresholds for a functional solution.
The code consists of preparation code written for the answer:
reading a local file over the FileReader API
decoding the file as audio data using the AudioContext API
and then, as described in the article:
filtering the audio, in this example with a low-pass filter
calculating peaks using a threshold
grouping interval counts and then tempo counts
For the threshold I used an arbitrary value of .98 of the range between maximum and minimum values; when grouping I added some additional checks and arbitrary rounding to avoid possible infinite loops and make it an easy-to-debug sample.
Note that commenting is scarce to keep the sample implementation brief because:
the logic behind processing is explained in the referenced article
the syntax can be referenced in the API docs of the related methods
audio_file.onchange = function() {
var file = this.files[0];
var reader = new FileReader();
var context = new(window.AudioContext || window.webkitAudioContext)();
reader.onload = function() {
context.decodeAudioData(reader.result, function(buffer) {
prepare(buffer);
});
};
reader.readAsArrayBuffer(file);
};
function prepare(buffer) {
var offlineContext = new OfflineAudioContext(1, buffer.length, buffer.sampleRate);
var source = offlineContext.createBufferSource();
source.buffer = buffer;
var filter = offlineContext.createBiquadFilter();
filter.type = "lowpass";
source.connect(filter);
filter.connect(offlineContext.destination);
source.start(0);
offlineContext.startRendering();
offlineContext.oncomplete = function(e) {
process(e);
};
}
function process(e) {
var filteredBuffer = e.renderedBuffer;
//If you want to analyze both channels, use the other channel later
var data = filteredBuffer.getChannelData(0);
var max = arrayMax(data);
var min = arrayMin(data);
var threshold = min + (max - min) * 0.98;
var peaks = getPeaksAtThreshold(data, threshold);
var intervalCounts = countIntervalsBetweenNearbyPeaks(peaks);
var tempoCounts = groupNeighborsByTempo(intervalCounts);
tempoCounts.sort(function(a, b) {
return b.count - a.count;
});
if (tempoCounts.length) {
output.innerHTML = tempoCounts[0].tempo;
}
}
// http://tech.beatport.com/2014/web-audio/beat-detection-using-web-audio/
function getPeaksAtThreshold(data, threshold) {
var peaksArray = [];
var length = data.length;
for (var i = 0; i < length;) {
if (data[i] > threshold) {
peaksArray.push(i);
// Skip forward ~ 1/4s to get past this peak.
i += 10000;
}
i++;
}
return peaksArray;
}
function countIntervalsBetweenNearbyPeaks(peaks) {
var intervalCounts = [];
peaks.forEach(function(peak, index) {
for (var i = 0; i < 10; i++) {
var interval = peaks[index + i] - peak;
var foundInterval = intervalCounts.some(function(intervalCount) {
if (intervalCount.interval === interval) return intervalCount.count++;
});
//Additional checks to avoid infinite loops in later processing
if (!isNaN(interval) && interval !== 0 && !foundInterval) {
intervalCounts.push({
interval: interval,
count: 1
});
}
}
});
return intervalCounts;
}
function groupNeighborsByTempo(intervalCounts) {
var tempoCounts = [];
intervalCounts.forEach(function(intervalCount) {
//Convert an interval to tempo
var theoreticalTempo = 60 / (intervalCount.interval / 44100);
theoreticalTempo = Math.round(theoreticalTempo);
if (theoreticalTempo === 0) {
return;
}
// Adjust the tempo to fit within the 90-180 BPM range
while (theoreticalTempo < 90) theoreticalTempo *= 2;
while (theoreticalTempo > 180) theoreticalTempo /= 2;
var foundTempo = tempoCounts.some(function(tempoCount) {
if (tempoCount.tempo === theoreticalTempo) return tempoCount.count += intervalCount.count;
});
if (!foundTempo) {
tempoCounts.push({
tempo: theoreticalTempo,
count: intervalCount.count
});
}
});
return tempoCounts;
}
// http://stackoverflow.com/questions/1669190/javascript-min-max-array-values
function arrayMin(arr) {
var len = arr.length,
min = Infinity;
while (len--) {
if (arr[len] < min) {
min = arr[len];
}
}
return min;
}
function arrayMax(arr) {
var len = arr.length,
max = -Infinity;
while (len--) {
if (arr[len] > max) {
max = arr[len];
}
}
return max;
}
<input id="audio_file" type="file" accept="audio/*"></input>
<audio id="audio_player"></audio>
<p>
Most likely tempo: <span id="output"></span>
</p>
I wrote a tutorial here which shows how to do this with the javascript Web Audio API.
https://askmacgyver.com/blog/tutorial/how-to-implement-tempo-detection-in-your-application
Outline of Steps
Transform Audio File into an Array Buffer
Run Array Buffer Through Low Pass Filter
Trim a 10 second Clip from the Array Buffer
Down Sample the Data
Normalize the Data
Count Volume Groupings
Infer Tempo from Groupings Count
This code below does the heavy lifting.
Load Audio File Into Array Buffer and Run Through Low Pass Filter
function createBuffers(url) {
// Fetch Audio Track via AJAX with URL
request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = function(ajaxResponseBuffer) {
// Create and Save Original Buffer Audio Context in 'originalBuffer'
var audioCtx = new AudioContext();
var songLength = ajaxResponseBuffer.total;
// Arguments: Channels, Length, Sample Rate
var offlineCtx = new OfflineAudioContext(1, songLength, 44100);
source = offlineCtx.createBufferSource();
var audioData = request.response;
audioCtx.decodeAudioData(audioData, function(buffer) {
window.originalBuffer = buffer.getChannelData(0);
var source = offlineCtx.createBufferSource();
source.buffer = buffer;
// Create a Low Pass Filter to Isolate Low End Beat
var filter = offlineCtx.createBiquadFilter();
filter.type = "lowpass";
filter.frequency.value = 140;
source.connect(filter);
filter.connect(offlineCtx.destination);
// Render this low pass filter data to new Audio Context and Save in 'lowPassBuffer'
offlineCtx.startRendering().then(function(lowPassAudioBuffer) {
var audioCtx = new(window.AudioContext || window.webkitAudioContext)();
var song = audioCtx.createBufferSource();
song.buffer = lowPassAudioBuffer;
song.connect(audioCtx.destination);
// Save lowPassBuffer in Global Array
window.lowPassBuffer = song.buffer.getChannelData(0);
console.log("Low Pass Buffer Rendered!");
});
},
function(e) {});
}
request.send();
}
createBuffers('https://askmacgyver.com/test/Maroon5-Moves-Like-Jagger-128bpm.mp3');
You Now Have an Array Buffer of the Low Pass Filtered Song (And Original)
It's comprised of a number of entries, sampleRate (44100 multiplied by the number of seconds of the song).
window.lowPassBuffer // Low Pass Array Buffer
window.originalBuffer // Original Non Filtered Array Buffer
Trim a 10 Second Clip from the Song
function getClip(length, startTime, data) {
var clip_length = length * 44100;
var section = startTime * 44100;
var newArr = [];
for (var i = 0; i < clip_length; i++) {
newArr.push(data[section + i]);
}
return newArr;
}
// Overwrite our array buffer to a 10 second clip starting from 00:10s
window.lowPassFilter = getClip(10, 10, lowPassFilter);
Down Sample Your Clip
function getSampleClip(data, samples) {
var newArray = [];
var modulus_coefficient = Math.round(data.length / samples);
for (var i = 0; i < data.length; i++) {
if (i % modulus_coefficient == 0) {
newArray.push(data[i]);
}
}
return newArray;
}
// Overwrite our array to down-sampled array.
lowPassBuffer = getSampleClip(lowPassFilter, 300);
Normalize Your Data
function normalizeArray(data) {
var newArray = [];
for (var i = 0; i < data.length; i++) {
newArray.push(Math.abs(Math.round((data[i + 1] - data[i]) * 1000)));
}
return newArray;
}
// Overwrite our array to the normalized array
lowPassBuffer = normalizeArray(lowPassBuffer);
Count the Flat Line Groupings
function countFlatLineGroupings(data) {
var groupings = 0;
var newArray = normalizeArray(data);
function getMax(a) {
var m = -Infinity,
i = 0,
n = a.length;
for (; i != n; ++i) {
if (a[i] > m) {
m = a[i];
}
}
return m;
}
function getMin(a) {
var m = Infinity,
i = 0,
n = a.length;
for (; i != n; ++i) {
if (a[i] < m) {
m = a[i];
}
}
return m;
}
var max = getMax(newArray);
var min = getMin(newArray);
var count = 0;
var threshold = Math.round((max - min) * 0.2);
for (var i = 0; i < newArray.length; i++) {
if (newArray[i] > threshold && newArray[i + 1] < threshold && newArray[i + 2] < threshold && newArray[i + 3] < threshold && newArray[i + 6] < threshold) {
count++;
}
}
return count;
}
// Count the Groupings
countFlatLineGroupings(lowPassBuffer);
Scale 10 Second Grouping Count to 60 Seconds to Derive Beats Per Minute
var final_tempo = countFlatLineGroupings(lowPassBuffer);
// final_tempo will be 21
final_tempo = final_tempo * 6;
console.log("Tempo: " + final_tempo);
// final_tempo will be 126