Umstellung auf eine Score-Heuristik zur Entscheidung. Empirische Schwellen

Anstelle eines klaren, logischen Kriteriums müssen wir mit einem Score
und Trigger-Schwellen arbeiten, damit die hörbaren Events auch tatsächlich
sicher erkannt werden
This commit is contained in:
Fischlurch 2014-08-31 03:31:46 +02:00
parent 910e85a90a
commit be57e53479
2 changed files with 69 additions and 28 deletions

View file

@ -30,8 +30,9 @@ const size_t CONTEXT = 100;
const size_t BUFF_SIZE = CHUNK_SIZE + 2*CONTEXT;
const float EPSILON = 1.0f / (1 << 23); // one normalised sample step for 24bit samples
const float SILENCE_GATE = 1024.0f * EPSILON;// signal below this level (-39dB) is too low to detect jumps
const float TRIGGER_FACTOR = 6.0f; // discontinuity detected by local change compared to average change
const float SILENCE_GATE = 4096.0f * EPSILON;// signal below this level (-33dB) is too low to detect jumps
const float TRIGGER_FACTOR = 5.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
/* ==== Helpers ==== */
@ -66,15 +67,40 @@ reduceBufferPos (size_t & pos, size_t elementsToRemove)
/* ==== Abstraction: single channel to be processed ==== */
enum Event {
NO=0,
MAYBE,
YES
};
/**
* Helper to decide if a glitch event should be considered,
* while visiting multiple tracks at the same location.
*/
class EventScore
{
uint contributions_ = 0;
uint positives_ = 0;
public:
float
confirmationDegree()
{
return contributions_? float(positives_)/contributions_
: 0.0f;
}
operator bool()
{
return CONFIRMATION_DEGREE <= this->confirmationDegree();
}
void
recordConfirmation(bool confirmed)
{
++contributions_;
if (confirmed)
++positives_;
}
};
class Channel
{
string name_;
SNDFILE *in_ = nullptr,
*out_ = nullptr;
SF_INFO info_in_,
@ -87,13 +113,14 @@ class Channel
public:
string name_;
static bool hitEnd;
void
open (string name, string target)
{
this->name_ = name;
target += name_without_path(name);
this->name_ = name_without_path(name);
target += name_;
info_in_.format = 0;
in_ = sf_open (name.c_str(), SFM_READ, &info_in_);
@ -117,7 +144,7 @@ class Channel
if (in_) sf_close (in_);
if (out_)
if (0 != sf_close (out_))
cerr << "WARNING: problems while closing target '" << name_without_path(name_)
cerr << "WARNING: problems while closing target '" << name_
<< "' -- " << sf_strerror(out_) << "\n";
in_ = out_ = nullptr;
@ -169,8 +196,8 @@ class Channel
* the event state is confirmed; if a glitch can not be confirmed, the state is
* cleared. Otherwise, the undecided state is passed on.
*/
Event
visit (uint i, Event event)
void
visit (uint i, EventScore& event)
{
if (i + CONTEXT < numSamples_
&& CONTEXT <= i
@ -179,17 +206,19 @@ class Channel
workPos_ = i;
float averageDelta = updateMovingAverage();
float changeFactor = delta() / (averageDelta + EPSILON);
if (event != NO && SILENCE_GATE < abs(sample()))
event = (changeFactor > TRIGGER_FACTOR? YES : NO);
if (SILENCE_GATE < abs(sample()))
event.recordConfirmation(changeFactor > TRIGGER_FACTOR);
}
return event;
}
private:
float
sample()
{
return buff_[workPos_];
if (0 < workPos_)
return std::max(buff_[workPos_-1], buff_[workPos_]);
else
return buff_[workPos_];
}
float
@ -279,10 +308,16 @@ process()
channel.fillBuffer();
for (uint i=0; i < BUFF_SIZE; ++i)
{
Event event = MAYBE;
for (Channel& channel : streams)
event = channel.visit(i, event);
if (YES == event)
EventScore event;
bool yesss = false;
for (Channel& channel : streams){
channel.visit(i, event);
if (event || yesss){
cerr << "Event ? "<<i<<" "<<channel.name_<<"\t deg "<<event.confirmationDegree()<<"\n";
yesss = true;
}
}
if (event)
{
cout << "Skip-Event at "<< absStartPos+i <<"\n";
for (Channel& channel : streams)

View file

@ -61,7 +61,7 @@ Arbeitsnotizen
.Juli 2014
Nach Abschluß der Aufnahmen stelle ich in mindestens zwei der Sitzungen einen regelmäßigen Defekt fest.
Nach Analyse beschließe ich, eine Reparatur zu versuchen. Dies ist auch ein Anlaß, mich einmal mit der
*`libsndfile`* zu beschäftigen.
http://www.mega-nerd.com/libsndfile[libsndfile] zu beschäftigen.
.28.8.2014
Nach Rückkehr von meiner üblichen Ende-August-FrOSCon-Reise wird diese Aufgabe nun dringend. +
@ -72,10 +72,16 @@ _Leider_ ist die Aufgabe nicht so einfach wie vermutet. Habe mir nun die Wellenf
und drei Probe-Schnipsel zur Entwicklung und für Tests extrahiert. Es handelt sich nicht wie anfangs vermutet,
um punktuell auf allen Spuren auftretende Null-Werte. Sondern vielmehr um einen ganz gewöhnlichen *Verlust von
Samples*. Das heißt, es fehlt -- synchron auf allen Spuren -- ein Stück Tonaufzeichnung unbekannter Länge, so
daß an der betreffenden Stelle ein Sprung in allen Wellenformen auftritt. +
Damit ändert sich das Konzept. Ich muß wohl einen Sprung in den Deltas zwischen benachbarten Samples erkennen,
und dies, zur Sicherheit, auf mehreren Spuren parallel. Und die Korrektur ist wesentlich mühsamer, wenn nicht
sogar unmöglich. In Anlehnung an mein altes `streckDich`-Skript mache ich eine kurze Kreuzblende, mit der damals
als optimal 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?_
daß an der betreffenden Stelle ein Sprung in allen Wellenformen auftritt.
- Damit ändert sich das Konzept. Ich muß wohl einen Sprung in den Deltas zwischen benachbarten Samples erkennen,
und dies, zur Sicherheit, auf mehreren Spuren parallel. Und ein Sprung ist nicht so einfach zu erkennen, denn
er kann (mit guter Wahrscheinlichkeit) zufällig ehr gering ausfallen -- für eine zuverlässige Erkennung müßten
wir in der Frequenzdomäne arbeiten, was hier den gegebenen Rahmen sprengen würde. +
So sind wir also auf eine Heuristik mit empirisch abzustimmenden Trigger-Schwellen angewiesen.
- Auch ist die Korrektur wesentlich mühsamer, wenn nicht sogar unmöglich.
In Anlehnung an mein altes `streckDich`-Skript mache ich eine kurze Kreuzblende, mit der damals als optimal
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?_