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
2014-01-16 17:55:35 +00:00
The filter is applied using overlap / add of Hanning 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
various graphing code , such as provided by FreqWindow 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
2010-01-23 19:44:49 +00:00
# include "../Audacity.h"
2015-07-03 04:20:21 +00:00
# include "Equalization.h"
2015-04-17 03:53:42 +00:00
# include <math.h>
2010-01-23 19:44:49 +00:00
# include <vector>
# 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
2010-01-23 19:44:49 +00:00
# include <wx/dcmemory.h>
# include <wx/event.h>
# include <wx/image.h>
# include <wx/intl.h>
# include <wx/choice.h>
# include <wx/radiobut.h>
# 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>
# include <wx/checkbox.h>
2015-04-17 03:53:42 +00:00
# include <wx/tooltip.h>
# include <wx/utils.h>
# include "../Experimental.h"
# include "../AColor.h"
# include "../ShuttleGui.h"
# include "../PlatformCompatibility.h"
# include "../FileNames.h"
# include "../Envelope.h"
# include "../widgets/LinkingHtmlWindow.h"
# include "../widgets/ErrorDialog.h"
# include "../FFT.h"
# include "../Prefs.h"
# include "../Project.h"
# include "../WaveTrack.h"
# include "../widgets/Ruler.h"
# include "../xml/XMLFileReader.h"
# include "../Theme.h"
# include "../AllThemeResources.h"
# include "../float_cast.h"
2015-05-04 04:36:30 +00:00
# include "FileDialog.h"
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
2018-02-09 19:12:25 +00:00
static const wxString kInterpStrings [ nInterpolations ] =
2015-04-17 03:53:42 +00:00
{
/* i18n-hint: Technical term for a kind of curve.*/
2015-04-19 03:49:05 +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
2017-10-03 22:07:04 +00:00
Param ( FilterLength , int , wxT ( " FilterLength " ) , 4001 , 21 , 8191 , 0 ) ;
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
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
EffectEqualization : : EffectEqualization ( )
2016-04-14 16:35:15 +00:00
: mFFTBuffer { windowSize }
, mFilterFuncR { windowSize }
, mFilterFuncI { windowSize }
2010-01-23 19:44:49 +00:00
{
2015-08-15 17:45:16 +00:00
mCurve = NULL ;
mPanel = NULL ;
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 ) ;
2018-02-09 19:12:25 +00:00
for ( int i = 0 ; i < nInterpolations ; i + + )
2015-04-17 03:53:42 +00:00
{
mInterpolations . Add ( wxGetTranslation ( kInterpStrings [ i ] ) ) ;
}
2010-01-23 19:44:49 +00:00
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
2010-01-23 19:44:49 +00:00
}
EffectEqualization : : ~ EffectEqualization ( )
{
}
2015-04-17 03:53:42 +00:00
// IdentInterface implementation
wxString EffectEqualization : : GetSymbol ( )
{
return EQUALIZATION_PLUGIN_SYMBOL ;
}
wxString EffectEqualization : : GetDescription ( )
{
2018-01-08 00:45:20 +00:00
return _ ( " 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 ( )
{
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 ) {
2018-02-09 19:12:25 +00:00
wxArrayString interp ( nInterpolations , kInterpStrings ) ;
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 ( mM , FilterLength ) ;
S . SHUTTLE_PARAM ( mCurveName , CurveName ) ;
S . SHUTTLE_PARAM ( mLin , InterpLin ) ;
S . SHUTTLE_ENUM_PARAM ( mInterp , InterpMeth , interp ) ;
2015-04-17 03:53:42 +00:00
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 ) ;
2015-04-17 03:53:42 +00:00
parms . Write ( KEY_CurveName , mCurveName ) ;
2015-08-16 20:04:24 +00:00
parms . Write ( KEY_InterpLin , mLin ) ;
2018-02-09 19:12:25 +00:00
parms . WriteEnum ( KEY_InterpMeth , mInterp , wxArrayString ( nInterpolations , kInterpStrings ) ) ;
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.
wxArrayString interpolations ( mInterpolations ) ;
2018-02-09 19:12:25 +00:00
for ( int i = 0 ; i < nInterpolations ; i + + )
2015-04-17 03:53:42 +00:00
{
interpolations . Add ( kInterpStrings [ i ] ) ;
}
ReadAndVerifyInt ( FilterLength ) ;
ReadAndVerifyString ( CurveName ) ;
ReadAndVerifyBool ( InterpLin ) ;
ReadAndVerifyEnum ( InterpMeth , interpolations ) ;
mM = FilterLength ;
mCurveName = CurveName ;
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 ;
}
2015-04-27 15:52:42 +00:00
bool EffectEqualization : : LoadFactoryDefaults ( )
{
mdBMin = DEF_dBMin ;
mdBMax = DEF_dBMax ;
mDrawMode = DEF_DrawMode ;
mDrawGrid = DEF_DrawGrid ;
return Effect : : LoadFactoryDefaults ( ) ;
}
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
if ( mDisallowCustom & & mCurveName . IsSameAs ( 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.
2018-03-16 18:47:39 +00:00
Effect : : MessageBox ( _ ( " To use this EQ 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 ,
2017-09-10 14:42:33 +00:00
_ ( " EQ Curve 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
bool EffectEqualization : : Startup ( )
{
wxString base = wxT ( " /Effects/Equalization/ " ) ;
// 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 ;
2014-01-08 01:03:31 +00:00
TrackListIterator iter ( GetActiveProject ( ) - > GetTracks ( ) ) ;
Track * t = iter . First ( ) ;
while ( t ) {
if ( t - > GetSelected ( ) & & t - > GetKind ( ) = = Track : : Wave ) {
WaveTrack * track = ( WaveTrack * ) t ;
if ( selcount = = 0 ) {
rate = track - > GetRate ( ) ;
}
else {
if ( track - > GetRate ( ) ! = rate ) {
2017-09-10 14:42:33 +00:00
Effect : : MessageBox ( _ ( " To apply Equalization, all selected tracks must have the same sample rate. " ) ) ;
2014-01-08 01:03:31 +00:00
return ( false ) ;
}
}
selcount + + ;
}
t = iter . Next ( ) ;
}
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 ) {
2017-09-10 14:42:33 +00:00
Effect : : MessageBox ( _ ( " Track sample rate is too low for this effect. " ) ,
wxOK | wxCENTRE ,
_ ( " 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.
bool bGoodResult = true ;
2016-08-13 13:26:53 +00:00
SelectedTrackListOfKindIterator iter ( Track : : Wave , mOutputTracks . get ( ) ) ;
2010-01-23 19:44:49 +00:00
WaveTrack * track = ( WaveTrack * ) iter . First ( ) ;
int count = 0 ;
while ( track ) {
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 ;
}
}
track = ( WaveTrack * ) iter . Next ( ) ;
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 : : PopulateUI ( wxWindow * parent )
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
mUIParent = parent ;
mUIParent - > PushEventHandler ( this ) ;
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
LoadUserPreset ( GetCurrentSettingsGroup ( ) ) ;
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
ShuttleGui S ( mUIParent , eIsCreating ) ;
PopulateOrExchange ( S ) ;
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
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
{
2016-02-14 07:54:25 +00:00
wxWindow * const parent = S . GetParent ( ) ;
2015-04-17 03:53:42 +00:00
2015-08-15 17:45:16 +00:00
LoadCurves ( ) ;
2010-01-23 19:44:49 +00:00
2016-12-29 15:36:37 +00:00
TrackListOfKindIterator iter ( Track : : Wave , inputTracks ( ) ) ;
2015-08-15 17:45:16 +00:00
WaveTrack * t = ( WaveTrack * ) iter . First ( ) ;
mHiFreq = ( t ? t - > GetRate ( ) : GetActiveProject ( ) - > GetRate ( ) ) / 2.0 ;
mLoFreq = loFreqI ;
2010-01-23 19:44:49 +00:00
2015-08-15 17:45:16 +00:00
S . SetBorder ( 0 ) ;
S . SetSizerProportion ( 1 ) ;
S . StartMultiColumn ( 1 , wxEXPAND ) ;
2010-01-23 19:44:49 +00:00
{
2015-08-15 17:45:16 +00:00
S . SetStretchyCol ( 0 ) ;
S . SetStretchyRow ( 1 ) ;
szrV = S . GetSizer ( ) ;
// -------------------------------------------------------------------
// ROW 1: Top border
// -------------------------------------------------------------------
S . AddSpace ( 5 ) ;
S . SetSizerProportion ( 1 ) ;
S . StartMultiColumn ( 3 , wxEXPAND ) ;
{
S . SetStretchyCol ( 1 ) ;
S . SetStretchyRow ( 0 ) ;
szr1 = S . GetSizer ( ) ;
// -------------------------------------------------------------------
// ROW 2: Equalization panel and sliders for vertical scale
// -------------------------------------------------------------------
S . StartVerticalLay ( ) ;
{
2017-11-01 13:16:56 +00:00
mdBRuler = safenew RulerPanel (
parent , wxID_ANY , wxVERTICAL ,
wxSize { 100 , 100 } , // Ruler can't handle small sizes
RulerPanel : : Range { 60.0 , - 120.0 } ,
Ruler : : LinearDBFormat ,
_ ( " dB " ) ,
RulerPanel : : Options { }
. LabelEdges ( true )
. TicksAtExtremes ( true )
. TickColour ( { 0 , 0 , 0 } )
) ;
2015-08-15 17:45:16 +00:00
S . Prop ( 1 ) ;
S . AddSpace ( 0 , 1 ) ;
S . AddWindow ( mdBRuler , wxEXPAND | wxALIGN_RIGHT ) ;
S . AddSpace ( 0 , 1 ) ;
}
S . EndVerticalLay ( ) ;
2017-10-20 16:06:29 +00:00
mPanel = safenew EqualizationPanel ( parent , wxID_ANY , this ) ;
2015-08-15 17:45:16 +00:00
S . Prop ( 1 ) ;
S . AddWindow ( mPanel , wxEXPAND | wxALIGN_LEFT | wxALIGN_TOP ) ;
S . SetSizeHints ( wxDefaultCoord , wxDefaultCoord ) ;
2010-01-23 19:44:49 +00:00
2015-08-15 17:45:16 +00:00
S . SetBorder ( 5 ) ;
S . StartVerticalLay ( ) ;
{
S . AddVariableText ( _ ( " + dB " ) , false , wxCENTER ) ;
S . SetStyle ( wxSL_VERTICAL | wxSL_INVERSE ) ;
2017-09-28 01:20:14 +00:00
mdBMaxSlider = S . Id ( ID_dBMax ) . AddSlider ( { } , 30 , 60 , 0 ) ;
2010-01-23 19:44:49 +00:00
# if wxUSE_ACCESSIBILITY
2015-08-15 17:45:16 +00:00
mdBMaxSlider - > SetName ( _ ( " Max dB " ) ) ;
2018-01-04 01:47:56 +00:00
mdBMaxSlider - > SetAccessible ( safenew SliderAx ( mdBMaxSlider , _ ( " %d dB " ) ) ) ;
2010-01-23 19:44:49 +00:00
# endif
2015-08-15 17:45:16 +00:00
S . SetStyle ( wxSL_VERTICAL | wxSL_INVERSE ) ;
2017-09-28 01:20:14 +00:00
mdBMinSlider = S . Id ( ID_dBMin ) . AddSlider ( { } , - 30 , - 10 , - 120 ) ;
2015-08-15 17:45:16 +00:00
S . AddVariableText ( _ ( " - dB " ) , false , wxCENTER ) ;
# if wxUSE_ACCESSIBILITY
mdBMinSlider - > SetName ( _ ( " Min dB " ) ) ;
2018-01-04 01:47:56 +00:00
mdBMinSlider - > SetAccessible ( safenew SliderAx ( mdBMinSlider , _ ( " %d dB " ) ) ) ;
2013-10-11 20:01:41 +00:00
# endif
2015-08-15 17:45:16 +00:00
}
S . EndVerticalLay ( ) ;
S . SetBorder ( 0 ) ;
// -------------------------------------------------------------------
// ROW 3: Frequency ruler
// -------------------------------------------------------------------
// Column 1 is empty
S . AddSpace ( 1 , 1 ) ;
2017-11-01 13:16:56 +00:00
mFreqRuler = safenew RulerPanel (
parent , wxID_ANY , wxHORIZONTAL ,
wxSize { 100 , 100 } , // Ruler can't handle small sizes
RulerPanel : : Range { mLoFreq , mHiFreq } ,
Ruler : : IntFormat ,
_ ( " Hz " ) ,
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 . Prop ( 1 ) ;
S . SetBorder ( 1 ) ;
S . AddWindow ( mFreqRuler , wxEXPAND | wxALIGN_LEFT | wxALIGN_TOP | wxLEFT ) ;
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
// -------------------------------------------------------------------
// ROW 3: Graphic EQ - this gets laid out horizontally in onSize
// -------------------------------------------------------------------
S . StartHorizontalLay ( wxEXPAND , 0 ) ;
{
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.
2016-06-25 18:18:23 +00:00
mGraphicPanel = safenew wxPanelWrapper ( parent , wxID_ANY , wxDefaultPosition , wxSize ( - 1 , 150 ) ) ;
2015-08-15 17:45:16 +00:00
S . Prop ( 1 ) . AddWindow ( mGraphicPanel , wxEXPAND ) ;
for ( int i = 0 ; ( i < NUMBER_OF_BANDS ) & & ( kThirdOct [ i ] < = mHiFreq ) ; + + i )
{
2016-02-13 23:06:49 +00:00
mSliders [ i ] = safenew wxSlider ( mGraphicPanel , ID_Slider + i , 0 , - 20 , + 20 ,
2015-08-15 17:45:16 +00:00
wxDefaultPosition , wxDefaultSize , wxSL_VERTICAL | wxSL_INVERSE ) ;
2018-02-12 22:27:51 +00:00
mSliders [ i ] - > Bind ( wxEVT_ERASE_BACKGROUND ,
// ignore it
[ ] ( wxEvent & ) { } ) ;
2015-08-15 17:45:16 +00:00
# if wxUSE_ACCESSIBILITY
wxString name ;
if ( kThirdOct [ i ] < 1000. )
2018-01-04 01:47:56 +00:00
name . Printf ( _ ( " %d Hz " ) , ( int ) kThirdOct [ i ] ) ;
2015-08-15 17:45:16 +00:00
else
2018-01-04 01:47:56 +00:00
name . Printf ( _ ( " %g kHz " ) , kThirdOct [ i ] / 1000. ) ;
2015-08-15 17:45:16 +00:00
mSliders [ i ] - > SetName ( name ) ;
2018-01-04 01:47:56 +00:00
mSliders [ i ] - > SetAccessible ( safenew SliderAx ( mSliders [ i ] , _ ( " %d dB " ) ) ) ;
2015-08-15 17:45:16 +00:00
# endif
mSlidersOld [ i ] = 0 ;
mEQVals [ i ] = 0. ;
}
2014-01-16 17:55:35 +00:00
}
2015-08-15 17:45:16 +00:00
S . EndHorizontalLay ( ) ;
S . StartMultiColumn ( 7 , wxALIGN_CENTER_HORIZONTAL ) ;
{
S . SetBorder ( 5 ) ;
// -------------------------------------------------------------------
// ROWS 4:
// -------------------------------------------------------------------
S . AddSpace ( 5 , 5 ) ;
S . StartHorizontalLay ( wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL ) ;
{
S . AddPrompt ( _ ( " &EQ Type: " ) ) ;
}
S . EndHorizontalLay ( ) ;
S . StartHorizontalLay ( wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL , 1 ) ;
{
S . StartHorizontalLay ( wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL , 1 ) ;
{
mDraw = S . Id ( ID_Draw ) . AddRadioButton ( _ ( " &Draw " ) ) ;
mDraw - > SetName ( _ ( " Draw Curves " ) ) ;
mGraphic = S . Id ( ID_Graphic ) . AddRadioButtonToGroup ( _ ( " &Graphic " ) ) ;
mGraphic - > SetName ( _ ( " Graphic EQ " ) ) ;
}
S . EndHorizontalLay ( ) ;
}
S . EndHorizontalLay ( ) ;
S . StartHorizontalLay ( wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL , 1 ) ;
{
szrH = S . GetSizer ( ) ;
S . StartHorizontalLay ( wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL , 1 ) ;
{
szrI = S . GetSizer ( ) ;
2017-09-28 01:20:14 +00:00
mInterpChoice = S . Id ( ID_Interp ) . AddChoice ( { } , wxT ( " " ) , & mInterpolations ) ;
2015-08-15 17:45:16 +00:00
mInterpChoice - > SetName ( _ ( " Interpolation type " ) ) ;
mInterpChoice - > SetSelection ( 0 ) ;
}
S . EndHorizontalLay ( ) ;
S . StartHorizontalLay ( wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL , 1 ) ;
{
szrL = S . GetSizer ( ) ;
mLinFreq = S . Id ( ID_Linear ) . AddCheckBox ( _ ( " Li&near Frequency Scale " ) , wxT ( " false " ) ) ;
mLinFreq - > SetName ( _ ( " Linear Frequency Scale " ) ) ;
}
S . EndHorizontalLay ( ) ;
}
S . EndHorizontalLay ( ) ;
// -------------------------------------------------------------------
// Filter length grouping
// -------------------------------------------------------------------
S . StartHorizontalLay ( wxEXPAND , 1 ) ;
{
S . StartHorizontalLay ( wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL , 0 ) ;
{
S . AddPrompt ( _ ( " Length of &Filter: " ) ) ;
}
S . EndHorizontalLay ( ) ;
S . StartHorizontalLay ( wxEXPAND , 1 ) ;
{
S . SetStyle ( wxSL_HORIZONTAL ) ;
2017-09-28 01:20:14 +00:00
mMSlider = S . Id ( ID_Length ) . AddSlider ( { } , ( mM - 1 ) / 2 , 4095 , 10 ) ;
2015-08-15 17:45:16 +00:00
mMSlider - > SetName ( _ ( " Length of Filter " ) ) ;
}
S . EndHorizontalLay ( ) ;
S . StartHorizontalLay ( wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL , 0 ) ;
{
wxString label ;
2016-09-08 18:28:34 +00:00
label . Printf ( wxT ( " %ld " ) , mM ) ;
2015-08-15 17:45:16 +00:00
mMText = S . AddVariableText ( label ) ;
mMText - > SetName ( label ) ; // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
}
S . EndHorizontalLay ( ) ;
}
S . EndHorizontalLay ( ) ;
S . AddSpace ( 1 , 1 ) ;
S . AddSpace ( 5 , 5 ) ;
// -------------------------------------------------------------------
// ROW 5:
// -------------------------------------------------------------------
S . AddSpace ( 5 , 5 ) ;
S . StartHorizontalLay ( wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL ) ;
{
S . AddPrompt ( _ ( " &Select Curve: " ) ) ;
}
S . EndHorizontalLay ( ) ;
S . StartHorizontalLay ( wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL , 1 ) ;
{
S . StartHorizontalLay ( wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL , 1 ) ;
{
wxArrayString curves ;
2018-02-02 18:24:53 +00:00
for ( size_t i = 0 , cnt = mCurves . size ( ) ; i < cnt ; i + + )
2015-08-15 17:45:16 +00:00
{
curves . Add ( mCurves [ i ] . Name ) ;
}
2017-09-28 01:20:14 +00:00
mCurve = S . Id ( ID_Curve ) . AddChoice ( { } , wxT ( " " ) , & curves ) ;
2015-08-15 17:45:16 +00:00
mCurve - > SetName ( _ ( " Select Curve " ) ) ;
}
S . EndHorizontalLay ( ) ;
}
S . EndHorizontalLay ( ) ;
S . Id ( ID_Manage ) . AddButton ( _ ( " S&ave/Manage Curves... " ) ) ;
S . StartHorizontalLay ( wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL , 1 ) ;
{
S . Id ( ID_Clear ) . AddButton ( _ ( " Fla&tten " ) ) ;
S . Id ( ID_Invert ) . AddButton ( _ ( " &Invert " ) ) ;
mGridOnOff = S . Id ( ID_Grid ) . AddCheckBox ( _ ( " Show g&rid lines " ) , wxT ( " false " ) ) ;
mGridOnOff - > SetName ( _ ( " Show grid lines " ) ) ;
}
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 ( ) ;
{
S . AddUnits ( _ ( " &Processing: " ) ) ;
mMathProcessingType [ 0 ] = S . Id ( ID_DefaultMath ) .
AddRadioButton ( _ ( " D&efault " ) ) ;
mMathProcessingType [ 1 ] = S . Id ( ID_SSE ) .
AddRadioButtonToGroup ( _ ( " &SSE " ) ) ;
mMathProcessingType [ 2 ] = S . Id ( ID_SSEThreaded ) .
AddRadioButtonToGroup ( _ ( " SSE &Threaded " ) ) ;
mMathProcessingType [ 3 ] = S . Id ( ID_AVX ) .
AddRadioButtonToGroup ( _ ( " A&VX " ) ) ;
mMathProcessingType [ 4 ] = S . Id ( ID_AVXThreaded ) .
AddRadioButtonToGroup ( _ ( " AV&X Threaded " ) ) ;
if ( ! EffectEqualization48x : : GetMathCaps ( ) - > SSE )
{
mMathProcessingType [ 1 ] - > Disable ( ) ;
mMathProcessingType [ 2 ] - > Disable ( ) ;
}
if ( true ) //!EffectEqualization48x::GetMathCaps()->AVX) { not implemented
{
mMathProcessingType [ 3 ] - > Disable ( ) ;
mMathProcessingType [ 4 ] - > Disable ( ) ;
}
// update the control state
mMathProcessingType [ 0 ] - > SetValue ( true ) ;
int mathPath = EffectEqualization48x : : GetMathPath ( ) ;
if ( mathPath & MATH_FUNCTION_SSE )
{
mMathProcessingType [ 1 ] - > SetValue ( true ) ;
if ( mathPath & MATH_FUNCTION_THREADED )
mMathProcessingType [ 2 ] - > SetValue ( true ) ;
}
if ( false ) //mathPath&MATH_FUNCTION_AVX) { not implemented
{
mMathProcessingType [ 3 ] - > SetValue ( true ) ;
if ( mathPath & MATH_FUNCTION_THREADED )
mMathProcessingType [ 4 ] - > SetValue ( true ) ;
}
S . Id ( ID_Bench ) . AddButton ( _ ( " &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 ) ;
mUIParent - > Layout ( ) ;
2010-01-23 19:44:49 +00:00
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
szrV - > Show ( szrG , true ) ; // eq sliders
szrH - > Show ( szrI , true ) ; // interpolation choice
szrH - > Show ( szrL , false ) ; // linear freq checkbox
2010-01-23 19:44:49 +00:00
2015-08-15 17:45:16 +00:00
mUIParent - > SetSizeHints ( mUIParent - > GetBestSize ( ) ) ;
// szrL->SetMinSize( szrI->GetSize() );
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
2016-09-08 18:28:34 +00:00
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 ) ;
// Set Graphic (Fader) or Draw mode
if ( mDrawMode )
{
mDraw - > SetValue ( true ) ;
2016-09-26 08:03:54 +00:00
szrV - > Show ( szrG , false ) ; // eq sliders
szrH - > Show ( szrI , false ) ; // interpolation choice
szrH - > Show ( szrL , true ) ; // linear freq checkbox
2015-04-17 03:53:42 +00:00
}
else
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
mGraphic - > SetValue ( true ) ;
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 ) ) ;
LayoutEQSliders ( ) ;
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
2016-04-14 16:35:15 +00:00
size_t m = 2 * mMSlider - > GetValue ( ) + 1 ; // odd numbers only
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
2018-03-15 17:48:25 +00:00
tip . Printf ( wxT ( " %d " ) , ( int ) mM ) ;
2015-04-17 03:53:42 +00:00
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 ) ;
}
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
2015-04-17 03:53:42 +00:00
AudacityProject * p = GetActiveProject ( ) ;
2016-03-02 19:59:31 +00:00
auto output = p - > GetTrackFactory ( ) - > NewWaveTrack ( floatSample , t - > GetRate ( ) ) ;
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
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
//t might have whitespace seperating multiple clips
//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 ) ) ;
}
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
}
}
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
{
// 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
if ( fileName = = wxT ( " " ) ) {
// 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 ) ;
2016-02-27 13:17:27 +00:00
wxString eqCurvesInstalledVersion = wxT ( " " ) ;
gPrefs - > Read ( wxT ( " /Effects/Equalization/PresetVersion " ) , & eqCurvesInstalledVersion , wxT ( " " ) ) ;
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 ( ) ;
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
{
wxString msg ;
/* i18n-hint: EQ stands for 'Equalization'.*/
2017-10-09 05:03:14 +00:00
msg . Printf ( _ ( " Error Loading EQ Curves from file: \n %s \n Error message says: \n %s " ) , fullPath , reader . GetErrorStr ( ) ) ;
2015-04-17 03:53:42 +00:00
// Inform user of load failure
2017-09-10 14:42:33 +00:00
Effect : : MessageBox ( msg ,
wxOK | wxCENTRE ,
_ ( " 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
}
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 ;
/* i18n-hint: name of the 'unnamed' custom curve */
wxString unnamed = _ ( " unnamed " ) ;
// 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 ] ;
// is the name in the dfault set?
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 ) ;
gPrefs - > Write ( wxT ( " /Effects/Equalization/PresetVersion " ) , eqCurvesCurrentVersion ) ;
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???
//wxString errorMessage;
2017-10-09 05:03:14 +00:00
//errorMessage.Printf(_("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"), FileNames::DataDir());
2016-02-27 21:15:48 +00:00
//ShowErrorDialog(mUIParent, _("EQCurves.xml and EQDefaultCurves.xml missing"),
// 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 ;
if ( fileName = = wxT ( " " ) )
{
// 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 ( ) } ;
XMLFileWriter eqFile { fullPath , _ ( " 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 ;
2017-05-03 00:58:18 +00:00
env - > InsertOrReplace ( 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 ) {
2017-05-03 00:58:18 +00:00
env - > InsertOrReplace ( 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 ;
2017-05-03 00:58:18 +00:00
env - > InsertOrReplace ( 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 ;
2017-05-03 00:58:18 +00:00
env - > InsertOrReplace ( 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 ) ) ) ;
2017-05-03 00:58:18 +00:00
env - > InsertOrReplace ( 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 ) {
2017-05-03 00:58:18 +00:00
env - > InsertOrReplace ( 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 ) ) ) ;
}
2017-05-03 00:58:18 +00:00
env - > InsertOrReplace ( 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
{
2017-09-10 14:42:33 +00:00
Effect : : MessageBox ( _ ( " Requested curve not found, using 'unnamed' " ) ,
wxOK | wxICON_ERROR ,
_ ( " Curve not found " ) ) ;
2018-02-02 18:24:53 +00:00
setCurve ( ( int ) mCurves . size ( ) - 1 ) ;
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
{
// Start our heirarchy
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 " ) ) ;
}
// Terminate our heirarchy
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-04-17 03:53:42 +00:00
void EffectEqualization : : LayoutEQSliders ( )
2010-01-23 19:44:49 +00:00
{
2015-08-15 17:45:16 +00:00
// layout the Graphic EQ sliders here
wxRect rulerR = mFreqRuler - > GetRect ( ) ;
int sliderW = mSliders [ 0 ] - > GetSize ( ) . GetWidth ( ) ;
int sliderH = mGraphicPanel - > GetRect ( ) . GetHeight ( ) ;
2010-01-23 19:44:49 +00:00
2015-08-15 17:45:16 +00:00
int start = rulerR . GetLeft ( ) - ( sliderW / 2 ) ;
float range = rulerR . GetWidth ( ) ;
2010-01-23 19:44:49 +00:00
double loLog = log10 ( mLoFreq ) ;
double hiLog = log10 ( mHiFreq ) ;
double denom = hiLog - loLog ;
2015-08-15 17:45:16 +00:00
for ( int i = 0 ; ( i < NUMBER_OF_BANDS ) & & ( kThirdOct [ i ] < = mHiFreq ) ; + + i )
2015-04-17 03:53:42 +00:00
{
2015-08-15 17:45:16 +00:00
// centre of this slider, from start
float posn = range * ( log10 ( kThirdOct [ i ] ) - loLog ) / denom ;
mSliders [ i ] - > SetSize ( start + ( posn + 0.5 ) , 0 , sliderW , sliderH ) ;
2015-04-17 03:53:42 +00:00
}
2015-08-15 17:45:16 +00:00
mGraphicPanel - > Refresh ( ) ;
2015-04-17 03:53:42 +00:00
}
2015-05-29 16:32:55 +00:00
void EffectEqualization : : UpdateCurves ( )
{
// Reload the curve names
mCurve - > Clear ( ) ;
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
{
mCurve - > Append ( mCurves [ i ] . Name ) ;
}
mCurve - > SetStringSelection ( mCurveName ) ;
// Allow the control to resize
mCurve - > SetSizeHints ( - 1 , - 1 ) ;
// 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 ) ;
2017-05-03 00:58:18 +00:00
mLinEnvelope - > InsertOrReplace ( 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
szrH - > Show ( szrI , true ) ; // interpolation choice
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(); // Make all sizers get resized first
LayoutEQSliders ( ) ; // Then layout sliders
mUIParent - > Layout ( ) ;
wxGetTopLevelParent ( mUIParent ) - > Layout ( ) ;
// mUIParent->Layout(); // And layout again to resize dialog
2015-08-15 17:45:16 +00:00
#if 0
2015-04-17 03:53:42 +00:00
wxSize wsz = mUIParent - > GetSize ( ) ;
wxSize ssz = szrV - > GetSize ( ) ;
if ( ssz . x > wsz . x | | ssz . y > wsz . y )
{
mUIParent - > Fit ( ) ;
}
2015-08-15 17:45:16 +00:00
# endif
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 + + )
2017-05-03 00:58:18 +00:00
mLinEnvelope - > InsertOrReplace ( pow ( 10. , ( ( when [ i ] * denom ) + loLog ) ) / mHiFreq , value [ i ] ) ;
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 )
{
2016-03-08 21:38:13 +00:00
// Caution: on Linux, when when == 20, the log calulation rounds
// to just under zero, which causes an assert error.
double flog = ( log10 ( when [ i ] * mHiFreq ) - loLog ) / denom ;
2017-05-03 00:58:18 +00:00
mLogEnvelope - > InsertOrReplace ( 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 ] ;
2017-05-03 00:58:18 +00:00
mLogEnvelope - > InsertOrReplace ( 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 ) ;
env - > InsertOrReplace ( 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 ) ;
env - > InsertOrReplace ( 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 )
{
2017-05-03 00:58:18 +00:00
env - > InsertOrReplace ( 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. ) ;
}
2015-04-17 03:53:42 +00:00
void EffectEqualization : : OnSize ( wxSizeEvent & event )
{
mUIParent - > Layout ( ) ;
if ( ! mDrawMode )
{
LayoutEQSliders ( ) ;
}
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 ) )
{
2015-08-15 17:45:16 +00:00
if ( mGraphic - > GetValue ( ) )
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-27 15:52:42 +00:00
UpdateDraw ( ) ;
2010-01-23 19:44:49 +00:00
2015-04-17 03:53:42 +00:00
mDrawMode = true ;
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
{
UpdateGraphic ( ) ;
2015-04-17 03:53:42 +00:00
mDrawMode = false ;
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 ;
2016-08-04 11:33:22 +00:00
mBitmap = std : : make_unique < wxBitmap > ( mWidth , mHeight ) ;
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
memDC . SetPen ( wxPen ( theTheme . Colour ( clrGraphLines ) , 1 , wxSOLID ) ) ;
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
2016-04-14 16:35:15 +00:00
memDC . SetPen ( wxPen ( theTheme . Colour ( clrGraphLines ) , 3 , wxSOLID ) ) ;
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
memDC . SetPen ( wxPen ( theTheme . Colour ( clrResponseLines ) , 1 , wxSOLID ) ) ;
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
// set up for calculating cos using recurrance - faster than calculating it directly each time
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.
// do recurrance
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 ) ;
if ( mEffect - > mDraw - > GetValue ( ) )
2015-08-15 17:45:16 +00:00
{
2017-06-23 23:28:38 +00:00
TrackPanelDrawingContext context { memDC , { } , { } } ;
mEffect - > mEnvelope - > DrawPoints (
context , mEnvRect , ZoomInfo ( 0.0 , mEnvRect . width - 1 ) , false , 0.0 ,
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 ) :
2016-07-10 21:10:50 +00:00
wxDialogWrapper ( parent , wxID_ANY , _ ( " 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
{
SetLabel ( _ ( " Manage Curves " ) ) ; // Provide visual label
SetName ( _ ( " Manage Curves List " ) ) ; // Provide audible label
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
{
2010-06-27 22:20:58 +00:00
S . StartStatic ( _ ( " &Curves " ) , 1 ) ;
2010-06-15 23:49:51 +00:00
{
S . SetStyle ( wxSUNKEN_BORDER | wxLC_REPORT | wxLC_HRULES | wxLC_VRULES ) ;
mList = S . Id ( CurvesListID ) . AddListControlReportMode ( ) ;
mList - > InsertColumn ( 0 , _ ( " Curve Name " ) , wxLIST_FORMAT_RIGHT ) ;
}
S . EndStatic ( ) ;
2010-06-27 22:20:58 +00:00
S . StartVerticalLay ( 0 ) ;
2010-06-15 23:49:51 +00:00
{
S . Id ( UpButtonID ) . AddButton ( _ ( " Move &Up " ) , wxALIGN_LEFT ) ;
S . Id ( DownButtonID ) . AddButton ( _ ( " Move &Down " ) , wxALIGN_LEFT ) ;
S . Id ( RenameButtonID ) . AddButton ( _ ( " &Rename... " ) , wxALIGN_LEFT ) ;
S . Id ( DeleteButtonID ) . AddButton ( _ ( " D&elete... " ) , wxALIGN_LEFT ) ;
S . Id ( ImportButtonID ) . AddButton ( _ ( " I&mport... " ) , wxALIGN_LEFT ) ;
S . Id ( ExportButtonID ) . AddButton ( _ ( " E&xport... " ) , wxALIGN_LEFT ) ;
S . Id ( LibraryButtonID ) . AddButton ( _ ( " &Get More... " ) , wxALIGN_LEFT ) ;
S . Id ( DefaultsButtonID ) . AddButton ( _ ( " De&faults " ) , wxALIGN_LEFT ) ;
}
S . EndVerticalLay ( ) ;
}
S . EndHorizontalLay ( ) ;
S . AddStandardButtons ( ) ;
2010-06-27 22:20:58 +00:00
S . StartStatic ( _ ( " Help " ) ) ;
2017-09-28 01:20:14 +00:00
S . AddConstTextBox ( { } , _ ( " 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
2017-09-10 14:42:33 +00:00
mEffect - > Effect : : MessageBox ( _ ( " 'unnamed' always stays at the bottom of the list " ) ,
Effect : : DefaultMessageBoxStyle ,
_ ( " '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
wxArrayString exclude ;
exclude . Add ( wxT ( " < " ) ) ;
exclude . Add ( wxT ( " > " ) ) ;
exclude . Add ( wxT ( " ' " ) ) ;
exclude . Add ( wxT ( " \" " ) ) ;
// 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 ,
2018-01-04 01:47:56 +00:00
wxString : : Format ( _ ( " Rename '%s' to... " ) , mEditCurves [ item ] . Name ) ,
2014-01-16 17:55:35 +00:00
_ ( " 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 ;
if ( name . IsSameAs ( mEditCurves [ curve ] . Name ) ) // case sensitive
{
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
{
2017-09-10 14:42:33 +00:00
mEffect - > Effect : : MessageBox ( _ ( " Name is the same as the original one " ) , wxOK , _ ( " Same name " ) ) ;
2010-06-15 23:49:51 +00:00
break ;
}
2017-09-10 14:42:33 +00:00
int answer = mEffect - > Effect : : MessageBox ( _ ( " Overwrite existing curve ' " ) + name + _ ( " '? " ) ,
wxYES_NO , _ ( " 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 ;
}
}
}
if ( name = = wxT ( " " ) | | name = = wxT ( " unnamed " ) )
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
{
// We could could count them here
// 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
{
2017-09-10 14:42:33 +00:00
mEffect - > Effect : : MessageBox ( _ ( " You cannot delete the 'unnamed' curve. " ) ,
wxOK | wxCENTRE , _ ( " Can't delete 'unnamed' " ) ) ;
2010-06-15 23:49:51 +00:00
}
else
{
// Create the prompt
wxString quest ;
quest = wxString ( _ ( " Delete ' " ) ) + mEditCurves [ item - deleted ] . Name + _ ( " ' ? " ) ;
// Ask for confirmation before removal
2017-09-10 14:42:33 +00:00
int ans = mEffect - > Effect : : MessageBox ( quest , wxYES_NO | wxCENTRE , _ ( " 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 )
PopulateList ( mEditCurves . GetCount ( ) - 1 ) ; // set 'unnamed' as the selected curve
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
wxString quest ;
if ( count > 1 )
quest . Printf ( _ ( " Delete " ) + wxString ( wxT ( " %d " ) ) + _ ( " items? " ) , count ) ;
else
if ( count = = 1 )
quest = wxString ( _ ( " Delete ' " ) ) + mEditCurves [ item ] . Name + _ ( " ' ? " ) ;
else
return ;
// Ask for confirmation before removal
2017-09-10 14:42:33 +00:00
int ans = mEffect - > Effect : : MessageBox ( quest , wxYES_NO | wxCENTRE , _ ( " 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 )
{
if ( item = = mList - > GetItemCount ( ) - 1 ) //unnamed
{
2017-09-10 14:42:33 +00:00
mEffect - > Effect : : MessageBox ( _ ( " You cannot delete the 'unnamed' curve, it is special. " ) ,
Effect : : DefaultMessageBoxStyle ,
_ ( " Can't delete 'unnamed' " ) ) ;
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
}
2013-08-25 21:51:26 +00:00
void EditCurvesDialog : : OnImport ( wxCommandEvent & WXUNUSED ( event ) )
2010-06-15 23:49:51 +00:00
{
2017-10-12 19:41:52 +00:00
FileDialogWrapper filePicker ( this , _ ( " Choose an EQ curve file " ) , FileNames : : DataDir ( ) , wxT ( " " ) , _ ( " xml files (*.xml ; * . XML ) | * . xml ; * . XML " ));
2010-06-15 23:49:51 +00:00
wxString fileName = wxT ( " " ) ;
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
{
2017-10-12 19:41:52 +00:00
FileDialogWrapper filePicker ( this , _ ( " Export EQ curves as... " ) , FileNames : : DataDir ( ) , wxT ( " " ) , wxT ( " *.XML " ) , wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER ) ; // wxFD_CHANGE_DIR?
2010-06-15 23:49:51 +00:00
wxString fileName = wxT ( " " ) ;
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
2017-09-10 14:42:33 +00:00
mEffect - > Effect : : MessageBox ( _ ( " You cannot export 'unnamed' curve, it is special. " ) ,
Effect : : DefaultMessageBoxStyle ,
_ ( " 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 ;
2010-06-15 23:49:51 +00:00
wxString message ;
2017-10-09 05:03:14 +00:00
message . Printf ( _ ( " %d curves exported to %s " ) , i , fileName ) ;
2017-09-10 14:42:33 +00:00
mEffect - > Effect : : MessageBox ( message ,
Effect : : DefaultMessageBoxStyle ,
_ ( " Curves exported " ) ) ;
2010-06-15 23:49:51 +00:00
}
else
2017-09-10 14:42:33 +00:00
mEffect - > Effect : : MessageBox ( _ ( " No curves exported " ) ,
Effect : : DefaultMessageBoxStyle ,
_ ( " 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 ) ;
}
2010-01-23 19:44:49 +00:00
# if wxUSE_ACCESSIBILITY
2016-02-23 02:17:19 +00:00
SliderAx : : SliderAx ( wxWindow * window , const wxString & fmt ) :
2014-01-16 17:55:35 +00:00
wxWindowAccessible ( window )
2010-01-23 19:44:49 +00:00
{
mParent = window ;
mFmt = fmt ;
}
SliderAx : : ~ SliderAx ( )
{
}
// Retrieves the address of an IDispatch interface for the specified child.
// All objects must support this property.
wxAccStatus SliderAx : : GetChild ( int childId , wxAccessible * * child )
{
if ( childId = = wxACC_SELF )
{
* child = this ;
}
else
{
* child = NULL ;
}
return wxACC_OK ;
}
// Gets the number of children.
wxAccStatus SliderAx : : GetChildCount ( int * childCount )
{
* childCount = 3 ;
return wxACC_OK ;
}
// Gets the default action for this object (0) or > 0 (the action for a child).
// Return wxACC_OK even if there is no action. actionName is the action, or the empty
// string if there is no action.
// The retrieved string describes the action that is performed on an object,
// not what the object does as a result. For example, a toolbar button that prints
// a document has a default action of "Press" rather than "Prints the current document."
2013-09-25 22:57:54 +00:00
wxAccStatus SliderAx : : GetDefaultAction ( int WXUNUSED ( childId ) , wxString * actionName )
2010-01-23 19:44:49 +00:00
{
actionName - > Clear ( ) ;
return wxACC_OK ;
}
// Returns the description for this object or a child.
2013-09-25 22:57:54 +00:00
wxAccStatus SliderAx : : GetDescription ( int WXUNUSED ( childId ) , wxString * description )
2010-01-23 19:44:49 +00:00
{
description - > Clear ( ) ;
return wxACC_OK ;
}
// Gets the window with the keyboard focus.
// If childId is 0 and child is NULL, no object in
// this subhierarchy has the focus.
// If this object has the focus, child should be 'this'.
wxAccStatus SliderAx : : GetFocus ( int * childId , wxAccessible * * child )
{
* childId = 0 ;
* child = this ;
return wxACC_OK ;
}
// Returns help text for this object or a child, similar to tooltip text.
2013-09-25 22:57:54 +00:00
wxAccStatus SliderAx : : GetHelpText ( int WXUNUSED ( childId ) , wxString * helpText )
2010-01-23 19:44:49 +00:00
{
helpText - > Clear ( ) ;
return wxACC_OK ;
}
// Returns the keyboard shortcut for this object or child.
// Return e.g. ALT+K
2013-09-25 22:57:54 +00:00
wxAccStatus SliderAx : : GetKeyboardShortcut ( int WXUNUSED ( childId ) , wxString * shortcut )
2010-01-23 19:44:49 +00:00
{
shortcut - > Clear ( ) ;
return wxACC_OK ;
}
// Returns the rectangle for this object (id = 0) or a child element (id > 0).
// rect is in screen coordinates.
2013-09-25 22:57:54 +00:00
wxAccStatus SliderAx : : GetLocation ( wxRect & rect , int WXUNUSED ( elementId ) )
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
wxSlider * s = wxDynamicCast ( GetWindow ( ) , wxSlider ) ;
2010-01-23 19:44:49 +00:00
rect = s - > GetRect ( ) ;
rect . SetPosition ( s - > GetParent ( ) - > ClientToScreen ( rect . GetPosition ( ) ) ) ;
return wxACC_OK ;
}
// Gets the name of the specified object.
2013-09-25 22:57:54 +00:00
wxAccStatus SliderAx : : GetName ( int WXUNUSED ( childId ) , wxString * name )
2010-01-23 19:44:49 +00:00
{
2015-04-17 03:53:42 +00:00
wxSlider * s = wxDynamicCast ( GetWindow ( ) , wxSlider ) ;
2010-01-23 19:44:49 +00:00
* name = s - > GetName ( ) ;
return wxACC_OK ;
}
// Returns a role constant.
wxAccStatus SliderAx : : GetRole ( int childId , wxAccRole * role )
{
switch ( childId )
{
2014-01-16 17:55:35 +00:00
case 0 :
* role = wxROLE_SYSTEM_SLIDER ;
2010-01-23 19:44:49 +00:00
break ;
2014-01-16 17:55:35 +00:00
case 1 :
case 3 :
* role = wxROLE_SYSTEM_PUSHBUTTON ;
2010-01-23 19:44:49 +00:00
break ;
2014-01-16 17:55:35 +00:00
case 2 :
* role = wxROLE_SYSTEM_INDICATOR ;
2010-01-23 19:44:49 +00:00
break ;
}
return wxACC_OK ;
}
// Gets a variant representing the selected children
// of this object.
// Acceptable values:
// - a null variant (IsNull() returns TRUE)
// - a list variant (GetType() == wxT("list"))
// - an integer representing the selected child element,
// or 0 if this object is selected (GetType() == wxT("long"))
// - a "void*" pointer to a wxAccessible child object
2013-09-25 22:57:54 +00:00
wxAccStatus SliderAx : : GetSelections ( wxVariant * WXUNUSED ( selections ) )
2010-01-23 19:44:49 +00:00
{
return wxACC_NOT_IMPLEMENTED ;
}
// Returns a state constant.
wxAccStatus SliderAx : : GetState ( int childId , long * state )
{
2015-04-17 03:53:42 +00:00
wxSlider * s = wxDynamicCast ( GetWindow ( ) , wxSlider ) ;
2010-01-23 19:44:49 +00:00
switch ( childId )
{
2014-01-16 17:55:35 +00:00
case 0 :
* state = wxACC_STATE_SYSTEM_FOCUSABLE ;
2010-01-23 19:44:49 +00:00
break ;
2014-01-16 17:55:35 +00:00
case 1 :
if ( s - > GetValue ( ) = = s - > GetMin ( ) )
{
* state = wxACC_STATE_SYSTEM_INVISIBLE ;
}
2010-01-23 19:44:49 +00:00
break ;
2014-01-16 17:55:35 +00:00
case 3 :
if ( s - > GetValue ( ) = = s - > GetMax ( ) )
{
* state = wxACC_STATE_SYSTEM_INVISIBLE ;
}
2010-01-23 19:44:49 +00:00
break ;
}
// Do not use mSliderIsFocused is not set until after this method
// is called.
* state | = ( s = = wxWindow : : FindFocus ( ) ? wxACC_STATE_SYSTEM_FOCUSED : 0 ) ;
return wxACC_OK ;
}
// Returns a localized string representing the value for the object
// or child.
wxAccStatus SliderAx : : GetValue ( int childId , wxString * strValue )
{
2015-04-17 03:53:42 +00:00
wxSlider * s = wxDynamicCast ( GetWindow ( ) , wxSlider ) ;
2010-01-23 19:44:49 +00:00
if ( childId = = 0 )
{
strValue - > Printf ( mFmt , s - > GetValue ( ) ) ;
return wxACC_OK ;
}
return wxACC_NOT_SUPPORTED ;
}
# endif
2014-01-16 17:55:35 +00:00