1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-22 15:20:15 +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 ;type analyze
;categories "http://lv2plug.in/ns/lv2core#AnalyserPlugin" ;categories "http://lv2plug.in/ns/lv2core#AnalyserPlugin"
;name "Silence Finder..." ;name "Silence Finder..."
;manpage "Silence_Finder"
;action "Finding silence..." ;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." ;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" ;author "Alex S. Brown"

View File

@ -3,6 +3,7 @@
;type analyze ;type analyze
;categories "http://lv2plug.in/ns/lv2core#AnalyserPlugin" ;categories "http://lv2plug.in/ns/lv2core#AnalyserPlugin"
;name "Sound Finder..." ;name "Sound Finder..."
;manpage "Sound_Finder"
;action "Finding sound..." ;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." ;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" ;author "Jeremy R. Brown"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,6 +2,8 @@
;version 4 ;version 4
;type process ;type process
;name "Crossfade Tracks..." ;name "Crossfade Tracks..."
;manpage "Crossfade_Tracks"
;debug disabled
;action "Crossfading..." ;action "Crossfading..."
;preview selection ;preview selection
;author "Steve Daulton" ;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))))))) (setf out-dist (min out-dist (abs (- end (second (nth i clips)))))))
(if (< in-dist out-dist) 'in 'out))) (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 ;preview linear
;categories "http://lv2plug.in/ns/lv2core#DelayPlugin" ;categories "http://lv2plug.in/ns/lv2core#DelayPlugin"
;name "Delay..." ;name "Delay..."
;manpage "Delay"
;action "Applying Delay Effect..." ;action "Applying Delay Effect..."
;author "Steve Daulton" ;author "Steve Daulton"
;copyright "Released under terms of the GNU General Public License version 2" ;copyright "Released under terms of the GNU General Public License version 2"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,6 +4,7 @@
;preview enabled ;preview enabled
;categories "http://lv2plug.in/ns/lv2core#SpectralPlugin" ;categories "http://lv2plug.in/ns/lv2core#SpectralPlugin"
;name "Vocoder..." ;name "Vocoder..."
;manpage "Vocoder"
;action "Processing Vocoder..." ;action "Processing Vocoder..."
;author "Edgar-RFT" ;author "Edgar-RFT"
;copyright "Released under terms of the GNU General Public License version 2" ;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( bmpMic, wxImage( 25, 25 ), wxT("Mic"));
DEFINE_IMAGE( bmpSpeaker, wxImage( 25, 25 ), wxT("Speaker")); 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 ); SET_THEME_FLAGS( resFlagPaired );
DEFINE_IMAGE( bmpZoomFit, wxImage( 27, 27 ), wxT("ZoomFit")); DEFINE_IMAGE( bmpZoomFit, wxImage( 27, 27 ), wxT("ZoomFit"));
DEFINE_IMAGE( bmpZoomFitDisabled, wxImage( 27, 27 ), wxT("ZoomFitDisabled")); 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( bmpTnSelectSoundDisabled, wxImage( 24, 24 ), wxT("TnSelectSoundDisabled"));
DEFINE_IMAGE( bmpTnSelectSilence, wxImage( 24, 24 ), wxT("TnSelectSilence")); DEFINE_IMAGE( bmpTnSelectSilence, wxImage( 24, 24 ), wxT("TnSelectSilence"));
DEFINE_IMAGE( bmpTnSelectSilenceDisabled, wxImage( 24, 24 ), wxT("TnSelectSilenceDisabled")); 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 ); SET_THEME_FLAGS( resFlagNone );
DEFINE_IMAGE( bmpLabelGlyph0, wxImage( 15, 23 ), wxT("LabelGlyph0")); 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")); DEFINE_IMAGE( bmpPostfishLoop, wxImage( 29, 17 ), wxT("PostfishLoop"));
SET_THEME_FLAGS( resFlagNone ); SET_THEME_FLAGS( resFlagNone );
SET_THEME_FLAGS( resFlagSkip );
DEFINE_IMAGE( bmpDockDown, wxImage( 15, 55 ), wxT("DockDown")); DEFINE_IMAGE( bmpDockDown, wxImage( 15, 55 ), wxT("DockDown"));
SET_THEME_FLAGS( resFlagSkip );
DEFINE_IMAGE( bmpDockDownShort, wxImage( 15, 27 ), wxT("DockDownShort")); DEFINE_IMAGE( bmpDockDownShort, wxImage( 15, 27 ), wxT("DockDownShort"));
SET_THEME_FLAGS( resFlagSkip );
DEFINE_IMAGE( bmpDockOver, wxImage( 15, 55 ), wxT("DockOver")); DEFINE_IMAGE( bmpDockOver, wxImage( 15, 55 ), wxT("DockOver"));
SET_THEME_FLAGS( resFlagSkip );
DEFINE_IMAGE( bmpDockOverShort, wxImage( 15, 27 ), wxT("DockOverShort")); DEFINE_IMAGE( bmpDockOverShort, wxImage( 15, 27 ), wxT("DockOverShort"));
SET_THEME_FLAGS( resFlagSkip );
DEFINE_IMAGE( bmpDockUp, wxImage( 15, 55 ), wxT("DockUp")); DEFINE_IMAGE( bmpDockUp, wxImage( 15, 55 ), wxT("DockUp"));
SET_THEME_FLAGS( resFlagSkip );
DEFINE_IMAGE( bmpDockUpShort, wxImage( 15, 27 ), wxT("DockUpShort")); DEFINE_IMAGE( bmpDockUpShort, wxImage( 15, 27 ), wxT("DockUpShort"));
SET_THEME_FLAGS( resFlagSkip );
DEFINE_IMAGE( bmpPinnedPlayRecordHead, wxImage( 27, 27 ), wxT("PinnedPlayRecordHead")); DEFINE_IMAGE( bmpPinnedPlayRecordHead, wxImage( 27, 27 ), wxT("PinnedPlayRecordHead"));
SET_THEME_FLAGS( resFlagSkip );
DEFINE_IMAGE( bmpUnpinnedPlayRecordHead, wxImage( 27, 27 ), wxT("UnpinnedPlayRecordHead")); DEFINE_IMAGE( bmpUnpinnedPlayRecordHead, wxImage( 27, 27 ), wxT("UnpinnedPlayRecordHead"));
DEFINE_IMAGE( bmpSyncLockSelTile, wxImage(20, 22), wxT("SyncLockSelTile")); 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( bmpSyncLockTracksUp, wxImage( 20, 20 ), wxT("SyncLockTracksUp"));
DEFINE_IMAGE( bmpSyncLockTracksDisabled, wxImage( 20, 20 ), wxT("SyncLockTracksDisabled")); DEFINE_IMAGE( bmpSyncLockTracksDisabled, wxImage( 20, 20 ), wxT("SyncLockTracksDisabled"));
DEFINE_IMAGE( bmpToggleScrubRuler, wxImage( 20, 20 ), wxT("ToggleScrubRuler")); DEFINE_IMAGE( bmpToggleScrubRuler, wxImage( 20, 20 ), wxT("ToggleScrubRuler"));
// DEFINE_IMAGE( bmpSliderThumb, wxImage( 11, 14 ), wxT("SliderThumb"));
DEFINE_IMAGE( bmpSyncLockIcon, wxImage(12, 12), wxT("SyncLockIcon")); DEFINE_IMAGE( bmpSyncLockIcon, wxImage(12, 12), wxT("SyncLockIcon"));
SET_THEME_FLAGS( resFlagNewLine ); SET_THEME_FLAGS( resFlagNewLine );
@ -189,7 +193,9 @@ from there. Audacity will look for a file called "Pause.png".
SET_THEME_FLAGS( resFlagNone ); SET_THEME_FLAGS( resFlagNone );
DEFINE_IMAGE( bmpSliderThumb, wxImage( 11, 20 ), wxT("SliderThumb")); DEFINE_IMAGE( bmpSliderThumb, wxImage( 11, 20 ), wxT("SliderThumb"));
SET_THEME_FLAGS( resFlagSkip );
DEFINE_IMAGE( bmpSlider, wxImage( 80, 20 ), wxT("Slider")); DEFINE_IMAGE( bmpSlider, wxImage( 80, 20 ), wxT("Slider"));
SET_THEME_FLAGS( resFlagSkip );
DEFINE_IMAGE( bmpHiliteSlider, wxImage( 80, 20 ), wxT("HiliteSlider")); DEFINE_IMAGE( bmpHiliteSlider, wxImage( 80, 20 ), wxT("HiliteSlider"));
DEFINE_IMAGE( bmpUpButtonExpandSel, wxImage( 96, 18 ), wxT("UpButtonExpandSel")); DEFINE_IMAGE( bmpUpButtonExpandSel, wxImage( 96, 18 ), wxT("UpButtonExpandSel"));
DEFINE_IMAGE( bmpDownButtonExpandSel, wxImage( 96, 18 ), wxT("DownButtonExpandSel")); 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( bmpUpButtonSmall, wxImage( 27, 27 ), wxT("UpButtonSmall"));
DEFINE_IMAGE( bmpDownButtonSmall, wxImage( 27, 27 ), wxT("DownButtonSmall")); DEFINE_IMAGE( bmpDownButtonSmall, wxImage( 27, 27 ), wxT("DownButtonSmall"));
DEFINE_IMAGE( bmpHiliteButtonSmall, wxImage( 27, 27 ), wxT("HiliteButtonSmall")); 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 ); SET_THEME_FLAGS( resFlagNone );
DEFINE_IMAGE( bmpMacUpButton, wxImage( 36, 36 ), wxT("MacUpButton")); 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( bmpMacUpButtonSmall, wxImage( 27, 27 ), wxT("MacUpButtonSmall"));
DEFINE_IMAGE( bmpMacDownButtonSmall, wxImage( 27, 27 ), wxT("MacDownButtonSmall")); DEFINE_IMAGE( bmpMacDownButtonSmall, wxImage( 27, 27 ), wxT("MacDownButtonSmall"));
DEFINE_IMAGE( bmpMacHiliteButtonSmall, wxImage( 27, 27 ), wxT("MacHiliteButtonSmall")); 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 ); SET_THEME_FLAGS( resFlagInternal );
DEFINE_IMAGE( bmpRecoloredUpLarge, wxImage( 48, 48 ), wxT("RecoloredUpLarge")); 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( bmpTopFrequencyCursor, wxImage( 32, 32 ), wxT("TopFrequencyCursor"));
DEFINE_IMAGE( bmpBandWidthCursor, wxImage(32, 32), wxT("BandWidthCursor")); DEFINE_IMAGE( bmpBandWidthCursor, wxImage(32, 32), wxT("BandWidthCursor"));
//SET_THEME_FLAGS( resFlagNewLine );
/*
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 );
// DA: The logo with name xpm has a different width. // DA: The logo with name xpm has a different width.
#ifdef EXPERIMENTAL_DA #ifdef EXPERIMENTAL_DA
@ -264,9 +254,11 @@ from there. Audacity will look for a file called "Pause.png".
#define LOGOWITHNAME_HEIGHT 200 #define LOGOWITHNAME_HEIGHT 200
SET_THEME_FLAGS( resFlagSkip | resFlagNewLine );
DEFINE_IMAGE( bmpAudacityLogo, wxImage( 215, 190 ), wxT("AudacityLogo")); DEFINE_IMAGE( bmpAudacityLogo, wxImage( 215, 190 ), wxT("AudacityLogo"));
DEFINE_IMAGE( bmpAudacityLogo48x48, wxImage( 48, 48 ), wxT("AudacityLogo48x48")); DEFINE_IMAGE( bmpAudacityLogo48x48, wxImage( 48, 48 ), wxT("AudacityLogo48x48"));
DEFINE_COLOUR( clrBlank, wxColour( 64, 64, 64), wxT("Blank")); DEFINE_COLOUR( clrBlank, wxColour( 64, 64, 64), wxT("Blank"));
DEFINE_COLOUR( clrUnselected, wxColour( 30, 30, 30), wxT("Unselected")); DEFINE_COLOUR( clrUnselected, wxColour( 30, 30, 30), wxT("Unselected"));
DEFINE_COLOUR( clrSelected, wxColour( 93, 65, 93), wxT("Selected")); DEFINE_COLOUR( clrSelected, wxColour( 93, 65, 93), wxT("Selected"));

View File

@ -1508,15 +1508,26 @@ bool AudacityApp::OnInit()
AudacityProject *project; 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( wxSplashScreen temporarywindow(
logo, logo,
wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_NO_TIMEOUT, wxSPLASH_NO_CENTRE | wxSPLASH_NO_TIMEOUT,
0, 0,
NULL, NULL,
wxID_ANY, wxID_ANY,
wxDefaultPosition, wxDefaultPosition,
wxDefaultSize, wxDefaultSize,
wxSTAY_ON_TOP); 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...")); temporarywindow.SetTitle(_("Audacity is starting up..."));
SetTopWindow(&temporarywindow); SetTopWindow(&temporarywindow);

View File

@ -2130,7 +2130,7 @@ void AudioIO::PrepareMidiIterator(bool send, double offset)
// Iterator not yet intialized, must add each track... // Iterator not yet intialized, must add each track...
for (i = 0; i < nTracks; i++) { for (i = 0; i < nTracks; i++) {
NoteTrack *t = mMidiPlaybackTracks[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 // mark sequence tracks as "in use" since we're handing this
// off to another thread and want to make sure nothing happens // off to another thread and want to make sure nothing happens
// to the data until playback finishes. This is just a sanity check. // to the data until playback finishes. This is just a sanity check.
@ -2387,7 +2387,7 @@ void AudioIO::StopStream()
int nTracks = mMidiPlaybackTracks.size(); int nTracks = mMidiPlaybackTracks.size();
for (int i = 0; i < nTracks; i++) { for (int i = 0; i < nTracks; i++) {
NoteTrack *t = mMidiPlaybackTracks[i]; NoteTrack *t = mMidiPlaybackTracks[i];
Alg_seq_ptr seq = t->GetSequence(); Alg_seq_ptr seq = &t->GetSeq();
seq->set_in_use(false); 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 ); 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. // Preserve the right-side limit.
if ( 1 + range.first < range.second ) if ( 1 + range.first < range.second )
// There was a control point already. // There was a control point already.
; ;
else else
InsertOrReplaceRelative( t0 + tlen, val ); InsertOrReplaceRelative( t0 + tlen, val );
mTrackLen += tlen;
} }
int Envelope::Reassign(double when, double value) 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 * 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 * if they don't have system admin rights. Under default settings, it's
* <DataDir>/Plug-Ins/ */ * <DataDir>/Plug-Ins/ */
static wxString PlugInDir(); static wxString PlugInDir(); // Windows and Mac only
static wxString ThemeDir(); static wxString ThemeDir();
static wxString ThemeComponentsDir(); static wxString ThemeComponentsDir();
static wxString ThemeCachePng(); static wxString ThemeCachePng();

View File

@ -493,6 +493,9 @@ FreqWindow::FreqWindow(wxWindow * parent, wxWindowID id,
Layout(); Layout();
Fit(); Fit();
// Bug 1607:
Center();
SetMinSize(GetSize()); SetMinSize(GetSize());
mAlgChoice->SetFocus(); 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 <stdio.h>
#include <algorithm> #include <algorithm>
#include <limits.h>
#include <wx/bitmap.h> #include <wx/bitmap.h>
#include <wx/brush.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. // Initially none of the rows have been used.
// So set a value that is less than any valid value. // 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) for (auto &x : xUsed)
x = xStart; x = xStart;
} }

View File

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

View File

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

View File

@ -999,7 +999,7 @@ void AudacityProject::CreateMenusAndCommands()
c->AddSeparator(); c->AddSeparator();
c->AddCheck(wxT("MoveSelectionWithTracks"), _("&Move Selection with Tracks (on/off)"), c->AddCheck(wxT("MoveSelectionWithTracks"), _("&Move Selection with Tracks (on/off)"),
FN(OnMoveSelectionWithTracks), FN(OnMoveSelectionWithTracks),
gPrefs->Read(wxT("/GUI/MoveSelectionWithTracks"), 1L), gPrefs->Read(wxT("/GUI/MoveSelectionWithTracks"), 0L),
AlwaysEnabledFlag, AlwaysEnabledFlag); AlwaysEnabledFlag, AlwaysEnabledFlag);
c->EndSubMenu(); c->EndSubMenu();
@ -1240,7 +1240,7 @@ void AudacityProject::CreateMenusAndCommands()
// Ext-Bar Menu // Ext-Bar Menu
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
c->BeginMenu("Ext-Bar"); c->BeginMenu("Ext-&Bar");
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -1399,7 +1399,7 @@ void AudacityProject::CreateMenusAndCommands()
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
c->SetDefaultFlags(AlwaysEnabledFlag, AlwaysEnabledFlag); 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("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")); 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("CursorLongJumpLeft"), _("Cursor Long Jump Left"), FN(OnCursorLongJumpLeft), wxT("Shift+,"));
c->AddItem(wxT("CursorLongJumpRight"), _("Cursor Long Jump Right"), FN(OnCursorLongJumpRight), 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("ClipLeft"), _("Clip Left"), FN(OnClipLeft), wxT("\twantKeyup"));
c->AddItem(wxT("ClipRight"), _("Clip Right"), FN(OnClipRight), wxT("")); c->AddItem(wxT("ClipRight"), _("Clip Right"), FN(OnClipRight), wxT("\twantKeyup"));
c->EndSubMenu(); c->EndSubMenu();
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -2082,7 +2082,7 @@ void AudacityProject::SelectAllIfNone()
auto flags = GetUpdateFlags(); auto flags = GetUpdateFlags();
if(!(flags & TracksSelectedFlag) || if(!(flags & TracksSelectedFlag) ||
(mViewInfo.selectedRegion.isPoint())) (mViewInfo.selectedRegion.isPoint()))
OnSelectAll(); OnSelectSomething();
} }
void AudacityProject::StopIfPaused() void AudacityProject::StopIfPaused()
@ -3081,14 +3081,55 @@ void AudacityProject::OnSelContractRight(const wxEvent * evt)
OnCursorLeft( true, true, bKeyUp ); 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. //this pops up a dialog which allows the left selection to be set.
@ -5374,20 +5415,87 @@ void AudacityProject::OnSplitNew()
RedrawProject(); RedrawProject();
} }
void AudacityProject::OnSelectAll() int AudacityProject::CountSelectedWaveTracks()
{ {
TrackListIterator iter(GetTracks()); TrackListIterator iter(GetTracks());
Track *t = iter.First(); Track *t = iter.First();
while (t) {
t->SetSelected(true); int count =0;
t = iter.Next(); 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); 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); mTrackPanel->Refresh(false);
if (mMixerBoard) if (mMixerBoard)
mMixerBoard->Refresh(false); mMixerBoard->Refresh(false);
@ -5524,6 +5632,8 @@ AudacityProject::FoundClip AudacityProject::FindNextClip(const WaveTrack* wt, do
result.waveTrack = wt; result.waveTrack = wt;
const auto clips = wt->SortedClipArray(); const auto clips = wt->SortedClipArray();
t0 = AdjustForFindingStartTimes(clips, t0);
auto p = std::find_if(clips.begin(), clips.end(), [&] (const WaveClip* const& clip) { auto p = std::find_if(clips.begin(), clips.end(), [&] (const WaveClip* const& clip) {
return clip->GetStartTime() == t0; }); return clip->GetStartTime() == t0; });
if (p != clips.end() && (*p)->GetEndTime() > t1) { if (p != clips.end() && (*p)->GetEndTime() > t1) {
@ -5552,6 +5662,8 @@ AudacityProject::FoundClip AudacityProject::FindPrevClip(const WaveTrack* wt, do
result.waveTrack = wt; result.waveTrack = wt;
const auto clips = wt->SortedClipArray(); const auto clips = wt->SortedClipArray();
t0 = AdjustForFindingStartTimes(clips, t0);
auto p = std::find_if(clips.begin(), clips.end(), [&] (const WaveClip* const& clip) { auto p = std::find_if(clips.begin(), clips.end(), [&] (const WaveClip* const& clip) {
return clip->GetStartTime() == t0; }); return clip->GetStartTime() == t0; });
if (p != clips.end() && (*p)->GetEndTime() < t1) { if (p != clips.end() && (*p)->GetEndTime() < t1) {
@ -5677,10 +5789,10 @@ void AudacityProject::OnSelectClip(bool next)
message += temp; message += temp;
} }
if (result.waveTrack->GetNumClips() > 1 || nTracksSearched == 1) { temp.Printf(wxT("%d %s %d %s "), result.index + 1, _("of"), result.waveTrack->GetNumClips(),
temp.Printf(wxT("%d %s %d "), result.index + 1, _("of"), result.waveTrack->GetNumClips()); result.waveTrack->GetNumClips() == 1 ? _("clip") : _("clips"));
message += temp; message += temp;
}
message += wxT(", "); message += wxT(", ");
} }
mTrackPanel->MessageForScreenReader(message); mTrackPanel->MessageForScreenReader(message);
@ -5719,20 +5831,6 @@ void AudacityProject::OnSelectSyncLockSel()
mMixerBoard->Refresh(false); 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 // View Menu
// //
@ -6527,27 +6625,18 @@ AudacityProject::FoundClipBoundary AudacityProject::FindNextClipBoundary(const W
{ {
AudacityProject::FoundClipBoundary result{}; AudacityProject::FoundClipBoundary result{};
result.waveTrack = wt; result.waveTrack = wt;
const auto clips = wt->SortedClipArray(); 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) { 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) { 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 != clips.end() && pEnd != clips.end()) {
if ((*pStart)->GetStartTime() < (*pEnd)->GetEndTime()) { if ((*pEnd)->SharesBoundaryWithNextClip(*pStart)) {
result.nFound = 1; // boundary between two clips which are immediately next to each other.
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
result.nFound = 2; result.nFound = 2;
result.time = (*pEnd)->GetEndTime(); result.time = (*pEnd)->GetEndTime();
result.index1 = std::distance(clips.begin(), pEnd); 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.index2 = std::distance(clips.begin(), pStart);
result.clipStart2 = true; 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()) { else if (pEnd != clips.end()) {
result.nFound = 1; result.nFound = 1;
@ -6570,34 +6671,37 @@ AudacityProject::FoundClipBoundary AudacityProject::FindPrevClipBoundary(const W
{ {
AudacityProject::FoundClipBoundary result{}; AudacityProject::FoundClipBoundary result{};
result.waveTrack = wt; result.waveTrack = wt;
const auto clips = wt->SortedClipArray(); 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) { 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) { 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 != clips.rend() && pEnd != clips.rend()) {
if ((*pStart)->GetStartTime() > (*pEnd)->GetEndTime()) { if ((*pEnd)->SharesBoundaryWithNextClip(*pStart)) {
result.nFound = 1; // boundary between two clips which are immediately next to each other.
result.time = (*pStart)->GetStartTime(); result.nFound = 2;
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
result.time = (*pStart)->GetStartTime(); result.time = (*pStart)->GetStartTime();
result.index1 = static_cast<int>(clips.size()) - 1 - std::distance(clips.rbegin(), pStart); result.index1 = static_cast<int>(clips.size()) - 1 - std::distance(clips.rbegin(), pStart);
result.clipStart1 = true; result.clipStart1 = true;
result.index2 = static_cast<int>(clips.size()) - 1 - std::distance(clips.rbegin(), pEnd); result.index2 = static_cast<int>(clips.size()) - 1 - std::distance(clips.rbegin(), pEnd);
result.clipStart2 = false; 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()) { else if (pStart != clips.rend()) {
result.nFound = 1; result.nFound = 1;
@ -6609,6 +6713,42 @@ AudacityProject::FoundClipBoundary AudacityProject::FindPrevClipBoundary(const W
return result; 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) int AudacityProject::FindClipBoundaries(double time, bool next, std::vector<FoundClipBoundary>& finalResults)
{ {
const TrackList* tracks = GetTracks(); const TrackList* tracks = GetTracks();
@ -6664,7 +6804,8 @@ void AudacityProject::OnCursorPrevClipBoundary()
void AudacityProject::OnCursorClipBoundary(bool next) void AudacityProject::OnCursorClipBoundary(bool next)
{ {
std::vector<FoundClipBoundary> results; 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) { if (results.size() > 0) {
// note that if there is more than one result, each has the same time value. // 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 += temp;
} }
message += (result.clipStart1 ? _("start") : _("end")) + wxT(" "); message += (result.clipStart1 ? _("start") : _("end")) + wxT(" ");
if (result.waveTrack->GetNumClips() > 1 ) {
temp.Printf(wxT("%d %s %d "), result.index1 + 1, _("of"), result.waveTrack->GetNumClips()); temp.Printf(wxT("%d %s %d %s "), result.index1 + 1, _("of"), result.waveTrack->GetNumClips(),
message += temp; result.waveTrack->GetNumClips() == 1 ? _("clip") : _("clips"));
} message += temp;
if (result.nFound == 2) { if (result.nFound == 2) {
temp.Printf(wxT("%s %s %d "), _("and"), result.clipStart2 ? _("start") : _("end"), temp.Printf(wxT("%s %s %d "), _("and"), result.clipStart2 ? _("start") : _("end"),
result.index2 + 1); result.index2 + 1);
@ -6892,7 +7034,7 @@ void AudacityProject::OnAlignNoSync(int index)
void AudacityProject::OnAlign(int index) void AudacityProject::OnAlign(int index)
{ {
bool bMoveWith; bool bMoveWith;
gPrefs->Read(wxT("/GUI/MoveSelectionWithTracks"), &bMoveWith, true); gPrefs->Read(wxT("/GUI/MoveSelectionWithTracks"), &bMoveWith, false);
HandleAlign(index, bMoveWith); HandleAlign(index, bMoveWith);
} }
/* /*
@ -7098,14 +7240,6 @@ void AudacityProject::OnScoreAlign()
// Make a copy of the note track in case alignment is canceled or fails // Make a copy of the note track in case alignment is canceled or fails
auto holder = nt->Duplicate(); auto holder = nt->Duplicate();
auto alignedNoteTrack = static_cast<NoteTrack*>(holder.get()); 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 // Remove offset from NoteTrack because audio is
// mixed starting at zero and incorporating clip offsets. // mixed starting at zero and incorporating clip offsets.
if (alignedNoteTrack->GetOffset() < 0) { if (alignedNoteTrack->GetOffset() < 0) {
@ -7145,7 +7279,7 @@ void AudacityProject::OnScoreAlign()
#ifndef SKIP_ACTUAL_SCORE_ALIGNMENT #ifndef SKIP_ACTUAL_SCORE_ALIGNMENT
result = scorealign((void *) &mix, &mixer_process, result = scorealign((void *) &mix, &mixer_process,
2 /* channels */, 44100.0 /* srate */, endTime, 2 /* channels */, 44100.0 /* srate */, endTime,
alignedNoteTrack->GetSequence(), &progress, params); &alignedNoteTrack->GetSeq(), &progress, params);
#else #else
result = SA_SUCCESS; result = SA_SUCCESS;
#endif #endif
@ -7398,7 +7532,7 @@ int AudacityProject::DoAddLabel(const SelectedRegion &region, bool preserveFocus
void AudacityProject::OnMoveSelectionWithTracks() void AudacityProject::OnMoveSelectionWithTracks()
{ {
bool bMoveWith; bool bMoveWith;
gPrefs->Read(wxT("/GUI/MoveSelectionWithTracks"), &bMoveWith, true); gPrefs->Read(wxT("/GUI/MoveSelectionWithTracks"), &bMoveWith, false);
gPrefs->Write(wxT("/GUI/MoveSelectionWithTracks"), !bMoveWith); gPrefs->Write(wxT("/GUI/MoveSelectionWithTracks"), !bMoveWith);
gPrefs->Flush(); gPrefs->Flush();
@ -7878,6 +8012,7 @@ void AudacityProject::OnResample()
} }
} }
GetUndoManager()->StopConsolidating();
RedrawProject(); RedrawProject();
// Need to reset // Need to reset

View File

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

View File

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

View File

@ -95,7 +95,9 @@ public:
// PluginManager use // PluginManager use
bool DiscoverProviders(); bool DiscoverProviders();
// Seems we don't currently use FindAllPlugins
void FindAllPlugins(PluginIDList & providers, wxArrayString & paths); void FindAllPlugins(PluginIDList & providers, wxArrayString & paths);
wxArrayString FindPluginsForProvider(const PluginID & provider, const wxString & path); wxArrayString FindPluginsForProvider(const PluginID & provider, const wxString & path);
bool RegisterPlugin(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 Track::Holder NoteTrack::Duplicate() const
{ {
auto duplicate = std::make_unique<NoteTrack>(mDirManager); auto duplicate = std::make_unique<NoteTrack>(mDirManager);
duplicate->Init(*this); duplicate->Init(*this);
// Duplicate on NoteTrack moves data from mSeq to mSerializationBuffer // The duplicate begins life in serialized state. Often the duplicate is
// and from mSerializationBuffer to mSeq on alternate calls. Duplicate // pushed on the Undo stack. Then we want to un-serialize it (or a further
// to the undo stack and Duplicate back to the project should result // copy) only on demand after an Undo.
// in serialized blobs on the undo stack and traversable data in the
// project object.
if (mSeq) { if (mSeq) {
SonifyBeginSerialize(); SonifyBeginSerialize();
wxASSERT(!mSerializationBuffer); wxASSERT(!mSerializationBuffer);
@ -145,15 +165,19 @@ Track::Holder NoteTrack::Duplicate() const
&duplicate->mSerializationLength); &duplicate->mSerializationLength);
duplicate->mSerializationBuffer.reset( (char*)buffer ); duplicate->mSerializationBuffer.reset( (char*)buffer );
SonifyEndSerialize(); SonifyEndSerialize();
} else if (mSerializationBuffer) { }
SonifyBeginUnserialize(); else if (mSerializationBuffer) {
// Copy already serialized data.
wxASSERT(!mSeq); wxASSERT(!mSeq);
std::unique_ptr<Alg_track> alg_track{ Alg_seq::unserialize(mSerializationBuffer.get(), duplicate->mSerializationLength = this->mSerializationLength;
mSerializationLength) }; duplicate->mSerializationBuffer.reset
wxASSERT(alg_track->get_type() == 's'); ( new char[ this->mSerializationLength ] );
duplicate->mSeq.reset(static_cast<Alg_seq*>(alg_track.release())); memcpy( duplicate->mSerializationBuffer.get(),
SonifyEndUnserialize(); this->mSerializationBuffer.get(), this->mSerializationLength );
} else wxFAIL_MSG("neither mSeq nor mSerializationBuffer were present"); // bug if neither mSeq nor mSerializationBuffer }
else {
// We are duplicating a default-constructed NoteTrack, and that's okay
}
// copy some other fields here // copy some other fields here
duplicate->SetBottomNote(mBottomNote); duplicate->SetBottomNote(mBottomNote);
duplicate->SetPitchHeight(mPitchHeight); duplicate->SetPitchHeight(mPitchHeight);
@ -180,7 +204,7 @@ double NoteTrack::GetStartTime() const
double NoteTrack::GetEndTime() 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, const TimeWarper &warper,
double semitones) 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 double offset = this->GetOffset(); // track is shifted this amount
if (!mSeq) { // replace saveme with an (unserialized) duplicate auto &seq = GetSeq();
Track::Holder unt{ Duplicate() }; seq.convert_to_seconds(); // make sure time units are right
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
t1 -= offset; // adjust time range to compensate for track offset t1 -= offset; // adjust time range to compensate for track offset
t0 -= offset; t0 -= offset;
if (t1 > mSeq->get_dur()) { // make sure t0, t1 are within sequence if (t1 > seq.get_dur()) { // make sure t0, t1 are within sequence
t1 = mSeq->get_dur(); t1 = seq.get_dur();
if (t0 >= t1) return; if (t0 >= t1) return;
} }
Alg_iterator iter(mSeq.get(), false); Alg_iterator iter(mSeq.get(), false);
@ -219,8 +231,8 @@ void NoteTrack::WarpAndTransposeNotes(double t0, double t1,
} }
iter.end(); iter.end();
// now, use warper to warp the tempo map // now, use warper to warp the tempo map
mSeq->convert_to_beats(); // beats remain the same seq.convert_to_beats(); // beats remain the same
Alg_time_map_ptr map = mSeq->get_time_map(); Alg_time_map_ptr map = seq.get_time_map();
map->insert_beat(t0, map->time_to_beat(t0)); map->insert_beat(t0, map->time_to_beat(t0));
map->insert_beat(t1, map->time_to_beat(t1)); map->insert_beat(t1, map->time_to_beat(t1));
int i, len = map->length(); int i, len = map->length();
@ -229,7 +241,7 @@ void NoteTrack::WarpAndTransposeNotes(double t0, double t1,
beat.time = warper.Warp(beat.time + offset) - offset; beat.time = warper.Warp(beat.time + offset) - offset;
} }
// about to redisplay, so might as well convert back to time now // 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. // 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); mSeq = std::move(seq);
} }
Alg_seq* NoteTrack::GetSequence()
{
return mSeq.get();
}
void NoteTrack::PrintSequence() void NoteTrack::PrintSequence()
{ {
FILE *debugOutput; FILE *debugOutput;
@ -360,6 +367,8 @@ void NoteTrack::PrintSequence()
debugOutput = fopen("debugOutput.txt", "wt"); debugOutput = fopen("debugOutput.txt", "wt");
fprintf(debugOutput, "Importing MIDI...\n"); fprintf(debugOutput, "Importing MIDI...\n");
// This is called for debugging purposes. Do not compute mSeq on demand
// with GetSeq()
if (mSeq) { if (mSeq) {
int i = 0; int i = 0;
@ -416,15 +425,23 @@ Track::Holder NoteTrack::Cut(double t0, double t1)
THROW_INCONSISTENCY_EXCEPTION; THROW_INCONSISTENCY_EXCEPTION;
double len = t1-t0; double len = t1-t0;
//auto delta = -(
//( std::min( t1, GetEndTime() ) ) - ( std::max( t0, GetStartTime() ) )
//);
auto newTrack = std::make_unique<NoteTrack>(mDirManager); auto newTrack = std::make_unique<NoteTrack>(mDirManager);
newTrack->Init(*this); newTrack->Init(*this);
mSeq->convert_to_seconds(); auto &seq = GetSeq();
newTrack->mSeq.reset(mSeq->cut(t0 - GetOffset(), len, false)); seq.convert_to_seconds();
newTrack->mSeq.reset(seq.cut(t0 - GetOffset(), len, false));
newTrack->SetOffset(GetOffset()); 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? // What should be done with the rest of newTrack's members?
//(mBottomNote, mDirManager, mLastMidiPosition, //(mBottomNote, mDirManager, mLastMidiPosition,
// mSerializationBuffer, mSerializationLength, mVisibleChannels) // mSerializationBuffer, mSerializationLength, mVisibleChannels)
@ -444,8 +461,9 @@ Track::Holder NoteTrack::Copy(double t0, double t1, bool) const
newTrack->Init(*this); newTrack->Init(*this);
mSeq->convert_to_seconds(); auto &seq = GetSeq();
newTrack->mSeq.reset(mSeq->copy(t0 - GetOffset(), len, false)); seq.convert_to_seconds();
newTrack->mSeq.reset(seq.copy(t0 - GetOffset(), len, false));
newTrack->SetOffset(GetOffset()); newTrack->SetOffset(GetOffset());
// What should be done with the rest of newTrack's members? // 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) if (t1 < t0)
return false; 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: // 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: // 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 // want starting time to be t0
SetOffset(t0); SetOffset(t0);
// Not needed
// Alg_seq::clear seems to handle this
//AddToDuration( delta );
return true; return true;
} }
@ -477,8 +505,15 @@ void NoteTrack::Clear(double t0, double t1)
double len = t1-t0; double len = t1-t0;
if (mSeq) auto &seq = GetSeq();
mSeq->clear(t0 - GetOffset(), len, false); //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) void NoteTrack::Paste(double t, const Track *src)
@ -497,19 +532,27 @@ void NoteTrack::Paste(double t, const Track *src)
return; return;
NoteTrack* other = (NoteTrack*)src; NoteTrack* other = (NoteTrack*)src;
if (other->mSeq == NULL)
// THROW_INCONSISTENCY_EXCEPTION; // ?
return;
if(!mSeq) double delta = 0.0;
mSeq = std::make_unique<Alg_seq>(); auto &seq = GetSeq();
auto offset = other->GetOffset();
if (other->GetOffset() > 0) { if ( offset > 0 ) {
mSeq->convert_to_seconds(); seq.convert_to_seconds();
mSeq->insert_silence(t - GetOffset(), other->GetOffset()); seq.insert_silence( t - GetOffset(), offset );
t += other->GetOffset(); 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) void NoteTrack::Silence(double t0, double t1)
@ -519,20 +562,25 @@ void NoteTrack::Silence(double t0, double t1)
auto len = t1 - t0; auto len = t1 - t0;
mSeq->convert_to_seconds(); auto &seq = GetSeq();
seq.convert_to_seconds();
// XXX: do we want to set the all param? // 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, // 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 // 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) void NoteTrack::InsertSilence(double t, double len)
{ {
if (len <= 0) if (len < 0)
THROW_INCONSISTENCY_EXCEPTION; THROW_INCONSISTENCY_EXCEPTION;
mSeq->convert_to_seconds(); auto &seq = GetSeq();
mSeq->insert_silence(t - GetOffset(), len); 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 // 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 bool NoteTrack::Shift(double t) // t is always seconds
{ {
if (t > 0) { if (t > 0) {
auto &seq = GetSeq();
// insert an even number of measures // insert an even number of measures
mSeq->convert_to_beats(); seq.convert_to_beats();
// get initial tempo // get initial tempo
double tempo = mSeq->get_tempo(0.0); double tempo = seq.get_tempo(0.0);
double beats_per_measure = mSeq->get_bar_len(0.0); double beats_per_measure = seq.get_bar_len(0.0);
int m = ROUND(t * tempo / beats_per_measure); int m = ROUND(t * tempo / beats_per_measure);
// need at least 1 measure, so if we rounded down to zero, fix it // need at least 1 measure, so if we rounded down to zero, fix it
if (m == 0) m = 1; if (m == 0) m = 1;
// compute NEW tempo so that m measures at NEW tempo take t seconds // compute NEW tempo so that m measures at NEW tempo take t seconds
tempo = beats_per_measure * m / t; // in beats per second tempo = beats_per_measure * m / t; // in beats per second
mSeq->insert_silence(0.0, beats_per_measure * m); seq.insert_silence(0.0, beats_per_measure * m);
mSeq->set_tempo(tempo * 60.0 /* bpm */, 0.0, beats_per_measure * m); seq.set_tempo(tempo * 60.0 /* bpm */, 0.0, beats_per_measure * m);
mSeq->write("afterShift.gro"); seq.write("afterShift.gro");
} else if (t < 0) { } else if (t < 0) {
mSeq->convert_to_seconds(); auto &seq = GetSeq();
mSeq->clear(0, t, true); seq.convert_to_seconds();
seq.clear(0, t, true);
} else { // offset is zero, no modifications } else { // offset is zero, no modifications
return false; return false;
} }
return true; 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 // Alg_seq knows nothing about offset, so remove offset time
double seq_time = time - GetOffset(); 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 // 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); auto &seq = GetSeq();
// Alg_seq::stretch_region uses beats, so we translate time #if 0
// to beats first: // PRL: Would this be better ?
t0 -= GetOffset(); seq.set_real_dur( seq.get_real_dur() + delta );
t1 -= GetOffset(); #else
double b0 = mSeq->get_time_map()->time_to_beat(t0); seq.convert_to_seconds();
double b1 = mSeq->get_time_map()->time_to_beat(t1); seq.set_dur( seq.get_dur() + delta );
bool result = mSeq->stretch_region(b0, b1, dur); #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) { if (result) {
mSeq->convert_to_seconds(); const auto oldDur = t1.first - t0.first;
mSeq->set_dur(mSeq->get_dur() + dur - (t1 - t0)); AddToDuration( newDur - oldDur );
} }
return result; return result;
} }
@ -604,24 +662,21 @@ Alg_seq *NoteTrack::MakeExportableSeq(std::unique_ptr<Alg_seq> &cleanup) const
cleanup.reset(); cleanup.reset();
double offset = GetOffset(); double offset = GetOffset();
if (offset == 0) if (offset == 0)
return mSeq.get(); return &GetSeq();
// make a copy, deleting events that are shifted before time 0 // make a copy, deleting events that are shifted before time 0
double start = -offset; double start = -offset;
if (start < 0) start = 0; if (start < 0) start = 0;
// notes that begin before "start" are not included even if they // notes that begin before "start" are not included even if they
// extend past "start" (because "all" parameter is set to false) // 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(); auto seq = cleanup.get();
if (offset > 0) { 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 cleanup and mSeq so that Shift operates on the NEW copy
swap(pMutable->mSeq, cleanup); swap( this->mSeq, cleanup );
auto cleanup2 = finally( [&] { swap(pMutable->mSeq, cleanup); } ); auto cleanup2 = finally( [&] { swap( this->mSeq, cleanup ); } );
pMutable->Shift(offset); const_cast< NoteTrack *>( this )->Shift(offset);
} }
#ifdef OLD_CODE #ifdef OLD_CODE
// now shift events by offset. This must be done with an integer // 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); seq->set_tempo(bps * 60.0, 0, beats_per_measure * n);
#endif #endif
} else { } else {
auto &mySeq = GetSeq();
// if offset is negative, it might not be a multiple of beats, but // 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 // we want to preserve the relative positions of measures. I.e. we
// should shift barlines and time signatures as well as notes. // should shift barlines and time signatures as well as notes.
// Insert a time signature at the first bar-line if necessary. // Insert a time signature at the first bar-line if necessary.
// Translate start from seconds to beats and call it beat: // Translate start from seconds to beats and call it beat:
double beat = mSeq->get_time_map()->time_to_beat(start); double beat = mySeq.get_time_map()->time_to_beat(start);
// Find the time signature in mSeq in effect at start (beat): // Find the time signature in mySeq in effect at start (beat):
int i = mSeq->time_sig.find_beat(beat); int i = mySeq.time_sig.find_beat(beat);
// i is where you would insert a NEW time sig at 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 // 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 // at beat means that there is a barline at beat, so when beat
// is shifted to 0, the relative barline positions are preserved // is shifted to 0, the relative barline positions are preserved
if (mSeq->time_sig.length() > 0 && if (mySeq.time_sig.length() > 0 &&
within(beat, mSeq->time_sig[i].beat, ALG_EPS)) { within(beat, mySeq.time_sig[i].beat, ALG_EPS)) {
// beat coincides with time signature change, so offset must // beat coincides with time signature change, so offset must
// be a multiple of beats // be a multiple of beats
/* do nothing */ ; /* do nothing */ ;
// Case 2: there is no time signature before beat. // Case 2: there is no time signature before beat.
} else if (i == 0 && (mSeq->time_sig.length() == 0 || } else if (i == 0 && (mySeq.time_sig.length() == 0 ||
mSeq->time_sig[i].beat > beat)) { mySeq.time_sig[i].beat > beat)) {
// If beat does not fall on an implied barline, we need to // If beat does not fall on an implied barline, we need to
// insert a time signature. // insert a time signature.
double measures = beat / 4.0; 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 // Case 3: i-1 must be the effective time sig position
} else { } else {
i -= 1; // index the time signature in effect at beat 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 beats_per_measure = (tsp->num * 4) / tsp->den;
double measures = (beat - tsp->beat) / beats_per_measure; double measures = (beat - tsp->beat) / beats_per_measure;
int imeasures = ROUND(measures); int imeasures = ROUND(measures);
@ -737,12 +793,13 @@ bool NoteTrack::ExportAllegro(const wxString &f) const
double offset = GetOffset(); double offset = GetOffset();
bool in_seconds; bool in_seconds;
gPrefs->Read(wxT("/FileFormats/AllegroStyle"), &in_seconds, true); gPrefs->Read(wxT("/FileFormats/AllegroStyle"), &in_seconds, true);
auto &seq = GetSeq();
if (in_seconds) { if (in_seconds) {
mSeq->convert_to_seconds(); seq.convert_to_seconds();
} else { } 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 // may throw
{ {
std::ostringstream data; 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; Track::Holder holder;
const NoteTrack *saveme = this; 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(); holder = Duplicate();
saveme = static_cast<NoteTrack*>(holder.get()); saveme = static_cast<NoteTrack*>(holder.get());
wxASSERT(saveme->mSeq);
} }
saveme->mSeq->write(data, true); saveme->GetSeq().write(data, true);
xmlFile.StartTag(wxT("notetrack")); xmlFile.StartTag(wxT("notetrack"));
xmlFile.WriteAttr(wxT("name"), saveme->mName); xmlFile.WriteAttr(wxT("name"), saveme->mName);
this->NoteTrackBase::WriteXMLAttributes(xmlFile); this->NoteTrackBase::WriteXMLAttributes(xmlFile);

View File

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

View File

@ -1437,11 +1437,13 @@ void PluginManager::FindFilesInPathList(const wxString & pattern,
wxArrayString paths; 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(); const wxFileName &ff = FileNames::PlugInDir();
paths.Add(ff.GetFullPath()); paths.Add(ff.GetFullPath());
} }
#endif
// Add the "Audacity" plug-ins directory // Add the "Audacity" plug-ins directory
wxFileName ff = PlatformCompatibility::GetExecutablePath(); wxFileName ff = PlatformCompatibility::GetExecutablePath();

View File

@ -2052,8 +2052,10 @@ void AudacityProject::UpdateLayout()
if (!mTrackPanel) if (!mTrackPanel)
return; return;
mToolManager->LayoutToolBars(); // Layout first to get our new width,
// Then and only then we can arrange the toolbars.
Layout(); Layout();
mToolManager->LayoutToolBars();
// Retrieve size of this projects window // Retrieve size of this projects window
wxSize mainsz = GetSize(); wxSize mainsz = GetSize();
@ -2107,7 +2109,6 @@ void AudacityProject::RefreshAllTitles(bool bShowProjectNumbers )
void AudacityProject::OnIconize(wxIconizeEvent &event) void AudacityProject::OnIconize(wxIconizeEvent &event)
{ {
int VisibleProjectCount = 0; int VisibleProjectCount = 0;
//JKC: On Iconizing we get called twice. Don't know //JKC: On Iconizing we get called twice. Don't know
@ -2311,7 +2312,9 @@ bool AudacityProject::TryToMakeActionAllowed
if( (MissingFlags & ~( TimeSelectedFlag | WaveTracksSelectedFlag)) ) if( (MissingFlags & ~( TimeSelectedFlag | WaveTracksSelectedFlag)) )
return false; return false;
OnSelectAll(); // This was 'OnSelectAll'. Changing it to OnSelectSomething means if
// selecting all tracks is enough, we just do that.
OnSelectSomething();
flags = GetUpdateFlags(); flags = GetUpdateFlags();
bAllowed = ((flags & mask) == (flagsRqd & mask)); bAllowed = ((flags & mask) == (flagsRqd & mask));
return bAllowed; return bAllowed;
@ -2319,7 +2322,19 @@ bool AudacityProject::TryToMakeActionAllowed
void AudacityProject::OnMenu(wxCommandEvent & event) 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(), bool handled = mCommandManager.HandleMenuID(event.GetId(),
GetUpdateFlags(), GetUpdateFlags(),
NoFlagsSpecifed); NoFlagsSpecifed);
@ -4607,19 +4622,6 @@ void AudacityProject::Clear()
RedrawProject(); 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 // Utility function called by other zoom methods
void AudacityProject::Zoom(double level) void AudacityProject::Zoom(double level)
{ {

View File

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

View File

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

View File

@ -103,14 +103,20 @@ for registering for changes.
#include <wx/notebook.h> #include <wx/notebook.h>
#include <wx/treectrl.h> #include <wx/treectrl.h>
#include <wx/spinctrl.h> #include <wx/spinctrl.h>
#include <wx/bmpbuttn.h>
#include "Internat.h" #include "Internat.h"
#include "Experimental.h" #include "Experimental.h"
#include "Shuttle.h" #include "Shuttle.h"
#include "WrappedType.h" #include "WrappedType.h"
#include "widgets/wxPanelWrapper.h" #include "widgets/wxPanelWrapper.h"
#include "../images/Help.xpm"
ShuttleGuiBase::ShuttleGuiBase(wxWindow * pParent, teShuttleMode ShuttleMode ) ShuttleGuiBase::ShuttleGuiBase(wxWindow * pParent, teShuttleMode ShuttleMode )
{ {
// Suppress warnings about the header file
wxUnusedVar(Help_xpm);
wxASSERT( (pParent != NULL ) || ( ShuttleMode != eIsCreating)); wxASSERT( (pParent != NULL ) || ( ShuttleMode != eIsCreating));
mpParent = pParent; mpParent = pParent;
@ -2168,7 +2174,11 @@ std::unique_ptr<wxSizer> CreateStdButtonSizer(wxWindow *parent, long buttons, wx
if( buttons & eHelpButton ) 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) if (buttons & ePreviewButton)

View File

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

View File

@ -41,7 +41,8 @@ enum teResourceFlags
resFlagPaired =0x01, resFlagPaired =0x01,
resFlagCursor =0x02, resFlagCursor =0x02,
resFlagNewLine = 0x04, 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 enum teThemeType

View File

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

View File

@ -273,7 +273,7 @@ TrackArtist::TrackArtist()
mdBrange = ENV_DB_RANGE; mdBrange = ENV_DB_RANGE;
mShowClipping = false; mShowClipping = false;
mSampleDisplay = 0; mSampleDisplay = 1;// Stem plots by default.
UpdatePrefs(); UpdatePrefs();
SetColours(); SetColours();
@ -2730,7 +2730,7 @@ void TrackArtist::DrawNoteBackground(const NoteTrack *track, wxDC &dc,
int left = TIME_TO_X(track->GetOffset()); int left = TIME_TO_X(track->GetOffset());
if (left < sel.x) left = sel.x; // clip on left 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 if (right > sel.x + sel.width) right = sel.x + sel.width; // clip on right
// need overlap between MIDI data and the background region // need overlap between MIDI data and the background region
@ -2772,7 +2772,7 @@ void TrackArtist::DrawNoteBackground(const NoteTrack *track, wxDC &dc,
} }
// draw bar lines // 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 // We assume that sliding a NoteTrack around slides the barlines
// along with the notes. This means that when we write out a track // 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 // 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 h = X_TO_TIME(rect.x);
const double h1 = X_TO_TIME(rect.x + rect.width); const double h1 = X_TO_TIME(rect.x + rect.width);
Alg_seq_ptr seq = track->mSeq.get(); Alg_seq_ptr seq = &track->GetSeq();
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);
if (!track->GetSelected()) if (!track->GetSelected())
sel0 = sel1 = 0.0; sel0 = sel1 = 0.0;
@ -3214,7 +3202,7 @@ void TrackArtist::UpdatePrefs()
{ {
mdBrange = gPrefs->Read(ENV_DB_KEY, mdBrange); mdBrange = gPrefs->Read(ENV_DB_KEY, mdBrange);
mShowClipping = gPrefs->Read(wxT("/GUI/ShowClipping"), mShowClipping); mShowClipping = gPrefs->Read(wxT("/GUI/ShowClipping"), mShowClipping);
gPrefs->Read(wxT("/GUI/SampleView"), &mSampleDisplay, 0); gPrefs->Read(wxT("/GUI/SampleView"), &mSampleDisplay, 1);
SetColours(); 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. 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 { class AUDACITY_DLL_API TrackPanel final : public OverlayPanel {
public: public:
@ -256,7 +275,9 @@ class AUDACITY_DLL_API TrackPanel final : public OverlayPanel {
// (ignoring any fisheye) // (ignoring any fisheye)
virtual double GetScreenEndTime() const; virtual double GetScreenEndTime() const;
virtual void OnClipMove(bool right); static double OnClipMove
(ViewInfo &viewInfo, Track *track,
TrackList &trackList, bool syncLocked, bool right);
protected: protected:
virtual MixerBoard* GetMixerBoard(); virtual MixerBoard* GetMixerBoard();
@ -299,20 +320,33 @@ class AUDACITY_DLL_API TrackPanel final : public OverlayPanel {
// part shrinks, keeping the leftmost and rightmost boundaries // part shrinks, keeping the leftmost and rightmost boundaries
// fixed. // fixed.
enum StretchEnum { enum StretchEnum {
stretchNone = 0, // false value!
stretchLeft, stretchLeft,
stretchCenter, stretchCenter,
stretchRight stretchRight
}; };
StretchEnum mStretchMode; // remembers what to drag struct StretchState {
bool mStretching; // true between mouse down and mouse up StretchEnum mMode { stretchCenter }; // remembers what to drag
bool mStretched; // true after drag has pushed state
double mStretchStart; // time of initial mouse position, quantized using QuantizedTimeAndBeat = std::pair< double, double >;
// to the nearest beat
double mStretchSel0; // initial sel0 (left) quantized to nearest beat bool mStretching {}; // true between mouse down and mouse up
double mStretchSel1; // initial sel1 (left) quantized to nearest beat double mOrigT0 {};
double mStretchLeftBeats; // how many beats from left to cursor double mOrigT1 {};
double mStretchRightBeats; // how many beats from cursor to right QuantizedTimeAndBeat mBeatCenter { 0, 0 };
virtual bool HitTestStretch(Track *track, const wxRect &rect, const wxMouseEvent & event); 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); virtual void Stretch(int mouseXCoordinate, int trackLeftEdge, Track *pTrack);
#endif #endif
@ -383,10 +417,16 @@ protected:
virtual void HandleSlide(wxMouseEvent & event); virtual void HandleSlide(wxMouseEvent & event);
virtual void StartSlide(wxMouseEvent &event); virtual void StartSlide(wxMouseEvent &event);
virtual void DoSlide(wxMouseEvent &event); virtual void DoSlide(wxMouseEvent &event);
virtual void DoSlideHorizontal(); static void DoSlideHorizontal
virtual void CreateListOfCapturedClips(double clickTime); ( ClipMoveState &state, TrackList &trackList, Track &capturedTrack );
virtual void AddClipsToCaptured(Track *t, bool withinSelection); static void CreateListOfCapturedClips
virtual void AddClipsToCaptured(Track *t, double t0, double t1); ( 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 // AS: Handle zooming into tracks
virtual void HandleZoom(wxMouseEvent & event); virtual void HandleZoom(wxMouseEvent & event);
@ -658,10 +698,7 @@ protected:
Track *mCapturedTrack; Track *mCapturedTrack;
Envelope *mCapturedEnvelope; Envelope *mCapturedEnvelope;
WaveClip *mCapturedClip; ClipMoveState mClipMoveState;
TrackClipArray mCapturedClipArray;
TrackArray mTrackExclusions;
bool mCapturedClipIsSelection;
WaveTrackLocation mCapturedTrackLocation; WaveTrackLocation mCapturedTrackLocation;
wxRect mCapturedTrackLocationRect; wxRect mCapturedTrackLocationRect;
wxRect mCapturedRect; wxRect mCapturedRect;
@ -678,10 +715,6 @@ protected:
wxBaseArrayDouble mSlideSnapToPoints; wxBaseArrayDouble mSlideSnapToPoints;
wxArrayInt mSlideSnapLinePixels; 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 mDidSlideVertically;
bool mRedrawAfterStop; bool mRedrawAfterStop;
@ -702,10 +735,28 @@ protected:
// are the horizontal index of pixels to display user feedback // are the horizontal index of pixels to display user feedback
// guidelines so the user knows when such snapping is taking place. // guidelines so the user knows when such snapping is taking place.
std::unique_ptr<SnapManager> mSnapManager; std::unique_ptr<SnapManager> mSnapManager;
wxInt64 mSnapLeft; wxInt64 mSnapLeft { -1 };
wxInt64 mSnapRight; wxInt64 mSnapRight { -1 };
bool mSnapPreferRightEdge; 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; NumericConverter mConverter;
WaveTrack * mDrawingTrack; // Keeps track of which track you are drawing on between events cf. HandleDraw() 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; current = -1;
saved = -1; saved = -1;
consolidationCount = 0;
ResetODChangesFlag(); ResetODChangesFlag();
} }
@ -257,10 +256,9 @@ void UndoManager::PushState(const TrackList * l,
{ {
unsigned int i; unsigned int i;
// If consolidate is set to true, group up to 3 identical operations. if ( ((flags & UndoPush::CONSOLIDATE) != UndoPush::MINIMAL) &&
if (((flags & UndoPush::CONSOLIDATE) != UndoPush::MINIMAL) && lastAction == longDescription && lastAction == longDescription &&
consolidationCount < 2) { mayConsolidate ) {
consolidationCount++;
ModifyState(l, selectedRegion, tags); ModifyState(l, selectedRegion, tags);
// MB: If the "saved" state was modified by ModifyState, reset // MB: If the "saved" state was modified by ModifyState, reset
// it so that UnsavedChanges returns true. // it so that UnsavedChanges returns true.
@ -270,7 +268,7 @@ void UndoManager::PushState(const TrackList * l,
return; return;
} }
consolidationCount = 0; mayConsolidate = true;
i = current + 1; i = current + 1;
while (i < stack.size()) { while (i < stack.size()) {
@ -319,7 +317,7 @@ const UndoState &UndoManager::SetStateTo
} }
lastAction = wxT(""); lastAction = wxT("");
consolidationCount = 0; mayConsolidate = false;
return stack[current]->state; return stack[current]->state;
} }
@ -333,7 +331,7 @@ const UndoState &UndoManager::Undo(SelectedRegion *selectedRegion)
*selectedRegion = stack[current]->state.selectedRegion; *selectedRegion = stack[current]->state.selectedRegion;
lastAction = wxT(""); lastAction = wxT("");
consolidationCount = 0; mayConsolidate = false;
return stack[current]->state; return stack[current]->state;
} }
@ -360,7 +358,7 @@ const UndoState &UndoManager::Redo(SelectedRegion *selectedRegion)
*/ */
lastAction = wxT(""); lastAction = wxT("");
consolidationCount = 0; mayConsolidate = false;
return stack[current]->state; return stack[current]->state;
} }

View File

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

View File

@ -1948,3 +1948,17 @@ void WaveClip::Resample(int rate, ProgressDialog *progress)
mRate = rate; 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; } bool GetIsPlaceholder() const { return mIsPlaceholder; }
void SetIsPlaceholder(bool val) { mIsPlaceholder = val; } void SetIsPlaceholder(bool val) { mIsPlaceholder = val; }
// used by commands which interact with clips using the keyboard
bool SharesBoundaryWithNextClip(const WaveClip* next) const;
public: public:
// Cache of values to colour pixels of Spectrogram - used by TrackArtist // Cache of values to colour pixels of Spectrogram - used by TrackArtist
mutable std::unique_ptr<SpecPxCache> mSpecPxCache; mutable std::unique_ptr<SpecPxCache> mSpecPxCache;

View File

@ -2212,15 +2212,27 @@ WaveClip* WaveTrack::GetClipAtSample(sampleCount sample)
return NULL; 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) 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(); 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 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) Envelope* WaveTrack::GetEnvelopeAtX(int xcoord)

View File

@ -448,7 +448,7 @@ void CommandManager::PurgeData()
mCommandIDHash.clear(); mCommandIDHash.clear();
mCurrentMenuName = COMMAND; mCurrentMenuName = COMMAND;
mCurrentID = 0; mCurrentID = 17000;
} }
@ -1204,12 +1204,17 @@ bool CommandManager::FilterKeyEvent(AudacityProject *project, const wxKeyEvent &
{ {
wxWindow * pWnd = wxWindow::FindFocus(); wxWindow * pWnd = wxWindow::FindFocus();
wxWindow * pTrackPanel = (wxWindow*)GetActiveProject()->GetTrackPanel(); 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 ); //wxLogDebug("Focus: %p TrackPanel: %p", pWnd, pTrackPanel );
// We allow the keystrokes below to be handled by wxWidgets controls IF we are // We allow the keystrokes below to be handled by wxWidgets controls IF we are
// in some sub window rather than in the TrackPanel itself. // in some sub window rather than in the TrackPanel itself.
// Otherwise they will go to our command handler and if it handles them // Otherwise they will go to our command handler and if it handles them
// they will NOT be available to wxWidgets. // they will NOT be available to wxWidgets.
if( pWnd != pTrackPanel ){ if( bIntercept ){
switch( evt.GetKeyCode() ){ switch( evt.GetKeyCode() ){
case WXK_LEFT: case WXK_LEFT:
case WXK_RIGHT: 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"); return XO("Increases or decreases the volume of the audio you have selected");
} }
wxString EffectAmplify::ManualPage()
{
return wxT("Amplify");
}
// EffectIdentInterface implementation // EffectIdentInterface implementation
EffectType EffectAmplify::GetType() EffectType EffectAmplify::GetType()

View File

@ -37,6 +37,7 @@ public:
wxString GetSymbol() override; wxString GetSymbol() override;
wxString GetDescription() override; wxString GetDescription() override;
wxString ManualPage() override;
// EffectIdentInterface implementation // 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"); 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 // EffectIdentInterface implementation
EffectType EffectAutoDuck::GetType() EffectType EffectAutoDuck::GetType()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -177,6 +177,7 @@ ContrastDialog::ContrastDialog(wxWindow * parent, wxWindowID id,
S.SetBorder(5); S.SetBorder(5);
S.StartHorizontalLay(wxCENTER, false); 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.AddTitle(_("Contrast Analyzer, for measuring RMS volume differences between two selections of audio."));
} }
S.EndHorizontalLay(); S.EndHorizontalLay();
@ -362,6 +363,67 @@ void ContrastDialog::OnGetBackground(wxCommandEvent & /*event*/)
results(); 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() void ContrastDialog::results()
{ {
mPassFailText->SetName(wxT("")); mPassFailText->SetName(wxT(""));
@ -381,25 +443,17 @@ void ContrastDialog::results()
mPassFailText->ChangeValue(_("Background higher than foreground")); mPassFailText->ChangeValue(_("Background higher than foreground"));
} }
else if(diffdB > WCAG2_PASS) { else if(diffdB > WCAG2_PASS) {
/* i18n-hint: WCAG abbreviates Web Content Accessibility Guidelines */
mPassFailText->ChangeValue(_("WCAG2 Pass")); mPassFailText->ChangeValue(_("WCAG2 Pass"));
} }
else { else {
/* i18n-hint: WCAG abbreviates Web Content Accessibility Guidelines */
mPassFailText->ChangeValue(_("WCAG2 Fail")); mPassFailText->ChangeValue(_("WCAG2 Fail"));
} }
/* i18n-hint: i.e. difference in loudness at the moment. */ /* i18n-hint: i.e. difference in loudness at the moment. */
mDiffText->SetName(_("Current difference")); mDiffText->SetName(_("Current difference"));
if( diffdB != diffdB ) { // test for NaN, reliant on IEEE implementation mDiffText->ChangeValue( FormatDifference( diffdB ) );
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")));
}
}
} }
if (mForegroundIsDefined) { if (mForegroundIsDefined) {
@ -459,6 +513,7 @@ void ContrastDialog::OnExport(wxCommandEvent & WXUNUSED(event))
} }
f.AddLine(wxT("===================================")); 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(_("WCAG 2.0 Success Criteria 1.4.7 Contrast Results"));
f.AddLine(wxT("")); f.AddLine(wxT(""));
f.AddLine(wxString::Format(_("Filename = %s."), project->GetFileName().c_str() )); 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); m = (int)((t - h*3600)/60);
s = t - h*3600.0 - m*60.0; 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 )); f.AddLine(wxString::Format(_("Time ended = %2d hour(s), %2d minute(s), %.2f seconds."), h, m, s ));
if(mForegroundIsDefined) f.AddLine( FormatRMSMessage( mForegroundIsDefined ? &foregrounddB : nullptr ) );
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(wxT("")); f.AddLine(wxT(""));
f.AddLine(_("Background")); f.AddLine(_("Background"));
t = (float)mBackgroundStartT->GetValue(); t = (float)mBackgroundStartT->GetValue();
@ -493,23 +542,12 @@ void ContrastDialog::OnExport(wxCommandEvent & WXUNUSED(event))
m = (int)((t - h*3600)/60); m = (int)((t - h*3600)/60);
s = t - h*3600.0 - m*60.0; 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 )); f.AddLine(wxString::Format(_("Time ended = %2d hour(s), %2d minute(s), %.2f seconds."), h, m, s ));
if(mBackgroundIsDefined) f.AddLine( FormatRMSMessage( mBackgroundIsDefined ? &backgrounddB : nullptr ) );
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(wxT("")); f.AddLine(wxT(""));
f.AddLine(_("Results")); f.AddLine(_("Results"));
float diffdB = foregrounddB - backgrounddB; float diffdB = foregrounddB - backgrounddB;
if( diffdB != diffdB ) //test for NaN, reliant on IEEE implementation
f.AddLine(wxString::Format(_("Difference is indeterminate.") )); f.AddLine( FormatDifferenceForExport( diffdB ) );
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.")));
if( diffdB > 20. ) if( diffdB > 20. )
f.AddLine(_("Success Criteria 1.4.7 of WCAG 2.0: Pass")); f.AddLine(_("Success Criteria 1.4.7 of WCAG 2.0: Pass"));
else else

View File

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

View File

@ -68,6 +68,7 @@ public:
wxString GetSymbol() override; wxString GetSymbol() override;
wxString GetDescription() override; wxString GetDescription() override;
wxString ManualPage() override;
// EffectIdentInterface implementation // 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"); 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 // EffectIdentInterface implementation
EffectType EffectDtmf::GetType() EffectType EffectDtmf::GetType()

View File

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

View File

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

View File

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

View File

@ -53,6 +53,10 @@ greater use in future.
#include "../ondemand/ODManager.h" #include "../ondemand/ODManager.h"
#include "TimeWarper.h" #include "TimeWarper.h"
#include "nyquist/Nyquist.h" #include "nyquist/Nyquist.h"
#include "../widgets/HelpSystem.h"
#include "../widgets/LinkingHtmlWindow.h"
#include "../widgets/ErrorDialog.h"
#include "../FileNames.h"
#if defined(__WXMAC__) #if defined(__WXMAC__)
#include <Cocoa/Cocoa.h> #include <Cocoa/Cocoa.h>
@ -1098,6 +1102,16 @@ wxString Effect::GetPreset(wxWindow * parent, const wxString & parms)
return wxEmptyString; return wxEmptyString;
} }
wxString Effect::ManualPage()
{
return wxEmptyString;
}
wxString Effect::HelpPage()
{
return wxEmptyString;
}
bool Effect::IsBatchProcessing() bool Effect::IsBatchProcessing()
{ {
return mIsBatch; return mIsBatch;
@ -2604,8 +2618,9 @@ void Effect::Preview(bool dryOnly)
} }
} }
else { else {
wxMessageBox(_("Error opening sound device. Try changing the audio host, playback device and the project sample rate."), ShowErrorDialog(FocusDialog, _("Error"),
_("Error"), wxOK | wxICON_EXCLAMATION, FocusDialog); _("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_CLOSE(EffectUIHost::OnClose)
EVT_BUTTON(wxID_APPLY, EffectUIHost::OnApply) EVT_BUTTON(wxID_APPLY, EffectUIHost::OnApply)
EVT_BUTTON(wxID_CANCEL, EffectUIHost::OnCancel) EVT_BUTTON(wxID_CANCEL, EffectUIHost::OnCancel)
EVT_BUTTON(wxID_HELP, EffectUIHost::OnHelp)
EVT_BUTTON(eDebugID, EffectUIHost::OnDebug) EVT_BUTTON(eDebugID, EffectUIHost::OnDebug)
EVT_BUTTON(kMenuID, EffectUIHost::OnMenu) EVT_BUTTON(kMenuID, EffectUIHost::OnMenu)
EVT_CHECKBOX(kEnableID, EffectUIHost::OnEnable) EVT_CHECKBOX(kEnableID, EffectUIHost::OnEnable)
@ -3039,9 +3055,24 @@ bool EffectUIHost::Initialize()
bar->SetSizerAndFit(bs.release()); bar->SetSizerAndFit(bs.release());
} }
long buttons = eApplyButton + eCloseButton; long buttons;
if (mEffect->mUIDebug) 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; buttons += eDebugButton;
} }
@ -3203,6 +3234,19 @@ void EffectUIHost::OnCancel(wxCommandEvent & evt)
return; 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) void EffectUIHost::OnDebug(wxCommandEvent & evt)
{ {
OnApply(evt); OnApply(evt);

View File

@ -226,6 +226,11 @@ class AUDACITY_DLL_API Effect /* not final */ : public wxEvtHandler,
virtual bool HasFactoryDefaults(); virtual bool HasFactoryDefaults();
virtual wxString GetPreset(wxWindow * parent, const wxString & parms); 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 bool IsBatchProcessing();
virtual void SetBatchProcessing(bool start); virtual void SetBatchProcessing(bool start);
@ -593,6 +598,7 @@ private:
void OnApply(wxCommandEvent & evt); void OnApply(wxCommandEvent & evt);
void DoCancel(); void DoCancel();
void OnCancel(wxCommandEvent & evt); void OnCancel(wxCommandEvent & evt);
void OnHelp(wxCommandEvent & evt);
void OnDebug(wxCommandEvent & evt); void OnDebug(wxCommandEvent & evt);
void OnMenu(wxCommandEvent & evt); void OnMenu(wxCommandEvent & evt);
void OnEnable(wxCommandEvent & evt); void OnEnable(wxCommandEvent & evt);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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