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)
158 , mpFactory( orig.mpFactory )
159 , mpSpectrumSettings(orig.mpSpectrumSettings
163 , mpWaveformSettings(orig.mpWaveformSettings
175 for (
const auto &clip : orig.
mClips)
177 ( std::make_unique<WaveClip>( *clip,
mpFactory,
true ) );
251 for (
const auto &clip :
mClips)
287 if (
static_cast<int>(linkType) == 1 ||
291 if (next ==
nullptr) {
294 wxLogWarning(L
"Right track %s is expected to be a WaveTrack."
295 "\n Removing link from left wave track %s.",
307 if (newLinkType != linkType)
319 {
"wave",
"wave",
XO(
"Wave Track") },
363 const float top = (rate / 2.);
370 const auto half =
settings.GetFFTLength() / 2;
372 const float bin2 = rate / half;
386 *max = std::max(bottom,
std::min(top, spectrumMax));
394 *
min = std::max(bottom, top / 1000.0f);
396 *
min = std::max(bottom,
std::min(top, spectrumMin));
408 return rect.GetTop() +
412template<
typename Container >
416 for (
const auto &clip: clips) {
417 result.emplace_back( clip->GetPlayStartTime(), clip->GetPlayEndTime(),
418 std::make_unique<WaveTrack::IntervalData>( clip ) );
426 auto &pSampleBlockFactory = trackFactory.GetSampleBlockFactory();
427 auto pNewTrack =
EmptyCopy( pSampleBlockFactory );
428 pNewTrack->Paste(0.0,
this);
434 return MakeIntervals<ConstIntervals>(
mClips );
439 return MakeIntervals<Intervals>( mClips );
444 for (
const auto& clip :
mClips)
446 if (clip->GetName() ==
name)
454 return std::make_shared<WaveTrack>( *
this );
459 auto name = originalName;
460 for (
auto i = 1;; ++i)
465 name =
XC(
"%s.%i",
"clip name template").Format(originalName, i).Translation();
472 for (
auto i = 1;; ++i)
477 name =
XC(
"%s %i",
"clip name template").Format(
GetName(), i).Translation();
488 wxASSERT( newRate > 0 );
489 newRate = std::max( 1.0, newRate );
490 auto ratio =
mRate / newRate;
491 mRate = (int) newRate;
492 for (
const auto &clip :
mClips) {
493 clip->SetRate((
int)newRate);
494 clip->SetSequenceStartTime( clip->GetSequenceStartTime() * ratio );
500 return mGain.load(std::memory_order_relaxed);
505 mGain.store(value, std::memory_order_relaxed);
518 return mPan.load(std::memory_order_relaxed);
523 mPan.store(value, std::memory_order_relaxed);
530 else if (newPan < -1.0)
533 if (
GetPan() != newPan ) {
544 const auto pan =
GetPan();
551 if ((channel%2) == 0)
572 for (
const auto &clip :
mClips)
573 clip->SetColourIndex( colorIndex );
581 for (
const auto& clip :
mClips)
582 result += clip->GetPlaySamplesCount();
591 for (
const auto& clip :
mClips)
592 result += clip->GetSequenceSamplesCount();
599 const std::function<
void(
size_t)> & progressReport)
601 for (
const auto& clip :
mClips)
602 clip->ConvertToSampleFormat(
format, progressReport);
613 for (
const auto &clip :
mClips)
615 if (!clip->BeforePlayStartTime(t1) && !clip->AfterPlayEndTime(t0)) {
634 auto tmp =
Copy(t0, t1);
648 auto tmp =
Copy(t0, t1);
656Track::Holder WaveTrack::CutAndAddCutLine(
double t0,
double t1)
662 auto tmp =
Copy(t0, t1);
677 bool inside0 =
false;
678 bool inside1 =
false;
680 for (
const auto &clip :
mClips)
682 if(t1 > clip->GetPlayStartTime() && t1 < clip->GetPlayEndTime())
684 clip->SetTrimRight(clip->GetTrimRight() + clip->GetPlayEndTime() - t1);
688 if(t0 > clip->GetPlayStartTime() && t0 < clip->GetPlayEndTime())
690 clip->SetTrimLeft(clip->GetTrimLeft() + t0 - clip->GetPlayStartTime());
710 auto result = std::make_shared<WaveTrack>( pFactory,
mFormat,
mRate );
712 result->mpFactory = pFactory ? pFactory :
mpFactory;
728 for (
const auto &clip :
mClips)
730 if (t0 <= clip->GetPlayStartTime() && t1 >= clip->GetPlayEndTime())
735 newTrack->
mClips.push_back
736 (std::make_unique<WaveClip>(*clip,
mpFactory, ! forClipboard));
740 else if (t1 > clip->GetPlayStartTime() && t0 < clip->GetPlayEndTime())
745 const double clip_t0 = std::max(t0, clip->GetPlayStartTime());
746 const double clip_t1 =
std::min(t1, clip->GetPlayEndTime());
748 auto newClip = std::make_unique<WaveClip>
749 (*clip,
mpFactory, ! forClipboard, clip_t0, clip_t1);
750 newClip->SetName(clip->GetName());
754 newClip->Offset(-t0);
755 if (newClip->GetPlayStartTime() < 0)
756 newClip->SetPlayStartTime(0);
758 newTrack->
mClips.push_back(std::move(newClip));
769 auto placeholder = std::make_unique<WaveClip>(
mpFactory,
771 static_cast<int>(newTrack->
GetRate()),
773 placeholder->SetIsPlaceholder(
true);
774 placeholder->InsertSilence(0, (t1 - t0) - newTrack->
GetEndTime());
776 newTrack->
mClips.push_back(std::move(placeholder));
880 std::shared_ptr<WaveClip>
left;
932 std::vector<SplitInfo> splits;
937 auto get_split = [&](
double time) {
938 auto it = std::find_if(splits.begin(), splits.end(), [time](
const SplitInfo& split) {
939 return split.time == time;
941 if(it == splits.end())
944 { time, nullptr, nullptr, std::nullopt, std::nullopt }
951 const TimeWarper *warper = (effectWarper ? effectWarper : &localWarper);
960 for (
const auto &clip :
mClips) {
967 if (st >= t0 && st <= t1) {
968 auto it = get_split(st);
969 if (clip->GetTrimLeft() != 0)
970 it->right = std::make_shared<WaveClip>(*clip,
mpFactory,
false, clip->GetSequenceStartTime(), st);
971 it->rightClipName = clip->GetName();
975 if (st >= t0 && st <= t1) {
976 auto it = get_split(st);
977 if (clip->GetTrimRight() != 0)
978 it->left = std::make_shared<WaveClip>(*clip,
mpFactory,
false, st, clip->GetSequenceEndTime());
979 it->leftClipName = clip->GetName();
983 auto &cutlines = clip->GetCutLines();
985 for (
auto it = cutlines.begin(); it != cutlines.end(); ) {
991 if (cs >= t0 && cs <= t1) {
995 cuts.push_back(std::move(*it));
996 it = cutlines.erase(it);
1003 const auto tolerance = 2.0 /
GetRate();
1013 if (merge && splits.size() > 0)
1025 for (
const auto clip : clips) {
1028 if (fabs(t1 - clip->GetPlayStartTime()) < tolerance) {
1045 for (
const auto clip : clips) {
1053 if (fabs(t0 - clip->GetPlayEndTime()) < tolerance)
1072 auto trim = src->GetPlayEndTime() - src->GetPlayStartTime();
1086 auto trim = src->GetPlayEndTime() - src->GetPlayStartTime();
1092 for (
const auto& split: splits) {
1094 for (
const auto& clip :
GetClips())
1096 if (clip->WithinPlayRegion(at))
1098 auto newClip = std::make_unique<WaveClip>(*clip,
mpFactory,
true);
1100 clip->ClearRight(at);
1101 newClip->ClearLeft(at);
1103 attachRight(clip.get(), split.left.get());
1105 attachLeft(newClip.get(), split.right.get());
1111 attachLeft(clip.get(), split.right.get());
1116 attachRight(clip.get(), split.left.get());
1123 for (
const auto& split : splits)
1128 if (split.rightClipName.has_value() && clip->GetPlayStartSample() == s)
1129 clip->SetName(*split.rightClipName);
1130 else if (split.leftClipName.has_value() && clip->GetPlayEndSample() == s)
1131 clip->SetName(*split.leftClipName);
1136 for (
const auto &clip :
mClips) {
1140 st = clip->GetPlayStartTime();
1141 et = clip->GetPlayEndTime();
1144 for (
auto it = cuts.begin(); it != cuts.end();) {
1151 if (cs >= st && cs <= et) {
1153 clip->GetCutLines().push_back( std::move(*it) );
1154 it = cuts.erase(it);
1167 bool addCutLines =
false;
1174 WaveClipHolders::const_iterator
1179 auto it = list.begin();
1180 for (
const auto end = list.end(); it !=
end; ++it)
1182 if (it->get() == clip)
1190 WaveClipHolders::iterator
1195 auto it = list.begin();
1196 for (
const auto end = list.end(); it !=
end; ++it)
1198 if (it->get() == clip)
1211 if (it !=
mClips.end()) {
1212 auto result = std::move(*it);
1222 if (clip->GetSequence()->GetFactory() != this->mpFactory)
1234 bool addCutLines,
bool split)
1238 wxASSERT( t1 >= t0 );
1251 for (
const auto &clip :
mClips)
1253 if (!clip->BeforePlayStartTime(t1) && !clip->AfterPlayEndTime(t0) &&
1254 (clip->BeforePlayStartTime(t0) || clip->AfterPlayEndTime(t1)))
1256 addCutLines =
false;
1262 for (
const auto &clip :
mClips)
1264 if (clip->BeforePlayStartTime(t0) && clip->AfterPlayEndTime(t1))
1267 clipsToDelete.push_back(clip.get());
1269 else if (!clip->BeforePlayStartTime(t1) && !clip->AfterPlayEndTime(t0))
1276 clipsToDelete.push_back( clip.get() );
1277 auto newClip = std::make_unique<WaveClip>( *clip,
mpFactory,
true );
1278 newClip->ClearAndAddCutLine( t0, t1 );
1279 clipsToAdd.push_back( std::move( newClip ) );
1286 if (clip->BeforePlayStartTime(t0)) {
1291 clipsToDelete.push_back( clip.get() );
1292 auto newClip = std::make_unique<WaveClip>( *clip,
mpFactory,
true );
1293 newClip->TrimLeft(t1 - clip->GetPlayStartTime());
1294 clipsToAdd.push_back( std::move( newClip ) );
1296 else if (clip->AfterPlayEndTime(t1)) {
1301 clipsToDelete.push_back( clip.get() );
1302 auto newClip = std::make_unique<WaveClip>( *clip,
mpFactory,
true );
1303 newClip->TrimRight(clip->GetPlayEndTime() - t0);
1305 clipsToAdd.push_back( std::move( newClip ) );
1311 auto leftClip = std::make_unique<WaveClip>(*clip,
mpFactory,
true);
1312 leftClip->TrimRight(clip->GetPlayEndTime() - t0);
1313 clipsToAdd.push_back(std::move(leftClip));
1315 auto rightClip = std::make_unique<WaveClip>(*clip,
mpFactory,
true);
1316 rightClip->TrimLeft(t1 - rightClip->GetPlayStartTime());
1317 clipsToAdd.push_back(std::move(rightClip));
1319 clipsToDelete.push_back(clip.get());
1327 clipsToDelete.push_back( clip.get() );
1328 auto newClip = std::make_unique<WaveClip>( *clip,
mpFactory,
true );
1331 newClip->Clear(t0,t1);
1333 clipsToAdd.push_back( std::move( newClip ) );
1342 if (!split && editClipCanMove)
1346 for (
const auto& clip :
mClips)
1348 if (clip->BeforePlayStartTime(t1))
1349 clip->Offset(-(t1 - t0));
1353 for (
const auto &clip: clipsToDelete)
1356 if (myIt !=
mClips.end())
1362 for (
auto &clip: clipsToAdd)
1363 mClips.push_back(std::move(clip));
1368 if (newT1 > oldT1) {
1383 Paste(newT1, tmp.get());
1390 auto tmp = std::make_shared<WaveTrack>(
1393 tmp->InsertSilence(0.0, newT1 - oldT1);
1395 Paste(oldT1, tmp.get());
1398 else if (newT1 < oldT1) {
1399 Clear(newT1, oldT1);
1431 bool singleClipMode = (other->
GetNumClips() == 1 &&
1434 const double insertDuration = other->
GetEndTime();
1435 if (insertDuration != 0 && insertDuration < 1.0 /
mRate)
1444 auto pastingFromTempTrack = !other->
GetOwner();
1448 if (editClipCanMove) {
1449 if (!singleClipMode) {
1454 Paste(t0 + insertDuration, tmp.get());
1460 for (
const auto& clip :
mClips)
1462 if (clip->GetPlayStartTime() > t0 - (1.0 /
mRate))
1463 clip->Offset(insertDuration);
1475 for (
const auto& clip :
mClips)
1477 if (editClipCanMove)
1479 if (clip->WithinPlayRegion(t0))
1483 insideClip = clip.get();
1490 if (clip->WithinPlayRegion(t0) ||
1493 insideClip = clip.get();
1503 if (!editClipCanMove)
1507 for (
const auto& clip :
mClips)
1511 clip->GetPlayStartTime())
1516 XO(
"There is not enough room available to paste the selection"),
1518 "Error:_Insufficient_space_in_track"
1531 if (!editClipCanMove && !
IsEmpty(t0, t0 + insertDuration - 1.0 /
mRate))
1536 XO(
"There is not enough room available to paste the selection"),
1538 "Error:_Insufficient_space_in_track"
1541 for (
const auto& clip : other->
mClips)
1544 if (!clip->GetIsPlaceholder())
1547 std::make_unique<WaveClip>(*clip,
mpFactory,
true);
1548 newClip->Resample(
mRate);
1549 newClip->Offset(t0);
1550 newClip->MarkChanged();
1551 if (pastingFromTempTrack)
1557 mClips.push_back(std::move(newClip));
1565 if (
auto other =
dynamic_cast<const WaveTrack*
>(src))
1580 for (
const auto &clip :
mClips)
1582 auto clipStart = clip->GetPlayStartSample();
1583 auto clipEnd = clip->GetPlayEndSample();
1585 if (clipEnd > start && clipStart <
end)
1587 auto offset = std::max(start - clipStart,
sampleCount(0));
1589 auto length =
std::min(
end, clipEnd) - (clipStart + offset);
1591 clip->SetSilence(offset, length);
1610 clip->InsertSilence(0, len);
1612 mClips.push_back( std::move( clip ) );
1618 const auto it = std::find_if(
mClips.begin(),
end,
1619 [&](
const WaveClipHolder &clip) { return clip->WithinPlayRegion(t); } );
1623 it->get()->InsertSilence(t, len);
1626 for (
const auto &clip :
mClips)
1628 if (clip->BeforePlayStartTime(t))
1640 const size_t maxAtOnce = 1048576;
1641 Floats buffer{ maxAtOnce };
1644 for (
const auto &clip :
mClips)
1646 double startTime = clip->GetPlayStartTime();
1647 double endTime = clip->GetPlayEndTime();
1649 if( endTime < t0 || startTime > t1 )
1656 auto start = clip->TimeToSamples(std::max(.0, t0 - startTime));
1657 auto end = clip->TimeToSamples(
std::min(endTime, t1) - startTime);
1659 auto len = (
end - start );
1660 for(
decltype(len) done = 0; done < len; done += maxAtOnce )
1666 for(
decltype(numSamples) i = 0; i < numSamples; i++ )
1668 auto curSamplePos = start + done + i;
1671 if( buffer[ i ] == 0.0 && seqStart == -1 )
1672 seqStart = curSamplePos;
1673 else if( buffer[ i ] != 0.0 || curSamplePos ==
end - 1 )
1675 if( seqStart != -1 )
1677 decltype(
end) seqEnd;
1680 if( curSamplePos ==
end - 1 && buffer[ i ] == 0.0 )
1683 seqEnd = curSamplePos;
1684 if( seqEnd - seqStart + 1 > minSamples )
1688 startTime + clip->SamplesToTime(seqStart),
1689 startTime + clip->SamplesToTime(seqEnd)
1700 for(
unsigned int i = 0; i < regions.size(); i++ )
1702 const Region ®ion = regions.at(i);
1715 for (
const auto &clip:
mClips)
1717 if (clip->GetPlayStartTime() < t1-(1.0/
mRate) &&
1718 clip->GetPlayEndTime()-(1.0/
mRate) > t0) {
1721 auto it = clipsToDelete.begin(),
end = clipsToDelete.end();
1722 for (; it !=
end; ++it)
1723 if ((*it)->GetPlayStartTime() > clip->GetPlayStartTime())
1726 clipsToDelete.insert(it, clip.get());
1731 if( clipsToDelete.size() == 0 )
1734 auto t = clipsToDelete[0]->GetPlayStartTime();
1736 newClip =
CreateClip(clipsToDelete[0]->GetSequenceStartTime(),
1739 for (
const auto &clip : clipsToDelete)
1744 if (clip->GetPlayStartTime() - t > (1.0 /
mRate)) {
1745 double addedSilence = (clip->GetPlayStartTime() - t);
1747 auto offset = clip->GetPlayStartTime();
1748 auto value = clip->GetEnvelope()->GetValue( offset );
1749 newClip->AppendSilence( addedSilence, value );
1754 newClip->Paste(t, clip);
1756 t = newClip->GetPlayEndTime();
1767 size_t len,
unsigned int stride )
1774 for (
const auto &clip :
mClips)
1776 const auto startSample = clip->GetPlayStartSample();
1777 const auto endSample = clip->GetPlayEndSample();
1778 if (s >= startSample && s < endSample)
1780 auto blockStartOffset = clip->GetSequence()->GetBlockStart(clip->ToSequenceSamples(s));
1781 return std::max(startSample, clip->GetSequenceStartSample() + blockStartOffset);
1792 for (
const auto &clip :
mClips)
1794 auto startSample = clip->GetPlayStartSample();
1795 auto endSample = clip->GetPlayEndSample();
1796 if (s >= startSample && s < endSample)
1798 bestBlockSize = clip->GetSequence()->GetBestBlockSize(s - clip->GetSequenceStartSample());
1803 return bestBlockSize;
1809 for (
const auto &clip :
mClips)
1811 maxblocksize = std::max(maxblocksize, clip->GetSequence()->GetMaxBlockSize());
1814 if (maxblocksize == 0)
1821 wxASSERT(maxblocksize > 0);
1823 return maxblocksize;
1851 if (tag ==
"wavetrack") {
1855 for (
auto pair : attrs)
1857 auto attr = pair.first;
1858 auto value = pair.second;
1863 if (!value.TryGet(dblValue) ||
1864 (dblValue < 1.0) || (dblValue > 1000000.0))
1869 else if (attr ==
"offset" && value.TryGet(dblValue))
1880 else if (attr ==
"gain" && value.TryGet(dblValue))
1882 else if (attr ==
"pan" && value.TryGet(dblValue) &&
1883 (dblValue >= -1.0) && (dblValue <= 1.0))
1885 else if (attr ==
"channel")
1887 if (!value.TryGet(nValue) ||
1892 else if (attr ==
"linked" && value.TryGet(nValue))
1894 else if (attr ==
"colorindex" && value.TryGet(nValue))
1897 else if (attr ==
"sampleformat" && value.TryGet(nValue) &&
1917 .CallObjectAccessor(tag, *
this) )
1923 if (tag ==
"sequence" || tag ==
"envelope")
1929 if (tag ==
"sequence")
1931 else if (tag ==
"envelope")
1937 if (tag ==
"waveblock")
1948 if (tag ==
"waveclip")
1957 xmlFile.StartTag(wxT(
"wavetrack"));
1959 xmlFile.WriteAttr(wxT(
"channel"), mChannel);
1960 xmlFile.WriteAttr(wxT(
"linked"),
static_cast<int>(GetLinkType()));
1962 xmlFile.WriteAttr(wxT(
"rate"), mRate);
1963 xmlFile.WriteAttr(wxT(
"gain"),
static_cast<double>(GetGain()));
1964 xmlFile.WriteAttr(wxT(
"pan"),
static_cast<double>(GetPan()));
1965 xmlFile.WriteAttr(wxT(
"colorindex"), mWaveColorIndex );
1966 xmlFile.WriteAttr(wxT(
"sampleformat"),
static_cast<long>(mFormat) );
1970 for (
const auto &clip : mClips)
1972 clip->WriteXML(xmlFile);
1975 xmlFile.EndTag(wxT(
"wavetrack"));
1980 for (
const auto &clip :
mClips)
1981 if (clip->GetSequence()->GetErrorOpening())
1989 for (
const auto &clip :
mClips)
2003 for (
const auto &clip :
mClips)
2007 best = clip->GetPlayStartTime();
2009 else if (clip->GetPlayStartTime() < best)
2010 best = clip->GetPlayStartTime();
2023 for (
const auto &clip :
mClips)
2027 best = clip->GetPlayEndTime();
2029 else if (clip->GetPlayEndTime() > best)
2030 best = clip->GetPlayEndTime();
2041 double t0,
double t1,
bool mayThrow)
const
2043 std::pair<float, float> results {
2047 bool clipFound =
false;
2058 for (
const auto &clip:
mClips)
2060 if (t1 >= clip->GetPlayStartTime() && t0 <= clip->GetPlayEndTime())
2063 auto clipResults = clip->GetMinMax(t0, t1, mayThrow);
2064 if (clipResults.first < results.first)
2065 results.first = clipResults.first;
2066 if (clipResults.second > results.second)
2067 results.second = clipResults.second;
2073 results = { 0.f, 0.f };
2093 for (
const auto &clip:
mClips)
2098 if (t1 >= clip->GetPlayStartTime() && t0 <= clip->GetPlayEndTime())
2100 auto clipStart = clip->TimeToSequenceSamples(wxMax(t0, clip->GetPlayStartTime()));
2101 auto clipEnd = clip->TimeToSequenceSamples(wxMin(t1, clip->GetPlayEndTime()));
2103 float cliprms = clip->GetRMS(t0, t1, mayThrow);
2105 sumsq += cliprms * cliprms * (clipEnd - clipStart).as_float();
2106 length += (clipEnd - clipStart);
2109 return length > 0 ? sqrt(sumsq / length.
as_double()) : 0.0;
2114 bool mayThrow,
sampleCount * pNumWithinClips)
const
2119 bool doClear =
true;
2122 for (
const auto &clip:
mClips)
2124 if (start >= clip->GetPlayStartSample() && start+len <= clip->GetPlayEndSample())
2139 float * pBuffer = (
float*)buffer;
2140 for(
size_t i=0;i<len;i++)
2145 wxFAIL_MSG(wxT(
"Invalid fill format"));
2150 for (
const auto &clip:
mClips)
2152 auto clipStart = clip->GetPlayStartSample();
2153 auto clipEnd = clip->GetPlayEndSample();
2155 if (clipEnd > start && clipStart < start+len)
2158 auto samplesToCopy =
2159 std::min( start+len - clipStart, clip->GetPlaySamplesCount() );
2160 auto startDelta = clipStart - start;
2161 decltype(startDelta) inclipDelta = 0;
2164 inclipDelta = -startDelta;
2165 samplesToCopy -= inclipDelta;
2179 if (!clip->GetSamples(
2181 startDelta.as_size_t() *
2183 format, inclipDelta, samplesToCopy.as_size_t(), mayThrow ))
2186 samplesCopied += samplesToCopy;
2189 if( pNumWithinClips )
2190 *pNumWithinClips = samplesCopied;
2198 for (
const auto &clip:
mClips)
2200 auto clipStart = clip->GetPlayStartSample();
2201 auto clipEnd = clip->GetPlayEndSample();
2203 if (clipEnd > start && clipStart < start+len)
2206 auto samplesToCopy =
2207 std::min( start+len - clipStart, clip->GetPlaySamplesCount() );
2208 auto startDelta = clipStart - start;
2209 decltype(startDelta) inclipDelta = 0;
2212 inclipDelta = -startDelta;
2213 samplesToCopy -= inclipDelta;
2229 startDelta.as_size_t() *
2231 format, inclipDelta, samplesToCopy.as_size_t() );
2232 clip->MarkChanged();
2250 for (
decltype(bufferLen) i = 0; i < bufferLen; i++)
2255 double startTime = t0;
2256 auto tstep = 1.0 /
mRate;
2257 double endTime = t0 + tstep * bufferLen;
2258 for (
const auto &clip:
mClips)
2261 auto dClipStartTime = clip->GetPlayStartTime();
2262 auto dClipEndTime = clip->GetPlayEndTime();
2263 if ((dClipStartTime < endTime) && (dClipEndTime > startTime))
2266 auto rlen = bufferLen;
2269 if (rt0 < dClipStartTime)
2274 auto snDiff = nDiff.as_size_t();
2276 wxASSERT(snDiff <= rlen);
2278 rt0 = dClipStartTime;
2281 if (rt0 + rlen*tstep > dClipEndTime)
2283 auto nClipLen = clip->GetPlayEndSample() - clip->GetPlayStartSample();
2294 rlen =
std::min(rlen,
size_t(floor(0.5 + (dClipEndTime - rt0) / tstep)));
2298 clip->GetEnvelope()->GetValues(rbuf, rlen, rt0, tstep);
2305 for (
const auto &clip:
mClips)
2308 auto len = clip->GetPlaySamplesCount();
2310 if (sample >= start && sample < start + len)
2323 auto p = std::find_if(clips.rbegin(), clips.rend(), [&] (
WaveClip*
const& clip) {
2324 return time >= clip->GetPlayStartTime() && time <= clip->GetPlayEndTime(); });
2331 if (p != clips.rend() && p != clips.rbegin() &&
2332 time == (*p)->GetPlayEndTime() &&
2333 (*p)->SharesBoundaryWithNextClip(*(p-1))) {
2337 return p != clips.rend() ? *p :
nullptr;
2361 clip->SetName(
name);
2362 clip->SetSequenceStartTime(offset);
2363 mClips.push_back(std::move(clip));
2365 return mClips.back().get();
2374 return mClips.back().get();
2385 auto it =
mClips.begin();
2386 WaveClip *rightmost = (*it++).get();
2392 if (maxOffset < offset)
2393 maxOffset = offset, rightmost = clip;
2408 if(index < (
int)
mClips.size())
2409 return mClips[index].get();
2425 const std::vector<WaveClip*> &clips,
2427 double *allowedAmount )
2430 *allowedAmount = amount;
2432 const auto &moving = [&](
WaveClip *clip){
2435 return clips.end() != std::find( clips.begin(), clips.end(), clip );
2438 for (
const auto &c:
mClips) {
2439 if ( moving( c.get() ) )
2441 for (
const auto clip : clips) {
2442 if (c->GetPlayStartTime() < clip->GetPlayEndTime() + amount &&
2443 c->GetPlayEndTime() > clip->GetPlayStartTime() + amount)
2450 if (c->GetPlayStartTime() - clip->GetPlayEndTime() < *allowedAmount)
2451 *allowedAmount = c->GetPlayStartTime() - clip->GetPlayEndTime();
2452 if (*allowedAmount < 0)
2456 if (c->GetPlayEndTime() - clip->GetPlayStartTime() > *allowedAmount)
2457 *allowedAmount = c->GetPlayEndTime() - clip->GetPlayStartTime();
2458 if (*allowedAmount > 0)
2467 if (*allowedAmount == amount)
2483 WaveClip* clip,
double &slideBy,
double &tolerance)
const
2485 for (
const auto &c :
mClips)
2487 double d1 = c->GetPlayStartTime() - (clip->
GetPlayEndTime()+slideBy);
2489 if ( (d1<0) && (d2<0) )
2497 if( -d1 < tolerance ){
2502 }
else if( -d2 < tolerance ){
2527 for (
const auto &c :
mClips)
2529 if (c->WithinPlayRegion(t))
2532 auto newClip = std::make_unique<WaveClip>( *c,
mpFactory,
true );
2534 newClip->TrimLeftTo(t);
2538 mClips.push_back(std::move(newClip));
2554 for (
const auto clip : clips)
2575 const WaveClip *previousClip =
nullptr;
2576 for (
const auto clip: clips)
2578 for (
const auto &cc : clip->GetCutLines())
2581 if (clip->WithinPlayRegion(cutlinePosition))
2596 if (fabs(previousClip->
GetPlayEndTime() - clip->GetPlayStartTime())
2610 previousClip = clip;
2613 wxASSERT(curpos == num);
2624 double start = 0,
end = 0;
2625 auto pEnd =
mClips.end();
2626 auto pClip = std::find_if(
mClips.begin(), pEnd,
2628 return clip->FindCutLine(cutLinePosition, &start, &end); } );
2631 auto &clip = *pClip;
2632 if (!editClipCanMove)
2636 for (
const auto &clip2:
mClips)
2638 if (clip2->GetPlayStartTime() > clip->GetPlayStartTime() &&
2639 clip->GetPlayEndTime() +
end - start > clip2->GetPlayStartTime())
2643 XO(
"There is not enough room available to expand the cut line"),
2645 "Error:_Insufficient_space_in_track"
2650 clip->ExpandCutLine(cutLinePosition);
2655 *cutlineStart = start;
2660 if (editClipCanMove)
2662 for (
const auto &clip2 :
mClips)
2664 if (clip2->GetPlayStartTime() > clip->GetPlayStartTime())
2665 clip2->Offset(
end - start);
2673 for (
const auto &clip :
mClips)
2674 if (clip->RemoveCutLine(cutLinePosition))
2686 if (!clip1 || !clip2)
2703 for (
const auto &clip :
mClips)
2704 clip->Resample(rate, progress);
2710 template <
typename Cont1,
typename Cont2 >
2714 for (
const auto &clip : mClips)
2715 clips.push_back(clip.get());
2716 std::sort(clips.begin(), clips.end(),
2718 { return a->GetPlayStartTime() < b->GetPlayStartTime(); });
2725 return FillSortedClipArray<WaveClipPointers>(
mClips);
2730 return FillSortedClipArray<WaveClipConstPointers>(
mClips);
2737 if ( !mStack.empty() ) {
2738 auto &pair = mStack.back();
2739 if ( ++pair.first == pair.second ) {
2743 push( (*pair.first)->GetCutLines() );
2751 auto pClips = &clips;
2752 while (!pClips->empty()) {
2753 auto first = pClips->begin();
2754 mStack.push_back(
Pair( first, pClips->end() ) );
2755 pClips = &(*first)->GetCutLines();
2765 for(
const auto &clip : wt->GetAllClips()) {
2767 auto blocks = clip->GetSequenceBlockArray();
2768 for (
const auto &block : *blocks) {
2769 auto &pBlock = block.sb;
2771 if ( pIDs && !pIDs->insert(pBlock->GetBlockID()).second )
2785 const_cast<TrackList &
>(tracks), std::move( inspector ), pIDs );
2791 return std::make_shared< WaveTrackFactory >(
2813 project.AttachedObjects::Assign(
key2, result );
2819 project.AttachedObjects::Assign(
key2,
nullptr );
2829 for (
const auto& clip : wt->GetAllClips())
2831 if (clip->GetTrimLeft() > 0.0 || clip->GetTrimRight() > 0.0)
2832 return { 3, 1, 0, 0 };
2841 L
"/GUI/TrackNames/DefaultTrackName",
2853 gPrefs->Read(wxT(
"/GUI/SyncLockTracks"), &mIsSyncLocked,
false);
2856 bool editClipsCanMove;
2861 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)
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.
sampleCount GetPlayStartSample() const
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
bool Append(constSamplePtr buffer, sampleFormat format, size_t len, unsigned int stride)
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
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
void SetOldChannelGain(int channel, float gain) override
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
void Set(constSamplePtr buffer, sampleFormat format, sampleCount start, size_t len)
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.
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)
Holder EmptyCopy(const SampleBlockFactoryPtr &pFactory={}) const
void Disjoin(double t0, double t1)
bool Append(constSamplePtr buffer, sampleFormat format, size_t len, unsigned int stride=1) override
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
WaveClip * GetClipAtSample(sampleCount sample)
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.
float mOldGain[2]
A memo used by PortAudio thread, doesn't need atomics:
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 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
void GetSpectrumBounds(float *min, float *max) const
void PasteWaveTrack(double t0, const WaveTrack *other)
SpectrogramSettings & GetIndependentSpectrogramSettings()
float GetOldChannelGain(int channel) const override
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, if Traits<Type>::iterated_type is defined.
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)
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