javascript - recorderJS record/download audio buffer WEB AUDIO API -
i want record audio output simple drum sequencer , export download wav file. have live link current attempt @ implementation attempt.
the sum output of sequencer routed variable finalmixnode
yet setting input recorder.js doesn't work. think may problem audio context can't figure out. created oscillator , recorded output can't extend sequencer.
here main js code in trying record output. i'm hoping see 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 quality multiplier, 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 dynamics compressor sweeten overall mix. compressor = context.createdynamicscompressor(); compressor.connect(context.destination); finalmixnode = compressor; } else { // no compressor available in implementation. finalmixnode = context.destination; } // create master volume. // now, master volume static, in future there slider mastergainnode = context.creategain(); mastergainnode.gain.value = 0.7; // reduce overall volume avoid clipping mastergainnode.connect(finalmixnode); //connect sounds mastergainnode play them //don't need now, no wet dry mix effects // // create effect volume. // effectlevelnode = context.creategain(); // effectlevelnode.gain.value = 1.0; // effect level slider controls // effectlevelnode.connect(mastergainnode); // create convolver effect convolver = context.createconvolver(); convolver.active = false; // convolver.connect(effectlevelnode); //create low pass filter lowpassfilternode = context.createbiquadfilter(); //this backwards compatibility, type used integer lowpassfilternode.type = (typeof lowpassfilternode.type === 'string') ? 'lowpass' : 0; // lowpass //default value max cutoff, or passing frequencies lowpassfilternode.frequency.value = context.samplerate / 2; lowpassfilternode.connect(mastergainnode); lowpassfilternode.active = false; } function loadkits() { //name must same path var kit = new kit("tr808"); kit.load(); //todo: figure out how test if kit loaded currentkit = kit; } function loadimpulseresponses() { reverbimpulseresponse = new impulseresponse("sounds/impulse- responses/matrix-reverb2.wav"); reverbimpulseresponse.load(); } //todo delete 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 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; // sequence starts @ starttime, normalize currenttime it's 0 @ start of 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 buffer //store data element in row tells 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 class selector select column var $newrows = $('.column_' + xindex); var $oldrows = $('.column_' + lastindex); $newrows.addclass("playing"); $oldrows.removeclass("playing"); } function advancenote() { // advance time 16th note... // var secondsperbeat = 60.0 / thebeat.tempo; //todo change tempo here, convert float tempo = number($("#tempo-input").val()); var secondsperbeat = 60.0 / tempo; rhythmindex++; if (rhythmindex == loop_length) { rhythmindex = 0; } //0.25 because each square 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 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 browser!'); } startusermedia(); };
your finalmixnode
scoped initializeaudionodes()
function, therefore undefined when call startusermedia()
.
also, variable either dynamiccompressor node or audiocontext's destination.
recorderjs needs node output (which audiodestinationnode doesn't have currently1 ) make sure construct recorder final compressor node (or final gainnode)
executing in js console page work :
var recorder = new recorder(compressor);
1 @padenot noticing me it's being discussed here
Comments
Post a Comment