Benutzer:JürgenB

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Audio-Oszillograph

Im folgenden Beispiel soll gezeigt werden, wie man auf die Daten des Mikrofons zugreifen und diese dann weiter verarbeiten, z.B. grafisch darstellen kann. Auch wird noch eine Spektralanalyse durchgeführt.

Der Zugriff auf das Mikrofon erfolgt über navigator.mediaDevices.getUserMedia. Hierzu muss die Seite über https://, über file:// oder localhost aufgerufen werden, und der Besucher muss noch seine Zustimmung geben. Über den Parameter wird der Audioeingang gewählt. Da die Methode einen Promise zurückgibt, wird das weitere Script im then-Bereich notiert:

navigator.mediaDevices.getUserMedia ({ audio: true })
  .then(function(stream) {
    ...
  })
  .catch(function(error) {
    console.error('The following getUserMedia error occured: ' + error);
  });

Für den Zugriff auf die Audiodaten werden die Knoten AnalyserNode und MediaStreamAudioSourceNode des AudioContext verwendet:

audioCtx = new AudioContext({sampleRate: 22050});

Über den Parameter wird die Abtastrate eingestellt. Der AnalyserNode wird dann mit

analyser = audioCtx.createAnalyser();

erzeugt. Mit

analyser.smoothingTimeConstant = 0;
analyser.fftSize = 1024;

wird die Glättung der Messwerte abgeschaltet und die Größe des Messblocks auf 1024 Werte festgelegt.

Die Verknüpfung des Analyser-Knoten mit dem Audiostream erfolgt über

const source = audioCtx.createMediaStreamSource(stream);
source.connect(analyser);

Die Audiodaten und das Spektrum werden über ein TypedArray übergeben, das mit

bufferLength_t = analyser.fftSize;
dataArray_t = new Float32Array(bufferLength_t);

bufferLength_f = analyser.frequencyBinCount;
dataArray_f = new Float32Array(bufferLength_f);

angelegt wird.

Die Erfassung der Daten erfolgt dann über

analyser.getFloatTimeDomainData(dataArray_t);
analyser.getFloatFrequencyData(dataArray_f);

Da das Array mit den Spektralwerten auch schon mal den Wert "-Infinity" enthalten kann und die verwendete Grafik keine Werte kleiner als den in der Skalierung angegebene Minimalwert haben sollte, werden die Spektralwerte noch nach unten begrenzt:

dataArray_f.forEach((v, i, a) => { if(v < -100) a[i] = -100; });

Für die Anzeige wird die Plotfunkltion aus Datenvisualisierung/Funktionsplotter verwendet.

Die Messung erfolgt einer requestAnimationFrame-Schleife.

Das fertige Script sieht dann so aus:

Beispiel ansehen …
let audioCtx, analyser, plot_t, plot_f, animationFrame, lastVisualize = 0;
let dataArray_t, bufferLength_t, dataArray_f, bufferLength_f;
let t_werte = [], f_werte = [], t_scale, f_scale;

function initAudio() {
														
	// Verbindung zum Mikrofon herstellen
	navigator.mediaDevices.getUserMedia ({ audio: true })
		.then(function(stream) {
			// Audio-Schnittstelle einrichten
			audioCtx = new AudioContext({sampleRate: 22050});

			// Analyser für Abfrage der Zeitdaten und die Spektralanalyse einrichten
			analyser = audioCtx.createAnalyser();
			analyser.smoothingTimeConstant = 0;
			analyser.fftSize = 1024;

			// Analyser mit Audiostream verknüpfen
			const source = audioCtx.createMediaStreamSource(stream);
			source.connect(analyser);

			// Rest initialisieren
			initVisualize();
			initPlot();
			initStartPauseWeiterButton();	
		})
		.catch(function(error) {
			console.error('The following getUserMedia error occured: ' + error);
		});

} // initAudio

function initVisualize() {
	// Sound-Daten Abruf und Plot vorberteiten

	bufferLength_t = analyser.fftSize;
	dataArray_t = new Float32Array(bufferLength_t);

	bufferLength_f = analyser.frequencyBinCount;
	dataArray_f = new Float32Array(bufferLength_f);
	
} // initVisualize

function initPlot() {
	// Plot-Funktion einrichten

	const canvas_t = document.querySelector('#plot_t');
	canvas_t.setAttribute('width', canvas_t.offsetWidth);
	canvas_t.setAttribute('height', canvas_t.offsetHeight);
	plot_t = new SW.plot(canvas_t, "t", "ampl");

	t_scale = [{t: 0, ampl: -1}, {t: bufferLength_t/audioCtx.sampleRate, ampl: 1}];

	for(let i=0; i<bufferLength_t; i++) t_werte[i] = { t: i/audioCtx.sampleRate, ampl: 0 } ;

	const canvas_f = document.querySelector('#plot_f');
	canvas_f.setAttribute('width', canvas_f.offsetWidth);
	canvas_f.setAttribute('height', canvas_f.offsetHeight);
	plot_f = new SW.plot(canvas_f, "f", "spekt");

	f_scale = [{f: 0, spekt: -100} ,{f: .5*audioCtx.sampleRate, spekt: -10}];

	for(let i=0; i<bufferLength_f; i++) f_werte[i] = { f: 0.5*i*audioCtx.sampleRate/bufferLength_f, spekt: 0 } ;

} // initPlot

function initStartPauseWeiterButton() {
// Der Start/Pause/Weiter-Button

	document.querySelector('#StartPauseWeiter').addEventListener("click", function() {
		if(this.innerHTML == "Pause") {
			window.cancelAnimationFrame(animationFrame);
			this.innerHTML = "Weiter";
		} 
		else {
			visualize();
			this.innerHTML = "Pause";
		}
	});

} // initStartPauseWeiterButton

function visualize(timestamp) {
	// Mess- und Anzeigeschleife
	animationFrame = window.requestAnimationFrame(visualize);

	if((timestamp - lastVisualize) > 100) {
		lastVisualize = timestamp;
		// Zeit- und Frequenzdaten abrufen
		getData();
		// und plotten
		draw_t();
		draw_f();
	}

} // visualize

function getData() {
	// Zeit- und Frequenzdaten abrufen
	analyser.getFloatTimeDomainData(dataArray_t);
	analyser.getFloatFrequencyData(dataArray_f);
	// das Frequenzarray enthält schon mal den Wert "-Infinity"
	dataArray_f.forEach((v, i, a) => {
		if(v < -100) a[i] = -100;
	});

} // getData

function draw_t() {
	// Zeitdiagramm
	dataArray_t.forEach((v, i, a) => { t_werte[i].ampl = v; });
		
	plot_t.clear();
	plot_t.scale(t_scale);
	plot_t.frame(40, 30, "Zeit", "Amplitude");
	plot_t.plot(t_werte, "#f00");

} // draw_t

function draw_f() {
	// Frequenzdiagramm
	dataArray_f.forEach((v, i, a) => { f_werte[i].spekt = v; });

	plot_f.clear();
	plot_f.scale(f_scale);
	plot_f.frame(40, 30, "Frequenz", "Amplitude in dB");
	plot_f.plot(f_werte, "#f00");

} // draw_f

document.addEventListener("DOMContentLoaded", initAudio);

Quellen

Web audio

Web Audio API

getUserMedia

Fliegen fangen

Beispiel ansehen …
Fliegen fangen

Tannenbaum mit Worker und OffscreenCanvas

Beispiel für das Erstellen einer Canvas-Grafik vom Worker aus

Normalerweise kann man aus dem Worker heraus nicht direkt auf das DOM zugreifen. Die Rechnung läuft im Worker, aber die Ausgabe muss dann doch noch im normalen Script-Bereich erfolgen. Mit OffscreenCanvas hat man aber die Möglichkeit, vom Worker aus direkt eine Canvas-Grafik zu erstellen:

const worker = new Worker(...);
const canvas = document.querySelector("#Tanne");
canvas.height = canvas.clientHeight;
canvas.width  = canvas.clientWidth;
const offscreen = canvas.transferControlToOffscreen();
worker.postMessage({ canvas: offscreen }, [offscreen]);

und im Worker

self.addEventListener('message',function(messageEvent) { 
	canvas = messageEvent.data.canvas;
	canvasHeight = canvas.height;
	canvasWidth  = canvas.width;
	context = canvas.getContext("2d");
...
}


Beispiel ansehen …

Zugängliche Navigation mit popover und anchor

Beispiel ansehen …

Inline-Tooltips mit Popover und Anchor-Technik

Infobox/Tooltips_mit_Popover#Anchor_Positioning

Test verschachtelte Popover mit Anchor positioniert

Beispiel ansehen …

Test der anchor-Technik

Beispiel ansehen …

Test der popover-Technik

Beispiel ansehen …

Custom-Element mit Min-Max-Schieberegler

Formulare/Eingabe_von_Zahlen#.3Cdouble-range-input.3E_mit_Min-Max-Schieberegler

Custom-Element mit synchronisierten Range- und Number-Inputs

Formulare/Eingabe_von_Zahlen#.3Cself-slider.3E_mit_synchronisierten_Range-_und_Number-Inputs

Vergleich der Farbmodelle RGB, HSL und OKLAB

Vergleich der Farbmodelle

Beispiel ansehen …
Vergleich der Farbmodelle
Beispiel ansehen …

Komfort-Bildwechsler

Komfort-Bildwechsler

Bubbling, Capturing und stopPropagation

JavaScript/Tutorials/DOM/Ereignisverarbeitung#Bubbling.2C_Capturing_und_stopPropagation

Tutorial Drag 'n Drop

JavaScript/Tutorials/Drag_and_Drop

Tutorial Tabellen dynamisch sortieren

JavaScript/Tutorials/Tabellen_dynamisch_sortieren

Tutorial Mouse and More

JavaScript/Tutorials/Mouse and More

Testseite für Maus-, Touch- und Pointerevents

Testseite für Maus-, Touch- und Pointerevents

Beispiel ansehen …
Testseite für Maus-, Touch- und Pointerevents

Testseite Multipointerevents

Testseite Multipointerevents

Beispiel ansehen …
Testseite Multipointerevents

Blog-Artikel Einstieg in Leaflet

https://blog.selfhtml.org/2019/jan/13/einstieg-in-leaflet

Blog-Artikel Einbinden einer OSM-Karte als Beispiel für Custom Elements

https://blog.selfhtml.org/2020/dec/03/einbinden-einer-osm-karte-als-beispiel-fuer-custom-elements