mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-23 15:50:05 +02:00
Fix for bugs in keyboard clip commands due to rounding errors
When When two clips are immediately next to each other, the GetEndTime() of the first clip and the GetStartTime() of the second clip may not be exactly equal due to rounding errors. The existing code assumed they were equal, and this lead to the wrong clip boundaries or clips being found. There are a number of ways of fixing this which could be explored. The current solution involves changing only the code for the keyboard interaction with clips. The fix: 1. The test used for two clips being immediately next to each other is that GetEndSample() on the first clip is equal to to GetStartSample() on the second clip. 2. When searching for the start/end times of clips, the cases where GetEndTime() and GetStartTime() are not equal are taken into account. This is done in the two functions AudacityProject::AdjustForFindingStartTimes and AudacityProject::AdjustForFindingEndTimes.
This commit is contained in:
parent
85e984de63
commit
b2d8f36969
114
src/Menus.cpp
114
src/Menus.cpp
@ -5524,6 +5524,8 @@ AudacityProject::FoundClip AudacityProject::FindNextClip(const WaveTrack* wt, do
|
||||
result.waveTrack = wt;
|
||||
const auto clips = wt->SortedClipArray();
|
||||
|
||||
t0 = AdjustForFindingStartTimes(clips, t0);
|
||||
|
||||
auto p = std::find_if(clips.begin(), clips.end(), [&] (const WaveClip* const& clip) {
|
||||
return clip->GetStartTime() == t0; });
|
||||
if (p != clips.end() && (*p)->GetEndTime() > t1) {
|
||||
@ -5552,6 +5554,8 @@ AudacityProject::FoundClip AudacityProject::FindPrevClip(const WaveTrack* wt, do
|
||||
result.waveTrack = wt;
|
||||
const auto clips = wt->SortedClipArray();
|
||||
|
||||
t0 = AdjustForFindingStartTimes(clips, t0);
|
||||
|
||||
auto p = std::find_if(clips.begin(), clips.end(), [&] (const WaveClip* const& clip) {
|
||||
return clip->GetStartTime() == t0; });
|
||||
if (p != clips.end() && (*p)->GetEndTime() < t1) {
|
||||
@ -6527,27 +6531,19 @@ AudacityProject::FoundClipBoundary AudacityProject::FindNextClipBoundary(const W
|
||||
{
|
||||
AudacityProject::FoundClipBoundary result{};
|
||||
result.waveTrack = wt;
|
||||
|
||||
const auto clips = wt->SortedClipArray();
|
||||
double timeStart = AdjustForFindingStartTimes(clips, time);
|
||||
double timeEnd = AdjustForFindingEndTimes(clips, time);
|
||||
|
||||
auto pStart = std::find_if(clips.begin(), clips.end(), [&] (const WaveClip* const& clip) {
|
||||
return clip->GetStartTime() > time; });
|
||||
return clip->GetStartTime() > timeStart; });
|
||||
auto pEnd = std::find_if(clips.begin(), clips.end(), [&] (const WaveClip* const& clip) {
|
||||
return clip->GetEndTime() > time; });
|
||||
return clip->GetEndTime() > timeEnd; });
|
||||
|
||||
if (pStart != clips.end() && pEnd != clips.end()) {
|
||||
if ((*pStart)->GetStartTime() < (*pEnd)->GetEndTime()) {
|
||||
result.nFound = 1;
|
||||
result.time = (*pStart)->GetStartTime();
|
||||
result.index1 = std::distance(clips.begin(), pStart);
|
||||
result.clipStart1 = true;
|
||||
}
|
||||
else if ((*pStart)->GetStartTime() > (*pEnd)->GetEndTime()) {
|
||||
result.nFound = 1;
|
||||
result.time = (*pEnd)->GetEndTime();
|
||||
result.index1 = std::distance(clips.begin(), pEnd);
|
||||
result.clipStart1 = false;
|
||||
}
|
||||
else { // both the end of one clip and the start of the next clip
|
||||
if ((*pStart)->GetStartSample() == (*pEnd)->GetEndSample()) {
|
||||
// boundary between two clips which are immediately next to each other. Tested for
|
||||
// using samples rather than times because of rounding errors
|
||||
result.nFound = 2;
|
||||
result.time = (*pEnd)->GetEndTime();
|
||||
result.index1 = std::distance(clips.begin(), pEnd);
|
||||
@ -6555,6 +6551,18 @@ AudacityProject::FoundClipBoundary AudacityProject::FindNextClipBoundary(const W
|
||||
result.index2 = std::distance(clips.begin(), pStart);
|
||||
result.clipStart2 = true;
|
||||
}
|
||||
else if ((*pStart)->GetStartTime() < (*pEnd)->GetEndTime()) {
|
||||
result.nFound = 1;
|
||||
result.time = (*pStart)->GetStartTime();
|
||||
result.index1 = std::distance(clips.begin(), pStart);
|
||||
result.clipStart1 = true;
|
||||
}
|
||||
else {
|
||||
result.nFound = 1;
|
||||
result.time = (*pEnd)->GetEndTime();
|
||||
result.index1 = std::distance(clips.begin(), pEnd);
|
||||
result.clipStart1 = false;
|
||||
}
|
||||
}
|
||||
else if (pEnd != clips.end()) {
|
||||
result.nFound = 1;
|
||||
@ -6570,34 +6578,38 @@ AudacityProject::FoundClipBoundary AudacityProject::FindPrevClipBoundary(const W
|
||||
{
|
||||
AudacityProject::FoundClipBoundary result{};
|
||||
result.waveTrack = wt;
|
||||
|
||||
const auto clips = wt->SortedClipArray();
|
||||
double timeStart = AdjustForFindingStartTimes(clips, time);
|
||||
double timeEnd = AdjustForFindingEndTimes(clips, time);
|
||||
|
||||
auto pStart = std::find_if(clips.rbegin(), clips.rend(), [&] (const WaveClip* const& clip) {
|
||||
return clip->GetStartTime() < time; });
|
||||
return clip->GetStartTime() < timeStart; });
|
||||
auto pEnd = std::find_if(clips.rbegin(), clips.rend(), [&] (const WaveClip* const& clip) {
|
||||
return clip->GetEndTime() < time; });
|
||||
return clip->GetEndTime() < timeEnd; });
|
||||
|
||||
if (pStart != clips.rend() && pEnd != clips.rend()) {
|
||||
if ((*pStart)->GetStartTime() > (*pEnd)->GetEndTime()) {
|
||||
result.nFound = 1;
|
||||
result.time = (*pStart)->GetStartTime();
|
||||
result.index1 = static_cast<int>(clips.size()) - 1 - std::distance(clips.rbegin(), pStart);
|
||||
result.clipStart1 = true;
|
||||
}
|
||||
else if ((*pStart)->GetStartTime() < (*pEnd)->GetEndTime()) {
|
||||
result.nFound = 1;
|
||||
result.time = (*pEnd)->GetEndTime();
|
||||
result.index1 = static_cast<int>(clips.size()) - 1 - std::distance(clips.rbegin(), pEnd);
|
||||
result.clipStart1 = false;
|
||||
}
|
||||
else {
|
||||
result.nFound = 2; // both the start of one clip and the end of the previous clip
|
||||
if ((*pStart)->GetStartSample() == (*pEnd)->GetEndSample()) {
|
||||
// boundary between two clips which are immediately next to each other. Tested for
|
||||
// using samples rather than times because of rounding errors
|
||||
result.nFound = 2;
|
||||
result.time = (*pStart)->GetStartTime();
|
||||
result.index1 = static_cast<int>(clips.size()) - 1 - std::distance(clips.rbegin(), pStart);
|
||||
result.clipStart1 = true;
|
||||
result.index2 = static_cast<int>(clips.size()) - 1 - std::distance(clips.rbegin(), pEnd);
|
||||
result.clipStart2 = false;
|
||||
}
|
||||
else if ((*pStart)->GetStartTime() > (*pEnd)->GetEndTime()) {
|
||||
result.nFound = 1;
|
||||
result.time = (*pStart)->GetStartTime();
|
||||
result.index1 = static_cast<int>(clips.size()) - 1 - std::distance(clips.rbegin(), pStart);
|
||||
result.clipStart1 = true;
|
||||
}
|
||||
else {
|
||||
result.nFound = 1;
|
||||
result.time = (*pEnd)->GetEndTime();
|
||||
result.index1 = static_cast<int>(clips.size()) - 1 - std::distance(clips.rbegin(), pEnd);
|
||||
result.clipStart1 = false;
|
||||
}
|
||||
}
|
||||
else if (pStart != clips.rend()) {
|
||||
result.nFound = 1;
|
||||
@ -6609,6 +6621,42 @@ AudacityProject::FoundClipBoundary AudacityProject::FindPrevClipBoundary(const W
|
||||
return result;
|
||||
}
|
||||
|
||||
// When two clips are immediately next to each other, the GetEndTime() of the first clip and the
|
||||
// GetStartTime() of the second clip may not be exactly equal due to rounding errors. When searching
|
||||
// for the next/prev start time from a given time, the following function adjusts that given time if
|
||||
// necessary to take this into account. If the given time is the end time of the first of two clips which
|
||||
// are next to each other, then the given time is changed to the start time of the second clip.
|
||||
// This ensures that the correct next/prev start time is found.
|
||||
double AudacityProject::AdjustForFindingStartTimes(const std::vector<const WaveClip*> & clips, double time)
|
||||
{
|
||||
auto q = std::find_if(clips.begin(), clips.end(), [&] (const WaveClip* const& clip) {
|
||||
return clip->GetEndTime() == time; });
|
||||
if (q <= clips.end() - 2 &&
|
||||
(*q)->GetEndSample() == (*(q+1))->GetStartSample()) {
|
||||
time = (*(q+1))->GetStartTime();
|
||||
}
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
// When two clips are immediately next to each other, the GetEndTime() of the first clip and the
|
||||
// GetStartTime() of the second clip may not be exactly equal due to rounding errors. When searching
|
||||
// for the next/prev end time from a given time, the following function adjusts that given time if
|
||||
// necessary to take this into account. If the given time is the start time of the second of two clips which
|
||||
// are next to each other, then the given time is changed to the end time of the first clip.
|
||||
// This ensures that the correct next/prev end time is found.
|
||||
double AudacityProject::AdjustForFindingEndTimes(const std::vector<const WaveClip*>& clips, double time)
|
||||
{
|
||||
auto q = std::find_if(clips.begin(), clips.end(), [&] (const WaveClip* const& clip) {
|
||||
return clip->GetStartTime() == time; });
|
||||
if (q != clips.end() && q != clips.begin() &&
|
||||
(*(q - 1))->GetEndSample() == (*q)->GetStartSample()) {
|
||||
time = (*(q-1))->GetEndTime();
|
||||
}
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
int AudacityProject::FindClipBoundaries(double time, bool next, std::vector<FoundClipBoundary>& finalResults)
|
||||
{
|
||||
const TrackList* tracks = GetTracks();
|
||||
|
@ -411,6 +411,8 @@ typedef struct FoundClipBoundary {
|
||||
} FoundClipBoundary;
|
||||
FoundClipBoundary FindNextClipBoundary(const WaveTrack* wt, double time);
|
||||
FoundClipBoundary FindPrevClipBoundary(const WaveTrack* wt, double time);
|
||||
double AdjustForFindingStartTimes(const std::vector<const WaveClip*>& clips, double time);
|
||||
double AdjustForFindingEndTimes(const std::vector<const WaveClip*>& clips, double time);
|
||||
int FindClipBoundaries(double time, bool next, std::vector<FoundClipBoundary>& results);
|
||||
void OnCursorNextClipBoundary();
|
||||
void OnCursorPrevClipBoundary();
|
||||
|
@ -100,6 +100,7 @@ class UndoManager;
|
||||
enum class UndoPush : unsigned char;
|
||||
|
||||
class Track;
|
||||
class WaveClip;
|
||||
|
||||
AudacityProject *CreateNewAudacityProject();
|
||||
AUDACITY_DLL_API AudacityProject *GetActiveProject();
|
||||
|
@ -2212,15 +2212,27 @@ WaveClip* WaveTrack::GetClipAtSample(sampleCount sample)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// When the time is both the end of a clip and the start of the next clip, the
|
||||
// latter clip is returned.
|
||||
WaveClip* WaveTrack::GetClipAtTime(double time)
|
||||
{
|
||||
// When the time is both the end of a clip and the start of the next clip, the
|
||||
// latter clip is returned.
|
||||
|
||||
const auto clips = SortedClipArray();
|
||||
auto result = std::find_if(clips.rbegin(), clips.rend(), [&] (WaveClip* const& clip) {
|
||||
auto p = std::find_if(clips.rbegin(), clips.rend(), [&] (WaveClip* const& clip) {
|
||||
return time >= clip->GetStartTime() && time <= clip->GetEndTime(); });
|
||||
|
||||
return result != clips.rend() ? *result : nullptr;
|
||||
// When two clips are immediately next to each other, the GetEndTime() of the first clip
|
||||
// and the GetStartTime() of the second clip may not be exactly equal due to rounding errors.
|
||||
// If "time" is the end time of the first of two such clips, and the end time is slightly
|
||||
// less than the start time of the second clip, then the first rather than the
|
||||
// second clip is found by the above code. So correct this.
|
||||
if (p != clips.rend() & p != clips.rbegin() &&
|
||||
time == (*p)->GetEndTime() &&
|
||||
(*p)->GetEndSample() == (*(p-1))->GetStartSample()) {
|
||||
p--;
|
||||
}
|
||||
|
||||
return p != clips.rend() ? *p : nullptr;
|
||||
}
|
||||
|
||||
Envelope* WaveTrack::GetEnvelopeAtX(int xcoord)
|
||||
|
Loading…
x
Reference in New Issue
Block a user