I have to use ffmpeg in my project to convert an audio file from wav t mp3,pcm and vox. my project is javascript/php built can anyone direct me how to use ffmpeg.I've been reading a lot and now that it depends on the command lines.but where to write these command lines.If someone can show me a demo of how to use it i will thankful
I have found this function, but don't know how to give it the file to convert.
function getFFMPEGWorker() {
// regexps for extracting time from ffmpeg logs
var durationRegexp = /Duration: (.*?), /
var timeRegexp = /time=(.*?) /;
var duration;
var ffmpegWorker = new Worker('worker.js');
var durationLine;
ffmpegWorker.addEventListener('message', function(event) {
var message = event.data;
console.log(message.type);
if (message.type === "ready" && window.File && window.FileList && window.FileReader) {
// script loaded, hide loader
$('#loading').hide();
} else if (message.type == "stdout") {
console.log(message.data);
} else if (message.type == "stderr") {
console.log(message.data);
// try to extract duration
if (durationRegexp.exec(message.data)) {
duration = timeToSeconds(durationRegexp.exec(message.data)[1]);
}
// try to extract time
if (timeRegexp.exec(message.data)) {
var time = timeToSeconds(timeRegexp.exec(message.data)[1]);
if (duration) {
$("#progress").text("Progress: " + Math.floor(time / duration * 100) + "%");
$("#progress").show();
}
}
} else if (message.type == "done") {
var code = message.data.code;
console.log(message.data);
var outFileNames = Object.keys(message.data.outputFiles);
console.log(outFileNames);
if (code == 0 && outFileNames.length) {
var outFileName = outFileNames[0];
var outFileBuffer = message.data.outputFiles[outFileName];
var src = window.URL.createObjectURL(new Blob([outFileBuffer]));
$("#downloadLink").attr('href', src);
$("#download").show();
} else {
$("#error").show();
}
$("#converting").hide();
$("#progress").hide();
}
}, false);
return ffmpegWorker;
}
// create ffmpeg worker
var ffmpegWorker = getFFMPEGWorker();
var ffmpegRunning = false;
$('#convert').click(function() {
// terminate existing worker
if (ffmpegRunning) {
ffmpegWorker.terminate();
ffmpegWorker = getFFMPEGWorker();
}
ffmpegRunning = true;
// display converting animation
$("#converting").show();
$("#error").hide();
// hide download div
$("#download").hide();
// change download file name
var fileNameExt = fileName.substr(fileName.lastIndexOf('.') + 1);
var outFileName = fileName.substr(0, fileName.lastIndexOf('.')) + "." + getOutFormat();
$("#downloadLink").attr("download", outFileName);
$("#downloadLink").text(outFileName);
var arguments = [];
arguments.push("-i");
arguments.push(fileName);
arguments.push("-b:a");
arguments.push(getBitrate());
switch (getOutFormat()) {
case "mp3":
arguments.push("-acodec");
arguments.push("libmp3lame");
arguments.push("out.mp3");
break;
case "wma":
arguments.push("-acodec");
arguments.push("wmav1");
arguments.push("out.asf");
break;
case "pcm":
arguments.push("-f");
arguments.push("s16le");
arguments.push("-acodec");
arguments.push("pcm_s16le");
arguments.push("out.pcm");
}
ffmpegWorker.postMessage({
type: "command",
arguments: arguments,
files: [
{
"name": fileName,
"buffer": fileBuffer
}
]
});
});
Related
I have been looking around for days now :( so really needs your help.
what i am developing is an application that downloads images and videos and display them in full screen.
for the images all is good.
But when playing videos a got this image some times and the video doesn't play.
This my code for downloading:
download(URL, fileName, callback) {
var folderName = "serverMediaDir";
var uri = encodeURI(URL);
window.requestFileSystem(
LocalFileSystem.PERSISTENT,
0,
function (fileSystem) {
var directoryEntry = fileSystem.root; // to get root path of directory
directoryEntry.getDirectory(
folderName,
{
create: true,
exclusive: false
},
function (parent) {
// Directory created successfuly
var filename = fileSystem.root.toURL() + folderName + "/" + fileName;
console.log("Directory created successfuly: " + filename);
var fileTransfer = new FileTransfer();
fileTransfer.download(
uri,
filename,
function (entry) {
// download success
var path = entry.toURL(); //**THIS IS WHAT I NEED**
console.log("Download Completed: " + entry.fullPath);
callback(null, filename, entry);
},
function (error) {
callback(error, null);
} // irrelevant download error
);
},
function (error) {
//Error while creating directory
callback(error, null);
}
);
}, function (error) {
callback(error, null);
} // irrelevant request fileSystem error
);
}
and after that i save the videos and images full paths into an array that i use to display them.
This is how i play them:
vid: function () {
var self = this;
//Accepts any number of ‘src‘ to a same video ('.mp4', '.ogg' or '.webm')
var el = document.createElement("video");
// var source = document.createElement("source");
/*for (var i = 0; i < arguments.length; i++) {
source.src = arguments[i];
// source.type = "video/" + arguments[i].split(".")[arguments[i].split(".").length - 1];
el.appendChild(source);
}*/
el.src = arguments[0];
el.onplay = function () {
clearInterval(window.sliding);
};
el.onended = function () {
window.sliding = setInterval(self.rotateImages, self.mediaUnitDuration * 1000);
self.rotateImages();
};
return el;
},
rotateMedias: function () {
var self = this;
if (self.newMedia) {
self.galleryArray = [];
if (self.mediaServer.length === 0) {
self.galleryArray.push(self.img(require(window.display.toUpperCase() ==='H'? '~/assets/H.jpg':'~/assets/V.jpg')))
}
for (var i = 0; i < self.mediaServer.length; i++) {
if (self.mediaServer[i].type.toLowerCase() === "video" && self.mediaServer[i].status) {
var obj = {};
obj = self.vid(self.mediaServer[i].path);
} else if (self.mediaServer[i].type.toLowerCase() === "image" && self.mediaServer[i].status) {
var obj = {};
obj = self.img(self.mediaServer[i].path);
}
self.galleryArray.push(obj);
}
self.newMedia = false;
}
$("#slideShow").fadeOut("slow");
setTimeout(function () {
self.curImg = self.curImg < self.galleryArray.length - 1 ? self.curImg + 1 : 0;
document.getElementById("slideShow").innerHTML = "";
self.galleryArray[self.curImg].style.width = "100%";
self.galleryArray[self.curImg].style.height = "100%";
self.galleryArray[self.curImg].style.margin = "0px";
document.getElementById("slideShow").appendChild(self.galleryArray[self.curImg]);
if (self.galleryArray[self.curImg].tagName.toLowerCase() === "video") {
self.galleryArray[self.curImg].play();
}
$("#slideShow").fadeIn("slow");
}, 500);
}
What i did try as a solution is to change the dir in which i store the files (cordova.file.applicationDirectory|dataDirectory|externalApplicationStorageDirectory...) and tried them public and private also but not working also.
Also i tried both protocol file:/// and cdvfile://localhost
I use :
cordova 10.0.0
cordova-plugin-file-transfer 1.7.1
cordova-plugin-file 6.0.2
And runing the app on X96mini box with android 7.1.2
After a lot of testing i find that it is a box bug (the application work on many others android devices with different versions). Thnx god, i found this plugin so i use it and it works fine with some work arounds for sure.
I am developing an office 365 word web addin, wherein I need to upload the currently opened document to my server. For which I am trying to get the file data using following code.
The method getSliceAsync() is returning only first slice of data.
On debugging it gives "Addin Error: Sorry, we had to restart because this addin wasn't responding" while getting second slice.
I am using this link for reference : [https://learn.microsoft.com/en-us/office/dev/add-ins/word/get-the-whole-document-from-an-add-in-for-word][1]
Here is my code:
Office.context.document.getFileAsync(Office.FileType.Compressed, { sliceSize: 65536 }, function (result) {
if (result.status == "succeeded") {
// If the getFileAsync call succeeded, then result.value will return a valid File Object
var myFile = result.value;
var filename1 = myFile.name;
console.log(filename1);
var sliceCount = myFile.sliceCount;
var slicesReceived = 0, isAllSlicesSuccess = true, docdataSlices = [];
// document.getElementById("result").innerText = "File size:" + myFile.size + "#Slices: " + sliceCount;
console.log(" File size:" + myFile.size + " #Slices: " + sliceCount, "");
makeProgress(20);
// Iterate over the file slices
for (var i = 0; i < sliceCount && isAllSlicesSuccess; i++) {
var diffPercent = ((i / sliceCount) * 100);
myFile.getSliceAsync(i, function (sliceResult) {
if (sliceResult.status == "succeeded") {
if (!isAllSlicesSuccess) { // Some slice has failed to get, no need to continue
console.log("Error", "One slice failed to get");
return;
console.log(sliceResult);
}
console.log('sliceResult', sliceResult);
console.log("Success", "i: " + i);
console.log("++slicesReceived ",slicesReceived );
console.log(" sliceCount",sliceCount );
console.log("++slicesReceived == sliceCount",slicesReceived == sliceCount);
// One chunk was got, store it in a temporal array
// ++slicesReceived;
// or you can do something with the chunk, such as sent it to a third party server
docdataSlices[sliceResult.value.index] = sliceResult.value.data;
if (++slicesReceived == sliceCount) {
getAllSlicesTime = Date.now();
var performance = (getAllSlicesTime - startTime) / 1000.0;
console.log("Success", "All slices has been get, Seconds: " + performance);
// All slices have been received
myFile.closeAsync(function (closeRes) {
if (closeRes.status == "succeeded") {
console.log("Close Success", "Success");
// DUClick();
}
else {
console.log("Close Error", closeRes.error.message);
}
});
onGetAllSlicesSucceeded(docdataSlices, false);
}
}
else {
isAllSlicesSuccess = false;
myFile.closeAsync(function (closeRes) {
if (closeRes.status == "succeeded") {
console.log("Close Success", "Success");
// DUClick();
}
else {
console.log("Close Error", closeRes.error.message);
}
});
console.log("Get Slice Error:", sliceResult.error.message);
}
});
}
}
else {
getFileTime = Date.now();
var performance = (getFileTime - startTime) / 1000.0;
console.log('Get File Error:', "Seconds: " + performance + " " + result.error.message);
}
});
Please suggest! Thanks in advance!
[1]: https://learn.microsoft.com/en-us/office/dev/add-ins/word/get-the-whole-document-from-an-add-in-for-word
Recently I want to start a project by piggyback someone's extension. I want to scope one of the image source (local variable, a base64 url) and then photo recognize it on the popup page. I keep getting error "imgb64.replace is not a function" or "imgb64" not defined.
like my title said, I want to scope the local variable in.in.inside a function (from backgound.js )and use it globally (or in popup.js). very new to this, please help guys.
// this is popup.js
chrome.runtime.getBackgroundPage(function(bg) {
bg.capture(window);
});
/// what I did
function img_find() {
var imgs = document.getElementsByTagName("img");
var imgSrcs = [];
for (var i = 0; i < imgs.length; i++) {
imgSrcs.push(imgs[i].src);
}
return imgSrcs;
}
var imgb64 = img_find();
try {
const app = new Clarifai.App({
apiKey: 'mykey'
});
}
catch(err) {
alert("Need a valid API Key!");
throw "Invalid API Key";
}
// Checks for valid image type
function validFile(imageName) {
var lowerImageName = imageName.toLowerCase();
return lowerImageName.search(/jpg|png|bmp|tiff/gi) != -1;
}
var imageDetails = imgb64.replace(/^data:image\/(.*);base64,/, '');
console.log(imageDetails)
app.models.predict("e466caa0619f444ab97497640cefc4dc", {base64:
imageDetails}).then(
function(response) {
// do something with response
},
function(err) {
// there was an error
}
);
/// end what I did
below is background.js, I think what I need is the local var img.src, thats all.
function capture(popup) {
function callOnLoad(func) {
popup.addEventListener("load", func);
if (popup.document.readyState === "complete") {
func();
}
}
crxCS.insert(null, { file: "capture.js" }, function() {
crxCS.callA(null, "get", function(result) {
var scrShot, zm, bufCav, bufCavCtx;
function mkImgList() {
for (var i = 0; i < result.vidShots.length; i++) {
var img = new popup.Image();
img.onload = function() {
this.style.height = this.naturalHeight /
(this.naturalWidth / 400) + "px";
};
if (result.vidShots[i].constructor === String) {
img.src = result.vidShots[i];
} else {
bufCav.width = result.vidShots[i].width * zm;
bufCav.height = result.vidShots[i].height * zm;
bufCavCtx.drawImage(scrShot, -result.vidShots[i].left *
zm, -result.vidShots[i].top * zm);
img.src = bufCav.toDataURL('image/png');
////// maybe clarifai here ?
////end clarifai
}
popup.document.body.appendChild(img);
}
popup.onclick = function(mouseEvent) {
if (mouseEvent.target.tagName === "IMG") {
chrome.downloads.download({ url: mouseEvent.target.src,
saveAs: true, filename: "chrome_video_capture_" + (new Date()).getTime() +
".png" });
}
};
popup.onunload = function(mouseEvent) {
crxCS.callA(null, "rcy");
};
} /// end mkImgList
if (result.needScrShot) {
bufCav = popup.document.createElement("canvas");
bufCavCtx = bufCav.getContext("2d");
chrome.tabs.captureVisibleTab({ format: "png" },
function(dataUrl) {
scrShot = new Image();
scrShot.onload = function() {
chrome.tabs.getZoom(function(zoomFactor) {
zm = zoomFactor;
callOnLoad(function() {
mkImgList(zoomFactor);
});
});
};
scrShot.src = dataUrl;
});
} else if (result.vidShots.length) {
callOnLoad(mkImgList);
} else {
popup.document.body.appendChild(notFound);
}
}); // end crxCS.callA
}); // end crxCS.insert
} // end capture
Please help guys. :)
I have been experimenting with cute file browser (perfect for my project).
Cute File Browser
But came accross a incomaptibiliy issue. Im not getting any errors in console, but im also not getting any elements being rendered. I have switched libraries about and I think this plugin only works with jquery version 1.11.0, the version my project is using is 1.11.3.
How should I attempt to fix/update this small script?
CUTE SCRIPT:
$(function(){
var filemanager = $('.filemanager'),
breadcrumbs = $('.breadcrumbs'),
fileList = filemanager.find('.data');
// Start by fetching the file data from scan.php with an AJAX request
$.get('scan.php', function(data) {
var response = [data],
currentPath = '',
breadcrumbsUrls = [];
var folders = [],
files = [];
// This event listener monitors changes on the URL. We use it to
// capture back/forward navigation in the browser.
$(window).on('hashchange', function(){
goto(window.location.hash);
// We are triggering the event. This will execute
// this function on page load, so that we show the correct folder:
}).trigger('hashchange');
// Hiding and showing the search box
filemanager.find('.search').click(function(){
var search = $(this);
search.find('span').hide();
search.find('input[type=search]').show().focus();
});
// Listening for keyboard input on the search field.
// We are using the "input" event which detects cut and paste
// in addition to keyboard input.
filemanager.find('input').on('input', function(e){
folders = [];
files = [];
var value = this.value.trim();
if(value.length) {
filemanager.addClass('searching');
// Update the hash on every key stroke
window.location.hash = 'search=' + value.trim();
}
else {
filemanager.removeClass('searching');
window.location.hash = encodeURIComponent(currentPath);
}
}).on('keyup', function(e){
// Clicking 'ESC' button triggers focusout and cancels the search
var search = $(this);
if(e.keyCode == 27) {
search.trigger('focusout');
}
}).focusout(function(e){
// Cancel the search
var search = $(this);
if(!search.val().trim().length) {
window.location.hash = encodeURIComponent(currentPath);
search.hide();
search.parent().find('span').show();
}
});
// Clicking on folders
fileList.on('click', 'li.folders', function(e){
e.preventDefault();
var nextDir = $(this).find('a.folders').attr('href');
if(filemanager.hasClass('searching')) {
// Building the breadcrumbs
breadcrumbsUrls = generateBreadcrumbs(nextDir);
filemanager.removeClass('searching');
filemanager.find('input[type=search]').val('').hide();
filemanager.find('span').show();
}
else {
breadcrumbsUrls.push(nextDir);
}
window.location.hash = encodeURIComponent(nextDir);
currentPath = nextDir;
});
// Clicking on breadcrumbs
breadcrumbs.on('click', 'a', function(e){
e.preventDefault();
var index = breadcrumbs.find('a').index($(this)),
nextDir = breadcrumbsUrls[index];
breadcrumbsUrls.length = Number(index);
window.location.hash = encodeURIComponent(nextDir);
});
// Navigates to the given hash (path)
function goto(hash) {
hash = decodeURIComponent(hash).slice(1).split('=');
if (hash.length) {
var rendered = '';
// if hash has search in it
if (hash[0] === 'search') {
filemanager.addClass('searching');
rendered = searchData(response, hash[1].toLowerCase());
if (rendered.length) {
currentPath = hash[0];
render(rendered);
}
else {
render(rendered);
}
}
// if hash is some path
else if (hash[0].trim().length) {
rendered = searchByPath(hash[0]);
if (rendered.length) {
currentPath = hash[0];
breadcrumbsUrls = generateBreadcrumbs(hash[0]);
render(rendered);
}
else {
currentPath = hash[0];
breadcrumbsUrls = generateBreadcrumbs(hash[0]);
render(rendered);
}
}
// if there is no hash
else {
currentPath = data.path;
breadcrumbsUrls.push(data.path);
render(searchByPath(data.path));
}
}
}
// Splits a file path and turns it into clickable breadcrumbs
function generateBreadcrumbs(nextDir){
var path = nextDir.split('/').slice(0);
for(var i=1;i<path.length;i++){
path[i] = path[i-1]+ '/' +path[i];
}
return path;
}
// Locates a file by path
function searchByPath(dir) {
var path = dir.split('/'),
demo = response,
flag = 0;
for(var i=0;i<path.length;i++){
for(var j=0;j<demo.length;j++){
if(demo[j].name === path[i]){
flag = 1;
demo = demo[j].items;
break;
}
}
}
demo = flag ? demo : [];
return demo;
}
// Recursively search through the file tree
function searchData(data, searchTerms) {
data.forEach(function(d){
if(d.type === 'folder') {
searchData(d.items,searchTerms);
if(d.name.toLowerCase().match(searchTerms)) {
folders.push(d);
}
}
else if(d.type === 'file') {
if(d.name.toLowerCase().match(searchTerms)) {
files.push(d);
}
}
});
return {folders: folders, files: files};
}
// Render the HTML for the file manager
function render(data) {
var scannedFolders = [],
scannedFiles = [];
if(Array.isArray(data)) {
data.forEach(function (d) {
if (d.type === 'folder') {
scannedFolders.push(d);
}
else if (d.type === 'file') {
scannedFiles.push(d);
}
});
}
else if(typeof data === 'object') {
scannedFolders = data.folders;
scannedFiles = data.files;
}
// Empty the old result and make the new one
fileList.empty().hide();
if(!scannedFolders.length && !scannedFiles.length) {
filemanager.find('.nothingfound').show();
}
else {
filemanager.find('.nothingfound').hide();
}
if(scannedFolders.length) {
scannedFolders.forEach(function(f) {
var itemsLength = f.items.length,
name = escapeHTML(f.name),
icon = '<span class="icon folder"></span>';
if(itemsLength) {
icon = '<span class="icon folder full"></span>';
}
if(itemsLength == 1) {
itemsLength += ' item';
}
else if(itemsLength > 1) {
itemsLength += ' items';
}
else {
itemsLength = 'Empty';
}
var folder = $('<li class="folders">'+icon+'<span class="name">' + name + '</span> <span class="details">' + itemsLength + '</span></li>');
folder.appendTo(fileList);
});
}
if(scannedFiles.length) {
scannedFiles.forEach(function(f) {
var fileSize = bytesToSize(f.size),
name = escapeHTML(f.name),
fileType = name.split('.'),
icon = '<span class="icon file"></span>';
fileType = fileType[fileType.length-1];
icon = '<span class="icon file f-'+fileType+'">.'+fileType+'</span>';
var file = $('<li class="files">'+icon+'<span class="name">'+ name +'</span> <span class="details">'+fileSize+'</span></li>');
file.appendTo(fileList);
});
}
// Generate the breadcrumbs
var url = '';
if(filemanager.hasClass('searching')){
url = '<span>Search results: </span>';
fileList.removeClass('animated');
}
else {
fileList.addClass('animated');
breadcrumbsUrls.forEach(function (u, i) {
var name = u.split('/');
if (i !== breadcrumbsUrls.length - 1) {
url += '<span class="folderName">' + name[name.length-1] + '</span> <span class="arrow">→</span> ';
}
else {
url += '<span class="folderName">' + name[name.length-1] + '</span>';
}
});
}
breadcrumbs.text('').append(url);
// Show the generated elements
fileList.animate({'display':'inline-block'});
}
// This function escapes special html characters in names
function escapeHTML(text) {
return text.replace(/\&/g,'&').replace(/\</g,'<').replace(/\>/g,'>');
}
// Convert file sizes from bytes to human readable units
function bytesToSize(bytes) {
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes == 0) return '0 Bytes';
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i];
}
});
});
I tweaked certain jquery methods around in the render function using the 1.11.3 version and it appears animate() was causing the issues.
Change Script.js line 375:
From fileList.animate({'display':'inline-block'});
To fileList.css('display','inline-block');.
EDIT:
I noticed a slightly more improved method of revealing the hidden filelist without using inline styles and adding it to a more appoporate section of the script. Simply use filelist.show() in the following section of the render function.
change Script.js line 286-291:
if(!scannedFolders.length && !scannedFiles.length) {
filemanager.find('.nothingfound').show();
fileList.hide();
}
else {
filemanager.find('.nothingfound').hide();
fileList.show();
}
Hiding the filelist using filelist.hide() also helped me with a style bug relating to the .nothing-found error message being pushed down to the bottom of the page when needing to use a fixed height on the filelist.
Now im not depenedant on what version of jquery im using. Hope this helps others using this nice little script.
I want to record the audio output from a simple drum sequencer and export it for download as a wav file. I have a live link to my current attempt at this implementation attempt.
The sum output of the sequencer is routed to the variable finalMixNode
yet setting this as the input for the recorder.js doesn't work. I think it may be a problem with the audio context but I can't figure it out. I successfully created a oscillator and recorded its output but I can't extend this to the sequencer.
Here is the main js code in which I am trying to record the output. I'm hoping someone will see what I am missing.
//audio node variables
var context;
var convolver;
var compressor;
var masterGainNode;
var effectLevelNode;
var lowPassFilterNode;
var noteTime;
var startTime;
var lastDrawTime = -1;
var LOOP_LENGTH = 16;
var rhythmIndex = 0;
var timeoutId;
var testBuffer = null;
var currentKit = null;
var reverbImpulseResponse = null;
var tempo = 120;
var TEMPO_MAX = 200;
var TEMPO_MIN = 40;
var TEMPO_STEP = 4;
if (window.hasOwnProperty('AudioContext') && !window.hasOwnProperty('webkitAudioContext')) {
window.webkitAudioContext = AudioContext;
}
$(function() {
init();
toggleSelectedListener();
playPauseListener();
lowPassFilterListener();
reverbListener();
createLowPassFilterSliders();
initializeTempo();
changeTempoListener();
});
function createLowPassFilterSliders() {
$("#freq-slider").slider({
value: 1,
min: 0,
max: 1,
step: 0.01,
disabled: true,
slide: changeFrequency
});
$("#quality-slider").slider({
value: 0,
min: 0,
max: 1,
step: 0.01,
disabled: true,
slide: changeQuality
});
}
function lowPassFilterListener() {
$('#lpf').click(function() {
$(this).toggleClass("active");
$(this).blur();
if ($(this).hasClass("btn-default")) {
$(this).removeClass("btn-default");
$(this).addClass("btn-warning");
lowPassFilterNode.active = true;
$("#freq-slider,#quality-slider").slider( "option", "disabled", false );
}
else {
$(this).addClass("btn-default");
$(this).removeClass("btn-warning");
lowPassFilterNode.active = false;
$("#freq-slider,#quality-slider").slider( "option", "disabled", true );
}
})
}
function reverbListener() {
$("#reverb").click(function() {
$(this).toggleClass("active");
$(this).blur();
if ($(this).hasClass("btn-default")) {
$(this).removeClass("btn-default");
$(this).addClass("btn-warning");
convolver.active = true;
}
else {
$(this).addClass("btn-default");
$(this).removeClass("btn-warning");
convolver.active = false;
}
})
}
function changeFrequency(event, ui) {
var minValue = 40;
var maxValue = context.sampleRate / 2;
var numberOfOctaves = Math.log(maxValue / minValue) / Math.LN2;
var multiplier = Math.pow(2, numberOfOctaves * (ui.value - 1.0));
lowPassFilterNode.frequency.value = maxValue * multiplier;
}
function changeQuality(event, ui) {
//30 is the quality multiplier, for now.
lowPassFilterNode.Q.value = ui.value * 30;
}
function playPauseListener() {
$('#play-pause').click(function() {
var $span = $(this).children("span");
if($span.hasClass('glyphicon-play')) {
$span.removeClass('glyphicon-play');
$span.addClass('glyphicon-pause');
handlePlay();
}
else {
$span.addClass('glyphicon-play');
$span.removeClass('glyphicon-pause');
handleStop();
}
});
}
function toggleSelectedListener() {
$('.pad').click(function() {
$(this).toggleClass("selected");
});
}
function init() {
initializeAudioNodes();
loadKits();
loadImpulseResponses();
}
function initializeAudioNodes() {
context = new webkitAudioContext();
var finalMixNode;
if (context.createDynamicsCompressor) {
// Create a dynamics compressor to sweeten the overall mix.
compressor = context.createDynamicsCompressor();
compressor.connect(context.destination);
finalMixNode = compressor;
} else {
// No compressor available in this implementation.
finalMixNode = context.destination;
}
// Create master volume.
// for now, the master volume is static, but in the future there will be a slider
masterGainNode = context.createGain();
masterGainNode.gain.value = 0.7; // reduce overall volume to avoid clipping
masterGainNode.connect(finalMixNode);
//connect all sounds to masterGainNode to play them
//don't need this for now, no wet dry mix for effects
// // Create effect volume.
// effectLevelNode = context.createGain();
// effectLevelNode.gain.value = 1.0; // effect level slider controls this
// effectLevelNode.connect(masterGainNode);
// Create convolver for effect
convolver = context.createConvolver();
convolver.active = false;
// convolver.connect(effectLevelNode);
//Create Low Pass Filter
lowPassFilterNode = context.createBiquadFilter();
//this is for backwards compatibility, the type used to be an integer
lowPassFilterNode.type = (typeof lowPassFilterNode.type === 'string') ? 'lowpass' : 0; // LOWPASS
//default value is max cutoff, or passing all frequencies
lowPassFilterNode.frequency.value = context.sampleRate / 2;
lowPassFilterNode.connect(masterGainNode);
lowPassFilterNode.active = false;
}
function loadKits() {
//name must be same as path
var kit = new Kit("TR808");
kit.load();
//TODO: figure out how to test if a kit is loaded
currentKit = kit;
}
function loadImpulseResponses() {
reverbImpulseResponse = new ImpulseResponse("sounds/impulse- responses/matrix-reverb2.wav");
reverbImpulseResponse.load();
}
//TODO delete this
function loadTestBuffer() {
var request = new XMLHttpRequest();
var url = "http://www.freesound.org/data/previews/102/102130_1721044-lq.mp3";
request.open("GET", url, true);
request.responseType = "arraybuffer";
request.onload = function() {
context.decodeAudioData(
request.response,
function(buffer) {
testBuffer = buffer;
},
function(buffer) {
console.log("Error decoding drum samples!");
}
);
}
request.send();
}
//TODO delete this
function sequencePads() {
$('.pad.selected').each(function() {
$('.pad').removeClass("selected");
$(this).addClass("selected");
});
}
function playNote(buffer, noteTime) {
var voice = context.createBufferSource();
voice.buffer = buffer;
var currentLastNode = masterGainNode;
if (lowPassFilterNode.active) {
lowPassFilterNode.connect(currentLastNode);
currentLastNode = lowPassFilterNode;
}
if (convolver.active) {
convolver.buffer = reverbImpulseResponse.buffer;
convolver.connect(currentLastNode);
currentLastNode = convolver;
}
voice.connect(currentLastNode);
voice.start(noteTime);
}
function schedule() {
var currentTime = context.currentTime;
// The sequence starts at startTime, so normalize currentTime so that it's 0 at the start of the sequence.
currentTime -= startTime;
while (noteTime < currentTime + 0.200) {
var contextPlayTime = noteTime + startTime;
var $currentPads = $(".column_" + rhythmIndex);
$currentPads.each(function() {
if ($(this).hasClass("selected")) {
var instrumentName = $(this).parents().data("instrument");
switch (instrumentName) {
case "kick":
playNote(currentKit.kickBuffer, contextPlayTime);
break;
case "snare":
playNote(currentKit.snareBuffer, contextPlayTime);
break;
case "hihat":
playNote(currentKit.hihatBuffer, contextPlayTime);
break;
case "tomhi":
playNote(currentKit.tomhiBuffer, contextPlayTime);
break;
case "tommid":
playNote(currentKit.tommidBuffer, contextPlayTime);
break;
case "tomlow":
playNote(currentKit.tomlowBuffer, contextPlayTime);
break;
case "cl":
playNote(currentKit.clBuffer, contextPlayTime);
break;
case "cb":
playNote(currentKit.cbBuffer, contextPlayTime);
break;
case "cp":
playNote(currentKit.cpBuffer, contextPlayTime);
break;
case "cy":
playNote(currentKit.cyBuffer, contextPlayTime);
break;
case "rs":
playNote(currentKit.rsBuffer, contextPlayTime);
break;
}
//play the buffer
//store a data element in the row that tells you what instrument
}
});
if (noteTime != lastDrawTime) {
lastDrawTime = noteTime;
drawPlayhead(rhythmIndex);
}
advanceNote();
}
timeoutId = requestAnimationFrame(schedule)
}
function drawPlayhead(xindex) {
var lastIndex = (xindex + LOOP_LENGTH - 1) % LOOP_LENGTH;
//can change this to class selector to select a column
var $newRows = $('.column_' + xindex);
var $oldRows = $('.column_' + lastIndex);
$newRows.addClass("playing");
$oldRows.removeClass("playing");
}
function advanceNote() {
// Advance time by a 16th note...
// var secondsPerBeat = 60.0 / theBeat.tempo;
//TODO CHANGE TEMPO HERE, convert to float
tempo = Number($("#tempo-input").val());
var secondsPerBeat = 60.0 / tempo;
rhythmIndex++;
if (rhythmIndex == LOOP_LENGTH) {
rhythmIndex = 0;
}
//0.25 because each square is a 16th note
noteTime += 0.25 * secondsPerBeat
// if (rhythmIndex % 2) {
// noteTime += (0.25 + kMaxSwing * theBeat.swingFactor) * secondsPerBeat;
// } else {
// noteTime += (0.25 - kMaxSwing * theBeat.swingFactor) * secondsPerBeat;
// }
}
function handlePlay(event) {
rhythmIndex = 0;
noteTime = 0.0;
startTime = context.currentTime + 0.005;
schedule();
}
function handleStop(event) {
cancelAnimationFrame(timeoutId);
$(".pad").removeClass("playing");
}
function initializeTempo() {
$("#tempo-input").val(tempo);
}
function changeTempoListener() {
$("#increase-tempo").click(function() {
if (tempo < TEMPO_MAX) {
tempo += TEMPO_STEP;
$("#tempo-input").val(tempo);
}
});
$("#decrease-tempo").click(function() {
if (tempo > TEMPO_MIN) {
tempo -= TEMPO_STEP;
$("#tempo-input").val(tempo);
}
});
}
function __log(e, data) {
log.innerHTML += "\n" + e + " " + (data || '');
}
var audio_context;
var recorder;
function startUserMedia() {
var input = finalMixNode;
__log('Media stream created.');
input.start();
__log('Input connected to audio context destination.');
recorder = new Recorder(input);
__log('Recorder initialised.');
}
function startRecording(button) {
recorder && recorder.record();
button.disabled = true;
button.nextElementSibling.disabled = false;
__log('Recording...');
}
function stopRecording(button) {
recorder && recorder.stop();
button.disabled = true;
button.previousElementSibling.disabled = false;
__log('Stopped recording.');
// create WAV download link using audio data blob
createDownloadLink();
recorder.clear();
}
function createDownloadLink() {
recorder && recorder.exportWAV(function(blob) {
var url = URL.createObjectURL(blob);
var li = document.createElement('li');
var au = document.createElement('audio');
var hf = document.createElement('a');
au.controls = true;
au.src = url;
hf.href = url;
hf.download = new Date().toISOString() + '.wav';
hf.innerHTML = hf.download;
li.appendChild(au);
li.appendChild(hf);
recordingslist.appendChild(li);
});
}
window.onload = function init() {
try {
// webkit shim
window.AudioContext = window.AudioContext || window.webkitAudioContext;
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
window.URL = window.URL || window.webkitURL;
// audio_context = new AudioContext;
__log('Audio context set up.');
} catch (e) {
alert('No web audio support in this browser!');
}
startUserMedia();
};
Your finalMixNode is scoped into the initializeAudioNodes() function, therefore it is undefined when you call it from startUserMedia().
Also, this variable is either a dynamicCompressor node or the AudioContext's destination.
Recorderjs needs a node with an output (which AudioDestinationNode doesn't have currently1 ) so make sure to construct your recorder with the final compressor node (or a final gainNode)
Executing this in my js console from your page does work :
var recorder = new Recorder(compressor);
1 Thanks #padenot for noticing me it's being discussed here