mirror of
https://github.com/cookiengineer/audacity
synced 2025-09-24 16:01:16 +02:00
Use TypeSwitch and track_cast
This commit is contained in:
parent
fd10ed26cd
commit
51842fc78b
@ -2549,42 +2549,47 @@ Track::Holder LabelTrack::Copy(double t0, double t1, bool) const
|
||||
|
||||
bool LabelTrack::PasteOver(double t, const Track * src)
|
||||
{
|
||||
if (src->GetKind() != Track::Label)
|
||||
auto result = src->TypeSwitch< bool >( [&](const LabelTrack *sl) {
|
||||
int len = mLabels.size();
|
||||
int pos = 0;
|
||||
|
||||
while (pos < len && mLabels[pos].getT0() < t)
|
||||
pos++;
|
||||
|
||||
for (auto &labelStruct: sl->mLabels) {
|
||||
LabelStruct l {
|
||||
labelStruct.selectedRegion,
|
||||
labelStruct.getT0() + t,
|
||||
labelStruct.getT1() + t,
|
||||
labelStruct.title
|
||||
};
|
||||
mLabels.insert(mLabels.begin() + pos++, l);
|
||||
}
|
||||
|
||||
return true;
|
||||
} );
|
||||
|
||||
if (! result )
|
||||
// THROW_INCONSISTENCY_EXCEPTION; // ?
|
||||
return false;
|
||||
;
|
||||
|
||||
int len = mLabels.size();
|
||||
int pos = 0;
|
||||
|
||||
while (pos < len && mLabels[pos].getT0() < t)
|
||||
pos++;
|
||||
|
||||
auto sl = static_cast<const LabelTrack *>(src);
|
||||
for (auto &labelStruct: sl->mLabels) {
|
||||
LabelStruct l {
|
||||
labelStruct.selectedRegion,
|
||||
labelStruct.getT0() + t,
|
||||
labelStruct.getT1() + t,
|
||||
labelStruct.title
|
||||
};
|
||||
mLabels.insert(mLabels.begin() + pos++, l);
|
||||
}
|
||||
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
||||
void LabelTrack::Paste(double t, const Track *src)
|
||||
{
|
||||
if (src->GetKind() != Track::Label)
|
||||
bool bOk = src->TypeSwitch< bool >( [&](const LabelTrack *lt) {
|
||||
double shiftAmt = lt->mClipLen > 0.0 ? lt->mClipLen : lt->GetEndTime();
|
||||
|
||||
ShiftLabelsOnInsert(shiftAmt, t);
|
||||
PasteOver(t, src);
|
||||
|
||||
return true;
|
||||
} );
|
||||
|
||||
if ( !bOk )
|
||||
// THROW_INCONSISTENCY_EXCEPTION; // ?
|
||||
return;
|
||||
|
||||
LabelTrack *lt = (LabelTrack *)src;
|
||||
|
||||
double shiftAmt = lt->mClipLen > 0.0 ? lt->mClipLen : lt->GetEndTime();
|
||||
|
||||
ShiftLabelsOnInsert(shiftAmt, t);
|
||||
PasteOver(t, src);
|
||||
;
|
||||
}
|
||||
|
||||
// This repeats the labels in a time interval a specified number of times.
|
||||
|
167
src/Menus.cpp
167
src/Menus.cpp
@ -3874,10 +3874,9 @@ double MenuCommandHandler::OnClipMove
|
||||
auto &selectedRegion = viewInfo.selectedRegion;
|
||||
|
||||
// just dealing with clips in wave tracks for the moment. Note tracks??
|
||||
if (track && track->GetKind() == Track::Wave) {
|
||||
if (track) return track->TypeSwitch<double>( [&]( WaveTrack *wt ) {
|
||||
ClipMoveState state;
|
||||
|
||||
auto wt = static_cast<WaveTrack*>(track);
|
||||
auto t0 = selectedRegion.t0();
|
||||
|
||||
state.capturedClip = wt->GetClipAtTime( t0 );
|
||||
@ -3924,7 +3923,7 @@ double MenuCommandHandler::OnClipMove
|
||||
selectedRegion.setTimes(newT0, newT0 + diff);
|
||||
|
||||
return state.hSlideAmount;
|
||||
}
|
||||
} );
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
@ -4268,15 +4267,11 @@ void MenuCommandHandler::OnTrackPan(const CommandContext &context)
|
||||
auto trackPanel = project.GetTrackPanel();
|
||||
|
||||
Track *const track = trackPanel->GetFocusedTrack();
|
||||
if (!track || (track->GetKind() != Track::Wave)) {
|
||||
return;
|
||||
}
|
||||
const auto wt = static_cast<WaveTrack*>(track);
|
||||
|
||||
LWSlider *slider = trackPanel->PanSlider(wt);
|
||||
if (slider->ShowDialog()) {
|
||||
project.SetTrackPan(wt, slider);
|
||||
}
|
||||
if (track) track->TypeSwitch( [&](WaveTrack *wt) {
|
||||
LWSlider *slider = trackPanel->PanSlider(wt);
|
||||
if (slider->ShowDialog())
|
||||
project.SetTrackPan(wt, slider);
|
||||
});
|
||||
}
|
||||
|
||||
void MenuCommandHandler::OnTrackPanLeft(const CommandContext &context)
|
||||
@ -4285,14 +4280,11 @@ void MenuCommandHandler::OnTrackPanLeft(const CommandContext &context)
|
||||
auto trackPanel = project.GetTrackPanel();
|
||||
|
||||
Track *const track = trackPanel->GetFocusedTrack();
|
||||
if (!track || (track->GetKind() != Track::Wave)) {
|
||||
return;
|
||||
}
|
||||
const auto wt = static_cast<WaveTrack*>(track);
|
||||
|
||||
LWSlider *slider = trackPanel->PanSlider(wt);
|
||||
slider->Decrease(1);
|
||||
project.SetTrackPan(wt, slider);
|
||||
if (track) track->TypeSwitch( [&](WaveTrack *wt) {
|
||||
LWSlider *slider = trackPanel->PanSlider(wt);
|
||||
slider->Decrease(1);
|
||||
project.SetTrackPan(wt, slider);
|
||||
});
|
||||
}
|
||||
|
||||
void MenuCommandHandler::OnTrackPanRight(const CommandContext &context)
|
||||
@ -4301,14 +4293,11 @@ void MenuCommandHandler::OnTrackPanRight(const CommandContext &context)
|
||||
auto trackPanel = project.GetTrackPanel();
|
||||
|
||||
Track *const track = trackPanel->GetFocusedTrack();
|
||||
if (!track || (track->GetKind() != Track::Wave)) {
|
||||
return;
|
||||
}
|
||||
const auto wt = static_cast<WaveTrack*>(track);
|
||||
|
||||
LWSlider *slider = trackPanel->PanSlider(wt);
|
||||
slider->Increase(1);
|
||||
project.SetTrackPan(wt, slider);
|
||||
if (track) track->TypeSwitch( [&](WaveTrack *wt) {
|
||||
LWSlider *slider = trackPanel->PanSlider(wt);
|
||||
slider->Increase(1);
|
||||
project.SetTrackPan(wt, slider);
|
||||
});
|
||||
}
|
||||
|
||||
void MenuCommandHandler::OnTrackGain(const CommandContext &context)
|
||||
@ -4318,15 +4307,11 @@ void MenuCommandHandler::OnTrackGain(const CommandContext &context)
|
||||
|
||||
/// This will pop up the track gain dialog for specified track
|
||||
Track *const track = trackPanel->GetFocusedTrack();
|
||||
if (!track || (track->GetKind() != Track::Wave)) {
|
||||
return;
|
||||
}
|
||||
const auto wt = static_cast<WaveTrack*>(track);
|
||||
|
||||
LWSlider *slider = trackPanel->GainSlider(wt);
|
||||
if (slider->ShowDialog()) {
|
||||
project.SetTrackGain(wt, slider);
|
||||
}
|
||||
if (track) track->TypeSwitch( [&](WaveTrack *wt) {
|
||||
LWSlider *slider = trackPanel->GainSlider(wt);
|
||||
if (slider->ShowDialog())
|
||||
project.SetTrackGain(wt, slider);
|
||||
});
|
||||
}
|
||||
|
||||
void MenuCommandHandler::OnTrackGainInc(const CommandContext &context)
|
||||
@ -4335,14 +4320,11 @@ void MenuCommandHandler::OnTrackGainInc(const CommandContext &context)
|
||||
auto trackPanel = project.GetTrackPanel();
|
||||
|
||||
Track *const track = trackPanel->GetFocusedTrack();
|
||||
if (!track || (track->GetKind() != Track::Wave)) {
|
||||
return;
|
||||
}
|
||||
const auto wt = static_cast<WaveTrack*>(track);
|
||||
|
||||
LWSlider *slider = trackPanel->GainSlider(wt);
|
||||
slider->Increase(1);
|
||||
project.SetTrackGain(wt, slider);
|
||||
if (track) track->TypeSwitch( [&](WaveTrack *wt) {
|
||||
LWSlider *slider = trackPanel->GainSlider(wt);
|
||||
slider->Increase(1);
|
||||
project.SetTrackGain(wt, slider);
|
||||
});
|
||||
}
|
||||
|
||||
void MenuCommandHandler::OnTrackGainDec(const CommandContext &context)
|
||||
@ -4351,14 +4333,11 @@ void MenuCommandHandler::OnTrackGainDec(const CommandContext &context)
|
||||
auto trackPanel = project.GetTrackPanel();
|
||||
|
||||
Track *const track = trackPanel->GetFocusedTrack();
|
||||
if (!track || (track->GetKind() != Track::Wave)) {
|
||||
return;
|
||||
}
|
||||
const auto wt = static_cast<WaveTrack*>(track);
|
||||
|
||||
LWSlider *slider = trackPanel->GainSlider(wt);
|
||||
slider->Decrease(1);
|
||||
project.SetTrackGain(wt, slider);
|
||||
if (track) track->TypeSwitch( [&](WaveTrack *wt) {
|
||||
LWSlider *slider = trackPanel->GainSlider(wt);
|
||||
slider->Decrease(1);
|
||||
project.SetTrackGain(wt, slider);
|
||||
});
|
||||
}
|
||||
|
||||
void MenuCommandHandler::OnTrackMenu(const CommandContext &context)
|
||||
@ -4374,13 +4353,10 @@ void MenuCommandHandler::OnTrackMute(const CommandContext &context)
|
||||
auto &project = context.project;
|
||||
auto trackPanel = project.GetTrackPanel();
|
||||
|
||||
Track *t = NULL;
|
||||
if (!t) {
|
||||
t = trackPanel->GetFocusedTrack();
|
||||
if (!dynamic_cast<PlayableTrack*>(t))
|
||||
return;
|
||||
}
|
||||
project.DoTrackMute(t, false);
|
||||
const auto track = trackPanel->GetFocusedTrack();
|
||||
if (track) track->TypeSwitch( [&](PlayableTrack *t) {
|
||||
project.DoTrackMute(t, false);
|
||||
});
|
||||
}
|
||||
|
||||
void MenuCommandHandler::OnTrackSolo(const CommandContext &context)
|
||||
@ -4388,14 +4364,10 @@ void MenuCommandHandler::OnTrackSolo(const CommandContext &context)
|
||||
auto &project = context.project;
|
||||
auto trackPanel = project.GetTrackPanel();
|
||||
|
||||
Track *t = NULL;
|
||||
if (!t)
|
||||
{
|
||||
t = trackPanel->GetFocusedTrack();
|
||||
if (!dynamic_cast<PlayableTrack*>(t))
|
||||
return;
|
||||
}
|
||||
project.DoTrackSolo(t, false);
|
||||
const auto track = trackPanel->GetFocusedTrack();
|
||||
if (track) track->TypeSwitch( [&](PlayableTrack *t) {
|
||||
project.DoTrackSolo(t, false);
|
||||
});
|
||||
}
|
||||
|
||||
void MenuCommandHandler::OnTrackClose(const CommandContext &context)
|
||||
@ -6103,45 +6075,38 @@ bool MenuCommandHandler::HandlePasteNothingSelected(AudacityProject &project)
|
||||
Track* pFirstNewTrack = NULL;
|
||||
while (pClip) {
|
||||
Maybe<WaveTrack::Locker> locker;
|
||||
if ((AudacityProject::msClipProject != &project) && (pClip->GetKind() == Track::Wave))
|
||||
// Cause duplication of block files on disk, when copy is
|
||||
// between projects
|
||||
locker.create(static_cast<const WaveTrack*>(pClip));
|
||||
|
||||
Track::Holder uNewTrack;
|
||||
Track *pNewTrack;
|
||||
switch (pClip->GetKind()) {
|
||||
case Track::Wave:
|
||||
{
|
||||
WaveTrack *w = (WaveTrack *)pClip;
|
||||
uNewTrack = trackFactory->NewWaveTrack(w->GetSampleFormat(), w->GetRate()),
|
||||
pClip->TypeSwitch(
|
||||
[&](const WaveTrack *wc) {
|
||||
if ((AudacityProject::msClipProject != &project))
|
||||
// Cause duplication of block files on disk, when copy is
|
||||
// between projects
|
||||
locker.create(wc);
|
||||
uNewTrack = trackFactory->NewWaveTrack(
|
||||
wc->GetSampleFormat(), wc->GetRate()),
|
||||
pNewTrack = uNewTrack.get();
|
||||
},
|
||||
#ifdef USE_MIDI
|
||||
[&](const NoteTrack *) {
|
||||
uNewTrack = trackFactory->NewNoteTrack(),
|
||||
pNewTrack = uNewTrack.get();
|
||||
},
|
||||
#endif
|
||||
[&](const LabelTrack *) {
|
||||
uNewTrack = trackFactory->NewLabelTrack(),
|
||||
pNewTrack = uNewTrack.get();
|
||||
},
|
||||
[&](const TimeTrack *) {
|
||||
// Maintain uniqueness of the time track!
|
||||
pNewTrack = tracks->GetTimeTrack();
|
||||
if (!pNewTrack)
|
||||
uNewTrack = trackFactory->NewTimeTrack(),
|
||||
pNewTrack = uNewTrack.get();
|
||||
}
|
||||
break;
|
||||
);
|
||||
|
||||
#ifdef USE_MIDI
|
||||
case Track::Note:
|
||||
uNewTrack = trackFactory->NewNoteTrack(),
|
||||
pNewTrack = uNewTrack.get();
|
||||
break;
|
||||
#endif // USE_MIDI
|
||||
|
||||
case Track::Label:
|
||||
uNewTrack = trackFactory->NewLabelTrack(),
|
||||
pNewTrack = uNewTrack.get();
|
||||
break;
|
||||
case Track::Time: {
|
||||
// Maintain uniqueness of the time track!
|
||||
pNewTrack = tracks->GetTimeTrack();
|
||||
if (!pNewTrack)
|
||||
uNewTrack = trackFactory->NewTimeTrack(),
|
||||
pNewTrack = uNewTrack.get();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
pClip = iterClip.Next();
|
||||
continue;
|
||||
}
|
||||
wxASSERT(pClip);
|
||||
|
||||
pNewTrack->Paste(0.0, pClip);
|
||||
|
@ -576,40 +576,43 @@ void NoteTrack::Paste(double t, const Track *src)
|
||||
// the destination track).
|
||||
|
||||
//Check that src is a non-NULL NoteTrack
|
||||
if (src == NULL || src->GetKind() != Track::Note)
|
||||
bool bOk = src && src->TypeSwitch< bool >( [&](const NoteTrack *other) {
|
||||
|
||||
auto myOffset = this->GetOffset();
|
||||
if (t < myOffset) {
|
||||
// workaround strange behavior described at
|
||||
// http://bugzilla.audacityteam.org/show_bug.cgi?id=1735#c3
|
||||
SetOffset(t);
|
||||
InsertSilence(t, myOffset - t);
|
||||
}
|
||||
|
||||
double delta = 0.0;
|
||||
auto &seq = GetSeq();
|
||||
auto offset = other->GetOffset();
|
||||
if ( offset > 0 ) {
|
||||
seq.convert_to_seconds();
|
||||
seq.insert_silence( t - GetOffset(), offset );
|
||||
t += offset;
|
||||
// Is this needed or does Alg_seq::insert_silence take care of it?
|
||||
//delta += offset;
|
||||
}
|
||||
|
||||
// This seems to be needed:
|
||||
delta += std::max( 0.0, t - GetEndTime() );
|
||||
|
||||
// This, not:
|
||||
//delta += other->GetSeq().get_real_dur();
|
||||
|
||||
seq.paste(t - GetOffset(), &other->GetSeq());
|
||||
|
||||
AddToDuration( delta );
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if ( !bOk )
|
||||
// THROW_INCONSISTENCY_EXCEPTION; // ?
|
||||
return;
|
||||
|
||||
auto myOffset = this->GetOffset();
|
||||
if (t < myOffset) {
|
||||
// workaround strange behavior described at
|
||||
// http://bugzilla.audacityteam.org/show_bug.cgi?id=1735#c3
|
||||
SetOffset(t);
|
||||
InsertSilence(t, myOffset - t);
|
||||
}
|
||||
|
||||
NoteTrack* other = (NoteTrack*)src;
|
||||
|
||||
double delta = 0.0;
|
||||
auto &seq = GetSeq();
|
||||
auto offset = other->GetOffset();
|
||||
if ( offset > 0 ) {
|
||||
seq.convert_to_seconds();
|
||||
seq.insert_silence( t - GetOffset(), offset );
|
||||
t += offset;
|
||||
// Is this needed or does Alg_seq::insert_silence take care of it?
|
||||
//delta += offset;
|
||||
}
|
||||
|
||||
// This seems to be needed:
|
||||
delta += std::max( 0.0, t - GetEndTime() );
|
||||
|
||||
// This, not:
|
||||
//delta += other->GetSeq().get_real_dur();
|
||||
|
||||
seq.paste(t - GetOffset(), &other->GetSeq());
|
||||
|
||||
AddToDuration( delta );
|
||||
;
|
||||
}
|
||||
|
||||
void NoteTrack::Silence(double t0, double t1)
|
||||
|
@ -4317,9 +4317,6 @@ AudacityProject::AddImportedTracks(const wxString &fileName,
|
||||
|
||||
auto newTrack = mTracks->Add(std::move(uNewTrack));
|
||||
results.push_back(Track::Pointer(newTrack));
|
||||
if (newRate == 0 && newTrack->GetKind() == Track::Wave) {
|
||||
newRate = ((WaveTrack *)newTrack)->GetRate();
|
||||
}
|
||||
newTrack->SetSelected(true);
|
||||
//we need to check link status based on the first channel only.
|
||||
if(0==i)
|
||||
@ -4331,11 +4328,13 @@ AudacityProject::AddImportedTracks(const wxString &fileName,
|
||||
newTrack->SetName(trackNameBase);
|
||||
}
|
||||
|
||||
// Check if NEW track contains aliased blockfiles and if yes,
|
||||
// remember this to show a warning later
|
||||
if (newTrack->GetKind() == WaveTrack::Wave)
|
||||
{
|
||||
if (WaveClip* clip = ((WaveTrack*)newTrack)->GetClipByIndex(0)) {
|
||||
newTrack->TypeSwitch( [&](WaveTrack *wt) {
|
||||
if (newRate == 0)
|
||||
newRate = wt->GetRate();
|
||||
|
||||
// Check if NEW track contains aliased blockfiles and if yes,
|
||||
// remember this to show a warning later
|
||||
if(WaveClip* clip = wt->GetClipByIndex(0)) {
|
||||
BlockArray &blocks = clip->GetSequence()->GetBlockArray();
|
||||
if (blocks.size())
|
||||
{
|
||||
@ -4346,7 +4345,7 @@ AudacityProject::AddImportedTracks(const wxString &fileName,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Automatically assign rate of imported file to whole project,
|
||||
@ -4442,9 +4441,9 @@ bool AudacityProject::Import(const wxString &fileName, WaveTrackArray* pTrackArr
|
||||
|
||||
if (pTrackArray) {
|
||||
for (const auto &newTrack : newSharedTracks) {
|
||||
if (newTrack->GetKind() == Track::Wave)
|
||||
pTrackArray->push_back(
|
||||
std::static_pointer_cast<WaveTrack>(newTrack));
|
||||
newTrack->TypeSwitch( [&](WaveTrack *wt) {
|
||||
pTrackArray->push_back( Track::Pointer< WaveTrack >( wt ) );
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -4799,8 +4798,7 @@ void AudacityProject::PopState(const UndoState &state)
|
||||
auto copyTrack = mTracks->Add(t->Duplicate());
|
||||
|
||||
//add the track to OD if the manager exists. later we might do a more rigorous check...
|
||||
if (copyTrack->GetKind() == Track::Wave)
|
||||
{
|
||||
copyTrack->TypeSwitch( [&](WaveTrack *wt) {
|
||||
//if the ODManager hasn't been initialized, there's no chance this track has OD blocks since this
|
||||
//is a "Redo" operation.
|
||||
//TODO: update this to look like the update loop in OpenFile that handles general purpose ODTasks.
|
||||
@ -4818,9 +4816,10 @@ void AudacityProject::PopState(const UndoState &state)
|
||||
// PRL: Is it correct to add all tracks to one task, even if they
|
||||
// are not partnered channels? Rather than
|
||||
// make one task for each?
|
||||
computeTask->AddWaveTrack((WaveTrack*)copyTrack);
|
||||
computeTask->AddWaveTrack(wt);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
t = iter.Next();
|
||||
}
|
||||
|
||||
|
@ -119,13 +119,16 @@ void TimeTrack::Clear(double t0, double t1)
|
||||
|
||||
void TimeTrack::Paste(double t, const Track * src)
|
||||
{
|
||||
if (src->GetKind() != Track::Time)
|
||||
// THROW_INCONSISTENCY_EXCEPTION; // ?
|
||||
return;
|
||||
bool bOk = src && src->TypeSwitch< bool >( [&] (const TimeTrack *tt) {
|
||||
auto sampleTime = 1.0 / GetActiveProject()->GetRate();
|
||||
mEnvelope->PasteEnvelope
|
||||
(t, tt->mEnvelope.get(), sampleTime);
|
||||
return true;
|
||||
} );
|
||||
|
||||
auto sampleTime = 1.0 / GetActiveProject()->GetRate();
|
||||
mEnvelope->PasteEnvelope
|
||||
(t, static_cast<const TimeTrack*>(src)->mEnvelope.get(), sampleTime);
|
||||
if (! bOk )
|
||||
// THROW_INCONSISTENCY_EXCEPTION // ?
|
||||
;
|
||||
}
|
||||
|
||||
void TimeTrack::Silence(double WXUNUSED(t0), double WXUNUSED(t1))
|
||||
|
@ -449,75 +449,69 @@ void TrackArtist::DrawTrack(TrackPanelDrawingContext &context,
|
||||
bool hasSolo)
|
||||
{
|
||||
auto &dc = context.dc;
|
||||
switch (t->GetKind()) {
|
||||
case Track::Wave:
|
||||
{
|
||||
const WaveTrack* wt = static_cast<const WaveTrack*>(t);
|
||||
for (const auto &clip : wt->GetClips()) {
|
||||
clip->ClearDisplayRect();
|
||||
}
|
||||
t->TypeSwitch(
|
||||
[&](const WaveTrack *wt) {
|
||||
for (const auto &clip : wt->GetClips()) {
|
||||
clip->ClearDisplayRect();
|
||||
}
|
||||
|
||||
bool muted = (hasSolo || wt->GetMute()) &&
|
||||
!wt->GetSolo();
|
||||
bool muted = (hasSolo || wt->GetMute()) &&
|
||||
!wt->GetSolo();
|
||||
|
||||
#if defined(__WXMAC__)
|
||||
wxAntialiasMode aamode = dc.GetGraphicsContext()->GetAntialiasMode();
|
||||
dc.GetGraphicsContext()->SetAntialiasMode(wxANTIALIAS_NONE);
|
||||
#endif
|
||||
#if defined(__WXMAC__)
|
||||
wxAntialiasMode aamode = dc.GetGraphicsContext()->GetAntialiasMode();
|
||||
dc.GetGraphicsContext()->SetAntialiasMode(wxANTIALIAS_NONE);
|
||||
#endif
|
||||
|
||||
switch (wt->GetDisplay()) {
|
||||
case WaveTrack::Waveform:
|
||||
DrawWaveform(context, wt, rect, selectedRegion, zoomInfo,
|
||||
drawEnvelope, bigPoints, drawSliders, muted);
|
||||
break;
|
||||
case WaveTrack::Spectrum:
|
||||
DrawSpectrum(wt, dc, rect, selectedRegion, zoomInfo);
|
||||
break;
|
||||
default:
|
||||
wxASSERT(false);
|
||||
}
|
||||
switch (wt->GetDisplay()) {
|
||||
case WaveTrack::Waveform:
|
||||
DrawWaveform(context, wt, rect, selectedRegion, zoomInfo,
|
||||
drawEnvelope, bigPoints, drawSliders, muted);
|
||||
break;
|
||||
case WaveTrack::Spectrum:
|
||||
DrawSpectrum(wt, dc, rect, selectedRegion, zoomInfo);
|
||||
break;
|
||||
default:
|
||||
wxASSERT(false);
|
||||
}
|
||||
|
||||
#if defined(__WXMAC__)
|
||||
dc.GetGraphicsContext()->SetAntialiasMode(aamode);
|
||||
#endif
|
||||
#if defined(__WXMAC__)
|
||||
dc.GetGraphicsContext()->SetAntialiasMode(aamode);
|
||||
#endif
|
||||
|
||||
if (mbShowTrackNameInWaveform &&
|
||||
// Exclude right channel of stereo track
|
||||
!(!wt->GetLinked() && wt->GetLink()) &&
|
||||
// Exclude empty name.
|
||||
!wt->GetName().IsEmpty()) {
|
||||
wxBrush Brush;
|
||||
wxCoord x,y;
|
||||
wxFont labelFont(12, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
|
||||
dc.SetFont(labelFont);
|
||||
dc.GetTextExtent( wt->GetName(), &x, &y );
|
||||
dc.SetTextForeground(theTheme.Colour( clrTrackPanelText ));
|
||||
// A nice improvement would be to draw the shield / background translucently.
|
||||
AColor::UseThemeColour( &dc, clrTrackInfoSelected, clrTrackPanelText );
|
||||
dc.DrawRoundedRectangle( wxPoint( rect.x+7, rect.y+1 ), wxSize( x+16, y+4), 8.0 );
|
||||
dc.DrawText (wt->GetName(), rect.x+15, rect.y+3); // move right 15 pixels to avoid overwriting <- symbol
|
||||
}
|
||||
break; // case Wave
|
||||
}
|
||||
if (mbShowTrackNameInWaveform &&
|
||||
// Exclude right channel of stereo track
|
||||
!(!wt->GetLinked() && wt->GetLink()) &&
|
||||
// Exclude empty name.
|
||||
!wt->GetName().IsEmpty()) {
|
||||
wxBrush Brush;
|
||||
wxCoord x,y;
|
||||
wxFont labelFont(12, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
|
||||
dc.SetFont(labelFont);
|
||||
dc.GetTextExtent( wt->GetName(), &x, &y );
|
||||
dc.SetTextForeground(theTheme.Colour( clrTrackPanelText ));
|
||||
// A nice improvement would be to draw the shield / background translucently.
|
||||
AColor::UseThemeColour( &dc, clrTrackInfoSelected, clrTrackPanelText );
|
||||
dc.DrawRoundedRectangle( wxPoint( rect.x+7, rect.y+1 ), wxSize( x+16, y+4), 8.0 );
|
||||
dc.DrawText (wt->GetName(), rect.x+15, rect.y+3); // move right 15 pixels to avoid overwriting <- symbol
|
||||
}
|
||||
},
|
||||
#ifdef USE_MIDI
|
||||
case Track::Note:
|
||||
{
|
||||
auto nt = static_cast<const NoteTrack *>(t);
|
||||
bool muted = false;
|
||||
[&](const NoteTrack *nt) {
|
||||
bool muted = false;
|
||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||
muted = (hasSolo || nt->GetMute()) && !nt->GetSolo();
|
||||
muted = (hasSolo || nt->GetMute()) && !nt->GetSolo();
|
||||
#endif
|
||||
DrawNoteTrack((NoteTrack *)t, dc, rect, selectedRegion, zoomInfo, muted);
|
||||
break;
|
||||
}
|
||||
DrawNoteTrack(nt, dc, rect, selectedRegion, zoomInfo, muted);
|
||||
},
|
||||
#endif // USE_MIDI
|
||||
case Track::Label:
|
||||
DrawLabelTrack(context, (LabelTrack *)t, rect, selectedRegion, zoomInfo);
|
||||
break;
|
||||
case Track::Time:
|
||||
DrawTimeTrack(context, (TimeTrack *)t, rect, zoomInfo);
|
||||
break;
|
||||
}
|
||||
[&](const LabelTrack *lt) {
|
||||
DrawLabelTrack(context, lt, rect, selectedRegion, zoomInfo);
|
||||
},
|
||||
[&](const TimeTrack *tt) {
|
||||
DrawTimeTrack(context, tt, rect, zoomInfo);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void TrackArtist::DrawVRuler
|
||||
@ -529,431 +523,429 @@ void TrackArtist::DrawVRuler
|
||||
highlight = rect.Contains(context.lastState.GetPosition());
|
||||
#endif
|
||||
|
||||
auto kind = t->GetKind();
|
||||
|
||||
// Label and Time tracks do not have a vruler
|
||||
// But give it a beveled area
|
||||
if (kind == Track::Label) {
|
||||
wxRect bev = rect;
|
||||
bev.Inflate(-1, 0);
|
||||
bev.width += 1;
|
||||
AColor::BevelTrackInfo(*dc, true, bev);
|
||||
t->TypeSwitch(
|
||||
[&](const LabelTrack *) {
|
||||
wxRect bev = rect;
|
||||
bev.Inflate(-1, 0);
|
||||
bev.width += 1;
|
||||
AColor::BevelTrackInfo(*dc, true, bev);
|
||||
},
|
||||
|
||||
return;
|
||||
}
|
||||
[&](const TimeTrack *) {
|
||||
wxRect bev = rect;
|
||||
bev.Inflate(-1, 0);
|
||||
bev.width += 1;
|
||||
AColor::BevelTrackInfo(*dc, true, bev);
|
||||
|
||||
// Time tracks
|
||||
if (kind == Track::Time) {
|
||||
wxRect bev = rect;
|
||||
bev.Inflate(-1, 0);
|
||||
bev.width += 1;
|
||||
AColor::BevelTrackInfo(*dc, true, bev);
|
||||
// Right align the ruler
|
||||
wxRect rr = rect;
|
||||
rr.width--;
|
||||
if (t->vrulerSize.GetWidth() < rect.GetWidth()) {
|
||||
int adj = rr.GetWidth() - t->vrulerSize.GetWidth();
|
||||
rr.x += adj;
|
||||
rr.width -= adj;
|
||||
}
|
||||
|
||||
// Right align the ruler
|
||||
wxRect rr = rect;
|
||||
rr.width--;
|
||||
if (t->vrulerSize.GetWidth() < rect.GetWidth()) {
|
||||
int adj = rr.GetWidth() - t->vrulerSize.GetWidth();
|
||||
rr.x += adj;
|
||||
rr.width -= adj;
|
||||
UpdateVRuler(t, rr);
|
||||
|
||||
vruler->SetTickColour( theTheme.Colour( clrTrackPanelText ));
|
||||
vruler->Draw(*dc);
|
||||
},
|
||||
|
||||
[&](const WaveTrack *) {
|
||||
// All waves have a ruler in the info panel
|
||||
// The ruler needs a bevelled surround.
|
||||
wxRect bev = rect;
|
||||
bev.Inflate(-1, 0);
|
||||
bev.width += 1;
|
||||
AColor::BevelTrackInfo(*dc, true, bev, highlight);
|
||||
|
||||
// Right align the ruler
|
||||
wxRect rr = rect;
|
||||
rr.width--;
|
||||
if (t->vrulerSize.GetWidth() < rect.GetWidth()) {
|
||||
int adj = rr.GetWidth() - t->vrulerSize.GetWidth();
|
||||
rr.x += adj;
|
||||
rr.width -= adj;
|
||||
}
|
||||
|
||||
UpdateVRuler(t, rr);
|
||||
|
||||
vruler->SetTickColour( theTheme.Colour( clrTrackPanelText ));
|
||||
vruler->Draw(*dc);
|
||||
}
|
||||
|
||||
UpdateVRuler(t, rr);
|
||||
vruler->SetTickColour( theTheme.Colour( clrTrackPanelText ));
|
||||
vruler->Draw(*dc);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// All waves have a ruler in the info panel
|
||||
// The ruler needs a bevelled surround.
|
||||
if (kind == Track::Wave) {
|
||||
wxRect bev = rect;
|
||||
bev.Inflate(-1, 0);
|
||||
bev.width += 1;
|
||||
AColor::BevelTrackInfo(*dc, true, bev, highlight);
|
||||
|
||||
// Right align the ruler
|
||||
wxRect rr = rect;
|
||||
rr.width--;
|
||||
if (t->vrulerSize.GetWidth() < rect.GetWidth()) {
|
||||
int adj = rr.GetWidth() - t->vrulerSize.GetWidth();
|
||||
rr.x += adj;
|
||||
rr.width -= adj;
|
||||
}
|
||||
|
||||
UpdateVRuler(t, rr);
|
||||
vruler->SetTickColour( theTheme.Colour( clrTrackPanelText ));
|
||||
vruler->Draw(*dc);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef USE_MIDI
|
||||
// The note track draws a vertical keyboard to label pitches
|
||||
if (kind == Track::Note) {
|
||||
UpdateVRuler(t, rect);
|
||||
,
|
||||
[&](const NoteTrack *track) {
|
||||
// The note track draws a vertical keyboard to label pitches
|
||||
UpdateVRuler(t, rect);
|
||||
|
||||
dc->SetPen(highlight ? AColor::uglyPen : *wxTRANSPARENT_PEN);
|
||||
dc->SetBrush(*wxWHITE_BRUSH);
|
||||
wxRect bev = rect;
|
||||
bev.x++;
|
||||
bev.width--;
|
||||
dc->DrawRectangle(bev);
|
||||
dc->SetPen(highlight ? AColor::uglyPen : *wxTRANSPARENT_PEN);
|
||||
dc->SetBrush(*wxWHITE_BRUSH);
|
||||
wxRect bev = rect;
|
||||
bev.x++;
|
||||
bev.width--;
|
||||
dc->DrawRectangle(bev);
|
||||
|
||||
rect.y += 1;
|
||||
rect.height -= 1;
|
||||
rect.y += 1;
|
||||
rect.height -= 1;
|
||||
|
||||
//int bottom = GetBottom((NoteTrack *) t, rect);
|
||||
const NoteTrack *track = (NoteTrack *) t;
|
||||
track->PrepareIPitchToY(rect);
|
||||
//int bottom = GetBottom(track, rect);
|
||||
track->PrepareIPitchToY(rect);
|
||||
|
||||
wxPen hilitePen;
|
||||
hilitePen.SetColour(120, 120, 120);
|
||||
wxBrush blackKeyBrush;
|
||||
blackKeyBrush.SetColour(70, 70, 70);
|
||||
wxPen hilitePen;
|
||||
hilitePen.SetColour(120, 120, 120);
|
||||
wxBrush blackKeyBrush;
|
||||
blackKeyBrush.SetColour(70, 70, 70);
|
||||
|
||||
dc->SetBrush(blackKeyBrush);
|
||||
dc->SetBrush(blackKeyBrush);
|
||||
|
||||
int fontSize = 10;
|
||||
#ifdef __WXMSW__
|
||||
fontSize = 8;
|
||||
#endif
|
||||
int fontSize = 10;
|
||||
#ifdef __WXMSW__
|
||||
fontSize = 8;
|
||||
#endif
|
||||
|
||||
wxFont labelFont(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
|
||||
dc->SetFont(labelFont);
|
||||
wxFont labelFont(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
|
||||
dc->SetFont(labelFont);
|
||||
|
||||
int octave = 0;
|
||||
int obottom = track->GetOctaveBottom(octave);
|
||||
int marg = track->GetNoteMargin(rect.height);
|
||||
//IPITCH_TO_Y(octave * 12) + PITCH_HEIGHT + 1;
|
||||
while (obottom >= rect.y) {
|
||||
int octave = 0;
|
||||
int obottom = track->GetOctaveBottom(octave);
|
||||
int marg = track->GetNoteMargin(rect.height);
|
||||
//IPITCH_TO_Y(octave * 12) + PITCH_HEIGHT + 1;
|
||||
while (obottom >= rect.y) {
|
||||
dc->SetPen(*wxBLACK_PEN);
|
||||
for (int white = 0; white < 7; white++) {
|
||||
int pos = track->GetWhitePos(white);
|
||||
if (obottom - pos > rect.y + marg + 1 &&
|
||||
// don't draw too close to margin line -- it's annoying
|
||||
obottom - pos < rect.y + rect.height - marg - 3)
|
||||
AColor::Line(*dc, rect.x, obottom - pos,
|
||||
rect.x + rect.width, obottom - pos);
|
||||
}
|
||||
wxRect br = rect;
|
||||
br.height = track->GetPitchHeight(1);
|
||||
br.x++;
|
||||
br.width = 17;
|
||||
for (int black = 0; black < 5; black++) {
|
||||
br.y = obottom - track->GetBlackPos(black);
|
||||
if (br.y > rect.y + marg - 2 && br.y + br.height < rect.y + rect.height - marg) {
|
||||
dc->SetPen(hilitePen);
|
||||
dc->DrawRectangle(br);
|
||||
dc->SetPen(*wxBLACK_PEN);
|
||||
AColor::Line(*dc,
|
||||
br.x + 1, br.y + br.height - 1,
|
||||
br.x + br.width - 1, br.y + br.height - 1);
|
||||
AColor::Line(*dc,
|
||||
br.x + br.width - 1, br.y + 1,
|
||||
br.x + br.width - 1, br.y + br.height - 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (octave >= 1 && octave <= 10) {
|
||||
wxString s;
|
||||
// ISO standard: A440 is in the 4th octave, denoted
|
||||
// A4 <- the "4" should be a subscript.
|
||||
s.Printf(wxT("C%d"), octave - 1);
|
||||
wxCoord width, height;
|
||||
dc->GetTextExtent(s, &width, &height);
|
||||
if (obottom - height + 4 > rect.y &&
|
||||
obottom + 4 < rect.y + rect.height) {
|
||||
dc->SetTextForeground(wxColour(60, 60, 255));
|
||||
dc->DrawText(s, rect.x + rect.width - width,
|
||||
obottom - height + 2);
|
||||
}
|
||||
}
|
||||
obottom = track->GetOctaveBottom(++octave);
|
||||
}
|
||||
// draw lines delineating the out-of-bounds margins
|
||||
dc->SetPen(*wxBLACK_PEN);
|
||||
for (int white = 0; white < 7; white++) {
|
||||
int pos = track->GetWhitePos(white);
|
||||
if (obottom - pos > rect.y + marg + 1 &&
|
||||
// don't draw too close to margin line -- it's annoying
|
||||
obottom - pos < rect.y + rect.height - marg - 3)
|
||||
AColor::Line(*dc, rect.x, obottom - pos,
|
||||
rect.x + rect.width, obottom - pos);
|
||||
}
|
||||
wxRect br = rect;
|
||||
br.height = track->GetPitchHeight(1);
|
||||
br.x++;
|
||||
br.width = 17;
|
||||
for (int black = 0; black < 5; black++) {
|
||||
br.y = obottom - track->GetBlackPos(black);
|
||||
if (br.y > rect.y + marg - 2 && br.y + br.height < rect.y + rect.height - marg) {
|
||||
dc->SetPen(hilitePen);
|
||||
dc->DrawRectangle(br);
|
||||
dc->SetPen(*wxBLACK_PEN);
|
||||
AColor::Line(*dc,
|
||||
br.x + 1, br.y + br.height - 1,
|
||||
br.x + br.width - 1, br.y + br.height - 1);
|
||||
AColor::Line(*dc,
|
||||
br.x + br.width - 1, br.y + 1,
|
||||
br.x + br.width - 1, br.y + br.height - 1);
|
||||
}
|
||||
}
|
||||
// you would think the -1 offset here should be -2 to match the
|
||||
// adjustment to rect.y (see above), but -1 produces correct output
|
||||
AColor::Line(*dc, rect.x, rect.y + marg - 1, rect.x + rect.width, rect.y + marg - 1);
|
||||
// since the margin gives us the bottom of the line,
|
||||
// the extra -1 gets us to the top
|
||||
AColor::Line(*dc, rect.x, rect.y + rect.height - marg - 1,
|
||||
rect.x + rect.width, rect.y + rect.height - marg - 1);
|
||||
|
||||
if (octave >= 1 && octave <= 10) {
|
||||
wxString s;
|
||||
// ISO standard: A440 is in the 4th octave, denoted
|
||||
// A4 <- the "4" should be a subscript.
|
||||
s.Printf(wxT("C%d"), octave - 1);
|
||||
wxCoord width, height;
|
||||
dc->GetTextExtent(s, &width, &height);
|
||||
if (obottom - height + 4 > rect.y &&
|
||||
obottom + 4 < rect.y + rect.height) {
|
||||
dc->SetTextForeground(wxColour(60, 60, 255));
|
||||
dc->DrawText(s, rect.x + rect.width - width,
|
||||
obottom - height + 2);
|
||||
}
|
||||
}
|
||||
obottom = track->GetOctaveBottom(++octave);
|
||||
}
|
||||
// draw lines delineating the out-of-bounds margins
|
||||
dc->SetPen(*wxBLACK_PEN);
|
||||
// you would think the -1 offset here should be -2 to match the
|
||||
// adjustment to rect.y (see above), but -1 produces correct output
|
||||
AColor::Line(*dc, rect.x, rect.y + marg - 1, rect.x + rect.width, rect.y + marg - 1);
|
||||
// since the margin gives us the bottom of the line,
|
||||
// the extra -1 gets us to the top
|
||||
AColor::Line(*dc, rect.x, rect.y + rect.height - marg - 1,
|
||||
rect.x + rect.width, rect.y + rect.height - marg - 1);
|
||||
|
||||
}
|
||||
#endif // USE_MIDI
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
void TrackArtist::UpdateVRuler(const Track *t, wxRect & rect)
|
||||
{
|
||||
// Label tracks do not have a vruler
|
||||
if (t->GetKind() == Track::Label) {
|
||||
return;
|
||||
}
|
||||
auto update = t->TypeSwitch<bool>(
|
||||
[] (const LabelTrack *) {
|
||||
// Label tracks do not have a vruler
|
||||
return false;
|
||||
},
|
||||
|
||||
// Time tracks
|
||||
if (t->GetKind() == Track::Time) {
|
||||
const TimeTrack *tt = (TimeTrack *)t;
|
||||
float min, max;
|
||||
min = tt->GetRangeLower() * 100.0;
|
||||
max = tt->GetRangeUpper() * 100.0;
|
||||
[&](const TimeTrack *tt) {
|
||||
float min, max;
|
||||
min = tt->GetRangeLower() * 100.0;
|
||||
max = tt->GetRangeUpper() * 100.0;
|
||||
|
||||
vruler->SetDbMirrorValue( 0.0 );
|
||||
vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height-1);
|
||||
vruler->SetOrientation(wxVERTICAL);
|
||||
vruler->SetRange(max, min);
|
||||
vruler->SetFormat((tt->GetDisplayLog()) ? Ruler::RealLogFormat : Ruler::RealFormat);
|
||||
vruler->SetUnits(wxT(""));
|
||||
vruler->SetLabelEdges(false);
|
||||
vruler->SetLog(tt->GetDisplayLog());
|
||||
}
|
||||
vruler->SetDbMirrorValue( 0.0 );
|
||||
vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height-1);
|
||||
vruler->SetOrientation(wxVERTICAL);
|
||||
vruler->SetRange(max, min);
|
||||
vruler->SetFormat((tt->GetDisplayLog()) ? Ruler::RealLogFormat : Ruler::RealFormat);
|
||||
vruler->SetUnits(wxT(""));
|
||||
vruler->SetLabelEdges(false);
|
||||
vruler->SetLog(tt->GetDisplayLog());
|
||||
return true;
|
||||
},
|
||||
|
||||
// All waves have a ruler in the info panel
|
||||
// The ruler needs a bevelled surround.
|
||||
if (t->GetKind() == Track::Wave) {
|
||||
const WaveTrack *wt = static_cast<const WaveTrack*>(t);
|
||||
const float dBRange =
|
||||
wt->GetWaveformSettings().dBRange;
|
||||
[&](const WaveTrack *wt) {
|
||||
// All waves have a ruler in the info panel
|
||||
// The ruler needs a bevelled surround.
|
||||
const float dBRange =
|
||||
wt->GetWaveformSettings().dBRange;
|
||||
|
||||
const int display = wt->GetDisplay();
|
||||
const int display = wt->GetDisplay();
|
||||
|
||||
if (display == WaveTrack::Waveform) {
|
||||
WaveformSettings::ScaleType scaleType =
|
||||
wt->GetWaveformSettings().scaleType;
|
||||
if (display == WaveTrack::Waveform) {
|
||||
WaveformSettings::ScaleType scaleType =
|
||||
wt->GetWaveformSettings().scaleType;
|
||||
|
||||
if (scaleType == WaveformSettings::stLinear) {
|
||||
// Waveform
|
||||
if (scaleType == WaveformSettings::stLinear) {
|
||||
// Waveform
|
||||
|
||||
float min, max;
|
||||
wt->GetDisplayBounds(&min, &max);
|
||||
if (wt->GetLastScaleType() != scaleType &&
|
||||
wt->GetLastScaleType() != -1)
|
||||
{
|
||||
// do a translation into the linear space
|
||||
wt->SetLastScaleType();
|
||||
wt->SetLastdBRange();
|
||||
float sign = (min >= 0 ? 1 : -1);
|
||||
if (min != 0.) {
|
||||
min = DB_TO_LINEAR(fabs(min) * dBRange - dBRange);
|
||||
if (min < 0.0)
|
||||
min = 0.0;
|
||||
min *= sign;
|
||||
float min, max;
|
||||
wt->GetDisplayBounds(&min, &max);
|
||||
if (wt->GetLastScaleType() != scaleType &&
|
||||
wt->GetLastScaleType() != -1)
|
||||
{
|
||||
// do a translation into the linear space
|
||||
wt->SetLastScaleType();
|
||||
wt->SetLastdBRange();
|
||||
float sign = (min >= 0 ? 1 : -1);
|
||||
if (min != 0.) {
|
||||
min = DB_TO_LINEAR(fabs(min) * dBRange - dBRange);
|
||||
if (min < 0.0)
|
||||
min = 0.0;
|
||||
min *= sign;
|
||||
}
|
||||
sign = (max >= 0 ? 1 : -1);
|
||||
|
||||
if (max != 0.) {
|
||||
max = DB_TO_LINEAR(fabs(max) * dBRange - dBRange);
|
||||
if (max < 0.0)
|
||||
max = 0.0;
|
||||
max *= sign;
|
||||
}
|
||||
wt->SetDisplayBounds(min, max);
|
||||
}
|
||||
sign = (max >= 0 ? 1 : -1);
|
||||
|
||||
if (max != 0.) {
|
||||
max = DB_TO_LINEAR(fabs(max) * dBRange - dBRange);
|
||||
if (max < 0.0)
|
||||
max = 0.0;
|
||||
max *= sign;
|
||||
}
|
||||
wt->SetDisplayBounds(min, max);
|
||||
vruler->SetDbMirrorValue( 0.0 );
|
||||
vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1);
|
||||
vruler->SetOrientation(wxVERTICAL);
|
||||
vruler->SetRange(max, min);
|
||||
vruler->SetFormat(Ruler::RealFormat);
|
||||
vruler->SetUnits(wxT(""));
|
||||
vruler->SetLabelEdges(false);
|
||||
vruler->SetLog(false);
|
||||
}
|
||||
else {
|
||||
wxASSERT(scaleType == WaveformSettings::stLogarithmic);
|
||||
scaleType = WaveformSettings::stLogarithmic;
|
||||
|
||||
vruler->SetDbMirrorValue( 0.0 );
|
||||
vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1);
|
||||
vruler->SetOrientation(wxVERTICAL);
|
||||
vruler->SetRange(max, min);
|
||||
vruler->SetFormat(Ruler::RealFormat);
|
||||
vruler->SetUnits(wxT(""));
|
||||
vruler->SetLabelEdges(false);
|
||||
vruler->SetLog(false);
|
||||
}
|
||||
else {
|
||||
wxASSERT(scaleType == WaveformSettings::stLogarithmic);
|
||||
scaleType = WaveformSettings::stLogarithmic;
|
||||
vruler->SetUnits(wxT(""));
|
||||
|
||||
vruler->SetUnits(wxT(""));
|
||||
float min, max;
|
||||
wt->GetDisplayBounds(&min, &max);
|
||||
float lastdBRange;
|
||||
|
||||
float min, max;
|
||||
wt->GetDisplayBounds(&min, &max);
|
||||
float lastdBRange;
|
||||
if (wt->GetLastScaleType() != scaleType &&
|
||||
wt->GetLastScaleType() != -1)
|
||||
{
|
||||
// do a translation into the dB space
|
||||
wt->SetLastScaleType();
|
||||
wt->SetLastdBRange();
|
||||
float sign = (min >= 0 ? 1 : -1);
|
||||
if (min != 0.) {
|
||||
min = (LINEAR_TO_DB(fabs(min)) + dBRange) / dBRange;
|
||||
if (min < 0.0)
|
||||
min = 0.0;
|
||||
min *= sign;
|
||||
}
|
||||
sign = (max >= 0 ? 1 : -1);
|
||||
|
||||
if (wt->GetLastScaleType() != scaleType &&
|
||||
wt->GetLastScaleType() != -1)
|
||||
{
|
||||
// do a translation into the dB space
|
||||
wt->SetLastScaleType();
|
||||
wt->SetLastdBRange();
|
||||
float sign = (min >= 0 ? 1 : -1);
|
||||
if (min != 0.) {
|
||||
min = (LINEAR_TO_DB(fabs(min)) + dBRange) / dBRange;
|
||||
if (min < 0.0)
|
||||
min = 0.0;
|
||||
min *= sign;
|
||||
if (max != 0.) {
|
||||
max = (LINEAR_TO_DB(fabs(max)) + dBRange) / dBRange;
|
||||
if (max < 0.0)
|
||||
max = 0.0;
|
||||
max *= sign;
|
||||
}
|
||||
wt->SetDisplayBounds(min, max);
|
||||
}
|
||||
sign = (max >= 0 ? 1 : -1);
|
||||
|
||||
if (max != 0.) {
|
||||
max = (LINEAR_TO_DB(fabs(max)) + dBRange) / dBRange;
|
||||
if (max < 0.0)
|
||||
max = 0.0;
|
||||
max *= sign;
|
||||
}
|
||||
wt->SetDisplayBounds(min, max );
|
||||
}
|
||||
else if (dBRange != (lastdBRange = wt->GetLastdBRange())) {
|
||||
wt->SetLastdBRange();
|
||||
// Remap the max of the scale
|
||||
float newMax = max;
|
||||
else if (dBRange != (lastdBRange = wt->GetLastdBRange())) {
|
||||
wt->SetLastdBRange();
|
||||
// Remap the max of the scale
|
||||
float newMax = max;
|
||||
|
||||
// This commented out code is problematic.
|
||||
// min and max may be correct, and this code cause them to change.
|
||||
#ifdef ONLY_LABEL_POSITIVE
|
||||
const float sign = (max >= 0 ? 1 : -1);
|
||||
if (max != 0.) {
|
||||
const float sign = (max >= 0 ? 1 : -1);
|
||||
if (max != 0.) {
|
||||
|
||||
// Ugh, duplicating from TrackPanel.cpp
|
||||
#define ZOOMLIMIT 0.001f
|
||||
// Ugh, duplicating from TrackPanel.cpp
|
||||
#define ZOOMLIMIT 0.001f
|
||||
|
||||
const float extreme = LINEAR_TO_DB(2);
|
||||
// recover dB value of max
|
||||
const float dB = std::min(extreme, (float(fabs(max)) * lastdBRange - lastdBRange));
|
||||
// find NEW scale position, but old max may get trimmed if the db limit rises
|
||||
// Don't trim it to zero though, but leave max and limit distinct
|
||||
newMax = sign * std::max(ZOOMLIMIT, (dBRange + dB) / dBRange);
|
||||
// Adjust the min of the scale if we can,
|
||||
// so the db Limit remains where it was on screen, but don't violate extremes
|
||||
if (min != 0.)
|
||||
min = std::max(-extreme, newMax * min / max);
|
||||
}
|
||||
const float extreme = LINEAR_TO_DB(2);
|
||||
// recover dB value of max
|
||||
const float dB = std::min(extreme, (float(fabs(max)) * lastdBRange - lastdBRange));
|
||||
// find NEW scale position, but old max may get trimmed if the db limit rises
|
||||
// Don't trim it to zero though, but leave max and limit distinct
|
||||
newMax = sign * std::max(ZOOMLIMIT, (dBRange + dB) / dBRange);
|
||||
// Adjust the min of the scale if we can,
|
||||
// so the db Limit remains where it was on screen, but don't violate extremes
|
||||
if (min != 0.)
|
||||
min = std::max(-extreme, newMax * min / max);
|
||||
}
|
||||
#endif
|
||||
wt->SetDisplayBounds(min, newMax );
|
||||
}
|
||||
|
||||
wt->SetDisplayBounds(min, newMax);
|
||||
}
|
||||
|
||||
// Old code was if ONLY_LABEL_POSITIVE were defined.
|
||||
// it uses the +1 to 0 range only.
|
||||
// the enabled code uses +1 to -1, and relies on set ticks labelling knowing about
|
||||
// the enabled code uses +1 to -1, and relies on set ticks labelling knowing about
|
||||
// the dB scale.
|
||||
#ifdef ONLY_LABEL_POSITIVE
|
||||
if (max > 0) {
|
||||
if (max > 0) {
|
||||
#endif
|
||||
int top = 0;
|
||||
float topval = 0;
|
||||
int bot = rect.height;
|
||||
float botval = -dBRange;
|
||||
int top = 0;
|
||||
float topval = 0;
|
||||
int bot = rect.height;
|
||||
float botval = -dBRange;
|
||||
|
||||
#ifdef ONLY_LABEL_POSITIVE
|
||||
if (min < 0) {
|
||||
bot = top + (int)((max / (max - min))*(bot - top));
|
||||
min = 0;
|
||||
}
|
||||
if (min < 0) {
|
||||
bot = top + (int)((max / (max - min))*(bot - top));
|
||||
min = 0;
|
||||
}
|
||||
|
||||
if (max > 1) {
|
||||
top += (int)((max - 1) / (max - min) * (bot - top));
|
||||
max = 1;
|
||||
}
|
||||
if (max > 1) {
|
||||
top += (int)((max - 1) / (max - min) * (bot - top));
|
||||
max = 1;
|
||||
}
|
||||
|
||||
if (max < 1 && max > 0)
|
||||
topval = -((1 - max) * dBRange);
|
||||
if (max < 1 && max > 0)
|
||||
topval = -((1 - max) * dBRange);
|
||||
|
||||
if (min > 0) {
|
||||
botval = -((1 - min) * dBRange);
|
||||
}
|
||||
if (min > 0) {
|
||||
botval = -((1 - min) * dBRange);
|
||||
}
|
||||
#else
|
||||
topval = -((1 - max) * dBRange);
|
||||
botval = -((1 - min) * dBRange);
|
||||
vruler->SetDbMirrorValue( dBRange );
|
||||
topval = -((1 - max) * dBRange);
|
||||
botval = -((1 - min) * dBRange);
|
||||
vruler->SetDbMirrorValue( dBRange );
|
||||
#endif
|
||||
vruler->SetBounds(rect.x, rect.y + top, rect.x + rect.width, rect.y + bot - 1);
|
||||
vruler->SetOrientation(wxVERTICAL);
|
||||
vruler->SetRange(topval, botval);
|
||||
vruler->SetBounds(rect.x, rect.y + top, rect.x + rect.width, rect.y + bot - 1);
|
||||
vruler->SetOrientation(wxVERTICAL);
|
||||
vruler->SetRange(topval, botval);
|
||||
#ifdef ONLY_LABEL_POSITIVE
|
||||
}
|
||||
else
|
||||
vruler->SetBounds(0.0, 0.0, 0.0, 0.0); // A.C.H I couldn't find a way to just disable it?
|
||||
}
|
||||
else
|
||||
vruler->SetBounds(0.0, 0.0, 0.0, 0.0); // A.C.H I couldn't find a way to just disable it?
|
||||
#endif
|
||||
vruler->SetFormat(Ruler::RealLogFormat);
|
||||
vruler->SetLabelEdges(true);
|
||||
vruler->SetLog(false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
wxASSERT(display == WaveTrack::Spectrum);
|
||||
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
|
||||
float minFreq, maxFreq;
|
||||
wt->GetSpectrumBounds(&minFreq, &maxFreq);
|
||||
vruler->SetDbMirrorValue( 0.0 );
|
||||
|
||||
switch (settings.scaleType) {
|
||||
default:
|
||||
wxASSERT(false);
|
||||
case SpectrogramSettings::stLinear:
|
||||
{
|
||||
// Spectrum
|
||||
|
||||
if (rect.height < 60)
|
||||
return;
|
||||
|
||||
/*
|
||||
draw the ruler
|
||||
we will use Hz if maxFreq is < 2000, otherwise we represent kHz,
|
||||
and append to the numbers a "k"
|
||||
*/
|
||||
vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1);
|
||||
vruler->SetOrientation(wxVERTICAL);
|
||||
vruler->SetFormat(Ruler::RealFormat);
|
||||
vruler->SetLabelEdges(true);
|
||||
// use kHz in scale, if appropriate
|
||||
if (maxFreq >= 2000) {
|
||||
vruler->SetRange((maxFreq / 1000.), (minFreq / 1000.));
|
||||
vruler->SetUnits(wxT("k"));
|
||||
vruler->SetFormat(Ruler::RealLogFormat);
|
||||
vruler->SetLabelEdges(true);
|
||||
vruler->SetLog(false);
|
||||
}
|
||||
else {
|
||||
// use Hz
|
||||
vruler->SetRange((int)(maxFreq), (int)(minFreq));
|
||||
}
|
||||
else {
|
||||
wxASSERT(display == WaveTrack::Spectrum);
|
||||
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
|
||||
float minFreq, maxFreq;
|
||||
wt->GetSpectrumBounds(&minFreq, &maxFreq);
|
||||
vruler->SetDbMirrorValue( 0.0 );
|
||||
|
||||
switch (settings.scaleType) {
|
||||
default:
|
||||
wxASSERT(false);
|
||||
case SpectrogramSettings::stLinear:
|
||||
{
|
||||
// Spectrum
|
||||
|
||||
if (rect.height < 60)
|
||||
return false;
|
||||
|
||||
/*
|
||||
draw the ruler
|
||||
we will use Hz if maxFreq is < 2000, otherwise we represent kHz,
|
||||
and append to the numbers a "k"
|
||||
*/
|
||||
vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1);
|
||||
vruler->SetOrientation(wxVERTICAL);
|
||||
vruler->SetFormat(Ruler::RealFormat);
|
||||
vruler->SetLabelEdges(true);
|
||||
// use kHz in scale, if appropriate
|
||||
if (maxFreq >= 2000) {
|
||||
vruler->SetRange((maxFreq / 1000.), (minFreq / 1000.));
|
||||
vruler->SetUnits(wxT("k"));
|
||||
}
|
||||
else {
|
||||
// use Hz
|
||||
vruler->SetRange((int)(maxFreq), (int)(minFreq));
|
||||
vruler->SetUnits(wxT(""));
|
||||
}
|
||||
vruler->SetLog(false);
|
||||
}
|
||||
break;
|
||||
case SpectrogramSettings::stLogarithmic:
|
||||
case SpectrogramSettings::stMel:
|
||||
case SpectrogramSettings::stBark:
|
||||
case SpectrogramSettings::stErb:
|
||||
case SpectrogramSettings::stPeriod:
|
||||
{
|
||||
// SpectrumLog
|
||||
|
||||
if (rect.height < 10)
|
||||
return false;
|
||||
|
||||
/*
|
||||
draw the ruler
|
||||
we will use Hz if maxFreq is < 2000, otherwise we represent kHz,
|
||||
and append to the numbers a "k"
|
||||
*/
|
||||
vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1);
|
||||
vruler->SetOrientation(wxVERTICAL);
|
||||
vruler->SetFormat(Ruler::IntFormat);
|
||||
vruler->SetLabelEdges(true);
|
||||
vruler->SetRange(maxFreq, minFreq);
|
||||
vruler->SetUnits(wxT(""));
|
||||
vruler->SetLog(true);
|
||||
NumberScale scale(
|
||||
wt->GetSpectrogramSettings().GetScale( minFreq, maxFreq )
|
||||
.Reversal() );
|
||||
vruler->SetNumberScale(&scale);
|
||||
}
|
||||
break;
|
||||
}
|
||||
vruler->SetLog(false);
|
||||
}
|
||||
break;
|
||||
case SpectrogramSettings::stLogarithmic:
|
||||
case SpectrogramSettings::stMel:
|
||||
case SpectrogramSettings::stBark:
|
||||
case SpectrogramSettings::stErb:
|
||||
case SpectrogramSettings::stPeriod:
|
||||
{
|
||||
// SpectrumLog
|
||||
|
||||
if (rect.height < 10)
|
||||
return;
|
||||
|
||||
/*
|
||||
draw the ruler
|
||||
we will use Hz if maxFreq is < 2000, otherwise we represent kHz,
|
||||
and append to the numbers a "k"
|
||||
*/
|
||||
vruler->SetBounds(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height - 1);
|
||||
vruler->SetOrientation(wxVERTICAL);
|
||||
vruler->SetFormat(Ruler::IntFormat);
|
||||
vruler->SetLabelEdges(true);
|
||||
vruler->SetRange(maxFreq, minFreq);
|
||||
vruler->SetUnits(wxT(""));
|
||||
vruler->SetLog(true);
|
||||
NumberScale scale(
|
||||
wt->GetSpectrogramSettings().GetScale( minFreq, maxFreq )
|
||||
.Reversal() );
|
||||
vruler->SetNumberScale(&scale);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_MIDI
|
||||
// The note track isn't drawing a ruler at all!
|
||||
// But it needs to!
|
||||
else if (t->GetKind() == Track::Note) {
|
||||
vruler->SetBounds(rect.x, rect.y, rect.x + 1, rect.y + rect.height-1);
|
||||
vruler->SetOrientation(wxVERTICAL);
|
||||
}
|
||||
,
|
||||
[&](const NoteTrack *) {
|
||||
// The note track isn't drawing a ruler at all!
|
||||
// But it needs to!
|
||||
vruler->SetBounds(rect.x, rect.y, rect.x + 1, rect.y + rect.height-1);
|
||||
vruler->SetOrientation(wxVERTICAL);
|
||||
return true;
|
||||
}
|
||||
#endif // USE_MIDI
|
||||
);
|
||||
|
||||
vruler->GetMaxSize(&t->vrulerSize.x, &t->vrulerSize.y);
|
||||
if (update)
|
||||
vruler->GetMaxSize(&t->vrulerSize.x, &t->vrulerSize.y);
|
||||
}
|
||||
|
||||
/// Takes a value between min and max and returns a value between
|
||||
|
@ -192,9 +192,8 @@ void WaveTrack::Reinit(const WaveTrack &orig)
|
||||
|
||||
void WaveTrack::Merge(const Track &orig)
|
||||
{
|
||||
if (orig.GetKind() == Wave)
|
||||
{
|
||||
const WaveTrack &wt = static_cast<const WaveTrack&>(orig);
|
||||
orig.TypeSwitch( [&](const WaveTrack *pwt) {
|
||||
const WaveTrack &wt = *pwt;
|
||||
mDisplay = wt.mDisplay;
|
||||
mGain = wt.mGain;
|
||||
mPan = wt.mPan;
|
||||
@ -204,7 +203,7 @@ void WaveTrack::Merge(const Track &orig)
|
||||
? std::make_unique<SpectrogramSettings>(*wt.mpSpectrumSettings) : nullptr);
|
||||
SetWaveformSettings
|
||||
(wt.mpWaveformSettings ? std::make_unique<WaveformSettings>(*wt.mpWaveformSettings) : nullptr);
|
||||
}
|
||||
});
|
||||
PlayableTrack::Merge(orig);
|
||||
}
|
||||
|
||||
@ -1210,159 +1209,158 @@ void WaveTrack::Paste(double t0, const Track *src)
|
||||
// WEAK-GUARANTEE
|
||||
{
|
||||
bool editClipCanMove = gPrefs->GetEditClipsCanMove();
|
||||
|
||||
if( src == NULL )
|
||||
// THROW_INCONSISTENCY_EXCEPTION; // ?
|
||||
return;
|
||||
|
||||
if (src->GetKind() != Track::Wave)
|
||||
// THROW_INCONSISTENCY_EXCEPTION; // ?
|
||||
return;
|
||||
bool bOk = src && src->TypeSwitch< bool >( [&](const WaveTrack *other) {
|
||||
|
||||
const WaveTrack* other = static_cast<const WaveTrack*>(src);
|
||||
//
|
||||
// Pasting is a bit complicated, because with the existence of multiclip mode,
|
||||
// we must guess the behaviour the user wants.
|
||||
//
|
||||
// Currently, two modes are implemented:
|
||||
//
|
||||
// - If a single clip should be pasted, and it should be pasted inside another
|
||||
// clip, no NEW clips are generated. The audio is simply inserted.
|
||||
// This resembles the old (pre-multiclip support) behaviour. However, if
|
||||
// the clip is pasted outside of any clip, a NEW clip is generated. This is
|
||||
// the only behaviour which is different to what was done before, but it
|
||||
// shouldn't confuse users too much.
|
||||
//
|
||||
// - If multiple clips should be pasted, or a single clip that does not fill
|
||||
// the duration of the pasted track, these are always pasted as single
|
||||
// clips, and the current clip is splitted, when necessary. This may seem
|
||||
// strange at first, but it probably is better than trying to auto-merge
|
||||
// anything. The user can still merge the clips by hand (which should be a
|
||||
// simple command reachable by a hotkey or single mouse click).
|
||||
//
|
||||
|
||||
//
|
||||
// Pasting is a bit complicated, because with the existence of multiclip mode,
|
||||
// we must guess the behaviour the user wants.
|
||||
//
|
||||
// Currently, two modes are implemented:
|
||||
//
|
||||
// - If a single clip should be pasted, and it should be pasted inside another
|
||||
// clip, no NEW clips are generated. The audio is simply inserted.
|
||||
// This resembles the old (pre-multiclip support) behaviour. However, if
|
||||
// the clip is pasted outside of any clip, a NEW clip is generated. This is
|
||||
// the only behaviour which is different to what was done before, but it
|
||||
// shouldn't confuse users too much.
|
||||
//
|
||||
// - If multiple clips should be pasted, or a single clip that does not fill
|
||||
// the duration of the pasted track, these are always pasted as single
|
||||
// clips, and the current clip is splitted, when necessary. This may seem
|
||||
// strange at first, but it probably is better than trying to auto-merge
|
||||
// anything. The user can still merge the clips by hand (which should be a
|
||||
// simple command reachable by a hotkey or single mouse click).
|
||||
//
|
||||
if (other->GetNumClips() == 0)
|
||||
return true;
|
||||
|
||||
if (other->GetNumClips() == 0)
|
||||
return;
|
||||
//wxPrintf("paste: we have at least one clip\n");
|
||||
|
||||
//wxPrintf("paste: we have at least one clip\n");
|
||||
bool singleClipMode = (other->GetNumClips() == 1 &&
|
||||
other->GetStartTime() == 0.0);
|
||||
|
||||
bool singleClipMode = (other->GetNumClips() == 1 &&
|
||||
other->GetStartTime() == 0.0);
|
||||
const double insertDuration = other->GetEndTime();
|
||||
if( insertDuration != 0 && insertDuration < 1.0/mRate )
|
||||
// PRL: I added this check to avoid violations of preconditions in other WaveClip and Sequence
|
||||
// methods, but allow the value 0 so I don't subvert the purpose of commit
|
||||
// 739422ba70ceb4be0bb1829b6feb0c5401de641e which causes append-recording always to make
|
||||
// a new clip.
|
||||
return true;
|
||||
|
||||
const double insertDuration = other->GetEndTime();
|
||||
if( insertDuration != 0 && insertDuration < 1.0/mRate )
|
||||
// PRL: I added this check to avoid violations of preconditions in other WaveClip and Sequence
|
||||
// methods, but allow the value 0 so I don't subvert the purpose of commit
|
||||
// 739422ba70ceb4be0bb1829b6feb0c5401de641e which causes append-recording always to make
|
||||
// a new clip.
|
||||
return;
|
||||
//wxPrintf("Check if we need to make room for the pasted data\n");
|
||||
|
||||
//wxPrintf("Check if we need to make room for the pasted data\n");
|
||||
|
||||
// Make room for the pasted data
|
||||
if (editClipCanMove) {
|
||||
if (!singleClipMode) {
|
||||
// We need to insert multiple clips, so split the current clip and
|
||||
// move everything to the right, then try to paste again
|
||||
if (!IsEmpty(t0, GetEndTime())) {
|
||||
auto tmp = Cut(t0, GetEndTime()+1.0/mRate);
|
||||
Paste(t0 + insertDuration, tmp.get());
|
||||
}
|
||||
}
|
||||
else {
|
||||
// We only need to insert one single clip, so just move all clips
|
||||
// to the right of the paste point out of the way
|
||||
for (const auto &clip : mClips)
|
||||
{
|
||||
if (clip->GetStartTime() > t0-(1.0/mRate))
|
||||
clip->Offset(insertDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (singleClipMode)
|
||||
{
|
||||
// Single clip mode
|
||||
// wxPrintf("paste: checking for single clip mode!\n");
|
||||
|
||||
WaveClip *insideClip = NULL;
|
||||
|
||||
for (const auto &clip : mClips)
|
||||
{
|
||||
if (editClipCanMove)
|
||||
{
|
||||
if (clip->WithinClip(t0))
|
||||
{
|
||||
//wxPrintf("t0=%.6f: inside clip is %.6f ... %.6f\n",
|
||||
// t0, clip->GetStartTime(), clip->GetEndTime());
|
||||
insideClip = clip.get();
|
||||
break;
|
||||
// Make room for the pasted data
|
||||
if (editClipCanMove) {
|
||||
if (!singleClipMode) {
|
||||
// We need to insert multiple clips, so split the current clip and
|
||||
// move everything to the right, then try to paste again
|
||||
if (!IsEmpty(t0, GetEndTime())) {
|
||||
auto tmp = Cut(t0, GetEndTime()+1.0/mRate);
|
||||
Paste(t0 + insertDuration, tmp.get());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If clips are immovable we also allow prepending to clips
|
||||
if (clip->WithinClip(t0) ||
|
||||
TimeToLongSamples(t0) == clip->GetStartSample())
|
||||
{
|
||||
insideClip = clip.get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (insideClip)
|
||||
{
|
||||
// Exhibit traditional behaviour
|
||||
//wxPrintf("paste: traditional behaviour\n");
|
||||
if (!editClipCanMove)
|
||||
{
|
||||
// We did not move other clips out of the way already, so
|
||||
// check if we can paste without having to move other clips
|
||||
else {
|
||||
// We only need to insert one single clip, so just move all clips
|
||||
// to the right of the paste point out of the way
|
||||
for (const auto &clip : mClips)
|
||||
{
|
||||
if (clip->GetStartTime() > insideClip->GetStartTime() &&
|
||||
insideClip->GetEndTime() + insertDuration >
|
||||
clip->GetStartTime())
|
||||
// STRONG-GUARANTEE in case of this path
|
||||
// not that it matters.
|
||||
throw SimpleMessageBoxException{
|
||||
_("There is not enough room available to paste the selection")
|
||||
};
|
||||
if (clip->GetStartTime() > t0-(1.0/mRate))
|
||||
clip->Offset(insertDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (singleClipMode)
|
||||
{
|
||||
// Single clip mode
|
||||
// wxPrintf("paste: checking for single clip mode!\n");
|
||||
|
||||
WaveClip *insideClip = NULL;
|
||||
|
||||
for (const auto &clip : mClips)
|
||||
{
|
||||
if (editClipCanMove)
|
||||
{
|
||||
if (clip->WithinClip(t0))
|
||||
{
|
||||
//wxPrintf("t0=%.6f: inside clip is %.6f ... %.6f\n",
|
||||
// t0, clip->GetStartTime(), clip->GetEndTime());
|
||||
insideClip = clip.get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If clips are immovable we also allow prepending to clips
|
||||
if (clip->WithinClip(t0) ||
|
||||
TimeToLongSamples(t0) == clip->GetStartSample())
|
||||
{
|
||||
insideClip = clip.get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
insideClip->Paste(t0, other->GetClipByIndex(0));
|
||||
return;
|
||||
if (insideClip)
|
||||
{
|
||||
// Exhibit traditional behaviour
|
||||
//wxPrintf("paste: traditional behaviour\n");
|
||||
if (!editClipCanMove)
|
||||
{
|
||||
// We did not move other clips out of the way already, so
|
||||
// check if we can paste without having to move other clips
|
||||
for (const auto &clip : mClips)
|
||||
{
|
||||
if (clip->GetStartTime() > insideClip->GetStartTime() &&
|
||||
insideClip->GetEndTime() + insertDuration >
|
||||
clip->GetStartTime())
|
||||
// STRONG-GUARANTEE in case of this path
|
||||
// not that it matters.
|
||||
throw SimpleMessageBoxException{
|
||||
_("There is not enough room available to paste the selection")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
insideClip->Paste(t0, other->GetClipByIndex(0));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Just fall through and exhibit NEW behaviour
|
||||
|
||||
}
|
||||
|
||||
// Just fall through and exhibit NEW behaviour
|
||||
}
|
||||
// Insert NEW clips
|
||||
//wxPrintf("paste: multi clip mode!\n");
|
||||
|
||||
// Insert NEW clips
|
||||
//wxPrintf("paste: multi clip mode!\n");
|
||||
if (!editClipCanMove && !IsEmpty(t0, t0+insertDuration-1.0/mRate))
|
||||
// STRONG-GUARANTEE in case of this path
|
||||
// not that it matters.
|
||||
throw SimpleMessageBoxException{
|
||||
_("There is not enough room available to paste the selection")
|
||||
};
|
||||
|
||||
if (!editClipCanMove && !IsEmpty(t0, t0+insertDuration-1.0/mRate))
|
||||
// STRONG-GUARANTEE in case of this path
|
||||
// not that it matters.
|
||||
throw SimpleMessageBoxException{
|
||||
_("There is not enough room available to paste the selection")
|
||||
};
|
||||
|
||||
for (const auto &clip : other->mClips)
|
||||
{
|
||||
// AWD Oct. 2009: Don't actually paste in placeholder clips
|
||||
if (!clip->GetIsPlaceholder())
|
||||
for (const auto &clip : other->mClips)
|
||||
{
|
||||
auto newClip =
|
||||
std::make_unique<WaveClip>( *clip, mDirManager, true );
|
||||
newClip->Resample(mRate);
|
||||
newClip->Offset(t0);
|
||||
newClip->MarkChanged();
|
||||
mClips.push_back(std::move(newClip)); // transfer ownership
|
||||
// AWD Oct. 2009: Don't actually paste in placeholder clips
|
||||
if (!clip->GetIsPlaceholder())
|
||||
{
|
||||
auto newClip =
|
||||
std::make_unique<WaveClip>( *clip, mDirManager, true );
|
||||
newClip->Resample(mRate);
|
||||
newClip->Offset(t0);
|
||||
newClip->MarkChanged();
|
||||
mClips.push_back(std::move(newClip)); // transfer ownership
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} );
|
||||
|
||||
if( !bOk )
|
||||
// THROW_INCONSISTENCY_EXCEPTION; // ?
|
||||
;
|
||||
}
|
||||
|
||||
void WaveTrack::Silence(double t0, double t1)
|
||||
|
@ -248,10 +248,8 @@ bool GetInfoCommand::SendBoxes(const CommandContext &context)
|
||||
bool GetInfoCommand::SendTracks(const CommandContext & context)
|
||||
{
|
||||
TrackList *projTracks = context.GetProject()->GetTracks();
|
||||
TrackListIterator iter(projTracks);
|
||||
Track *trk = iter.First();
|
||||
context.StartArray();
|
||||
while (trk)
|
||||
for (auto trk : projTracks->Leaders())
|
||||
{
|
||||
|
||||
TrackPanel *panel = context.GetProject()->GetTrackPanel();
|
||||
@ -264,9 +262,7 @@ bool GetInfoCommand::SendTracks(const CommandContext & context)
|
||||
//JKC: Possibly add these two later...
|
||||
//context.AddItem( trk->GetKind(), "kind" );
|
||||
//context.AddItem( trk->GetHeight(), "height" );
|
||||
auto t = dynamic_cast<WaveTrack*>( trk );
|
||||
if( t )
|
||||
{
|
||||
trk->TypeSwitch( [&] (const WaveTrack* t ) {
|
||||
context.AddItem( t->GetStartTime(), "start" );
|
||||
context.AddItem( t->GetEndTime(), "end" );
|
||||
context.AddItem( t->GetPan() , "pan");
|
||||
@ -274,13 +270,8 @@ bool GetInfoCommand::SendTracks(const CommandContext & context)
|
||||
context.AddItem( t->GetLinked() ? 2:1, "channels");
|
||||
context.AddBool( t->GetSolo(), "solo" );
|
||||
context.AddBool( t->GetMute(), "mute");
|
||||
}
|
||||
} );
|
||||
context.EndStruct();
|
||||
// Skip second tracks of stereo...
|
||||
if( trk->GetLinked() )
|
||||
trk= iter.Next();
|
||||
if( trk )
|
||||
trk=iter.Next();
|
||||
}
|
||||
context.EndArray();
|
||||
return true;
|
||||
@ -368,48 +359,35 @@ bool GetInfoCommand::SendEnvelopes(const CommandContext &context)
|
||||
bool GetInfoCommand::SendLabels(const CommandContext &context)
|
||||
{
|
||||
TrackList *tracks = context.GetProject()->GetTracks();
|
||||
TrackListIterator iter(tracks);
|
||||
Track *t = iter.First();
|
||||
int i=0;
|
||||
context.StartArray();
|
||||
while (t) {
|
||||
if (t->GetKind() == Track::Label) {
|
||||
LabelTrack *labelTrack = static_cast<LabelTrack*>(t);
|
||||
if( labelTrack )
|
||||
{
|
||||
|
||||
for (auto t : tracks->Leaders()) {
|
||||
t->TypeSwitch( [&](LabelTrack *labelTrack) {
|
||||
#ifdef VERBOSE_LABELS_FORMATTING
|
||||
for (int nn = 0; nn< (int)labelTrack->mLabels.size(); nn++) {
|
||||
const auto &label = labelTrack->mLabels[nn];
|
||||
context.StartStruct();
|
||||
context.AddItem( (double)i, "track" );
|
||||
context.AddItem( label.getT0(), "start" );
|
||||
context.AddItem( label.getT1(), "end" );
|
||||
context.AddItem( label.title, "text" );
|
||||
context.EndStruct();
|
||||
}
|
||||
#else
|
||||
context.AddItem( (double)i ); // Track number.
|
||||
context.StartArray();
|
||||
for (int nn = 0; nn< (int)labelTrack->mLabels.size(); nn++) {
|
||||
const auto &label = labelTrack->mLabels[nn];
|
||||
context.StartArray();
|
||||
context.AddItem( label.getT0() ); // start
|
||||
context.AddItem( label.getT1() ); // end
|
||||
context.AddItem( label.title ); //text.
|
||||
context.EndArray();
|
||||
}
|
||||
context.EndArray();
|
||||
#endif
|
||||
for (int nn = 0; nn< (int)labelTrack->mLabels.size(); nn++) {
|
||||
const auto &label = labelTrack->mLabels[nn];
|
||||
context.StartStruct();
|
||||
context.AddItem( (double)i, "track" );
|
||||
context.AddItem( label.getT0(), "start" );
|
||||
context.AddItem( label.getT1(), "end" );
|
||||
context.AddItem( label.title, "text" );
|
||||
context.EndStruct();
|
||||
}
|
||||
}
|
||||
// Skip second tracks of stereo...
|
||||
// This has no effect on label tracks themselves, which are never stereo
|
||||
// but is needed for per track rather than per channel numbering.
|
||||
if( t->GetLinked() )
|
||||
t= iter.Next();
|
||||
if( t )
|
||||
t=iter.Next();
|
||||
#else
|
||||
context.AddItem( (double)i ); // Track number.
|
||||
context.StartArray();
|
||||
for (int nn = 0; nn< (int)labelTrack->mLabels.size(); nn++) {
|
||||
const auto &label = labelTrack->mLabels[nn];
|
||||
context.StartArray();
|
||||
context.AddItem( label.getT0() ); // start
|
||||
context.AddItem( label.getT1() ); // end
|
||||
context.AddItem( label.title ); //text.
|
||||
context.EndArray();
|
||||
}
|
||||
context.EndArray();
|
||||
#endif
|
||||
} );
|
||||
// Per track numbering counts all tracks
|
||||
i++;
|
||||
}
|
||||
context.EndArray();
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "../Project.h"
|
||||
#include "../Track.h"
|
||||
#include "../TrackPanel.h"
|
||||
#include "../NoteTrack.h"
|
||||
#include "../WaveTrack.h"
|
||||
#include "../ShuttleGui.h"
|
||||
#include "CommandContext.h"
|
||||
|
@ -74,33 +74,29 @@ void SetClipCommand::PopulateOrExchange(ShuttleGui & S)
|
||||
bool SetClipCommand::ApplyInner( const CommandContext & context, Track * t )
|
||||
{
|
||||
static_cast<void>(context);
|
||||
if( t->GetKind() != Track::Wave)
|
||||
return true;
|
||||
|
||||
// if no 'At' is specified, then any clip in any selected track will be set.
|
||||
t->TypeSwitch([&](WaveTrack *waveTrack) {
|
||||
WaveClipPointers ptrs( waveTrack->SortedClipArray());
|
||||
for(auto it = ptrs.begin(); (it != ptrs.end()); it++ ){
|
||||
WaveClip * pClip = *it;
|
||||
bool bFound =
|
||||
!bHasContainsTime || (
|
||||
( pClip->GetStartTime() <= mContainsTime ) &&
|
||||
( pClip->GetEndTime() >= mContainsTime )
|
||||
);
|
||||
if( bFound )
|
||||
{
|
||||
// Inside this IF is where we actually apply the command
|
||||
|
||||
WaveTrack *waveTrack = static_cast<WaveTrack*>(t);
|
||||
wxASSERT( waveTrack );
|
||||
WaveClipPointers ptrs( waveTrack->SortedClipArray());
|
||||
for(auto it = ptrs.begin(); (it != ptrs.end()); it++ ){
|
||||
WaveClip * pClip = *it;
|
||||
bool bFound =
|
||||
!bHasContainsTime || (
|
||||
( pClip->GetStartTime() <= mContainsTime ) &&
|
||||
( pClip->GetEndTime() >= mContainsTime )
|
||||
);
|
||||
if( bFound )
|
||||
{
|
||||
// Inside this IF is where we actually apply the command
|
||||
|
||||
if( bHasColour )
|
||||
pClip->SetColourIndex(mColour);
|
||||
// No validation of overlap yet. We assume the user is sensible!
|
||||
if( bHasT0 )
|
||||
pClip->SetOffset(mT0);
|
||||
// \todo Use SetClip to move a clip between tracks too.
|
||||
if( bHasColour )
|
||||
pClip->SetColourIndex(mColour);
|
||||
// No validation of overlap yet. We assume the user is sensible!
|
||||
if( bHasT0 )
|
||||
pClip->SetOffset(mT0);
|
||||
// \todo Use SetClip to move a clip between tracks too.
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
return true;
|
||||
}
|
||||
|
@ -54,32 +54,29 @@ void SetEnvelopeCommand::PopulateOrExchange(ShuttleGui & S)
|
||||
bool SetEnvelopeCommand::ApplyInner( const CommandContext & context, Track * t )
|
||||
{
|
||||
static_cast<void>(context);
|
||||
if( (t->GetKind() != Track::Wave))
|
||||
return true;
|
||||
|
||||
// if no time is specified, then
|
||||
// if no time is specified, then
|
||||
// - delete deletes any envelope in selected tracks.
|
||||
// - value is not set for any clip
|
||||
|
||||
WaveTrack *waveTrack = static_cast<WaveTrack*>(t);
|
||||
WaveClipPointers ptrs( waveTrack->SortedClipArray());
|
||||
for(auto it = ptrs.begin(); (it != ptrs.end()); it++ ){
|
||||
WaveClip * pClip = *it;
|
||||
bool bFound =
|
||||
!bHasT || (
|
||||
( pClip->GetStartTime() <= mT) &&
|
||||
( pClip->GetEndTime() >= mT )
|
||||
);
|
||||
if( bFound )
|
||||
{
|
||||
// Inside this IF is where we actually apply the command
|
||||
Envelope* pEnv = pClip->GetEnvelope();
|
||||
if( bHasDelete && mbDelete )
|
||||
pEnv->mEnv.clear();
|
||||
if( bHasT && bHasV )
|
||||
pEnv->InsertOrReplace( mT, mV );
|
||||
t->TypeSwitch([&](WaveTrack *waveTrack) {
|
||||
WaveClipPointers ptrs( waveTrack->SortedClipArray());
|
||||
for(auto it = ptrs.begin(); (it != ptrs.end()); it++ ){
|
||||
WaveClip * pClip = *it;
|
||||
bool bFound =
|
||||
!bHasT || (
|
||||
( pClip->GetStartTime() <= mT) &&
|
||||
( pClip->GetEndTime() >= mT )
|
||||
);
|
||||
if( bFound )
|
||||
{
|
||||
// Inside this IF is where we actually apply the command
|
||||
Envelope* pEnv = pClip->GetEnvelope();
|
||||
if( bHasDelete && mbDelete )
|
||||
pEnv->mEnv.clear();
|
||||
if( bHasT && bHasV )
|
||||
pEnv->InsertOrReplace( mT, mV );
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -217,34 +217,32 @@ bool EffectAutoDuck::Init()
|
||||
|
||||
while(t)
|
||||
{
|
||||
if (lastWasSelectedWaveTrack && !t->GetSelected() &&
|
||||
t->GetKind() == Track::Wave)
|
||||
{
|
||||
if (lastWasSelectedWaveTrack && !t->GetSelected()) {
|
||||
// This could be the control track, so remember it
|
||||
controlTrackCandidate = (WaveTrack*)t;
|
||||
controlTrackCandidate = track_cast<const WaveTrack *>(t);
|
||||
}
|
||||
|
||||
lastWasSelectedWaveTrack = false;
|
||||
|
||||
if (t->GetSelected())
|
||||
{
|
||||
if (t->GetKind() == Track::Wave)
|
||||
{
|
||||
lastWasSelectedWaveTrack = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Effect::MessageBox(
|
||||
_("You selected a track which does not contain audio. AutoDuck can only process audio tracks."),
|
||||
/* i18n-hint: Auto duck is the name of an effect that 'ducks' (reduces the volume)
|
||||
* of the audio automatically when there is sound on another track. Not as
|
||||
* in 'Donald-Duck'!*/
|
||||
wxICON_ERROR);
|
||||
if (t->GetSelected()) {
|
||||
bool ok = t->TypeSwitch<bool>(
|
||||
[&](const WaveTrack *) {
|
||||
lastWasSelectedWaveTrack = true;
|
||||
return true;
|
||||
},
|
||||
[&](const Track *) {
|
||||
Effect::MessageBox(
|
||||
_("You selected a track which does not contain audio. AutoDuck can only process audio tracks."),
|
||||
/* i18n-hint: Auto duck is the name of an effect that 'ducks' (reduces the volume)
|
||||
* of the audio automatically when there is sound on another track. Not as
|
||||
* in 'Donald-Duck'!*/
|
||||
wxICON_ERROR);
|
||||
return false;
|
||||
}
|
||||
);
|
||||
if (!ok)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
t = iter.Next();
|
||||
}
|
||||
|
||||
if (!controlTrackCandidate)
|
||||
|
@ -1073,37 +1073,36 @@ bool NyquistEffect::ProcessOne()
|
||||
wxString bitFormat;
|
||||
wxString spectralEditp;
|
||||
|
||||
switch (mCurTrack[0]->GetKind())
|
||||
{
|
||||
case Track::Wave:
|
||||
mCurTrack[0]->TypeSwitch(
|
||||
[&](const WaveTrack *wt) {
|
||||
type = wxT("wave");
|
||||
spectralEditp = mCurTrack[0]->GetSpectrogramSettings().SpectralSelectionEnabled()? wxT("T") : wxT("NIL");
|
||||
switch (((WaveTrack *) mCurTrack[0])->GetDisplay())
|
||||
switch (wt->GetDisplay())
|
||||
{
|
||||
case WaveTrack::Waveform:
|
||||
view = (mCurTrack[0]->GetWaveformSettings().scaleType == 0) ? wxT("\"Waveform\"") : wxT("\"Waveform (dB)\"");
|
||||
break;
|
||||
case WaveTrack::Spectrum:
|
||||
view = wxT("\"Spectrogram\"");
|
||||
break;
|
||||
default: view = wxT("NIL"); break;
|
||||
case WaveTrack::Waveform:
|
||||
view = (mCurTrack[0]->GetWaveformSettings().scaleType == 0) ? wxT("\"Waveform\"") : wxT("\"Waveform (dB)\"");
|
||||
break;
|
||||
case WaveTrack::Spectrum:
|
||||
view = wxT("\"Spectrogram\"");
|
||||
break;
|
||||
default: view = wxT("NIL"); break;
|
||||
}
|
||||
break;
|
||||
},
|
||||
#if defined(USE_MIDI)
|
||||
case Track::Note:
|
||||
[&](const NoteTrack *) {
|
||||
type = wxT("midi");
|
||||
view = wxT("\"Midi\"");
|
||||
break;
|
||||
},
|
||||
#endif
|
||||
case Track::Label:
|
||||
[&](const LabelTrack *) {
|
||||
type = wxT("label");
|
||||
view = wxT("\"Label\"");
|
||||
break;
|
||||
case Track::Time:
|
||||
},
|
||||
[&](const TimeTrack *) {
|
||||
type = wxT("time");
|
||||
view = wxT("\"Time\"");
|
||||
break;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
cmd += wxString::Format(wxT("(putprop '*TRACK* %d 'INDEX)\n"), ++mTrackIndex);
|
||||
cmd += wxString::Format(wxT("(putprop '*TRACK* \"%s\" 'NAME)\n"), mCurTrack[0]->GetName());
|
||||
|
Loading…
x
Reference in New Issue
Block a user