1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-01 08:29:27 +02:00

Move code for Wave track menu items

This commit is contained in:
Paul Licameli 2015-08-02 22:39:32 -04:00 committed by Paul Licameli
parent ba5f6ce411
commit a313bcdb11
3 changed files with 826 additions and 737 deletions

View File

@ -173,7 +173,6 @@ is time to refresh some aspect of the screen.
#include "NumberScale.h"
#include "Prefs.h"
#include "RefreshCode.h"
#include "ShuttleGui.h"
#include "TrackArtist.h"
#include "TrackPanelAx.h"
#include "UndoManager.h"
@ -185,10 +184,8 @@ is time to refresh some aspect of the screen.
#include "ondemand/ODManager.h"
#include "prefs/PrefsDialog.h"
#include "prefs/SpectrumPrefs.h"
#include "prefs/TracksBehaviorsPrefs.h"
#include "prefs/WaveformPrefs.h"
#include "prefs/SpectrogramSettings.h"
#include "prefs/WaveformSettings.h"
#include "toolbars/ControlToolBar.h"
#include "toolbars/ToolsToolBar.h"
@ -286,38 +283,6 @@ template < class CLIPPEE, class CLIPVAL >
enum {
TrackPanelFirstID = 2000,
OnChannelLeftID,
OnChannelRightID,
OnChannelMonoID,
OnRate8ID, // <---
OnRate11ID, // |
OnRate16ID, // |
OnRate22ID, // |
OnRate44ID, // |
OnRate48ID, // | Leave these in order
OnRate88ID, // |
OnRate96ID, // |
OnRate176ID, // | see OnTrackMenu()
OnRate192ID, // |
OnRate352ID, // |
OnRate384ID, // |
OnRateOtherID, // |
// |
On16BitID, // |
On24BitID, // |
OnFloatID, // <---
OnWaveformID,
OnWaveformDBID,
OnSpectrumID,
OnSpectrogramSettingsID,
OnSplitStereoID,
OnSplitStereoMonoID,
OnMergeStereoID,
OnSwapChannelsID,
// Reserve an ample block of ids for waveform scale types
OnFirstWaveformScaleID,
OnLastWaveformScaleID = OnFirstWaveformScaleID + 9,
@ -343,18 +308,6 @@ BEGIN_EVENT_TABLE(TrackPanel, OverlayPanel)
EVT_KILL_FOCUS(TrackPanel::OnKillFocus)
EVT_CONTEXT_MENU(TrackPanel::OnContextMenu)
EVT_MENU_RANGE(OnChannelLeftID, OnChannelMonoID,
TrackPanel::OnChannelChange)
EVT_MENU_RANGE(OnWaveformID, OnSpectrumID, TrackPanel::OnSetDisplay)
EVT_MENU(OnSpectrogramSettingsID, TrackPanel::OnSpectrogramSettings)
EVT_MENU_RANGE(OnRate8ID, OnRate384ID, TrackPanel::OnRateChange)
EVT_MENU_RANGE(On16BitID, OnFloatID, TrackPanel::OnFormatChange)
EVT_MENU(OnRateOtherID, TrackPanel::OnRateOther)
EVT_MENU(OnSwapChannelsID, TrackPanel::OnSwapChannels)
EVT_MENU(OnSplitStereoID, TrackPanel::OnSplitStereo)
EVT_MENU(OnSplitStereoMonoID, TrackPanel::OnSplitStereoMono)
EVT_MENU(OnMergeStereoID, TrackPanel::OnMergeStereo)
EVT_MENU_RANGE(OnFirstWaveformScaleID, OnLastWaveformScaleID, TrackPanel::OnWaveformScaleType)
EVT_MENU_RANGE(OnFirstSpectrumScaleID, OnLastSpectrumScaleID, TrackPanel::OnSpectrumScaleType)
@ -461,12 +414,6 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id,
mAdjustLeftSelectionCursor = std::make_unique<wxCursor>(wxCURSOR_POINT_LEFT);
mAdjustRightSelectionCursor = std::make_unique<wxCursor>(wxCURSOR_POINT_RIGHT);
// Menu pointers are set to NULL here. Delay building of menus until after
// the command managter is finished, so that we can look up shortcut
// key strings that need to appear in some of the popup menus.
mWaveTrackMenu = NULL;
mChannelItemsInsertionPoint = 0;
mTrackArtist = std::make_unique<TrackArtist>();
mTrackArtist->SetMargins(1, kTopMargin, kRightMargin, kBottomMargin);
@ -559,67 +506,11 @@ SelectionState &TrackPanel::GetSelectionState()
return GetProject()->GetSelectionState();
}
void TrackPanel::BuildMenusIfNeeded(void)
{
if (!mRateMenu)
BuildMenus();
}
void TrackPanel::BuildMenus(void)
{
// Get rid of existing menus
DeleteMenus();
// Use AppendCheckItem so we can have ticks beside the items.
// We would use AppendRadioItem but it only currently works on windows and GTK.
auto rateMenu = std::make_unique<wxMenu>();
rateMenu->AppendRadioItem(OnRate8ID, wxT("8000 Hz"));
rateMenu->AppendRadioItem(OnRate11ID, wxT("11025 Hz"));
rateMenu->AppendRadioItem(OnRate16ID, wxT("16000 Hz"));
rateMenu->AppendRadioItem(OnRate22ID, wxT("22050 Hz"));
rateMenu->AppendRadioItem(OnRate44ID, wxT("44100 Hz"));
rateMenu->AppendRadioItem(OnRate48ID, wxT("48000 Hz"));
rateMenu->AppendRadioItem(OnRate88ID, wxT("88200 Hz"));
rateMenu->AppendRadioItem(OnRate96ID, wxT("96000 Hz"));
rateMenu->AppendRadioItem(OnRate176ID, wxT("176400 Hz"));
rateMenu->AppendRadioItem(OnRate192ID, wxT("192000 Hz"));
rateMenu->AppendRadioItem(OnRate352ID, wxT("352800 Hz"));
rateMenu->AppendRadioItem(OnRate384ID, wxT("384000 Hz"));
rateMenu->AppendRadioItem(OnRateOtherID, _("&Other..."));
auto formatMenu = std::make_unique<wxMenu>();
formatMenu->AppendRadioItem(On16BitID, GetSampleFormatStr(int16Sample));
formatMenu->AppendRadioItem(On24BitID, GetSampleFormatStr(int24Sample));
formatMenu->AppendRadioItem(OnFloatID, GetSampleFormatStr(floatSample));
/* build the pop-down menu used on wave (sampled audio) tracks */
mWaveTrackMenu = std::make_unique<wxMenu>();
mWaveTrackMenu->AppendRadioItem(OnWaveformID, _("Wa&veform"));
mWaveTrackMenu->AppendRadioItem(OnWaveformDBID, _("&Waveform (dB)"));
mWaveTrackMenu->AppendRadioItem(OnSpectrumID, _("&Spectrogram"));
mWaveTrackMenu->Append(OnSpectrogramSettingsID, _("S&pectrogram Settings..."));
mWaveTrackMenu->AppendSeparator();
// include both mono and stereo items as a work around for bug 1250
// mWaveTrackMenu->AppendRadioItem(OnChannelMonoID, _("&Mono"));
// mWaveTrackMenu->AppendRadioItem(OnChannelLeftID, _("&Left Channel"));
// mWaveTrackMenu->AppendRadioItem(OnChannelRightID, _("&Right Channel"));
mWaveTrackMenu->Append(OnMergeStereoID, _("Ma&ke Stereo Track"));
mWaveTrackMenu->Append(OnSwapChannelsID, _("Swap Stereo &Channels"));
mWaveTrackMenu->Append(OnSplitStereoID, _("Spl&it Stereo Track"));
// DA: Uses split stereo track and then drag pan sliders for split-stereo-to-mono
#ifndef EXPERIMENTAL_DA
mWaveTrackMenu->Append(OnSplitStereoMonoID, _("Split Stereo to Mo&no"));
#endif
mWaveTrackMenu->AppendSeparator();
mWaveTrackMenu->Append(0, _("&Format"), (mFormatMenu = formatMenu.release()));
mWaveTrackMenu->AppendSeparator();
mWaveTrackMenu->Append(0, _("Rat&e"), (mRateMenu = rateMenu.release()));
/*
mRulerWaveformMenu = std::make_unique<wxMenu>();
BuildVRulerMenuItems
@ -651,12 +542,6 @@ void TrackPanel::BuildVRulerMenuItems
void TrackPanel::DeleteMenus(void)
{
// Note that the submenus (mRateMenu, ...)
// are deleted by their parent
mRateMenu = mFormatMenu = nullptr;
mWaveTrackMenu.reset();
mRulerWaveformMenu.reset();
mRulerSpectrumMenu.reset();
}
@ -5885,8 +5770,6 @@ void TrackPanel::ScrollIntoView(int x)
void TrackPanel::OnTrackMenu(Track *t)
{
BuildMenusIfNeeded();
if(!t) {
t = GetFocusedTrack();
if(!t)
@ -5904,74 +5787,8 @@ void TrackPanel::OnTrackMenu(Track *t)
mPopupMenuTarget = t;
Track *next = mTracks->GetNext(t);
wxMenu *theMenu = NULL;
if (t->GetKind() == Track::Wave) {
theMenu = mWaveTrackMenu.get();
const bool isMono = !t->GetLinked();
const bool canMakeStereo =
(next && isMono && !next->GetLinked() &&
next->GetKind() == Track::Wave);
// Unsafe to change channels during real-time preview (bug 1560)
bool unsafe = EffectManager::Get().RealtimeIsActive() && IsUnsafe();
theMenu->Enable(OnSwapChannelsID, t->GetLinked() && !unsafe);
theMenu->Enable(OnMergeStereoID, canMakeStereo && !unsafe);
theMenu->Enable(OnSplitStereoID, t->GetLinked() && !unsafe);
// Several menu items no longer needed....
#if 0
theMenu->Enable(OnSplitStereoMonoID, t->GetLinked() && !unsafe);
// We only need to set check marks. Clearing checks causes problems on Linux (bug 851)
// + Setting unchecked items to false is to get round a linux bug
switch (t->GetChannel()) {
case Track::LeftChannel:
theMenu->Check(OnChannelLeftID, true);
theMenu->Check(OnChannelRightID, false);
theMenu->Check(OnChannelMonoID, false);
break;
case Track::RightChannel:
theMenu->Check(OnChannelRightID, true);
theMenu->Check(OnChannelLeftID, false);
theMenu->Check(OnChannelMonoID, false);
break;
default:
theMenu->Check(OnChannelMonoID, true);
theMenu->Check(OnChannelLeftID, false);
theMenu->Check(OnChannelRightID, false);
}
theMenu->Enable(OnChannelMonoID, !t->GetLinked());
theMenu->Enable(OnChannelLeftID, !t->GetLinked());
theMenu->Enable(OnChannelRightID, !t->GetLinked());
#endif
WaveTrack *const track = (WaveTrack *)t;
const int display = track->GetDisplay();
theMenu->Check(
(display == WaveTrack::Waveform)
? (track->GetWaveformSettings().isLinear() ? OnWaveformID : OnWaveformDBID)
: OnSpectrumID,
true
);
// Bug 1253. Shouldn't open preferences if audio is busy.
// We can't change them on the fly yet anyway.
const bool bAudioBusy = gAudioIO->IsBusy();
theMenu->Enable(OnSpectrogramSettingsID,
(display == WaveTrack::Spectrum) && !bAudioBusy);
SetMenuCheck(*mRateMenu, IdOfRate((int) track->GetRate()));
SetMenuCheck(*mFormatMenu, IdOfFormat(track->GetSampleFormat()));
unsafe = IsUnsafe();
for (int i = OnRate8ID; i <= OnFloatID; i++) {
theMenu->Enable(i, !unsafe);
}
}
if (theMenu) {
//We need to find the location of the menu rectangle.
const wxRect rect = FindTrackRect(t,true);
@ -6177,521 +5994,6 @@ void TrackPanel::DrawShadow(Track * /* t */ , wxDC * dc, const wxRect & rect)
AColor::Line(*dc, right, rect.y, right, rect.y + 1);
}
/// Handle the menu options that change a track between
/// left channel, right channel, and mono.
static int channels[] = { Track::LeftChannel, Track::RightChannel,
Track::MonoChannel
};
static const wxChar *channelmsgs[] = { _("Left Channel"), _("Right Channel"),
_("Mono")
};
void TrackPanel::OnChannelChange(wxCommandEvent & event)
{
int id = event.GetId();
wxASSERT(id >= OnChannelLeftID && id <= OnChannelMonoID);
wxASSERT(mPopupMenuTarget);
mPopupMenuTarget->SetChannel(channels[id - OnChannelLeftID]);
MakeParentPushState(wxString::Format(_("Changed '%s' to %s"),
mPopupMenuTarget->GetName().c_str(),
channelmsgs[id - OnChannelLeftID]),
_("Channel"));
Refresh(false);
}
/// Swap the left and right channels of a stero track...
void TrackPanel::OnSwapChannels(wxCommandEvent & WXUNUSED(event))
{
Track *partner = mPopupMenuTarget->GetLink();
Track *const focused = GetFocusedTrack();
const bool hasFocus =
(focused == mPopupMenuTarget || focused == partner);
SplitStereo(false);
mPopupMenuTarget->SetChannel(Track::RightChannel);
partner->SetChannel(Track::LeftChannel);
(mTracks->MoveUp(partner));
partner->SetLinked(true);
MixerBoard* pMixerBoard = this->GetMixerBoard();
if (pMixerBoard) {
pMixerBoard->UpdateTrackClusters();
}
if (hasFocus)
SetFocusedTrack(partner);
MakeParentPushState(wxString::Format(_("Swapped Channels in '%s'"),
mPopupMenuTarget->GetName().c_str()),
_("Swap Channels"));
}
/// Split a stereo track into two tracks...
void TrackPanel::OnSplitStereo(wxCommandEvent & WXUNUSED(event))
{
SplitStereo(true);
MakeParentPushState(wxString::Format(_("Split stereo track '%s'"),
mPopupMenuTarget->GetName().c_str()),
_("Split"));
}
/// Split a stereo track into two mono tracks...
void TrackPanel::OnSplitStereoMono(wxCommandEvent & WXUNUSED(event))
{
SplitStereo(false);
MakeParentPushState(wxString::Format(_("Split Stereo to Mono '%s'"),
mPopupMenuTarget->GetName().c_str()),
_("Split to Mono"));
}
/// Split a stereo track into two tracks...
void TrackPanel::SplitStereo(bool stereo)
{
wxASSERT(mPopupMenuTarget);
if (stereo){
mPopupMenuTarget->SetPanFromChannelType();
}
mPopupMenuTarget->SetChannel(Track::MonoChannel);
// Assume partner is present, and is wave
auto partner = static_cast<WaveTrack*>(mPopupMenuTarget->GetLink());
wxASSERT(partner);
if (!partner)
return;
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
if(!stereo && MONO_WAVE_PAN(mPopupMenuTarget))
// Come here only from wave track menu
static_cast<WaveTrack*>(mPopupMenuTarget)->SetVirtualState(true,true);
if(!stereo && MONO_WAVE_PAN(partner))
partner->SetVirtualState(true,true);
#endif
if (partner)
{
partner->SetName(mPopupMenuTarget->GetName());
if (stereo){
partner->SetPanFromChannelType();
}
partner->SetChannel(Track::MonoChannel); // Keep original stereo track name.
//On Demand - have each channel add it's own.
if (ODManager::IsInstanceCreated() && partner->GetKind() == Track::Wave)
ODManager::Instance()->MakeWaveTrackIndependent(partner);
}
mPopupMenuTarget->SetLinked(false);
//make sure neither track is smaller than its minimum height
if (mPopupMenuTarget->GetHeight() < mPopupMenuTarget->GetMinimizedHeight())
mPopupMenuTarget->SetHeight(mPopupMenuTarget->GetMinimizedHeight());
if (partner)
{
if (partner->GetHeight() < partner->GetMinimizedHeight())
partner->SetHeight(partner->GetMinimizedHeight());
// Make tracks the same height
if (mPopupMenuTarget->GetHeight() != partner->GetHeight())
{
mPopupMenuTarget->SetHeight(((mPopupMenuTarget->GetHeight())+(partner->GetHeight())) / 2.0);
partner->SetHeight(mPopupMenuTarget->GetHeight());
}
}
Refresh(false);
}
/// Merge two tracks into one stereo track ??
void TrackPanel::OnMergeStereo(wxCommandEvent & WXUNUSED(event))
{
wxASSERT(mPopupMenuTarget);
mPopupMenuTarget->SetLinked(true);
Track *partner = mPopupMenuTarget->GetLink();
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
if(MONO_WAVE_PAN(mPopupMenuTarget))
((WaveTrack*)mPopupMenuTarget)->SetVirtualState(false);
if(MONO_WAVE_PAN(partner))
((WaveTrack*)partner)->SetVirtualState(false);
#endif
if (partner) {
// Set partner's parameters to match target.
partner->Merge(*mPopupMenuTarget);
mPopupMenuTarget->SetPan( 0.0f );
mPopupMenuTarget->SetChannel(Track::LeftChannel);
partner->SetPan( 0.0f );
partner->SetChannel(Track::RightChannel);
// Set NEW track heights and minimized state
bool bBothMinimizedp=((mPopupMenuTarget->GetMinimized())&&(partner->GetMinimized()));
mPopupMenuTarget->SetMinimized(false);
partner->SetMinimized(false);
int AverageHeight=(mPopupMenuTarget->GetHeight() + partner->GetHeight())/ 2;
mPopupMenuTarget->SetHeight(AverageHeight);
partner->SetHeight(AverageHeight);
mPopupMenuTarget->SetMinimized(bBothMinimizedp);
partner->SetMinimized(bBothMinimizedp);
//On Demand - join the queues together.
WaveTrack *wt, *pwt;
if(ODManager::IsInstanceCreated() &&
// Assume linked track is wave or null
nullptr != (pwt = static_cast<WaveTrack*>(partner)) &&
// Come here only from the wave track menu
nullptr != (wt = static_cast<WaveTrack*>(mPopupMenuTarget)))
if(!ODManager::Instance()->MakeWaveTrackDependent(pwt, wt))
{
;
//TODO: in the future, we will have to check the return value of MakeWaveTrackDependent -
//if the tracks cannot merge, it returns false, and in that case we should not allow a merging.
//for example it returns false when there are two different types of ODTasks on each track's queue.
//we will need to display this to the user.
}
MakeParentPushState(wxString::Format(_("Made '%s' a stereo track"),
mPopupMenuTarget->GetName().
c_str()),
_("Make Stereo"));
} else
mPopupMenuTarget->SetLinked(false);
Refresh(false);
}
class ViewSettingsDialog final : public PrefsDialog
{
public:
ViewSettingsDialog
(wxWindow *parent, const wxString &title, PrefsDialog::Factories &factories,
int page)
: PrefsDialog(parent, title, factories)
, mPage(page)
{
}
long GetPreferredPage() override
{
return mPage;
}
void SavePreferredPage() override
{
}
private:
const int mPage;
};
void TrackPanel::OnSpectrogramSettings(wxCommandEvent &)
{
if (gAudioIO->IsBusy()){
wxMessageBox(_("To change Spectrogram Settings, stop any\n."
"playing or recording first."),
_("Stop the Audio First"), wxOK | wxICON_EXCLAMATION | wxCENTRE);
return;
}
// Get here only from the wave track menu
const auto wt = static_cast<WaveTrack*>(mPopupMenuTarget);
// WaveformPrefsFactory waveformFactory(wt);
//TracksBehaviorsPrefsFactory tracksBehaviorsFactory();
SpectrumPrefsFactory spectrumFactory(wt);
PrefsDialog::Factories factories;
// factories.push_back(&waveformFactory);
factories.push_back(&spectrumFactory);
const int page = (wt->GetDisplay() == WaveTrack::Spectrum)
? 1 : 0;
wxString title(wt->GetName() + wxT(": "));
ViewSettingsDialog dialog(this, title, factories, page);
if (0 != dialog.ShowModal()) {
MakeParentModifyState(true);
// Redraw
Refresh(false);
}
}
/// Set the Display mode based on the menu choice in the Track Menu.
/// Note that gModes MUST BE IN THE SAME ORDER AS THE MENU CHOICES!!
/// const wxChar *gModes[] = { wxT("waveform"), wxT("waveformDB"),
/// wxT("spectrum"), wxT("pitch") };
void TrackPanel::OnSetDisplay(wxCommandEvent & event)
{
int idInt = event.GetId();
wxASSERT(idInt >= OnWaveformID && idInt <= OnSpectrumID);
wxASSERT(mPopupMenuTarget
&& mPopupMenuTarget->GetKind() == Track::Wave);
bool linear = false;
WaveTrack::WaveTrackDisplay id;
switch (idInt) {
default:
case OnWaveformID:
linear = true, id = WaveTrack::Waveform; break;
case OnWaveformDBID:
id = WaveTrack::Waveform; break;
case OnSpectrumID:
id = WaveTrack::Spectrum; break;
}
WaveTrack *wt = (WaveTrack *) mPopupMenuTarget;
const bool wrongType = wt->GetDisplay() != id;
const bool wrongScale =
(id == WaveTrack::Waveform &&
wt->GetWaveformSettings().isLinear() != linear);
if (wrongType || wrongScale) {
wt->SetLastScaleType();
wt->SetDisplay(WaveTrack::WaveTrackDisplay(id));
if (wrongScale)
wt->GetIndependentWaveformSettings().scaleType = linear
? WaveformSettings::stLinear
: WaveformSettings::stLogarithmic;
// Assume linked track is wave or null
const auto l = static_cast<WaveTrack*>(wt->GetLink());
if (l) {
l->SetLastScaleType();
l->SetDisplay(WaveTrack::WaveTrackDisplay(id));
if (wrongScale)
l->GetIndependentWaveformSettings().scaleType = linear
? WaveformSettings::stLinear
: WaveformSettings::stLogarithmic;
}
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
if (wt->GetDisplay() == WaveTrack::Waveform) {
wt->SetVirtualState(false);
}else if (id == WaveTrack::Waveform) {
wt->SetVirtualState(true);
}
#endif
UpdateVRuler(wt);
MakeParentModifyState(true);
Refresh(false);
}
}
/// Sets the sample rate for a track, and if it is linked to
/// another track, that one as well.
void TrackPanel::SetRate(WaveTrack * wt, double rate)
{
wt->SetRate(rate);
// Assume linked track is wave or null
const auto partner = static_cast<WaveTrack*>(wt->GetLink());
if (partner)
partner->SetRate(rate);
// Separate conversion of "rate" enables changing the decimals without affecting i18n
wxString rateString = wxString::Format(wxT("%.3f"), rate);
MakeParentPushState(wxString::Format(_("Changed '%s' to %s Hz"),
wt->GetName().c_str(), rateString.c_str()),
_("Rate Change"));
}
/// Handles the selection from the Format submenu of the
/// track menu.
void TrackPanel::OnFormatChange(wxCommandEvent & event)
{
BuildMenusIfNeeded();
int id = event.GetId();
wxASSERT(id >= On16BitID && id <= OnFloatID);
wxASSERT(mPopupMenuTarget
&& mPopupMenuTarget->GetKind() == Track::Wave);
sampleFormat newFormat = int16Sample;
switch (id) {
case On16BitID:
newFormat = int16Sample;
break;
case On24BitID:
newFormat = int24Sample;
break;
case OnFloatID:
newFormat = floatSample;
break;
default:
// ERROR -- should not happen
wxASSERT(false);
break;
}
if (newFormat == ((WaveTrack*)mPopupMenuTarget)->GetSampleFormat())
return; // Nothing to do.
((WaveTrack*)mPopupMenuTarget)->ConvertToSampleFormat(newFormat);
// Assume linked track is wave or null
const auto partner =
static_cast<WaveTrack*>(mPopupMenuTarget->GetLink());
if (partner)
partner->ConvertToSampleFormat(newFormat);
MakeParentPushState(wxString::Format(_("Changed '%s' to %s"),
mPopupMenuTarget->GetName().
c_str(),
GetSampleFormatStr(newFormat)),
_("Format Change"));
SetMenuCheck( *mFormatMenu, id );
MakeParentRedrawScrollbars();
Refresh(false);
}
/// Converts a format enumeration to a wxWidgets menu item Id.
int TrackPanel::IdOfFormat( int format )
{
switch (format) {
case int16Sample:
return On16BitID;
case int24Sample:
return On24BitID;
case floatSample:
return OnFloatID;
default:
// ERROR -- should not happen
wxASSERT( false );
break;
}
return OnFloatID;// Compiler food.
}
/// Puts a check mark at a given position in a menu.
void TrackPanel::SetMenuCheck( wxMenu & menu, int newId )
{
auto & list = menu.GetMenuItems();
for ( auto item : list )
{
auto id = item->GetId();
// We only need to set check marks. Clearing checks causes problems on Linux (bug 851)
if (id==newId)
menu.Check( id, true );
}
}
const int nRates=12;
/// gRates MUST CORRESPOND DIRECTLY TO THE RATES AS LISTED IN THE MENU!!
/// IN THE SAME ORDER!!
static int gRates[nRates] = { 8000, 11025, 16000, 22050, 44100, 48000, 88200, 96000,
176400, 192000, 352800, 384000 };
/// This method handles the selection from the Rate
/// submenu of the track menu, except for "Other" (/see OnRateOther).
void TrackPanel::OnRateChange(wxCommandEvent & event)
{
BuildMenusIfNeeded();
int id = event.GetId();
wxASSERT(id >= OnRate8ID && id <= OnRate384ID);
SetMenuCheck( *mRateMenu, id );
// Come here only from wave track menu
SetRate(static_cast<WaveTrack*>(mPopupMenuTarget), gRates[id - OnRate8ID]);
MakeParentRedrawScrollbars();
Refresh(false);
}
/// Converts a sampling rate to a wxWidgets menu item id
int TrackPanel::IdOfRate( int rate )
{
for(int i=0;i<nRates;i++) {
if( gRates[i] == rate )
return i+OnRate8ID;
}
return OnRateOtherID;
}
void TrackPanel::OnRateOther(wxCommandEvent &event)
{
BuildMenusIfNeeded();
// Come here only from the wave track menu
const auto wt = static_cast<WaveTrack*>(mPopupMenuTarget);
wxASSERT(wt);
int newRate;
/// \todo Remove artificial constants!!
/// \todo Make a real dialog box out of this!!
while (true)
{
wxDialogWrapper dlg(this, wxID_ANY, wxString(_("Set Rate")));
dlg.SetName(dlg.GetTitle());
ShuttleGui S(&dlg, eIsCreating);
wxString rate;
wxArrayString rates;
wxComboBox *cb;
rate.Printf(wxT("%ld"), lrint(((WaveTrack *) mPopupMenuTarget)->GetRate()));
rates.Add(wxT("8000"));
rates.Add(wxT("11025"));
rates.Add(wxT("16000"));
rates.Add(wxT("22050"));
rates.Add(wxT("44100"));
rates.Add(wxT("48000"));
rates.Add(wxT("88200"));
rates.Add(wxT("96000"));
rates.Add(wxT("176400"));
rates.Add(wxT("192000"));
rates.Add(wxT("352800"));
rates.Add(wxT("384000"));
S.StartVerticalLay(true);
{
S.SetBorder(10);
S.StartHorizontalLay(wxEXPAND, false);
{
cb = S.AddCombo(_("New sample rate (Hz):"),
rate,
&rates);
#if defined(__WXMAC__)
// As of wxMac-2.8.12, setting manually is required
// to handle rates not in the list. See: Bug #427
cb->SetValue(rate);
#endif
}
S.EndHorizontalLay();
S.AddStandardButtons();
}
S.EndVerticalLay();
dlg.SetClientSize(dlg.GetSizer()->CalcMin());
dlg.Center();
if (dlg.ShowModal() != wxID_OK)
{
return; // user cancelled dialog
}
long lrate;
if (cb->GetValue().ToLong(&lrate) && lrate >= 1 && lrate <= 1000000)
{
newRate = (int)lrate;
break;
}
wxMessageBox(_("The entered value is invalid"), _("Error"),
wxICON_ERROR, this);
}
SetMenuCheck( *mRateMenu, event.GetId() );
SetRate(wt, newRate);
MakeParentRedrawScrollbars();
Refresh(false);
}
void TrackPanel::OnWaveformScaleType(wxCommandEvent &evt)
{
// Get here only from vertical ruler menu for wave tracks

View File

@ -258,7 +258,6 @@ class AUDACITY_DLL_API TrackPanel final : public OverlayPanel {
virtual ~ TrackPanel();
virtual void BuildMenusIfNeeded(void);
virtual void BuildMenus(void);
virtual void DeleteMenus(void);
@ -489,10 +488,6 @@ protected:
virtual void MakeParentModifyState(bool bWantsAutoSave); // if true, writes auto-save file. Should set only if you really want the state change restored after
// a crash, as it can take many seconds for large (eg. 10 track-hours) projects
virtual void OnChannelChange(wxCommandEvent &event);
virtual void OnSpectrogramSettings(wxCommandEvent &event);
virtual void OnSetDisplay (wxCommandEvent &event);
virtual void OnWaveformScaleType(wxCommandEvent &event);
virtual void OnSpectrumScaleType(wxCommandEvent &event);
@ -500,19 +495,6 @@ protected:
virtual void OnZoomOutVertical(wxCommandEvent &event);
virtual void OnZoomFitVertical(wxCommandEvent &event);
virtual void SetMenuCheck( wxMenu & menu, int newId );
virtual void SetRate(WaveTrack *pTrack, double rate);
virtual void OnRateChange(wxCommandEvent &event);
virtual void OnRateOther(wxCommandEvent &event);
virtual void OnFormatChange(wxCommandEvent &event);
virtual void OnSwapChannels(wxCommandEvent &event);
virtual void OnSplitStereo(wxCommandEvent &event);
virtual void OnSplitStereoMono(wxCommandEvent &event);
virtual void SplitStereo(bool stereo);
virtual void OnMergeStereo(wxCommandEvent &event);
// Find track info by coordinate
enum class CellType { Label, Track, VRuler, Background };
struct FoundCell {
@ -570,10 +552,6 @@ public:
virtual void SetBackgroundCell
(const std::shared_ptr< TrackPanelCell > &pCell);
protected:
virtual int IdOfRate( int rate );
virtual int IdOfFormat( int format );
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
void UpdateVirtualStereoOrder();
#endif
@ -789,16 +767,9 @@ protected:
mStretchCursor, mStretchLeftCursor, mStretchRightCursor;
#endif
std::unique_ptr<wxMenu> mWaveTrackMenu;
size_t mChannelItemsInsertionPoint {};
std::unique_ptr<wxMenu>
mRulerWaveformMenu, mRulerSpectrumMenu;
// These sub-menus are owned by parent menus,
// so not unique_ptrs
wxMenu *mRateMenu{}, *mFormatMenu{};
Track *mPopupMenuTarget {};
friend class TrackPanelAx;

View File

@ -9,15 +9,44 @@ Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#include "../../../../Audacity.h"
#include "../../../../Experimental.h"
#include "WaveTrackControls.h"
#include "../../ui/PlayableTrackButtonHandles.h"
#include "WaveTrackSliderHandles.h"
#include "../../../../AudioIO.h"
#include "../../../../HitTestResult.h"
#include "../../../../MixerBoard.h"
#include "../../../../Project.h"
#include "../../../../RefreshCode.h"
#include "../../../../WaveTrack.h"
#include "../../../../ShuttleGui.h"
#include "../../../../TrackPanel.h"
#include "../../../../TrackPanelMouseEvent.h"
#include "../../../../widgets/PopupMenuTable.h"
#include "../../../../ondemand/ODManager.h"
#include "../../../../prefs/PrefsDialog.h"
#include "../../../../prefs/SpectrumPrefs.h"
#include "../../../../prefs/TracksBehaviorsPrefs.h"
#include "../../../../prefs/WaveformPrefs.h"
#include <wx/combobox.h>
namespace
{
/// Puts a check mark at a given position in a menu.
template<typename Pred>
void SetMenuChecks(wxMenu & menu, const Pred &pred)
{
for (auto &item : menu.GetMenuItems())
{
if (item->IsCheckable()) {
auto id = item->GetId();
menu.Check(id, pred(id));
}
}
}
}
WaveTrackControls::WaveTrackControls()
{
@ -67,25 +96,387 @@ HitTestResult WaveTrackControls::HitTest
return TrackControls::HitTest(evt, pProject);
}
enum {
OnRate8ID = 2001, // <---
OnRate11ID, // |
OnRate16ID, // |
OnRate22ID, // |
OnRate44ID, // |
OnRate48ID, // | Leave these in order
OnRate88ID, // |
OnRate96ID, // |
OnRate176ID, // |
OnRate192ID, // |
OnRate352ID, // |
OnRate384ID, // |
OnRateOtherID, // |
// |
On16BitID, // |
On24BitID, // |
OnFloatID, // <---
OnWaveformID,
OnWaveformDBID,
OnSpectrumID,
OnSpectrogramSettingsID,
OnChannelLeftID,
OnChannelRightID,
OnChannelMonoID,
OnMergeStereoID,
OnSwapChannelsID,
OnSplitStereoID,
OnSplitStereoMonoID,
ChannelMenuID,
};
//=============================================================================
// Table class for a sub-menu
class FormatMenuTable : public PopupMenuTable
{
FormatMenuTable() : mpData(NULL) {}
DECLARE_POPUP_MENU(FormatMenuTable);
public:
static FormatMenuTable &Instance();
private:
void InitMenu(Menu *pMenu, void *pUserData) override;
void DestroyMenu() override
{
mpData = NULL;
}
TrackControls::InitMenuData *mpData;
int IdOfFormat(int format);
void OnFormatChange(wxCommandEvent & event);
};
FormatMenuTable &FormatMenuTable::Instance()
{
static FormatMenuTable instance;
return instance;
}
void FormatMenuTable::InitMenu(Menu *pMenu, void *pUserData)
{
mpData = static_cast<TrackControls::InitMenuData*>(pUserData);
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
auto formatId = IdOfFormat(pTrack->GetSampleFormat());
SetMenuChecks(*pMenu, [=](int id){ return id == formatId; });
AudacityProject *const project = ::GetActiveProject();
bool unsafe = project->IsAudioActive();
for (int i = On16BitID; i <= OnFloatID; i++) {
pMenu->Enable(i, !unsafe);
}
}
BEGIN_POPUP_MENU(FormatMenuTable)
POPUP_MENU_RADIO_ITEM(On16BitID,
GetSampleFormatStr(int16Sample), OnFormatChange)
POPUP_MENU_RADIO_ITEM(On24BitID,
GetSampleFormatStr(int24Sample), OnFormatChange)
POPUP_MENU_RADIO_ITEM(OnFloatID,
GetSampleFormatStr(floatSample), OnFormatChange)
END_POPUP_MENU()
/// Converts a format enumeration to a wxWidgets menu item Id.
int FormatMenuTable::IdOfFormat(int format)
{
switch (format) {
case int16Sample:
return On16BitID;
case int24Sample:
return On24BitID;
case floatSample:
return OnFloatID;
default:
// ERROR -- should not happen
wxASSERT(false);
break;
}
return OnFloatID;// Compiler food.
}
/// Handles the selection from the Format submenu of the
/// track menu.
void FormatMenuTable::OnFormatChange(wxCommandEvent & event)
{
int id = event.GetId();
wxASSERT(id >= On16BitID && id <= OnFloatID);
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
wxASSERT(pTrack && pTrack->GetKind() == Track::Wave);
sampleFormat newFormat = int16Sample;
switch (id) {
case On16BitID:
newFormat = int16Sample;
break;
case On24BitID:
newFormat = int24Sample;
break;
case OnFloatID:
newFormat = floatSample;
break;
default:
// ERROR -- should not happen
wxASSERT(false);
break;
}
if (newFormat == pTrack->GetSampleFormat())
return; // Nothing to do.
AudacityProject *const project = ::GetActiveProject();
TrackList *const tracks = project->GetTracks();
pTrack->ConvertToSampleFormat(newFormat);
// Assume partner is wave or null
const auto partner = static_cast<WaveTrack*>(pTrack->GetLink());
if (partner)
partner->ConvertToSampleFormat(newFormat);
project->PushState(wxString::Format(_("Changed '%s' to %s"),
pTrack->GetName().
c_str(),
GetSampleFormatStr(newFormat)),
_("Format Change"));
using namespace RefreshCode;
mpData->result = RefreshAll | FixScrollbars;
}
//=============================================================================
// Table class for a sub-menu
class RateMenuTable : public PopupMenuTable
{
RateMenuTable() : mpData(NULL) {}
DECLARE_POPUP_MENU(RateMenuTable);
public:
static RateMenuTable &Instance();
private:
void InitMenu(Menu *pMenu, void *pUserData) override;
void DestroyMenu() override
{
mpData = NULL;
}
TrackControls::InitMenuData *mpData;
int IdOfRate(int rate);
void SetRate(WaveTrack * pTrack, double rate);
void OnRateChange(wxCommandEvent & event);
void OnRateOther(wxCommandEvent & event);
};
RateMenuTable &RateMenuTable::Instance()
{
static RateMenuTable instance;
return instance;
}
void RateMenuTable::InitMenu(Menu *pMenu, void *pUserData)
{
mpData = static_cast<TrackControls::InitMenuData*>(pUserData);
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
const auto rateId = IdOfRate((int)pTrack->GetRate());
SetMenuChecks(*pMenu, [=](int id){ return id == rateId; });
AudacityProject *const project = ::GetActiveProject();
bool unsafe = project->IsAudioActive();
for (int i = OnRate8ID; i <= OnRateOtherID; i++) {
pMenu->Enable(i, !unsafe);
}
}
BEGIN_POPUP_MENU(RateMenuTable)
POPUP_MENU_RADIO_ITEM(OnRate8ID, _("8000 Hz"), OnRateChange)
POPUP_MENU_RADIO_ITEM(OnRate11ID, _("11025 Hz"), OnRateChange)
POPUP_MENU_RADIO_ITEM(OnRate16ID, _("16000 Hz"), OnRateChange)
POPUP_MENU_RADIO_ITEM(OnRate22ID, _("22050 Hz"), OnRateChange)
POPUP_MENU_RADIO_ITEM(OnRate44ID, _("44100 Hz"), OnRateChange)
POPUP_MENU_RADIO_ITEM(OnRate48ID, _("48000 Hz"), OnRateChange)
POPUP_MENU_RADIO_ITEM(OnRate88ID, _("88200 Hz"), OnRateChange)
POPUP_MENU_RADIO_ITEM(OnRate96ID, _("96000 Hz"), OnRateChange)
POPUP_MENU_RADIO_ITEM(OnRate176ID, _("176400 Hz"), OnRateChange)
POPUP_MENU_RADIO_ITEM(OnRate192ID, _("192000 Hz"), OnRateChange)
POPUP_MENU_RADIO_ITEM(OnRate352ID, _("352800 Hz"), OnRateChange)
POPUP_MENU_RADIO_ITEM(OnRate384ID, _("384000 Hz"), OnRateChange)
POPUP_MENU_RADIO_ITEM(OnRateOtherID, _("&Other..."), OnRateOther)
END_POPUP_MENU()
const int nRates = 12;
/// gRates MUST CORRESPOND DIRECTLY TO THE RATES AS LISTED IN THE MENU!!
/// IN THE SAME ORDER!!
static int gRates[nRates] = { 8000, 11025, 16000, 22050, 44100, 48000, 88200, 96000,
176400, 192000, 352800, 384000 };
/// Converts a sampling rate to a wxWidgets menu item id
int RateMenuTable::IdOfRate(int rate)
{
for (int i = 0; i<nRates; i++) {
if (gRates[i] == rate)
return i + OnRate8ID;
}
return OnRateOtherID;
}
/// Sets the sample rate for a track, and if it is linked to
/// another track, that one as well.
void RateMenuTable::SetRate(WaveTrack * pTrack, double rate)
{
AudacityProject *const project = ::GetActiveProject();
TrackList *const tracks = project->GetTracks();
pTrack->SetRate(rate);
// Assume linked track is wave or null
const auto partner = static_cast<WaveTrack*>(pTrack->GetLink());
if (partner)
partner->SetRate(rate);
// Separate conversion of "rate" enables changing the decimals without affecting i18n
wxString rateString = wxString::Format(wxT("%.3f"), rate);
project->PushState(wxString::Format(_("Changed '%s' to %s Hz"),
pTrack->GetName().c_str(), rateString.c_str()),
_("Rate Change"));
}
/// This method handles the selection from the Rate
/// submenu of the track menu, except for "Other" (/see OnRateOther).
void RateMenuTable::OnRateChange(wxCommandEvent & event)
{
int id = event.GetId();
wxASSERT(id >= OnRate8ID && id <= OnRate384ID);
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
wxASSERT(pTrack->GetKind() == Track::Wave);
SetRate(pTrack, gRates[id - OnRate8ID]);
using namespace RefreshCode;
mpData->result = RefreshAll | FixScrollbars;
}
void RateMenuTable::OnRateOther(wxCommandEvent &)
{
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
wxASSERT(pTrack && pTrack->GetKind() == Track::Wave);
int newRate;
/// \todo Remove artificial constants!!
/// \todo Make a real dialog box out of this!!
while (true)
{
wxDialogWrapper dlg(mpData->pParent, wxID_ANY, wxString(_("Set Rate")));
dlg.SetName(dlg.GetTitle());
ShuttleGui S(&dlg, eIsCreating);
wxString rate;
wxArrayString rates;
wxComboBox *cb;
rate.Printf(wxT("%ld"), lrint(pTrack->GetRate()));
rates.Add(wxT("8000"));
rates.Add(wxT("11025"));
rates.Add(wxT("16000"));
rates.Add(wxT("22050"));
rates.Add(wxT("44100"));
rates.Add(wxT("48000"));
rates.Add(wxT("88200"));
rates.Add(wxT("96000"));
rates.Add(wxT("176400"));
rates.Add(wxT("192000"));
rates.Add(wxT("352800"));
rates.Add(wxT("384000"));
S.StartVerticalLay(true);
{
S.SetBorder(10);
S.StartHorizontalLay(wxEXPAND, false);
{
cb = S.AddCombo(_("New sample rate (Hz):"),
rate,
&rates);
#if defined(__WXMAC__)
// As of wxMac-2.8.12, setting manually is required
// to handle rates not in the list. See: Bug #427
cb->SetValue(rate);
#endif
}
S.EndHorizontalLay();
S.AddStandardButtons();
}
S.EndVerticalLay();
dlg.SetClientSize(dlg.GetSizer()->CalcMin());
dlg.Center();
if (dlg.ShowModal() != wxID_OK)
{
return; // user cancelled dialog
}
long lrate;
if (cb->GetValue().ToLong(&lrate) && lrate >= 1 && lrate <= 1000000)
{
newRate = (int)lrate;
break;
}
wxMessageBox(_("The entered value is invalid"), _("Error"),
wxICON_ERROR, mpData->pParent);
}
SetRate(pTrack, newRate);
using namespace RefreshCode;
mpData->result = RefreshAll | FixScrollbars;
}
//=============================================================================
// Class defining common command handlers for mono and stereo tracks
class WaveTrackMenuTable : public PopupMenuTable
{
WaveTrackMenuTable() : mpData(NULL) {}
DECLARE_POPUP_MENU(WaveTrackMenuTable);
public:
static WaveTrackMenuTable &Instance();
void InitMenu(Menu*, void *pUserData) override
{
mpData = static_cast<TrackControls::InitMenuData*>(pUserData);
}
protected:
WaveTrackMenuTable() : mpData(NULL) {}
void InitMenu(Menu *pMenu, void *pUserData) override;
void DestroyMenu() override
{
mpData = nullptr;
}
DECLARE_POPUP_MENU(WaveTrackMenuTable);
TrackControls::InitMenuData *mpData;
void OnSetDisplay(wxCommandEvent & event);
void OnSpectrogramSettings(wxCommandEvent & event);
void OnChannelChange(wxCommandEvent & event);
void OnMergeStereo(wxCommandEvent & event);
void SplitStereo(bool stereo);
void OnSwapChannels(wxCommandEvent & event);
void OnSplitStereo(wxCommandEvent & event);
void OnSplitStereoMono(wxCommandEvent & event);
};
WaveTrackMenuTable &WaveTrackMenuTable::Instance()
@ -94,10 +485,435 @@ WaveTrackMenuTable &WaveTrackMenuTable::Instance()
return instance;
}
void WaveTrackMenuTable::InitMenu(Menu *pMenu, void *pUserData)
{
mpData = static_cast<TrackControls::InitMenuData*>(pUserData);
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
std::vector<int> checkedIds;
const int display = pTrack->GetDisplay();
checkedIds.push_back(
display == WaveTrack::Waveform
? (pTrack->GetWaveformSettings().isLinear()
? OnWaveformID : OnWaveformDBID)
: OnSpectrumID);
// Bug 1253. Shouldn't open preferences if audio is busy.
// We can't change them on the fly yet anyway.
const bool bAudioBusy = gAudioIO->IsBusy();
pMenu->Enable(OnSpectrogramSettingsID,
(display == WaveTrack::Spectrum) && !bAudioBusy);
AudacityProject *const project = ::GetActiveProject();
bool unsafe = EffectManager::Get().RealtimeIsActive() &&
project->IsAudioActive();
const bool isMono = !pTrack->GetLink();
if ( isMono )
{
mpData = static_cast<TrackControls::InitMenuData*>(pUserData);
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
TrackList *const tracks = project->GetTracks();
Track *const next = tracks->GetNext(pTrack);
if (isMono) {
const bool canMakeStereo =
(next && !next->GetLinked()
&& pTrack->GetKind() == Track::Wave
&& next->GetKind() == Track::Wave);
pMenu->Enable(OnMergeStereoID, canMakeStereo && !unsafe);
int itemId;
switch (pTrack->GetChannel()) {
case Track::LeftChannel:
itemId = OnChannelLeftID;
break;
case Track::RightChannel:
itemId = OnChannelRightID;
break;
default:
itemId = OnChannelMonoID;
break;
}
checkedIds.push_back(itemId);
}
}
else
{
pMenu->Enable(OnMergeStereoID, false);
}
SetMenuChecks(*pMenu, [&](int id){
auto end = checkedIds.end();
return end != std::find(checkedIds.begin(), end, id);
});
pMenu->Enable(OnSwapChannelsID, !isMono && !unsafe);
pMenu->Enable(OnSplitStereoID, !isMono && !unsafe);
// Several menu items no longer needed....
#if 0
pMenu->Enable(OnSplitStereoMonoID, !isMono && !unsafe);
pMenu->Enable(OnChannelMonoID, isMono);
pMenu->Enable(OnChannelLeftID, isMono);
pMenu->Enable(OnChannelRightID, isMono);
#endif
}
BEGIN_POPUP_MENU(WaveTrackMenuTable)
POPUP_MENU_SEPARATOR()
POPUP_MENU_RADIO_ITEM(OnWaveformID, _("Wa&veform"), OnSetDisplay)
POPUP_MENU_RADIO_ITEM(OnWaveformDBID, _("&Waveform (dB)"), OnSetDisplay)
POPUP_MENU_RADIO_ITEM(OnSpectrumID, _("&Spectrogram"), OnSetDisplay)
POPUP_MENU_ITEM(OnSpectrogramSettingsID, _("S&pectrogram Settings..."), OnSpectrogramSettings)
POPUP_MENU_SEPARATOR()
// POPUP_MENU_RADIO_ITEM(OnChannelMonoID, _("&Mono"), OnChannelChange)
// POPUP_MENU_RADIO_ITEM(OnChannelLeftID, _("&Left Channel"), OnChannelChange)
// POPUP_MENU_RADIO_ITEM(OnChannelRightID, _("R&ight Channel"), OnChannelChange)
POPUP_MENU_ITEM(OnMergeStereoID, _("Make &Stereo"), OnMergeStereo)
POPUP_MENU_ITEM(OnSwapChannelsID, _("S&wap"), OnSwapChannels)
POPUP_MENU_ITEM(OnSplitStereoID, _("S&plit"), OnSplitStereo)
// DA: Uses split stereo track and then drag pan sliders for split-stereo-to-mono
#ifndef EXPERIMENTAL_DA
POPUP_MENU_ITEM(OnSplitStereoMonoID, _("Split Stereo to &Mono"), OnSplitStereoMono)
#endif
POPUP_MENU_SEPARATOR()
POPUP_MENU_SUB_MENU(0, _("&Format"), FormatMenuTable)
POPUP_MENU_SEPARATOR()
POPUP_MENU_SUB_MENU(0, _("Rat&e"), RateMenuTable)
END_POPUP_MENU()
PopupMenuTable *WaveTrackControls::GetMenuExtension(Track*)
/// Set the Display mode based on the menu choice in the Track Menu.
void WaveTrackMenuTable::OnSetDisplay(wxCommandEvent & event)
{
int idInt = event.GetId();
wxASSERT(idInt >= OnWaveformID && idInt <= OnSpectrumID);
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
wxASSERT(pTrack && pTrack->GetKind() == Track::Wave);
bool linear = false;
WaveTrack::WaveTrackDisplay id;
switch (idInt) {
default:
case OnWaveformID:
linear = true, id = WaveTrack::Waveform; break;
case OnWaveformDBID:
id = WaveTrack::Waveform; break;
case OnSpectrumID:
id = WaveTrack::Spectrum; break;
}
const bool wrongType = pTrack->GetDisplay() != id;
const bool wrongScale =
(id == WaveTrack::Waveform &&
pTrack->GetWaveformSettings().isLinear() != linear);
if (wrongType || wrongScale) {
pTrack->SetLastScaleType();
pTrack->SetDisplay(WaveTrack::WaveTrackDisplay(id));
if (wrongScale)
pTrack->GetIndependentWaveformSettings().scaleType = linear
? WaveformSettings::stLinear
: WaveformSettings::stLogarithmic;
// Assume partner is wave or null
auto partner = static_cast<WaveTrack *>(pTrack->GetLink());
if (partner) {
partner->SetLastScaleType();
partner->SetDisplay(WaveTrack::WaveTrackDisplay(id));
if (wrongScale)
partner->GetIndependentWaveformSettings().scaleType = linear
? WaveformSettings::stLinear
: WaveformSettings::stLogarithmic;
}
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
if (pTrack->GetDisplay() == WaveTrack::Waveform) {
pTrack->SetVirtualState(false);
}
else if (id == WaveTrack::Waveform) {
pTrack->SetVirtualState(true);
}
#endif
AudacityProject *const project = ::GetActiveProject();
project->ModifyState(true);
using namespace RefreshCode;
mpData->result = RefreshAll | UpdateVRuler;
}
}
void WaveTrackMenuTable::OnSpectrogramSettings(wxCommandEvent &)
{
class ViewSettingsDialog final : public PrefsDialog
{
public:
ViewSettingsDialog
(wxWindow *parent, const wxString &title, PrefsDialog::Factories &factories,
int page)
: PrefsDialog(parent, title, factories)
, mPage(page)
{
}
long GetPreferredPage() override
{
return mPage;
}
void SavePreferredPage() override
{
}
private:
const int mPage;
};
if (gAudioIO->IsBusy()){
wxMessageBox(_("To change Spectrogram Settings, stop any\n"
"playing or recording first."),
_("Stop the Audio First"), wxOK | wxICON_EXCLAMATION | wxCENTRE);
return;
}
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
// WaveformPrefsFactory waveformFactory(pTrack);
// TracksBehaviorsPrefsFactory tracksBehaviorsFactory();
SpectrumPrefsFactory spectrumFactory(pTrack);
PrefsDialog::Factories factories;
// factories.push_back(&waveformFactory);
factories.push_back(&spectrumFactory);
const int page =
// (pTrack->GetDisplay() == WaveTrack::Spectrum) ? 1 :
0;
wxString title(pTrack->GetName() + wxT(": "));
ViewSettingsDialog dialog(mpData->pParent, title, factories, page);
if (0 != dialog.ShowModal()) {
// Redraw
AudacityProject *const project = ::GetActiveProject();
project->ModifyState(true);
mpData->result = RefreshCode::RefreshAll;
}
}
void WaveTrackMenuTable::OnChannelChange(wxCommandEvent & event)
{
int id = event.GetId();
wxASSERT(id >= OnChannelLeftID && id <= OnChannelMonoID);
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
wxASSERT(pTrack);
int channel;
wxString channelmsg;
switch (id) {
default:
case OnChannelMonoID:
channel = Track::MonoChannel;
channelmsg = _("Mono");
break;
case OnChannelLeftID:
channel = Track::LeftChannel;
channelmsg = _("Left Channel");
break;
case OnChannelRightID:
channel = Track::RightChannel;
channelmsg = _("Right Channel");
break;
}
pTrack->SetChannel(channel);
AudacityProject *const project = ::GetActiveProject();
project->PushState(wxString::Format(_("Changed '%s' to %s"),
pTrack->GetName().c_str(),
channelmsg),
_("Channel"));
mpData->result = RefreshCode::RefreshAll;
}
/// Merge two tracks into one stereo track ??
void WaveTrackMenuTable::OnMergeStereo(wxCommandEvent &)
{
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
wxASSERT(pTrack);
pTrack->SetLinked(true);
// Assume partner is wave or null
const auto partner = static_cast<WaveTrack*>(pTrack->GetLink());
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
if (MONO_WAVE_PAN(pTrack))
pTrack->SetVirtualState(false);
if (MONO_WAVE_PAN(partner))
static_cast<WaveTrack*>(partner)->SetVirtualState(false);
#endif
if (partner) {
// Set partner's parameters to match target.
partner->Merge(*pTrack);
pTrack->SetPan( 0.0f );
pTrack->SetChannel(Track::LeftChannel);
partner->SetPan( 0.0f );
partner->SetChannel(Track::RightChannel);
// Set NEW track heights and minimized state
bool bBothMinimizedp = ((pTrack->GetMinimized()) && (partner->GetMinimized()));
pTrack->SetMinimized(false);
partner->SetMinimized(false);
int AverageHeight = (pTrack->GetHeight() + partner->GetHeight()) / 2;
pTrack->SetHeight(AverageHeight);
partner->SetHeight(AverageHeight);
pTrack->SetMinimized(bBothMinimizedp);
partner->SetMinimized(bBothMinimizedp);
//On Demand - join the queues together.
if (ODManager::IsInstanceCreated())
if (!ODManager::Instance()->MakeWaveTrackDependent(partner, pTrack))
{
;
//TODO: in the future, we will have to check the return value of MakeWaveTrackDependent -
//if the tracks cannot merge, it returns false, and in that case we should not allow a merging.
//for example it returns false when there are two different types of ODTasks on each track's queue.
//we will need to display this to the user.
}
AudacityProject *const project = ::GetActiveProject();
project->PushState(wxString::Format(_("Made '%s' a stereo track"),
pTrack->GetName().
c_str()),
_("Make Stereo"));
}
else
pTrack->SetLinked(false);
mpData->result = RefreshCode::RefreshAll;
}
/// Split a stereo track into two tracks...
void WaveTrackMenuTable::SplitStereo(bool stereo)
{
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
wxASSERT(pTrack);
if (stereo)
pTrack->SetPanFromChannelType();
pTrack->SetChannel(Track::MonoChannel);
// Assume partner is present, and is wave
const auto partner = static_cast<WaveTrack*>(pTrack->GetLink());
wxASSERT(partner);
if (!partner)
return;
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
if (!stereo && MONO_WAVE_PAN(pTrack))
pTrack->SetVirtualState(true, true);
if (!stereo && MONO_WAVE_PAN(partner))
partner->SetVirtualState(true, true);
#endif
if (partner)
{
// Keep original stereo track name.
partner->SetName(pTrack->GetName());
if (stereo)
partner->SetPanFromChannelType();
partner->SetChannel(Track::MonoChannel);
//On Demand - have each channel add its own.
if (ODManager::IsInstanceCreated() && partner->GetKind() == Track::Wave)
ODManager::Instance()->MakeWaveTrackIndependent(partner);
}
pTrack->SetLinked(false);
//make sure neither track is smaller than its minimum height
if (pTrack->GetHeight() < pTrack->GetMinimizedHeight())
pTrack->SetHeight(pTrack->GetMinimizedHeight());
if (partner)
{
if (partner->GetHeight() < partner->GetMinimizedHeight())
partner->SetHeight(partner->GetMinimizedHeight());
// Make tracks the same height
if (pTrack->GetHeight() != partner->GetHeight())
{
pTrack->SetHeight((pTrack->GetHeight() + partner->GetHeight()) / 2.0);
partner->SetHeight(pTrack->GetHeight());
}
}
mpData->result = RefreshCode::RefreshAll;
}
/// Swap the left and right channels of a stero track...
void WaveTrackMenuTable::OnSwapChannels(wxCommandEvent &)
{
AudacityProject *const project = ::GetActiveProject();
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
// Assume partner is wave or null
const auto partner = static_cast<WaveTrack*>(pTrack->GetLink());
Track *const focused = project->GetTrackPanel()->GetFocusedTrack();
const bool hasFocus =
(focused == pTrack || focused == partner);
SplitStereo(false);
pTrack->SetChannel(Track::RightChannel);
partner->SetChannel(Track::LeftChannel);
TrackList *const tracks = project->GetTracks();
(tracks->MoveUp(partner));
partner->SetLinked(true);
MixerBoard* pMixerBoard = project->GetMixerBoard();
if (pMixerBoard)
pMixerBoard->UpdateTrackClusters();
if (hasFocus)
project->GetTrackPanel()->SetFocusedTrack(partner);
project->PushState(wxString::Format(_("Swapped Channels in '%s'"),
pTrack->GetName().c_str()),
_("Swap Channels"));
mpData->result = RefreshCode::RefreshAll;
}
/// Split a stereo track into two tracks...
void WaveTrackMenuTable::OnSplitStereo(wxCommandEvent &)
{
SplitStereo(true);
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
AudacityProject *const project = ::GetActiveProject();
project->PushState(wxString::Format(_("Split stereo track '%s'"),
pTrack->GetName().c_str()),
_("Split"));
mpData->result = RefreshCode::RefreshAll;
}
/// Split a stereo track into two mono tracks...
void WaveTrackMenuTable::OnSplitStereoMono(wxCommandEvent &)
{
SplitStereo(false);
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
AudacityProject *const project = ::GetActiveProject();
project->PushState(wxString::Format(_("Split Stereo to Mono '%s'"),
pTrack->GetName().c_str()),
_("Split to Mono"));
mpData->result = RefreshCode::RefreshAll;
}
//=============================================================================
PopupMenuTable *WaveTrackControls::GetMenuExtension(Track *pTrack)
{
return &WaveTrackMenuTable::Instance();
}