for I := 36 to 36*2 do
Du benutzt zur Auswertung nur eine Welle deiner Referenzfrequenz. Das ist das Minimalste was noch Sinn macht. Bei diesem Verfahren gelten die gleichen Regeln wie bei einer FFT oder dem Sampling, das Nyquist-Theorem. Das bedeutet das du mindestens 3 Wellen -> 36 to 36 + 36 * 3, auswerten solltest.Um so größer dieser abgetastete Bereich um so "glatter" deine errechneten Werte. Sie werden also weniger schwanken. Dazu solltest du aber in der Berchnung r =Sqrt(x^2 + y^2), x und y reduzieren um einen Durchschnitt zu ermitteln, also so r = Sqrt((x/10)^2 + (y/10)^2) wenn du zb. 36 * 10 Samples auswertest.
Je mehr Samples du berücksichtigst, also je länger dein Window wird, desto
1.) mehr Gain hast du, je mehr Verstärkung, je empfindlicher kannst du die Signale messen
2.) mehr glättest du die Ausgabewerte, dh. je stabiler wird deine
DC Gleichspannung oder in unserem Softwarefall schwanken diese Werte weniger
Leider bin ich auf diesem Gebiet auch nocht nicht so fit, eigentlich ist das eines meiner nächsten "Lernziele" falls ich mal Freizeit haben sollte. Dh. ich bin selber noch am rumbasteln und lernen
Denoch freut es mich das das alles so aus der Theorie heraus funktioniert hat.
Nun noch was zu deinem "
API basierten Überbau":
1.) vergiß Multimedia Timer und das damit realisierte Polling deinerseits
2.) falls Bass.dll keine andere Möglichkeit bietet als das was ich in deinem Source sehe, dann vergiß Bass.dll ganz schnell
3.) du benötigst eine asynchrone Aufnahme deines Signales
4.) dann 2-16 Buffer a 44000, 22000, 11000 oder 5500 Bytes
5.) du zeichnest nun asynchron in diese Buffer auf, dh. dem
API teilst du mit das es in 1 deiner Buffer aufzeichnen soll -> asynchron im Hintergrund
6.) das
API wird dich informieren wenn es diesen Buffer fertig hat
7.) in der Zwischenzeit führst du deine Berechnungen durch auf den Buffer der aktuell ausgewertet werden soll
8.) auf Grund der mehrfachen Buffer (Ringbuffer übrigens) und auf Grund dessen das deine Auswertung schneller als das Füllen eines Buffers geht kommst du zu einer kontinuierlichen Auswertung und Aufzwichnung im Hintergrund
Nun, auch hier musst du defakto nicht mit 16 einzelnen Buffern arbeiten, sondern nur mit 1 Buffer der dann in 16 Teile zerlegt wird. Deine Auswertung geht immer über diesen Buffer und das Aufzeichen nacheinander in diesen 16 Teilbereichen.
Die Buffergröße sollte am besten ein Mehrfaches/Teilbares aus allen auszuwertende Frequenzen sein. Beispiel:
44000Hz Samplerate
1800Hz 1.Freq
2100Hz 2.Freq
16 Teilbuffer für die Aufzeichung
44000/1800 = 24 Samples
44000/2100 = 20 Samples
20*24 = 480 Samples
480 * 10 ~Gain = 4800 Samples
4800 Samples * 16 = 76800 Samples
pro Sample 1 Byte = 76.8 KByte sollte dein Buffer groß sein.
Aufnehmen tust du in Stücken a 4.8 KByte reihum in den Buffer, das erfolgt asynchron im Hintergrund.
4800 Bytes / 44000 Saples/sec * 1000 = 109 Millisekunden benötigt unser Aufnahme um diese 4800 Bytes zu füllen. Damit es also nicht dazu kommt das deine Berechnungen zu einem Buffer-Überlauf führt muß diese Berechung also in maximal 100 Millisekunden fertig sein.
100ms zu schaffen sollte absolut KEIN Problem sein, ergo liegen deine jetzigen Probleme nur darin begründet das deine Aufnahmemethode schlecht ist->eventuell eben die Bass.dll
Nochwas zur Schleife:
wir benutzen ja zb.
for I := 36 to 36 * x do
Das ist natürlich nur ein Beispiel meinerseits gewesen um die Sache einfacher darzustellen. In real musst du natürlich mit
for I := 0 to 36 * x -1 do
rechnen. Das führt aber in der Schleife dazu das mit I - 36 -> 0 -36 -> -36 Index auf den Buffer zugegriffen wird und eine
AV erzeugen würde. Da unser Buffer aber ein Ringbuffer ist bedeutet dies das alle negatoiven Indizes auf Daten am Ende des Buffers zeigen.
Die Schleife sähe also so richtiger aus
Delphi-Quellcode:
BufferLength := Length(Buffer);
NextBufferIndex := xyz;
LastBufferIndex := xyz - 36 * 10;
if LastBufferIndex < 0 then Inc(LastBufferIndex, BufferLength);
for I := 0 to 36 * 10 -1 do
begin
aX := aX - Buffer[LastBufferIndex] * ...;
aX := aX + Buffer[NextBufferIndex] * ...;
Inc(LastBufferIndex);
if LastBufferIndex >= BufferLength then LastBufferIndex := BufferLength;
Inc(NextBufferIndex);
if NextBufferIndex >= BufferLength then NextBufferIndex := BufferLength;
end;
Wenn das dann alles funktioniert und du in 4 Schleifen über den Buffer gehst dann müssen wir nun noch diese Schleifen zueinander synchronisieren.
Wir haben ja einmal 44000/1800 = 24 und 44000/2100 = 20, und einen Bufferteilbereich von 20*24*10 = 4800 Bytes. Unsere Schleifen gehen also for I := 0 to 4800-1 do und damit werden für 1800Hz = 200 Wellen und für 2100Hz = 240 Wellen ausgewertet.
Wichtig ist dabei nur das alle Schleifen zueinander synchron laufen und alle Samples in unserem Buffer auch berücksichtigen. Als letzte Optimierung wirst du also alle 4 Schleifen in eine einzigste Schleife bauen und der Zugriff auf den Buffer erfolgt dann nur einmal pro Sample.
Das könte so aussehen
Delphi-Quellcode:
lbI := BufferIndex;
Inc(BufferIndex, BufferLength div 16); // div 16 weil unser Buffer aus 16'Teilen besteht
if BufferIndex >= BufferLength then BufferIndex := 0;
lbI := BufferIndex;
for I := 0 to BufferLength div 16 -1 do
begin
bL := Buffer[lbI]; Inc(lbI);
bN := Buffer[nbI]; Inc(nbI);
aX_1800 := aX_1800 - bL * rX_1800 + bN * rX_1800;
aX_2100 := aX_2100 - bL * rX_2100 + bN * rX_2100;
aY_1800 := aY_1800 - bL * rY_1800 + bN * rY_1800;
aY_2100 := aY_2100 - bL * rY_2100 + bN * rY_2100;
Inc(cX_1800);
if cX_1800 >= 24 then
begin
cX_1800 := 0;
rX_1800 := -rX_1800;
end;
Inc(cX_2100);
if cX_2100 >= 20 then
begin
cX_2100 := 0;
rX_2100 := -rX_2100;
end;
Inc(cY_1800);
if cY_1800 >= 24 then
begin
cY_1800 := 0;
rY_1800 := -rY_1800;
end;
Inc(cY_2100);
if cY_2100 >= 20 then
begin
cY_2100 := 0;
rY_2100 := -rY_2100;
end;
r_1800 := Sqrt(Sqr(aX_1800 / 200) + Sqr(aY_1800 / 200));
r_2100 := Sqrt(Sqr(aX_2100 / 240) + Sqr(aY_2100 / 240));
end;
Fast alle obigen Variablen (ausser I,bL,bN) sind global und werden nur beim Start deiner Aufnahme initialisiert. Danach rotieren sie kontinuierlich.
Wir haben hier schon einiges an Vereinfachungen reingebaut. ZB. eben das der Buffer ein Mehrfaches in der Länge ist von unseren Referenzfrequenzen. Das muß man nicht so machen und wird bei der Auswertung zb. von 16 Frequenzen immer komplizierter. Ich werde mal ein Beispiel bauen das ohne obige "Vereinfachungen" arbeitet, mit beliebigen Frequenzen.
BufferIndex springt bei jeder Berechung also um 4800 Samples weiter, und 4800 * 16 Samples ist der Buffer komplett groß. Nachdem nun unser
API Aufnahmegerät ein Buffer a 4800 Samples fertig hat rufen wir unsere Schleife so lange auf bis BufferIndex auf den Anfang des Teilbuffers zeigt der gerade jetzt asynchron durchs
API gefüllt wird. Wenn also 4800 Samples bei 44Khz ca. 100 Millisekunden benötigen und wir 16 Teilbuffer haben und davon immer 1 Buffer gefüllt wird dann haben wir 15 * 100 Millisekunden Zeit bevor wir unsere Auswertungen beginnen, also eine Reserve von 1.5 Sekunden ohne das wir Verzögerungen produzieren.
Wenn die
API Aufnahmefunktion meldet das sie fertig ist erhöhen wir einen RecordBufferIndex um +4800 Bytes und lassen den nächsten der 16 Teilbuffer aufnehmen. Danach rufen wir unsere Auswertungsschleife wiederholt auf bis BufferIndex == RecordBufferIndex ist.
Gruß Hagen