Imagine that you are editing some raw audio. As you’re listening to the playback you notice the beginning of an area that you wish to remove. You use “Stop Here”, search around a little bit and find the exact start point at which you wish to start deleting – you insert a label. You start listening again and find the endpoint for your deletion – you insert a label.
Now you start the process of deleting the unwanted audio. First you do some zooming out and in until both labels are visible on screen so that you may click at the point of one of the labels and drag select over to the other label (an alternative is to click on one of the labels and drag the selection toward the other label and allow the display to scroll automatically). No matter what method you use to select the audio you must be very careful to click right on the label. Now you press Delete and as often as not you want to insert a label at this point as well.
I propose a new function – added to the edit menu so that it may be accessed via a shortcut key: Delete Between Labels. This function would allow you to click anywhere between two labels (and/or label regions) and with a shortcut keypress delete all the audio between the two. It would also insert a new label at this point, the label would be empty and active so a single backspace keypress would delete it if unneeded. If there was no label at the start or end of the audio and you made your click such that the selection would fall between the label and the start/end a dialog would open asking if you wish to proceed as though there were a label at the start/end.
As it turns out I found this idea so attractive I wrote the code! It turned out to be very simple. First you must change the Menus.h header file by adding a single line (I have surrounded it with a little context and labeled it with my initials as a comment (I would suggest putting it at or near line number 244):
void OnSelectAllTracks();
void OnDeleteBetweenLabels();//efm5
// View Menu
Next you have to add a line of code to Menus.cpp so that Audacity knows what to do when you choose the menu item-- this single line needs to go at about line number 502 (again I surrounded with some context and label with my initials):
c->AddItem(wxT("UnlockPlayRegion"), _("&Unlock"), FN(OnUnlockPlayRegion),
PlayRegionLockedFlag,
PlayRegionLockedFlag);
c->EndSubMenu();
/////////////////////////////////////////////////////////////////////////////
c->AddItem(wxT("DeleteBetweenLabels"), _("Delete Between Labels"), FN(OnDeleteBetweenLabels), wxT("Ctrl+Shift+D"));//efm5
#ifndef __WXMAC__
c->AddSeparator();
#endif
// The default shortcut key for Preferences is different on different platforms.
Note that I have chosen a shortcut key <Ctrl+Shift+D> which happens to be unused and is a good mnemonic for Delete Between Labels. Finally, I wrote a single function which accomplishes all work – it should be added at the very end of Menus.cpp.
void AudacityProject::OnDeleteBetweenLabels()
{
AudacityProject * activeProject = GetActiveProject();
double currentCursor = activeProject->GetSel0();
if (currentCursor != activeProject->GetSel1()) {
wxMessageBox(_("You may not perform this action with audio selected."));
return;
}
TrackList * trackList = activeProject->GetTracks();
TrackListIterator trackListIterator;
int numLabels = 0;
int numWaveTracks = 0;
LabelTrack * labelTrack = NULL;
WaveTrack * waveTrack = NULL;
Track * aTrack;
for (aTrack = trackListIterator.First(mTracks); aTrack != NULL; aTrack = trackListIterator.Next()) {
switch (aTrack->GetKind()) {
// Count WaveTracks, and for linked pairs, count only the second of the pair.
case Track::Wave:
{
waveTrack = (WaveTrack *)aTrack;
if (aTrack->GetLinked() == false)
numWaveTracks++;
break;
}
case Track::Label:
{
// Supports only one LabelTrack.
if (labelTrack == NULL) {
labelTrack = (LabelTrack*)aTrack;
numLabels = labelTrack->GetNumLabels();
}
break;
}
}
}
if (numWaveTracks < 1) {
wxString dialogCaption(_("Multiple Tracks Found"));
wxString dialogMessage(_("Delete Between Labels might not be suitable for multiple tracks!nnContinue anyway?"));
wxMessageDialog messageDialog(this, dialogMessage, dialogCaption, wxICON_QUESTION | wxYES_NO | wxYES_DEFAULT);
int userResponse = messageDialog.ShowModal();
if (userResponse == wxID_NO)
return;
}
if (labelTrack) {
double startSelection = 0;
double endSelection = 0;
const LabelStruct * aLabel = NULL;
int labelIndex = 0;
//find the last label which is before the cursor
bool foundStart = false;
bool foundEnd = false;
bool reLabel = true;
while (labelIndex < numLabels) {
aLabel = labelTrack->GetLabel(labelIndex);
if (aLabel->t != aLabel->t1) {//region
if ( (aLabel->t == currentCursor) ||
(aLabel->t1 == currentCursor) ||
( (aLabel->t > currentCursor) && (aLabel->t1 < currentCursor) ) ) {
startSelection = aLabel->t;
foundStart = true;
endSelection = aLabel->t1;
foundEnd = true;
labelIndex = numLabels;
}
else {
if (aLabel->t1 < currentCursor) {
startSelection = aLabel->t1;
foundStart = true;
labelIndex++;
}
else
labelIndex = numLabels;
}
}
else {//single
if (aLabel->t == currentCursor) {
wxMessageBox(_("The cursor may not be on a label."));
return;
}
if (aLabel->t < currentCursor) {
startSelection = aLabel->t;
foundStart = true;
labelIndex++;
}
else
labelIndex = numLabels;
}
}
//find the first label which is after the cursor
if (!foundEnd) {
labelIndex = 0;
while (labelIndex < numLabels) {
aLabel = labelTrack->GetLabel(labelIndex);
if (aLabel->t != aLabel->t1) {//region
if (aLabel->t > currentCursor) {
endSelection = aLabel->t;
foundEnd = true;
labelIndex = numLabels;
reLabel = false;
}
else
labelIndex++;
}
else {//single
if (aLabel->t > currentCursor) {
endSelection = aLabel->t;
foundEnd = true;
labelIndex = numLabels;
}
else
labelIndex++;
}
}
}
if ((!foundStart) || (!foundEnd)) {
wxString dialogCaption(_("Label Not Found"));
wxString dialogMessage;
if (!foundStart)
dialogMessage.Append(_("No start label found.nnDo you want to delete all audio betweennthe START and the FIRST LABEL?"));
else //!foundEnd
dialogMessage.Append(_("No end label found.nnDo you want to delete all audio betweennthe LAST LABEL and the END?"));
wxMessageDialog messageDialog(this, dialogMessage, dialogCaption, wxICON_QUESTION | wxYES_NO | wxYES_DEFAULT);
int userResponse = messageDialog.ShowModal();
if (userResponse == wxID_YES) {
if (!foundStart){
startSelection = waveTrack->GetStartTime();
foundStart = true;
}
else {//!foundEnd
endSelection = waveTrack->GetEndTime();
foundEnd = true;
}
}
}
if (foundStart && foundEnd) {
activeProject->SetSel0(startSelection);
activeProject->SetSel1(endSelection);
activeProject->UpdateLayout();
TrackPanel * trackPanel = activeProject->GetTrackPanel();
trackPanel->Refresh(false);
OnDelete();
if (reLabel)
OnAddLabel();
}
}
}
I have attached a patch file.
delBetweenLabels.patch (6.4 KB)