comparison core.js @ 1430:e3ff8b7d6538

Automatic Loudness normalisation to -23 LUFS
author Nicholas Jillings <nickjillings@users.noreply.github.com>
date Wed, 16 Dec 2015 12:15:18 +0000
parents 61d11e39bdab
children a75a4e0af44f
comparison
equal deleted inserted replaced
1429:c06930d14ff7 1430:e3ff8b7d6538
17 var projectReturn; // Hold the URL for the return 17 var projectReturn; // Hold the URL for the return
18 18
19 19
20 // Add a prototype to the bufferSourceNode to reference to the audioObject holding it 20 // Add a prototype to the bufferSourceNode to reference to the audioObject holding it
21 AudioBufferSourceNode.prototype.owner = undefined; 21 AudioBufferSourceNode.prototype.owner = undefined;
22 // Add a prototype to the bufferNode to hold the desired LINEAR gain
23 AudioBuffer.prototype.gain = undefined;
24 // Add a prototype to the bufferNode to hold the computed LUFS loudness
25 AudioBuffer.prototype.lufs = undefined;
22 26
23 window.onload = function() { 27 window.onload = function() {
24 // Function called once the browser has loaded all files. 28 // Function called once the browser has loaded all files.
25 // This should perform any initial commands such as structure / loading documents 29 // This should perform any initial commands such as structure / loading documents
26 30
111 break; 115 break;
112 } 116 }
113 } 117 }
114 if (buffer == null) 118 if (buffer == null)
115 { 119 {
116 buffer = new audioEngineContext.bufferObj(URL); 120 buffer = new audioEngineContext.bufferObj();
121 buffer.getMedia(URL);
117 audioEngineContext.buffers.push(buffer); 122 audioEngineContext.buffers.push(buffer);
118 } 123 }
119 }); 124 });
120 }); 125 });
121 126
755 760
756 // Create store for new audioObjects 761 // Create store for new audioObjects
757 this.audioObjects = []; 762 this.audioObjects = [];
758 763
759 this.buffers = []; 764 this.buffers = [];
760 this.bufferObj = function(url) 765 this.bufferObj = function()
761 { 766 {
762 this.url = url; 767 this.url = null;
763 this.buffer = null; 768 this.buffer = null;
764 this.xmlRequest = new XMLHttpRequest(); 769 this.xmlRequest = new XMLHttpRequest();
765 this.users = []; 770 this.users = [];
766 this.xmlRequest.open('GET',this.url,true); 771 this.getMedia = function(url) {
767 this.xmlRequest.responseType = 'arraybuffer'; 772 this.url = url;
768 773 this.xmlRequest.open('GET',this.url,true);
769 var bufferObj = this; 774 this.xmlRequest.responseType = 'arraybuffer';
770 775
771 // Create callback to decode the data asynchronously 776 var bufferObj = this;
772 this.xmlRequest.onloadend = function() { 777
773 audioContext.decodeAudioData(bufferObj.xmlRequest.response, function(decodedData) { 778 // Create callback to decode the data asynchronously
774 bufferObj.buffer = decodedData; 779 this.xmlRequest.onloadend = function() {
775 for (var i=0; i<bufferObj.users.length; i++) 780 audioContext.decodeAudioData(bufferObj.xmlRequest.response, function(decodedData) {
776 { 781 bufferObj.buffer = decodedData;
777 bufferObj.users[i].state = 1; 782 for (var i=0; i<bufferObj.users.length; i++)
778 if (bufferObj.users[i].interfaceDOM != null)
779 { 783 {
780 bufferObj.users[i].interfaceDOM.enable(); 784 bufferObj.users[i].state = 1;
785 if (bufferObj.users[i].interfaceDOM != null)
786 {
787 bufferObj.users[i].interfaceDOM.enable();
788 }
781 } 789 }
782 } 790 calculateLoudness(bufferObj.buffer,"I");
783 }, function(){ 791 }, function(){
784 // Should only be called if there was an error, but sometimes gets called continuously 792 // Should only be called if there was an error, but sometimes gets called continuously
785 // Check here if the error is genuine 793 // Check here if the error is genuine
786 if (bufferObj.buffer == undefined) { 794 if (bufferObj.buffer == undefined) {
787 // Genuine error 795 // Genuine error
788 console.log('FATAL - Error loading buffer on '+audioObj.id); 796 console.log('FATAL - Error loading buffer on '+audioObj.id);
789 if (request.status == 404) 797 if (request.status == 404)
790 { 798 {
791 console.log('FATAL - Fragment '+audioObj.id+' 404 error'); 799 console.log('FATAL - Fragment '+audioObj.id+' 404 error');
792 console.log('URL: '+audioObj.url); 800 console.log('URL: '+audioObj.url);
793 errorSessionDump('Fragment '+audioObj.id+' 404 error'); 801 errorSessionDump('Fragment '+audioObj.id+' 404 error');
802 }
794 } 803 }
795 } 804 });
796 }); 805 };
806 this.xmlRequest.send();
797 }; 807 };
798 this.xmlRequest.send();
799 }; 808 };
800 809
801 this.play = function(id) { 810 this.play = function(id) {
802 // Start the timer and set the audioEngine state to playing (1) 811 // Start the timer and set the audioEngine state to playing (1)
803 if (this.status == 0 && this.loopPlayback) { 812 if (this.status == 0 && this.loopPlayback) {
836 { 845 {
837 if (i != id) { 846 if (i != id) {
838 this.audioObjects[i].outputGain.gain.value = 0.0; 847 this.audioObjects[i].outputGain.gain.value = 0.0;
839 this.audioObjects[i].stop(); 848 this.audioObjects[i].stop();
840 } else if (i == id) { 849 } else if (i == id) {
841 this.audioObjects[id].outputGain.gain.value = this.audioObjects[id].specification.gain; 850 this.audioObjects[id].outputGain.gain.value = this.audioObjects[id].specification.gain*this.audioObjects[id].buffer.buffer.gain;
842 this.audioObjects[id].play(audioContext.currentTime+0.01); 851 this.audioObjects[id].play(audioContext.currentTime+0.01);
843 } 852 }
844 } 853 }
845 } 854 }
846 interfaceContext.playhead.start(); 855 interfaceContext.playhead.start();
879 } 888 }
880 } 889 }
881 if (buffer == null) 890 if (buffer == null)
882 { 891 {
883 console.log("[WARN]: Buffer was not loaded in pre-test! "+URL); 892 console.log("[WARN]: Buffer was not loaded in pre-test! "+URL);
884 buffer = new this.bufferObj(URL); 893 buffer = new this.bufferObj();
894 buffer.getMedia(URL);
885 this.buffers.push(buffer); 895 this.buffers.push(buffer);
886 } 896 }
887 this.audioObjects[audioObjectId].specification = element; 897 this.audioObjects[audioObjectId].specification = element;
888 this.audioObjects[audioObjectId].url = URL; 898 this.audioObjects[audioObjectId].url = URL;
889 this.audioObjects[audioObjectId].buffer = buffer; 899 this.audioObjects[audioObjectId].buffer = buffer;
929 }; 939 };
930 940
931 this.setSynchronousLoop = function() { 941 this.setSynchronousLoop = function() {
932 // Pads the signals so they are all exactly the same length 942 // Pads the signals so they are all exactly the same length
933 var length = 0; 943 var length = 0;
934 var lens = [];
935 var maxId; 944 var maxId;
936 for (var i=0; i<this.audioObjects.length; i++) 945 for (var i=0; i<this.audioObjects.length; i++)
937 { 946 {
938 lens.push(this.audioObjects[i].buffer.buffer.length);
939 if (length < this.audioObjects[i].buffer.buffer.length) 947 if (length < this.audioObjects[i].buffer.buffer.length)
940 { 948 {
941 length = this.audioObjects[i].buffer.buffer.length; 949 length = this.audioObjects[i].buffer.buffer.length;
942 maxId = i; 950 maxId = i;
943 } 951 }
944 } 952 }
945 // Perform difference
946 for (var i=0; i<lens.length; i++)
947 {
948 lens[i] = length - lens[i];
949 }
950 // Extract the audio and zero-pad 953 // Extract the audio and zero-pad
951 for (var i=0; i<lens.length; i++) 954 for (var i=0; i<this.audioObjects.length; i++)
952 { 955 {
953 var orig = this.audioObjects[i].buffer.buffer; 956 var orig = this.audioObjects[i].buffer.buffer;
954 var hold = audioContext.createBuffer(orig.numberOfChannels,length,orig.sampleRate); 957 var hold = audioContext.createBuffer(orig.numberOfChannels,length,orig.sampleRate);
955 for (var c=0; c<orig.numberOfChannels; c++) 958 for (var c=0; c<orig.numberOfChannels; c++)
956 { 959 {
957 var inData = hold.getChannelData(c); 960 var inData = hold.getChannelData(c);
958 var outData = orig.getChannelData(c); 961 var outData = orig.getChannelData(c);
959 for (var n=0; n<orig.length; n++) 962 for (var n=0; n<orig.length; n++)
960 {inData[n] = outData[n];} 963 {inData[n] = outData[n];}
961 } 964 }
965 hold.gain = orig.gain;
966 hold.lufs = orig.lufs;
962 this.audioObjects[i].buffer.buffer = hold; 967 this.audioObjects[i].buffer.buffer = hold;
963 delete orig;
964 } 968 }
965 }; 969 };
966 970
967 } 971 }
968 972
992 // the audiobuffer is not designed for multi-start playback 996 // the audiobuffer is not designed for multi-start playback
993 // When stopeed, the buffer node is deleted and recreated with the stored buffer. 997 // When stopeed, the buffer node is deleted and recreated with the stored buffer.
994 this.buffer; 998 this.buffer;
995 999
996 this.loopStart = function() { 1000 this.loopStart = function() {
997 this.outputGain.gain.value = 1.0; 1001 this.outputGain.gain.value = this.specification.gain*this.buffer.buffer.gain;
998 this.metric.startListening(audioEngineContext.timer.getTestTime()); 1002 this.metric.startListening(audioEngineContext.timer.getTestTime());
999 }; 1003 };
1000 1004
1001 this.loopStop = function() { 1005 this.loopStop = function() {
1002 if (this.outputGain.gain.value != 0.0) { 1006 if (this.outputGain.gain.value != 0.0) {