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

Fensterfunktion auf FIR Filter Funktioniert nicht

xorg1990

New member
Hallo,
ich stehe mal wider vor einen Rätsel und zwar erhalte ich ein kein Ton sobald ich auf die Filterkoeffizienten das Hanning Fenster anwende.
Ein 2tes Problem ist das im Ton immer ein kleines knacken zu hören ist, das ist absolut nervig und macht auch dementsprechende Störungen im FFT Spektrum.

Code:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html">
<head>
    <title>HILBERT-Trafo</title>
 </head>
<body>


<script type="text/javascript">
var  HILBERT_LENGTH = 200; //FIR Hilbert Length 

var FILTER_LEN = Math.floor((HILBERT_LENGTH-1)/2);

var HILBERT_ARRAY = new Float64Array(FILTER_LEN);

var jj=0;
HILBERT_ARRAY[jj]=0.0;
for(;jj<=FILTER_LEN; jj++){
 var coeff = jj&1 ? 2.0/(jj*Math.PI) : 0.0;
  HILBERT_ARRAY[FILTER_LEN+jj] = coeff;
  HILBERT_ARRAY[FILTER_LEN-jj] = -coeff;
}

//Hanning window
/*
for(var I=0;I<FILTER_LEN;I++){
	HILBERT_ARRAY[I]*=0.5-0.5*Math.cos((2*Math.PI*I)/(I-1));
}
*/
console.log(HILBERT_ARRAY);


window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();

//Globals

var sourceRate = context.sampleRate;
var OscData = new Float32Array(16384);
var NCO_I = new Float32Array(sourceRate);
var NCO_Q = new Float32Array(sourceRate);
var DelayQueue =  new Float32Array(FILTER_LEN);
var BufferQueue =  new Float32Array(FILTER_LEN);
var queueIndex =0, iWrite =0, iRead=0, n=0, y=null, dbl_i=null, out=null, dbl_q=null, mixed=0, queue_ptr=0, iDelayIndex=0;


var NCO_Freq=500;//NCO Mix Freq.

for (var i = 0; i < sourceRate; i++) {
NCO_I[i] = Math.sin(Math.PI * 2 / sourceRate * i * NCO_Freq) / 4;
NCO_Q[i] = Math.cos(Math.PI * 2 / sourceRate * i * NCO_Freq) / 4;
}

function mix(ev){
	
		
	var outputLeft = ev.outputBuffer.getChannelData(0);
	var outputRight = ev.outputBuffer.getChannelData(1);
	var bufferLength = ev.outputBuffer.length;
	for(ev=0;ev<bufferLength;ev++,queueIndex++){
		//mix dbl_i & dbl_q
		dbl_i = OscData[ev]*NCO_I[queueIndex];
		dbl_q = OscData[ev]*NCO_Q[queueIndex];
		sourceRate<=queueIndex&&(queueIndex-=sourceRate);
		
		if((iDelayIndex>=FILTER_LEN))iDelayIndex=0;
		y=DelayQueue[iDelayIndex];
		DelayQueue[iDelayIndex++]=dbl_i;//<- DelayLine input
		dbl_i=y;
	
		queue_ptr = (queue_ptr + 1) % FILTER_LEN;
		BufferQueue[queue_ptr]=dbl_q;//<- Hilbert trafo input
		n = queue_ptr;
		out = 0.0;
		for(var i=FILTER_LEN,coeff_ptr=0;i>=0;i--,coeff_ptr++){
			out+= HILBERT_ARRAY[coeff_ptr++] * BufferQueue[n--];
			if(n<0)n+=FILTER_LEN;
			if(coeff_ptr>=FILTER_LEN-1)coeff_ptr=0;
		}
		dbl_q=out;
		
		outputLeft[ev] = dbl_i+dbl_q;
		//outputRight[ev] = y;
	}
}

function OscGetData(ev){
	var bufferLength = ev.inputBuffer.length;
	var input = ev.inputBuffer.getChannelData(0);
	for(var i=0;i<bufferLength;i++){
	OscData[i]=input[i];
	}
}

var Mixer = context.createScriptProcessor(2048, 0, 2);
var oscGetData = context.createScriptProcessor(2048, 1, 1);

Mixer.onaudioprocess = mix;
oscGetData.onaudioprocess = OscGetData;

//soll ein Soundkartensignal darstellen 
var oscillator = context.createOscillator();
oscillator.frequency.value = 1200;
oscillator.type = 0;
oscillator.connect(oscGetData);

oscGetData.connect(context.destination);
Mixer.connect(context.destination);

oscillator.start(0);
</script>

</body>
</html>
Vielleicht gibt es auch noch eine bessere Version des FIR’s , aber es handelt sich hierbei um einen speziellen Hilbert Trafo FIR, dieser löscht das unerwünschte Seitenband aus.
Dazu ist eben eine 180° Phasenverschiebung der beiden Eingangeignale (dbl_i und dbl_q) notwendig und das ist nicht bei jedem FIR geboten.

Es sei noch zu erwähnen das es sich hierbei um eine von C Portierte Version handelt
Original C Code:
Code:
   {   // Optimized mixer for "downconversion".
       // Based on a schematic diagram from the ARRL handbook 1996,
       // page 17.73:   "The R2: An image-rejecting D-C Receiver"
       // Principle: Splitter, TWO Mixers (with I and Q output),
       //            audio phase shift network, summer, filter.
       // Note: 90° phase shifting is done AFTER mixing (in contrast to the
       //           schemes  CFG_FREQ_MIX_LSB_UP  and  CFG_FREQ_MIX_USB_UP.
    for(sample_i=0; sample_i<number_of_samples; ++sample_i)
     {
      // Let the NCO (numerical controlled oscillator) produce quadrature-phase signals :
      dblNcoPhase += dblPhzInc;
      while(dblNcoPhase >=(T_Float)SOUND_COS_TABLE_LEN)  // "while", not "if" !!
            dblNcoPhase -=(T_Float)SOUND_COS_TABLE_LEN; // table index wrap
      iCosTableIndex = (int)dblNcoPhase;
      dblNcoI = SoundTab_fltCosTable[iCosTableIndex];
      dblNcoQ = SoundTab_fltCosTable[(iCosTableIndex+iSineTableOffset) % SOUND_COS_TABLE_LEN];

      // Now mix the incoming (higher-frequency) signal ..input_samples[]..
      //         with a sine and a cosine signal from the NCO ("LO").
      dbl_i = input_samples[sample_i];   // this is the INPUT signal

      if(pcnv->iMixerScheme == CFG_FREQ_MIX_LSB_DOWNCONVERTER)
       {
        dbl_q =  dbl_i * dblNcoI;   // LSB (below NCO freq) moved DOWN
        dbl_i =  dbl_i * dblNcoQ;
       }
      else  // the "other" sideband with reversed oscillator phases:
       {
        dbl_q =  dbl_i * dblNcoQ;   // USB (above NCO freq) moved DOWN
        dbl_i =  dbl_i * dblNcoI;
       }
      // arrived here: dbl_i is the output of the "I"-mixer,
      //               dbl_q is the output of the "Q"-mixer.


      // Let the I-channel run through a delay line [ length (N-1)/2 ]
      //  to compensate the delay from the hilbert trafo (see below).
      if(  (pcnv->iDelayQIndex < 0)
         ||(pcnv->iDelayQIndex >= ((SOUND_Hilbert_filter_length-1)/2)) )
            pcnv->iDelayQIndex = 0;
      y = pcnv->dblDelayQueue[pcnv->iDelayQIndex];
      pcnv->dblDelayQueue[pcnv->iDelayQIndex++] = dbl_i;
      dbl_i = y;

      // Let the Q-channel run through a broadband 90° phase shifter
      // (hilbert transformer, implemented as FIR filter here).
      //    keep the circular buffer pointer 'valid' all the time :
      input_queue_end = &pcnv->dblHilbertQueue[SOUND_Hilbert_filter_length-1];
      if(  (pcnv->pdblHilbertQPointer < &pcnv->dblHilbertQueue[0])
         ||(pcnv->pdblHilbertQPointer > input_queue_end) )
            pcnv->pdblHilbertQPointer = &pcnv->dblHilbertQueue[0];
      if( --pcnv->pdblHilbertQPointer < &pcnv->dblHilbertQueue[0] )
            pcnv->pdblHilbertQPointer = input_queue_end; // deal with wraparound
      *pcnv->pdblHilbertQPointer = dbl_q;     // hilbert trafo input
      queue_ptr = pcnv->pdblHilbertQPointer;  // pointer to read from input queue
      coeff_ptr = &SOUND_HilbertCoeffs[0]; // pointer to read from coeff table
      y = 0.0;                  // clear the 'global adder'
      j = (SOUND_Hilbert_filter_length+1)/2 -1;  // j=12 for 25th-order filter
      while( j-- )              // do the MAC's (with every 2nd coeff ZERO)
        {
         // coeffs[0], [2], [4].. are (almost) zero and are not calculated
         //                       so just skip the 'queue' pointer
         ++queue_ptr;  ++coeff_ptr;
         if( queue_ptr > input_queue_end ) // deal with wraparound
             queue_ptr = &pcnv->dblHilbertQueue[0];
         // odd coefficients, non-zero, do a MAC-operation :
         y += ( (*queue_ptr++) * (*coeff_ptr++) );
         if( queue_ptr > input_queue_end ) // deal with wraparound
             queue_ptr = &pcnv->dblHilbertQueue[0];
        }
      dbl_q = y;    // filter output = sum from all 'taps'

      // finally add the output of the "audio phase-shift network"...
      output_samples[sample_i] = dbl_i + dbl_q;

     } // end for (sample_i ... )



   // Generate the coefficients for the Hilber transformer (broadband 90° phase shifter)
   n = UConfig.freq_mixer_hilbert_length;
   if(n<9)
      n=9;        // added 2002-12-15
   n |=  0x0001;  // ensure ODD filter length, but why must bit1 be CLEARED ?
   n &= ~0x0002;
   if(n > SOUND_HILBERT_MAX_LENGTH)
      n = SOUND_HILBERT_MAX_LENGTH;  // limit the order of the Hilbert trafo
   SOUND_Hilbert_filter_length = n;
   /* store the n (odd) length Hilbert Transformer coefficients          */
   /*                            in array SOUND_HilbertCoeffs[]          */
   m = (n - 1) / 2;
   SOUND_HilbertCoeffs[m] = 0.0;
   for(i = 1; i <= m; i ++)
     {
       f = i & 1 ? 2.0/(i * pi) : 0.0;
       SOUND_HilbertCoeffs[m+i] = f;
       SOUND_HilbertCoeffs[m-i] = -f;
     }

   // apply a Hanning window to the hilbert coeffs to reduce ringing
   for(i = 0; i < n; i ++)
      SOUND_HilbertCoeffs[i] *= (.5 - .5*cos( (2*pi*i)/(n-1) ));
THX Wolfgang Büscher für den C-Code.

Wenn das mit den Hanning Fenster nicht klappt, dann muss wenigstens das knacken beseitigt werden.

MfG Xorg1990

- - - Aktualisiert - - -

Ähm ja wie soll ich sagen das Hanning-Fester Funktioniert nun.
Code:
//Hanning window
for(var I=0;I<FILTER_LEN;I++){
	HILBERT_ARRAY[I]*=0.5-0.5*Math.cos(2*Math.PI*I/FILTER_LEN-1);
}

Ich hätte mir gleich mal die Formel auf Wikipedia genauer ansehen sollen.:boxing:
Nun muss nur nach das lästige knacken verschwinden und ich bin zufrieden.
 
Hallo k, da ich gedacht habe ich brauch den Hilbert nicht, habe ich nicht geantwortet. Nun ja ich brauch den Hilbert Phasenschieber doch.

Auf geht’s in die 2te Runde:rolleyes::

kkapsner schrieb:
Wann kommt denn das Knacken?
Das knacken entsprach immer der blocklänge des ScriptProcessors, das lag daran das ich den index des NCO’s an falscher stelle auf 0 zurückgesetzt habe.


Probiere den Code mal, der sollte gehen.
Code:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html">
<head>
    <title>HILBERT-Trafo</title>
 </head>
<body>


<script type="text/javascript">
var  HILBERT_LENGTH = 200; //FIR Hilbert length 

var FILTER_LEN = Math.floor((HILBERT_LENGTH-1)/2);

var HILBERT_ARRAY = new Float64Array(FILTER_LEN);

HILBERT_ARRAY[jj]=0.0;
for(var jj=0;jj<=FILTER_LEN; jj++){
 var coeff = jj&1 ? 2.0/(jj*Math.PI) : 0.0;
  HILBERT_ARRAY[FILTER_LEN+jj] = coeff;
  HILBERT_ARRAY[FILTER_LEN-jj] = -coeff;
  
}

//Hanning window
for(var I=0;I<FILTER_LEN;I++){
	HILBERT_ARRAY[I]*=0.5-0.5*Math.cos(2*Math.PI*I/FILTER_LEN-1);
}

console.log(HILBERT_ARRAY);



window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();

//Globals

var sourceRate = context.sampleRate;
console.log(sourceRate);
var OscData = new Float32Array(16384);
var NCO_I = new Float32Array(sourceRate);
var NCO_Q = new Float32Array(sourceRate);
var DelayQueue =  new Float32Array(FILTER_LEN);
var BufferQueue =  new Float32Array(FILTER_LEN);
var Index =0, iWrite =0, iRead=0, n=0, y=null, dbl_i=null, out=null, dbl_q=null, mixed=0, queue_ptr=0, iDelayIndex=0;


var NCO_Freq=1000;//NCO Mix Freq.
var OSC_Freq = 1200;

for (var i = 0; i < sourceRate; i++) {
NCO_I[i] = Math.sin(Math.PI * 2 / sourceRate * i * NCO_Freq) / 4;
NCO_Q[i] = Math.cos(Math.PI * 2 / sourceRate * i * NCO_Freq) / 4;
}

function mix(ev){
	var outputLeft = ev.outputBuffer.getChannelData(0);
	var outputRight = ev.outputBuffer.getChannelData(1);
	var bufferLength = ev.outputBuffer.length;
	
	for(ev=0;ev<bufferLength;ev++,Index++){
		//mix dbl_i & dbl_q
		if(Index>=sourceRate)Index=0;
		dbl_i = OscData[ev]*NCO_I[Index];
		dbl_q = OscData[ev]*NCO_Q[Index];
		
		
		if(iDelayIndex>=FILTER_LEN)iDelayIndex=0;
		y=DelayQueue[iDelayIndex];
		DelayQueue[iDelayIndex++]=dbl_i;//<- DelayLine input
		
		dbl_i=y;
		
		
		queue_ptr = (queue_ptr + 1) % FILTER_LEN;
		BufferQueue[queue_ptr]=dbl_q;//<- Hilbert trafo input
		n = queue_ptr;
		out = 0.0;
		for(var i=FILTER_LEN,coeff_ptr=0;i>=0;i--,++coeff_ptr){
			out+= HILBERT_ARRAY[coeff_ptr++] * BufferQueue[n--];
			if(n<0)n+=FILTER_LEN;
			if(coeff_ptr>=FILTER_LEN-1)coeff_ptr=0;
		}
		
		dbl_q=out;
		outputLeft[ev] = dbl_i+dbl_q;
		//outputRight[ev] = y;
	}
}

function OscGetData(ev){
	var bufferLength = ev.inputBuffer.length;
	var input = ev.inputBuffer.getChannelData(0);
	for(var i=0;i<bufferLength;i++){
	OscData[i]=input[i];
	}
}

var Mixer = context.createScriptProcessor(16384, 0, 2);
var oscGetData = context.createScriptProcessor(16384, 1, 1);


Mixer.onaudioprocess = mix;
oscGetData.onaudioprocess = OscGetData;

//soll ein Soundkartensignal darstellen 
var oscillator = context.createOscillator();
oscillator.frequency.value = OSC_Freq;
oscillator.type = 0;
oscillator.connect(oscGetData);

oscGetData.connect(context.destination);
Mixer.connect(context.destination);

oscillator.start(0);
</script>
</body>
</html>


Das Problem besteht aber nun darin das sich das unerwünschte Seitenband nicht aufhebt.
Es ist zwar leicht gedämpft ca.-15 dB, eigentlich sollten es -40 bis -60dB sein. Nur warum klappt das nicht? Mögliche Ursachen es gibt keine wirkliche 90° Phasenverschiebung, die Hilbert Koeffizienten sind falsch, eine Zählervariable wird an Falscher stelle auf 0 zurückgesetzt. Alles ist möglich.

Kleine Frage am Rande:
Warum kann ich keine while Schleife in einer for Schleife laufen lasen, das verursacht immer 100% cpu load. Aber in dem C geht das.

Wenn einer eine bessere idee hat den C Code in JS zu Portieren, ich bin ganz Ohr.
 
OK - jetzt hör' ich auch was.

Ich würde jetzt auf nicht genau 90° tippen - aber ich muss sagen, dass ich die mathematische Richigkeit des Codes nicht wirklich überprüfen kann. Kannst du mir mal einen Link geben, in dem gezeigt wird, wie das mathematisch aussieht?

Ich glaube aber, dass da mit der Faltung irgendwas nicht ganz stimmt.

Warum kann ich keine while Schleife in einer for Schleife laufen lasen, das verursacht immer 100% cpu load. Aber in dem C geht das.
Ein while in einer for ist schon möglich - eigentlich macht die for auch mehr und sollte die CPU mehr belasten. Wie sah' das denn genau aus?
 
Code:
Hi kkapsner, das mit der while schleife in eine for Schleife hat Problem hat sich erledigt.
Es war so dass mein System auf 100% CPU Load gefahren ist und ich nur noch den Stecker ziehen konnte. Nach einer Neuformierung geht das nun… muss ein Bug gewesen sein.

Den Hilbert Transformator versteh ich so:
Man hat 2 Oszillator Signale eine Phase davon ist 90° Phasenverschoben (cos).
Diese werden jetzt mit dem eingangs Sample gemischt. Der Hilbert Trafo verschiebt nun eine Mischung nochmals um 90° was Zusammen 180° ergibt. Beim Addieren löscht sich dann das eine Seitenband aus.

Für was die Hilbert Koeffizienten da sind habe ich auch noch nicht begriffen.
Und was ist gemeint mit odd filter length??

Mathematik zum Hilbert gibt’s hier auf Seite 7, oder auf Wikipedia.
Wobei bei Wiki mehr auf die Transformation eingegangen wird. Ich will ja das Sinus nicht verändern, sondern nur verschieben.

Habe nun noch mal eine 1zu1 Portierung des C Skripts vorgenommen, die führt aber auch nicht zum gewünschten Effekt.
Die Frage ist ja: Was macht C so anders das es in C klappt und in JS nicht?

Code:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html">
<head>
    <title>HILBERT-Trafo</title>
 </head>
<body>
<div id="ausgabe"></div>
<br>
<div id="ausgabe2"></div>
<script type="text/javascript">
var  HILBERT_LENGTH =21; //FIR Hilbert Length 

var FILTER_LEN = Math.floor((HILBERT_LENGTH-1)/2);

var HILBERT_ARRAY = new Float64Array(FILTER_LEN);


for(var jj=1;jj<FILTER_LEN; jj++){
 var coeff = jj&1 ? 2.0/(jj*Math.PI) : 0.0;
 // HILBERT_ARRAY[jj] = 1/((jj-FILTER_LEN/2)-0.5)/Math.PI; //<- Eigene Implementierung  der Koeffizienten
  HILBERT_ARRAY[jj] = coeff;
  HILBERT_ARRAY[FILTER_LEN-jj] = Math.abs(coeff);
}

//Hanning window
for(var I=1;I<FILTER_LEN;I++){
HILBERT_ARRAY[I]*=(0.5-0.5*Math.cos((2*Math.PI*I)/(FILTER_LEN-1)));
}

console.log(HILBERT_ARRAY);



window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();

//Globals

var sourceRate = context.sampleRate;
console.log(sourceRate);
var OscData = new Float32Array(16384);
var NCO_I = new Float32Array(sourceRate);
var NCO_Q = new Float32Array(sourceRate);

var DelayQueue =  new Float32Array(FILTER_LEN);
var BufferQueue =  new Float32Array(FILTER_LEN);
var Index =0, iWrite =0, iRead=0, n=0, y=null, dbl_i=null, out=null, dbl_q=null, iDelayIndex=0, queue_ptr=0, qDelayIndex=0;


var NCO_Freq=1200;//NCO Mix Freq.
var OSC_Freq = 1000;

for (var i = 0; i < sourceRate; i++) {
NCO_I[i] = Math.sin(Math.PI * 2 / sourceRate * i * NCO_Freq) / 4;
NCO_Q[i] = Math.cos(Math.PI * 2 / sourceRate * i * NCO_Freq) / 4;
}


function mix(ev){
	var outputLeft = ev.outputBuffer.getChannelData(0);
	var outputRight = ev.outputBuffer.getChannelData(1);
	var bufferLength = ev.outputBuffer.length;
	//for Schleife durch 16384 samples
	for(ev=0;ev<bufferLength;ev++,Index++){
		sourceRate<=Index&&(Index-=sourceRate);
		//mix dbl_i & dbl_q
		dbl_i = OscData[ev]*NCO_I[Index];
		dbl_q = OscData[ev]*NCO_Q[Index];
		
		if(iDelayIndex>=FILTER_LEN)iDelayIndex=0;
		var y=DelayQueue[iDelayIndex];
		DelayQueue[iDelayIndex++]=dbl_i;
		dbl_i=y;
		
		if(qDelayIndex < 0)qDelayIndex = 0;
		var coeff_ptr = 0;
		if(qDelayIndex < 0 || qDelayIndex > FILTER_LEN)qDelayIndex=0;
		if(--qDelayIndex < 0)qDelayIndex = FILTER_LEN;
		BufferQueue[queue_ptr]=dbl_q;
		queue_ptr = qDelayIndex;
		
		out = 0.0;
		var n=FILTER_LEN;
		//document.getElementById("ausgabe").innerHTML=(coeff_ptr);
		while(n--){
			++coeff_ptr;++queue_ptr;
			if(queue_ptr>=FILTER_LEN)queue_ptr=0,coeff_ptr = 0;
			out+= HILBERT_ARRAY[coeff_ptr++] * BufferQueue[queue_ptr++];
			if(queue_ptr>=FILTER_LEN)queue_ptr=0,coeff_ptr = 0;
		}
		dbl_q=out;
		outputLeft[ev] = dbl_i+dbl_q;
		//document.getElementById("ausgabe2").innerHTML=(coeff_ptr);
		//outputRight[ev] = y;
	}
}

function OscGetData(ev){
	var bufferLength = ev.inputBuffer.length;
	var input = ev.inputBuffer.getChannelData(0);
	for(var i=0;i<bufferLength;i++){
	OscData[i]=input[i];
	}
}




var Mixer = context.createScriptProcessor(16384, 0, 2);
var oscGetData = context.createScriptProcessor(16384, 1, 1);


Mixer.onaudioprocess = mix;
oscGetData.onaudioprocess = OscGetData;

//soll ein Soundkartensignal darstellen 
var oscillator = context.createOscillator();
oscillator.frequency.value = OSC_Freq;
oscillator.type = 0;
oscillator.connect(oscGetData);

oscGetData.connect(context.destination);
Mixer.connect(context.destination);



oscillator.start(0);

</script>

</body>
</html>
 
So - hab' mir das jetzt mal ein bisschen näher angesehen und da ist einiges (auch schon im C) falsch.

Deswegen hab' ich mich mal hingesetzt und das etwas umgeschrieben:
HTML:
<!DOCTYPE html>
<html>
<head>
    <title>HILBERT-Trafo</title>
 </head>
<body>
<div id="ausgabe"></div>
<br>
<div id="ausgabe2"></div>
<script>

function createHilbertFilterCoefficients(halfWidth){
	var completeWidth = halfWidth * 2 + 1;
	var coefficients = new Float64Array(completeWidth);
	coefficients[halfWidth] = 0;
	for (var i = 1; i <= halfWidth; i += 1){
		var coeff = i&1? 2.0/(i*Math.PI): 0.0;
		coefficients[halfWidth + i] = coeff;
		coefficients[halfWidth - i] = -coeff;
	}
	
	return coefficients;
}
function Ring(length){
	this.buffer = new Float32Array(length);
	this.index = 0;
	this.length = length;
}
Ring.prototype.push = function(value){
	var oldValue = this.buffer[this.index];
	this.buffer[this.index] = value;
	this.index = (this.index + 1) % this.length;
	return oldValue;
}
Ring.prototype.shift = function(){
	var oldValue = this.buffer[this.index];
	this.index = (this.index + 1) % this.length;
	return oldValue;
}
Ring.prototype.get = function(index){
	index = (index + this.index) % this.length;
	while (index < 0){
		index += this.length;
	}
	return this.buffer[index];
}
Ring.prototype.set = function(index, value){
	index = (index + this.index) % this.length;
	while (index < 0){
		index += this.length;
	}
	this.buffer[index] = value;
	return this;
}
Ring.prototype.forEach = function(func){
	for (var i = 0; i < this.length; i += 1){
		func(this.get(i), i, this);
	}
}


FILTER_LEN = 100;
HILBERT_LENGTH = FILTER_LEN * 2 + 1
HILBERT_ARRAY = createHilbertFilterCoefficients(FILTER_LEN);



console.log(HILBERT_ARRAY);

window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();

//Globals

var sourceRate = context.sampleRate;
var oscData = new Float32Array(16384);

var delayQueue =  new Ring(FILTER_LEN);
var bufferQueue =  new Ring(HILBERT_LENGTH);

var NCO_Freq = 1200;
var OSC_Freq = 1000;

var NCO_I = new Ring(sourceRate);
var NCO_Q = new Ring(sourceRate);
for (var i = 0; i < sourceRate; i += 1) {
	NCO_I.push(Math.sin(Math.PI * 2 / sourceRate * i * NCO_Freq) / 4);
	NCO_Q.push(Math.cos(Math.PI * 2 / sourceRate * i * NCO_Freq) / 4);
}


function mix(ev){
	var outputLeft = ev.outputBuffer.getChannelData(0);
	var outputRight = ev.outputBuffer.getChannelData(1);
	var bufferLength = ev.outputBuffer.length;
	for (var i = 0; i < bufferLength; i += 1){
		var dbl_i = delayQueue.push(oscData[i] * NCO_I.shift());
		bufferQueue.push(oscData[i] * NCO_Q.shift());
		
		var dbl_q = 0.0;
		bufferQueue.forEach(function(value, i){
			dbl_q += HILBERT_ARRAY[i] * value;
		});
		
		outputLeft[i] = dbl_i + dbl_q;
		outputRight[i] = outputLeft[i];
	}
}

function OscGetData(ev){
	var bufferLength = ev.inputBuffer.length;
	var input = ev.inputBuffer.getChannelData(0);
	for(var i = 0; i < bufferLength; i += 1){
		oscData[i] = input[i];
	}
}




var Mixer = context.createScriptProcessor(16384, 0, 2);
var oscGetData = context.createScriptProcessor(16384, 1, 1);


Mixer.onaudioprocess = mix;
oscGetData.onaudioprocess = OscGetData;

//soll ein Soundkartensignal darstellen 
var oscillator = context.createOscillator();
oscillator.frequency.value = OSC_Freq;
oscillator.type = "sine";
oscillator.connect(oscGetData);
//oscillator.connect(context.destination);

oscGetData.connect(context.destination);
Mixer.connect(context.destination);



oscillator.start(0);

</script>

</body>
</html>
- aber irgendwas ist da noch falsch, da beide stark gedämpft werden.
 
Du nun wieder, hast ja gleich alles auf den Kopf gestellt.
Das Problem besteht anscheinend darin das JavaScript keine 90° Phasenverschiebung zu stande bringt. Der Screenshot zeigt keinen perfekten Kreis als Lissajous. Dafür habe ich im rechten Channel NCO_I ausgegeben und im Linken dbl_q.
Keine 90 grad.png
Die Änderung:
Code:
function mix(ev){
	var outputLeft = ev.outputBuffer.getChannelData(0);
	var outputRight = ev.outputBuffer.getChannelData(1);
	var bufferLength = ev.outputBuffer.length;
	for (var i = 0; i < bufferLength; i += 1){
		var dbl_i = delayQueue.push(oscData[i] * NCO_I.shift());
		bufferQueue.push(NCO_I.shift());
		
		var dbl_q = 0.0;
		bufferQueue.forEach(function(value, i){
			dbl_q += HILBERT_ARRAY[i] * value;
		});
		
		outputLeft[i] = dbl_q;
		outputRight[i] = NCO_I.shift();
	}
}



Was mir auf Anhieb auffällt ist:
Wo überspringst du jedes 2te Array Element? Jedes 2te ist ja 0.
Das Hilbert Array enthält bis zur hälfte negative Werte und dann Positive. Sollte es nicht genau umgekehrt sein?! K.a. ob das überhaupt eine Rolle spielt.


Ich war in der Zeit auch nicht untätig und habe auch noch mal von vorne begonnen.

Rausgekommen ist dieser Code:
Code:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html">

<head>
    <title>HILBERT-Trafo</title>
</head>

<body>


    <script type="text/javascript">
        var HILBERT_LENGTH = 196; //FIR Hilbert length 

        var FILTER_LEN = Math.floor((HILBERT_LENGTH - 1) / 2);

        var HILBERT_ARRAY = new Float64Array(FILTER_LEN);

        var Pegel = 0;
        for (var jj = 0; jj < Math.floor(FILTER_LEN / 2); jj++) {
            var coeff = jj & 1 ? 2.0 / ((FILTER_LEN / 2 - jj) * Math.PI) : 0.0; //<- Implementierung W Kiefer
            //var coeff = jj&1 ? 2.0/(jj*Math.PI) : 0.0;//<- Implementierung Wolf Büscher
            //var coeff = 1/((jj-FILTER_LEN/2)-0.5)/Math.PI; //<- Eigene Implementierung  der Koeffizienten
            HILBERT_ARRAY[jj - 1] = coeff;
            HILBERT_ARRAY[FILTER_LEN - jj - 1] = -coeff;
        }

        for (var i = 0; i < FILTER_LEN; i += 2) {
            Pegel += HILBERT_ARRAY[i];
        }

         //Hanning window
         //for(var I=0;I<FILTER_LEN;I++){
         //HILBERT_ARRAY[I]*=0.5-0.5*Math.cos(2*Math.PI*I/FILTER_LEN-1);
         //}

         // Print the Hilbert coefficients in the Console (Chrome F12, FF strg+shift+j)
        console.log(HILBERT_ARRAY);
        console.log(Pegel);
        window.AudioContext = window.AudioContext || window.webkitAudioContext;
        var context = new AudioContext();

         //Globals
         //Save the Samplerate from the Web audio api in var sourceRate
        var sourceRate = context.sampleRate;

        var OscData = new Float32Array(16384);

         //
        var NCO_I = new Float32Array(sourceRate);
        var NCO_Q = new Float32Array(sourceRate);

         // DelayQueue is the delay buffer 
        var DelayQueue = new Float32Array(FILTER_LEN);
         // BufferQueue is the FIR RingBuffer
        var BufferQueue = new Float32Array(FILTER_LEN);

        var Index = 0,
            y = null,
            dbl_i = null,
            out = null,
            dbl_q = null,
            queue_ptr = 0,
            coeff_ptr = 0,
            iDelayIndex = 0,
            maxi = 0.001,
            maxq = 0.001;


        var NCO_Freq = 1000; //NCO Mix Freq.
        var OSC_Freq = 1200;

         //generate sine in float 32Bit samples and save the result in var NCO_I
        for (var i = 0; i < sourceRate; i++) {
            NCO_I[i] = Math.sin(Math.PI * 2 / sourceRate * i * NCO_Freq) / 4;
            NCO_Q[i] = Math.cos(Math.PI * 2 / sourceRate * i * NCO_Freq) / 4;
        }

         //this function fires all  0.03715 sec
         //1/44100*16384 Samples
        function mix(ev) {
            var outputLeft = ev.outputBuffer.getChannelData(0);
            var outputRight = ev.outputBuffer.getChannelData(1);
            var bufferLength = ev.outputBuffer.length;
            //for loop through 16384 samples	
            for (ev = 0; ev < bufferLength; ev++, Index++) {
                //mix dbl_i & dbl_q
                if (Index >= sourceRate) Index = 0;
                dbl_i = OscData[ev] * NCO_I[Index]; //<-The index variable run up to 44100 samples, so the sine needs 44100 Samples
                dbl_q = OscData[ev] * NCO_Q[Index];

                //Let the I-channel run through a delay line
                if (iDelayIndex >= Math.floor(FILTER_LEN / 2)) iDelayIndex = 0;
                y = DelayQueue[iDelayIndex];
                DelayQueue[iDelayIndex++] = dbl_i; //<- DelayLine input
                dbl_i = y;

                coeff_ptr = 0;
                queue_ptr = (queue_ptr + 1) % FILTER_LEN;
                BufferQueue[queue_ptr] = dbl_q; //<- Hilbert trafo input
                var n = queue_ptr;
                out = 0.0;
                var j = Math.floor(FILTER_LEN / 2);
                // Fir filter Loop 
                while (j--) {
                    out += HILBERT_ARRAY[coeff_ptr++] * BufferQueue[n--]; // accumulate the Hilbert coeffs with the Q-Channel samples 
                    n--;
                    coeff_ptr++;
                    if (n < 0) n += FILTER_LEN;
                }
                dbl_q = out;

                //Ermittlung der beiden Amplituden:
                if (maxi < dbl_i) maxi = dbl_i;
                if (maxq < dbl_q) maxq = dbl_q;

                // left output to the speaker
                outputLeft[ev] = (maxi / maxq) * (dbl_i + (dbl_q * Pegel));
                //right output to the speaker
                //outputRight[ev] = out;
            }
        }

         //get the oscillator signal from the Web audio api ans save the samples in  var OscData
        function OscGetData(ev) {
            var bufferLength = ev.inputBuffer.length;
            var input = ev.inputBuffer.getChannelData(0);
            for (var i = 0; i < bufferLength; i++) {
                OscData[i] = input[i];
            }
        }

        var Mixer = context.createScriptProcessor(16384, 0, 2);
        var oscGetData = context.createScriptProcessor(16384, 1, 1);

        Mixer.onaudioprocess = mix;
        oscGetData.onaudioprocess = OscGetData;

         //soll ein Soundkartensignal darstellen 
        var oscillator = context.createOscillator();
        oscillator.frequency.value = OSC_Freq;
        oscillator.type = 0;
        oscillator.connect(oscGetData);

        oscGetData.connect(context.destination);
        Mixer.connect(context.destination);
		
        oscillator.start(0);
    </script>
</body>
</html>

Das Ergebnis ist dasselbe. Die Unterdrückung beträgt bei beiden Skripten ca. 20dB.
Unterdrückung Kkapsner:
kkapsner unter.png
Meine Unterdrückung:
meine unter.png

Ich hatte vorhin noch ein Anfall geistiger Umnachtung und habe alle Koeffizienten zusammen addiert, und vor der ausgabe mit rein Multipliziert bring natürlich überhaupt nix.
Desweitern habe ich die Amplituden von I und Q Channel mit bei der Ausgabe angeglichen, hat auch nicht viel gebracht.

Jedenfalls ich gebe hier auf. :mad:
 
Du nun wieder, hast ja gleich alles auf den Kopf gestellt.
Der C-Code (und deine Portierung) war mir einfach viel zu unübersichtlich...

Das Problem besteht anscheinend darin das JavaScript keine 90° Phasenverschiebung zu stande bringt. Der Screenshot zeigt keinen perfekten Kreis als Lissajous. Dafür habe ich im rechten Channel NCO_I ausgegeben und im Linken dbl_q.
Nein - JS kann schon die 90°. Du hast da zu oft .shift() aufgerufen und dadurch kommt ein zusätzlicher Phasenversatz rein. Außerdem hat oscData natürlich auch irgendeinen Phasenversatz. Das müsste so aussehen:
Code:
function mix(ev){
	var outputLeft = ev.outputBuffer.getChannelData(0);
	var outputRight = ev.outputBuffer.getChannelData(1);
	var bufferLength = ev.outputBuffer.length;
	for (var i = 0; i < bufferLength; i += 1){
		var dbl_i = delayQueue.push(NCO_I.get(0));
		bufferQueue.push(NCO_I.shift());
		
		var dbl_q = 0.0;
		bufferQueue.forEach(function(value, i){
			dbl_q += HILBERT_ARRAY[i] * value;
		});
		
		outputLeft[i] = dbl_q;
		outputRight[i] = dbl_i;
	}
}
Hilbert.PNG


Wo überspringst du jedes 2te Array Element? Jedes 2te ist ja 0.
Nirgens. Ist ja auch nicht nötig, da ja mit 0 multipliziert wird... und erst muss es funktionieren, bevor man sich an's optimieren macht.

Das Hilbert Array enthält bis zur hälfte negative Werte und dann Positive. Sollte es nicht genau umgekehrt sein?! K.a. ob das überhaupt eine Rolle spielt.
Nein, das sollte nicht umgekehrt sein und ja, das spielt eine Rolle. Bestimmt ob um Pi/2 nach vorne oder hinten verschoben wird.

Rausgekommen ist dieser Code:
Ist mir, wie gesagt, viel zu unübersichtlich und

Das Ergebnis ist dasselbe. Die Unterdrückung beträgt bei beiden Skripten ca. 20dB.
Die Unterdrückung ist zwar ähnlich, aber bei mir ist das Frequenzspektrum viel sauberer - du hast da lauter kleine Spikes drin...

Jedenfalls ich gebe hier auf.
Das fände ich jetzt schade.
 
xoerg1990 schrieb:
Jedenfalls ich gebe hier auf.
kkapsern schrieb:
Das fände ich jetzt schade.
Naja ich weiß einfach nicht mehr weiter.
Da ja nun eine 90° Phasenverschiebung bestätigt ist bleibt ja der Fehler nur noch bei den Koeffizienten.

kkapsner schrieb:
du hast da lauter kleine Spikes drin...
In meien Code geht das jetzt auch ohne 'Spikes', habe das Überspringen jeder 2ten Arrayelemente herausgenommen und es geht -20bB , die 'Spikes' sind auch fast weg.
Ach und die negativen Koeffizienten kommen jetzt zu erst.

In deiner Interpretation von den Hilbert Trafo sind die Spikes ja nur nicht da, da alles irgendwie Gedämpft ist.

Werde mir jetzt via getUserMedia einen Mikrofon input basteln und dann mal sehen ob das eine Seitenband im Rauschen verschwindet.
 
Zuletzt bearbeitet:
In deiner Interpretation von den Hilbert Trafo sind die Spikes ja nur nicht da, da alles irgendwie Gedämpft ist.
Ganau das meinte ich mit
aber irgendwas ist da noch falsch
Ich hab' die Daten vom Oscillator im Verdacht. Werde das die Signalerzeugung und Amplitudenmodulieren mal auch per Hand machen. Mal sehen, ob's was hilft.

Werde mir jetzt via getUserMedia einen Mikrofon input basteln und dann mal sehen ob das eine Seitenband im Rauschen verschwindet.
Klingt nach einer guten Idee.

PS: Hab' jetzt doch mal kurz in deinen Code geschaut. Du hast bei der Erzeugung von HILBER_ARRAY das "n" und "m" im C-Code irgendwie durcheinander gebracht. HILBER_ARRAY muss die Länge HILBER_LENGTH haben. Und auch wird das falsch belegt, da deine Pegel-Variable Null sein muss.
 
Nix aber auch gar nix funktioniert. Erst habe ich den halben Tag damit verbracht getUserMedia in Gang zu bringen,
bis ich dann herausgefunden habe das man das File auf einem Server (Apache) liegen haben muss.
Dann habe ich meinen Pegelgenerator (in Hardware) aufgebaut angeschlossen und… Puff das Ding hat sich in Rauch aufgelöst und hat meine Soundkarte gleich mit in den Tot gerissen.

~300€ ganz schön Teuer so’n bissel Programmiren.:mad:

Das getUserMedia Skript schaut so aus.
Code:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html">

<head>
    <title>HILBERT-Trafo</title>
</head>

<body>
    <div id="ausgabe"></div>
    <script type="text/javascript">
        window.AudioContext = window.AudioContext || window.webkitAudioContext;
        var context = new AudioContext();

        if (!navigator.getUserMedia)
            navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
        if (!navigator.cancelAnimationFrame)
            navigator.cancelAnimationFrame = navigator.webkitCancelAnimationFrame || navigator.mozCancelAnimationFrame;
        if (!navigator.requestAnimationFrame)
            navigator.requestAnimationFrame = navigator.webkitRequestAnimationFrame || navigator.mozRequestAnimationFrame;

        navigator.getUserMedia({
            audio: true
        }, success, function(e) {
            alert('Error getting audio');
            console.log(e);
        });

        function createHilbertFilterCoefficients(halfWidth) {
            var completeWidth = halfWidth * 2 + 1;
            var coefficients = new Float64Array(completeWidth);
            coefficients[halfWidth] = 0;
            for (var i = 1; i <= halfWidth; i += 1) {
                // var coeff = i & 1 ? 2.0 / ((FILTER_LEN / 2 - i) * Math.PI) : 0.0;
                var coeff = i & 1 ? 2.0 / (i * Math.PI) : 0.0;
                coefficients[halfWidth + i] = coeff;
                coefficients[halfWidth - i] = -coeff;
            }

            return coefficients;
        }

        function Ring(length) {
            this.buffer = new Float32Array(length);
            this.index = 0;
            this.length = length;
        }
        Ring.prototype.push = function(value) {
            var oldValue = this.buffer[this.index];
            this.buffer[this.index] = value;
            this.index = (this.index + 1) % this.length;
            return oldValue;
        }
        Ring.prototype.shift = function() {
            var oldValue = this.buffer[this.index];
            this.index = (this.index + 1) % this.length;
            return oldValue;
        }
        Ring.prototype.get = function(index) {
            index = (index + this.index) % this.length;
            while (index < 0) {
                index += this.length;
            }
            return this.buffer[index];
        }
        Ring.prototype.set = function(index, value) {
            index = (index + this.index) % this.length;
            while (index < 0) {
                index += this.length;
            }
            this.buffer[index] = value;
            return this;
        }
        Ring.prototype.forEach = function(func) {
            for (var i = 0; i < this.length; i += 1) {
                func(this.get(i), i, this);
            }
        }
        var Pegel = 0;

        FILTER_LEN = 100;
        HILBERT_LENGTH = FILTER_LEN * 2 + 1
         HILBERT_ARRAY = createHilbertFilterCoefficients(FILTER_LEN);


        for (var i = 0; i < FILTER_LEN; i += 2) {
            Pegel += HILBERT_ARRAY[i];
        }


         // Print the Hilbert coefficients in the Console (Chrome F12, FF strg+shift+j)
        console.log(HILBERT_ARRAY);
        console.log(Pegel);


        var sourceRate = context.sampleRate;


        var delayQueue = new Ring(FILTER_LEN);
        var bufferQueue = new Ring(HILBERT_LENGTH);

        var NCO_Freq = 1000;

        var NCO_I = new Ring(sourceRate);
        var NCO_Q = new Ring(sourceRate);
        for (var i = 0; i < sourceRate; i += 1) {
            NCO_I.push(Math.sin(Math.PI * 2 / sourceRate * i * NCO_Freq) / 4);
            NCO_Q.push(Math.cos(Math.PI * 2 / sourceRate * i * NCO_Freq) / 4);
        }

        var maxi = 0.001,
            maxq = 0.001;


        function success(stream) {
            Mixer = context.createScriptProcessor(2048, 1, 2);
            Microphone = context.createMediaStreamSource(stream);



            function mix(ev) {
                var inputLeft = ev.inputBuffer.getChannelData(0)
                var outputLeft = ev.outputBuffer.getChannelData(0);
                var outputRight = ev.outputBuffer.getChannelData(1);
                var bufferLength = ev.outputBuffer.length;
                for (var i = 0; i < bufferLength; i += 1) {
                    var dbl_i = delayQueue.push(inputLeft[i] * NCO_I.shift());
                    bufferQueue.push(inputLeft[i] * NCO_Q.shift());

                    var dbl_q = 0.0;

                    bufferQueue.forEach(function(value, i) {
                        dbl_q += HILBERT_ARRAY[i] * value;
                    });

                    //Ermittlung der beiden Amplituden:
                    if (maxi < dbl_i) maxi = dbl_i;
                    if (maxq < dbl_q) maxq = dbl_q;

                    outputLeft[i] = (maxi / maxq) * dbl_i + dbl_q;
                    //outputRight[i] = outputLeft[i];
                }
            }

            var Mixer = context.createScriptProcessor(16384, 1, 2);
            Mixer.onaudioprocess = mix;


            Microphone.connect(Mixer);
            Mixer.connect(context.destination);

        }
    </script>
</body>

</html>

Ich habe mi in der Zwischenzeit überlegt: Was spricht den gegen einen IIR??

Da gibt schon einen fertig in audiolib.js
Doch der Stürzt in NodeJs immer ab , im Browser kein ding.
Benötigen tue den Filter aber in NodeJs

Fehlermledung:
Code:
speakerTest.js:96
	this.reset(sampleRate, b0/a0, b1/a0, b2/a0, a1/a0, a2/a0);
	     ^
TypeError: Object function BiquadFilter(sampleRate, b0, b1, b2, a1, a2) {
	this.reset.apply(this, arguments);
} has no method 'reset'
    at Function.BiquadFilter.LowPass (/home/xorg1990/Dokumente/speakerTest.js:96:7)
    at Object.<anonymous> (/home/xorg1990/Dokumente/speakerTest.js:195:28)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:906:3

und den Code dazu:
Code:
/**
 * A Custom Biquad Filter Effect
 * http://en.wikipedia.org/wiki/Digital_biquad_filter
 * Adapted from http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
 * 
 * @effect
 *
 * @arg =sampleRate
 * @arg =b0
 * @arg =b1
 * @arg =b2
 * @arg =a1
 * @arg =a2
 *
 * @param type:UInt units:Hz default:44100 sampleRate Sample Rate the apparatus operates on.
 * @param {number} b0 Biquadratic difference equation parameter
 * @param {number} b1 Biquadratic difference equation parameter
 * @param {number} b2 Biquadratic difference equation parameter
 * @param {number} a1 Biquadratic difference equation parameter
 * @param {number} a2 Biquadratic difference equation parameter
*/
function BiquadFilter (sampleRate, b0, b1, b2, a1, a2) {
	this.reset.apply(this, arguments);
}

/**
 * A generic Biquad Filter class, used internally to create BiquadFilter classes.
 * @constructor
 * @this BiquadFilterClass
*/
BiquadFilter.BiquadFilterClass = function BiquadFilterClass () {
	var k;
	for (k in BiquadFilterClass.prototype) {
		if (BiquadFilterClass.prototype.hasOwnProperty) {
			this[k] = this[k];
		}
	}
};

BiquadFilter.BiquadFilterClass.prototype = {
	sampleRate:	44100,
	sample:		0,
	inputs:		null,
	outputs:	null,
	coefs:		null,

	pushSample: function (s) {
		var	c	= this.coefs,
			i	= this.inputs,
			o	= this.outputs;
		this.sample = c.b0 * s + c.b1 * i[0] + c.b2 * i[1] - c.a1 * o[0] - c.a2 * o[1];
		i.pop();
		i.unshift(s);
		o.pop();
		o.unshift(this.sample);
		return this.sample;
	},
	getMix: function () {
		return this.sample;
	},
	reset: function (sampleRate, b0, b1, b2, a1, a2) {
		this.inputs = [0,0];
		this.outputs = [0,0];
		this.sampleRate = isNaN(sampleRate) ? this.sampleRate : sampleRate;
		if (arguments.length > 1){
			this.coefs	= { b0:b0, b1:b1, b2:b2, a1:a1, a2:a2 };
		}
	}
};

/**
 * Creates a Biquad Low-Pass Filter Effect
 * 
 * @name LowPass
 * @subeffect BiquadFilter BiquadLowPassFilter
 *
 * @arg =sampleRate
 * @arg =cutoff
 * @arg =Q
 *
 * @param type:UInt units:Hz sampleRate Sample Rate the apparatus operates on.
 * @param type:Float units:Hz cutoff Low-pass cutoff frequency.
 * @param type:Float min:0.0 max:1.0 Q Filter Q-factor (Q<0.5 filter underdamped, Q>0.5 filter overdamped)
*/
BiquadFilter.LowPass = function (sampleRate, cutoff, Q) {
	var	w0	= 2* Math.PI*cutoff/sampleRate,
		cosw0	= Math.cos(w0),
		sinw0   = Math.sin(w0),
		alpha   = sinw0/(2*Q),
		b0	=  (1 - cosw0)/2,
		b1	=   1 - cosw0,
		b2	=   b0,
		a0	=   1 + alpha,
		a1	=  -2*cosw0,
		a2	=   1 - alpha;
	this.reset(sampleRate, b0/a0, b1/a0, b2/a0, a1/a0, a2/a0);
};

/**
 * Creates a Biquad High-Pass Filter Effect
 * 
 * @name HighPass
 * @subeffect BiquadFilter BiquadHighPassFilter
 *
 * @arg =sampleRate
 * @arg =cutoff
 * @arg =Q
 *
 * @param type:UInt units:Hz sampleRate Sample Rate the apparatus operates on.
 * @param type:Float units:Hz cutoff High-pass cutoff frequency.
 * @param type:Float min:0.0 max:1.0 Q Filter Q-factor (Q<0.5 filter underdamped, Q>0.5 filter overdamped)
*/
BiquadFilter.HighPass = function (sampleRate, cutoff, Q) {
	var	w0	= 2* Math.PI*cutoff/sampleRate,
		cosw0   = Math.cos(w0),
		sinw0   = Math.sin(w0),
		alpha   = sinw0/(2*Q),
		b0	=  (1 + cosw0)/2,
		b1	= -(1 + cosw0),
		b2	=   b0,
		a0	=   1 + alpha,
		a1	=  -2*cosw0,
		a2	=   1 - alpha;
	this.reset(sampleRate, b0/a0, b1/a0, b2/a0, a1/a0, a2/a0);
};

/**
 * Creates a Biquad All-Pass Filter Effect
 * 
 * @name AllPass
 * @subeffect BiquadFilter BiquadAllPassFilter
 *
 * @arg =sampleRate
 * @arg =f0
 * @arg =Q
 *
 * @param type:UInt units:Hz sampleRate Sample Rate the apparatus operates on.
 * @param type:Float units:Hz min:0.0 f0 Significant frequency: filter will cause a phase shift of 180deg at f0.
 * @param type:Float min:0.0 max:1.0 Q Filter Q-factor (Q<0.5 filter underdamped, Q>0.5 filter overdamped)
*/
BiquadFilter.AllPass = function (sampleRate, f0, Q) {
	var	w0	= 2* Math.PI*f0/sampleRate,
		cosw0   = Math.cos(w0),
		sinw0   = Math.sin(w0),
		alpha   = sinw0/(2*Q),
		b0	=  1 - alpha,
		b1	= -2*cosw0,
		b2	=  1 + alpha,
		a0	=  b2,
		a1	=  b1,
		a2	=  b0;
	this.reset(sampleRate, b0/a0, b1/a0, b2/a0, a1/a0, a2/a0);
};

/**
 * Creates a Biquad Band-Pass Filter Effect
 * 
 * @name BandPass
 * @subeffect BiquadFilter BiquadBandPassFilter
 *
 * @arg =sampleRate
 * @arg =centerFreq
 * @arg =bandwidthInOctaves
 *
 * @param type:UInt units:Hz sampleRate Sample Rate the apparatus operates on.
 * @param type:Float units:Hz min:0.0 centerFreq Center frequency of filter: 0dB gain at center peak
 * @param type:Float units:octaves min:0 bandwidthInOctaves Bandwidth of the filter (between -3dB points).
*/
BiquadFilter.BandPass = function (sampleRate, centerFreq, bandwidthInOctaves) {
	var	w0	= 2* Math.PI*centerFreq/sampleRate,
		cosw0	= Math.cos(w0),
		sinw0	= Math.sin(w0),
		toSinh	= Math.log(2)/2 * bandwidthInOctaves * w0/sinw0,
		alpha	= sinw0*(Math.exp(toSinh) - Math.exp(-toSinh))/2,
		b0	= alpha,
		b1	= 0,
		b2	= -alpha,
		a0	= 1 + alpha,
		a1	= -2 * cosw0,
		a2	= 1 - alpha;
	this.reset(sampleRate, b0/a0, b1/a0, b2/a0, a1/a0, a2/a0);
};

(function (classes, i) {

for (i=0; i<classes.length; i++) {
	classes[i].prototype = new BiquadFilter.BiquadFilterClass();
}

}([BiquadFilter, BiquadFilter.LowPass, BiquadFilter.HighPass, BiquadFilter.AllPass, BiquadFilter.BandPass]));



//Hier beginnt mein Teil
var AudioContext = require('web-audio-api').AudioContext
var sine =  require('audiolib').Oscillator(44100, 440);
//var cosine =  require('audiolib').Oscillator(44100, 440);
var allpass = BiquadFilter.LowPass(44100, 1000, 0.6);

sine.waveShape = 'sine';

console.log(allpass);
var context = new AudioContext();


function play(ev){

var left = ev.outputBuffer.getChannelData(0);
var rigth = ev.outputBuffer.getChannelData(1);

var len = ev.outputBuffer.length;
for(var i=0;i<=len;i++){
sine.generate();
var s = sine.getMix();
//allpass.pushSample(s);



left[i]=s;
//rigth[i]=allpass.getMix();
}

}

var node = context.createScriptProcessor(16384,1,2);

node.onaudioprocess = play;

node.connect(context.destination);

kkapsner schrieb:
Du hast bei der Erzeugung von HILBER_ARRAY das "n" und "m" im C-Code irgendwie durcheinander gebracht.
Oh ja stimmt, aber selbst wenn ich das jetzt fixen würde, es würde ja doch nicht funktionieren.

kkapsner schrieb:
Ich hab' die Daten vom Oscillator im Verdacht. Werde das die Signalerzeugung und Amplitudenmodulieren mal auch per Hand machen.
Was ist genau von Hand?? Der Web Audio Osci macht auf jeden Fall starke Oberwellen. Ein 1kHz Signal macht auch noch ein Peak (oder Spike) bei 2kHz, 3khz usw. Ein 3Khz Osci macht Oberwellen bei 6kHz, 9khz usw.. Also immer um Faktor zwei gerechnet.

Puh das war jetzt viel.:dejection:
 
Dann habe ich meinen Pegelgenerator (in Hardware) aufgebaut angeschlossen und… Puff das Ding hat sich in Rauch aufgelöst und hat meine Soundkarte gleich mit in den Tot gerissen.
Das nenn' ich mal Pech... war denn eventuell noch Garantie drauf?

Was spricht den gegen einen IIR??
Nichts. Die Hilbert-Transformation ist ja eigentlich ein IIR. Man muss nur irgendwo abschneiden.

Hm... das ist etwas seltsam... lass' dir in der console mal BiquadFilter.prototype ausgeben.

Was ist genau von Hand?
So wie hald auch NCO_I und NCO_Q.

Der Web Audio Osci macht auf jeden Fall starke Oberwellen.
Wird einfach kein perfekter Sinus sein. Aber das sollte eigentlich kein Problem darstellen. Aber mal sehen...
 
kkapsner schrieb:
war denn eventuell noch Garantie drauf?
Nee leider nicht, Soundkarten habe ich ohne Ende da und beim Sender hat es die Endstufe zerdonnert... ist DDR Technik alles reparabel.
Habe mir beim Kumpel eine NF Sinus Generator geholt, der stammt zwar aus einer Zeit als es noch ISA Steckplätze gab aber mach ja nix.

Der NCO stand auf 1000Hz, das Ergebnis ist folgendes:
Ergebins Mischer.jpg
Beide Skript’s unterdrücken das rechte Seitenband.
Beide Verstärken das Eingangsignal bis zum Ultimo. Warum k.a..
Bewegt man den Träger weg bleibt das Mischergebnis an derselben Stelle stehen, warum habe ich noch nicht erroriren können. Bug in getUserMedia?!

Fakt ist aber das bei beiden Skripte das rechte Seitenband unterdrücken, es sei den man mischt mit 100Hz.

Zum audiolib Part:

BiquadFilter.prototype ergab:
Code:
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }
{ sampleRate: 44100,
  sample: 0,
  inputs: null,
  outputs: null,
  coefs: null,
  pushSample: [Function],
  getMix: [Function],
  reset: [Function] }

Ein console.log in der Selbstausführenden Funktion ergab.
Code:
{ [Function: BiquadFilter]
  BiquadFilterClass: [Function: BiquadFilterClass],
  LowPass: [Function],
  HighPass: [Function],
  AllPass: [Function],
  BandPass: [Function] }
[Function]
[Function]
[Function]
[Function]

Ich habe dann aus Spaß einfach mal das this.reset im entsprechendem Filter auskommentiert und das Console.log was vor der erstellung des new AudioContext(); steht spuckte undefined aus.


kkapsner schrieb:
So wie hald auch NCO_I und NCO_Q.
Asso, habe ich schon Probiert... bringt nix.

kkapsner schrieb:
Man muss nur irgendwo abschneiden.
Wie meinen? Der IIR läuft doch in etwa so ab wie der mein jetziges vorhaben.
Es wird mit einen Cosinus gemischt und das wird durch den Hilbert IIR geschickt nochmal 90 Grad gedreht und im Addierer zusammen gemanscht.
 
Dann ist's ja gut. Aber trotzdem ärgerlich.
Bewegt man den Träger weg bleibt das Mischergebnis an derselben Stelle stehen, warum habe ich noch nicht erroriren können. Bug in getUserMedia?!
Hm... sehr komisch. Hast du dir das Spektrum des Generators mal ohne Filter angesehen? Vielleicht kann der nicht wechseln, solange er läuft.
Fakt ist aber das bei beiden Skripte das rechte Seitenband unterdrücken
Das ist ja schon mal gut.
es sei den man mischt mit 100Hz.
Wie sieht es dann da aus?

BiquadFilter.prototype ergab:
Hm... da ist der reset eigentlich drin...

Asso, habe ich schon Probiert... bringt nix.
Gut - dann brauch' ich's ja nicht mehr probieren... mal sehen, ob mir noch was einfällt, woran es liegen könnte.

Wie meinen? Der IIR läuft doch in etwa so ab wie der mein jetziges vorhaben.
Für den Hilbert brauchst du ja eignetlich immer alle Datenpunkte von -Unendlich bis + Unendlich. Aber die Koeffizienten werden am Rand immer kleiner und deswegen kann man da wo Abschneiden.
Und ein IIR, der wirklich eine unendliche Impulsantwort produzieren kann, muss anders aussehen. Der braucht sowas wie ein Gedächtnis.
 
So, habe gestern den halben Tag damit verbracht den Fehler zu finden, warum der Scriptprocessor immer einfriert.

Es ist so das einem 64Bit System der SP immer einfriert, der in den Success callback von getUserMeida steckt.
Dabei bleibt aber nur der Outputbuffer stehen, der Inputbuffer holt weiterer Daten ab. Das passiert in jeden Browser in Windows 64bit.
Linux, Mac ungetestet.


Habe mich da rausgemogelt indem ich einen Buffer außerhalb von den Success callback erstellt habe und ein zweiten SP der auch außerhalb des Success callbacks steht.
Der eine SP schriebt die Daten in den Buffer der andere holt die Daten ab. Das funzt halbwegs stabil.

Es gibt dazu wohl schon einen Bug report.
https://bugs.webkit.org/show_bug.cgi?id=112521

Das Resultat, beide Skripts unterdrücken gleich gut, auch KKapsners das macht aber einen „Hof“ um das Singal.
Auflösung1.jpg
Noch ein Bild mit eiener anderen FFT Auflösung:
Auflösung2.png

Jedenfalls habe ich die beiden Skripts auf meinem Host hochgeladen… da kann jeder selber probieren.
Ach und ich habe noch schnell ein Formular eingebaut in dem man die NCO Frequenz ändern kann.
Des Wweiteren gleiche ich die Signallautstäre wider an((maxi / maxq) * 2) * (dbl_i + dbl_q);
Das ist wichtig da ja ein Signal ganz ließe ankommen kann und dann wird das durch den Algorithms noch mal gedämpft, das ist schei**.

Spectrum Laboratory ist beim betrachten ganz hilfreich.

Zu meiner Version
Zu Kkapsners Version


kkapsner schrieb:
Vielleicht kann der nicht wechseln, solange er läuft.
Ne da bist du flasch, der SiGen ist doch in Hardware, da.

kkapsner schrieb:
Wie sieht es dann da aus?
Der Pegel wird lauter auf der rechten Seite .

kkapsner schrieb:
wirklich eine unendliche Impulsantwort produzieren kann, muss anders aussehen.
Asso stimmt der IIR verschiebt ja nach dem Signal was rein geht, mit einem gemischten Signal kommt der nicht mehr klar.

kkapsner schrieb:
Hm... da ist der reset eigentlich drin...
Mir ist da doch was aufgefallen woher kommt den das apply in der ersten Funktion?
Code:
function BiquadFilter (sampleRate, b0, b1, b2, a1, a2) {
	this.reset.apply(this, arguments);
}
 
Zurück
Oben