70 if (a.size() != b.size())
77 return a->GetPlayStartTime() == b->GetPlayStartTime() &&
78 a->GetSequenceStartTime() == b->GetSequenceStartTime() &&
79 a->GetPlayEndTime() == b->GetPlayEndTime() &&
80 a->GetSequenceEndTime() == b->GetSequenceEndTime();
83 return std::mismatch(a.begin(), a.end(), b.begin(), compare).first == a.end();
132 auto result = tracks.Add(trackFactory.Create());
133 result->AttachedTrackObjects::BuildAll();
140 , mpFactory(pFactory)
156 , mpFactory( orig.mpFactory )
157 , mpSpectrumSettings(orig.mpSpectrumSettings
161 , mpWaveformSettings(orig.mpWaveformSettings
169 for (
const auto &clip : orig.
mClips)
171 ( std::make_unique<WaveClip>( *clip,
mpFactory,
true ) );
243 for (
const auto &clip :
mClips)
279 if (
static_cast<int>(linkType) == 1 ||
283 if (next ==
nullptr) {
286 wxLogWarning(L
"Right track %s is expected to be a WaveTrack."
287 "\n Removing link from left wave track %s.",
299 if (newLinkType != linkType)
311 {
"wave",
"wave",
XO(
"Wave Track") },
355 const float top = (rate / 2.);
362 const auto half =
settings.GetFFTLength() / 2;
364 const float bin2 = rate / half;
378 *max = std::max(bottom,
std::min(top, spectrumMax));
386 *
min = std::max(bottom, top / 1000.0f);
388 *
min = std::max(bottom,
std::min(top, spectrumMin));
400 return rect.GetTop() +
404template<
typename Container >
408 for (
const auto &clip: clips) {
409 result.emplace_back( clip->GetPlayStartTime(), clip->GetPlayEndTime(),
410 std::make_unique<WaveTrack::IntervalData>( clip ) );
418 auto &pSampleBlockFactory = trackFactory.GetSampleBlockFactory();
419 auto pNewTrack =
EmptyCopy( pSampleBlockFactory );
420 pNewTrack->Paste(0.0,
this);
426 return MakeIntervals<ConstIntervals>(
mClips );
431 return MakeIntervals<Intervals>( mClips );
436 for (
const auto& clip :
mClips)
438 if (clip->GetName() ==
name)
453 auto name = originalName;
454 for (
auto i = 1;; ++i)
459 name =
XC(
"%s.%i",
"clip name template").Format(originalName, i).Translation();
466 for (
auto i = 1;; ++i)
471 name =
XC(
"%s %i",
"clip name template").Format(
GetName(), i).Translation();
482 wxASSERT( newRate > 0 );
483 newRate = std::max( 1.0, newRate );
484 auto ratio =
mRate / newRate;
485 mRate = (int) newRate;
486 for (
const auto &clip :
mClips) {
487 clip->SetRate((
int)newRate);
488 clip->SetSequenceStartTime( clip->GetSequenceStartTime() * ratio );
494 return mGain.load(std::memory_order_relaxed);
499 mGain.store(value, std::memory_order_relaxed);
512 return mPan.load(std::memory_order_relaxed);
517 mPan.store(value, std::memory_order_relaxed);
524 else if (newPan < -1.0)
527 if (
GetPan() != newPan ) {
538 const auto pan =
GetPan();
545 if ((channel%2) == 0)
554 for (
const auto &clip :
mClips)
555 clip->SetColourIndex( colorIndex );
563 for (
const auto& clip :
mClips)
564 result += clip->GetPlaySamplesCount();
573 for (
const auto& clip :
mClips)
574 result += clip->GetSequenceSamplesCount();
581 const std::function<
void(
size_t)> & progressReport)
583 for (
const auto& clip :
mClips)
584 clip->ConvertToSampleFormat(
format, progressReport);
595 for (
const auto &clip :
mClips)
597 if (!clip->BeforePlayStartTime(t1) && !clip->AfterPlayEndTime(t0)) {
616 auto tmp =
Copy(t0, t1);
630 auto tmp =
Copy(t0, t1);
638Track::Holder WaveTrack::CutAndAddCutLine(
double t0,
double t1)
644 auto tmp =
Copy(t0, t1);
659 bool inside0 =
false;
660 bool inside1 =
false;
662 for (
const auto &clip :
mClips)
664 if(t1 > clip->GetPlayStartTime() && t1 < clip->GetPlayEndTime())
666 clip->SetTrimRight(clip->GetTrimRight() + clip->GetPlayEndTime() - t1);
670 if(t0 > clip->GetPlayStartTime() && t0 < clip->GetPlayEndTime())
672 clip->SetTrimLeft(clip->GetTrimLeft() + t0 - clip->GetPlayStartTime());
692 auto result = std::make_shared<WaveTrack>( pFactory,
mFormat,
mRate );
694 result->mpFactory = pFactory ? pFactory :
mpFactory;
712 for (
const auto &clip :
mClips)
714 if (t0 <= clip->GetPlayStartTime() && t1 >= clip->GetPlayEndTime())
719 newTrack->
mClips.push_back
720 (std::make_unique<WaveClip>(*clip,
mpFactory, ! forClipboard));
724 else if (t1 > clip->GetPlayStartTime() && t0 < clip->GetPlayEndTime())
729 const double clip_t0 = std::max(t0, clip->GetPlayStartTime());
730 const double clip_t1 =
std::min(t1, clip->GetPlayEndTime());
732 auto newClip = std::make_unique<WaveClip>
733 (*clip,
mpFactory, ! forClipboard, clip_t0, clip_t1);
734 newClip->SetName(clip->GetName());
738 newClip->Offset(-t0);
739 if (newClip->GetPlayStartTime() < 0)
740 newClip->SetPlayStartTime(0);
742 newTrack->
mClips.push_back(std::move(newClip));
753 auto placeholder = std::make_unique<WaveClip>(
mpFactory,
755 static_cast<int>(newTrack->
GetRate()),
757 placeholder->SetIsPlaceholder(
true);
758 placeholder->InsertSilence(0, (t1 - t0) - newTrack->
GetEndTime());
760 newTrack->
mClips.push_back(std::move(placeholder));
864 std::shared_ptr<WaveClip>
left;
916 std::vector<SplitInfo> splits;
921 auto get_split = [&](
double time) {
922 auto it = std::find_if(splits.begin(), splits.end(), [time](
const SplitInfo& split) {
923 return split.time == time;
925 if(it == splits.end())
928 { time, nullptr, nullptr, std::nullopt, std::nullopt }
935 const TimeWarper *warper = (effectWarper ? effectWarper : &localWarper);
944 for (
const auto &clip :
mClips) {
951 if (st >= t0 && st <= t1) {
952 auto it = get_split(st);
953 if (clip->GetTrimLeft() != 0)
956 it->right = std::make_shared<WaveClip>(*clip,
mpFactory,
false);
957 it->right->SetTrimLeft(.0);
958 it->right->ClearRight(clip->GetPlayStartTime());
960 it->rightClipName = clip->GetName();
964 if (st >= t0 && st <= t1) {
965 auto it = get_split(st);
966 if (clip->GetTrimRight() != 0)
969 it->left = std::make_shared<WaveClip>(*clip,
mpFactory,
false);
970 it->left->SetTrimRight(.0);
971 it->left->ClearLeft(clip->GetPlayEndTime());
973 it->leftClipName = clip->GetName();
977 auto &cutlines = clip->GetCutLines();
979 for (
auto it = cutlines.begin(); it != cutlines.end(); ) {
985 if (cs >= t0 && cs <= t1) {
989 cuts.push_back(std::move(*it));
990 it = cutlines.erase(it);
997 const auto tolerance = 2.0 /
GetRate();
1007 if (merge && splits.size() > 0)
1019 for (
const auto clip : clips) {
1022 if (fabs(t1 - clip->GetPlayStartTime()) < tolerance) {
1039 for (
const auto clip : clips) {
1047 if (fabs(t0 - clip->GetPlayEndTime()) < tolerance)
1066 auto trim = src->GetPlayEndTime() - src->GetPlayStartTime();
1080 auto trim = src->GetPlayEndTime() - src->GetPlayStartTime();
1086 for (
const auto& split: splits) {
1088 for (
const auto& clip :
GetClips())
1090 if (clip->WithinPlayRegion(at))
1092 auto newClip = std::make_unique<WaveClip>(*clip,
mpFactory,
true);
1094 clip->ClearRight(at);
1095 newClip->ClearLeft(at);
1097 attachRight(clip.get(), split.left.get());
1099 attachLeft(newClip.get(), split.right.get());
1105 attachLeft(clip.get(), split.right.get());
1110 attachRight(clip.get(), split.left.get());
1117 for (
const auto& split : splits)
1122 if (split.rightClipName.has_value() && clip->GetPlayStartSample() == s)
1123 clip->SetName(*split.rightClipName);
1124 else if (split.leftClipName.has_value() && clip->GetPlayEndSample() == s)
1125 clip->SetName(*split.leftClipName);
1130 for (
const auto &clip :
mClips) {
1134 st = clip->GetPlayStartTime();
1135 et = clip->GetPlayEndTime();
1138 for (
auto it = cuts.begin(); it != cuts.end();) {
1145 if (cs >= st && cs <= et) {
1147 clip->GetCutLines().push_back( std::move(*it) );
1148 it = cuts.erase(it);
1161 bool addCutLines =
false;
1168 WaveClipHolders::const_iterator
1173 auto it = list.begin();
1174 for (
const auto end = list.end(); it !=
end; ++it)
1176 if (it->get() == clip)
1184 WaveClipHolders::iterator
1189 auto it = list.begin();
1190 for (
const auto end = list.end(); it !=
end; ++it)
1192 if (it->get() == clip)
1205 if (it !=
mClips.end()) {
1206 auto result = std::move(*it);
1216 if (clip->GetSequence()->GetFactory() != this->mpFactory)
1228 bool addCutLines,
bool split)
1232 wxASSERT( t1 >= t0 );
1245 for (
const auto &clip :
mClips)
1247 if (!clip->BeforePlayStartTime(t1) && !clip->AfterPlayEndTime(t0) &&
1248 (clip->BeforePlayStartTime(t0) || clip->AfterPlayEndTime(t1)))
1250 addCutLines =
false;
1256 for (
const auto &clip :
mClips)
1258 if (clip->BeforePlayStartTime(t0) && clip->AfterPlayEndTime(t1))
1261 clipsToDelete.push_back(clip.get());
1263 else if (!clip->BeforePlayStartTime(t1) && !clip->AfterPlayEndTime(t0))
1270 clipsToDelete.push_back( clip.get() );
1271 auto newClip = std::make_unique<WaveClip>( *clip,
mpFactory,
true );
1272 newClip->ClearAndAddCutLine( t0, t1 );
1273 clipsToAdd.push_back( std::move( newClip ) );
1280 if (clip->BeforePlayStartTime(t0)) {
1285 clipsToDelete.push_back( clip.get() );
1286 auto newClip = std::make_unique<WaveClip>( *clip,
mpFactory,
true );
1287 newClip->TrimLeft(t1 - clip->GetPlayStartTime());
1288 clipsToAdd.push_back( std::move( newClip ) );
1290 else if (clip->AfterPlayEndTime(t1)) {
1295 clipsToDelete.push_back( clip.get() );
1296 auto newClip = std::make_unique<WaveClip>( *clip,
mpFactory,
true );
1297 newClip->TrimRight(clip->GetPlayEndTime() - t0);
1299 clipsToAdd.push_back( std::move( newClip ) );
1305 auto leftClip = std::make_unique<WaveClip>(*clip,
mpFactory,
true);
1306 leftClip->TrimRight(clip->GetPlayEndTime() - t0);
1307 clipsToAdd.push_back(std::move(leftClip));
1309 auto rightClip = std::make_unique<WaveClip>(*clip,
mpFactory,
true);
1310 rightClip->TrimLeft(t1 - rightClip->GetPlayStartTime());
1311 clipsToAdd.push_back(std::move(rightClip));
1313 clipsToDelete.push_back(clip.get());
1321 clipsToDelete.push_back( clip.get() );
1322 auto newClip = std::make_unique<WaveClip>( *clip,
mpFactory,
true );
1325 newClip->Clear(t0,t1);
1327 clipsToAdd.push_back( std::move( newClip ) );
1336 if (!split && editClipCanMove)
1340 for (
const auto& clip :
mClips)
1342 if (clip->BeforePlayStartTime(t1))
1343 clip->Offset(-(t1 - t0));
1347 for (
const auto &clip: clipsToDelete)
1350 if (myIt !=
mClips.end())
1356 for (
auto &clip: clipsToAdd)
1357 mClips.push_back(std::move(clip));
1362 if (newT1 > oldT1) {
1376 const auto offset = newT1 - oldT1;
1377 for(
const auto& clip :
mClips)
1379 if (clip->GetPlayStartTime() > oldT1 - (1.0 /
mRate))
1380 clip->Offset(offset);
1388 auto tmp = std::make_shared<WaveTrack>(
1391 tmp->InsertSilence(0.0, newT1 - oldT1);
1393 Paste(oldT1, tmp.get());
1396 else if (newT1 < oldT1) {
1397 Clear(newT1, oldT1);
1429 bool singleClipMode = other->
GetNumClips() == 1 &&
1432 const double insertDuration = other->
GetEndTime();
1433 if (insertDuration != 0 && insertDuration < 1.0 /
mRate)
1442 auto pastingFromTempTrack = !other->
GetOwner();
1446 if (editClipCanMove) {
1447 if (!singleClipMode) {
1454 for (
const auto& clip :
mClips)
1456 if (clip->GetPlayStartTime() > t0 - (1.0 /
mRate))
1457 clip->Offset(insertDuration);
1468 for (
const auto& clip :
mClips)
1470 if (editClipCanMove)
1472 if (clip->WithinPlayRegion(t0))
1476 insideClip = clip.get();
1483 if (clip->WithinPlayRegion(t0) ||
1486 insideClip = clip.get();
1496 if (!editClipCanMove)
1500 for (
const auto& clip :
mClips)
1504 clip->GetPlayStartTime())
1509 XO(
"There is not enough room available to paste the selection"),
1511 "Error:_Insufficient_space_in_track"
1524 if (!editClipCanMove && !
IsEmpty(t0, t0 + insertDuration - 1.0 /
mRate))
1529 XO(
"There is not enough room available to paste the selection"),
1531 "Error:_Insufficient_space_in_track"
1534 for (
const auto& clip : other->
mClips)
1537 if (!clip->GetIsPlaceholder())
1540 std::make_unique<WaveClip>(*clip,
mpFactory,
true);
1541 newClip->Resample(
mRate);
1542 newClip->Offset(t0);
1543 newClip->MarkChanged();
1544 if (pastingFromTempTrack)
1550 mClips.push_back(std::move(newClip));
1558 if (
auto other =
dynamic_cast<const WaveTrack*
>(src))
1573 for (
const auto &clip :
mClips)
1575 auto clipStart = clip->GetPlayStartSample();
1576 auto clipEnd = clip->GetPlayEndSample();
1578 if (clipEnd > start && clipStart <
end)
1580 auto offset = std::max(start - clipStart,
sampleCount(0));
1582 auto length =
std::min(
end, clipEnd) - (clipStart + offset);
1584 clip->SetSilence(offset, length);
1603 clip->InsertSilence(0, len);
1605 mClips.push_back( std::move( clip ) );
1611 const auto it = std::find_if(
mClips.begin(),
end,
1612 [&](
const WaveClipHolder &clip) { return clip->WithinPlayRegion(t); } );
1616 it->get()->InsertSilence(t, len);
1619 for (
const auto &clip :
mClips)
1621 if (clip->BeforePlayStartTime(t))
1633 const size_t maxAtOnce = 1048576;
1634 Floats buffer{ maxAtOnce };
1637 for (
const auto &clip :
mClips)
1639 double startTime = clip->GetPlayStartTime();
1640 double endTime = clip->GetPlayEndTime();
1642 if( endTime < t0 || startTime > t1 )
1649 auto start = clip->TimeToSamples(std::max(.0, t0 - startTime));
1650 auto end = clip->TimeToSamples(
std::min(endTime, t1) - startTime);
1652 auto len = (
end - start );
1653 for(
decltype(len) done = 0; done < len; done += maxAtOnce )
1659 for(
decltype(numSamples) i = 0; i < numSamples; i++ )
1661 auto curSamplePos = start + done + i;
1664 if( buffer[ i ] == 0.0 && seqStart == -1 )
1665 seqStart = curSamplePos;
1666 else if( buffer[ i ] != 0.0 || curSamplePos ==
end - 1 )
1668 if( seqStart != -1 )
1670 decltype(
end) seqEnd;
1673 if( curSamplePos ==
end - 1 && buffer[ i ] == 0.0 )
1676 seqEnd = curSamplePos;
1677 if( seqEnd - seqStart + 1 > minSamples )
1681 startTime + clip->SamplesToTime(seqStart),
1682 startTime + clip->SamplesToTime(seqEnd)
1693 for(
unsigned int i = 0; i < regions.size(); i++ )
1695 const Region ®ion = regions.at(i);
1708 for (
const auto &clip:
mClips)
1710 if (clip->GetPlayStartTime() < t1-(1.0/
mRate) &&
1711 clip->GetPlayEndTime()-(1.0/
mRate) > t0) {
1714 auto it = clipsToDelete.begin(),
end = clipsToDelete.end();
1715 for (; it !=
end; ++it)
1716 if ((*it)->GetPlayStartTime() > clip->GetPlayStartTime())
1719 clipsToDelete.insert(it, clip.get());
1724 if( clipsToDelete.size() == 0 )
1727 auto t = clipsToDelete[0]->GetPlayStartTime();
1729 newClip =
CreateClip(clipsToDelete[0]->GetSequenceStartTime(),
1732 for (
const auto &clip : clipsToDelete)
1737 if (clip->GetPlayStartTime() - t > (1.0 /
mRate)) {
1738 double addedSilence = (clip->GetPlayStartTime() - t);
1740 auto offset = clip->GetPlayStartTime();
1741 auto value = clip->GetEnvelope()->GetValue( offset );
1742 newClip->AppendSilence( addedSilence, value );
1747 newClip->Paste(t, clip);
1749 t = newClip->GetPlayEndTime();
1760 size_t len,
unsigned int stride,
sampleFormat effectiveFormat)
1763 ->
Append(buffer,
format, len, stride, effectiveFormat);
1768 for (
const auto &clip :
mClips)
1770 const auto startSample = clip->GetPlayStartSample();
1771 const auto endSample = clip->GetPlayEndSample();
1772 if (s >= startSample && s < endSample)
1774 auto blockStartOffset = clip->GetSequence()->GetBlockStart(clip->ToSequenceSamples(s));
1775 return std::max(startSample, clip->GetSequenceStartSample() + blockStartOffset);
1786 for (
const auto &clip :
mClips)
1788 auto startSample = clip->GetPlayStartSample();
1789 auto endSample = clip->GetPlayEndSample();
1790 if (s >= startSample && s < endSample)
1792 bestBlockSize = clip->GetSequence()->GetBestBlockSize(s - clip->GetSequenceStartSample());
1797 return bestBlockSize;
1803 for (
const auto &clip :
mClips)
1805 maxblocksize = std::max(maxblocksize, clip->GetSequence()->GetMaxBlockSize());
1808 if (maxblocksize == 0)
1817 wxASSERT(maxblocksize > 0);
1819 return maxblocksize;
1847 if (tag ==
"wavetrack") {
1851 for (
auto pair : attrs)
1853 auto attr = pair.first;
1854 auto value = pair.second;
1859 if (!value.TryGet(dblValue) ||
1860 (dblValue < 1.0) || (dblValue > 1000000.0))
1865 else if (attr ==
"offset" && value.TryGet(dblValue))
1876 else if (attr ==
"gain" && value.TryGet(dblValue))
1878 else if (attr ==
"pan" && value.TryGet(dblValue) &&
1879 (dblValue >= -1.0) && (dblValue <= 1.0))
1881 else if (attr ==
"channel")
1883 if (!value.TryGet(nValue) ||
1888 else if (attr ==
"linked" && value.TryGet(nValue))
1890 else if (attr ==
"colorindex" && value.TryGet(nValue))
1893 else if (attr ==
"sampleformat" && value.TryGet(nValue) &&
1913 .CallObjectAccessor(tag, *
this) )
1919 if (tag ==
"sequence" || tag ==
"envelope")
1925 if (tag ==
"sequence")
1927 else if (tag ==
"envelope")
1933 if (tag ==
"waveblock")
1944 if (tag ==
"waveclip")
1953 xmlFile.StartTag(
wxT(
"wavetrack"));
1955 xmlFile.WriteAttr(
wxT(
"channel"), mChannel);
1956 xmlFile.WriteAttr(
wxT(
"linked"),
static_cast<int>(GetLinkType()));
1958 xmlFile.WriteAttr(
wxT(
"rate"), mRate);
1959 xmlFile.WriteAttr(
wxT(
"gain"),
static_cast<double>(GetGain()));
1960 xmlFile.WriteAttr(
wxT(
"pan"),
static_cast<double>(GetPan()));
1961 xmlFile.WriteAttr(
wxT(
"colorindex"), mWaveColorIndex );
1962 xmlFile.WriteAttr(
wxT(
"sampleformat"),
static_cast<long>(mFormat) );
1966 for (
const auto &clip : mClips)
1968 clip->WriteXML(xmlFile);
1971 xmlFile.EndTag(
wxT(
"wavetrack"));
1976 for (
const auto &clip :
mClips)
1977 if (clip->GetSequence()->GetErrorOpening())
1985 for (
const auto &clip :
mClips)
1999 for (
const auto &clip :
mClips)
2003 best = clip->GetPlayStartTime();
2005 else if (clip->GetPlayStartTime() < best)
2006 best = clip->GetPlayStartTime();
2019 for (
const auto &clip :
mClips)
2023 best = clip->GetPlayEndTime();
2025 else if (clip->GetPlayEndTime() > best)
2026 best = clip->GetPlayEndTime();
2037 double t0,
double t1,
bool mayThrow)
const
2039 std::pair<float, float> results {
2043 bool clipFound =
false;
2054 for (
const auto &clip:
mClips)
2056 if (t1 >= clip->GetPlayStartTime() && t0 <= clip->GetPlayEndTime())
2059 auto clipResults = clip->GetMinMax(t0, t1, mayThrow);
2060 if (clipResults.first < results.first)
2061 results.first = clipResults.first;
2062 if (clipResults.second > results.second)
2063 results.second = clipResults.second;
2069 results = { 0.f, 0.f };
2089 for (
const auto &clip:
mClips)
2094 if (t1 >= clip->GetPlayStartTime() && t0 <= clip->GetPlayEndTime())
2096 auto clipStart = clip->TimeToSequenceSamples(wxMax(t0, clip->GetPlayStartTime()));
2097 auto clipEnd = clip->TimeToSequenceSamples(wxMin(t1, clip->GetPlayEndTime()));
2099 float cliprms = clip->GetRMS(t0, t1, mayThrow);
2101 sumsq += cliprms * cliprms * (clipEnd - clipStart).as_float();
2102 length += (clipEnd - clipStart);
2105 return length > 0 ? sqrt(sumsq / length.
as_double()) : 0.0;
2110 bool mayThrow,
sampleCount * pNumWithinClips)
const
2115 bool doClear =
true;
2118 for (
const auto &clip:
mClips)
2120 if (start >= clip->GetPlayStartSample() && start+len <= clip->GetPlayEndSample())
2135 float * pBuffer = (
float*)buffer;
2136 for(
size_t i=0;i<len;i++)
2141 wxFAIL_MSG(
wxT(
"Invalid fill format"));
2146 for (
const auto &clip:
mClips)
2148 auto clipStart = clip->GetPlayStartSample();
2149 auto clipEnd = clip->GetPlayEndSample();
2151 if (clipEnd > start && clipStart < start+len)
2154 auto samplesToCopy =
2155 std::min( start+len - clipStart, clip->GetPlaySamplesCount() );
2156 auto startDelta = clipStart - start;
2157 decltype(startDelta) inclipDelta = 0;
2160 inclipDelta = -startDelta;
2161 samplesToCopy -= inclipDelta;
2175 if (!clip->GetSamples(
2177 startDelta.as_size_t() *
2179 format, inclipDelta, samplesToCopy.as_size_t(), mayThrow ))
2182 samplesCopied += samplesToCopy;
2185 if( pNumWithinClips )
2186 *pNumWithinClips = samplesCopied;
2194 for (
const auto &clip:
mClips)
2196 auto clipStart = clip->GetPlayStartSample();
2197 auto clipEnd = clip->GetPlayEndSample();
2199 if (clipEnd > start && clipStart < start+len)
2202 auto samplesToCopy =
2203 std::min( start+len - clipStart, clip->GetPlaySamplesCount() );
2204 auto startDelta = clipStart - start;
2205 decltype(startDelta) inclipDelta = 0;
2208 inclipDelta = -startDelta;
2209 samplesToCopy -= inclipDelta;
2225 format, inclipDelta, samplesToCopy.as_size_t(), effectiveFormat );
2226 clip->MarkChanged();
2236 return std::max(format,
2237 pClip->GetSequence()->GetSampleFormats().Effective());
2244 return std::all_of(clips.begin(), clips.end(),
2245 [](
const auto &pClip){ return pClip->GetEnvelope()->IsTrivial(); });
2261 for (
decltype(bufferLen) i = 0; i < bufferLen; i++)
2266 double startTime = t0;
2267 auto tstep = 1.0 /
mRate;
2268 double endTime = t0 + tstep * bufferLen;
2269 for (
const auto &clip:
mClips)
2272 auto dClipStartTime = clip->GetPlayStartTime();
2273 auto dClipEndTime = clip->GetPlayEndTime();
2274 if ((dClipStartTime < endTime) && (dClipEndTime > startTime))
2277 auto rlen = bufferLen;
2280 if (rt0 < dClipStartTime)
2285 auto snDiff = nDiff.as_size_t();
2287 wxASSERT(snDiff <= rlen);
2289 rt0 = dClipStartTime;
2292 if (rt0 + rlen*tstep > dClipEndTime)
2294 auto nClipLen = clip->GetPlayEndSample() - clip->GetPlayStartSample();
2305 rlen =
std::min(rlen,
size_t(floor(0.5 + (dClipEndTime - rt0) / tstep)));
2309 clip->GetEnvelope()->GetValues(rbuf, rlen, rt0, tstep);
2320 auto p = std::find_if(clips.rbegin(), clips.rend(), [&] (
WaveClip*
const& clip) {
2321 return time >= clip->GetPlayStartTime() && time <= clip->GetPlayEndTime(); });
2328 if (p != clips.rend() && p != clips.rbegin() &&
2329 time == (*p)->GetPlayEndTime() &&
2330 (*p)->SharesBoundaryWithNextClip(*(p-1))) {
2334 return p != clips.rend() ? *p :
nullptr;
2358 clip->SetName(
name);
2359 clip->SetSequenceStartTime(offset);
2360 mClips.push_back(std::move(clip));
2362 return mClips.back().get();
2371 return mClips.back().get();
2382 auto it =
mClips.begin();
2383 WaveClip *rightmost = (*it++).get();
2389 if (maxOffset < offset)
2390 maxOffset = offset, rightmost = clip;
2405 if(index < (
int)
mClips.size())
2406 return mClips[index].get();
2422 const std::vector<WaveClip*> &clips,
2424 double *allowedAmount )
2427 *allowedAmount = amount;
2429 const auto &moving = [&](
WaveClip *clip){
2432 return clips.end() != std::find( clips.begin(), clips.end(), clip );
2435 for (
const auto &c:
mClips) {
2436 if ( moving( c.get() ) )
2438 for (
const auto clip : clips) {
2439 if (c->GetPlayStartTime() < clip->GetPlayEndTime() + amount &&
2440 c->GetPlayEndTime() > clip->GetPlayStartTime() + amount)
2447 if (c->GetPlayStartTime() - clip->GetPlayEndTime() < *allowedAmount)
2448 *allowedAmount = c->GetPlayStartTime() - clip->GetPlayEndTime();
2449 if (*allowedAmount < 0)
2453 if (c->GetPlayEndTime() - clip->GetPlayStartTime() > *allowedAmount)
2454 *allowedAmount = c->GetPlayEndTime() - clip->GetPlayStartTime();
2455 if (*allowedAmount > 0)
2464 if (*allowedAmount == amount)
2480 WaveClip* clip,
double &slideBy,
double &tolerance)
const
2482 for (
const auto &c :
mClips)
2484 double d1 = c->GetPlayStartTime() - (clip->
GetPlayEndTime()+slideBy);
2486 if ( (d1<0) && (d2<0) )
2494 if( -d1 < tolerance ){
2499 }
else if( -d2 < tolerance ){
2524 for (
const auto &c :
mClips)
2526 if (c->WithinPlayRegion(t))
2529 auto newClip = std::make_unique<WaveClip>( *c,
mpFactory,
true );
2531 newClip->TrimLeftTo(t);
2535 mClips.push_back(std::move(newClip));
2551 for (
const auto clip : clips)
2572 const WaveClip *previousClip =
nullptr;
2573 for (
const auto clip: clips)
2575 for (
const auto &cc : clip->GetCutLines())
2578 if (clip->WithinPlayRegion(cutlinePosition))
2593 if (fabs(previousClip->
GetPlayEndTime() - clip->GetPlayStartTime())
2607 previousClip = clip;
2610 wxASSERT(curpos == num);
2621 double start = 0,
end = 0;
2622 auto pEnd =
mClips.end();
2623 auto pClip = std::find_if(
mClips.begin(), pEnd,
2625 return clip->FindCutLine(cutLinePosition, &start, &end); } );
2628 auto &clip = *pClip;
2629 if (!editClipCanMove)
2633 for (
const auto &clip2:
mClips)
2635 if (clip2->GetPlayStartTime() > clip->GetPlayStartTime() &&
2636 clip->GetPlayEndTime() +
end - start > clip2->GetPlayStartTime())
2640 XO(
"There is not enough room available to expand the cut line"),
2642 "Error:_Insufficient_space_in_track"
2647 clip->ExpandCutLine(cutLinePosition);
2652 *cutlineStart = start;
2657 if (editClipCanMove)
2659 for (
const auto &clip2 :
mClips)
2661 if (clip2->GetPlayStartTime() > clip->GetPlayStartTime())
2662 clip2->Offset(
end - start);
2670 for (
const auto &clip :
mClips)
2671 if (clip->RemoveCutLine(cutLinePosition))
2683 if (!clip1 || !clip2)
2700 for (
const auto &clip :
mClips)
2701 clip->Resample(rate, progress);
2707 template <
typename Cont1,
typename Cont2 >
2711 for (
const auto &clip : mClips)
2712 clips.push_back(clip.get());
2713 std::sort(clips.begin(), clips.end(),
2715 { return a->GetPlayStartTime() < b->GetPlayStartTime(); });
2722 return FillSortedClipArray<WaveClipPointers>(
mClips);
2727 return FillSortedClipArray<WaveClipConstPointers>(
mClips);
2734 if ( !mStack.empty() ) {
2735 auto &pair = mStack.back();
2736 if ( ++pair.first == pair.second ) {
2740 push( (*pair.first)->GetCutLines() );
2748 auto pClips = &clips;
2749 while (!pClips->empty()) {
2750 auto first = pClips->begin();
2751 mStack.push_back(
Pair( first, pClips->end() ) );
2752 pClips = &(*first)->GetCutLines();
2762 for(
const auto &clip : wt->GetAllClips()) {
2764 auto blocks = clip->GetSequenceBlockArray();
2765 for (
const auto &block : *blocks) {
2766 auto &pBlock = block.sb;
2768 if ( pIDs && !pIDs->insert(pBlock->GetBlockID()).second )
2782 const_cast<TrackList &
>(tracks), std::move( inspector ), pIDs );
2788 return std::make_shared< WaveTrackFactory >(
2810 project.AttachedObjects::Assign(
key2, result );
2816 project.AttachedObjects::Assign(
key2,
nullptr );
2826 for (
const auto& clip : wt->GetAllClips())
2828 if (clip->GetTrimLeft() > 0.0 || clip->GetTrimRight() > 0.0)
2829 return { 3, 1, 0, 0 };
2838 L
"/GUI/TrackNames/DefaultTrackName",
2850 gPrefs->Read(
wxT(
"/GUI/SyncLockTracks"), &mIsSyncLocked,
false);
2853 bool editClipsCanMove;
2858 L
"/GUI/EditClipCanMove",
false };
@ BadUserAction
Indicates that the user performed an action that is not allowed.
const TranslatableString name
MessageBoxException for violation of preconditions or assertions.
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
an object holding per-project preferred sample rate
std::shared_ptr< SampleBlockFactory > SampleBlockFactoryPtr
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Contains declarations for TimeWarper, IdentityTimeWarper, ShiftTimeWarper, LinearTimeWarper,...
static Settings & settings()
std::shared_ptr< WaveClip > WaveClipHolder
std::vector< WaveClipHolder > WaveClipHolders
ProjectFormatExtensionsRegistry::Extension smartClipsExtension([](const AudacityProject &project) -> ProjectFormatVersion { const TrackList &trackList=TrackList::Get(project);for(auto wt :trackList.Any< const WaveTrack >()) { for(const auto &clip :wt->GetAllClips()) { if(clip->GetTrimLeft() > 0.0||clip->GetTrimRight() > 0.0) return { 3, 1, 0, 0 };} } return BaseProjectFormatVersion;})
bool GetEditClipsCanMove()
static ProjectFileIORegistry::ObjectReaderEntry readerEntry
static const AudacityProject::AttachedObjects::RegisteredFactory key2
DEFINE_XML_METHOD_REGISTRY(WaveTrackIORegistry)
BoolSetting EditClipsCanMove
static Container MakeIntervals(const std::vector< WaveClipHolder > &clips)
static auto TrackFactoryFactory
void VisitBlocks(TrackList &tracks, BlockVisitor visitor, SampleBlockIDSet *pIDs)
StringSetting AudioTrackNameSetting
void InspectBlocks(const TrackList &tracks, BlockInspector inspector, SampleBlockIDSet *pIDs)
static const Track::TypeInfo & typeInfo()
std::function< void(SampleBlock &) > BlockVisitor
#define WAVETRACK_MERGE_POINT_TOLERANCE
std::unordered_set< SampleBlockID > SampleBlockIDSet
std::function< void(const SampleBlock &) > BlockInspector
std::vector< WaveClip * > WaveClipPointers
std::vector< const WaveClip * > WaveClipConstPointers
std::vector< Attribute > AttributesList
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Abstraction of a progress dialog with well defined time-to-completion estimate.
This specialization of Setting for bool adds a Toggle method to negate the saved value.
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Subclass * Find(const RegisteredFactory &key)
Get a (bare) pointer to an attachment, or null, down-cast it to Subclass *; will not create on demand...
Piecewise linear or piecewise exponential function from double to double.
No change to time at all.
bool HandleXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &value)
void Init(const PlayableTrack &init)
void Merge(const Track &init) override
void WriteXMLAttributes(XMLWriter &xmlFile) const
static ProjectRate & Get(AudacityProject &project)
static SampleBlockFactoryPtr New(AudacityProject &project)
double LongSamplesToTime(sampleCount pos) const
Convert correctly between a number of samples and an (absolute) time in seconds.
sampleCount TimeToLongSamples(double t0) const
Convert correctly between an (absolute) time in seconds and a number of samples.
A WaveTrack contains WaveClip(s). A WaveClip contains a Sequence. A Sequence is primarily an interfac...
size_t GetIdealBlockSize() const
static bool IsValidSampleFormat(const int nValue)
true if nValue is one of the sampleFormat enum values
bool ReadWithDefault(T *pVar, const T &defaultValue) const
overload of ReadWithDefault returning a boolean that is true if the value was previously defined */
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined */
A MessageBoxException that shows a given, unvarying string.
Spectrogram settings, either for one track or as defaults.
static SpectrogramSettings & defaults()
Specialization of Setting for strings.
Transforms one point in time to another point. For example, a time stretching effect might use one to...
virtual double Warp(double originalTime) const =0
Abstract base class for an object holding data associated with points on a time axis.
void SetChannel(ChannelType c) noexcept
virtual bool LinkConsistencyFix(bool doFix=true, bool completeList=true)
Check consistency of channel groups, and maybe fix it.
std::shared_ptr< TrackList > GetOwner() const
R TypeSwitch(const Functions &...functions)
Use this function rather than testing track type explicitly and making down-casts.
void SetLinkType(LinkType linkType, bool completeList=true)
std::shared_ptr< Track > Holder
bool HandleCommonXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &valueView)
void WriteCommonXMLAttributes(XMLWriter &xmlFile, bool includeNameAndSelected=true) const
virtual double GetEndTime() const =0
std::vector< Interval > Intervals
LinkType
For two tracks describes the type of the linkage.
LinkType GetLinkType() const noexcept
std::vector< ConstInterval > ConstIntervals
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
auto Any() -> TrackIterRange< TrackType >
static TrackList & Get(AudacityProject &project)
This allows multiple clips to be a part of one WaveTrack.
double GetSequenceStartTime() const noexcept
void SetSequenceStartTime(double startTime)
bool Append(constSamplePtr buffer, sampleFormat format, size_t len, unsigned int stride, sampleFormat effectiveFormat)
double GetPlayStartTime() const noexcept
double GetTrimRight() const noexcept
Returns the play end offset in seconds from the ending of the underlying sequence.
double GetTrimLeft() const noexcept
Returns the play start offset in seconds from the beginning of the underlying sequence.
void Paste(double t0, const WaveClip *other)
Paste data from other clip, resampling it if not equal rate.
void HandleXMLEndTag(const std::string_view &tag) override
double GetPlayEndTime() const
void SetTrimRight(double trim)
Sets the play end offset in seconds from the ending of the underlying sequence.
void Offset(double delta) noexcept
void SetTrimLeft(double trim)
Sets the play start offset in seconds from the beginning of the underlying sequence.
size_t NumCutLines() const
void Flush()
Flush must be called after last Append.
std::pair< Iterator, Iterator > Pair
void push(WaveClipHolders &clips)
AllClipsIterator & operator++()
Used to create or clone a WaveTrack, with appropriate context from the project that will own the trac...
std::shared_ptr< WaveTrack > Create()
Creates an unnamed empty WaveTrack with default sample format and default rate.
static void Destroy(AudacityProject &project)
static WaveTrackFactory & Get(AudacityProject &project)
static WaveTrackFactory & Reset(AudacityProject &project)
SampleBlockFactoryPtr mpFactory
const ProjectRate & mRate
A Track that contains audio waveform data.
void HandleXMLEndTag(const std::string_view &tag) override
bool LinkConsistencyFix(bool doFix, bool completeList) override
Check consistency of channel groups, and maybe fix it.
Track::Holder PasteInto(AudacityProject &) const override
Find or create the destination track for a paste, maybe in a different project.
void SetDisplayBounds(float min, float max) const
SampleBlockFactoryPtr mpFactory
bool Append(constSamplePtr buffer, sampleFormat format, size_t len, unsigned int stride=1, sampleFormat effectiveFormat=widestSampleFormat) override
void SetRate(double newRate)
void Paste(double t0, const Track *src) override
void DoSetPan(float value)
bool CanInsertClip(WaveClip *clip, double &slideBy, double &tolerance) const
void SetLastScaleType() const
static WaveTrack * New(AudacityProject &project)
void SplitDelete(double t0, double t1)
std::vector< Region > Regions
bool Get(samplePtr buffer, sampleFormat format, sampleCount start, size_t len, fillFormat fill=fillZero, bool mayThrow=true, sampleCount *pNumWithinClips=nullptr) const override
bool AddClip(const std::shared_ptr< WaveClip > &clip)
Append a clip to the track; which must have the same block factory as this track; return success.
ChannelType GetChannelIgnoringPan() const override
void Reinit(const WaveTrack &orig)
ConstIntervals GetIntervals() const override
Report times on the track where important intervals begin and end, for UI to snap to.
bool CanOffsetClips(const std::vector< WaveClip * > &clips, double amount, double *allowedAmount=nullptr)
Decide whether the clips could be offset (and inserted) together without overlapping other clips.
void ExpandCutLine(double cutLinePosition, double *cutlineStart=NULL, double *cutlineEnd=NULL)
double GetStartTime() const override
Get the time at which the first clip in the track starts.
void ConvertToSampleFormat(sampleFormat format, const std::function< void(size_t)> &progressReport={})
size_t GetMaxBlockSize() const override
This returns a nonnegative number of samples meant to size a memory buffer.
void InsertSilence(double t, double len) override
size_t GetBestBlockSize(sampleCount t) const override
This returns a nonnegative number of samples meant to size a memory buffer.
WaveClipPointers SortedClipArray()
const SpectrogramSettings & GetSpectrogramSettings() const
void WriteXML(XMLWriter &xmlFile) const override
wxString MakeNewClipName() const
void SetPan(float newPan) override
WaveClip * CreateClip(double offset=.0, const wxString &name=wxEmptyString)
static wxString GetDefaultAudioTrackNamePreference()
sampleCount GetBlockStart(sampleCount t) const override
This returns a possibly large or negative value.
void Join(double t0, double t1)
std::pair< float, float > GetMinMax(double t0, double t1, bool mayThrow=true) const
int GetWaveColorIndex() const
WaveClip * NewestOrNewClip()
Get access to the most recently added clip, or create a clip, if there is not already one....
sampleFormat GetSampleFormat() const override
void Silence(double t0, double t1) override
void ClearAndAddCutLine(double t0, double t1)
void SyncLockAdjust(double oldT1, double newT1) override
const TypeInfo & GetTypeInfo() const override
WaveClip * GetClipByIndex(int index)
WaveTrack(const SampleBlockFactoryPtr &pFactory, sampleFormat format, double rate)
size_t GetIdealBlockSize()
WaveClip * GetClipAtTime(double time)
Track::Holder Cut(double t0, double t1) override
bool GetErrorOpening() override
void Clear(double t0, double t1) override
void Init(const WaveTrack &orig)
void SetLastdBRange() const
void Split(double t0, double t1)
bool RemoveCutLine(double cutLinePosition)
sampleCount GetPlaySamplesCount() const
void SetWaveColorIndex(int colorIndex)
sampleCount GetSequenceSamplesCount() const
std::atomic< float > mPan
Atomic because it may be read by worker threads in playback.
Track::Holder Clone() const override
ChannelType GetChannel() const override
double GetOffset() const override
std::vector< Location > mDisplayLocationsCache
bool IsEmpty(double t0, double t1) const
Returns true if there are no WaveClips in the specified region.
sampleFormat WidestEffectiveFormat() const override
XMLTagHandler * HandleXMLChild(const std::string_view &tag) override
bool HandleXMLTag(const std::string_view &tag, const AttributesList &attrs) override
static const TypeInfo & ClassTypeInfo()
std::shared_ptr< WaveClip > RemoveAndReturnClip(WaveClip *clip)
void Trim(double t0, double t1)
void Disjoin(double t0, double t1)
std::unique_ptr< SpectrogramSettings > mpSpectrumSettings
void DoSetGain(float value)
void Resample(int rate, BasicUI::ProgressDialog *progress=NULL)
float GetRMS(double t0, double t1, bool mayThrow=true) const
std::atomic< float > mGain
Atomic because it may be read by worker threads in playback.
double GetEndTime() const override
Get the time at which the last clip in the track ends, plus recorded stuff.
int ZeroLevelYCoordinate(wxRect rect) const
const WaveClip * FindClipByName(const wxString &name) const
Returns nullptr if clip with such name was not found.
int GetClipIndex(const WaveClip *clip) const
void SetGain(float newGain)
double GetRate() const override
WaveClip * RightmostOrNewClip()
Get access to the last (rightmost) clip, or create a clip, if there is not already one.
Sequence * GetSequenceAtTime(double time)
void GetDisplayBounds(float *min, float *max) const
void SetSpectrumBounds(float min, float max) const
Track::Holder CopyNonconst(double t0, double t1)
void Merge(const Track &orig) override
WaveClipHolders & GetClips()
float GetChannelGain(int channel) const override
Takes gain and pan into account.
void SetWaveformSettings(std::unique_ptr< WaveformSettings > &&pSettings)
void Set(constSamplePtr buffer, sampleFormat format, sampleCount start, size_t len, sampleFormat effectiveFormat=widestSampleFormat)
bool HasTrivialEnvelope() const override
void MergeClips(int clipidx1, int clipidx2)
double mLegacyProjectFileOffset
const WaveformSettings & GetWaveformSettings() const
void ClearAndPaste(double t0, double t1, const Track *src, bool preserve=true, bool merge=true, const TimeWarper *effectWarper=NULL)
void UpdateLocationsCache() const
std::shared_ptr< WaveTrack > Holder
void HandleClear(double t0, double t1, bool addCutLines, bool split)
void SetOffset(double o) override
Track::Holder SplitCut(double t0, double t1)
void SetSpectrogramSettings(std::unique_ptr< SpectrogramSettings > &&pSettings)
std::unique_ptr< WaveformSettings > mpWaveformSettings
void UseSpectralPrefs(bool bUse=true)
Track::Holder Copy(double t0, double t1, bool forClipboard=true) const override
Envelope * GetEnvelopeAtTime(double time)
virtual void SetPanFromChannelType() override
Holder EmptyCopy(const SampleBlockFactoryPtr &pFactory={}, bool keepLink=true) const
void GetSpectrumBounds(float *min, float *max) const
void PasteWaveTrack(double t0, const WaveTrack *other)
SpectrogramSettings & GetIndependentSpectrogramSettings()
void GetEnvelopeValues(double *buffer, size_t bufferLen, double t0) const override
Fetch envelope values corresponding to uniformly separated sample times starting at the given time.
wxString MakeClipCopyName(const wxString &originalName) const
static const TypeInfo & ClassTypeInfo()
static XMLMethodRegistry & Get()
Get the unique instance.
void CallWriters(const Host &host, XMLWriter &writer)
This class is an interface which should be implemented by classes which wish to be able to load and s...
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
Positions or offsets within audio files need a wide type.
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
PROJECT_RATE_API sampleFormat SampleFormatChoice()
bool IsValidChannel(const int nValue)
WaveClipHolders::iterator FindClip(WaveClipHolders &list, const WaveClip *clip, int *distance=nullptr)
bool AreAligned(const WaveClipPointers &a, const WaveClipPointers &b)
Track::LinkType ToLinkType(int value)
Cont1 FillSortedClipArray(const Cont2 &mClips)
Empty argument passed to some public constructors.
Structure to hold region of a wavetrack and a comparison function for sortability.
std::optional< wxString > leftClipName
std::shared_ptr< WaveClip > right
std::optional< wxString > rightClipName
std::shared_ptr< WaveClip > left