Entscheidungslogik noch mal verfeinert und Umgebung mit einbezogen
This commit is contained in:
parent
139225fc5c
commit
109adce56c
2 changed files with 98 additions and 12 deletions
|
|
@ -36,6 +36,7 @@ const float SILENCE_GATE = 4096.0f * EPSILON;// signal below this level (-33dB)
|
|||
const float TRIGGER_FACTOR = 6.0f; // discontinuity detected by local change compared to average change
|
||||
const float CONFIRMATION_DEGREE = 0.66f; // degree of confirmation by multiple tracks for an event to be accepted
|
||||
const uint AVERAGE_SAMPLES = 50; // number of past samples to use for average delta
|
||||
const uint POS_LEEWAY = 3; // events within this timespan (samples) still count as "synchronous"
|
||||
|
||||
const bool VERBOSE = true;
|
||||
|
||||
|
|
@ -79,6 +80,9 @@ class EventScore
|
|||
{
|
||||
float weights_ = 0.0f;
|
||||
float score_ = 0.0f;
|
||||
uint confs_ = 0;
|
||||
bool isHere_ = false;
|
||||
static uint minConfs;
|
||||
|
||||
uint last_ = 0;
|
||||
float fac_ = 0.0f;
|
||||
|
|
@ -94,22 +98,32 @@ class EventScore
|
|||
|
||||
operator bool()
|
||||
{
|
||||
return CONFIRMATION_DEGREE <= this->confirmationDegree();
|
||||
return CONFIRMATION_DEGREE <= this->confirmationDegree()
|
||||
&& confs_ >= minConfs
|
||||
&& isHere_;
|
||||
}
|
||||
|
||||
void
|
||||
decide(bool unclear, bool confirmed, float factor)
|
||||
record (bool unclear, bool confirmed, bool noOffset, float factor)
|
||||
{
|
||||
fac_ = factor;
|
||||
last_ = unclear? 0 : confirmed? 2 : 1; // record for diagnostics
|
||||
if (unclear) return;
|
||||
if (confirmed) ++confs_;
|
||||
if (noOffset) isHere_ = true;
|
||||
|
||||
float weight = confirmed? (factor/TRIGGER_FACTOR)
|
||||
: (0.0<factor? TRIGGER_FACTOR/factor : -10);
|
||||
: (0.0<factor? TRIGGER_FACTOR/factor : 10);
|
||||
weights_ += weight;
|
||||
score_ += weight * (confirmed? 1.0 : -1.0);
|
||||
}
|
||||
|
||||
void
|
||||
recordNeutral()
|
||||
{
|
||||
record (true, false, true, 0.0);
|
||||
}
|
||||
|
||||
bool
|
||||
possible()
|
||||
{
|
||||
|
|
@ -124,6 +138,17 @@ class EventScore
|
|||
: "rej \t"
|
||||
) + to_string(fac_) +'\t'+ to_string(confirmationDegree());
|
||||
}
|
||||
|
||||
/**
|
||||
* to avoid triggering by noise peaks, we require a confirmation
|
||||
* by multiple channels; but the minimum fraction of confirming
|
||||
* channels is lowered when processing a large number of channels.
|
||||
*/
|
||||
static void
|
||||
adjustRequiredMinimalConfirmations (uint channelCount)
|
||||
{
|
||||
minConfs = 1 + uint(std::log2(channelCount));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -229,16 +254,17 @@ class Channel
|
|||
visit (uint i, EventScore& event)
|
||||
{
|
||||
if (i + CONTEXT < numSamples_
|
||||
&& CONTEXT <= i
|
||||
&& workPos_ <= i)
|
||||
&& CONTEXT <= i)
|
||||
{
|
||||
workPos_ = i;
|
||||
bool noOffset = fineTuneWorkPos (i);
|
||||
float averageDelta = updateMovingAverage();
|
||||
float changeFactor = delta() / (averageDelta + EPSILON);
|
||||
float changeFactor = delta(workPos_) / (averageDelta + EPSILON);
|
||||
bool unclear = (sampleLevel() < SILENCE_GATE );
|
||||
bool trigger = (changeFactor > TRIGGER_FACTOR);
|
||||
event.decide (unclear, trigger, changeFactor);
|
||||
event.record (unclear, trigger, noOffset, changeFactor);
|
||||
}
|
||||
else
|
||||
event.recordNeutral();
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -253,10 +279,10 @@ class Channel
|
|||
}
|
||||
|
||||
float
|
||||
delta()
|
||||
delta(size_t pos)
|
||||
{
|
||||
if (0 < workPos_)
|
||||
return abs(buff_[workPos_] - buff_[workPos_-1]);
|
||||
if (0 < pos)
|
||||
return abs(buff_[pos] - buff_[pos-1]);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -266,10 +292,34 @@ class Channel
|
|||
{
|
||||
float old_ema = ema_; // avoid poisoning EMA used for detection
|
||||
|
||||
ema_ = (delta() + ema_*(AVERAGE_SAMPLES - 1)) / AVERAGE_SAMPLES;
|
||||
ema_ = (delta(workPos_) + ema_*(AVERAGE_SAMPLES - 1)) / AVERAGE_SAMPLES;
|
||||
return old_ema;
|
||||
}
|
||||
|
||||
/**
|
||||
* sometimes the actual jump appears slightly offset on neighbouring tracks.
|
||||
* We allow for some leeway by moving the work position to maximise delta.
|
||||
* @return if the triggering event is at the current position
|
||||
*/
|
||||
bool
|
||||
fineTuneWorkPos (size_t currPos)
|
||||
{
|
||||
size_t maxPos = 0;
|
||||
float largestDelta = 0.0f;
|
||||
for (size_t j=currPos; j < currPos+POS_LEEWAY && j < numSamples_; ++j)
|
||||
{
|
||||
float thisDelta = delta(j);
|
||||
if (thisDelta > largestDelta)
|
||||
{
|
||||
largestDelta = thisDelta;
|
||||
maxPos = j;
|
||||
}
|
||||
}
|
||||
|
||||
workPos_ = maxPos;
|
||||
return (workPos_ == currPos);
|
||||
}
|
||||
|
||||
float*
|
||||
accessCurrentContext()
|
||||
{
|
||||
|
|
@ -343,6 +393,7 @@ class Patch
|
|||
int num_sources = 0;
|
||||
vector<Channel> streams(0);
|
||||
bool Channel::hitEnd = false;
|
||||
uint EventScore::minConfs = 1;
|
||||
|
||||
|
||||
void
|
||||
|
|
@ -351,6 +402,7 @@ openStreams (char* name[], string targetDir)
|
|||
assert (num_sources);
|
||||
assert (0 == streams.size());
|
||||
streams.resize(num_sources);
|
||||
EventScore::adjustRequiredMinimalConfirmations (num_sources);
|
||||
|
||||
assert (targetDir.length());
|
||||
if (targetDir.back() != '/')
|
||||
|
|
|
|||
|
|
@ -55,6 +55,19 @@ Dies erfordert, von einer gegebenen Position auch stets auf das _Vorgänger-_ un
|
|||
zu können, was besondere Vorsicht an den Puffergrenzen bedingt. Dies werde ich durch entsprechende Abstraktion
|
||||
und Trennung des Lese- vom Verarbeitungsvorgang lösen.
|
||||
|
||||
Im Detail wird die Entscheidungs-Logik einigermaßen komplex, da wir gezwungenermaßen auch einige gemischte
|
||||
und nicht regelmäßige Fälle mit abdecken müssen. Dadurch gewinnt dieses Werkzeug aber auch eine größere
|
||||
Einsatzbreite.
|
||||
|
||||
- die Entscheidung, ob tatsächlich ein zu behandelndes Ereignis vorliegt, wird durch eine
|
||||
gewichtete Abstimmung aller beteiligten Spuren getroffen. Es zählt die Stärke des Sprunges.
|
||||
- die genaue Stelle des Sprunges wird mit einer geringen Toleranz ggfs noch um wenige Samples
|
||||
vor- oder zurück-justiert, so daß auch _nur fast synchrone_ Defekte mit erfaßt werden.
|
||||
- wenn sich vor oder hinter dem Sprung ein drop-Out befindet, wird dieser _vor_ Aufbringen
|
||||
der eigentlichen Korrektur durch eine einfache, lineare Verbindung ersetzt, so daß die
|
||||
durch den drop-Out bedingten, zusätzlichen Sprung-Stellen schon beseitigt sind.
|
||||
- eine Spur, welche _definitiv keinen_ Defekt aufweist, wird grundsätzlich nicht repariert.
|
||||
|
||||
|
||||
Arbeitsnotizen
|
||||
--------------
|
||||
|
|
@ -84,4 +97,25 @@ daß an der betreffenden Stelle ein Sprung in allen Wellenformen auftritt.
|
|||
ausgeknobelten Länge von 4.5ms (200 Samples).
|
||||
- das große Problem ist bloß: _woher bekomme ich das zusätzliche Material für die Kreuzblende?_
|
||||
|
||||
.31.8.2014
|
||||
Nun habe ich einen ganzen Arbeitstag gebraucht, diese komplexere Entscheidungs-Logik zu implementieren,
|
||||
zu testen und zu optimieren. Ich verwende nun ein gewichtetes Mittel, um die Stärke des erkannten Sprunges
|
||||
in die Entscheidung einfließen zu lassen; häufig liegt nämlich auch auf den anderen Spuren ein Sprung knapp
|
||||
unter der Trigger-Schwelle vor; das negativ-Votum solcher Spuren fällt nun geringer aus, als das Votum einer
|
||||
Spur, die nun wirklich kontinuierlich ist. Damit komme ich an den ausgewählten Probe-Samples zu klaren
|
||||
Entscheidungen. +
|
||||
Als Kompensation habe ich zunächst das fehlende Signal ganz einfach durch _Rückwärts-Abspielen_ der Wellenform
|
||||
gewonnen. Das funktioniert im _Idealfall_ verblüffend gut -- man hört nur noch ein klopfendes Geräusch.
|
||||
Die Kompensation funktioniert allerdings oft nicht befriedigend, denn es treten zusätzliche Komplikationen auf:
|
||||
|
||||
- bei manchen Sprung-Ereignissen, liegt die Diskontinuität um ein bis zwei Samples verschoben auf den
|
||||
Spuren vor. Die Sprünge sind also nur _fast_ synchron.
|
||||
- direkt anschließend an den Sprung tritt auch öfters tatsächlich eine kurze Strecke von Null-Werten auf.
|
||||
Dies hatte mich wohl ursprünglich zur falschen Diagnose geführt, es handele sich um drop-Outs.
|
||||
|
||||
Die Wirklichkeit ist eben _widerständig_ -- einfache Methoden funktionieren nur in einer konditionierten
|
||||
Umgebung. Hier führen also die Verschiebungen dazu, daß Ereignisse nicht, oder doppelt erkannt werden.
|
||||
Und die im Idealfall sehr gut funktionierende Reparatur mit dem gespiegelten Signal führt bei den Null-Strecken
|
||||
dazu, daß ein weiterer Sprung (am anderen Ende des drop-Out) _rückwärts_ in das ursprünglich saubere Signal
|
||||
verschleppt wird. Das heitßt, in diesem Fall verschlimmert die ``Korrektur'' den Schaden.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue