mirror of
https://github.com/cookiengineer/audacity
synced 2025-07-17 17:17:40 +02:00
Merge branch 'master' into scrollplay
This commit is contained in:
commit
d793152442
@ -67,8 +67,10 @@ from there. Audacity will look for a file called "Pause.png".
|
|||||||
DEFINE_IMAGE( bmpCutPreviewDisabled, wxImage( 16, 16 ), wxT("CutPreviewDisabled"));
|
DEFINE_IMAGE( bmpCutPreviewDisabled, wxImage( 16, 16 ), wxT("CutPreviewDisabled"));
|
||||||
DEFINE_IMAGE( bmpAppendRecord, wxImage( 16, 16 ), wxT("AppendRecord"));
|
DEFINE_IMAGE( bmpAppendRecord, wxImage( 16, 16 ), wxT("AppendRecord"));
|
||||||
DEFINE_IMAGE( bmpAppendRecordDisabled, wxImage( 16, 16 ), wxT("AppendRecordDisabled"));
|
DEFINE_IMAGE( bmpAppendRecordDisabled, wxImage( 16, 16 ), wxT("AppendRecordDisabled"));
|
||||||
DEFINE_IMAGE( bmpScrubDisabled, wxImage( 16, 16 ), wxT("ScrubDisabled"));
|
DEFINE_IMAGE( bmpScrubDisabled, wxImage( 18, 16 ), wxT("ScrubDisabled"));
|
||||||
DEFINE_IMAGE( bmpScrub, wxImage( 16, 16 ), wxT("Scrub"));
|
DEFINE_IMAGE( bmpScrub, wxImage( 18, 16 ), wxT("Scrub"));
|
||||||
|
DEFINE_IMAGE( bmpSeekDisabled, wxImage( 26, 16 ), wxT("SeekDisabled"));
|
||||||
|
DEFINE_IMAGE( bmpSeek, wxImage( 26, 16 ), wxT("Seek"));
|
||||||
|
|
||||||
SET_THEME_FLAGS( resFlagNewLine );
|
SET_THEME_FLAGS( resFlagNewLine );
|
||||||
DEFINE_IMAGE( bmpUpButtonLarge, wxImage( 48, 48 ), wxT("UpButtonLarge"));
|
DEFINE_IMAGE( bmpUpButtonLarge, wxImage( 48, 48 ), wxT("UpButtonLarge"));
|
||||||
|
@ -1529,10 +1529,14 @@ void AudacityApp::OnKeyDown(wxKeyEvent &event)
|
|||||||
// Stop play, including scrub, but not record
|
// Stop play, including scrub, but not record
|
||||||
auto project = ::GetActiveProject();
|
auto project = ::GetActiveProject();
|
||||||
auto token = project->GetAudioIOToken();
|
auto token = project->GetAudioIOToken();
|
||||||
|
auto &scrubber = project->GetScrubber();
|
||||||
|
auto scrubbing = scrubber.HasStartedScrubbing();
|
||||||
|
if (scrubbing)
|
||||||
|
scrubber.Cancel();
|
||||||
if((token > 0 &&
|
if((token > 0 &&
|
||||||
gAudioIO->IsAudioTokenActive(token) &&
|
gAudioIO->IsAudioTokenActive(token) &&
|
||||||
gAudioIO->GetNumCaptureChannels() == 0) ||
|
gAudioIO->GetNumCaptureChannels() == 0) ||
|
||||||
project->GetScrubber().HasStartedScrubbing())
|
scrubbing)
|
||||||
// ESC out of other play (but not record)
|
// ESC out of other play (but not record)
|
||||||
project->OnStop();
|
project->OnStop();
|
||||||
else
|
else
|
||||||
|
@ -1798,6 +1798,10 @@ int AudioIO::StartStream(const WaveTrackArray &playbackTracks,
|
|||||||
if (mListener && captureChannels > 0)
|
if (mListener && captureChannels > 0)
|
||||||
mListener->OnAudioIOStopRecording();
|
mListener->OnAudioIOStopRecording();
|
||||||
mStreamToken = 0;
|
mStreamToken = 0;
|
||||||
|
|
||||||
|
// Don't cause a busy wait in the audio thread after stopping scrubbing
|
||||||
|
mPlayMode = PLAY_STRAIGHT;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2100,6 +2104,10 @@ void AudioIO::StartStreamCleanup(bool bOnlyBuffers)
|
|||||||
mScrubQueue = 0;
|
mScrubQueue = 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// Don't cause a busy wait in the audio thread after stopping scrubbing
|
||||||
|
mPlayMode = PLAY_STRAIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||||
@ -2530,6 +2538,9 @@ void AudioIO::StopStream()
|
|||||||
// Tell UI to hide sample rate
|
// Tell UI to hide sample rate
|
||||||
mListener->OnAudioIORate(0);
|
mListener->OnAudioIORate(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't cause a busy wait in the audio thread after stopping scrubbing
|
||||||
|
mPlayMode = PLAY_STRAIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioIO::SetPaused(bool state)
|
void AudioIO::SetPaused(bool state)
|
||||||
|
@ -726,6 +726,7 @@ void AudacityProject::CreateMenusAndCommands()
|
|||||||
c->AddCheck(wxT("ShowTranscriptionTB"), _("Transcri&ption Toolbar"), FN(OnShowTranscriptionToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
|
c->AddCheck(wxT("ShowTranscriptionTB"), _("Transcri&ption Toolbar"), FN(OnShowTranscriptionToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
|
||||||
/* i18n-hint: Clicking this menu item shows the toolbar with the big buttons on it (play record etc)*/
|
/* i18n-hint: Clicking this menu item shows the toolbar with the big buttons on it (play record etc)*/
|
||||||
c->AddCheck(wxT("ShowTransportTB"), _("&Transport Toolbar"), FN(OnShowTransportToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
|
c->AddCheck(wxT("ShowTransportTB"), _("&Transport Toolbar"), FN(OnShowTransportToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
|
||||||
|
c->AddCheck(wxT("ShowScrubbingTB"), _("Scrubbing Toolbar"), FN(OnShowScrubbingToolBar), 0, AlwaysEnabledFlag, AlwaysEnabledFlag);
|
||||||
|
|
||||||
c->AddSeparator();
|
c->AddSeparator();
|
||||||
|
|
||||||
@ -1807,6 +1808,8 @@ void AudacityProject::ModifyToolbarMenus()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mCommandManager.Check(wxT("ShowScrubbingTB"),
|
||||||
|
mToolManager->IsVisible(ScrubbingBarID));
|
||||||
mCommandManager.Check(wxT("ShowDeviceTB"),
|
mCommandManager.Check(wxT("ShowDeviceTB"),
|
||||||
mToolManager->IsVisible(DeviceBarID));
|
mToolManager->IsVisible(DeviceBarID));
|
||||||
mCommandManager.Check(wxT("ShowEditTB"),
|
mCommandManager.Check(wxT("ShowEditTB"),
|
||||||
@ -2292,19 +2295,30 @@ void AudacityProject::OnRecordAppend()
|
|||||||
GetControlToolBar()->OnRecord(evt);
|
GetControlToolBar()->OnRecord(evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The code for "OnPlayStopSelect" is simply the code of "OnPlayStop" and "OnStopSelect" merged.
|
||||||
void AudacityProject::OnPlayStopSelect()
|
void AudacityProject::OnPlayStopSelect()
|
||||||
{
|
{
|
||||||
DoPlayStopSelect(false, false);
|
ControlToolBar *toolbar = GetControlToolBar();
|
||||||
|
wxCommandEvent evt;
|
||||||
|
if (DoPlayStopSelect(false, false))
|
||||||
|
toolbar->OnStop(evt);
|
||||||
|
else if (!gAudioIO->IsBusy()) {
|
||||||
|
//Otherwise, start playing (assuming audio I/O isn't busy)
|
||||||
|
//toolbar->SetPlay(true); // Not needed as set in PlayPlayRegion()
|
||||||
|
toolbar->SetStop(false);
|
||||||
|
|
||||||
|
// Will automatically set mLastPlayMode
|
||||||
|
toolbar->PlayCurrentRegion(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The code for "OnPlayStopSelect" is simply the code of "OnPlayStop" and "OnStopSelect" merged.
|
bool AudacityProject::DoPlayStopSelect(bool click, bool shift)
|
||||||
void AudacityProject::DoPlayStopSelect(bool click, bool shift)
|
|
||||||
{
|
{
|
||||||
wxCommandEvent evt;
|
|
||||||
ControlToolBar *toolbar = GetControlToolBar();
|
ControlToolBar *toolbar = GetControlToolBar();
|
||||||
|
|
||||||
//If busy, stop playing, make sure everything is unpaused.
|
//If busy, stop playing, make sure everything is unpaused.
|
||||||
if (gAudioIO->IsStreamActive(GetAudioIOToken())) {
|
if (GetScrubber().HasStartedScrubbing() ||
|
||||||
|
gAudioIO->IsStreamActive(GetAudioIOToken())) {
|
||||||
toolbar->SetPlay(false); //Pops
|
toolbar->SetPlay(false); //Pops
|
||||||
toolbar->SetStop(true); //Pushes stop down
|
toolbar->SetStop(true); //Pushes stop down
|
||||||
|
|
||||||
@ -2338,16 +2352,9 @@ void AudacityProject::DoPlayStopSelect(bool click, bool shift)
|
|||||||
selection.setT0(time, false);
|
selection.setT0(time, false);
|
||||||
|
|
||||||
ModifyState(false); // without bWantsAutoSave
|
ModifyState(false); // without bWantsAutoSave
|
||||||
toolbar->OnStop(evt);
|
return true;
|
||||||
}
|
|
||||||
else if (!gAudioIO->IsBusy()) {
|
|
||||||
//Otherwise, start playing (assuming audio I/O isn't busy)
|
|
||||||
//toolbar->SetPlay(true); // Not needed as set in PlayPlayRegion()
|
|
||||||
toolbar->SetStop(false);
|
|
||||||
|
|
||||||
// Will automatically set mLastPlayMode
|
|
||||||
toolbar->PlayCurrentRegion(false);
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudacityProject::OnStopSelect()
|
void AudacityProject::OnStopSelect()
|
||||||
@ -2384,7 +2391,6 @@ void AudacityProject::OnTogglePinnedHead()
|
|||||||
auto ruler = GetRulerPanel();
|
auto ruler = GetRulerPanel();
|
||||||
if (ruler)
|
if (ruler)
|
||||||
// Update button image
|
// Update button image
|
||||||
|
|
||||||
ruler->UpdateButtonStates();
|
ruler->UpdateButtonStates();
|
||||||
|
|
||||||
auto &scrubber = GetScrubber();
|
auto &scrubber = GetScrubber();
|
||||||
@ -5439,6 +5445,12 @@ void AudacityProject::OnShowMixerToolBar()
|
|||||||
ModifyToolbarMenus();
|
ModifyToolbarMenus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudacityProject::OnShowScrubbingToolBar()
|
||||||
|
{
|
||||||
|
mToolManager->ShowHide( ScrubbingBarID );
|
||||||
|
ModifyToolbarMenus();
|
||||||
|
}
|
||||||
|
|
||||||
void AudacityProject::OnShowSelectionToolBar()
|
void AudacityProject::OnShowSelectionToolBar()
|
||||||
{
|
{
|
||||||
mToolManager->ShowHide( SelectionBarID );
|
mToolManager->ShowHide( SelectionBarID );
|
||||||
|
@ -80,7 +80,7 @@ void OnSeekRightLong();
|
|||||||
|
|
||||||
bool MakeReadyToPlay(bool loop = false, bool cutpreview = false); // Helper function that sets button states etc.
|
bool MakeReadyToPlay(bool loop = false, bool cutpreview = false); // Helper function that sets button states etc.
|
||||||
void OnPlayStop();
|
void OnPlayStop();
|
||||||
void DoPlayStopSelect(bool click, bool shift);
|
bool DoPlayStopSelect(bool click, bool shift);
|
||||||
void OnPlayStopSelect();
|
void OnPlayStopSelect();
|
||||||
void OnPlayOneSecond();
|
void OnPlayOneSecond();
|
||||||
void OnPlayToSelection();
|
void OnPlayToSelection();
|
||||||
@ -305,6 +305,7 @@ void OnShowSelectionToolBar();
|
|||||||
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
||||||
void OnShowSpectralSelectionToolBar();
|
void OnShowSpectralSelectionToolBar();
|
||||||
#endif
|
#endif
|
||||||
|
void OnShowScrubbingToolBar();
|
||||||
void OnShowToolsToolBar();
|
void OnShowToolsToolBar();
|
||||||
void OnShowTranscriptionToolBar();
|
void OnShowTranscriptionToolBar();
|
||||||
void OnResetToolBars();
|
void OnResetToolBars();
|
||||||
|
@ -890,7 +890,7 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id,
|
|||||||
mRuler = safenew AdornedRulerPanel( this,
|
mRuler = safenew AdornedRulerPanel( this,
|
||||||
wxID_ANY,
|
wxID_ANY,
|
||||||
wxDefaultPosition,
|
wxDefaultPosition,
|
||||||
wxSize( -1, AdornedRulerPanel::GetRulerHeight() ),
|
wxSize( -1, AdornedRulerPanel::GetRulerHeight(false) ),
|
||||||
&mViewInfo );
|
&mViewInfo );
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -4370,6 +4370,14 @@ MixerToolBar *AudacityProject::GetMixerToolBar()
|
|||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScrubbingToolBar *AudacityProject::GetScrubbingToolBar()
|
||||||
|
{
|
||||||
|
return dynamic_cast<ScrubbingToolBar*>
|
||||||
|
(mToolManager ?
|
||||||
|
mToolManager->GetToolBar(ScrubbingBarID) :
|
||||||
|
nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
SelectionBar *AudacityProject::GetSelectionBar()
|
SelectionBar *AudacityProject::GetSelectionBar()
|
||||||
{
|
{
|
||||||
return (SelectionBar *)
|
return (SelectionBar *)
|
||||||
|
@ -75,6 +75,7 @@ class EditToolBar;
|
|||||||
class MeterToolBar;
|
class MeterToolBar;
|
||||||
class MixerToolBar;
|
class MixerToolBar;
|
||||||
class Scrubber;
|
class Scrubber;
|
||||||
|
class ScrubbingToolBar;
|
||||||
class SelectionBar;
|
class SelectionBar;
|
||||||
class SpectralSelectionBar;
|
class SpectralSelectionBar;
|
||||||
class Toolbar;
|
class Toolbar;
|
||||||
@ -442,6 +443,7 @@ class AUDACITY_DLL_API AudacityProject final : public wxFrame,
|
|||||||
DeviceToolBar *GetDeviceToolBar();
|
DeviceToolBar *GetDeviceToolBar();
|
||||||
EditToolBar *GetEditToolBar();
|
EditToolBar *GetEditToolBar();
|
||||||
MixerToolBar *GetMixerToolBar();
|
MixerToolBar *GetMixerToolBar();
|
||||||
|
ScrubbingToolBar *GetScrubbingToolBar();
|
||||||
SelectionBar *GetSelectionBar();
|
SelectionBar *GetSelectionBar();
|
||||||
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
||||||
SpectralSelectionBar *GetSpectralSelectionBar();
|
SpectralSelectionBar *GetSpectralSelectionBar();
|
||||||
|
8583
src/ThemeAsCeeCode.h
8583
src/ThemeAsCeeCode.h
File diff suppressed because it is too large
Load Diff
@ -452,6 +452,10 @@ bool ScreenshotCommand::Apply(CommandExecutionContext context)
|
|||||||
{
|
{
|
||||||
CaptureToolbar(context.GetProject()->GetToolManager(), TranscriptionBarID, fileName);
|
CaptureToolbar(context.GetProject()->GetToolManager(), TranscriptionBarID, fileName);
|
||||||
}
|
}
|
||||||
|
else if (captureMode.IsSameAs(wxT("scrubbing")))
|
||||||
|
{
|
||||||
|
CaptureToolbar(context.GetProject()->GetToolManager(), ScrubbingBarID, fileName);
|
||||||
|
}
|
||||||
else if (captureMode.IsSameAs(wxT("trackpanel")))
|
else if (captureMode.IsSameAs(wxT("trackpanel")))
|
||||||
{
|
{
|
||||||
TrackPanel *panel = context.GetProject()->GetTrackPanel();
|
TrackPanel *panel = context.GetProject()->GetTrackPanel();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
|
|
||||||
Audacity: A Digital Audio Editor
|
Audacity: A Digital Audio Editor
|
||||||
Audacity(R) is copyright (c) 1999-2013 Audacity Team.
|
Audacity(R) is copyright (c) 1999-2016 Audacity Team.
|
||||||
License: GPL v2. See License.txt.
|
License: GPL v2. See License.txt.
|
||||||
|
|
||||||
BassTreble.cpp
|
BassTreble.cpp
|
||||||
@ -12,16 +12,13 @@
|
|||||||
\class EffectBassTreble
|
\class EffectBassTreble
|
||||||
\brief A high shelf and low shelf filter.
|
\brief A high shelf and low shelf filter.
|
||||||
|
|
||||||
The first pass applies the equalization and calculates the
|
|
||||||
peak value. The second pass, if enabled, normalizes to the
|
|
||||||
level set by the level control.
|
|
||||||
|
|
||||||
*//*******************************************************************/
|
*//*******************************************************************/
|
||||||
|
|
||||||
#include "../Audacity.h"
|
#include "../Audacity.h"
|
||||||
#include "BassTreble.h"
|
#include "BassTreble.h"
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <wx/button.h>
|
#include <wx/button.h>
|
||||||
#include <wx/intl.h>
|
#include <wx/intl.h>
|
||||||
@ -37,21 +34,20 @@ enum
|
|||||||
{
|
{
|
||||||
ID_Bass = 10000,
|
ID_Bass = 10000,
|
||||||
ID_Treble,
|
ID_Treble,
|
||||||
ID_Level,
|
ID_Gain,
|
||||||
ID_Normalize,
|
ID_Link
|
||||||
};
|
};
|
||||||
|
|
||||||
// Define keys, defaults, minimums, and maximums for the effect parameters
|
// Define keys, defaults, minimums, and maximums for the effect parameters
|
||||||
//
|
//
|
||||||
// Name Type Key Def Min Max Scale
|
// Name Type Key Def Min Max Scale
|
||||||
Param( Bass, double, XO("Bass"), 0.0, -30.0, 30.0, 1 );
|
Param( Bass, double, XO("Bass"), 0.0, -30.0, 30.0, 1 );
|
||||||
Param( Treble, double, XO("Treble"), 0.0, -30.0, 30.0, 1 );
|
Param( Treble, double, XO("Treble"), 0.0, -30.0, 30.0, 1 );
|
||||||
Param( Level, double, XO("Level"), -1.0, -30.0, 0.0, 1 );
|
Param( Gain, double, XO("Gain"), 0.0, -30.0, 30.0, 1 );
|
||||||
Param( Normalize, bool, XO("Normalize"), true, false, true, 1 );
|
Param( Link, bool, XO("Link Sliders"), false, false, true, 1 );
|
||||||
|
|
||||||
// Sliders are integer, so range is x 10
|
#include <wx/arrimpl.cpp>
|
||||||
// to allow 1 decimal place resolution
|
WX_DEFINE_OBJARRAY(EffectBassTrebleStateArray);
|
||||||
static const int kSliderScale = 10;
|
|
||||||
|
|
||||||
// Used to communicate the type of the filter.
|
// Used to communicate the type of the filter.
|
||||||
enum kShelfType
|
enum kShelfType
|
||||||
@ -61,23 +57,23 @@ enum kShelfType
|
|||||||
};
|
};
|
||||||
|
|
||||||
BEGIN_EVENT_TABLE(EffectBassTreble, wxEvtHandler)
|
BEGIN_EVENT_TABLE(EffectBassTreble, wxEvtHandler)
|
||||||
EVT_SLIDER(ID_Bass, EffectBassTreble::OnBassSlider)
|
EVT_SLIDER(ID_Bass, EffectBassTreble::OnBassSlider)
|
||||||
EVT_SLIDER(ID_Treble, EffectBassTreble::OnTrebleSlider)
|
EVT_SLIDER(ID_Treble, EffectBassTreble::OnTrebleSlider)
|
||||||
EVT_SLIDER(ID_Level, EffectBassTreble::OnLevelSlider)
|
EVT_SLIDER(ID_Gain, EffectBassTreble::OnGainSlider)
|
||||||
EVT_TEXT(ID_Bass, EffectBassTreble::OnBassText)
|
EVT_TEXT(ID_Bass, EffectBassTreble::OnBassText)
|
||||||
EVT_TEXT(ID_Treble, EffectBassTreble::OnTrebleText)
|
EVT_TEXT(ID_Treble, EffectBassTreble::OnTrebleText)
|
||||||
EVT_TEXT(ID_Level, EffectBassTreble::OnLevelText)
|
EVT_TEXT(ID_Gain, EffectBassTreble::OnGainText)
|
||||||
EVT_CHECKBOX(ID_Normalize, EffectBassTreble::OnNormalize)
|
EVT_CHECKBOX(ID_Link, EffectBassTreble::OnLinkCheckbox)
|
||||||
END_EVENT_TABLE()
|
END_EVENT_TABLE()
|
||||||
|
|
||||||
EffectBassTreble::EffectBassTreble()
|
EffectBassTreble::EffectBassTreble()
|
||||||
{
|
{
|
||||||
dB_bass = DEF_Bass;
|
mBass = DEF_Bass;
|
||||||
dB_treble = DEF_Treble;
|
mTreble = DEF_Treble;
|
||||||
dB_level = DEF_Level;
|
mGain = DEF_Gain;
|
||||||
mbNormalize = DEF_Normalize;
|
mLink = DEF_Link;
|
||||||
|
|
||||||
SetLinearEffectFlag(false);
|
SetLinearEffectFlag(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
EffectBassTreble::~EffectBassTreble()
|
EffectBassTreble::~EffectBassTreble()
|
||||||
@ -93,7 +89,7 @@ wxString EffectBassTreble::GetSymbol()
|
|||||||
|
|
||||||
wxString EffectBassTreble::GetDescription()
|
wxString EffectBassTreble::GetDescription()
|
||||||
{
|
{
|
||||||
return XO("Increases or decreases the lower frequencies and higher frequencies of your audio independently");
|
return XO("Simple tone control effect");
|
||||||
}
|
}
|
||||||
|
|
||||||
// EffectIdentInterface implementation
|
// EffectIdentInterface implementation
|
||||||
@ -103,6 +99,16 @@ EffectType EffectBassTreble::GetType()
|
|||||||
return EffectTypeProcess;
|
return EffectTypeProcess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EffectBassTreble::SupportsRealtime()
|
||||||
|
{
|
||||||
|
#if defined(EXPERIMENTAL_REALTIME_AUDACITY_EFFECTS)
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// EffectClientInterface implementation
|
// EffectClientInterface implementation
|
||||||
|
|
||||||
int EffectBassTreble::GetAudioInCount()
|
int EffectBassTreble::GetAudioInCount()
|
||||||
@ -117,61 +123,57 @@ int EffectBassTreble::GetAudioOutCount()
|
|||||||
|
|
||||||
bool EffectBassTreble::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelNames WXUNUSED(chanMap))
|
bool EffectBassTreble::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelNames WXUNUSED(chanMap))
|
||||||
{
|
{
|
||||||
if (GetPass() == 1)
|
InstanceInit(mMaster, mSampleRate);
|
||||||
{
|
|
||||||
const float slope = 0.4f; // same slope for both filters
|
|
||||||
const double hzBass = 250.0f;
|
|
||||||
const double hzTreble = 4000.0f;
|
|
||||||
|
|
||||||
//(re)initialise filter parameters
|
|
||||||
xn1Bass=xn2Bass=yn1Bass=yn2Bass=0;
|
|
||||||
xn1Treble=xn2Treble=yn1Treble=yn2Treble=0;
|
|
||||||
|
|
||||||
// Compute coefficents of the low shelf biquand IIR filter
|
|
||||||
Coefficents(hzBass, slope, dB_bass, kBass,
|
|
||||||
a0Bass, a1Bass, a2Bass,
|
|
||||||
b0Bass, b1Bass, b2Bass);
|
|
||||||
|
|
||||||
// Compute coefficents of the high shelf biquand IIR filter
|
|
||||||
Coefficents(hzTreble, slope, dB_treble, kTreble,
|
|
||||||
a0Treble, a1Treble, a2Treble,
|
|
||||||
b0Treble, b1Treble, b2Treble);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
sampleCount EffectBassTreble::ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen)
|
sampleCount EffectBassTreble::ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen)
|
||||||
{
|
{
|
||||||
float *ibuf = inBlock[0];
|
return InstanceProcess(mMaster, inBlock, outBlock, blockLen);
|
||||||
float *obuf = outBlock[0];
|
}
|
||||||
|
|
||||||
if (GetPass() == 1)
|
bool EffectBassTreble::RealtimeInitialize()
|
||||||
{
|
{
|
||||||
for (sampleCount i = 0; i < blockLen; i++)
|
SetBlockSize(512);
|
||||||
{
|
|
||||||
obuf[i] = DoFilter(ibuf[i]) / mPreGain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
float gain = DB_TO_LINEAR(dB_level) / mMax;
|
|
||||||
for (sampleCount i = 0; i < blockLen; i++)
|
|
||||||
{
|
|
||||||
// Normalize to specified level
|
|
||||||
obuf[i] = ibuf[i] * (mPreGain * gain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return blockLen;
|
mSlaves.Clear();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EffectBassTreble::RealtimeAddProcessor(int WXUNUSED(numChannels), float sampleRate)
|
||||||
|
{
|
||||||
|
EffectBassTrebleState slave;
|
||||||
|
|
||||||
|
InstanceInit(slave, sampleRate);
|
||||||
|
|
||||||
|
mSlaves.Add(slave);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EffectBassTreble::RealtimeFinalize()
|
||||||
|
{
|
||||||
|
mSlaves.Clear();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
sampleCount EffectBassTreble::RealtimeProcess(int group,
|
||||||
|
float **inbuf,
|
||||||
|
float **outbuf,
|
||||||
|
sampleCount numSamples)
|
||||||
|
{
|
||||||
|
return InstanceProcess(mSlaves[group], inbuf, outbuf, numSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EffectBassTreble::GetAutomationParameters(EffectAutomationParameters & parms)
|
bool EffectBassTreble::GetAutomationParameters(EffectAutomationParameters & parms)
|
||||||
{
|
{
|
||||||
parms.Write(KEY_Bass, dB_bass);
|
parms.Write(KEY_Bass, mBass);
|
||||||
parms.Write(KEY_Treble, dB_treble);
|
parms.Write(KEY_Treble, mTreble);
|
||||||
parms.Write(KEY_Level, dB_level);
|
parms.Write(KEY_Gain, mGain);
|
||||||
parms.Write(KEY_Normalize, mbNormalize);
|
parms.Write(KEY_Link, mLink);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -180,144 +182,88 @@ bool EffectBassTreble::SetAutomationParameters(EffectAutomationParameters & parm
|
|||||||
{
|
{
|
||||||
ReadAndVerifyDouble(Bass);
|
ReadAndVerifyDouble(Bass);
|
||||||
ReadAndVerifyDouble(Treble);
|
ReadAndVerifyDouble(Treble);
|
||||||
ReadAndVerifyDouble(Level);
|
ReadAndVerifyDouble(Gain);
|
||||||
ReadAndVerifyBool(Normalize);
|
ReadAndVerifyBool(Link);
|
||||||
|
|
||||||
dB_bass = Bass;
|
mBass = Bass;
|
||||||
dB_treble = Treble;
|
mTreble = Treble;
|
||||||
dB_level = Level;
|
mGain = Gain;
|
||||||
mbNormalize = Normalize;
|
mLink = Link;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EffectBassTreble::CheckWhetherSkipEffect()
|
||||||
|
{
|
||||||
|
return (mBass == 0.0 && mTreble == 0.0 && mGain == 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Effect implementation
|
// Effect implementation
|
||||||
|
|
||||||
bool EffectBassTreble::Startup()
|
|
||||||
{
|
|
||||||
wxString base = wxT("/Effects/BassTreble/");
|
|
||||||
|
|
||||||
// Migrate settings from 2.1.0 or before
|
|
||||||
|
|
||||||
// Already migrated, so bail
|
|
||||||
if (gPrefs->Exists(base + wxT("Migrated")))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the old "current" settings
|
|
||||||
if (gPrefs->Exists(base))
|
|
||||||
{
|
|
||||||
int readBool;
|
|
||||||
gPrefs->Read(base + wxT("Bass"), &dB_bass, 0.0);
|
|
||||||
gPrefs->Read(base + wxT("Treble"), &dB_treble, 0.0);
|
|
||||||
gPrefs->Read(base + wxT("Level"), &dB_level, -1.0);
|
|
||||||
gPrefs->Read(base + wxT("Normalize"), &readBool, 1 );
|
|
||||||
|
|
||||||
// Validate data
|
|
||||||
dB_level = (dB_level > 0) ? 0 : dB_level;
|
|
||||||
mbNormalize = (readBool != 0);
|
|
||||||
|
|
||||||
SaveUserPreset(GetCurrentSettingsGroup());
|
|
||||||
|
|
||||||
// Do not migrate again
|
|
||||||
gPrefs->Write(base + wxT("Migrated"), true);
|
|
||||||
gPrefs->Flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EffectBassTreble::InitPass1()
|
|
||||||
{
|
|
||||||
mMax = 0.0;
|
|
||||||
|
|
||||||
// Integer format tracks require headroom to avoid clipping
|
|
||||||
// when saved between passes (bug 619)
|
|
||||||
|
|
||||||
if (mbNormalize) // don't need to calculate this if only doing one pass.
|
|
||||||
{
|
|
||||||
// Up to (gain + 6dB) headroom required for treble boost (experimental).
|
|
||||||
mPreGain = (dB_treble > 0) ? (dB_treble + 6.0) : 0.0;
|
|
||||||
if (dB_bass >= 0)
|
|
||||||
{
|
|
||||||
mPreGain = (mPreGain > dB_bass) ? mPreGain : dB_bass;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Up to 6 dB headroom reaquired for bass cut (experimental)
|
|
||||||
mPreGain = (mPreGain > 6.0) ? mPreGain : 6.0;
|
|
||||||
}
|
|
||||||
mPreGain = (exp(log(10.0) * mPreGain / 20)); // to linear
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mPreGain = 1.0; // Unity gain
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EffectBassTreble::InitPass2()
|
|
||||||
{
|
|
||||||
return mbNormalize && mMax != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EffectBassTreble::PopulateOrExchange(ShuttleGui & S)
|
void EffectBassTreble::PopulateOrExchange(ShuttleGui & S)
|
||||||
{
|
{
|
||||||
S.StartVerticalLay(0);
|
S.SetBorder(5);
|
||||||
|
S.AddSpace(0, 5);
|
||||||
|
|
||||||
|
S.StartStatic(_("Tone controls"));
|
||||||
{
|
{
|
||||||
S.StartMultiColumn(3, wxEXPAND);
|
S.StartMultiColumn(3, wxEXPAND);
|
||||||
S.SetStretchyCol(2);
|
|
||||||
{
|
{
|
||||||
|
S.SetStretchyCol(2);
|
||||||
|
|
||||||
// Bass control
|
// Bass control
|
||||||
FloatingPointValidator<double> vldBass(1, &dB_bass);
|
FloatingPointValidator<double> vldBass(1, &mBass);
|
||||||
vldBass.SetRange(MIN_Bass, MAX_Bass);
|
vldBass.SetRange(MIN_Bass, MAX_Bass);
|
||||||
mBassT = S.Id(ID_Bass).AddTextBox(_("&Bass (dB):"), wxT(""), 10);
|
mBassT = S.Id(ID_Bass).AddTextBox(_("&Bass (dB):"), wxT(""), 10);
|
||||||
mBassT->SetName(_("Bass (dB):"));
|
mBassT->SetName(_("Bass (dB):"));
|
||||||
mBassT->SetValidator(vldBass);
|
mBassT->SetValidator(vldBass);
|
||||||
|
|
||||||
S.SetStyle(wxSL_HORIZONTAL);
|
S.SetStyle(wxSL_HORIZONTAL);
|
||||||
mBassS = S.Id(ID_Bass).AddSlider(wxT(""), 0, MAX_Bass * kSliderScale, MIN_Bass * kSliderScale);
|
mBassS = S.Id(ID_Bass).AddSlider(wxT(""), 0, MAX_Bass * SCL_Bass, MIN_Bass * SCL_Bass);
|
||||||
mBassS->SetName(_("Bass"));
|
mBassS->SetName(_("Bass"));
|
||||||
mBassS->SetPageSize(30);
|
|
||||||
|
|
||||||
// Treble control
|
// Treble control
|
||||||
FloatingPointValidator<double> vldTreble(1, &dB_treble);
|
FloatingPointValidator<double> vldTreble(1, &mTreble);
|
||||||
vldTreble.SetRange(MIN_Treble, MAX_Treble);
|
vldTreble.SetRange(MIN_Treble, MAX_Treble);
|
||||||
mTrebleT = S.Id(ID_Treble).AddTextBox(_("&Treble (dB):"), wxT(""), 10);
|
mTrebleT = S.Id(ID_Treble).AddTextBox(_("&Treble (dB):"), wxT(""), 10);
|
||||||
mTrebleT->SetValidator(vldTreble);
|
mTrebleT->SetValidator(vldTreble);
|
||||||
|
|
||||||
S.SetStyle(wxSL_HORIZONTAL);
|
S.SetStyle(wxSL_HORIZONTAL);
|
||||||
mTrebleS = S.Id(ID_Treble).AddSlider(wxT(""), 0, MAX_Treble * kSliderScale, MIN_Treble * kSliderScale);
|
mTrebleS = S.Id(ID_Treble).AddSlider(wxT(""), 0, MAX_Treble * SCL_Treble, MIN_Treble * SCL_Treble);
|
||||||
mTrebleS->SetName(_("Treble"));
|
mTrebleS->SetName(_("Treble"));
|
||||||
mTrebleS->SetPageSize(30);
|
}
|
||||||
|
S.EndMultiColumn();
|
||||||
|
}
|
||||||
|
S.EndStatic();
|
||||||
|
|
||||||
// Level control
|
S.StartStatic("Output");
|
||||||
FloatingPointValidator<double> vldLevel(1, &dB_level);
|
{
|
||||||
vldLevel.SetRange(MIN_Level, MAX_Level);
|
S.StartMultiColumn(3, wxEXPAND);
|
||||||
mLevelT = S.Id(ID_Level).AddTextBox(_("&Level (dB):"), wxT(""), 10);
|
{
|
||||||
mLevelT->SetValidator(vldLevel);
|
S.SetStretchyCol(2);
|
||||||
|
|
||||||
|
// Gain control
|
||||||
|
FloatingPointValidator<double> vldGain(1, &mGain);
|
||||||
|
vldGain.SetRange(MIN_Gain, MAX_Gain);
|
||||||
|
mGainT = S.Id(ID_Gain).AddTextBox(_("&Volume (dB):"), wxT(""), 10);
|
||||||
|
mGainT->SetValidator(vldGain);
|
||||||
|
|
||||||
S.SetStyle(wxSL_HORIZONTAL);
|
S.SetStyle(wxSL_HORIZONTAL);
|
||||||
mLevelS = S.Id(ID_Level).AddSlider(wxT(""), 0, MAX_Level * kSliderScale, MIN_Level * kSliderScale);
|
mGainS = S.Id(ID_Gain).AddSlider(wxT(""), 0, MAX_Gain * SCL_Gain, MIN_Gain * SCL_Gain);
|
||||||
mLevelS->SetName(_("Level"));
|
mGainS->SetName(_("Level"));
|
||||||
mLevelS->SetPageSize(30);
|
|
||||||
}
|
}
|
||||||
S.EndMultiColumn();
|
S.EndMultiColumn();
|
||||||
|
|
||||||
// Normalize checkbox
|
S.StartMultiColumn(2, wxCENTER);
|
||||||
S.StartHorizontalLay(wxLEFT, true);
|
|
||||||
{
|
{
|
||||||
mNormalizeCheckBox = S.Id(ID_Normalize).AddCheckBox(_("&Enable level control"),
|
// Link checkbox
|
||||||
DEF_Normalize ? wxT("true") : wxT("false"));
|
mLinkCheckBox = S.Id(ID_Link).AddCheckBox(_("Link Volume control to Tone controls"),
|
||||||
mWarning = S.AddVariableText(wxT(""), false, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
|
DEF_Link ? wxT("true") : wxT("false"));
|
||||||
}
|
}
|
||||||
S.EndHorizontalLay();
|
S.EndMultiColumn();
|
||||||
}
|
}
|
||||||
S.EndVerticalLay();
|
S.EndStatic();
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EffectBassTreble::TransferDataToWindow()
|
bool EffectBassTreble::TransferDataToWindow()
|
||||||
@ -327,12 +273,10 @@ bool EffectBassTreble::TransferDataToWindow()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mBassS->SetValue((int) dB_bass * kSliderScale + 0.5);
|
mBassS->SetValue((int) (mBass * SCL_Bass));
|
||||||
mTrebleS->SetValue((int) dB_treble * kSliderScale + 0.5);
|
mTrebleS->SetValue((int) mTreble *SCL_Treble);
|
||||||
mLevelS->SetValue((int) dB_level * kSliderScale + 0.5);
|
mGainS->SetValue((int) mGain * SCL_Gain);
|
||||||
mNormalizeCheckBox->SetValue(mbNormalize);
|
mLinkCheckBox->SetValue(mLink);
|
||||||
|
|
||||||
UpdateUI();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -344,18 +288,96 @@ bool EffectBassTreble::TransferDataFromWindow()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mbNormalize = mNormalizeCheckBox->GetValue();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// EffectBassTreble implementation
|
// EffectBassTreble implementation
|
||||||
|
|
||||||
void EffectBassTreble::Coefficents(double hz, float slope, double gain, int type,
|
void EffectBassTreble::InstanceInit(EffectBassTrebleState & data, float sampleRate)
|
||||||
float& a0, float& a1, float& a2,
|
|
||||||
float& b0, float& b1, float& b2)
|
|
||||||
{
|
{
|
||||||
double w = 2 * M_PI * hz / mSampleRate;
|
data.samplerate = sampleRate;
|
||||||
|
data.slope = 0.4f; // same slope for both filters
|
||||||
|
data.hzBass = 250.0f; // could be tunable in a more advanced version
|
||||||
|
data.hzTreble = 4000.0f; // could be tunable in a more advanced version
|
||||||
|
|
||||||
|
data.a0Bass = 1;
|
||||||
|
data.a1Bass = 0;
|
||||||
|
data.a2Bass = 0;
|
||||||
|
data.b0Bass = 0;
|
||||||
|
data.b1Bass = 0;
|
||||||
|
data.b2Bass = 0;
|
||||||
|
|
||||||
|
data.a0Treble = 1;
|
||||||
|
data.a1Treble = 0;
|
||||||
|
data.a2Treble = 0;
|
||||||
|
data.b0Treble = 0;
|
||||||
|
data.b1Treble = 0;
|
||||||
|
data.b2Treble = 0;
|
||||||
|
|
||||||
|
data.xn1Bass = 0;
|
||||||
|
data.xn2Bass = 0;
|
||||||
|
data.yn1Bass = 0;
|
||||||
|
data.yn2Bass = 0;
|
||||||
|
|
||||||
|
data.xn1Treble = 0;
|
||||||
|
data.xn2Treble = 0;
|
||||||
|
data.yn1Treble = 0;
|
||||||
|
data.yn2Treble = 0;
|
||||||
|
|
||||||
|
data.bass = -1;
|
||||||
|
data.treble = -1;
|
||||||
|
data.gain = DB_TO_LINEAR(mGain);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// EffectClientInterface implementation
|
||||||
|
|
||||||
|
|
||||||
|
sampleCount EffectBassTreble::InstanceProcess(EffectBassTrebleState & data,
|
||||||
|
float **inBlock,
|
||||||
|
float **outBlock,
|
||||||
|
sampleCount blockLen)
|
||||||
|
{
|
||||||
|
float *ibuf = inBlock[0];
|
||||||
|
float *obuf = outBlock[0];
|
||||||
|
|
||||||
|
// Set value to ensure correct rounding
|
||||||
|
double oldBass = DB_TO_LINEAR(mBass);
|
||||||
|
double oldTreble = DB_TO_LINEAR(mTreble);
|
||||||
|
|
||||||
|
data.gain = DB_TO_LINEAR(mGain);
|
||||||
|
|
||||||
|
// Compute coefficents of the low shelf biquand IIR filter
|
||||||
|
if (data.bass != oldBass)
|
||||||
|
Coefficents(data.hzBass, data.slope, mBass, data.samplerate, kBass,
|
||||||
|
data.a0Bass, data.a1Bass, data.a2Bass,
|
||||||
|
data.b0Bass, data.b1Bass, data.b2Bass);
|
||||||
|
|
||||||
|
// Compute coefficents of the high shelf biquand IIR filter
|
||||||
|
if (data.treble != oldTreble)
|
||||||
|
Coefficents(data.hzTreble, data.slope, mTreble, data.samplerate, kTreble,
|
||||||
|
data.a0Treble, data.a1Treble, data.a2Treble,
|
||||||
|
data.b0Treble, data.b1Treble, data.b2Treble);
|
||||||
|
|
||||||
|
for (sampleCount i = 0; i < blockLen; i++) {
|
||||||
|
obuf[i] = DoFilter(data, ibuf[i]) * data.gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
return blockLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Effect implementation
|
||||||
|
|
||||||
|
|
||||||
|
void EffectBassTreble::Coefficents(double hz, double slope, double gain, double samplerate, int type,
|
||||||
|
double& a0, double& a1, double& a2,
|
||||||
|
double& b0, double& b1, double& b2)
|
||||||
|
{
|
||||||
|
double w = 2 * M_PI * hz / samplerate;
|
||||||
double a = exp(log(10.0) * gain / 40);
|
double a = exp(log(10.0) * gain / 40);
|
||||||
double b = sqrt((a * a + 1) / slope - (pow((a - 1), 2)));
|
double b = sqrt((a * a + 1) / slope - (pow((a - 1), 2)));
|
||||||
|
|
||||||
@ -379,109 +401,112 @@ void EffectBassTreble::Coefficents(double hz, float slope, double gain, int type
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float EffectBassTreble::DoFilter(float in)
|
float EffectBassTreble::DoFilter(EffectBassTrebleState & data, float in)
|
||||||
{
|
{
|
||||||
// Bass filter
|
// Bass filter
|
||||||
float out = (b0Bass * in + b1Bass * xn1Bass + b2Bass * xn2Bass -
|
float out = (data.b0Bass * in + data.b1Bass * data.xn1Bass + data.b2Bass * data.xn2Bass -
|
||||||
a1Bass * yn1Bass - a2Bass * yn2Bass) / a0Bass;
|
data.a1Bass * data.yn1Bass - data.a2Bass * data.yn2Bass) / data.a0Bass;
|
||||||
xn2Bass = xn1Bass;
|
data.xn2Bass = data.xn1Bass;
|
||||||
xn1Bass = in;
|
data.xn1Bass = in;
|
||||||
yn2Bass = yn1Bass;
|
data.yn2Bass = data.yn1Bass;
|
||||||
yn1Bass = out;
|
data.yn1Bass = out;
|
||||||
|
|
||||||
// Treble filter
|
// Treble filter
|
||||||
in = out;
|
in = out;
|
||||||
out = (b0Treble * in + b1Treble * xn1Treble + b2Treble * xn2Treble -
|
out = (data.b0Treble * in + data.b1Treble * data.xn1Treble + data.b2Treble * data.xn2Treble -
|
||||||
a1Treble * yn1Treble - a2Treble * yn2Treble) / a0Treble;
|
data.a1Treble * data.yn1Treble - data.a2Treble * data.yn2Treble) / data.a0Treble;
|
||||||
xn2Treble = xn1Treble;
|
data.xn2Treble = data.xn1Treble;
|
||||||
xn1Treble = in;
|
data.xn1Treble = in;
|
||||||
yn2Treble = yn1Treble;
|
data.yn2Treble = data.yn1Treble;
|
||||||
yn1Treble = out;
|
data.yn1Treble = out;
|
||||||
|
|
||||||
// Retain the maximum value for use in the normalization pass
|
|
||||||
if(mMax < fabs(out))
|
|
||||||
mMax = fabs(out);
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectBassTreble::UpdateUI()
|
|
||||||
{
|
|
||||||
double bass, treble, level;
|
|
||||||
mBassT->GetValue().ToDouble(&bass);
|
|
||||||
mTrebleT->GetValue().ToDouble(&treble);
|
|
||||||
mLevelT->GetValue().ToDouble(&level);
|
|
||||||
bool enable = mNormalizeCheckBox->GetValue();
|
|
||||||
|
|
||||||
// Disallow level control if disabled
|
|
||||||
mLevelT->Enable(enable);
|
|
||||||
mLevelS->Enable(enable);
|
|
||||||
|
|
||||||
if (bass == 0 && treble == 0 && !enable)
|
|
||||||
{
|
|
||||||
// Disallow Apply if nothing to do
|
|
||||||
EnableApply(false);
|
|
||||||
mWarning->SetLabel(_(" No change to apply."));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (level > 0 && enable)
|
|
||||||
{
|
|
||||||
// Disallow Apply if level enabled and > 0
|
|
||||||
EnableApply(false);
|
|
||||||
mWarning->SetLabel(_(": Maximum 0 dB."));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Apply enabled
|
|
||||||
EnableApply(true);
|
|
||||||
mWarning->SetLabel(wxT(""));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EffectBassTreble::OnBassText(wxCommandEvent & WXUNUSED(evt))
|
void EffectBassTreble::OnBassText(wxCommandEvent & WXUNUSED(evt))
|
||||||
{
|
{
|
||||||
mBassT->GetValidator()->TransferFromWindow();
|
double oldBass = mBass;
|
||||||
mBassS->SetValue((int) floor(dB_bass * kSliderScale + 0.5));
|
|
||||||
UpdateUI();
|
if (!EnableApply(mUIParent->TransferDataFromWindow()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mLink) UpdateGain(oldBass, kBass);
|
||||||
|
mBassS->SetValue((int) (mBass * SCL_Bass));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectBassTreble::OnTrebleText(wxCommandEvent & WXUNUSED(evt))
|
void EffectBassTreble::OnTrebleText(wxCommandEvent & WXUNUSED(evt))
|
||||||
{
|
{
|
||||||
mTrebleT->GetValidator()->TransferFromWindow();
|
double oldTreble = mTreble;
|
||||||
mTrebleS->SetValue((int) floor(dB_treble * kSliderScale + 0.5));
|
|
||||||
UpdateUI();
|
if (!EnableApply(mUIParent->TransferDataFromWindow()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mLink) UpdateGain(oldTreble, kTreble);
|
||||||
|
mTrebleS->SetValue((int) (mTreble * SCL_Treble));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectBassTreble::OnLevelText(wxCommandEvent & WXUNUSED(evt))
|
void EffectBassTreble::OnGainText(wxCommandEvent & WXUNUSED(evt))
|
||||||
{
|
{
|
||||||
mLevelT->GetValidator()->TransferFromWindow();
|
if (!EnableApply(mUIParent->TransferDataFromWindow()))
|
||||||
mLevelS->SetValue((int) floor(dB_level * kSliderScale + 0.5));
|
{
|
||||||
UpdateUI();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mGainS->SetValue((int) (mGain * SCL_Gain));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectBassTreble::OnBassSlider(wxCommandEvent & evt)
|
void EffectBassTreble::OnBassSlider(wxCommandEvent & evt)
|
||||||
{
|
{
|
||||||
dB_bass = (double) evt.GetInt() / kSliderScale;
|
double oldBass = mBass;
|
||||||
|
mBass = (double) evt.GetInt() / SCL_Bass;
|
||||||
mBassT->GetValidator()->TransferToWindow();
|
mBassT->GetValidator()->TransferToWindow();
|
||||||
UpdateUI();
|
|
||||||
|
if (mLink) UpdateGain(oldBass, kBass);
|
||||||
|
EnableApply(mUIParent->Validate());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectBassTreble::OnTrebleSlider(wxCommandEvent & evt)
|
void EffectBassTreble::OnTrebleSlider(wxCommandEvent & evt)
|
||||||
{
|
{
|
||||||
dB_treble = (double) evt.GetInt() / kSliderScale;
|
double oldTreble = mTreble;
|
||||||
|
mTreble = (double) evt.GetInt() / SCL_Treble;
|
||||||
mTrebleT->GetValidator()->TransferToWindow();
|
mTrebleT->GetValidator()->TransferToWindow();
|
||||||
UpdateUI();
|
|
||||||
|
if (mLink) UpdateGain(oldTreble, kTreble);
|
||||||
|
EnableApply(mUIParent->Validate());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectBassTreble::OnLevelSlider(wxCommandEvent & evt)
|
void EffectBassTreble::OnGainSlider(wxCommandEvent & evt)
|
||||||
{
|
{
|
||||||
dB_level = (double) evt.GetInt() / kSliderScale;
|
mGain = (double) evt.GetInt() / SCL_Gain;
|
||||||
mLevelT->GetValidator()->TransferToWindow();
|
mGainT->GetValidator()->TransferToWindow();
|
||||||
UpdateUI();
|
|
||||||
|
EnableApply(mUIParent->Validate());
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectBassTreble::OnNormalize(wxCommandEvent& WXUNUSED(evt))
|
void EffectBassTreble::OnLinkCheckbox(wxCommandEvent& evt)
|
||||||
{
|
{
|
||||||
UpdateUI();
|
mLink = mLinkCheckBox->GetValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectBassTreble::UpdateGain(double oldVal, int control)
|
||||||
|
{
|
||||||
|
double newVal;
|
||||||
|
oldVal = (oldVal > 0)? oldVal / 2.0 : oldVal / 4.0;
|
||||||
|
|
||||||
|
if (control == kBass)
|
||||||
|
newVal = (mBass > 0)? mBass / 2.0 : mBass / 4.0;
|
||||||
|
else
|
||||||
|
newVal = (mTreble > 0)? mTreble / 2.0 : mTreble / 4.0;
|
||||||
|
|
||||||
|
mGain -= newVal - oldVal;
|
||||||
|
mGain = std::min(MAX_Gain, std::max(MIN_Gain, mGain));
|
||||||
|
|
||||||
|
mGainS->SetValue(mGain);
|
||||||
|
mGainT->GetValidator()->TransferToWindow();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
|
|
||||||
Audacity: A Digital Audio Editor
|
Audacity: A Digital Audio Editor
|
||||||
Audacity(R) is copyright (c) 1999-2013 Audacity Team.
|
Audacity(R) is copyright (c) 1999-2016 Audacity Team.
|
||||||
License: GPL v2. See License.txt.
|
License: GPL v2. See License.txt.
|
||||||
|
|
||||||
BassTreble.h (two shelf filters)
|
BassTreble.h (two shelf filters)
|
||||||
@ -12,12 +12,12 @@
|
|||||||
#ifndef __AUDACITY_EFFECT_BASS_TREBLE__
|
#ifndef __AUDACITY_EFFECT_BASS_TREBLE__
|
||||||
#define __AUDACITY_EFFECT_BASS_TREBLE__
|
#define __AUDACITY_EFFECT_BASS_TREBLE__
|
||||||
|
|
||||||
#include <wx/checkbox.h>
|
|
||||||
#include <wx/event.h>
|
#include <wx/event.h>
|
||||||
#include <wx/slider.h>
|
#include <wx/slider.h>
|
||||||
#include <wx/stattext.h>
|
#include <wx/stattext.h>
|
||||||
#include <wx/string.h>
|
#include <wx/string.h>
|
||||||
#include <wx/textctrl.h>
|
#include <wx/textctrl.h>
|
||||||
|
#include <wx/checkbox.h>
|
||||||
|
|
||||||
#include "Effect.h"
|
#include "Effect.h"
|
||||||
|
|
||||||
@ -25,7 +25,23 @@ class ShuttleGui;
|
|||||||
|
|
||||||
#define BASSTREBLE_PLUGIN_SYMBOL XO("Bass and Treble")
|
#define BASSTREBLE_PLUGIN_SYMBOL XO("Bass and Treble")
|
||||||
|
|
||||||
class EffectBassTreble final : public Effect
|
class EffectBassTrebleState
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
float samplerate;
|
||||||
|
double treble;
|
||||||
|
double bass;
|
||||||
|
double gain;
|
||||||
|
double slope, hzBass, hzTreble;
|
||||||
|
double a0Bass, a1Bass, a2Bass, b0Bass, b1Bass, b2Bass;
|
||||||
|
double a0Treble, a1Treble, a2Treble, b0Treble, b1Treble, b2Treble;
|
||||||
|
double xn1Bass, xn2Bass, yn1Bass, yn2Bass;
|
||||||
|
double xn1Treble, xn2Treble, yn1Treble, yn2Treble;
|
||||||
|
};
|
||||||
|
|
||||||
|
WX_DECLARE_OBJARRAY(EffectBassTrebleState, EffectBassTrebleStateArray);
|
||||||
|
|
||||||
|
class EffectBassTreble : public Effect
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
EffectBassTreble();
|
EffectBassTreble();
|
||||||
@ -39,6 +55,7 @@ public:
|
|||||||
// EffectIdentInterface implementation
|
// EffectIdentInterface implementation
|
||||||
|
|
||||||
EffectType GetType() override;
|
EffectType GetType() override;
|
||||||
|
bool SupportsRealtime() override;
|
||||||
|
|
||||||
// EffectClientInterface implementation
|
// EffectClientInterface implementation
|
||||||
|
|
||||||
@ -46,57 +63,64 @@ public:
|
|||||||
int GetAudioOutCount() override;
|
int GetAudioOutCount() override;
|
||||||
bool ProcessInitialize(sampleCount totalLen, ChannelNames chanMap = NULL) override;
|
bool ProcessInitialize(sampleCount totalLen, ChannelNames chanMap = NULL) override;
|
||||||
sampleCount ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen) override;
|
sampleCount ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen) override;
|
||||||
|
bool RealtimeInitialize() override;
|
||||||
|
bool RealtimeAddProcessor(int numChannels, float sampleRate) override;
|
||||||
|
bool RealtimeFinalize() override;
|
||||||
|
sampleCount RealtimeProcess(int group,
|
||||||
|
float **inbuf,
|
||||||
|
float **outbuf,
|
||||||
|
sampleCount numSamples) override;
|
||||||
bool GetAutomationParameters(EffectAutomationParameters & parms) override;
|
bool GetAutomationParameters(EffectAutomationParameters & parms) override;
|
||||||
bool SetAutomationParameters(EffectAutomationParameters & parms) override;
|
bool SetAutomationParameters(EffectAutomationParameters & parms) override;
|
||||||
|
|
||||||
// Effect Implementation
|
|
||||||
|
|
||||||
bool Startup() override;
|
// Effect Implementation
|
||||||
bool InitPass1() override;
|
|
||||||
bool InitPass2() override;
|
|
||||||
|
|
||||||
void PopulateOrExchange(ShuttleGui & S) override;
|
void PopulateOrExchange(ShuttleGui & S) override;
|
||||||
bool TransferDataToWindow() override;
|
bool TransferDataToWindow() override;
|
||||||
bool TransferDataFromWindow() override;
|
bool TransferDataFromWindow() override;
|
||||||
|
|
||||||
|
bool CheckWhetherSkipEffect() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// EffectBassTreble implementation
|
// EffectBassTreble implementation
|
||||||
|
|
||||||
void Coefficents(double hz, float slope, double gain, int type,
|
void InstanceInit(EffectBassTrebleState & data, float sampleRate);
|
||||||
float& a0, float& a1, float& a2, float& b0, float& b1, float& b2);
|
sampleCount InstanceProcess(EffectBassTrebleState & data, float **inBlock, float **outBlock, sampleCount blockLen);
|
||||||
float DoFilter(float in);
|
|
||||||
void UpdateUI();
|
void Coefficents(double hz, double slope, double gain, double samplerate, int type,
|
||||||
|
double& a0, double& a1, double& a2, double& b0, double& b1, double& b2);
|
||||||
|
float DoFilter(EffectBassTrebleState & data, float in);
|
||||||
|
|
||||||
void OnBassText(wxCommandEvent & evt);
|
void OnBassText(wxCommandEvent & evt);
|
||||||
void OnTrebleText(wxCommandEvent & evt);
|
void OnTrebleText(wxCommandEvent & evt);
|
||||||
void OnLevelText(wxCommandEvent & evt);
|
void OnGainText(wxCommandEvent & evt);
|
||||||
void OnBassSlider(wxCommandEvent & evt);
|
void OnBassSlider(wxCommandEvent & evt);
|
||||||
void OnTrebleSlider(wxCommandEvent & evt);
|
void OnTrebleSlider(wxCommandEvent & evt);
|
||||||
void OnLevelSlider(wxCommandEvent & evt);
|
void OnGainSlider(wxCommandEvent & evt);
|
||||||
void OnNormalize(wxCommandEvent & evt);
|
void OnLinkCheckbox(wxCommandEvent & evt);
|
||||||
|
|
||||||
|
// Auto-adjust gain to reduce variation in peak level
|
||||||
|
void UpdateGain(double oldVal, int control );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float xn1Bass, xn2Bass, yn1Bass, yn2Bass,
|
EffectBassTrebleState mMaster;
|
||||||
wBass, swBass, cwBass, aBass, bBass,
|
EffectBassTrebleStateArray mSlaves;
|
||||||
a0Bass, a1Bass, a2Bass, b0Bass, b1Bass, b2Bass;
|
|
||||||
// High shelf
|
|
||||||
float xn1Treble, xn2Treble, yn1Treble, yn2Treble,
|
|
||||||
wTreble, swTreble, cwTreble, aTreble, bTreble,
|
|
||||||
b0Treble, b1Treble, b2Treble, a0Treble, a1Treble, a2Treble;
|
|
||||||
|
|
||||||
double dB_bass, dB_treble, dB_level;
|
double mBass;
|
||||||
double mMax;
|
double mTreble;
|
||||||
bool mbNormalize;
|
double mGain;
|
||||||
double mPreGain;
|
bool mLink;
|
||||||
|
|
||||||
wxSlider *mBassS;
|
wxSlider *mBassS;
|
||||||
wxSlider *mTrebleS;
|
wxSlider *mTrebleS;
|
||||||
wxSlider *mLevelS;
|
wxSlider *mGainS;
|
||||||
wxTextCtrl *mBassT;
|
|
||||||
wxTextCtrl *mTrebleT;
|
wxTextCtrl *mBassT;
|
||||||
wxTextCtrl *mLevelT;
|
wxTextCtrl *mTrebleT;
|
||||||
wxCheckBox *mNormalizeCheckBox;
|
wxTextCtrl *mGainT;
|
||||||
wxStaticText *mWarning;
|
|
||||||
|
wxCheckBox *mLinkCheckBox;
|
||||||
|
|
||||||
DECLARE_EVENT_TABLE();
|
DECLARE_EVENT_TABLE();
|
||||||
};
|
};
|
||||||
|
@ -168,6 +168,8 @@ void ControlToolBar::Populate()
|
|||||||
bmpCutPreview, bmpCutPreview, bmpCutPreviewDisabled);
|
bmpCutPreview, bmpCutPreview, bmpCutPreviewDisabled);
|
||||||
MakeAlternateImages(*mPlay, 3,
|
MakeAlternateImages(*mPlay, 3,
|
||||||
bmpScrub, bmpScrub, bmpScrubDisabled);
|
bmpScrub, bmpScrub, bmpScrubDisabled);
|
||||||
|
MakeAlternateImages(*mPlay, 4,
|
||||||
|
bmpSeek, bmpSeek, bmpSeekDisabled);
|
||||||
mPlay->FollowModifierKeys();
|
mPlay->FollowModifierKeys();
|
||||||
|
|
||||||
mStop = MakeButton( bmpStop, bmpStop, bmpStopDisabled ,
|
mStop = MakeButton( bmpStop, bmpStop, bmpStopDisabled ,
|
||||||
|
@ -62,7 +62,7 @@ class ControlToolBar final : public ToolBar {
|
|||||||
|
|
||||||
// Choice among the appearances of the play button:
|
// Choice among the appearances of the play button:
|
||||||
enum class PlayAppearance {
|
enum class PlayAppearance {
|
||||||
Straight, Looped, CutPreview, Scrub
|
Straight, Looped, CutPreview, Scrub, Seek
|
||||||
};
|
};
|
||||||
|
|
||||||
//These allow buttons to be controlled externally:
|
//These allow buttons to be controlled externally:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
|
|
||||||
Audacity: A Digital Audio Editor
|
Audacity: A Digital Audio Editor
|
||||||
|
|
||||||
@ -330,3 +330,203 @@ void EditToolBar::EnableDisableButtons()
|
|||||||
mButtons[ETBSyncLockID]->PopUp();
|
mButtons[ETBSyncLockID]->PopUp();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// PRL: to do: move the below to its own file
|
||||||
|
// Much of this is imitative of EditToolBar. Should there be a common base
|
||||||
|
// class?
|
||||||
|
#include "../Audacity.h"
|
||||||
|
|
||||||
|
// For compilers that support precompilation, includes "wx/wx.h".
|
||||||
|
#include <wx/wxprec.h>
|
||||||
|
|
||||||
|
#ifndef WX_PRECOMP
|
||||||
|
#include <wx/event.h>
|
||||||
|
#include <wx/image.h>
|
||||||
|
#include <wx/intl.h>
|
||||||
|
#include <wx/sizer.h>
|
||||||
|
#include <wx/tooltip.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "../AllThemeResources.h"
|
||||||
|
#include "../AudioIO.h"
|
||||||
|
#include "../ImageManipulation.h"
|
||||||
|
#include "../Internat.h"
|
||||||
|
#include "../Prefs.h"
|
||||||
|
#include "../Project.h"
|
||||||
|
#include "../Theme.h"
|
||||||
|
#include "../Track.h"
|
||||||
|
#include "../UndoManager.h"
|
||||||
|
#include "../widgets/AButton.h"
|
||||||
|
#include "../tracks/ui/Scrubbing.h"
|
||||||
|
|
||||||
|
#include "../Experimental.h"
|
||||||
|
|
||||||
|
IMPLEMENT_CLASS(ScrubbingToolBar, ToolBar);
|
||||||
|
|
||||||
|
//const int BUTTON_WIDTH = 27;
|
||||||
|
//const int SEPARATOR_WIDTH = 14;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
/// Methods for ScrubbingToolBar
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
BEGIN_EVENT_TABLE( ScrubbingToolBar, ToolBar )
|
||||||
|
EVT_COMMAND_RANGE( STBStartID,
|
||||||
|
STBStartID + STBNumButtons - 1,
|
||||||
|
wxEVT_COMMAND_BUTTON_CLICKED,
|
||||||
|
ScrubbingToolBar::OnButton )
|
||||||
|
END_EVENT_TABLE()
|
||||||
|
|
||||||
|
//Standard contructor
|
||||||
|
ScrubbingToolBar::ScrubbingToolBar()
|
||||||
|
: ToolBar(ScrubbingBarID, _("Scrub"), wxT("Scrub"))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrubbingToolBar::~ScrubbingToolBar()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrubbingToolBar::Create(wxWindow * parent)
|
||||||
|
{
|
||||||
|
ToolBar::Create(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is a convenience function that allows for button creation in
|
||||||
|
/// MakeButtons() with fewer arguments
|
||||||
|
/// Very similar to code in ControlToolBar...
|
||||||
|
AButton *ScrubbingToolBar::AddButton
|
||||||
|
(teBmps eEnabledUp, teBmps eEnabledDown, teBmps eDisabled,
|
||||||
|
int id,
|
||||||
|
const wxChar *label,
|
||||||
|
bool toggle)
|
||||||
|
{
|
||||||
|
AButton *&r = mButtons[id];
|
||||||
|
|
||||||
|
r = ToolBar::MakeButton
|
||||||
|
(this,
|
||||||
|
bmpRecoloredUpSmall, bmpRecoloredDownSmall, bmpRecoloredHiliteSmall,
|
||||||
|
eEnabledUp, eEnabledDown, eDisabled,
|
||||||
|
wxWindowID(id),
|
||||||
|
wxDefaultPosition,
|
||||||
|
toggle,
|
||||||
|
theTheme.ImageSize( bmpRecoloredUpSmall ));
|
||||||
|
|
||||||
|
r->SetLabel(label);
|
||||||
|
// JKC: Unlike ControlToolBar, does not have a focus rect. Shouldn't it?
|
||||||
|
// r->SetFocusRect( r->GetRect().Deflate( 4, 4 ) );
|
||||||
|
|
||||||
|
Add( r, 0, wxALIGN_CENTER );
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrubbingToolBar::Populate()
|
||||||
|
{
|
||||||
|
MakeButtonBackgroundsSmall();
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
|
AddButton(bmpPlay, bmpStop, bmpPlayDisabled, STBStartID,
|
||||||
|
_("Start scrubbing"), true);
|
||||||
|
AddButton(bmpScrub, bmpScrub, bmpScrubDisabled, STBScrubID,
|
||||||
|
_("Scrub"), true);
|
||||||
|
AddButton(bmpSeek, bmpSeek, bmpSeekDisabled, STBSeekID,
|
||||||
|
_("Seek"), true);
|
||||||
|
|
||||||
|
|
||||||
|
RegenerateTooltips();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrubbingToolBar::UpdatePrefs()
|
||||||
|
{
|
||||||
|
RegenerateTooltips();
|
||||||
|
|
||||||
|
// Set label to pull in language change
|
||||||
|
SetLabel(_("Scrubbing"));
|
||||||
|
|
||||||
|
// Give base class a chance
|
||||||
|
ToolBar::UpdatePrefs();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrubbingToolBar::RegenerateTooltips()
|
||||||
|
{
|
||||||
|
#if wxUSE_TOOLTIPS
|
||||||
|
/* i18n-hint: These commands assist the user in finding a sound by ear. ...
|
||||||
|
"Scrubbing" is variable-speed playback, ...
|
||||||
|
"Seeking" is normal speed playback but with skips
|
||||||
|
*/
|
||||||
|
auto project = GetActiveProject();
|
||||||
|
if (project) {
|
||||||
|
auto startStop = mButtons[STBStartID];
|
||||||
|
auto &scrubber = project->GetScrubber();
|
||||||
|
if(scrubber.HasStartedScrubbing() || scrubber.IsScrubbing()) {
|
||||||
|
if (scrubber.Seeks())
|
||||||
|
startStop->SetToolTip(_("Stop seeking"));
|
||||||
|
else
|
||||||
|
startStop->SetToolTip(_("Stop scrubbing"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (scrubber.Seeks())
|
||||||
|
startStop->SetToolTip(_("Start seeking"));
|
||||||
|
else
|
||||||
|
startStop->SetToolTip(_("Start scrubbing"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mButtons[STBScrubID]->SetToolTip(_("Scrub"));
|
||||||
|
mButtons[STBSeekID]->SetToolTip(_("Seek"));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrubbingToolBar::OnButton(wxCommandEvent &event)
|
||||||
|
{
|
||||||
|
AudacityProject *p = GetActiveProject();
|
||||||
|
if (!p) return;
|
||||||
|
auto &scrubber = p->GetScrubber();
|
||||||
|
|
||||||
|
int id = event.GetId();
|
||||||
|
|
||||||
|
switch (id) {
|
||||||
|
case STBStartID:
|
||||||
|
scrubber.OnStartStop(event);
|
||||||
|
break;
|
||||||
|
case STBScrubID:
|
||||||
|
scrubber.OnScrub(event);
|
||||||
|
break;
|
||||||
|
case STBSeekID:
|
||||||
|
scrubber.OnSeek(event);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
wxASSERT(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
EnableDisableButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrubbingToolBar::EnableDisableButtons()
|
||||||
|
{
|
||||||
|
const auto scrubButton = mButtons[STBScrubID];
|
||||||
|
scrubButton->SetEnabled(true);
|
||||||
|
const auto seekButton = mButtons[STBSeekID];
|
||||||
|
seekButton->SetEnabled(true);
|
||||||
|
|
||||||
|
AudacityProject *p = GetActiveProject();
|
||||||
|
if (!p) return;
|
||||||
|
|
||||||
|
auto &scrubber = p->GetScrubber();
|
||||||
|
if (scrubber.Scrubs())
|
||||||
|
scrubButton->PushDown();
|
||||||
|
else
|
||||||
|
scrubButton->PopUp();
|
||||||
|
|
||||||
|
if (scrubber.Seeks())
|
||||||
|
seekButton->PushDown();
|
||||||
|
else
|
||||||
|
seekButton->PopUp();
|
||||||
|
|
||||||
|
const auto startButton = mButtons[STBStartID];
|
||||||
|
if (scrubber.CanScrub())
|
||||||
|
startButton->Enable();
|
||||||
|
else
|
||||||
|
startButton->Disable();
|
||||||
|
}
|
||||||
|
@ -97,5 +97,66 @@ class EditToolBar final : public ToolBar {
|
|||||||
DECLARE_EVENT_TABLE();
|
DECLARE_EVENT_TABLE();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// PRL: to do: move this to its own file
|
||||||
|
|
||||||
|
#include <wx/defs.h>
|
||||||
|
|
||||||
|
#include "ToolBar.h"
|
||||||
|
#include "../Theme.h"
|
||||||
|
#include "../Experimental.h"
|
||||||
|
|
||||||
|
class wxCommandEvent;
|
||||||
|
class wxDC;
|
||||||
|
class wxImage;
|
||||||
|
class wxWindow;
|
||||||
|
|
||||||
|
class AButton;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
STBStartID,
|
||||||
|
STBScrubID,
|
||||||
|
STBSeekID,
|
||||||
|
|
||||||
|
STBNumButtons
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScrubbingToolBar final : public ToolBar {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
ScrubbingToolBar();
|
||||||
|
virtual ~ScrubbingToolBar();
|
||||||
|
|
||||||
|
void Create(wxWindow *parent);
|
||||||
|
|
||||||
|
void OnButton(wxCommandEvent & event);
|
||||||
|
|
||||||
|
void Populate();
|
||||||
|
void Repaint(wxDC * WXUNUSED(dc)) {};
|
||||||
|
void EnableDisableButtons();
|
||||||
|
void UpdatePrefs();
|
||||||
|
|
||||||
|
void RegenerateTooltips();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
AButton *AddButton(teBmps eEnabledUp, teBmps eEnabledDown, teBmps eDisabled,
|
||||||
|
int id, const wxChar *label, bool toggle = false);
|
||||||
|
|
||||||
|
void MakeButtons();
|
||||||
|
|
||||||
|
AButton *mButtons[STBNumButtons];
|
||||||
|
|
||||||
|
wxImage *upImage;
|
||||||
|
wxImage *downImage;
|
||||||
|
wxImage *hiliteImage;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DECLARE_CLASS(EditToolBar);
|
||||||
|
DECLARE_EVENT_TABLE();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -71,6 +71,7 @@ enum
|
|||||||
MixerBarID,
|
MixerBarID,
|
||||||
EditBarID,
|
EditBarID,
|
||||||
TranscriptionBarID,
|
TranscriptionBarID,
|
||||||
|
ScrubbingBarID,
|
||||||
DeviceBarID,
|
DeviceBarID,
|
||||||
SelectionBarID,
|
SelectionBarID,
|
||||||
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
||||||
|
@ -448,6 +448,7 @@ ToolManager::ToolManager( AudacityProject *parent )
|
|||||||
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
||||||
mBars[SpectralSelectionBarID] = new SpectralSelectionBar();
|
mBars[SpectralSelectionBarID] = new SpectralSelectionBar();
|
||||||
#endif
|
#endif
|
||||||
|
mBars[ ScrubbingBarID ] = new ScrubbingToolBar();
|
||||||
|
|
||||||
// We own the timer
|
// We own the timer
|
||||||
mTimer.SetOwner( this );
|
mTimer.SetOwner( this );
|
||||||
|
@ -99,7 +99,7 @@ void PlayIndicatorOverlayBase::Draw(OverlayPanel &panel, wxDC &dc)
|
|||||||
else if(auto ruler = dynamic_cast<AdornedRulerPanel*>(&panel)) {
|
else if(auto ruler = dynamic_cast<AdornedRulerPanel*>(&panel)) {
|
||||||
wxASSERT(!mIsMaster);
|
wxASSERT(!mIsMaster);
|
||||||
|
|
||||||
ruler->DoDrawIndicator(&dc, mLastIndicatorX, !rec, IndicatorMediumWidth, false);
|
ruler->DoDrawIndicator(&dc, mLastIndicatorX, !rec, IndicatorMediumWidth, false, false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
wxASSERT(false);
|
wxASSERT(false);
|
||||||
|
@ -21,6 +21,7 @@ Paul Licameli split from TrackPanel.cpp
|
|||||||
#include "../../commands/CommandFunctors.h"
|
#include "../../commands/CommandFunctors.h"
|
||||||
#include "../../prefs/PlaybackPrefs.h"
|
#include "../../prefs/PlaybackPrefs.h"
|
||||||
#include "../../toolbars/ControlToolBar.h"
|
#include "../../toolbars/ControlToolBar.h"
|
||||||
|
#include "../../toolbars/EditToolBar.h"
|
||||||
|
|
||||||
#undef USE_TRANSCRIPTION_TOOLBAR
|
#undef USE_TRANSCRIPTION_TOOLBAR
|
||||||
#ifdef USE_TRANSCRIPTION_TOOLBAR
|
#ifdef USE_TRANSCRIPTION_TOOLBAR
|
||||||
@ -33,9 +34,6 @@ Paul Licameli split from TrackPanel.cpp
|
|||||||
|
|
||||||
#include <wx/dc.h>
|
#include <wx/dc.h>
|
||||||
|
|
||||||
// Conditional compilation switch for making scrub menu items checkable
|
|
||||||
#define CHECKABLE_SCRUB_MENU_ITEMS
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
// PRL:
|
// PRL:
|
||||||
// Mouse must move at least this far to distinguish ctrl-drag to scrub
|
// Mouse must move at least this far to distinguish ctrl-drag to scrub
|
||||||
@ -165,7 +163,7 @@ private:
|
|||||||
|
|
||||||
void Scrubber::ScrubPoller::Notify()
|
void Scrubber::ScrubPoller::Notify()
|
||||||
{
|
{
|
||||||
// Call ContinueScrubbing() here in a timer handler
|
// Call Continue functions here in a timer handler
|
||||||
// rather than in SelectionHandleDrag()
|
// rather than in SelectionHandleDrag()
|
||||||
// so that even without drag events, we can instruct the play head to
|
// so that even without drag events, we can instruct the play head to
|
||||||
// keep approaching the mouse cursor, when its maximum speed is limited.
|
// keep approaching the mouse cursor, when its maximum speed is limited.
|
||||||
@ -183,7 +181,6 @@ Scrubber::Scrubber(AudacityProject *project)
|
|||||||
, mPaused(true)
|
, mPaused(true)
|
||||||
, mScrubSpeedDisplayCountdown(0)
|
, mScrubSpeedDisplayCountdown(0)
|
||||||
, mScrubStartPosition(-1)
|
, mScrubStartPosition(-1)
|
||||||
, mScrubSeekPress(false)
|
|
||||||
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
|
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
|
||||||
, mSmoothScrollingScrub(false)
|
, mSmoothScrollingScrub(false)
|
||||||
, mLogMaxScrubSpeed(0)
|
, mLogMaxScrubSpeed(0)
|
||||||
@ -221,24 +218,27 @@ namespace {
|
|||||||
wxString status;
|
wxString status;
|
||||||
void (Scrubber::*memFn)(wxCommandEvent&);
|
void (Scrubber::*memFn)(wxCommandEvent&);
|
||||||
bool seek;
|
bool seek;
|
||||||
|
bool (Scrubber::*StatusTest)() const;
|
||||||
|
|
||||||
const wxString &GetStatus() const { return status; }
|
const wxString &GetStatus() const { return status; }
|
||||||
} menuItems[] = {
|
} menuItems[] = {
|
||||||
/* i18n-hint: These commands assist the user in finding a sound by ear. ...
|
/* i18n-hint: These commands assist the user in finding a sound by ear. ...
|
||||||
"Scrubbing" is variable-speed playback, ...
|
"Scrubbing" is variable-speed playback, ...
|
||||||
"Seeking" is normal speed playback but with skips, ...
|
"Seeking" is normal speed playback but with skips, ...
|
||||||
"Scrolling" keeps the playback position at a fixed place on screen while the waveform moves
|
|
||||||
*/
|
*/
|
||||||
{ wxT("Scrub"), XO("&Scrub"), XO("Scrubbing"),
|
{ wxT("Scrub"), XO("&Scrub"), XO("Scrubbing"),
|
||||||
&Scrubber::OnScrub, false },
|
&Scrubber::OnScrub, false, &Scrubber::Scrubs },
|
||||||
|
|
||||||
{ wxT("Seek"), XO("See&k"), XO("Seeking"),
|
{ wxT("Seek"), XO("See&k"), XO("Seeking"),
|
||||||
&Scrubber::OnSeek, true },
|
&Scrubber::OnSeek, true, &Scrubber::Seeks },
|
||||||
|
|
||||||
|
{ wxT("StartStopScrubSeek"), XO("Star&t/Stop"), XO(""),
|
||||||
|
&Scrubber::OnStartStop, true, nullptr },
|
||||||
};
|
};
|
||||||
|
|
||||||
enum { nMenuItems = sizeof(menuItems) / sizeof(*menuItems) };
|
enum { nMenuItems = sizeof(menuItems) / sizeof(*menuItems), StartMenuItem = 2 };
|
||||||
|
|
||||||
|
// This never finds the last item:
|
||||||
inline const MenuItem &FindMenuItem(bool seek)
|
inline const MenuItem &FindMenuItem(bool seek)
|
||||||
{
|
{
|
||||||
return *std::find_if(menuItems, menuItems + nMenuItems,
|
return *std::find_if(menuItems, menuItems + nMenuItems,
|
||||||
@ -252,16 +252,13 @@ namespace {
|
|||||||
|
|
||||||
void Scrubber::MarkScrubStart(
|
void Scrubber::MarkScrubStart(
|
||||||
// Assume xx is relative to the left edge of TrackPanel!
|
// Assume xx is relative to the left edge of TrackPanel!
|
||||||
wxCoord xx, bool smoothScrolling, bool alwaysSeeking
|
wxCoord xx, bool smoothScrolling
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
UncheckAllMenuItems();
|
|
||||||
|
|
||||||
// Don't actually start scrubbing, but collect some information
|
// Don't actually start scrubbing, but collect some information
|
||||||
// needed for the decision to start scrubbing later when handling
|
// needed for the decision to start scrubbing later when handling
|
||||||
// drag events.
|
// drag events.
|
||||||
mSmoothScrollingScrub = smoothScrolling;
|
mSmoothScrollingScrub = smoothScrolling;
|
||||||
mAlwaysSeeking = alwaysSeeking;
|
|
||||||
|
|
||||||
ControlToolBar * const ctb = mProject->GetControlToolBar();
|
ControlToolBar * const ctb = mProject->GetControlToolBar();
|
||||||
|
|
||||||
@ -272,14 +269,15 @@ void Scrubber::MarkScrubStart(
|
|||||||
// scrubber state
|
// scrubber state
|
||||||
mProject->SetAudioIOToken(0);
|
mProject->SetAudioIOToken(0);
|
||||||
|
|
||||||
ctb->SetPlay(true, ControlToolBar::PlayAppearance::Scrub);
|
ctb->SetPlay(true, mSeeking
|
||||||
|
? ControlToolBar::PlayAppearance::Seek
|
||||||
ctb->UpdateStatusBar(mProject);
|
: ControlToolBar::PlayAppearance::Scrub);
|
||||||
|
|
||||||
mScrubStartPosition = xx;
|
mScrubStartPosition = xx;
|
||||||
|
ctb->UpdateStatusBar(mProject);
|
||||||
mOptions.startClockTimeMillis = ::wxGetLocalTimeMillis();
|
mOptions.startClockTimeMillis = ::wxGetLocalTimeMillis();
|
||||||
|
|
||||||
CheckMenuItem();
|
mCancelled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
|
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
|
||||||
@ -354,8 +352,9 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx)
|
|||||||
mOptions.minStutter =
|
mOptions.minStutter =
|
||||||
mDragging ? 0.0 : lrint(std::max(0.0, MinStutter) * options.rate);
|
mDragging ? 0.0 : lrint(std::max(0.0, MinStutter) * options.rate);
|
||||||
|
|
||||||
ControlToolBar::PlayAppearance appearance =
|
ControlToolBar::PlayAppearance appearance = mSeeking
|
||||||
ControlToolBar::PlayAppearance::Scrub;
|
? ControlToolBar::PlayAppearance::Seek
|
||||||
|
: ControlToolBar::PlayAppearance::Scrub;
|
||||||
const bool cutPreview = false;
|
const bool cutPreview = false;
|
||||||
const bool backwards = time1 < time0;
|
const bool backwards = time1 < time0;
|
||||||
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
|
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
|
||||||
@ -398,12 +397,12 @@ void Scrubber::ContinueScrubbingPoll()
|
|||||||
{
|
{
|
||||||
// Thus scrubbing relies mostly on periodic polling of mouse and keys,
|
// Thus scrubbing relies mostly on periodic polling of mouse and keys,
|
||||||
// not event notifications. But there are a few event handlers that
|
// not event notifications. But there are a few event handlers that
|
||||||
// leave messages for this routine, in mScrubSeekPress and in mPaused.
|
// leave messages for this routine, in mPaused.
|
||||||
|
|
||||||
// Decide whether to skip play, because either mouse is down now,
|
// Decide whether to skip play, because either mouse is down now,
|
||||||
// or there was a left click event. (This is then a delayed reaction, in a
|
// or there was a left click event. (This is then a delayed reaction, in a
|
||||||
// timer callback, to a left click event detected elsewhere.)
|
// timer callback, to a left click event detected elsewhere.)
|
||||||
const bool seek = PollIsSeeking() || mScrubSeekPress;
|
const bool seek = Seeks();
|
||||||
|
|
||||||
bool result = false;
|
bool result = false;
|
||||||
if (mPaused) {
|
if (mPaused) {
|
||||||
@ -447,11 +446,6 @@ void Scrubber::ContinueScrubbingPoll()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result)
|
|
||||||
mScrubSeekPress = false;
|
|
||||||
// else, if seek requested, try again at a later time when we might
|
|
||||||
// enqueue a long enough stutter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scrubber::ContinueScrubbingUI()
|
void Scrubber::ContinueScrubbingUI()
|
||||||
@ -461,19 +455,21 @@ void Scrubber::ContinueScrubbingUI()
|
|||||||
if (mDragging && !state.LeftIsDown()) {
|
if (mDragging && !state.LeftIsDown()) {
|
||||||
// Stop and set cursor
|
// Stop and set cursor
|
||||||
mProject->DoPlayStopSelect(true, state.ShiftDown());
|
mProject->DoPlayStopSelect(true, state.ShiftDown());
|
||||||
|
wxCommandEvent evt;
|
||||||
|
mProject->GetControlToolBar()->OnStop(evt);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool seek = PollIsSeeking();
|
const bool seek = Seeks();
|
||||||
|
|
||||||
{
|
{
|
||||||
// Show the correct status for seeking.
|
// Show the correct status for seeking.
|
||||||
bool backup = mAlwaysSeeking;
|
bool backup = mSeeking;
|
||||||
mAlwaysSeeking = seek;
|
mSeeking = seek;
|
||||||
const auto ctb = mProject->GetControlToolBar();
|
const auto ctb = mProject->GetControlToolBar();
|
||||||
if (ctb)
|
if (ctb)
|
||||||
ctb->UpdateStatusBar(mProject);
|
ctb->UpdateStatusBar(mProject);
|
||||||
mAlwaysSeeking = backup;
|
mSeeking = backup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seek)
|
if (seek)
|
||||||
@ -498,7 +494,11 @@ void Scrubber::StopScrubbing()
|
|||||||
|
|
||||||
mPoller->Stop();
|
mPoller->Stop();
|
||||||
|
|
||||||
UncheckAllMenuItems();
|
if (HasStartedScrubbing() && !mCancelled) {
|
||||||
|
const wxMouseState state(::wxGetMouseState());
|
||||||
|
// Stop and set cursor
|
||||||
|
mProject->DoPlayStopSelect(true, state.ShiftDown());
|
||||||
|
}
|
||||||
|
|
||||||
mScrubStartPosition = -1;
|
mScrubStartPosition = -1;
|
||||||
mDragging = false;
|
mDragging = false;
|
||||||
@ -536,7 +536,7 @@ bool Scrubber::ShouldDrawScrubSpeed()
|
|||||||
return IsScrubbing() &&
|
return IsScrubbing() &&
|
||||||
!mPaused && (
|
!mPaused && (
|
||||||
// Draw for (non-scroll) scrub, sometimes, but never for seek
|
// Draw for (non-scroll) scrub, sometimes, but never for seek
|
||||||
(!PollIsSeeking() && mScrubSpeedDisplayCountdown > 0)
|
(!Seeks() && mScrubSpeedDisplayCountdown > 0)
|
||||||
// Draw always for scroll-scrub and for scroll-seek
|
// Draw always for scroll-scrub and for scroll-seek
|
||||||
|| mSmoothScrollingScrub
|
|| mSmoothScrollingScrub
|
||||||
);
|
);
|
||||||
@ -598,12 +598,7 @@ void Scrubber::Forwarder::OnMouse(wxMouseEvent &event)
|
|||||||
auto ruler = scrubber.mProject->GetRulerPanel();
|
auto ruler = scrubber.mProject->GetRulerPanel();
|
||||||
auto isScrubbing = scrubber.IsScrubbing();
|
auto isScrubbing = scrubber.IsScrubbing();
|
||||||
if (isScrubbing && !event.HasAnyModifiers()) {
|
if (isScrubbing && !event.HasAnyModifiers()) {
|
||||||
if(event.LeftDown() ||
|
if (event.m_wheelRotation) {
|
||||||
(event.LeftIsDown() && event.Dragging())) {
|
|
||||||
if (!scrubber.mDragging)
|
|
||||||
scrubber.mScrubSeekPress = true;
|
|
||||||
}
|
|
||||||
else if (event.m_wheelRotation) {
|
|
||||||
double steps = event.m_wheelRotation /
|
double steps = event.m_wheelRotation /
|
||||||
(event.m_wheelDelta > 0 ? (double)event.m_wheelDelta : 120.0);
|
(event.m_wheelDelta > 0 ? (double)event.m_wheelDelta : 120.0);
|
||||||
scrubber.HandleScrollWheel(steps);
|
scrubber.HandleScrollWheel(steps);
|
||||||
@ -717,7 +712,7 @@ void ScrubbingOverlay::OnTimer(wxCommandEvent &event)
|
|||||||
// Where's the mouse?
|
// Where's the mouse?
|
||||||
position = trackPanel->ScreenToClient(position);
|
position = trackPanel->ScreenToClient(position);
|
||||||
|
|
||||||
const bool seeking = scrubber.PollIsSeeking();
|
const bool seeking = scrubber.Seeks();
|
||||||
|
|
||||||
// Find the text
|
// Find the text
|
||||||
const double maxScrubSpeed = GetScrubber().GetMaxScrubSpeed();
|
const double maxScrubSpeed = GetScrubber().GetMaxScrubSpeed();
|
||||||
@ -768,15 +763,9 @@ Scrubber &ScrubbingOverlay::GetScrubber()
|
|||||||
return mProject->GetScrubber();
|
return mProject->GetScrubber();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Scrubber::PollIsSeeking()
|
void Scrubber::DoScrub()
|
||||||
{
|
{
|
||||||
return mDragging || (mAlwaysSeeking || ::wxGetMouseState().LeftIsDown());
|
const bool wasScrubbing = HasStartedScrubbing() || IsScrubbing();
|
||||||
}
|
|
||||||
|
|
||||||
void Scrubber::DoScrub(bool seek)
|
|
||||||
{
|
|
||||||
const bool wasScrubbing = IsScrubbing();
|
|
||||||
const bool match = (seek == mAlwaysSeeking);
|
|
||||||
const bool scroll = PlaybackPrefs::GetPinnedHeadPreference();
|
const bool scroll = PlaybackPrefs::GetPinnedHeadPreference();
|
||||||
if (!wasScrubbing) {
|
if (!wasScrubbing) {
|
||||||
auto tp = mProject->GetTrackPanel();
|
auto tp = mProject->GetTrackPanel();
|
||||||
@ -788,33 +777,49 @@ void Scrubber::DoScrub(bool seek)
|
|||||||
const auto offset = tp->GetLeftOffset();
|
const auto offset = tp->GetLeftOffset();
|
||||||
xx = (std::max(offset, std::min(offset + width - 1, xx)));
|
xx = (std::max(offset, std::min(offset + width - 1, xx)));
|
||||||
|
|
||||||
MarkScrubStart(xx, scroll, seek);
|
MarkScrubStart(xx, scroll);
|
||||||
}
|
}
|
||||||
else if(!match) {
|
else
|
||||||
mSmoothScrollingScrub = scroll;
|
mProject->GetControlToolBar()->StopPlaying();
|
||||||
mAlwaysSeeking = seek;
|
}
|
||||||
UncheckAllMenuItems();
|
|
||||||
CheckMenuItem();
|
|
||||||
|
|
||||||
|
void Scrubber::OnScrubOrSeek(bool &toToggle, bool &other)
|
||||||
|
{
|
||||||
|
toToggle = !toToggle;
|
||||||
|
if (toToggle)
|
||||||
|
other = false;
|
||||||
|
|
||||||
|
if (HasStartedScrubbing()) {
|
||||||
// Show the correct status.
|
// Show the correct status.
|
||||||
const auto ctb = mProject->GetControlToolBar();
|
const auto ctb = mProject->GetControlToolBar();
|
||||||
ctb->UpdateStatusBar(mProject);
|
ctb->UpdateStatusBar(mProject);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// This will call back to Scrubber::StopScrubbing
|
auto ruler = mProject->GetRulerPanel();
|
||||||
const auto ctb = mProject->GetControlToolBar();
|
if (ruler)
|
||||||
ctb->StopPlaying();
|
// Update button images
|
||||||
}
|
ruler->UpdateButtonStates();
|
||||||
|
|
||||||
|
auto scrubbingToolBar = mProject->GetScrubbingToolBar();
|
||||||
|
scrubbingToolBar->EnableDisableButtons();
|
||||||
|
scrubbingToolBar->RegenerateTooltips();
|
||||||
|
|
||||||
|
CheckMenuItem();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scrubber::OnScrub(wxCommandEvent&)
|
void Scrubber::OnScrub(wxCommandEvent&)
|
||||||
{
|
{
|
||||||
DoScrub(false);
|
OnScrubOrSeek(mScrubbing, mSeeking);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scrubber::OnSeek(wxCommandEvent&)
|
void Scrubber::OnSeek(wxCommandEvent&)
|
||||||
{
|
{
|
||||||
DoScrub(true);
|
OnScrubOrSeek(mSeeking, mScrubbing);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scrubber::OnStartStop(wxCommandEvent&)
|
||||||
|
{
|
||||||
|
DoScrub();
|
||||||
}
|
}
|
||||||
|
|
||||||
enum { CMD_ID = 8000 };
|
enum { CMD_ID = 8000 };
|
||||||
@ -822,20 +827,21 @@ enum { CMD_ID = 8000 };
|
|||||||
BEGIN_EVENT_TABLE(Scrubber, wxEvtHandler)
|
BEGIN_EVENT_TABLE(Scrubber, wxEvtHandler)
|
||||||
EVT_MENU(CMD_ID, Scrubber::OnScrub)
|
EVT_MENU(CMD_ID, Scrubber::OnScrub)
|
||||||
EVT_MENU(CMD_ID + 1, Scrubber::OnSeek)
|
EVT_MENU(CMD_ID + 1, Scrubber::OnSeek)
|
||||||
|
EVT_MENU(CMD_ID + 2, Scrubber::OnStartStop)
|
||||||
END_EVENT_TABLE()
|
END_EVENT_TABLE()
|
||||||
|
|
||||||
BEGIN_EVENT_TABLE(Scrubber::Forwarder, wxEvtHandler)
|
BEGIN_EVENT_TABLE(Scrubber::Forwarder, wxEvtHandler)
|
||||||
EVT_MOUSE_EVENTS(Scrubber::Forwarder::OnMouse)
|
EVT_MOUSE_EVENTS(Scrubber::Forwarder::OnMouse)
|
||||||
END_EVENT_TABLE()
|
END_EVENT_TABLE()
|
||||||
|
|
||||||
static_assert(nMenuItems == 2, "wrong number of items");
|
static_assert(nMenuItems == 3, "wrong number of items");
|
||||||
|
|
||||||
const wxString &Scrubber::GetUntranslatedStateString() const
|
const wxString &Scrubber::GetUntranslatedStateString() const
|
||||||
{
|
{
|
||||||
static wxString empty;
|
static wxString empty;
|
||||||
|
|
||||||
if (HasStartedScrubbing()) {
|
if (HasStartedScrubbing()) {
|
||||||
auto &item = FindMenuItem(mAlwaysSeeking);
|
auto &item = FindMenuItem(mSeeking);
|
||||||
return item.status;
|
return item.status;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -846,34 +852,42 @@ std::vector<wxString> Scrubber::GetAllUntranslatedStatusStrings()
|
|||||||
{
|
{
|
||||||
using namespace std;
|
using namespace std;
|
||||||
vector<wxString> results;
|
vector<wxString> results;
|
||||||
transform(menuItems, menuItems + nMenuItems, back_inserter(results),
|
for (const auto &item : menuItems) {
|
||||||
mem_fun_ref(&MenuItem::GetStatus));
|
const auto &status = item.GetStatus();
|
||||||
|
if (!status.empty())
|
||||||
|
results.push_back(status);
|
||||||
|
}
|
||||||
return move(results);
|
return move(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Scrubber::CanScrub() const
|
bool Scrubber::CanScrub() const
|
||||||
{
|
{
|
||||||
|
// Return the enabled state for the menu item that really launches the scrub or seek.
|
||||||
auto cm = mProject->GetCommandManager();
|
auto cm = mProject->GetCommandManager();
|
||||||
return cm->GetEnabled(menuItems[0].name);
|
return cm->GetEnabled(menuItems[StartMenuItem].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scrubber::AddMenuItems()
|
void Scrubber::AddMenuItems()
|
||||||
{
|
{
|
||||||
auto cm = mProject->GetCommandManager();
|
auto cm = mProject->GetCommandManager();
|
||||||
auto flags = cm->GetDefaultFlags() | WaveTracksExistFlag;
|
auto flag = WaveTracksExistFlag;
|
||||||
auto mask = cm->GetDefaultMask() | WaveTracksExistFlag;
|
auto flags = cm->GetDefaultFlags() | flag;
|
||||||
|
auto mask = cm->GetDefaultMask() | flag;
|
||||||
|
|
||||||
cm->BeginSubMenu(_("Scru&bbing"));
|
cm->BeginSubMenu(_("Scru&bbing"));
|
||||||
for (const auto &item : menuItems) {
|
for (const auto &item : menuItems) {
|
||||||
#ifdef CHECKABLE_SCRUB_MENU_ITEMS
|
if (!item.GetStatus().empty())
|
||||||
cm->AddCheck(item.name, wxGetTranslation(item.label),
|
cm->AddCheck(item.name, wxGetTranslation(item.label),
|
||||||
FNT(Scrubber, this, item.memFn),
|
FNT(Scrubber, this, item.memFn),
|
||||||
false, flags, mask);
|
false,
|
||||||
#else
|
// Less restricted:
|
||||||
cm->AddItem(item.name, wxGetTranslation(item.label),
|
AlwaysEnabledFlag, AlwaysEnabledFlag);
|
||||||
FNT(Scrubber, this, item.memFn),
|
else
|
||||||
flags, mask);
|
// The start item
|
||||||
#endif
|
cm->AddItem(item.name, wxGetTranslation(item.label),
|
||||||
|
FNT(Scrubber, this, item.memFn),
|
||||||
|
// More restricted:
|
||||||
|
flags, mask);
|
||||||
}
|
}
|
||||||
cm->EndSubMenu();
|
cm->EndSubMenu();
|
||||||
CheckMenuItem();
|
CheckMenuItem();
|
||||||
@ -883,42 +897,22 @@ void Scrubber::PopulateMenu(wxMenu &menu)
|
|||||||
{
|
{
|
||||||
int id = CMD_ID;
|
int id = CMD_ID;
|
||||||
auto cm = mProject->GetCommandManager();
|
auto cm = mProject->GetCommandManager();
|
||||||
const MenuItem *checkedItem =
|
const MenuItem *checkedItem = &FindMenuItem(mSeeking);
|
||||||
HasStartedScrubbing()
|
|
||||||
? &FindMenuItem(mAlwaysSeeking)
|
|
||||||
: nullptr;
|
|
||||||
for (const auto &item : menuItems) {
|
for (const auto &item : menuItems) {
|
||||||
if (cm->GetEnabled(item.name)) {
|
if (cm->GetEnabled(item.name)) {
|
||||||
#ifdef CHECKABLE_SCRUB_MENU_ITEMS
|
|
||||||
menu.AppendCheckItem(id, item.label);
|
menu.AppendCheckItem(id, item.label);
|
||||||
if(&item == checkedItem)
|
if(&item == checkedItem)
|
||||||
menu.FindItem(id)->Check();
|
menu.FindItem(id)->Check();
|
||||||
#else
|
|
||||||
menu.Append(id, item.label);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
++id;
|
++id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scrubber::UncheckAllMenuItems()
|
|
||||||
{
|
|
||||||
#ifdef CHECKABLE_SCRUB_MENU_ITEMS
|
|
||||||
auto cm = mProject->GetCommandManager();
|
|
||||||
for (const auto &item : menuItems)
|
|
||||||
cm->Check(item.name, false);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Scrubber::CheckMenuItem()
|
void Scrubber::CheckMenuItem()
|
||||||
{
|
{
|
||||||
#ifdef CHECKABLE_SCRUB_MENU_ITEMS
|
auto cm = mProject->GetCommandManager();
|
||||||
if(HasStartedScrubbing()) {
|
cm->Check(menuItems[0].name, mScrubbing);
|
||||||
auto cm = mProject->GetCommandManager();
|
cm->Check(menuItems[1].name, mSeeking);
|
||||||
auto item = FindMenuItem(mAlwaysSeeking);
|
|
||||||
cm->Check(item.name, true);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -71,11 +71,7 @@ public:
|
|||||||
~Scrubber();
|
~Scrubber();
|
||||||
|
|
||||||
// Assume xx is relative to the left edge of TrackPanel!
|
// Assume xx is relative to the left edge of TrackPanel!
|
||||||
void MarkScrubStart(
|
void MarkScrubStart(wxCoord xx, bool smoothScrolling);
|
||||||
wxCoord xx, bool smoothScrolling,
|
|
||||||
bool alwaysSeeking // if false, can switch seeking or scrubbing
|
|
||||||
// by mouse button state
|
|
||||||
);
|
|
||||||
|
|
||||||
// Returns true iff the event should be considered consumed by this:
|
// Returns true iff the event should be considered consumed by this:
|
||||||
// Assume xx is relative to the left edge of TrackPanel!
|
// Assume xx is relative to the left edge of TrackPanel!
|
||||||
@ -101,8 +97,14 @@ public:
|
|||||||
void SetScrollScrubbing(bool value)
|
void SetScrollScrubbing(bool value)
|
||||||
{ mSmoothScrollingScrub = value; }
|
{ mSmoothScrollingScrub = value; }
|
||||||
|
|
||||||
bool IsAlwaysSeeking() const
|
bool Seeks() const
|
||||||
{ return mAlwaysSeeking; }
|
{ return mSeeking; }
|
||||||
|
|
||||||
|
bool Scrubs() const
|
||||||
|
{ return mScrubbing; }
|
||||||
|
|
||||||
|
void Cancel()
|
||||||
|
{ mCancelled = true; }
|
||||||
|
|
||||||
bool ShouldDrawScrubSpeed();
|
bool ShouldDrawScrubSpeed();
|
||||||
double FindScrubSpeed(bool seeking, double time) const;
|
double FindScrubSpeed(bool seeking, double time) const;
|
||||||
@ -110,8 +112,6 @@ public:
|
|||||||
|
|
||||||
void HandleScrollWheel(int steps);
|
void HandleScrollWheel(int steps);
|
||||||
|
|
||||||
bool PollIsSeeking();
|
|
||||||
|
|
||||||
// This returns the same as the enabled state of the menu items:
|
// This returns the same as the enabled state of the menu items:
|
||||||
bool CanScrub() const;
|
bool CanScrub() const;
|
||||||
|
|
||||||
@ -120,10 +120,13 @@ public:
|
|||||||
// For popup
|
// For popup
|
||||||
void PopulateMenu(wxMenu &menu);
|
void PopulateMenu(wxMenu &menu);
|
||||||
|
|
||||||
|
void OnScrubOrSeek(bool &toToggle, bool &other);
|
||||||
void OnScrub(wxCommandEvent&);
|
void OnScrub(wxCommandEvent&);
|
||||||
void OnSeek(wxCommandEvent&);
|
void OnSeek(wxCommandEvent&);
|
||||||
|
void OnStartStop(wxCommandEvent&);
|
||||||
|
|
||||||
// A string to put in the leftmost part of the status bar.
|
// A string to put in the leftmost part of the status bar
|
||||||
|
// when scrub or seek is in progress, or else empty.
|
||||||
const wxString &GetUntranslatedStateString() const;
|
const wxString &GetUntranslatedStateString() const;
|
||||||
|
|
||||||
// All possible status strings.
|
// All possible status strings.
|
||||||
@ -133,9 +136,8 @@ public:
|
|||||||
bool IsPaused() const;
|
bool IsPaused() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DoScrub(bool seek);
|
void DoScrub();
|
||||||
void OnActivateOrDeactivateApp(wxActivateEvent & event);
|
void OnActivateOrDeactivateApp(wxActivateEvent & event);
|
||||||
void UncheckAllMenuItems();
|
|
||||||
void CheckMenuItem();
|
void CheckMenuItem();
|
||||||
|
|
||||||
// I need this because I can't push the scrubber as an event handler
|
// I need this because I can't push the scrubber as an event handler
|
||||||
@ -156,11 +158,17 @@ private:
|
|||||||
int mScrubSpeedDisplayCountdown;
|
int mScrubSpeedDisplayCountdown;
|
||||||
wxCoord mScrubStartPosition;
|
wxCoord mScrubStartPosition;
|
||||||
wxCoord mLastScrubPosition {};
|
wxCoord mLastScrubPosition {};
|
||||||
bool mScrubSeekPress;
|
|
||||||
bool mSmoothScrollingScrub;
|
bool mSmoothScrollingScrub;
|
||||||
bool mAlwaysSeeking {};
|
|
||||||
|
// These hold the three-way choice among click-to-scrub, click-to-seek, or disabled.
|
||||||
|
// Not both true.
|
||||||
|
bool mScrubbing {};
|
||||||
|
bool mSeeking {};
|
||||||
|
|
||||||
bool mDragging {};
|
bool mDragging {};
|
||||||
|
|
||||||
|
bool mCancelled {};
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
|
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
|
||||||
int mLogMaxScrubSpeed;
|
int mLogMaxScrubSpeed;
|
||||||
#endif
|
#endif
|
||||||
|
@ -106,8 +106,9 @@ class Grabber final : public wxWindow
|
|||||||
// not a need to dock/float a toolbar from the keyboard. If this
|
// not a need to dock/float a toolbar from the keyboard. If this
|
||||||
// changes, remove this and add the necessary keyboard movement
|
// changes, remove this and add the necessary keyboard movement
|
||||||
// handling.
|
// handling.
|
||||||
// PRL: Commented out so the ESC key can stop dragging.
|
// Note that AcceptsFocusFromKeyboard() rather than AcceptsFocus()
|
||||||
// bool AcceptsFocus() const {return false;}
|
// is overridden so that ESC can cancel toolbar drag.
|
||||||
|
bool AcceptsFocusFromKeyboard() const override {return false;}
|
||||||
|
|
||||||
void PushButton(bool state);
|
void PushButton(bool state);
|
||||||
|
|
||||||
|
@ -1791,7 +1791,7 @@ std::pair<wxRect, bool> QuickPlayRulerOverlay::DoGetRectangle(wxSize size)
|
|||||||
if (x >= 0) {
|
if (x >= 0) {
|
||||||
// These dimensions are always sufficient, even if a little
|
// These dimensions are always sufficient, even if a little
|
||||||
// excessive for the small triangle:
|
// excessive for the small triangle:
|
||||||
const int width = IndicatorBigWidth();
|
const int width = IndicatorBigWidth() * 3 / 2;
|
||||||
const auto height = IndicatorHeightForWidth(width);
|
const auto height = IndicatorHeightForWidth(width);
|
||||||
|
|
||||||
const int indsize = width / 2;
|
const int indsize = width / 2;
|
||||||
@ -1814,12 +1814,14 @@ void QuickPlayRulerOverlay::Draw(OverlayPanel &panel, wxDC &dc)
|
|||||||
mOldQPIndicatorPos = mNewQPIndicatorPos;
|
mOldQPIndicatorPos = mNewQPIndicatorPos;
|
||||||
if (mOldQPIndicatorPos >= 0) {
|
if (mOldQPIndicatorPos >= 0) {
|
||||||
auto ruler = GetRuler();
|
auto ruler = GetRuler();
|
||||||
|
const auto &scrubber = mPartner.mProject->GetScrubber();
|
||||||
auto scrub =
|
auto scrub =
|
||||||
ruler->mMouseEventState == AdornedRulerPanel::mesNone &&
|
ruler->mMouseEventState == AdornedRulerPanel::mesNone &&
|
||||||
(ruler->mPrevZone == AdornedRulerPanel::StatusChoice::EnteringScrubZone ||
|
(ruler->mPrevZone == AdornedRulerPanel::StatusChoice::EnteringScrubZone ||
|
||||||
(mPartner.mProject->GetScrubber().HasStartedScrubbing()));
|
(scrubber.HasStartedScrubbing()));
|
||||||
|
auto seek = scrub && scrubber.Seeks();
|
||||||
auto width = scrub ? IndicatorBigWidth() : IndicatorSmallWidth;
|
auto width = scrub ? IndicatorBigWidth() : IndicatorSmallWidth;
|
||||||
ruler->DoDrawIndicator(&dc, mOldQPIndicatorPos, true, width, scrub);
|
ruler->DoDrawIndicator(&dc, mOldQPIndicatorPos, true, width, scrub, seek);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1918,8 +1920,6 @@ enum {
|
|||||||
OnLockPlayRegionID,
|
OnLockPlayRegionID,
|
||||||
|
|
||||||
OnTogglePinnedStateID,
|
OnTogglePinnedStateID,
|
||||||
|
|
||||||
OnShowHideScrubbingID,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
BEGIN_EVENT_TABLE(AdornedRulerPanel, OverlayPanel)
|
BEGIN_EVENT_TABLE(AdornedRulerPanel, OverlayPanel)
|
||||||
@ -1935,15 +1935,12 @@ BEGIN_EVENT_TABLE(AdornedRulerPanel, OverlayPanel)
|
|||||||
EVT_MENU(OnAutoScrollID, AdornedRulerPanel::OnAutoScroll)
|
EVT_MENU(OnAutoScrollID, AdornedRulerPanel::OnAutoScroll)
|
||||||
EVT_MENU(OnLockPlayRegionID, AdornedRulerPanel::OnLockPlayRegion)
|
EVT_MENU(OnLockPlayRegionID, AdornedRulerPanel::OnLockPlayRegion)
|
||||||
|
|
||||||
// Scrub bar menu commands
|
|
||||||
EVT_MENU(OnShowHideScrubbingID, AdornedRulerPanel::OnToggleScrubbing)
|
|
||||||
|
|
||||||
// Pop up menus on Windows
|
// Pop up menus on Windows
|
||||||
EVT_CONTEXT_MENU(AdornedRulerPanel::OnContextMenu)
|
EVT_CONTEXT_MENU(AdornedRulerPanel::OnContextMenu)
|
||||||
|
|
||||||
EVT_COMMAND( OnTogglePinnedStateID,
|
EVT_COMMAND( OnTogglePinnedStateID,
|
||||||
wxEVT_COMMAND_BUTTON_CLICKED,
|
wxEVT_COMMAND_BUTTON_CLICKED,
|
||||||
AdornedRulerPanel::OnTogglePinnedState )
|
AdornedRulerPanel::OnTogglePinnedState )
|
||||||
|
|
||||||
END_EVENT_TABLE()
|
END_EVENT_TABLE()
|
||||||
|
|
||||||
@ -1959,8 +1956,6 @@ AdornedRulerPanel::AdornedRulerPanel(AudacityProject* parent,
|
|||||||
for (auto &button : mButtons)
|
for (auto &button : mButtons)
|
||||||
button = nullptr;
|
button = nullptr;
|
||||||
|
|
||||||
ReCreateButtons();
|
|
||||||
|
|
||||||
SetLabel( _("Timeline") );
|
SetLabel( _("Timeline") );
|
||||||
SetName(GetLabel());
|
SetName(GetLabel());
|
||||||
SetBackgroundStyle(wxBG_STYLE_PAINT);
|
SetBackgroundStyle(wxBG_STYLE_PAINT);
|
||||||
@ -1998,8 +1993,6 @@ AdornedRulerPanel::AdornedRulerPanel(AudacityProject* parent,
|
|||||||
mPlayRegionDragsSelection = (gPrefs->Read(wxT("/QuickPlay/DragSelection"), 0L) == 1)? true : false;
|
mPlayRegionDragsSelection = (gPrefs->Read(wxT("/QuickPlay/DragSelection"), 0L) == 1)? true : false;
|
||||||
mQuickPlayEnabled = !!gPrefs->Read(wxT("/QuickPlay/QuickPlayEnabled"), 1L);
|
mQuickPlayEnabled = !!gPrefs->Read(wxT("/QuickPlay/QuickPlayEnabled"), 1L);
|
||||||
|
|
||||||
UpdatePrefs();
|
|
||||||
|
|
||||||
#if wxUSE_TOOLTIPS
|
#if wxUSE_TOOLTIPS
|
||||||
wxToolTip::Enable(true);
|
wxToolTip::Enable(true);
|
||||||
#endif
|
#endif
|
||||||
@ -2026,6 +2019,7 @@ AdornedRulerPanel::~AdornedRulerPanel()
|
|||||||
this);
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
namespace {
|
namespace {
|
||||||
static const wxChar *scrubEnabledPrefName = wxT("/QuickPlay/ScrubbingEnabled");
|
static const wxChar *scrubEnabledPrefName = wxT("/QuickPlay/ScrubbingEnabled");
|
||||||
|
|
||||||
@ -2041,6 +2035,7 @@ namespace {
|
|||||||
gPrefs->Write(scrubEnabledPrefName, value);
|
gPrefs->Write(scrubEnabledPrefName, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void AdornedRulerPanel::UpdatePrefs()
|
void AdornedRulerPanel::UpdatePrefs()
|
||||||
{
|
{
|
||||||
@ -2058,7 +2053,7 @@ void AdornedRulerPanel::UpdatePrefs()
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mShowScrubbing = ReadScrubEnabledPref();
|
// mShowScrubbing = ReadScrubEnabledPref();
|
||||||
// Affected by the last
|
// Affected by the last
|
||||||
UpdateRects();
|
UpdateRects();
|
||||||
|
|
||||||
@ -2088,9 +2083,10 @@ void AdornedRulerPanel::ReCreateButtons()
|
|||||||
|
|
||||||
// Make the short row of time ruler pushbottons.
|
// Make the short row of time ruler pushbottons.
|
||||||
// Don't bother with sizers. Their sizes and positions are fixed.
|
// Don't bother with sizers. Their sizes and positions are fixed.
|
||||||
wxPoint position{ FocusBorderLeft, FocusBorderTop };
|
wxPoint position{ FocusBorderLeft, 0 };
|
||||||
size_t iButton = 0;
|
size_t iButton = 0;
|
||||||
const auto size = theTheme.ImageSize( bmpRecoloredUpSmall );
|
auto size = theTheme.ImageSize( bmpRecoloredUpSmall );
|
||||||
|
size.y = std::min(size.y, GetRulerHeight(false));
|
||||||
|
|
||||||
auto buttonMaker = [&]
|
auto buttonMaker = [&]
|
||||||
(wxWindowID id, teBmps bitmap, bool toggle)
|
(wxWindowID id, teBmps bitmap, bool toggle)
|
||||||
@ -2122,6 +2118,40 @@ void AdornedRulerPanel::InvalidateRuler()
|
|||||||
mRuler.Invalidate();
|
mRuler.Invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const wxString StartScrubbingMessage(const Scrubber &scrubber)
|
||||||
|
{
|
||||||
|
/* i18n-hint: These commands assist the user in finding a sound by ear. ...
|
||||||
|
"Scrubbing" is variable-speed playback, ...
|
||||||
|
"Seeking" is normal speed playback but with skips
|
||||||
|
*/
|
||||||
|
if(scrubber.Seeks())
|
||||||
|
return _("Click or drag to begin seeking");
|
||||||
|
else
|
||||||
|
return _("Click or drag to begin scrubbing");
|
||||||
|
}
|
||||||
|
|
||||||
|
const wxString ContinueScrubbingMessage(const Scrubber &scrubber)
|
||||||
|
{
|
||||||
|
/* i18n-hint: These commands assist the user in finding a sound by ear. ...
|
||||||
|
"Scrubbing" is variable-speed playback, ...
|
||||||
|
"Seeking" is normal speed playback but with skips
|
||||||
|
*/
|
||||||
|
if(scrubber.Seeks())
|
||||||
|
return _("Move to seek");
|
||||||
|
else
|
||||||
|
return _("Move to scrub");
|
||||||
|
}
|
||||||
|
|
||||||
|
const wxString ScrubbingMessage(const Scrubber &scrubber)
|
||||||
|
{
|
||||||
|
if (scrubber.HasStartedScrubbing())
|
||||||
|
return ContinueScrubbingMessage(scrubber);
|
||||||
|
else
|
||||||
|
return StartScrubbingMessage(scrubber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AdornedRulerPanel::RegenerateTooltips(StatusChoice choice)
|
void AdornedRulerPanel::RegenerateTooltips(StatusChoice choice)
|
||||||
{
|
{
|
||||||
#if wxUSE_TOOLTIPS
|
#if wxUSE_TOOLTIPS
|
||||||
@ -2140,7 +2170,10 @@ void AdornedRulerPanel::RegenerateTooltips(StatusChoice choice)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case StatusChoice::EnteringScrubZone :
|
case StatusChoice::EnteringScrubZone :
|
||||||
this->SetToolTip(_("Scrub Bar"));
|
{
|
||||||
|
const auto message = ScrubbingMessage(mProject->GetScrubber());
|
||||||
|
this->SetToolTip(message);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this->SetToolTip(NULL);
|
this->SetToolTip(NULL);
|
||||||
@ -2182,7 +2215,8 @@ void AdornedRulerPanel::OnPaint(wxPaintEvent & WXUNUSED(evt))
|
|||||||
mNeedButtonUpdate = false;
|
mNeedButtonUpdate = false;
|
||||||
// Do this first time setting of button status texts
|
// Do this first time setting of button status texts
|
||||||
// when we are sure the CommandManager is initialized.
|
// when we are sure the CommandManager is initialized.
|
||||||
UpdateButtonStates();
|
ReCreateButtons();
|
||||||
|
UpdatePrefs();
|
||||||
}
|
}
|
||||||
|
|
||||||
wxPaintDC dc(this);
|
wxPaintDC dc(this);
|
||||||
@ -2421,7 +2455,7 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
|
|||||||
}
|
}
|
||||||
else if (!HasCapture() && inScrubZone) {
|
else if (!HasCapture() && inScrubZone) {
|
||||||
if (evt.LeftDown()) {
|
if (evt.LeftDown()) {
|
||||||
scrubber.MarkScrubStart(evt.m_x, PlaybackPrefs::GetPinnedHeadPreference(), false);
|
scrubber.MarkScrubStart(evt.m_x, PlaybackPrefs::GetPinnedHeadPreference());
|
||||||
UpdateStatusBarAndTooltips(StatusChoice::EnteringScrubZone);
|
UpdateStatusBarAndTooltips(StatusChoice::EnteringScrubZone);
|
||||||
}
|
}
|
||||||
ShowQuickPlayIndicator();
|
ShowQuickPlayIndicator();
|
||||||
@ -2730,12 +2764,7 @@ void AdornedRulerPanel::UpdateStatusBarAndTooltips(StatusChoice choice)
|
|||||||
|
|
||||||
case StatusChoice::EnteringScrubZone:
|
case StatusChoice::EnteringScrubZone:
|
||||||
{
|
{
|
||||||
if (scrubbing) {
|
message = ScrubbingMessage(scrubber);
|
||||||
if(!scrubber.IsAlwaysSeeking())
|
|
||||||
message = _("Click or drag to seek");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
message = _("Click to scrub, Double-Click to scroll, Drag to seek");
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -2749,10 +2778,10 @@ void AdornedRulerPanel::UpdateStatusBarAndTooltips(StatusChoice choice)
|
|||||||
RegenerateTooltips(choice);
|
RegenerateTooltips(choice);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdornedRulerPanel::OnToggleScrubbing(wxCommandEvent&)
|
void AdornedRulerPanel::OnToggleScrubbing(/*wxCommandEvent&*/)
|
||||||
{
|
{
|
||||||
mShowScrubbing = !mShowScrubbing;
|
mShowScrubbing = !mShowScrubbing;
|
||||||
WriteScrubEnabledPref(mShowScrubbing);
|
//WriteScrubEnabledPref(mShowScrubbing);
|
||||||
gPrefs->Flush();
|
gPrefs->Flush();
|
||||||
wxSize size { GetSize().GetWidth(), GetRulerHeight(mShowScrubbing) };
|
wxSize size { GetSize().GetWidth(), GetRulerHeight(mShowScrubbing) };
|
||||||
SetSize(size);
|
SetSize(size);
|
||||||
@ -2767,6 +2796,13 @@ void AdornedRulerPanel::OnContextMenu(wxContextMenuEvent & WXUNUSED(event))
|
|||||||
|
|
||||||
void AdornedRulerPanel::UpdateButtonStates()
|
void AdornedRulerPanel::UpdateButtonStates()
|
||||||
{
|
{
|
||||||
|
auto common = [this]
|
||||||
|
(wxWindow *button, const wxString &commandName, const wxString &label){
|
||||||
|
const auto &fullLabel = ComposeButtonLabel(*mProject, commandName, label);
|
||||||
|
button->SetLabel(fullLabel);
|
||||||
|
button->SetToolTip(fullLabel);
|
||||||
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
bool state = PlaybackPrefs::GetPinnedHeadPreference();
|
bool state = PlaybackPrefs::GetPinnedHeadPreference();
|
||||||
auto pinButton = static_cast<AButton*>(FindWindow(OnTogglePinnedStateID));
|
auto pinButton = static_cast<AButton*>(FindWindow(OnTogglePinnedStateID));
|
||||||
@ -2777,10 +2813,13 @@ void AdornedRulerPanel::UpdateButtonStates()
|
|||||||
// (which is, to toggle the state)
|
// (which is, to toggle the state)
|
||||||
? _("Pinned play/record Head")
|
? _("Pinned play/record Head")
|
||||||
: _("Unpinned play/record Head");
|
: _("Unpinned play/record Head");
|
||||||
const auto &fullLabel = ComposeButtonLabel(*mProject, wxT("PinnedHead"), label);
|
common(pinButton, wxT("PinnedHead"), label);
|
||||||
pinButton->SetLabel(fullLabel);
|
|
||||||
pinButton->SetToolTip(fullLabel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto &scrubber = mProject->GetScrubber();
|
||||||
|
|
||||||
|
if(mShowScrubbing != (scrubber.Scrubs() || scrubber.Seeks()))
|
||||||
|
OnToggleScrubbing();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdornedRulerPanel::OnTogglePinnedState(wxCommandEvent & event)
|
void AdornedRulerPanel::OnTogglePinnedState(wxCommandEvent & event)
|
||||||
@ -2858,12 +2897,6 @@ void AdornedRulerPanel::ShowScrubMenu(const wxPoint & pos)
|
|||||||
auto cleanup = finally([this]{ PopEventHandler(); });
|
auto cleanup = finally([this]{ PopEventHandler(); });
|
||||||
|
|
||||||
wxMenu rulerMenu;
|
wxMenu rulerMenu;
|
||||||
rulerMenu.AppendCheckItem(OnShowHideScrubbingID, _("Scrub Bar"));
|
|
||||||
if(mShowScrubbing)
|
|
||||||
rulerMenu.FindItem(OnShowHideScrubbingID)->Check();
|
|
||||||
|
|
||||||
rulerMenu.AppendSeparator();
|
|
||||||
|
|
||||||
mProject->GetScrubber().PopulateMenu(rulerMenu);
|
mProject->GetScrubber().PopulateMenu(rulerMenu);
|
||||||
PopupMenu(&rulerMenu, pos);
|
PopupMenu(&rulerMenu, pos);
|
||||||
}
|
}
|
||||||
@ -3104,11 +3137,6 @@ void AdornedRulerPanel::DoDrawSelection(wxDC * dc)
|
|||||||
dc->DrawRectangle( r );
|
dc->DrawRectangle( r );
|
||||||
}
|
}
|
||||||
|
|
||||||
int AdornedRulerPanel::GetRulerHeight()
|
|
||||||
{
|
|
||||||
return GetRulerHeight(ReadScrubEnabledPref());
|
|
||||||
}
|
|
||||||
|
|
||||||
int AdornedRulerPanel::GetRulerHeight(bool showScrubBar)
|
int AdornedRulerPanel::GetRulerHeight(bool showScrubBar)
|
||||||
{
|
{
|
||||||
return ProperRulerHeight + (showScrubBar ? ScrubHeight : 0);
|
return ProperRulerHeight + (showScrubBar ? ScrubHeight : 0);
|
||||||
@ -3122,14 +3150,46 @@ void AdornedRulerPanel::SetLeftOffset(int offset)
|
|||||||
|
|
||||||
// Draws the play/recording position indicator.
|
// Draws the play/recording position indicator.
|
||||||
void AdornedRulerPanel::DoDrawIndicator
|
void AdornedRulerPanel::DoDrawIndicator
|
||||||
(wxDC * dc, wxCoord xx, bool playing, int width, bool scrub)
|
(wxDC * dc, wxCoord xx, bool playing, int width, bool scrub, bool seek)
|
||||||
{
|
{
|
||||||
ADCChanger changer(dc); // Undo pen and brush changes at function exit
|
ADCChanger changer(dc); // Undo pen and brush changes at function exit
|
||||||
|
|
||||||
AColor::IndicatorColor( dc, playing );
|
AColor::IndicatorColor( dc, playing );
|
||||||
|
|
||||||
wxPoint tri[ 3 ];
|
wxPoint tri[ 3 ];
|
||||||
if (scrub) {
|
if (seek) {
|
||||||
|
auto height = IndicatorHeightForWidth(width);
|
||||||
|
// Make four triangles
|
||||||
|
const int TriangleWidth = width * 3 / 8;
|
||||||
|
|
||||||
|
// Double-double headed, left-right
|
||||||
|
auto yy = mShowScrubbing
|
||||||
|
? mScrubZone.y
|
||||||
|
: (mInner.GetBottom() + 1) - 1 /* bevel */ - height;
|
||||||
|
tri[ 0 ].x = xx - IndicatorOffset;
|
||||||
|
tri[ 0 ].y = yy;
|
||||||
|
tri[ 1 ].x = xx - IndicatorOffset;
|
||||||
|
tri[ 1 ].y = yy + height;
|
||||||
|
tri[ 2 ].x = xx - TriangleWidth;
|
||||||
|
tri[ 2 ].y = yy + height / 2;
|
||||||
|
dc->DrawPolygon( 3, tri );
|
||||||
|
|
||||||
|
tri[ 0 ].x -= TriangleWidth;
|
||||||
|
tri[ 1 ].x -= TriangleWidth;
|
||||||
|
tri[ 2 ].x -= TriangleWidth;
|
||||||
|
dc->DrawPolygon( 3, tri );
|
||||||
|
|
||||||
|
tri[ 0 ].x = tri[ 1 ].x = xx + IndicatorOffset;
|
||||||
|
tri[ 2 ].x = xx + TriangleWidth;
|
||||||
|
dc->DrawPolygon( 3, tri );
|
||||||
|
|
||||||
|
|
||||||
|
tri[ 0 ].x += TriangleWidth;
|
||||||
|
tri[ 1 ].x += TriangleWidth;
|
||||||
|
tri[ 2 ].x += TriangleWidth;
|
||||||
|
dc->DrawPolygon( 3, tri );
|
||||||
|
}
|
||||||
|
else if (scrub) {
|
||||||
auto height = IndicatorHeightForWidth(width);
|
auto height = IndicatorHeightForWidth(width);
|
||||||
const int IndicatorHalfWidth = width / 2;
|
const int IndicatorHalfWidth = width / 2;
|
||||||
|
|
||||||
|
@ -298,7 +298,7 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static int GetRulerHeight();
|
int GetRulerHeight() { return GetRulerHeight(mShowScrubbing); }
|
||||||
static int GetRulerHeight(bool showScrubBar);
|
static int GetRulerHeight(bool showScrubBar);
|
||||||
wxRect GetInnerRect() const { return mInner; }
|
wxRect GetInnerRect() const { return mInner; }
|
||||||
|
|
||||||
@ -351,7 +351,7 @@ private:
|
|||||||
void DoDrawSelection(wxDC * dc);
|
void DoDrawSelection(wxDC * dc);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void DoDrawIndicator(wxDC * dc, wxCoord xx, bool playing, int width, bool scrub);
|
void DoDrawIndicator(wxDC * dc, wxCoord xx, bool playing, int width, bool scrub, bool seek);
|
||||||
void UpdateButtonStates();
|
void UpdateButtonStates();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -413,7 +413,7 @@ private:
|
|||||||
void OnAutoScroll(wxCommandEvent &evt);
|
void OnAutoScroll(wxCommandEvent &evt);
|
||||||
void OnLockPlayRegion(wxCommandEvent &evt);
|
void OnLockPlayRegion(wxCommandEvent &evt);
|
||||||
|
|
||||||
void OnToggleScrubbing(wxCommandEvent&);
|
void OnToggleScrubbing(/*wxCommandEvent&*/);
|
||||||
|
|
||||||
void OnContextMenu(wxContextMenuEvent & WXUNUSED(event));
|
void OnContextMenu(wxContextMenuEvent & WXUNUSED(event));
|
||||||
|
|
||||||
@ -446,7 +446,7 @@ private:
|
|||||||
|
|
||||||
friend QuickPlayRulerOverlay;
|
friend QuickPlayRulerOverlay;
|
||||||
|
|
||||||
wxWindow *mButtons[1];
|
wxWindow *mButtons[3];
|
||||||
bool mNeedButtonUpdate { true };
|
bool mNeedButtonUpdate { true };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user