2010-01-23 19:44:49 +00:00
/**********************************************************************
2014-01-16 17:55:35 +00:00
Audacity : A Digital Audio Editor
2010-01-23 19:44:49 +00:00
2014-01-16 17:55:35 +00:00
EffectEqualization . cpp
2010-01-23 19:44:49 +00:00
2014-01-16 17:55:35 +00:00
Mitch Golden
Vaughan Johnson ( Preview )
Martyn Shaw ( FIR filters , response curve , graphic EQ )
2010-01-23 19:44:49 +00:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /**
2014-01-16 17:55:35 +00:00
\ file Equalization . cpp
\ brief Implements EffectEqualiztaion , EqualizationDialog ,
EqualizationPanel , EQCurve and EQPoint .
2010-01-23 19:44:49 +00:00
*/ /****************************************************************/ /**
2014-01-16 17:55:35 +00:00
\ class EffectEqualization
2015-05-09 16:36:54 +00:00
\ brief An Effect that modifies volume in different frequency bands .
2010-01-23 19:44:49 +00:00
2014-01-16 17:55:35 +00:00
Performs filtering , using an FFT to do a FIR filter .
It lets the user draw an arbitrary envelope ( using the same
envelope editing code that is used to edit the track ' s
amplitude envelope ) .
2010-01-23 19:44:49 +00:00
2014-01-16 17:55:35 +00:00
Also allows the curve to be specified with a series of ' graphic EQ '
sliders .
2010-01-23 19:44:49 +00:00
2019-03-14 10:10:20 +00:00
The filter is applied using overlap / add of Hann windows .
2010-01-23 19:44:49 +00:00
2014-01-16 17:55:35 +00:00
Clone of the FFT Filter effect , no longer part of Audacity .
2010-01-23 19:44:49 +00:00
*/ /****************************************************************/ /**
2014-01-16 17:55:35 +00:00
\ class EqualizationPanel
\ brief EqualizationPanel is used with EqualizationDialog and controls
a graph for EffectEqualization . We should look at amalgamating the
2019-12-06 10:39:07 +00:00
various graphing code , such as provided by FrequencyPlotDialog and FilterPanel .
2010-01-23 19:44:49 +00:00
*/ /****************************************************************/ /**
2014-01-16 17:55:35 +00:00
\ class EQCurve
\ brief EQCurve is used with EffectEqualization .
2010-01-23 19:44:49 +00:00
*/ /****************************************************************/ /**
2014-01-16 17:55:35 +00:00
\ class EQPoint
\ brief EQPoint is used with EQCurve and hence EffectEqualization .
2010-01-23 19:44:49 +00:00
*/ /*******************************************************************/
2014-01-16 17:55:35 +00:00
2021-05-09 15:16:56 +00:00
2015-07-03 04:20:21 +00:00
# include "Equalization.h"
2019-01-17 23:31:08 +00:00
# include "LoadEffects.h"
2015-04-17 03:53:42 +00:00
# include <math.h>
2010-01-23 19:44:49 +00:00
# include <vector>
2019-03-23 18:28:37 +00:00
# include <wx/setup.h> // for wxUSE_* macros
2010-01-23 19:44:49 +00:00
# include <wx/bitmap.h>
# include <wx/button.h>
# include <wx/brush.h>
2010-06-15 23:49:51 +00:00
# include <wx/button.h> // not really needed here
2018-11-14 22:05:30 +00:00
# include <wx/dcclient.h>
2010-01-23 19:44:49 +00:00
# include <wx/dcmemory.h>
# include <wx/event.h>
2018-11-16 15:12:10 +00:00
# include <wx/listctrl.h>
2010-01-23 19:44:49 +00:00
# include <wx/image.h>
# include <wx/intl.h>
# include <wx/choice.h>
# include <wx/radiobut.h>
2018-11-11 21:18:23 +00:00
# include <wx/slider.h>
2010-01-23 19:44:49 +00:00
# include <wx/stattext.h>
# include <wx/string.h>
# include <wx/textdlg.h>
# include <wx/ffile.h>
# include <wx/filefn.h>
# include <wx/stdpaths.h>
# include <wx/settings.h>
2019-03-28 07:29:15 +00:00
# include <wx/sizer.h>
2010-01-23 19:44:49 +00:00
# include <wx/checkbox.h>
2015-04-17 03:53:42 +00:00
# include <wx/tooltip.h>
# include <wx/utils.h>
# include "../AColor.h"
2019-02-06 18:44:52 +00:00
# include "../Shuttle.h"
2015-04-17 03:53:42 +00:00
# include "../ShuttleGui.h"
# include "../PlatformCompatibility.h"
# include "../FileNames.h"
# include "../Envelope.h"
2019-06-09 11:46:42 +00:00
# include "../EnvelopeEditor.h"
2015-04-17 03:53:42 +00:00
# include "../widgets/ErrorDialog.h"
# include "../FFT.h"
# include "../Prefs.h"
# include "../Project.h"
2020-05-26 17:31:32 +00:00
# include "../Theme.h"
2018-11-03 17:34:48 +00:00
# include "../TrackArtist.h"
2018-12-05 02:54:40 +00:00
# include "../WaveClip.h"
2019-04-28 10:49:47 +00:00
# include "../ViewInfo.h"
2015-04-17 03:53:42 +00:00
# include "../WaveTrack.h"
# include "../widgets/Ruler.h"
# include "../xml/XMLFileReader.h"
# include "../AllThemeResources.h"
# include "../float_cast.h"
2019-03-30 19:15:10 +00:00
# if wxUSE_ACCESSIBILITY
# include "../widgets/WindowAccessible.h"
# endif
2020-05-25 15:51:28 +00:00
# include "../widgets/FileDialog/FileDialog.h"
2015-05-04 04:36:30 +00:00
2014-01-16 17:55:35 +00:00
# ifdef EXPERIMENTAL_EQ_SSE_THREADED
# include "Equalization48x.h"
# endif
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
enum
{
ID_Length = 10000 ,
ID_dBMax ,
ID_dBMin ,
ID_Clear ,
ID_Invert ,
2015-08-15 17:45:16 +00:00
ID_Mode ,
2015-04-17 03:53:42 +00:00
ID_Draw ,
ID_Graphic ,
ID_Interp ,
ID_Linear ,
ID_Grid ,
ID_Curve ,
ID_Manage ,
ID_Delete ,
# ifdef EXPERIMENTAL_EQ_SSE_THREADED
ID_DefaultMath ,
ID_SSE ,
ID_SSEThreaded ,
ID_AVX ,
ID_AVXThreaded ,
ID_Bench ,
# endif
ID_Slider , // needs to come last
} ;
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
enum kInterpolations
{
kBspline ,
kCosine ,
kCubic ,
2018-02-09 19:12:25 +00:00
nInterpolations
2015-04-17 03:53:42 +00:00
} ;
2010-01-23 19:44:49 +00:00
2016-02-27 13:17:27 +00:00
// Increment whenever EQCurves.xml is updated
# define EQCURVES_VERSION 1
# define EQCURVES_REVISION 0
2016-08-08 13:50:55 +00:00
# define UPDATE_ALL 0 // 0 = merge NEW presets only, 1 = Update all factory presets.
2016-02-27 13:17:27 +00:00
2019-02-28 18:16:09 +00:00
static const EnumValueSymbol kInterpStrings [ nInterpolations ] =
2015-04-17 03:53:42 +00:00
{
2018-03-27 18:17:23 +00:00
// These are acceptable dual purpose internal/visible names
2015-04-17 03:53:42 +00:00
/* i18n-hint: Technical term for a kind of curve.*/
2018-03-27 02:03:45 +00:00
{ XO ( " B-spline " ) } ,
{ XO ( " Cosine " ) } ,
{ XO ( " Cubic " ) }
2015-04-17 03:53:42 +00:00
} ;
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
static const double kThirdOct [ ] =
2010-01-23 19:44:49 +00:00
{
20. , 25. , 31. , 40. , 50. , 63. , 80. , 100. , 125. , 160. , 200. ,
250. , 315. , 400. , 500. , 630. , 800. , 1000. , 1250. , 1600. , 2000. ,
2500. , 3150. , 4000. , 5000. , 6300. , 8000. , 10000. , 12500. , 16000. , 20000. ,
} ;
2015-04-17 03:53:42 +00:00
// Define keys, defaults, minimums, and maximums for the effect parameters
2010-01-23 19:44:49 +00:00
//
2015-04-19 03:49:05 +00:00
// Name Type Key Def Min Max Scale
2019-08-27 16:28:57 +00:00
Param ( FilterLength , int , wxT ( " FilterLength " ) , 8191 , 21 , 8191 , 0 ) ;
2017-10-03 22:07:04 +00:00
Param ( CurveName , wxChar * , wxT ( " CurveName " ) , wxT ( " unnamed " ) , wxT ( " " ) , wxT ( " " ) , wxT ( " " ) ) ;
Param ( InterpLin , bool , wxT ( " InterpolateLin " ) , false , false , true , false ) ;
Param ( InterpMeth , int , wxT ( " InterpolationMethod " ) , 0 , 0 , 0 , 0 ) ;
2015-04-19 03:49:05 +00:00
Param ( DrawMode , bool , wxT ( " " ) , true , false , true , false ) ;
Param ( DrawGrid , bool , wxT ( " " ) , true , false , true , false ) ;
Param ( dBMin , float , wxT ( " " ) , - 30.0 , - 120.0 , - 10.0 , 0 ) ;
Param ( dBMax , float , wxT ( " " ) , 30.0 , 0.0 , 60.0 , 0 ) ;
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
///----------------------------------------------------------------------------
// EffectEqualization
//----------------------------------------------------------------------------
2010-01-23 19:44:49 +00:00
2019-01-17 22:33:49 +00:00
const ComponentInterfaceSymbol EffectEqualization : : Symbol
{ XO ( " Equalization " ) } ;
2019-01-17 23:31:08 +00:00
// namespace{ BuiltinEffectsModule::Registration< EffectEqualization > reg; }
2019-01-17 22:33:49 +00:00
// "Filter Curve EQ" in the user-facing string, but preserve the old
// internal string
const ComponentInterfaceSymbol EffectEqualizationCurve : : Symbol
{ wxT ( " Filter Curve " ) , XO ( " Filter Curve EQ " ) } ;
2019-01-17 23:31:08 +00:00
namespace { BuiltinEffectsModule : : Registration < EffectEqualizationCurve > reg2 ; }
2019-01-17 22:33:49 +00:00
const ComponentInterfaceSymbol EffectEqualizationGraphic : : Symbol
{ wxT ( " Graphic EQ " ) , XO ( " Graphic EQ " ) } ;
2019-01-17 23:31:08 +00:00
namespace { BuiltinEffectsModule : : Registration < EffectEqualizationGraphic > reg3 ; }
2015-04-17 03:53:42 +00:00
BEGIN_EVENT_TABLE ( EffectEqualization , wxEvtHandler )
EVT_SIZE ( EffectEqualization : : OnSize )
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
EVT_SLIDER ( ID_Length , EffectEqualization : : OnSliderM )
EVT_SLIDER ( ID_dBMax , EffectEqualization : : OnSliderDBMAX )
EVT_SLIDER ( ID_dBMin , EffectEqualization : : OnSliderDBMIN )
EVT_COMMAND_RANGE ( ID_Slider ,
ID_Slider + NUMBER_OF_BANDS - 1 ,
wxEVT_COMMAND_SLIDER_UPDATED ,
EffectEqualization : : OnSlider )
EVT_CHOICE ( ID_Interp , EffectEqualization : : OnInterp )
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
EVT_CHOICE ( ID_Curve , EffectEqualization : : OnCurve )
EVT_BUTTON ( ID_Manage , EffectEqualization : : OnManage )
EVT_BUTTON ( ID_Clear , EffectEqualization : : OnClear )
EVT_BUTTON ( ID_Invert , EffectEqualization : : OnInvert )
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
EVT_RADIOBUTTON ( ID_Draw , EffectEqualization : : OnDrawMode )
EVT_RADIOBUTTON ( ID_Graphic , EffectEqualization : : OnGraphicMode )
EVT_CHECKBOX ( ID_Linear , EffectEqualization : : OnLinFreq )
EVT_CHECKBOX ( ID_Grid , EffectEqualization : : OnGridOnOff )
2012-08-02 06:03:19 +00:00
2014-01-16 17:55:35 +00:00
# ifdef EXPERIMENTAL_EQ_SSE_THREADED
2015-04-17 03:53:42 +00:00
EVT_RADIOBUTTON ( ID_DefaultMath , EffectEqualization : : OnProcessingRadio )
EVT_RADIOBUTTON ( ID_SSE , EffectEqualization : : OnProcessingRadio )
EVT_RADIOBUTTON ( ID_SSEThreaded , EffectEqualization : : OnProcessingRadio )
EVT_RADIOBUTTON ( ID_AVX , EffectEqualization : : OnProcessingRadio )
EVT_RADIOBUTTON ( ID_AVXThreaded , EffectEqualization : : OnProcessingRadio )
EVT_BUTTON ( ID_Bench , EffectEqualization : : OnBench )
2014-01-16 17:55:35 +00:00
# endif
2015-04-17 03:53:42 +00:00
END_EVENT_TABLE ( )
2010-01-23 19:44:49 +00:00
2019-03-22 11:10:41 +00:00
EffectEqualization : : EffectEqualization ( int Options )
2016-04-14 16:35:15 +00:00
: mFFTBuffer { windowSize }
, mFilterFuncR { windowSize }
, mFilterFuncI { windowSize }
2010-01-23 19:44:49 +00:00
{
2019-03-22 11:10:41 +00:00
mOptions = Options ;
mGraphic = NULL ;
mDraw = NULL ;
2015-08-15 17:45:16 +00:00
mCurve = NULL ;
mPanel = NULL ;
2019-08-27 16:28:57 +00:00
mMSlider = NULL ;
2015-08-15 17:45:16 +00:00
2016-08-13 15:02:35 +00:00
hFFT = GetFFT ( windowSize ) ;
2014-01-16 17:55:35 +00:00
2015-05-15 11:47:51 +00:00
SetLinearEffectFlag ( true ) ;
2015-04-17 03:53:42 +00:00
mM = DEF_FilterLength ;
mLin = DEF_InterpLin ;
mInterp = DEF_InterpMeth ;
mCurveName = DEF_CurveName ;
2010-01-23 19:44:49 +00:00
2015-04-22 18:02:55 +00:00
GetPrivateConfig ( GetCurrentSettingsGroup ( ) , wxT ( " dBMin " ) , mdBMin , DEF_dBMin ) ;
GetPrivateConfig ( GetCurrentSettingsGroup ( ) , wxT ( " dBMax " ) , mdBMax , DEF_dBMax ) ;
GetPrivateConfig ( GetCurrentSettingsGroup ( ) , wxT ( " DrawMode " ) , mDrawMode , DEF_DrawMode ) ;
GetPrivateConfig ( GetCurrentSettingsGroup ( ) , wxT ( " DrawGrid " ) , mDrawGrid , DEF_DrawGrid ) ;
2017-05-03 00:55:40 +00:00
mLogEnvelope = std : : make_unique < Envelope >
( false ,
MIN_dBMin , MAX_dBMax , // MB: this is the highest possible range
2017-09-16 16:27:56 +00:00
0.0 ) ;
mLogEnvelope - > SetTrackLen ( 1.0 ) ;
2015-04-17 03:53:42 +00:00
2017-05-03 00:55:40 +00:00
mLinEnvelope = std : : make_unique < Envelope >
( false ,
MIN_dBMin , MAX_dBMax , // MB: this is the highest possible range
2017-09-16 16:27:56 +00:00
0.0 ) ;
mLinEnvelope - > SetTrackLen ( 1.0 ) ;
2015-04-17 03:53:42 +00:00
2016-08-04 11:33:22 +00:00
mEnvelope = ( mLin ? mLinEnvelope : mLogEnvelope ) . get ( ) ;
2015-04-17 03:53:42 +00:00
mWindowSize = windowSize ;
mDirty = false ;
2015-04-30 09:49:21 +00:00
mDisallowCustom = false ;
2015-04-17 03:53:42 +00:00
// Load the EQ curves
LoadCurves ( ) ;
// Note: initial curve is set in TransferDataToWindow
mBandsInUse = NUMBER_OF_BANDS ;
//double loLog = log10(mLoFreq);
//double stepLog = (log10(mHiFreq) - loLog)/((double)NUM_PTS-1.);
for ( int i = 0 ; i < NUM_PTS - 1 ; i + + )
mWhens [ i ] = ( double ) i / ( NUM_PTS - 1. ) ;
mWhens [ NUM_PTS - 1 ] = 1. ;
mWhenSliders [ NUMBER_OF_BANDS ] = 1. ;
mEQVals [ NUMBER_OF_BANDS ] = 0. ;
# ifdef EXPERIMENTAL_EQ_SSE_THREADED
bool useSSE ;
GetPrivateConfig ( GetCurrentSettingsGroup ( ) , wxT ( " /SSE/GUI " ) , useSSE , false ) ;
if ( useSSE & & ! mEffectEqualization48x )
2016-08-04 11:33:22 +00:00
mEffectEqualization48x = std : : make_unique < EffectEqualization48x > ( ) ;
else if ( ! useSSE )
mEffectEqualization48x . reset ( ) ;
2015-04-17 03:53:42 +00:00
mBench = false ;
# endif
2020-03-02 18:57:09 +00:00
2021-01-12 09:54:34 +00:00
// We expect these Hi and Lo frequencies to be overridden by Init().
2020-03-02 18:57:09 +00:00
// Don't use inputTracks(). See bug 2321.
#if 0
auto trackList = inputTracks ( ) ;
const auto t = trackList
? * trackList - > Any < const WaveTrack > ( ) . first
: nullptr
;
mHiFreq =
( t
? t - > GetRate ( )
: mProjectRate )
/ 2.0 ;
# endif
mHiFreq = mProjectRate / 2.0 ;
mLoFreq = loFreqI ;
2010-01-23 19:44:49 +00:00
}
EffectEqualization : : ~ EffectEqualization ( )
{
}
2018-11-02 15:31:44 +00:00
// ComponentInterface implementation
2015-04-17 03:53:42 +00:00
2018-11-02 15:31:44 +00:00
ComponentInterfaceSymbol EffectEqualization : : GetSymbol ( )
2015-04-17 03:53:42 +00:00
{
2019-03-22 11:10:41 +00:00
if ( mOptions = = kEqOptionGraphic )
2019-01-17 22:33:49 +00:00
return EffectEqualizationGraphic : : Symbol ;
2019-03-22 11:10:41 +00:00
if ( mOptions = = kEqOptionCurve )
2019-01-17 22:33:49 +00:00
return EffectEqualizationCurve : : Symbol ;
return EffectEqualization : : Symbol ;
2015-04-17 03:53:42 +00:00
}
2019-12-08 18:53:48 +00:00
TranslatableString EffectEqualization : : GetDescription ( )
2015-04-17 03:53:42 +00:00
{
2019-12-08 18:53:48 +00:00
return XO ( " Adjusts the volume levels of particular frequencies " ) ;
2015-04-17 03:53:42 +00:00
}
2017-05-20 13:40:09 +00:00
wxString EffectEqualization : : ManualPage ( )
{
2020-07-28 11:19:35 +00:00
// Bug 2509: Must use _ and not space in names.
2019-08-23 17:41:26 +00:00
if ( mOptions = = kEqOptionGraphic )
2020-07-28 11:19:35 +00:00
return wxT ( " Graphic_EQ " ) ;
2019-08-23 17:41:26 +00:00
if ( mOptions = = kEqOptionCurve )
2020-07-28 11:19:35 +00:00
return wxT ( " Filter_Curve_EQ " ) ;
2017-05-20 13:40:09 +00:00
return wxT ( " Equalization " ) ;
}
Automation: AudacityCommand
This is a squash of 50 commits.
This merges the capabilities of BatchCommands and Effects using a new
AudacityCommand class. AudacityCommand provides one function to specify the
parameters, and then we leverage that one function in automation, whether by chains,
mod-script-pipe or (future) Nyquist.
- Now have AudacityCommand which is using the same mechanism as Effect
- Has configurable parameters
- Has data-entry GUI (built using shuttle GUI)
- Registers with PluginManager.
- Menu commands now provided in chains, and to python batch.
- Tested with Zoom Toggle.
- ShuttleParams now can set, get, set defaults, validate and specify
the parameters.
- Bugfix: Don't overwrite values with defaults first time out.
- Add DefineParams function for all built-in effects.
- Extend CommandContext to carry output channels for results.
We abuse EffectsManager. It handles both Effects and
AudacityCommands now. In time an Effect should become a special case of
AudacityCommand and we'll split and rename the EffectManager class.
- Don't use 'default' as a parameter name.
- Massive renaming for CommandDefinitionInterface
- EffectIdentInterface becomes EffectDefinitionInterface
- EffectAutomationParameters becomes CommandAutomationParameters
- PluginType is now a bit field.
This way we can search for related types at the same time.
- Most old batch commands made into AudacityCommands.
The ones that weren't are for a reason. They are used by mod-script-pipe
to carry commands and responses across from a non-GUI thread to the GUI
thread.
- Major tidy up of ScreenshotCommand
- Reworking of SelectCommand
- GetPreferenceCommand and SetPreferenceCommand
- GetTrackInfo and SetTrackInfo
- GetInfoCommand
- Help, Open, Save, Import and Export commands.
- Removed obsolete commands ExecMenu, GetProjectInfo and SetProjectInfo
which are now better handled by other commands.
- JSONify "GetInfo: Commands" output, i.e. commas in the right places.
- General work on better Doxygen.
- Lyrics -> LyricsPanel
- Meter -> MeterPanel
- Updated Linux makefile.
- Scripting commands added into Extra menu.
- Distinct names for previously duplicated find-clipping parameters.
- Fixed longstanding error with erroneous status field number which
previously caused an ASSERT in debug.
- Sensible formatting of numbers in Chains, 0.1 not 0.1000000000137
2018-01-14 18:51:41 +00:00
// EffectDefinitionInterface implementation
2015-04-17 03:53:42 +00:00
EffectType EffectEqualization : : GetType ( )
{
return EffectTypeProcess ;
}
// EffectClientInterface implementation
Automation: AudacityCommand
This is a squash of 50 commits.
This merges the capabilities of BatchCommands and Effects using a new
AudacityCommand class. AudacityCommand provides one function to specify the
parameters, and then we leverage that one function in automation, whether by chains,
mod-script-pipe or (future) Nyquist.
- Now have AudacityCommand which is using the same mechanism as Effect
- Has configurable parameters
- Has data-entry GUI (built using shuttle GUI)
- Registers with PluginManager.
- Menu commands now provided in chains, and to python batch.
- Tested with Zoom Toggle.
- ShuttleParams now can set, get, set defaults, validate and specify
the parameters.
- Bugfix: Don't overwrite values with defaults first time out.
- Add DefineParams function for all built-in effects.
- Extend CommandContext to carry output channels for results.
We abuse EffectsManager. It handles both Effects and
AudacityCommands now. In time an Effect should become a special case of
AudacityCommand and we'll split and rename the EffectManager class.
- Don't use 'default' as a parameter name.
- Massive renaming for CommandDefinitionInterface
- EffectIdentInterface becomes EffectDefinitionInterface
- EffectAutomationParameters becomes CommandAutomationParameters
- PluginType is now a bit field.
This way we can search for related types at the same time.
- Most old batch commands made into AudacityCommands.
The ones that weren't are for a reason. They are used by mod-script-pipe
to carry commands and responses across from a non-GUI thread to the GUI
thread.
- Major tidy up of ScreenshotCommand
- Reworking of SelectCommand
- GetPreferenceCommand and SetPreferenceCommand
- GetTrackInfo and SetTrackInfo
- GetInfoCommand
- Help, Open, Save, Import and Export commands.
- Removed obsolete commands ExecMenu, GetProjectInfo and SetProjectInfo
which are now better handled by other commands.
- JSONify "GetInfo: Commands" output, i.e. commas in the right places.
- General work on better Doxygen.
- Lyrics -> LyricsPanel
- Meter -> MeterPanel
- Updated Linux makefile.
- Scripting commands added into Extra menu.
- Distinct names for previously duplicated find-clipping parameters.
- Fixed longstanding error with erroneous status field number which
previously caused an ASSERT in debug.
- Sensible formatting of numbers in Chains, 0.1 not 0.1000000000137
2018-01-14 18:51:41 +00:00
bool EffectEqualization : : DefineParams ( ShuttleParams & S ) {
S . SHUTTLE_PARAM ( mM , FilterLength ) ;
2019-08-21 16:56:31 +00:00
//S.SHUTTLE_PARAM( mCurveName, CurveName);
Automation: AudacityCommand
This is a squash of 50 commits.
This merges the capabilities of BatchCommands and Effects using a new
AudacityCommand class. AudacityCommand provides one function to specify the
parameters, and then we leverage that one function in automation, whether by chains,
mod-script-pipe or (future) Nyquist.
- Now have AudacityCommand which is using the same mechanism as Effect
- Has configurable parameters
- Has data-entry GUI (built using shuttle GUI)
- Registers with PluginManager.
- Menu commands now provided in chains, and to python batch.
- Tested with Zoom Toggle.
- ShuttleParams now can set, get, set defaults, validate and specify
the parameters.
- Bugfix: Don't overwrite values with defaults first time out.
- Add DefineParams function for all built-in effects.
- Extend CommandContext to carry output channels for results.
We abuse EffectsManager. It handles both Effects and
AudacityCommands now. In time an Effect should become a special case of
AudacityCommand and we'll split and rename the EffectManager class.
- Don't use 'default' as a parameter name.
- Massive renaming for CommandDefinitionInterface
- EffectIdentInterface becomes EffectDefinitionInterface
- EffectAutomationParameters becomes CommandAutomationParameters
- PluginType is now a bit field.
This way we can search for related types at the same time.
- Most old batch commands made into AudacityCommands.
The ones that weren't are for a reason. They are used by mod-script-pipe
to carry commands and responses across from a non-GUI thread to the GUI
thread.
- Major tidy up of ScreenshotCommand
- Reworking of SelectCommand
- GetPreferenceCommand and SetPreferenceCommand
- GetTrackInfo and SetTrackInfo
- GetInfoCommand
- Help, Open, Save, Import and Export commands.
- Removed obsolete commands ExecMenu, GetProjectInfo and SetProjectInfo
which are now better handled by other commands.
- JSONify "GetInfo: Commands" output, i.e. commas in the right places.
- General work on better Doxygen.
- Lyrics -> LyricsPanel
- Meter -> MeterPanel
- Updated Linux makefile.
- Scripting commands added into Extra menu.
- Distinct names for previously duplicated find-clipping parameters.
- Fixed longstanding error with erroneous status field number which
previously caused an ASSERT in debug.
- Sensible formatting of numbers in Chains, 0.1 not 0.1000000000137
2018-01-14 18:51:41 +00:00
S . SHUTTLE_PARAM ( mLin , InterpLin ) ;
2018-03-27 01:06:35 +00:00
S . SHUTTLE_ENUM_PARAM ( mInterp , InterpMeth , kInterpStrings , nInterpolations ) ;
2015-04-17 03:53:42 +00:00
2019-08-21 16:56:31 +00:00
// if saving the preferences...
if ( dynamic_cast < ShuttleGetAutomation * > ( & S ) )
{
int numPoints = mCurves [ 0 ] . points . size ( ) ;
int point ;
for ( point = 0 ; point < numPoints ; point + + )
{
const wxString nameFreq = wxString : : Format ( " f%i " , point ) ;
const wxString nameVal = wxString : : Format ( " v%i " , point ) ;
S . Define ( mCurves [ 0 ] . points [ point ] . Freq , nameFreq , 0.0 , 0.0 , 0.0 , 0.0 ) ;
S . Define ( mCurves [ 0 ] . points [ point ] . dB , nameVal , 0.0 , 0.0 , 0.0 , 0.0 ) ;
}
}
else
{
mCurves [ 0 ] . points . clear ( ) ;
2019-08-23 10:54:14 +00:00
for ( int i = 0 ; i < 200 ; i + + )
2019-08-21 16:56:31 +00:00
{
const wxString nameFreq = wxString : : Format ( " f%i " , i ) ;
const wxString nameVal = wxString : : Format ( " v%i " , i ) ;
2019-08-23 10:54:14 +00:00
double f = - 1000.0 ;
double d = 0.0 ;
2019-08-23 15:39:05 +00:00
S . Define ( f , nameFreq , 0.0 , - 10000.0 , 1000000.0 , 0.0 ) ;
2019-08-21 16:56:31 +00:00
S . Define ( d , nameVal , 0.0 , - 10000.0 , 10000.0 , 0.0 ) ;
2019-08-23 10:54:14 +00:00
if ( f < = 0.0 )
break ;
2019-08-21 16:56:31 +00:00
mCurves [ 0 ] . points . push_back ( EQPoint ( f , d ) ) ;
}
setCurve ( 0 ) ;
}
Automation: AudacityCommand
This is a squash of 50 commits.
This merges the capabilities of BatchCommands and Effects using a new
AudacityCommand class. AudacityCommand provides one function to specify the
parameters, and then we leverage that one function in automation, whether by chains,
mod-script-pipe or (future) Nyquist.
- Now have AudacityCommand which is using the same mechanism as Effect
- Has configurable parameters
- Has data-entry GUI (built using shuttle GUI)
- Registers with PluginManager.
- Menu commands now provided in chains, and to python batch.
- Tested with Zoom Toggle.
- ShuttleParams now can set, get, set defaults, validate and specify
the parameters.
- Bugfix: Don't overwrite values with defaults first time out.
- Add DefineParams function for all built-in effects.
- Extend CommandContext to carry output channels for results.
We abuse EffectsManager. It handles both Effects and
AudacityCommands now. In time an Effect should become a special case of
AudacityCommand and we'll split and rename the EffectManager class.
- Don't use 'default' as a parameter name.
- Massive renaming for CommandDefinitionInterface
- EffectIdentInterface becomes EffectDefinitionInterface
- EffectAutomationParameters becomes CommandAutomationParameters
- PluginType is now a bit field.
This way we can search for related types at the same time.
- Most old batch commands made into AudacityCommands.
The ones that weren't are for a reason. They are used by mod-script-pipe
to carry commands and responses across from a non-GUI thread to the GUI
thread.
- Major tidy up of ScreenshotCommand
- Reworking of SelectCommand
- GetPreferenceCommand and SetPreferenceCommand
- GetTrackInfo and SetTrackInfo
- GetInfoCommand
- Help, Open, Save, Import and Export commands.
- Removed obsolete commands ExecMenu, GetProjectInfo and SetProjectInfo
which are now better handled by other commands.
- JSONify "GetInfo: Commands" output, i.e. commas in the right places.
- General work on better Doxygen.
- Lyrics -> LyricsPanel
- Meter -> MeterPanel
- Updated Linux makefile.
- Scripting commands added into Extra menu.
- Distinct names for previously duplicated find-clipping parameters.
- Fixed longstanding error with erroneous status field number which
previously caused an ASSERT in debug.
- Sensible formatting of numbers in Chains, 0.1 not 0.1000000000137
2018-01-14 18:51:41 +00:00
return true ;
}
2018-02-21 14:24:25 +00:00
bool EffectEqualization : : GetAutomationParameters ( CommandParameters & parms )
2015-04-17 03:53:42 +00:00
{
2016-09-19 10:47:23 +00:00
parms . Write ( KEY_FilterLength , ( unsigned long ) mM ) ;
2019-08-21 21:22:13 +00:00
//parms.Write(KEY_CurveName, mCurveName);
2015-08-16 20:04:24 +00:00
parms . Write ( KEY_InterpLin , mLin ) ;
2018-03-27 01:06:35 +00:00
parms . WriteEnum ( KEY_InterpMeth , mInterp , kInterpStrings , nInterpolations ) ;
2015-04-17 03:53:42 +00:00
return true ;
}
2018-02-21 14:24:25 +00:00
bool EffectEqualization : : SetAutomationParameters ( CommandParameters & parms )
2015-04-17 03:53:42 +00:00
{
// Pretty sure the interpolation name shouldn't have been interpreted when
// specified in chains, but must keep it that way for compatibility.
ReadAndVerifyInt ( FilterLength ) ;
2019-08-21 21:22:13 +00:00
//ReadAndVerifyString(CurveName);
2015-04-17 03:53:42 +00:00
ReadAndVerifyBool ( InterpLin ) ;
2018-03-27 01:06:35 +00:00
ReadAndVerifyEnum ( InterpMeth , kInterpStrings , nInterpolations ) ;
2015-04-17 03:53:42 +00:00
mM = FilterLength ;
2019-08-21 21:22:13 +00:00
//mCurveName = CurveName;
2015-04-17 03:53:42 +00:00
mLin = InterpLin ;
mInterp = InterpMeth ;
2018-02-09 19:12:25 +00:00
if ( InterpMeth > = nInterpolations )
2015-04-17 03:53:42 +00:00
{
2018-02-09 19:12:25 +00:00
InterpMeth - = nInterpolations ;
2015-04-17 03:53:42 +00:00
}
2016-08-04 11:33:22 +00:00
mEnvelope = ( mLin ? mLinEnvelope : mLogEnvelope ) . get ( ) ;
2015-04-17 03:53:42 +00:00
return true ;
}
2019-08-24 12:55:54 +00:00
// This function Apparently not used anymore.
2015-04-27 15:52:42 +00:00
bool EffectEqualization : : LoadFactoryDefaults ( )
{
mdBMin = DEF_dBMin ;
mdBMax = DEF_dBMax ;
mDrawMode = DEF_DrawMode ;
mDrawGrid = DEF_DrawGrid ;
2019-03-22 11:10:41 +00:00
if ( mOptions = = kEqOptionCurve )
mDrawMode = true ;
if ( mOptions = = kEqOptionGraphic )
mDrawMode = false ;
2015-04-27 15:52:42 +00:00
return Effect : : LoadFactoryDefaults ( ) ;
}
2020-02-17 13:21:44 +00:00
// Constants determining who the prests are for.
const bool kCURVE = false ;
const bool kBOTH = true ;
2019-08-24 12:55:54 +00:00
static const struct
{
2020-02-17 13:21:44 +00:00
const bool bForBoth ; // more extended set is used for Filter EQ
// See Bug 2254 for rationale.
2019-11-30 18:32:18 +00:00
const TranslatableString name ;
2019-08-24 12:55:54 +00:00
const wxChar * values ;
}
FactoryPresets [ ] =
{
2020-02-17 13:21:44 +00:00
{ kCURVE , XO ( " 100Hz Rumble " ) , wxT ( " f0= \" 20.0 \" v0= \" -80.0 \" f1= \" 49.237316986327 \" v1= \" -33.107692718506 \" f2= \" 54.196034330446 \" v2= \" -29.553844451904 \" f3= \" 88.033573501041 \" v3= \" -6.923076629639 \" f4= \" 95.871851182279 \" v4= \" -4.523078918457 \" f5= \" 108.957037410504 \" v5= \" -1.938461303711 \" f6= \" 123.828171198057 \" v6= \" -0.73846244812 \" f7= \" 149.228077614658 \" v7= \" -0.092308044434 \" " ) } ,
{ kCURVE , XO ( " AM Radio " ) , wxT ( " f0= \" 20.0 \" v0= \" -63.67 \" f1= \" 31.0 \" v1= \" -33.219 \" f2= \" 50.0 \" v2= \" -3.01 \" f3= \" 63.0 \" v3= \" -0.106 \" f4= \" 100.0 \" v4= \" 0.0 \" f5= \" 2500.0 \" v5= \" 0.0 \" f6= \" 4000.0 \" v6= \" -0.614 \" f7= \" 5000.0 \" v7= \" -8.059 \" f8= \" 8000.0 \" v8= \" -39.981 \" f9= \" 20000.0 \" v9= \" -103.651 \" f10= \" 48000.0 \" v10= \" -164.485 \" " ) } ,
{ kBOTH , XO ( " Bass Boost " ) , wxT ( " f0= \" 100.0 \" v0= \" 9.0 \" f1= \" 500.0 \" v1= \" 0.0 \" " ) } ,
{ kBOTH , XO ( " Bass Cut " ) , wxT ( " f0= \" 150.0 \" v0= \" -50.0 \" f1= \" 300.0 \" v1= \" 0.0 \" " ) } ,
{ kCURVE , XO ( " Low rolloff for speech " ) , wxT ( " f0= \" 50.0 \" v0= \" -120.0 \" f1= \" 60.0 \" v1= \" -50.0 \" f2= \" 65.0 \" v2= \" -24.0 \" f3= \" 70.0 \" v3= \" -12.0 \" f4= \" 80.0 \" v4= \" -4.0 \" f5= \" 90.0 \" v5= \" -1.0 \" f6= \" 100.0 \" v6= \" 0.0 \" " ) } ,
{ kBOTH , XO ( " RIAA " ) , wxT ( " f0= \" 20.0 \" v0= \" 19.274 \" f1= \" 25.0 \" v1= \" 18.954 \" f2= \" 31.0 \" v2= \" 18.516 \" f3= \" 40.0 \" v3= \" 17.792 \" f4= \" 50.0 \" v4= \" 16.946 \" f5= \" 63.0 \" v5= \" 15.852 \" f6= \" 80.0 \" v6= \" 14.506 \" f7= \" 100.0 \" v7= \" 13.088 \" f8= \" 125.0 \" v8= \" 11.563 \" f9= \" 160.0 \" v9= \" 9.809 \" f10= \" 200.0 \" v10= \" 8.219 \" f11= \" 250.0 \" v11= \" 6.677 \" f12= \" 315.0 \" v12= \" 5.179 \" f13= \" 400.0 \" v13= \" 3.784 \" f14= \" 500.0 \" v14= \" 2.648 \" f15= \" 630.0 \" v15= \" 1.642 \" f16= \" 800.0 \" v16= \" 0.751 \" f17= \" 1000.0 \" v17= \" 0.0 \" f18= \" 1250.0 \" v18= \" -0.744 \" f19= \" 1600.0 \" v19= \" -1.643 \" f20= \" 2000.0 \" v20= \" -2.589 \" f21= \" 2500.0 \" v21= \" -3.7 \" f22= \" 3150.0 \" v22= \" -5.038 \" f23= \" 4000.0 \" v23= \" -6.605 \" f24= \" 5000.0 \" v24= \" -8.21 \" f25= \" 6300.0 \" v25= \" -9.98 \" f26= \" 8000.0 \" v26= \" -11.894 \" f27= \" 10000.0 \" v27= \" -13.734 \" f28= \" 12500.0 \" v28= \" -15.609 \" f29= \" 16000.0 \" v29= \" -17.708 \" f30= \" 20000.0 \" v30= \" -19.62 \" f31= \" 25000.0 \" v31= \" -21.542 \" f32= \" 48000.0 \" v32= \" -27.187 \" " ) } ,
{ kCURVE , XO ( " Telephone " ) , wxT ( " f0= \" 20.0 \" v0= \" -94.087 \" f1= \" 200.0 \" v1= \" -14.254 \" f2= \" 250.0 \" v2= \" -7.243 \" f3= \" 315.0 \" v3= \" -2.245 \" f4= \" 400.0 \" v4= \" -0.414 \" f5= \" 500.0 \" v5= \" 0.0 \" f6= \" 2500.0 \" v6= \" 0.0 \" f7= \" 3150.0 \" v7= \" -0.874 \" f8= \" 4000.0 \" v8= \" -3.992 \" f9= \" 5000.0 \" v9= \" -9.993 \" f10= \" 48000.0 \" v10= \" -88.117 \" " ) } ,
{ kBOTH , XO ( " Treble Boost " ) , wxT ( " f0= \" 4000.0 \" v0= \" 0.0 \" f1= \" 5000.0 \" v1= \" 9.0 \" " ) } ,
{ kBOTH , XO ( " Treble Cut " ) , wxT ( " f0= \" 6000.0 \" v0= \" 0.0 \" f1= \" 10000.0 \" v1= \" -110.0 \" " ) } ,
{ kCURVE , XO ( " Walkie-talkie " ) , wxT ( " f0= \" 100.0 \" v0= \" -120.0 \" f1= \" 101.0 \" v1= \" 0.0 \" f2= \" 2000.0 \" v2= \" 0.0 \" f3= \" 2001.0 \" v3= \" -120.0 \" " ) } ,
2019-08-24 12:55:54 +00:00
} ;
RegistryPaths EffectEqualization : : GetFactoryPresets ( )
{
RegistryPaths names ;
for ( size_t i = 0 ; i < WXSIZEOF ( FactoryPresets ) ; i + + )
{
2020-02-17 13:21:44 +00:00
if ( ( mOptions = = kEqOptionGraphic ) & & ( FactoryPresets [ i ] . bForBoth = = false ) )
continue ;
2019-11-30 18:32:18 +00:00
names . push_back ( FactoryPresets [ i ] . name . Translation ( ) ) ;
2019-08-24 12:55:54 +00:00
}
return names ;
}
bool EffectEqualization : : LoadFactoryPreset ( int id )
{
2020-02-17 13:21:44 +00:00
int index = - 1 ;
for ( size_t i = 0 ; i < WXSIZEOF ( FactoryPresets ) ; i + + )
2019-08-24 12:55:54 +00:00
{
2020-02-17 13:21:44 +00:00
if ( ( mOptions = = kEqOptionGraphic ) & & ( FactoryPresets [ i ] . bForBoth = = false ) )
continue ;
if ( id - - = = 0 ) {
index = i ;
break ;
}
2019-08-24 12:55:54 +00:00
}
2020-02-17 13:21:44 +00:00
if ( index < 0 )
return false ;
2019-08-24 12:55:54 +00:00
// mParams =
2020-02-17 13:21:44 +00:00
wxString params = FactoryPresets [ index ] . values ;
2019-08-24 12:55:54 +00:00
CommandParameters eap ( params ) ;
ShuttleSetAutomation S ;
S . SetForWriting ( & eap ) ;
DefineParams ( S ) ;
if ( mUIDialog )
{
TransferDataToWindow ( ) ;
}
return true ;
}
2015-04-17 03:53:42 +00:00
// EffectUIClientInterface implementation
bool EffectEqualization : : ValidateUI ( )
{
2018-03-16 18:47:39 +00:00
// If editing a macro, we don't want to be using the unnamed curve so
2015-04-17 03:53:42 +00:00
// we offer to save it.
2017-10-12 02:38:42 +00:00
2019-02-28 13:54:36 +00:00
if ( mDisallowCustom & & mCurveName = = wxT ( " unnamed " ) )
2015-04-17 03:53:42 +00:00
{
2017-10-12 02:38:42 +00:00
// PRL: This is unreachable. mDisallowCustom is always false.
2019-12-19 19:19:51 +00:00
Effect : : MessageBox (
XO ( " To use this filter curve in a macro, please choose a new name for it. \n Choose the 'Save/Manage Curves...' button and rename the 'unnamed' curve, then use that one. " ) ,
2015-04-17 03:53:42 +00:00
wxOK | wxCENTRE ,
2020-01-10 17:02:31 +00:00
XO ( " Filter Curve EQ needs a different name " ) ) ;
2015-04-17 03:53:42 +00:00
return false ;
}
2015-08-15 17:45:16 +00:00
// Update unnamed curve (so it's there for next time)
//(done in a hurry, may not be the neatest -MJS)
2015-08-27 15:15:02 +00:00
if ( mDirty & & ! mDrawMode )
2015-08-15 17:45:16 +00:00
{
2016-04-14 16:35:15 +00:00
size_t numPoints = mLogEnvelope - > GetNumberOfPoints ( ) ;
Doubles when { numPoints } ;
Doubles value { numPoints } ;
mLogEnvelope - > GetPoints ( when . get ( ) , value . get ( ) , numPoints ) ;
for ( size_t i = 0 , j = 0 ; j + 2 < numPoints ; i + + , j + + )
2015-08-15 17:45:16 +00:00
{
if ( ( value [ i ] < value [ i + 1 ] + .05 ) & & ( value [ i ] > value [ i + 1 ] - .05 ) & &
( value [ i + 1 ] < value [ i + 2 ] + .05 ) & & ( value [ i + 1 ] > value [ i + 2 ] - .05 ) )
{ // within < 0.05 dB?
mLogEnvelope - > Delete ( j + 1 ) ;
numPoints - - ;
j - - ;
}
}
2018-02-02 18:24:53 +00:00
Select ( ( int ) mCurves . size ( ) - 1 ) ;
2015-08-15 17:45:16 +00:00
}
SaveCurves ( ) ;
2015-04-17 03:53:42 +00:00
SetPrivateConfig ( GetCurrentSettingsGroup ( ) , wxT ( " dBMin " ) , mdBMin ) ;
SetPrivateConfig ( GetCurrentSettingsGroup ( ) , wxT ( " dBMax " ) , mdBMax ) ;
SetPrivateConfig ( GetCurrentSettingsGroup ( ) , wxT ( " DrawMode " ) , mDrawMode ) ;
SetPrivateConfig ( GetCurrentSettingsGroup ( ) , wxT ( " DrawGrid " ) , mDrawGrid ) ;
return true ;
}
// Effect implementation
2019-03-22 11:10:41 +00:00
wxString EffectEqualization : : GetPrefsPrefix ( )
2015-04-17 03:53:42 +00:00
{
wxString base = wxT ( " /Effects/Equalization/ " ) ;
2019-03-22 11:10:41 +00:00
if ( mOptions = = kEqOptionGraphic )
base = wxT ( " /Effects/GraphicEq/ " ) ;
else if ( mOptions = = kEqOptionCurve )
base = wxT ( " /Effects/FilterCurve/ " ) ;
return base ;
}
bool EffectEqualization : : Startup ( )
{
wxString base = GetPrefsPrefix ( ) ;
2015-04-17 03:53:42 +00:00
// 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 ) )
{
// These get saved to the current preset
2016-09-08 18:28:34 +00:00
int filterLength ;
gPrefs - > Read ( base + wxT ( " FilterLength " ) , & filterLength , 4001 ) ;
mM = std : : max ( 0 , filterLength ) ;
2015-04-17 03:53:42 +00:00
if ( ( mM < 21 ) | | ( mM > 8191 ) ) { // corrupted Prefs?
mM = 4001 ; //default
}
gPrefs - > Read ( base + wxT ( " CurveName " ) , & mCurveName , wxT ( " unnamed " ) ) ;
gPrefs - > Read ( base + wxT ( " Lin " ) , & mLin , false ) ;
gPrefs - > Read ( base + wxT ( " Interp " ) , & mInterp , 0 ) ;
SaveUserPreset ( GetCurrentSettingsGroup ( ) ) ;
// These persist across preset changes
double dBMin ;
gPrefs - > Read ( base + wxT ( " dBMin " ) , & dBMin , - 30.0 ) ;
if ( ( dBMin < - 120 ) | | ( dBMin > - 10 ) ) { // corrupted Prefs?
dBMin = - 30 ; //default
}
mdBMin = dBMin ;
SetPrivateConfig ( GetCurrentSettingsGroup ( ) , wxT ( " dBMin " ) , mdBMin ) ;
double dBMax ;
gPrefs - > Read ( base + wxT ( " dBMax " ) , & dBMax , 30. ) ;
if ( ( dBMax < 0 ) | | ( dBMax > 60 ) ) { // corrupted Prefs?
dBMax = 30 ; //default
}
mdBMax = dBMax ;
SetPrivateConfig ( GetCurrentSettingsGroup ( ) , wxT ( " dBMax " ) , mdBMax ) ;
gPrefs - > Read ( base + wxT ( " DrawMode " ) , & mDrawMode , true ) ;
SetPrivateConfig ( GetCurrentSettingsGroup ( ) , wxT ( " DrawMode " ) , mDrawMode ) ;
gPrefs - > Read ( base + wxT ( " DrawGrid " ) , & mDrawGrid , true ) ;
SetPrivateConfig ( GetCurrentSettingsGroup ( ) , wxT ( " DrawGrid " ) , mDrawGrid ) ;
// Do not migrate again
gPrefs - > Write ( base + wxT ( " Migrated " ) , true ) ;
gPrefs - > Flush ( ) ;
}
return true ;
}
2010-01-23 19:44:49 +00:00
bool EffectEqualization : : Init ( )
{
2014-01-08 01:03:31 +00:00
int selcount = 0 ;
2014-01-09 00:13:34 +00:00
double rate = 0.0 ;
2018-09-11 17:07:32 +00:00
auto trackRange =
2019-05-21 18:38:43 +00:00
TrackList : : Get ( * FindProject ( ) ) . Selected < const WaveTrack > ( ) ;
2018-09-11 17:07:32 +00:00
if ( trackRange ) {
rate = ( * ( trackRange . first + + ) ) - > GetRate ( ) ;
+ + selcount ;
for ( auto track : trackRange ) {
if ( track - > GetRate ( ) ! = rate ) {
2019-12-19 19:19:51 +00:00
Effect : : MessageBox (
XO (
" To apply Equalization, all selected tracks must have the same sample rate. " ) ) ;
2018-09-11 17:07:32 +00:00
return ( false ) ;
2014-01-08 01:03:31 +00:00
}
2018-09-11 17:07:32 +00:00
+ + selcount ;
2014-01-08 01:03:31 +00:00
}
}
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
mHiFreq = rate / 2.0 ;
2016-03-08 21:38:13 +00:00
// Unlikely, but better than crashing.
if ( mHiFreq < = loFreqI ) {
2019-12-19 19:19:51 +00:00
Effect : : MessageBox (
XO ( " Track sample rate is too low for this effect. " ) ,
wxOK | wxCENTRE ,
XO ( " Effect Unavailable " ) ) ;
2016-03-08 21:38:13 +00:00
return ( false ) ;
}
2015-04-17 03:53:42 +00:00
mLoFreq = loFreqI ;
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
mBandsInUse = 0 ;
while ( kThirdOct [ mBandsInUse ] < = mHiFreq ) {
mBandsInUse + + ;
if ( mBandsInUse = = NUMBER_OF_BANDS )
break ;
2010-01-23 19:44:49 +00:00
}
2016-08-04 11:33:22 +00:00
mEnvelope = ( mLin ? mLinEnvelope : mLogEnvelope ) . get ( ) ;
2015-08-15 17:45:16 +00:00
setCurve ( mCurveName ) ;
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
CalcFilter ( ) ;
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
return ( true ) ;
2010-01-23 19:44:49 +00:00
}
bool EffectEqualization : : Process ( )
{
2014-01-16 17:55:35 +00:00
# ifdef EXPERIMENTAL_EQ_SSE_THREADED
2016-04-14 16:35:15 +00:00
if ( mEffectEqualization48x ) {
2014-01-16 17:55:35 +00:00
if ( mBench ) {
mBench = false ;
return mEffectEqualization48x - > Benchmark ( this ) ;
2016-04-14 16:35:15 +00:00
}
else
2014-01-16 17:55:35 +00:00
return mEffectEqualization48x - > Process ( this ) ;
2016-04-14 16:35:15 +00:00
}
2014-01-16 17:55:35 +00:00
# endif
2010-01-23 19:44:49 +00:00
this - > CopyInputTracks ( ) ; // Set up mOutputTracks.
2019-11-08 20:12:13 +00:00
CalcFilter ( ) ;
2010-01-23 19:44:49 +00:00
bool bGoodResult = true ;
int count = 0 ;
2018-09-11 17:07:32 +00:00
for ( auto track : mOutputTracks - > Selected < WaveTrack > ( ) ) {
2010-01-23 19:44:49 +00:00
double trackStart = track - > GetStartTime ( ) ;
double trackEnd = track - > GetEndTime ( ) ;
double t0 = mT0 < trackStart ? trackStart : mT0 ;
double t1 = mT1 > trackEnd ? trackEnd : mT1 ;
if ( t1 > t0 ) {
2016-08-24 15:24:26 +00:00
auto start = track - > TimeToLongSamples ( t0 ) ;
auto end = track - > TimeToLongSamples ( t1 ) ;
auto len = end - start ;
2010-01-23 19:44:49 +00:00
if ( ! ProcessOne ( count , track , start , len ) )
{
bGoodResult = false ;
break ;
}
}
count + + ;
}
2014-06-03 20:30:19 +00:00
this - > ReplaceProcessedTracks ( bGoodResult ) ;
2010-01-23 19:44:49 +00:00
return bGoodResult ;
}
2015-04-17 03:53:42 +00:00
bool EffectEqualization : : CloseUI ( )
2010-01-23 19:44:49 +00:00
{
2015-08-15 17:45:16 +00:00
mCurve = NULL ;
mPanel = NULL ;
2016-12-16 20:32:56 +00:00
return Effect : : CloseUI ( ) ;
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
void EffectEqualization : : PopulateOrExchange ( ShuttleGui & S )
2010-01-23 19:44:49 +00:00
{
2020-02-26 12:30:48 +00:00
if ( ( S . GetMode ( ) = = eIsCreating ) & & ! IsBatchProcessing ( ) )
2017-10-26 11:21:00 +00:00
LoadUserPreset ( GetCurrentSettingsGroup ( ) ) ;
2019-08-23 17:41:26 +00:00
//LoadCurves();
2010-01-23 19:44:49 +00:00
2020-03-02 18:57:09 +00:00
2010-01-23 19:44:49 +00:00
2015-08-15 17:45:16 +00:00
S . SetBorder ( 0 ) ;
S . SetSizerProportion ( 1 ) ;
2019-08-22 18:34:43 +00:00
S . Prop ( 1 ) . StartMultiColumn ( 1 , wxEXPAND ) ;
2010-01-23 19:44:49 +00:00
{
2015-08-15 17:45:16 +00:00
S . SetStretchyCol ( 0 ) ;
2019-08-22 18:34:43 +00:00
//S.SetStretchyRow(0); // The 5px Top border
S . SetStretchyRow ( 1 ) ; // The Graph
S . SetStretchyRow ( 2 ) ; // The EQ sliders
2015-08-15 17:45:16 +00:00
szrV = S . GetSizer ( ) ;
// -------------------------------------------------------------------
2019-08-22 18:34:43 +00:00
// ROW 0: Top border
2015-08-15 17:45:16 +00:00
// -------------------------------------------------------------------
S . AddSpace ( 5 ) ;
2019-08-22 18:34:43 +00:00
// -------------------------------------------------------------------
// ROW 1: Equalization panel and sliders for vertical scale
// -------------------------------------------------------------------
2015-08-15 17:45:16 +00:00
S . SetSizerProportion ( 1 ) ;
2019-08-22 18:34:43 +00:00
S . Prop ( 1 ) . StartMultiColumn ( 3 , wxEXPAND ) ;
2015-08-15 17:45:16 +00:00
{
S . SetStretchyCol ( 1 ) ;
S . SetStretchyRow ( 0 ) ;
szr1 = S . GetSizer ( ) ;
2019-08-22 18:34:43 +00:00
S . StartVerticalLay ( wxEXPAND , 1 ) ;
2015-08-15 17:45:16 +00:00
{
2017-11-01 13:16:56 +00:00
mdBRuler = safenew RulerPanel (
2017-10-20 16:45:27 +00:00
S . GetParent ( ) , wxID_ANY , wxVERTICAL ,
2017-11-01 13:16:56 +00:00
wxSize { 100 , 100 } , // Ruler can't handle small sizes
RulerPanel : : Range { 60.0 , - 120.0 } ,
Ruler : : LinearDBFormat ,
2019-12-29 01:11:08 +00:00
XO ( " dB " ) ,
2017-11-01 13:16:56 +00:00
RulerPanel : : Options { }
. LabelEdges ( true )
. TicksAtExtremes ( true )
. TickColour ( { 0 , 0 , 0 } )
) ;
2015-08-15 17:45:16 +00:00
2019-08-22 18:34:43 +00:00
S . Prop ( 0 ) . AddSpace ( 0 , 1 ) ;
2017-10-31 23:44:00 +00:00
S . Prop ( 1 )
. Position ( wxEXPAND )
. AddWindow ( mdBRuler ) ;
2015-08-15 17:45:16 +00:00
S . AddSpace ( 0 , 1 ) ;
}
S . EndVerticalLay ( ) ;
2017-10-20 16:45:27 +00:00
mPanel = safenew EqualizationPanel ( S . GetParent ( ) , wxID_ANY , this ) ;
2017-10-31 23:44:00 +00:00
S . Prop ( 1 )
. Position ( wxEXPAND )
2018-01-31 20:31:22 +00:00
. MinSize ( { wxDefaultCoord , wxDefaultCoord } )
2017-10-31 23:44:00 +00:00
. AddWindow ( mPanel ) ;
2010-01-23 19:44:49 +00:00
2015-08-15 17:45:16 +00:00
S . SetBorder ( 5 ) ;
S . StartVerticalLay ( ) ;
{
2019-12-22 22:01:20 +00:00
S . AddVariableText ( XO ( " + dB " ) , false , wxCENTER ) ;
2017-10-29 14:27:23 +00:00
mdBMaxSlider = S . Id ( ID_dBMax )
. Name ( XO ( " Max dB " ) )
2017-10-31 18:52:01 +00:00
. Style ( wxSL_VERTICAL | wxSL_INVERSE )
2017-10-29 14:27:23 +00:00
. AddSlider ( { } , 30 , 60 , 0 ) ;
2010-01-23 19:44:49 +00:00
# if wxUSE_ACCESSIBILITY
2019-12-28 03:30:19 +00:00
mdBMaxSlider - > SetAccessible ( safenew SliderAx ( mdBMaxSlider , XO ( " %d dB " ) ) ) ;
2010-01-23 19:44:49 +00:00
# endif
2015-08-15 17:45:16 +00:00
2017-10-29 14:27:23 +00:00
mdBMinSlider = S . Id ( ID_dBMin )
. Name ( XO ( " Min dB " ) )
2017-10-31 18:52:01 +00:00
. Style ( wxSL_VERTICAL | wxSL_INVERSE )
2017-10-29 14:27:23 +00:00
. AddSlider ( { } , - 30 , - 10 , - 120 ) ;
2019-12-22 22:01:20 +00:00
S . AddVariableText ( XO ( " - dB " ) , false , wxCENTER ) ;
2015-08-15 17:45:16 +00:00
# if wxUSE_ACCESSIBILITY
2019-12-28 03:30:19 +00:00
mdBMinSlider - > SetAccessible ( safenew SliderAx ( mdBMinSlider , XO ( " %d dB " ) ) ) ;
2013-10-11 20:01:41 +00:00
# endif
2015-08-15 17:45:16 +00:00
}
S . EndVerticalLay ( ) ;
S . SetBorder ( 0 ) ;
// -------------------------------------------------------------------
2019-08-22 18:34:43 +00:00
// Frequency ruler below graph
2015-08-15 17:45:16 +00:00
// -------------------------------------------------------------------
// Column 1 is empty
S . AddSpace ( 1 , 1 ) ;
2017-11-01 13:16:56 +00:00
mFreqRuler = safenew RulerPanel (
2017-10-20 16:45:27 +00:00
S . GetParent ( ) , wxID_ANY , wxHORIZONTAL ,
2017-11-01 13:16:56 +00:00
wxSize { 100 , 100 } , // Ruler can't handle small sizes
RulerPanel : : Range { mLoFreq , mHiFreq } ,
Ruler : : IntFormat ,
2019-12-29 01:11:08 +00:00
XO ( " Hz " ) ,
2017-11-01 13:16:56 +00:00
RulerPanel : : Options { }
. Log ( true )
. Flip ( true )
. LabelEdges ( true )
. TicksAtExtremes ( true )
. TickColour ( { 0 , 0 , 0 } )
) ;
2017-04-02 22:07:13 +00:00
2015-08-15 17:45:16 +00:00
S . SetBorder ( 1 ) ;
2017-10-31 23:44:00 +00:00
S . Prop ( 1 )
. Position ( wxEXPAND | wxALIGN_LEFT | wxALIGN_TOP | wxLEFT )
. AddWindow ( mFreqRuler ) ;
2015-08-15 17:45:16 +00:00
S . SetBorder ( 0 ) ;
// Column 3 is empty
S . AddSpace ( 1 , 1 ) ;
}
S . EndMultiColumn ( ) ;
2010-01-23 19:44:49 +00:00
2015-08-15 17:45:16 +00:00
// -------------------------------------------------------------------
2019-08-22 18:34:43 +00:00
// ROW 2: Graphic EQ
2015-08-15 17:45:16 +00:00
// -------------------------------------------------------------------
2019-08-22 18:34:43 +00:00
S . SetSizerProportion ( 1 ) ;
S . StartHorizontalLay ( wxEXPAND , 1 ) ;
2015-08-15 17:45:16 +00:00
{
szrG = S . GetSizer ( ) ;
2010-01-23 19:44:49 +00:00
2015-08-15 17:45:16 +00:00
// Panel used to host the sliders since they will be positioned manually.
2018-02-04 00:51:41 +00:00
//mGraphicPanel = S.Prop(1)
2017-10-31 23:44:00 +00:00
//.Position(wxEXPAND)
2018-02-04 00:51:41 +00:00
//.Size( { -1, 150 } )
//.StartPanel();
2019-08-22 18:34:43 +00:00
wxWindow * pParent = S . GetParent ( ) ;
S . AddSpace ( 15 , 0 ) ;
2018-02-04 00:51:41 +00:00
{
2017-10-31 23:44:00 +00:00
2020-02-17 22:17:16 +00:00
// for (int i = 0; (i < NUMBER_OF_BANDS) && (kThirdOct[i] <= mHiFreq); ++i)
// May show more sliders than needed. Fixes Bug 2269
for ( int i = 0 ; i < NUMBER_OF_BANDS ; + + i )
2015-08-15 17:45:16 +00:00
{
2020-02-17 14:10:37 +00:00
TranslatableString freq = kThirdOct [ i ] < 1000.
? XO ( " %d Hz " ) . Format ( ( int ) kThirdOct [ i ] )
: XO ( " %g kHz " ) . Format ( kThirdOct [ i ] / 1000. ) ;
TranslatableString fNum = kThirdOct [ i ] < 1000.
2020-02-22 19:02:28 +00:00
? Verbatim ( " %d " ) . Format ( ( int ) kThirdOct [ i ] )
2020-02-23 19:41:58 +00:00
/* i18n-hint k is SI abbreviation for x1,000. Usually unchanged in translation. */
2020-02-17 14:10:37 +00:00
: XO ( " %gk " ) . Format ( kThirdOct [ i ] / 1000. ) ;
S . StartVerticalLay ( ) ;
{
S . AddFixedText ( fNum ) ;
mSliders [ i ] = safenew wxSliderWrapper ( pParent , ID_Slider + i , 0 , - 20 , + 20 ,
wxDefaultPosition , wxSize ( - 1 , 50 ) , wxSL_VERTICAL | wxSL_INVERSE ) ;
2015-08-15 17:45:16 +00:00
# if wxUSE_ACCESSIBILITY
2020-02-17 14:10:37 +00:00
mSliders [ i ] - > SetAccessible ( safenew SliderAx ( mSliders [ i ] , XO ( " %d dB " ) ) ) ;
2015-08-15 17:45:16 +00:00
# endif
2018-01-31 16:31:55 +00:00
2020-02-17 14:10:37 +00:00
mSlidersOld [ i ] = 0 ;
mEQVals [ i ] = 0. ;
S . Prop ( 1 )
. Name ( freq )
. ConnectRoot (
wxEVT_ERASE_BACKGROUND , & EffectEqualization : : OnErase )
. Position ( wxEXPAND )
. Size ( { - 1 , 50 } )
. AddWindow ( mSliders [ i ] ) ;
}
S . EndVerticalLay ( ) ;
2015-08-15 17:45:16 +00:00
}
2019-08-22 18:34:43 +00:00
S . AddSpace ( 15 , 0 ) ;
2018-02-04 00:51:41 +00:00
} //S.EndPanel();
2014-01-16 17:55:35 +00:00
}
2015-08-15 17:45:16 +00:00
S . EndHorizontalLay ( ) ;
2019-08-22 18:34:43 +00:00
// -------------------------------------------------------------------
// ROW 4: Various controls
// -------------------------------------------------------------------
S . SetSizerProportion ( 1 ) ;
S . Prop ( 1 ) . StartMultiColumn ( 7 , wxALIGN_CENTER_HORIZONTAL ) ;
2015-08-15 17:45:16 +00:00
{
S . SetBorder ( 5 ) ;
S . AddSpace ( 5 , 5 ) ;
2019-04-01 18:14:37 +00:00
if ( mOptions = = kEqLegacy )
2015-08-15 17:45:16 +00:00
{
2019-04-01 18:14:37 +00:00
S . StartHorizontalLay ( wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL ) ;
{
2020-05-11 15:28:14 +00:00
S . AddPrompt ( XXO ( " &EQ Type: " ) ) ;
2019-04-01 18:14:37 +00:00
}
S . EndHorizontalLay ( ) ;
2015-08-15 17:45:16 +00:00
S . StartHorizontalLay ( wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL , 1 ) ;
{
2019-03-22 11:10:41 +00:00
S . StartHorizontalLay ( wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL , 1 ) ;
{
2017-10-29 14:27:23 +00:00
mDraw = S . Id ( ID_Draw )
. Name ( XO ( " Draw Curves " ) )
2020-05-11 15:28:14 +00:00
. AddRadioButton ( XXO ( " &Draw " ) ) ;
2015-08-15 17:45:16 +00:00
2017-10-29 14:27:23 +00:00
mGraphic = S . Id ( ID_Graphic )
. Name ( XO ( " Graphic EQ " ) )
2020-05-11 15:28:14 +00:00
. AddRadioButtonToGroup ( XXO ( " &Graphic " ) ) ;
2019-03-22 11:10:41 +00:00
}
S . EndHorizontalLay ( ) ;
2015-08-15 17:45:16 +00:00
}
S . EndHorizontalLay ( ) ;
}
2019-08-22 18:34:43 +00:00
S . StartHorizontalLay ( wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL , 0 ) ;
2015-08-15 17:45:16 +00:00
{
szrH = S . GetSizer ( ) ;
S . StartHorizontalLay ( wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL , 1 ) ;
{
szrI = S . GetSizer ( ) ;
2019-02-22 20:15:19 +00:00
mInterpChoice = S . Id ( ID_Interp )
2017-10-29 14:27:23 +00:00
. Name ( XO ( " Interpolation type " ) )
2019-02-22 20:15:19 +00:00
. AddChoice ( { } ,
2019-12-18 03:52:42 +00:00
Msgids ( kInterpStrings , nInterpolations ) , 0 ) ;
2019-08-23 10:10:15 +00:00
# if wxUSE_ACCESSIBILITY
// so that name can be set on a standard control
mInterpChoice - > SetAccessible ( safenew WindowAccessible ( mInterpChoice ) ) ;
# endif
2015-08-15 17:45:16 +00:00
}
S . EndHorizontalLay ( ) ;
S . StartHorizontalLay ( wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL , 1 ) ;
{
szrL = S . GetSizer ( ) ;
2017-10-29 14:27:23 +00:00
mLinFreq = S . Id ( ID_Linear )
. Name ( XO ( " Linear Frequency Scale " ) )
2020-05-11 15:28:14 +00:00
. AddCheckBox ( XXO ( " Li&near Frequency Scale " ) , false ) ;
2015-08-15 17:45:16 +00:00
}
S . EndHorizontalLay ( ) ;
}
S . EndHorizontalLay ( ) ;
// -------------------------------------------------------------------
// Filter length grouping
// -------------------------------------------------------------------
2019-08-27 16:28:57 +00:00
if ( mOptions = = kEqLegacy ) {
S . StartHorizontalLay ( wxEXPAND , 0 ) ;
2015-08-15 17:45:16 +00:00
{
2019-08-27 16:28:57 +00:00
S . StartHorizontalLay ( wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL , 0 ) ;
{
2020-05-11 15:28:14 +00:00
S . AddPrompt ( XXO ( " Length of &Filter: " ) ) ;
2019-08-27 16:28:57 +00:00
}
S . EndHorizontalLay ( ) ;
2015-08-15 17:45:16 +00:00
2019-08-27 16:28:57 +00:00
S . StartHorizontalLay ( wxEXPAND , 1 ) ;
{
2017-10-29 14:27:23 +00:00
mMSlider = S . Id ( ID_Length )
. Name ( XO ( " Length of Filter " ) )
2017-10-31 18:52:01 +00:00
. Style ( wxSL_HORIZONTAL )
2017-10-29 14:27:23 +00:00
. AddSlider ( { } , ( mM - 1 ) / 2 , 4095 , 10 ) ;
2019-08-27 16:28:57 +00:00
}
S . EndHorizontalLay ( ) ;
2015-08-15 17:45:16 +00:00
2019-08-27 16:28:57 +00:00
S . StartHorizontalLay ( wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL , 0 ) ;
{
wxString label ;
label . Printf ( wxT ( " %ld " ) , mM ) ;
2019-12-19 21:20:41 +00:00
mMText = S . Name ( Verbatim ( label ) )
2017-10-29 14:27:23 +00:00
// fix for bug 577 (NVDA/Narrator screen readers do not
// read static text in dialogs)
2019-12-22 22:01:20 +00:00
. AddVariableText ( Verbatim ( label ) ) ;
2019-08-27 16:28:57 +00:00
}
S . EndHorizontalLay ( ) ;
2015-08-15 17:45:16 +00:00
}
S . EndHorizontalLay ( ) ;
2019-08-27 16:28:57 +00:00
S . AddSpace ( 1 , 1 ) ;
}
2015-08-15 17:45:16 +00:00
S . AddSpace ( 5 , 5 ) ;
2019-04-01 18:14:37 +00:00
if ( mOptions = = kEqLegacy ) {
S . AddSpace ( 5 , 5 ) ;
S . StartHorizontalLay ( wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL ) ;
{
2020-05-11 15:28:14 +00:00
S . AddPrompt ( XXO ( " &Select Curve: " ) ) ;
2019-04-01 18:14:37 +00:00
}
S . EndHorizontalLay ( ) ;
2015-08-15 17:45:16 +00:00
S . StartHorizontalLay ( wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL , 1 ) ;
{
2019-04-01 18:14:37 +00:00
S . StartHorizontalLay ( wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL , 1 ) ;
2015-08-15 17:45:16 +00:00
{
2019-02-22 20:15:19 +00:00
mCurve = S . Id ( ID_Curve )
2017-10-29 14:27:23 +00:00
. Name ( XO ( " Select Curve " ) )
2019-02-22 20:15:19 +00:00
. AddChoice ( { } ,
[ this ] {
2019-12-18 03:52:42 +00:00
TranslatableStrings curves ;
2019-02-22 20:15:19 +00:00
for ( const auto & curve : mCurves )
2019-12-18 03:52:42 +00:00
curves . push_back ( Verbatim ( curve . Name ) ) ;
2019-02-22 20:15:19 +00:00
return curves ;
} ( )
) ;
2019-04-01 18:14:37 +00:00
}
S . EndHorizontalLay ( ) ;
2015-08-15 17:45:16 +00:00
}
S . EndHorizontalLay ( ) ;
2019-03-22 11:10:41 +00:00
2020-05-11 15:28:14 +00:00
S . Id ( ID_Manage ) . AddButton ( XXO ( " S&ave/Manage Curves... " ) ) ;
2019-04-01 18:14:37 +00:00
}
2015-08-15 17:45:16 +00:00
S . StartHorizontalLay ( wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL , 1 ) ;
{
2020-05-11 15:28:14 +00:00
S . Id ( ID_Clear ) . AddButton ( XXO ( " Fla&tten " ) ) ;
S . Id ( ID_Invert ) . AddButton ( XXO ( " &Invert " ) ) ;
2015-08-15 17:45:16 +00:00
2017-10-29 14:27:23 +00:00
mGridOnOff = S . Id ( ID_Grid )
. Name ( XO ( " Show grid lines " ) )
2020-05-11 15:28:14 +00:00
. AddCheckBox ( XXO ( " Show g&rid lines " ) , false ) ;
2015-08-15 17:45:16 +00:00
}
S . EndHorizontalLay ( ) ;
S . AddSpace ( 5 , 5 ) ;
2014-01-16 17:55:35 +00:00
}
2015-08-15 17:45:16 +00:00
S . EndMultiColumn ( ) ;
}
S . EndMultiColumn ( ) ;
# ifdef EXPERIMENTAL_EQ_SSE_THREADED
2016-04-03 15:37:18 +00:00
if ( mEffectEqualization48x )
2015-08-15 17:45:16 +00:00
{
// -------------------------------------------------------------------
// ROW 6: Processing routine selection
// -------------------------------------------------------------------
// Column 1 is blank
S . AddSpace ( 1 , 1 ) ;
S . StartHorizontalLay ( ) ;
{
2019-12-22 20:40:29 +00:00
S . AddUnits ( XO ( " &Processing: " ) ) ;
2015-08-15 17:45:16 +00:00
2018-01-29 20:37:20 +00:00
// update the control state
int mathPath = EffectEqualization48x : : GetMathPath ( ) ;
int value =
( mathPath & MATH_FUNCTION_SSE )
? ( mathPath & MATH_FUNCTION_THREADED )
? 2
: 1
: false // (mathPath & MATH_FUNCTION_AVX) // not implemented
? ( mathPath & MATH_FUNCTION_THREADED )
? 4
: 3
: 0 ;
mMathProcessingType [ 0 ] = S . Id ( ID_DefaultMath )
2020-05-11 15:28:14 +00:00
. AddRadioButton ( XXO ( " D&efault " ) ,
2018-01-29 20:37:20 +00:00
0 , value ) ;
mMathProcessingType [ 1 ] = S . Id ( ID_SSE )
2019-11-19 16:34:36 +00:00
. Disable ( ! EffectEqualization48x : : GetMathCaps ( ) - > SSE )
2020-05-11 15:28:14 +00:00
. AddRadioButtonToGroup ( XXO ( " &SSE " ) ,
2018-01-29 20:37:20 +00:00
1 , value ) ;
mMathProcessingType [ 2 ] = S . Id ( ID_SSEThreaded )
2019-11-19 16:34:36 +00:00
. Disable ( ! EffectEqualization48x : : GetMathCaps ( ) - > SSE )
2020-05-11 15:28:14 +00:00
. AddRadioButtonToGroup ( XXO ( " SSE &Threaded " ) ,
2018-01-29 20:37:20 +00:00
2 , value ) ;
mMathProcessingType [ 3 ] = S . Id ( ID_AVX )
2019-11-19 16:34:36 +00:00
// not implemented
. Disable ( true /* !EffectEqualization48x::GetMathCaps()->AVX */ )
2020-05-11 15:28:14 +00:00
. AddRadioButtonToGroup ( XXO ( " A&VX " ) ,
2018-01-29 20:37:20 +00:00
3 , value ) ;
mMathProcessingType [ 4 ] = S . Id ( ID_AVXThreaded )
2019-11-19 16:34:36 +00:00
// not implemented
. Disable ( true /* !EffectEqualization48x::GetMathCaps()->AVX */ )
2020-05-11 15:28:14 +00:00
. AddRadioButtonToGroup ( XXO ( " AV&X Threaded " ) ,
2018-01-29 20:37:20 +00:00
4 , value ) ;
2020-05-11 15:28:14 +00:00
S . Id ( ID_Bench ) . AddButton ( XXO ( " &Bench " ) ) ;
2014-01-16 17:55:35 +00:00
}
2015-08-15 17:45:16 +00:00
S . EndHorizontalLay ( ) ;
2014-01-16 17:55:35 +00:00
2015-08-15 17:45:16 +00:00
// Column 3 is blank
S . AddSpace ( 1 , 1 ) ;
2014-01-16 17:55:35 +00:00
}
# endif
2015-08-15 17:45:16 +00:00
mUIParent - > SetAutoLayout ( false ) ;
2019-08-21 21:22:13 +00:00
if ( mOptions ! = kEqOptionGraphic )
mUIParent - > Layout ( ) ;
2010-01-23 19:44:49 +00:00
2021-04-20 14:32:55 +00:00
if ( mOptions = = kEqOptionCurve )
mDrawMode = true ;
if ( mOptions = = kEqOptionGraphic )
mDrawMode = false ;
2016-09-23 12:53:47 +00:00
// "show" settings for graphics mode before setting the size of the dialog
// as this needs more space than draw mode
2021-04-20 14:32:55 +00:00
szrV - > Show ( szrG , ! mDrawMode ) ; // eq sliders
2016-09-23 12:53:47 +00:00
szrH - > Show ( szrI , true ) ; // interpolation choice
szrH - > Show ( szrL , false ) ; // linear freq checkbox
2010-01-23 19:44:49 +00:00
2019-08-21 21:22:13 +00:00
if ( mOptions = = kEqOptionGraphic ) {
mPanel - > Show ( false ) ;
wxSize sz = szrV - > GetMinSize ( ) ;
2020-02-17 14:10:37 +00:00
sz + = wxSize ( 30 , 0 ) ;
2018-01-30 06:06:23 +00:00
mUIParent - > SetMinSize ( sz ) ;
2019-08-21 21:22:13 +00:00
}
2019-08-22 18:34:43 +00:00
else {
mPanel - > Show ( true ) ;
szrV - > Show ( szr1 , true ) ;
2018-01-30 06:06:23 +00:00
mUIParent - > SetMinSize ( mUIParent - > GetBestSize ( ) ) ;
2019-08-22 18:34:43 +00:00
}
2019-08-23 17:41:26 +00:00
ForceRecalc ( ) ;
2018-01-30 06:06:23 +00:00
2010-01-23 19:44:49 +00:00
return ;
}
//
2015-04-17 03:53:42 +00:00
// Populate the window with relevant variables
2010-01-23 19:44:49 +00:00
//
2015-04-17 03:53:42 +00:00
bool EffectEqualization : : TransferDataToWindow ( )
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
// Set log or lin freq scale (affects interpolation as well)
2015-08-16 20:04:24 +00:00
mLinFreq - > SetValue ( mLin ) ;
2015-04-17 03:53:42 +00:00
wxCommandEvent dummyEvent ;
OnLinFreq ( dummyEvent ) ; // causes a CalcFilter
mGridOnOff - > SetValue ( mDrawGrid ) ; // checks/unchecks the box on the interface
2010-01-23 19:44:49 +00:00
2019-08-27 16:28:57 +00:00
if ( mMSlider )
mMSlider - > SetValue ( ( mM - 1 ) / 2 ) ;
2015-04-17 03:53:42 +00:00
mM = 0 ; // force refresh in TransferDataFromWindow()
mdBMinSlider - > SetValue ( ( int ) mdBMin ) ;
mdBMin = 0 ; // force refresh in TransferDataFromWindow()
mdBMaxSlider - > SetValue ( ( int ) mdBMax ) ;
mdBMax = 0 ; // force refresh in TransferDataFromWindow()
// Reload the curve names
2015-05-29 16:32:55 +00:00
UpdateCurves ( ) ;
2015-04-17 03:53:42 +00:00
// Set graphic interpolation mode
mInterpChoice - > SetSelection ( mInterp ) ;
2019-03-22 11:10:41 +00:00
// Override draw mode, if we're not displaying the radio buttons.
if ( mOptions = = kEqOptionCurve )
mDrawMode = true ;
if ( mOptions = = kEqOptionGraphic )
mDrawMode = false ;
2019-08-22 18:34:43 +00:00
if ( mDraw )
mDraw - > SetValue ( mDrawMode ) ;
szrV - > Show ( szr1 , mOptions ! = kEqOptionGraphic ) ; // Graph
szrV - > Show ( szrG , ! mDrawMode ) ; // eq sliders
2019-08-27 16:28:57 +00:00
szrH - > Show ( szrI , mOptions = = kEqLegacy ) ; // interpolation choice
2019-08-22 18:34:43 +00:00
szrH - > Show ( szrL , mDrawMode ) ; // linear freq checkbox
if ( mGraphic )
mGraphic - > SetValue ( ! mDrawMode ) ;
mGridOnOff - > Show ( mDrawMode ) ;
2015-04-17 03:53:42 +00:00
// Set Graphic (Fader) or Draw mode
2019-08-22 18:34:43 +00:00
if ( ! mDrawMode )
2016-09-24 08:26:46 +00:00
UpdateGraphic ( ) ;
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
TransferDataFromWindow ( ) ;
mUIParent - > Layout ( ) ;
wxGetTopLevelParent ( mUIParent ) - > Layout ( ) ;
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
return true ;
2010-01-23 19:44:49 +00:00
}
//
2015-04-17 03:53:42 +00:00
// Retrieve data from the window
2010-01-23 19:44:49 +00:00
//
2015-04-17 03:53:42 +00:00
bool EffectEqualization : : TransferDataFromWindow ( )
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
wxString tip ;
bool rr = false ;
float dB = ( float ) mdBMinSlider - > GetValue ( ) ;
if ( dB ! = mdBMin ) {
rr = true ;
mdBMin = dB ;
2018-01-04 01:47:56 +00:00
tip . Printf ( _ ( " %d dB " ) , ( int ) mdBMin ) ;
2015-04-17 03:53:42 +00:00
mdBMinSlider - > SetToolTip ( tip ) ;
}
dB = ( float ) mdBMaxSlider - > GetValue ( ) ;
if ( dB ! = mdBMax ) {
rr = true ;
mdBMax = dB ;
2018-01-04 01:47:56 +00:00
tip . Printf ( _ ( " %d dB " ) , ( int ) mdBMax ) ;
2015-04-17 03:53:42 +00:00
mdBMaxSlider - > SetToolTip ( tip ) ;
}
// Refresh ruler if values have changed
if ( rr ) {
int w1 , w2 , h ;
mdBRuler - > ruler . GetMaxSize ( & w1 , & h ) ;
mdBRuler - > ruler . SetRange ( mdBMax , mdBMin ) ;
mdBRuler - > ruler . GetMaxSize ( & w2 , & h ) ;
if ( w1 ! = w2 ) // Reduces flicker
{
mdBRuler - > SetSize ( wxSize ( w2 , h ) ) ;
mFreqRuler - > Refresh ( false ) ;
}
mdBRuler - > Refresh ( false ) ;
mPanel - > Refresh ( false ) ;
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
2019-08-27 16:28:57 +00:00
size_t m = DEF_FilterLength ; // m must be odd.
if ( mMSlider )
m = 2 * mMSlider - > GetValue ( ) + 1 ;
wxASSERT ( ( m & 1 ) = = 1 ) ;
2015-04-17 03:53:42 +00:00
if ( m ! = mM ) {
mM = m ;
2015-08-15 17:45:16 +00:00
ForceRecalc ( ) ;
2015-04-17 03:53:42 +00:00
2019-08-27 16:28:57 +00:00
if ( mMSlider )
{
tip . Printf ( wxT ( " %d " ) , ( int ) mM ) ;
mMText - > SetLabel ( tip ) ;
mMText - > SetName ( mMText - > GetLabel ( ) ) ; // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
mMSlider - > SetToolTip ( tip ) ;
}
2015-04-17 03:53:42 +00:00
}
2010-01-23 19:44:49 +00:00
return true ;
}
2015-04-17 03:53:42 +00:00
// EffectEqualization implementation
bool EffectEqualization : : ProcessOne ( int count , WaveTrack * t ,
sampleCount start , sampleCount len )
2010-01-23 19:44:49 +00:00
{
2016-02-13 15:43:16 +00:00
// create a NEW WaveTrack to hold all of the output, including 'tails' each end
Bug2346: Complete fix...
... without making undesirable dependency cycles.
Eliminate calls to NewWaveTrack in effects, but in Edit>Copy too, which was
not mentioned in the bug report. (Copying a track, deselecting all, and pasting
preserved CLIP colors, but not the TRACK color setting which applies to newly
generated clips.)
Instead, always use the new function WaveTrack::EmptyCopy from the track to be
later replaced, getting color information.
NewWaveTrack is still used in benchmark test, import, the Track menu
commands that make new tracks, recording to new tracks, and generators without
a selection, where there is no track to copy from.
Also when deserializing tracks from the .aup file, in which case the saved
color is later retrieved from the file.
Also, in mix-and-render, where other logic decides whether to copy colors
afterward.
See commit a9658e6ef7f7eaefce4dc37a93d389cca7705f41
2020-03-11 01:40:14 +00:00
auto output = t - > EmptyCopy ( ) ;
t - > ConvertToSampleFormat ( floatSample ) ;
2010-01-23 19:44:49 +00:00
2016-09-08 18:28:34 +00:00
wxASSERT ( mM - 1 < windowSize ) ;
size_t L = windowSize - ( mM - 1 ) ; //Process L samples at a go
2016-08-24 15:24:26 +00:00
auto s = start ;
auto idealBlockLen = t - > GetMaxBlockSize ( ) * 4 ;
2015-04-17 03:53:42 +00:00
if ( idealBlockLen % L ! = 0 )
idealBlockLen + = ( L - ( idealBlockLen % L ) ) ;
2010-01-23 19:44:49 +00:00
2016-04-14 16:35:15 +00:00
Floats buffer { idealBlockLen } ;
2010-01-23 19:44:49 +00:00
2016-04-14 16:35:15 +00:00
Floats window1 { windowSize } ;
Floats window2 { windowSize } ;
float * thisWindow = window1 . get ( ) ;
float * lastWindow = window2 . get ( ) ;
2010-01-23 19:44:49 +00:00
2016-08-24 15:24:26 +00:00
auto originalLen = len ;
2010-01-23 19:44:49 +00:00
2016-09-08 18:28:34 +00:00
for ( size_t i = 0 ; i < windowSize ; i + + )
2015-04-17 03:53:42 +00:00
lastWindow [ i ] = 0 ;
TrackProgress ( count , 0. ) ;
bool bLoopSuccess = true ;
2016-09-08 18:28:34 +00:00
size_t wcopy = 0 ;
int offset = ( mM - 1 ) / 2 ;
2015-04-17 03:53:42 +00:00
2016-08-22 19:04:15 +00:00
while ( len ! = 0 )
2015-04-17 03:53:42 +00:00
{
2016-08-27 02:02:58 +00:00
auto block = limitSampleBufferSize ( idealBlockLen , len ) ;
2015-04-17 03:53:42 +00:00
2016-04-14 16:35:15 +00:00
t - > Get ( ( samplePtr ) buffer . get ( ) , floatSample , s , block ) ;
2015-04-17 03:53:42 +00:00
2016-09-08 18:28:34 +00:00
for ( size_t i = 0 ; i < block ; i + = L ) //go through block in lumps of length L
2015-04-17 03:53:42 +00:00
{
2016-09-08 18:28:34 +00:00
wcopy = std : : min < size_t > ( L , block - i ) ;
for ( size_t j = 0 ; j < wcopy ; j + + )
2015-04-17 03:53:42 +00:00
thisWindow [ j ] = buffer [ i + j ] ; //copy the L (or remaining) samples
2016-09-08 18:28:34 +00:00
for ( auto j = wcopy ; j < windowSize ; j + + )
2015-04-17 03:53:42 +00:00
thisWindow [ j ] = 0 ; //this includes the padding
Filter ( windowSize , thisWindow ) ;
// Overlap - Add
2016-09-08 18:28:34 +00:00
for ( size_t j = 0 ; ( j < mM - 1 ) & & ( j < wcopy ) ; j + + )
2015-04-17 03:53:42 +00:00
buffer [ i + j ] = thisWindow [ j ] + lastWindow [ L + j ] ;
2016-09-08 18:28:34 +00:00
for ( size_t j = mM - 1 ; j < wcopy ; j + + )
2015-04-17 03:53:42 +00:00
buffer [ i + j ] = thisWindow [ j ] ;
2016-12-15 12:30:14 +00:00
std : : swap ( thisWindow , lastWindow ) ;
2015-04-17 03:53:42 +00:00
} //next i, lump of this block
2016-04-14 16:35:15 +00:00
output - > Append ( ( samplePtr ) buffer . get ( ) , floatSample , block ) ;
2015-04-17 03:53:42 +00:00
len - = block ;
s + = block ;
2016-08-25 12:53:59 +00:00
if ( TrackProgress ( count , ( s - start ) . as_double ( ) /
originalLen . as_double ( ) ) )
2015-04-17 03:53:42 +00:00
{
bLoopSuccess = false ;
break ;
}
}
if ( bLoopSuccess )
{
// mM-1 samples of 'tail' left in lastWindow, get them now
2016-09-08 18:28:34 +00:00
if ( wcopy < ( mM - 1 ) ) {
2015-04-17 03:53:42 +00:00
// Still have some overlap left to process
// (note that lastWindow and thisWindow have been exchanged at this point
// so that 'thisWindow' is really the window prior to 'lastWindow')
2016-09-08 18:28:34 +00:00
size_t j = 0 ;
for ( ; j < mM - 1 - wcopy ; j + + )
2015-04-17 03:53:42 +00:00
buffer [ j ] = lastWindow [ wcopy + j ] + thisWindow [ L + wcopy + j ] ;
// And fill in the remainder after the overlap
2016-09-08 18:28:34 +00:00
for ( ; j < mM - 1 ; j + + )
2015-04-17 03:53:42 +00:00
buffer [ j ] = lastWindow [ wcopy + j ] ;
} else {
2016-09-08 18:28:34 +00:00
for ( size_t j = 0 ; j < mM - 1 ; j + + )
2015-04-17 03:53:42 +00:00
buffer [ j ] = lastWindow [ wcopy + j ] ;
}
2016-04-14 16:35:15 +00:00
output - > Append ( ( samplePtr ) buffer . get ( ) , floatSample , mM - 1 ) ;
2015-04-17 03:53:42 +00:00
output - > Flush ( ) ;
2010-01-23 19:44:49 +00:00
2020-08-09 08:07:41 +00:00
std : : vector < EnvPoint > envPoints ;
2015-04-17 03:53:42 +00:00
// now move the appropriate bit of the output back to the track
// (this could be enhanced in the future to use the tails)
2016-08-24 12:02:09 +00:00
double offsetT0 = t - > LongSamplesToTime ( offset ) ;
2015-04-17 03:53:42 +00:00
double lenT = t - > LongSamplesToTime ( originalLen ) ;
// 'start' is the sample offset in 't', the passed in track
// 'startT' is the equivalent time value
// 'output' starts at zero
double startT = t - > LongSamplesToTime ( start ) ;
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
//output has one waveclip for the total length, even though
2020-04-11 07:08:33 +00:00
//t might have whitespace separating multiple clips
2015-04-17 03:53:42 +00:00
//we want to maintain the original clip structure, so
2016-02-13 15:43:16 +00:00
//only paste the intersections of the NEW clip.
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
//Find the bits of clips that need replacing
std : : vector < std : : pair < double , double > > clipStartEndTimes ;
std : : vector < std : : pair < double , double > > clipRealStartEndTimes ; //the above may be truncated due to a clip being partially selected
2016-02-20 00:58:30 +00:00
for ( const auto & clip : t - > GetClips ( ) )
2015-04-17 03:53:42 +00:00
{
double clipStartT ;
double clipEndT ;
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
clipStartT = clip - > GetStartTime ( ) ;
clipEndT = clip - > GetEndTime ( ) ;
if ( clipEndT < = startT )
continue ; // clip is not within selection
if ( clipStartT > = startT + lenT )
continue ; // clip is not within selection
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
//save the actual clip start/end so that we can rejoin them after we paste.
clipRealStartEndTimes . push_back ( std : : pair < double , double > ( clipStartT , clipEndT ) ) ;
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
if ( clipStartT < startT ) // does selection cover the whole clip?
2016-02-13 15:43:16 +00:00
clipStartT = startT ; // don't copy all the NEW clip
2015-04-17 03:53:42 +00:00
if ( clipEndT > startT + lenT ) // does selection cover the whole clip?
2016-02-13 15:43:16 +00:00
clipEndT = startT + lenT ; // don't copy all the NEW clip
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
//save them
clipStartEndTimes . push_back ( std : : pair < double , double > ( clipStartT , clipEndT ) ) ;
2020-08-09 08:07:41 +00:00
// Save the envelope points
const auto & env = * clip - > GetEnvelope ( ) ;
for ( size_t i = 0 , numPoints = env . GetNumberOfPoints ( ) ; i < numPoints ; + + i ) {
envPoints . push_back ( env [ i ] ) ;
}
2015-04-17 03:53:42 +00:00
}
2020-08-09 08:07:41 +00:00
2016-02-13 15:43:16 +00:00
//now go thru and replace the old clips with NEW
2016-09-08 18:28:34 +00:00
for ( unsigned int i = 0 ; i < clipStartEndTimes . size ( ) ; i + + )
2010-01-23 19:44:49 +00:00
{
2016-02-13 15:43:16 +00:00
//remove the old audio and get the NEW
2015-04-17 03:53:42 +00:00
t - > Clear ( clipStartEndTimes [ i ] . first , clipStartEndTimes [ i ] . second ) ;
2016-03-02 20:36:44 +00:00
auto toClipOutput = output - > Copy ( clipStartEndTimes [ i ] . first - startT + offsetT0 , clipStartEndTimes [ i ] . second - startT + offsetT0 ) ;
2017-03-31 19:00:16 +00:00
//put the processed audio in
2017-03-23 15:10:14 +00:00
t - > Paste ( clipStartEndTimes [ i ] . first , toClipOutput . get ( ) ) ;
2017-03-31 19:00:16 +00:00
//if the clip was only partially selected, the Paste will have created a split line. Join is needed to take care of this
//This is not true when the selection is fully contained within one clip (second half of conditional)
if ( ( clipRealStartEndTimes [ i ] . first ! = clipStartEndTimes [ i ] . first | |
clipRealStartEndTimes [ i ] . second ! = clipStartEndTimes [ i ] . second ) & &
! ( clipRealStartEndTimes [ i ] . first < = startT & &
clipRealStartEndTimes [ i ] . second > = startT + lenT ) )
t - > Join ( clipRealStartEndTimes [ i ] . first , clipRealStartEndTimes [ i ] . second ) ;
2010-01-23 19:44:49 +00:00
}
2020-08-09 08:07:41 +00:00
// Restore the envelope points
for ( auto point : envPoints ) {
WaveClip * clip = t - > GetClipAtTime ( point . GetT ( ) ) ;
clip - > GetEnvelope ( ) - > Insert ( point . GetT ( ) , point . GetVal ( ) ) ;
}
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
return bLoopSuccess ;
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
bool EffectEqualization : : CalcFilter ( )
2010-01-23 19:44:49 +00:00
{
double loLog = log10 ( mLoFreq ) ;
double hiLog = log10 ( mHiFreq ) ;
double denom = hiLog - loLog ;
2016-09-08 18:28:34 +00:00
double delta = mHiFreq / ( ( double ) ( mWindowSize / 2. ) ) ;
2010-01-23 19:44:49 +00:00
double val0 ;
double val1 ;
2015-08-15 17:45:16 +00:00
if ( IsLinear ( ) )
2010-01-23 19:44:49 +00:00
{
val0 = mLinEnvelope - > GetValue ( 0.0 ) ; //no scaling required - saved as dB
val1 = mLinEnvelope - > GetValue ( 1.0 ) ;
}
else
{
val0 = mLogEnvelope - > GetValue ( 0.0 ) ; //no scaling required - saved as dB
val1 = mLogEnvelope - > GetValue ( 1.0 ) ;
}
mFilterFuncR [ 0 ] = val0 ;
double freq = delta ;
2016-09-08 18:28:34 +00:00
for ( size_t i = 1 ; i < = mWindowSize / 2 ; i + + )
2010-01-23 19:44:49 +00:00
{
double when ;
2015-08-15 17:45:16 +00:00
if ( IsLinear ( ) )
2010-01-23 19:44:49 +00:00
when = freq / mHiFreq ;
else
when = ( log10 ( freq ) - loLog ) / denom ;
if ( when < 0. )
{
mFilterFuncR [ i ] = val0 ;
}
else if ( when > 1.0 )
2014-01-16 17:55:35 +00:00
{
mFilterFuncR [ i ] = val1 ;
}
else
{
2015-08-15 17:45:16 +00:00
if ( IsLinear ( ) )
2014-01-16 17:55:35 +00:00
mFilterFuncR [ i ] = mLinEnvelope - > GetValue ( when ) ;
else
mFilterFuncR [ i ] = mLogEnvelope - > GetValue ( when ) ;
}
2010-01-23 19:44:49 +00:00
freq + = delta ;
}
2016-09-08 18:28:34 +00:00
mFilterFuncR [ mWindowSize / 2 ] = val1 ;
2010-01-23 19:44:49 +00:00
2015-07-24 20:59:34 +00:00
mFilterFuncR [ 0 ] = DB_TO_LINEAR ( mFilterFuncR [ 0 ] ) ;
2016-09-08 18:28:34 +00:00
2010-01-23 19:44:49 +00:00
{
2016-09-08 18:28:34 +00:00
size_t i = 1 ;
for ( ; i < mWindowSize / 2 ; i + + )
{
mFilterFuncR [ i ] = DB_TO_LINEAR ( mFilterFuncR [ i ] ) ;
mFilterFuncR [ mWindowSize - i ] = mFilterFuncR [ i ] ; //Fill entire array
}
mFilterFuncR [ i ] = DB_TO_LINEAR ( mFilterFuncR [ i ] ) ; //do last one
2010-01-23 19:44:49 +00:00
}
//transfer to time domain to do the padding and windowing
2016-04-14 16:35:15 +00:00
Floats outr { mWindowSize } ;
Floats outi { mWindowSize } ;
InverseRealFFT ( mWindowSize , mFilterFuncR . get ( ) , NULL , outr . get ( ) ) ; // To time domain
2010-01-23 19:44:49 +00:00
2016-09-08 18:28:34 +00:00
{
size_t i = 0 ;
for ( ; i < = ( mM - 1 ) / 2 ; i + + )
{ //Windowing - could give a choice, fixed for now - MJS
// double mult=0.54-0.46*cos(2*M_PI*(i+(mM-1)/2.0)/(mM-1)); //Hamming
//Blackman
double mult =
0.42 -
0.5 * cos ( 2 * M_PI * ( i + ( mM - 1 ) / 2.0 ) / ( mM - 1 ) ) +
.08 * cos ( 4 * M_PI * ( i + ( mM - 1 ) / 2.0 ) / ( mM - 1 ) ) ;
outr [ i ] * = mult ;
if ( i ! = 0 ) {
outr [ mWindowSize - i ] * = mult ;
}
}
for ( ; i < = mWindowSize / 2 ; i + + )
{ //Padding
outr [ i ] = 0 ;
outr [ mWindowSize - i ] = 0 ;
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
}
2016-04-14 16:35:15 +00:00
Floats tempr { mM } ;
2016-09-08 18:28:34 +00:00
{
size_t i = 0 ;
for ( ; i < ( mM - 1 ) / 2 ; i + + )
{ //shift so that padding on right
tempr [ ( mM - 1 ) / 2 + i ] = outr [ i ] ;
tempr [ i ] = outr [ mWindowSize - ( mM - 1 ) / 2 + i ] ;
}
tempr [ ( mM - 1 ) / 2 + i ] = outr [ i ] ;
2015-04-17 03:53:42 +00:00
}
2016-04-14 16:35:15 +00:00
for ( size_t i = 0 ; i < mM ; i + + )
2015-04-17 03:53:42 +00:00
{ //and copy useful values back
2016-09-08 18:28:34 +00:00
outr [ i ] = tempr [ i ] ;
2015-04-17 03:53:42 +00:00
}
2016-04-14 16:35:15 +00:00
for ( size_t i = mM ; i < mWindowSize ; i + + )
2015-04-17 03:53:42 +00:00
{ //rest is padding
outr [ i ] = 0. ;
}
//Back to the frequency domain so we can use it
2016-04-14 16:35:15 +00:00
RealFFT ( mWindowSize , outr . get ( ) , mFilterFuncR . get ( ) , mFilterFuncI . get ( ) ) ;
2015-04-17 03:53:42 +00:00
return TRUE ;
}
2016-09-08 18:28:34 +00:00
void EffectEqualization : : Filter ( size_t len , float * buffer )
2015-04-17 03:53:42 +00:00
{
float re , im ;
// Apply FFT
2016-08-13 15:02:35 +00:00
RealFFTf ( buffer , hFFT . get ( ) ) ;
2015-04-17 03:53:42 +00:00
//FFT(len, false, inr, NULL, outr, outi);
// Apply filter
// DC component is purely real
mFFTBuffer [ 0 ] = buffer [ 0 ] * mFilterFuncR [ 0 ] ;
2016-09-08 18:28:34 +00:00
for ( size_t i = 1 ; i < ( len / 2 ) ; i + + )
2015-04-17 03:53:42 +00:00
{
re = buffer [ hFFT - > BitReversed [ i ] ] ;
im = buffer [ hFFT - > BitReversed [ i ] + 1 ] ;
mFFTBuffer [ 2 * i ] = re * mFilterFuncR [ i ] - im * mFilterFuncI [ i ] ;
mFFTBuffer [ 2 * i + 1 ] = re * mFilterFuncI [ i ] + im * mFilterFuncR [ i ] ;
}
// Fs/2 component is purely real
mFFTBuffer [ 1 ] = buffer [ 1 ] * mFilterFuncR [ len / 2 ] ;
// Inverse FFT and normalization
2016-08-13 15:02:35 +00:00
InverseRealFFTf ( mFFTBuffer . get ( ) , hFFT . get ( ) ) ;
ReorderToTime ( hFFT . get ( ) , mFFTBuffer . get ( ) , buffer ) ;
2015-04-17 03:53:42 +00:00
}
//
// Load external curves with fallback to default, then message
//
2016-02-23 02:17:19 +00:00
void EffectEqualization : : LoadCurves ( const wxString & fileName , bool append )
2015-04-17 03:53:42 +00:00
{
2019-08-23 10:54:14 +00:00
// We've disabled the XML management of curves.
// Just going via .cfg files now.
# if 1
( void ) fileName ;
( void ) append ;
mCurves . clear ( ) ;
2020-04-04 07:54:55 +00:00
mCurves . push_back ( wxT ( " unnamed " ) ) ; // we still need a default curve to use
2019-08-23 10:54:14 +00:00
# else
2015-04-17 03:53:42 +00:00
// Construct normal curve filename
//
// LLL: Wouldn't you know that as of WX 2.6.2, there is a conflict
// between wxStandardPaths and wxConfig under Linux. The latter
// creates a normal file as "$HOME/.audacity", while the former
// expects the ".audacity" portion to be a directory.
// MJS: I don't know what the above means, or if I have broken it.
wxFileName fn ;
2016-02-27 13:17:27 +00:00
2019-03-14 17:04:37 +00:00
if ( fileName . empty ( ) ) {
2016-02-27 13:17:27 +00:00
// Check if presets are up to date.
2016-02-29 13:25:43 +00:00
wxString eqCurvesCurrentVersion = wxString : : Format ( wxT ( " %d.%d " ) , EQCURVES_VERSION , EQCURVES_REVISION ) ;
2019-03-15 18:41:21 +00:00
wxString eqCurvesInstalledVersion ;
2019-03-22 11:10:41 +00:00
gPrefs - > Read ( GetPrefsPrefix ( ) + " PresetVersion " , & eqCurvesInstalledVersion , wxT ( " " ) ) ;
2016-02-27 13:17:27 +00:00
2016-02-29 13:25:43 +00:00
bool needUpdate = ( eqCurvesCurrentVersion ! = eqCurvesInstalledVersion ) ;
2016-02-22 04:33:13 +00:00
2016-08-08 13:50:55 +00:00
// UpdateDefaultCurves allows us to import NEW factory presets only,
2016-02-27 13:17:27 +00:00
// or update all factory preset curves.
if ( needUpdate )
2016-02-27 21:15:48 +00:00
UpdateDefaultCurves ( UPDATE_ALL ! = 0 ) ;
2015-04-17 03:53:42 +00:00
fn = wxFileName ( FileNames : : DataDir ( ) , wxT ( " EQCurves.xml " ) ) ;
2016-02-27 13:17:27 +00:00
}
2016-02-22 04:33:13 +00:00
else
fn = fileName ; // user is loading a specific set of curves
2015-04-17 03:53:42 +00:00
// If requested file doesn't exist...
2016-02-27 21:15:48 +00:00
if ( ! fn . FileExists ( ) & & ! GetDefaultFileName ( fn ) ) {
2018-02-02 18:24:53 +00:00
mCurves . clear ( ) ;
2020-04-05 12:40:15 +00:00
/* i18n-hint: name of the 'unnamed' custom curve */
2018-02-02 18:24:53 +00:00
mCurves . push_back ( _ ( " unnamed " ) ) ; // we still need a default curve to use
2016-02-27 21:15:48 +00:00
return ;
2015-04-17 03:53:42 +00:00
}
EQCurve tempCustom ( wxT ( " temp " ) ) ;
2016-02-27 21:15:48 +00:00
if ( append = = false ) // Start from scratch
2018-02-02 18:24:53 +00:00
mCurves . clear ( ) ;
2015-04-17 03:53:42 +00:00
else // appending so copy and remove 'unnamed', to replace later
{
2018-02-02 18:24:53 +00:00
tempCustom . points = mCurves . back ( ) . points ;
mCurves . pop_back ( ) ;
2015-04-17 03:53:42 +00:00
}
// Load the curves
XMLFileReader reader ;
2016-02-22 04:33:13 +00:00
const wxString fullPath { fn . GetFullPath ( ) } ;
if ( ! reader . Parse ( this , fullPath ) )
2015-04-17 03:53:42 +00:00
{
/* i18n-hint: EQ stands for 'Equalization'.*/
2020-04-05 12:40:15 +00:00
auto msg = XO ( " Error Loading EQ Curves from file: \n %s \n Error message says: \n %s " )
2019-12-19 19:19:51 +00:00
. Format ( fullPath , reader . GetErrorStr ( ) ) ;
2015-04-17 03:53:42 +00:00
// Inform user of load failure
2019-12-19 19:19:51 +00:00
Effect : : MessageBox (
msg ,
2017-09-10 14:42:33 +00:00
wxOK | wxCENTRE ,
2019-12-19 19:19:51 +00:00
XO ( " Error Loading EQ Curves " ) ) ;
2018-02-02 18:24:53 +00:00
mCurves . push_back ( _ ( " unnamed " ) ) ; // we always need a default curve to use
2015-04-17 03:53:42 +00:00
return ;
}
// Move "unnamed" to end, if it exists in current language.
2018-02-02 18:24:53 +00:00
int numCurves = mCurves . size ( ) ;
2015-04-17 03:53:42 +00:00
int curve ;
EQCurve tempUnnamed ( wxT ( " tempUnnamed " ) ) ;
for ( curve = 0 ; curve < numCurves - 1 ; curve + + )
{
if ( mCurves [ curve ] . Name = = _ ( " unnamed " ) )
{
tempUnnamed . points = mCurves [ curve ] . points ;
2018-02-02 18:24:53 +00:00
mCurves . erase ( mCurves . begin ( ) + curve ) ;
mCurves . push_back ( _ ( " unnamed " ) ) ; // add 'unnamed' back at the end
mCurves . back ( ) . points = tempUnnamed . points ;
2015-04-17 03:53:42 +00:00
}
}
2018-02-02 18:24:53 +00:00
if ( mCurves . back ( ) . Name ! = _ ( " unnamed " ) )
mCurves . push_back ( _ ( " unnamed " ) ) ; // we always need a default curve to use
2015-04-17 03:53:42 +00:00
if ( append = = true )
{
2018-02-02 18:24:53 +00:00
mCurves . back ( ) . points = tempCustom . points ;
2015-04-17 03:53:42 +00:00
}
2019-08-23 10:54:14 +00:00
# endif
2015-04-17 03:53:42 +00:00
return ;
}
2016-02-27 13:17:27 +00:00
//
// Update presets to match Audacity version.
//
void EffectEqualization : : UpdateDefaultCurves ( bool updateAll /* false */ )
{
2018-02-02 18:24:53 +00:00
if ( mCurves . size ( ) = = 0 )
2016-02-27 13:17:27 +00:00
return ;
2020-04-04 07:54:55 +00:00
wxString unnamed = wxT ( " unnamed " ) ;
2016-02-27 13:17:27 +00:00
// Save the "unnamed" curve and remove it so we can add it back as the final curve.
EQCurve userUnnamed ( wxT ( " temp " ) ) ;
2018-02-02 18:24:53 +00:00
userUnnamed = mCurves . back ( ) ;
mCurves . pop_back ( ) ;
2016-02-27 13:17:27 +00:00
EQCurveArray userCurves = mCurves ;
2018-02-02 18:24:53 +00:00
mCurves . clear ( ) ;
2016-03-21 11:44:48 +00:00
// We only wamt to look for the shipped EQDefaultCurves.xml
2017-01-25 19:52:40 +00:00
wxFileName fn = wxFileName ( FileNames : : ResourcesDir ( ) , wxT ( " EQDefaultCurves.xml " ) ) ;
2017-10-09 05:03:14 +00:00
wxLogDebug ( wxT ( " Attempting to load EQDefaultCurves.xml from %s " ) , fn . GetFullPath ( ) ) ;
2016-02-27 13:17:27 +00:00
XMLFileReader reader ;
2016-03-21 11:44:48 +00:00
if ( ! reader . Parse ( this , fn . GetFullPath ( ) ) ) {
2016-02-27 21:15:48 +00:00
wxLogError ( wxT ( " EQDefaultCurves.xml could not be read. " ) ) ;
2016-02-27 13:17:27 +00:00
return ;
}
2016-03-21 11:44:48 +00:00
else {
wxLogDebug ( wxT ( " Loading EQDefaultCurves.xml successful. " ) ) ;
}
2016-02-27 13:17:27 +00:00
EQCurveArray defaultCurves = mCurves ;
2018-02-02 18:24:53 +00:00
mCurves . clear ( ) ; // clear now so that we can sort then add back.
2016-02-27 13:17:27 +00:00
// Remove "unnamed" if it exists.
2018-02-02 18:24:53 +00:00
if ( defaultCurves . back ( ) . Name = = unnamed ) {
defaultCurves . pop_back ( ) ;
2016-02-27 13:17:27 +00:00
}
else {
wxLogError ( wxT ( " Error in EQDefaultCurves.xml " ) ) ;
}
2018-02-02 18:24:53 +00:00
int numUserCurves = userCurves . size ( ) ;
int numDefaultCurves = defaultCurves . size ( ) ;
2016-02-27 13:17:27 +00:00
EQCurve tempCurve ( wxT ( " test " ) ) ;
if ( updateAll ) {
// Update all factory preset curves.
// Sort and add factory defaults first;
mCurves = defaultCurves ;
2018-02-02 18:24:53 +00:00
std : : sort ( mCurves . begin ( ) , mCurves . end ( ) ) ;
2016-02-27 13:17:27 +00:00
// then add remaining user curves:
for ( int curveCount = 0 ; curveCount < numUserCurves ; curveCount + + ) {
bool isCustom = true ;
tempCurve = userCurves [ curveCount ] ;
2020-04-11 07:08:33 +00:00
// is the name in the default set?
2016-02-27 13:17:27 +00:00
for ( int defCurveCount = 0 ; defCurveCount < numDefaultCurves ; defCurveCount + + ) {
if ( tempCurve . Name = = mCurves [ defCurveCount ] . Name ) {
isCustom = false ;
break ;
}
}
// if tempCurve is not in the default set, add it to mCurves.
if ( isCustom ) {
2018-02-02 18:24:53 +00:00
mCurves . push_back ( tempCurve ) ;
2016-02-27 13:17:27 +00:00
}
}
}
else {
2016-08-08 13:50:55 +00:00
// Import NEW factory defaults but retain all user modified curves.
2016-02-27 13:17:27 +00:00
for ( int defCurveCount = 0 ; defCurveCount < numDefaultCurves ; defCurveCount + + ) {
bool isUserCurve = false ;
// Add if the curve is in the user's set (preserve user's copy)
for ( int userCurveCount = 0 ; userCurveCount < numUserCurves ; userCurveCount + + ) {
if ( userCurves [ userCurveCount ] . Name = = defaultCurves [ defCurveCount ] . Name ) {
isUserCurve = true ;
2018-02-02 18:24:53 +00:00
mCurves . push_back ( userCurves [ userCurveCount ] ) ;
2016-02-27 13:17:27 +00:00
break ;
}
}
if ( ! isUserCurve ) {
2018-02-02 18:24:53 +00:00
mCurves . push_back ( defaultCurves [ defCurveCount ] ) ;
2016-02-27 13:17:27 +00:00
}
}
2018-02-02 18:24:53 +00:00
std : : sort ( mCurves . begin ( ) , mCurves . end ( ) ) ;
2016-02-27 13:17:27 +00:00
// now add the rest of the user's curves.
for ( int userCurveCount = 0 ; userCurveCount < numUserCurves ; userCurveCount + + ) {
bool isDefaultCurve = false ;
tempCurve = userCurves [ userCurveCount ] ;
for ( int defCurveCount = 0 ; defCurveCount < numDefaultCurves ; defCurveCount + + ) {
if ( tempCurve . Name = = defaultCurves [ defCurveCount ] . Name ) {
isDefaultCurve = true ;
break ;
}
}
if ( ! isDefaultCurve ) {
2018-02-02 18:24:53 +00:00
mCurves . push_back ( tempCurve ) ;
2016-02-27 13:17:27 +00:00
}
}
}
2018-02-02 18:24:53 +00:00
defaultCurves . clear ( ) ;
userCurves . clear ( ) ;
2016-02-27 13:17:27 +00:00
// Add back old "unnamed"
if ( userUnnamed . Name = = unnamed ) {
2018-02-02 18:24:53 +00:00
mCurves . push_back ( userUnnamed ) ; // we always need a default curve to use
2016-02-27 13:17:27 +00:00
}
SaveCurves ( ) ;
// Write current EqCurve version number
// TODO: Probably better if we used pluginregistry.cfg
wxString eqCurvesCurrentVersion = wxString : : Format ( wxT ( " %d.%d " ) , EQCURVES_VERSION , EQCURVES_REVISION ) ;
2019-03-22 11:10:41 +00:00
gPrefs - > Write ( GetPrefsPrefix ( ) + " PresetVersion " , eqCurvesCurrentVersion ) ;
2016-02-27 13:17:27 +00:00
gPrefs - > Flush ( ) ;
return ;
}
2016-02-27 21:15:48 +00:00
//
// Get fully qualified filename of EQDefaultCurves.xml
//
bool EffectEqualization : : GetDefaultFileName ( wxFileName & fileName )
{
// look in data dir first, in case the user has their own defaults (maybe downloaded ones)
fileName = wxFileName ( FileNames : : DataDir ( ) , wxT ( " EQDefaultCurves.xml " ) ) ;
if ( ! fileName . FileExists ( ) )
{ // Default file not found in the data dir. Fall back to Resources dir.
// See http://docs.wxwidgets.org/trunk/classwx_standard_paths.html#5514bf6288ee9f5a0acaf065762ad95d
2017-01-25 19:52:40 +00:00
fileName = wxFileName ( FileNames : : ResourcesDir ( ) , wxT ( " EQDefaultCurves.xml " ) ) ;
2016-02-27 21:15:48 +00:00
}
if ( ! fileName . FileExists ( ) )
{
// LLL: Is there really a need for an error message at all???
2019-12-05 18:38:07 +00:00
//auto errorMessage = XO("EQCurves.xml and EQDefaultCurves.xml were not found on your system.\nPlease press 'help' to visit the download page.\n\nSave the curves at %s")
// .Format( FileNames::DataDir() );
//ShowErrorDialog(mUIParent, XO("EQCurves.xml and EQDefaultCurves.xml missing"),
2016-02-27 21:15:48 +00:00
// errorMessage, wxT("http://wiki.audacityteam.org/wiki/EQCurvesDownload"), false);
// Have another go at finding EQCurves.xml in the data dir, in case 'help' helped
fileName = wxFileName ( FileNames : : DataDir ( ) , wxT ( " EQDefaultCurves.xml " ) ) ;
}
return ( fileName . FileExists ( ) ) ;
}
2016-02-27 13:17:27 +00:00
2015-04-17 03:53:42 +00:00
//
// Save curves to external file
//
2016-02-23 02:17:19 +00:00
void EffectEqualization : : SaveCurves ( const wxString & fileName )
2015-04-17 03:53:42 +00:00
{
wxFileName fn ;
2019-03-14 17:04:37 +00:00
if ( fileName . empty ( ) )
2015-04-17 03:53:42 +00:00
{
// Construct default curve filename
//
// LLL: Wouldn't you know that as of WX 2.6.2, there is a conflict
// between wxStandardPaths and wxConfig under Linux. The latter
// creates a normal file as "$HOME/.audacity", while the former
// expects the ".audacity" portion to be a directory.
fn = wxFileName ( FileNames : : DataDir ( ) , wxT ( " EQCurves.xml " ) ) ;
// If the directory doesn't exist...
if ( ! fn . DirExists ( ) )
{
// Attempt to create it
if ( ! fn . Mkdir ( fn . GetPath ( ) , 511 , wxPATH_MKDIR_FULL ) )
{
// MkDir() will emit message
return ;
}
}
}
else
2016-02-22 04:33:13 +00:00
fn = fileName ;
2010-01-23 19:44:49 +00:00
2018-01-13 06:32:41 +00:00
GuardedCall ( [ & ] {
2016-12-02 01:40:05 +00:00
// Create/Open the file
const wxString fullPath { fn . GetFullPath ( ) } ;
2019-12-19 19:36:37 +00:00
XMLFileWriter eqFile { fullPath , XO ( " Error Saving Equalization Curves " ) } ;
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
// Write the curves
WriteXML ( eqFile ) ;
2010-01-23 19:44:49 +00:00
2016-12-02 01:40:05 +00:00
eqFile . Commit ( ) ;
} ) ;
2010-01-23 19:44:49 +00:00
}
//
// Make the passed curve index the active one
//
2015-04-17 03:53:42 +00:00
void EffectEqualization : : setCurve ( int currentCurve )
2010-01-23 19:44:49 +00:00
{
// Set current choice
2018-02-02 18:24:53 +00:00
wxASSERT ( currentCurve < ( int ) mCurves . size ( ) ) ;
2017-05-07 18:22:44 +00:00
Select ( currentCurve ) ;
2010-01-23 19:44:49 +00:00
2016-03-08 21:38:13 +00:00
Envelope * env ;
2018-02-02 18:24:53 +00:00
int numPoints = ( int ) mCurves [ currentCurve ] . points . size ( ) ;
2010-01-23 19:44:49 +00:00
2016-03-08 21:38:13 +00:00
if ( mLin ) { // linear freq mode
2016-08-04 11:33:22 +00:00
env = mLinEnvelope . get ( ) ;
2016-03-08 21:38:13 +00:00
}
else { // log freq mode
2016-08-04 11:33:22 +00:00
env = mLogEnvelope . get ( ) ;
2016-03-08 21:38:13 +00:00
}
env - > Flatten ( 0. ) ;
env - > SetTrackLen ( 1.0 ) ;
// Handle special case of no points.
if ( numPoints = = 0 ) {
ForceRecalc ( ) ;
return ;
}
double when , value ;
// Handle special case 1 point.
if ( numPoints = = 1 ) {
// only one point, so ensure it is in range then return.
when = mCurves [ currentCurve ] . points [ 0 ] . Freq ;
if ( mLin ) {
when = when / mHiFreq ;
}
else { // log scale
// We don't go below loFreqI (20 Hz) in log view.
double loLog = log10 ( ( double ) loFreqI ) ;
double hiLog = log10 ( mHiFreq ) ;
double denom = hiLog - loLog ;
when = ( log10 ( std : : max ( ( double ) loFreqI , when ) ) - loLog ) / denom ;
}
value = mCurves [ currentCurve ] . points [ 0 ] . dB ;
2019-08-23 18:28:35 +00:00
env - > Insert ( std : : min ( 1.0 , std : : max ( 0.0 , when ) ) , value ) ;
2016-03-08 21:38:13 +00:00
ForceRecalc ( ) ;
return ;
}
// We have at least two points, so ensure they are in frequency order.
2018-02-02 18:24:53 +00:00
std : : sort ( mCurves [ currentCurve ] . points . begin ( ) ,
mCurves [ currentCurve ] . points . end ( ) ) ;
2016-03-08 21:38:13 +00:00
if ( mCurves [ currentCurve ] . points [ 0 ] . Freq < 0 ) {
// Corrupt or invalid curve, so bail.
ForceRecalc ( ) ;
return ;
}
if ( mLin ) { // linear Hz scale
for ( int pointCount = 0 ; pointCount < numPoints ; pointCount + + ) {
when = mCurves [ currentCurve ] . points [ pointCount ] . Freq / mHiFreq ;
value = mCurves [ currentCurve ] . points [ pointCount ] . dB ;
if ( when < = 1 ) {
2019-08-23 18:28:35 +00:00
env - > Insert ( when , value ) ;
2017-05-06 14:05:06 +00:00
if ( when = = 1 )
break ;
2010-01-23 19:44:49 +00:00
}
2016-03-08 21:38:13 +00:00
else {
2017-07-14 14:50:28 +00:00
// There are more points at higher freqs,
// so interpolate next one then stop.
2016-03-08 21:38:13 +00:00
when = 1.0 ;
double nextDB = mCurves [ currentCurve ] . points [ pointCount ] . dB ;
2017-07-14 14:50:28 +00:00
if ( pointCount > 0 ) {
double nextF = mCurves [ currentCurve ] . points [ pointCount ] . Freq ;
double lastF = mCurves [ currentCurve ] . points [ pointCount - 1 ] . Freq ;
double lastDB = mCurves [ currentCurve ] . points [ pointCount - 1 ] . dB ;
value = lastDB +
( ( nextDB - lastDB ) *
( ( mHiFreq - lastF ) / ( nextF - lastF ) ) ) ;
}
else
value = nextDB ;
2019-08-23 18:28:35 +00:00
env - > Insert ( when , value ) ;
2016-03-08 21:38:13 +00:00
break ;
2010-01-23 19:44:49 +00:00
}
}
}
2016-03-08 21:38:13 +00:00
else { // log Hz scale
double loLog = log10 ( ( double ) loFreqI ) ;
double hiLog = log10 ( mHiFreq ) ;
double denom = hiLog - loLog ;
int firstAbove20Hz ;
2010-01-23 19:44:49 +00:00
2016-03-08 21:38:13 +00:00
// log scale EQ starts at 20 Hz (threshold of hearing).
// so find the first point (if any) above 20 Hz.
for ( firstAbove20Hz = 0 ; firstAbove20Hz < numPoints ; firstAbove20Hz + + ) {
if ( mCurves [ currentCurve ] . points [ firstAbove20Hz ] . Freq > loFreqI )
break ;
}
if ( firstAbove20Hz = = numPoints ) {
// All points below 20 Hz, so just use final point.
when = 0.0 ;
value = mCurves [ currentCurve ] . points [ numPoints - 1 ] . dB ;
2019-08-23 18:28:35 +00:00
env - > Insert ( when , value ) ;
2016-03-08 21:38:13 +00:00
ForceRecalc ( ) ;
return ;
}
if ( firstAbove20Hz > 0 ) {
// At least one point is before 20 Hz and there are more
// beyond 20 Hz, so interpolate the first
double prevF = mCurves [ currentCurve ] . points [ firstAbove20Hz - 1 ] . Freq ;
prevF = log10 ( std : : max ( 1.0 , prevF ) ) ; // log zero is bad.
double prevDB = mCurves [ currentCurve ] . points [ firstAbove20Hz - 1 ] . dB ;
double nextF = log10 ( mCurves [ currentCurve ] . points [ firstAbove20Hz ] . Freq ) ;
double nextDB = mCurves [ currentCurve ] . points [ firstAbove20Hz ] . dB ;
when = 0.0 ;
value = nextDB - ( ( nextDB - prevDB ) * ( ( nextF - loLog ) / ( nextF - prevF ) ) ) ;
2019-08-23 18:28:35 +00:00
env - > Insert ( when , value ) ;
2016-03-08 21:38:13 +00:00
}
// Now get the rest.
for ( int pointCount = firstAbove20Hz ; pointCount < numPoints ; pointCount + + )
2010-01-23 19:44:49 +00:00
{
2016-03-08 21:38:13 +00:00
double flog = log10 ( mCurves [ currentCurve ] . points [ pointCount ] . Freq ) ;
wxASSERT ( mCurves [ currentCurve ] . points [ pointCount ] . Freq > = loFreqI ) ;
2010-01-23 19:44:49 +00:00
2016-03-08 21:38:13 +00:00
when = ( flog - loLog ) / denom ;
value = mCurves [ currentCurve ] . points [ pointCount ] . dB ;
if ( when < = 1.0 ) {
2019-08-23 18:28:35 +00:00
env - > Insert ( when , value ) ;
2016-03-08 21:38:13 +00:00
}
else {
// This looks weird when adjusting curve in Draw mode if
// there is a point off-screen.
/*
// we have a point beyond fs/2. Insert it so that env code can use it.
// but just this one, we have no use for the rest
env - > SetTrackLen ( when ) ; // can't Insert if the envelope isn't long enough
env - > Insert ( when , value ) ;
break ;
*/
// interpolate the final point instead
when = 1.0 ;
2017-07-14 14:50:28 +00:00
if ( pointCount > 0 ) {
double lastDB = mCurves [ currentCurve ] . points [ pointCount - 1 ] . dB ;
double logLastF =
log10 ( mCurves [ currentCurve ] . points [ pointCount - 1 ] . Freq ) ;
value = lastDB +
( ( value - lastDB ) *
( ( log10 ( mHiFreq ) - logLastF ) / ( flog - logLastF ) ) ) ;
}
2019-08-23 18:28:35 +00:00
env - > Insert ( when , value ) ;
2016-03-08 21:38:13 +00:00
break ;
2010-01-23 19:44:49 +00:00
}
}
}
2015-08-15 17:45:16 +00:00
ForceRecalc ( ) ;
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
void EffectEqualization : : setCurve ( )
2010-01-23 19:44:49 +00:00
{
2018-02-02 18:24:53 +00:00
setCurve ( ( int ) mCurves . size ( ) - 1 ) ;
2010-01-23 19:44:49 +00:00
}
2016-02-23 02:17:19 +00:00
void EffectEqualization : : setCurve ( const wxString & curveName )
2010-01-23 19:44:49 +00:00
{
unsigned i = 0 ;
2018-02-02 18:24:53 +00:00
for ( i = 0 ; i < mCurves . size ( ) ; i + + )
2010-01-23 19:44:49 +00:00
if ( curveName = = mCurves [ i ] . Name )
break ;
2018-02-02 18:24:53 +00:00
if ( i = = mCurves . size ( ) )
2010-01-23 19:44:49 +00:00
{
2019-12-19 19:19:51 +00:00
Effect : : MessageBox (
XO ( " Requested curve not found, using 'unnamed' " ) ,
2017-09-10 14:42:33 +00:00
wxOK | wxICON_ERROR ,
2019-12-19 19:19:51 +00:00
XO ( " Curve not found " ) ) ;
2018-06-21 20:55:16 +00:00
setCurve ( ) ;
2010-01-23 19:44:49 +00:00
}
else
setCurve ( i ) ;
}
//
2016-02-13 15:43:16 +00:00
// Set NEW curve selection (safe to call outside of the UI)
2010-01-23 19:44:49 +00:00
//
2015-04-17 03:53:42 +00:00
void EffectEqualization : : Select ( int curve )
2010-01-23 19:44:49 +00:00
{
// Set current choice
2015-08-15 17:45:16 +00:00
if ( mCurve )
{
mCurve - > SetSelection ( curve ) ;
mCurveName = mCurves [ curve ] . Name ;
}
}
//
// Tell panel to recalc (safe to call outside of UI)
//
void EffectEqualization : : ForceRecalc ( )
{
if ( mPanel )
{
mPanel - > ForceRecalc ( ) ;
}
2010-01-23 19:44:49 +00:00
}
//
// Capture updated envelope
//
2015-04-17 03:53:42 +00:00
void EffectEqualization : : EnvelopeUpdated ( )
2010-01-23 19:44:49 +00:00
{
2015-08-15 17:45:16 +00:00
if ( IsLinear ( ) )
2015-04-17 03:53:42 +00:00
{
2016-08-04 11:33:22 +00:00
EnvelopeUpdated ( mLinEnvelope . get ( ) , true ) ;
2015-04-17 03:53:42 +00:00
}
2010-01-23 19:44:49 +00:00
else
2015-04-17 03:53:42 +00:00
{
2016-08-04 11:33:22 +00:00
EnvelopeUpdated ( mLogEnvelope . get ( ) , false ) ;
2015-04-17 03:53:42 +00:00
}
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
void EffectEqualization : : EnvelopeUpdated ( Envelope * env , bool lin )
2010-01-23 19:44:49 +00:00
{
// Allocate and populate point arrays
2016-04-14 16:35:15 +00:00
size_t numPoints = env - > GetNumberOfPoints ( ) ;
Doubles when { numPoints } ;
Doubles value { numPoints } ;
env - > GetPoints ( when . get ( ) , value . get ( ) , numPoints ) ;
2010-01-23 19:44:49 +00:00
2010-06-15 23:49:51 +00:00
// Clear the unnamed curve
2018-02-02 18:24:53 +00:00
int curve = mCurves . size ( ) - 1 ;
mCurves [ curve ] . points . clear ( ) ;
2010-01-23 19:44:49 +00:00
if ( lin )
{
// Copy and convert points
2016-04-14 16:35:15 +00:00
for ( size_t point = 0 ; point < numPoints ; point + + )
2010-01-23 19:44:49 +00:00
{
double freq = when [ point ] * mHiFreq ;
double db = value [ point ] ;
// Add it to the curve
2018-02-02 18:24:53 +00:00
mCurves [ curve ] . points . push_back ( EQPoint ( freq , db ) ) ;
2010-01-23 19:44:49 +00:00
}
}
else
{
double loLog = log10 ( 20. ) ;
double hiLog = log10 ( mHiFreq ) ;
double denom = hiLog - loLog ;
// Copy and convert points
2016-04-14 16:35:15 +00:00
for ( size_t point = 0 ; point < numPoints ; point + + )
2010-01-23 19:44:49 +00:00
{
double freq = pow ( 10. , ( ( when [ point ] * denom ) + loLog ) ) ;
double db = value [ point ] ;
// Add it to the curve
2018-02-02 18:24:53 +00:00
mCurves [ curve ] . points . push_back ( EQPoint ( freq , db ) ) ;
2010-01-23 19:44:49 +00:00
}
}
2010-06-15 23:49:51 +00:00
// Remember that we've updated the unnamed curve
2010-01-23 19:44:49 +00:00
mDirty = true ;
2010-06-15 23:49:51 +00:00
// set 'unnamed' as the selected curve
2018-02-02 18:24:53 +00:00
Select ( ( int ) mCurves . size ( ) - 1 ) ;
2010-01-23 19:44:49 +00:00
}
2015-08-15 17:45:16 +00:00
//
//
//
bool EffectEqualization : : IsLinear ( )
{
return mDrawMode & & mLin ;
}
2015-04-22 18:02:55 +00:00
//
// Flatten the curve
//
void EffectEqualization : : Flatten ( )
{
mLogEnvelope - > Flatten ( 0. ) ;
mLogEnvelope - > SetTrackLen ( 1.0 ) ;
mLinEnvelope - > Flatten ( 0. ) ;
mLinEnvelope - > SetTrackLen ( 1.0 ) ;
2015-08-15 17:45:16 +00:00
ForceRecalc ( ) ;
2015-04-22 18:02:55 +00:00
if ( ! mDrawMode )
{
2016-04-14 16:35:15 +00:00
for ( size_t i = 0 ; i < mBandsInUse ; i + + )
2015-04-22 18:02:55 +00:00
{
mSliders [ i ] - > SetValue ( 0 ) ;
mSlidersOld [ i ] = 0 ;
mEQVals [ i ] = 0. ;
wxString tip ;
if ( kThirdOct [ i ] < 1000. )
tip . Printf ( wxT ( " %dHz \n %.1fdB " ) , ( int ) kThirdOct [ i ] , 0. ) ;
else
tip . Printf ( wxT ( " %gkHz \n %.1fdB " ) , kThirdOct [ i ] / 1000. , 0. ) ;
mSliders [ i ] - > SetToolTip ( tip ) ;
}
}
EnvelopeUpdated ( ) ;
}
2010-01-23 19:44:49 +00:00
//
// Process XML tags and handle the ones we recognize
//
2015-04-17 03:53:42 +00:00
bool EffectEqualization : : HandleXMLTag ( const wxChar * tag , const wxChar * * attrs )
2010-01-23 19:44:49 +00:00
{
// May want to add a version strings...
if ( ! wxStrcmp ( tag , wxT ( " equalizationeffect " ) ) )
{
return true ;
}
2016-02-13 15:43:16 +00:00
// Located a NEW curve
2010-01-23 19:44:49 +00:00
if ( ! wxStrcmp ( tag , wxT ( " curve " ) ) )
{
// Process the attributes
while ( * attrs )
{
// Cache attr/value and bump to next
const wxChar * attr = * attrs + + ;
const wxChar * value = * attrs + + ;
2016-02-13 15:43:16 +00:00
// Create a NEW curve and name it
2010-01-23 19:44:49 +00:00
if ( ! wxStrcmp ( attr , wxT ( " name " ) ) )
{
const wxString strValue = value ;
if ( ! XMLValueChecker : : IsGoodString ( strValue ) )
return false ;
2010-06-27 22:20:58 +00:00
// check for a duplicate name and add (n) if there is one
int n = 0 ;
wxString strValueTemp = strValue ;
bool exists ;
do
{
exists = false ;
2018-02-02 18:24:53 +00:00
for ( size_t i = 0 ; i < mCurves . size ( ) ; i + + )
2010-06-27 22:20:58 +00:00
{
2010-07-02 05:33:50 +00:00
if ( n > 0 )
2017-10-09 05:03:14 +00:00
strValueTemp . Printf ( wxT ( " %s (%d) " ) , strValue , n ) ;
2010-06-27 22:20:58 +00:00
if ( mCurves [ i ] . Name = = strValueTemp )
{
exists = true ;
break ;
}
}
n + + ;
}
while ( exists = = true ) ;
2018-02-02 18:24:53 +00:00
mCurves . push_back ( EQCurve ( strValueTemp ) ) ;
2010-01-23 19:44:49 +00:00
}
}
// Tell caller it was processed
return true ;
}
2016-02-13 15:43:16 +00:00
// Located a NEW point
2010-01-23 19:44:49 +00:00
if ( ! wxStrcmp ( tag , wxT ( " point " ) ) )
{
// Set defaults in case attributes are missing
double f = 0.0 ;
double d = 0.0 ;
// Process the attributes
double dblValue ;
while ( * attrs )
{ // Cache attr/value and bump to next
const wxChar * attr = * attrs + + ;
const wxChar * value = * attrs + + ;
const wxString strValue = value ;
// Get the frequency
if ( ! wxStrcmp ( attr , wxT ( " f " ) ) )
{
if ( ! XMLValueChecker : : IsGoodString ( strValue ) | |
2014-01-16 17:55:35 +00:00
! Internat : : CompatibleToDouble ( strValue , & dblValue ) )
2010-01-23 19:44:49 +00:00
return false ;
f = dblValue ;
}
// Get the dB
else if ( ! wxStrcmp ( attr , wxT ( " d " ) ) )
{
if ( ! XMLValueChecker : : IsGoodString ( strValue ) | |
2014-01-16 17:55:35 +00:00
! Internat : : CompatibleToDouble ( strValue , & dblValue ) )
2010-01-23 19:44:49 +00:00
return false ;
d = dblValue ;
}
}
2016-02-13 15:43:16 +00:00
// Create a NEW point
2018-02-02 18:24:53 +00:00
mCurves [ mCurves . size ( ) - 1 ] . points . push_back ( EQPoint ( f , d ) ) ;
2010-01-23 19:44:49 +00:00
// Tell caller it was processed
return true ;
}
// Tell caller we didn't understand the tag
return false ;
}
//
// Return handler for recognized tags
//
2015-04-17 03:53:42 +00:00
XMLTagHandler * EffectEqualization : : HandleXMLChild ( const wxChar * tag )
2010-01-23 19:44:49 +00:00
{
if ( ! wxStrcmp ( tag , wxT ( " equalizationeffect " ) ) )
{
return this ;
}
if ( ! wxStrcmp ( tag , wxT ( " curve " ) ) )
{
return this ;
}
if ( ! wxStrcmp ( tag , wxT ( " point " ) ) )
{
return this ;
}
return NULL ;
}
//
// Write all of the curves to the XML file
//
2017-02-22 19:23:35 +00:00
void EffectEqualization : : WriteXML ( XMLWriter & xmlFile ) const
2016-12-01 22:03:40 +00:00
// may throw
2010-01-23 19:44:49 +00:00
{
2020-04-11 07:08:33 +00:00
// Start our hierarchy
2010-01-23 19:44:49 +00:00
xmlFile . StartTag ( wxT ( " equalizationeffect " ) ) ;
// Write all curves
2018-02-02 18:24:53 +00:00
int numCurves = mCurves . size ( ) ;
2010-01-23 19:44:49 +00:00
int curve ;
for ( curve = 0 ; curve < numCurves ; curve + + )
{
2016-02-13 15:43:16 +00:00
// Start a NEW curve
2010-01-23 19:44:49 +00:00
xmlFile . StartTag ( wxT ( " curve " ) ) ;
xmlFile . WriteAttr ( wxT ( " name " ) , mCurves [ curve ] . Name ) ;
// Write all points
2018-02-02 18:24:53 +00:00
int numPoints = mCurves [ curve ] . points . size ( ) ;
2010-01-23 19:44:49 +00:00
int point ;
for ( point = 0 ; point < numPoints ; point + + )
{
2016-02-13 15:43:16 +00:00
// Write NEW point
2010-01-23 19:44:49 +00:00
xmlFile . StartTag ( wxT ( " point " ) ) ;
xmlFile . WriteAttr ( wxT ( " f " ) , mCurves [ curve ] . points [ point ] . Freq , 12 ) ;
xmlFile . WriteAttr ( wxT ( " d " ) , mCurves [ curve ] . points [ point ] . dB , 12 ) ;
xmlFile . EndTag ( wxT ( " point " ) ) ;
}
// Terminate curve
xmlFile . EndTag ( wxT ( " curve " ) ) ;
}
2020-04-11 07:08:33 +00:00
// Terminate our hierarchy
2010-01-23 19:44:49 +00:00
xmlFile . EndTag ( wxT ( " equalizationeffect " ) ) ;
}
2015-04-17 03:53:42 +00:00
///////////////////////////////////////////////////////////////////////////////
2010-01-23 19:44:49 +00:00
//
2015-04-17 03:53:42 +00:00
// All EffectEqualization methods beyond this point interact with the UI, so
// can't be called while the UI is not displayed.
2010-01-23 19:44:49 +00:00
//
2015-04-17 03:53:42 +00:00
///////////////////////////////////////////////////////////////////////////////
2010-01-23 19:44:49 +00:00
2015-05-29 16:32:55 +00:00
void EffectEqualization : : UpdateCurves ( )
{
2019-04-01 18:14:37 +00:00
2015-05-29 16:32:55 +00:00
// Reload the curve names
2019-04-01 18:14:37 +00:00
if ( mCurve )
mCurve - > Clear ( ) ;
2018-06-21 20:55:16 +00:00
bool selectedCurveExists = false ;
2018-02-02 18:24:53 +00:00
for ( size_t i = 0 , cnt = mCurves . size ( ) ; i < cnt ; i + + )
2015-05-29 16:32:55 +00:00
{
2018-11-13 15:52:18 +00:00
if ( mCurveName = = mCurves [ i ] . Name )
2018-06-21 20:55:16 +00:00
selectedCurveExists = true ;
2019-04-01 18:14:37 +00:00
if ( mCurve )
mCurve - > Append ( mCurves [ i ] . Name ) ;
2015-05-29 16:32:55 +00:00
}
2018-06-21 20:55:16 +00:00
// In rare circumstances, mCurveName may not exist (bug 1891)
if ( ! selectedCurveExists )
mCurveName = mCurves [ ( int ) mCurves . size ( ) - 1 ] . Name ;
2019-04-01 18:14:37 +00:00
if ( mCurve )
mCurve - > SetStringSelection ( mCurveName ) ;
2015-05-29 16:32:55 +00:00
// Allow the control to resize
2019-04-01 18:14:37 +00:00
if ( mCurve )
2018-01-30 06:06:23 +00:00
mCurve - > SetMinSize ( { - 1 , - 1 } ) ;
2015-05-29 16:32:55 +00:00
// Set initial curve
setCurve ( mCurveName ) ;
}
2015-04-27 15:52:42 +00:00
void EffectEqualization : : UpdateDraw ( )
{
2016-04-14 16:35:15 +00:00
size_t numPoints = mLogEnvelope - > GetNumberOfPoints ( ) ;
Doubles when { numPoints } ;
Doubles value { numPoints } ;
2015-04-27 15:52:42 +00:00
double deltadB = 0.1 ;
double dx , dy , dx1 , dy1 , err ;
2016-04-14 16:35:15 +00:00
mLogEnvelope - > GetPoints ( when . get ( ) , value . get ( ) , numPoints ) ;
2015-04-27 15:52:42 +00:00
// set 'unnamed' as the selected curve
EnvelopeUpdated ( ) ;
bool flag = true ;
while ( flag )
{
flag = false ;
int numDeleted = 0 ;
2016-04-14 16:35:15 +00:00
mLogEnvelope - > GetPoints ( when . get ( ) , value . get ( ) , numPoints ) ;
for ( size_t j = 0 ; j + 2 < numPoints ; j + + )
2015-04-27 15:52:42 +00:00
{
dx = when [ j + 2 + numDeleted ] - when [ j + numDeleted ] ;
dy = value [ j + 2 + numDeleted ] - value [ j + numDeleted ] ;
dx1 = when [ j + numDeleted + 1 ] - when [ j + numDeleted ] ;
dy1 = dy * dx1 / dx ;
err = fabs ( value [ j + numDeleted + 1 ] - ( value [ j + numDeleted ] + dy1 ) ) ;
if ( err < deltadB )
{ // within < deltadB dB?
mLogEnvelope - > Delete ( j + 1 ) ;
numPoints - - ;
numDeleted + + ;
flag = true ;
}
}
}
2015-08-15 17:45:16 +00:00
if ( mLin ) // do not use IsLinear() here
2015-04-27 15:52:42 +00:00
{
EnvLogToLin ( ) ;
2016-08-04 11:33:22 +00:00
mEnvelope = mLinEnvelope . get ( ) ;
2015-04-27 15:52:42 +00:00
mFreqRuler - > ruler . SetLog ( false ) ;
mFreqRuler - > ruler . SetRange ( 0 , mHiFreq ) ;
}
szrV - > Show ( szrG , false ) ;
szrH - > Show ( szrI , false ) ;
szrH - > Show ( szrL , true ) ;
2015-08-15 17:45:16 +00:00
2015-04-27 15:52:42 +00:00
mUIParent - > Layout ( ) ;
wxGetTopLevelParent ( mUIParent ) - > Layout ( ) ;
2015-08-15 17:45:16 +00:00
ForceRecalc ( ) ; // it may have changed slightly due to the deletion of points
2015-04-27 15:52:42 +00:00
}
2015-04-17 03:53:42 +00:00
void EffectEqualization : : UpdateGraphic ( )
{
double loLog = log10 ( mLoFreq ) ;
double hiLog = log10 ( mHiFreq ) ;
double denom = hiLog - loLog ;
2015-08-15 17:45:16 +00:00
if ( mLin ) //going from lin to log freq scale - do not use IsLinear() here
2015-04-17 03:53:42 +00:00
{ // add some extra points to the linear envelope for the graphic to follow
double step = pow ( 2. , 1. / 12. ) ; // twelve steps per octave
double when , value ;
for ( double freq = 10. ; freq < mHiFreq ; freq * = step )
{
when = freq / mHiFreq ;
value = mLinEnvelope - > GetValue ( when ) ;
2019-08-23 18:28:35 +00:00
mLinEnvelope - > Insert ( when , value ) ;
2015-04-17 03:53:42 +00:00
}
EnvLinToLog ( ) ;
2016-08-04 11:33:22 +00:00
mEnvelope = mLogEnvelope . get ( ) ;
2015-04-17 03:53:42 +00:00
mFreqRuler - > ruler . SetLog ( true ) ;
mFreqRuler - > ruler . SetRange ( mLoFreq , mHiFreq ) ;
}
2016-04-14 16:35:15 +00:00
for ( size_t i = 0 ; i < mBandsInUse ; i + + )
2015-04-17 03:53:42 +00:00
{
if ( kThirdOct [ i ] = = mLoFreq )
mWhenSliders [ i ] = 0. ;
else
mWhenSliders [ i ] = ( log10 ( kThirdOct [ i ] ) - loLog ) / denom ;
mEQVals [ i ] = mLogEnvelope - > GetValue ( mWhenSliders [ i ] ) ; //set initial values of sliders
if ( mEQVals [ i ] > 20. )
mEQVals [ i ] = 20. ;
if ( mEQVals [ i ] < - 20. )
mEQVals [ i ] = - 20. ;
}
ErrMin ( ) ; //move sliders to minimise error
2016-04-14 16:35:15 +00:00
for ( size_t i = 0 ; i < mBandsInUse ; i + + )
2015-04-17 03:53:42 +00:00
{
mSliders [ i ] - > SetValue ( lrint ( mEQVals [ i ] ) ) ; //actually set slider positions
mSlidersOld [ i ] = mSliders [ i ] - > GetValue ( ) ;
wxString tip ;
if ( kThirdOct [ i ] < 1000. )
tip . Printf ( wxT ( " %dHz \n %.1fdB " ) , ( int ) kThirdOct [ i ] , mEQVals [ i ] ) ;
else
tip . Printf ( wxT ( " %gkHz \n %.1fdB " ) , kThirdOct [ i ] / 1000. , mEQVals [ i ] ) ;
mSliders [ i ] - > SetToolTip ( tip ) ;
}
szrV - > Show ( szrG , true ) ; // eq sliders
2019-08-27 16:28:57 +00:00
szrH - > Show ( szrI , mOptions = = kEqLegacy ) ; // interpolation choice
2015-04-17 03:53:42 +00:00
szrH - > Show ( szrL , false ) ; // linear freq checkbox
2015-08-15 17:45:16 +00:00
2015-04-17 03:53:42 +00:00
mUIParent - > Layout ( ) ;
wxGetTopLevelParent ( mUIParent ) - > Layout ( ) ;
mUIParent - > Layout ( ) ;
wxGetTopLevelParent ( mUIParent ) - > Layout ( ) ;
2016-08-04 11:33:22 +00:00
GraphicEQ ( mLogEnvelope . get ( ) ) ;
2015-04-17 03:53:42 +00:00
mDrawMode = false ;
}
void EffectEqualization : : EnvLogToLin ( void )
{
2016-04-14 16:35:15 +00:00
size_t numPoints = mLogEnvelope - > GetNumberOfPoints ( ) ;
2015-04-17 03:53:42 +00:00
if ( numPoints = = 0 )
{
return ;
}
2016-04-14 16:35:15 +00:00
Doubles when { numPoints } ;
Doubles value { numPoints } ;
2015-04-17 03:53:42 +00:00
mLinEnvelope - > Flatten ( 0. ) ;
mLinEnvelope - > SetTrackLen ( 1.0 ) ;
2016-04-14 16:35:15 +00:00
mLogEnvelope - > GetPoints ( when . get ( ) , value . get ( ) , numPoints ) ;
2017-05-03 00:58:18 +00:00
mLinEnvelope - > Reassign ( 0. , value [ 0 ] ) ;
2015-04-17 03:53:42 +00:00
double loLog = log10 ( 20. ) ;
double hiLog = log10 ( mHiFreq ) ;
double denom = hiLog - loLog ;
2016-04-14 16:35:15 +00:00
for ( size_t i = 0 ; i < numPoints ; i + + )
2019-08-23 18:28:35 +00:00
mLinEnvelope - > Insert ( pow ( 10. , ( ( when [ i ] * denom ) + loLog ) ) / mHiFreq , value [ i ] ) ;
2017-05-03 00:58:18 +00:00
mLinEnvelope - > Reassign ( 1. , value [ numPoints - 1 ] ) ;
2015-04-17 03:53:42 +00:00
}
void EffectEqualization : : EnvLinToLog ( void )
{
2016-04-14 16:35:15 +00:00
size_t numPoints = mLinEnvelope - > GetNumberOfPoints ( ) ;
2015-04-17 03:53:42 +00:00
if ( numPoints = = 0 )
{
return ;
}
2016-04-14 16:35:15 +00:00
Doubles when { numPoints } ;
Doubles value { numPoints } ;
2015-04-17 03:53:42 +00:00
mLogEnvelope - > Flatten ( 0. ) ;
mLogEnvelope - > SetTrackLen ( 1.0 ) ;
2016-04-14 16:35:15 +00:00
mLinEnvelope - > GetPoints ( when . get ( ) , value . get ( ) , numPoints ) ;
2017-05-03 00:58:18 +00:00
mLogEnvelope - > Reassign ( 0. , value [ 0 ] ) ;
2015-04-17 03:53:42 +00:00
double loLog = log10 ( 20. ) ;
double hiLog = log10 ( mHiFreq ) ;
double denom = hiLog - loLog ;
bool changed = false ;
2016-04-14 16:35:15 +00:00
for ( size_t i = 0 ; i < numPoints ; i + + )
2015-04-17 03:53:42 +00:00
{
if ( when [ i ] * mHiFreq > = 20 )
{
2020-04-11 07:08:33 +00:00
// Caution: on Linux, when when == 20, the log calculation rounds
2016-03-08 21:38:13 +00:00
// to just under zero, which causes an assert error.
double flog = ( log10 ( when [ i ] * mHiFreq ) - loLog ) / denom ;
2019-08-23 18:28:35 +00:00
mLogEnvelope - > Insert ( std : : max ( 0.0 , flog ) , value [ i ] ) ;
2015-04-17 03:53:42 +00:00
}
else
{ //get the first point as close as we can to the last point requested
changed = true ;
double v = value [ i ] ;
2019-08-23 18:28:35 +00:00
mLogEnvelope - > Insert ( 0. , v ) ;
2015-04-17 03:53:42 +00:00
}
}
2017-05-03 00:58:18 +00:00
mLogEnvelope - > Reassign ( 1. , value [ numPoints - 1 ] ) ;
2015-04-17 03:53:42 +00:00
if ( changed )
2016-08-04 11:33:22 +00:00
EnvelopeUpdated ( mLogEnvelope . get ( ) , false ) ;
2015-04-17 03:53:42 +00:00
}
void EffectEqualization : : ErrMin ( void )
{
double vals [ NUM_PTS ] ;
double error = 0.0 ;
double oldError = 0.0 ;
double mEQValsOld = 0.0 ;
double correction = 1.6 ;
bool flag ;
2016-04-14 16:35:15 +00:00
size_t j = 0 ;
2017-05-03 00:55:40 +00:00
Envelope testEnvelope { * mLogEnvelope } ;
2015-04-17 03:53:42 +00:00
2016-04-14 16:35:15 +00:00
for ( size_t i = 0 ; i < NUM_PTS ; i + + )
2016-02-01 01:39:24 +00:00
vals [ i ] = testEnvelope . GetValue ( mWhens [ i ] ) ;
2015-04-17 03:53:42 +00:00
// Do error minimisation
error = 0. ;
2016-02-01 01:39:24 +00:00
GraphicEQ ( & testEnvelope ) ;
2016-04-14 16:35:15 +00:00
for ( size_t i = 0 ; i < NUM_PTS ; i + + ) //calc initial error
2015-04-17 03:53:42 +00:00
{
2016-02-01 01:39:24 +00:00
double err = vals [ i ] - testEnvelope . GetValue ( mWhens [ i ] ) ;
2015-04-17 03:53:42 +00:00
error + = err * err ;
}
oldError = error ;
while ( j < mBandsInUse * 12 ) //loop over the sliders a number of times
{
2016-04-14 16:35:15 +00:00
auto i = j % mBandsInUse ; //use this slider
2015-04-17 03:53:42 +00:00
if ( ( j > 0 ) & ( i = = 0 ) ) // if we've come back to the first slider again...
{
if ( correction > 0 )
correction = - correction ; //go down
else
correction = - correction / 2. ; //go up half as much
}
flag = true ; // check if we've hit the slider limit
do
{
oldError = error ;
mEQValsOld = mEQVals [ i ] ;
mEQVals [ i ] + = correction ; //move fader value
if ( mEQVals [ i ] > 20. )
{
mEQVals [ i ] = 20. ;
flag = false ;
}
if ( mEQVals [ i ] < - 20. )
{
mEQVals [ i ] = - 20. ;
flag = false ;
}
2016-02-01 01:39:24 +00:00
GraphicEQ ( & testEnvelope ) ; //calculate envelope
2015-04-17 03:53:42 +00:00
error = 0. ;
2016-04-14 16:35:15 +00:00
for ( size_t k = 0 ; k < NUM_PTS ; k + + ) //calculate error
2015-04-17 03:53:42 +00:00
{
2016-02-01 01:39:24 +00:00
double err = vals [ k ] - testEnvelope . GetValue ( mWhens [ k ] ) ;
2015-04-17 03:53:42 +00:00
error + = err * err ;
}
}
2017-11-04 17:34:39 +00:00
while ( ( error < oldError ) & & flag ) ;
2015-04-17 03:53:42 +00:00
if ( error > oldError )
{
mEQVals [ i ] = mEQValsOld ; //last one didn't work
error = oldError ;
}
else
oldError = error ;
if ( error < .0025 * mBandsInUse )
break ; // close enuff
j + + ; //try next slider
}
if ( error > .0025 * mBandsInUse ) // not within 0.05dB on each slider, on average
2010-01-23 19:44:49 +00:00
{
2018-02-02 18:24:53 +00:00
Select ( ( int ) mCurves . size ( ) - 1 ) ;
2016-02-01 01:39:24 +00:00
EnvelopeUpdated ( & testEnvelope , false ) ;
2010-01-23 19:44:49 +00:00
}
}
2015-04-17 03:53:42 +00:00
void EffectEqualization : : GraphicEQ ( Envelope * env )
2010-01-23 19:44:49 +00:00
{
2014-06-03 20:30:19 +00:00
// JKC: 'value' is for height of curve.
// The 0.0 initial value would only get used if NUM_PTS were 0.
double value = 0.0 ;
2012-02-08 05:09:14 +00:00
double dist , span , s ;
2010-01-23 19:44:49 +00:00
env - > Flatten ( 0. ) ;
env - > SetTrackLen ( 1.0 ) ;
2015-04-17 03:53:42 +00:00
switch ( mInterp )
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
case kBspline : // B-spline
2010-01-23 19:44:49 +00:00
{
int minF = 0 ;
2016-04-14 16:35:15 +00:00
for ( size_t i = 0 ; i < NUM_PTS ; i + + )
2010-01-23 19:44:49 +00:00
{
2017-12-08 11:26:09 +00:00
while ( ( mWhenSliders [ minF ] < = mWhens [ i ] ) & ( minF < ( int ) mBandsInUse ) )
2010-01-23 19:44:49 +00:00
minF + + ;
minF - - ;
if ( minF < 0 ) //before first slider
{
2015-04-17 03:53:42 +00:00
dist = mWhens [ i ] - mWhenSliders [ 0 ] ;
span = mWhenSliders [ 1 ] - mWhenSliders [ 0 ] ;
2010-01-23 19:44:49 +00:00
s = dist / span ;
if ( s < - 1.5 )
value = 0. ;
2012-02-09 18:22:43 +00:00
else if ( s < - .5 )
2015-04-17 03:53:42 +00:00
value = mEQVals [ 0 ] * ( s + 1.5 ) * ( s + 1.5 ) / 2. ;
2010-01-23 19:44:49 +00:00
else
2015-04-17 03:53:42 +00:00
value = mEQVals [ 0 ] * ( .75 - s * s ) + mEQVals [ 1 ] * ( s + .5 ) * ( s + .5 ) / 2. ;
2010-01-23 19:44:49 +00:00
}
else
{
2015-04-17 03:53:42 +00:00
if ( mWhens [ i ] > mWhenSliders [ mBandsInUse - 1 ] ) //after last fader
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
dist = mWhens [ i ] - mWhenSliders [ mBandsInUse - 1 ] ;
span = mWhenSliders [ mBandsInUse - 1 ] - mWhenSliders [ mBandsInUse - 2 ] ;
2010-01-23 19:44:49 +00:00
s = dist / span ;
if ( s > 1.5 )
value = 0. ;
2012-02-09 18:22:43 +00:00
else if ( s > .5 )
2015-04-17 03:53:42 +00:00
value = mEQVals [ mBandsInUse - 1 ] * ( s - 1.5 ) * ( s - 1.5 ) / 2. ;
2010-01-23 19:44:49 +00:00
else
2015-04-17 03:53:42 +00:00
value = mEQVals [ mBandsInUse - 1 ] * ( .75 - s * s ) +
mEQVals [ mBandsInUse - 2 ] * ( s - .5 ) * ( s - .5 ) / 2. ;
2010-01-23 19:44:49 +00:00
}
else //normal case
{
2015-04-17 03:53:42 +00:00
dist = mWhens [ i ] - mWhenSliders [ minF ] ;
span = mWhenSliders [ minF + 1 ] - mWhenSliders [ minF ] ;
2010-01-23 19:44:49 +00:00
s = dist / span ;
if ( s < .5 )
{
2015-04-17 03:53:42 +00:00
value = mEQVals [ minF ] * ( 0.75 - s * s ) ;
2017-12-08 11:26:09 +00:00
if ( minF + 1 < ( int ) mBandsInUse )
2015-04-17 03:53:42 +00:00
value + = mEQVals [ minF + 1 ] * ( s + .5 ) * ( s + .5 ) / 2. ;
2010-01-23 19:44:49 +00:00
if ( minF - 1 > = 0 )
2015-04-17 03:53:42 +00:00
value + = mEQVals [ minF - 1 ] * ( s - .5 ) * ( s - .5 ) / 2. ;
2010-01-23 19:44:49 +00:00
}
else
{
2015-04-17 03:53:42 +00:00
value = mEQVals [ minF ] * ( s - 1.5 ) * ( s - 1.5 ) / 2. ;
2017-12-08 11:26:09 +00:00
if ( minF + 1 < ( int ) mBandsInUse )
2015-04-17 03:53:42 +00:00
value + = mEQVals [ minF + 1 ] * ( .75 - ( 1. - s ) * ( 1. - s ) ) ;
2017-12-08 11:26:09 +00:00
if ( minF + 2 < ( int ) mBandsInUse )
2015-04-17 03:53:42 +00:00
value + = mEQVals [ minF + 2 ] * ( s - .5 ) * ( s - .5 ) / 2. ;
2010-01-23 19:44:49 +00:00
}
}
}
2015-04-17 03:53:42 +00:00
if ( mWhens [ i ] < = 0. )
2017-05-03 00:58:18 +00:00
env - > Reassign ( 0. , value ) ;
2019-08-23 18:28:35 +00:00
env - > Insert ( mWhens [ i ] , value ) ;
2010-01-23 19:44:49 +00:00
}
2017-05-03 00:58:18 +00:00
env - > Reassign ( 1. , value ) ;
2010-01-23 19:44:49 +00:00
break ;
}
2015-04-17 03:53:42 +00:00
case kCosine : // Cosine squared
2010-01-23 19:44:49 +00:00
{
int minF = 0 ;
2016-04-14 16:35:15 +00:00
for ( size_t i = 0 ; i < NUM_PTS ; i + + )
2010-01-23 19:44:49 +00:00
{
2017-12-08 11:26:09 +00:00
while ( ( mWhenSliders [ minF ] < = mWhens [ i ] ) & ( minF < ( int ) mBandsInUse ) )
2010-01-23 19:44:49 +00:00
minF + + ;
minF - - ;
if ( minF < 0 ) //before first slider
{
2015-04-17 03:53:42 +00:00
dist = mWhenSliders [ 0 ] - mWhens [ i ] ;
span = mWhenSliders [ 1 ] - mWhenSliders [ 0 ] ;
2010-01-23 19:44:49 +00:00
if ( dist < span )
2015-04-17 03:53:42 +00:00
value = mEQVals [ 0 ] * ( 1. + cos ( M_PI * dist / span ) ) / 2. ;
2010-01-23 19:44:49 +00:00
else
value = 0. ;
}
else
{
2015-04-17 03:53:42 +00:00
if ( mWhens [ i ] > mWhenSliders [ mBandsInUse - 1 ] ) //after last fader
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
span = mWhenSliders [ mBandsInUse - 1 ] - mWhenSliders [ mBandsInUse - 2 ] ;
dist = mWhens [ i ] - mWhenSliders [ mBandsInUse - 1 ] ;
2010-01-23 19:44:49 +00:00
if ( dist < span )
2015-04-17 03:53:42 +00:00
value = mEQVals [ mBandsInUse - 1 ] * ( 1. + cos ( M_PI * dist / span ) ) / 2. ;
2010-01-23 19:44:49 +00:00
else
value = 0. ;
}
else //normal case
{
2015-04-17 03:53:42 +00:00
span = mWhenSliders [ minF + 1 ] - mWhenSliders [ minF ] ;
dist = mWhenSliders [ minF + 1 ] - mWhens [ i ] ;
value = mEQVals [ minF ] * ( 1. + cos ( M_PI * ( span - dist ) / span ) ) / 2. +
mEQVals [ minF + 1 ] * ( 1. + cos ( M_PI * dist / span ) ) / 2. ;
2010-01-23 19:44:49 +00:00
}
}
2015-04-17 03:53:42 +00:00
if ( mWhens [ i ] < = 0. )
2017-05-03 00:58:18 +00:00
env - > Reassign ( 0. , value ) ;
2019-08-23 18:28:35 +00:00
env - > Insert ( mWhens [ i ] , value ) ;
2010-01-23 19:44:49 +00:00
}
2017-05-03 00:58:18 +00:00
env - > Reassign ( 1. , value ) ;
2010-01-23 19:44:49 +00:00
break ;
}
2015-04-17 03:53:42 +00:00
case kCubic : // Cubic Spline
2010-01-23 19:44:49 +00:00
{
double y2 [ NUMBER_OF_BANDS + 1 ] ;
2015-04-17 03:53:42 +00:00
mEQVals [ mBandsInUse ] = mEQVals [ mBandsInUse - 1 ] ;
spline ( mWhenSliders , mEQVals , mBandsInUse + 1 , y2 ) ;
2010-01-23 19:44:49 +00:00
for ( double xf = 0 ; xf < 1. ; xf + = 1. / NUM_PTS )
{
2019-08-23 18:28:35 +00:00
env - > Insert ( xf , splint ( mWhenSliders , mEQVals , mBandsInUse + 1 , y2 , xf ) ) ;
2010-01-23 19:44:49 +00:00
}
break ;
}
}
2015-08-15 17:45:16 +00:00
ForceRecalc ( ) ;
2010-01-23 19:44:49 +00:00
}
2016-04-14 16:35:15 +00:00
void EffectEqualization : : spline ( double x [ ] , double y [ ] , size_t n , double y2 [ ] )
2010-01-23 19:44:49 +00:00
{
2016-04-14 16:35:15 +00:00
wxASSERT ( n > 0 ) ;
double p , sig ;
Doubles u { n } ;
2010-01-23 19:44:49 +00:00
y2 [ 0 ] = 0. ; //
u [ 0 ] = 0. ; //'natural' boundary conditions
2016-04-14 16:35:15 +00:00
for ( size_t i = 1 ; i + 1 < n ; i + + )
2010-01-23 19:44:49 +00:00
{
sig = ( x [ i ] - x [ i - 1 ] ) / ( x [ i + 1 ] - x [ i - 1 ] ) ;
p = sig * y2 [ i - 1 ] + 2. ;
y2 [ i ] = ( sig - 1. ) / p ;
u [ i ] = ( y [ i + 1 ] - y [ i ] ) / ( x [ i + 1 ] - x [ i ] ) - ( y [ i ] - y [ i - 1 ] ) / ( x [ i ] - x [ i - 1 ] ) ;
u [ i ] = ( 6. * u [ i ] / ( x [ i + 1 ] - x [ i - 1 ] ) - sig * u [ i - 1 ] ) / p ;
}
2016-04-14 16:35:15 +00:00
y2 [ n - 1 ] = 0. ;
for ( size_t i = n - 1 ; i - - ; )
2010-01-23 19:44:49 +00:00
y2 [ i ] = y2 [ i ] * y2 [ i + 1 ] + u [ i ] ;
}
2016-04-14 16:35:15 +00:00
double EffectEqualization : : splint ( double x [ ] , double y [ ] , size_t n , double y2 [ ] , double xr )
2010-01-23 19:44:49 +00:00
{
2016-04-14 16:35:15 +00:00
wxASSERT ( n > 1 ) ;
2010-01-23 19:44:49 +00:00
double a , b , h ;
static double xlast = 0. ; // remember last x value requested
2016-04-14 16:35:15 +00:00
static size_t k = 0 ; // and which interval we were in
2010-01-23 19:44:49 +00:00
if ( xr < xlast )
k = 0 ; // gone back to start, (or somewhere to the left)
xlast = xr ;
2016-04-14 16:35:15 +00:00
while ( ( x [ k ] < = xr ) & & ( k + 1 < n ) )
2010-01-23 19:44:49 +00:00
k + + ;
2016-04-14 16:35:15 +00:00
wxASSERT ( k > 0 ) ;
2010-01-23 19:44:49 +00:00
k - - ;
h = x [ k + 1 ] - x [ k ] ;
a = ( x [ k + 1 ] - xr ) / h ;
b = ( xr - x [ k ] ) / h ;
return ( a * y [ k ] + b * y [ k + 1 ] + ( ( a * a * a - a ) * y2 [ k ] + ( b * b * b - b ) * y2 [ k + 1 ] ) * h * h / 6. ) ;
}
2018-01-31 16:31:55 +00:00
void EffectEqualization : : OnErase ( wxEvent & )
{
}
2015-04-17 03:53:42 +00:00
void EffectEqualization : : OnSize ( wxSizeEvent & event )
{
mUIParent - > Layout ( ) ;
event . Skip ( ) ;
}
void EffectEqualization : : OnSlider ( wxCommandEvent & event )
{
wxSlider * s = ( wxSlider * ) event . GetEventObject ( ) ;
2016-04-14 16:35:15 +00:00
for ( size_t i = 0 ; i < mBandsInUse ; i + + )
2015-04-17 03:53:42 +00:00
{
if ( s = = mSliders [ i ] )
{
int posn = mSliders [ i ] - > GetValue ( ) ;
if ( wxGetKeyState ( WXK_SHIFT ) )
{
if ( posn > mSlidersOld [ i ] )
mEQVals [ i ] + = ( float ) .1 ;
else
if ( posn < mSlidersOld [ i ] )
mEQVals [ i ] - = .1f ;
}
else
mEQVals [ i ] + = ( posn - mSlidersOld [ i ] ) ;
if ( mEQVals [ i ] > 20. )
mEQVals [ i ] = 20. ;
if ( mEQVals [ i ] < - 20. )
mEQVals [ i ] = - 20. ;
int newPosn = ( int ) mEQVals [ i ] ;
mSliders [ i ] - > SetValue ( newPosn ) ;
mSlidersOld [ i ] = newPosn ;
wxString tip ;
if ( kThirdOct [ i ] < 1000. )
tip . Printf ( wxT ( " %dHz \n %.1fdB " ) , ( int ) kThirdOct [ i ] , mEQVals [ i ] ) ;
else
tip . Printf ( wxT ( " %gkHz \n %.1fdB " ) , kThirdOct [ i ] / 1000. , mEQVals [ i ] ) ;
s - > SetToolTip ( tip ) ;
break ;
}
}
2016-08-04 11:33:22 +00:00
GraphicEQ ( mLogEnvelope . get ( ) ) ;
2015-04-17 03:53:42 +00:00
EnvelopeUpdated ( ) ;
}
void EffectEqualization : : OnInterp ( wxCommandEvent & WXUNUSED ( event ) )
{
2019-03-22 11:10:41 +00:00
bool bIsGraphic = ! mDrawMode ;
if ( bIsGraphic )
2015-04-17 03:53:42 +00:00
{
2016-08-04 11:33:22 +00:00
GraphicEQ ( mLogEnvelope . get ( ) ) ;
2015-04-17 03:53:42 +00:00
EnvelopeUpdated ( ) ;
}
mInterp = mInterpChoice - > GetSelection ( ) ;
}
void EffectEqualization : : OnDrawMode ( wxCommandEvent & WXUNUSED ( event ) )
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
mDrawMode = true ;
2019-03-22 11:10:41 +00:00
UpdateDraw ( ) ;
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
void EffectEqualization : : OnGraphicMode ( wxCommandEvent & WXUNUSED ( event ) )
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
mDrawMode = false ;
2019-03-22 11:10:41 +00:00
UpdateGraphic ( ) ;
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
void EffectEqualization : : OnSliderM ( wxCommandEvent & WXUNUSED ( event ) )
2010-01-23 19:44:49 +00:00
{
TransferDataFromWindow ( ) ;
2015-08-15 17:45:16 +00:00
ForceRecalc ( ) ;
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
void EffectEqualization : : OnSliderDBMIN ( wxCommandEvent & WXUNUSED ( event ) )
2010-01-23 19:44:49 +00:00
{
TransferDataFromWindow ( ) ;
}
2015-04-17 03:53:42 +00:00
void EffectEqualization : : OnSliderDBMAX ( wxCommandEvent & WXUNUSED ( event ) )
2010-01-23 19:44:49 +00:00
{
TransferDataFromWindow ( ) ;
}
//
// New curve was selected
//
2015-04-17 03:53:42 +00:00
void EffectEqualization : : OnCurve ( wxCommandEvent & WXUNUSED ( event ) )
2010-01-23 19:44:49 +00:00
{
2016-02-13 15:43:16 +00:00
// Select NEW curve
2017-05-07 18:22:44 +00:00
wxASSERT ( mCurve ! = NULL ) ;
2010-01-23 19:44:49 +00:00
setCurve ( mCurve - > GetCurrentSelection ( ) ) ;
2015-04-17 03:53:42 +00:00
if ( ! mDrawMode )
2010-01-23 19:44:49 +00:00
UpdateGraphic ( ) ;
}
//
2010-06-15 23:49:51 +00:00
// User wants to modify the list in some way
2010-01-23 19:44:49 +00:00
//
2015-04-17 03:53:42 +00:00
void EffectEqualization : : OnManage ( wxCommandEvent & WXUNUSED ( event ) )
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
EditCurvesDialog d ( mUIParent , this , mCurve - > GetSelection ( ) ) ;
2010-06-15 23:49:51 +00:00
d . ShowModal ( ) ;
2015-05-29 16:32:55 +00:00
// Reload the curve names
UpdateCurves ( ) ;
// Allow control to resize
mUIParent - > Layout ( ) ;
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
void EffectEqualization : : OnClear ( wxCommandEvent & WXUNUSED ( event ) )
2010-01-23 19:44:49 +00:00
{
2015-04-22 18:02:55 +00:00
Flatten ( ) ;
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
void EffectEqualization : : OnInvert ( wxCommandEvent & WXUNUSED ( event ) ) // Inverts any curve
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
if ( ! mDrawMode ) // Graphic (Slider) mode. Invert the sliders.
2010-01-23 19:44:49 +00:00
{
2016-04-14 16:35:15 +00:00
for ( size_t i = 0 ; i < mBandsInUse ; i + + )
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
mEQVals [ i ] = - mEQVals [ i ] ;
int newPosn = ( int ) mEQVals [ i ] ;
mSliders [ i ] - > SetValue ( newPosn ) ;
mSlidersOld [ i ] = newPosn ;
2010-01-23 19:44:49 +00:00
wxString tip ;
2015-04-17 03:53:42 +00:00
if ( kThirdOct [ i ] < 1000. )
tip . Printf ( wxT ( " %dHz \n %.1fdB " ) , ( int ) kThirdOct [ i ] , mEQVals [ i ] ) ;
2010-01-23 19:44:49 +00:00
else
2015-04-17 03:53:42 +00:00
tip . Printf ( wxT ( " %gkHz \n %.1fdB " ) , kThirdOct [ i ] / 1000. , mEQVals [ i ] ) ;
mSliders [ i ] - > SetToolTip ( tip ) ;
2010-01-23 19:44:49 +00:00
}
2016-08-04 11:33:22 +00:00
GraphicEQ ( mLogEnvelope . get ( ) ) ;
2010-01-23 19:44:49 +00:00
}
else // Draw mode. Invert the points.
{
2015-08-15 17:45:16 +00:00
bool lin = IsLinear ( ) ; // refers to the 'log' or 'lin' of the frequency scale, not the amplitude
2016-04-14 16:35:15 +00:00
size_t numPoints ; // number of points in the curve/envelope
2010-01-23 19:44:49 +00:00
// determine if log or lin curve is the current one
// and find out how many points are in the curve
2015-08-15 17:45:16 +00:00
if ( lin ) // lin freq scale and so envelope
2010-01-23 19:44:49 +00:00
{
numPoints = mLinEnvelope - > GetNumberOfPoints ( ) ;
}
else
{
numPoints = mLogEnvelope - > GetNumberOfPoints ( ) ;
}
if ( numPoints = = 0 )
return ;
2016-04-14 16:35:15 +00:00
Doubles when { numPoints } ;
Doubles value { numPoints } ;
2010-01-23 19:44:49 +00:00
if ( lin )
2016-04-14 16:35:15 +00:00
mLinEnvelope - > GetPoints ( when . get ( ) , value . get ( ) , numPoints ) ;
2010-01-23 19:44:49 +00:00
else
2016-04-14 16:35:15 +00:00
mLogEnvelope - > GetPoints ( when . get ( ) , value . get ( ) , numPoints ) ;
2010-01-23 19:44:49 +00:00
// invert the curve
2016-04-14 16:35:15 +00:00
for ( size_t i = 0 ; i < numPoints ; i + + )
2010-01-23 19:44:49 +00:00
{
if ( lin )
2017-05-03 00:58:18 +00:00
mLinEnvelope - > Reassign ( when [ i ] , - value [ i ] ) ;
2010-01-23 19:44:49 +00:00
else
2017-05-03 00:58:18 +00:00
mLogEnvelope - > Reassign ( when [ i ] , - value [ i ] ) ;
2010-01-23 19:44:49 +00:00
}
// copy it back to the other one (just in case)
if ( lin )
EnvLinToLog ( ) ;
else
EnvLogToLin ( ) ;
}
// and update the display etc
2015-08-15 17:45:16 +00:00
ForceRecalc ( ) ;
2010-01-23 19:44:49 +00:00
EnvelopeUpdated ( ) ;
}
2015-04-17 03:53:42 +00:00
void EffectEqualization : : OnGridOnOff ( wxCommandEvent & WXUNUSED ( event ) )
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
mDrawGrid = mGridOnOff - > IsChecked ( ) ;
mPanel - > Refresh ( false ) ;
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
void EffectEqualization : : OnLinFreq ( wxCommandEvent & WXUNUSED ( event ) )
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
mLin = mLinFreq - > IsChecked ( ) ;
2015-08-15 17:45:16 +00:00
if ( IsLinear ( ) ) //going from log to lin freq scale
2015-04-17 03:53:42 +00:00
{
mFreqRuler - > ruler . SetLog ( false ) ;
mFreqRuler - > ruler . SetRange ( 0 , mHiFreq ) ;
EnvLogToLin ( ) ;
2016-08-04 11:33:22 +00:00
mEnvelope = mLinEnvelope . get ( ) ;
2015-04-17 03:53:42 +00:00
mLin = true ;
}
else //going from lin to log freq scale
{
mFreqRuler - > ruler . SetLog ( true ) ;
mFreqRuler - > ruler . SetRange ( mLoFreq , mHiFreq ) ;
EnvLinToLog ( ) ;
2016-08-04 11:33:22 +00:00
mEnvelope = mLogEnvelope . get ( ) ;
2015-04-17 03:53:42 +00:00
mLin = false ;
}
mFreqRuler - > Refresh ( false ) ;
2015-08-15 17:45:16 +00:00
ForceRecalc ( ) ;
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
# ifdef EXPERIMENTAL_EQ_SSE_THREADED
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
void EffectEqualization : : OnProcessingRadio ( wxCommandEvent & event )
{
int testEvent = event . GetId ( ) ;
switch ( testEvent )
2010-01-23 19:44:49 +00:00
{
2016-04-03 15:37:18 +00:00
case ID_DefaultMath : EffectEqualization48x : : SetMathPath ( MATH_FUNCTION_ORIGINAL ) ;
2015-04-17 03:53:42 +00:00
break ;
2016-04-03 15:37:18 +00:00
case ID_SSE : EffectEqualization48x : : SetMathPath ( MATH_FUNCTION_SSE ) ;
2015-04-17 03:53:42 +00:00
break ;
2016-04-03 15:37:18 +00:00
case ID_SSEThreaded : EffectEqualization48x : : SetMathPath ( MATH_FUNCTION_THREADED | MATH_FUNCTION_SSE ) ;
2015-04-17 03:53:42 +00:00
break ;
2016-04-03 15:37:18 +00:00
case ID_AVX : testEvent = 2 ;
2015-04-17 03:53:42 +00:00
break ;
2016-04-03 15:37:18 +00:00
case ID_AVXThreaded : testEvent = 2 ;
2015-04-17 03:53:42 +00:00
break ;
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
} ;
void EffectEqualization : : OnBench ( wxCommandEvent & event )
{
2016-04-03 15:37:18 +00:00
mBench = true ;
// OnOk(event);
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
# endif
//----------------------------------------------------------------------------
// EqualizationPanel
//----------------------------------------------------------------------------
2016-06-25 18:18:23 +00:00
BEGIN_EVENT_TABLE ( EqualizationPanel , wxPanelWrapper )
2015-04-17 03:53:42 +00:00
EVT_PAINT ( EqualizationPanel : : OnPaint )
EVT_MOUSE_EVENTS ( EqualizationPanel : : OnMouseEvent )
EVT_MOUSE_CAPTURE_LOST ( EqualizationPanel : : OnCaptureLost )
EVT_SIZE ( EqualizationPanel : : OnSize )
END_EVENT_TABLE ( )
2017-10-20 16:06:29 +00:00
EqualizationPanel : : EqualizationPanel (
wxWindow * parent , wxWindowID winid , EffectEqualization * effect )
: wxPanelWrapper ( parent , winid )
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
mParent = parent ;
mEffect = effect ;
mBitmap = NULL ;
mWidth = 0 ;
mHeight = 0 ;
2017-09-10 02:51:14 +00:00
mLinEditor = std : : make_unique < EnvelopeEditor > ( * mEffect - > mLinEnvelope , false ) ;
mLogEditor = std : : make_unique < EnvelopeEditor > ( * mEffect - > mLogEnvelope , false ) ;
2015-04-17 03:53:42 +00:00
mEffect - > mEnvelope - > Flatten ( 0. ) ;
mEffect - > mEnvelope - > SetTrackLen ( 1.0 ) ;
ForceRecalc ( ) ;
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
EqualizationPanel : : ~ EqualizationPanel ( )
2010-01-23 19:44:49 +00:00
{
2016-04-25 05:04:38 +00:00
if ( HasCapture ( ) )
ReleaseMouse ( ) ;
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
void EqualizationPanel : : ForceRecalc ( )
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
mRecalcRequired = true ;
Refresh ( false ) ;
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
void EqualizationPanel : : Recalc ( )
2010-01-23 19:44:49 +00:00
{
2016-04-14 16:35:15 +00:00
mOutr = Floats { mEffect - > mWindowSize } ;
mOuti = Floats { mEffect - > mWindowSize } ;
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
mEffect - > CalcFilter ( ) ; //to calculate the actual response
2016-04-14 16:35:15 +00:00
InverseRealFFT ( mEffect - > mWindowSize , mEffect - > mFilterFuncR . get ( ) , mEffect - > mFilterFuncI . get ( ) , mOutr . get ( ) ) ;
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
void EqualizationPanel : : OnSize ( wxSizeEvent & WXUNUSED ( event ) )
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
Refresh ( false ) ;
2010-01-23 19:44:49 +00:00
}
2017-07-10 14:26:10 +00:00
# include "../TrackPanelDrawingContext.h"
2015-04-17 03:53:42 +00:00
void EqualizationPanel : : OnPaint ( wxPaintEvent & WXUNUSED ( event ) )
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
wxPaintDC dc ( this ) ;
if ( mRecalcRequired ) {
Recalc ( ) ;
mRecalcRequired = false ;
}
int width , height ;
GetSize ( & width , & height ) ;
if ( ! mBitmap | | mWidth ! = width | | mHeight ! = height )
{
mWidth = width ;
mHeight = height ;
2018-08-03 17:29:49 +00:00
mBitmap = std : : make_unique < wxBitmap > ( mWidth , mHeight , 24 ) ;
2015-04-17 03:53:42 +00:00
}
wxBrush bkgndBrush ( wxSystemSettings : : GetColour ( wxSYS_COLOUR_3DFACE ) ) ;
wxMemoryDC memDC ;
memDC . SelectObject ( * mBitmap ) ;
wxRect bkgndRect ;
bkgndRect . x = 0 ;
bkgndRect . y = 0 ;
bkgndRect . width = mWidth ;
bkgndRect . height = mHeight ;
memDC . SetBrush ( bkgndBrush ) ;
memDC . SetPen ( * wxTRANSPARENT_PEN ) ;
memDC . DrawRectangle ( bkgndRect ) ;
bkgndRect . y = mHeight ;
memDC . DrawRectangle ( bkgndRect ) ;
wxRect border ;
border . x = 0 ;
border . y = 0 ;
border . width = mWidth ;
border . height = mHeight ;
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
memDC . SetBrush ( * wxWHITE_BRUSH ) ;
memDC . SetPen ( * wxBLACK_PEN ) ;
memDC . DrawRectangle ( border ) ;
mEnvRect = border ;
mEnvRect . Deflate ( PANELBORDER , PANELBORDER ) ;
// Pure blue x-axis line
2018-07-23 17:21:15 +00:00
memDC . SetPen ( wxPen ( theTheme . Colour ( clrGraphLines ) , 1 , wxPENSTYLE_SOLID ) ) ;
2015-04-17 03:53:42 +00:00
int center = ( int ) ( mEnvRect . height * mEffect - > mdBMax / ( mEffect - > mdBMax - mEffect - > mdBMin ) + .5 ) ;
AColor : : Line ( memDC ,
mEnvRect . GetLeft ( ) , mEnvRect . y + center ,
mEnvRect . GetRight ( ) , mEnvRect . y + center ) ;
// Draw the grid, if asked for. Do it now so it's underneath the main plots.
if ( mEffect - > mDrawGrid )
{
mEffect - > mFreqRuler - > ruler . DrawGrid ( memDC , mEnvRect . height , true , true , PANELBORDER , PANELBORDER ) ;
mEffect - > mdBRuler - > ruler . DrawGrid ( memDC , mEnvRect . width , true , true , PANELBORDER , PANELBORDER ) ;
}
// Med-blue envelope line
2018-07-23 17:21:15 +00:00
memDC . SetPen ( wxPen ( theTheme . Colour ( clrGraphLines ) , 3 , wxPENSTYLE_SOLID ) ) ;
2015-04-17 03:53:42 +00:00
// Draw envelope
int x , y , xlast = 0 , ylast = 0 ;
2010-01-23 19:44:49 +00:00
{
2016-04-14 16:35:15 +00:00
Doubles values { size_t ( mEnvRect . width ) } ;
mEffect - > mEnvelope - > GetValues ( values . get ( ) , mEnvRect . width , 0.0 , 1.0 / mEnvRect . width ) ;
bool off = false , off1 = false ;
for ( int i = 0 ; i < mEnvRect . width ; i + + )
2015-04-17 03:53:42 +00:00
{
2016-04-14 16:35:15 +00:00
x = mEnvRect . x + i ;
y = lrint ( mEnvRect . height * ( ( mEffect - > mdBMax - values [ i ] ) / ( mEffect - > mdBMax - mEffect - > mdBMin ) ) + .25 ) ; //needs more optimising, along with'what you get'?
if ( y > = mEnvRect . height )
{
y = mEnvRect . height - 1 ;
off = true ;
}
else
{
off = false ;
off1 = false ;
}
if ( ( i ! = 0 ) & ( ! off1 ) )
{
AColor : : Line ( memDC , xlast , ylast ,
x , mEnvRect . y + y ) ;
}
off1 = off ;
xlast = x ;
ylast = mEnvRect . y + y ;
2015-04-17 03:53:42 +00:00
}
}
//Now draw the actual response that you will get.
//mFilterFunc has a linear scale, window has a log one so we have to fiddle about
2018-07-23 17:21:15 +00:00
memDC . SetPen ( wxPen ( theTheme . Colour ( clrResponseLines ) , 1 , wxPENSTYLE_SOLID ) ) ;
2015-04-17 03:53:42 +00:00
double scale = ( double ) mEnvRect . height / ( mEffect - > mdBMax - mEffect - > mdBMin ) ; //pixels per dB
double yF ; //gain at this freq
2016-09-08 18:28:34 +00:00
double delta = mEffect - > mHiFreq / ( ( ( double ) mEffect - > mWindowSize / 2. ) ) ; //size of each freq bin
2015-04-17 03:53:42 +00:00
2015-08-15 17:45:16 +00:00
bool lin = mEffect - > IsLinear ( ) ; // log or lin scale?
2015-04-17 03:53:42 +00:00
double loLog = log10 ( mEffect - > mLoFreq ) ;
double step = lin ? mEffect - > mHiFreq : ( log10 ( mEffect - > mHiFreq ) - loLog ) ;
step / = ( ( double ) mEnvRect . width - 1. ) ;
double freq ; //actual freq corresponding to x position
2016-09-08 18:28:34 +00:00
int halfM = ( mEffect - > mM - 1 ) / 2 ;
2015-04-17 03:53:42 +00:00
int n ; //index to mFreqFunc
for ( int i = 0 ; i < mEnvRect . width ; i + + )
{
x = mEnvRect . x + i ;
freq = lin ? step * i : pow ( 10. , loLog + i * step ) ; //Hz
if ( ( lin ? step : ( pow ( 10. , loLog + ( i + 1 ) * step ) - freq ) ) < delta )
{ //not enough resolution in FFT
2020-04-11 07:08:33 +00:00
// set up for calculating cos using recurrence - faster than calculating it directly each time
2015-04-17 03:53:42 +00:00
double theta = M_PI * freq / mEffect - > mHiFreq ; //radians, normalized
double wtemp = sin ( 0.5 * theta ) ;
double wpr = - 2.0 * wtemp * wtemp ;
double wpi = - 1.0 * sin ( theta ) ;
double wr = cos ( theta * halfM ) ;
double wi = sin ( theta * halfM ) ;
yF = 0. ;
for ( int j = 0 ; j < halfM ; j + + )
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
yF + = 2. * mOutr [ j ] * wr ; // This works for me, compared to the previous version. Compare wr to cos(theta*(halfM-j)). Works for me. Keep everything as doubles though.
2020-04-11 07:08:33 +00:00
// do recurrence
2015-04-17 03:53:42 +00:00
wr = ( wtemp = wr ) * wpr - wi * wpi + wr ;
wi = wi * wpr + wtemp * wpi + wi ;
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
yF + = mOutr [ halfM ] ;
yF = fabs ( yF ) ;
if ( yF ! = 0. )
2015-07-24 20:59:34 +00:00
yF = LINEAR_TO_DB ( yF ) ;
2015-04-17 03:53:42 +00:00
else
yF = mEffect - > mdBMin ;
}
else
{ //use FFT, it has enough resolution
n = ( int ) ( freq / delta + .5 ) ;
if ( pow ( mEffect - > mFilterFuncR [ n ] , 2 ) + pow ( mEffect - > mFilterFuncI [ n ] , 2 ) ! = 0. )
yF = 10.0 * log10 ( pow ( mEffect - > mFilterFuncR [ n ] , 2 ) + pow ( mEffect - > mFilterFuncI [ n ] , 2 ) ) ; //10 here, a power
else
yF = mEffect - > mdBMin ;
}
if ( yF < mEffect - > mdBMin )
yF = mEffect - > mdBMin ;
yF = center - scale * yF ;
if ( yF > mEnvRect . height )
yF = mEnvRect . height - 1 ;
if ( yF < 0. )
yF = 0. ;
y = ( int ) ( yF + .5 ) ;
if ( i ! = 0 )
{
AColor : : Line ( memDC , xlast , ylast , x , mEnvRect . y + y ) ;
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
xlast = x ;
ylast = mEnvRect . y + y ;
2010-01-23 19:44:49 +00:00
}
2015-04-17 03:53:42 +00:00
memDC . SetPen ( * wxBLACK_PEN ) ;
2019-03-22 11:10:41 +00:00
if ( mEffect - > mDrawMode )
2015-08-15 17:45:16 +00:00
{
2018-11-03 17:34:48 +00:00
ZoomInfo zoomInfo ( 0.0 , mEnvRect . width - 1 ) ;
2018-11-05 15:45:03 +00:00
// Back pointer to TrackPanel won't be needed in the one drawing
// function we use here
TrackArtist artist ( nullptr ) ;
2018-11-03 17:34:48 +00:00
artist . pZoomInfo = & zoomInfo ;
TrackPanelDrawingContext context { memDC , { } , { } , & artist } ;
2019-06-09 11:46:42 +00:00
EnvelopeEditor : : DrawPoints ( * mEffect - > mEnvelope ,
2018-11-03 17:34:48 +00:00
context , mEnvRect , false , 0.0 ,
2017-06-23 23:28:38 +00:00
mEffect - > mdBMin , mEffect - > mdBMax , false ) ;
2015-08-15 17:45:16 +00:00
}
2015-04-17 03:53:42 +00:00
2015-08-15 17:45:16 +00:00
dc . Blit ( 0 , 0 , mWidth , mHeight , & memDC , 0 , 0 , wxCOPY , FALSE ) ;
2015-04-17 03:53:42 +00:00
}
void EqualizationPanel : : OnMouseEvent ( wxMouseEvent & event )
{
2015-04-22 18:02:55 +00:00
if ( ! mEffect - > mDrawMode )
{
return ;
}
2015-04-17 03:53:42 +00:00
if ( event . ButtonDown ( ) & & ! HasCapture ( ) )
{
CaptureMouse ( ) ;
}
2017-09-10 02:51:14 +00:00
auto & pEditor = ( mEffect - > mLin ? mLinEditor : mLogEditor ) ;
if ( pEditor - > MouseEvent ( event , mEnvRect , ZoomInfo ( 0.0 , mEnvRect . width ) ,
2015-08-15 22:07:29 +00:00
false , 0.0 ,
2015-04-17 03:53:42 +00:00
mEffect - > mdBMin , mEffect - > mdBMax ) )
{
mEffect - > EnvelopeUpdated ( ) ;
ForceRecalc ( ) ;
}
if ( event . ButtonUp ( ) & & HasCapture ( ) )
{
ReleaseMouse ( ) ;
}
}
void EqualizationPanel : : OnCaptureLost ( wxMouseCaptureLostEvent & WXUNUSED ( event ) )
{
if ( HasCapture ( ) )
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
ReleaseMouse ( ) ;
2010-01-23 19:44:49 +00:00
}
}
2010-06-27 22:20:58 +00:00
//----------------------------------------------------------------------------
// EditCurvesDialog
//----------------------------------------------------------------------------
// Note that the 'modified' curve used to be called 'custom' but is now called 'unnamed'
2010-06-15 23:49:51 +00:00
// Some things that deal with 'unnamed' curves still use, for example, 'mCustomBackup' as variable names.
/// Constructor
2010-06-27 22:20:58 +00:00
2016-07-10 21:10:50 +00:00
BEGIN_EVENT_TABLE ( EditCurvesDialog , wxDialogWrapper )
2014-01-16 18:25:03 +00:00
EVT_BUTTON ( UpButtonID , EditCurvesDialog : : OnUp )
EVT_BUTTON ( DownButtonID , EditCurvesDialog : : OnDown )
EVT_BUTTON ( RenameButtonID , EditCurvesDialog : : OnRename )
EVT_BUTTON ( DeleteButtonID , EditCurvesDialog : : OnDelete )
EVT_BUTTON ( ImportButtonID , EditCurvesDialog : : OnImport )
EVT_BUTTON ( ExportButtonID , EditCurvesDialog : : OnExport )
EVT_BUTTON ( LibraryButtonID , EditCurvesDialog : : OnLibrary )
EVT_BUTTON ( DefaultsButtonID , EditCurvesDialog : : OnDefaults )
EVT_BUTTON ( wxID_OK , EditCurvesDialog : : OnOK )
2016-06-22 23:08:47 +00:00
EVT_LIST_ITEM_SELECTED ( CurvesListID ,
EditCurvesDialog : : OnListSelectionChange )
EVT_LIST_ITEM_DESELECTED ( CurvesListID ,
EditCurvesDialog : : OnListSelectionChange )
2010-06-27 22:20:58 +00:00
END_EVENT_TABLE ( )
2015-04-17 03:53:42 +00:00
EditCurvesDialog : : EditCurvesDialog ( wxWindow * parent , EffectEqualization * effect , int position ) :
2019-12-08 05:25:47 +00:00
wxDialogWrapper ( parent , wxID_ANY , XO ( " Manage Curves List " ) ,
2014-01-16 17:55:35 +00:00
wxDefaultPosition , wxDefaultSize ,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER )
2010-06-15 23:49:51 +00:00
{
2019-12-08 05:25:47 +00:00
SetLabel ( XO ( " Manage Curves " ) ) ; // Provide visual label
SetName ( XO ( " Manage Curves List " ) ) ; // Provide audible label
2010-06-15 23:49:51 +00:00
mParent = parent ;
2015-04-17 03:53:42 +00:00
mEffect = effect ;
2010-06-15 23:49:51 +00:00
mPosition = position ;
2015-04-17 03:53:42 +00:00
// make a copy of mEffect->mCurves here to muck about with.
2018-02-02 18:24:53 +00:00
mEditCurves . clear ( ) ;
for ( unsigned int i = 0 ; i < mEffect - > mCurves . size ( ) ; i + + )
2010-06-15 23:49:51 +00:00
{
2018-02-02 18:24:53 +00:00
mEditCurves . push_back ( mEffect - > mCurves [ i ] . Name ) ;
2015-04-17 03:53:42 +00:00
mEditCurves [ i ] . points = mEffect - > mCurves [ i ] . points ;
2010-06-15 23:49:51 +00:00
}
Populate ( ) ;
2017-01-19 19:08:23 +00:00
SetMinSize ( GetSize ( ) ) ;
2010-06-15 23:49:51 +00:00
}
EditCurvesDialog : : ~ EditCurvesDialog ( )
{
}
/// Creates the dialog and its contents.
void EditCurvesDialog : : Populate ( )
{
//------------------------- Main section --------------------
ShuttleGui S ( this , eIsCreating ) ;
PopulateOrExchange ( S ) ;
// ----------------------- End of main section --------------
}
/// Defines the dialog and does data exchange with it.
void EditCurvesDialog : : PopulateOrExchange ( ShuttleGui & S )
{
2010-06-27 22:20:58 +00:00
S . StartHorizontalLay ( wxEXPAND ) ;
2010-06-15 23:49:51 +00:00
{
2019-12-22 19:58:36 +00:00
S . StartStatic ( XO ( " &Curves " ) , 1 ) ;
2010-06-15 23:49:51 +00:00
{
2018-02-03 00:22:07 +00:00
mList = S . Id ( CurvesListID )
2017-10-31 18:52:01 +00:00
. Style ( wxSUNKEN_BORDER | wxLC_REPORT | wxLC_HRULES | wxLC_VRULES )
2018-02-03 00:22:07 +00:00
. AddListControlReportMode ( {
2019-12-23 22:57:45 +00:00
{ XO ( " Curve Name " ) , wxLIST_FORMAT_RIGHT }
2018-02-03 00:22:07 +00:00
} ) ;
2010-06-15 23:49:51 +00:00
}
S . EndStatic ( ) ;
2010-06-27 22:20:58 +00:00
S . StartVerticalLay ( 0 ) ;
2010-06-15 23:49:51 +00:00
{
2020-05-11 15:28:14 +00:00
S . Id ( UpButtonID ) . AddButton ( XXO ( " Move &Up " ) , wxALIGN_LEFT ) ;
S . Id ( DownButtonID ) . AddButton ( XXO ( " Move &Down " ) , wxALIGN_LEFT ) ;
S . Id ( RenameButtonID ) . AddButton ( XXO ( " &Rename... " ) , wxALIGN_LEFT ) ;
S . Id ( DeleteButtonID ) . AddButton ( XXO ( " D&elete... " ) , wxALIGN_LEFT ) ;
S . Id ( ImportButtonID ) . AddButton ( XXO ( " I&mport... " ) , wxALIGN_LEFT ) ;
S . Id ( ExportButtonID ) . AddButton ( XXO ( " E&xport... " ) , wxALIGN_LEFT ) ;
S . Id ( LibraryButtonID ) . AddButton ( XXO ( " &Get More... " ) , wxALIGN_LEFT ) ;
S . Id ( DefaultsButtonID ) . AddButton ( XXO ( " De&faults " ) , wxALIGN_LEFT ) ;
2010-06-15 23:49:51 +00:00
}
S . EndVerticalLay ( ) ;
}
S . EndHorizontalLay ( ) ;
S . AddStandardButtons ( ) ;
2019-12-22 19:58:36 +00:00
S . StartStatic ( XO ( " Help " ) ) ;
S . AddConstTextBox ( { } , XO ( " Rename 'unnamed' to save a new entry. \n 'OK' saves all changes, 'Cancel' doesn't. " ) ) ;
2010-06-27 22:20:58 +00:00
S . EndStatic ( ) ;
2010-06-15 23:49:51 +00:00
PopulateList ( mPosition ) ;
Fit ( ) ;
return ;
}
void EditCurvesDialog : : PopulateList ( int position )
{
mList - > DeleteAllItems ( ) ;
2018-02-02 18:24:53 +00:00
for ( unsigned int i = 0 ; i < mEditCurves . size ( ) ; i + + )
2010-06-15 23:49:51 +00:00
mList - > InsertItem ( i , mEditCurves [ i ] . Name ) ;
mList - > SetColumnWidth ( 0 , wxLIST_AUTOSIZE ) ;
int curvesWidth = mList - > GetColumnWidth ( 0 ) ;
mList - > SetColumnWidth ( 0 , wxLIST_AUTOSIZE_USEHEADER ) ;
int headerWidth = mList - > GetColumnWidth ( 0 ) ;
mList - > SetColumnWidth ( 0 , wxMax ( headerWidth , curvesWidth ) ) ;
// use 'position' to set focus
mList - > EnsureVisible ( position ) ;
mList - > SetItemState ( position , wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED , wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED ) ;
}
2013-08-25 21:51:26 +00:00
void EditCurvesDialog : : OnUp ( wxCommandEvent & WXUNUSED ( event ) )
2010-06-15 23:49:51 +00:00
{
long item = mList - > GetNextItem ( - 1 , wxLIST_NEXT_ALL , wxLIST_STATE_SELECTED ) ;
if ( item = = - 1 )
return ; // no items selected
if ( item = = 0 )
item = mList - > GetNextItem ( item , wxLIST_NEXT_ALL , wxLIST_STATE_SELECTED ) ; // top item selected, can't move up
int state ;
while ( item ! = - 1 )
{
if ( item = = mList - > GetItemCount ( ) - 1 )
{ // 'unnamed' always stays at the bottom
2019-12-19 19:19:51 +00:00
mEffect - > Effect : : MessageBox (
XO ( " 'unnamed' always stays at the bottom of the list " ) ,
Effect : : DefaultMessageBoxStyle ,
XO ( " 'unnamed' is special " ) ) ; // these could get tedious!
2010-06-15 23:49:51 +00:00
return ;
}
state = mList - > GetItemState ( item - 1 , wxLIST_STATE_SELECTED ) ;
if ( state ! = wxLIST_STATE_SELECTED )
{ // swap this with one above but only if it isn't selected
EQCurve temp ( wxT ( " temp " ) ) ;
temp . Name = mEditCurves [ item ] . Name ;
temp . points = mEditCurves [ item ] . points ;
mEditCurves [ item ] . Name = mEditCurves [ item - 1 ] . Name ;
mEditCurves [ item ] . points = mEditCurves [ item - 1 ] . points ;
mEditCurves [ item - 1 ] . Name = temp . Name ;
mEditCurves [ item - 1 ] . points = temp . points ;
wxString sTemp = mList - > GetItemText ( item ) ;
mList - > SetItem ( item , 0 , mList - > GetItemText ( item - 1 ) ) ;
mList - > SetItem ( item - 1 , 0 , sTemp ) ;
mList - > SetItemState ( item , 0 , wxLIST_STATE_SELECTED ) ;
mList - > SetItemState ( item - 1 , wxLIST_STATE_SELECTED , wxLIST_STATE_SELECTED ) ;
}
item = mList - > GetNextItem ( item , wxLIST_NEXT_ALL , wxLIST_STATE_SELECTED ) ;
}
}
2013-08-25 21:51:26 +00:00
void EditCurvesDialog : : OnDown ( wxCommandEvent & WXUNUSED ( event ) )
2010-06-15 23:49:51 +00:00
{ // looks harder than OnUp as we need to seek backwards up the list, hence GetPreviousItem
long item = GetPreviousItem ( mList - > GetItemCount ( ) ) ;
if ( item = = - 1 )
return ; // nothing selected
int state ;
while ( item ! = - 1 )
{
if ( ( item ! = mList - > GetItemCount ( ) - 1 ) & & ( item ! = mList - > GetItemCount ( ) - 2 ) )
{ // can't move 'unnamed' down, or the one above it
state = mList - > GetItemState ( item + 1 , wxLIST_STATE_SELECTED ) ;
if ( state ! = wxLIST_STATE_SELECTED )
{ // swap this with one below but only if it isn't selected
EQCurve temp ( wxT ( " temp " ) ) ;
temp . Name = mEditCurves [ item ] . Name ;
temp . points = mEditCurves [ item ] . points ;
mEditCurves [ item ] . Name = mEditCurves [ item + 1 ] . Name ;
mEditCurves [ item ] . points = mEditCurves [ item + 1 ] . points ;
mEditCurves [ item + 1 ] . Name = temp . Name ;
mEditCurves [ item + 1 ] . points = temp . points ;
wxString sTemp = mList - > GetItemText ( item ) ;
mList - > SetItem ( item , 0 , mList - > GetItemText ( item + 1 ) ) ;
mList - > SetItem ( item + 1 , 0 , sTemp ) ;
mList - > SetItemState ( item , 0 , wxLIST_STATE_SELECTED ) ;
mList - > SetItemState ( item + 1 , wxLIST_STATE_SELECTED , wxLIST_STATE_SELECTED ) ;
}
}
item = GetPreviousItem ( item ) ;
}
}
long EditCurvesDialog : : GetPreviousItem ( long item ) // wx doesn't have this
{
long lastItem = - 1 ;
long itemTemp = mList - > GetNextItem ( - 1 , wxLIST_NEXT_ALL ,
2014-01-16 17:55:35 +00:00
wxLIST_STATE_SELECTED ) ;
2010-06-15 23:49:51 +00:00
while ( ( itemTemp ! = - 1 ) & & ( itemTemp < item ) )
{
lastItem = itemTemp ;
itemTemp = mList - > GetNextItem ( itemTemp , wxLIST_NEXT_ALL , wxLIST_STATE_SELECTED ) ;
}
return lastItem ;
}
// Rename curve/curves
2013-08-25 21:51:26 +00:00
void EditCurvesDialog : : OnRename ( wxCommandEvent & WXUNUSED ( event ) )
2010-06-15 23:49:51 +00:00
{
wxString name ;
2018-02-02 18:24:53 +00:00
int numCurves = mEditCurves . size ( ) ;
2012-02-08 05:09:14 +00:00
int curve = 0 ;
2010-06-15 23:49:51 +00:00
// Setup list of characters that aren't allowed
2019-02-12 21:30:22 +00:00
wxArrayStringEx exclude {
wxT ( " < " ) ,
wxT ( " > " ) ,
wxT ( " ' " ) ,
wxT ( " \" " ) ,
} ;
2010-06-15 23:49:51 +00:00
// Get the first one to be renamed
long item = mList - > GetNextItem ( - 1 , wxLIST_NEXT_ALL , wxLIST_STATE_SELECTED ) ;
long firstItem = item ; // for reselection with PopulateList
while ( item > = 0 )
{
// Prompt the user until a valid name is enter or cancelled
bool overwrite = false ;
bool bad = true ;
while ( bad ) // Check for an unacceptable duplicate
{ // Show the dialog and bail if the user cancels
bad = false ;
// build the dialog
2017-10-12 15:49:57 +00:00
AudacityTextEntryDialog dlg ( this ,
2019-12-18 05:07:58 +00:00
XO ( " Rename '%s' to... " ) . Format ( mEditCurves [ item ] . Name ) ,
XO ( " Rename... " ) ) ;
2010-06-15 23:49:51 +00:00
dlg . SetTextValidator ( wxFILTER_EXCLUDE_CHAR_LIST ) ;
2018-01-04 01:47:56 +00:00
dlg . SetName (
wxString : : Format ( _ ( " Rename '%s' " ) , mEditCurves [ item ] . Name ) ) ;
2010-06-15 23:49:51 +00:00
wxTextValidator * tv = dlg . GetTextValidator ( ) ;
tv - > SetExcludes ( exclude ) ; // Tell the validator about excluded chars
if ( dlg . ShowModal ( ) = = wxID_CANCEL )
{
bad = true ;
break ;
}
// Extract the name from the dialog
name = dlg . GetValue ( ) ;
// Search list of curves for a duplicate name
for ( curve = 0 ; curve < numCurves ; curve + + )
{
wxString temp = mEditCurves [ curve ] . Name ;
2019-02-28 13:54:36 +00:00
if ( name = = mEditCurves [ curve ] . Name ) // case sensitive
2010-06-15 23:49:51 +00:00
{
2012-02-09 18:22:43 +00:00
bad = true ;
2010-06-15 23:49:51 +00:00
if ( curve = = item ) // trying to rename a curve with the same name
{
2019-12-19 19:19:51 +00:00
mEffect - > Effect : : MessageBox (
XO ( " Name is the same as the original one " ) ,
wxOK ,
XO ( " Same name " ) ) ;
2010-06-15 23:49:51 +00:00
break ;
}
2018-03-24 19:22:50 +00:00
int answer = mEffect - > Effect : : MessageBox (
2019-12-19 19:19:51 +00:00
XO ( " Overwrite existing curve '%s'? " ) . Format ( name ) ,
wxYES_NO ,
XO ( " Curve exists " ) ) ;
2010-06-15 23:49:51 +00:00
if ( answer = = wxYES )
{
bad = false ;
overwrite = true ; // we are going to overwrite the one with this name
break ;
}
}
}
2019-03-14 17:04:37 +00:00
if ( name . empty ( ) | | name = = wxT ( " unnamed " ) )
2010-06-15 23:49:51 +00:00
bad = true ;
}
2012-02-09 18:22:43 +00:00
// if bad, we cancelled the rename dialog, so nothing to do.
2014-06-03 20:30:19 +00:00
if ( bad = = true )
2012-02-09 18:22:43 +00:00
;
else if ( overwrite ) {
// Overwrite another curve.
// JKC: because 'overwrite' is true, 'curve' is the number of the curve that
// we are about to overwrite.
mEditCurves [ curve ] . Name = name ;
mEditCurves [ curve ] . points = mEditCurves [ item ] . points ;
// if renaming the unnamed item, then select it,
// otherwise get rid of the item we've renamed.
2014-06-03 20:30:19 +00:00
if ( item = = ( numCurves - 1 ) )
2012-02-09 18:22:43 +00:00
mList - > SetItem ( curve , 0 , name ) ;
else
2010-06-15 23:49:51 +00:00
{
2018-02-02 18:24:53 +00:00
mEditCurves . erase ( mEditCurves . begin ( ) + item ) ;
2012-02-09 18:22:43 +00:00
numCurves - - ;
2010-06-15 23:49:51 +00:00
}
}
2012-02-09 18:22:43 +00:00
else if ( item = = ( numCurves - 1 ) ) // renaming 'unnamed'
2016-02-13 15:43:16 +00:00
{ // Create a NEW entry
2018-02-02 18:24:53 +00:00
mEditCurves . push_back ( EQCurve ( wxT ( " unnamed " ) ) ) ;
2012-02-09 18:22:43 +00:00
// Copy over the points
mEditCurves [ numCurves ] . points = mEditCurves [ numCurves - 1 ] . points ;
2016-02-13 15:43:16 +00:00
// Give the original unnamed entry the NEW name
2012-02-09 18:22:43 +00:00
mEditCurves [ numCurves - 1 ] . Name = name ;
numCurves + + ;
}
else // just rename (the 'normal' case)
{
mEditCurves [ item ] . Name = name ;
mList - > SetItem ( item , 0 , name ) ;
}
2010-06-15 23:49:51 +00:00
// get next selected item
item = mList - > GetNextItem ( item , wxLIST_NEXT_ALL , wxLIST_STATE_SELECTED ) ;
}
PopulateList ( firstItem ) ; // Note: only saved to file when you OK out of the dialog
return ;
}
// Delete curve/curves
2013-08-25 21:51:26 +00:00
void EditCurvesDialog : : OnDelete ( wxCommandEvent & WXUNUSED ( event ) )
2010-06-15 23:49:51 +00:00
{
2020-04-11 07:08:33 +00:00
// We could count them here
2010-06-15 23:49:51 +00:00
// And then put in a 'Delete N items?' prompt.
#if 0 // 'one at a time' prompt code
// Get the first one to be deleted
long item = mList - > GetNextItem ( - 1 , wxLIST_NEXT_ALL , wxLIST_STATE_SELECTED ) ;
// Take care, mList and mEditCurves will get out of sync as curves are deleted
int deleted = 0 ;
long highlight = - 1 ;
while ( item > = 0 )
{
if ( item = = mList - > GetItemCount ( ) - 1 ) //unnamed
{
2019-12-19 19:19:51 +00:00
mEffect - > Effect : : MessageBox (
XO ( " You cannot delete the 'unnamed' curve. " ) ,
wxOK | wxCENTRE ,
XO ( " Can't delete 'unnamed' " ) ) ;
2010-06-15 23:49:51 +00:00
}
else
{
// Create the prompt
2019-12-19 19:19:51 +00:00
auto quest = XO ( " Delete '%s'? " )
. Format ( mEditCurves [ item - deleted ] . Name ) ) ;
2010-06-15 23:49:51 +00:00
// Ask for confirmation before removal
2019-12-19 19:19:51 +00:00
int ans = mEffect - > Effect : : MessageBox (
quest ,
wxYES_NO | wxCENTRE ,
XO ( " Confirm Deletion " ) ) ;
2010-06-15 23:49:51 +00:00
if ( ans = = wxYES )
{ // Remove the curve from the array
mEditCurves . RemoveAt ( item - deleted ) ;
deleted + + ;
}
else
highlight = item - deleted ; // if user presses 'No', select that curve
}
// get next selected item
item = mList - > GetNextItem ( item , wxLIST_NEXT_ALL , wxLIST_STATE_SELECTED ) ;
}
if ( highlight = = - 1 )
2019-02-12 00:10:48 +00:00
PopulateList ( mEditCurves . size ( ) - 1 ) ; // set 'unnamed' as the selected curve
2010-06-15 23:49:51 +00:00
else
PopulateList ( highlight ) ; // user said 'No' to deletion
2016-02-14 23:50:45 +00:00
# else // 'DELETE all N' code
2010-06-15 23:49:51 +00:00
int count = mList - > GetSelectedItemCount ( ) ;
long item = mList - > GetNextItem ( - 1 , wxLIST_NEXT_ALL , wxLIST_STATE_SELECTED ) ;
// Create the prompt
2019-12-19 19:19:51 +00:00
TranslatableString quest ;
2010-06-15 23:49:51 +00:00
if ( count > 1 )
2019-12-19 19:19:51 +00:00
quest = XO ( " Delete %d items? " ) . Format ( count ) ;
2010-06-15 23:49:51 +00:00
else
if ( count = = 1 )
2019-12-19 19:19:51 +00:00
quest = XO ( " Delete '%s'? " ) . Format ( mEditCurves [ item ] . Name ) ;
2010-06-15 23:49:51 +00:00
else
return ;
// Ask for confirmation before removal
2019-12-19 19:19:51 +00:00
int ans = mEffect - > Effect : : MessageBox (
quest ,
wxYES_NO | wxCENTRE ,
XO ( " Confirm Deletion " ) ) ;
2010-06-15 23:49:51 +00:00
if ( ans = = wxYES )
{ // Remove the curve(s) from the array
// Take care, mList and mEditCurves will get out of sync as curves are deleted
int deleted = 0 ;
while ( item > = 0 )
{
2018-06-21 14:55:20 +00:00
// TODO: Migrate to the standard "Manage" dialog.
if ( item = = mList - > GetItemCount ( ) - 1 ) //unnamed
{
2019-12-19 19:19:51 +00:00
mEffect - > Effect : : MessageBox (
XO ( " You cannot delete the 'unnamed' curve, it is special. " ) ,
Effect : : DefaultMessageBoxStyle ,
XO ( " Can't delete 'unnamed' " ) ) ;
2018-06-21 14:55:20 +00:00
}
2010-06-15 23:49:51 +00:00
else
{
2018-02-02 18:24:53 +00:00
mEditCurves . erase ( mEditCurves . begin ( ) + item - deleted ) ;
2010-06-15 23:49:51 +00:00
deleted + + ;
}
item = mList - > GetNextItem ( item , wxLIST_NEXT_ALL , wxLIST_STATE_SELECTED ) ;
}
2018-02-02 18:24:53 +00:00
PopulateList ( mEditCurves . size ( ) - 1 ) ; // set 'unnamed' as the selected curve
2010-06-15 23:49:51 +00:00
}
# endif
}
2020-05-22 21:13:20 +00:00
static const FileNames : : FileTypes & XMLtypes ( )
{
static const FileNames : : FileTypes results {
FileNames : : XMLFiles
} ;
return results ;
}
2019-12-20 18:02:31 +00:00
2013-08-25 21:51:26 +00:00
void EditCurvesDialog : : OnImport ( wxCommandEvent & WXUNUSED ( event ) )
2010-06-15 23:49:51 +00:00
{
2019-12-20 18:02:31 +00:00
FileDialogWrapper filePicker (
this ,
2019-12-20 17:13:39 +00:00
XO ( " Choose an EQ curve file " ) , FileNames : : DataDir ( ) , wxT ( " " ) ,
2020-05-22 21:13:20 +00:00
XMLtypes ( ) ) ;
2019-03-15 18:41:21 +00:00
wxString fileName ;
2010-06-15 23:49:51 +00:00
if ( filePicker . ShowModal ( ) = = wxID_CANCEL )
return ;
else
fileName = filePicker . GetPath ( ) ;
// Use EqualizationDialog::LoadCurves to read into (temporary) mEditCurves
// This may not be the best OOP way of doing it, but I don't know better (MJS)
EQCurveArray temp ;
2015-04-17 03:53:42 +00:00
temp = mEffect - > mCurves ; // temp copy of the main dialog curves
mEffect - > mCurves = mEditCurves ; // copy EditCurvesDialog to main interface
mEffect - > LoadCurves ( fileName , true ) ; // use main interface to load imported curves
mEditCurves = mEffect - > mCurves ; // copy back to this interface
mEffect - > mCurves = temp ; // and reset the main interface how it was
2010-06-15 23:49:51 +00:00
PopulateList ( 0 ) ; // update the EditCurvesDialog dialog
return ;
}
2013-08-25 21:51:26 +00:00
void EditCurvesDialog : : OnExport ( wxCommandEvent & WXUNUSED ( event ) )
2010-06-15 23:49:51 +00:00
{
2019-12-20 17:13:39 +00:00
FileDialogWrapper filePicker ( this , XO ( " Export EQ curves as... " ) ,
2019-12-20 18:02:31 +00:00
FileNames : : DataDir ( ) , wxT ( " " ) ,
2020-05-22 21:13:20 +00:00
XMLtypes ( ) ,
2019-12-20 17:13:39 +00:00
wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER ) ; // wxFD_CHANGE_DIR?
2019-03-15 18:41:21 +00:00
wxString fileName ;
2010-06-15 23:49:51 +00:00
if ( filePicker . ShowModal ( ) = = wxID_CANCEL )
return ;
else
fileName = filePicker . GetPath ( ) ;
EQCurveArray temp ;
2015-04-17 03:53:42 +00:00
temp = mEffect - > mCurves ; // backup the parent's curves
2010-06-15 23:49:51 +00:00
EQCurveArray exportCurves ; // Copy selected curves to export
2018-02-02 18:24:53 +00:00
exportCurves . clear ( ) ;
2010-06-15 23:49:51 +00:00
long item = mList - > GetNextItem ( - 1 , wxLIST_NEXT_ALL , wxLIST_STATE_SELECTED ) ;
int i = 0 ;
while ( item > = 0 )
{
if ( item ! = mList - > GetItemCount ( ) - 1 ) // not 'unnamed'
{
2018-02-02 18:24:53 +00:00
exportCurves . push_back ( mEditCurves [ item ] . Name ) ;
2010-06-15 23:49:51 +00:00
exportCurves [ i ] . points = mEditCurves [ item ] . points ;
i + + ;
}
else
2019-12-19 19:19:51 +00:00
mEffect - > Effect : : MessageBox (
XO ( " You cannot export 'unnamed' curve, it is special. " ) ,
Effect : : DefaultMessageBoxStyle ,
XO ( " Cannot Export 'unnamed' " ) ) ;
2010-06-15 23:49:51 +00:00
// get next selected item
item = mList - > GetNextItem ( item , wxLIST_NEXT_ALL , wxLIST_STATE_SELECTED ) ;
}
if ( i > 0 )
{
2015-04-17 03:53:42 +00:00
mEffect - > mCurves = exportCurves ;
mEffect - > SaveCurves ( fileName ) ;
mEffect - > mCurves = temp ;
2019-12-19 19:19:51 +00:00
auto message = XO ( " %d curves exported to %s " ) . Format ( i , fileName ) ;
mEffect - > Effect : : MessageBox (
message ,
Effect : : DefaultMessageBoxStyle ,
XO ( " Curves exported " ) ) ;
2010-06-15 23:49:51 +00:00
}
else
2019-12-19 19:19:51 +00:00
mEffect - > Effect : : MessageBox (
XO ( " No curves exported " ) ,
Effect : : DefaultMessageBoxStyle ,
XO ( " No curves exported " ) ) ;
2010-06-15 23:49:51 +00:00
}
2013-08-25 21:51:26 +00:00
void EditCurvesDialog : : OnLibrary ( wxCommandEvent & WXUNUSED ( event ) )
2010-06-15 23:49:51 +00:00
{
2017-08-25 12:54:37 +00:00
// full path to wiki.
wxLaunchDefaultBrowser ( wxT ( " https://wiki.audacityteam.org/wiki/EQCurvesDownload " ) ) ;
2010-06-15 23:49:51 +00:00
}
2013-08-25 21:51:26 +00:00
void EditCurvesDialog : : OnDefaults ( wxCommandEvent & WXUNUSED ( event ) )
2010-06-15 23:49:51 +00:00
{
EQCurveArray temp ;
2015-04-17 03:53:42 +00:00
temp = mEffect - > mCurves ;
2010-12-11 22:32:07 +00:00
// we expect this to fail in LoadCurves (due to a lack of path) and handle that there
2015-04-17 03:53:42 +00:00
mEffect - > LoadCurves ( wxT ( " EQDefaultCurves.xml " ) ) ;
mEditCurves = mEffect - > mCurves ;
mEffect - > mCurves = temp ;
2010-06-15 23:49:51 +00:00
PopulateList ( 0 ) ; // update the EditCurvesDialog dialog
}
2013-08-25 21:51:26 +00:00
void EditCurvesDialog : : OnOK ( wxCommandEvent & WXUNUSED ( event ) )
2010-06-15 23:49:51 +00:00
{
// Make a backup of the current curves
wxString backupPlace = wxFileName ( FileNames : : DataDir ( ) , wxT ( " EQBackup.xml " ) ) . GetFullPath ( ) ;
2015-04-17 03:53:42 +00:00
mEffect - > SaveCurves ( backupPlace ) ;
2010-06-15 23:49:51 +00:00
// Load back into the main dialog
2018-02-02 18:24:53 +00:00
mEffect - > mCurves . clear ( ) ;
for ( unsigned int i = 0 ; i < mEditCurves . size ( ) ; i + + )
2010-06-15 23:49:51 +00:00
{
2018-02-02 18:24:53 +00:00
mEffect - > mCurves . push_back ( mEditCurves [ i ] . Name ) ;
2015-04-17 03:53:42 +00:00
mEffect - > mCurves [ i ] . points = mEditCurves [ i ] . points ;
2010-06-15 23:49:51 +00:00
}
2015-04-17 03:53:42 +00:00
mEffect - > SaveCurves ( ) ;
mEffect - > LoadCurves ( ) ;
// mEffect->CreateChoice();
wxGetTopLevelParent ( mEffect - > mUIParent ) - > Layout ( ) ;
// mEffect->mUIParent->Layout();
2010-06-15 23:49:51 +00:00
// Select something sensible
long item = mList - > GetNextItem ( - 1 ,
2014-01-16 17:55:35 +00:00
wxLIST_NEXT_ALL ,
wxLIST_STATE_SELECTED ) ;
2010-06-15 23:49:51 +00:00
if ( item = = - 1 )
item = mList - > GetItemCount ( ) - 1 ; // nothing selected, default to 'unnamed'
2015-04-17 03:53:42 +00:00
mEffect - > setCurve ( item ) ;
2010-06-15 23:49:51 +00:00
EndModal ( true ) ;
}
2016-06-22 23:08:47 +00:00
void EditCurvesDialog : : OnListSelectionChange ( wxListEvent & )
{
const bool enable = mList - > GetSelectedItemCount ( ) > 0 ;
static const int ids [ ] = {
UpButtonID ,
DownButtonID ,
RenameButtonID ,
DeleteButtonID ,
} ;
for ( auto id : ids )
FindWindowById ( id , this ) - > Enable ( enable ) ;
}