PageSourceSearch

https://funky.radio/wp-content/plugins/qtmplayer/assets/js/qtmplayer-waveform.js?ver=PR.9.5

js funky.radio collected 2026-10-02 05:33:29 UTC 12,437 bytes, 383 lines download raw bytes

1// Igor 
2jQuery.storedPeaks = [];
3
4
5function qtWaveformDebug(m){
6	if( jQuery('#qtmusicplayer').data('debug') ){
7		var c = jQuery("#qtmPlayerDebugger");
8		if(c.length > 0){
9			c.append(m+'<br>');
10		}
11	}
12}
13
14
15/**
16 * Retrieves audio from an external source, the initializes the drawing function
17 * @param {String} url the url of the audio we'd like to fetch
18 */
19var qtDrawAudio = url => {
20
21	if(jQuery('#qtmplayerWaveform').length <= 0){
22		return;
23	}
24	// Set up audio context
25	window.AudioContext = window.AudioContext || window.webkitAudioContext;
26	const audioContext = new AudioContext();
27	jQuery('#qtmplayerWaveform canvas').remove();
28	jQuery('#qtmplayerWaveform').append('<canvas id="qtwaveformOriginal"></canvas><canvas id="qtwaveformClone"></canvas>');//.append('<canvas></canvas>');
29	jQuery.qtmplayerCanvas = jQuery('#qtmplayerWaveform #qtwaveformOriginal');
30	jQuery.qtmplayerCanvasClone = jQuery('#qtmplayerWaveform #qtwaveformClone');
31	jQuery.qtmplayerCanvasColor = jQuery('#qtmplayerWaveform').data('qtmplayer-color');
32	jQuery.qtmplayerCanvasCloneColor = jQuery('#qtmplayerWaveform').data('qtmplayer-color-clone');
33	jQuery('#qtmplayerTrackControl canvas').css({position: "absolute", 'top':0,'left':0,'width':'100%','height':'100%'});
34
35	if( undefined === jQuery.storedPeaks[url] ){
36		var storedSongPeaks = false;
37		/**
38		*	first try to get peaks from the database
39		*/
40		jQuery.ajax({
41			type: "post",
42			url: qtmplayer_ajax_var.url,
43			data: {
44				'action': 'qtmplayer-get-peaks',
45				'nonce': qtmplayer_ajax_var.peaksnonce,
46				'url': url
47			},
48			success: function( response ){
49				if(!response || response == '' || response == null){
50
51					fetch(url)
52					.then(response => response.arrayBuffer())
53					.then(arrayBuffer => audioContext.decodeAudioData(arrayBuffer))
54					.then(audioBuffer => draw(url,normalizeData(filterData(audioBuffer)),true)); // third parameter: store the data?
55				} else {
56					var peaks = jQuery.parseJSON(response );
57					jQuery.storedPeaks[url] = peaks;
58					draw(url, peaks, false);
59				}
60			},
61			fail: function(e){
62				console.log( e );
63			}
64		});
65	} else {
66		draw(url, jQuery.storedPeaks[url], false);
67	}
68};
69
70
71
72
73
74
75
76
77
78
79
80
81/**
82 * Filters the AudioBuffer retrieved from an external source
83 * @param {AudioBuffer} audioBuffer the AudioBuffer from drawAudio()
84 * @returns {Array} an array of floating point numbers
85 */
86var filterData = audioBuffer => {
87	const rawData = audioBuffer.getChannelData(0); // We only need to work with one channel of data
88	let samples = 120; 
89	if( jQuery('#qtmplayer').data('qtmplayer-detailedwaveform') == 1 ){
90		samples = 271; // Number of samples we want to have in our final data set
91	} 
92
93	const blockSize = Math.floor(rawData.length / samples); // the number of samples in each subdivision
94	const filteredData = [];
95	for (let i = 0; i < samples; i++) {
96		let blockStart = blockSize * i; // the location of the first sample in the block
97		let sum = 0;
98		for (let j = 0; j < blockSize; j++) {
99			sum = sum + Math.abs(rawData[blockStart + j]); // find the sum of all the samples in the block
100		}
101		filteredData.push(sum / blockSize); // divide the sum by the block size to get the average
102	}
103	return filteredData;
104};
105
106/**
107 * Normalizes the audio data to make a cleaner illustration 
108 * @param {Array} filteredData the data from filterData()
109 * @returns {Array} an normalized array of floating point numbers
110 */
111var normalizeData = filteredData => {
112	const multiplier = Math.pow(Math.max(...filteredData), -1);
113	let normalized = filteredData.map(n => (n *  multiplier ).toFixed(2) );
114	return normalized;
115}
116
117
118
119/**
120 * Draws the audio file into a canvas element.
121 * @param {Array} normalizedData The filtered array returned from filterData()
122 * @returns {Array} a normalized array of data 
123 */
124const drawDetail = ( url, normalizedData, storeData) => {
125	
126	
127	const dpr = window.devicePixelRatio || 1;
128	const gap = 1;
129	const padding = 1;
130	let x;
131	jQuery.storedPeaks[url] = normalizedData;// Save the peaks associated with the URL to avoid reloading the same file again
132
133
134
135	const canvas = jQuery.qtmplayerCanvas[0];;
136	canvas.width = canvas.offsetWidth * dpr;
137	canvas.height = (canvas.offsetHeight + padding * 2) * dpr;
138	const ctx = canvas.getContext("2d");
139	ctx.scale(dpr, -dpr);
140	ctx.translate(0, - canvas.offsetHeight - padding- padding  ); // set Y = 0 to be in the middle of the canvas
141	const width =  (canvas.offsetWidth / normalizedData.length).toFixed(3);
142	
143
144
145	ctx.beginPath();
146	ctx.moveTo( 0, 0 );
147	for ( let i = 0; i < normalizedData.length; i++ ) {
148		let x = width * i;
149		let height = normalizedData[i] * canvas.offsetHeight - padding +2;
150		ctx.moveTo( x, 0 );
151		ctx.lineTo( x, height );
152		ctx.lineTo( Math.round(width - gap + x)  , height);
153		ctx.lineTo( Math.round(width - gap + x), 0);
154		ctx.lineTo(  Math.round(width + x) , 0);
155	}
156	// Clone for tracking
157	ctx.moveTo( 0, 0 );
158	ctx.fillStyle= jQuery.qtmplayerCanvasColor;
159	ctx.fill();
160
161
162	// ==========================================================================================================
163	// The clone
164	// ==========================================================================================================
165	const canvasClone = jQuery.qtmplayerCanvasClone[0];//document.querySelector("canvas");
166	canvasClone.width = canvasClone.offsetWidth * dpr;
167	canvasClone.height = (canvasClone.offsetHeight + padding * 2) * dpr;
168	let ctxClone = canvasClone.getContext("2d");
169	ctxClone.scale(dpr, -dpr);
170	ctxClone.translate(0, - canvasClone.offsetHeight - padding - padding  ); // set Y = 0 to be in the middle of the canvas
171	ctxClone.beginPath();
172	ctxClone.moveTo( 0, 0 );
173	// Draw the upper path.
174	for ( let i = 0; i < normalizedData.length; i++ ) {
175		let x = width * i;
176		let height = normalizedData[i] * canvas.offsetHeight - padding +2;
177
178		ctxClone.moveTo( x, 0 );
179		ctxClone.lineTo( x, height );
180		ctxClone.lineTo( Math.round(width - gap + x)  , height);
181		ctxClone.lineTo( Math.round(width - gap + x), 0);
182		ctxClone.lineTo( Math.round(width + x) , 0);
183	}
184	// Clone for tracking
185	ctxClone.moveTo( 0, 0 );
186	
187	ctxClone.fillStyle = jQuery.qtmplayerCanvasCloneColor;
188	ctxClone.fill();
189
190
191	let trackContainer = jQuery('[data-qtmplayer-file="'+url+'"]').closest('.qtmplayer-track-inpage');
192	// console.log(url);
193	if( trackContainer.length > 0  ){
194		trackContainer.each(function(i,c){
195			var thisTrackContainer = jQuery(c);
196			thisTrackContainer.find('canvas').remove();
197			thisTrackContainer.css({'position':'relative'}).append('<canvas>');
198			jQuery.qtmplayeCurrentCanvas = thisTrackContainer.find('canvas');
199			jQuery.qtmplayeCurrentCanvas.css({"position": "absolute", "top": 0, "left": '0', "width": "100%", "height": "100%", 'background':'transparent', 'pointer-events':'none'});
200			var canvasC = jQuery.qtmplayeCurrentCanvas[0];
201			canvasC.width = canvasC.offsetWidth * dpr;
202			canvasC.height = (canvasC.offsetHeight + padding * 2) * dpr;
203			var ctxC = canvasC.getContext("2d");
204			ctxC.scale(dpr, -dpr);
205			ctxC.translate(0, - canvasC.offsetHeight - padding- padding  ); // set Y = 0 to be in the middle of the canvas
206			var widthC = (canvasC.offsetWidth / normalizedData.length).toFixed(1) ;
207			ctxC.beginPath();
208			ctxC.moveTo( 0, 0 );
209			for ( let i = 0; i < normalizedData.length; i++ ) {
210				let x = widthC * i;
211				let height = normalizedData[i] * canvasC.offsetHeight - padding + 3;
212				ctxC.moveTo( x, 0 );
213				ctxC.lineTo( x, height );
214				ctxC.lineTo( Math.round(widthC - gap + x)  , height);
215				ctxC.lineTo( Math.round(widthC - gap + x), 0);
216				ctxC.lineTo( widthC + x , 0);
217			}
218			// Clone for tracking
219			ctxC.moveTo( 0, 0 );
220			ctxC.fillStyle= "rgba(210,210,210,0.5)";
221			ctxC.fill();
222		});
223
224			
225	}
226
227
228
229	// CREATE CACHE ===========================================================================================
230	if(true == storeData){
231		jQuery.ajax({
232			type: "post",
233			url: qtmplayer_ajax_var.url,
234			data: {
235				'action': 'qtmplayer-store-peaks',
236				'nonce': qtmplayer_ajax_var.peaksnonce,
237				'url': url,
238				'peaks': normalizedData
239			},
240			success: function( response ){
241				return true;
242			},
243			fail: function(e){
244				console.log( e );
245				return false;
246			}
247		});
248	}
249};
250
251
252
253
254
255
256
257
258/**
259 * A utility function for drawing our line segments
260 * @param {AudioContext} ctx the audio context 
261 * @param {number} x  the x coordinate of the beginning of the line segment
262 * @param {number} height the desired height of the line segment
263 * @param {number} width the desired width of the line segment
264 * @param {boolean} isEven whether or not the segmented is even-numbered
265 */
266var drawLineSegment = (ctx, x, height, width, isEven, color) => {
267	ctx.lineWidth = 1; // how thick the line is
268	ctx.strokeStyle =color; // what color our line is
269	ctx.beginPath();
270	height = isEven ? height : -height;
271	ctx.moveTo(x, 0);
272	ctx.lineTo(x, height);
273	ctx.arc(x + width / 2, height, width / 2, Math.PI, 0, isEven);
274	ctx.lineTo(x + width, 0);
275	ctx.stroke();
276};
277
278
279
280
281/**
282 * Draws the audio file into a canvas element.
283 * @param {Array} normalizedData The filtered array returned from filterData()
284 * @returns {Array} a normalized array of data 
285 */
286var draw = ( url, normalizedData, storeData) => {
287	const UseDetailWaveform = jQuery('#qtmplayer').data('qtmplayer-detailedwaveform');
288	if(UseDetailWaveform == 1){
289		drawDetail( url, normalizedData, storeData );
290		return;
291	}
292	const canvas = jQuery.qtmplayerCanvas[0];//document.querySelector("canvas");
293	const dpr = window.devicePixelRatio || 1;
294	const padding = 14;
295	canvas.width = canvas.offsetWidth * dpr;
296	canvas.height = (canvas.offsetHeight + padding * 2) * dpr;
297	const ctx = canvas.getContext("2d");
298	ctx.scale(dpr, dpr);
299	ctx.translate(0, canvas.offsetHeight / 2 + padding); // set Y = 0 to be in the middle of the canvas
300	// draw the line segments
301	const width = canvas.offsetWidth / normalizedData.length;
302	jQuery.storedPeaks[url] = normalizedData;// Save the peaks associated with the URL to avoid reloading the same file again
303	
304
305	if(true == storeData){
306		jQuery.ajax({
307			type: "post",
308			url: qtmplayer_ajax_var.url,
309			data: {
310				'action': 'qtmplayer-store-peaks',
311				'nonce': qtmplayer_ajax_var.peaksnonce,
312				'url': url,
313				'peaks': normalizedData
314			},
315			success: function( response ){
316				return true;
317			},
318			fail: function(e){
319				console.log( e );
320				return false;
321			}
322		});
323	}
324	for (let i = 0; i < normalizedData.length; i++) {
325		const x = width * i;
326		let height = normalizedData[i] * canvas.offsetHeight - padding;
327		if (height < 0) {
328				height = 0;
329		} else if (height > canvas.offsetHeight / 2) {
330				height = height > canvas.offsetHeight / 2;
331		}
332		drawLineSegment(ctx, x, height, width, (i + 1) % 2, jQuery.qtmplayerCanvasColor);
333	}
334
335
336	// ==========================================================================================================
337	// The clone
338	// ==========================================================================================================
339	const canvasClone = jQuery.qtmplayerCanvasClone[0];//document.querySelector("canvas");
340	canvasClone.width = canvasClone.offsetWidth * dpr;
341	canvasClone.height = (canvasClone.offsetHeight + padding * 2) * dpr;
342	const ctxClone = canvasClone.getContext("2d");
343	ctxClone.scale(dpr, dpr);
344	ctxClone.translate(0, canvas.offsetHeight / 2 + padding); // set Y = 0 to be in the middle of the canvas
345	
346	// Draw the upper path.
347	for ( let i = 0; i < normalizedData.length; i++ ) {
348		const x = width * i;
349		let height = normalizedData[i] * canvas.offsetHeight - padding;
350		if (height < 0) {
351				height = 0;
352		} else if (height > canvas.offsetHeight / 2) {
353				height = height > canvas.offsetHeight / 2;
354		}
355		drawLineSegment(ctxClone, x, height, width, (i + 1) % 2, jQuery.qtmplayerCanvasCloneColor);
356	}
357
358};
359
360
361
362
363
364
365/**
366 * A utility function for drawing our line segments
367 * @param {AudioContext} ctx the audio context 
368 * @param {number} x  the x coordinate of the beginning of the line segment
369 * @param {number} height the desired height of the line segment
370 * @param {number} width the desired width of the line segment
371 * @param {boolean} isEven whether or not the segmented is even-numbered
372 */
373var drawLineSegment = (ctx, x, height, width, isEven, color) => {
374	ctx.lineWidth = 1; // how thick the line is
375	ctx.strokeStyle =color; // what color our line is
376	ctx.beginPath();
377	height = isEven ? height : -height;
378	ctx.moveTo(x, 0);
379	ctx.lineTo(x, height);
380	ctx.arc(x + width / 2, height, width / 2, Math.PI, 0, isEven);
381	ctx.lineTo(x + width, 0);
382	ctx.stroke();
383};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.