Benutzer:JürgenB
Informationen zum Autor
- Name:
- Jürgen Berkemeier
- E-Mail:
- juergen@berkemeier.eu
- Homepage:
- https://www.j-berkemeier.de
Inhaltsverzeichnis
- 1 Audio-Oszillograph
- 2 Fliegen fangen
- 3 Tannenbaum mit Worker und OffscreenCanvas
- 4 Zugängliche Navigation mit popover und anchor
- 5 Inline-Tooltips mit Popover und Anchor-Technik
- 6 Test verschachtelte Popover mit Anchor positioniert
- 7 Test der anchor-Technik
- 8 Test der popover-Technik
- 9 Custom-Element mit Min-Max-Schieberegler
- 10 Custom-Element mit synchronisierten Range- und Number-Inputs
- 11 Vergleich der Farbmodelle RGB, HSL und OKLAB
- 12 Komfort-Bildwechsler
- 13 Bubbling, Capturing und stopPropagation
- 14 Tutorial Drag 'n Drop
- 15 Tutorial Tabellen dynamisch sortieren
- 16 Tutorial Mouse and More
- 17 Testseite für Maus-, Touch- und Pointerevents
- 18 Testseite Multipointerevents
- 19 Blog-Artikel Einstieg in Leaflet
- 20 Blog-Artikel Einbinden einer OSM-Karte als Beispiel für Custom Elements
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:
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
Fliegen fangen
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");
...
}
Inline-Tooltips mit Popover und Anchor-Technik
Infobox/Tooltips_mit_Popover#Anchor_Positioning
Test verschachtelte Popover mit Anchor positioniert
Test der anchor-Technik
Test der popover-Technik
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
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
Testseite für Maus-, Touch- und Pointerevents
Testseite Multipointerevents
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