source: gs3-extensions/web-audio/trunk/js-dsp/frequency-plotter.js@ 28548

Last change on this file since 28548 was 28548, checked in by davidb, 10 years ago

Changes after developing the demo for the SMAM-2013 keynote talk

File size: 13.2 KB
Line 
1var FrequencyPlotter = function(audio, canvas, label_canvas,activeWorkflow, colorMappingMode, totalNumFrames) {
2
3 this.canvas = canvas;
4 this.label_canvas = label_canvas;
5 this.chromaTransform = activeWorkflow.chromaCpt;;
6 this.colorMappingMode = colorMappingMode;
7
8 this.totalNumFrames = totalNumFrames;
9
10 this.width = canvas.width;
11 this.height = canvas.height;
12
13 this.fudgeWidth = this.width - (FrequencyPlotter.fudgeOffset + FrequencyPlotter.fudgeTrim);
14
15 //console.debug("frequency-plotter constructor(): Canvas w x h: [" + this.width +","+ this.height + "]");
16
17 this.duration = audio.duration;
18
19 this.dataMapping = { "freqSpectrum": activeWorkflow.freqSpectrumCpt.fft.spectrum,
20 "freqPowerSpectrum": activeWorkflow.freqSpectrumCpt.fft.powerSpectrum,
21 "filterBank": activeWorkflow.filterBankCpt.filterBankSequence,
22 "chromaFeatures": activeWorkflow.chromaCpt.chromaFeaturesSequence };
23
24 this.plotMapping = { "freqSpectrum": plotFrequencySpectrumLabels,
25 "freqPowerSpectrum": plotFrequencyPowerSpectrumLabels,
26 "filterBank": plotFilterBankLabels,
27 "chromaFeatures": plotChromaFeaturesLabels };
28
29
30 this.cptMapping = { "freqSpectrum": activeWorkflow.freqSpectrumCpt,
31 "freqPowerSpectrum": activeWorkflow.freqSpectrumCpt,
32 "filterBank": activeWorkflow.filterBankCpt,
33 "chromaFeatures": activeWorkflow.chromaCpt };
34
35 this.ctx = canvas.getContext('2d');
36 this.label_ctx = label_canvas.getContext('2d');
37 this.canvas_saved_img = null;
38
39 var image_server_url = "http://localhost:8181/greenstone3/cgi-bin/image-server.pl?a=load&c="+gs.cgiParams.c
40 +"&d="+gs.cgiParams.d+"&site="+gs.xsltParams.site_name;
41 var canvas_load_data = { a: "load",
42 d: gs.cgiParams.d,
43 c: gs.cgiParams.c,
44 site: gs.xsltParams.site_name };
45
46 var outerThis = this;
47 var jqxhr = $.ajax(image_server_url)
48 .done(function(imagedata) {
49 var check_prefix = imagedata.toString().substring(0, 5);
50 if (check_prefix == "data:") {
51 console.log("Loaded in previously stored self-similarity map");
52 outerThis.canvas_saved_img = document.createElement("img");
53 outerThis.canvas_saved_img.src = imagedata;
54 }
55 })
56 .fail(function() {
57 console.log("No currently stored self-similarity map for this document");
58 });
59
60
61 this.plotMapping[activeWorkflow.chromaCpt.transformMode](this);
62
63 this.prevXLine = null;
64 this.prevXLineMaxArray = null;
65 this.plottedDataBuffer = new Array();
66
67 this.printedOutOfDataWarning = false;
68
69};
70
71
72FrequencyPlotter.prototype.reset = function() {
73 this.chromaTransform.reset();
74 this.prevXLine = null;
75 this.prevXLineMaxArray = null;
76 this.plottedDataBuffer = new Array();
77}
78
79
80FrequencyPlotter.prototype.plotDataLine = function(eventTime,data) {
81
82 var lineX = FrequencyPlotter.fudgeOffset + Math.round((eventTime/this.duration) * this.fudgeWidth);
83
84 var box_width = 1;
85 var box_height = Math.round(this.height/data.length);
86
87 var gapX = 0;
88
89 if (this.prevXLine!=null) {
90 gapX = lineX - this.prevXLine;
91
92 if (gapX==0) {
93 // layer max values on top of prevXLineMaxArray
94 for (var i=0; i<data.length; i++) {
95 this.prevXLineMaxArray[i] = Math.max(this.prevXLineMaxArray[i],data[i]);
96 }
97 }
98 else {
99 // on to a new column => lay in the values in 'data'
100 this.plottedDataBuffer[lineX-1] = new Float32Array(this.prevXLineMaxArray);
101
102 for (var i=0; i<data.length; i++) {
103 this.prevXLineMaxArray[i] = data[i];
104 }
105 }
106
107 if (gapX>0) {
108// box_width = gapX+1;
109 box_width = gapX;
110 }
111 }
112 else {
113 this.prevXLineMaxArray = new Float32Array(data.length);
114 for (var i=0; i<data.length; i++) {
115 this.prevXLineMaxArray[i] = data[i];
116 }
117 }
118
119 var transformMode = this.chromaTransform.transformMode;
120 var maxVal = this.cptMapping[transformMode].getMaxVal();
121
122 var $displayElem = $("#"+this.chromaTransform.getTransformMode()+"Max");
123 if ($displayElem.length>0) {
124 $displayElem.html(maxVal.toPrecision(4).substring(0,5));
125 }
126
127 for (var i=0; i<this.prevXLineMaxArray.length; i++) {
128
129 //var cv_perc = 100 * data[i] / maxVal;
130 var cv_perc = 100 * this.prevXLineMaxArray[i] / maxVal;
131
132 if (cv_perc>100) {
133 console.log("Percent exceeds 100%: cv_perc[ = " + i + "] = " + cv_perc + " => Capping to 100%");
134 cv_perc = 100;
135 }
136
137 var plot_color;
138 if (this.colorMappingMode == "rgb") {
139 var rr = Math.floor(255 * (cv_perc / 100));
140 plot_color = "rgb("+ rr +",0,0)";
141 }
142 else {
143 var hue = Math.floor(240 * ((100.0-cv_perc) / 100));
144 plot_color = "hsl("+ hue +",100%,50%)";
145 }
146
147 this.ctx.fillStyle=plot_color;
148 // //this.ctx.fillRect(lineX-gapX,i*box_height+1,box_width,box_height-1);
149 //this.ctx.fillRect(lineX,i*box_height+1,box_width,box_height-1);
150 this.ctx.fillRect(this.prevXLine,i*box_height+1,box_width,box_height-1);
151
152 //this.ctx.fillRect(0,0,200,400);
153
154/*
155 //var plot_color2 = "rgb(0,"+ i*20 +",0)";
156 var plot_color2 = "hsl(358,100%,50%)";
157 this.ctx.fillStyle=plot_color2;
158 this.ctx.fillRect(lineX-gapX,i*box_height+1,box_width,box_height-1);
159*/
160
161 }
162
163/*
164 if (gapX > 0) {
165 // on to a new column => lay in the values in 'data'
166 for (var i=0; i<data.length; i++) {
167 this.prevXLineMaxArray[i] = data[i];
168 }
169 }
170*/
171
172 //console.log("*** setting prevXLine = " + lineX);
173
174 this.prevXLine = lineX;
175 this.plottedDataBuffer[lineX] = new Float32Array(this.prevXLineMaxArray);
176
177};
178
179
180var _fpChangingMode = false;
181
182FrequencyPlotter.prototype.plotVisualLine = function(needsRefresh,eventTime) {
183
184
185 if (_fpChangingMode) {
186 return;
187 }
188
189 if (needsRefresh) {
190 // Clear the canvas before drawing line
191
192 this.ctx.clearRect(FrequencyPlotter.fudgeOffset,0, this.width, this.fudgeHeight);
193 this.label_ctx.clearRect(FrequencyPlotter.fudgeOffset,0, this.width, this.fudgeHeight);
194 }
195
196
197 //var dataPos = Math.round((eventTime * this.sampleRate)/(this.frameBufferLength*this.concatLimit));
198
199 var dataPos = Math.round((eventTime / this.duration) * this.totalNumFrames);
200
201 var dataSequence = this.dataMapping[this.chromaTransform.transformMode];
202
203 if (dataPos<dataSequence.length) {
204 var data = dataSequence[dataPos];
205
206 this.plotDataLine(eventTime,data);
207 }
208 else {
209 if (!this.printedOutOfDataWarning) {
210 console.error("FrequencyPlotter::plotVisualLine(): No data to play for event time: " + eventTime + "\n"
211 + "(Supressing subsequent error messages of this type)");
212 this.printedOutOfDataWarning = true;
213 }
214
215 }
216
217};
218
219FrequencyPlotter.prototype.replotVisual = function() {
220
221 //console.log("**** FrequencyPlotter.replotVisual()");
222
223 var transformMode = this.chromaTransform.transformMode;
224 var maxVal = this.cptMapping[transformMode].getMaxVal();
225
226 var prevX = -1;
227 var box_width = 0;
228
229 for (var x=0; x<this.plottedDataBuffer.length; x++) {
230 var data = this.plottedDataBuffer[x];
231
232 if (data != null) {
233
234 box_width = x - prevX;
235
236 var box_height = Math.round(this.height/data.length);
237
238 for (var i=0; i<data.length; i++) {
239
240 var cv_perc = 100 * data[i] / maxVal;
241
242 var plot_color;
243 if (this.colorMappingMode == "rgb") {
244 var rr = Math.floor(255 * (cv_perc / 100));
245 plot_color = "rgb("+ rr +",0,0)";
246 }
247 else {
248 var hue = Math.floor(240 * ((100.0-cv_perc) / 100));
249 plot_color = "hsl("+ hue +",100%,50%)";
250 }
251
252 this.ctx.fillStyle=plot_color;
253 this.ctx.fillRect(x,i*box_height+1,box_width,box_height-1);
254 }
255
256 prevX = x;
257 }
258 }
259}
260
261
262FrequencyPlotter.prototype.hasStoredSelfSimilarityMatrix = function() {
263 return this.canvas_saved_img != null;
264}
265
266FrequencyPlotter.prototype.plotStoredSelfSimilarityMatrix = function() {
267
268 this.ctx.drawImage(this.canvas_saved_img,0,0);
269 //var img_data = canvas.toDataURL("image/png");
270}
271
272
273FrequencyPlotter.prototype.plotSelfSimilarityMatrix = function(ssm,pausePerc) {
274
275
276 var num_frames = ssm.length;
277
278 console.log("plotSelfSimilarityMatrex: num_frames = " + num_frames + ", pausePerc = " + pausePerc);
279 var activeWidth = this.fudgeWidth * pausePerc;
280 var activeHeight = this.height * pausePerc;
281
282 var x_scale = activeWidth / num_frames;
283 var y_scale = activeHeight / num_frames;
284 var x_width = Math.ceil(x_scale);
285 var y_height = Math.ceil(y_scale);
286
287 console.log("SelfSimilarityMatrix: x scale = " + x_scale);
288 console.log("SelfSimilarityMatrix: y scale = " + y_scale);
289
290 //console.log("box width = " + x_width);
291 //console.log("box hieght = " + y_height);
292
293 this.label_ctx.clearRect(0,0, this.width, this.height);
294
295 this.progress = 0;
296 var total_calcs = num_frames * num_frames;
297 var c=0;
298 var y=0;
299
300 (function(outerThis) {
301 // for (var y=0; y<num_frames; y++) {
302
303 for (var x=0; x<num_frames; x++) {
304
305 outerThis.progress = Math.round((100.0*c)/total_calcs);
306
307 var v = ssm[y][x];
308 var hue = Math.floor(240 * (1.0 - v));
309 var plot_color = "hsl("+ hue +",100%,50%)";
310
311 outerThis.ctx.fillStyle=plot_color;
312 outerThis.ctx.fillRect(FrequencyPlotter.fudgeOffset + (x*x_scale),y*y_scale,x_width,y_height);
313
314 c++;
315 }
316
317 y++;
318
319 if (y<num_frames) {
320 setTimeout(arguments.callee,0,outerThis);
321 }
322 else {
323 outerThis.progress = 100;
324
325 if ((jsMadProcessing == null) ||
326 ((jsMadProcessing != null) && jsMadProcessing.processingFinished())) {
327
328 var canvas = document.getElementById("freq-plot" );
329 var img_data = canvas.toDataURL("image/png");
330
331 outerThis.canvas_saved_img = document.createElement("img");
332 outerThis.canvas_saved_img.src = img_data;
333
334
335 var image_server_url = "http://localhost:8181/greenstone3/cgi-bin/image-server.pl";
336
337 var canvas_save_data = { a: "save",
338 d: gs.cgiParams.d,
339 c: gs.cgiParams.c,
340 site: gs.xsltParams.site_name,
341 imagedata: img_data };
342
343 $.post(image_server_url, canvas_save_data).done(function( data ) {
344 console.log( "Image data saved " );
345 });
346/*
347 $.ajax({
348 type: "POST",
349 url: url,
350 data: { "a" : "save" }
351 success: success,
352 dataType: dataType
353 });
354*/
355
356 console.log("Done save");
357
358 }
359 }
360 })(this);
361
362};
363
364var plotFrequencySpectrumLabels = function(fp) {
365};
366
367var plotFrequencyPowerSpectrumLabels = function(fp) {
368};
369
370var plotFilterBankLabels = function(fp) {
371
372 var transformMode = fp.chromaTransform.transformMode;
373 var numSteps = fp.cptMapping[transformMode].filterBankOut.length;
374
375 var stepY = Math.round(fp.height/numSteps);
376
377 var startX;
378 if (fp.prevXLine==null) {
379 startX=0;
380 }
381 else {
382 startX = fp.prevXLine+1;
383 }
384
385 //console.log("*** Filter bank start x = " + startX);
386
387 for (var i=0; i<numSteps; i++) {
388
389 fp.label_ctx.beginPath();
390 fp.label_ctx.moveTo(startX, i*stepY);
391 fp.label_ctx.lineTo(fp.width, i*stepY);
392 fp.label_ctx.strokeStyle = "white";
393 fp.label_ctx.stroke();
394 fp.label_ctx.closePath();
395 }
396
397 fp.label_ctx.fillStyle = "black";
398 fp.label_ctx.font = "bold 14px Arial";
399 fp.label_ctx.textBaseline = "top";
400
401 var x = FrequencyPlotter.fudgeOffset + fp.fudgeWidth + 5;
402 if (FrequencyPlotter.fudgeTrim==0) {
403 // A fudge on top of a fudge!
404 x -= 60;
405 }
406
407 var y = 1;
408 var loEdge = fp.chromaTransform.loEdge;
409
410 fp.label_ctx.fillText(loEdge.toPrecision(3) + " Hz", x,y);
411
412};
413
414var plotChromaFeaturesLabels = function(fp) {
415
416 var labels = ['C','C#/Db','D','D#/Eb','E','F','F#/Gb','G','G#/Ab','A','A#/Bb','B'];
417
418 fp.label_ctx.fillStyle = "black";
419 fp.label_ctx.font = "bold 14px Arial";
420 fp.label_ctx.textBaseline = "top";
421
422
423 // fp.ctx.lineWidth = 1;
424
425 var x = FrequencyPlotter.fudgeOffset + fp.fudgeWidth + 5;
426 if (FrequencyPlotter.fudgeTrim==0) {
427 // A fudge on top of a fudge!
428 x -= 50;
429 }
430
431 var stepY = fp.height/labels.length;
432
433 var startX;
434 if (fp.prevXLine==null) {
435 startX=0;
436 }
437 else {
438 startX = fp.prevXLine+1;
439 }
440
441 //console.log("*** Chromagram start x = " + startX);
442
443 for (var i=0; i<labels.length; i++) {
444
445 var y = 12 + i * stepY;
446
447 fp.label_ctx.fillText(labels[i], x,y);
448
449 fp.label_ctx.beginPath();
450 fp.label_ctx.moveTo(startX, i*stepY);
451 fp.label_ctx.lineTo(fp.width, i*stepY);
452 fp.label_ctx.strokeStyle = "white";
453 fp.label_ctx.stroke();
454 fp.label_ctx.closePath();
455 }
456
457};
458
459FrequencyPlotter.prototype.setTransformMode = function(transformMode) {
460
461 _fpChangingMode = true;
462
463 this.chromaTransform.setTransformMode(transformMode);
464
465 var startX;
466 if (this.prevXLine==null) {
467 startX=0;
468 }
469 else {
470 startX = this.prevXLine+1;
471 }
472
473 //this.ctx.clearRect(startX,0, this.width-startX, this.height);
474 this.label_ctx.clearRect(startX,0, this.width-startX, this.height);
475
476 this.plotMapping[transformMode](this);
477
478 _fpChangingMode = false;
479
480};
481
482
483FrequencyPlotter.prototype.setColorMappingMode = function(colorMappingMode) {
484 this.colorMappingMode = colorMappingMode;
485}
486
487// Guestimate for Firefox audio element
488//FrequencyPlotter.fudgeOffset = 28;
489//FrequencyPlotter.fudgeTrim = 58;
490
491// SoundManager2 settings
492FrequencyPlotter.fudgeOffset = 0;
493FrequencyPlotter.fudgeTrim = 0;
Note: See TracBrowser for help on using the repository browser.