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 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)
|
||||
|
|
|
|||
|
|
@ -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?_
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue