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

Ankommende Puffer zusammenführen

xorg1990

New member
Hi, ich steh mal wieder mit den rücken zur Wand.

Folgende Situation:
Ich habe ein globales Float32Array mit einer Größe von 384000bytes (var = f32 new Float32Array(384000))
In dieses Array will ich jetzt Daten schreiben, diese kommen von einer websocket Verbindung, der ankommende Puffer schwankt zwischen 2229bytes und 8916bytes.

Irgendwie habe ich da ein Brett vorm Kopf, ich kriege das nicht gebacken die Arrybuffer-Daten in das Float32Array zu schreiben, ohne dabei die ersten Bytes wider zu überschreiben.

So war Mein denken:
var offs = 0, Bufferlength =0;

s.on('data', function(data) {
var d = new Float32Array(data),c;
for(c=0;c<d.length;c++){
if(d[c] == 0){
f32[c]=d[c];
offs= c+=1;
Bufferlength = d.length;


else if (offs == Bufferlength){
f32[offs+c]=d[c];
…usw…

So wird das allerdings nüscht, da ich am Ende unzählige else if’s hätte und dazu kommt immer noch ein Wert der den aktuellen Offset des arrays "f32" speichert.
Dafür muss es doch eine schleifen Sache geben, aber wie aussehen oder bin ich da mit einen Ringbuffer besser bedient??
 
Ich glaube nicht, dass ein Ringbuffer da viel helfen würde. Ich würde den Buffer so lange beschreiben, bis er voll ist und dann einen neuen anfangen - ich glaube, dass dich das besser mit der Audio-API verträgt.

Deinen Code verstehe ich nicht wirklich. Warum vergleichst du im if den zu übertragenden Wert mit Null? Warum addierst du zu dem Index in f32 nicht den Offset dazu? ...

PS: Pack' deinen Code doch in [code]-Tags, dann kann man ihn besser lesen.
 
Hi kkapsner,


kkapsner schrieb:
Warum addierst du zu dem Index in f32 nicht den Offset dazu?
Ich dödel, natürlich das ist die Idee :beaten:
Code:
var f32= new Float32Array(384000), offs=0;

//Dient nur zur test ob auch daten geschrieben wurden
setInterval(function(){
document.getElementById("ausgabe").innerHTML=(f32[341000]);
});


var client = new BinaryClient("ws://192.168.2.104:3000");
client.on('stream', function(s, meta) {
	if (meta === 'MF-CW') {
    	s.on('data', function(data) {
			var d = new Float32Array(data),c;
			offs+=d.length;//offset von d zu Variable offs dazu addieren
			for(c=0;c<d.length;c++){
				//stopp bei 384000 bytes
				if(offs<384000){
					f32[offs+c]=d[c];
					document.getElementById("ausgabe2").innerHTML=(offs);
				}
			}
        });	

    }
});

kkapsner schrieb:
ich glaube, dass dich das besser mit der Audio-API verträgt.
Darum geht es ja auch.:grin:


Ein frage stellst sich mir trotzdem noch.
Woher weiß ich das der Script Processor fertig ist mit dem spielen der Samples in f32??
Irgendwann muss ich ja die die Variable offs wider auf 0 stellen damit es weiter geht.


kkapsner schrieb:
Warum vergleichst du im if den zu übertragenden Wert mit Null?
Keine Ahnung ist im „de boer“ Skript auch so.

Code:
//WebSDR HTML5 client side
(function(){
	var A=null,V=window.AudioContext||window.webkitAudioContext;
	if(V){document.ct||(document.ct=new V);
		var W;
		try{
			W=document.ct.createConvolver
		}
		catch(ba){}W||(document.ct=A,sup_webaudio=!1)
	}
	
	function ja(){
		function ca(b){
			var d=b.outputBuffer.getChannelData(0),c=b.outputBuffer.length;
			for(b=0;b<c;b++){
				var e=r[n],h=x*y/g;
				j+=h;
				if(1<=j){
					j-=1;n++;
					8192<=n&&(n-=8192);
					var a=r[n];
					X&&(e=(j*a+(h-j)*e)/h)
				}
				L&&(e=0);
				d[b]=2E-5*e*Q}
			H=(new Date).getTime()
		}
	document.getElementById("soundappletdiv").innerHTML='<span id="debug"></span> <span id="debug2"></span> <span id="smeter"></span>';
	var r=new Int16Array(8192),e=0,n=6144,j=0,y=8E3,g=48E3,x=1,Q=1,Y=-1,I,t=0,u=[],


        
    X=!0,B=1E3,L=!1,H=0,p,C,z,D=[],F,M=0,R=0,Z=0,N=0,S=0,T=0,$=0;

	if(p=document.ct){
		p.sampleRate=g;
		var g=p.sampleRate,f=2048;sup_iOS&&(f*=2);
		try{
			C=p.createScriptProcessor(f,1,1)
		}
		catch(ka){
			C=p.createJavaScriptNode(f,1,1)
		}
		C.onaudioprocess=ca;
		this.p=C;
		z=p.createConvolver();
		C.connect(z);
		z.connect(p.destination);
		var v,f=p.createBuffer(1,512,g);
		v=f.getChannelData(0);
		v.set(fa);
		D[0]=f;
		f=p.createBuffer(1,512,g);
		v=f.getChannelData(0);
		v.set(ea);
		D[1]=f;
		f=p.createBuffer(1,512,g);
		v=f.getChannelData(0);
		v.set(ga);
		D[2]=f;
		f=p.createBuffer(1,512,g);
		v=f.getChannelData(0);
		v.set(ha);
		D[3]=f;
		z.normalize=!1;
		z.buffer=D[0]
	}else{
		F=new Audio,F.mozSetup(1,g);
		var O=0,J=0.3*g,P;
		doe=function(){
			if(F){
				var b=F.mozCurrentSampleOffset(),d=M-b,c=Math.round(J-d);
				0==O&&(0==b?c=Math.round(0.05*g):(O=M,J=O+Math.round(0.05*g),J<0.25*g&&(J=Math.round(0.25*g))));
				var e,b=[];
				for(e=0;e<c;e++){
					var h=r[n],a=x*y/g;
					j+=a;
					if(1<=j){
						j-=1;
						r[n]=0;n++;
						8192<=n&&(n-=8192);
						var k=r[n];
						X&&(h=(j*k+(a-j)*h)/a)
					}
					L&&(h=0);
					(h=2E-5*h*Q)||(h=0);
					a=0.044*h+0.088*R+0.044*Z- -1.354*N-0.53*S;
					k=0.044*a+0.088*N+0.044*S- -1.354*T-0.53*$;
					Z=R;
					R=h;
					S=N;
					N=a;
					$=T;
					T=k;
					b[e]=k;
					3==P&&(b[e]=h)
				}
				e=H;
				H=(new Date).getTime();
				document.getElementById("debug2").innerHTML="timing: "+(H-e)+" "+d+" "+c+" "+O/g+" "+J/g;
				d=F.mozWriteAudio(b);
				M+=d
			}
		};
	}

	var aa,s,U,G=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],w=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],K=0,ia=0,l=new WebSocket("ws://websdr.ewi.utwente.nl:8901/~~stream");
	l.binaryType="arraybuffer";
	var E=500;
	l.onmessage=function(b){
		p||doe();
		var d=(16384+e-n-((new Date).getTime()-H)*y/1E3)%8192;E+=0.01*(d-E);
		E>2*B&&(n=(8192+e-B)%8192,d=E=B);
		x=1+1E-5*(E-B);1.002<x&&(x=1.002);
		0.998>x&&(x=0.998);
		document.getElementById("debug").innerHTML=Math.round(d)+" "+Math.round(B)+" "+Math.round(E)+" "+Math.round(1E6*(x-1))+"ppm"+n+" "+Math.round(y)+"Hz<br>";
		var d=new Uint8Array(b.data),c;
		b=e;document.getElementById("anzeige").innerHTML=("Daaaaaaaaaaa"+d.length);
		for(c=0;c<d.length;c++){
			var g=0,h=0;
			if(112==(d[c]&240))smeter=256*(d[c]&15)+d[c+1],c++;
			else if(0==d[c]){
				ia++;
				var a;
				for(a=0;128>a;a++)r[e+a]=da[d[c+1+a]];
				e+=128;8192<=e&&(e-=8192);
				c+=128;
				var k;
				for(k=0;20>k;k++)G[k]=w[k]=0;K=0
			}
			else if(16<=d[c]&&95>=d[c])h=4,g=2,s=6-(d[c]>>4);
			else if(d[c]&128)h=1,g=2;
			else if(1==d[c])y=256*d[c+1]+d[c+2],Y=0<y?1:0,c+=2;
			else if(2==d[c])aa=256*d[c+1]+d[c+2],c+=2;
			else if(3==d[c])U=d[c+1],k=d[c+1]&15,k!=P&&(P=k,z&&(z.buffer=D[P])),c++;
			else if(4==d[c]){
				for(a=0;128>a;a++)
				r[e+a]=0;
				e+=128;8192<=e&&(e-=8192);
				for(k=0;20>k;k++)G[k]=w[k]=0;
				K=0
			}
			if(2==g){
				g=0;
				for(k=16==(U&16)?12:14;128>g;){
					a=d[c+3]&255|(d[c+2]&255)<<8|(d[c+1]&255)<<16|(d[c+0]&255)<<24;
					a<<=h;
					var m=0,f;
					f=15-s;
					var j=aa,q=[999,999,8,4,2,1,99,99];
					if(0!=a)
					for(;0==(a&2147483648)&&m<f;)a<<=1,m++;m<f?(f=m,m++,a<<=1):(f=a>>24&255,m+=8,a<<=8);
					var l=0;
					f>=q[s]&&l++;f>=q[s-1]&&l++;
					l>s-1&&(l=s-1);q=(a>>16&65535)>>17-s&-1<<l;
					q+=f<<s-1;0!=(a&1<<32-s+l)&&(q|=(1<<l)-1,q=~q);
					for(h+=m+s-l;8<=h;)c++,h-=8;
					for(a=m=0;20>a;a++)m+=G[a]*w[a];m|=0;m=0<=m?m>>12:m+4095>>12;j=q*j+j/2;q=j>>4;
					for(a=19;0<=a;a--){G[a]+=-(G[a]>>7)+(w[a]*q>>k);
					if(0==a)
					break;
					w[a]=w[a-1]}w[0]=m+j;
					j=w[0]+(K>>4);
					K=16==(U&16)?0:K+(w[0]<<4>>3);
					r[e++]=j;8192<=e&&(e-=8192);
					g++}0==h&&c--
				}
			}//for Zeile 152
							
	};//function onmessage Zeile 143
	l.onopen=function(){
		l.send("GET /~~param?f=828");
		soundappletstarted()
	};
	l.onclose=function(){
		for(var b=0;8192>b;b++)r[b]=0;l.onclose=A;l=l.onmessage=A
	};
	this.setparam=function(b){
		l.send("GET /~~param?"+b)};
		this.smeter=function(){
		return 10*smeter
	};
	this.getid=function(){
	return Y
	};
	this.mute=function(){
	L=!L
	};
	this.setvolume=function(b){
		Q=b
	};
	this.setdelay1=function(b){
		B=b;
		n=(8192+e-B)%8192
	};
	this.destroy=function(){
		p?(l.close(),p.pause(),v=D=this.p=p=z=C=C.onaudioprocess=A):(l.close(),F=A);window.soundapplet=A
	};
	this.rec_start=function(){
		t=0;var b=new ArrayBuffer(65536);
		I=new DataView(b);
		u=[b]
	};
	this.rec_finish=function(){
		u[u.length-1]=u[u.length-1].slice(0,t);
		I=A;
		var b={};
		b.wavdata=u;
		b.len=65536*(u.length-1)+t;
		b.sr=y;
		1E4<=b.sr&&(b.sr/=2);
		return b
		};
	this.rec_length_kB=function(){
		return(65536*(u.length-1)+t)/1024}
	
	}//function ja
	
window.prep_html5sound=function(){
	window.soundapplet=new ja
};
	prep_html5sound();
		
})//function Zeile 2
();

Dieses Skript macht das was ich die ganze Zeit versuche zu verstehen, es spielt einen Livestream an PCM am Daten ab in Realtime.

Allerdings baue ich kein Skript auf meine Seite was ich nicht verstehe und 2 klaue ich keine Ideen,
in diesem fall versuch ich den Weg der Daten nur nachzuvollziehen.

Wenn einer versteht wie die Daten von dem Onmessege Event zum ScriptProcessor(function ca) gelangen kann er das gerne hier schreiben.


Pieter-Tjerk schrieb:
Im WebSDR-Client mache ich Pufferung und eine Samplerateänderung die
von einer Regelschleife dauernd angepasst wird um die Latency ungefähr
konstant zu halten.

Vielleicht hilft das, mir hat‘s nicht geholfen.
 
Puh... der Code ist echt mal unübersichtlich und schwer zu lesen...

... aber ich hab' mich jetzt mal genauer mit dem Thema beschäftigt: wäre es nicht viel einfacher, wenn du auf deinem Server, anstatt des Websockets, einen Audiostream bereitstellst, den du dann mit einem <audio> direkt ansprechen kannst. Das übernimmt dann das ganze Puffern und du musst dich nicht darum kümmern. Über context.createMediaElementSource() kannst du dieses <audio> dann in deine Audio API einbinden und deine Frequenzanalyse durchführen.
 
Hallo kkapsner, das mit der MediaElementSource() habe ich mir auch schon überlegt, nur ist das Problem das die <audio>Tags entweder mp3 oder ogg verstehen. Bei mp3 sind die Verluste zu hoch
1. die Sampling Rate ist 44100, dann enthält mp3 Daten wie das Albumcover und id3 Tag… brauch ich nicht.
Bei ogg habe ich keine Ahnung ogg ist meist vorbis enkodiert, bin der Meinung ogg geht bis 96000Hz, das könnte man mal probieren. Vorbis ist float32 normalized (werte bis -1,+1) wärend lame(mp3) nur int16 ist.

WebRTC habe ich auch schon in Betracht gezogen. Wird aber noch nicht von allen Browser unterstüzt (IE erst in 100Jahren die bekommen die Audio api noch nicht mal auf die Reihe).

kkapsner schrieb:
Puh... der Code ist echt mal unübersichtlich und schwer zu lesen...
Dazu komme ich gleich noch mal.

Ich habe paar Fakten zu Web Audio Api, vielleicht fällt den einen oder andern was dazu ein:
- Die Sampling-Rate der Web Audio api richtet sich nach der Hardware oder nach den Browserherstellen, langsamer als 44100 Samples wird es aber nicht.

- Die createBuffer method der api funzt nicht so richtig bei livestream, Der Ton irgendwann immer langsamer, das geht so weht das sich der Browser aufhängt.
2tens auf Tablets, IPhone usw. geht keine Samplingrate von 96000.

- Jetzt bleibt für die Soundausgabe nur noch der ScriptProcessor. Dazu erst einmal etwas Mathematik: 1/44100(Hz=1/s)=0,000022676s*2048=0,046439909s Pufferdauer.
1/0,046439909s=20,226. Das bedeutet das der onaudioprocess Event rund 20 mal in der Sekunde ausgeführt wird, bei einer Samplingrate von 44100 und einer Größe von 2048 sample Frames.
Das wiederum sind 41422,848 Samples in einer Sekunde. 20,226*2048=41422,848.

41422,848*32/4=331382,784Bytes die da in einer Sekunde bewältigt werden können.

Die Soundkarte auf dem Server liefert ebenfalls float32 mit einer Rate von 44100. Eigentlich möchte ich eine Sampling Rate von 96000… ist aber erst mal 2tens.
44100*32/4=352800Bytes. Diese Datenmenge wir ja nun nicht mit einmal übertragen.

Ein Puffer von 352800byte ist viel zu groß, es dauert zu lange eh dieser voll ist.
Und was ist mit dem 21418bytes die übrigbleiben?



Der onmessage Event, liefert in x Sekunden ein Puffer von x Bytes, bis 16384bytes hatte ich an einer lokalen Netzwerkverbindung. Ich habe mir mal sagen lassen das der onmessage-event 46mal in der Sekunde losgefeuert wird, genau weiß ich das nicht.

Jetzt seid ihr gefragt, wie groß muss ich meinen Puffer Dimensionieren um Ton in einwandfreier Qualität widergeben zu können.

2te Fragte:
Wie bekomme ich es hin im onaudio Event 41422,848 Samples zu spielen, wenn doch die for Schleife nur bis 2048 zählt?
Code:
f32 var f32= new Float32Array(16384), offs=0// wird im onmessage event erhöt 

//onaudioprocess
function play(b){
			var output=b.outputBuffer.getChannelData(0), outlength=b.outputBuffer.length;
			schleife durch 2048 samples
			Variable++ außerhalb der for schleife ist zu langsam 
			for(b=0;b<outlength;b++){
			//Varibale++ geht nicht immer 0 warscheinlich zu schnell für die Engine 
			output[b]=f32[b];//f32[b] b müsste bis 41422 gehen 
			}		
}

zum „de boer“ Skript
Alles was ich versteh ist der das was in onaudioprocess abgeht:
Code:
function ca(b){
	var d=b.outputBuffer.getChannelData(0),c=b.outputBuffer.length;
		for(b=0;b<c;b++){
		    var e=r[n],h=x*y/g;// e= ein sample wird aus dem globlen  Int16Array abgeholt (was auch immer h ist)
		                j+=h;
				if(1<=j){
					j-=1;n++;
					8192<=n&&(n-=8192); // diese zeile seht nach eienr int zu float konvertierung aus 
					var a=r[n]; 
					X&&(e=(j*a+(h-j)*e)/h)
				}
				L&&(e=0);//e=0
				d[b]=2E-5*e*Q}// schreibe entweter sample e oder Q in den Ausgapuffer e=0*1=1 oder wenn e=1 1*0=0 Sprich entweder ist e0 oder Q0
			H=(new Date).getTime()
		}
Den Teil im onmessage verstehe ich auch nicht die ganze else if abfragen ergeben schon keinen sinn.
Um‘s kurz zu machen dieses Skript versteht man ohne Kommentierung überhaut nicht.

Vielleicht hat der eine oder andere jetzt eine Idee aufgrund meiner Mathematik, ansonsten muss ich doch mal in’s Mikrocontroller Forum gehen… das ist sozusagen das Deutsche stackoverflow…
Mfg xorg1990
 
Zurück
Oben