1
0
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:
Paul Licameli 2017-04-11 18:16:03 -04:00
parent fd10ed26cd
commit 51842fc78b
13 changed files with 806 additions and 872 deletions

View File

@ -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.

View File

@ -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);

View File

@ -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)

View File

@ -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();
}

View File

@ -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))

View File

@ -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

View File

@ -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)

View File

@ -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();

View File

@ -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"

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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());