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

Last change on this file since 28388 was 28388, checked in by davidb, 11 years ago

Set of JS, CSS, PNG etc web resources to support a mixture of audio player/document display capabilities

File size: 10.9 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
38 this.plotMapping[activeWorkflow.chromaCpt.transformMode](this);
39
40 this.prevXLine = null;
41 this.prevXLineMaxArray = null;
42 this.plottedDataBuffer = new Array();
43
44};
45
46
47FrequencyPlotter.prototype.reset = function() {
48 this.chromaTransform.reset();
49 this.prevXLine = null;
50 this.prevXLineMaxArray = null;
51 this.plottedDataBuffer = new Array();
52}
53
54
55FrequencyPlotter.prototype.plotDataLine = function(eventTime,data) {
56
57 var lineX = FrequencyPlotter.fudgeOffset + Math.round((eventTime/this.duration) * this.fudgeWidth);
58
59 var box_width = 1;
60 var box_height = Math.round(this.height/data.length);
61
62 var gapX = 0;
63
64 if (this.prevXLine!=null) {
65 gapX = lineX - this.prevXLine;
66
67 if (gapX==0) {
68 // layer max values on top of prevXLineMaxArray
69 for (var i=0; i<data.length; i++) {
70 this.prevXLineMaxArray[i] = Math.max(this.prevXLineMaxArray[i],data[i]);
71 }
72 }
73 else {
74 // on to a new column => lay in the values in 'data'
75 this.plottedDataBuffer[lineX-1] = new Float32Array(this.prevXLineMaxArray);
76
77 for (var i=0; i<data.length; i++) {
78 this.prevXLineMaxArray[i] = data[i];
79 }
80 }
81
82 if (gapX>0) {
83// box_width = gapX+1;
84 box_width = gapX;
85 }
86 }
87 else {
88 this.prevXLineMaxArray = new Float32Array(data.length);
89 for (var i=0; i<data.length; i++) {
90 this.prevXLineMaxArray[i] = data[i];
91 }
92 }
93
94 var transformMode = this.chromaTransform.transformMode;
95 var maxVal = this.cptMapping[transformMode].getMaxVal();
96
97 var $displayElem = $("#"+this.chromaTransform.getTransformMode()+"Max");
98 if ($displayElem.length>0) {
99 $displayElem.html(maxVal.toPrecision(4).substring(0,5));
100 }
101
102 for (var i=0; i<this.prevXLineMaxArray.length; i++) {
103
104 //var cv_perc = 100 * data[i] / maxVal;
105 var cv_perc = 100 * this.prevXLineMaxArray[i] / maxVal;
106
107 if (cv_perc>100) {
108 console.log("Percent exceeds 100%: cv_perc[ = " + i + "] = " + cv_perc + " => Capping to 100%");
109 cv_perc = 100;
110 }
111
112 var plot_color;
113 if (this.colorMappingMode == "rgb") {
114 var rr = Math.floor(255 * (cv_perc / 100));
115 plot_color = "rgb("+ rr +",0,0)";
116 }
117 else {
118 var hue = Math.floor(240 * ((100.0-cv_perc) / 100));
119 plot_color = "hsl("+ hue +",100%,50%)";
120 }
121
122 this.ctx.fillStyle=plot_color;
123 // //this.ctx.fillRect(lineX-gapX,i*box_height+1,box_width,box_height-1);
124 //this.ctx.fillRect(lineX,i*box_height+1,box_width,box_height-1);
125 this.ctx.fillRect(this.prevXLine,i*box_height+1,box_width,box_height-1);
126
127 //this.ctx.fillRect(0,0,200,400);
128
129/*
130 //var plot_color2 = "rgb(0,"+ i*20 +",0)";
131 var plot_color2 = "hsl(358,100%,50%)";
132 this.ctx.fillStyle=plot_color2;
133 this.ctx.fillRect(lineX-gapX,i*box_height+1,box_width,box_height-1);
134*/
135
136 }
137
138/*
139 if (gapX > 0) {
140 // on to a new column => lay in the values in 'data'
141 for (var i=0; i<data.length; i++) {
142 this.prevXLineMaxArray[i] = data[i];
143 }
144 }
145*/
146
147 //console.log("*** setting prevXLine = " + lineX);
148
149 this.prevXLine = lineX;
150 this.plottedDataBuffer[lineX] = new Float32Array(this.prevXLineMaxArray);
151
152};
153
154
155var _fpChangingMode = false;
156
157FrequencyPlotter.prototype.plotVisualLine = function(needsRefresh,eventTime) {
158
159
160 if (_fpChangingMode) {
161 return;
162 }
163
164 if (needsRefresh) {
165 // Clear the canvas before drawing line
166
167 this.ctx.clearRect(FrequencyPlotter.fudgeOffset,0, this.width, this.fudgeHeight);
168 this.label_ctx.clearRect(FrequencyPlotter.fudgeOffset,0, this.width, this.fudgeHeight);
169 }
170
171
172 //var dataPos = Math.round((eventTime * this.sampleRate)/(this.frameBufferLength*this.concatLimit));
173
174 var dataPos = Math.round((eventTime / this.duration) * this.totalNumFrames);
175
176 var dataSequence = this.dataMapping[this.chromaTransform.transformMode];
177
178 if (dataPos<dataSequence.length) {
179 var data = dataSequence[dataPos];
180
181 this.plotDataLine(eventTime,data);
182 }
183 else {
184 console.error("FrequencyPlotter::plotVisualLine(): No data to play for event time: " + eventTime);
185 }
186
187};
188
189FrequencyPlotter.prototype.replotVisual = function() {
190
191 //console.log("**** FrequencyPlotter.replotVisual()");
192
193 var transformMode = this.chromaTransform.transformMode;
194 var maxVal = this.cptMapping[transformMode].getMaxVal();
195
196 var prevX = -1;
197 var box_width = 0;
198
199 for (var x=0; x<this.plottedDataBuffer.length; x++) {
200 var data = this.plottedDataBuffer[x];
201
202 if (data != null) {
203
204 box_width = x - prevX;
205
206 var box_height = Math.round(this.height/data.length);
207
208 for (var i=0; i<data.length; i++) {
209
210 var cv_perc = 100 * data[i] / maxVal;
211
212 var plot_color;
213 if (this.colorMappingMode == "rgb") {
214 var rr = Math.floor(255 * (cv_perc / 100));
215 plot_color = "rgb("+ rr +",0,0)";
216 }
217 else {
218 var hue = Math.floor(240 * ((100.0-cv_perc) / 100));
219 plot_color = "hsl("+ hue +",100%,50%)";
220 }
221
222 this.ctx.fillStyle=plot_color;
223 this.ctx.fillRect(x,i*box_height+1,box_width,box_height-1);
224 }
225
226 prevX = x;
227 }
228 }
229}
230
231
232
233FrequencyPlotter.prototype.plotSelfSimilarityMatrix = function(ssm,pausePerc) {
234
235
236 var num_frames = ssm.length;
237
238 var activeWidth = this.fudgeWidth * pausePerc;
239 var activeHeight = this.height * pausePerc;
240
241 var x_scale = activeWidth / num_frames;
242 var y_scale = activeHeight / num_frames;
243
244 var x_width = Math.ceil(x_scale);
245 var y_height = Math.ceil(y_scale);
246
247 console.log("SelfSimilarityMatrix: x scale = " + x_scale);
248 console.log("SelfSimilarityMatrix: y scale = " + y_scale);
249
250 //console.log("box width = " + x_width);
251 //console.log("box hieght = " + y_height);
252
253 this.label_ctx.clearRect(0,0, this.width, this.height);
254
255 this.progress = 0;
256 var total_calcs = num_frames * num_frames;
257 var c=0;
258 var y=0;
259
260 (function(outerThis) {
261 // for (var y=0; y<num_frames; y++) {
262
263 for (var x=0; x<num_frames; x++) {
264
265 outerThis.progress = Math.round((100.0*c)/total_calcs);
266
267 var v = ssm[y][x];
268 var hue = Math.floor(240 * (1.0 - v));
269 var plot_color = "hsl("+ hue +",100%,50%)";
270
271 outerThis.ctx.fillStyle=plot_color;
272 outerThis.ctx.fillRect(FrequencyPlotter.fudgeOffset + (x*x_scale),y*y_scale,x_width,y_height);
273
274 c++;
275 }
276
277 y++;
278
279 if (y<num_frames) {
280 setTimeout(arguments.callee,0,outerThis);
281 }
282 else {
283 outerThis.progress = 100;
284 }
285 })(this);
286
287};
288
289var plotFrequencySpectrumLabels = function(fp) {
290};
291
292var plotFrequencyPowerSpectrumLabels = function(fp) {
293};
294
295var plotFilterBankLabels = function(fp) {
296
297 var transformMode = fp.chromaTransform.transformMode;
298 var numSteps = fp.cptMapping[transformMode].filterBankOut.length;
299
300 var stepY = Math.round(fp.height/numSteps);
301
302 var startX;
303 if (fp.prevXLine==null) {
304 startX=0;
305 }
306 else {
307 startX = fp.prevXLine+1;
308 }
309
310 //console.log("*** Filter bank start x = " + startX);
311
312 for (var i=0; i<numSteps; i++) {
313
314 fp.label_ctx.beginPath();
315 fp.label_ctx.moveTo(startX, i*stepY);
316 fp.label_ctx.lineTo(fp.width, i*stepY);
317 fp.label_ctx.strokeStyle = "white";
318 fp.label_ctx.stroke();
319 fp.label_ctx.closePath();
320 }
321
322 fp.label_ctx.fillStyle = "black";
323 fp.label_ctx.font = "bold 14px Arial";
324 fp.label_ctx.textBaseline = "top";
325
326 var x = FrequencyPlotter.fudgeOffset + fp.fudgeWidth + 5;
327 if (FrequencyPlotter.fudgeTrim==0) {
328 // A fudge on top of a fudge!
329 x -= 60;
330 }
331
332 var y = 1;
333 var loEdge = fp.chromaTransform.loEdge;
334
335 fp.label_ctx.fillText(loEdge.toPrecision(3) + " Hz", x,y);
336
337};
338
339var plotChromaFeaturesLabels = function(fp) {
340
341 var labels = ['C','C#/Db','D','D#/Eb','E','F','F#/Gb','G','G#/Ab','A','A#/Bb','B'];
342
343 fp.label_ctx.fillStyle = "black";
344 fp.label_ctx.font = "bold 14px Arial";
345 fp.label_ctx.textBaseline = "top";
346
347
348 // fp.ctx.lineWidth = 1;
349
350 var x = FrequencyPlotter.fudgeOffset + fp.fudgeWidth + 5;
351 if (FrequencyPlotter.fudgeTrim==0) {
352 // A fudge on top of a fudge!
353 x -= 50;
354 }
355
356 var stepY = fp.height/labels.length;
357
358 var startX;
359 if (fp.prevXLine==null) {
360 startX=0;
361 }
362 else {
363 startX = fp.prevXLine+1;
364 }
365
366 //console.log("*** Chromagram start x = " + startX);
367
368 for (var i=0; i<labels.length; i++) {
369
370 var y = 12 + i * stepY;
371
372 fp.label_ctx.fillText(labels[i], x,y);
373
374 fp.label_ctx.beginPath();
375 fp.label_ctx.moveTo(startX, i*stepY);
376 fp.label_ctx.lineTo(fp.width, i*stepY);
377 fp.label_ctx.strokeStyle = "white";
378 fp.label_ctx.stroke();
379 fp.label_ctx.closePath();
380 }
381
382};
383
384FrequencyPlotter.prototype.setTransformMode = function(transformMode) {
385
386 _fpChangingMode = true;
387
388 this.chromaTransform.setTransformMode(transformMode);
389
390 var startX;
391 if (this.prevXLine==null) {
392 startX=0;
393 }
394 else {
395 startX = this.prevXLine+1;
396 }
397
398 //this.ctx.clearRect(startX,0, this.width-startX, this.height);
399 this.label_ctx.clearRect(startX,0, this.width-startX, this.height);
400
401 this.plotMapping[transformMode](this);
402
403 _fpChangingMode = false;
404
405};
406
407
408FrequencyPlotter.prototype.setColorMappingMode = function(colorMappingMode) {
409 this.colorMappingMode = colorMappingMode;
410}
411
412// Guestimate for Firefox audio element
413//FrequencyPlotter.fudgeOffset = 28;
414//FrequencyPlotter.fudgeTrim = 58;
415
416// SoundManager2 settings
417FrequencyPlotter.fudgeOffset = 0;
418FrequencyPlotter.fudgeTrim = 0;
Note: See TracBrowser for help on using the repository browser.