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:
parent
910e85a90a
commit
be57e53479
2 changed files with 69 additions and 28 deletions
|
|
@ -30,8 +30,9 @@ const size_t CONTEXT = 100;
|
||||||
const size_t BUFF_SIZE = CHUNK_SIZE + 2*CONTEXT;
|
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 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 SILENCE_GATE = 4096.0f * EPSILON;// signal below this level (-33dB) is too low to detect jumps
|
||||||
const float TRIGGER_FACTOR = 6.0f; // discontinuity detected by local change compared to average change
|
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
|
const uint AVERAGE_SAMPLES = 50; // number of past samples to use for average delta
|
||||||
|
|
||||||
/* ==== Helpers ==== */
|
/* ==== Helpers ==== */
|
||||||
|
|
@ -66,15 +67,40 @@ reduceBufferPos (size_t & pos, size_t elementsToRemove)
|
||||||
|
|
||||||
/* ==== Abstraction: single channel to be processed ==== */
|
/* ==== Abstraction: single channel to be processed ==== */
|
||||||
|
|
||||||
enum Event {
|
/**
|
||||||
NO=0,
|
* Helper to decide if a glitch event should be considered,
|
||||||
MAYBE,
|
* while visiting multiple tracks at the same location.
|
||||||
YES
|
*/
|
||||||
};
|
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
|
class Channel
|
||||||
{
|
{
|
||||||
string name_;
|
|
||||||
SNDFILE *in_ = nullptr,
|
SNDFILE *in_ = nullptr,
|
||||||
*out_ = nullptr;
|
*out_ = nullptr;
|
||||||
SF_INFO info_in_,
|
SF_INFO info_in_,
|
||||||
|
|
@ -87,13 +113,14 @@ class Channel
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
string name_;
|
||||||
static bool hitEnd;
|
static bool hitEnd;
|
||||||
|
|
||||||
void
|
void
|
||||||
open (string name, string target)
|
open (string name, string target)
|
||||||
{
|
{
|
||||||
this->name_ = name;
|
this->name_ = name_without_path(name);
|
||||||
target += name_without_path(name);
|
target += name_;
|
||||||
|
|
||||||
info_in_.format = 0;
|
info_in_.format = 0;
|
||||||
in_ = sf_open (name.c_str(), SFM_READ, &info_in_);
|
in_ = sf_open (name.c_str(), SFM_READ, &info_in_);
|
||||||
|
|
@ -117,7 +144,7 @@ class Channel
|
||||||
if (in_) sf_close (in_);
|
if (in_) sf_close (in_);
|
||||||
if (out_)
|
if (out_)
|
||||||
if (0 != sf_close (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";
|
<< "' -- " << sf_strerror(out_) << "\n";
|
||||||
|
|
||||||
in_ = out_ = nullptr;
|
in_ = out_ = nullptr;
|
||||||
|
|
@ -169,8 +196,8 @@ class Channel
|
||||||
* the event state is confirmed; if a glitch can not be confirmed, the state is
|
* the event state is confirmed; if a glitch can not be confirmed, the state is
|
||||||
* cleared. Otherwise, the undecided state is passed on.
|
* cleared. Otherwise, the undecided state is passed on.
|
||||||
*/
|
*/
|
||||||
Event
|
void
|
||||||
visit (uint i, Event event)
|
visit (uint i, EventScore& event)
|
||||||
{
|
{
|
||||||
if (i + CONTEXT < numSamples_
|
if (i + CONTEXT < numSamples_
|
||||||
&& CONTEXT <= i
|
&& CONTEXT <= i
|
||||||
|
|
@ -179,17 +206,19 @@ class Channel
|
||||||
workPos_ = i;
|
workPos_ = i;
|
||||||
float averageDelta = updateMovingAverage();
|
float averageDelta = updateMovingAverage();
|
||||||
float changeFactor = delta() / (averageDelta + EPSILON);
|
float changeFactor = delta() / (averageDelta + EPSILON);
|
||||||
if (event != NO && SILENCE_GATE < abs(sample()))
|
if (SILENCE_GATE < abs(sample()))
|
||||||
event = (changeFactor > TRIGGER_FACTOR? YES : NO);
|
event.recordConfirmation(changeFactor > TRIGGER_FACTOR);
|
||||||
}
|
}
|
||||||
return event;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float
|
float
|
||||||
sample()
|
sample()
|
||||||
{
|
{
|
||||||
return buff_[workPos_];
|
if (0 < workPos_)
|
||||||
|
return std::max(buff_[workPos_-1], buff_[workPos_]);
|
||||||
|
else
|
||||||
|
return buff_[workPos_];
|
||||||
}
|
}
|
||||||
|
|
||||||
float
|
float
|
||||||
|
|
@ -279,10 +308,16 @@ process()
|
||||||
channel.fillBuffer();
|
channel.fillBuffer();
|
||||||
for (uint i=0; i < BUFF_SIZE; ++i)
|
for (uint i=0; i < BUFF_SIZE; ++i)
|
||||||
{
|
{
|
||||||
Event event = MAYBE;
|
EventScore event;
|
||||||
for (Channel& channel : streams)
|
bool yesss = false;
|
||||||
event = channel.visit(i, event);
|
for (Channel& channel : streams){
|
||||||
if (YES == event)
|
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";
|
cout << "Skip-Event at "<< absStartPos+i <<"\n";
|
||||||
for (Channel& channel : streams)
|
for (Channel& channel : streams)
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ Arbeitsnotizen
|
||||||
.Juli 2014
|
.Juli 2014
|
||||||
Nach Abschluß der Aufnahmen stelle ich in mindestens zwei der Sitzungen einen regelmäßigen Defekt fest.
|
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
|
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
|
.28.8.2014
|
||||||
Nach Rückkehr von meiner üblichen Ende-August-FrOSCon-Reise wird diese Aufgabe nun dringend. +
|
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,
|
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
|
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
|
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. +
|
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
|
- Damit ändert sich das Konzept. Ich muß wohl einen Sprung in den Deltas zwischen benachbarten Samples erkennen,
|
||||||
sogar unmöglich. In Anlehnung an mein altes `streckDich`-Skript mache ich eine kurze Kreuzblende, mit der damals
|
und dies, zur Sicherheit, auf mehreren Spuren parallel. Und ein Sprung ist nicht so einfach zu erkennen, denn
|
||||||
als optimal ausgeknobelten Länge von 4.5ms (200 Samples). Das große Problem ist bloß: _woher bekomme ich das
|
er kann (mit guter Wahrscheinlichkeit) zufällig ehr gering ausfallen -- für eine zuverlässige Erkennung müßten
|
||||||
zusätzliche Material für die Kreuzblende?_
|
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?_
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue