1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-01 16:19:43 +02:00

Add buffer handling and flow control code to new Loudness effect.

Currently, the effect still does nothing.
This commit is contained in:
Max Maisel 2019-03-15 19:41:52 +01:00
parent c002165952
commit d8424755d1
2 changed files with 247 additions and 4 deletions

View File

@ -160,10 +160,76 @@ bool EffectLoudness::Startup()
return true;
}
// TODO: more method extraction
bool EffectLoudness::Process()
{
// TODO
return true;
if(mNormalizeTo == kLoudness)
// LU use 10*log10(...) instead of 20*log10(...)
// so multiply level by 2 and use standard DB_TO_LINEAR macro.
mRatio = DB_TO_LINEAR(TrapDouble(mLUFSLevel*2, MIN_LUFSLevel, MAX_LUFSLevel));
// Iterate over each track
this->CopyInputTracks(); // Set up mOutputTracks.
bool bGoodResult = true;
wxString topMsg = _("Normalizing Loudness...\n");
AllocBuffers();
mProgressVal = 0;
for(auto track : mOutputTracks->Selected<WaveTrack>()
+ (mStereoInd ? &Track::Any : &Track::IsLeader))
{
// Get start and end times from track
// PRL: No accounting for multiple channels ?
double trackStart = track->GetStartTime();
double trackEnd = track->GetEndTime();
// Set the current bounds to whichever left marker is
// greater and whichever right marker is less:
mCurT0 = mT0 < trackStart? trackStart: mT0;
mCurT1 = mT1 > trackEnd? trackEnd: mT1;
// Get the track rate
mCurRate = track->GetRate();
wxString msg;
auto trackName = track->GetName();
mSteps = 2;
mProgressMsg =
topMsg + wxString::Format(_("Analyzing: %s"), trackName);
auto range = mStereoInd
? TrackList::SingletonRange(track)
: TrackList::Channels(track);
mProcStereo = range.size() > 1;
InitTrackAnalysis();
if(!ProcessOne(range, true))
{
// Processing failed -> abort
bGoodResult = false;
break;
}
// Calculate normalization values the analysis results
float extent = 1.0;
// TODO: add it in separate method
mMult = mRatio / extent;
mProgressMsg = topMsg + wxString::Format(_("Processing: %s"), trackName);
if(!ProcessOne(range, false))
{
// Processing failed -> abort
bGoodResult = false;
break;
}
}
this->ReplaceProcessedTracks(bGoodResult);
FreeBuffers();
return bGoodResult;
}
void EffectLoudness::PopulateOrExchange(ShuttleGui & S)
@ -240,7 +306,171 @@ bool EffectLoudness::TransferDataFromWindow()
// EffectLoudness implementation
// TODO
/// Get required buffer size for the largest whole track and allocate buffers.
/// This reduces the amount of allocations required.
void EffectLoudness::AllocBuffers()
{
mTrackBufferCapacity = 0;
bool stereoTrackFound = false;
double maxSampleRate = 0;
mProcStereo = false;
for(auto track : mOutputTracks->Selected<WaveTrack>() + &Track::Any)
{
mTrackBufferCapacity = std::max(mTrackBufferCapacity, track->GetMaxBlockSize());
maxSampleRate = std::max(maxSampleRate, track->GetRate());
// There is a stereo track
if(track->IsLeader())
stereoTrackFound = true;
}
// TODO: hist and block buffers go here
// Initiate a processing buffer. This buffer will (most likely)
// be shorter than the length of the track being processed.
mTrackBuffer[0].reinit(mTrackBufferCapacity);
if(!mStereoInd && stereoTrackFound)
mTrackBuffer[1].reinit(mTrackBufferCapacity);
}
void EffectLoudness::FreeBuffers()
{
mTrackBuffer[0].reset();
mTrackBuffer[1].reset();
// TODO: additional destroy -> function
}
void EffectLoudness::InitTrackAnalysis()
{
mCount = 0;
// TODO: additional init goes here
}
/// ProcessOne() takes a track, transforms it to bunch of buffer-blocks,
/// and executes ProcessData, on it...
/// uses mMult to normalize a track.
/// mMult must be set before this is called
/// In analyse mode, it executes the selected analyse operation on it...
/// mMult does not have to be set before this is called
bool EffectLoudness::ProcessOne(TrackIterRange<WaveTrack> range, bool analyse)
{
WaveTrack* track = *range.begin();
// Transform the marker timepoints to samples
auto start = track->TimeToLongSamples(mCurT0);
auto end = track->TimeToLongSamples(mCurT1);
// Get the length of the buffer (as double). len is
// used simply to calculate a progress meter, so it is easier
// to make it a double now than it is to do it later
mTrackLen = (end - start).as_double();
// Abort if the right marker is not to the right of the left marker
if(mCurT1 <= mCurT0)
return false;
// Go through the track one buffer at a time. s counts which
// sample the current buffer starts at.
auto s = start;
while(s < end)
{
// Get a block of samples (smaller than the size of the buffer)
// Adjust the block size if it is the final block in the track
auto blockLen = limitSampleBufferSize(
track->GetBestBlockSize(s),
mTrackBufferCapacity);
const size_t remainingLen = (end - s).as_size_t();
blockLen = blockLen > remainingLen ? remainingLen : blockLen;
if(!LoadBufferBlock(range, s, blockLen))
return false;
// Process the buffer.
if(analyse)
{
if(!AnalyseBufferBlock())
return false;
}
else
{
if(!ProcessBufferBlock())
return false;
}
sleep(1);
if(!analyse)
StoreBufferBlock(range, s, blockLen);
// Increment s one blockfull of samples
s += blockLen;
}
// Return true because the effect processing succeeded ... unless cancelled
return true;
}
bool EffectLoudness::LoadBufferBlock(TrackIterRange<WaveTrack> range,
sampleCount pos, size_t len)
{
sampleCount read_size = -1;
// Get the samples from the track and put them in the buffer
int idx = 0;
for(auto channel : range)
{
channel->Get((samplePtr) mTrackBuffer[idx].get(), floatSample, pos, len,
fillZero, true, &read_size);
mTrackBufferLen = read_size.as_size_t();
// Fail if we read different sample count from stereo pair tracks.
// Ignore this check during first iteration (read_size == -1).
if(read_size.as_size_t() != mTrackBufferLen && read_size != -1)
return false;
++idx;
}
return true;
}
/// Calculates sample sum (for DC) and EBU R128 weighted square sum
/// (for loudness).
bool EffectLoudness::AnalyseBufferBlock()
{
// TODO: analysis loop goes here
mCount += mTrackBufferLen;
if(!UpdateProgress())
return false;
return true;
}
bool EffectLoudness::ProcessBufferBlock()
{
for(size_t i = 0; i < mTrackBufferLen; i++)
{
mTrackBuffer[0][i] = mTrackBuffer[0][i] * mMult;
if(mProcStereo)
mTrackBuffer[1][i] = mTrackBuffer[1][i] * mMult;
}
if(!UpdateProgress())
return false;
return true;
}
void EffectLoudness::StoreBufferBlock(TrackIterRange<WaveTrack> range,
sampleCount pos, size_t len)
{
int idx = 0;
for(auto channel : range)
{
// Copy the newly-changed samples back onto the track.
channel->Set((samplePtr) mTrackBuffer[idx].get(), floatSample, pos, len);
++idx;
}
}
bool EffectLoudness::UpdateProgress()
{

View File

@ -4,7 +4,7 @@
Loudness.h
Max Maisel
Max Maisel (based on Normalize effect)
**********************************************************************/
@ -59,6 +59,17 @@ public:
private:
// EffectLoudness implementation
void AllocBuffers();
void FreeBuffers();
void InitTrackAnalysis();
bool ProcessOne(TrackIterRange<WaveTrack> range, bool analyse);
bool LoadBufferBlock(TrackIterRange<WaveTrack> range,
sampleCount pos, size_t len);
bool AnalyseBufferBlock();
bool ProcessBufferBlock();
void StoreBufferBlock(TrackIterRange<WaveTrack> range,
sampleCount pos, size_t len);
bool UpdateProgress();
void OnUpdateUI(wxCommandEvent & evt);
void UpdateUI();
@ -79,6 +90,8 @@ private:
double mTrackLen;
double mCurRate;
float mMult;
float mRatio;
sampleCount mCount;
wxTextCtrl *mLevelTextCtrl;