1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-22 23:30:07 +02:00

Merge branch 'master' into HEAD

This commit is contained in:
Paul Licameli 2017-05-25 08:15:20 -04:00
commit 250a2ea12a
170 changed files with 8275 additions and 10178 deletions

55
images/Help.xpm Normal file
View File

@ -0,0 +1,55 @@
/* XPM */
static const char * Help_xpm[] = {
"21 21 31 1",
" c None",
". c #6699FF",
"+ c #8CB2FE",
"@ c #CDDDFE",
"# c #F0F5FE",
"$ c #F9FAFE",
"% c #E4ECFE",
"& c #A4C2FE",
"* c #6698FE",
"= c #FFFFFF",
"- c #A9C5FE",
"; c #DAE6FE",
"> c #95B8FE",
", c #6D9DFE",
"' c #71A0FE",
") c #BFD4FE",
"! c #E9F0FE",
"~ c #F7F9FE",
"{ c #90B4FE",
"] c #C8DAFE",
"^ c #78A5FE",
"/ c #EEF3FE",
"( c #E3ECFE",
"_ c #6F9FFE",
": c #7CA7FE",
"< c #EDF2FE",
"[ c #E6EEFE",
"} c #DBE7FE",
"| c #87AEFE",
"1 c #FBFCFE",
"2 c #6799FE",
" ..... ",
" ......... ",
" ............. ",
" ............... ",
" .....+@#$%&*..... ",
" .....======-..... ",
" ......;>,')=!...... ",
" ..........,=~...... ",
"...........{=].......",
"..........^/(_.......",
".........:<['........",
".........}=|.........",
".........1=2.........",
" ........==......... ",
" ................... ",
" .......==........ ",
" .......==........ ",
" ............... ",
" ............. ",
" ......... ",
" ..... "};

View File

@ -3,6 +3,7 @@
;type analyze
;categories "http://lv2plug.in/ns/lv2core#AnalyserPlugin"
;name "Silence Finder..."
;manpage "Silence_Finder"
;action "Finding silence..."
;info "Adds point labels in areas of silence according to the specified\nlevel and duration of silence. If too many silences are detected,\nincrease the silence level and duration; if too few are detected,\nreduce the level and duration."
;author "Alex S. Brown"

View File

@ -3,6 +3,7 @@
;type analyze
;categories "http://lv2plug.in/ns/lv2core#AnalyserPlugin"
;name "Sound Finder..."
;manpage "Sound_Finder"
;action "Finding sound..."
;info "Adds region labels for areas of sound according to the specified level\nand duration of surrounding silence. If too many labels are produced,\nincrease the silence level and duration; if too few are produced,\nreduce the level and duration."
;author "Jeremy R. Brown"

View File

@ -3,6 +3,7 @@
;type process spectral
;preview linear
;name "Spectral edit parametric EQ..."
;manpage "Spectral_edit_parametric_EQ"
;action "Filtering..."
;author "Paul Licameli"
;copyright "Released under terms of the GNU General Public License version 2"

View File

@ -3,6 +3,7 @@
;type process spectral
;preview linear
;name "Spectral edit shelves..."
;manpage "Spectral_edit_shelves"
;action "Filtering..."
;author "Paul Licameli"
;copyright "Released under terms of the GNU General Public License version 2"

View File

@ -3,6 +3,7 @@
;type process
;categories "http://lv2plug.in/ns/lv2core#MixerPlugin"
;name "Studio Fade Out"
;manpage "Fades#studio_fadeout"
;action "Applying Fade..."
;author "Steve Daulton"
;copyright "Released under terms of the GNU General Public License version 2"

View File

@ -5,6 +5,8 @@
;preview selection
;categories "http://lv2plug.in/ns/lv2core#MixerPlugin"
;name "Adjustable Fade..."
;manpage "Adjustable_Fade"
;debug false
;action "Applying Fade..."
;author "Steve Daulton"
;copyright "Released under terms of the GNU General Public License version 2"

View File

@ -3,6 +3,7 @@
;type analyze
;categories "http://audacityteam.org/namespace#OnsetDetector"
;name "Beat Finder..."
;manpage "Beat_Finder"
;action "Finding beats..."
;author "Audacity"
;copyright "Released under terms of the GNU General Public License version 2"

View File

@ -3,6 +3,7 @@
;type process
;preview enabled
;name "Clip Fix..."
;manpage "Clip_Fix"
;action "Reconstructing clips..."
;author "Benjamin Schwartz and Steve Daulton"
;copyright "Licensing confirmed under terms of the GNU General Public License version 2"

View File

@ -4,6 +4,7 @@
;mergeclips 1
;restoresplits 0
;name "Crossfade Clips"
;manpage "Crossfade_Clips"
;action "Crossfading..."
;author "Steve Daulton"
;copyright "Released under terms of the GNU General Public License version 2"

View File

@ -2,6 +2,8 @@
;version 4
;type process
;name "Crossfade Tracks..."
;manpage "Crossfade_Tracks"
;debug disabled
;action "Crossfading..."
;preview selection
;author "Steve Daulton"
@ -69,4 +71,6 @@ audio clip, fade in, otherwise fade out."
(setf out-dist (min out-dist (abs (- end (second (nth i clips)))))))
(if (< in-dist out-dist) 'in 'out)))
(crossfade type direction curve)
(if (< (length (get '*selection* 'tracks)) 2)
"Error.\nSelect 2 (or more) tracks to crossfade."
(crossfade type direction curve))

View File

@ -4,6 +4,7 @@
;preview linear
;categories "http://lv2plug.in/ns/lv2core#DelayPlugin"
;name "Delay..."
;manpage "Delay"
;action "Applying Delay Effect..."
;author "Steve Daulton"
;copyright "Released under terms of the GNU General Public License version 2"

View File

@ -2,6 +2,7 @@
;version 4
;type analyze
;name "Regular Interval Labels..."
;manpage "Regular_Interval_Labels"
;action "Adding equally-spaced labels to the label track..."
;author "Steve Daulton"
;copyright "Released under terms of the GNU General Public License version 2"

View File

@ -3,6 +3,8 @@
;type process
;preview linear
;name "High Pass Filter..."
;manpage "High_Pass_Filter"
;debug disabled
;action "Performing High Pass Filter..."
;author "Dominic Mazzoni"
;copyright "Released under terms of the GNU General Public License version 2"

View File

@ -3,6 +3,8 @@
;type process
;categories "http://lv2plug.in/ns/lv2core/#DynamicsPlugin"
;name "Limiter..."
;manpage "Limiter"
;debug false
;action "Limiting..."
;preview enabled
;author "Steve Daulton"

View File

@ -3,6 +3,8 @@
;type process
;preview linear
;name "Low Pass Filter..."
;manpage "Low_Pass_Filter"
;debug disabled
;action "Performing Low Pass Filter..."
;author "Dominic Mazzoni"
;copyright "Released under terms of the GNU General Public License version 2"

View File

@ -3,28 +3,27 @@
;type process
;preview linear
;name "Notch Filter..."
;manpage "Notch_Filter"
;debug false
;action "Applying Notch Filter..."
;author "Steve Daulton and Bill Wharrie"
;copyright "Released under terms of the GNU General Public License version 2"
;; notch.ny by Steve Daulton and Bill Wharrie, September 2010.
;; Last updated August 2015.
;; notch.ny by Steve Daulton and Bill Wharrie
;; Released under terms of the GNU General Public License version 2:
;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html .
;control frequency "Frequency (Hz)" float-text "" 60 0 nil
;control q "Q (higher value reduces width)" float-text "" 1 0.1 nil
;control q "Q (higher value reduces width)" float-text "" 1 0.1 1000
(cond
((< frequency 0.1) "Frequency must be at least 0.1 Hz.")
((>= frequency (/ *sound-srate* 2.0))
(format nil "Error:~%~%Frequency (~a Hz) is too high for track sample rate.~%~%~
Track sample rate is ~a Hz~%~
Frequency must be less than ~a Hz."
frequency
*sound-srate*
(/ *sound-srate* 2.0)))
((< q 0.1) "Q must be at least 0.1.")
(format nil "Error:~%~%Frequency (~a Hz) is too high for track sample rate.~%~%~
Track sample rate is ~a Hz.~%~
Frequency must be less than ~a Hz."
frequency
*sound-srate*
(/ *sound-srate* 2.0)))
(T (notch2 *track* frequency q)))

View File

@ -3,6 +3,7 @@
;type generate
;categories "http://lv2plug.in/ns/lv2core#GeneratorPlugin"
;name "Pluck..."
;manpage "Pluck"
;preview linear
;action "Generating pluck sound..."
;info "MIDI values for C notes: 36, 48, 60 [middle C], 72, 84, 96."

View File

@ -3,6 +3,7 @@
;type generate
;categories "http://lv2plug.in/ns/lv2core#GeneratorPlugin"
;name "Rhythm Track..."
;manpage "Rhythm_Track"
;preview linear
;action "Generating Rhythm..."
;author "Dominic Mazzoni"

View File

@ -4,6 +4,7 @@
;categories "http://lv2plug.in/ns/lv2core#GeneratorPlugin"
;preview linear
;name "Risset Drum..."
;manpage "Risset_Drum"
;action "Generating Risset Drum..."
;author "Steven Jones"
;copyright "Released under terms of the GNU General Public License version 2"

View File

@ -2,6 +2,7 @@
;version 3
;type analyze
;name "Sample Data Export..."
;manpage "Sample_Data_Export"
;action "Analyzing..."
;maxlen 1000001
;categories "http://lv2plug.in/ns/lv2core#AnalyserPlugin"

View File

@ -2,6 +2,7 @@
;version 4
;type generate
;name "Sample Data Import..."
;manpage "Sample_Data_Import"
;action "Reading and rendering samples..."
;author "Steve Daulton"
;copyright "Released under terms of the GNU General Public License version 2"

View File

@ -4,6 +4,8 @@
;preview linear
;categories "http://lv2plug.in/ns/lv2core#ModulatorPlugin"
;name "Tremolo..."
;manpage "Tremolo"
;debug disabled
;action "Applying Tremolo..."
;author "Steve Daulton"
;copyright "Released under terms of the GNU General Public License version 2"
@ -18,11 +20,8 @@
;control wave "Waveform type" choice "sine,triangle,sawtooth,inverse sawtooth,square" 0
;control phase "Starting phase (degrees)" int "" 0 -180 180
;control wet "Wet level (percent)" int "" 40 0 100
;control lfo "Frequency (Hz)" real "" 4 0 10
; Limit to sensible range
(setq lfo (min 1000 (max lfo (/ (get-duration 1)))))
;control wet "Wet level (percent)" int "" 40 1 100
;control lfo "Frequency (Hz)" float-text "" 4 0.001 1000
; Convert % to linear
(setq wet (/ wet 200.0))

View File

@ -3,6 +3,7 @@
;type process
;categories "http://lv2plug.in/ns/lv2core#MixerPlugin"
;name "Vocal Reduction and Isolation..."
;manpage "Vocal_Reduction_and_Isolation"
;action "Applying Action..."
;author "Robert Haenggi"
;copyright "Released under terms of the GNU General Public License version 2"

View File

@ -3,6 +3,7 @@
;type process
;preview linear
;name "Vocal Remover..."
;manpage "Vocal_Remover"
;action "Removing center-panned audio..."
;info "For reducing center-panned vocals"
;author "Steve Daulton"

View File

@ -4,6 +4,7 @@
;preview enabled
;categories "http://lv2plug.in/ns/lv2core#SpectralPlugin"
;name "Vocoder..."
;manpage "Vocoder"
;action "Processing Vocoder..."
;author "Edgar-RFT"
;copyright "Released under terms of the GNU General Public License version 2"

View File

@ -85,11 +85,6 @@ from there. Audacity will look for a file called "Pause.png".
DEFINE_IMAGE( bmpMic, wxImage( 25, 25 ), wxT("Mic"));
DEFINE_IMAGE( bmpSpeaker, wxImage( 25, 25 ), wxT("Speaker"));
DEFINE_IMAGE( bmpPinnedPlayHead, wxImage( 27, 27 ), wxT("PinnedPlayHead"));
DEFINE_IMAGE( bmpUnpinnedPlayHead, wxImage( 27, 27 ), wxT("UnpinnedPlayHead"));
DEFINE_IMAGE( bmpPinnedRecordHead, wxImage( 27, 27 ), wxT("PinnedRecordHead"));
DEFINE_IMAGE( bmpUnpinnedRecordHead, wxImage( 27, 27 ), wxT("UnpinnedRecordHead"));
SET_THEME_FLAGS( resFlagPaired );
DEFINE_IMAGE( bmpZoomFit, wxImage( 27, 27 ), wxT("ZoomFit"));
DEFINE_IMAGE( bmpZoomFitDisabled, wxImage( 27, 27 ), wxT("ZoomFitDisabled"));
@ -135,6 +130,8 @@ from there. Audacity will look for a file called "Pause.png".
DEFINE_IMAGE( bmpTnSelectSoundDisabled, wxImage( 24, 24 ), wxT("TnSelectSoundDisabled"));
DEFINE_IMAGE( bmpTnSelectSilence, wxImage( 24, 24 ), wxT("TnSelectSilence"));
DEFINE_IMAGE( bmpTnSelectSilenceDisabled, wxImage( 24, 24 ), wxT("TnSelectSilenceDisabled"));
DEFINE_IMAGE( bmpOptions, wxImage( 24, 24 ), wxT("Options"));
DEFINE_IMAGE( bmpOptionsDisabled, wxImage( 24, 24 ), wxT("OptionsDisabled"));
SET_THEME_FLAGS( resFlagNone );
DEFINE_IMAGE( bmpLabelGlyph0, wxImage( 15, 23 ), wxT("LabelGlyph0"));
@ -161,13 +158,21 @@ from there. Audacity will look for a file called "Pause.png".
DEFINE_IMAGE( bmpPostfishLoop, wxImage( 29, 17 ), wxT("PostfishLoop"));
SET_THEME_FLAGS( resFlagNone );
SET_THEME_FLAGS( resFlagSkip );
DEFINE_IMAGE( bmpDockDown, wxImage( 15, 55 ), wxT("DockDown"));
SET_THEME_FLAGS( resFlagSkip );
DEFINE_IMAGE( bmpDockDownShort, wxImage( 15, 27 ), wxT("DockDownShort"));
SET_THEME_FLAGS( resFlagSkip );
DEFINE_IMAGE( bmpDockOver, wxImage( 15, 55 ), wxT("DockOver"));
SET_THEME_FLAGS( resFlagSkip );
DEFINE_IMAGE( bmpDockOverShort, wxImage( 15, 27 ), wxT("DockOverShort"));
SET_THEME_FLAGS( resFlagSkip );
DEFINE_IMAGE( bmpDockUp, wxImage( 15, 55 ), wxT("DockUp"));
SET_THEME_FLAGS( resFlagSkip );
DEFINE_IMAGE( bmpDockUpShort, wxImage( 15, 27 ), wxT("DockUpShort"));
SET_THEME_FLAGS( resFlagSkip );
DEFINE_IMAGE( bmpPinnedPlayRecordHead, wxImage( 27, 27 ), wxT("PinnedPlayRecordHead"));
SET_THEME_FLAGS( resFlagSkip );
DEFINE_IMAGE( bmpUnpinnedPlayRecordHead, wxImage( 27, 27 ), wxT("UnpinnedPlayRecordHead"));
DEFINE_IMAGE( bmpSyncLockSelTile, wxImage(20, 22), wxT("SyncLockSelTile"));
@ -176,7 +181,6 @@ from there. Audacity will look for a file called "Pause.png".
DEFINE_IMAGE( bmpSyncLockTracksUp, wxImage( 20, 20 ), wxT("SyncLockTracksUp"));
DEFINE_IMAGE( bmpSyncLockTracksDisabled, wxImage( 20, 20 ), wxT("SyncLockTracksDisabled"));
DEFINE_IMAGE( bmpToggleScrubRuler, wxImage( 20, 20 ), wxT("ToggleScrubRuler"));
// DEFINE_IMAGE( bmpSliderThumb, wxImage( 11, 14 ), wxT("SliderThumb"));
DEFINE_IMAGE( bmpSyncLockIcon, wxImage(12, 12), wxT("SyncLockIcon"));
SET_THEME_FLAGS( resFlagNewLine );
@ -189,7 +193,9 @@ from there. Audacity will look for a file called "Pause.png".
SET_THEME_FLAGS( resFlagNone );
DEFINE_IMAGE( bmpSliderThumb, wxImage( 11, 20 ), wxT("SliderThumb"));
SET_THEME_FLAGS( resFlagSkip );
DEFINE_IMAGE( bmpSlider, wxImage( 80, 20 ), wxT("Slider"));
SET_THEME_FLAGS( resFlagSkip );
DEFINE_IMAGE( bmpHiliteSlider, wxImage( 80, 20 ), wxT("HiliteSlider"));
DEFINE_IMAGE( bmpUpButtonExpandSel, wxImage( 96, 18 ), wxT("UpButtonExpandSel"));
DEFINE_IMAGE( bmpDownButtonExpandSel, wxImage( 96, 18 ), wxT("DownButtonExpandSel"));
@ -206,8 +212,6 @@ from there. Audacity will look for a file called "Pause.png".
DEFINE_IMAGE( bmpUpButtonSmall, wxImage( 27, 27 ), wxT("UpButtonSmall"));
DEFINE_IMAGE( bmpDownButtonSmall, wxImage( 27, 27 ), wxT("DownButtonSmall"));
DEFINE_IMAGE( bmpHiliteButtonSmall, wxImage( 27, 27 ), wxT("HiliteButtonSmall"));
//DEFINE_IMAGE( bmpVolumeSlider, wxImage( 100, 28 ), wxT("VolumeSlider"));
//DEFINE_IMAGE( bmpVolumeSliderThumb, wxImage( 10, 28 ), wxT("VolumeSliderThumb"));
SET_THEME_FLAGS( resFlagNone );
DEFINE_IMAGE( bmpMacUpButton, wxImage( 36, 36 ), wxT("MacUpButton"));
@ -216,8 +220,6 @@ from there. Audacity will look for a file called "Pause.png".
DEFINE_IMAGE( bmpMacUpButtonSmall, wxImage( 27, 27 ), wxT("MacUpButtonSmall"));
DEFINE_IMAGE( bmpMacDownButtonSmall, wxImage( 27, 27 ), wxT("MacDownButtonSmall"));
DEFINE_IMAGE( bmpMacHiliteButtonSmall, wxImage( 27, 27 ), wxT("MacHiliteButtonSmall"));
DEFINE_IMAGE( bmpMacSlider, wxImage( 100, 28 ), wxT("MacSlider"));
DEFINE_IMAGE( bmpMacSliderThumb, wxImage( 17, 28 ), wxT("MacSliderThumb"));
SET_THEME_FLAGS( resFlagInternal );
DEFINE_IMAGE( bmpRecoloredUpLarge, wxImage( 48, 48 ), wxT("RecoloredUpLarge"));
@ -241,19 +243,7 @@ from there. Audacity will look for a file called "Pause.png".
DEFINE_IMAGE( bmpTopFrequencyCursor, wxImage( 32, 32 ), wxT("TopFrequencyCursor"));
DEFINE_IMAGE( bmpBandWidthCursor, wxImage(32, 32), wxT("BandWidthCursor"));
/*
DEFINE_IMAGE( bmpToolBarToggle, wxImage( 43, 35 ), wxT("ToolBarToggle"));
DEFINE_IMAGE( bmpToolBarTarget, wxImage( 17, 26 ), wxT("ToolBarTarget"));
DEFINE_IMAGE( bmpToolBarGrabber, wxImage( 17, 8 ), wxT("ToolBarGrabber"));
DEFINE_IMAGE( bmpArrow, wxImage( 9, 16 ), wxT("Arrow"));
DEFINE_IMAGE( bmpUploadFile, wxImage( 16, 16 ), wxT("UploadFile"));
DEFINE_IMAGE( bmpUploadFolder, wxImage( 16, 16 ), wxT("UploadFolder"));
DEFINE_IMAGE( bmpUploadMp3, wxImage( 16, 16 ), wxT("UploadMp3"));
DEFINE_IMAGE( bmpUploadUp, wxImage( 16, 16 ), wxT("UploadUp"));
*/
SET_THEME_FLAGS( resFlagNewLine );
//SET_THEME_FLAGS( resFlagNewLine );
// DA: The logo with name xpm has a different width.
#ifdef EXPERIMENTAL_DA
@ -264,9 +254,11 @@ from there. Audacity will look for a file called "Pause.png".
#define LOGOWITHNAME_HEIGHT 200
SET_THEME_FLAGS( resFlagSkip | resFlagNewLine );
DEFINE_IMAGE( bmpAudacityLogo, wxImage( 215, 190 ), wxT("AudacityLogo"));
DEFINE_IMAGE( bmpAudacityLogo48x48, wxImage( 48, 48 ), wxT("AudacityLogo48x48"));
DEFINE_COLOUR( clrBlank, wxColour( 64, 64, 64), wxT("Blank"));
DEFINE_COLOUR( clrUnselected, wxColour( 30, 30, 30), wxT("Unselected"));
DEFINE_COLOUR( clrSelected, wxColour( 93, 65, 93), wxT("Selected"));

View File

@ -1508,15 +1508,26 @@ bool AudacityApp::OnInit()
AudacityProject *project;
{
// Bug 718: Position splash screen on same screen
// as where Audacity project will appear.
wxRect wndRect;
bool bMaximized = false;
bool bIconized = false;
GetNextWindowPlacement(&wndRect, &bMaximized, &bIconized);
wxSplashScreen temporarywindow(
logo,
wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_NO_TIMEOUT,
wxSPLASH_NO_CENTRE | wxSPLASH_NO_TIMEOUT,
0,
NULL,
wxID_ANY,
wxDefaultPosition,
wxDefaultSize,
wxSTAY_ON_TOP);
// Possibly move it on to the second screen...
temporarywindow.SetPosition( wndRect.GetTopLeft() );
// Centered on whichever screen it is on.
temporarywindow.Center();
temporarywindow.SetTitle(_("Audacity is starting up..."));
SetTopWindow(&temporarywindow);

View File

@ -2130,7 +2130,7 @@ void AudioIO::PrepareMidiIterator(bool send, double offset)
// Iterator not yet intialized, must add each track...
for (i = 0; i < nTracks; i++) {
NoteTrack *t = mMidiPlaybackTracks[i];
Alg_seq_ptr seq = t->GetSequence();
Alg_seq_ptr seq = &t->GetSeq();
// mark sequence tracks as "in use" since we're handing this
// off to another thread and want to make sure nothing happens
// to the data until playback finishes. This is just a sanity check.
@ -2387,7 +2387,7 @@ void AudioIO::StopStream()
int nTracks = mMidiPlaybackTracks.size();
for (int i = 0; i < nTracks; i++) {
NoteTrack *t = mMidiPlaybackTracks[i];
Alg_seq_ptr seq = t->GetSequence();
Alg_seq_ptr seq = &t->GetSeq();
seq->set_in_use(false);
}

File diff suppressed because it is too large Load Diff

View File

@ -933,14 +933,15 @@ void Envelope::InsertSpace( double t0, double tlen )
point.SetT( point.GetT() + tlen );
}
// increase track len, before insert or replace,
// since it range chacks the values.
mTrackLen += tlen;
// Preserve the right-side limit.
if ( 1 + range.first < range.second )
// There was a control point already.
;
else
InsertOrReplaceRelative( t0 + tlen, val );
mTrackLen += tlen;
}
int Envelope::Reassign(double when, double value)

View File

@ -53,7 +53,7 @@ public:
* This returns the string path to where the user may have put plug-ins
* if they don't have system admin rights. Under default settings, it's
* <DataDir>/Plug-Ins/ */
static wxString PlugInDir();
static wxString PlugInDir(); // Windows and Mac only
static wxString ThemeDir();
static wxString ThemeComponentsDir();
static wxString ThemeCachePng();

View File

@ -493,6 +493,9 @@ FreqWindow::FreqWindow(wxWindow * parent, wxWindowID id,
Layout();
Fit();
// Bug 1607:
Center();
SetMinSize(GetSize());
mAlgChoice->SetFocus();

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,7 @@ for drawing different aspects of the label and its text box.
#include <stdio.h>
#include <algorithm>
#include <limits.h>
#include <wx/bitmap.h>
#include <wx/brush.h>
@ -473,7 +474,9 @@ void LabelTrack::ComputeLayout(const wxRect & r, const ZoomInfo &zoomInfo) const
// Initially none of the rows have been used.
// So set a value that is less than any valid value.
{
const int xStart = zoomInfo.TimeToPosition(0.0, r.x) - 100;
// Bug 502: With dragging left of zeros, labels can be in
// negative space. So set least possible value as starting point.
const int xStart = INT_MIN;
for (auto &x : xUsed)
x = xStart;
}

View File

@ -461,6 +461,7 @@ void Lyrics::OnKeyEvent(wxKeyEvent & event)
{
AudacityProject *project = GetActiveProject();
project->GetCommandManager()->FilterKeyEvent(project, event, true);
event.Skip();
}
void Lyrics::OnPaint(wxPaintEvent & WXUNUSED(event))

View File

@ -130,6 +130,7 @@ LyricsWindow::LyricsWindow(AudacityProject *parent):
wxCommandEventHandler(LyricsWindow::OnTimer),
NULL,
this);
Center();
}
LyricsWindow::~LyricsWindow()

View File

@ -999,7 +999,7 @@ void AudacityProject::CreateMenusAndCommands()
c->AddSeparator();
c->AddCheck(wxT("MoveSelectionWithTracks"), _("&Move Selection with Tracks (on/off)"),
FN(OnMoveSelectionWithTracks),
gPrefs->Read(wxT("/GUI/MoveSelectionWithTracks"), 1L),
gPrefs->Read(wxT("/GUI/MoveSelectionWithTracks"), 0L),
AlwaysEnabledFlag, AlwaysEnabledFlag);
c->EndSubMenu();
@ -1240,7 +1240,7 @@ void AudacityProject::CreateMenusAndCommands()
// Ext-Bar Menu
/////////////////////////////////////////////////////////////////////////////
c->BeginMenu("Ext-Bar");
c->BeginMenu("Ext-&Bar");
//////////////////////////////////////////////////////////////////////////
@ -1399,7 +1399,7 @@ void AudacityProject::CreateMenusAndCommands()
/////////////////////////////////////////////////////////////////////////////
c->SetDefaultFlags(AlwaysEnabledFlag, AlwaysEnabledFlag);
c->BeginMenu("Ext-Command");
c->BeginMenu("Ext-Co&mmand");
c->AddGlobalCommand(wxT("PrevWindow"), _("Move backward thru active windows"), FN(PrevWindow), wxT("Alt+Shift+F6"));
c->AddGlobalCommand(wxT("NextWindow"), _("Move forward thru active windows"), FN(NextWindow), wxT("Alt+F6"));
@ -1438,8 +1438,8 @@ void AudacityProject::CreateMenusAndCommands()
c->AddItem(wxT("CursorLongJumpLeft"), _("Cursor Long Jump Left"), FN(OnCursorLongJumpLeft), wxT("Shift+,"));
c->AddItem(wxT("CursorLongJumpRight"), _("Cursor Long Jump Right"), FN(OnCursorLongJumpRight), wxT("Shift+."));
c->AddItem(wxT("ClipLeft"), _("Clip Left"), FN(OnClipLeft), wxT(""));
c->AddItem(wxT("ClipRight"), _("Clip Right"), FN(OnClipRight), wxT(""));
c->AddItem(wxT("ClipLeft"), _("Clip Left"), FN(OnClipLeft), wxT("\twantKeyup"));
c->AddItem(wxT("ClipRight"), _("Clip Right"), FN(OnClipRight), wxT("\twantKeyup"));
c->EndSubMenu();
//////////////////////////////////////////////////////////////////////////
@ -2082,7 +2082,7 @@ void AudacityProject::SelectAllIfNone()
auto flags = GetUpdateFlags();
if(!(flags & TracksSelectedFlag) ||
(mViewInfo.selectedRegion.isPoint()))
OnSelectAll();
OnSelectSomething();
}
void AudacityProject::StopIfPaused()
@ -3081,14 +3081,55 @@ void AudacityProject::OnSelContractRight(const wxEvent * evt)
OnCursorLeft( true, true, bKeyUp );
}
void AudacityProject::OnClipLeft()
void AudacityProject::DoClipLeftOrRight(bool right, bool keyUp )
{
mTrackPanel->OnClipMove(false);
if (keyUp) {
GetUndoManager()->StopConsolidating();
return;
}
auto &panel = *GetTrackPanel();
auto amount = TrackPanel::OnClipMove
( mViewInfo, panel.GetFocusedTrack(),
*GetTracks(), IsSyncLocked(), right );
panel.ScrollIntoView(mViewInfo.selectedRegion.t0());
panel.Refresh(false);
if (amount != 0.0) {
wxString message = right? _("Time shifted clips to the right") :
_("Time shifted clips to the left");
// The following use of the UndoPush flags is so that both a single
// keypress (keydown, then keyup), and holding down a key
// (multiple keydowns followed by a keyup) result in a single
// entry in Audacity's history dialog.
PushState(message, _("Time-Shift"), UndoPush::CONSOLIDATE);
}
if ( amount == 0.0 )
panel.MessageForScreenReader( _("clip not moved"));
}
void AudacityProject::OnClipRight()
void AudacityProject::OnClipLeft(const wxEvent* evt)
{
mTrackPanel->OnClipMove(true);
if (evt)
DoClipLeftOrRight( false, evt->GetEventType() == wxEVT_KEY_UP );
else { // called from menu, so simulate keydown and keyup
DoClipLeftOrRight( false, false );
DoClipLeftOrRight( false, true );
}
}
void AudacityProject::OnClipRight(const wxEvent* evt)
{
if (evt)
DoClipLeftOrRight( true, evt->GetEventType() == wxEVT_KEY_UP );
else { // called from menu, so simulate keydown and keyup
DoClipLeftOrRight( true, false );
DoClipLeftOrRight( true, true );
}
}
//this pops up a dialog which allows the left selection to be set.
@ -5374,20 +5415,87 @@ void AudacityProject::OnSplitNew()
RedrawProject();
}
void AudacityProject::OnSelectAll()
int AudacityProject::CountSelectedWaveTracks()
{
TrackListIterator iter(GetTracks());
Track *t = iter.First();
while (t) {
t->SetSelected(true);
t = iter.Next();
int count =0;
for (Track *t = iter.First(); t; t = iter.Next()) {
if( (t->GetKind() == Track::Wave) && t->GetSelected() )
count++;
}
return count;
}
int AudacityProject::CountSelectedTracks()
{
TrackListIterator iter(GetTracks());
Track *t = iter.First();
int count =0;
for (Track *t = iter.First(); t; t = iter.Next()) {
if( t->GetSelected() )
count++;
}
return count;
}
void AudacityProject::OnSelectTimeAndTracks(bool bAllTime, bool bAllTracks)
{
if( bAllTime )
mViewInfo.selectedRegion.setTimes(
mTracks->GetMinOffset(), mTracks->GetEndTime());
if( bAllTracks ){
TrackListIterator iter(GetTracks());
for (Track *t = iter.First(); t; t = iter.Next()) {
t->SetSelected(true);
}
mViewInfo.selectedRegion.setTimes(
mTracks->GetMinOffset(), mTracks->GetEndTime());
ModifyState(false);
mTrackPanel->Refresh(false);
if (mMixerBoard)
mMixerBoard->Refresh(false);}
}
void AudacityProject::OnSelectAllTime()
{
OnSelectTimeAndTracks( true, false );
}
void AudacityProject::OnSelectAllTracks()
{
OnSelectTimeAndTracks( false, true );
}
void AudacityProject::OnSelectAll()
{
OnSelectTimeAndTracks( true, true );
}
// This function selects all tracks if no tracks selected,
// and all time if no time selected.
// There is an argument for making it just count wave tracks,
// However you could then not select a label and cut it,
// without this function selecting all tracks.
void AudacityProject::OnSelectSomething()
{
bool bTime = mViewInfo.selectedRegion.isPoint();
bool bTracks = CountSelectedTracks() == 0;
if( bTime || bTracks )
OnSelectTimeAndTracks(bTime,bTracks);
}
void AudacityProject::SelectNone()
{
TrackListIterator iter(GetTracks());
Track *t = iter.First();
while (t) {
t->SetSelected(false);
t = iter.Next();
}
mTrackPanel->Refresh(false);
if (mMixerBoard)
mMixerBoard->Refresh(false);
@ -5524,6 +5632,8 @@ AudacityProject::FoundClip AudacityProject::FindNextClip(const WaveTrack* wt, do
result.waveTrack = wt;
const auto clips = wt->SortedClipArray();
t0 = AdjustForFindingStartTimes(clips, t0);
auto p = std::find_if(clips.begin(), clips.end(), [&] (const WaveClip* const& clip) {
return clip->GetStartTime() == t0; });
if (p != clips.end() && (*p)->GetEndTime() > t1) {
@ -5552,6 +5662,8 @@ AudacityProject::FoundClip AudacityProject::FindPrevClip(const WaveTrack* wt, do
result.waveTrack = wt;
const auto clips = wt->SortedClipArray();
t0 = AdjustForFindingStartTimes(clips, t0);
auto p = std::find_if(clips.begin(), clips.end(), [&] (const WaveClip* const& clip) {
return clip->GetStartTime() == t0; });
if (p != clips.end() && (*p)->GetEndTime() < t1) {
@ -5677,10 +5789,10 @@ void AudacityProject::OnSelectClip(bool next)
message += temp;
}
if (result.waveTrack->GetNumClips() > 1 || nTracksSearched == 1) {
temp.Printf(wxT("%d %s %d "), result.index + 1, _("of"), result.waveTrack->GetNumClips());
message += temp;
}
temp.Printf(wxT("%d %s %d %s "), result.index + 1, _("of"), result.waveTrack->GetNumClips(),
result.waveTrack->GetNumClips() == 1 ? _("clip") : _("clips"));
message += temp;
message += wxT(", ");
}
mTrackPanel->MessageForScreenReader(message);
@ -5719,20 +5831,6 @@ void AudacityProject::OnSelectSyncLockSel()
mMixerBoard->Refresh(false);
}
void AudacityProject::OnSelectAllTracks()
{
TrackListIterator iter(GetTracks());
for (Track *t = iter.First(); t; t = iter.Next()) {
t->SetSelected(true);
}
ModifyState(false);
mTrackPanel->Refresh(false);
if (mMixerBoard)
mMixerBoard->Refresh(false);
}
//
// View Menu
//
@ -6527,27 +6625,18 @@ AudacityProject::FoundClipBoundary AudacityProject::FindNextClipBoundary(const W
{
AudacityProject::FoundClipBoundary result{};
result.waveTrack = wt;
const auto clips = wt->SortedClipArray();
double timeStart = AdjustForFindingStartTimes(clips, time);
double timeEnd = AdjustForFindingEndTimes(clips, time);
auto pStart = std::find_if(clips.begin(), clips.end(), [&] (const WaveClip* const& clip) {
return clip->GetStartTime() > time; });
return clip->GetStartTime() > timeStart; });
auto pEnd = std::find_if(clips.begin(), clips.end(), [&] (const WaveClip* const& clip) {
return clip->GetEndTime() > time; });
return clip->GetEndTime() > timeEnd; });
if (pStart != clips.end() && pEnd != clips.end()) {
if ((*pStart)->GetStartTime() < (*pEnd)->GetEndTime()) {
result.nFound = 1;
result.time = (*pStart)->GetStartTime();
result.index1 = std::distance(clips.begin(), pStart);
result.clipStart1 = true;
}
else if ((*pStart)->GetStartTime() > (*pEnd)->GetEndTime()) {
result.nFound = 1;
result.time = (*pEnd)->GetEndTime();
result.index1 = std::distance(clips.begin(), pEnd);
result.clipStart1 = false;
}
else { // both the end of one clip and the start of the next clip
if ((*pEnd)->SharesBoundaryWithNextClip(*pStart)) {
// boundary between two clips which are immediately next to each other.
result.nFound = 2;
result.time = (*pEnd)->GetEndTime();
result.index1 = std::distance(clips.begin(), pEnd);
@ -6555,6 +6644,18 @@ AudacityProject::FoundClipBoundary AudacityProject::FindNextClipBoundary(const W
result.index2 = std::distance(clips.begin(), pStart);
result.clipStart2 = true;
}
else if ((*pStart)->GetStartTime() < (*pEnd)->GetEndTime()) {
result.nFound = 1;
result.time = (*pStart)->GetStartTime();
result.index1 = std::distance(clips.begin(), pStart);
result.clipStart1 = true;
}
else {
result.nFound = 1;
result.time = (*pEnd)->GetEndTime();
result.index1 = std::distance(clips.begin(), pEnd);
result.clipStart1 = false;
}
}
else if (pEnd != clips.end()) {
result.nFound = 1;
@ -6570,34 +6671,37 @@ AudacityProject::FoundClipBoundary AudacityProject::FindPrevClipBoundary(const W
{
AudacityProject::FoundClipBoundary result{};
result.waveTrack = wt;
const auto clips = wt->SortedClipArray();
double timeStart = AdjustForFindingStartTimes(clips, time);
double timeEnd = AdjustForFindingEndTimes(clips, time);
auto pStart = std::find_if(clips.rbegin(), clips.rend(), [&] (const WaveClip* const& clip) {
return clip->GetStartTime() < time; });
return clip->GetStartTime() < timeStart; });
auto pEnd = std::find_if(clips.rbegin(), clips.rend(), [&] (const WaveClip* const& clip) {
return clip->GetEndTime() < time; });
return clip->GetEndTime() < timeEnd; });
if (pStart != clips.rend() && pEnd != clips.rend()) {
if ((*pStart)->GetStartTime() > (*pEnd)->GetEndTime()) {
result.nFound = 1;
result.time = (*pStart)->GetStartTime();
result.index1 = static_cast<int>(clips.size()) - 1 - std::distance(clips.rbegin(), pStart);
result.clipStart1 = true;
}
else if ((*pStart)->GetStartTime() < (*pEnd)->GetEndTime()) {
result.nFound = 1;
result.time = (*pEnd)->GetEndTime();
result.index1 = static_cast<int>(clips.size()) - 1 - std::distance(clips.rbegin(), pEnd);
result.clipStart1 = false;
}
else {
result.nFound = 2; // both the start of one clip and the end of the previous clip
if ((*pEnd)->SharesBoundaryWithNextClip(*pStart)) {
// boundary between two clips which are immediately next to each other.
result.nFound = 2;
result.time = (*pStart)->GetStartTime();
result.index1 = static_cast<int>(clips.size()) - 1 - std::distance(clips.rbegin(), pStart);
result.clipStart1 = true;
result.index2 = static_cast<int>(clips.size()) - 1 - std::distance(clips.rbegin(), pEnd);
result.clipStart2 = false;
}
else if ((*pStart)->GetStartTime() > (*pEnd)->GetEndTime()) {
result.nFound = 1;
result.time = (*pStart)->GetStartTime();
result.index1 = static_cast<int>(clips.size()) - 1 - std::distance(clips.rbegin(), pStart);
result.clipStart1 = true;
}
else {
result.nFound = 1;
result.time = (*pEnd)->GetEndTime();
result.index1 = static_cast<int>(clips.size()) - 1 - std::distance(clips.rbegin(), pEnd);
result.clipStart1 = false;
}
}
else if (pStart != clips.rend()) {
result.nFound = 1;
@ -6609,6 +6713,42 @@ AudacityProject::FoundClipBoundary AudacityProject::FindPrevClipBoundary(const W
return result;
}
// When two clips are immediately next to each other, the GetEndTime() of the first clip and the
// GetStartTime() of the second clip may not be exactly equal due to rounding errors. When searching
// for the next/prev start time from a given time, the following function adjusts that given time if
// necessary to take this into account. If the given time is the end time of the first of two clips which
// are next to each other, then the given time is changed to the start time of the second clip.
// This ensures that the correct next/prev start time is found.
double AudacityProject::AdjustForFindingStartTimes(const std::vector<const WaveClip*> & clips, double time)
{
auto q = std::find_if(clips.begin(), clips.end(), [&] (const WaveClip* const& clip) {
return clip->GetEndTime() == time; });
if (q != clips.end() && q + 1 != clips.end() &&
(*q)->SharesBoundaryWithNextClip(*(q+1))) {
time = (*(q+1))->GetStartTime();
}
return time;
}
// When two clips are immediately next to each other, the GetEndTime() of the first clip and the
// GetStartTime() of the second clip may not be exactly equal due to rounding errors. When searching
// for the next/prev end time from a given time, the following function adjusts that given time if
// necessary to take this into account. If the given time is the start time of the second of two clips which
// are next to each other, then the given time is changed to the end time of the first clip.
// This ensures that the correct next/prev end time is found.
double AudacityProject::AdjustForFindingEndTimes(const std::vector<const WaveClip*>& clips, double time)
{
auto q = std::find_if(clips.begin(), clips.end(), [&] (const WaveClip* const& clip) {
return clip->GetStartTime() == time; });
if (q != clips.end() && q != clips.begin() &&
(*(q - 1))->SharesBoundaryWithNextClip(*q)) {
time = (*(q-1))->GetEndTime();
}
return time;
}
int AudacityProject::FindClipBoundaries(double time, bool next, std::vector<FoundClipBoundary>& finalResults)
{
const TrackList* tracks = GetTracks();
@ -6664,7 +6804,8 @@ void AudacityProject::OnCursorPrevClipBoundary()
void AudacityProject::OnCursorClipBoundary(bool next)
{
std::vector<FoundClipBoundary> results;
int nTracksSearched = FindClipBoundaries(mViewInfo.selectedRegion.t0(), next, results);
int nTracksSearched = FindClipBoundaries(next ? mViewInfo.selectedRegion.t1() :
mViewInfo.selectedRegion.t0(), next, results);
if (results.size() > 0) {
// note that if there is more than one result, each has the same time value.
@ -6697,10 +6838,11 @@ wxString AudacityProject::ClipBoundaryMessage(int nTracksSearched, const std::ve
message += temp;
}
message += (result.clipStart1 ? _("start") : _("end")) + wxT(" ");
if (result.waveTrack->GetNumClips() > 1 ) {
temp.Printf(wxT("%d %s %d "), result.index1 + 1, _("of"), result.waveTrack->GetNumClips());
message += temp;
}
temp.Printf(wxT("%d %s %d %s "), result.index1 + 1, _("of"), result.waveTrack->GetNumClips(),
result.waveTrack->GetNumClips() == 1 ? _("clip") : _("clips"));
message += temp;
if (result.nFound == 2) {
temp.Printf(wxT("%s %s %d "), _("and"), result.clipStart2 ? _("start") : _("end"),
result.index2 + 1);
@ -6892,7 +7034,7 @@ void AudacityProject::OnAlignNoSync(int index)
void AudacityProject::OnAlign(int index)
{
bool bMoveWith;
gPrefs->Read(wxT("/GUI/MoveSelectionWithTracks"), &bMoveWith, true);
gPrefs->Read(wxT("/GUI/MoveSelectionWithTracks"), &bMoveWith, false);
HandleAlign(index, bMoveWith);
}
/*
@ -7098,14 +7240,6 @@ void AudacityProject::OnScoreAlign()
// Make a copy of the note track in case alignment is canceled or fails
auto holder = nt->Duplicate();
auto alignedNoteTrack = static_cast<NoteTrack*>(holder.get());
// Duplicate() on note tracks serializes seq to a buffer, but we need
// the seq, so Duplicate again and discard the track with buffer. The
// test is here in case Duplicate() is changed in the future.
if (alignedNoteTrack->GetSequence() == NULL) {
holder = alignedNoteTrack->Duplicate();
alignedNoteTrack = static_cast<NoteTrack*>(holder.get());
wxASSERT(alignedNoteTrack->GetSequence());
}
// Remove offset from NoteTrack because audio is
// mixed starting at zero and incorporating clip offsets.
if (alignedNoteTrack->GetOffset() < 0) {
@ -7145,7 +7279,7 @@ void AudacityProject::OnScoreAlign()
#ifndef SKIP_ACTUAL_SCORE_ALIGNMENT
result = scorealign((void *) &mix, &mixer_process,
2 /* channels */, 44100.0 /* srate */, endTime,
alignedNoteTrack->GetSequence(), &progress, params);
&alignedNoteTrack->GetSeq(), &progress, params);
#else
result = SA_SUCCESS;
#endif
@ -7398,7 +7532,7 @@ int AudacityProject::DoAddLabel(const SelectedRegion &region, bool preserveFocus
void AudacityProject::OnMoveSelectionWithTracks()
{
bool bMoveWith;
gPrefs->Read(wxT("/GUI/MoveSelectionWithTracks"), &bMoveWith, true);
gPrefs->Read(wxT("/GUI/MoveSelectionWithTracks"), &bMoveWith, false);
gPrefs->Write(wxT("/GUI/MoveSelectionWithTracks"), !bMoveWith);
gPrefs->Flush();
@ -7878,6 +8012,7 @@ void AudacityProject::OnResample()
}
}
GetUndoManager()->StopConsolidating();
RedrawProject();
// Need to reset

View File

@ -160,8 +160,10 @@ void OnSelExtendRight(const wxEvent * evt);
void OnSelContractLeft(const wxEvent * evt);
void OnSelContractRight(const wxEvent * evt);
void OnClipLeft();
void OnClipRight();
public:
void DoClipLeftOrRight(bool right, bool keyUp );
void OnClipLeft(const wxEvent* evt);
void OnClipRight(const wxEvent* evt);
void OnCursorShortJumpLeft();
void OnCursorShortJumpRight();
@ -275,8 +277,16 @@ void OnSplitLabels();
void OnJoinLabels();
void OnDisjoinLabels();
void OnSelectTimeAndTracks(bool bAllTime, bool bAllTracks);
void OnSelectAllTime();
void OnSelectAllTracks();
void OnSelectAll();
void OnSelectSomething();
void OnSelectNone();
private:
int CountSelectedWaveTracks();
int CountSelectedTracks();
public:
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
void OnToggleSpectralSelection();
void DoNextPeakFrequency(bool up);
@ -303,7 +313,6 @@ void OnSelectNextClip();
void OnSelectClip(bool next);
void OnSelectCursorStoredCursor();
void OnSelectSyncLockSel();
void OnSelectAllTracks();
// View Menu
@ -411,6 +420,8 @@ typedef struct FoundClipBoundary {
} FoundClipBoundary;
FoundClipBoundary FindNextClipBoundary(const WaveTrack* wt, double time);
FoundClipBoundary FindPrevClipBoundary(const WaveTrack* wt, double time);
double AdjustForFindingStartTimes(const std::vector<const WaveClip*>& clips, double time);
double AdjustForFindingEndTimes(const std::vector<const WaveClip*>& clips, double time);
int FindClipBoundaries(double time, bool next, std::vector<FoundClipBoundary>& results);
void OnCursorNextClipBoundary();
void OnCursorPrevClipBoundary();

View File

@ -776,11 +776,9 @@ void MixerTrackCluster::OnButton_Solo(wxCommandEvent& WXUNUSED(event))
// Have to refresh all tracks.
mMixerBoard->UpdateMute();
mMixerBoard->UpdateSolo();
mProject->RedrawProject();
}
else
// Update only the changed track.
mProject->RefreshTPTrack(mTrack);
// Bug 509: Must repaint all, as many tracks can change with one Solo change.
mProject->RedrawProject();
}
@ -1512,7 +1510,8 @@ MixerBoardFrame::MixerBoardFrame(AudacityProject* parent)
#endif
SetIcon(ic);
}
#endif
#endif
Center();
}
MixerBoardFrame::~MixerBoardFrame()

View File

@ -95,7 +95,9 @@ public:
// PluginManager use
bool DiscoverProviders();
// Seems we don't currently use FindAllPlugins
void FindAllPlugins(PluginIDList & providers, wxArrayString & paths);
wxArrayString FindPluginsForProvider(const PluginID & provider, const wxString & path);
bool RegisterPlugin(const PluginID & provider, const wxString & path);

View File

@ -127,15 +127,35 @@ NoteTrack::~NoteTrack()
{
}
Alg_seq &NoteTrack::GetSeq() const
{
if (!mSeq) {
if (!mSerializationBuffer)
mSeq = std::make_unique<Alg_seq>();
else {
std::unique_ptr<Alg_track> alg_track
{ Alg_seq::unserialize
( mSerializationBuffer.get(), mSerializationLength ) };
wxASSERT(alg_track->get_type() == 's');
mSeq.reset( static_cast<Alg_seq*>(alg_track.release()) );
// Preserve the invariant that at most one of the representations is
// valid
mSerializationBuffer.reset();
mSerializationLength = 0;
}
}
wxASSERT(mSeq);
return *mSeq;
}
Track::Holder NoteTrack::Duplicate() const
{
auto duplicate = std::make_unique<NoteTrack>(mDirManager);
duplicate->Init(*this);
// Duplicate on NoteTrack moves data from mSeq to mSerializationBuffer
// and from mSerializationBuffer to mSeq on alternate calls. Duplicate
// to the undo stack and Duplicate back to the project should result
// in serialized blobs on the undo stack and traversable data in the
// project object.
// The duplicate begins life in serialized state. Often the duplicate is
// pushed on the Undo stack. Then we want to un-serialize it (or a further
// copy) only on demand after an Undo.
if (mSeq) {
SonifyBeginSerialize();
wxASSERT(!mSerializationBuffer);
@ -145,15 +165,19 @@ Track::Holder NoteTrack::Duplicate() const
&duplicate->mSerializationLength);
duplicate->mSerializationBuffer.reset( (char*)buffer );
SonifyEndSerialize();
} else if (mSerializationBuffer) {
SonifyBeginUnserialize();
}
else if (mSerializationBuffer) {
// Copy already serialized data.
wxASSERT(!mSeq);
std::unique_ptr<Alg_track> alg_track{ Alg_seq::unserialize(mSerializationBuffer.get(),
mSerializationLength) };
wxASSERT(alg_track->get_type() == 's');
duplicate->mSeq.reset(static_cast<Alg_seq*>(alg_track.release()));
SonifyEndUnserialize();
} else wxFAIL_MSG("neither mSeq nor mSerializationBuffer were present"); // bug if neither mSeq nor mSerializationBuffer
duplicate->mSerializationLength = this->mSerializationLength;
duplicate->mSerializationBuffer.reset
( new char[ this->mSerializationLength ] );
memcpy( duplicate->mSerializationBuffer.get(),
this->mSerializationBuffer.get(), this->mSerializationLength );
}
else {
// We are duplicating a default-constructed NoteTrack, and that's okay
}
// copy some other fields here
duplicate->SetBottomNote(mBottomNote);
duplicate->SetPitchHeight(mPitchHeight);
@ -180,7 +204,7 @@ double NoteTrack::GetStartTime() const
double NoteTrack::GetEndTime() const
{
return GetStartTime() + (mSeq ? mSeq->get_real_dur() : 0.0);
return GetStartTime() + GetSeq().get_real_dur();
}
@ -188,25 +212,13 @@ void NoteTrack::WarpAndTransposeNotes(double t0, double t1,
const TimeWarper &warper,
double semitones)
{
// Since this is a duplicate and duplicates convert mSeq to
// a text string for saving as XML, we probably have to
// duplicate again to get back an mSeq
double offset = this->GetOffset(); // track is shifted this amount
if (!mSeq) { // replace saveme with an (unserialized) duplicate
Track::Holder unt{ Duplicate() };
const auto nt = static_cast<NoteTrack*>(unt.get());
wxASSERT(!mSeq && nt->mSeq && !nt->mSerializationBuffer);
// swap mSeq and Buffer between this and nt
nt->mSerializationBuffer = std::move(mSerializationBuffer);
nt->mSerializationLength = mSerializationLength;
mSerializationLength = 0;
mSeq = std::move(nt->mSeq);
}
mSeq->convert_to_seconds(); // make sure time units are right
auto &seq = GetSeq();
seq.convert_to_seconds(); // make sure time units are right
t1 -= offset; // adjust time range to compensate for track offset
t0 -= offset;
if (t1 > mSeq->get_dur()) { // make sure t0, t1 are within sequence
t1 = mSeq->get_dur();
if (t1 > seq.get_dur()) { // make sure t0, t1 are within sequence
t1 = seq.get_dur();
if (t0 >= t1) return;
}
Alg_iterator iter(mSeq.get(), false);
@ -219,8 +231,8 @@ void NoteTrack::WarpAndTransposeNotes(double t0, double t1,
}
iter.end();
// now, use warper to warp the tempo map
mSeq->convert_to_beats(); // beats remain the same
Alg_time_map_ptr map = mSeq->get_time_map();
seq.convert_to_beats(); // beats remain the same
Alg_time_map_ptr map = seq.get_time_map();
map->insert_beat(t0, map->time_to_beat(t0));
map->insert_beat(t1, map->time_to_beat(t1));
int i, len = map->length();
@ -229,7 +241,7 @@ void NoteTrack::WarpAndTransposeNotes(double t0, double t1,
beat.time = warper.Warp(beat.time + offset) - offset;
}
// about to redisplay, so might as well convert back to time now
mSeq->convert_to_seconds();
seq.convert_to_seconds();
}
// Draws the midi channel toggle buttons within the given rect.
@ -348,11 +360,6 @@ void NoteTrack::SetSequence(std::unique_ptr<Alg_seq> &&seq)
mSeq = std::move(seq);
}
Alg_seq* NoteTrack::GetSequence()
{
return mSeq.get();
}
void NoteTrack::PrintSequence()
{
FILE *debugOutput;
@ -360,6 +367,8 @@ void NoteTrack::PrintSequence()
debugOutput = fopen("debugOutput.txt", "wt");
fprintf(debugOutput, "Importing MIDI...\n");
// This is called for debugging purposes. Do not compute mSeq on demand
// with GetSeq()
if (mSeq) {
int i = 0;
@ -416,15 +425,23 @@ Track::Holder NoteTrack::Cut(double t0, double t1)
THROW_INCONSISTENCY_EXCEPTION;
double len = t1-t0;
//auto delta = -(
//( std::min( t1, GetEndTime() ) ) - ( std::max( t0, GetStartTime() ) )
//);
auto newTrack = std::make_unique<NoteTrack>(mDirManager);
newTrack->Init(*this);
mSeq->convert_to_seconds();
newTrack->mSeq.reset(mSeq->cut(t0 - GetOffset(), len, false));
auto &seq = GetSeq();
seq.convert_to_seconds();
newTrack->mSeq.reset(seq.cut(t0 - GetOffset(), len, false));
newTrack->SetOffset(GetOffset());
// Not needed
// Alg_seq::cut seems to handle this
//AddToDuration( delta );
// What should be done with the rest of newTrack's members?
//(mBottomNote, mDirManager, mLastMidiPosition,
// mSerializationBuffer, mSerializationLength, mVisibleChannels)
@ -444,8 +461,9 @@ Track::Holder NoteTrack::Copy(double t0, double t1, bool) const
newTrack->Init(*this);
mSeq->convert_to_seconds();
newTrack->mSeq.reset(mSeq->copy(t0 - GetOffset(), len, false));
auto &seq = GetSeq();
seq.convert_to_seconds();
newTrack->mSeq.reset(seq.copy(t0 - GetOffset(), len, false));
newTrack->SetOffset(GetOffset());
// What should be done with the rest of newTrack's members?
@ -460,13 +478,23 @@ bool NoteTrack::Trim(double t0, double t1)
{
if (t1 < t0)
return false;
mSeq->convert_to_seconds();
auto &seq = GetSeq();
//auto delta = -(
//( GetEndTime() - std::min( GetEndTime(), t1 ) ) +
//( std::max(t0, GetStartTime()) - GetStartTime() )
//);
seq.convert_to_seconds();
// DELETE way beyond duration just in case something is out there:
mSeq->clear(t1 - GetOffset(), mSeq->get_dur() + 10000.0, false);
seq.clear(t1 - GetOffset(), seq.get_dur() + 10000.0, false);
// Now that stuff beyond selection is cleared, clear before selection:
mSeq->clear(0.0, t0 - GetOffset(), false);
seq.clear(0.0, t0 - GetOffset(), false);
// want starting time to be t0
SetOffset(t0);
// Not needed
// Alg_seq::clear seems to handle this
//AddToDuration( delta );
return true;
}
@ -477,8 +505,15 @@ void NoteTrack::Clear(double t0, double t1)
double len = t1-t0;
if (mSeq)
mSeq->clear(t0 - GetOffset(), len, false);
auto &seq = GetSeq();
//auto delta = -(
//( std::min( t1, GetEndTime() ) ) - ( std::max( t0, GetStartTime() ) )
//);
seq.clear(t0 - GetOffset(), len, false);
// Not needed
// Alg_seq::clear seems to handle this
// AddToDuration( delta );
}
void NoteTrack::Paste(double t, const Track *src)
@ -497,19 +532,27 @@ void NoteTrack::Paste(double t, const Track *src)
return;
NoteTrack* other = (NoteTrack*)src;
if (other->mSeq == NULL)
// THROW_INCONSISTENCY_EXCEPTION; // ?
return;
if(!mSeq)
mSeq = std::make_unique<Alg_seq>();
if (other->GetOffset() > 0) {
mSeq->convert_to_seconds();
mSeq->insert_silence(t - GetOffset(), other->GetOffset());
t += other->GetOffset();
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;
}
mSeq->paste(t - GetOffset(), other->mSeq.get());
// 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)
@ -519,20 +562,25 @@ void NoteTrack::Silence(double t0, double t1)
auto len = t1 - t0;
mSeq->convert_to_seconds();
auto &seq = GetSeq();
seq.convert_to_seconds();
// XXX: do we want to set the all param?
// If it's set, then it seems like notes are silenced if they start or end in the range,
// otherwise only if they start in the range. --Poke
mSeq->silence(t0 - GetOffset(), len, false);
seq.silence(t0 - GetOffset(), len, false);
}
void NoteTrack::InsertSilence(double t, double len)
{
if (len <= 0)
if (len < 0)
THROW_INCONSISTENCY_EXCEPTION;
mSeq->convert_to_seconds();
mSeq->insert_silence(t - GetOffset(), len);
auto &seq = GetSeq();
seq.convert_to_seconds();
seq.insert_silence(t - GetOffset(), len);
// is this needed?
// AddToDuration( len );
}
// Call this function to manipulate the underlying sequence data. This is
@ -540,51 +588,61 @@ void NoteTrack::InsertSilence(double t, double len)
bool NoteTrack::Shift(double t) // t is always seconds
{
if (t > 0) {
auto &seq = GetSeq();
// insert an even number of measures
mSeq->convert_to_beats();
seq.convert_to_beats();
// get initial tempo
double tempo = mSeq->get_tempo(0.0);
double beats_per_measure = mSeq->get_bar_len(0.0);
double tempo = seq.get_tempo(0.0);
double beats_per_measure = seq.get_bar_len(0.0);
int m = ROUND(t * tempo / beats_per_measure);
// need at least 1 measure, so if we rounded down to zero, fix it
if (m == 0) m = 1;
// compute NEW tempo so that m measures at NEW tempo take t seconds
tempo = beats_per_measure * m / t; // in beats per second
mSeq->insert_silence(0.0, beats_per_measure * m);
mSeq->set_tempo(tempo * 60.0 /* bpm */, 0.0, beats_per_measure * m);
mSeq->write("afterShift.gro");
seq.insert_silence(0.0, beats_per_measure * m);
seq.set_tempo(tempo * 60.0 /* bpm */, 0.0, beats_per_measure * m);
seq.write("afterShift.gro");
} else if (t < 0) {
mSeq->convert_to_seconds();
mSeq->clear(0, t, true);
auto &seq = GetSeq();
seq.convert_to_seconds();
seq.clear(0, t, true);
} else { // offset is zero, no modifications
return false;
}
return true;
}
double NoteTrack::NearestBeatTime(double time, double *beat) const
QuantizedTimeAndBeat NoteTrack::NearestBeatTime( double time ) const
{
wxASSERT(mSeq);
// Alg_seq knows nothing about offset, so remove offset time
double seq_time = time - GetOffset();
seq_time = mSeq->nearest_beat_time(seq_time, beat);
double beat;
auto &seq = GetSeq();
seq_time = seq.nearest_beat_time(seq_time, &beat);
// add the offset back in to get "actual" audacity track time
return seq_time + GetOffset();
return { seq_time + GetOffset(), beat };
}
bool NoteTrack::StretchRegion(double t0, double t1, double dur)
void NoteTrack::AddToDuration( double delta )
{
wxASSERT(mSeq);
// Alg_seq::stretch_region uses beats, so we translate time
// to beats first:
t0 -= GetOffset();
t1 -= GetOffset();
double b0 = mSeq->get_time_map()->time_to_beat(t0);
double b1 = mSeq->get_time_map()->time_to_beat(t1);
bool result = mSeq->stretch_region(b0, b1, dur);
auto &seq = GetSeq();
#if 0
// PRL: Would this be better ?
seq.set_real_dur( seq.get_real_dur() + delta );
#else
seq.convert_to_seconds();
seq.set_dur( seq.get_dur() + delta );
#endif
}
bool NoteTrack::StretchRegion
( QuantizedTimeAndBeat t0, QuantizedTimeAndBeat t1, double newDur )
{
auto &seq = GetSeq();
bool result = seq.stretch_region( t0.second, t1.second, newDur );
if (result) {
mSeq->convert_to_seconds();
mSeq->set_dur(mSeq->get_dur() + dur - (t1 - t0));
const auto oldDur = t1.first - t0.first;
AddToDuration( newDur - oldDur );
}
return result;
}
@ -604,24 +662,21 @@ Alg_seq *NoteTrack::MakeExportableSeq(std::unique_ptr<Alg_seq> &cleanup) const
cleanup.reset();
double offset = GetOffset();
if (offset == 0)
return mSeq.get();
return &GetSeq();
// make a copy, deleting events that are shifted before time 0
double start = -offset;
if (start < 0) start = 0;
// notes that begin before "start" are not included even if they
// extend past "start" (because "all" parameter is set to false)
cleanup.reset( mSeq->copy(start, mSeq->get_dur() - start, false) );
cleanup.reset( GetSeq().copy(start, GetSeq().get_dur() - start, false) );
auto seq = cleanup.get();
if (offset > 0) {
{
// Cheat a little
NoteTrack *pMutable = const_cast< NoteTrack * >(this);
// swap cleanup and mSeq so that Shift operates on the NEW copy
swap(pMutable->mSeq, cleanup);
auto cleanup2 = finally( [&] { swap(pMutable->mSeq, cleanup); } );
swap( this->mSeq, cleanup );
auto cleanup2 = finally( [&] { swap( this->mSeq, cleanup ); } );
pMutable->Shift(offset);
const_cast< NoteTrack *>( this )->Shift(offset);
}
#ifdef OLD_CODE
// now shift events by offset. This must be done with an integer
@ -664,27 +719,28 @@ Alg_seq *NoteTrack::MakeExportableSeq(std::unique_ptr<Alg_seq> &cleanup) const
seq->set_tempo(bps * 60.0, 0, beats_per_measure * n);
#endif
} else {
auto &mySeq = GetSeq();
// if offset is negative, it might not be a multiple of beats, but
// we want to preserve the relative positions of measures. I.e. we
// should shift barlines and time signatures as well as notes.
// Insert a time signature at the first bar-line if necessary.
// Translate start from seconds to beats and call it beat:
double beat = mSeq->get_time_map()->time_to_beat(start);
// Find the time signature in mSeq in effect at start (beat):
int i = mSeq->time_sig.find_beat(beat);
double beat = mySeq.get_time_map()->time_to_beat(start);
// Find the time signature in mySeq in effect at start (beat):
int i = mySeq.time_sig.find_beat(beat);
// i is where you would insert a NEW time sig at beat,
// Case 1: beat coincides with a time sig at i. Time signature
// at beat means that there is a barline at beat, so when beat
// is shifted to 0, the relative barline positions are preserved
if (mSeq->time_sig.length() > 0 &&
within(beat, mSeq->time_sig[i].beat, ALG_EPS)) {
if (mySeq.time_sig.length() > 0 &&
within(beat, mySeq.time_sig[i].beat, ALG_EPS)) {
// beat coincides with time signature change, so offset must
// be a multiple of beats
/* do nothing */ ;
// Case 2: there is no time signature before beat.
} else if (i == 0 && (mSeq->time_sig.length() == 0 ||
mSeq->time_sig[i].beat > beat)) {
} else if (i == 0 && (mySeq.time_sig.length() == 0 ||
mySeq.time_sig[i].beat > beat)) {
// If beat does not fall on an implied barline, we need to
// insert a time signature.
double measures = beat / 4.0;
@ -701,7 +757,7 @@ Alg_seq *NoteTrack::MakeExportableSeq(std::unique_ptr<Alg_seq> &cleanup) const
// Case 3: i-1 must be the effective time sig position
} else {
i -= 1; // index the time signature in effect at beat
Alg_time_sig_ptr tsp = &(mSeq->time_sig[i]);
Alg_time_sig_ptr tsp = &(mySeq.time_sig[i]);
double beats_per_measure = (tsp->num * 4) / tsp->den;
double measures = (beat - tsp->beat) / beats_per_measure;
int imeasures = ROUND(measures);
@ -737,12 +793,13 @@ bool NoteTrack::ExportAllegro(const wxString &f) const
double offset = GetOffset();
bool in_seconds;
gPrefs->Read(wxT("/FileFormats/AllegroStyle"), &in_seconds, true);
auto &seq = GetSeq();
if (in_seconds) {
mSeq->convert_to_seconds();
seq.convert_to_seconds();
} else {
mSeq->convert_to_beats();
seq.convert_to_beats();
}
return mSeq->write(f.mb_str(), offset);
return seq.write(f.mb_str(), offset);
}
@ -810,28 +867,15 @@ void NoteTrack::WriteXML(XMLWriter &xmlFile) const
// may throw
{
std::ostringstream data;
// Normally, Duplicate is called in pairs -- once to put NoteTrack
// on the Undo stack, and again to move from the Undo stack to an
// "active" editable state. For efficiency, we do not do a "real"
// Duplicate followed by serialization into a binary blob. Instead,
// we combine the Duplicate with serialization or unserialization.
// Serialization and Unserialization happen on alternate calls to
// Duplicate and (usually) produce the right results at the right
// time.
// It turns out that this optimized Duplicate is a little too
// clever. There is at least one case where a track can be duplicated
// and then AutoSave'd. (E.g. do an "Insert Silence" effect on a
// NoteTrack.) In this case, mSeq will be NULL. To avoid a crash
// and perform WriteXML, we may need to restore NoteTracks from binary
// blobs to regular data structures (with an Alg_seq member).
Track::Holder holder;
const NoteTrack *saveme = this;
if (!mSeq) { // replace saveme with an (unserialized) duplicate
if (!mSeq) {
// replace saveme with an (unserialized) duplicate, which is
// destroyed at end of function.
holder = Duplicate();
saveme = static_cast<NoteTrack*>(holder.get());
wxASSERT(saveme->mSeq);
}
saveme->mSeq->write(data, true);
saveme->GetSeq().write(data, true);
xmlFile.StartTag(wxT("notetrack"));
xmlFile.WriteAttr(wxT("name"), saveme->mName);
this->NoteTrackBase::WriteXMLAttributes(xmlFile);

View File

@ -11,6 +11,7 @@
#ifndef __AUDACITY_NOTETRACK__
#define __AUDACITY_NOTETRACK__
#include <utility>
#include <wx/string.h>
#include "Audacity.h"
#include "Experimental.h"
@ -58,12 +59,12 @@ using NoteTrackBase =
#endif
;
using QuantizedTimeAndBeat = std::pair< double, double >;
class AUDACITY_DLL_API NoteTrack final
: public NoteTrackBase
{
public:
friend class TrackArtist;
NoteTrack(const std::shared_ptr<DirManager> &projDirManager);
virtual ~NoteTrack();
@ -76,6 +77,8 @@ class AUDACITY_DLL_API NoteTrack final
double GetStartTime() const override;
double GetEndTime() const override;
Alg_seq &GetSeq() const;
void WarpAndTransposeNotes(double t0, double t1,
const TimeWarper &warper, double semitones);
@ -83,7 +86,6 @@ class AUDACITY_DLL_API NoteTrack final
bool LabelClick(const wxRect &rect, int x, int y, bool right);
void SetSequence(std::unique_ptr<Alg_seq> &&seq);
Alg_seq* GetSequence();
void PrintSequence();
Alg_seq *MakeExportableSeq(std::unique_ptr<Alg_seq> &cleanup) const;
@ -112,8 +114,9 @@ class AUDACITY_DLL_API NoteTrack final
void SetVelocity(float velocity) { mVelocity = velocity; }
#endif
double NearestBeatTime(double time, double *beat) const;
bool StretchRegion(double b0, double b1, double dur);
QuantizedTimeAndBeat NearestBeatTime( double time ) const;
bool StretchRegion
( QuantizedTimeAndBeat t0, QuantizedTimeAndBeat t1, double newDur );
int GetBottomNote() const { return mBottomNote; }
int GetPitchHeight() const { return mPitchHeight; }
@ -204,21 +207,17 @@ class AUDACITY_DLL_API NoteTrack final
else
mVisibleChannels = CHANNEL_BIT(c);
}
private:
std::unique_ptr<Alg_seq> mSeq; // NULL means no sequence
// when Duplicate() is called, assume that it is to put a copy
// of the track into the undo stack or to redo/copy from the
// stack to the project object. We want copies to the stack
// to be serialized (therefore compact) representations, so
// copy will set mSeq to NULL and serialize to the following
// variables. If this design is correct, the track will be
// duplicated again (in the event of redo) back to the project
// at which point we will unserialize the data back to the
// mSeq variable. (TrackArtist should check to make sure this
// flip-flop from mSeq to mSerializationBuffer happened an
// even number of times, otherwise mSeq will be NULL).
mutable std::unique_ptr<char[]> mSerializationBuffer; // NULL means no buffer
long mSerializationLength;
void AddToDuration( double delta );
// These are mutable to allow NoteTrack to switch details of representation
// in logically const methods
// At most one of the two pointers is not null at any time.
// Both are null in a newly constructed NoteTrack.
mutable std::unique_ptr<Alg_seq> mSeq;
mutable std::unique_ptr<char[]> mSerializationBuffer;
mutable long mSerializationLength;
#ifdef EXPERIMENTAL_MIDI_OUT
float mVelocity; // velocity offset

View File

@ -1437,11 +1437,13 @@ void PluginManager::FindFilesInPathList(const wxString & pattern,
wxArrayString paths;
// Add the "per-user" plug-ins directory
// Add the "per-user" plug-ins directory Windows / Mac
#if defined(__WXMAC__) || defined(__WXMSW__)
{
const wxFileName &ff = FileNames::PlugInDir();
paths.Add(ff.GetFullPath());
}
#endif
// Add the "Audacity" plug-ins directory
wxFileName ff = PlatformCompatibility::GetExecutablePath();

View File

@ -2052,8 +2052,10 @@ void AudacityProject::UpdateLayout()
if (!mTrackPanel)
return;
mToolManager->LayoutToolBars();
// Layout first to get our new width,
// Then and only then we can arrange the toolbars.
Layout();
mToolManager->LayoutToolBars();
// Retrieve size of this projects window
wxSize mainsz = GetSize();
@ -2107,7 +2109,6 @@ void AudacityProject::RefreshAllTitles(bool bShowProjectNumbers )
void AudacityProject::OnIconize(wxIconizeEvent &event)
{
int VisibleProjectCount = 0;
//JKC: On Iconizing we get called twice. Don't know
@ -2311,7 +2312,9 @@ bool AudacityProject::TryToMakeActionAllowed
if( (MissingFlags & ~( TimeSelectedFlag | WaveTracksSelectedFlag)) )
return false;
OnSelectAll();
// This was 'OnSelectAll'. Changing it to OnSelectSomething means if
// selecting all tracks is enough, we just do that.
OnSelectSomething();
flags = GetUpdateFlags();
bAllowed = ((flags & mask) == (flagsRqd & mask));
return bAllowed;
@ -2319,7 +2322,19 @@ bool AudacityProject::TryToMakeActionAllowed
void AudacityProject::OnMenu(wxCommandEvent & event)
{
#ifdef __WXMSW__
// Bug 1642: We can arrive here with bogus menu IDs, which we
// proceed to process. So if bogus, don't.
// The bogus menu IDs are probably generated by controls on the TrackPanel,
// such as the Project Rate.
// 17000 is the magic number at which we start our menu.
// This code would probably NOT be OK on Mac, since we assign
// some specific ID numbers.
if( event.GetId() < 17000){
event.Skip();
return;
}
#endif
bool handled = mCommandManager.HandleMenuID(event.GetId(),
GetUpdateFlags(),
NoFlagsSpecifed);
@ -4607,19 +4622,6 @@ void AudacityProject::Clear()
RedrawProject();
}
void AudacityProject::SelectNone()
{
TrackListIterator iter(GetTracks());
Track *t = iter.First();
while (t) {
t->SetSelected(false);
t = iter.Next();
}
mTrackPanel->Refresh(false);
if (mMixerBoard)
mMixerBoard->Refresh(false);
}
// Utility function called by other zoom methods
void AudacityProject::Zoom(double level)
{

View File

@ -100,6 +100,7 @@ class UndoManager;
enum class UndoPush : unsigned char;
class Track;
class WaveClip;
AudacityProject *CreateNewAudacityProject();
AUDACITY_DLL_API AudacityProject *GetActiveProject();

View File

@ -120,7 +120,7 @@ ScreenFramePtr mFrame;
void OpenScreenshotTools()
{
if (!mFrame) {
mFrame = ScreenFramePtr{ safenew ScreenFrame(NULL, -1) };
mFrame = ScreenFramePtr{ safenew ScreenFrame(wxGetApp().GetTopWindow(), -1) };
}
mFrame->Show();
mFrame->Raise();
@ -298,6 +298,7 @@ ScreenFrame::ScreenFrame(wxWindow * parent, wxWindowID id)
// The monitoring will switch off temporarily
// because we've switched monitor mid play.
mContext.GetProject()->GetToolManager()->Reset();
Center();
}
ScreenFrame::~ScreenFrame()

View File

@ -103,14 +103,20 @@ for registering for changes.
#include <wx/notebook.h>
#include <wx/treectrl.h>
#include <wx/spinctrl.h>
#include <wx/bmpbuttn.h>
#include "Internat.h"
#include "Experimental.h"
#include "Shuttle.h"
#include "WrappedType.h"
#include "widgets/wxPanelWrapper.h"
#include "../images/Help.xpm"
ShuttleGuiBase::ShuttleGuiBase(wxWindow * pParent, teShuttleMode ShuttleMode )
{
// Suppress warnings about the header file
wxUnusedVar(Help_xpm);
wxASSERT( (pParent != NULL ) || ( ShuttleMode != eIsCreating));
mpParent = pParent;
@ -2168,7 +2174,11 @@ std::unique_ptr<wxSizer> CreateStdButtonSizer(wxWindow *parent, long buttons, wx
if( buttons & eHelpButton )
{
bs->AddButton(safenew wxButton(parent, wxID_HELP));
// Replace standard Help button with smaller icon button.
// bs->AddButton(safenew wxButton(parent, wxID_HELP));
b = new wxBitmapButton(parent, wxID_HELP, Help_xpm);
b->SetToolTip( _("Help") );
bs->AddButton( b );
}
if (buttons & ePreviewButton)

View File

@ -490,6 +490,7 @@ void ThemeBase::RegisterImage( int &iIndex, const wxImage &Image, const wxString
mBitmapNames.Add( Name );
mBitmapFlags.Add( mFlow.mFlags );
mFlow.mFlags &= ~resFlagSkip;
iIndex = mBitmaps.GetCount()-1;
}
@ -541,7 +542,7 @@ void FlowPacker::GetNextPosition( int xSize, int ySize )
xSize += 2*mBorderWidth;
ySize += 2*mBorderWidth;
// if the height has increased, then we are on a NEW group.
if(( ySize > myHeight )||(mFlags != mOldFlags ))
if(( ySize > myHeight )||(((mFlags ^ mOldFlags )& ~resFlagSkip)!=0))
{
SetNewGroup( ((mFlags & resFlagPaired)!=0) ? 2 : 1 );
myHeight = ySize;
@ -690,9 +691,12 @@ void ThemeBase::CreateImageCache( bool bBinarySave )
{
mFlow.GetNextPosition( SrcImage.GetWidth(), SrcImage.GetHeight());
ImageCache.SetRGB( mFlow.Rect(), 0xf2, 0xb0, 0x27 );
PasteSubImage( &ImageCache, &SrcImage,
mFlow.mxPos + mFlow.mBorderWidth,
mFlow.myPos + mFlow.mBorderWidth);
if( (mFlow.mFlags & resFlagSkip) == 0 )
PasteSubImage( &ImageCache, &SrcImage,
mFlow.mxPos + mFlow.mBorderWidth,
mFlow.myPos + mFlow.mBorderWidth);
else
ImageCache.SetRGB( mFlow.RectInner(), 1,1,1);
#ifdef IMAGE_MAP
// No href in html. Uses title not alt.
wxRect R( mFlow.Rect() );
@ -753,12 +757,14 @@ void ThemeBase::CreateImageCache( bool bBinarySave )
return;
}
#endif
#if 0
// Deliberate policy to use the fast/cheap blocky pixel-multiplication
// algorithm, as this introduces no artifacts on repeated scale up/down.
ImageCache.Rescale(
ImageCache.GetWidth() * 4,
ImageCache.GetHeight() *4,
ImageCache.GetWidth()*4,
ImageCache.GetHeight()*4,
wxIMAGE_QUALITY_NEAREST );
#endif
if( !ImageCache.SaveFile( FileName, wxBITMAP_TYPE_PNG ))
{
wxMessageBox(

View File

@ -41,7 +41,8 @@ enum teResourceFlags
resFlagPaired =0x01,
resFlagCursor =0x02,
resFlagNewLine = 0x04,
resFlagInternal = 0x08 // For image manipulation. Don't save or load.
resFlagInternal = 0x08, // For image manipulation. Don't save or load.
resFlagSkip = 0x10
};
enum teThemeType

View File

@ -250,7 +250,7 @@ class AUDACITY_DLL_API Track /* not final */ : public XMLTagHandler
bool IsSyncLockSelected() const;
};
class AudioTrack /* not final */ : public Track
class AUDACITY_DLL_API AudioTrack /* not final */ : public Track
{
public:
AudioTrack(const std::shared_ptr<DirManager> &projDirManager)
@ -265,7 +265,7 @@ public:
{ return false; }
};
class PlayableTrack /* not final */ : public AudioTrack
class AUDACITY_DLL_API PlayableTrack /* not final */ : public AudioTrack
{
public:
PlayableTrack(const std::shared_ptr<DirManager> &projDirManager)

View File

@ -273,7 +273,7 @@ TrackArtist::TrackArtist()
mdBrange = ENV_DB_RANGE;
mShowClipping = false;
mSampleDisplay = 0;
mSampleDisplay = 1;// Stem plots by default.
UpdatePrefs();
SetColours();
@ -2730,7 +2730,7 @@ void TrackArtist::DrawNoteBackground(const NoteTrack *track, wxDC &dc,
int left = TIME_TO_X(track->GetOffset());
if (left < sel.x) left = sel.x; // clip on left
int right = TIME_TO_X(track->GetOffset() + track->mSeq->get_real_dur());
int right = TIME_TO_X(track->GetOffset() + track->GetSeq().get_real_dur());
if (right > sel.x + sel.width) right = sel.x + sel.width; // clip on right
// need overlap between MIDI data and the background region
@ -2772,7 +2772,7 @@ void TrackArtist::DrawNoteBackground(const NoteTrack *track, wxDC &dc,
}
// draw bar lines
Alg_seq_ptr seq = track->mSeq.get();
Alg_seq_ptr seq = &track->GetSeq();
// We assume that sliding a NoteTrack around slides the barlines
// along with the notes. This means that when we write out a track
// as Allegro or MIDI without the offset, we'll need to insert an
@ -2824,19 +2824,7 @@ void TrackArtist::DrawNoteTrack(const NoteTrack *track,
const double h = X_TO_TIME(rect.x);
const double h1 = X_TO_TIME(rect.x + rect.width);
Alg_seq_ptr seq = track->mSeq.get();
if (!seq) {
wxASSERT(track->mSerializationBuffer);
// JKC: Previously this indirected via seq->, a NULL pointer.
// This was actually OK, since unserialize is a static function.
// Alg_seq:: is clearer.
std::unique_ptr<Alg_track> alg_track{ Alg_seq::unserialize(track->mSerializationBuffer.get(),
track->mSerializationLength) };
wxASSERT(alg_track->get_type() == 's');
const_cast<NoteTrack*>(track)->mSeq.reset(seq = static_cast<Alg_seq*>(alg_track.release()));
track->mSerializationBuffer.reset();
}
wxASSERT(seq);
Alg_seq_ptr seq = &track->GetSeq();
if (!track->GetSelected())
sel0 = sel1 = 0.0;
@ -3214,7 +3202,7 @@ void TrackArtist::UpdatePrefs()
{
mdBrange = gPrefs->Read(ENV_DB_KEY, mdBrange);
mShowClipping = gPrefs->Read(wxT("/GUI/ShowClipping"), mShowClipping);
gPrefs->Read(wxT("/GUI/SampleView"), &mSampleDisplay, 0);
gPrefs->Read(wxT("/GUI/SampleView"), &mSampleDisplay, 1);
SetColours();
}

File diff suppressed because it is too large Load Diff

View File

@ -155,6 +155,25 @@ private:
const int DragThreshold = 3;// Anything over 3 pixels is a drag, else a click.
struct ClipMoveState {
WaveClip *capturedClip {};
bool capturedClipIsSelection {};
TrackArray trackExclusions {};
double hSlideAmount {};
TrackClipArray capturedClipArray {};
wxInt64 snapLeft { -1 }, snapRight { -1 };
void clear()
{
capturedClip = nullptr;
capturedClipIsSelection = false;
trackExclusions.clear();
hSlideAmount = 0;
capturedClipArray.clear();
snapLeft = snapRight = -1;
}
};
class AUDACITY_DLL_API TrackPanel final : public OverlayPanel {
public:
@ -256,7 +275,9 @@ class AUDACITY_DLL_API TrackPanel final : public OverlayPanel {
// (ignoring any fisheye)
virtual double GetScreenEndTime() const;
virtual void OnClipMove(bool right);
static double OnClipMove
(ViewInfo &viewInfo, Track *track,
TrackList &trackList, bool syncLocked, bool right);
protected:
virtual MixerBoard* GetMixerBoard();
@ -299,20 +320,33 @@ class AUDACITY_DLL_API TrackPanel final : public OverlayPanel {
// part shrinks, keeping the leftmost and rightmost boundaries
// fixed.
enum StretchEnum {
stretchNone = 0, // false value!
stretchLeft,
stretchCenter,
stretchRight
};
StretchEnum mStretchMode; // remembers what to drag
bool mStretching; // true between mouse down and mouse up
bool mStretched; // true after drag has pushed state
double mStretchStart; // time of initial mouse position, quantized
// to the nearest beat
double mStretchSel0; // initial sel0 (left) quantized to nearest beat
double mStretchSel1; // initial sel1 (left) quantized to nearest beat
double mStretchLeftBeats; // how many beats from left to cursor
double mStretchRightBeats; // how many beats from cursor to right
virtual bool HitTestStretch(Track *track, const wxRect &rect, const wxMouseEvent & event);
struct StretchState {
StretchEnum mMode { stretchCenter }; // remembers what to drag
using QuantizedTimeAndBeat = std::pair< double, double >;
bool mStretching {}; // true between mouse down and mouse up
double mOrigT0 {};
double mOrigT1 {};
QuantizedTimeAndBeat mBeatCenter { 0, 0 };
QuantizedTimeAndBeat mBeat0 { 0, 0 };
QuantizedTimeAndBeat mBeat1 { 0, 0 };
double mLeftBeats {}; // how many beats from left to cursor
double mRightBeats {}; // how many beats from cursor to right
} mStretchState;
virtual StretchEnum HitTestStretch
( const Track *track, const wxRect &rect, const wxMouseEvent & event,
StretchState *pState = nullptr );
wxCursor *ChooseStretchCursor( StretchEnum mode );
static StretchEnum ChooseStretchMode
( const wxMouseEvent &event, const wxRect &rect, const ViewInfo &viewInfo,
const NoteTrack *nt, StretchState *pState = nullptr );
virtual void Stretch(int mouseXCoordinate, int trackLeftEdge, Track *pTrack);
#endif
@ -383,10 +417,16 @@ 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);
static void DoSlideHorizontal
( ClipMoveState &state, TrackList &trackList, Track &capturedTrack );
static void CreateListOfCapturedClips
( ClipMoveState &state, const ViewInfo &viewInfo, Track &capturedTrack,
TrackList &trackList, bool syncLocked, double clickTime );
static void AddClipsToCaptured
( ClipMoveState &state, const ViewInfo &viewInfo,
Track *t, bool withinSelection );
static void AddClipsToCaptured
( ClipMoveState &state, Track *t, double t0, double t1 );
// AS: Handle zooming into tracks
virtual void HandleZoom(wxMouseEvent & event);
@ -658,10 +698,7 @@ protected:
Track *mCapturedTrack;
Envelope *mCapturedEnvelope;
WaveClip *mCapturedClip;
TrackClipArray mCapturedClipArray;
TrackArray mTrackExclusions;
bool mCapturedClipIsSelection;
ClipMoveState mClipMoveState;
WaveTrackLocation mCapturedTrackLocation;
wxRect mCapturedTrackLocationRect;
wxRect mCapturedRect;
@ -678,10 +715,6 @@ protected:
wxBaseArrayDouble mSlideSnapToPoints;
wxArrayInt mSlideSnapLinePixels;
// The amount that clips are sliding horizontally; this allows
// us to undo the slide and then slide it by another amount
double mHSlideAmount;
bool mDidSlideVertically;
bool mRedrawAfterStop;
@ -702,10 +735,28 @@ protected:
// are the horizontal index of pixels to display user feedback
// guidelines so the user knows when such snapping is taking place.
std::unique_ptr<SnapManager> mSnapManager;
wxInt64 mSnapLeft;
wxInt64 mSnapRight;
wxInt64 mSnapLeft { -1 };
wxInt64 mSnapRight { -1 };
bool mSnapPreferRightEdge;
public:
wxInt64 GetSnapLeft () const
{
if ( mMouseCapture == IsSliding )
return mClipMoveState.snapLeft ;
else
return mSnapLeft ;
}
wxInt64 GetSnapRight() const
{
if ( mMouseCapture == IsSliding )
return mClipMoveState.snapRight;
else
return mSnapRight;
}
protected:
NumericConverter mConverter;
WaveTrack * mDrawingTrack; // Keeps track of which track you are drawing on between events cf. HandleDraw()

View File

@ -61,7 +61,6 @@ UndoManager::UndoManager()
{
current = -1;
saved = -1;
consolidationCount = 0;
ResetODChangesFlag();
}
@ -257,10 +256,9 @@ void UndoManager::PushState(const TrackList * l,
{
unsigned int i;
// If consolidate is set to true, group up to 3 identical operations.
if (((flags & UndoPush::CONSOLIDATE) != UndoPush::MINIMAL) && lastAction == longDescription &&
consolidationCount < 2) {
consolidationCount++;
if ( ((flags & UndoPush::CONSOLIDATE) != UndoPush::MINIMAL) &&
lastAction == longDescription &&
mayConsolidate ) {
ModifyState(l, selectedRegion, tags);
// MB: If the "saved" state was modified by ModifyState, reset
// it so that UnsavedChanges returns true.
@ -270,7 +268,7 @@ void UndoManager::PushState(const TrackList * l,
return;
}
consolidationCount = 0;
mayConsolidate = true;
i = current + 1;
while (i < stack.size()) {
@ -319,7 +317,7 @@ const UndoState &UndoManager::SetStateTo
}
lastAction = wxT("");
consolidationCount = 0;
mayConsolidate = false;
return stack[current]->state;
}
@ -333,7 +331,7 @@ const UndoState &UndoManager::Undo(SelectedRegion *selectedRegion)
*selectedRegion = stack[current]->state.selectedRegion;
lastAction = wxT("");
consolidationCount = 0;
mayConsolidate = false;
return stack[current]->state;
}
@ -360,7 +358,7 @@ const UndoState &UndoManager::Redo(SelectedRegion *selectedRegion)
*/
lastAction = wxT("");
consolidationCount = 0;
mayConsolidate = false;
return stack[current]->state;
}

View File

@ -30,8 +30,9 @@
UndoManager can also automatically consolidate actions into
a single state change. If the "consolidate" argument to
PushState is true, then up to 3 identical events in a row
will result in one PushState and 2 ModifyStates.
PushState is true, then new changes may accumulate into the most
recent Undo state, if descriptions match and if no Undo or Redo or rollback
operation intervened since that state was pushed.
Undo() temporarily moves down one state and returns the track
hierarchy. If another PushState is called, the redo information
@ -107,6 +108,8 @@ class AUDACITY_DLL_API UndoManager {
unsigned int GetNumStates();
unsigned int GetCurrentState();
void StopConsolidating() { mayConsolidate = false; }
void GetShortDescription(unsigned int n, wxString *desc);
// Return value must first be calculated by CalculateSpaceUsage():
wxLongLong_t GetLongDescription(unsigned int n, wxString *desc, wxString *size);
@ -143,7 +146,7 @@ class AUDACITY_DLL_API UndoManager {
UndoStack stack;
wxString lastAction;
int consolidationCount;
bool mayConsolidate { false };
SpaceArray space;
unsigned long long mClipboardSpaceUsage {};

View File

@ -1948,3 +1948,17 @@ void WaveClip::Resample(int rate, ProgressDialog *progress)
mRate = rate;
}
}
// Used by commands which interact with clips using the keyboard.
// When two clips are immediately next to each other, the GetEndTime()
// of the first clip and the GetStartTime() of the second clip may not
// be exactly equal due to rounding errors.
bool WaveClip::SharesBoundaryWithNextClip(const WaveClip* next) const
{
double endThis = GetRate() * GetOffset() + GetNumSamples().as_double();
double startNext = next->GetRate() * next->GetOffset();
// given that a double has about 15 significant digits, using a criterion
// of half a sample should be safe in all normal usage.
return fabs(startNext - endThis) < 0.5;
}

View File

@ -378,6 +378,9 @@ public:
bool GetIsPlaceholder() const { return mIsPlaceholder; }
void SetIsPlaceholder(bool val) { mIsPlaceholder = val; }
// used by commands which interact with clips using the keyboard
bool SharesBoundaryWithNextClip(const WaveClip* next) const;
public:
// Cache of values to colour pixels of Spectrogram - used by TrackArtist
mutable std::unique_ptr<SpecPxCache> mSpecPxCache;

View File

@ -2212,15 +2212,27 @@ WaveClip* WaveTrack::GetClipAtSample(sampleCount sample)
return NULL;
}
// When the time is both the end of a clip and the start of the next clip, the
// latter clip is returned.
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 = std::find_if(clips.rbegin(), clips.rend(), [&] (WaveClip* const& clip) {
auto p = std::find_if(clips.rbegin(), clips.rend(), [&] (WaveClip* const& clip) {
return time >= clip->GetStartTime() && time <= clip->GetEndTime(); });
return result != clips.rend() ? *result : nullptr;
// When two clips are immediately next to each other, the GetEndTime() of the first clip
// and the GetStartTime() of the second clip may not be exactly equal due to rounding errors.
// If "time" is the end time of the first of two such clips, and the end time is slightly
// less than the start time of the second clip, then the first rather than the
// second clip is found by the above code. So correct this.
if (p != clips.rend() & p != clips.rbegin() &&
time == (*p)->GetEndTime() &&
(*p)->SharesBoundaryWithNextClip(*(p-1))) {
p--;
}
return p != clips.rend() ? *p : nullptr;
}
Envelope* WaveTrack::GetEnvelopeAtX(int xcoord)

View File

@ -448,7 +448,7 @@ void CommandManager::PurgeData()
mCommandIDHash.clear();
mCurrentMenuName = COMMAND;
mCurrentID = 0;
mCurrentID = 17000;
}
@ -1204,12 +1204,17 @@ bool CommandManager::FilterKeyEvent(AudacityProject *project, const wxKeyEvent &
{
wxWindow * pWnd = wxWindow::FindFocus();
wxWindow * pTrackPanel = (wxWindow*)GetActiveProject()->GetTrackPanel();
bool bIntercept = pWnd != pTrackPanel;
// Intercept keys from windows that are NOT panels
if( bIntercept ){
bIntercept = pWnd && ( dynamic_cast<wxPanel*>(pWnd) == NULL );
}
//wxLogDebug("Focus: %p TrackPanel: %p", pWnd, pTrackPanel );
// We allow the keystrokes below to be handled by wxWidgets controls IF we are
// in some sub window rather than in the TrackPanel itself.
// Otherwise they will go to our command handler and if it handles them
// they will NOT be available to wxWidgets.
if( pWnd != pTrackPanel ){
if( bIntercept ){
switch( evt.GetKeyCode() ){
case WXK_LEFT:
case WXK_RIGHT:

View File

@ -90,6 +90,11 @@ wxString EffectAmplify::GetDescription()
return XO("Increases or decreases the volume of the audio you have selected");
}
wxString EffectAmplify::ManualPage()
{
return wxT("Amplify");
}
// EffectIdentInterface implementation
EffectType EffectAmplify::GetType()

View File

@ -37,6 +37,7 @@ public:
wxString GetSymbol() override;
wxString GetDescription() override;
wxString ManualPage() override;
// EffectIdentInterface implementation

View File

@ -115,6 +115,11 @@ wxString EffectAutoDuck::GetDescription()
return XO("Reduces (ducks) the volume of one or more tracks whenever the volume of a specified \"control\" track reaches a particular level");
}
wxString EffectAutoDuck::ManualPage()
{
return wxT("Auto_Duck");
}
// EffectIdentInterface implementation
EffectType EffectAutoDuck::GetType()

View File

@ -38,6 +38,7 @@ public:
wxString GetSymbol() override;
wxString GetDescription() override;
wxString ManualPage() override;
// EffectIdentInterface implementation

View File

@ -92,6 +92,11 @@ wxString EffectBassTreble::GetDescription()
return XO("Simple tone control effect");
}
wxString EffectBassTreble::ManualPage()
{
return wxT("Bass_and_Treble");
}
// EffectIdentInterface implementation
EffectType EffectBassTreble::GetType()

View File

@ -51,6 +51,7 @@ public:
wxString GetSymbol() override;
wxString GetDescription() override;
wxString ManualPage() override;
// EffectIdentInterface implementation

View File

@ -126,6 +126,11 @@ wxString EffectChangePitch::GetDescription()
return XO("Change the pitch of a track without changing its tempo");
}
wxString EffectChangePitch::ManualPage()
{
return wxT("Change_Pitch");
}
// EffectIdentInterface implementation
EffectType EffectChangePitch::GetType()

View File

@ -48,6 +48,7 @@ public:
wxString GetSymbol() override;
wxString GetDescription() override;
wxString ManualPage() override;
// EffectIdentInterface implementation

View File

@ -115,6 +115,12 @@ wxString EffectChangeSpeed::GetDescription()
return XO("Change the speed of a track, also changing its pitch");
}
wxString EffectChangeSpeed::ManualPage()
{
return wxT("Change_Speed");
}
// EffectIdentInterface implementation
EffectType EffectChangeSpeed::GetType()

View File

@ -37,6 +37,7 @@ public:
wxString GetSymbol() override;
wxString GetDescription() override;
wxString ManualPage() override;
// EffectIdentInterface implementation

View File

@ -102,6 +102,11 @@ wxString EffectChangeTempo::GetDescription()
return XO("Change the tempo of a selection without changing its pitch");
}
wxString EffectChangeTempo::ManualPage()
{
return wxT("Change_Tempo");
}
// EffectIdentInterface implementation
EffectType EffectChangeTempo::GetType()

View File

@ -42,6 +42,7 @@ public:
wxString GetSymbol() override;
wxString GetDescription() override;
wxString ManualPage() override;
// EffectIdentInterface implementation

View File

@ -85,6 +85,11 @@ wxString EffectClickRemoval::GetDescription()
return XO("Click Removal is designed to remove clicks on audio tracks");
}
wxString EffectClickRemoval::ManualPage()
{
return wxT("Click_Removal");
}
// EffectIdentInterface implementation
EffectType EffectClickRemoval::GetType()

View File

@ -38,6 +38,7 @@ public:
wxString GetSymbol() override;
wxString GetDescription() override;
wxString ManualPage() override;
// EffectIdentInterface implementation

View File

@ -107,6 +107,11 @@ wxString EffectCompressor::GetDescription()
return XO("Compresses the dynamic range of audio");
}
wxString EffectCompressor::ManualPage()
{
return wxT("Compressor");
}
// EffectIdentInterface implementation
EffectType EffectCompressor::GetType()

View File

@ -40,6 +40,7 @@ public:
wxString GetSymbol() override;
wxString GetDescription() override;
wxString ManualPage() override;
// EffectIdentInterface implementation

View File

@ -177,6 +177,7 @@ ContrastDialog::ContrastDialog(wxWindow * parent, wxWindowID id,
S.SetBorder(5);
S.StartHorizontalLay(wxCENTER, false);
{
/* i18n-hint: RMS abbreviates root mean square, a certain averaging method */
S.AddTitle(_("Contrast Analyzer, for measuring RMS volume differences between two selections of audio."));
}
S.EndHorizontalLay();
@ -362,6 +363,67 @@ void ContrastDialog::OnGetBackground(wxCommandEvent & /*event*/)
results();
}
namespace {
// PRL: I gathered formatting into these functions, and eliminated some
// repetitions, and removed the redundant word "Average" as applied to RMS.
// Should these variations in formats be collapsed further?
// Pass nullptr when value is not yet defined
wxString FormatRMSMessage( float *pValue )
{
/* i18n-hint: RMS abbreviates root mean square, a certain averaging method */
wxString format0{ _("RMS = %s.") };
/* i18n-hint: dB abbreviates decibels */
wxString format1{ _("%s dB") };
wxString value;
if ( pValue )
if( fabs( *pValue ) != std::numeric_limits<float>::infinity() ) {
auto number = wxString::Format( _("%.2f"), *pValue );
value = wxString::Format( format1, number );
}
else
value = _("zero");
else
value = wxString::Format( format1, "" );
return wxString::Format( format0, value );
}
wxString FormatDifference( float diffdB )
{
if( diffdB != diffdB ) // test for NaN, reliant on IEEE implementation
return _("indeterminate");
else {
if( diffdB != std::numeric_limits<float>::infinity() )
/* i18n-hint: dB abbreviates decibels */
/* i18n-hint: RMS abbreviates root mean square, a certain averaging method */
return wxString::Format(_("%.2f dB RMS"), diffdB);
else
/* i18n-hint: dB abbreviates decibels */
return wxString::Format(_("Infinite dB difference"));
}
}
wxString FormatDifferenceForExport( float diffdB )
{
if( diffdB != diffdB ) //test for NaN, reliant on IEEE implementation
return _("Difference is indeterminate.");
else
if( fabs(diffdB) != std::numeric_limits<float>::infinity() )
/* i18n-hint: dB abbreviates decibels */
/* i18n-hint: RMS abbreviates root mean square, a certain averaging method */
return wxString::Format(_("Difference = %.2f RMS dB."), diffdB );
else
/* i18n-hint: dB abbreviates decibels */
/* i18n-hint: RMS abbreviates root mean square, a certain averaging method */
return _("Difference = infinite RMS dB.");
}
}
void ContrastDialog::results()
{
mPassFailText->SetName(wxT(""));
@ -381,25 +443,17 @@ void ContrastDialog::results()
mPassFailText->ChangeValue(_("Background higher than foreground"));
}
else if(diffdB > WCAG2_PASS) {
/* i18n-hint: WCAG abbreviates Web Content Accessibility Guidelines */
mPassFailText->ChangeValue(_("WCAG2 Pass"));
}
else {
/* i18n-hint: WCAG abbreviates Web Content Accessibility Guidelines */
mPassFailText->ChangeValue(_("WCAG2 Fail"));
}
/* i18n-hint: i.e. difference in loudness at the moment. */
mDiffText->SetName(_("Current difference"));
if( diffdB != diffdB ) { // test for NaN, reliant on IEEE implementation
mDiffText->ChangeValue(wxString::Format(_("indeterminate")));
}
else {
if( diffdB != std::numeric_limits<float>::infinity() ) {
mDiffText->ChangeValue(wxString::Format(_("%.2f dB Average RMS"), diffdB));
}
else {
mDiffText->ChangeValue(wxString::Format(_("Infinite dB difference")));
}
}
mDiffText->ChangeValue( FormatDifference( diffdB ) );
}
if (mForegroundIsDefined) {
@ -459,6 +513,7 @@ void ContrastDialog::OnExport(wxCommandEvent & WXUNUSED(event))
}
f.AddLine(wxT("==================================="));
/* i18n-hint: WCAG abbreviates Web Content Accessibility Guidelines */
f.AddLine(_("WCAG 2.0 Success Criteria 1.4.7 Contrast Results"));
f.AddLine(wxT(""));
f.AddLine(wxString::Format(_("Filename = %s."), project->GetFileName().c_str() ));
@ -474,13 +529,7 @@ void ContrastDialog::OnExport(wxCommandEvent & WXUNUSED(event))
m = (int)((t - h*3600)/60);
s = t - h*3600.0 - m*60.0;
f.AddLine(wxString::Format(_("Time ended = %2d hour(s), %2d minute(s), %.2f seconds."), h, m, s ));
if(mForegroundIsDefined)
if( fabs(foregrounddB) != std::numeric_limits<float>::infinity() )
f.AddLine(wxString::Format(_("Average RMS = %.2f dB."), foregrounddB ));
else
f.AddLine(wxString::Format(_("Average RMS = zero.") ));
else
f.AddLine(wxString::Format(_("Average RMS = dB.")));
f.AddLine( FormatRMSMessage( mForegroundIsDefined ? &foregrounddB : nullptr ) );
f.AddLine(wxT(""));
f.AddLine(_("Background"));
t = (float)mBackgroundStartT->GetValue();
@ -493,23 +542,12 @@ void ContrastDialog::OnExport(wxCommandEvent & WXUNUSED(event))
m = (int)((t - h*3600)/60);
s = t - h*3600.0 - m*60.0;
f.AddLine(wxString::Format(_("Time ended = %2d hour(s), %2d minute(s), %.2f seconds."), h, m, s ));
if(mBackgroundIsDefined)
if( fabs(backgrounddB) != std::numeric_limits<float>::infinity() )
f.AddLine(wxString::Format(_("Average RMS = %.2f dB."), backgrounddB ));
else
f.AddLine(wxString::Format(_("Average RMS = zero.") ));
else
f.AddLine(wxString::Format(_("Average RMS = dB.")));
f.AddLine( FormatRMSMessage( mBackgroundIsDefined ? &backgrounddB : nullptr ) );
f.AddLine(wxT(""));
f.AddLine(_("Results"));
float diffdB = foregrounddB - backgrounddB;
if( diffdB != diffdB ) //test for NaN, reliant on IEEE implementation
f.AddLine(wxString::Format(_("Difference is indeterminate.") ));
else
if( fabs(diffdB) != std::numeric_limits<float>::infinity() )
f.AddLine(wxString::Format(_("Difference = %.2f Average RMS dB."), diffdB ));
else
f.AddLine(wxString::Format(_("Difference = infinite Average RMS dB.")));
f.AddLine( FormatDifferenceForExport( diffdB ) );
if( diffdB > 20. )
f.AddLine(_("Success Criteria 1.4.7 of WCAG 2.0: Pass"));
else

View File

@ -192,6 +192,11 @@ wxString EffectDistortion::GetDescription()
return XO("Waveshaping distortion effect");
}
wxString EffectDistortion::ManualPage()
{
return wxT("Distortion");
}
// EffectIdentInterface implementation
EffectType EffectDistortion::GetType()

View File

@ -68,6 +68,7 @@ public:
wxString GetSymbol() override;
wxString GetDescription() override;
wxString ManualPage() override;
// EffectIdentInterface implementation

View File

@ -101,6 +101,11 @@ wxString EffectDtmf::GetDescription()
return XO("Generates dual-tone multi-frequency (DTMF) tones like those produced by the keypad on telephones");
}
wxString EffectDtmf::ManualPage()
{
return wxT("Generate_Menu#dtmf");
}
// EffectIdentInterface implementation
EffectType EffectDtmf::GetType()

View File

@ -37,6 +37,7 @@ public:
wxString GetSymbol() override;
wxString GetDescription() override;
wxString ManualPage() override;
// EffectIdentInterface implementation

View File

@ -60,6 +60,11 @@ wxString EffectEcho::GetDescription()
return XO("Repeats the selected audio again and again");
}
wxString EffectEcho::ManualPage()
{
return wxT("Echo");
}
// EffectIdentInterface implementation
EffectType EffectEcho::GetType()

View File

@ -33,6 +33,7 @@ public:
wxString GetSymbol() override;
wxString GetDescription() override;
wxString ManualPage() override;
// EffectIdentInterface implementation

View File

@ -53,6 +53,10 @@ greater use in future.
#include "../ondemand/ODManager.h"
#include "TimeWarper.h"
#include "nyquist/Nyquist.h"
#include "../widgets/HelpSystem.h"
#include "../widgets/LinkingHtmlWindow.h"
#include "../widgets/ErrorDialog.h"
#include "../FileNames.h"
#if defined(__WXMAC__)
#include <Cocoa/Cocoa.h>
@ -1098,6 +1102,16 @@ wxString Effect::GetPreset(wxWindow * parent, const wxString & parms)
return wxEmptyString;
}
wxString Effect::ManualPage()
{
return wxEmptyString;
}
wxString Effect::HelpPage()
{
return wxEmptyString;
}
bool Effect::IsBatchProcessing()
{
return mIsBatch;
@ -2604,8 +2618,9 @@ void Effect::Preview(bool dryOnly)
}
}
else {
wxMessageBox(_("Error opening sound device. Try changing the audio host, playback device and the project sample rate."),
_("Error"), wxOK | wxICON_EXCLAMATION, FocusDialog);
ShowErrorDialog(FocusDialog, _("Error"),
_("Error opening sound device.\nTry changing the audio host, playback device and the project sample rate."),
wxT("http://manual.audacityteam.org/man/faq_errors.html#sound_device"), false);
}
}
}
@ -2767,6 +2782,7 @@ BEGIN_EVENT_TABLE(EffectUIHost, wxDialogWrapper)
EVT_CLOSE(EffectUIHost::OnClose)
EVT_BUTTON(wxID_APPLY, EffectUIHost::OnApply)
EVT_BUTTON(wxID_CANCEL, EffectUIHost::OnCancel)
EVT_BUTTON(wxID_HELP, EffectUIHost::OnHelp)
EVT_BUTTON(eDebugID, EffectUIHost::OnDebug)
EVT_BUTTON(kMenuID, EffectUIHost::OnMenu)
EVT_CHECKBOX(kEnableID, EffectUIHost::OnEnable)
@ -3039,9 +3055,24 @@ bool EffectUIHost::Initialize()
bar->SetSizerAndFit(bs.release());
}
long buttons = eApplyButton + eCloseButton;
if (mEffect->mUIDebug)
{
long buttons;
if ( mEffect->ManualPage().IsEmpty() && mEffect->HelpPage().IsEmpty()) {
buttons = eApplyButton + eCloseButton;
this->SetAcceleratorTable(wxNullAcceleratorTable);
}
else {
buttons = eApplyButton + eCloseButton + eHelpButton;
wxAcceleratorEntry entries[1];
#if defined(__WXMAC__)
// Is there a standard shortcut on Mac?
#else
entries[0].Set(wxACCEL_NORMAL, (int) WXK_F1, wxID_HELP);
#endif
wxAcceleratorTable accel(1, entries);
this->SetAcceleratorTable(accel);
}
if (mEffect->mUIDebug) {
buttons += eDebugButton;
}
@ -3203,6 +3234,19 @@ void EffectUIHost::OnCancel(wxCommandEvent & evt)
return;
}
void EffectUIHost::OnHelp(wxCommandEvent & WXUNUSED(event))
{
if (mEffect->GetFamily().IsSameAs(NYQUISTEFFECTS_FAMILY) && (mEffect->ManualPage().IsEmpty())) {
// Old ShowHelpDialog required when there is no on-line manual.
// Always use default web browser to allow full-featured HTML pages.
HelpSystem::ShowHelpDialog(FindWindow(wxID_HELP), mEffect->HelpPage(), wxEmptyString, true, true);
}
else {
// otherwise use the new ShowHelpDialog
HelpSystem::ShowHelpDialog(FindWindow(wxID_HELP), mEffect->ManualPage(), true);
}
}
void EffectUIHost::OnDebug(wxCommandEvent & evt)
{
OnApply(evt);

View File

@ -226,6 +226,11 @@ class AUDACITY_DLL_API Effect /* not final */ : public wxEvtHandler,
virtual bool HasFactoryDefaults();
virtual wxString GetPreset(wxWindow * parent, const wxString & parms);
// Name of page in the Audacity alpha manual
virtual wxString ManualPage();
// Fully qualified local help file name
virtual wxString HelpPage();
virtual bool IsBatchProcessing();
virtual void SetBatchProcessing(bool start);
@ -593,6 +598,7 @@ private:
void OnApply(wxCommandEvent & evt);
void DoCancel();
void OnCancel(wxCommandEvent & evt);
void OnHelp(wxCommandEvent & evt);
void OnDebug(wxCommandEvent & evt);
void OnMenu(wxCommandEvent & evt);
void OnEnable(wxCommandEvent & evt);

View File

@ -298,6 +298,11 @@ wxString EffectEqualization::GetDescription()
return XO("Adjusts the volume levels of particular frequencies");
}
wxString EffectEqualization::ManualPage()
{
return wxT("Equalization");
}
// EffectIdentInterface implementation
EffectType EffectEqualization::GetType()

View File

@ -95,6 +95,7 @@ public:
wxString GetSymbol() override;
wxString GetDescription() override;
wxString ManualPage() override;
// EffectIdentInterface implementation

View File

@ -61,6 +61,11 @@ wxString EffectFindClipping::GetDescription()
return XO("Creates labels where clipping is detected");
}
wxString EffectFindClipping::ManualPage()
{
return wxT("Find_Clipping");
}
// EffectIdentInterface implementation
EffectType EffectFindClipping::GetType()

View File

@ -32,6 +32,7 @@ public:
wxString GetSymbol() override;
wxString GetDescription() override;
wxString ManualPage() override;
// EffectIdentInterface implementation

View File

@ -78,6 +78,11 @@ wxString EffectNoise::GetDescription()
return XO("Generates one of three different types of noise");
}
wxString EffectNoise::ManualPage()
{
return wxT("Generate_Menu#noise");
}
// EffectIdentInterface implementation
EffectType EffectNoise::GetType()

View File

@ -33,6 +33,7 @@ public:
wxString GetSymbol() override;
wxString GetDescription() override;
wxString ManualPage() override;
// EffectIdentInterface implementation

View File

@ -68,6 +68,11 @@ wxString EffectNormalize::GetDescription()
return XO("Sets the peak amplitude of one or more tracks");
}
wxString EffectNormalize::ManualPage()
{
return wxT("Normalize");
}
// EffectIdentInterface implementation
EffectType EffectNormalize::GetType()

View File

@ -34,6 +34,7 @@ public:
wxString GetSymbol() override;
wxString GetDescription() override;
wxString ManualPage() override;
// EffectIdentInterface implementation

View File

@ -107,6 +107,11 @@ wxString EffectPaulstretch::GetDescription()
return XO("Use Paulstretch only for an extreme time-stretch or \"stasis\" effect");
}
wxString EffectPaulstretch::ManualPage()
{
return wxT("Paulstretch");
}
// EffectIdentInterface implementation
EffectType EffectPaulstretch::GetType()

View File

@ -28,6 +28,7 @@ public:
wxString GetSymbol() override;
wxString GetDescription() override;
wxString ManualPage() override;
// EffectIdentInterface implementation

View File

@ -112,6 +112,11 @@ wxString EffectPhaser::GetDescription()
return XO("Combines phase-shifted signals with the original signal");
}
wxString EffectPhaser::ManualPage()
{
return wxT("Phaser");
}
// EffectIdentInterface implementation
EffectType EffectPhaser::GetType()

View File

@ -56,6 +56,7 @@ public:
wxString GetSymbol() override;
wxString GetDescription() override;
wxString ManualPage() override;
// EffectIdentInterface implementation

View File

@ -67,6 +67,11 @@ wxString EffectRepeat::GetDescription()
return XO("Repeats the selection the specified number of times");
}
wxString EffectRepeat::ManualPage()
{
return wxT("Repeat");
}
// EffectIdentInterface implementation
EffectType EffectRepeat::GetType()

View File

@ -32,6 +32,7 @@ public:
wxString GetSymbol() override;
wxString GetDescription() override;
wxString ManualPage() override;
// EffectIdentInterface implementation

View File

@ -153,6 +153,11 @@ wxString EffectReverb::GetDescription()
return XO("Adds ambience or a \"hall effect\"");
}
wxString EffectReverb::ManualPage()
{
return wxT("Reverb");
}
// EffectIdentInterface implementation
EffectType EffectReverb::GetType()

Some files were not shown because too many files have changed in this diff Show More