• Das Erstellen neuer Accounts wurde ausgesetzt. Bei berechtigtem Interesse bitte Kontaktaufnahme über die üblichen Wege. Beste Grüße der Admin

[FRAGE] Web Audio Api

ullidrachenfels

New member
Hallo,

ich habe einen MP3-Player mit ID3-Reader gebaut auf Basis der Web Audio Api.
Leider bin ich in Sachen Javascript blutiger Anfänger und könnte daher Hilfe gebrauchen.
Probleme gibt es mehrere.
Zuerst mal der Link zur Anwendung:
http:www.globalgamers.de/mp3id3player/
Der Player ist noch nicht fertig, es fehlen noch diverse Funktionen.
Achtung, funktioniert im Moment nur mit Chrome.

Jetzt zu den Problemen:
1. Die Anzeige der Laustärke wird über 2 Canvas realisiert. Das Ergebnis sind 2 Balken. Schön anzusehen aber noch nicht ganz was ich wollte.
Eigentlich wollte ich da kleine Klötzen haben.
So ähnlich wie das hier:
http://ak8.picdn.net/shutterstock/videos/1934338/preview/stock-footage-digital-sound-vu-meter-equalizer.jpg
Da das ganze aber nicht über ein Image geregelt wird, muss es wohl über den Analyser gemacht werden.
Das ist dann allerdings zu viel für mich. Vielleicht hat da einer eine Idee.

2. Playliste für multiple Sounds.
Es gibt jede Menge Beispiele dafür, ich hatte mir dieses ausgesucht.
Audio-Player - Audio-Dateien über JavaScript steuern - Webbausteine.de
Leider bekomme ich das nicht hin, da in meiner Version das Audio Element erst erstellt wird.
Vielleicht hat da jemand eine Idee, wie ich trotzdem eine Playliste realisieren kann.
3. 10 Band Equalizer
Auch hier gibt es jede Menge Beispiele. Leider funktioniert keins davon.
Vielleicht weiß einer einen Link zu einem funktionierenden.

Jetzt zum Code:
index.html
HTML:
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8">
		<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
		<title>Mp3-Player mit ID3-Reader</title>
		<meta name="description" content="">
		<meta name="author" content="ulli">
		<meta name="viewport" content="width=device-width; initial-scale=1.0">
		<link rel="stylesheet" type="text/css" href="css/main.css">
    	<script src="js/id3-minimized.js" type="text/javascript"></script>
	   <script src="js/main.js"></script>
	   <script src="js/volume-meter.js"></script>

		</head>
		<body>
			<div id="audio_player">
				  <div id="cover">
				  	
				  	  <img id="picture" src=""/>
				  	  
				  </div>
				  <div id="screen">
					    <p><b>Title:</b><br /> <span id="title"></span></p>
					    <p><b>Artist:</b><br /> <span id="artist"></span></p>
					    <p><b>Album:</b><br /> <span id="album"></span></p>
					    <p><b>Genre:</b><br /> <span id="genre"></span></p>
					    <p><b>Track:</b> <span id="track"></span><br />
					    <b>Jahr:</b> <span id="year"></span></p>
				  </div>
				  
				  <canvas id="analyser_render"></canvas>
			      <div><canvas id="meter1" width="500" height="10"></canvas></div>
				  <div><canvas id="meter2" width="500" height="10"></canvas></div>
				  	
			      <div id="audio_controls">
					<button id="pausebtn"></button>
					<button id="playbtn"></button>
					<button id="stopbtn"></button>
					<input id="seekslider" type="range" min="0" max="100" value="0" step="1">
					<div id="timebox">
						<span id="curtimetext">00:00</span> / <span id="durtimetext">00:00</span>
					</div>
					<button id="mutebtn"></button>
					<button id="loopbtn"></button>
					<input id="volumeslider" type="range" min="0" max="100" value="20" step="1">
				</div>
			</div>
			<footer>
				<p>
					© Copyright  by ulli
				</p>
			<footer>
<script>

</script>		
	</body>
</html>
main.js
Code:
var audio,
	playbtn,
	mutebtn,
	seekslider,
	volumeslider,
	seeking = false,
	seekto,
	curtimetext,
	durtimetext;
	
function initAudioPlayer(mp3src) {
	audio = new Audio();
	audio.src = "musik/Tamara_Laurel_-_05_-_Dying.mp3";
	//audio.id = 'player';
	//audio.loop = true;
	//audio.play();
	audio.volume = 0.2;
	// Set object references
	playpausebtn = document.getElementById("playpausebtn");
	playbtn = document.getElementById("playbtn");
	mutebtn = document.getElementById("mutebtn");
	seekslider = document.getElementById("seekslider");
	volumeslider = document.getElementById("volumeslider");
	curtimetext = document.getElementById("curtimetext");
	durtimetext = document.getElementById("durtimetext");
	// Add Event Handling
	playbtn.addEventListener("click", play);
	mutebtn.addEventListener("click", mute);
	loopbtn.addEventListener("click", loop);
	pausebtn.addEventListener("click", pause);
	stopbtn.addEventListener("click", stop);
	seekslider.addEventListener("mousedown", function(event) {
		seeking = true;
		seek(event);
	});
	seekslider.addEventListener("mousemove", function(event) {
		seek(event);
	});
	seekslider.addEventListener("mouseup", function() {
		seeking = false;
	});
	volumeslider.addEventListener("mousemove", setvolume);
	audio.addEventListener("timeupdate", function() {
		seektimeupdate();
	});
			
// Player-Functions
function play() {
	audio.play();
	playbtn.style.background = "url(images/set1/play_on.png) no-repeat";
	pausebtn.style.background = "url(images/set1/pause.png) no-repeat";
}

function stop() {
	audio.pause();
	audio.currentTime = 0;
	stopbtn.style.background = "url(images/set1/stop.png) no-repeat";
	playbtn.style.background = "url(images/set1/play.png) no-repeat";
	curmins = 0;
	cursecs = 0;
	durmins = 0;
	dursecs = 0;
	curtimetext.innerHTML = curmins + ":" + cursecs;
	durtimetext.innerHTML = durmins + ":" + dursecs;
}

function pause() {
	if (audio.paused) {
		audio.play();
		pausebtn.style.background = "url(images/set1/pause.png) no-repeat";
		playbtn.style.background = "url(images/set1/play_on.png) no-repeat";
	} else { 
		audio.pause();
		pausebtn.style.background = "url(images/set1/pause_on.png) no-repeat";
		playbtn.style.background = "url(images/set1/play.png) no-repeat";
	}
}

function mute() {
	if (audio.muted) {
		audio.muted = false;
		mutebtn.style.background = "url(images/set1/volume_on.png) no-repeat";
	} else {
		audio.muted = true;
		mutebtn.style.background = "url(images/set1/volume.png) no-repeat";
	}
}
				
function loop() {
	if 	(audio.loop) {
		audio.loop = false;
	    loopbtn.style.background = "url(images/set1/loop.png) no-repeat";
	} else {
	    audio.loop = true;
	    loopbtn.style.background = "url(images/set1/loop_on.png) no-repeat";
	}
}

function seek(event) {
	if (seeking) {
		seekslider.value = event.clientX - seekslider.offsetLeft;
		seekto = audio.duration * (seekslider.value / 100);
		audio.currentTime = seekto;
	}
}

function setvolume() {
	audio.volume = volumeslider.value / 100;
}

function seektimeupdate() {
	var nt = audio.currentTime * (100 / audio.duration);
	seekslider.value = nt;
	var curmins = Math.floor(audio.currentTime / 60);
	var cursecs = Math.floor(audio.currentTime - curmins * 60);
	var durmins = Math.floor(audio.duration / 60);
	var dursecs = Math.floor(audio.duration - durmins * 60);
	if (cursecs < 10) {
		cursecs = "0" + cursecs;
	}
	if (dursecs < 10) {
	dursecs = "0" + dursecs;
	}
	if (curmins < 10) {
		curmins = "0" + curmins;
	}
	if (durmins < 10) {
		durmins = "0" + durmins;
	}
	curtimetext.innerHTML = curmins + ":" + cursecs;
	durtimetext.innerHTML = durmins + ":" + dursecs;
	}
}
		   
window.addEventListener("load", initAudioPlayer);
var canvas, ctx, meter, source, context, analyser, fbc_array, bars, bar_x, bar_width, bar_height, url;

// Initialize the MP3 player after the page loads all of its HTML into the window
window.addEventListener("load", initMp3Player, false);
		  
function initMp3Player(){
    document.getElementById('audio_player').appendChild(audio);
    context = new window.AudioContext || window.webkitAudioContext(); // AudioContext object instance
 	analyser = context.createAnalyser(); // AnalyserNode method
    canvas = document.getElementById('analyser_render');
    ctx = canvas.getContext('2d');
    canvasContext1 = document.getElementById( "meter1" ).getContext("2d");
    canvasContext2 = document.getElementById( "meter2" ).getContext("2d");
    meter1 = createAudioMeter(context);
    meter2 = createAudioMeter(context);
    splitter = context.createChannelSplitter();
    splitter.connect(meter1,0,0);
    splitter.connect(meter2,1,0);
 
    // Re-route audio playback into the processing graph of the AudioContext
    source = context.createMediaElementSource(audio); 
    source.connect(analyser);
    source.connect(meter1);
    source.connect(meter2);
    analyser.connect(context.destination);
    
    frameLooper();
	drawLoop();
}

// frameLooper() animates any style of graphics you wish to the audio frequency
// Looping at the default frame rate that the browser provides(approx. 60 FPS)
    
function frameLooper(){
    window.webkitRequestAnimationFrame(frameLooper);
    fbc_array = new Uint8Array(analyser.frequencyBinCount);
    analyser.getByteFrequencyData(fbc_array);
    ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
    ctx.fillStyle = '#00CCFF'; // Color of the bars
    bars = 100;
    for (var i = 0; i < bars; i++) {
        bar_x = i * 3;
        bar_width = 2;
        bar_height = -(fbc_array[i] / 2);
        //  fillRect( x, y, width, height ) // Explanation of the parameters below
        ctx.fillRect(bar_x, canvas.height, bar_width, bar_height);
    }
}
//Volume Canvas
var canvasContext = null;
var WIDTH=570;
var HEIGHT=10;
var rafID = null;

function drawLoop( time ) {
    // clear the background
    canvasContext1.clearRect(1,0,WIDTH,HEIGHT);

    // check if we're currently clipping
    if (meter1.checkClipping())
        canvasContext1.fillStyle = "red";
    else
        canvasContext1.fillStyle = "#00CCFF";
    canvasContext2.clearRect(1,0,WIDTH,HEIGHT);    
    if (meter2.checkClipping())
        canvasContext2.fillStyle = "red";
    else
        canvasContext2.fillStyle = "#000CFF";

    // draw a bar based on the current volume
    canvasContext1.fillRect(1, 0, meter1.volume*WIDTH*1.4, HEIGHT);
    canvasContext2.fillRect(1, 0, meter2.volume*WIDTH*1.4, HEIGHT);

    // set up the next visual callback
    rafID = window.requestAnimationFrame( drawLoop );
}

//ID3 Tags-Reader
	ID3.loadTags("musik/Tamara_Laurel_-_05_-_Dying.mp3", function() {
		showTags("musik/Tamara_Laurel_-_05_-_Dying.mp3");
    }, {
      tags: ["title","artist","album","picture", "track", "year", "genre"]
    });

function showTags(url) {
	var tags = ID3.getAllTags(url);
    console.log(tags);
    document.getElementById('title').textContent = tags.title || "";
    document.getElementById('artist').textContent = tags.artist || "";
    document.getElementById('album').textContent = tags.album || "";
    document.getElementById('genre').textContent = tags.genre || "";
    document.getElementById('track').textContent = tags.track || "";
    document.getElementById('year').textContent = tags.year || "";
    var image = tags.picture;
    if (image) {
    	var base64String = "";
        	for (var i = 0; i < image.data.length; i++) {
            	base64String += String.fromCharCode(image.data[i]);
        	}
        var base64 = "data:" + image.format + ";base64," +
        	window.btoa(base64String);
        		for (i = 0; i < document.images.length; ++i) {
    				if (document.images[i].complete == true) {        
        					document.getElementById('picture').setAttribute('src',base64);
					} else {
						document.getElementById('picture').setAttribute('src',"images/set1/loading.gif");
						} 
        			} 
      } else {
        document.getElementById('picture').style.display = "none";
      }
}

So, das wars erstmal.
Vielen Dank schon im vorraus.

Gruß,

Ulli
P.S.
Es sind sicher noch diverse Sachen zu verbessern und zu korrigieren.
Seit nachsichtig.

P.P.S
Die Musik ist Gemafrei und von hier:
Free Music Archive: Tamara Laurel - Lightning EP
 
Puh wie erkläre ich das jetzt kurz :eek:
ullidrachenfels schrieb:
Da das ganze aber nicht über ein Image geregelt wird, muss es wohl über den Analyser gemacht werden.
Nee wie Du dein Canvas visualisiert ist dir überlasen, der Analyser wandelt die "Time Domain" in die "Frequency domain" um er macht die Fourier Transformation.
Für den Klötzen effect könnte man was mit dem Modulo Operator machen. Je nach dem wie dein Renderalgorithmus aus schaut.
Du könntest die Puffergröße des Scriptprocessors von 512 auf 16384 anheben dann wabert das ganze nicht so schnell hin und her. Ich persönlich würde das messen der Lautstärke so lassen
in der Time Domain.


ullidrachenfels schrieb:
Leider bekomme ich das nicht hin, da in meiner Version das Audio Element erst erstellt wird.
Na statt new Audio() sagst du halt audio = document.getElementById("myMusic");


ullidrachenfels schrieb:
10 Band Equalizer
Auch hier gibt es jede Menge Beispiele. Leider funktioniert keins davon.
Beispiele... mir ist kein einzige bekannt, um einen zu programmieren brauch man ein wenig know how.
Ein Equalizer ist in der Hardware gesehen eine Schaltung aus Bandpassfiltern wo gewiesse Höhen/Tiefen verstärkt oder gedämpft werden mithilfe von Operationsverstärkern.
Wenn du einen mit der Webaudio api bauen möchtest müsstest du den Biqaudfilter nehmen, dir die "Oktaven" begucken und an jeden filter noch ein Gain Knoten hinten dran hängen.
Zu beachten wäre dann noch die güte des jeweiligen Filters nicht das du dann irgend wo mal ein Lücke hast wo nichts gefiltert wird. Rundum es ist ein Aufwand.
Ganz schlau wäre natürlich natürlich den Equalizer in der Frequency domain zu machen mit Hilfe der iFFT.

ullidrachenfels schrieb:
Achtung, funktioniert im Moment nur mit Chrome.
Gerade mal ausprobiert im FF... geht doch

Vielleicht hat hesst noch etwas hinzuzufügen.

ich bin erst mal durch.
VG xorg1990
 
Zuletzt bearbeitet:
Hallo Xorg,

erstmal vielen Dank für Deine Antwort.
Dein Hinweis, das es unter FF jetzt gehen sollte, hab ich sofort getestet, da ja die Version 36 rausgekommen ist.
Aber.........:(, es die Canvas werden nicht ausgeführt.
Allerdings habe ich jetzt eine Version gefunden, die sowohl im Chrome als auch im Firefox läuft.
Die werde ich jetzt erstmal implementieren.

Gruß,

Ulli
 
ullidrachenfels schrieb:
die Canvas werden nicht ausgeführt.
Ist mir gestern gar nicht aufgefallen.
liegt daran:
window.webkitRequestAnimationFrame(frameLooper);

es reicht aus wen da steht requestAnimationFrame(frameLooper);

bei window.requestAnimationFrame( drawLoop );
kann das Window auch weg.

Dann geht’s.

xorg1990 schrieb:
Du könntest die Puffergröße des Scriptprocessors von 512 auf 16384 anheben dann wabert das ganze nicht so schnell hin und her.
Birgt die in diesem Fall gar nix, da du das canvas über den AnimationFrame animierst.
 
Supi, genial. Das hat schonmal funktioniert. Jetzt werden die Canvas auch unter FF angezeigt. Das Anzeigen der Lautstärke ist im Moment eh noch nen Fake, da eigentlich nur die Gesamtlautstärke angezeigt wird, und die wird einfach geteilt. In meiner neuen Version wird jeder Channel einzeln analysiert. Da ich in der neuen Version allerdings komplett auf das Audio-Element verzichte, wird das noch ein Weilchen dauern bis ich wieder was vorzeigbares habe, da das Ansteuern der Controls und der Nodes ganz schön kompliziert ist.

Gruß,

Ulli
 
ullidrachenfels schrieb:
da eigentlich nur die Gesamtlautstärke angezeigt wird, und die wird einfach geteilt.
Das kannst Du alles im ScriptProcessor lösen, über getChanneldata Method. Den ChanneSplitter kannst Du verwerfen.
Das triggern des renderns für das Canvas würde ich auch den Sp erledigen lassen.

ullidrachenfels schrieb:
Da ich in der neuen Version allerdings komplett auf das Audio-Element verzichte,
Das ist ein vernünftiger Ansatz, dann ist das mit der Playlist auch einfacher. Steht hier niedergeschrieben: Getting Started with Web Audio API - HTML5 Rocks
(BufferLoader class suchen)

Aber es gibt ein Bug in decodeAudiodata und zwar wenn das mp3 ein Album cover enthält wird nix decodiert, dass musste man via Bitverschiebung vorher "herausrechen".
Allerdings liegt das schon ein paar viele Mode zurück, könnte gefixt sein.

Bei dem 10 band EQ regelt man alles über den Gütefaktor habe ich mal so nebenbei erfahren. Macht aus sinn, denn wenn man hinter jeden Filter ein gain Knoten schaltet würde man ja alle Samples verstärken oder Dämpfen, und nicht die bei 1000Hz z.B.
Hier steht noch was dazu.
 
Zurück
Oben