1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-05 22:28:57 +02:00

Merge branch 'master' into HEAD

This commit is contained in:
Paul Licameli 2017-04-03 23:25:26 -04:00
commit b62bddfaeb
11 changed files with 611 additions and 167 deletions

View File

@ -2163,14 +2163,17 @@ void LabelTrack::Import(wxTextFile & in)
//Currently, we expect a tag file to have two values and a label
//on each line. If the second token is not a number, we treat
//it as a single-value label.
bool error = false;
for (int index = 0; index < lines;) {
try {
// Let LabelStruct::Import advance index
LabelStruct l { LabelStruct::Import(in, index) };
mLabels.push_back(l);
}
catch(const LabelStruct::BadFormatException&) {}
catch(const LabelStruct::BadFormatException&) { error = true; }
}
if (error)
::wxMessageBox( _("One or more saved labels could not be read.") );
SortLabels();
}

View File

@ -35,6 +35,7 @@ simplifies construction of menu items.
#include <cfloat>
#include <iterator>
#include <algorithm>
#include <limits>
#include <math.h>
@ -494,11 +495,6 @@ void AudacityProject::CreateMenusAndCommands()
c->AddItem(wxT("Disjoin"), _("Detac&h at Silences"), FN(OnDisjoin), wxT("Ctrl+Alt+J"));
c->EndSubMenu();
c->AddSeparator();
c->AddItem(wxT("EditMetaData"), _("Me&tadata..."), FN(OnEditMetadata));
c->AddSeparator();
/////////////////////////////////////////////////////////////////////////////
c->BeginSubMenu(_("La&bels"));
@ -562,70 +558,7 @@ void AudacityProject::CreateMenusAndCommands()
c->EndSubMenu();
/////////////////////////////////////////////////////////////////////////////
/* i18n-hint: (verb) It's an item on a menu. */
c->BeginSubMenu(_("&Select"));
c->SetDefaultFlags(TracksExistFlag, TracksExistFlag);
c->AddItem(wxT("SelectAll"), _("&All"), FN(OnSelectAll), wxT("Ctrl+A"));
c->AddItem(wxT("SelectNone"), _("&None"), FN(OnSelectNone), wxT("Ctrl+Shift+A"));
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
c->BeginSubMenu(_("S&pectral"));
c->AddItem(wxT("ToggleSpectralSelection"), _("To&ggle spectral selection"), FN(OnToggleSpectralSelection), wxT("Q"));
c->AddItem(wxT("NextHigherPeakFrequency"), _("Next Higher Peak Frequency"), FN(OnNextHigherPeakFrequency));
c->AddItem(wxT("NextLowerPeakFrequency"), _("Next Lower Peak Frequency"), FN(OnNextLowerPeakFrequency));
c->EndSubMenu();
#endif
c->AddItem(wxT("SetLeftSelection"), _("&Left at Playback Position"), FN(OnSetLeftSelection), wxT("["));
c->AddItem(wxT("SetRightSelection"), _("&Right at Playback Position"), FN(OnSetRightSelection), wxT("]"));
c->SetDefaultFlags(TracksSelectedFlag, TracksSelectedFlag);
c->AddItem(wxT("SelStartCursor"), _("Track &Start to Cursor"), FN(OnSelectStartCursor), wxT("Shift+J"));
c->AddItem(wxT("SelCursorEnd"), _("Cursor to Track &End"), FN(OnSelectCursorEnd), wxT("Shift+K"));
c->AddItem(wxT("SelCursorStoredCursor"), _("Cursor to Saved &Cursor Position"), FN(OnSelectCursorStoredCursor),
wxT(""), TracksExistFlag, TracksExistFlag);
c->AddSeparator();
c->AddItem(wxT("SelAllTracks"), _("In All &Tracks"), FN(OnSelectAllTracks),
wxT("Ctrl+Shift+K"),
TracksExistFlag, TracksExistFlag);
#ifdef EXPERIMENTAL_SYNC_LOCK
c->AddItem(wxT("SelSyncLockTracks"), _("In All Sync-Locked Tracks"),
FN(OnSelectSyncLockSel), wxT("Ctrl+Shift+Y"),
TracksSelectedFlag | IsSyncLockedFlag,
TracksSelectedFlag | IsSyncLockedFlag);
#endif
c->AddSeparator();
c->AddItem(wxT("ZeroCross"), _("Ends to &Zero Crossings"), FN(OnZeroCrossing), wxT("Z"));
c->AddSeparator();
c->AddItem(wxT("StoreCursorPosition"), _("Save Cursor Pos&ition"), FN(OnCursorPositionStore),
WaveTracksExistFlag,
WaveTracksExistFlag);
// Save cursor position is used in some selctions.
// Maybe there should be a restore for it?
// Audacity has 'Store Re&gion' here.
c->AddItem(wxT("SelSave"), _("Save Sele&ction"), FN(OnSelectionSave),
WaveTracksSelectedFlag,
WaveTracksSelectedFlag);
// Audacity has 'Retrieve Regio&n' here.
c->AddItem(wxT("SelRestore"), _("Restore Selectio&n"), FN(OnSelectionRestore),
TracksExistFlag,
TracksExistFlag);
c->EndSubMenu();
/////////////////////////////////////////////////////////////////////////////
c->AddItem(wxT("EditMetaData"), _("Me&tadata..."), FN(OnEditMetadata));
/////////////////////////////////////////////////////////////////////////////
@ -645,6 +578,104 @@ void AudacityProject::CreateMenusAndCommands()
AudioIONotBusyFlag,
AudioIONotBusyFlag);
c->EndMenu();
/////////////////////////////////////////////////////////////////////////////
// Select Menu
/////////////////////////////////////////////////////////////////////////////
/* i18n-hint: (verb) It's an item on a menu. */
c->BeginMenu(_("&Select"));
c->SetDefaultFlags(TracksExistFlag, TracksExistFlag);
c->AddItem(wxT("SelectAll"), _("&All"), FN(OnSelectAll), wxT("Ctrl+A"));
c->AddItem(wxT("SelectNone"), _("&None"), FN(OnSelectNone), wxT("Ctrl+Shift+A"));
/////////////////////////////////////////////////////////////////////////////
c->SetDefaultFlags(TracksSelectedFlag, TracksSelectedFlag);
c->BeginSubMenu(_("Tracks"));
c->AddItem(wxT("SelAllTracks"), _("In All &Tracks"), FN(OnSelectAllTracks),
wxT("Ctrl+Shift+K"),
TracksExistFlag, TracksExistFlag);
#ifdef EXPERIMENTAL_SYNC_LOCK
c->AddItem(wxT("SelSyncLockTracks"), _("In All Sync-Locked Tracks"),
FN(OnSelectSyncLockSel), wxT("Ctrl+Shift+Y"),
TracksSelectedFlag | IsSyncLockedFlag,
TracksSelectedFlag | IsSyncLockedFlag);
#endif
c->EndSubMenu();
c->SetDefaultFlags(TracksExistFlag, TracksExistFlag);
/////////////////////////////////////////////////////////////////////////////
c->BeginSubMenu(_("Region"));
c->AddItem(wxT("SetLeftSelection"), _("&Left at Playback Position"), FN(OnSetLeftSelection), wxT("["));
c->AddItem(wxT("SetRightSelection"), _("&Right at Playback Position"), FN(OnSetRightSelection), wxT("]"));
c->SetDefaultFlags(TracksSelectedFlag, TracksSelectedFlag);
c->AddItem(wxT("SelStartCursor"), _("Track &Start to Cursor"), FN(OnSelectStartCursor), wxT("Shift+J"));
c->AddItem(wxT("SelCursorEnd"), _("Cursor to Track &End"), FN(OnSelectCursorEnd), wxT("Shift+K"));
c->AddSeparator();
// Audacity has 'Store Re&gion' here.
c->AddItem(wxT("SelSave"), _("Save Sele&ction"), FN(OnSelectionSave),
WaveTracksSelectedFlag,
WaveTracksSelectedFlag);
// Audacity has 'Retrieve Regio&n' here.
c->AddItem(wxT("SelRestore"), _("Restore Selectio&n"), FN(OnSelectionRestore),
TracksExistFlag,
TracksExistFlag);
c->AddSeparator();
c->AddItem(wxT("ZeroCross"), _("Ends to &Zero Crossings"), FN(OnZeroCrossing), wxT("Z"));
c->EndSubMenu();
/////////////////////////////////////////////////////////////////////////////
c->SetDefaultFlags(TracksExistFlag, TracksExistFlag);
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
c->BeginSubMenu(_("S&pectral"));
c->AddItem(wxT("ToggleSpectralSelection"), _("To&ggle spectral selection"), FN(OnToggleSpectralSelection), wxT("Q"));
c->AddItem(wxT("NextHigherPeakFrequency"), _("Next Higher Peak Frequency"), FN(OnNextHigherPeakFrequency));
c->AddItem(wxT("NextLowerPeakFrequency"), _("Next Lower Peak Frequency"), FN(OnNextLowerPeakFrequency));
c->EndSubMenu();
#endif
/////////////////////////////////////////////////////////////////////////////
c->SetDefaultFlags(TracksSelectedFlag, TracksSelectedFlag);
c->BeginSubMenu(_("Clip Boundaries"));
c->AddItem(wxT("SelPrevClipBoundaryToCursor"), _("Pre&vious Clip Boundary to Cursor"),
FN(OnSelectPrevClipBoundaryToCursor), wxT(""),
TrackPanelHasFocus | WaveTracksExistFlag, TrackPanelHasFocus | WaveTracksExistFlag);
c->AddItem(wxT("SelCursorToNextClipBoundary"), _("Cursor to Ne&xt Clip Boundary"),
FN(OnSelectCursorToNextClipBoundary), wxT(""),
TrackPanelHasFocus |WaveTracksExistFlag, TrackPanelHasFocus | WaveTracksExistFlag);
c->AddItem(wxT("SelPrevClip"), _("Previo&us Clip"), FN(OnSelectPrevClip), wxT(""),
WaveTracksExistFlag | TrackPanelHasFocus, WaveTracksExistFlag | TrackPanelHasFocus);
c->AddItem(wxT("SelNextClip"), _("N&ext Clip"), FN(OnSelectNextClip), wxT(""),
WaveTracksExistFlag | TrackPanelHasFocus, WaveTracksExistFlag | TrackPanelHasFocus);
c->EndSubMenu();
/////////////////////////////////////////////////////////////////////////////
c->AddItem(wxT("SelCursorStoredCursor"), _("Cursor to Saved &Cursor Position"), FN(OnSelectCursorStoredCursor),
wxT(""), TracksExistFlag, TracksExistFlag);
c->AddItem(wxT("StoreCursorPosition"), _("Save Cursor Pos&ition"), FN(OnCursorPositionStore),
WaveTracksExistFlag,
WaveTracksExistFlag);
// Save cursor position is used in some selctions.
// Maybe there should be a restore for it?
c->EndMenu();
/////////////////////////////////////////////////////////////////////////////
@ -817,6 +848,11 @@ void AudacityProject::CreateMenusAndCommands()
c->AddItem(wxT("CursTrackStart"), _("Track &Start"), FN(OnCursorTrackStart), wxT("J"));
c->AddItem(wxT("CursTrackEnd"), _("Track &End"), FN(OnCursorTrackEnd), wxT("K"));
c->AddItem(wxT("CursPrevClipBoundary"), _("Pre&vious Clip Boundary"), FN(OnCursorPrevClipBoundary), wxT(""),
TrackPanelHasFocus | WaveTracksExistFlag, TrackPanelHasFocus | WaveTracksExistFlag);
c->AddItem(wxT("CursNextClipBoundary"), _("Ne&xt Clip Boundary"), FN(OnCursorNextClipBoundary), wxT(""),
TrackPanelHasFocus | WaveTracksExistFlag, TrackPanelHasFocus | WaveTracksExistFlag);
c->AddItem(wxT("CursProjectStart"), _("&Project Start"), FN(OnSkipStart), wxT("Home"));
c->AddItem(wxT("CursProjectEnd"), _("Project E&nd"), FN(OnSkipEnd), wxT("End"));
@ -918,6 +954,13 @@ void AudacityProject::CreateMenusAndCommands()
c->AddItem(wxT("UnMuteAllTracks"), _("&Unmute All Tracks"), FN(OnUnMuteAllTracks), wxT("Ctrl+Shift+U"));
c->EndSubMenu();
c->BeginSubMenu("Pan");
c->AddItem(wxT("PanLeft"), _("&Left"), FN(OnPanLeft));
c->AddItem(wxT("PanRight"), _("&Right"), FN(OnPanRight));
c->AddItem(wxT("PanCenter"), _("&Center"), FN(OnPanCenter));
c->EndSubMenu();
c->AddSeparator();
wxArrayString alignLabelsNoSync;
@ -1260,6 +1303,9 @@ void AudacityProject::CreateMenusAndCommands()
c->AddCommand(wxT("CursorLongJumpLeft"), _("Cursor Long Jump Left"), FN(OnCursorLongJumpLeft), wxT("Shift+,"));
c->AddCommand(wxT("CursorLongJumpRight"), _("Cursor Long Jump Right"), FN(OnCursorLongJumpRight), wxT("Shift+."));
c->AddCommand(wxT("ClipLeft"), _("Clip Left"), FN(OnClipLeft), wxT(""));
c->AddCommand(wxT("ClipRight"), _("Clip Right"), FN(OnClipRight), wxT(""));
c->AddCommand(wxT("SelExtLeft"), _("Selection Extend Left"), FN(OnSelExtendLeft), wxT("Shift+Left\twantKeyup\tallowDup"));
c->AddCommand(wxT("SelExtRight"), _("Selection Extend Right"), FN(OnSelExtendRight), wxT("Shift+Right\twantKeyup\tallowDup"));
@ -2914,6 +2960,16 @@ void AudacityProject::OnSelContractRight(const wxEvent * evt)
OnCursorLeft( true, true, evt->GetEventType() == wxEVT_KEY_UP );
}
void AudacityProject::OnClipLeft()
{
mTrackPanel->OnClipMove(false);
}
void AudacityProject::OnClipRight()
{
mTrackPanel->OnClipMove(true);
}
//this pops up a dialog which allows the left selection to be set.
//If playing/recording is happening, it sets the left selection at
//the current play position.
@ -5309,6 +5365,110 @@ void AudacityProject::OnSelectStartCursor()
mTrackPanel->Refresh(false);
}
void AudacityProject::OnSelectPrevClipBoundaryToCursor()
{
OnSelectClipBoundary(false);
}
void AudacityProject::OnSelectCursorToNextClipBoundary()
{
OnSelectClipBoundary(true);
}
void AudacityProject::OnSelectClipBoundary(bool next)
{
const auto track = mTrackPanel->GetFocusedTrack();
if (track && track->GetKind() == Track::Wave) {
const auto wt = static_cast<WaveTrack*>(track);
if (wt->GetNumClips()) {
auto result = next ? FindNextClipBoundary(wt, mViewInfo.selectedRegion.t1()) :
FindPrevClipBoundary(wt, mViewInfo.selectedRegion.t0());
if (result.nFound > 0) {
if (next)
mViewInfo.selectedRegion.setT1(result.time);
else
mViewInfo.selectedRegion.setT0(result.time);
ModifyState(false);
mTrackPanel->Refresh(false);
wxString message;
message.Printf(wxT("%d %s %d %s"), result.index1 + 1, _("of"), wt->GetNumClips(),
result.clipStart1 ? _("start") : _("end"));
if (result.nFound == 2) {
wxString messageAdd;
messageAdd.Printf(wxT(" %s %d %s"), _("and"), result.index2 + 1,
result.clipStart2 ? _("start") : _("end"));
message += messageAdd;
}
mTrackPanel->MessageForScreenReader(message);
}
}
}
}
void AudacityProject::OnSelectPrevClip()
{
OnSelectClip(false);
}
void AudacityProject::OnSelectNextClip()
{
OnSelectClip(true);
}
void AudacityProject::OnSelectClip(bool next)
{
auto track = mTrackPanel->GetFocusedTrack();
if (track && track->GetKind() == Track::Wave) {
auto wt = static_cast<WaveTrack*>(track);
const auto clips = wt->SortedClipArray();
double t0 = mViewInfo.selectedRegion.t0();
double t1 = mViewInfo.selectedRegion.t1();
WaveClip* clip = nullptr;
int i = 0;
if (next) {
auto result = find_if(clips.begin(), clips.end(), [&] (WaveClip* const& clip) {
return clip->GetStartTime() == t0; });
if (result != clips.end() && (*result)->GetEndTime() != t1 ) {
clip = *result;
i = result - clips.begin();
}
else {
auto result = find_if(clips.begin(), clips.end(), [&] (WaveClip* const& clip) {
return clip->GetStartTime() > t0; });
if (result != clips.end()) {
clip = *result;
i = result - clips.begin();
}
}
}
else {
auto result = find_if(clips.rbegin(), clips.rend(), [&] (WaveClip* const& clip) {
return clip->GetStartTime() < t0; });
if (result != clips.rend()) {
clip = *result;
i = static_cast<int>(clips.size()) - 1 - (result - clips.rbegin());
}
}
if (clip) {
mViewInfo.selectedRegion.setTimes(clip->GetStartTime(), clip->GetEndTime());
mTrackPanel->ScrollIntoView(mViewInfo.selectedRegion.t0());
ModifyState(false);
mTrackPanel->Refresh(false);
wxString message;
message.Printf(wxT("%d %s %d %s"), i + 1, _("of"), clips.size(), _("selected"));
mTrackPanel->MessageForScreenReader(message);
}
}
}
void AudacityProject::OnSelectCursorStoredCursor()
{
if (mCursorPositionHasBeenStored) {
@ -6136,6 +6296,131 @@ void AudacityProject::OnCursorSelEnd()
mTrackPanel->Refresh(false);
}
AudacityProject::FoundClipBoundary AudacityProject::FindNextClipBoundary(const WaveTrack* wt, double time)
{
AudacityProject::FoundClipBoundary result{};
const auto clips = wt->SortedClipArray();
auto resultStart = find_if(clips.begin(), clips.end(), [&] (const WaveClip* const& clip) {
return clip->GetStartTime() > time; });
auto resultEnd = find_if(clips.begin(), clips.end(), [&] (const WaveClip* const& clip) {
return clip->GetEndTime() > time; });
if (resultStart != clips.end() && resultEnd != clips.end()) {
if ((*resultStart)->GetStartTime() < (*resultEnd)->GetEndTime()) {
result.nFound = 1;
result.time = (*resultStart)->GetStartTime();
result.index1 = resultStart - clips.begin();
result.clipStart1 = true;
}
else if ((*resultStart)->GetStartTime() > (*resultEnd)->GetEndTime()) {
result.nFound = 1;
result.time = (*resultEnd)->GetEndTime();
result.index1 = resultEnd - clips.begin();
result.clipStart1 = false;
}
else { // both the end of one clip and the start of the next clip
result.nFound = 2;
result.time = (*resultEnd)->GetEndTime();
result.index1 = resultEnd - clips.begin();
result.clipStart1 = false;
result.index2 = resultStart - clips.begin();
result.clipStart2 = true;
}
}
else if (resultEnd != clips.end()) {
result.nFound = 1;
result.time = (*resultEnd)->GetEndTime();
result.index1 = resultEnd - clips.begin();
result.clipStart1 = false;
}
return result;
}
AudacityProject::FoundClipBoundary AudacityProject::FindPrevClipBoundary(const WaveTrack* wt, double time)
{
AudacityProject::FoundClipBoundary result{};
const auto clips = wt->SortedClipArray();
auto resultStart = find_if(clips.rbegin(), clips.rend(), [&] (const WaveClip* const& clip) {
return clip->GetStartTime() < time; });
auto resultEnd = find_if(clips.rbegin(), clips.rend(), [&] (const WaveClip* const& clip) {
return clip->GetEndTime() < time; });
if (resultStart != clips.rend() && resultEnd != clips.rend()) {
if ((*resultStart)->GetStartTime() > (*resultEnd)->GetEndTime()) {
result.nFound = 1;
result.time = (*resultStart)->GetStartTime();
result.index1 = static_cast<int>(clips.size()) - 1 - (resultStart - clips.rbegin());
result.clipStart1 = true;
}
else if ((*resultStart)->GetStartTime() < (*resultEnd)->GetEndTime()) {
result.nFound = 1;
result.time = (*resultEnd)->GetEndTime();
result.index1 = static_cast<int>(clips.size()) - 1 - (resultEnd - clips.rbegin());
result.clipStart1 = false;
}
else {
result.nFound = 2; // both the start of one clip and the end of the previous clip
result.time = (*resultStart)->GetStartTime();
result.index1 = static_cast<int>(clips.size()) - 1 - (resultStart - clips.rbegin());
result.clipStart1 = true;
result.index2 = static_cast<int>(clips.size()) - 1 - (resultEnd - clips.rbegin());
result.clipStart2 = false;
}
}
else if (resultStart != clips.rend()) {
result.nFound = 1;
result.time = (*resultStart)->GetStartTime();
result.index1 = static_cast<int>(clips.size()) - 1 - (resultStart - clips.rbegin());
result.clipStart1 = true;
}
return result;
}
void AudacityProject::OnCursorNextClipBoundary()
{
OnCursorClipBoundary(true);
}
void AudacityProject::OnCursorPrevClipBoundary()
{
OnCursorClipBoundary(false);
}
void AudacityProject::OnCursorClipBoundary(bool next)
{
const auto track = mTrackPanel->GetFocusedTrack();
if (track && track->GetKind() == Track::Wave) {
const auto wt = static_cast<WaveTrack*>(track);
if (wt->GetNumClips()) {
auto result = next ? FindNextClipBoundary(wt, mViewInfo.selectedRegion.t0()) :
FindPrevClipBoundary(wt, mViewInfo.selectedRegion.t0());
if (result.nFound > 0) {
mViewInfo.selectedRegion.setTimes(result.time, result.time);
ModifyState(false);
mTrackPanel->ScrollIntoView(mViewInfo.selectedRegion.t0());
mTrackPanel->Refresh(false);
wxString message;
message.Printf(wxT("%d %s %d %s"), result.index1 + 1, _("of"), wt->GetNumClips(),
result.clipStart1 ? _("start") : _("end"));
if (result.nFound == 2) {
wxString messageAdd;
messageAdd.Printf(wxT(" %s %d %s"), _("and"), result.index2 + 1,
result.clipStart2 ? _("start") : _("end"));
message += messageAdd;
}
mTrackPanel->MessageForScreenReader(message);
}
}
}
}
void AudacityProject::HandleAlign(int index, bool moveSel)
{
TrackListIterator iter(GetTracks());
@ -7085,6 +7370,41 @@ void AudacityProject::OnExpandAllTracks()
RedrawProject();
}
void AudacityProject::OnPanTracks(float PanValue)
{
TrackListIterator iter(GetTracks());
Track *t = iter.First();
// count selected wave tracks
int count =0;
while (t)
{
if( t->GetKind() == Track::Wave && t->GetSelected() )
count++;
t = iter.Next();
}
// iter through them, all if none selected.
t = iter.First();
while (t)
{
if( t->GetKind() == Track::Wave && ((count==0) || t->GetSelected()) ){
WaveTrack *left = (WaveTrack *)t;
left->SetPan( PanValue );
}
t = iter.Next();
}
ModifyState(true);
RedrawProject();
if (mMixerBoard)
mMixerBoard->UpdatePan();
}
void AudacityProject::OnPanLeft(){ OnPanTracks( -1.0);}
void AudacityProject::OnPanRight(){ OnPanTracks( 1.0);}
void AudacityProject::OnPanCenter(){ OnPanTracks( 0.0);}
void AudacityProject::OnMuteAllTracks()
{

View File

@ -160,6 +160,9 @@ void OnSelExtendRight(const wxEvent * evt);
void OnSelContractLeft(const wxEvent * evt);
void OnSelContractRight(const wxEvent * evt);
void OnClipLeft();
void OnClipRight();
void OnCursorShortJumpLeft();
void OnCursorShortJumpRight();
void OnCursorLongJumpLeft();
@ -282,6 +285,12 @@ void OnNextLowerPeakFrequency();
#endif
void OnSelectCursorEnd();
void OnSelectStartCursor();
void OnSelectPrevClipBoundaryToCursor();
void OnSelectCursorToNextClipBoundary();
void OnSelectClipBoundary(bool next);
void OnSelectPrevClip();
void OnSelectNextClip();
void OnSelectClip(bool next);
void OnSelectCursorStoredCursor();
void OnSelectSyncLockSel();
void OnSelectAllTracks();
@ -302,6 +311,11 @@ void OnGoSelEnd();
void OnExpandAllTracks();
void OnCollapseAllTracks();
void OnPanTracks(float PanValue);
void OnPanLeft();
void OnPanRight();
void OnPanCenter();
void OnMuteAllTracks();
void OnUnMuteAllTracks();
@ -375,6 +389,19 @@ void OnCursorTrackStart();
void OnCursorTrackEnd();
void OnCursorSelStart();
void OnCursorSelEnd();
typedef struct FoundClipBoundary {
int nFound; // 0, 1, or 2
double time;
int index1;
bool clipStart1;
int index2;
bool clipStart2;
} FoundClipBoundary;
FoundClipBoundary FindNextClipBoundary(const WaveTrack* wt, double time);
FoundClipBoundary FindPrevClipBoundary(const WaveTrack* wt, double time);
void OnCursorNextClipBoundary();
void OnCursorPrevClipBoundary();
void OnCursorClipBoundary(bool next);
void OnAlignNoSync(int index);
void OnAlign(int index);

View File

@ -1227,10 +1227,18 @@ void MixerBoard::UpdateSolo(const PlayableTrack* pTrack /*= NULL*/) // NULL mean
void MixerBoard::UpdatePan(const PlayableTrack* pTrack)
{
MixerTrackCluster* pMixerTrackCluster;
FindMixerTrackCluster(pTrack, &pMixerTrackCluster);
if (pMixerTrackCluster)
pMixerTrackCluster->UpdatePan();
if (pTrack == NULL)
{
for (unsigned int i = 0; i < mMixerTrackClusters.GetCount(); i++)
mMixerTrackClusters[i]->UpdatePan();
}
else
{
MixerTrackCluster* pMixerTrackCluster;
FindMixerTrackCluster(pTrack, &pMixerTrackCluster);
if (pMixerTrackCluster)
pMixerTrackCluster->UpdatePan();
}
}
void MixerBoard::UpdateGain(const PlayableTrack* pTrack)

View File

@ -236,7 +236,7 @@ public:
void UpdateName(const PlayableTrack* pTrack);
void UpdateMute(const PlayableTrack* pTrack = NULL); // NULL means update for all tracks.
void UpdateSolo(const PlayableTrack* pTrack = NULL); // NULL means update for all tracks.
void UpdatePan(const PlayableTrack* pTrack);
void UpdatePan(const PlayableTrack* pTrack = NULL); // NULL means update for all tracks.
void UpdateGain(const PlayableTrack* pTrack);
#ifdef EXPERIMENTAL_MIDI_OUT
void UpdateVelocity(const PlayableTrack* pTrack);

View File

@ -609,7 +609,7 @@ void Sequence::Paste(sampleCount s, const Sequence *src)
auto file = mDirManager->CopyBlockFile(block.f);
if (!file) {
wxASSERT(false); // TODO: Handle this better, alert the user of failure.
return false;
return;
}
newBlock.push_back(SeqBlock(file, block.start + s));
@ -743,7 +743,7 @@ void Sequence::AppendBlock
if (!newBlock.f) {
/// \todo Error Could not paste! (Out of disk space?)
wxASSERT(false); // TODO: Handle this better, alert the user of failure.
return false;
return;
}
mBlock.push_back(newBlock);

View File

@ -371,7 +371,7 @@ BEGIN_EVENT_TABLE(TrackPanel, OverlayPanel)
EVT_MENU_RANGE(On16BitID, OnFloatID, TrackPanel::OnFormatChange)
EVT_MENU(OnRateOtherID, TrackPanel::OnRateOther)
EVT_MENU(OnSwapChannelsID, TrackPanel::OnSwapChannels)
EVT_MENU(OnSplitStereoID, TrackPanel::OnSplitStereoMono)
EVT_MENU(OnSplitStereoID, TrackPanel::OnSplitStereo)
EVT_MENU(OnSplitStereoMonoID, TrackPanel::OnSplitStereoMono)
EVT_MENU(OnMergeStereoID, TrackPanel::OnMergeStereo)
@ -630,7 +630,10 @@ void TrackPanel::BuildMenus(void)
mWaveTrackMenu->Append(OnMergeStereoID, _("Ma&ke Stereo Track"));
mWaveTrackMenu->Append(OnSwapChannelsID, _("Swap Stereo &Channels"));
mWaveTrackMenu->Append(OnSplitStereoID, _("Spl&it Stereo Track"));
// mWaveTrackMenu->Append(OnSplitStereoMonoID, _("Split Stereo to Mo&no"));
// 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()));
@ -3479,89 +3482,18 @@ void TrackPanel::StartSlide(wxMouseEvent & event)
if (mCapturedClip == NULL)
return;
}
// The captured clip is the focus, but we need to create a list
// of all clips that have to move, also...
mCapturedClipArray.clear();
// First, if click was in selection, capture selected clips; otherwise
// just the clicked-on clip
if (mCapturedClipIsSelection) {
TrackListIterator iter(GetTracks());
for (Track *t = iter.First(); t; t = iter.Next()) {
if (t->GetSelected()) {
AddClipsToCaptured(t, true);
if (t->GetKind() != Track::Wave)
mTrackExclusions.push_back(t);
}
}
}
else {
mCapturedClipArray.push_back(TrackClip(vt, mCapturedClip));
// Check for stereo partner
Track *partner = vt->GetLink();
WaveTrack *wt;
if (mCapturedClip &&
// Assume linked track is wave or null
nullptr != (wt = static_cast<WaveTrack*>(partner))) {
WaveClip *const clip =
FindClipAtTime(wt,
mViewInfo->PositionToTime(event.m_x, GetLeftOffset()));
if (clip)
mCapturedClipArray.push_back(TrackClip(partner, clip));
}
}
// Now, if sync-lock is enabled, capture any clip that's linked to a
// captured clip.
if (GetProject()->IsSyncLocked()) {
// AWD: mCapturedClipArray expands as the loop runs, so newly-added
// clips are considered (the effect is like recursion and terminates
// because AddClipsToCaptured doesn't add duplicate clips); to remove
// this behavior just store the array size beforehand.
for (unsigned int i = 0; i < mCapturedClipArray.size(); ++i) {
// Capture based on tracks that have clips -- that means we
// don't capture based on links to label tracks for now (until
// we can treat individual labels as clips)
if (mCapturedClipArray[i].clip) {
// Iterate over sync-lock group tracks.
SyncLockedTracksIterator git(GetTracks());
for (Track *t = git.StartWith(mCapturedClipArray[i].track);
t; t = git.Next() )
{
AddClipsToCaptured(t,
mCapturedClipArray[i].clip->GetStartTime(),
mCapturedClipArray[i].clip->GetEndTime() );
if (t->GetKind() != Track::Wave)
mTrackExclusions.push_back(t);
}
}
#ifdef USE_MIDI
// Capture additional clips from NoteTracks
Track *nt = mCapturedClipArray[i].track;
if (nt->GetKind() == Track::Note) {
// Iterate over sync-lock group tracks.
SyncLockedTracksIterator git(GetTracks());
for (Track *t = git.StartWith(nt); t; t = git.Next())
{
AddClipsToCaptured(t, nt->GetStartTime(), nt->GetEndTime());
if (t->GetKind() != Track::Wave)
mTrackExclusions.push_back(t);
}
}
#endif
}
}
mCapturedTrack = vt;
CreateListOfCapturedClips(clickTime);
} else {
mCapturedClip = NULL;
mCapturedClipArray.clear();
mCapturedTrack = vt;
}
mSlideUpDownOnly = event.CmdDown() && !multiToolModeActive;
mCapturedTrack = vt;
mCapturedRect = rect;
mMouseClickX = event.m_x;
@ -3587,6 +3519,83 @@ void TrackPanel::StartSlide(wxMouseEvent & event)
mMouseCapture = IsSliding;
}
void TrackPanel::CreateListOfCapturedClips(double clickTime)
{
// The captured clip is the focus, but we need to create a list
// of all clips that have to move, also...
mCapturedClipArray.clear();
// First, if click was in selection, capture selected clips; otherwise
// just the clicked-on clip
if (mCapturedClipIsSelection) {
TrackListIterator iter(GetTracks());
for (Track *t = iter.First(); t; t = iter.Next()) {
if (t->GetSelected()) {
AddClipsToCaptured(t, true);
if (t->GetKind() != Track::Wave)
mTrackExclusions.push_back(t);
}
}
}
else {
mCapturedClipArray.push_back(TrackClip(mCapturedTrack, mCapturedClip));
// Check for stereo partner
Track *partner = mCapturedTrack->GetLink();
WaveTrack *wt;
if (mCapturedClip &&
// Assume linked track is wave or null
nullptr != (wt = static_cast<WaveTrack*>(partner))) {
WaveClip *const clip = FindClipAtTime(wt, clickTime);
if (clip)
mCapturedClipArray.push_back(TrackClip(partner, clip));
}
}
// Now, if sync-lock is enabled, capture any clip that's linked to a
// captured clip.
if (GetProject()->IsSyncLocked()) {
// AWD: mCapturedClipArray expands as the loop runs, so newly-added
// clips are considered (the effect is like recursion and terminates
// because AddClipsToCaptured doesn't add duplicate clips); to remove
// this behavior just store the array size beforehand.
for (unsigned int i = 0; i < mCapturedClipArray.size(); ++i) {
// Capture based on tracks that have clips -- that means we
// don't capture based on links to label tracks for now (until
// we can treat individual labels as clips)
if (mCapturedClipArray[i].clip) {
// Iterate over sync-lock group tracks.
SyncLockedTracksIterator git(GetTracks());
for (Track *t = git.StartWith(mCapturedClipArray[i].track);
t; t = git.Next() )
{
AddClipsToCaptured(t,
mCapturedClipArray[i].clip->GetStartTime(),
mCapturedClipArray[i].clip->GetEndTime() );
if (t->GetKind() != Track::Wave)
mTrackExclusions.push_back(t);
}
}
#ifdef USE_MIDI
// Capture additional clips from NoteTracks
Track *nt = mCapturedClipArray[i].track;
if (nt->GetKind() == Track::Note) {
// Iterate over sync-lock group tracks.
SyncLockedTracksIterator git(GetTracks());
for (Track *t = git.StartWith(nt); t; t = git.Next())
{
AddClipsToCaptured(t, nt->GetStartTime(), nt->GetEndTime());
if (t->GetKind() != Track::Wave)
mTrackExclusions.push_back(t);
}
}
#endif
}
}
}
// Helper for the above, adds a track's clips to mCapturedClipArray (eliminates
// duplication of this logic)
void TrackPanel::AddClipsToCaptured(Track *t, bool withinSelection)
@ -3877,6 +3886,24 @@ void TrackPanel::DoSlide(wxMouseEvent & event)
mHSlideAmount = desiredSlideAmount;
DoSlideHorizontal();
if (mCapturedClipIsSelection) {
// Slide the selection, too
mViewInfo->selectedRegion.move(mHSlideAmount);
}
if (slidVertically) {
// NEW origin
mHSlideAmount = 0;
}
Refresh(false);
}
void TrackPanel::DoSlideHorizontal()
{
#ifdef USE_MIDI
if (mCapturedClipArray.size())
#else
@ -3947,18 +3974,55 @@ void TrackPanel::DoSlide(wxMouseEvent & event)
if (link)
link->Offset(mHSlideAmount);
}
}
if (mCapturedClipIsSelection) {
// Slide the selection, too
mViewInfo->selectedRegion.move(mHSlideAmount);
void TrackPanel::OnClipMove(bool right)
{
auto track = GetFocusedTrack();
// just dealing with clips in wave tracks for the moment. Note tracks??
if (track && track->GetKind() == Track::Wave) {
auto wt = static_cast<WaveTrack*>(track);
mCapturedClip = wt->GetClipAtTime(mViewInfo->selectedRegion.t0());
if (mCapturedClip == nullptr)
return;
mCapturedTrack = track;
mCapturedClipIsSelection = track->GetSelected() && !mViewInfo->selectedRegion.isPoint();
mTrackExclusions.clear();
CreateListOfCapturedClips(mViewInfo->selectedRegion.t0());
double desiredSlideAmount = mViewInfo->OffsetTimeByPixels(0.0, 1);
// set it to a sample point, and minimum of 1 sample point
double nSamples = rint(wt->GetRate() * desiredSlideAmount);
nSamples = std::max(nSamples, 1.0);
desiredSlideAmount = nSamples / wt->GetRate();
if (!right)
desiredSlideAmount *= -1;
mHSlideAmount = desiredSlideAmount;
DoSlideHorizontal();
// update t0 and t1. There is the possibility that the updated
// t0 may no longer be within the clip due to rounding errors,
// so t0 is adjusted so that it is.
double newT0 = mViewInfo->selectedRegion.t0() + mHSlideAmount;
if (newT0 < mCapturedClip->GetStartTime())
newT0 = mCapturedClip->GetStartTime();
if (newT0 > mCapturedClip->GetEndTime())
newT0 = mCapturedClip->GetEndTime();
double diff = mViewInfo->selectedRegion.t1() - mViewInfo->selectedRegion.t0();
mViewInfo->selectedRegion.setTimes(newT0, newT0 + diff);
ScrollIntoView(mViewInfo->selectedRegion.t0());
Refresh(false);
if (mHSlideAmount == 0.0)
MessageForScreenReader( _("clip not moved"));
}
if (slidVertically) {
// NEW origin
mHSlideAmount = 0;
}
Refresh(false);
}
@ -8064,10 +8128,11 @@ void TrackPanel::SplitStereo(bool stereo)
{
wxASSERT(mPopupMenuTarget);
if (!stereo){
if (stereo){
mPopupMenuTarget->SetPanFromChannelType();
mPopupMenuTarget->SetChannel(Track::MonoChannel);
}
mPopupMenuTarget->SetChannel(Track::MonoChannel);
// Assume partner is present, and is wave
auto partner = static_cast<WaveTrack*>(mPopupMenuTarget->GetLink());
@ -8086,10 +8151,11 @@ void TrackPanel::SplitStereo(bool stereo)
if (partner)
{
partner->SetName(mPopupMenuTarget->GetName());
if (!stereo){
if (stereo){
partner->SetPanFromChannelType();
partner->SetChannel(Track::MonoChannel); // Keep original stereo track name.
}
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)

View File

@ -252,6 +252,8 @@ class AUDACITY_DLL_API TrackPanel final : public OverlayPanel {
// (ignoring any fisheye)
virtual double GetScreenEndTime() const;
virtual void OnClipMove(bool right);
protected:
virtual MixerBoard* GetMixerBoard();
/** @brief Populates the track pop-down menu with the common set of
@ -377,6 +379,8 @@ protected:
virtual void HandleSlide(wxMouseEvent & event);
virtual void StartSlide(wxMouseEvent &event);
virtual void DoSlide(wxMouseEvent &event);
virtual void DoSlideHorizontal();
virtual void CreateListOfCapturedClips(double clickTime);
virtual void AddClipsToCaptured(Track *t, bool withinSelection);
virtual void AddClipsToCaptured(Track *t, double t0, double t1);

View File

@ -2228,6 +2228,17 @@ WaveClip* WaveTrack::GetClipAtSample(sampleCount sample)
return NULL;
}
WaveClip* WaveTrack::GetClipAtTime(double time)
{
// When the time is both the end of a clip and the start of the next clip, the
// latter clip is returned.
const auto clips = SortedClipArray();
auto result = find_if(clips.rbegin(), clips.rend(), [&] (WaveClip* const& clip) {
return time >= clip->GetStartTime() && time <= clip->GetEndTime(); });
return result != clips.rend() ? *result : nullptr;
}
Envelope* WaveTrack::GetEnvelopeAtX(int xcoord)
{
WaveClip* clip = GetClipAtX(xcoord);

View File

@ -276,6 +276,7 @@ class AUDACITY_DLL_API WaveTrack final : public PlayableTrack {
Envelope* GetEnvelopeAtX(int xcoord);
WaveClip* GetClipAtSample(sampleCount sample);
WaveClip* GetClipAtTime(double time);
//
// Getting information about the track's internal block sizes

View File

@ -164,7 +164,11 @@ void GUIPrefs::PopulateOrExchange(ShuttleGui & S)
S.StartStatic(_("Theme"));
{
#ifdef EXPERIMENTAL_DA
S.StartRadioButtonGroup(wxT("/GUI/Theme"), wxT("dark"));
#else
S.StartRadioButtonGroup(wxT("/GUI/Theme"), wxT("classic"));
#endif
{
S.TieRadioButton(_("Classic"),
wxT("classic"));