diff --git a/Benchmark/Benchmark.cpp b/Benchmark/Benchmark.cpp index 0795016..a5f5f2a 100644 --- a/Benchmark/Benchmark.cpp +++ b/Benchmark/Benchmark.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #ifdef DEBUG #include #include @@ -214,7 +215,7 @@ int main(int argc, char** argv) printf("\nWriting output to %s\n", output.c_str()); - SF_INFO info = {frameCount, sampleRate, channelCount, SF_FORMAT_WAV | SF_FORMAT_PCM_16, 0}; + SF_INFO info = {frameCount, (int)sampleRate, (int)channelCount, SF_FORMAT_WAV | SF_FORMAT_PCM_16, 0}; SNDFILE* outFile = sf_open(output.c_str(), SFM_WRITE, &info); if(outFile == NULL) { diff --git a/Benchmark/Benchmark.vcxproj b/Benchmark/Benchmark.vcxproj index 08a2108..f1c7f2d 100644 --- a/Benchmark/Benchmark.vcxproj +++ b/Benchmark/Benchmark.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -22,29 +22,34 @@ {D4474852-5724-4253-95C6-7A6DC424F194} Win32Proj Benchmark + 10.0.10240.0 Application true Unicode + v120 Application true Unicode + v120 Application false true Unicode + v120 Application false true Unicode + v120 @@ -64,28 +69,27 @@ true - $(SolutionDir);C:\Program Files\tclap-1.2.1\include;C:\Program Files\libsndfile\include;$(IncludePath) - C:\Program Files (x86)\libsndfile\lib;$(LibraryPath) + $(SolutionDir);C:\Program Files\tclap-1.2.1\include;C:\Program Files\libsndfile\include;C:\Program Files (x86)\fftw3;$(IncludePath) + C:\Program Files (x86)\libsndfile\lib;C:\Program Files (x86)\fftw3;$(LibraryPath) true - $(SolutionDir);C:\Program Files\tclap-1.2.1\include;C:\Program Files\libsndfile\include;$(IncludePath) - C:\Program Files\libsndfile\lib;$(LibraryPath) + $(SolutionDir);C:\Program Files\tclap-1.2.1\include;C:\Program Files\libsndfile\include;C:\Program Files\fftw3;$(IncludePath) + C:\Program Files\libsndfile\lib;C:\Program Files\fftw3;$(LibraryPath) false - $(SolutionDir);C:\Program Files\tclap-1.2.1\include;C:\Program Files\libsndfile\include;$(IncludePath) - C:\Program Files (x86)\libsndfile\lib;$(LibraryPath) + $(SolutionDir);C:\Program Files\tclap-1.2.1\include;C:\Program Files\libsndfile\include;C:\Program Files (x86)\fftw3;$(IncludePath) + C:\Program Files (x86)\libsndfile\lib;C:\Program Files (x86)\fftw3;$(LibraryPath) false - $(SolutionDir);C:\Program Files\tclap-1.2.1\include;C:\Program Files\libsndfile\include;$(IncludePath) - C:\Program Files\libsndfile\lib;$(LibraryPath) + $(SolutionDir);C:\Program Files\tclap-1.2.1\include;C:\Program Files\libsndfile\include;C:\Program Files\fftw3;$(IncludePath) + C:\Program Files\libsndfile\lib;C:\Program Files\fftw3;$(LibraryPath) - - + Use Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) @@ -93,13 +97,12 @@ Console true - muparserxd.lib;version.lib;Shlwapi.lib;libsndfile-1.lib;%(AdditionalDependencies) + muparserxd.lib;version.lib;Shlwapi.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) - - + Use Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) @@ -107,14 +110,13 @@ Console true - muparserxd.lib;version.lib;Shlwapi.lib;libsndfile-1.lib;%(AdditionalDependencies) + muparserxd.lib;version.lib;Shlwapi.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) Level3 - - + Use MaxSpeed true true @@ -126,14 +128,13 @@ true true true - muparserx.lib;version.lib;Shlwapi.lib;libsndfile-1.lib;%(AdditionalDependencies) + muparserx.lib;version.lib;Shlwapi.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) Level3 - - + Use MaxSpeed true true @@ -145,7 +146,7 @@ true true true - muparserx.lib;version.lib;Shlwapi.lib;libsndfile-1.lib;%(AdditionalDependencies) + muparserx.lib;version.lib;Shlwapi.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) @@ -156,12 +157,16 @@ + + + + @@ -169,14 +174,23 @@ + + + + + Create + Create + Create + Create + @@ -187,12 +201,16 @@ + + + + @@ -200,6 +218,8 @@ + + @@ -207,9 +227,11 @@ + + diff --git a/Benchmark/Benchmark.vcxproj.filters b/Benchmark/Benchmark.vcxproj.filters index ea87009..4b9547b 100644 --- a/Benchmark/Benchmark.vcxproj.filters +++ b/Benchmark/Benchmark.vcxproj.filters @@ -80,6 +80,28 @@ parser + + helpers + + + filters + + + filters + + + helpers + + + libHybridConv-0.1.1 + + + filters + + + filters + + @@ -164,6 +186,28 @@ parser + + helpers + + + filters + + + filters + + + helpers + + + libHybridConv-0.1.1 + + + filters + + + filters + + @@ -175,5 +219,8 @@ {4bf76d90-8b50-493a-848b-019f88fa1f11} + + {35b3b6f1-e4df-4214-bd4c-fa058942e547} + \ No newline at end of file diff --git a/ClassFactory.cpp b/ClassFactory.cpp index 7028a09..b0dd4f7 100644 --- a/ClassFactory.cpp +++ b/ClassFactory.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include "EqualizerAPO.h" #include "helpers/LogHelper.h" #include "ClassFactory.h" diff --git a/Configurator/Configurator.cpp b/Configurator/Configurator.cpp index 75bf3b1..0306e98 100644 --- a/Configurator/Configurator.cpp +++ b/Configurator/Configurator.cpp @@ -17,10 +17,12 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #define WIN32_LEAN_AND_MEAN #include #include #include +#include #include "helpers/StringHelper.h" #include "helpers/RegistryHelper.h" #include "Configurator.h" @@ -55,6 +57,7 @@ void Configurator::onInitDialog(HWND hDlg) useOriginalAPOPreMix = GetDlgItem(hDlg, IDC_USE_ORIGINAL_APO_PRE_MIX); useOriginalAPOPostMix = GetDlgItem(hDlg, IDC_USE_ORIGINAL_APO_POST_MIX); installModeComboBox = GetDlgItem(hDlg, IDC_INSTALL_MODE_COMBOBOX); + selectOneDeviceLabel = GetDlgItem(hDlg, IDC_SELECT_ONE_DEVICE); expandTroubleShooting(false); @@ -118,6 +121,9 @@ void Configurator::onInitDialog(HWND hDlg) } ComboBox_SetCurSel(installModeComboBox, 0); + LoadStringW(hInstance, IDS_SELECT_ONE_DEVICE, stringBuf, sizeof(stringBuf) / sizeof(wchar_t)); + SetWindowTextW(selectOneDeviceLabel, stringBuf); + for(int i=0; i<=1; i++) { HWND deviceList = deviceLists[i]; @@ -143,6 +149,7 @@ void Configurator::onInitDialog(HWND hDlg) try { apoInfos[i] = DeviceAPOInfo::loadAllInfos(i==1); + wstring defaultDevice = StringHelper::toLowerCase(DeviceAPOInfo::getDefaultDevice(i==1)); int itemCount=0; for(vector::iterator it = apoInfos[i].begin(); it != apoInfos[i].end(); it++) @@ -158,19 +165,31 @@ void Configurator::onInitDialog(HWND hDlg) { ListView_SetCheckState(deviceList, itemCount, TRUE); if(it->canBeUpgraded()) - LoadStringW(hInstance, IDS_WILL_BE_UPGRADED, stringBuf, sizeof(stringBuf)/sizeof(wchar_t)); + LoadStringW(hInstance, IDS_WILL_BE_UPGRADED, stringBuf, sizeof(stringBuf) / sizeof(wchar_t)); + else if(it->isEnhancementsDisabled) + LoadStringW(hInstance, IDS_ENHANCEMENTS_WILL_BE_ENABLED, stringBuf, sizeof(stringBuf) / sizeof(wchar_t)); else LoadStringW(hInstance, IDS_ALREADY_INSTALLED, stringBuf, sizeof(stringBuf)/sizeof(wchar_t)); - ListView_SetItemText(deviceList, itemCount, 2, stringBuf); } else if(it->isExperimental()) { LoadStringW(hInstance, IDS_CAN_BE_INSTALLED_EXPERIMENTAL, stringBuf, sizeof(stringBuf)/sizeof(wchar_t)); - ListView_SetItemText(deviceList, itemCount, 2, stringBuf); } else { LoadStringW(hInstance, IDS_CAN_BE_INSTALLED, stringBuf, sizeof(stringBuf)/sizeof(wchar_t)); + } + + if(it->isDefaultDevice) + { + wstring statusText = stringBuf; + LoadStringW(hInstance, IDS_DEFAULT_DEVICE, stringBuf, sizeof(stringBuf)/sizeof(wchar_t)); + statusText = wstring(stringBuf) + L", " + statusText; + + ListView_SetItemText(deviceList, itemCount, 2, const_cast(statusText.c_str())); + } + else + { ListView_SetItemText(deviceList, itemCount, 2, stringBuf); } @@ -186,6 +205,12 @@ void Configurator::onInitDialog(HWND hDlg) ListView_SetColumnWidth(deviceList, 1, LVSCW_AUTOSIZE); ListView_SetColumnWidth(deviceList, 2, LVSCW_AUTOSIZE); } + + if (!DeviceAPOInfo::checkProtectedAudioDG(true)) + { + LoadStringW(hInstance, IDS_REGISTRY_VALUE_FIXED, stringBuf, sizeof(stringBuf) / sizeof(wchar_t)); + MessageBoxW(hDlg, stringBuf, L"Info", MB_ICONINFORMATION | MB_OK); + } } void Configurator::onLvnItemChanged(unsigned sourceId, LPNMLISTVIEW info) @@ -221,7 +246,7 @@ bool Configurator::onButtonClicked(unsigned sourceId) info.install(); else if(!ListView_GetCheckState(deviceList, i) && info.isInstalled) info.uninstall(); - else if(ListView_GetCheckState(deviceList, i) && (info.canBeUpgraded() || info.hasChanges())) + else if(ListView_GetCheckState(deviceList, i) && (info.canBeUpgraded() || info.hasChanges() || info.isEnhancementsDisabled)) { info.uninstall(); info.load(info.deviceGuid); @@ -398,6 +423,33 @@ void Configurator::onTcnSelChange(unsigned sourceId) updateButtons(index); } +bool Configurator::isAnySelected() +{ + bool anySelected = false; + + for(int index = 0; index <= 1; index++) + { + HWND deviceList = deviceLists[index]; + + for(int i = 0; i < ListView_GetItemCount(deviceList); i++) + { + LVITEM item; + item.iItem = i; + item.iSubItem = 0; + item.mask = LVIF_PARAM; + ListView_GetItem(deviceList, &item); + DeviceAPOInfo apoInfo = apoInfos[index][item.lParam]; + if(ListView_GetCheckState(deviceList, i) != 0) + { + anySelected = true; + break; + } + } + } + + return anySelected; +} + bool Configurator::isChanged() { bool changed = false; @@ -415,7 +467,7 @@ bool Configurator::isChanged() ListView_GetItem(deviceList, &item); DeviceAPOInfo apoInfo = apoInfos[index][item.lParam]; if((ListView_GetCheckState(deviceList, i) != 0) != apoInfo.isInstalled - || ListView_GetCheckState(deviceList, i) && apoInfo.isInstalled && (apoInfo.canBeUpgraded() || apoInfo.hasChanges())) + || ListView_GetCheckState(deviceList, i) && apoInfo.isInstalled && (apoInfo.canBeUpgraded() || apoInfo.hasChanges() || apoInfo.isEnhancementsDisabled)) { changed = true; break; @@ -442,7 +494,7 @@ bool Configurator::hasUpgrades() item.mask = LVIF_PARAM; ListView_GetItem(deviceList, &item); DeviceAPOInfo apoInfo = apoInfos[index][item.lParam]; - if(ListView_GetCheckState(deviceList, i) && apoInfo.isInstalled && apoInfo.canBeUpgraded()) + if(ListView_GetCheckState(deviceList, i) && apoInfo.isInstalled && (apoInfo.canBeUpgraded() || apoInfo.isEnhancementsDisabled)) { hasUpgrades = true; break; @@ -457,6 +509,7 @@ void Configurator::expandTroubleShooting(bool expand) { int showCmd = expand ? SW_SHOW : SW_HIDE; ShowWindow(troubleShootingGroup, showCmd); + showCmd = expand && !IsWindowEnabled(selectOneDeviceLabel) ? SW_SHOW : SW_HIDE; ShowWindow(preMixLabel, showCmd); ShowWindow(postMixLabel, showCmd); ShowWindow(postMixLabel, showCmd); @@ -465,6 +518,8 @@ void Configurator::expandTroubleShooting(bool expand) ShowWindow(useOriginalAPOPreMix, showCmd); ShowWindow(useOriginalAPOPostMix, showCmd); ShowWindow(installModeComboBox, showCmd); + showCmd = expand && IsWindowEnabled(selectOneDeviceLabel) ? SW_SHOW : SW_HIDE; + ShowWindow(selectOneDeviceLabel, showCmd); int y = expand ? 229 : 188; @@ -509,6 +564,8 @@ void Configurator::updateList(int listIndex, int itemIndex) LoadStringW(hInstance, IDS_WILL_BE_UPGRADED, stringBuf, sizeof(stringBuf)/sizeof(wchar_t)); else if(apoInfo.isInstalled && apoInfo.hasChanges()) LoadStringW(hInstance, IDS_WILL_BE_CHANGED, stringBuf, sizeof(stringBuf)/sizeof(wchar_t)); + else if(apoInfo.isInstalled && apoInfo.isEnhancementsDisabled) + LoadStringW(hInstance, IDS_ENHANCEMENTS_WILL_BE_ENABLED, stringBuf, sizeof(stringBuf) / sizeof(wchar_t)); else if(apoInfo.isInstalled) LoadStringW(hInstance, IDS_ALREADY_INSTALLED, stringBuf, sizeof(stringBuf)/sizeof(wchar_t)); else if(apoInfo.isExperimental()) @@ -516,7 +573,18 @@ void Configurator::updateList(int listIndex, int itemIndex) else LoadStringW(hInstance, IDS_CAN_BE_INSTALLED, stringBuf, sizeof(stringBuf)/sizeof(wchar_t)); - ListView_SetItemText(deviceList, itemIndex, 2, stringBuf); + if(apoInfo.isDefaultDevice) + { + wstring statusText = stringBuf; + LoadStringW(hInstance, IDS_DEFAULT_DEVICE, stringBuf, sizeof(stringBuf)/sizeof(wchar_t)); + statusText = wstring(stringBuf) + L", " + statusText; + + ListView_SetItemText(deviceList, itemIndex, 2, const_cast(statusText.c_str())); + } + else + { + ListView_SetItemText(deviceList, itemIndex, 2, stringBuf); + } ListView_SetColumnWidth(deviceList, 2, LVSCW_AUTOSIZE); } @@ -526,7 +594,21 @@ void Configurator::updateButtons(int listIndex) { HWND deviceList = deviceLists[listIndex]; - EnableWindow(okButton, isChanged()); + wchar_t stringBuf[255]; + bool changed = isChanged(); + if(changed || !isAnySelected()) + { + ShowWindow(okButton, SW_SHOW); + EnableWindow(okButton, changed); + LoadStringW(hInstance, IDS_CANCEL, stringBuf, sizeof(stringBuf) / sizeof(wchar_t)); + SetWindowText(cancelButton, stringBuf); + } + else + { + ShowWindow(okButton, SW_HIDE); + LoadStringW(hInstance, IDS_CLOSE, stringBuf, sizeof(stringBuf) / sizeof(wchar_t)); + SetWindowText(cancelButton, stringBuf); + } unsigned selectedCount = ListView_GetSelectedCount(deviceList); EnableWindow(copyDeviceCommandButton, selectedCount > 0); @@ -558,6 +640,7 @@ void Configurator::updateButtons(int listIndex) EnableWindow(useOriginalAPOPreMix, enable && hasOriginalAPOPreMix && installState.installPreMix); EnableWindow(useOriginalAPOPostMix, enable && !isInput && hasOriginalAPOPostMix && installState.installPostMix); EnableWindow(installModeComboBox, enable); + EnableWindow(selectOneDeviceLabel, !enable); Button_SetCheck(installPreMix, installState.installPreMix); Button_SetCheck(installPostMix, installState.installPostMix); @@ -566,6 +649,8 @@ void Configurator::updateButtons(int listIndex) if(RegistryHelper::isWindowsVersionAtLeast(6, 3)) // Windows 8.1 ComboBox_SetCurSel(installModeComboBox, installState.installMode); + + expandTroubleShooting(Button_GetCheck(toggleTroubleShooting) != 0); } INT_PTR CALLBACK dlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); @@ -575,6 +660,8 @@ int APIENTRY wWinMain(HINSTANCE hInstance, LPWSTR lpCmdLine, int nCmdShow) { + CoInitializeEx(NULL, COINIT_MULTITHREADED); + INITCOMMONCONTROLSEX InitCtrls; InitCtrls.dwSize = sizeof(InitCtrls); InitCtrls.dwICC = ICC_WIN95_CLASSES; @@ -608,6 +695,8 @@ int APIENTRY wWinMain(HINSTANCE hInstance, configurator = new Configurator(hInstance, lpCmdLine); DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAINWINDOW), GetDesktopWindow(), dlgProc); } + + CoUninitialize(); return result; } diff --git a/Configurator/Configurator.h b/Configurator/Configurator.h index 87b8cd3..de85802 100644 --- a/Configurator/Configurator.h +++ b/Configurator/Configurator.h @@ -32,6 +32,7 @@ public: void onLvnItemChanged(unsigned sourceId, LPNMLISTVIEW info); bool onButtonClicked(unsigned sourceId); void onTcnSelChange(unsigned sourceId); + bool isAnySelected(); bool isChanged(); bool hasUpgrades(); @@ -53,6 +54,7 @@ private: HWND useOriginalAPOPreMix; HWND useOriginalAPOPostMix; HWND installModeComboBox; + HWND selectOneDeviceLabel; std::wstring cmdLine; void expandTroubleShooting(bool expand); diff --git a/Configurator/Configurator.rc b/Configurator/Configurator.rc index 0243d50..4c164a0 100644 Binary files a/Configurator/Configurator.rc and b/Configurator/Configurator.rc differ diff --git a/Configurator/Configurator.vcxproj b/Configurator/Configurator.vcxproj index b13d9b5..b8f1a87 100644 --- a/Configurator/Configurator.vcxproj +++ b/Configurator/Configurator.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -25,6 +25,7 @@ + @@ -34,6 +35,12 @@ + + Create + Create + Create + Create + @@ -52,29 +59,34 @@ {E18E1CA7-3D6C-4CD5-9CE9-E72565A206E5} Win32Proj Configurator + 10.0.10240.0 Application true Unicode + v120 Application true Unicode + v120 Application false true Unicode + v120 Application false true Unicode + v120 @@ -118,7 +130,7 @@ - NotUsing + Use Level3 Disabled WIN32;USE_WINDDK;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) @@ -131,7 +143,7 @@ - NotUsing + Use Level3 Disabled WIN32;USE_WINDDK;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) @@ -139,13 +151,13 @@ Windows true - audioeng.lib;version.lib;comctl32.lib;%(AdditionalDependencies) + audioeng.lib;version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;comctl32.lib;%(AdditionalDependencies) Level3 - NotUsing + Use MaxSpeed true true @@ -162,7 +174,7 @@ Level3 - NotUsing + Use MaxSpeed true true diff --git a/Configurator/Configurator.vcxproj.filters b/Configurator/Configurator.vcxproj.filters index 923bbbe..2d7c09b 100644 --- a/Configurator/Configurator.vcxproj.filters +++ b/Configurator/Configurator.vcxproj.filters @@ -15,6 +15,7 @@ helpers + @@ -35,6 +36,7 @@ helpers + diff --git a/Configurator/resource.h b/Configurator/resource.h index 549a843..bb42efb 100644 Binary files a/Configurator/resource.h and b/Configurator/resource.h differ diff --git a/DeviceAPOInfo.cpp b/DeviceAPOInfo.cpp index 675cfca..52f5970 100644 --- a/DeviceAPOInfo.cpp +++ b/DeviceAPOInfo.cpp @@ -17,16 +17,19 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include +#include #include "DeviceAPOInfo.h" -#include "EqualizerAPO.h" #include "helpers/StringHelper.h" #include "helpers/RegistryHelper.h" using namespace std; +#define protectedDGKeyPath L"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Audio" +#define protectedDGValueName L"DisableProtectedAudioDG" #define commonKeyPath L"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\Audio" #define renderKeyPath commonKeyPath L"\\Render" #define captureKeyPath commonKeyPath L"\\Capture" @@ -36,6 +39,8 @@ static const wchar_t* postMixChildGuidValueName = L"PostMixChild"; static const wchar_t* versionValueName = L"Version"; static const wchar_t* connectionValueName = L"{a45c254e-df1c-4efd-8020-67d146a850e0},2"; static const wchar_t* deviceValueName = L"{b3f8fa53-0004-438e-9003-51a46e139bfc},6"; +static const wchar_t* formatValueName = L"{f19f064d-082c-4e27-bc73-6882a1bb8e4c},0"; +static const wchar_t* channelMaskValueName = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e},3"; static const wchar_t* lfxGuidValueName = L"{d04e05a6-594b-4fb6-a80d-01af5eed7d1d},1"; static const wchar_t* gfxGuidValueName = L"{d04e05a6-594b-4fb6-a80d-01af5eed7d1d},2"; static const wchar_t* sfxGuidValueName = L"{d04e05a6-594b-4fb6-a80d-01af5eed7d1d},5"; @@ -43,7 +48,7 @@ static const wchar_t* mfxGuidValueName = L"{d04e05a6-594b-4fb6-a80d-01af5eed7d1d static const wchar_t* efxGuidValueName = L"{d04e05a6-594b-4fb6-a80d-01af5eed7d1d},7"; static const unsigned allGuidValueNameCount = 5; static const wchar_t* allGuidValueNames[] = {lfxGuidValueName, gfxGuidValueName, sfxGuidValueName, mfxGuidValueName, efxGuidValueName}; -static enum GuidValueIndices +enum GuidValueIndices { LFX_INDEX = 0, GFX_INDEX = 1, @@ -56,18 +61,21 @@ static const wchar_t* sfxProcessingModesValueName = L"{d3993a3f-99c2-4402-b5ec-a static const wchar_t* mfxProcessingModesValueName = L"{d3993a3f-99c2-4402-b5ec-a92a0367664b},6"; static const wchar_t* efxProcessingModesValueName = L"{d3993a3f-99c2-4402-b5ec-a92a0367664b},7"; static const wchar_t* defaultProcessingModeValue = L"{C18E2F7E-933D-4965-B7D1-1EEF228D2AF3}"; +static const wchar_t* disableEnhancementsValueName = L"{1da5d803-d492-4edd-8c23-e0c0ffee7f0e},5"; static const wchar_t* installVersion = L"2"; +static PROPERTYKEY guidPropertyKey = {{0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e}, 4}; vector DeviceAPOInfo::loadAllInfos(bool input) { vector result; vector deviceGuidStrings = RegistryHelper::enumSubKeys(input ? captureKeyPath : renderKeyPath); + wstring defaultDeviceGuid = getDefaultDevice(input); for(vector::iterator it = deviceGuidStrings.begin(); it != deviceGuidStrings.end(); it++) { wstring deviceGuidString = *it; DeviceAPOInfo info; - if(info.load(deviceGuidString)) + if(info.load(deviceGuidString, defaultDeviceGuid)) { info.selectedInstallState = info.currentInstallState; result.push_back(info); @@ -77,7 +85,56 @@ vector DeviceAPOInfo::loadAllInfos(bool input) return result; } -bool DeviceAPOInfo::load(const wstring& deviceGuid) +wstring DeviceAPOInfo::getDefaultDevice(bool input, int role) +{ + wstring result; + + IMMDeviceEnumerator* enumerator = NULL; + HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&enumerator); + if(SUCCEEDED(hr)) + { + IMMDevice* endPoint = NULL; + hr = enumerator->GetDefaultAudioEndpoint(input ? eCapture : eRender, (ERole)role, &endPoint); + if(SUCCEEDED(hr)) + { + IPropertyStore* propertyStore = NULL; + hr = endPoint->OpenPropertyStore(STGM_READ, &propertyStore); + if(SUCCEEDED(hr)) + { + PROPVARIANT variant; + PropVariantInit(&variant); + hr = propertyStore->GetValue(guidPropertyKey, &variant); + if(SUCCEEDED(hr)) + { + result = variant.pwszVal; + PropVariantClear(&variant); + } + propertyStore->Release(); + } + endPoint->Release(); + } + enumerator->Release(); + } + + return result; +} + +bool DeviceAPOInfo::checkProtectedAudioDG(bool fix) +{ + bool result = true; + + if (!RegistryHelper::valueExists(protectedDGKeyPath, protectedDGValueName) || RegistryHelper::readDWORDValue(protectedDGKeyPath, protectedDGValueName) != 1) + { + result = false; + + if (fix) + RegistryHelper::writeDWORDValue(protectedDGKeyPath, protectedDGValueName, 1); + } + + return result; +} + +bool DeviceAPOInfo::load(const wstring& deviceGuid, wstring defaultDeviceGuid) { wstring keyPath; if(RegistryHelper::keyExists(renderKeyPath L"\\" + deviceGuid)) @@ -100,6 +157,40 @@ bool DeviceAPOInfo::load(const wstring& deviceGuid) connectionName = RegistryHelper::readValue(keyPath + L"\\Properties", connectionValueName); deviceName = RegistryHelper::readValue(keyPath + L"\\Properties", deviceValueName); + channelCount = 0; + sampleRate = 0; + channelMask = 0; + if(RegistryHelper::valueExists(keyPath + L"\\Properties", formatValueName)) + { + std::vector format = RegistryHelper::readBinaryValue(keyPath + L"\\Properties", formatValueName); + if(format.size() >= sizeof(WAVEFORMATEX) + 8) + { + WAVEFORMATEX* waveFormat = (WAVEFORMATEX*)&format[8]; + channelCount = waveFormat->nChannels; + sampleRate = waveFormat->nSamplesPerSec; + if(waveFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + WAVEFORMATEXTENSIBLE* waveFormatExtensible = (WAVEFORMATEXTENSIBLE*)waveFormat; + channelMask = waveFormatExtensible->dwChannelMask; + } + } + } + if(channelMask == 0 && RegistryHelper::valueExists(keyPath + L"\\Properties", channelMaskValueName)) + channelMask = RegistryHelper::readDWORDValue(keyPath + L"\\Properties", channelMaskValueName); + + if(defaultDeviceGuid == L"") + defaultDeviceGuid = getDefaultDevice(isInput); + + GUID guid1, guid2; + if(SUCCEEDED(CLSIDFromString(deviceGuid.c_str(), &guid1)) && SUCCEEDED(CLSIDFromString(defaultDeviceGuid.c_str(), &guid2))) + isDefaultDevice = (guid1 == guid2) != 0; + else + isDefaultDevice = false; + + isEnhancementsDisabled = false; + if(RegistryHelper::keyExists(keyPath + L"\\FxProperties") && RegistryHelper::valueExists(keyPath + L"\\FxProperties", disableEnhancementsValueName)) + isEnhancementsDisabled = RegistryHelper::readDWORDValue(keyPath + L"\\FxProperties", disableEnhancementsValueName) != 0; + isInstalled = false; currentInstallState.installMode = INSTALL_LFX_GFX; version = L"0"; @@ -112,12 +203,12 @@ bool DeviceAPOInfo::load(const wstring& deviceGuid) if(!RegistryHelper::keyExists(keyPath + L"\\FxProperties")) { - for(int i=0; i < allGuidValueNameCount; i++) + for(int i = 0; i < allGuidValueNameCount; i++) originalApoGuids[i] = APOGUID_NOKEY; } else { - for(int i=0; i < allGuidValueNameCount; i++) + for(int i = 0; i < allGuidValueNameCount; i++) { if(RegistryHelper::valueExists(keyPath + L"\\FxProperties", allGuidValueNames[i])) originalApoGuids[i] = RegistryHelper::readValue(keyPath + L"\\FxProperties", allGuidValueNames[i]); @@ -127,7 +218,7 @@ bool DeviceAPOInfo::load(const wstring& deviceGuid) bool found = false; bool foundAt[allGuidValueNameCount]; - for(int i=0; i < allGuidValueNameCount; i++) + for(int i = 0; i < allGuidValueNameCount; i++) { foundAt[i] = false; @@ -164,7 +255,7 @@ bool DeviceAPOInfo::load(const wstring& deviceGuid) version = L"1"; } - for(int i=0; i < allGuidValueNameCount; i++) + for(int i = 0; i < allGuidValueNameCount; i++) { if(RegistryHelper::valueExists(childApoPath L"\\" + deviceGuid, allGuidValueNames[i])) originalApoGuids[i] = RegistryHelper::readValue(childApoPath L"\\" + deviceGuid, allGuidValueNames[i]); @@ -208,7 +299,7 @@ bool DeviceAPOInfo::load(const wstring& deviceGuid) } else if(RegistryHelper::valueExists(childApoPath, deviceGuid)) { - for(int i=0; i < allGuidValueNameCount; i++) + for(int i = 0; i < allGuidValueNameCount; i++) { if(foundAt[i]) { @@ -335,7 +426,7 @@ void DeviceAPOInfo::install() RegistryHelper::writeValue(keyPath + L"\\FxProperties", fxTitleValueName, L"Equalizer APO"); - for(int i=0; i < allGuidValueNameCount; i++) + for(int i = 0; i < allGuidValueNameCount; i++) { RegistryHelper::writeValue(childApoPath L"\\" + deviceGuid, allGuidValueNames[i], APOGUID_NOKEY); } @@ -344,7 +435,7 @@ void DeviceAPOInfo::install() { vector valuenames; - for(int i=0; i < allGuidValueNameCount; i++) + for(int i = 0; i < allGuidValueNameCount; i++) { wstring apoGuidString = APOGUID_NOVALUE; if(RegistryHelper::valueExists(keyPath + L"\\FxProperties", allGuidValueNames[i])) @@ -358,7 +449,7 @@ void DeviceAPOInfo::install() if(!valuenames.empty()) RegistryHelper::saveToFile(keyPath + L"\\FxProperties", valuenames, - L"backup_" + StringHelper::replaceIllegalCharacters(deviceName) + L"_" + StringHelper::replaceIllegalCharacters(connectionName) + L".reg"); + L"backup_" + StringHelper::replaceIllegalCharacters(deviceName) + L"_" + StringHelper::replaceIllegalCharacters(connectionName) + L".reg"); } wstring preMixValue; @@ -425,6 +516,10 @@ void DeviceAPOInfo::install() RegistryHelper::writeMultiValue(keyPath + L"\\FxProperties", efxProcessingModesValueName, defaultProcessingModeValue); } } + + // force-enable enhancements + if(RegistryHelper::valueExists(keyPath + L"\\FxProperties", disableEnhancementsValueName)) + RegistryHelper::deleteValue(keyPath + L"\\FxProperties", disableEnhancementsValueName); } void DeviceAPOInfo::uninstall() @@ -441,7 +536,7 @@ void DeviceAPOInfo::uninstall() } else { - for(int i=0; i < allGuidValueNameCount; i++) + for(int i = 0; i < allGuidValueNameCount; i++) { if(originalApoGuids[i] == APOGUID_NOVALUE) { diff --git a/DeviceAPOInfo.h b/DeviceAPOInfo.h index 044e902..6845a7d 100644 --- a/DeviceAPOInfo.h +++ b/DeviceAPOInfo.h @@ -60,7 +60,9 @@ public: }; static std::vector loadAllInfos(bool input); - bool load(const std::wstring& deviceGuid); + static std::wstring getDefaultDevice(bool input, int role = 1); + static bool checkProtectedAudioDG(bool fix); + bool load(const std::wstring& deviceGuid, std::wstring defaultDeviceGuid = L""); bool canBeUpgraded(); bool hasChanges(); bool isExperimental(); @@ -72,6 +74,11 @@ public: std::wstring deviceName; std::wstring connectionName; std::wstring deviceGuid; + unsigned channelCount; + unsigned sampleRate; + unsigned long channelMask; + bool isDefaultDevice; + bool isEnhancementsDisabled; // used for creating child APO std::wstring preMixChildGuid; diff --git a/DllMain.cpp b/DllMain.cpp index bcc0d01..2aa51ca 100644 --- a/DllMain.cpp +++ b/DllMain.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include #define WIN32_LEAN_AND_MEAN #include diff --git a/Editor/Editor.pro b/Editor/Editor.pro new file mode 100644 index 0000000..d1690a2 --- /dev/null +++ b/Editor/Editor.pro @@ -0,0 +1,207 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2014-10-21T22:44:17 +# +#------------------------------------------------- + +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = Editor +TEMPLATE = app + +PRECOMPILED_HEADER = stable.h +QMAKE_CXXFLAGS_WARN_ON -= -w34100 + +DEFINES += NO_FILTERENGINE + +SOURCES += main.cpp\ + ../helpers/LogHelper.cpp \ + ../helpers/StringHelper.cpp \ + ../helpers/RegistryHelper.cpp \ + IFilterGUIFactory.cpp \ + IFilterGUI.cpp \ + guis/PreampFilterGUI.cpp \ + guis/PreampFilterGUIFactory.cpp \ + guis/CommentFilterGUIFactory.cpp \ + guis/CommentFilterGUI.cpp \ + FilterTable.cpp \ + ../filters/PreampFilter.cpp \ + ../filters/PreampFilterFactory.cpp \ + ../helpers/MemoryHelper.cpp \ + FilterTableRow.cpp \ + FilterTemplate.cpp \ + guis/DeviceFilterGUI.cpp \ + guis/DeviceFilterGUIFactory.cpp \ + ../DeviceAPOInfo.cpp \ + guis/DeviceFilterGUIDialog.cpp \ + ../filters/DeviceFilterFactory.cpp \ + guis/ChannelFilterGUIDialog.cpp \ + guis/ChannelFilterGUI.cpp \ + guis/ChannelFilterGUIFactory.cpp \ + guis/BiQuadFilterGUI.cpp \ + ../filters/BiQuad.cpp \ + ../filters/BiQuadFilter.cpp \ + ../filters/BiQuadFilterFactory.cpp \ + guis/BiQuadFilterGUIFactory.cpp \ + guis/CopyFilterGUIFactory.cpp \ + guis/CopyFilterGUI.cpp \ + guis/CopyFilterGUIConnectionItem.cpp \ + guis/CopyFilterGUIChannelItem.cpp \ + ../filters/CopyFilter.cpp \ + ../filters/CopyFilterFactory.cpp \ + ../IFilter.cpp \ + guis/CopyFilterGUIScene.cpp \ + guis/CopyFilterGUIForm.cpp \ + guis/CopyFilterGUIRow.cpp \ + helpers/GUIChannelHelper.cpp \ + ../helpers/ChannelHelper.cpp \ + guis/DelayFilterGUI.cpp \ + guis/DelayFilterGUIFactory.cpp \ + ../filters/DelayFilter.cpp \ + ../filters/DelayFilterFactory.cpp \ + guis/IncludeFilterGUI.cpp \ + guis/IncludeFilterGUIFactory.cpp \ + widgets/ResizingLineEdit.cpp \ + widgets/ChannelGraphScene.cpp \ + widgets/ChannelGraphItem.cpp \ + guis/ChannelFilterGUIScene.cpp \ + guis/ChannelFilterGUIChannelItem.cpp \ + guis/GraphicEQFilterGUIFactory.cpp \ + guis/GraphicEQFilterGUI.cpp \ + ../filters/GraphicEQFilter.cpp \ + ../filters/GraphicEQFilterFactory.cpp \ + ../libHybridConv-0.1.1/libHybridConv_eapo.cpp \ + ../helpers/GainIterator.cpp \ + guis/GraphicEQFilterGUIScene.cpp \ + widgets/FrequencyPlotView.cpp \ + widgets/FrequencyPlotHRuler.cpp \ + widgets/FrequencyPlotVRuler.cpp \ + widgets/FrequencyPlotItem.cpp \ + guis/GraphicEQFilterGUIItem.cpp \ + guis/GraphicEQFilterGUIView.cpp \ + widgets/FrequencyPlotScene.cpp \ + widgets/CompactToolBar.cpp \ + guis/ConvolutionFilterGUIFactory.cpp \ + guis/ConvolutionFilterGUI.cpp \ + helpers/DisableWheelFilter.cpp \ + widgets/EscapableLineEdit.cpp \ + MainWindow.cpp \ + guis/StageFilterGUI.cpp \ + guis/StageFilterGUIFactory.cpp \ + guis/ExpressionFilterGUIFactory.cpp + +HEADERS += \ + ../helpers/LogHelper.h \ + ../helpers/StringHelper.h \ + ../helpers/RegistryHelper.h \ + IFilterGUIFactory.h \ + stable.h \ + IFilterGUI.h \ + guis/PreampFilterGUI.h \ + guis/PreampFilterGUIFactory.h \ + guis/CommentFilterGUIFactory.h \ + guis/CommentFilterGUI.h \ + FilterTable.h \ + ../filters/PreampFilter.h \ + ../filters/PreampFilterFactory.h \ + ../helpers/MemoryHelper.h \ + FilterTableRow.h \ + FilterTemplate.h \ + guis/DeviceFilterGUI.h \ + guis/DeviceFilterGUIFactory.h \ + ../DeviceAPOInfo.h \ + guis/DeviceFilterGUIDialog.h \ + ../filters/DeviceFilterFactory.h \ + guis/ChannelFilterGUIDialog.h \ + guis/ChannelFilterGUI.h \ + guis/ChannelFilterGUIFactory.h \ + guis/BiQuadFilterGUI.h \ + ../filters/BiQuad.h \ + ../filters/BiQuadFilter.h \ + ../filters/BiQuadFilterFactory.h \ + guis/BiQuadFilterGUIFactory.h \ + guis/CopyFilterGUIFactory.h \ + guis/CopyFilterGUI.h \ + guis/CopyFilterGUIConnectionItem.h \ + guis/CopyFilterGUIChannelItem.h \ + ../filters/CopyFilter.h \ + ../filters/CopyFilterFactory.h \ + ../IFilter.h \ + ../IFilterFactory.h \ + guis/CopyFilterGUIScene.h \ + guis/CopyFilterGUIForm.h \ + guis/CopyFilterGUIRow.h \ + helpers/GUIChannelHelper.h \ + ../helpers/ChannelHelper.h \ + guis/DelayFilterGUI.h \ + guis/DelayFilterGUIFactory.h \ + ../filters/DelayFilter.h \ + ../filters/DelayFilterFactory.h \ + guis/IncludeFilterGUI.h \ + guis/IncludeFilterGUIFactory.h \ + widgets/ResizingLineEdit.h \ + widgets/ChannelGraphScene.h \ + widgets/ChannelGraphItem.h \ + guis/ChannelFilterGUIScene.h \ + guis/ChannelFilterGUIChannelItem.h \ + guis/GraphicEQFilterGUIFactory.h \ + guis/GraphicEQFilterGUI.h \ + ../filters/GraphicEQFilter.h \ + ../filters/GraphicEQFilterFactory.h \ + ../libHybridConv-0.1.1/libHybridConv_eapo.h \ + ../helpers/GainIterator.h \ + guis/GraphicEQFilterGUIScene.h \ + widgets/FrequencyPlotView.h \ + widgets/FrequencyPlotHRuler.h \ + widgets/FrequencyPlotVRuler.h \ + widgets/FrequencyPlotItem.h \ + guis/GraphicEQFilterGUIItem.h \ + guis/GraphicEQFilterGUIView.h \ + widgets/FrequencyPlotScene.h \ + widgets/CompactToolBar.h \ + guis/ConvolutionFilterGUIFactory.h \ + guis/ConvolutionFilterGUI.h \ + helpers/DisableWheelFilter.h \ + widgets/EscapableLineEdit.h \ + ../version.h \ + ../stdafx.h \ + MainWindow.h \ + guis/StageFilterGUI.h \ + guis/StageFilterGUIFactory.h \ + guis/ExpressionFilterGUIFactory.h + +FORMS += \ + guis/PreampFilterGUI.ui \ + guis/CommentFilterGUI.ui \ + FilterTableRow.ui \ + guis/DeviceFilterGUI.ui \ + guis/DeviceFilterGUIDialog.ui \ + guis/ChannelFilterGUIDialog.ui \ + guis/ChannelFilterGUI.ui \ + guis/BiQuadFilterGUI.ui \ + guis/CopyFilterGUI.ui \ + guis/CopyFilterGUIRow.ui \ + guis/DelayFilterGUI.ui \ + guis/IncludeFilterGUI.ui \ + guis/GraphicEQFilterGUI.ui \ + guis/ConvolutionFilterGUI.ui \ + MainWindow.ui \ + guis/StageFilterGUI.ui + +INCLUDEPATH += $$PWD/.. "C:/Program Files/libsndfile/include" "C:/Program Files/fftw3" +LIBS += advapi32.lib version.lib ole32.lib libsndfile-1.lib libfftw3f-3.lib +contains(QT_ARCH, x86_64) { + QMAKE_LIBDIR += "C:/Program Files/libsndfile/lib" "C:/Program Files/fftw3" +} else { + QMAKE_LIBDIR += "C:/Program Files (x86)/libsndfile/lib" "C:/Program Files (x86)/fftw3" +} + +RESOURCES += \ + Editor.qrc +TRANSLATIONS += translations/Editor_de.ts + +RC_ICONS = icons/accessories-text-editor.ico + +DISTFILES += diff --git a/Editor/Editor.qrc b/Editor/Editor.qrc new file mode 100644 index 0000000..e64f1f9 --- /dev/null +++ b/Editor/Editor.qrc @@ -0,0 +1,26 @@ + + + icons/arrow_right.png + icons/16x16/document-open.png + icons/16x16/document-new.png + icons/16x16/document-save-as.png + icons/16x16/document-save.png + icons/16x16/edit-copy.png + icons/16x16/edit-cut.png + icons/16x16/edit-delete.png + icons/16x16/edit-paste.png + icons/16x16/accessories-text-editor.png + icons/16x16/list-add-green.png + icons/16x16/list-remove-red.png + icons/mirror_response.png + icons/normalize_response.png + icons/power_off.png + icons/power_on.png + icons/16x16/applications-graphics-rotated.png + icons/16x16/format-justify-fill-rotated.png + translations/Editor_de.qm + translations/qtbase_de.qm + icons/16x16/go-up.png + icons/reset_response.png + + diff --git a/Editor/FilterTable.cpp b/Editor/FilterTable.cpp new file mode 100644 index 0000000..c7371f1 --- /dev/null +++ b/Editor/FilterTable.cpp @@ -0,0 +1,847 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2014 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MainWindow.h" +#include "FilterTableRow.h" +#include "guis/ExpressionFilterGUIFactory.h" +#include "guis/CommentFilterGUIFactory.h" +#include "guis/DeviceFilterGUIFactory.h" +#include "guis/ChannelFilterGUIFactory.h" +#include "guis/StageFilterGUIFactory.h" +#include "guis/PreampFilterGUIFactory.h" +#include "guis/BiQuadFilterGUIFactory.h" +#include "guis/CopyFilterGUIFactory.h" +#include "guis/DelayFilterGUIFactory.h" +#include "guis/IncludeFilterGUIFactory.h" +#include "guis/GraphicEQFilterGUIFactory.h" +#include "guis/ConvolutionFilterGUIFactory.h" +#include "helpers/StringHelper.h" +#include "helpers/LogHelper.h" +#include "helpers/ChannelHelper.h" +#include "FilterTable.h" + +using namespace std; + +FilterTable::FilterTable(MainWindow* mainWindow, QWidget *parent) : + QWidget(parent), mainWindow(mainWindow) +{ + gridLayout = new QGridLayout(this); + + QPixmap pixmap(QStringLiteral(":/icons/arrow_right.png")); + insertArrow = new QLabel(this); + insertArrow->setPixmap(pixmap); + insertArrow->setVisible(false); + + factories.append(new ExpressionFilterGUIFactory); + factories.append(new CommentFilterGUIFactory); + factories.append(new IncludeFilterGUIFactory); + factories.append(new DeviceFilterGUIFactory); + factories.append(new ChannelFilterGUIFactory); + factories.append(new StageFilterGUIFactory); + factories.append(new PreampFilterGUIFactory); + factories.append(new BiQuadFilterGUIFactory); + factories.append(new DelayFilterGUIFactory); + factories.append(new CopyFilterGUIFactory); + factories.append(new GraphicEQFilterGUIFactory); + factories.append(new ConvolutionFilterGUIFactory); + + QApplication::instance()->installEventFilter(this); +} + +void FilterTable::initialize(QScrollArea* scrollArea, const QList& outputDevices, const QList& inputDevices) +{ + this->scrollArea = scrollArea; + this->outputDevices = outputDevices; + this->inputDevices = inputDevices; + + for(IFilterGUIFactory* factory : factories) + factory->initialize(this); +} + +void FilterTable::updateDeviceAndChannelMask(DeviceAPOInfo* selectedDevice, int channelMask) +{ + this->selectedDevice = selectedDevice; + this->selectedChannelMask = channelMask; + + if(!items.empty()) + updateGuis(); +} + +void FilterTable::updateGuis() +{ + QElapsedTimer timer; + timer.start(); + + guis.clear(); + delete layout(); + + for(QObject* object : children()) + { + if(object != insertArrow) + { + QWidget* widget = qobject_cast(object); + if(widget != NULL) + widget->setVisible(false); + object->deleteLater(); + } + } + + qDebug("Delete took %d ms", timer.elapsed()); + timer.start(); + + gridLayout = new QGridLayout(this); + gridLayout->setMargin(0); + gridLayout->setSpacing(0); + gridLayout->setColumnStretch(0, 0); + gridLayout->setColumnStretch(1, 1); + + for(IFilterGUIFactory* factory : factories) + factory->startOfFile(configPath); + + int row = 0; + for(Item* item : items) + { + QString line = item->text; + IFilterGUI* gui = NULL; + int pos = line.indexOf(L':'); + if(pos != -1) + { + QString key = line.mid(0, pos); + QString value = line.mid(pos + 1); + + // allow to use indentation + key = key.trimmed(); + QString factoryKey = key; + QString factoryValue = value; + + for(IFilterGUIFactory* factory : factories) + { + gui = factory->createFilterGUI(factoryKey, factoryValue); + + if(gui != NULL || factoryKey == "") + break; + } + + if(gui != NULL) + { + for(IFilterGUIFactory* factory : factories) + { + gui = factory->decorateFilterGUI(gui); + } + } + } + + FilterTableRow* rowWidget = new FilterTableRow(this, row + 1, item, gui); + gridLayout->addWidget(rowWidget, row, 0); + + if(gui != NULL) + { + guis.append(gui); + connect(gui, SIGNAL(updateModel()), this, SLOT(updateModel())); + connect(gui, SIGNAL(updateChannels()), this, SLOT(updateChannels())); + } + + row++; + } + + for(IFilterGUIFactory* factory : factories) + factory->endOfFile(configPath); + + propagateChannels(); + + QToolBar* toolBar = new QToolBar; + toolBar->setIconSize(QSize(16, 16)); + + QWidget* spacer = new QWidget; + spacer->setFixedWidth(25); + toolBar->addWidget(spacer); + + QAction* addAction = new QAction(QIcon(":/icons/16x16/list-add-green.png"), tr("Add filter"), toolBar); + addAction->setCheckable(true); + connect(addAction, SIGNAL(triggered()), this, SLOT(addActionTriggered())); + toolBar->addAction(addAction); + + gridLayout->addWidget(toolBar, row++, 0, 1, 1, Qt::AlignLeft | Qt::AlignTop); + + QSpacerItem* spacerItem = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding); + gridLayout->addItem(spacerItem, row, 0); + + gridLayout->setRowStretch(row, 1); + + disableWheelForWidgets(); + + qDebug("Create took %d ms", timer.elapsed()); + update(); +} + +void FilterTable::propagateChannels() +{ + vector channelNames = ChannelHelper::getChannelNames(selectedDevice->channelCount, selectedChannelMask); + + for(IFilterGUI* gui : guis) + { + gui->configureChannels(channelNames); + } +} + +QList FilterTable::getLines() +{ + QList result; + for(Item* item : items) + result.append(item->text); + + return result; +} + +void FilterTable::setLines(const QString& configPath, const QList& lines) +{ + this->configPath = configPath; + + qDeleteAll(items); + items.clear(); + + for(QString line : lines) + items.append(new Item(line)); + + if(!items.isEmpty()) + { + focused = items[0]; + selectionStart = items[0]; + } + else + { + focused = NULL; + selectionStart = NULL; + } + + updateGuis(); +} + +FilterTable::Item* FilterTable::addLine(const QString& line, FilterTable::Item* before) +{ + Item* newItem = new Item(line); + + if(before != NULL) + { + int index = items.indexOf(before); + items.insert(index, newItem); + } + else + { + items.append(newItem); + } + + emit linesChanged(); + + return newItem; +} + +void FilterTable::removeItem(FilterTable::Item* item) +{ + items.removeOne(item); + emit linesChanged(); +} + +QMenu* FilterTable::createAddPopupMenu() +{ + QHash, QMenu*> pathMap; + QMenu* rootMenu = new QMenu; + pathMap[QStringList()] = rootMenu; + + for(IFilterGUIFactory* f : factories) + { + QList templates = f->createFilterTemplates(); + for(FilterTemplate t : templates) + { + QMenu* menu = pathMap.value(t.getPath()); + if(menu == NULL) + { + QMenu* parentMenu = rootMenu; + QStringList currentPath; + for(QString pathSegment : t.getPath()) + { + currentPath.append(pathSegment); + menu = pathMap.value(currentPath); + if(menu == NULL) + { + menu = new QMenu(pathSegment); + pathMap.insert(currentPath, menu); + parentMenu->addMenu(menu); + } + parentMenu = menu; + } + } + + QAction* action = menu->addAction(t.getName()); + action->setData(QVariant::fromValue(t)); + } + } + + return rootMenu; +} + +void FilterTable::cut() +{ + copy(); + deleteSelectedLines(); +} + +void FilterTable::copy() +{ + QString text; + bool first = true; + for(Item* item : items) + { + if(selected.contains(item)) + { + if(first) + first = false; + else + text += "\n"; + text += item->text; + } + } + + if(selected.size() > 0) + { + QMimeData* mimeData = new QMimeData; + mimeData->setText(text); + QClipboard* clipboard = QApplication::clipboard(); + clipboard->setMimeData(mimeData); + } +} + +void FilterTable::paste() +{ + QClipboard* clipboard = QApplication::clipboard(); + const QMimeData* mimeData = clipboard->mimeData(); + if(mimeData->hasText()) + { + int dropRow = items.size(); + for(int i = 0; i < items.size(); i++) + { + if(selected.contains(items[i])) + { + dropRow = i; + break; + } + } + + QString text = mimeData->text(); + QStringList textLines = text.split("\n"); + selected.clear(); + focused = NULL; + selectionStart = NULL; + for(QString line : textLines) + { + Item* item = new Item(line); + selected.insert(item); + items.insert(dropRow++, item); + if(focused == NULL) + { + focused = item; + selectionStart = item; + } + } + + emit linesChanged(); + updateGuis(); + } +} + +void FilterTable::deleteSelectedLines() +{ + QList newItems; + for(Item* item : items) + { + if(selected.contains(item)) + { + if(item == focused) + focused = NULL; + if(item == selectionStart) + selectionStart = NULL; + delete item; + } + else + { + newItems.append(item); + } + } + selected.clear(); + items = newItems; + emit linesChanged(); + updateGuis(); +} + +void FilterTable::updateModel() +{ + emit linesChanged(); +} + +void FilterTable::updateChannels() +{ + propagateChannels(); +} + +void FilterTable::addActionTriggered() +{ + QMenu* menu = createAddPopupMenu(); + QAction* addAction = qobject_cast(QObject::sender()); + QToolBar* toolBar = qobject_cast(addAction->parentWidget()); + QRect rect = toolBar->actionGeometry(addAction); + QPoint p = toolBar->mapToGlobal(QPoint(rect.x(), rect.y() + rect.height())); + QAction* action = menu->exec(p); + addAction->setChecked(false); + if(action != NULL) + { + FilterTemplate t = action->data().value(); + QString line = t.getLine(); + addLine(line); + updateGuis(); + } +} + +void FilterTable::openConfig(QString path) +{ + mainWindow->load(path); +} + +void FilterTable::mousePressEvent(QMouseEvent* event) +{ + if(event->buttons() & Qt::LeftButton ) + { + int row = rowForPos(event->pos(), false); + if(row != -1) + { + Item* item = items[row]; + + if(event->modifiers() & Qt::ControlModifier) + { + if(!selected.remove(item)) + selected.insert(item); + selectionStart = item; + } + else if(event->modifiers() & Qt::ShiftModifier) + { + int startRow = items.indexOf(selectionStart); + if(startRow != -1) + { + selected.clear(); + for(int i = min(startRow, row); i <= max(startRow, row); i++) + { + selected.insert(items[i]); + } + } + } + else + { + if(!selected.contains(item)) + { + selected.clear(); + selected.insert(item); + } + selectionStart = item; + } + focused = item; + ensureRowVisible(row); + update(); + + dragStartPos = event->pos(); + } + else + { + selected.clear(); + update(); + } + } +} + +void FilterTable::mouseMoveEvent(QMouseEvent* event) +{ + if(event->buttons() & Qt::LeftButton) + { + if((event->pos() - dragStartPos).manhattanLength() >= QApplication::startDragDistance()) + { + QString text; + bool first = true; + int i = 0; + bool dragPosInside = false; + for(Item* item : items) + { + if(selected.contains(item)) + { + if(first) + first = false; + else + text += "\n"; + text += item->text; + + if(!dragPosInside) + { + FilterTableRow* tableRow = qobject_cast(gridLayout->itemAtPosition(i, 0)->widget()); + QRect rect = tableRow->getHeaderRect().translated(tableRow->pos()); + if(rect.contains(dragStartPos)) + dragPosInside = true; + } + } + i++; + } + + if(selected.size() > 0 && dragPosInside) + { + QMimeData* mimeData = new QMimeData; + mimeData->setText(text); + + QDrag* drag = new QDrag(this); + drag->setMimeData(mimeData); + QSet selectedBefore = selected; + internalDrag = true; + Qt::DropAction action = drag->exec(Qt::MoveAction | Qt::CopyAction); + internalDrag = false; + if(action == Qt::MoveAction) + { + for(Item* item : selectedBefore) + { + items.removeOne(item); + if(focused == item) + focused = NULL; + if(selectionStart == item) + selectionStart = NULL; + selected.remove(item); + delete item; + } + } + + if(action != Qt::IgnoreAction) + { + emit linesChanged(); + updateGuis(); + } + } + } + } + + QWidget::mouseMoveEvent(event); +} + +void FilterTable::dragEnterEvent(QDragEnterEvent* event) +{ + if(event->mimeData()->hasText()) + { + if(event->keyboardModifiers() & Qt::ControlModifier) + event->setDropAction(Qt::CopyAction); + else + event->setDropAction(Qt::MoveAction); + event->accept(); + } + + QWidget::dragEnterEvent(event); +} + +void FilterTable::dragMoveEvent(QDragMoveEvent* event) +{ + if(event->mimeData()->hasText()) + { + if(event->keyboardModifiers() & Qt::ControlModifier) + event->setDropAction(Qt::CopyAction); + else + event->setDropAction(Qt::MoveAction); + + int dropRow = rowForPos(event->pos(), true); + + QRect rect = gridLayout->itemAtPosition(dropRow == -1 ? gridLayout->rowCount() - 2 : dropRow, 0)->geometry(); + insertArrow->move(0, rect.top() - insertArrow->height() / 2 - gridLayout->verticalSpacing() / 2); + + insertArrow->raise(); + insertArrow->show(); + } + + QWidget::dragMoveEvent(event); +} + +void FilterTable::dragLeaveEvent(QDragLeaveEvent* event) +{ + insertArrow->hide(); +} + +void FilterTable::dropEvent(QDropEvent* event) +{ + if(event->mimeData()->hasText()) + { + if(event->keyboardModifiers() & Qt::ControlModifier) + event->setDropAction(Qt::CopyAction); + else + event->setDropAction(Qt::MoveAction); + + QString text = event->mimeData()->text(); + QStringList textLines = text.split("\n"); + int dropRow = rowForPos(event->pos(), true); + if(dropRow == -1) + dropRow = items.size(); + + selected.clear(); + focused = NULL; + selectionStart = NULL; + for(QString line : textLines) + { + Item* item = new Item(line); + selected.insert(item); + items.insert(dropRow++, item); + if(focused == NULL) + { + focused = item; + selectionStart = item; + } + } + event->accept(); + + if(!internalDrag) + { + emit linesChanged(); + updateGuis(); + } + } + + insertArrow->hide(); + + QWidget::dropEvent(event); +} + +void FilterTable::keyPressEvent(QKeyEvent* event) +{ + if(event->key() == Qt::Key_Down || event->key() == Qt::Key_Up) + { + if(focused != NULL) + { + int row = items.indexOf(focused); + if(row != -1) + { + int newRow = row; + if(event->key() == Qt::Key_Down && row + 1 < items.size()) + newRow = row + 1; + else if(event->key() == Qt::Key_Up && row - 1 >= 0) + newRow = row - 1; + + if(newRow != row) + { + focused = items[newRow]; + if(event->modifiers() & Qt::ControlModifier) + { + } + else if(event->modifiers() & Qt::ShiftModifier) + { + int startRow = items.indexOf(selectionStart); + if(startRow != -1) + { + selected.clear(); + for(int i = min(startRow, newRow); i <= max(startRow, newRow); i++) + { + selected.insert(items[i]); + } + } + } + else + { + selected.clear(); + selected.insert(focused); + selectionStart = focused; + } + + ensureRowVisible(newRow); + update(); + } + } + } + } + + if(event->key() == Qt::Key_Space) + { + if(!(event->modifiers() & Qt::ControlModifier) || !selected.remove(focused)) + selected.insert(focused); + update(); + } + + if(event->key() == Qt::Key_F2) + { + if(focused != NULL) + { + int rowIndex = items.indexOf(focused); + if(rowIndex != -1) + { + QLayoutItem* layoutItem = gridLayout->itemAtPosition(rowIndex, 0); + FilterTableRow* tableRow = qobject_cast(layoutItem->widget()); + if(tableRow != NULL) + tableRow->editText(); + } + } + } + + if(event->key() == Qt::Key_Delete) + { + deleteSelectedLines(); + } +} + +void FilterTable::wheelEvent(QWheelEvent * event) +{ + scrollingNow = true; + scrollStartPoint = event->globalPos(); + + QWidget::wheelEvent(event); +} + + +bool FilterTable::eventFilter(QObject* obj, QEvent* event) +{ + if(scrollingNow) + { + QEvent::Type type = event->type(); + if(type == QEvent::Wheel) + { + QWidget* widget = qobject_cast(obj); + if(widget != NULL) + { + if(isAncestorOf(widget)) + { + QApplication::sendEvent(parent(), event); + return true; + } + } + } + else if(type == QEvent::MouseMove) + { + QMouseEvent* mouseEvent = (QMouseEvent*)event; + + if((mouseEvent->globalPos() - scrollStartPoint).manhattanLength() > QApplication::startDragDistance()) + scrollingNow = false; + } + } + + return false; +} + +void FilterTable::ensureRowVisible(int row) +{ + QScrollBar* vScrollBar = scrollArea->verticalScrollBar(); + if(vScrollBar != NULL) + { + QRect rect = rowRect(row).toAlignedRect(); + if(rect.top() < vScrollBar->value()) + vScrollBar->setValue(max(0, rect.top())); + else if(rect.bottom() + 1 > vScrollBar->value() + scrollArea->viewport()->height()) + vScrollBar->setValue(min(vScrollBar->maximum(), rect.bottom() + 1 - scrollArea->viewport()->height())); + } +} + +int FilterTable::rowForPos(QPoint pos, bool insert) +{ + int row = -1; + for(int i = 0; i < gridLayout->rowCount() - 2; i++) + { + QRect rect = gridLayout->itemAtPosition(i, 0)->geometry(); + int y; + if(insert) + y = rect.center().y(); + else + y = rect.bottom(); + + if(pos.y() <= y) + { + row = i; + break; + } + } + + return row; +} + +QRectF FilterTable::rowRect(int row) +{ + QRectF rect = gridLayout->itemAtPosition(row, 0)->geometry(); + rect = rect.marginsAdded(QMarginsF(-1.5, -1.5, -1.5, -0.5)); + return rect; +} + +void FilterTable::disableWheelForWidgets() +{ + QList widgets = findChildren(); + for(QWidget* widget : widgets) + { + if(qobject_cast(widget) || qobject_cast(widget) || qobject_cast(widget)) + { + widget->installEventFilter(new DisableWheelFilter(this, widget)); + if(widget->focusPolicy() == Qt::WheelFocus) + widget->setFocusPolicy(Qt::StrongFocus); + } + } +} + +QString FilterTable::getConfigPath() const +{ + return configPath; +} + +void FilterTable::setConfigPath(const QString& value) +{ + configPath = value; +} + +FilterTable::Item* FilterTable::getFocusedItem() const +{ + return focused; +} + +const QSet& FilterTable::getSelectedItems() const +{ + return selected; +} + +const QList& FilterTable::getOutputDevices() const +{ + return outputDevices; +} + +const QList& FilterTable::getInputDevices() const +{ + return inputDevices; +} + +DeviceAPOInfo* FilterTable::getSelectedDevice() const +{ + return selectedDevice; +} + +int FilterTable::getSelectedChannelMask() const +{ + return selectedChannelMask; +} diff --git a/Editor/FilterTable.h b/Editor/FilterTable.h new file mode 100644 index 0000000..e4366e6 --- /dev/null +++ b/Editor/FilterTable.h @@ -0,0 +1,138 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2014 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "Editor/helpers/DisableWheelFilter.h" +#include "DeviceAPOInfo.h" +#include "IFilterGUI.h" +#include "IFilterGUIFactory.h" + +class MainWindow; + +class FilterTable : public QWidget +{ + Q_OBJECT +public: + struct Item { + Item() + { + } + + Item(const QString& text) + { + this->text = text; + } + + QString text; + }; + + explicit FilterTable(MainWindow* mainWindow, QWidget *parent = 0); + void initialize(QScrollArea* scrollArea, const QList& outputDevices, const QList& inputDevices); + void updateDeviceAndChannelMask(DeviceAPOInfo* selectedDevice, int selectedChannelMask); + void updateGuis(); + void propagateChannels(); + QList getLines(); + void setLines(const QString& configPath, const QList& lines); + Item* addLine(const QString& line, Item* before = NULL); + void removeItem(Item* item); + QMenu* createAddPopupMenu(); + void cut(); + void copy(); + void paste(); + void deleteSelectedLines(); + + const QList& getOutputDevices() const; + const QList& getInputDevices() const; + DeviceAPOInfo* getSelectedDevice() const; + int getSelectedChannelMask() const; + + const QSet& getSelectedItems() const; + Item* getFocusedItem() const; + + QString getConfigPath() const; + void setConfigPath(const QString& value); + + void openConfig(QString path); + +signals: + void linesChanged(); + +public slots: + void updateModel(); + void updateChannels(); + void addActionTriggered(); + +protected: + void mousePressEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void dragEnterEvent(QDragEnterEvent *event) override; + void dragMoveEvent(QDragMoveEvent *event) override; + void dragLeaveEvent(QDragLeaveEvent* event) override; + void dropEvent(QDropEvent *event) override; + void keyPressEvent(QKeyEvent* event) override; + void wheelEvent(QWheelEvent* event) override; + bool eventFilter(QObject* obj, QEvent* event) override; + +private: + void ensureRowVisible(int row); + int rowForPos(QPoint pos, bool insert); + QRectF rowRect(int row); + void disableWheelForWidgets(); + + MainWindow* mainWindow; + QScrollArea* scrollArea; + QGridLayout* gridLayout; + QLabel* insertArrow; + QPoint dragStartPos; + bool internalDrag = false; + QList items; + QSet selected; + Item* focused = NULL; + Item* selectionStart = NULL; + QList factories; + QList guis; + bool scrollingNow = false; + QPoint scrollStartPoint; + QList outputDevices; + QList inputDevices; + DeviceAPOInfo* selectedDevice; + int selectedChannelMask; + QString configPath; +}; + +template inline uint qHash(QList list) +{ + uint hashValue = 0; + + for(T t : list) + hashValue = 31 * hashValue + qHash(t); + + return hashValue; +} + +Q_DECLARE_METATYPE(FilterTable::Item*) diff --git a/Editor/FilterTableRow.cpp b/Editor/FilterTableRow.cpp new file mode 100644 index 0000000..4b38527 --- /dev/null +++ b/Editor/FilterTableRow.cpp @@ -0,0 +1,207 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include +#include +#include + +#include "FilterTableRow.h" +#include "ui_FilterTableRow.h" + +FilterTableRow::FilterTableRow(FilterTable* table, int number, FilterTable::Item* item, IFilterGUI* gui) : + QWidget(table), + ui(new Ui::FilterTableRow) +{ + ui->setupUi(this); + + this->table = table; + this->item = item; + this->gui = gui; + + ui->labelNumber->setText(QString("%0").arg(number)); + + ui->toolBar->addAction(ui->actionAdd); + ui->toolBar->addAction(ui->actionRemove); + ui->toolBar->addAction(ui->actionEditText); + ui->toolBar->updateMaximumHeight(); + + ui->stackedWidget->setContentsMargins(0, 5, 0, 5); + + if(gui != NULL) + { + connect(gui, SIGNAL(updateModel()), this, SLOT(updateModel())); + ui->stackedWidget->addWidget(gui); + } + else + { + ui->stackedWidget->addWidget(new QLabel(item->text)); + } + ui->stackedWidget->setCurrentIndex(1); +} + +FilterTableRow::~FilterTableRow() +{ + delete ui; +} + +QRect FilterTableRow::getHeaderRect() +{ + QRect rect(0, 0, ui->labelNumber->geometry().right() + 1, height()); + + return rect; +} + +void FilterTableRow::editText() +{ + if(!ui->actionEditText->isChecked()) + ui->actionEditText->trigger(); +} + +void FilterTableRow::mouseDoubleClickEvent(QMouseEvent*) +{ + if(gui == NULL && ui->stackedWidget->currentIndex() == 1) + ui->actionEditText->trigger(); +} + +void FilterTableRow::paintEvent(QPaintEvent*) +{ + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + QRectF r = rect(); + r = r.marginsAdded(QMarginsF(-1.5, -1.5, -1.5, -0.5)); + + QColor color; + if(table->getSelectedItems().contains(item)) + { + if(table->getFocusedItem() == item) + color = QColor(44, 111, 222); + else + color = QColor(64, 136, 255); + } + else + { + if(table->getFocusedItem() == item) + color = QColor(160, 160, 160); + else + color = QColor(180, 180, 180); + } + painter.setPen(color); + + QLinearGradient gradient(r.topLeft(), r.bottomLeft()); + gradient.setColorAt(0, QColor(255, 255, 255)); + gradient.setColorAt(1, QColor(230, 230, 230)); + painter.setBrush(gradient); + painter.drawRoundedRect(r, 5, 5); + + QRectF r2 = r; + r2.setRight(ui->labelNumber->geometry().right() - 0.5); + + QLinearGradient gradient2(r2.topLeft(), r2.bottomLeft()); + gradient2.setColorAt(0, color.darker(110)); + gradient2.setColorAt(1, color.darker(150)); + painter.setBrush(gradient2); + painter.drawRoundedRect(r2, 5, 5); +} + +void FilterTableRow::updateModel() +{ + IFilterGUI* sender = qobject_cast(QObject::sender()); + QString command; + QString parameters; + sender->store(command, parameters); + + item->text = command + ": " + parameters; + + qDebug("Updated item text to %s", item->text.toStdString().c_str()); +} + +void FilterTableRow::on_actionAdd_triggered() +{ + QMenu* menu = table->createAddPopupMenu(); + QRect rect = ui->toolBar->actionGeometry(ui->actionAdd); + QPoint p = ui->toolBar->mapToGlobal(QPoint(rect.x(), rect.y() + rect.height())); + QAction* action = menu->exec(p); + ui->actionAdd->setChecked(false); + if(action != NULL) + { + FilterTemplate t = action->data().value(); + QString line = t.getLine(); + table->addLine(line, item); + table->updateGuis(); + } +} + +void FilterTableRow::on_actionRemove_triggered() +{ + table->removeItem(item); + table->updateGuis(); +} + +void FilterTableRow::on_actionEditText_triggered(bool checked) +{ + if(checked) + { + if(!lastEditTime.isValid() || lastEditTime.elapsed() > 100) + { + ui->lineEdit->setText(item->text); + ui->stackedWidget->setCurrentIndex(0); + ui->lineEdit->setFocus(); + } + else + { + ui->actionEditText->setChecked(false); + } + } +} + +void FilterTableRow::on_lineEdit_editingFinished() +{ + if(ui->stackedWidget->currentIndex() == 0 && !editingDone) + { + if(ui->lineEdit->text() != item->text) + { + editingDone = true; + item->text = ui->lineEdit->text(); + // set focus to table so that enter key does not cause scrolling down + table->setFocus(); + table->updateGuis(); + } + else + { + ui->stackedWidget->setCurrentIndex(1); + lastEditTime.start(); + ui->actionEditText->setChecked(false); + } + } +} + +void FilterTableRow::on_lineEdit_editingCanceled() +{ + if(ui->stackedWidget->currentIndex() == 0) + { + // will cause editingFinished to be called, so prevent committing + editingDone = true; + table->setFocus(); + ui->stackedWidget->setCurrentIndex(1); + editingDone = false; + ui->actionEditText->setChecked(false); + } +} diff --git a/Editor/FilterTableRow.h b/Editor/FilterTableRow.h new file mode 100644 index 0000000..93fff79 --- /dev/null +++ b/Editor/FilterTableRow.h @@ -0,0 +1,63 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include +#include + +#include "FilterTable.h" + +namespace Ui { +class FilterTableRow; +} + +class FilterTableRow : public QWidget +{ + Q_OBJECT + +public: + explicit FilterTableRow(FilterTable* table, int index, FilterTable::Item* item, IFilterGUI* gui); + ~FilterTableRow(); + + QRect getHeaderRect(); + void editText(); + +protected: + void mouseDoubleClickEvent(QMouseEvent*) override; + void paintEvent(QPaintEvent*) override; + +private slots: + void updateModel(); + + void on_actionAdd_triggered(); + void on_actionRemove_triggered(); + void on_actionEditText_triggered(bool checked); + + void on_lineEdit_editingFinished(); + void on_lineEdit_editingCanceled(); + +private: + Ui::FilterTableRow *ui; + FilterTable* table; + FilterTable::Item* item; + IFilterGUI* gui; + bool editingDone = false; + QTime lastEditTime; +}; diff --git a/Editor/FilterTableRow.ui b/Editor/FilterTableRow.ui new file mode 100644 index 0000000..1a93e19 --- /dev/null +++ b/Editor/FilterTableRow.ui @@ -0,0 +1,192 @@ + + + FilterTableRow + + + + 0 + 0 + 400 + 300 + + + + Form + + + + 0 + + + 0 + + + 1 + + + 6 + + + 0 + + + + + + 25 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + + + 255 + 255 + 255 + + + + + + + + + 120 + 120 + 120 + + + + + + + + + 10 + 75 + true + + + + OpenHandCursor + + + 99 + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + true + + + + :/icons/16x16/list-add-green.png:/icons/16x16/list-add-green.png + + + Add + + + + + + :/icons/16x16/list-remove-red.png:/icons/16x16/list-remove-red.png + + + Remove + + + + + true + + + + :/icons/16x16/accessories-text-editor.png:/icons/16x16/accessories-text-editor.png + + + Edit text + + + + + + CompactToolBar + QWidget +
Editor/widgets/CompactToolBar.h
+ 1 +
+ + EscapableLineEdit + QLineEdit +
Editor/widgets/EscapableLineEdit.h
+ + editingCanceled() + +
+
+ + + + +
diff --git a/Editor/FilterTemplate.cpp b/Editor/FilterTemplate.cpp new file mode 100644 index 0000000..9799b9c --- /dev/null +++ b/Editor/FilterTemplate.cpp @@ -0,0 +1,55 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "FilterTemplate.h" + +FilterTemplate::FilterTemplate(QString name, QString line, QStringList path) + : name(name), line(line), path(path) +{ +} + +QString FilterTemplate::getName() const +{ + return name; +} + +void FilterTemplate::setName(const QString& value) +{ + name = value; +} + +QString FilterTemplate::getLine() const +{ + return line; +} + +void FilterTemplate::setLine(const QString& value) +{ + line = value; +} + +QStringList FilterTemplate::getPath() const +{ + return path; +} + +void FilterTemplate::setPath(const QStringList& value) +{ + path = value; +} diff --git a/Editor/FilterTemplate.h b/Editor/FilterTemplate.h new file mode 100644 index 0000000..35f8ed3 --- /dev/null +++ b/Editor/FilterTemplate.h @@ -0,0 +1,43 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +class FilterTemplate +{ +public: + FilterTemplate() {} + FilterTemplate(QString name, QString line, QStringList path); + + QString getName() const; + void setName(const QString& value); + + virtual QString getLine() const; + void setLine(const QString& value); + + QStringList getPath() const; + void setPath(const QStringList& value); + +private: + QString name; + QString line; + QStringList path; +}; + +Q_DECLARE_METATYPE(FilterTemplate) diff --git a/Editor/IFilterGUI.cpp b/Editor/IFilterGUI.cpp new file mode 100644 index 0000000..487bfeb --- /dev/null +++ b/Editor/IFilterGUI.cpp @@ -0,0 +1,31 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2014 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "IFilterGUI.h" + +IFilterGUI::IFilterGUI(QWidget* parent) : + QWidget(parent) +{ + +} + +IFilterGUI::~IFilterGUI() +{ + +} diff --git a/Editor/IFilterGUI.h b/Editor/IFilterGUI.h new file mode 100644 index 0000000..743cbfe --- /dev/null +++ b/Editor/IFilterGUI.h @@ -0,0 +1,41 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include +#include +#include + +class IFilterGUI : public QWidget +{ + Q_OBJECT + +public: + IFilterGUI(QWidget* parent = 0); + virtual ~IFilterGUI(); + + virtual void configureChannels(std::vector& channelNames) {} + + virtual void store(QString& command, QString& parameters) = 0; + +signals: + void updateModel(); + void updateChannels(); +}; diff --git a/Editor/IFilterGUIFactory.cpp b/Editor/IFilterGUIFactory.cpp new file mode 100644 index 0000000..eb9b5d8 --- /dev/null +++ b/Editor/IFilterGUIFactory.cpp @@ -0,0 +1,25 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "IFilterGUIFactory.h" + +IFilterGUIFactory::~IFilterGUIFactory() +{ + +} diff --git a/Editor/IFilterGUIFactory.h b/Editor/IFilterGUIFactory.h new file mode 100644 index 0000000..e704db4 --- /dev/null +++ b/Editor/IFilterGUIFactory.h @@ -0,0 +1,48 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "FilterTemplate.h" +#include "IFilterGUI.h" + +class FilterTable; + +class IFilterGUIFactory : public QObject +{ + Q_OBJECT + +public: + virtual ~IFilterGUIFactory(); + + virtual void initialize(FilterTable* filterTable) {} + + virtual QList createFilterTemplates() + { + return QList(); + } + + virtual void startOfFile(const QString& configPath) {} + virtual IFilterGUI* createFilterGUI(QString& command, QString& parameters) = 0; + virtual IFilterGUI* decorateFilterGUI(IFilterGUI* gui) + { + return gui; + } + virtual void endOfFile(const QString& configPath) {} +}; diff --git a/Editor/MainWindow.cpp b/Editor/MainWindow.cpp new file mode 100644 index 0000000..4dbc945 --- /dev/null +++ b/Editor/MainWindow.cpp @@ -0,0 +1,632 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include +#include + +#include "helpers/StringHelper.h" +#include "helpers/LogHelper.h" +#include "helpers/ChannelHelper.h" +#include "Editor/helpers/GUIChannelHelper.h" +#include "version.h" +#include "FilterTable.h" +#include "MainWindow.h" +#include "ui_MainWindow.h" + +using namespace std; + +MainWindow::MainWindow(QDir configDir, QWidget *parent) : + QMainWindow(parent), ui(new Ui::MainWindow), configDir(configDir) +{ + outputDevices = QList::fromVector(QVector::fromStdVector(DeviceAPOInfo::loadAllInfos(false))); + inputDevices = QList::fromVector(QVector::fromStdVector(DeviceAPOInfo::loadAllInfos(true))); + + defaultOutputDevice = NULL; + for(DeviceAPOInfo& apoInfo : outputDevices) + { + if(apoInfo.isDefaultDevice) + { + defaultOutputDevice = &apoInfo; + break; + } + } + + ui->setupUi(this); + + QString version = QString("%0.%1").arg(MAJOR).arg(MINOR); + if(REVISION != 0) + version += QString(".%0").arg(REVISION); + setWindowTitle(tr("Equalizer APO %0 Configuration Editor").arg(version)); + + QWidget* spacer = new QWidget; + spacer->setFixedWidth(10); + ui->mainToolBar->addWidget(spacer); + + instantModeCheckBox = new QCheckBox(tr("Instant mode")); + instantModeCheckBox->setChecked(true); + instantModeCheckBox->setToolTip(tr("Changes are saved immediately")); + connect(instantModeCheckBox, SIGNAL(toggled(bool)), this, SLOT(instantModeEnabled(bool))); + ui->mainToolBar->addWidget(instantModeCheckBox); + + spacer = new QWidget; + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + ui->mainToolBar->addWidget(spacer); + + ui->mainToolBar->addWidget(new QLabel(tr("Device: "))); + + deviceComboBox = new QComboBox; + connect(deviceComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(deviceSelected(int))); + ui->mainToolBar->addWidget(deviceComboBox); + + spacer = new QWidget; + spacer->setFixedWidth(10); + ui->mainToolBar->addWidget(spacer); + + ui->mainToolBar->addWidget(new QLabel(tr("Channel configuration: "))); + + channelConfigurationComboBox = new QComboBox; + channelConfigurationComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents); + ui->mainToolBar->addWidget(channelConfigurationComboBox); + + QStandardItemModel* model = qobject_cast(deviceComboBox->model()); + if(defaultOutputDevice != NULL) + deviceComboBox->addItem(tr("Default") + " (" + QString::fromStdWString(defaultOutputDevice->connectionName) + " - " + QString::fromStdWString(defaultOutputDevice->deviceName) + ")", NULL); + + deviceComboBox->addItem(tr("Playback devices:")); + QStandardItem* item = model->item(model->rowCount() - 1); + QFont font = item->font(); + font.setBold(true); + item->setFont(font); + item->setSelectable(false); + + for(DeviceAPOInfo& apoInfo : outputDevices) + if(apoInfo.isInstalled) + deviceComboBox->addItem(QString::fromStdWString(apoInfo.connectionName) + " - " + QString::fromStdWString(apoInfo.deviceName), QVariant::fromValue(&apoInfo)); + + deviceComboBox->addItem(tr("Capture devices:")); + item = model->item(model->rowCount() - 1); + item->setFont(font); + item->setSelectable(false); + + for(DeviceAPOInfo& apoInfo : inputDevices) + if(apoInfo.isInstalled) + deviceComboBox->addItem(QString::fromStdWString(apoInfo.connectionName) + " - " + QString::fromStdWString(apoInfo.deviceName), QVariant::fromValue(&apoInfo)); + + connect(channelConfigurationComboBox, SIGNAL(activated(int)), this, SLOT(channelConfigurationSelected(int))); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::doChecks() +{ + if(!DeviceAPOInfo::checkProtectedAudioDG(false)) + { + if(QMessageBox::warning(this, tr("Registry problem"), tr("A registry value that is required for the operation of Equalizer APO is not set correctly.\nDo you want to run the Configurator application to fix the problem?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) + { + QProcess::startDetached(QCoreApplication::applicationDirPath() + "/Configurator.exe", QStringList()); + return; + } + } + + if(!defaultOutputDevice->isInstalled) + { + if(QMessageBox::warning(this, tr("APO not installed to device"), tr("Equalizer APO has not been installed to the selected device.\nDo you want to run the Configurator application to fix the problem?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) + { + QProcess::startDetached(QCoreApplication::applicationDirPath() + "/Configurator.exe", QStringList()); + return; + } + } + + DeviceAPOInfo* disabledApoInfo = NULL; + for(DeviceAPOInfo& apoInfo : outputDevices) + { + if(apoInfo.isInstalled && apoInfo.isEnhancementsDisabled) + { + disabledApoInfo = &apoInfo; + break; + } + } + + if(disabledApoInfo == NULL) + { + for(DeviceAPOInfo& apoInfo : inputDevices) + { + if(apoInfo.isInstalled && apoInfo.isEnhancementsDisabled) + { + disabledApoInfo = &apoInfo; + break; + } + } + } + + if(disabledApoInfo != NULL) + { + if(QMessageBox::warning(this, tr("Audio enhancements disabled"), tr("Audio enhancements are not enabled for the device\n%0 %1.\nDo you want to run the Configurator application to fix the problem?").arg(QString::fromStdWString(disabledApoInfo->connectionName)).arg(QString::fromStdWString(disabledApoInfo->deviceName)), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) + { + QProcess::startDetached(QCoreApplication::applicationDirPath() + "/Configurator.exe", QStringList()); + return; + } + } +} + +void MainWindow::load(QString path) +{ + for(int i = 0; i < ui->tabWidget->count(); i++) + { + QScrollArea* scrollArea = qobject_cast(ui->tabWidget->widget(i)); + FilterTable* filterTable = qobject_cast(scrollArea->widget()); + + if(filterTable->getConfigPath() == path) + { + ui->tabWidget->setCurrentIndex(i); + return; + } + } + + QElapsedTimer timer; + timer.start(); + + HANDLE hFile = INVALID_HANDLE_VALUE; + while(hFile == INVALID_HANDLE_VALUE) + { + hFile = CreateFile(path.toStdWString().c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if(hFile == INVALID_HANDLE_VALUE) + { + DWORD error = GetLastError(); + if(error != ERROR_SHARING_VIOLATION) + { + QMessageBox::critical(this, tr("Error"), tr("Error while reading configuration file: %0").arg(QString::fromStdWString(StringHelper::getSystemErrorString(error)))); + return; + } + + // file is being written, so wait + Sleep(1); + } + } + + stringstream inputStream; + + char buf[8192]; + unsigned long bytesRead = -1; + while(ReadFile(hFile, buf, sizeof(buf), &bytesRead, NULL) && bytesRead != 0) + { + inputStream.write(buf, bytesRead); + } + + CloseHandle(hFile); + + inputStream.seekg(0); + + QList lines; + while(inputStream.good()) + { + string encodedLine; + getline(inputStream, encodedLine); + if(encodedLine.size() > 0 && encodedLine[encodedLine.size() - 1] == '\r') + encodedLine.resize(encodedLine.size() - 1); + + wstring line = StringHelper::toWString(encodedLine, CP_UTF8); + if(line.find(L'\uFFFD') != -1) + line = StringHelper::toWString(encodedLine, CP_ACP); + + lines.append(QString::fromStdWString(line)); + } + + QFileInfo fileInfo(path); + FilterTable* filterTable = addTab(fileInfo.fileName(), QDir::toNativeSeparators(fileInfo.absoluteFilePath())); + + filterTable->setLines(path, lines); + + connect(filterTable, SIGNAL(linesChanged()), this, SLOT(linesChanged())); + + qDebug("Loading took %.1f ms", timer.nsecsElapsed() / 1e6); + + ui->tabWidget->setCurrentIndex(ui->tabWidget->count() - 1); +} + +void MainWindow::save(FilterTable* filterTable, QString path) +{ + QElapsedTimer timer; + timer.start(); + + QList lines = filterTable->getLines(); + + bool first = true; + QByteArray byteArray; + for(QString line : lines) + { + if(first) + first = false; + else + byteArray.append("\r\n"); + byteArray.append(line.toUtf8()); + } + + HANDLE hFile = INVALID_HANDLE_VALUE; + while(hFile == INVALID_HANDLE_VALUE) + { + hFile = CreateFile(path.toStdWString().c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if(hFile == INVALID_HANDLE_VALUE) + { + DWORD error = GetLastError(); + if(error != ERROR_SHARING_VIOLATION) + { + QMessageBox::critical(this, tr("Error"), tr("Error while writing configuration file: %0").arg(QString::fromStdWString(StringHelper::getSystemErrorString(error)))); + return; + } + + // file is being written, so wait + Sleep(1); + } + } + + unsigned long bytesWritten; + WriteFile(hFile, byteArray.constData(), byteArray.length(), &bytesWritten, NULL); + if(bytesWritten != byteArray.length()) + { + // should never happen + QMessageBox::critical(this, tr("Error"), tr("Only %0/%1 bytes have been written!").arg(bytesWritten).arg(byteArray.length())); + } + + CloseHandle(hFile); + + qDebug("Saving took %.1f ms", timer.nsecsElapsed() / 1e6); +} + +void MainWindow::closeEvent(QCloseEvent* event) +{ + while(ui->tabWidget->count() > 0) + { + ui->tabWidget->setCurrentIndex(0); + + if(!on_tabWidget_tabCloseRequested(0)) + { + event->ignore(); + break; + } + } +} + +void MainWindow::deviceSelected(int index) +{ + DeviceAPOInfo* apoInfo = deviceComboBox->itemData(index).value(); + if(apoInfo == NULL) + apoInfo = defaultOutputDevice; + + channelConfigurationComboBox->clear(); + + const QList& infos = GUIChannelHelper::getInstance()->getChannelConfigurationInfos(); + + if(apoInfo != NULL) + { + const GUIChannelHelper::ChannelConfigurationInfo* selectedInfo = NULL; + for(const GUIChannelHelper::ChannelConfigurationInfo& info : infos) + { + if(info.channelMask == apoInfo->channelMask) + { + selectedInfo = &info; + break; + } + } + + if(selectedInfo != NULL) + channelConfigurationComboBox->addItem(tr("From device") + " (" + selectedInfo->name + ")", 0); + else + channelConfigurationComboBox->addItem(tr("From device") + " (" + apoInfo->channelCount + " channels)", 0); + } + + for(const GUIChannelHelper::ChannelConfigurationInfo& info : infos) + channelConfigurationComboBox->addItem(info.name, info.channelMask); + + channelConfigurationSelected(channelConfigurationComboBox->currentIndex()); +} + +void MainWindow::channelConfigurationSelected(int index) +{ + DeviceAPOInfo* selectedDevice; + int channelMask; + getDeviceAndChannelMask(&selectedDevice, &channelMask); + + for(int i = 0; i < ui->tabWidget->count(); i++) + { + QScrollArea* scrollArea = qobject_cast(ui->tabWidget->widget(i)); + FilterTable* filterTable = qobject_cast(scrollArea->widget()); + filterTable->updateDeviceAndChannelMask(selectedDevice, channelMask); + } +} + +void MainWindow::linesChanged() +{ + FilterTable* filterTable = qobject_cast(sender()); + + if(instantModeCheckBox->isChecked()) + { + QString configPath = filterTable->getConfigPath(); + if(configPath.length() > 0) + { + save(filterTable, configPath); + return; + } + } + + int tabIndex = -1; + for(int i = 0; i < ui->tabWidget->count(); i++) + { + QScrollArea* scrollArea = qobject_cast(ui->tabWidget->widget(i)); + if(scrollArea->widget() == filterTable) + { + tabIndex = i; + break; + } + } + QString tabText = ui->tabWidget->tabText(tabIndex); + if(!tabText.endsWith('*')) + { + tabText += '*'; + ui->tabWidget->setTabText(tabIndex, tabText); + } +} + +void MainWindow::getDeviceAndChannelMask(DeviceAPOInfo** selectedDevice, int* channelMask) +{ + *selectedDevice = deviceComboBox->currentData().value(); + if(*selectedDevice == NULL) + *selectedDevice = defaultOutputDevice; + + *channelMask = channelConfigurationComboBox->currentData().toInt(); + if(*channelMask == 0 && selectedDevice != NULL) + { + *channelMask = (*selectedDevice)->channelMask; + + if(*channelMask == 0) + *channelMask = ChannelHelper::getDefaultChannelMask((*selectedDevice)->channelCount); + } +} + +bool MainWindow::on_tabWidget_tabCloseRequested(int index) +{ + if(ui->tabWidget->tabText(index).endsWith('*')) + { + ui->tabWidget->setCurrentIndex(index); + QString configPath = ui->tabWidget->tabToolTip(index); + QMessageBox messageBox; + messageBox.setWindowTitle(tr("Unsaved changes")); + messageBox.setText(tr("The configuration file %0 has unsaved changes.").arg(configPath)); + messageBox.setInformativeText(tr("Do you want to save the changes before closing the file?")); + messageBox.setIcon(QMessageBox::Question); + messageBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + messageBox.setDefaultButton(QMessageBox::Save); + messageBox.setEscapeButton(QMessageBox::Cancel); + messageBox.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint); + int result = messageBox.exec(); + + switch(result) + { + case QMessageBox::Save: + ui->actionSave->trigger(); + if(ui->tabWidget->tabText(index).endsWith('*')) + // saving was canceled + return false; + break; + case QMessageBox::Discard: + break; + case QMessageBox::Cancel: + return false; + } + } + + ui->tabWidget->removeTab(index); + return true; +} + +void MainWindow::on_actionOpen_triggered() +{ + QScrollArea* scrollArea = qobject_cast(ui->tabWidget->currentWidget()); + QString path; + if(scrollArea != NULL) + { + FilterTable* filterTable = qobject_cast(scrollArea->widget()); + if(filterTable->getConfigPath().length() > 0) + { + QFileInfo fileInfo(filterTable->getConfigPath()); + path = fileInfo.absolutePath(); + } + } + if(path.length() == 0) + path = configDir.absolutePath(); + + QFileDialog dialog(this, tr("Open file"), path, "*.txt"); + dialog.setFileMode(QFileDialog::ExistingFiles); + dialog.setNameFilter(tr("E-APO configurations (*.txt)")); + + if(dialog.exec() == QDialog::Accepted) + { + for(QString file : dialog.selectedFiles()) + load(file); + } +} + +void MainWindow::on_actionSave_triggered() +{ + QScrollArea* scrollArea = qobject_cast(ui->tabWidget->currentWidget()); + if(scrollArea == NULL) + return; + + FilterTable* filterTable = qobject_cast(scrollArea->widget()); + + if(filterTable->getConfigPath().length() == 0) + { + ui->actionSaveAs->trigger(); + } + else + { + save(filterTable, filterTable->getConfigPath()); + + QString tabText = ui->tabWidget->tabText(ui->tabWidget->currentIndex()); + if(tabText.endsWith('*')) + ui->tabWidget->setTabText(ui->tabWidget->currentIndex(), tabText.left(tabText.length() - 1)); + } +} + +void MainWindow::on_actionSaveAs_triggered() +{ + QScrollArea* scrollArea = qobject_cast(ui->tabWidget->currentWidget()); + if(scrollArea == NULL) + return; + + FilterTable* filterTable = qobject_cast(scrollArea->widget()); + QString path; + QString filename; + if(filterTable->getConfigPath().length() == 0) + { + path = configDir.absolutePath(); + } + else + { + QFileInfo fileInfo(filterTable->getConfigPath()); + path = fileInfo.absolutePath(); + filename = fileInfo.fileName(); + } + QFileDialog dialog(this, tr("Save file as"), path, "*.txt"); + dialog.setFileMode(QFileDialog::AnyFile); + dialog.setAcceptMode(QFileDialog::AcceptSave); + dialog.setNameFilter(tr("E-APO configurations (*.txt)")); + dialog.setDefaultSuffix(".txt"); + if(filename.length() > 0) + dialog.selectFile(filename); + + if(dialog.exec() == QDialog::Accepted) + { + QString savePath = dialog.selectedFiles().first(); + save(filterTable, savePath); + filterTable->setConfigPath(savePath); + + QFileInfo fileInfo(savePath); + ui->tabWidget->setTabText(ui->tabWidget->currentIndex(), fileInfo.fileName()); + ui->tabWidget->setTabToolTip(ui->tabWidget->currentIndex(), QDir::toNativeSeparators(fileInfo.absoluteFilePath())); + } +} + +void MainWindow::on_actionNew_triggered() +{ + FilterTable* filterTable = addTab(tr("Unsaved"), ""); + filterTable->setLines("", QList()); + + connect(filterTable, SIGNAL(linesChanged()), this, SLOT(linesChanged())); + ui->tabWidget->setCurrentIndex(ui->tabWidget->count() - 1); +} + +FilterTable* MainWindow::addTab(QString title, QString tooltip) +{ + QScrollArea* scrollArea = new QScrollArea(ui->tabWidget); + scrollArea->setWidgetResizable(true); + FilterTable* filterTable = new FilterTable(this); + scrollArea->setWidget(filterTable); + filterTable->setAcceptDrops(true); + filterTable->setFocusPolicy(Qt::WheelFocus); + + int tabIndex = ui->tabWidget->addTab(scrollArea, title); + ui->tabWidget->setTabToolTip(tabIndex, tooltip); + + DeviceAPOInfo* selectedDevice; + int channelMask; + getDeviceAndChannelMask(&selectedDevice, &channelMask); + filterTable->updateDeviceAndChannelMask(selectedDevice, channelMask); + filterTable->initialize(scrollArea, outputDevices, inputDevices); + + return filterTable; +} + +void MainWindow::on_actionCut_triggered() +{ + QScrollArea* scrollArea = qobject_cast(ui->tabWidget->currentWidget()); + if(scrollArea == NULL) + return; + + FilterTable* filterTable = qobject_cast(scrollArea->widget()); + filterTable->cut(); +} + +void MainWindow::on_actionCopy_triggered() +{ + QScrollArea* scrollArea = qobject_cast(ui->tabWidget->currentWidget()); + if(scrollArea == NULL) + return; + + FilterTable* filterTable = qobject_cast(scrollArea->widget()); + filterTable->copy(); +} + +void MainWindow::on_actionPaste_triggered() +{ + QScrollArea* scrollArea = qobject_cast(ui->tabWidget->currentWidget()); + if(scrollArea == NULL) + return; + + FilterTable* filterTable = qobject_cast(scrollArea->widget()); + filterTable->paste(); +} + +void MainWindow::on_actionDelete_triggered() +{ + QScrollArea* scrollArea = qobject_cast(ui->tabWidget->currentWidget()); + if(scrollArea == NULL) + return; + + FilterTable* filterTable = qobject_cast(scrollArea->widget()); + filterTable->deleteSelectedLines(); +} + +void MainWindow::instantModeEnabled(bool enabled) +{ + if(enabled) + { + for(int i = 0; i < ui->tabWidget->count(); i++) + { + QScrollArea* scrollArea = qobject_cast(ui->tabWidget->widget(i)); + FilterTable* filterTable = qobject_cast(scrollArea->widget()); + + if(filterTable->getConfigPath().length() > 0) + { + save(filterTable, filterTable->getConfigPath()); + + QString tabText = ui->tabWidget->tabText(i); + if(tabText.endsWith('*')) + ui->tabWidget->setTabText(i, tabText.left(tabText.length() - 1)); + } + } + } +} diff --git a/Editor/MainWindow.h b/Editor/MainWindow.h new file mode 100644 index 0000000..1e7eb31 --- /dev/null +++ b/Editor/MainWindow.h @@ -0,0 +1,83 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "FilterTable.h" +#include "DeviceAPOInfo.h" + +namespace Ui { +class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QDir configDir, QWidget *parent = 0); + ~MainWindow(); + void doChecks(); + void load(QString path); + void save(FilterTable* filterTable, QString path); + +protected: + void closeEvent(QCloseEvent* event) override; + +private slots: + void deviceSelected(int index); + void channelConfigurationSelected(int index); + void linesChanged(); + + bool on_tabWidget_tabCloseRequested(int index); + void on_actionOpen_triggered(); + void on_actionSave_triggered(); + void on_actionSaveAs_triggered(); + void on_actionNew_triggered(); + + void on_actionCut_triggered(); + void on_actionDelete_triggered(); + void on_actionPaste_triggered(); + void on_actionCopy_triggered(); + + void instantModeEnabled(bool enabled); + +private: + FilterTable* addTab(QString title, QString tooltip); + void getDeviceAndChannelMask(DeviceAPOInfo** selectedDevice, int* channelMask); + + Ui::MainWindow *ui; + + QDir configDir; + QCheckBox* instantModeCheckBox; + QComboBox* deviceComboBox; + QComboBox* channelConfigurationComboBox; + QList outputDevices; + QList inputDevices; + DeviceAPOInfo* defaultOutputDevice; +}; + +Q_DECLARE_METATYPE(DeviceAPOInfo*) diff --git a/Editor/MainWindow.ui b/Editor/MainWindow.ui new file mode 100644 index 0000000..814ea5c --- /dev/null +++ b/Editor/MainWindow.ui @@ -0,0 +1,218 @@ + + + MainWindow + + + + 0 + 0 + 1024 + 768 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + true + + + true + + + + + + + + + 0 + 0 + 1024 + 21 + + + + + File + + + + + + + + + + + Edit + + + + + + + + + + + + TopToolBarArea + + + false + + + + + + + + + :/icons/16x16/document-new.png:/icons/16x16/document-new.png + + + New + + + Ctrl+N + + + + + + :/icons/16x16/document-open.png:/icons/16x16/document-open.png + + + Open + + + Open + + + Ctrl+O + + + + + + :/icons/16x16/document-save.png:/icons/16x16/document-save.png + + + Save + + + Save + + + Ctrl+S + + + + + + :/icons/16x16/document-save-as.png:/icons/16x16/document-save-as.png + + + Save as... + + + Save as... + + + + + Exit + + + Alt+F4 + + + + + + :/icons/16x16/edit-cut.png:/icons/16x16/edit-cut.png + + + Cut + + + Ctrl+X + + + + + + :/icons/16x16/edit-copy.png:/icons/16x16/edit-copy.png + + + Copy + + + Ctrl+C + + + + + + :/icons/16x16/edit-paste.png:/icons/16x16/edit-paste.png + + + Paste + + + Ctrl+V + + + + + + :/icons/16x16/edit-delete.png:/icons/16x16/edit-delete.png + + + Delete + + + + + + + + + + actionExit + triggered() + MainWindow + close() + + + -1 + -1 + + + 511 + 383 + + + + + diff --git a/Editor/guis/BiQuadFilterGUI.cpp b/Editor/guis/BiQuadFilterGUI.cpp new file mode 100644 index 0000000..d198550 --- /dev/null +++ b/Editor/guis/BiQuadFilterGUI.cpp @@ -0,0 +1,338 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#define _USE_MATH_DEFINES +#include +#include "BiQuadFilterGUI.h" +#include "ui_BiQuadFilterGUI.h" +#include + +static const double dialFreqSteps = 1000; +static const double dialFreqMin = 20; +static const double dialFreqMax = 20000; +static const double dialQSteps = 1000; +static const double dialQMin = 0.3333; +static const double dialQMax = 33.3333; + +BiQuadFilterGUI::BiQuadFilterGUI(BiQuadFilter* filter) : + ui(new Ui::BiQuadFilterGUI) +{ + ui->setupUi(this); + + ui->typeComboBox->addItem(tr("Peaking filter"), BiQuad::PEAKING); + ui->typeComboBox->addItem(tr("Low-pass filter"), BiQuad::LOW_PASS); + ui->typeComboBox->addItem(tr("High-pass filter"), BiQuad::HIGH_PASS); + ui->typeComboBox->addItem(tr("Band-pass filter"), BiQuad::BAND_PASS); + ui->typeComboBox->addItem(tr("Low-shelf filter"), BiQuad::LOW_SHELF); + ui->typeComboBox->addItem(tr("High-shelf filter"), BiQuad::HIGH_SHELF); + ui->typeComboBox->addItem(tr("Notch filter"), BiQuad::NOTCH); + ui->typeComboBox->addItem(tr("All-pass filter"), BiQuad::ALL_PASS); + + BiQuad::Type type = filter->getType(); + int typeIndex = ui->typeComboBox->findData(type); + if(typeIndex != -1) + ui->typeComboBox->setCurrentIndex(typeIndex); + + if(type == BiQuad::LOW_SHELF || type == BiQuad::HIGH_SHELF ) + ui->freqComboBox->setCurrentIndex(filter->getIsCornerFreq() ? 1 : 0); + + ui->freqSpinBox->setValue(filter->getFreq()); + + if(type == BiQuad::PEAKING ) + ui->qComboBox->setCurrentIndex(filter->getIsBandwidth() ? 1 : 0); + else if(type == BiQuad::LOW_PASS || type == BiQuad::HIGH_PASS || type == BiQuad::BAND_PASS) + ui->qComboBox->setCurrentIndex(filter->getBandwidthOrQOrS() == M_SQRT1_2 ? 0 : 1); + else if((type == BiQuad::LOW_SHELF || type == BiQuad::HIGH_SHELF) && !filter->getIsCornerFreq()) + ui->qComboBox->setCurrentIndex(filter->getBandwidthOrQOrS() == 0.9 ? 0 : 1); + + if((type == BiQuad::LOW_SHELF || type == BiQuad::HIGH_SHELF) && filter->getBandwidthOrQOrS() != 0.9) + ui->qSpinBox->setValue(filter->getBandwidthOrQOrS() * 12.0); + else + ui->qSpinBox->setValue(filter->getBandwidthOrQOrS()); + + ui->gainSpinBox->setValue(filter->getDbGain()); +} + +BiQuadFilterGUI::~BiQuadFilterGUI() +{ + delete ui; +} + +void BiQuadFilterGUI::store(QString& command, QString& parameters) +{ + BiQuad::Type type = (BiQuad::Type)ui->typeComboBox->currentData().toInt(); + QChar freqMode = ui->freqComboBox->currentData().toChar(); + QChar mode = ui->qComboBox->currentData().toChar(); + + command = "Filter"; + parameters = "ON "; + switch(type) + { + case BiQuad::PEAKING: + parameters += "PK"; + break; + case BiQuad::LOW_PASS: + if(mode == 'F') + parameters += "LP"; + else + parameters += "LPQ"; + break; + case BiQuad::HIGH_PASS: + if(mode == 'F') + parameters += "HP"; + else + parameters += "HPQ"; + break; + case BiQuad::BAND_PASS: + parameters += "BP"; + break; + case BiQuad::LOW_SHELF: + if(freqMode == 'E' && mode == 'S') + parameters += "LSC"; + else + parameters += "LS"; + break; + case BiQuad::HIGH_SHELF: + if(freqMode == 'E' && mode == 'S') + parameters += "HSC"; + else + parameters += "HS"; + break; + case BiQuad::NOTCH: + parameters += "NO"; + break; + case BiQuad::ALL_PASS: + parameters += "AP"; + break; + } + + if((type == BiQuad::LOW_SHELF || type == BiQuad::HIGH_SHELF) && mode == 'S') + parameters += QString(" %1 dB").arg(ui->qSpinBox->value()); + + parameters += QString(" Fc %1 Hz").arg(ui->freqSpinBox->value()); + + if(type == BiQuad::PEAKING || type == BiQuad::LOW_SHELF + || type == BiQuad::HIGH_SHELF) + parameters += QString(" Gain %1 dB").arg(ui->gainSpinBox->value()); + + if(mode == 'Q') + parameters += QString(" Q %1").arg(ui->qSpinBox->value()); + else if(mode == 'B') + parameters += QString(" BW Oct %1").arg(ui->qSpinBox->value()); +} + +void BiQuadFilterGUI::on_freqDial_valueChanged(int value) +{ + ui->freqSpinBox->setValue(pow(dialFreqMax / dialFreqMin, value / dialFreqSteps) * dialFreqMin); +} + +void BiQuadFilterGUI::on_freqSpinBox_valueChanged(double value) +{ + bool previousValue = ui->freqDial->blockSignals(true); + ui->freqDial->setValue(round(dialFreqSteps * log(value / dialFreqMin) / log(dialFreqMax / dialFreqMin))); + ui->freqDial->blockSignals(previousValue); + + emit updateModel(); +} + +void BiQuadFilterGUI::on_qDial_valueChanged(int value) +{ + ui->qSpinBox->setValue(pow(dialQMax / dialQMin, value / dialQSteps) * dialQMin); +} + +void BiQuadFilterGUI::on_qSpinBox_valueChanged(double value) +{ + bool previousValue = ui->qDial->blockSignals(true); + ui->qDial->setValue(round(dialQSteps * log(value / dialQMin) / log(dialQMax / dialQMin))); + ui->qDial->blockSignals(previousValue); + + emit updateModel(); +} + +void BiQuadFilterGUI::on_gainDial_valueChanged(int value) +{ + ui->gainSpinBox->setValue(value * 0.1); +} + +void BiQuadFilterGUI::on_gainSpinBox_valueChanged(double value) +{ + bool previousValue = ui->gainDial->blockSignals(true); + ui->gainDial->setValue(round(value / 0.1)); + ui->gainDial->blockSignals(previousValue); + + emit updateModel(); +} + +void BiQuadFilterGUI::on_typeComboBox_currentIndexChanged(int index) +{ + BiQuad::Type type = (BiQuad::Type)ui->typeComboBox->itemData(index).toInt(); + bool sameTypeCategory = getTypeCategory(previousType) == getTypeCategory(type); + + QChar freqMode = ui->freqComboBox->currentData().toChar(); + double freqValue = ui->freqSpinBox->value(); + ui->freqComboBox->clear(); + if(type == BiQuad::PEAKING || type == BiQuad::BAND_PASS || type == BiQuad::LOW_SHELF || type == BiQuad::HIGH_SHELF || type == BiQuad::NOTCH || type == BiQuad::ALL_PASS ) + ui->freqComboBox->addItem(tr("Center frequency"), 'E'); + if(type == BiQuad::LOW_PASS || type == BiQuad::HIGH_PASS || type == BiQuad::LOW_SHELF || type == BiQuad::HIGH_SHELF) + ui->freqComboBox->addItem(tr("Corner frequency"), 'O'); + ui->freqComboBox->setEnabled(ui->freqComboBox->count() > 1); + int freqIndex = ui->freqComboBox->findData(freqMode); + if(freqIndex != -1 && sameTypeCategory) + { + ui->freqComboBox->setCurrentIndex(freqIndex); + ui->freqSpinBox->setValue(freqValue); + } + else + { + freqMode = ui->freqComboBox->currentData().toChar(); + } + + QChar mode = ui->qComboBox->currentData().toChar(); + double qValue = ui->qSpinBox->value(); + ui->qComboBox->clear(); + if(type == BiQuad::LOW_PASS || type == BiQuad::HIGH_PASS || type == BiQuad::BAND_PASS || type == BiQuad::NOTCH ) + ui->qComboBox->addItem(tr("Fixed Q"), 'F'); + else if((type == BiQuad::LOW_SHELF || type == BiQuad::HIGH_SHELF) && freqMode == 'E') + ui->qComboBox->addItem(tr("Fixed S"), 'F'); + if(type == BiQuad::PEAKING || type == BiQuad::LOW_PASS || type == BiQuad::HIGH_PASS || type == BiQuad::ALL_PASS || type == BiQuad::BAND_PASS || type == BiQuad::NOTCH) + ui->qComboBox->addItem(tr("Q factor"), 'Q'); + if(type == BiQuad::PEAKING) + ui->qComboBox->addItem(tr("Bandwidth"), 'B'); + if(type == BiQuad::LOW_SHELF || type == BiQuad::HIGH_SHELF) + ui->qComboBox->addItem(tr("Slope"), 'S'); + ui->qComboBox->setEnabled(ui->qComboBox->count() > 1); + int qIndex = ui->qComboBox->findData(mode); + if(qIndex != -1 && sameTypeCategory) + { + ui->qComboBox->setCurrentIndex(qIndex); + ui->qSpinBox->setValue(qValue); + } + else if(type == BiQuad::PEAKING || type == BiQuad::ALL_PASS) + { + ui->qSpinBox->setValue(10); + } + + bool gainVisible = type == BiQuad::PEAKING || type == BiQuad::LOW_SHELF + || type == BiQuad::HIGH_SHELF; + ui->gainDial->setVisible(gainVisible); + ui->gainLabel->setVisible(gainVisible); + ui->gainSpinBox->setVisible(gainVisible); + + previousType = type; + + emit updateModel(); +} + +void BiQuadFilterGUI::on_freqComboBox_currentIndexChanged(int index) +{ + BiQuad::Type type = (BiQuad::Type)ui->typeComboBox->currentData().toInt(); + QChar freqMode = ui->freqComboBox->itemData(index).toChar(); + if(type == BiQuad::LOW_SHELF || type == BiQuad::HIGH_SHELF) + { + QChar mode = ui->qComboBox->currentData().toChar(); + double qValue = ui->qSpinBox->value(); + ui->qComboBox->clear(); + if(freqMode == 'E') + ui->qComboBox->addItem(tr("Fixed S"), 'F'); + ui->qComboBox->addItem(tr("Slope"), 'S'); + ui->qComboBox->setEnabled(ui->qComboBox->count() > 1); + int qIndex = ui->qComboBox->findData(mode); + if(qIndex != -1) + { + ui->qComboBox->setCurrentIndex(qIndex); + ui->qSpinBox->setValue(qValue); + } + + double centerFreqFactor = pow(10.0, abs(ui->gainSpinBox->value()) / 80.0 / (ui->qSpinBox->value() / 12.0)); + double freq = ui->freqSpinBox->value(); + if((freqMode == 'E') == (type == BiQuad::LOW_SHELF) ) + freq *= centerFreqFactor; + else + freq /= centerFreqFactor; + ui->freqSpinBox->setValue(freq); + } +} + +void BiQuadFilterGUI::on_qComboBox_currentIndexChanged(int index) +{ + QChar mode = ui->qComboBox->currentData().toChar(); + if(mode == 'F' || mode == 'Q') + ui->qSpinBox->setSuffix(""); + else if(mode == 'B') + ui->qSpinBox->setSuffix(" Oct"); + else if(mode == 'S') + ui->qSpinBox->setSuffix(" dB/Oct"); + + ui->qDial->setEnabled(mode != 'F'); + ui->qSpinBox->setEnabled(mode != 'F'); + + if(mode == 'F') + { + BiQuad::Type type = (BiQuad::Type)ui->typeComboBox->currentData().toInt(); + if(type == BiQuad::LOW_PASS || type == BiQuad::HIGH_PASS || type == BiQuad::BAND_PASS) + ui->qSpinBox->setValue(M_SQRT1_2); + else if(type == BiQuad::LOW_SHELF || type == BiQuad::HIGH_SHELF) + ui->qSpinBox->setValue(0.9); + else if(type == BiQuad::NOTCH) + ui->qSpinBox->setValue(30); + } + else if(qIsBw && mode == 'Q') + { + double n = ui->qSpinBox->value(); + double p2n = pow(2.0, n); + double q = sqrt(p2n) / (p2n - 1.0); + ui->qSpinBox->setValue(q); + qIsBw = false; + } + else if(!qIsBw && mode == 'B') + { + double q = ui->qSpinBox->value(); + double n = 2.0 / M_LN2 * asinh(1.0 / (2.0 * q)); + ui->qSpinBox->setValue(n); + qIsBw = true; + } + else if(mode == 'S') + { + ui->qSpinBox->setValue(12.0); + } + + emit updateModel(); +} + +int BiQuadFilterGUI::getTypeCategory(BiQuad::Type type) +{ + switch(type) + { + case BiQuad::PEAKING: + return 0; + case BiQuad::LOW_PASS: + case BiQuad::HIGH_PASS: + case BiQuad::BAND_PASS: + return 1; + case BiQuad::LOW_SHELF: + case BiQuad::HIGH_SHELF: + return 2; + case BiQuad::NOTCH: + return 3; + case BiQuad::ALL_PASS: + return 4; + } + + return -1; +} diff --git a/Editor/guis/BiQuadFilterGUI.h b/Editor/guis/BiQuadFilterGUI.h new file mode 100644 index 0000000..e278189 --- /dev/null +++ b/Editor/guis/BiQuadFilterGUI.h @@ -0,0 +1,56 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/IFilterGUI.h" +#include + +namespace Ui +{ +class BiQuadFilterGUI; +} + +class BiQuadFilterGUI : public IFilterGUI +{ + Q_OBJECT + +public: + BiQuadFilterGUI(BiQuadFilter* filter); + ~BiQuadFilterGUI(); + void store(QString& command, QString& parameters) override; + +private slots: + void on_freqDial_valueChanged(int value); + void on_freqSpinBox_valueChanged(double value); + void on_qDial_valueChanged(int value); + void on_qSpinBox_valueChanged(double value); + void on_gainDial_valueChanged(int value); + void on_gainSpinBox_valueChanged(double value); + void on_typeComboBox_currentIndexChanged(int index); + void on_freqComboBox_currentIndexChanged(int index); + void on_qComboBox_currentIndexChanged(int index); + +private: + int getTypeCategory(BiQuad::Type type); + + Ui::BiQuadFilterGUI* ui; + BiQuad::Type previousType = BiQuad::PEAKING; + bool qIsBw = false; +}; diff --git a/Editor/guis/BiQuadFilterGUI.ui b/Editor/guis/BiQuadFilterGUI.ui new file mode 100644 index 0000000..f2c5f44 --- /dev/null +++ b/Editor/guis/BiQuadFilterGUI.ui @@ -0,0 +1,238 @@ + + + BiQuadFilterGUI + + + + 0 + 0 + 734 + 66 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 66 + + + + + 16777215 + 66 + + + + 1000 + + + 10 + + + 566 + + + + + + + + 0 + 66 + + + + + 16777215 + 66 + + + + 0 + + + 1000 + + + false + + + + + + + true + + + Hz + + + 0.010000000000000 + + + 99999.990000000005239 + + + 1000.000000000000000 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 20 + + + + + + + + + 0 + 66 + + + + + 16777215 + 66 + + + + -200 + + + 200 + + + 20 + + + 9.850000000000000 + + + true + + + + + + + dB + + + -199.990000000000009 + + + 199.990000000000009 + + + + + + + 4 + + + 0.000100000000000 + + + 999.999900000000025 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + + 0 + 0 + + + + Gain: + + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 20 + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 10 + 20 + + + + + + + + + diff --git a/Editor/guis/BiQuadFilterGUIFactory.cpp b/Editor/guis/BiQuadFilterGUIFactory.cpp new file mode 100644 index 0000000..acb5505 --- /dev/null +++ b/Editor/guis/BiQuadFilterGUIFactory.cpp @@ -0,0 +1,64 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "BiQuadFilterGUI.h" +#include "BiQuadFilterGUIFactory.h" +#include + +BiQuadFilterGUIFactory::BiQuadFilterGUIFactory() +{ +} + +QList BiQuadFilterGUIFactory::createFilterTemplates() +{ + QList list; + list.append(FilterTemplate(tr("Peaking filter"), "Filter: ON PK Fc 100 Hz Gain 0 dB Q 10.0", QStringList(tr("Parametric filters")))); + list.append(FilterTemplate(tr("Low-pass filter"), "Filter: ON LP Fc 100 Hz", QStringList(tr("Parametric filters")))); + list.append(FilterTemplate(tr("High-pass filter"), "Filter: ON LP Fc 100 Hz", QStringList(tr("Parametric filters")))); + list.append(FilterTemplate(tr("Band-pass filter"), "Filter: ON BP Fc 100 Hz Q 10.0", QStringList(tr("Parametric filters")))); + list.append(FilterTemplate(tr("Low-shelf filter"), "Filter: ON LS Fc 100 Hz Gain 0 dB", QStringList(tr("Parametric filters")))); + list.append(FilterTemplate(tr("High-shelf filter"), "Filter: ON HS Fc 100 Hz Gain 0 dB", QStringList(tr("Parametric filters")))); + list.append(FilterTemplate(tr("Notch filter"), "Filter: ON NO Fc 100 Hz", QStringList(tr("Parametric filters")))); + list.append(FilterTemplate(tr("All-pass filter"), "Filter: ON AP Fc 100 Hz Q 10.0", QStringList(tr("Parametric filters")))); + return list; +} + +IFilterGUI* BiQuadFilterGUIFactory::createFilterGUI(QString& command, QString& parameters) +{ + BiQuadFilterGUI* result = NULL; + + if(command.startsWith("Filter")) + { + BiQuadFilterFactory factory; + std::vector filters = factory.createFilter(L"", command.toStdWString(), parameters.toStdWString()); + if(!filters.empty()) + { + BiQuadFilter* filter = (BiQuadFilter*)filters[0]; + result = new BiQuadFilterGUI(filter); + } + + for(IFilter* f : filters) + { + f->~IFilter(); + MemoryHelper::free(f); + } + } + + return result; +} diff --git a/Editor/guis/BiQuadFilterGUIFactory.h b/Editor/guis/BiQuadFilterGUIFactory.h new file mode 100644 index 0000000..a1c0cb1 --- /dev/null +++ b/Editor/guis/BiQuadFilterGUIFactory.h @@ -0,0 +1,33 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/IFilterGUIFactory.h" + +class BiQuadFilterGUIFactory : public IFilterGUIFactory +{ + Q_OBJECT + +public: + explicit BiQuadFilterGUIFactory(); + + QList createFilterTemplates() override; + IFilterGUI* createFilterGUI(QString& command, QString& parameters) override; +}; diff --git a/Editor/guis/ChannelFilterGUI.cpp b/Editor/guis/ChannelFilterGUI.cpp new file mode 100644 index 0000000..7c73089 --- /dev/null +++ b/Editor/guis/ChannelFilterGUI.cpp @@ -0,0 +1,86 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "ChannelFilterGUI.h" +#include "ChannelFilterGUIDialog.h" +#include "ui_ChannelFilterGUI.h" + +using namespace std; + +ChannelFilterGUI::ChannelFilterGUI(const QString& parameters, int selectedChannelMask) : + ui(new Ui::ChannelFilterGUI) +{ + ui->setupUi(this); + + this->selectedChannelMask = selectedChannelMask; + + scene = new ChannelFilterGUIScene; + ui->graphicsView->setScene(scene); + ui->graphicsView->setBackgroundRole(QPalette::Window); + connect(scene, SIGNAL(selectionChanged()), this, SLOT(updateSelectedChannels())); + + selectedChannels.clear(); + + QStringList words = parameters.trimmed().split(' ', QString::SkipEmptyParts); + for(QString word : words) + if(word.length() > 0) + selectedChannels.append(word.toUpper()); +} + +ChannelFilterGUI::~ChannelFilterGUI() +{ + delete ui; +} + +void ChannelFilterGUI::configureChannels(vector& channelNames) +{ + this->channelNames = channelNames; + + refreshGui(); +} + +void ChannelFilterGUI::store(QString& command, QString& parameters) +{ + command = "Channel"; + + selectedChannels = scene->getSelectedChannels(); + parameters = selectedChannels.join(' '); +} + +void ChannelFilterGUI::on_pushButton_clicked() +{ + ChannelFilterGUIDialog dialog(this, selectedChannels, selectedChannelMask, channelNames); + if(dialog.exec() == QDialog::Accepted) + { + selectedChannels = dialog.getSelectedChannels(); + refreshGui(); + emit updateModel(); + } +} + +void ChannelFilterGUI::updateSelectedChannels() +{ + selectedChannels = scene->getSelectedChannels(); + updateModel(); +} + +void ChannelFilterGUI::refreshGui() +{ + scene->load(channelNames, selectedChannels); +} diff --git a/Editor/guis/ChannelFilterGUI.h b/Editor/guis/ChannelFilterGUI.h new file mode 100644 index 0000000..cd89143 --- /dev/null +++ b/Editor/guis/ChannelFilterGUI.h @@ -0,0 +1,51 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/IFilterGUI.h" +#include "ChannelFilterGUIScene.h" + +namespace Ui { +class ChannelFilterGUI; +} + +class ChannelFilterGUI : public IFilterGUI +{ + Q_OBJECT + +public: + explicit ChannelFilterGUI(const QString& parameters, int selectedChannelMask); + ~ChannelFilterGUI(); + void configureChannels(std::vector& channelNames) override; + void store(QString& command, QString& parameters) override; + +private slots: + void on_pushButton_clicked(); + void updateSelectedChannels(); + +private: + void refreshGui(); + + Ui::ChannelFilterGUI *ui; + QStringList selectedChannels; + int selectedChannelMask; + std::vector channelNames; + ChannelFilterGUIScene* scene; +}; diff --git a/Editor/guis/ChannelFilterGUI.ui b/Editor/guis/ChannelFilterGUI.ui new file mode 100644 index 0000000..ab3b9c9 --- /dev/null +++ b/Editor/guis/ChannelFilterGUI.ui @@ -0,0 +1,81 @@ + + + ChannelFilterGUI + + + + 0 + 0 + 529 + 44 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 20 + + + + QAbstractScrollArea::AdjustToContents + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + QPainter::Antialiasing|QPainter::TextAntialiasing + + + QGraphicsView::RubberBandDrag + + + + + + + Change... + + + + + + + + 0 + 0 + + + + Selected channels: + + + + + + + + diff --git a/Editor/guis/ChannelFilterGUIChannelItem.cpp b/Editor/guis/ChannelFilterGUIChannelItem.cpp new file mode 100644 index 0000000..b11e37d --- /dev/null +++ b/Editor/guis/ChannelFilterGUIChannelItem.cpp @@ -0,0 +1,46 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include + +#include "ChannelFilterGUIScene.h" +#include "ChannelFilterGUIChannelItem.h" + +ChannelFilterGUIChannelItem::ChannelFilterGUIChannelItem(const QString& name) : + ChannelGraphItem(name) +{ + setFlag(ItemIsSelectable); +} + +void ChannelFilterGUIChannelItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + QColor color; + + if(isSelected()) + { + color = QColor(176, 229, 255); + } + else + { + painter->setPen(QColor(156, 156, 156)); + color = QColor(204, 204, 204); + } + + ChannelGraphItem::paint(painter, color); +} diff --git a/Editor/guis/ChannelFilterGUIChannelItem.h b/Editor/guis/ChannelFilterGUIChannelItem.h new file mode 100644 index 0000000..b1e88a9 --- /dev/null +++ b/Editor/guis/ChannelFilterGUIChannelItem.h @@ -0,0 +1,36 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/widgets/ChannelGraphItem.h" + +class ChannelFilterGUIChannelItem : public ChannelGraphItem +{ +public: + ChannelFilterGUIChannelItem(const QString& name); + + enum { Type = UserType + 2 }; + virtual int type() const + { + return Type; + } + + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; +}; diff --git a/Editor/guis/ChannelFilterGUIDialog.cpp b/Editor/guis/ChannelFilterGUIDialog.cpp new file mode 100644 index 0000000..1001310 --- /dev/null +++ b/Editor/guis/ChannelFilterGUIDialog.cpp @@ -0,0 +1,180 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include +#include + +#include "Editor/helpers/GUIChannelHelper.h" +#include "ChannelFilterGUIDialog.h" +#include "ui_ChannelFilterGUIDialog.h" + +#define PROPERTY_POSITION "CHANNEL_POSITION" +#define PROPERTY_NAME "CHANNEL_NAME" + +using namespace std; + +ChannelFilterGUIDialog::ChannelFilterGUIDialog(QWidget *parent, QStringList selectedChannels, int selectedChannelMask, const vector& channelNames) : + QDialog(parent), + ui(new Ui::ChannelFilterGUIDialog) +{ + ui->setupUi(this); + + ui->centerCheckBox->setProperty(PROPERTY_POSITION, SPEAKER_FRONT_CENTER); + ui->leftCheckBox->setProperty(PROPERTY_POSITION, SPEAKER_FRONT_LEFT); + ui->rightCheckBox->setProperty(PROPERTY_POSITION, SPEAKER_FRONT_RIGHT); + ui->sideLeftCheckBox->setProperty(PROPERTY_POSITION, SPEAKER_SIDE_LEFT); + ui->sideRightCheckBox->setProperty(PROPERTY_POSITION, SPEAKER_SIDE_RIGHT); + ui->rearLeftCheckBox->setProperty(PROPERTY_POSITION, SPEAKER_BACK_LEFT); + ui->rearRightCheckBox->setProperty(PROPERTY_POSITION, SPEAKER_BACK_RIGHT); + ui->rearCenterCheckBox->setProperty(PROPERTY_POSITION, SPEAKER_BACK_CENTER); + ui->subWooferCheckBox->setProperty(PROPERTY_POSITION, SPEAKER_LOW_FREQUENCY); + + ui->centerCheckBox->setProperty(PROPERTY_NAME, "C"); + ui->leftCheckBox->setProperty(PROPERTY_NAME, "L"); + ui->rightCheckBox->setProperty(PROPERTY_NAME, "R"); + ui->sideLeftCheckBox->setProperty(PROPERTY_NAME, "SL"); + ui->sideRightCheckBox->setProperty(PROPERTY_NAME, "SR"); + ui->rearLeftCheckBox->setProperty(PROPERTY_NAME, "RL"); + ui->rearRightCheckBox->setProperty(PROPERTY_NAME, "RR"); + ui->rearCenterCheckBox->setProperty(PROPERTY_NAME, "RC"); + ui->subWooferCheckBox->setProperty(PROPERTY_NAME, "SUB"); + + checkBoxes.append(ui->centerCheckBox); + checkBoxes.append(ui->leftCheckBox); + checkBoxes.append(ui->rightCheckBox); + checkBoxes.append(ui->sideLeftCheckBox); + checkBoxes.append(ui->sideRightCheckBox); + checkBoxes.append(ui->rearLeftCheckBox); + checkBoxes.append(ui->rearRightCheckBox); + checkBoxes.append(ui->rearCenterCheckBox); + checkBoxes.append(ui->subWooferCheckBox); + + vector remainingChannelNames = channelNames; + QStringList remainingSelectedChannels = selectedChannels; + if(selectedChannels.contains("ALL")) + { + ui->allChannelsCheckBox->setChecked(true); + remainingSelectedChannels.clear(); + } + int channelIndex = 0; + for(QCheckBox* checkBox : checkBoxes) + { + int channelPos = checkBox->property(PROPERTY_POSITION).toInt(); + QString channelName = checkBox->property(PROPERTY_NAME).toString(); + bool exists = selectedChannelMask & channelPos; + checkBox->setVisible(exists); + if(exists) + { + channelIndex++; + bool checked = false; + remainingChannelNames.erase(remove(remainingChannelNames.begin(), remainingChannelNames.end(), channelName.toStdWString()), remainingChannelNames.end()); + if(remainingSelectedChannels.removeOne(channelName)) + checked = true; + if(remainingSelectedChannels.removeOne(QString(channelIndex))) + checked = true; + checkBox->setChecked(checked); + } + else + { + checkBox->setChecked(false); + } + } + + ui->listWidget->clear(); + for(wstring channelName : remainingChannelNames) + { + QListWidgetItem* item = new QListWidgetItem(QString::fromStdWString(channelName)); + item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable); + if(remainingSelectedChannels.removeOne(QString::fromStdWString(channelName))) + item->setCheckState(Qt::Checked); + else + item->setCheckState(Qt::Unchecked); + ui->listWidget->addItem(item); + } + + for(QString c : remainingSelectedChannels) + { + QListWidgetItem* item = new QListWidgetItem(c); + item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable); + item->setCheckState(Qt::Checked); + ui->listWidget->addItem(item); + } + + ui->stackedWidget->setCurrentIndex(selectedChannelMask & SPEAKER_BACK_CENTER ? 1 : 0); +} + +ChannelFilterGUIDialog::~ChannelFilterGUIDialog() +{ + delete ui; +} + +void ChannelFilterGUIDialog::on_addButton_clicked() +{ + QListWidgetItem* item = new QListWidgetItem(""); + item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable); + item->setCheckState(Qt::Checked); + ui->listWidget->addItem(item); + ui->listWidget->editItem(item); +} + +void ChannelFilterGUIDialog::on_removeButton_clicked() +{ + qDeleteAll(ui->listWidget->selectedItems()); +} + +QStringList ChannelFilterGUIDialog::getSelectedChannels() const +{ + QStringList selectedChannels; + + if(ui->allChannelsCheckBox->isChecked()) + { + selectedChannels.append("ALL"); + } + else + { + for(QCheckBox* checkBox : checkBoxes) + { + if(checkBox->isChecked()) + { + QString channelName = checkBox->property(PROPERTY_NAME).toString(); + selectedChannels.append(channelName); + } + } + + for(int i = 0; i < ui->listWidget->count(); i++) + { + QListWidgetItem* item = ui->listWidget->item(i); + if(item->checkState() == Qt::Checked) + { + QString text = item->text().trimmed(); + if(text.length() > 0) + selectedChannels.append(text.toUpper()); + } + } + } + + return selectedChannels; +} + +void ChannelFilterGUIDialog::on_allChannelsCheckBox_toggled(bool checked) +{ + ui->positionsGroupBox->setEnabled(!checked); + ui->additionalChannelsGroupBox->setEnabled(!checked); +} diff --git a/Editor/guis/ChannelFilterGUIDialog.h b/Editor/guis/ChannelFilterGUIDialog.h new file mode 100644 index 0000000..dcd06e6 --- /dev/null +++ b/Editor/guis/ChannelFilterGUIDialog.h @@ -0,0 +1,49 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include +#include + +namespace Ui { +class ChannelFilterGUIDialog; +} + +class ChannelFilterGUIDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ChannelFilterGUIDialog(QWidget *parent, QStringList selectedChannels, int selectedChannelMask, const std::vector& channelNames); + ~ChannelFilterGUIDialog(); + + QStringList getSelectedChannels() const; + +private slots: + void on_addButton_clicked(); + + void on_removeButton_clicked(); + + void on_allChannelsCheckBox_toggled(bool checked); + +private: + Ui::ChannelFilterGUIDialog *ui; + QList checkBoxes; +}; diff --git a/Editor/guis/ChannelFilterGUIDialog.ui b/Editor/guis/ChannelFilterGUIDialog.ui new file mode 100644 index 0000000..7810a87 --- /dev/null +++ b/Editor/guis/ChannelFilterGUIDialog.ui @@ -0,0 +1,302 @@ + + + ChannelFilterGUIDialog + + + + 0 + 0 + 355 + 329 + + + + Channel selection + + + + + + Select all channels + + + + + + + Speaker positions + + + + + + SR (Side right) + + + + + + + C (Center) + + + + + + + SUB (Subwoofer) + + + + + + + L (Left) + + + + + + + + 0 + 0 + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 1 + 1 + + + + + + + + RL (Rear left) + + + + + + + RR (Rear right) + + + + + + + Qt::Horizontal + + + + 1 + 1 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 40 + 1 + + + + + + + + RC (Rear center) + + + + + + + Qt::Horizontal + + + + 40 + 1 + + + + + + + + + + + + SL (Side left) + + + + + + + R (Right) + + + + + + + + + + Additional channels + + + + + + Remove channel + + + + :/icons/16x16/list-remove-red.png:/icons/16x16/list-remove-red.png + + + + + + + Add channel + + + + :/icons/16x16/list-add-green.png:/icons/16x16/list-add-green.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QAbstractItemView::AnyKeyPressed|QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed + + + true + + + QAbstractItemView::InternalMove + + + QAbstractItemView::ExtendedSelection + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + ChannelFilterGUIDialog + accept() + + + 257 + 303 + + + 157 + 274 + + + + + buttonBox + rejected() + ChannelFilterGUIDialog + reject() + + + 325 + 303 + + + 286 + 274 + + + + + diff --git a/Editor/guis/ChannelFilterGUIFactory.cpp b/Editor/guis/ChannelFilterGUIFactory.cpp new file mode 100644 index 0000000..f2f70b4 --- /dev/null +++ b/Editor/guis/ChannelFilterGUIFactory.cpp @@ -0,0 +1,50 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "Editor/FilterTable.h" +#include "ChannelFilterGUI.h" +#include "ChannelFilterGUIFactory.h" + +ChannelFilterGUIFactory::ChannelFilterGUIFactory() +{ +} + +void ChannelFilterGUIFactory::initialize(FilterTable* filterTable) +{ + this->filterTable = filterTable; +} + +QList ChannelFilterGUIFactory::createFilterTemplates() +{ + QList list; + list.append(FilterTemplate(tr("Channel (Select channels)"), "Channel: all", QStringList(tr("Control")))); + return list; +} + +IFilterGUI* ChannelFilterGUIFactory::createFilterGUI(QString& command, QString& parameters) +{ + ChannelFilterGUI* result = NULL; + + if(command == "Channel") + { + result = new ChannelFilterGUI(parameters, filterTable->getSelectedChannelMask()); + } + + return result; +} diff --git a/Editor/guis/ChannelFilterGUIFactory.h b/Editor/guis/ChannelFilterGUIFactory.h new file mode 100644 index 0000000..8322d8e --- /dev/null +++ b/Editor/guis/ChannelFilterGUIFactory.h @@ -0,0 +1,39 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "DeviceAPOInfo.h" +#include "Editor/IFilterGUIFactory.h" + +class ChannelFilterGUIFactory : public IFilterGUIFactory +{ + Q_OBJECT + +public: + ChannelFilterGUIFactory(); + + void initialize(FilterTable* filterTable) override; + + QList createFilterTemplates() override; + IFilterGUI* createFilterGUI(QString& command, QString& parameters) override; + +private: + FilterTable* filterTable; +}; diff --git a/Editor/guis/ChannelFilterGUIScene.cpp b/Editor/guis/ChannelFilterGUIScene.cpp new file mode 100644 index 0000000..f8f020b --- /dev/null +++ b/Editor/guis/ChannelFilterGUIScene.cpp @@ -0,0 +1,88 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "ChannelFilterGUIChannelItem.h" +#include "ChannelFilterGUIScene.h" + +using namespace std; + +ChannelFilterGUIScene::ChannelFilterGUIScene() +{ +} + +void ChannelFilterGUIScene::load(vector channelNames, QStringList selectedChannels) +{ + blockSignals(true); + clear(); + + QHash channelMap; + + QGraphicsItem* lastItem = NULL; + + channelNames.push_back(L"ALL"); + + for(unsigned i = 0; i < channelNames.size(); i++) + { + QString c = QString::fromStdWString(channelNames[i]); + + ChannelFilterGUIChannelItem* item = new ChannelFilterGUIChannelItem(c); + item->setPos(getNextChannelPoint(lastItem, false)); + addItem(item); + + channelMap.insert(c, item); + if(c != "ALL") + channelMap.insert(QString().setNum(i), item); + lastItem = item; + } + + for(QString c : selectedChannels) + { + ChannelFilterGUIChannelItem* item = channelMap.value(c.toUpper()); + if(item == NULL) + { + item = new ChannelFilterGUIChannelItem(c); + item->setPos(getNextChannelPoint(lastItem, false)); + addItem(item); + + channelMap.insert(c, item); + lastItem = item; + } + item->setSelected(true); + } + + setSceneRect(itemsBoundingRect().marginsAdded(QMarginsF(2, 2, 2, 2))); + blockSignals(false); +} + +QStringList ChannelFilterGUIScene::getSelectedChannels() +{ + QStringList selectedChannels; + + for(QGraphicsItem* item : items(Qt::AscendingOrder)) + { + ChannelFilterGUIChannelItem* channelItem = qgraphicsitem_cast(item); + if(channelItem != NULL) + { + if(channelItem->isSelected()) + selectedChannels.append(channelItem->getName()); + } + } + + return selectedChannels; +} diff --git a/Editor/guis/ChannelFilterGUIScene.h b/Editor/guis/ChannelFilterGUIScene.h new file mode 100644 index 0000000..c8bd72d --- /dev/null +++ b/Editor/guis/ChannelFilterGUIScene.h @@ -0,0 +1,35 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include +#include + +#include "Editor/widgets/ChannelGraphScene.h" + +class ChannelFilterGUIScene : public ChannelGraphScene +{ + Q_OBJECT +public: + explicit ChannelFilterGUIScene(); + + void load(std::vector channelNames, QStringList selectedChannels); + QStringList getSelectedChannels(); +}; diff --git a/Editor/guis/CommentFilterGUI.cpp b/Editor/guis/CommentFilterGUI.cpp new file mode 100644 index 0000000..f515900 --- /dev/null +++ b/Editor/guis/CommentFilterGUI.cpp @@ -0,0 +1,77 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2014 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "CommentFilterGUI.h" +#include "ui_CommentFilterGUI.h" + +using namespace std; + +CommentFilterGUI::CommentFilterGUI(IFilterGUI* child, bool isComment) +{ + ui = new Ui::CommentFilterGUI; + ui->setupUi(this); + + this->child = child; + connect(child, SIGNAL(updateModel()), this, SIGNAL(updateModel())); + connect(child, SIGNAL(updateChannels()), this, SIGNAL(updateChannels())); + + ui->horizontalLayout->removeItem(ui->horizontalSpacer); + ui->horizontalLayout->addWidget(child); + + ui->actionPowerOn->setChecked(!isComment); + ui->toolBar->setIconSize(QSize(24, 24)); + ui->toolBar->addAction(ui->actionPowerOn); + ui->toolBar->updateMaximumHeight(); + + child->setEnabled(!isComment); +} + +CommentFilterGUI::~CommentFilterGUI() +{ + delete ui; +} + +void CommentFilterGUI::configureChannels(vector& channelNames) +{ + if(ui->actionPowerOn->isChecked()) + { + child->configureChannels(channelNames); + } + else + { + // prevent modification of channel names + vector copy = channelNames; + child->configureChannels(copy); + } +} + +void CommentFilterGUI::store(QString& command, QString& parameters) +{ + child->store(command, parameters); + + if(!ui->actionPowerOn->isChecked()) + command = "# " + command; +} + +void CommentFilterGUI::on_actionPowerOn_toggled(bool checked) +{ + child->setEnabled(checked); + + emit updateModel(); +} diff --git a/Editor/guis/CommentFilterGUI.h b/Editor/guis/CommentFilterGUI.h new file mode 100644 index 0000000..8e547d5 --- /dev/null +++ b/Editor/guis/CommentFilterGUI.h @@ -0,0 +1,45 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2014 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/IFilterGUI.h" + +namespace Ui +{ +class CommentFilterGUI; +} + +class CommentFilterGUI : public IFilterGUI +{ + Q_OBJECT + +public: + explicit CommentFilterGUI(IFilterGUI* child, bool isComment); + ~CommentFilterGUI(); + void configureChannels(std::vector& channelNames) override; + void store(QString& command, QString& parameters) override; + +private slots: + void on_actionPowerOn_toggled(bool checked); + +private: + Ui::CommentFilterGUI* ui; + IFilterGUI* child; +}; diff --git a/Editor/guis/CommentFilterGUI.ui b/Editor/guis/CommentFilterGUI.ui new file mode 100644 index 0000000..3ada669 --- /dev/null +++ b/Editor/guis/CommentFilterGUI.ui @@ -0,0 +1,72 @@ + + + CommentFilterGUI + + + + 0 + 0 + 400 + 104 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Qt::Horizontal + + + + 379 + 37 + + + + + + + + true + + + + :/icons/power_off.png + :/icons/power_on.png:/icons/power_off.png + + + Power on + + + + + + CompactToolBar + QWidget +
Editor/widgets/CompactToolBar.h
+ 1 +
+
+ + + + +
diff --git a/Editor/guis/CommentFilterGUIFactory.cpp b/Editor/guis/CommentFilterGUIFactory.cpp new file mode 100644 index 0000000..a73d59e --- /dev/null +++ b/Editor/guis/CommentFilterGUIFactory.cpp @@ -0,0 +1,51 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2014 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "helpers/StringHelper.h" +#include "CommentFilterGUI.h" +#include "CommentFilterGUIFactory.h" + +using namespace std; + +CommentFilterGUIFactory::CommentFilterGUIFactory() +{ +} + +QList CommentFilterGUIFactory::createFilterTemplates() +{ + QList list; + list.append(FilterTemplate(tr("Comment"), "# ", QStringList())); + return list; +} + +IFilterGUI* CommentFilterGUIFactory::createFilterGUI(QString& command, QString& parameters) +{ + lastWasComment = command.length() > 0 && command[0] == '#'; + if(lastWasComment) + command = command.mid(1).trimmed(); + + return NULL; +} + +IFilterGUI* CommentFilterGUIFactory::decorateFilterGUI(IFilterGUI* gui) +{ + gui = new CommentFilterGUI(gui, lastWasComment); + + return gui; +} diff --git a/Editor/guis/CommentFilterGUIFactory.h b/Editor/guis/CommentFilterGUIFactory.h new file mode 100644 index 0000000..8679ba8 --- /dev/null +++ b/Editor/guis/CommentFilterGUIFactory.h @@ -0,0 +1,38 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2014 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/IFilterGUIFactory.h" + +class CommentFilterGUIFactory : public IFilterGUIFactory +{ + Q_OBJECT + +public: + CommentFilterGUIFactory(); + + QList createFilterTemplates() override; + + IFilterGUI* createFilterGUI(QString& command, QString& parameters) override; + IFilterGUI* decorateFilterGUI(IFilterGUI* gui) override; + +private: + bool lastWasComment; +}; diff --git a/Editor/guis/ConvolutionFilterGUI.cpp b/Editor/guis/ConvolutionFilterGUI.cpp new file mode 100644 index 0000000..a5d414e --- /dev/null +++ b/Editor/guis/ConvolutionFilterGUI.cpp @@ -0,0 +1,137 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1 +#include + +#include "ConvolutionFilterGUI.h" +#include "ui_ConvolutionFilterGUI.h" + +ConvolutionFilterGUI::ConvolutionFilterGUI(const QString& configPath, unsigned deviceSampleRate, const QString& path) : + ui(new Ui::ConvolutionFilterGUI), deviceSampleRate(deviceSampleRate) +{ + ui->setupUi(this); + + this->configPath = configPath; + ui->pathLineEdit->setText(path); + + updateFileInfo(); +} + +ConvolutionFilterGUI::~ConvolutionFilterGUI() +{ + delete ui; +} + +void ConvolutionFilterGUI::store(QString& command, QString& parameters) +{ + command = "Convolution"; + parameters = ui->pathLineEdit->text(); +} + +void ConvolutionFilterGUI::on_selectFileToolButton_clicked() +{ + QFileInfo fileInfo(configPath); + QDir configDir = fileInfo.absoluteDir(); + QString path = ui->pathLineEdit->text(); + if(path.length() > 0) + fileInfo.setFile(configDir, path); + + QFileDialog dialog(this, tr("Select impulse response file"), fileInfo.absolutePath(), "*.wav;*.flac;*.ogg"); + dialog.setFileMode(QFileDialog::ExistingFile); + dialog.setNameFilter(tr("Impulse response (*.wav *.flac *.ogg)")); + if(path.length() > 0) + dialog.selectFile(fileInfo.fileName()); + if(dialog.exec() == QDialog::Accepted) + { + QString absolutePath = dialog.selectedFiles().first(); + QString relativePath = configDir.relativeFilePath(absolutePath); + if(relativePath.startsWith("../../")) + relativePath = absolutePath; + ui->pathLineEdit->setText(QDir::toNativeSeparators(relativePath)); + updateFileInfo(); + + emit updateModel(); + } +} + +void ConvolutionFilterGUI::on_pathLineEdit_editingFinished() +{ + updateFileInfo(); + + emit updateModel(); +} + +void ConvolutionFilterGUI::updateFileInfo() +{ + bool labelsVisible = true; + QString error = ""; + + QString path = ui->pathLineEdit->text(); + if(path.length() == 0) + { + error = tr("No file selected"); + labelsVisible = false; + } + else + { + QFileInfo fileInfo(configPath); + QDir configDir = fileInfo.absoluteDir(); + fileInfo.setFile(configDir, path); + if(!fileInfo.exists()) + { + error = tr("File not found"); + labelsVisible = false; + } + else + { + path = fileInfo.absoluteFilePath(); + + SF_INFO info; + SNDFILE* file = sf_wchar_open(path.toStdWString().c_str(), SFM_READ, &info); + if(file == NULL) + { + error = tr("Unsupported file format"); + labelsVisible = false; + } + else + { + int sampleRate = info.samplerate; + double length = info.frames * 1000.0 / sampleRate; + + ui->labelLengthValue->setText(tr("%0 ms (%1 samples)").arg(length).arg(info.frames)); + ui->labelSampleRateValue->setText(tr("%0 Hz").arg(sampleRate)); + sf_close(file); + + if(sampleRate != deviceSampleRate) + { + error = tr("The file sample rate does not match the device sample rate (%0 Hz)!\nSelect a different file or change the device configuration.").arg(deviceSampleRate); + } + } + } + } + + ui->labelLength->setVisible(labelsVisible); + ui->labelLengthValue->setVisible(labelsVisible); + ui->labelSampleRate->setVisible(labelsVisible); + ui->labelSampleRateValue->setVisible(labelsVisible); + ui->labelError->setVisible(error.length() > 0); + ui->labelError->setText(error); +} diff --git a/Editor/guis/ConvolutionFilterGUI.h b/Editor/guis/ConvolutionFilterGUI.h new file mode 100644 index 0000000..b9de55e --- /dev/null +++ b/Editor/guis/ConvolutionFilterGUI.h @@ -0,0 +1,49 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/IFilterGUI.h" + +namespace Ui { +class ConvolutionFilterGUI; +} + +class ConvolutionFilterGUI : public IFilterGUI +{ + Q_OBJECT + +public: + explicit ConvolutionFilterGUI(const QString& configPath, unsigned deviceSampleRate, const QString& path); + ~ConvolutionFilterGUI(); + + void store(QString& command, QString& parameters) override; + +private slots: + void on_selectFileToolButton_clicked(); + + void on_pathLineEdit_editingFinished(); + +private: + void updateFileInfo(); + + Ui::ConvolutionFilterGUI *ui; + QString configPath; + unsigned deviceSampleRate; +}; diff --git a/Editor/guis/ConvolutionFilterGUI.ui b/Editor/guis/ConvolutionFilterGUI.ui new file mode 100644 index 0000000..e132f1c --- /dev/null +++ b/Editor/guis/ConvolutionFilterGUI.ui @@ -0,0 +1,173 @@ + + + ConvolutionFilterGUI + + + + 0 + 0 + 652 + 65 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Select file + + + + :/icons/16x16/document-open.png:/icons/16x16/document-open.png + + + + + + + Qt::Horizontal + + + + 123 + 1 + + + + + + + + Convolution with impulse response: + + + + + + + + + + File information + + + + + + Sample rate: + + + + + + + No file selected + + + + + + + + + + + + 255 + 0 + 0 + + + + + + + + + 255 + 0 + 0 + + + + + + + + + 120 + 120 + 120 + + + + + + + + + + + + + + + Length: + + + + + + + No file selected + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 1 + + + + + + + + + + + + ResizingLineEdit + QLineEdit +
Editor/widgets/ResizingLineEdit.h
+
+
+ + + + +
diff --git a/Editor/guis/ConvolutionFilterGUIFactory.cpp b/Editor/guis/ConvolutionFilterGUIFactory.cpp new file mode 100644 index 0000000..d4fa61e --- /dev/null +++ b/Editor/guis/ConvolutionFilterGUIFactory.cpp @@ -0,0 +1,56 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "DeviceAPOInfo.h" +#include "Editor/FilterTable.h" +#include "ConvolutionFilterGUI.h" +#include "ConvolutionFilterGUIFactory.h" + +ConvolutionFilterGUIFactory::ConvolutionFilterGUIFactory() +{ +} + +void ConvolutionFilterGUIFactory::initialize(FilterTable* filterTable) +{ + deviceSampleRate = filterTable->getSelectedDevice()->sampleRate; +} + +QList ConvolutionFilterGUIFactory::createFilterTemplates() +{ + QList list; + list.append(FilterTemplate(tr("Convolution (Convolution with impulse response)"), "Convolution:", QStringList(tr("Advanced filters")))); + return list; +} + +void ConvolutionFilterGUIFactory::startOfFile(const QString& configPath) +{ + this->configPath = configPath; +} + +IFilterGUI* ConvolutionFilterGUIFactory::createFilterGUI(QString& command, QString& parameters) +{ + ConvolutionFilterGUI* result = NULL; + + if(command == "Convolution") + { + result = new ConvolutionFilterGUI(configPath, deviceSampleRate, parameters.trimmed()); + } + + return result; +} diff --git a/Editor/guis/ConvolutionFilterGUIFactory.h b/Editor/guis/ConvolutionFilterGUIFactory.h new file mode 100644 index 0000000..8518a01 --- /dev/null +++ b/Editor/guis/ConvolutionFilterGUIFactory.h @@ -0,0 +1,40 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/IFilterGUIFactory.h" + +class ConvolutionFilterGUIFactory : public IFilterGUIFactory +{ + Q_OBJECT +public: + explicit ConvolutionFilterGUIFactory(); + + void initialize(FilterTable* filterTable) override; + + QList createFilterTemplates() override; + + void startOfFile(const QString& configPath) override; + IFilterGUI* createFilterGUI(QString& command, QString& parameters) override; + +private: + QString configPath; + unsigned deviceSampleRate; +}; diff --git a/Editor/guis/CopyFilterGUI.cpp b/Editor/guis/CopyFilterGUI.cpp new file mode 100644 index 0000000..c99753f --- /dev/null +++ b/Editor/guis/CopyFilterGUI.cpp @@ -0,0 +1,151 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "helpers/ChannelHelper.h" +#include "CopyFilterGUIForm.h" +#include "CopyFilterGUI.h" +#include "ui_CopyFilterGUI.h" + +using namespace std; + +CopyFilterGUI::CopyFilterGUI(CopyFilter* filter) : + ui(new Ui::CopyFilterGUI) +{ + ui->setupUi(this); + + scene = new CopyFilterGUIScene; + ui->graphicsView->setScene(scene); + ui->graphicsView->setBackgroundRole(QPalette::Window); + + ui->form->load(filter->getAssignments()); + + connect(scene, SIGNAL(updateModel()), this, SIGNAL(updateModel())); + connect(scene, SIGNAL(updateChannels()), this, SIGNAL(updateChannels())); + + connect(ui->form, SIGNAL(updateModel()), this, SIGNAL(updateModel())); + connect(ui->form, SIGNAL(updateChannels()), this, SIGNAL(updateChannels())); +} + +CopyFilterGUI::~CopyFilterGUI() +{ + delete ui; +} + +void CopyFilterGUI::configureChannels(vector& channelNames) +{ + vector assignments = ui->form->buildAssignments(); + + if(channelNames != inputChannelNames) + { + inputChannelNames = channelNames; + + scene->load(inputChannelNames, assignments); + ui->form->setChannelNames(channelNames); + } + + for(Assignment assignment : assignments) + { + if(assignment.targetChannel == L"") + continue; + bool hasSummand = false; + for(Assignment::Summand summand : assignment.sourceSum) + { + if(summand.channel != L" ") + { + hasSummand = true; + break; + } + } + if(!hasSummand) + continue; + + int channelIndex = ChannelHelper::getChannelIndex(assignment.targetChannel, channelNames, true); + if(channelIndex == -1) + channelNames.push_back(assignment.targetChannel); + } +} + +void CopyFilterGUI::store(QString& command, QString& parameters) +{ + command = "Copy"; + + std::vector assignments; + + if(ui->tabWidget->currentIndex() == 0) + assignments = scene->buildAssignments(); + else + assignments = ui->form->buildAssignments(); + + bool firstAssignment = true; + for(const Assignment& assignment : assignments) + { + if(assignment.targetChannel == L"") + continue; + + bool firstSummand = true; + for(const Assignment::Summand& summand : assignment.sourceSum) + { + // skip not yet filled row + if(summand.channel == L" ") + continue; + + if(firstSummand) + { + firstSummand = false; + + if(firstAssignment) + firstAssignment = false; + else + parameters += " "; + + parameters += QString::fromStdWString(assignment.targetChannel); + parameters += "="; + } + else + { + parameters += "+"; + } + + bool hasChannel = summand.channel != L""; + bool hasFactor = !hasChannel || summand.factor != 1.0 || summand.isDecibel; + + if(hasFactor) + { + QString factorString; + factorString.setNum(summand.factor); + if(factorString != "0" && !factorString.contains('.')) + factorString += ".0"; + parameters += factorString; + if(summand.isDecibel) + parameters += "dB"; + } + + if(hasFactor && hasChannel) + parameters += "*"; + + if(hasChannel) + parameters += QString::fromStdWString(summand.channel); + } + + if(ui->tabWidget->currentIndex() == 0) + ui->form->load(assignments); + else + scene->load(inputChannelNames, assignments); + } +} diff --git a/Editor/guis/CopyFilterGUI.h b/Editor/guis/CopyFilterGUI.h new file mode 100644 index 0000000..f8c2267 --- /dev/null +++ b/Editor/guis/CopyFilterGUI.h @@ -0,0 +1,47 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "filters/CopyFilter.h" +#include "Editor/IFilterGUI.h" +#include "CopyFilterGUIScene.h" + +namespace Ui { +class CopyFilterGUI; +} + +class CopyFilterGUI : public IFilterGUI +{ + Q_OBJECT + +public: + explicit CopyFilterGUI(CopyFilter* filter); + ~CopyFilterGUI(); + + void configureChannels(std::vector& channelNames) override; + + void store(QString& command, QString& parameters) override; + +private: + Ui::CopyFilterGUI *ui; + CopyFilterGUIScene* scene; + + std::vector inputChannelNames; +}; diff --git a/Editor/guis/CopyFilterGUI.ui b/Editor/guis/CopyFilterGUI.ui new file mode 100644 index 0000000..f2b0f20 --- /dev/null +++ b/Editor/guis/CopyFilterGUI.ui @@ -0,0 +1,170 @@ + + + CopyFilterGUI + + + + 0 + 0 + 488 + 94 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Copy channels: + + + + + + + QTabWidget::East + + + 0 + + + Qt::ElideRight + + + false + + + true + + + + + :/icons/16x16/applications-graphics-rotated.png:/icons/16x16/applications-graphics-rotated.png + + + + + + Graph + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QAbstractScrollArea::AdjustToContents + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + QPainter::Antialiasing|QPainter::TextAntialiasing + + + QGraphicsView::RubberBandDrag + + + + + + + + + :/icons/16x16/format-justify-fill-rotated.png:/icons/16x16/format-justify-fill-rotated.png + + + + + + Assignments + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + + + 0 + 0 + 366 + 92 + + + + + + + + + + + + + + 16777215 + 0 + + + + Selected channels: + + + + + + + + CopyFilterGUIForm + QWidget +
CopyFilterGUIForm.h
+ 1 +
+
+ + + + +
diff --git a/Editor/guis/CopyFilterGUIChannelItem.cpp b/Editor/guis/CopyFilterGUIChannelItem.cpp new file mode 100644 index 0000000..c4e12f8 --- /dev/null +++ b/Editor/guis/CopyFilterGUIChannelItem.cpp @@ -0,0 +1,168 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include + +#include "CopyFilterGUIScene.h" +#include "CopyFilterGUIChannelItem.h" + +CopyFilterGUIChannelItem::CopyFilterGUIChannelItem(const QString& name, bool output) : + ChannelGraphItem(name), output(output) +{ + setZValue(-1); +} + +void CopyFilterGUIChannelItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + QColor color; + if(output) + color = QColor(255, 194, 194); + else + color = QColor(176, 255, 176); + + ChannelGraphItem::paint(painter, color) ; +} + +QPointF CopyFilterGUIChannelItem::getPerimeterPointTo(QPointF oppositePoint) +{ + QRectF rect = boundingRect(); + + if(output) + return scenePos() + QPointF(rect.center().x(), rect.top()); + else + return scenePos() + QPointF(rect.center().x(), rect.top() + rect.height()); +} + +CopyFilterGUIChannelItem* CopyFilterGUIChannelItem::findAt(QPointF mousePos) +{ + CopyFilterGUIChannelItem* result = NULL; + + QList items = scene()->items(mousePos); + for(QGraphicsItem* item : items) + { + CopyFilterGUIChannelItem* channelItem = qgraphicsitem_cast(item); + if(channelItem != NULL && channelItem->output != output) + { + result = channelItem; + break; + } + } + + return result; +} + +QLineF CopyFilterGUIChannelItem::getLineTo(CopyFilterGUIChannelItem* item) +{ + QPointF endPoint = item->getPerimeterPointTo(scenePos()); + + QPointF startPoint = getPerimeterPointTo(endPoint); + + QLineF line; + if(output) + line = QLineF(endPoint, startPoint); + else + line = QLineF(startPoint, endPoint); + + return line; +} + +void CopyFilterGUIChannelItem::mousePressEvent(QGraphicsSceneMouseEvent* event) +{ +} + +void CopyFilterGUIChannelItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event) +{ + if(currentConnection == NULL) + { + if( !boundingRect().contains(event->pos())) + { + currentConnection = new CopyFilterGUIConnectionItem(); + scene()->addItem(currentConnection); + } + } + + if(currentConnection != NULL) + { + CopyFilterGUIChannelItem* item = findAt(event->scenePos()); + + if(item == NULL) + { + QLineF line; + if(output) + line = QLineF(event->scenePos(), getPerimeterPointTo(event->scenePos())); + else + line = QLineF(getPerimeterPointTo(event->scenePos()), event->scenePos()); + currentConnection->setLine(line); + } + else + { + currentConnection->setLine(getLineTo(item)); + } + } +} + +void CopyFilterGUIChannelItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) +{ + if(currentConnection != NULL) + { + CopyFilterGUIChannelItem* item = findAt(event->scenePos()); + if(item != NULL) + { + CopyFilterGUIChannelItem* source; + CopyFilterGUIChannelItem* target; + if(output) + { + source = item; + target = this; + } + else + { + source = this; + target = item; + } + + double factor = 1.0; + if(source->getName() == "") + factor = 0.0; + + scene()->addItem(new CopyFilterGUIConnectionItem(source, target, factor, false)); + + emit ((CopyFilterGUIScene*)scene())->updateModel(); + emit ((CopyFilterGUIScene*)scene())->updateChannels(); + } + + scene()->removeItem(currentConnection); + delete currentConnection; + currentConnection = NULL; + } + else + { + scene()->clearSelection(); + QList items = scene()->items(); + for(QGraphicsItem* item : items) + { + CopyFilterGUIConnectionItem* connItem = qgraphicsitem_cast(item); + if(connItem != NULL) + { + if(connItem->getSource() == this || connItem->getTarget() == this) + connItem->setSelected(true); + } + } + } +} diff --git a/Editor/guis/CopyFilterGUIChannelItem.h b/Editor/guis/CopyFilterGUIChannelItem.h new file mode 100644 index 0000000..a9b5bdc --- /dev/null +++ b/Editor/guis/CopyFilterGUIChannelItem.h @@ -0,0 +1,53 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include +#include + +#include "Editor/widgets/ChannelGraphItem.h" +#include "CopyFilterGUIConnectionItem.h" + +class CopyFilterGUIChannelItem : public ChannelGraphItem +{ +public: + CopyFilterGUIChannelItem(const QString& name, bool output); + + enum { Type = UserType + 2 }; + virtual int type() const + { + return Type; + } + + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; + + QPointF getPerimeterPointTo(QPointF oppositePoint); + CopyFilterGUIChannelItem* findAt(QPointF mousePos); + QLineF getLineTo(CopyFilterGUIChannelItem* item); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent* event) override; + void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override; + +private: + bool output; + CopyFilterGUIConnectionItem* currentConnection = NULL; +}; diff --git a/Editor/guis/CopyFilterGUIConnectionItem.cpp b/Editor/guis/CopyFilterGUIConnectionItem.cpp new file mode 100644 index 0000000..b3f9ed1 --- /dev/null +++ b/Editor/guis/CopyFilterGUIConnectionItem.cpp @@ -0,0 +1,253 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include +#include +#include +#include + +#include "Editor/widgets/ResizingLineEdit.h" +#include "CopyFilterGUIChannelItem.h" +#include "CopyFilterGUIScene.h" +#include "CopyFilterGUIConnectionItem.h" + +static const int maxArrowSize = 6; + +CopyFilterGUIConnectionItem::CopyFilterGUIConnectionItem() +{ + +} + +CopyFilterGUIConnectionItem::CopyFilterGUIConnectionItem(CopyFilterGUIChannelItem* source, CopyFilterGUIChannelItem* target, double factor, bool isDecibel) +{ + this->source = source; + this->target = target; + this->factor = factor; + this->isDecibel = isDecibel; + + setLine(source->getLineTo(target)); + setFlag(ItemIsSelectable); + setAcceptHoverEvents(true); +} + +QRectF CopyFilterGUIConnectionItem::boundingRect() const +{ + QRectF rect = QGraphicsLineItem::boundingRect(); + + QPointF p2 = line().p2(); + QRectF rect2 = QRectF(p2.x() - maxArrowSize, p2.y() - maxArrowSize, 2 * maxArrowSize, 2 * maxArrowSize); + + return rect.united(rect2); +} + +QPainterPath CopyFilterGUIConnectionItem::shape() const +{ + QLineF l = line(); + + QPainterPath path; + path.setFillRule(Qt::WindingFill); + double length = line().length(); + if(length > 0) + { + double offset = min(length, maxArrowSize); + QLineF unit = l.unitVector(); + QLineF normal = l.normalVector().unitVector(); + QPointF v(unit.dx(), unit.dy()); + QPointF n(normal.dx(), normal.dy()); + QPointF p2 = l.p2(); + + QPointF p3 = p2 - v * offset + 0.5 * n * offset; + QPointF p4 = p2 - v * offset - 0.5 * n * offset; + QPolygonF polygon; + polygon.append(p4); + polygon.append(p3); + polygon.append(p2); + path.addPolygon(polygon); + + QPolygonF polygon2; + QPointF p1 = l.p1(); + polygon2.append(p2 + 3 * n); + polygon2.append(p2 - 2 * n); + polygon2.append(p1 - 2 * n); + polygon2.append(p1 + 3 * n); + path.addPolygon(polygon2); + + if(factor != 1.0 || isDecibel) + { + QFont font; + font.setPixelSize(10); + QPointF center = (l.p1() + l.p2()) / 2; + QString text = QString("%1").arg(factor); + if(isDecibel) + text += " dB"; + + QFontMetrics fontMetrics(font); + QSizeF size = fontMetrics.size(0, text); + size += QSizeF(2, 0); + QRectF rect; + rect.setSize(size); + rect.moveCenter(center); + path.addRoundedRect(rect.adjusted(-0.5, 0.5, 0.5, 0.5), 3, 3); + } + } + + return path; +} + +bool CopyFilterGUIConnectionItem::contains(const QPointF& point) const +{ + return shape().contains(point); +} + +void CopyFilterGUIConnectionItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + QLineF l = line(); + if(!l.isNull()) + { + painter->setBrush(Qt::black); + if(isSelected()) + { + painter->setBrush(Qt::blue); + painter->setPen(QPen(Qt::blue, 1.5)); + } + painter->drawLine(line()); + + double length = line().length(); + double offset = min(length, maxArrowSize); + QLineF unit = l.unitVector(); + QLineF normal = l.normalVector().unitVector(); + QPointF v(unit.dx(), unit.dy()); + QPointF n(normal.dx(), normal.dy()); + QPointF p2 = l.p2(); + + QPointF p3 = p2 - v * offset + 0.5 * n * offset; + QPointF p4 = p2 - v * offset - 0.5 * n * offset; + QPolygonF polygon; + polygon.append(p2); + polygon.append(p3); + polygon.append(p4); + + painter->drawPolygon(polygon); + + if(factor != 1.0 || isDecibel) + { + QFont font = painter->font(); + font.setPixelSize(10); + painter->setFont(font); + QPointF center = (l.p1() + l.p2()) / 2; + QString text = QString("%1").arg(factor); + if(isDecibel) + text += " dB"; + + QSizeF size = painter->fontMetrics().size(0, text); + size += QSizeF(2, 0); + QRectF rect; + rect.setSize(size); + rect.moveCenter(center); + painter->setBrush(Qt::white); + painter->drawRoundedRect(rect.adjusted(-0.5, 0.5, 0.5, 0.5), 3, 3); + painter->drawText(rect, Qt::AlignCenter, text); + } + } +} + +void CopyFilterGUIConnectionItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) +{ + QString text = QString("%1").arg(factor); + if(isDecibel) + text += " dB"; + + ResizingLineEdit* lineEdit = new ResizingLineEdit(""); + connect(lineEdit, SIGNAL(editingFinished()), this, SLOT(lineEditEditingFinished())); + QGraphicsProxyWidget* proxyItem = scene()->addWidget(lineEdit); + QLineF l = line(); + QPointF center = (l.p1() + l.p2()) / 2; + QRectF rect; + rect.setSize(lineEdit->size()); + rect.moveCenter(center); + proxyItem->setPos(rect.topLeft()); + proxyItem->setZValue(10); + lineEdit->setFocus(); +} + +void CopyFilterGUIConnectionItem::hoverEnterEvent(QGraphicsSceneHoverEvent* event) +{ + setZValue(isSelected() ? 3 : 2); +} + +void CopyFilterGUIConnectionItem::hoverLeaveEvent(QGraphicsSceneHoverEvent* event) +{ + setZValue(isSelected() ? 1 : 0); +} + +QVariant CopyFilterGUIConnectionItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant& value) +{ + if(change == ItemSelectedHasChanged) + { + setZValue(isSelected() ? 1 : 0); + } + + return QGraphicsItem::itemChange(change, value); +} + +void CopyFilterGUIConnectionItem::lineEditEditingFinished() +{ + QLineEdit* lineEdit = qobject_cast(QObject::sender()); + // might be called twice + if(!lineEdit->isVisible()) + return; + + QString text = lineEdit->text(); + text.replace(',', '.'); + bool ok = false; + int lengthBefore = text.length(); + text.replace("db", "", Qt::CaseInsensitive); + double value = text.toDouble(&ok); + if(ok) + { + factor = value; + isDecibel = text.length() < lengthBefore; + } + + lineEdit->setVisible(false); + lineEdit->deleteLater(); + + emit ((CopyFilterGUIScene*)scene())->updateModel(); +} + +CopyFilterGUIChannelItem* CopyFilterGUIConnectionItem::getSource() const +{ + return source; +} + +CopyFilterGUIChannelItem* CopyFilterGUIConnectionItem::getTarget() const +{ + return target; +} + +double CopyFilterGUIConnectionItem::getFactor() const +{ + return factor; +} + +bool CopyFilterGUIConnectionItem::getIsDecibel() const +{ + return isDecibel; +} diff --git a/Editor/guis/CopyFilterGUIConnectionItem.h b/Editor/guis/CopyFilterGUIConnectionItem.h new file mode 100644 index 0000000..ff3b977 --- /dev/null +++ b/Editor/guis/CopyFilterGUIConnectionItem.h @@ -0,0 +1,64 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include + +class CopyFilterGUIChannelItem; + +class CopyFilterGUIConnectionItem : public QObject, public QGraphicsLineItem +{ + Q_OBJECT + +public: + CopyFilterGUIConnectionItem(); + CopyFilterGUIConnectionItem(CopyFilterGUIChannelItem* source, CopyFilterGUIChannelItem* target, double factor, bool isDecibel); + + enum { Type = UserType + 1 }; + virtual int type() const + { + return Type; + } + + QRectF boundingRect() const override; + QPainterPath shape() const override; + bool contains(const QPointF& point) const override; + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; + + CopyFilterGUIChannelItem* getSource() const; + CopyFilterGUIChannelItem* getTarget() const; + double getFactor() const; + bool getIsDecibel() const; + +protected: + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) override; + void hoverEnterEvent(QGraphicsSceneHoverEvent* event) override; + void hoverLeaveEvent(QGraphicsSceneHoverEvent* event) override; + QVariant itemChange(GraphicsItemChange change, const QVariant& value) override; + +private slots: + void lineEditEditingFinished(); + +private: + CopyFilterGUIChannelItem* source = NULL; + CopyFilterGUIChannelItem* target = NULL; + double factor = 1.0; + bool isDecibel = false; +}; diff --git a/Editor/guis/CopyFilterGUIFactory.cpp b/Editor/guis/CopyFilterGUIFactory.cpp new file mode 100644 index 0000000..3a96bb6 --- /dev/null +++ b/Editor/guis/CopyFilterGUIFactory.cpp @@ -0,0 +1,58 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "filters/CopyFilter.h" +#include "filters/CopyFilterFactory.h" +#include "CopyFilterGUI.h" +#include "CopyFilterGUIFactory.h" + +CopyFilterGUIFactory::CopyFilterGUIFactory() +{ +} + +QList CopyFilterGUIFactory::createFilterTemplates() +{ + QList list; + list.append(FilterTemplate(tr("Copy (Copy between channels)"), "Copy: ", QStringList(tr("Basic filters")))); + return list; +} + +IFilterGUI* CopyFilterGUIFactory::createFilterGUI(QString& command, QString& parameters) +{ + CopyFilterGUI* result = NULL; + + if(command == "Copy") + { + CopyFilterFactory factory; + std::vector filters = factory.createFilter(L"", command.toStdWString(), parameters.toStdWString()); + if(!filters.empty()) + { + CopyFilter* filter = (CopyFilter*)filters[0]; + result = new CopyFilterGUI(filter); + } + + for(IFilter* f : filters) + { + f->~IFilter(); + MemoryHelper::free(f); + } + } + + return result; +} diff --git a/Editor/guis/CopyFilterGUIFactory.h b/Editor/guis/CopyFilterGUIFactory.h new file mode 100644 index 0000000..32b3bb2 --- /dev/null +++ b/Editor/guis/CopyFilterGUIFactory.h @@ -0,0 +1,33 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/IFilterGUIFactory.h" + +class CopyFilterGUIFactory : public IFilterGUIFactory +{ + Q_OBJECT + +public: + CopyFilterGUIFactory(); + + QList createFilterTemplates() override; + IFilterGUI* createFilterGUI(QString& command, QString& parameters) override; +}; diff --git a/Editor/guis/CopyFilterGUIForm.cpp b/Editor/guis/CopyFilterGUIForm.cpp new file mode 100644 index 0000000..55f5401 --- /dev/null +++ b/Editor/guis/CopyFilterGUIForm.cpp @@ -0,0 +1,236 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include +#include +#include +#include +#include + +#include "CopyFilterGUIRow.h" +#include "CopyFilterGUIForm.h" + +using namespace std; + +CopyFilterGUIForm::CopyFilterGUIForm(QWidget *parent) : + QWidget(parent) +{ +} + +void CopyFilterGUIForm::load(vector assignments) +{ + if(gridLayout != NULL) + delete gridLayout; + + for(QObject* object : children()) + { + QWidget* widget = qobject_cast(object); + if(widget != NULL) + widget->setVisible(false); + object->deleteLater(); + } + + gridLayout = new QGridLayout(this); + gridLayout->setContentsMargins(2, 2, 2, 2); + gridLayout->setHorizontalSpacing(2); + gridLayout->setVerticalSpacing(2); + gridLayout->setColumnStretch(4, 1); + gridLayout->setColumnStretch(5, 1); + + int row = 0; + for(Assignment assignment : assignments) + { + QString oc = QString::fromStdWString(assignment.targetChannel); + + bool firstSummand = true; + for(Assignment::Summand summand : assignment.sourceSum) + { + if(firstSummand) + { + gridLayout->addWidget(new QLabel(tr("Channel"), this), row, 0); + + QComboBox* targetComboBox = new QComboBox(this); + targetComboBox->setEditable(true); + for(wstring channelName : inputChannelNames) + targetComboBox->addItem(QString::fromStdWString(channelName)); + targetComboBox->setEditText(oc); + connect(targetComboBox, SIGNAL(editTextChanged(QString)), this, SIGNAL(updateModel())); + connect(targetComboBox, SIGNAL(editTextChanged(QString)), this, SIGNAL(updateChannels())); + gridLayout->addWidget(targetComboBox, row, 1); + + gridLayout->addWidget(new QLabel("=", this), row, 2); + + firstSummand = false; + } + else + { + gridLayout->addWidget(new QLabel("+", this), row, 2); + } + + CopyFilterGUIRow* rowWidget = new CopyFilterGUIRow(summand, inputChannelNames, this); + connect(rowWidget, SIGNAL(updateModel()), this, SIGNAL(updateModel())); + connect(rowWidget, SIGNAL(updateChannels()), this, SIGNAL(updateChannels())); + gridLayout->addWidget(rowWidget, row, 3); + + QPushButton* addSummandButton = new QPushButton(this); + addSummandButton->setText(tr("Add summand")); + addSummandButton->setIcon(QIcon(":/icons/16x16/list-add-green.png")); + gridLayout->addWidget(addSummandButton, row, 4); + connect(addSummandButton, SIGNAL(pressed()), this, SLOT(addSummand())); + + QPushButton* removeButton = new QPushButton(this); + if(assignment.sourceSum.size() == 1) + removeButton->setText(tr("Remove assignment")); + else + removeButton->setText(tr("Remove summand")); + removeButton->setIcon(QIcon(":/icons/16x16/list-remove-red.png")); + gridLayout->addWidget(removeButton, row, 5); + connect(removeButton, SIGNAL(pressed()), this, SLOT(remove())); + + QSpacerItem* spacerItem = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum); + gridLayout->addItem(spacerItem, row, 6); + + row++; + } + } + + QPushButton* addAssignmentButton = new QPushButton(this); + addAssignmentButton->setText(tr("Add assignment")); + addAssignmentButton->setIcon(QIcon(":/icons/16x16/list-add-green.png")); + gridLayout->addWidget(addAssignmentButton, row++, 0, 1, 4); + connect(addAssignmentButton, SIGNAL(pressed()), this, SLOT(addAssignment())); +} + +void CopyFilterGUIForm::setChannelNames(const vector& channelNames) +{ + inputChannelNames = channelNames; + + for(int row = 0; row < gridLayout->rowCount() - 1; row++) + { + QLayoutItem* item = gridLayout->itemAtPosition(row, 1); + if(item != NULL) + { + QComboBox* targetComboBox = qobject_cast(item->widget()); + targetComboBox->blockSignals(true); + QString text = targetComboBox->currentText(); + targetComboBox->clear(); + for(wstring channelName : channelNames) + targetComboBox->addItem(QString::fromStdWString(channelName)); + targetComboBox->setEditText(text); + targetComboBox->blockSignals(false); + } + + QLayoutItem* summandItem = gridLayout->itemAtPosition(row, 3); + CopyFilterGUIRow* summandWidget = qobject_cast(summandItem->widget()); + summandWidget->setChannelNames(channelNames); + } +} + +vector CopyFilterGUIForm::buildAssignments(QWidget* pressedButton) +{ + vector assignments; + + Assignment* currentAssignment = NULL; + for(int row = 0; row < gridLayout->rowCount() - 1; row++) + { + QLayoutItem* item = gridLayout->itemAtPosition(row, 1); + if(item != NULL) + { + QComboBox* targetComboBox = qobject_cast(item->widget()); + QString oc = targetComboBox->currentText().trimmed(); + assignments.push_back(Assignment()); + currentAssignment = &assignments.back(); + currentAssignment->targetChannel = oc.toStdWString(); + } + + if(currentAssignment != NULL) + { + if(pressedButton != NULL) + { + QWidget* removeButton = gridLayout->itemAtPosition(row, 5)->widget(); + if(removeButton == pressedButton) + { + continue; + } + } + + QLayoutItem* summandItem = gridLayout->itemAtPosition(row, 3); + CopyFilterGUIRow* summandWidget = qobject_cast(summandItem->widget()); + currentAssignment->sourceSum.push_back(summandWidget->buildSummand()); + + if(pressedButton != NULL) + { + QWidget* addSummandButton = gridLayout->itemAtPosition(row, 4)->widget(); + if(addSummandButton == pressedButton) + { + Assignment::Summand newSummand; + newSummand.channel = L" "; + newSummand.factor = 1.0; + newSummand.isDecibel = false; + currentAssignment->sourceSum.push_back(newSummand); + } + } + } + } + + // remove empty assignments (can be caused by remove button) + assignments.erase(remove_if(assignments.begin(), assignments.end(), [](Assignment assignment) { + return assignment.sourceSum.empty(); + }), assignments.end()); + + return assignments; +} + +void CopyFilterGUIForm::addSummand() +{ + QWidget* addSummandButton = qobject_cast(sender()); + + vector assignments = buildAssignments(addSummandButton); + + load(assignments); +} + +void CopyFilterGUIForm::remove() +{ + QWidget* removeButton = qobject_cast(sender()); + + vector assignments = buildAssignments(removeButton); + + load(assignments); + + emit updateModel(); + emit updateChannels(); +} + +void CopyFilterGUIForm::addAssignment() +{ + vector assignments = buildAssignments(); + + Assignment newAssignment; + Assignment::Summand summand; + summand.channel = L" "; + summand.factor = 1.0; + summand.isDecibel = false; + newAssignment.sourceSum.push_back(summand); + + assignments.push_back(newAssignment); + + load(assignments); +} diff --git a/Editor/guis/CopyFilterGUIForm.h b/Editor/guis/CopyFilterGUIForm.h new file mode 100644 index 0000000..7cc325c --- /dev/null +++ b/Editor/guis/CopyFilterGUIForm.h @@ -0,0 +1,50 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include +#include +#include + +#include "filters/CopyFilter.h" + +class CopyFilterGUIForm : public QWidget +{ + Q_OBJECT +public: + explicit CopyFilterGUIForm(QWidget *parent = 0); + + void load(std::vector assignments); + void setChannelNames(const std::vector& channelNames); + std::vector buildAssignments(QWidget* pressedButton = NULL); + +signals: + void updateModel(); + void updateChannels(); + +private slots: + void addSummand(); + void remove(); + void addAssignment(); + +private: + QGridLayout* gridLayout = NULL; + std::vector inputChannelNames; +}; diff --git a/Editor/guis/CopyFilterGUIRow.cpp b/Editor/guis/CopyFilterGUIRow.cpp new file mode 100644 index 0000000..3a6d681 --- /dev/null +++ b/Editor/guis/CopyFilterGUIRow.cpp @@ -0,0 +1,107 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "CopyFilterGUIRow.h" +#include "ui_CopyFilterGUIRow.h" + +using namespace std; + +CopyFilterGUIRow::CopyFilterGUIRow(Assignment::Summand summand, std::vector channelNames, QWidget *parent) : + QWidget(parent), + ui(new Ui::CopyFilterGUIRow) +{ + ui->setupUi(this); + + ui->modeComboBox->addItem(tr("Channel"), 'c'); + ui->modeComboBox->addItem(tr("Factor * channel"), 'f'); + ui->modeComboBox->addItem(tr("dB factor * channel"), 'd'); + ui->modeComboBox->addItem(tr("Constant value"), 'v'); + + QChar mode; + if(summand.channel == L"") + mode = 'v'; + else if(summand.isDecibel) + mode = 'd'; + else if(summand.factor != 1.0) + mode = 'f'; + else + mode = 'c'; + + ui->modeComboBox->setCurrentIndex(ui->modeComboBox->findData(mode)); + + ui->factorSpinBox->setValue(summand.factor); + + for(wstring channelName : channelNames) + ui->channelComboBox->addItem(QString::fromStdWString(channelName)); + ui->channelComboBox->setEditText(QString::fromStdWString(summand.channel).trimmed()); + + connect(ui->modeComboBox, SIGNAL(activated(int)), this, SIGNAL(updateModel())); + connect(ui->factorSpinBox, SIGNAL(valueChanged(double)), this, SIGNAL(updateModel())); + connect(ui->channelComboBox, SIGNAL(editTextChanged(QString)), this, SIGNAL(updateModel())); + connect(ui->channelComboBox, SIGNAL(editTextChanged(QString)), this, SIGNAL(updateChannels())); +} + +CopyFilterGUIRow::~CopyFilterGUIRow() +{ + delete ui; +} + +void CopyFilterGUIRow::setChannelNames(const vector& channelNames) +{ + ui->channelComboBox->blockSignals(true); + QString text = ui->channelComboBox->currentText(); + ui->channelComboBox->clear(); + for(wstring channelName : channelNames) + ui->channelComboBox->addItem(QString::fromStdWString(channelName)); + ui->channelComboBox->setEditText(text); + ui->channelComboBox->blockSignals(false); +} + +Assignment::Summand CopyFilterGUIRow::buildSummand() +{ + Assignment::Summand summand; + + QChar mode = ui->modeComboBox->currentData().toChar(); + if(mode != 'c') + summand.factor = ui->factorSpinBox->value(); + else + summand.factor = 1.0; + // fix for rounding error when using the spin buttons + if(abs(summand.factor) < 1e-10) + summand.factor = 0.0; + + if(mode != 'v') + summand.channel = ui->channelComboBox->currentText().trimmed().toStdWString(); + // allow channel mode to be remembered when reloading + if(mode == 'c' && summand.channel == L"") + summand.channel = L" "; + + summand.isDecibel = mode == 'd'; + + return summand; +} + +void CopyFilterGUIRow::on_modeComboBox_currentIndexChanged(int index) +{ + QChar mode = ui->modeComboBox->itemData(index).toChar(); + ui->factorSpinBox->setVisible(mode != 'c'); + ui->asteriskLabel->setVisible(mode == 'f' || mode == 'd'); + ui->channelComboBox->setVisible(mode != 'v'); + ui->factorSpinBox->setSuffix(mode == 'd' ? " dB" : ""); +} diff --git a/Editor/guis/CopyFilterGUIRow.h b/Editor/guis/CopyFilterGUIRow.h new file mode 100644 index 0000000..52a3e5a --- /dev/null +++ b/Editor/guis/CopyFilterGUIRow.h @@ -0,0 +1,50 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include + +#include "filters/CopyFilter.h" + +namespace Ui { +class CopyFilterGUIRow; +} + +class CopyFilterGUIRow : public QWidget +{ + Q_OBJECT + +public: + explicit CopyFilterGUIRow(Assignment::Summand summand, std::vector channelNames, QWidget *parent = 0); + ~CopyFilterGUIRow(); + + void setChannelNames(const std::vector& channelNames); + Assignment::Summand buildSummand(); + +signals: + void updateModel(); + void updateChannels(); + +private slots: + void on_modeComboBox_currentIndexChanged(int index); + +private: + Ui::CopyFilterGUIRow *ui; +}; diff --git a/Editor/guis/CopyFilterGUIRow.ui b/Editor/guis/CopyFilterGUIRow.ui new file mode 100644 index 0000000..0646b14 --- /dev/null +++ b/Editor/guis/CopyFilterGUIRow.ui @@ -0,0 +1,63 @@ + + + CopyFilterGUIRow + + + + 0 + 0 + 523 + 128 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 4 + + + -99.989999999999995 + + + 0.100000000000000 + + + + + + + * + + + + + + + true + + + + + + + + diff --git a/Editor/guis/CopyFilterGUIScene.cpp b/Editor/guis/CopyFilterGUIScene.cpp new file mode 100644 index 0000000..6fd55c7 --- /dev/null +++ b/Editor/guis/CopyFilterGUIScene.cpp @@ -0,0 +1,217 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include +#include +#include +#include + +#include "Editor/widgets/ResizingLineEdit.h" +#include "CopyFilterGUIChannelItem.h" +#include "CopyFilterGUIScene.h" + +using namespace std; + +void CopyFilterGUIScene::load(const vector& channelNames, vector assignments) +{ + clear(); + + QHash inputChannelMap; + QHash outputChannelMap; + + QGraphicsItem* lastInputItem = NULL; + lastOutputItem = NULL; + + for(unsigned i = 0; i < channelNames.size(); i++) + { + QString c = QString::fromStdWString(channelNames[i]); + + CopyFilterGUIChannelItem* inputItem = new CopyFilterGUIChannelItem(c, false); + inputItem->setPos(getNextChannelPoint(lastInputItem, false)); + addItem(inputItem); + + inputChannelMap.insert(c, inputItem); + inputChannelMap.insert(QString().setNum(i), inputItem); + lastInputItem = inputItem; + + CopyFilterGUIChannelItem* outputItem = new CopyFilterGUIChannelItem(c, true); + outputItem->setPos(getNextChannelPoint(lastOutputItem, true)); + addItem(outputItem); + + outputChannelMap.insert(c, outputItem); + outputChannelMap.insert(QString().setNum(i), outputItem); + lastOutputItem = outputItem; + } + + CopyFilterGUIChannelItem* noChannelItem = new CopyFilterGUIChannelItem("", false); + noChannelItem->setPos(getNextChannelPoint(lastInputItem, false)); + addItem(noChannelItem); + inputChannelMap.insert("", noChannelItem); + + for(Assignment assignment : assignments) + { + QString oc = QString::fromStdWString(assignment.targetChannel); + if(oc == "") + continue; + CopyFilterGUIChannelItem* outputItem = outputChannelMap.value(oc.toUpper()); + if(outputItem == NULL) + { + outputItem = new CopyFilterGUIChannelItem(oc, true); + outputItem->setPos(getNextChannelPoint(lastOutputItem, true)); + addItem(outputItem); + + outputChannelMap.insert(oc, outputItem); + lastOutputItem = outputItem; + } + for(Assignment::Summand summand : assignment.sourceSum) + { + QString ic = QString::fromStdWString(summand.channel); + if(ic == " ") + continue; + CopyFilterGUIChannelItem* inputItem = inputChannelMap.value(ic.toUpper()); + if(inputItem == NULL) + { + inputItem = new CopyFilterGUIChannelItem(ic, false); + inputItem->setPos(getNextChannelPoint(lastInputItem, false)); + addItem(inputItem); + + inputChannelMap.insert(ic, inputItem); + lastInputItem = inputItem; + } + + CopyFilterGUIConnectionItem* connItem = new CopyFilterGUIConnectionItem(inputItem, outputItem, summand.factor, summand.isDecibel); + addItem(connItem); + } + } + + QToolButton* addButton = new QToolButton; + addButton->setIcon(QIcon(":/icons/16x16/list-add-green.png")); + connect(addButton, SIGNAL(clicked()), this, SLOT(addOutputChannel())); + addProxyItem = addWidget(addButton); + addProxyItem->setPos(getNextChannelPoint(lastOutputItem, true)); + + setSceneRect(itemsBoundingRect().marginsAdded(QMarginsF(2, 2, 2, 2))); +} + +std::vector CopyFilterGUIScene::buildAssignments() +{ + std::vector connItems; + + for(QGraphicsItem* item : items()) + { + CopyFilterGUIConnectionItem* connItem = qgraphicsitem_cast(item); + if(connItem != NULL && connItem->getTarget() != NULL) + { + connItems.push_back(connItem); + } + } + + std::sort(connItems.begin(), connItems.end(), [](CopyFilterGUIConnectionItem * a, CopyFilterGUIConnectionItem * b) + { + double diff = a->getTarget()->x() - b->getTarget()->x(); + if(diff == 0.0) + diff = a->getSource()->x() - b->getSource()->x(); + + return diff < 0; + }); + + std::vector assignments; + QHash channelAssignmentMap; + for(CopyFilterGUIConnectionItem* connItem : connItems) + { + QString oc = connItem->getTarget()->getName(); + Assignment* assignment = channelAssignmentMap.value(oc); + if(assignment == NULL) + { + Assignment newAssignment; + newAssignment.targetChannel = oc.toStdWString(); + assignments.push_back(newAssignment); + assignment = &assignments.back(); + channelAssignmentMap.insert(oc, assignment); + } + + QString ic = connItem->getSource()->getName(); + Assignment::Summand summand; + summand.channel = ic.toStdWString(); + summand.factor = connItem->getFactor(); + summand.isDecibel = connItem->getIsDecibel(); + assignment->sourceSum.push_back(summand); + } + + return assignments; +} + +void CopyFilterGUIScene::addOutputChannel() +{ + ResizingLineEdit* lineEdit = new ResizingLineEdit(""); + connect(lineEdit, SIGNAL(editingFinished()), this, SLOT(lineEditEditingFinished())); + QGraphicsProxyWidget* proxyItem = addWidget(lineEdit); + QPointF point = getNextChannelPoint(lastOutputItem, true); + QRectF rect; + rect.setSize(lineEdit->size()); + rect.moveTo(point); + proxyItem->setPos(rect.topLeft()); + proxyItem->setZValue(10); + lineEdit->setFocus(); + addProxyItem->setVisible(false); +} + +void CopyFilterGUIScene::lineEditEditingFinished() +{ + QLineEdit* lineEdit = qobject_cast(QObject::sender()); + // might be called twice + if(!lineEdit->isVisible()) + return; + + QString text = lineEdit->text(); + + CopyFilterGUIChannelItem* item = new CopyFilterGUIChannelItem(text, true); + item->setPos(getNextChannelPoint(lastOutputItem, true)); + addItem(item); + lastOutputItem = item; + + addProxyItem->setPos(getNextChannelPoint(lastOutputItem, true)); + addProxyItem->setVisible(true); + + lineEdit->setVisible(false); + lineEdit->deleteLater(); +} + +void CopyFilterGUIScene::keyPressEvent(QKeyEvent* event) +{ + // focusItem != NULL means that the cursor is inside a line edit + if(event->key() == Qt::Key_Delete && focusItem() == NULL) + { + for(QGraphicsItem* item : selectedItems()) + { + CopyFilterGUIConnectionItem* connectionItem = qgraphicsitem_cast(item); + if(connectionItem != NULL) + { + removeItem(connectionItem); + delete connectionItem; + } + } + + emit updateModel(); + emit updateChannels(); + } + + QGraphicsScene::keyPressEvent(event); +} diff --git a/Editor/guis/CopyFilterGUIScene.h b/Editor/guis/CopyFilterGUIScene.h new file mode 100644 index 0000000..dae8aec --- /dev/null +++ b/Editor/guis/CopyFilterGUIScene.h @@ -0,0 +1,50 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include +#include + +#include "Editor/widgets/ChannelGraphScene.h" +#include "filters/CopyFilter.h" + +class CopyFilterGUIScene : public ChannelGraphScene +{ + Q_OBJECT + +public: + void load(const std::vector& channelNames, std::vector assignments); + std::vector buildAssignments(); + +signals: + void updateModel(); + void updateChannels(); + +protected: + void keyPressEvent(QKeyEvent* event) override; + +private slots: + void addOutputChannel(); + void lineEditEditingFinished(); + +private: + QGraphicsItem* lastOutputItem; + QGraphicsProxyWidget* addProxyItem; +}; diff --git a/Editor/guis/DelayFilterGUI.cpp b/Editor/guis/DelayFilterGUI.cpp new file mode 100644 index 0000000..1b84678 --- /dev/null +++ b/Editor/guis/DelayFilterGUI.cpp @@ -0,0 +1,71 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "DelayFilterGUI.h" +#include "ui_DelayFilterGUI.h" + +static const double dialSteps = 1000; +static const double dialMin = 1.0; +static const double dialMax = 10000.0; + +DelayFilterGUI::DelayFilterGUI(double delay, bool isMs) : + ui(new Ui::DelayFilterGUI) +{ + ui->setupUi(this); + + ui->unitComboBox->setCurrentIndex(isMs ? 0 : 1); + ui->delaySpinBox->setValue(delay); +} + +DelayFilterGUI::~DelayFilterGUI() +{ + delete ui; +} + +void DelayFilterGUI::store(QString& command, QString& parameters) +{ + command = "Delay"; + parameters = QString("%0").arg(ui->delaySpinBox->value()); + if(ui->unitComboBox->currentIndex() == 0) + parameters += " ms"; + else + parameters += " samples"; +} + +void DelayFilterGUI::on_unitComboBox_currentIndexChanged(int index) +{ + ui->delaySpinBox->setSuffix(index == 0 ? tr(" ms") : tr(" samples")); + ui->delaySpinBox->setDecimals(index == 0 ? 2 : 0); + + emit updateModel(); +} + +void DelayFilterGUI::on_delayDial_valueChanged(int value) +{ + ui->delaySpinBox->setValue(pow(dialMax / dialMin, value / dialSteps) * dialMin); +} + +void DelayFilterGUI::on_delaySpinBox_valueChanged(double value) +{ + bool previousValue = ui->delayDial->blockSignals(true); + ui->delayDial->setValue(round(dialSteps * log(value / dialMin) / log(dialMax / dialMin))); + ui->delayDial->blockSignals(previousValue); + + emit updateModel(); +} diff --git a/Editor/guis/DelayFilterGUI.h b/Editor/guis/DelayFilterGUI.h new file mode 100644 index 0000000..1107cd1 --- /dev/null +++ b/Editor/guis/DelayFilterGUI.h @@ -0,0 +1,47 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/IFilterGUI.h" + +namespace Ui { +class DelayFilterGUI; +} + +class DelayFilterGUI : public IFilterGUI +{ + Q_OBJECT + +public: + explicit DelayFilterGUI(double delay, bool isMs); + ~DelayFilterGUI(); + + void store(QString& command, QString& parameters) override; + +private slots: + void on_unitComboBox_currentIndexChanged(int index); + + void on_delayDial_valueChanged(int value); + + void on_delaySpinBox_valueChanged(double value); + +private: + Ui::DelayFilterGUI *ui; +}; diff --git a/Editor/guis/DelayFilterGUI.ui b/Editor/guis/DelayFilterGUI.ui new file mode 100644 index 0000000..0af92d5 --- /dev/null +++ b/Editor/guis/DelayFilterGUI.ui @@ -0,0 +1,96 @@ + + + DelayFilterGUI + + + + 0 + 0 + 262 + 66 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + ms + + + 1000000.000000000000000 + + + + + + + + 0 + 0 + + + + Delay: + + + + + + + + 0 + 66 + + + + 1000 + + + + + + + + Time + + + + + Samples + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + diff --git a/Editor/guis/DelayFilterGUIFactory.cpp b/Editor/guis/DelayFilterGUIFactory.cpp new file mode 100644 index 0000000..1987547 --- /dev/null +++ b/Editor/guis/DelayFilterGUIFactory.cpp @@ -0,0 +1,58 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "filters/DelayFilter.h" +#include "filters/DelayFilterFactory.h" +#include "DelayFilterGUI.h" +#include "DelayFilterGUIFactory.h" + +DelayFilterGUIFactory::DelayFilterGUIFactory() +{ +} + +QList DelayFilterGUIFactory::createFilterTemplates() +{ + QList list; + list.append(FilterTemplate(tr("Delay"), "Delay: 0 ms", QStringList(tr("Basic filters")))); + return list; +} + +IFilterGUI* DelayFilterGUIFactory::createFilterGUI(QString& command, QString& parameters) +{ + DelayFilterGUI* result = NULL; + + if(command == "Delay") + { + DelayFilterFactory factory; + std::vector filters = factory.createFilter(L"", command.toStdWString(), parameters.toStdWString()); + if(!filters.empty()) + { + DelayFilter* filter = (DelayFilter*)filters[0]; + result = new DelayFilterGUI(filter->getDelay(), filter->getIsMs()); + } + + for(IFilter* f : filters) + { + f->~IFilter(); + MemoryHelper::free(f); + } + } + + return result; +} diff --git a/Editor/guis/DelayFilterGUIFactory.h b/Editor/guis/DelayFilterGUIFactory.h new file mode 100644 index 0000000..332dedc --- /dev/null +++ b/Editor/guis/DelayFilterGUIFactory.h @@ -0,0 +1,33 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/IFilterGUIFactory.h" + +class DelayFilterGUIFactory : public IFilterGUIFactory +{ + Q_OBJECT + +public: + explicit DelayFilterGUIFactory(); + + QList createFilterTemplates() override; + IFilterGUI*createFilterGUI(QString& command, QString& parameters) override;; +}; diff --git a/Editor/guis/DeviceFilterGUI.cpp b/Editor/guis/DeviceFilterGUI.cpp new file mode 100644 index 0000000..0d8919d --- /dev/null +++ b/Editor/guis/DeviceFilterGUI.cpp @@ -0,0 +1,129 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include "DeviceFilterGUI.h" +#include "DeviceFilterGUIDialog.h" +#include +#include "ui_DeviceFilterGUI.h" + +using namespace std; + +DeviceFilterGUI::DeviceFilterGUI(DeviceFilterGUIFactory* factory): + ui(new Ui::DeviceFilterGUI) +{ + ui->setupUi(this); + this->factory = factory; +} + +DeviceFilterGUI::~DeviceFilterGUI() +{ + delete ui; +} + +void DeviceFilterGUI::load(const QString& parameters) +{ + pattern = parameters; + + ui->treeWidget->clear(); + + QStringList labels; + labels.append(tr("Type")); + labels.append(tr("Connection")); + labels.append(tr("Device")); + labels.append(tr("State")); + ui->treeWidget->setHeaderLabels(labels); + + QTreeWidgetItem* lastItem = NULL; + + if(parameters.trimmed() == "all") + { + QStringList values; + values.append(""); + values.append(tr("All devices")); + values.append(""); + + lastItem = new QTreeWidgetItem(ui->treeWidget, values); + } + else + { + const QList& devices = factory->getDevices(); + for(const DeviceAPOInfo& apoInfo : devices) + { + if(!apoInfo.isInstalled) + continue; + + wstring deviceString = apoInfo.connectionName + L" " + apoInfo.deviceName + L" " + apoInfo.deviceGuid; + if(DeviceFilterFactory::matchDevice(deviceString, pattern.toStdWString())) + { + QStringList values; + if(apoInfo.isInput) + values.append(tr("Capture")); + else + values.append(tr("Playback")); + values.append(QString::fromStdWString(apoInfo.connectionName)); + values.append(QString::fromStdWString(apoInfo.deviceName)); + QString state; + if(apoInfo.isInstalled) + state = tr("APO installed"); + else + state = tr("APO not installed"); + values.append(state); + lastItem = new QTreeWidgetItem(ui->treeWidget, values); + } + } + + if(ui->treeWidget->topLevelItemCount() == 0) + { + QStringList values; + values.append(""); + values.append(tr("No device matched!")); + values.append(""); + + QTreeWidgetItem* item = new QTreeWidgetItem(ui->treeWidget, values); + item->setForeground(1, QBrush(Qt::red)); + lastItem = item; + } + } + + for(int i = 0; i < ui->treeWidget->columnCount(); i++) + ui->treeWidget->resizeColumnToContents(i); + + int headerHeight = ui->treeWidget->header()->height(); + int itemAreaHeight = 0; + if(lastItem != NULL) + itemAreaHeight = ui->treeWidget->visualItemRect(lastItem).bottom() + 1; + ui->treeWidget->setFixedHeight(headerHeight + itemAreaHeight + 3); +} + +void DeviceFilterGUI::store(QString& command, QString& parameters) +{ + command = "Device"; + parameters = pattern; +} + +void DeviceFilterGUI::on_pushButton_clicked() +{ + DeviceFilterGUIDialog dialog(this, factory, pattern); + if(dialog.exec() == QDialog::Accepted) + { + load(dialog.getPattern()); + emit updateModel(); + } +} diff --git a/Editor/guis/DeviceFilterGUI.h b/Editor/guis/DeviceFilterGUI.h new file mode 100644 index 0000000..faaa119 --- /dev/null +++ b/Editor/guis/DeviceFilterGUI.h @@ -0,0 +1,46 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/IFilterGUI.h" +#include "DeviceFilterGUIFactory.h" + +namespace Ui { +class DeviceFilterGUI; +} + +class DeviceFilterGUI : public IFilterGUI +{ + Q_OBJECT + +public: + explicit DeviceFilterGUI(DeviceFilterGUIFactory* factory); + ~DeviceFilterGUI(); + void load(const QString& parameters); + void store(QString& command, QString& parameters) override; + +private slots: + void on_pushButton_clicked(); + +private: + Ui::DeviceFilterGUI *ui; + DeviceFilterGUIFactory* factory; + QString pattern; +}; diff --git a/Editor/guis/DeviceFilterGUI.ui b/Editor/guis/DeviceFilterGUI.ui new file mode 100644 index 0000000..ee3cb4e --- /dev/null +++ b/Editor/guis/DeviceFilterGUI.ui @@ -0,0 +1,106 @@ + + + DeviceFilterGUI + + + + 0 + 0 + 400 + 71 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 10 + + + + QAbstractScrollArea::AdjustToContents + + + false + + + 3 + + + + 1 + + + + + 2 + + + + + 3 + + + + + + + + 0 + + + + + + 0 + 0 + + + + Selected devices: + + + true + + + + + + + Change... + + + + + + + + + + diff --git a/Editor/guis/DeviceFilterGUIDialog.cpp b/Editor/guis/DeviceFilterGUIDialog.cpp new file mode 100644 index 0000000..ab6dc87 --- /dev/null +++ b/Editor/guis/DeviceFilterGUIDialog.cpp @@ -0,0 +1,128 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "DeviceFilterGUIDialog.h" +#include "ui_DeviceFilterGUIDialog.h" + +#include + +using namespace std; + +DeviceFilterGUIDialog::DeviceFilterGUIDialog(DeviceFilterGUI* gui, DeviceFilterGUIFactory* factory, const QString& pattern) : + QDialog(gui), + ui(new Ui::DeviceFilterGUIDialog) +{ + ui->setupUi(this); + + bool all = pattern.trimmed() == "all"; + ui->allDevicesCheckBox->setChecked(all); + + QStringList labels; + labels.append(tr("Connection")); + labels.append(tr("Device")); + labels.append(tr("State")); + ui->treeWidget->setHeaderLabels(labels); + QTreeWidgetItem* outputNode = new QTreeWidgetItem(ui->treeWidget, QStringList(tr("Playback devices"))); + outputNode->setExpanded(true); + QTreeWidgetItem* inputNode = new QTreeWidgetItem(ui->treeWidget, QStringList(tr("Capture devices"))); + inputNode->setExpanded(true); + const QList& devices = factory->getDevices(); + for(const DeviceAPOInfo& apoInfo : devices) + { + QStringList values; + values.append(QString::fromStdWString(apoInfo.connectionName)); + values.append(QString::fromStdWString(apoInfo.deviceName)); + QString state; + if(apoInfo.isInstalled) + state = tr("APO installed"); + else + state = tr("APO not installed"); + values.append(state); + QTreeWidgetItem* item = new QTreeWidgetItem(apoInfo.isInput ? inputNode : outputNode, values); + + wstring deviceString = apoInfo.connectionName + L" " + apoInfo.deviceName + L" " + apoInfo.deviceGuid; + bool matches = !all && DeviceFilterFactory::matchDevice(deviceString, pattern.toStdWString()); + item->setCheckState(0, matches ? Qt::Checked : Qt::Unchecked); + item->setData(0, Qt::UserRole, QVariant::fromValue(&apoInfo)); + item->setHidden(!matches && !apoInfo.isInstalled && ui->showOnlyInstalledCheckBox->isChecked()); + if(!apoInfo.isInstalled) + for(int i = 0; i < ui->treeWidget->columnCount(); i++) + item->setForeground(i, QBrush(Qt::gray)); + } + + for(int i = 0; i < ui->treeWidget->columnCount(); i++) + ui->treeWidget->resizeColumnToContents(i); +} + +DeviceFilterGUIDialog::~DeviceFilterGUIDialog() +{ + delete ui; +} + +QString DeviceFilterGUIDialog::getPattern() +{ + if(ui->allDevicesCheckBox->isChecked()) + { + return "all"; + } + else + { + QString pattern = ""; + for(int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) + { + QTreeWidgetItem* groupItem = ui->treeWidget->topLevelItem(i); + for(int j = 0; j < groupItem->childCount(); j++) + { + QTreeWidgetItem* item = groupItem->child(j); + if(item->checkState(0) == Qt::Checked) + { + if(pattern != "") + pattern += "; "; + const DeviceAPOInfo* apoInfo = item->data(0, Qt::UserRole).value(); + wstring deviceString = apoInfo->connectionName + L" " + apoInfo->deviceName + L" " + apoInfo->deviceGuid; + pattern += QString::fromStdWString(deviceString); + } + } + } + + return pattern; + } +} + +void DeviceFilterGUIDialog::on_allDevicesCheckBox_toggled(bool checked) +{ + ui->treeWidget->setEnabled(!checked); +} + +void DeviceFilterGUIDialog::on_showOnlyInstalledCheckBox_toggled(bool checked) +{ + for(int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) + { + QTreeWidgetItem* groupItem = ui->treeWidget->topLevelItem(i); + for(int j = 0; j < groupItem->childCount(); j++) + { + QTreeWidgetItem* item = groupItem->child(j); + const DeviceAPOInfo* apoInfo = item->data(0, Qt::UserRole).value(); + item->setHidden(item->checkState(0) != Qt::Checked && !apoInfo->isInstalled && checked); + } + } + + for(int i = 0; i < ui->treeWidget->columnCount(); i++) + ui->treeWidget->resizeColumnToContents(i); +} diff --git a/Editor/guis/DeviceFilterGUIDialog.h b/Editor/guis/DeviceFilterGUIDialog.h new file mode 100644 index 0000000..b573b72 --- /dev/null +++ b/Editor/guis/DeviceFilterGUIDialog.h @@ -0,0 +1,50 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include + +#include "DeviceFilterGUI.h" +#include "DeviceFilterGUIFactory.h" + +namespace Ui { +class DeviceFilterGUIDialog; +} + +class DeviceFilterGUIDialog : public QDialog +{ + Q_OBJECT + +public: + explicit DeviceFilterGUIDialog( DeviceFilterGUI* gui, DeviceFilterGUIFactory* factory, const QString& pattern); + ~DeviceFilterGUIDialog(); + + QString getPattern(); + +private slots: + void on_allDevicesCheckBox_toggled(bool checked); + + void on_showOnlyInstalledCheckBox_toggled(bool checked); + +private: + Ui::DeviceFilterGUIDialog *ui; +}; + +Q_DECLARE_METATYPE(const DeviceAPOInfo*) diff --git a/Editor/guis/DeviceFilterGUIDialog.ui b/Editor/guis/DeviceFilterGUIDialog.ui new file mode 100644 index 0000000..e345c77 --- /dev/null +++ b/Editor/guis/DeviceFilterGUIDialog.ui @@ -0,0 +1,103 @@ + + + DeviceFilterGUIDialog + + + + 0 + 0 + 500 + 350 + + + + Device selection + + + + + + 3 + + + + 1 + + + + + 2 + + + + + 3 + + + + + + + + Select all devices + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Show only devices with APO installed + + + true + + + + + + + + + buttonBox + accepted() + DeviceFilterGUIDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + DeviceFilterGUIDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/Editor/guis/DeviceFilterGUIFactory.cpp b/Editor/guis/DeviceFilterGUIFactory.cpp new file mode 100644 index 0000000..3174fc9 --- /dev/null +++ b/Editor/guis/DeviceFilterGUIFactory.cpp @@ -0,0 +1,57 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "Editor/FilterTable.h" +#include "DeviceFilterGUI.h" +#include "DeviceFilterGUIFactory.h" + +DeviceFilterGUIFactory::DeviceFilterGUIFactory() +{ +} + +void DeviceFilterGUIFactory::initialize(FilterTable* filterTable) +{ + devices.append(filterTable->getOutputDevices()); + devices.append(filterTable->getInputDevices()); +} + +QList DeviceFilterGUIFactory::createFilterTemplates() +{ + QList list; + list.append(FilterTemplate(tr("Device (Select device)"), "Device: all", QStringList(tr("Control")))); + return list; +} + +IFilterGUI* DeviceFilterGUIFactory::createFilterGUI(QString& command, QString& parameters) +{ + DeviceFilterGUI* result = NULL; + + if(command == "Device") + { + result = new DeviceFilterGUI(this); + result->load(parameters); + } + + return result; +} + +const QList& DeviceFilterGUIFactory::getDevices() const +{ + return devices; +} diff --git a/Editor/guis/DeviceFilterGUIFactory.h b/Editor/guis/DeviceFilterGUIFactory.h new file mode 100644 index 0000000..1456558 --- /dev/null +++ b/Editor/guis/DeviceFilterGUIFactory.h @@ -0,0 +1,41 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "DeviceAPOInfo.h" +#include "Editor/IFilterGUIFactory.h" + +class DeviceFilterGUIFactory : public IFilterGUIFactory +{ + Q_OBJECT + +public: + DeviceFilterGUIFactory(); + + void initialize(FilterTable* filterTable) override; + + QList createFilterTemplates() override; + IFilterGUI* createFilterGUI(QString& command, QString& parameters) override; + + const QList& getDevices() const; + +private: + QList devices; +}; diff --git a/Editor/guis/ExpressionFilterGUIFactory.cpp b/Editor/guis/ExpressionFilterGUIFactory.cpp new file mode 100644 index 0000000..9c564bd --- /dev/null +++ b/Editor/guis/ExpressionFilterGUIFactory.cpp @@ -0,0 +1,29 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "ExpressionFilterGUIFactory.h" + +IFilterGUI* ExpressionFilterGUIFactory::createFilterGUI(QString& command, QString& parameters) +{ + // do not create a gui if parameters contain expressions + if(parameters.contains('`')) + command = ""; + + return NULL; +} diff --git a/Editor/guis/ExpressionFilterGUIFactory.h b/Editor/guis/ExpressionFilterGUIFactory.h new file mode 100644 index 0000000..624b3a5 --- /dev/null +++ b/Editor/guis/ExpressionFilterGUIFactory.h @@ -0,0 +1,28 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/IFilterGUIFactory.h" + +class ExpressionFilterGUIFactory : public IFilterGUIFactory +{ +public: + IFilterGUI* createFilterGUI(QString& command, QString& parameters) override; +}; diff --git a/Editor/guis/GraphicEQFilterGUI.cpp b/Editor/guis/GraphicEQFilterGUI.cpp new file mode 100644 index 0000000..8b02311 --- /dev/null +++ b/Editor/guis/GraphicEQFilterGUI.cpp @@ -0,0 +1,389 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include +#include +#include + +#include "helpers/GainIterator.h" +#include "GraphicEQFilterGUIView.h" +#include "GraphicEQFilterGUI.h" +#include "ui_GraphicEQFilterGUI.h" + +using namespace std; + +QRegularExpression GraphicEQFilterGUI::numberRegEx("[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?"); + +GraphicEQFilterGUI::GraphicEQFilterGUI(GraphicEQFilter* filter, QString configPath) : + ui(new Ui::GraphicEQFilterGUI), configPath(configPath) +{ + ui->setupUi(this); + + setFixedHeight(150); + + scene = new GraphicEQFilterGUIScene(ui->graphicsView); + ui->graphicsView->setScene(scene); + + ui->toolBar->addAction(ui->actionImport); + ui->toolBar->addAction(ui->actionExport); + ui->toolBar->addAction(ui->actionMirrorResponse); + ui->toolBar->addAction(ui->actionNormalizeResponse); + ui->toolBar->addAction(ui->actionResetResponse); + + connect(scene, SIGNAL(nodeInserted(int, double, double)), this, SLOT(insertRow(int, double, double))); + connect(scene, SIGNAL(nodeRemoved(int)), this, SLOT(removeRow(int))); + connect(scene, SIGNAL(nodeUpdated(int, double, double)), this, SLOT(updateRow(int, double, double))); + connect(scene, SIGNAL(nodeMoved(int, int)), this, SLOT(moveRow(int, int))); + connect(scene, SIGNAL(nodeSelectionChanged(int, bool)), this, SLOT(selectRow(int, bool))); + connect(scene, SIGNAL(updateModel()), this, SIGNAL(updateModel())); + + scene->setNodes(filter->getNodes()); + + int bandCount = scene->verifyBands(filter->getNodes()); + switch(bandCount) + { + case 15: + ui->radioButton15->click(); + break; + case 31: + ui->radioButton31->click(); + break; + default: + ui->radioButtonVar->click(); + } + + ui->tableWidget->resizeColumnsToContents(); +} + +GraphicEQFilterGUI::~GraphicEQFilterGUI() +{ + delete ui; +} + +void GraphicEQFilterGUI::store(QString& command, QString& parameters) +{ + command = "GraphicEQ"; + + bool first = true; + for(FilterNode node : scene->getNodes()) + { + if(first) + first = false; + else + parameters += "; "; + + parameters += QString("%0 %1").arg(node.freq).arg(node.dbGain); + } +} + +void GraphicEQFilterGUI::insertRow(int index, double hz, double db) +{ + ui->tableWidget->blockSignals(true); + ui->tableWidget->insertRow(index); + QTableWidgetItem* freqItem = new QTableWidgetItem(QString("%0").arg(hz)); + ui->tableWidget->setItem(index, 0, freqItem); + + QTableWidgetItem* gainItem = new QTableWidgetItem(QString("%0").arg(db)); + ui->tableWidget->setItem(index, 1, gainItem); + ui->tableWidget->blockSignals(false); +} + +void GraphicEQFilterGUI::removeRow(int index) +{ + ui->tableWidget->blockSignals(true); + ui->tableWidget->removeRow(index); + ui->tableWidget->blockSignals(false); +} + +void GraphicEQFilterGUI::updateRow(int index, double hz, double db) +{ + ui->tableWidget->blockSignals(true); + QTableWidgetItem* freqItem = ui->tableWidget->item(index, 0); + freqItem->setText(QString("%0").arg(hz)); + + QTableWidgetItem* gainItem = ui->tableWidget->item(index, 1); + gainItem->setText(QString("%0").arg(db)); + ui->tableWidget->blockSignals(false); +} + +void GraphicEQFilterGUI::moveRow(int fromIndex, int toIndex) +{ + ui->tableWidget->blockSignals(true); + QTableWidgetItem* freqItem = ui->tableWidget->takeItem(fromIndex, 0); + QTableWidgetItem* gainItem = ui->tableWidget->takeItem(fromIndex, 1); + bool selected = ui->tableWidget->selectionModel()->isRowSelected(fromIndex, ui->tableWidget->rootIndex()); + ui->tableWidget->removeRow(fromIndex); + ui->tableWidget->insertRow(toIndex); + ui->tableWidget->setItem(toIndex, 0, freqItem); + ui->tableWidget->setItem(toIndex, 1, gainItem); + if(selected) + { + QModelIndex modelIndex = ui->tableWidget->model()->index(toIndex, 0); + ui->tableWidget->selectionModel()->select(modelIndex, QItemSelectionModel::Rows | QItemSelectionModel::Select); + ui->tableWidget->scrollTo(modelIndex); + } + ui->tableWidget->blockSignals(false); +} + +void GraphicEQFilterGUI::selectRow(int index, bool select) +{ + ui->tableWidget->blockSignals(true); + QItemSelectionModel::SelectionFlags command = QItemSelectionModel::Rows; + if(select) + command |= QItemSelectionModel::Select; + else + command |= QItemSelectionModel::Deselect; + QModelIndex modelIndex = ui->tableWidget->model()->index(index, 0); + ui->tableWidget->selectionModel()->select(modelIndex, command); + ui->tableWidget->scrollTo(modelIndex); + ui->tableWidget->blockSignals(false); +} + +void GraphicEQFilterGUI::on_tableWidget_cellChanged(int row, int column) +{ + if(column == 0) + { + QTableWidgetItem* freqItem = ui->tableWidget->item(row, 0); + bool ok; + double freq = freqItem->text().toDouble(&ok); + if(ok) + { + double gain = scene->getNodes()[row].dbGain; + scene->setNode(row, freq, gain); + } + else + { + freq = scene->getNodes()[row].freq; + freqItem->setText(QString("%0").arg(freq)); + } + } + else if(column == 1) + { + QTableWidgetItem* gainItem = ui->tableWidget->item(row, 1); + bool ok; + double gain = gainItem->text().toDouble(&ok); + if(ok) + { + double freq = scene->getNodes()[row].freq; + scene->setNode(row, freq, gain); + } + else + { + gain = scene->getNodes()[row].dbGain; + gainItem->setText(QString("%0").arg(gain)); + } + } +} + +void GraphicEQFilterGUI::on_tableWidget_itemSelectionChanged() +{ + QSet selectedRows; + for(QTableWidgetItem* item : ui->tableWidget->selectedItems()) + selectedRows.insert(item->row()); + + scene->setSelectedNodes(selectedRows); +} + +void GraphicEQFilterGUI::on_radioButton15_toggled(bool checked) +{ + if(checked) + { + scene->setBandCount(15); + setFreqEditable(false); + } +} + +void GraphicEQFilterGUI::on_radioButton31_toggled(bool checked) +{ + if(checked) + { + scene->setBandCount(31); + setFreqEditable(false); + } +} + +void GraphicEQFilterGUI::on_radioButtonVar_toggled(bool checked) +{ + if(checked) + { + scene->setBandCount(-1); + setFreqEditable(true); + } +} + +void GraphicEQFilterGUI::setFreqEditable(bool editable) +{ + ui->tableWidget->blockSignals(true); + for(int i = 0; i < ui->tableWidget->rowCount(); i++) + { + QTableWidgetItem* item = ui->tableWidget->item(i, 0); + if(editable) + item->setFlags(item->flags() | Qt::ItemIsEditable | Qt::ItemIsEnabled); + else + item->setFlags(item->flags() & ~(Qt::ItemIsEditable | Qt::ItemIsEnabled)); + } + ui->tableWidget->blockSignals(false); +} + +void GraphicEQFilterGUI::on_actionImport_triggered() +{ + QFileInfo fileInfo(configPath); + QFileDialog dialog(this, tr("Import frequency response"), fileInfo.absolutePath(), "*.csv"); + dialog.setFileMode(QFileDialog::ExistingFiles); + QStringList nameFilters; + nameFilters.append(tr("Frequency response (*.csv)")); + nameFilters.append(tr("All files (*.*)")); + dialog.setNameFilters(nameFilters); + if(dialog.exec() == QDialog::Accepted) + { + vector newNodes; + + for(QString path : dialog.selectedFiles()) + { + QFile file(path); + if(file.open(QFile::ReadOnly)) + { + QTextStream stream(&file); + QString text = stream.readAll(); + if(!text.contains('.')) + text = text.replace(',', '.'); + QRegularExpressionMatchIterator it = numberRegEx.globalMatch(text); + while(it.hasNext()) + { + QRegularExpressionMatch match = it.next(); + QString freqString = match.captured(); + bool ok; + double freq = freqString.toDouble(&ok); + if(ok && it.hasNext()) + { + match = it.next(); + QString gainString = match.captured(); + double gain = gainString.toDouble(&ok); + if(ok) + newNodes.push_back(FilterNode(freq, gain)); + } + } + } + } + sort(newNodes.begin(), newNodes.end()); + + scene->setNodes(newNodes); + + int bandCount = scene->verifyBands(newNodes); + if(bandCount != scene->getBandCount()) + { + switch(bandCount) + { + case 15: + ui->radioButton15->click(); + break; + case 31: + ui->radioButton31->click(); + break; + default: + ui->radioButtonVar->click(); + } + } + else + { + emit updateModel(); + } + + ui->tableWidget->resizeColumnsToContents(); + } +} + +void GraphicEQFilterGUI::on_actionExport_triggered() +{ + QFileInfo fileInfo(configPath); + QFileDialog dialog(this, tr("Export frequency response"), fileInfo.absolutePath(), "*.csv"); + dialog.setFileMode(QFileDialog::AnyFile); + dialog.setAcceptMode(QFileDialog::AcceptSave); + QStringList nameFilters; + nameFilters.append(tr("Frequency response (*.csv)")); + nameFilters.append(tr("All files (*.*)")); + dialog.setNameFilters(nameFilters); + dialog.setDefaultSuffix(".csv"); + + if(dialog.exec() == QDialog::Accepted) + { + QString savePath = dialog.selectedFiles().first(); + + QFile file(savePath); + if(file.open(QFile::WriteOnly | QFile::Truncate)) + { + QTextStream stream(&file); + for(FilterNode node : scene->getNodes()) + { + stream << node.freq << '\t' << node.dbGain << '\n'; + } + stream.flush(); + } + } +} + +void GraphicEQFilterGUI::on_actionMirrorResponse_triggered() +{ + vector newNodes = scene->getNodes(); + for(FilterNode& node : newNodes) + node.dbGain = -node.dbGain; + + scene->setNodes(newNodes); + emit updateModel(); +} + +void GraphicEQFilterGUI::on_actionNormalizeResponse_triggered() +{ + vector newNodes = scene->getNodes(); + + double maxGain = -DBL_MAX; + for(FilterNode node : newNodes) + { + if(node.dbGain > maxGain) + maxGain = node.dbGain; + } + + if(maxGain != 0) + { + for(FilterNode& node : newNodes) + node.dbGain -= maxGain; + + scene->setNodes(newNodes); + emit updateModel(); + } +} + +void GraphicEQFilterGUI::on_actionResetResponse_triggered() +{ + vector newNodes = scene->getNodes(); + QSet selectedIndices = scene->getSelectedIndices(); + + int index = 0; + for(FilterNode& node : newNodes) + { + if(selectedIndices.isEmpty() || selectedIndices.contains(index)) + node.dbGain = 0.0; + index++; + } + + scene->setNodes(newNodes); + scene->setSelectedNodes(selectedIndices); + emit updateModel(); +} diff --git a/Editor/guis/GraphicEQFilterGUI.h b/Editor/guis/GraphicEQFilterGUI.h new file mode 100644 index 0000000..00f07e5 --- /dev/null +++ b/Editor/guis/GraphicEQFilterGUI.h @@ -0,0 +1,73 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include + +#include "Editor/IFilterGUI.h" +#include "filters/GraphicEQFilter.h" +#include "GraphicEQFilterGUIScene.h" + +namespace Ui { +class GraphicEQFilterGUI; +} + +class GraphicEQFilterGUI : public IFilterGUI +{ + Q_OBJECT + +public: + explicit GraphicEQFilterGUI(GraphicEQFilter* filter, QString configPath); + ~GraphicEQFilterGUI(); + + void store(QString& command, QString& parameters) override; + +private slots: + void insertRow(int index, double hz, double db); + void removeRow(int index); + void updateRow(int index, double hz, double db); + void moveRow(int fromIndex, int toIndex); + void selectRow(int index, bool select); + + void on_tableWidget_cellChanged(int row, int column); + void on_tableWidget_itemSelectionChanged(); + + void on_radioButton15_toggled(bool checked); + void on_radioButton31_toggled(bool checked); + void on_radioButtonVar_toggled(bool checked); + + void on_actionImport_triggered(); + + void on_actionExport_triggered(); + + void on_actionMirrorResponse_triggered(); + + void on_actionNormalizeResponse_triggered(); + + void on_actionResetResponse_triggered(); + +private: + void setFreqEditable(bool editable); + + Ui::GraphicEQFilterGUI *ui; + GraphicEQFilterGUIScene* scene; + QString configPath; + static QRegularExpression numberRegEx; +}; diff --git a/Editor/guis/GraphicEQFilterGUI.ui b/Editor/guis/GraphicEQFilterGUI.ui new file mode 100644 index 0000000..e48ab7a --- /dev/null +++ b/Editor/guis/GraphicEQFilterGUI.ui @@ -0,0 +1,247 @@ + + + GraphicEQFilterGUI + + + + 0 + 0 + 442 + 170 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 3 + + + 0 + + + + + + 0 + 0 + + + + Graphic EQ: + + + + + + + + 0 + 0 + + + + + + + + + 51 + 153 + 255 + + + + + + + + + 122 + 189 + 255 + + + + + + + + + 51 + 153 + 255 + + + + + + + + QAbstractScrollArea::AdjustToContents + + + QAbstractItemView::ScrollPerPixel + + + false + + + 2 + + + false + + + 40 + + + false + + + 40 + + + 20 + + + + Freq. + + + + + Gain + + + + + + + + + 16 + 16 + + + + + + + + QPainter::Antialiasing|QPainter::TextAntialiasing + + + QGraphicsView::RubberBandDrag + + + + + + + 6 + + + + + 15-band + + + + + + + 31-band + + + + + + + variable + + + true + + + + + + + + + + :/icons/16x16/document-open.png:/icons/16x16/document-open.png + + + Import + + + + + + :/icons/16x16/document-save-as.png:/icons/16x16/document-save-as.png + + + Export + + + + + + :/icons/mirror_response.png:/icons/mirror_response.png + + + Mirror response + + + + + + :/icons/normalize_response.png:/icons/normalize_response.png + + + Normalize response + + + + + + :/icons/reset_response.png:/icons/reset_response.png + + + Reset response + + + + + + GraphicEQFilterGUIView + QGraphicsView +
GraphicEQFilterGUIView.h
+ + itemMoved() + +
+
+ + + + + + updateItemList() + +
diff --git a/Editor/guis/GraphicEQFilterGUIFactory.cpp b/Editor/guis/GraphicEQFilterGUIFactory.cpp new file mode 100644 index 0000000..988c427 --- /dev/null +++ b/Editor/guis/GraphicEQFilterGUIFactory.cpp @@ -0,0 +1,65 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "filters/GraphicEQFilter.h" +#include "filters/GraphicEQFilterFactory.h" +#include "GraphicEQFilterGUI.h" +#include "GraphicEQFilterGUIFactory.h" + +GraphicEQFilterGUIFactory::GraphicEQFilterGUIFactory() +{ +} + +QList GraphicEQFilterGUIFactory::createFilterTemplates() +{ + QList list; + list.append(FilterTemplate(tr("15-band graphic equalizer"), "GraphicEQ: 25 0; 40 0; 63 0; 100 0; 160 0; 250 0; 400 0; 630 0; 1000 0; 1600 0; 2500 0; 4000 0; 6300 0; 10000 0; 16000 0", QStringList(tr("Graphic equalizers")))); + list.append(FilterTemplate(tr("31-band graphic equalizer"), "GraphicEQ: 20 0; 25 0; 31.5 0; 40 0; 50 0; 63 0; 80 0; 100 0; 125 0; 160 0; 200 0; 250 0; 315 0; 400 0; 500 0; 630 0; 800 0; 1000 0; 1250 0; 1600 0; 2000 0; 2500 0; 3150 0; 4000 0; 5000 0; 6300 0; 8000 0; 10000 0; 12500 0; 16000 0; 20000 0", QStringList(tr("Graphic equalizers")))); + list.append(FilterTemplate(tr("Graphic equalizer with variable bands"), "GraphicEQ: ", QStringList(tr("Graphic equalizers")))); + return list; +} + +void GraphicEQFilterGUIFactory::startOfFile(const QString& configPath) +{ + this->configPath = configPath; +} + +IFilterGUI* GraphicEQFilterGUIFactory::createFilterGUI(QString& command, QString& parameters) +{ + GraphicEQFilterGUI* result = NULL; + + if(command == "GraphicEQ") + { + GraphicEQFilterFactory factory; + std::vector filters = factory.createFilter(L"", command.toStdWString(), parameters.toStdWString()); + if(!filters.empty()) + { + GraphicEQFilter* filter = (GraphicEQFilter*)filters[0]; + result = new GraphicEQFilterGUI(filter, configPath); + } + + for(IFilter* f : filters) + { + f->~IFilter(); + MemoryHelper::free(f); + } + } + + return result; +} diff --git a/Editor/guis/GraphicEQFilterGUIFactory.h b/Editor/guis/GraphicEQFilterGUIFactory.h new file mode 100644 index 0000000..b8f5435 --- /dev/null +++ b/Editor/guis/GraphicEQFilterGUIFactory.h @@ -0,0 +1,38 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/IFilterGUIFactory.h" + +class GraphicEQFilterGUIFactory : public IFilterGUIFactory +{ + Q_OBJECT + +public: + GraphicEQFilterGUIFactory(); + + QList createFilterTemplates() override; + + void startOfFile(const QString& configPath) override; + IFilterGUI* createFilterGUI(QString& command, QString& parameters) override; + +private: + QString configPath; +}; diff --git a/Editor/guis/GraphicEQFilterGUIItem.cpp b/Editor/guis/GraphicEQFilterGUIItem.cpp new file mode 100644 index 0000000..f832325 --- /dev/null +++ b/Editor/guis/GraphicEQFilterGUIItem.cpp @@ -0,0 +1,103 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include + +#include "GraphicEQFilterGUIScene.h" +#include "GraphicEQFilterGUIItem.h" + +static double size = 10; + +GraphicEQFilterGUIItem::GraphicEQFilterGUIItem(int index, double hz, double db) + : FrequencyPlotItem(hz, db), index(index) +{ + setFlag(ItemIsMovable); + setFlag(ItemIsSelectable); +} + +QRectF GraphicEQFilterGUIItem::boundingRect() const +{ + return QRectF(-size / 2, -size / 2, size, size); +} + +QPainterPath GraphicEQFilterGUIItem::shape() const +{ + QPainterPath path; + path.addEllipse(boundingRect()); + return path; +} + +void GraphicEQFilterGUIItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) +{ + painter->setBrush(Qt::white); + if(isSelected()) + painter->setBrush(QColor(38, 147, 255)); + painter->drawPath(shape()); + if(index < 99) + { + if(isSelected()) + painter->setPen(Qt::white); + + QFont font; + font.setPixelSize(9); + font.setLetterSpacing(QFont::AbsoluteSpacing, -1); + QFontMetrics metrics(font); + painter->setFont(font); + QString text = QString::number(index + 1); + painter->drawText(-metrics.width(text) / 2, metrics.boundingRect('0').height() / 2, text); + } +} + +QVariant GraphicEQFilterGUIItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant& value) +{ + QVariant result = QGraphicsItem::itemChange(change, value); + + if(change == ItemScenePositionHasChanged) + { + GraphicEQFilterGUIScene* s = qobject_cast(scene()); + s->itemMoved(index); + } + else if(change == ItemSelectedHasChanged) + { + GraphicEQFilterGUIScene* s = qobject_cast(scene()); + s->itemSelectionChanged(index, value.toBool()); + } + else if(change == ItemPositionChange) + { + GraphicEQFilterGUIScene* s = qobject_cast(scene()); + if(s->getBandCount() != -1) + { + QPointF newPos = value.toPointF(); + newPos.setX(s->hzToX(getHz())); + result = newPos; + } + } + + return result; +} +int GraphicEQFilterGUIItem::getIndex() const +{ + return index; +} + +void GraphicEQFilterGUIItem::setIndex(int value) +{ + index = value; +} + diff --git a/Editor/guis/GraphicEQFilterGUIItem.h b/Editor/guis/GraphicEQFilterGUIItem.h new file mode 100644 index 0000000..ed3397a --- /dev/null +++ b/Editor/guis/GraphicEQFilterGUIItem.h @@ -0,0 +1,47 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/widgets/FrequencyPlotItem.h" + +class GraphicEQFilterGUIItem: public FrequencyPlotItem +{ +public: + GraphicEQFilterGUIItem(int index, double hz, double db); + + enum { Type = UserType + 2 }; + virtual int type() const + { + return Type; + } + + QRectF boundingRect() const override; + QPainterPath shape() const override; + void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override; + + int getIndex() const; + void setIndex(int value); + +protected: + QVariant itemChange(GraphicsItemChange change, const QVariant& value) override; + +private: + int index; +}; diff --git a/Editor/guis/GraphicEQFilterGUIScene.cpp b/Editor/guis/GraphicEQFilterGUIScene.cpp new file mode 100644 index 0000000..7525753 --- /dev/null +++ b/Editor/guis/GraphicEQFilterGUIScene.cpp @@ -0,0 +1,371 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include +#include + +#include "Editor/widgets/FrequencyPlotView.h" +#include "GraphicEQFilterGUIScene.h" + +using namespace std; + +GraphicEQFilterGUIScene::GraphicEQFilterGUIScene(QObject* parent) : + FrequencyPlotScene(parent) +{ +} + +void GraphicEQFilterGUIScene::setNodes(const std::vector& nodes) +{ + noUpdateModel = true; + + for(int i = (int)this->nodes.size() - 1; i >= 0; i--) + removeNode(i); + + this->nodes = nodes; + + int i = 0; + for(FilterNode node : nodes) + { + GraphicEQFilterGUIItem* item = new GraphicEQFilterGUIItem(i, node.freq, node.dbGain); + addItem(item); + item->updatePos(); + items.append(item); + emit nodeInserted(i, node.freq, node.dbGain); + i++; + } + + update(); + + noUpdateModel = false; +} + +void GraphicEQFilterGUIScene::addNode(double hz, double db) +{ + FilterNode node(hz, db); + vector::iterator it = lower_bound(nodes.begin(), nodes.end(), node); + int index = it - nodes.begin(); + nodes.insert(it, node); + + GraphicEQFilterGUIItem* item = new GraphicEQFilterGUIItem(index, hz, db); + addItem(item); + item->updatePos(); + items.insert(index, item); + for(int i = index + 1; i < items.size(); i++) + items[i]->setIndex(i); + emit nodeInserted(index, hz, db); + + QRectF rect = sceneRect(); + if(index > 0) + rect.setLeft(items[index - 1]->x()); + if(index + 1 < items.size()) + rect.setRight(items[index + 1]->x()); + update(rect); + for(int i = index + 1; i < items.size(); i++) + items[i]->update(); + + if(!noUpdateModel) + emit updateModel(); +} + +void GraphicEQFilterGUIScene::removeNode(int index) +{ + nodes.erase(nodes.begin() + index); + GraphicEQFilterGUIItem* item = items[index]; + removeItem(item); + items.removeAt(index); + for(int i = index; i < items.size(); i++) + items[i]->setIndex(i); + emit nodeRemoved(index); + + QRectF rect = sceneRect(); + if(index > 0) + rect.setLeft(items[index - 1]->x()); + if(index < items.size()) + rect.setRight(items[index]->x()); + update(rect); + for(int i = index; i < items.size(); i++) + items[i]->update(); + + if(!noUpdateModel) + emit updateModel(); +} + +void GraphicEQFilterGUIScene::setNode(int index, double hz, double db) +{ + GraphicEQFilterGUIItem* item = items[index]; + item->setHz(hz); + item->setDb(db); + item->updatePos(); + + FilterNode node(hz, db); + vector::iterator it = lower_bound(nodes.begin(), nodes.end(), node); + int newIndex = it - nodes.begin(); + if(newIndex > index) + newIndex--; + if(newIndex != index) + { + items.move(index, newIndex); + if(newIndex < index) + { + for(int i = index; i > newIndex; i--) + nodes[i] = nodes[i - 1]; + for(int i = newIndex; i <= index; i++) + items[i]->setIndex(i); + emit nodeMoved(index, newIndex); + } + else + { + for(int i = index; i < newIndex; i++) + nodes[i] = nodes[i + 1]; + for(int i = index; i <= newIndex; i++) + items[i]->setIndex(i); + emit nodeMoved(index, newIndex); + } + } + nodes[newIndex] = node; + emit nodeUpdated(newIndex, hz, db); + + QRectF rect = sceneRect(); + int lowerIndex = min(index, newIndex); + int upperIndex = max(index, newIndex); + if(lowerIndex > 0) + rect.setLeft(items[lowerIndex - 1]->x()); + if(upperIndex + 1 < items.size()) + rect.setRight(items[upperIndex + 1]->x()); + update(rect); + + if(!noUpdateModel) + emit updateModel(); +} + +void GraphicEQFilterGUIScene::setSelectedNodes(QSet indices) +{ + for(GraphicEQFilterGUIItem* item : items) + item->setSelected(indices.contains(item->getIndex())); +} + +QSet GraphicEQFilterGUIScene::getSelectedIndices() +{ + QSet set; + + for(GraphicEQFilterGUIItem* item : items) + if(item->isSelected()) + set.insert(item->getIndex()); + + return set; +} + +void GraphicEQFilterGUIScene::itemMoved(int index) +{ + GraphicEQFilterGUIItem* item = items[index]; + double db = yToDb(item->scenePos().y()); + double oldHz = item->getHz(); + double hz; + if(getBandCount() != -1) + { + hz = oldHz; + } + else + { + hz = xToHz(item->scenePos().x()); + if(hz >= 10000) + hz = round(hz); + else + hz = round(hz * 10) / 10; + item->setHz(hz); + } + if(getZoom() < 4) + db = round(db * 10) / 10; + else + db = round(db * 100) / 100; + item->setDb(db); + + int newIndex = index; + if(hz < oldHz) + { + while(newIndex > 0 && nodes[newIndex - 1].freq > hz) + { + nodes[newIndex] = nodes[newIndex - 1]; + items[newIndex] = items[newIndex - 1]; + items[newIndex]->setIndex(newIndex); + newIndex--; + } + } + else if(hz > oldHz) + { + while(newIndex < (int)nodes.size() - 1 && nodes[newIndex + 1].freq < hz) + { + nodes[newIndex] = nodes[newIndex + 1]; + items[newIndex] = items[newIndex + 1]; + items[newIndex]->setIndex(newIndex); + newIndex++; + } + } + nodes[newIndex] = FilterNode(hz, db); + if(newIndex != index) + { + items[newIndex] = item; + item->setIndex(newIndex); + emit nodeMoved(index, newIndex); + } + emit nodeUpdated(newIndex, hz, db); + + QRectF rect = sceneRect(); + int lowerIndex = min(index, newIndex); + int upperIndex = max(index, newIndex); + if(lowerIndex > 0) + rect.setLeft(items[lowerIndex - 1]->x()); + if(upperIndex + 1 < items.size()) + rect.setRight(items[upperIndex + 1]->x()); + update(rect); + + if(!noUpdateModel) + emit updateModel(); +} + +void GraphicEQFilterGUIScene::itemSelectionChanged(int index, bool selected) +{ + emit nodeSelectionChanged(index, selected); +} + +void GraphicEQFilterGUIScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) +{ + if(getBandCount() != -1) + return; + + double hz = xToHz(event->scenePos().x()); + double db = yToDb(event->scenePos().y()); + addNode(hz, db); +} + +void GraphicEQFilterGUIScene::keyPressEvent(QKeyEvent* event) +{ + switch(event->key()) + { + case Qt::Key_Delete: + if(getBandCount() == -1) + { + for(QGraphicsItem* item : selectedItems()) + { + GraphicEQFilterGUIItem* plotItem = qgraphicsitem_cast(item); + if(plotItem != NULL) + { + // this works for multiple items because the index of the other items is updated inside removeNode + int index = plotItem->getIndex(); + removeNode(index); + } + } + } + break; + + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_Left: + case Qt::Key_Right: + for(QGraphicsItem* item : selectedItems()) + { + GraphicEQFilterGUIItem* plotItem = qgraphicsitem_cast(item); + if(plotItem != NULL) + { + int index = plotItem->getIndex(); + FilterNode node = nodes[index]; + switch(event->key()) + { + case Qt::Key_Up: + node.dbGain += 1; + break; + case Qt::Key_Down: + node.dbGain -= 1; + break; + case Qt::Key_Left: + node.freq -= 1; + break; + case Qt::Key_Right: + node.freq += 1; + break; + } + setNode(index, node.freq, node.dbGain); + } + } + break; + + case Qt::Key_A: + if(event->modifiers() & Qt::ControlModifier) + { + QPainterPath painterPath; + painterPath.addRect(sceneRect()); + setSelectionArea(painterPath); + } + break; + } +} + +vector& GraphicEQFilterGUIScene::getNodes() +{ + return nodes; +} + +int GraphicEQFilterGUIScene::verifyBands(const std::vector& nodes) +{ + const vector& bands = getBands((int)nodes.size()); + + int bandCount = -1; + if(!bands.empty()) + { + bandCount = (int)nodes.size(); + for(unsigned i = 0; i < nodes.size(); i++) + { + if(abs(nodes[i].freq - bands[i]) > 0.1) + { + bandCount = -1; + break; + } + } + } + + return bandCount; +} + +void GraphicEQFilterGUIScene::setBandCount(int value) +{ + if(value != getBandCount()) + { + const vector& bands = getBands(value); + if(!bands.empty()) + { + vector newNodes; + GainIterator gainIterator(nodes); + for(double band : bands) + { + double dbGain = gainIterator.gainAt(band); + dbGain = round(dbGain * 100) / 100; + FilterNode node(band, dbGain); + newNodes.push_back(node); + } + + setNodes(newNodes); + } + + FrequencyPlotScene::setBandCount(value); + + if(!noUpdateModel) + emit updateModel(); + } +} diff --git a/Editor/guis/GraphicEQFilterGUIScene.h b/Editor/guis/GraphicEQFilterGUIScene.h new file mode 100644 index 0000000..3e86a6f --- /dev/null +++ b/Editor/guis/GraphicEQFilterGUIScene.h @@ -0,0 +1,63 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include + +#include "Editor/widgets/FrequencyPlotScene.h" +#include "helpers/GainIterator.h" +#include "GraphicEQFilterGUIItem.h" + +class GraphicEQFilterGUIScene : public FrequencyPlotScene +{ + Q_OBJECT +public: + explicit GraphicEQFilterGUIScene(QObject *parent = 0); + void setNodes(const std::vector& nodes); + void addNode(double hz, double db); + void removeNode(int index); + void setNode(int index, double hz, double db); + void setSelectedNodes(QSet indices); + QSet getSelectedIndices(); + void itemMoved(int index); + void itemSelectionChanged(int index, bool selected); + + std::vector& getNodes(); + int verifyBands(const std::vector& nodes); + void setBandCount(int value) override; + +signals: + void nodeInserted(int index, double hz, double db); + void nodeRemoved(int index); + void nodeUpdated(int index, double hz, double db); + void nodeMoved(int fromIndex, int toIndex); + void nodeSelectionChanged(int index, bool selected); + void updateModel(); + +protected: + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event) override; + void keyPressEvent(QKeyEvent* event) override; + +private: + std::vector nodes; + QList items; + + bool noUpdateModel = false; +}; diff --git a/Editor/guis/GraphicEQFilterGUIView.cpp b/Editor/guis/GraphicEQFilterGUIView.cpp new file mode 100644 index 0000000..7334b5a --- /dev/null +++ b/Editor/guis/GraphicEQFilterGUIView.cpp @@ -0,0 +1,59 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "GraphicEQFilterGUIScene.h" +#include "GraphicEQFilterGUIView.h" + +GraphicEQFilterGUIView::GraphicEQFilterGUIView(QWidget *parent) : + FrequencyPlotView(parent) +{ +} + +void GraphicEQFilterGUIView::drawBackground(QPainter* painter, const QRectF& rect) +{ + FrequencyPlotView::drawBackground(painter, rect); + + painter->setRenderHint(QPainter::Antialiasing, true); + GraphicEQFilterGUIScene* s = qobject_cast(scene()); + std::vector& nodes = s->getNodes(); + GainIterator gainIterator(nodes); + QPainterPath path; + bool first = true; + double lastDb = -1000; + for(int x = rect.left() - 1; x <= rect.right() + 1; x++) + { + double hz = s->xToHz(x); + double db = gainIterator.gainAt(hz); + double y = s->dbToY(db); + if(db == lastDb) + y = floor(y) + 0.5; + lastDb = db; + if(first) + { + path.moveTo(x, y); + first = false; + } + else + { + path.lineTo(x, y); + } + } + painter->setPen(Qt::black); + painter->drawPath(path); +} diff --git a/Editor/guis/GraphicEQFilterGUIView.h b/Editor/guis/GraphicEQFilterGUIView.h new file mode 100644 index 0000000..2d0afc2 --- /dev/null +++ b/Editor/guis/GraphicEQFilterGUIView.h @@ -0,0 +1,33 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/widgets/FrequencyPlotView.h" + +class GraphicEQFilterGUIView : public FrequencyPlotView +{ + Q_OBJECT + +public: + explicit GraphicEQFilterGUIView(QWidget *parent = 0); + +protected: + void drawBackground(QPainter* painter, const QRectF& rect) override; +}; diff --git a/Editor/guis/IncludeFilterGUI.cpp b/Editor/guis/IncludeFilterGUI.cpp new file mode 100644 index 0000000..925738e --- /dev/null +++ b/Editor/guis/IncludeFilterGUI.cpp @@ -0,0 +1,85 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include + +#include "IncludeFilterGUI.h" +#include "ui_IncludeFilterGUI.h" + +IncludeFilterGUI::IncludeFilterGUI(FilterTable* filterTable, const QString& path) : + ui(new Ui::IncludeFilterGUI), filterTable(filterTable) +{ + ui->setupUi(this); + + ui->pathLineEdit->setText(path); +} + +IncludeFilterGUI::~IncludeFilterGUI() +{ + delete ui; +} + +void IncludeFilterGUI::store(QString& command, QString& parameters) +{ + command = "Include"; + parameters = ui->pathLineEdit->text(); +} + +void IncludeFilterGUI::on_selectFileToolButton_clicked() +{ + QFileInfo fileInfo(filterTable->getConfigPath()); + QDir configDir = fileInfo.absoluteDir(); + QString path = ui->pathLineEdit->text(); + if(path.length() > 0) + fileInfo.setFile(configDir, path); + + QFileDialog dialog(this, tr("Include file"), fileInfo.absolutePath(), "*.txt"); + dialog.setFileMode(QFileDialog::ExistingFile); + dialog.setNameFilter(tr("E-APO configurations (*.txt)")); + if(path.length() > 0) + dialog.selectFile(fileInfo.fileName()); + if(dialog.exec() == QDialog::Accepted) + { + QString absolutePath = dialog.selectedFiles().first(); + QString relativePath = configDir.relativeFilePath(absolutePath); + if(relativePath.startsWith("../../")) + relativePath = absolutePath; + ui->pathLineEdit->setText(QDir::toNativeSeparators(relativePath)); + + emit updateModel(); + } +} + +void IncludeFilterGUI::on_pathLineEdit_editingFinished() +{ + emit updateModel(); +} + +void IncludeFilterGUI::on_openFileToolButton_clicked() +{ + QFileInfo fileInfo(filterTable->getConfigPath()); + QDir configDir = fileInfo.absoluteDir(); + QString path = ui->pathLineEdit->text(); + if(path.length() > 0) + { + fileInfo.setFile(configDir, path); + + filterTable->openConfig(fileInfo.absoluteFilePath()); + } +} diff --git a/Editor/guis/IncludeFilterGUI.h b/Editor/guis/IncludeFilterGUI.h new file mode 100644 index 0000000..261be2e --- /dev/null +++ b/Editor/guis/IncludeFilterGUI.h @@ -0,0 +1,47 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/IFilterGUI.h" +#include "Editor/FilterTable.h" + +namespace Ui { +class IncludeFilterGUI; +} + +class IncludeFilterGUI : public IFilterGUI +{ + Q_OBJECT + +public: + explicit IncludeFilterGUI(FilterTable* filterTable, const QString& path); + ~IncludeFilterGUI(); + + void store(QString& command, QString& parameters) override; + +private slots: + void on_selectFileToolButton_clicked(); + void on_pathLineEdit_editingFinished(); + void on_openFileToolButton_clicked(); + +private: + Ui::IncludeFilterGUI *ui; + FilterTable* filterTable; +}; diff --git a/Editor/guis/IncludeFilterGUI.ui b/Editor/guis/IncludeFilterGUI.ui new file mode 100644 index 0000000..1d89a2f --- /dev/null +++ b/Editor/guis/IncludeFilterGUI.ui @@ -0,0 +1,87 @@ + + + IncludeFilterGUI + + + + 0 + 0 + 400 + 22 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Include: + + + + + + + + + + Select file + + + + :/icons/16x16/document-open.png:/icons/16x16/document-open.png + + + + + + + Qt::Horizontal + + + + 1 + 1 + + + + + + + + Open file in editor + + + + :/icons/16x16/go-up.png:/icons/16x16/go-up.png + + + + + + + + ResizingLineEdit + QLineEdit +
Editor/widgets/ResizingLineEdit.h
+
+
+ + + + +
diff --git a/Editor/guis/IncludeFilterGUIFactory.cpp b/Editor/guis/IncludeFilterGUIFactory.cpp new file mode 100644 index 0000000..7fda895 --- /dev/null +++ b/Editor/guis/IncludeFilterGUIFactory.cpp @@ -0,0 +1,49 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "IncludeFilterGUI.h" +#include "IncludeFilterGUIFactory.h" + +IncludeFilterGUIFactory::IncludeFilterGUIFactory() +{ +} + +void IncludeFilterGUIFactory::initialize(FilterTable* filterTable) +{ + this->filterTable = filterTable; +} + +QList IncludeFilterGUIFactory::createFilterTemplates() +{ + QList list; + list.append(FilterTemplate(tr("Include (Include configuration file)"), "Include:", QStringList(tr("Control")))); + return list; +} + +IFilterGUI* IncludeFilterGUIFactory::createFilterGUI(QString& command, QString& parameters) +{ + IncludeFilterGUI* result = NULL; + + if(command == "Include") + { + result = new IncludeFilterGUI(filterTable, parameters.trimmed()); + } + + return result; +} diff --git a/Editor/guis/IncludeFilterGUIFactory.h b/Editor/guis/IncludeFilterGUIFactory.h new file mode 100644 index 0000000..70a062a --- /dev/null +++ b/Editor/guis/IncludeFilterGUIFactory.h @@ -0,0 +1,38 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/IFilterGUIFactory.h" + +class IncludeFilterGUIFactory : public IFilterGUIFactory +{ + Q_OBJECT +public: + explicit IncludeFilterGUIFactory(); + + void initialize(FilterTable* filterTable) override; + + QList createFilterTemplates() override; + + IFilterGUI* createFilterGUI(QString& command, QString& parameters) override; + +private: + FilterTable* filterTable; +}; diff --git a/Editor/guis/PreampFilterGUI.cpp b/Editor/guis/PreampFilterGUI.cpp new file mode 100644 index 0000000..1f18e0a --- /dev/null +++ b/Editor/guis/PreampFilterGUI.cpp @@ -0,0 +1,58 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2014 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include + +#include "PreampFilterGUI.h" +#include "ui_PreampFilterGUI.h" + +using namespace std; + +PreampFilterGUI::PreampFilterGUI(double dbGain): + ui(new Ui::PreampFilterGUI) +{ + ui->setupUi(this); + + ui->doubleSpinBox->setValue(dbGain); +} + +PreampFilterGUI::~PreampFilterGUI() +{ + delete ui; +} + +void PreampFilterGUI::store(QString& command, QString& parameters) +{ + command = "Preamp"; + parameters = QString("%1 dB").arg(ui->doubleSpinBox->value()); +} + +void PreampFilterGUI::on_dial_valueChanged(int value) +{ + ui->doubleSpinBox->setValue(value * 0.1); +} + +void PreampFilterGUI::on_doubleSpinBox_valueChanged(double value) +{ + bool previousValue = ui->dial->blockSignals(true); + ui->dial->setValue(round(value / 0.1)); + ui->dial->blockSignals(previousValue); + + emit updateModel(); +} diff --git a/Editor/guis/PreampFilterGUI.h b/Editor/guis/PreampFilterGUI.h new file mode 100644 index 0000000..0f7df0b --- /dev/null +++ b/Editor/guis/PreampFilterGUI.h @@ -0,0 +1,46 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2014 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/IFilterGUI.h" +#include + +namespace Ui +{ +class PreampFilterGUI; +} + +class PreampFilterGUI : public IFilterGUI +{ + Q_OBJECT + +public: + PreampFilterGUI(double dbGain); + ~PreampFilterGUI(); + void store(QString& command, QString& parameters) override; + +private slots: + void on_doubleSpinBox_valueChanged(double value); + + void on_dial_valueChanged(int value); + +private: + Ui::PreampFilterGUI* ui; +}; diff --git a/Editor/guis/PreampFilterGUI.ui b/Editor/guis/PreampFilterGUI.ui new file mode 100644 index 0000000..73cdcfc --- /dev/null +++ b/Editor/guis/PreampFilterGUI.ui @@ -0,0 +1,134 @@ + + + PreampFilterGUI + + + + 0 + 0 + 525 + 66 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Gain: + + + + + + + + 0 + 0 + + + + true + + + dB + + + -100.000000000000000 + + + 100.000000000000000 + + + 0.100000000000000 + + + + + + + + 0 + 0 + + + + + 0 + 66 + + + + + 16777215 + 66 + + + + -200 + + + 200 + + + 20 + + + Qt::Vertical + + + 9.850000000000000 + + + true + + + + + + + Qt::Horizontal + + + + 229 + 20 + + + + + + + + Preamplification: + + + + + + + + diff --git a/Editor/guis/PreampFilterGUIFactory.cpp b/Editor/guis/PreampFilterGUIFactory.cpp new file mode 100644 index 0000000..21505e5 --- /dev/null +++ b/Editor/guis/PreampFilterGUIFactory.cpp @@ -0,0 +1,53 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2014 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "PreampFilterGUI.h" +#include +#include "PreampFilterGUIFactory.h" + +QList PreampFilterGUIFactory::createFilterTemplates() +{ + QList list; + list.append(FilterTemplate(tr("Preamp (Preamplification)"), "Preamp: 0 dB", QStringList(tr("Basic filters")))); + return list; +} + +IFilterGUI* PreampFilterGUIFactory::createFilterGUI(QString& command, QString& parameters) +{ + PreampFilterGUI* result = NULL; + + if(command == "Preamp") + { + PreampFilterFactory factory; + std::vector filters = factory.createFilter(L"", command.toStdWString(), parameters.toStdWString()); + if(!filters.empty()) + { + PreampFilter* filter = (PreampFilter*)filters[0]; + result = new PreampFilterGUI(filter->getDbGain()); + } + + for(IFilter* f : filters) + { + f->~IFilter(); + MemoryHelper::free(f); + } + } + + return result; +} diff --git a/Editor/guis/PreampFilterGUIFactory.h b/Editor/guis/PreampFilterGUIFactory.h new file mode 100644 index 0000000..35aec7a --- /dev/null +++ b/Editor/guis/PreampFilterGUIFactory.h @@ -0,0 +1,31 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2014 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/IFilterGUIFactory.h" + +class PreampFilterGUIFactory : public IFilterGUIFactory +{ + Q_OBJECT + +public: + QList createFilterTemplates() override; + IFilterGUI* createFilterGUI(QString& command, QString& parameters) override; +}; diff --git a/Editor/guis/StageFilterGUI.cpp b/Editor/guis/StageFilterGUI.cpp new file mode 100644 index 0000000..3d91d44 --- /dev/null +++ b/Editor/guis/StageFilterGUI.cpp @@ -0,0 +1,66 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "StageFilterGUI.h" +#include "ui_StageFilterGUI.h" + +StageFilterGUI::StageFilterGUI(const QString& parameters) : + ui(new Ui::StageFilterGUI) +{ + ui->setupUi(this); + + QStringList parts = parameters.toLower().split(' '); + ui->preMixCheckBox->setChecked(parts.contains("pre-mix")); + ui->postMixCheckBox->setChecked(parts.contains("post-mix")); + ui->captureCheckBox->setChecked(parts.contains("capture")); +} + +StageFilterGUI::~StageFilterGUI() +{ + delete ui; +} + +void StageFilterGUI::store(QString& command, QString& parameters) +{ + command = "Stage"; + QStringList list; + if(ui->preMixCheckBox->isChecked()) + list.append("pre-mix"); + if(ui->postMixCheckBox->isChecked()) + list.append("post-mix"); + if(ui->captureCheckBox->isChecked()) + list.append("capture"); + + parameters = list.join(' '); +} + +void StageFilterGUI::on_preMixCheckBox_toggled(bool checked) +{ + emit updateModel(); +} + +void StageFilterGUI::on_postMixCheckBox_toggled(bool checked) +{ + emit updateModel(); +} + +void StageFilterGUI::on_captureCheckBox_toggled(bool checked) +{ + emit updateModel(); +} diff --git a/Editor/guis/StageFilterGUI.h b/Editor/guis/StageFilterGUI.h new file mode 100644 index 0000000..7f85da3 --- /dev/null +++ b/Editor/guis/StageFilterGUI.h @@ -0,0 +1,47 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/IFilterGUI.h" + +namespace Ui { +class StageFilterGUI; +} + +class StageFilterGUI : public IFilterGUI +{ + Q_OBJECT + +public: + explicit StageFilterGUI(const QString& parameters); + ~StageFilterGUI(); + + void store(QString& command, QString& parameters) override; + +private slots: + void on_preMixCheckBox_toggled(bool checked); + + void on_postMixCheckBox_toggled(bool checked); + + void on_captureCheckBox_toggled(bool checked); + +private: + Ui::StageFilterGUI *ui; +}; diff --git a/Editor/guis/StageFilterGUI.ui b/Editor/guis/StageFilterGUI.ui new file mode 100644 index 0000000..b0a8a7f --- /dev/null +++ b/Editor/guis/StageFilterGUI.ui @@ -0,0 +1,92 @@ + + + StageFilterGUI + + + + 0 + 0 + 639 + 68 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Selected processing stage: + + + + + + + Playback + + + + + + Pre-mix + + + + + + + Post-mix + + + + + + + + + + Capture + + + + + + Capture + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + diff --git a/Editor/guis/StageFilterGUIFactory.cpp b/Editor/guis/StageFilterGUIFactory.cpp new file mode 100644 index 0000000..ca91e17 --- /dev/null +++ b/Editor/guis/StageFilterGUIFactory.cpp @@ -0,0 +1,40 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "StageFilterGUI.h" +#include "StageFilterGUIFactory.h" + +QList StageFilterGUIFactory::createFilterTemplates() +{ + QList list; + list.append(FilterTemplate(tr("Stage (Select processing stage)"), "Stage: post-mix", QStringList(tr("Control")))); + return list; +} + +IFilterGUI* StageFilterGUIFactory::createFilterGUI(QString& command, QString& parameters) +{ + StageFilterGUI* result = NULL; + + if(command == "Stage") + { + result = new StageFilterGUI(parameters); + } + + return result; +} diff --git a/Editor/guis/StageFilterGUIFactory.h b/Editor/guis/StageFilterGUIFactory.h new file mode 100644 index 0000000..8ed231d --- /dev/null +++ b/Editor/guis/StageFilterGUIFactory.h @@ -0,0 +1,31 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "Editor/IFilterGUIFactory.h" + +class StageFilterGUIFactory : public IFilterGUIFactory +{ + Q_OBJECT + +public: + QList createFilterTemplates() override; + IFilterGUI* createFilterGUI(QString& command, QString& parameters) override; +}; diff --git a/Editor/helpers/DisableWheelFilter.cpp b/Editor/helpers/DisableWheelFilter.cpp new file mode 100644 index 0000000..34709f0 --- /dev/null +++ b/Editor/helpers/DisableWheelFilter.cpp @@ -0,0 +1,42 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include + +#include "DisableWheelFilter.h" + +DisableWheelFilter::DisableWheelFilter(QObject* scrollTarget, QObject *parent) : + QObject(parent), scrollTarget(scrollTarget) +{ +} + +bool DisableWheelFilter::eventFilter(QObject* obj, QEvent* event) +{ + if(event->type() == QEvent::Wheel) + { + QWidget* widget = qobject_cast(obj); + if(widget == NULL || !widget->hasFocus()) + { + QApplication::sendEvent(scrollTarget, event); + return true; + } + } + + return false; +} diff --git a/Editor/helpers/DisableWheelFilter.h b/Editor/helpers/DisableWheelFilter.h new file mode 100644 index 0000000..ea23c6f --- /dev/null +++ b/Editor/helpers/DisableWheelFilter.h @@ -0,0 +1,34 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include + +class DisableWheelFilter : public QObject +{ + Q_OBJECT +public: + explicit DisableWheelFilter(QObject* scrollTarget, QObject *parent = 0); + + bool eventFilter(QObject* obj, QEvent* event) override; + +private: + QObject* scrollTarget; +}; diff --git a/Editor/helpers/GUIChannelHelper.cpp b/Editor/helpers/GUIChannelHelper.cpp new file mode 100644 index 0000000..ec78dff --- /dev/null +++ b/Editor/helpers/GUIChannelHelper.cpp @@ -0,0 +1,49 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include + +#include "GUIChannelHelper.h" + +GUIChannelHelper* GUIChannelHelper::instance = NULL; + +GUIChannelHelper* GUIChannelHelper::getInstance() +{ + if(instance == NULL) + instance = new GUIChannelHelper(); + + return instance; +} + +GUIChannelHelper::GUIChannelHelper() +{ + channelConfigurationInfos.append({KSAUDIO_SPEAKER_MONO, tr("Mono")}); + channelConfigurationInfos.append({KSAUDIO_SPEAKER_STEREO, tr("Stereo")}); + channelConfigurationInfos.append({KSAUDIO_SPEAKER_QUAD, tr("Quadraphonic")}); + channelConfigurationInfos.append({KSAUDIO_SPEAKER_SURROUND, tr("Surround")}); + channelConfigurationInfos.append({KSAUDIO_SPEAKER_5POINT1, tr("5.1")}); + channelConfigurationInfos.append({KSAUDIO_SPEAKER_5POINT1_SURROUND, tr("5.1 Surround")}); + channelConfigurationInfos.append({KSAUDIO_SPEAKER_7POINT1_SURROUND, tr("7.1 Surround")}); +} + +const QList& GUIChannelHelper::getChannelConfigurationInfos() const +{ + return channelConfigurationInfos; +} diff --git a/Editor/helpers/GUIChannelHelper.h b/Editor/helpers/GUIChannelHelper.h new file mode 100644 index 0000000..a0dec6f --- /dev/null +++ b/Editor/helpers/GUIChannelHelper.h @@ -0,0 +1,45 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include +#include + +class GUIChannelHelper: public QObject +{ + Q_OBJECT + +public: + static GUIChannelHelper* getInstance(); + + struct ChannelConfigurationInfo + { + int channelMask; + QString name; + }; + + const QList& getChannelConfigurationInfos() const; + +private: + GUIChannelHelper(); + static GUIChannelHelper* instance; + + QList channelConfigurationInfos; +}; diff --git a/Editor/icons/16x16/accessories-text-editor.png b/Editor/icons/16x16/accessories-text-editor.png new file mode 100644 index 0000000..188e1c1 Binary files /dev/null and b/Editor/icons/16x16/accessories-text-editor.png differ diff --git a/Editor/icons/16x16/applications-graphics-rotated.png b/Editor/icons/16x16/applications-graphics-rotated.png new file mode 100644 index 0000000..1c2b912 Binary files /dev/null and b/Editor/icons/16x16/applications-graphics-rotated.png differ diff --git a/Editor/icons/16x16/applications-graphics.png b/Editor/icons/16x16/applications-graphics.png new file mode 100644 index 0000000..4bb955f Binary files /dev/null and b/Editor/icons/16x16/applications-graphics.png differ diff --git a/Editor/icons/16x16/document-new.png b/Editor/icons/16x16/document-new.png new file mode 100644 index 0000000..4c3efdd Binary files /dev/null and b/Editor/icons/16x16/document-new.png differ diff --git a/Editor/icons/16x16/document-open.png b/Editor/icons/16x16/document-open.png new file mode 100644 index 0000000..ab94046 Binary files /dev/null and b/Editor/icons/16x16/document-open.png differ diff --git a/Editor/icons/16x16/document-save-as.png b/Editor/icons/16x16/document-save-as.png new file mode 100644 index 0000000..9bed143 Binary files /dev/null and b/Editor/icons/16x16/document-save-as.png differ diff --git a/Editor/icons/16x16/document-save.png b/Editor/icons/16x16/document-save.png new file mode 100644 index 0000000..22ff495 Binary files /dev/null and b/Editor/icons/16x16/document-save.png differ diff --git a/Editor/icons/16x16/edit-copy.png b/Editor/icons/16x16/edit-copy.png new file mode 100644 index 0000000..8dd48c4 Binary files /dev/null and b/Editor/icons/16x16/edit-copy.png differ diff --git a/Editor/icons/16x16/edit-cut.png b/Editor/icons/16x16/edit-cut.png new file mode 100644 index 0000000..dc9eb9a Binary files /dev/null and b/Editor/icons/16x16/edit-cut.png differ diff --git a/Editor/icons/16x16/edit-delete.png b/Editor/icons/16x16/edit-delete.png new file mode 100644 index 0000000..ea03150 Binary files /dev/null and b/Editor/icons/16x16/edit-delete.png differ diff --git a/Editor/icons/16x16/edit-paste.png b/Editor/icons/16x16/edit-paste.png new file mode 100644 index 0000000..24588a3 Binary files /dev/null and b/Editor/icons/16x16/edit-paste.png differ diff --git a/Editor/icons/16x16/format-justify-fill-rotated.png b/Editor/icons/16x16/format-justify-fill-rotated.png new file mode 100644 index 0000000..78e67ff Binary files /dev/null and b/Editor/icons/16x16/format-justify-fill-rotated.png differ diff --git a/Editor/icons/16x16/format-justify-fill.png b/Editor/icons/16x16/format-justify-fill.png new file mode 100644 index 0000000..663cbad Binary files /dev/null and b/Editor/icons/16x16/format-justify-fill.png differ diff --git a/Editor/icons/16x16/go-up.png b/Editor/icons/16x16/go-up.png new file mode 100644 index 0000000..fa9a7d7 Binary files /dev/null and b/Editor/icons/16x16/go-up.png differ diff --git a/Editor/icons/16x16/list-add-green.png b/Editor/icons/16x16/list-add-green.png new file mode 100644 index 0000000..d7a27bd Binary files /dev/null and b/Editor/icons/16x16/list-add-green.png differ diff --git a/Editor/icons/16x16/list-add.png b/Editor/icons/16x16/list-add.png new file mode 100644 index 0000000..1aa7f09 Binary files /dev/null and b/Editor/icons/16x16/list-add.png differ diff --git a/Editor/icons/16x16/list-remove-red.png b/Editor/icons/16x16/list-remove-red.png new file mode 100644 index 0000000..6f896e3 Binary files /dev/null and b/Editor/icons/16x16/list-remove-red.png differ diff --git a/Editor/icons/16x16/list-remove.png b/Editor/icons/16x16/list-remove.png new file mode 100644 index 0000000..00b654e Binary files /dev/null and b/Editor/icons/16x16/list-remove.png differ diff --git a/Editor/icons/accessories-text-editor.ico b/Editor/icons/accessories-text-editor.ico new file mode 100644 index 0000000..fda7ea9 Binary files /dev/null and b/Editor/icons/accessories-text-editor.ico differ diff --git a/Editor/icons/arrow_right.png b/Editor/icons/arrow_right.png new file mode 100644 index 0000000..17ace34 Binary files /dev/null and b/Editor/icons/arrow_right.png differ diff --git a/Editor/icons/arrow_right.svg b/Editor/icons/arrow_right.svg new file mode 100644 index 0000000..54a1551 --- /dev/null +++ b/Editor/icons/arrow_right.svg @@ -0,0 +1,69 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/Editor/icons/mirror_response.png b/Editor/icons/mirror_response.png new file mode 100644 index 0000000..b89a8cc Binary files /dev/null and b/Editor/icons/mirror_response.png differ diff --git a/Editor/icons/mirror_response.svg b/Editor/icons/mirror_response.svg new file mode 100644 index 0000000..e7b44fa --- /dev/null +++ b/Editor/icons/mirror_response.svg @@ -0,0 +1,78 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/Editor/icons/normalize_response.png b/Editor/icons/normalize_response.png new file mode 100644 index 0000000..5a8ba42 Binary files /dev/null and b/Editor/icons/normalize_response.png differ diff --git a/Editor/icons/normalize_response.svg b/Editor/icons/normalize_response.svg new file mode 100644 index 0000000..72092fb --- /dev/null +++ b/Editor/icons/normalize_response.svg @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/Editor/icons/power_off.png b/Editor/icons/power_off.png new file mode 100644 index 0000000..3a85acd Binary files /dev/null and b/Editor/icons/power_off.png differ diff --git a/Editor/icons/power_off.svg b/Editor/icons/power_off.svg new file mode 100644 index 0000000..f97069b --- /dev/null +++ b/Editor/icons/power_off.svg @@ -0,0 +1,70 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/Editor/icons/power_on.png b/Editor/icons/power_on.png new file mode 100644 index 0000000..c49b07f Binary files /dev/null and b/Editor/icons/power_on.png differ diff --git a/Editor/icons/power_on.svg b/Editor/icons/power_on.svg new file mode 100644 index 0000000..0250917 --- /dev/null +++ b/Editor/icons/power_on.svg @@ -0,0 +1,70 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/Editor/icons/reset_response.png b/Editor/icons/reset_response.png new file mode 100644 index 0000000..7ada6eb Binary files /dev/null and b/Editor/icons/reset_response.png differ diff --git a/Editor/icons/reset_response.svg b/Editor/icons/reset_response.svg new file mode 100644 index 0000000..29afdc3 --- /dev/null +++ b/Editor/icons/reset_response.svg @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/Editor/main.cpp b/Editor/main.cpp new file mode 100644 index 0000000..7f182f2 --- /dev/null +++ b/Editor/main.cpp @@ -0,0 +1,73 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include +#include +#include +#include "MainWindow.h" +#include "helpers/RegistryHelper.h" + +using namespace std; + +int main(int argc, char *argv[]) +{ + int result = -1; +#ifdef _DEBUG + //_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); + //_CrtSetBreakAlloc(3318); +#endif + + { + QCoreApplication::addLibraryPath("qt"); + + QApplication application(argc, argv); + + QTranslator qtTranslator; + qtTranslator.load(QLocale::system(), ":/translations/qtbase", "_"); + application.installTranslator(&qtTranslator); + + QTranslator editorTranslator; + editorTranslator.load(QLocale::system(), ":/translations/Editor", "_"); + application.installTranslator(&editorTranslator); + + QString configPath = QDir::currentPath(); + if(RegistryHelper::keyExists(APP_REGPATH) && RegistryHelper::valueExists(APP_REGPATH, L"ConfigPath")) + configPath = QString::fromStdWString(RegistryHelper::readValue(APP_REGPATH, L"ConfigPath")); + QDir configDir(configPath); + + MainWindow w(configDir); + w.show(); + + QCommandLineParser parser; + parser.process(application); + QStringList args = parser.positionalArguments(); + if(args.isEmpty()) + args = QStringList("config.txt"); + + for(QString arg : args) + w.load(configDir.absoluteFilePath(arg)); + + w.doChecks(); + + result = application.exec(); + } + + return result; +} diff --git a/Editor/stable.h b/Editor/stable.h new file mode 100644 index 0000000..5bd6f39 --- /dev/null +++ b/Editor/stable.h @@ -0,0 +1,47 @@ +#include "stdafx.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/Editor/translations/Editor_de.qm b/Editor/translations/Editor_de.qm new file mode 100644 index 0000000..ca0cc02 Binary files /dev/null and b/Editor/translations/Editor_de.qm differ diff --git a/Editor/translations/Editor_de.ts b/Editor/translations/Editor_de.ts new file mode 100644 index 0000000..b4d99b5 --- /dev/null +++ b/Editor/translations/Editor_de.ts @@ -0,0 +1,1208 @@ + + + + + BiQuadFilterGUI + + + Hz + Hz + + + + dB + dB + + + + Gain: + Verstärkung: + + + + Peaking filter + Peaking-Filter + + + + Low-pass filter + Tiefpassfilter + + + + High-pass filter + Hochpassfilter + + + + Band-pass filter + Bandpassfilter + + + + Low-shelf filter + Low Shelf-Filter + + + + High-shelf filter + High Shelf-Filter + + + + Notch filter + Notchfilter + + + + All-pass filter + Allpassfilter + + + + Center frequency + Mittenfrequenz + + + + Corner frequency + Eckfrequenz + + + + Fixed Q + Festes Q + + + + + Fixed S + Festes S + + + + Q factor + Q-Faktor + + + + Bandwidth + Bandbreite + + + + + Slope + Steilheit + + + + BiQuadFilterGUIFactory + + + Peaking filter + Peaking-Filter + + + + + + + + + + + Parametric filters + Parametrische Filter + + + + Low-pass filter + Tiefpassfilter + + + + High-pass filter + Hochpassfilter + + + + Band-pass filter + Bandpassfilter + + + + Low-shelf filter + Low Shelf-Filter + + + + High-shelf filter + High Shelf-Filter + + + + Notch filter + Notchfilter + + + + All-pass filter + Allpassfilter + + + + ChannelFilterGUI + + + Change... + Ändern... + + + + Selected channels: + Gewählte Kanäle: + + + + ChannelFilterGUIDialog + + + Channel selection + Kanelauswahl + + + + Select all channels + Alle Kanäle auswählen + + + + Speaker positions + Lautsprecherpositionen + + + + SR (Side right) + SR (Seite rechts) + + + + C (Center) + C (Mitte) + + + + SUB (Subwoofer) + SUB (Subwoofer) + + + + L (Left) + L (Links) + + + + RL (Rear left) + RL (Hinten links) + + + + RR (Rear right) + RR (Hinten rechts) + + + + RC (Rear center) + RC (Hinten Mitte) + + + + SL (Side left) + SL (Seite links) + + + + R (Right) + R (Rechts) + + + + Additional channels + Zusätzliche Kanäle + + + + Remove channel + Kanal entfernen + + + + Add channel + Kanal hinzufügen + + + + ChannelFilterGUIFactory + + Channel + Channel (Kanäle auswählen) + + + + Control + Steuerung + + + + Channel (Select channels) + Channel (Kanäle auswählen) + + + + CommentFilterGUI + + + Power on + Einschalten + + + + CommentFilterGUIFactory + + + Comment + Kommentar + + + + ConvolutionFilterGUI + + + Select file + Datei auswählen + + + + Convolution with impulse response: + Faltung mit Impulsantwort: + + + + File information + Dateiinformationen + + + + Sample rate: + Abtastrate: + + + + + + No file selected + Keine Datei gewählt + + + + Length: + Länge: + + + + Select impulse response file + Impulsantwort auswählen + + + + Impulse response (*.wav *.flac *.ogg) + Impulsantwort (*.wav *.flac *.ogg) + + + + File not found + Datei nicht gefunden + + + + Unsupported file format + Nicht unterstütztes Dateiformat + + + + %0 ms (%1 samples) + %0 ms (%1 Samples) + + + + %0 Hz + %0 Hz + + + + The file sample rate does not match the device sample rate (%0 Hz)! +Select a different file or change the device configuration. + Die Abtastrate der Datei stimmt nicht mit der Abtastrate des Geräts (%0 Hz) überein! +Wählen Sie eine andere Datei or ändern Sie die Konfiguration des Geräts. + + + + ConvolutionFilterGUIFactory + + Convolution + Convolution (Faltung mit Impulsantwort) + + + + Advanced filters + Fortgeschrittene Filter + + + + Convolution (Convolution with impulse response) + Convolution (Faltung mit Impulsantwort) + + + Advanced + Fortgeschritten + + + + CopyFilterGUI + + + Copy channels: + Kanäle kopieren: + + + + Graph + Graph + + + + Assignments + Zuweisungen + + + + Selected channels: + Gewählte Kanäle: + + + + CopyFilterGUIFactory + + Copy + Copy (Kanäle kopieren) + + + Control + Steuerung + + + + Copy (Copy between channels) + Copy (Kopieren zwischen Kanälen) + + + + Basic filters + Einfache Filter + + + + CopyFilterGUIForm + + + Channel + Kanal + + + + Add summand + Summand hinzufügen + + + + Remove assignment + Zuweisung entfernen + + + + Remove summand + Summand entfernen + + + + Add assignment + Zuweisung hinzufügen + + + + CopyFilterGUIRow + + + Channel + Kanal + + + + Factor * channel + Factor * Channel + Faktor * Kanal + + + + dB factor * channel + dB-Factor * Channel + dB-Faktor * Kanal + + + + Constant value + Konstanter Wert + + + + DelayFilterGUI + + + + ms + ms + + + + Delay: + Verzögerung: + + + + Time + Zeit + + + + Samples + Samples + + + + samples + Samples + + + + DelayFilterGUIFactory + + + Delay + Delay (Verzögerung) + + + + Basic filters + Einfache Filter + + + Simple filters + Einfache Filter + + + + DeviceFilterGUI + + + Selected devices: + Gewählte Geräte: + + + + Change... + Ändern... + + + + Type + Typ + + + + Connection + Anschluss + + + + Device + Gerät + + + + State + Status + + + + All devices + Alle Geräte + + + + Capture + Aufnahme + + + + Playback + Wiedergabe + + + + APO installed + APO installiert + + + + APO not installed + APO nicht installiert + + + + No device matched! + Kein Gerät stimmt überein! + + + + DeviceFilterGUIDialog + + + Device selection + Geräteauswahl + + + + Select all devices + Alle Geräte auswählen + + + + Show only devices with APO installed + Nur Geräte mit installiertem APO zeigen + + + + Connection + Anschluss + + + + Device + Gerät + + + + State + Status + + + + Playback devices + Wiedergabegeräte + + + + Capture devices + Aufnahmegeräte + + + + APO installed + APO installiert + + + + APO not installed + APO nicht installiert + + + + DeviceFilterGUIFactory + + Device + Device (Gerät auswählen) + + + + Control + Steuerung + + + + Device (Select device) + Device (Gerät auswählen) + + + + FilterTable + + + Add filter + Filter hinzufügen + + + + FilterTableRow + + 99 + 99 + + + + Add + Hinzufügen + + + + Remove + Entfernen + + + + Edit text + Text bearbeiten + + + + GUIChannelHelper + + + Mono + Mono + + + + Stereo + Stereo + + + + Quadraphonic + Quadrophonie + + + + Surround + Surround + + + + 5.1 + 5.1 + + + + 5.1 Surround + 5.1 Surround + + + + 7.1 Surround + 7.1 Surround + + + + GraphicEQFilterGUI + + + Graphic EQ: + Grafischer EQ: + + + + Freq. + Freq. + + + + Gain + Verst. + + + + 15-band + 15-Band + + + + 31-band + 31-Band + + + + variable + variabel + + + + Import + Importieren + + + + Export + Exportieren + + + + Mirror response + Frequenzgang spiegeln + + + + Normalize response + Frequenzgang normalisieren + + + + Reset response + Frequenzgang zurücksetzen + + + + Import frequency response + Frequenzgang importieren + + + + + Frequency response (*.csv) + Frequenzgang (*.csv) + + + + + All files (*.*) + Alle Dateien (*.*) + + + + Export frequency response + Frequenzgang exportieren + + + + GraphicEQFilterGUIFactory + + + 15-band graphic equalizer + Grafischer Equalizer mit 15 Bändern + + + + + + Graphic equalizers + Grafische Equalizer + + + + 31-band graphic equalizer + Grafischer Equalizer mit 31 Bändern + + + + Graphic equalizer with variable bands + Grafischer Equalizer mit variablen Bändern + + + + IncludeFilterGUI + + + Include: + Einbinden: + + + + Select file + Datei auswählen + + + + Open file in editor + Datei im Editor öffnen + + + + Include file + Datei einbinden + + + + E-APO configurations (*.txt) + E-APO-Konfigurationen (*.txt) + + + + IncludeFilterGUIFactory + + Include + Include (Einbinden) + + + + Control + Steuerung + + + + Include (Include configuration file) + Include (Konfigurationsdatei einbinden) + + + + MainWindow + + MainWindow + Equalizer APO Editor + + + + File + Datei + + + + Edit + Bearbeiten + + + + New + Neu + + + + Ctrl+N + Ctrl+N + + + + + Open + Öffnen + + + + Ctrl+O + Ctrl+O + + + + + Save + Speichern + + + + Ctrl+S + Ctrl+S + + + + + Save as... + Speichern unter... + + + + Exit + Beenden + + + + Alt+F4 + Alt+F4 + + + + Cut + Ausschneiden + + + + Ctrl+X + Ctrl+X + + + + Copy + Kopieren + + + + Ctrl+C + Ctrl+C + + + + Paste + Einfügen + + + + Ctrl+V + Ctrl+V + + + + Delete + Löschen + + + + Equalizer APO %0 Configuration Editor + Equalizer APO %0 Konfigurationseditor + + + + Instant mode + Sofort-Modus + + + + Changes are saved immediately + Änderungen werden sofort gespeichert + + + + Device: + Gerät: + + + + Channel configuration: + Kanalkonfiguration: + + + + Default + Standard + + + Output devices: + Ausgabegeräte: + + + Input devices: + Eingabegeräte: + + + + Playback devices: + Wiedergabegeräte: + + + + Capture devices: + Aufnahmegeräte: + + + + Registry problem + Registry-Problem + + + A registry value that is required for the operation of Equalizer APO is not set correctly. Do you want to run the Configurator application to fix the problem? + Ein Registrywert, der für den Betrieb von Equalizer APO erforderlich ist, ist nicht korrekt gesetzt. Möchten Sie die Configurator-Anwendung starten, um das Problem zu beheben? + + + + A registry value that is required for the operation of Equalizer APO is not set correctly. +Do you want to run the Configurator application to fix the problem? + Ein Registrywert, der für den Betrieb von Equalizer APO erforderlich ist, ist nicht korrekt gesetzt. +Möchten Sie die Configurator-Anwendung starten, um das Problem zu beheben? + + + + APO not installed to device + APO nicht für das Gerät installiert + + + + Equalizer APO has not been installed to the selected device. +Do you want to run the Configurator application to fix the problem? + Equalizer APO wurde nicht für das gewählte Gerät installiert. +Möchten Sie die Configurator-Anwendung starten, um das Problem zu beheben? + + + + Audio enhancements disabled + Signalverbesserungen deaktiviert + + + + Audio enhancements are not enabled for the device +%0 %1. +Do you want to run the Configurator application to fix the problem? + Signalverbesserungen sind nicht aktiviert für das Gerät +%0 %1. +Möchten Sie die Configurator-Anwendung starten, um das Problem zu beheben? + + + + + + Error + Fehler + + + + Error while reading configuration file: %0 + Fehler beim Lesen der Konfigurationsdatei: %0 + + + + Error while writing configuration file: %0 + Fehler beim Schreiben der Konfigurationsdatei: %0 + + + + Only %0/%1 bytes have been written! + Nur %0/%1 Bytes wurden geschrieben! + + + + + From device + Von Gerät + + + + Unsaved changes + Ungespeicherte Änderungen + + + + The configuration file %0 has unsaved changes. + Die Konfigurationsdatei %0 hat ungespeicherte Änderungen. + + + + Do you want to save the changes before closing the file? + Möchten Sie die Änderungen speichern, bevor die Datei geschlossen wird? + + + + Open file + Datei öffnen + + + + + E-APO configurations (*.txt) + E-APO-Konfigurationen (*.txt) + + + + Save file as + Datei speichern unter + + + + Unsaved + Ungespeichert + + + + PreampFilterGUI + + + Gain: + Verstärkung: + + + + dB + dB + + + + Preamplification: + Vorverstärkung: + + + + PreampFilterGUIFactory + + Preamp + Preamp (Vorverstärkung) + + + Simple filters + Einfache Filter + + + + Preamp (Preamplification) + Preamp (Vorverstärkung) + + + + Basic filters + Einfache Filter + + + Filters + Filter + + + + StageFilterGUI + + Select processing stage: + Verarbeitungsstufe wählen: + + + + Selected processing stage: + Gewählte Verarbeitungsstufe: + + + + Playback + Wiedergabe + + + + Pre-mix + Pre-mix + + + + Post-mix + Post-mix + + + + + Capture + Aufnahme + + + + StageFilterGUIFactory + + + Stage (Select processing stage) + Stage (Verarbeitungsstufe wählen) + + + + Control + Steuerung + + + diff --git a/Editor/translations/qtbase_de.qm b/Editor/translations/qtbase_de.qm new file mode 100644 index 0000000..a2bfcc2 Binary files /dev/null and b/Editor/translations/qtbase_de.qm differ diff --git a/Editor/widgets/ChannelGraphItem.cpp b/Editor/widgets/ChannelGraphItem.cpp new file mode 100644 index 0000000..5a4d48f --- /dev/null +++ b/Editor/widgets/ChannelGraphItem.cpp @@ -0,0 +1,67 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include + +#include "ChannelGraphItem.h" + +static const float margin = 5; + +ChannelGraphItem::ChannelGraphItem(const QString& name) + : name(name) +{ +} + +QRectF ChannelGraphItem::boundingRect() const +{ + if(cachedRect.isNull()) + { + QFontMetrics metrics(scene()->font()); + int width = metrics.width(name); + // we only use upper case characters and digits, so everything is above the baseline + int height = metrics.ascent(); + // make it at least quadratic + if(width < height) + width = height; + // shift by 0.5 to have sharp lines + cachedRect.setLeft(0.5); + cachedRect.setTop(0.5); + cachedRect.setWidth(width + 2 * margin); + cachedRect.setHeight(height + 2 * margin); + } + + return cachedRect; +} + +QString ChannelGraphItem::getName() const +{ + return name; +} + +void ChannelGraphItem::paint(QPainter* painter, QColor color) +{ + QRectF rect = boundingRect(); + QLinearGradient gradient(rect.topLeft(), rect.bottomLeft()); + gradient.setColorAt(0, QColor(255, 255, 255)); + gradient.setColorAt(1, color); + painter->setBrush(gradient); + painter->drawRoundedRect(rect, margin, margin); + painter->drawText(rect.translated(1, -1), Qt::AlignCenter, name); +} diff --git a/Editor/widgets/ChannelGraphItem.h b/Editor/widgets/ChannelGraphItem.h new file mode 100644 index 0000000..3d7c5df --- /dev/null +++ b/Editor/widgets/ChannelGraphItem.h @@ -0,0 +1,39 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include + +class ChannelGraphItem : public QGraphicsItem +{ +public: + explicit ChannelGraphItem(const QString& name = 0); + + QRectF boundingRect() const override; + + QString getName() const; + +protected: + virtual void paint(QPainter* painter, QColor color); + +private: + QString name; + mutable QRectF cachedRect; +}; diff --git a/Editor/widgets/ChannelGraphScene.cpp b/Editor/widgets/ChannelGraphScene.cpp new file mode 100644 index 0000000..a03b429 --- /dev/null +++ b/Editor/widgets/ChannelGraphScene.cpp @@ -0,0 +1,39 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include + +#include "ChannelGraphScene.h" + +ChannelGraphScene::ChannelGraphScene(QWidget *parent) : + QGraphicsScene(parent) +{ +} + +QPointF ChannelGraphScene::getNextChannelPoint(QGraphicsItem* lastItem, bool output) +{ + int x = 0; + if(lastItem != NULL) + { + QRectF lastRect = lastItem->sceneBoundingRect(); + x = lastRect.left() + lastRect.width() + 1 + 10; + } + + return QPointF(x, output ? 50 : 0); +} diff --git a/Editor/widgets/ChannelGraphScene.h b/Editor/widgets/ChannelGraphScene.h new file mode 100644 index 0000000..8d385a7 --- /dev/null +++ b/Editor/widgets/ChannelGraphScene.h @@ -0,0 +1,33 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include + +class ChannelGraphScene : public QGraphicsScene +{ + Q_OBJECT +public: + explicit ChannelGraphScene(QWidget *parent = 0); + +protected: + QPointF getNextChannelPoint(QGraphicsItem* lastItem, bool output); + +}; diff --git a/Editor/widgets/CompactToolBar.cpp b/Editor/widgets/CompactToolBar.cpp new file mode 100644 index 0000000..28813b0 --- /dev/null +++ b/Editor/widgets/CompactToolBar.cpp @@ -0,0 +1,38 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "CompactToolBar.h" + +CompactToolBar::CompactToolBar(QWidget *parent) : + QToolBar(parent) +{ + setMinimumHeight(1); + setOrientation(Qt::Vertical); + setIconSize(QSize(16, 16)); +} + +QSize CompactToolBar::sizeHint() const +{ + return QSize(1, 1); +} + +void CompactToolBar::updateMaximumHeight() +{ + setMaximumHeight(QToolBar::sizeHint().height()); +} diff --git a/Editor/widgets/CompactToolBar.h b/Editor/widgets/CompactToolBar.h new file mode 100644 index 0000000..2f03b81 --- /dev/null +++ b/Editor/widgets/CompactToolBar.h @@ -0,0 +1,32 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include + +class CompactToolBar : public QToolBar +{ + Q_OBJECT +public: + explicit CompactToolBar(QWidget *parent = 0); + + QSize sizeHint() const override; + void updateMaximumHeight(); +}; diff --git a/Editor/widgets/EscapableLineEdit.cpp b/Editor/widgets/EscapableLineEdit.cpp new file mode 100644 index 0000000..b3e7fe3 --- /dev/null +++ b/Editor/widgets/EscapableLineEdit.cpp @@ -0,0 +1,36 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include + +#include "EscapableLineEdit.h" + +EscapableLineEdit::EscapableLineEdit(QWidget *parent) : + QLineEdit(parent) +{ +} + +void EscapableLineEdit::keyPressEvent(QKeyEvent* event) +{ + + if(event->key() == Qt::Key_Escape) + emit editingCanceled(); + else + QLineEdit::keyPressEvent(event); +} diff --git a/Editor/widgets/EscapableLineEdit.h b/Editor/widgets/EscapableLineEdit.h new file mode 100644 index 0000000..b7f976a --- /dev/null +++ b/Editor/widgets/EscapableLineEdit.h @@ -0,0 +1,35 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include + +class EscapableLineEdit : public QLineEdit +{ + Q_OBJECT +public: + explicit EscapableLineEdit(QWidget *parent = 0); + +signals: + void editingCanceled(); + +protected: + void keyPressEvent(QKeyEvent* event) override; +}; diff --git a/Editor/widgets/FrequencyPlotHRuler.cpp b/Editor/widgets/FrequencyPlotHRuler.cpp new file mode 100644 index 0000000..5ef839c --- /dev/null +++ b/Editor/widgets/FrequencyPlotHRuler.cpp @@ -0,0 +1,90 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include + +#include "FrequencyPlotView.h" +#include "FrequencyPlotHRuler.h" + +using namespace std; + +FrequencyPlotHRuler::FrequencyPlotHRuler(QWidget* parent) : + QWidget(parent) +{ +} + +void FrequencyPlotHRuler::paintEvent(QPaintEvent*) +{ + QPainter painter(this); + FrequencyPlotView* view = qobject_cast(parentWidget()); + FrequencyPlotScene* s = view->scene(); + QFontMetrics metrics = painter.fontMetrics(); + painter.setPen(QColor(50, 50, 50)); + + QPointF topLeft = view->mapToScene(0, 0); + QPointF bottomRight = view->mapToScene(view->viewport()->width(), view->viewport()->height()); + double fromHz = s->xToHz(topLeft.x()); + double toHz = s->xToHz(bottomRight.x()); + const vector& bands = s->getBands(); + if(bands.empty()) + { + double hzBase = pow(10, floor(log10(fromHz))); + fromHz = floor(fromHz / hzBase) * hzBase; + fromHz += hzBase; + if(round(fromHz / hzBase) >= 10) + hzBase *= 10; + for(double hz = fromHz; hz <= toHz;) + { + double x = s->hzToX(hz); + if(x != -1) + { + QString text; + if(hz < 1000) + text = QString("%0").arg(hz); + else + text = QString("%0k").arg(hz / 1000); + if(metrics.width(text) + 2 < s->hzToX(hz + hzBase) - x) + painter.drawText(x - topLeft.x() + height() + 1, 0, 0, height(), Qt::TextDontClip | Qt::AlignCenter, text); + } + + hz += hzBase; + if(round(hz / hzBase) >= 10) + hzBase *= 10; + } + } + else + { + vector::const_iterator it = lower_bound(bands.cbegin(), bands.cend(), fromHz); + for(; it != bands.cend() && *it < toHz; it++) + { + double hz = *it; + double x = s->hzToX(hz); + if(x != -1) + { + QString text; + if(hz < 1000) + text = QString("%0").arg(hz); + else + text = QString("%0k").arg(hz / 1000); + painter.drawText(x - topLeft.x() + height() + 1, 0, 0, height(), Qt::TextDontClip | Qt::AlignCenter, text); + } + } + } +} diff --git a/Editor/widgets/FrequencyPlotHRuler.h b/Editor/widgets/FrequencyPlotHRuler.h new file mode 100644 index 0000000..849681b --- /dev/null +++ b/Editor/widgets/FrequencyPlotHRuler.h @@ -0,0 +1,32 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include + +class FrequencyPlotHRuler : public QWidget +{ + Q_OBJECT +public: + explicit FrequencyPlotHRuler(QWidget* parent = 0); + +protected: + void paintEvent(QPaintEvent*) override; +}; diff --git a/Editor/widgets/FrequencyPlotItem.cpp b/Editor/widgets/FrequencyPlotItem.cpp new file mode 100644 index 0000000..d6ae64d --- /dev/null +++ b/Editor/widgets/FrequencyPlotItem.cpp @@ -0,0 +1,57 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include + +#include "FrequencyPlotScene.h" +#include "FrequencyPlotItem.h" + +FrequencyPlotItem::FrequencyPlotItem(double hz, double db) + : hz(hz), db(db) +{ + setFlag(ItemSendsScenePositionChanges); +} + +void FrequencyPlotItem::updatePos() +{ + setFlag(ItemSendsScenePositionChanges, false); + FrequencyPlotScene* s = qobject_cast(scene()); + setPos(floor(s->hzToX(hz)) + 0.5, floor(s->dbToY(db)) + 0.5); + setFlag(ItemSendsScenePositionChanges, true); +} + +double FrequencyPlotItem::getHz() const +{ + return hz; +} + +void FrequencyPlotItem::setHz(double value) +{ + hz = value; +} + +double FrequencyPlotItem::getDb() const +{ + return db; +} + +void FrequencyPlotItem::setDb(double value) +{ + db = value; +} diff --git a/Editor/widgets/FrequencyPlotItem.h b/Editor/widgets/FrequencyPlotItem.h new file mode 100644 index 0000000..1e07dfd --- /dev/null +++ b/Editor/widgets/FrequencyPlotItem.h @@ -0,0 +1,46 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include + +class FrequencyPlotItem : public QGraphicsItem +{ +public: + FrequencyPlotItem(double hz, double db); + + enum { Type = UserType + 1 }; + virtual int type() const + { + return Type; + } + + void updatePos(); + + double getHz() const; + void setHz(double value); + + double getDb() const; + void setDb(double value); + +private: + double hz; + double db; +}; diff --git a/Editor/widgets/FrequencyPlotScene.cpp b/Editor/widgets/FrequencyPlotScene.cpp new file mode 100644 index 0000000..75e9428 --- /dev/null +++ b/Editor/widgets/FrequencyPlotScene.cpp @@ -0,0 +1,129 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "FrequencyPlotView.h" +#include "FrequencyPlotScene.h" + +using namespace std; + +static double maxX = 1000; +static double maxY = 500; +static double minHz = 1; +static double maxHz = 30000; +static double minDb = -100; +static double maxDb = 100; + +vector FrequencyPlotScene::bands15 = {25, 40, 63, 100, 160, 250, 400, 630, 1000, 1600, 2500, 4000, 6300, 10000, 16000}; +vector FrequencyPlotScene::bands31 = {20, 25, 31.5, 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 + }; +vector FrequencyPlotScene::bandsVar; + +FrequencyPlotScene::FrequencyPlotScene(QObject *parent) : + QGraphicsScene(parent) +{ + updateSceneRect(); +} + +void FrequencyPlotScene::addItem(FrequencyPlotItem* item) +{ + QGraphicsScene::addItem(item); +} + +double FrequencyPlotScene::xToHz(double x) +{ + return pow(maxHz / minHz, x / zoom / maxX) * minHz; +} + +double FrequencyPlotScene::hzToX(double hz) +{ + if(hz < minHz || hz > maxHz) + return -1; + + return log(hz / minHz) / log(maxHz / minHz) * maxX * zoom; +} + +double FrequencyPlotScene::yToDb(double y) +{ + return (minDb + (1 - y / zoom / maxY) * (maxDb - minDb)); +} + +double FrequencyPlotScene::dbToY(double db) +{ + if(db < minDb || db > maxDb) + return -1; + + return (1 - (db - minDb) / (maxDb - minDb)) * maxY * zoom; +} + +double FrequencyPlotScene::getZoom() const +{ + return zoom; +} + +void FrequencyPlotScene::setZoom(double value) +{ + zoom = value; + updateSceneRect(); + + for(QGraphicsItem* item : items()) + { + FrequencyPlotItem* plotItem = (FrequencyPlotItem*)item; + plotItem->updatePos(); + } +} + +void FrequencyPlotScene::updateSceneRect() +{ + setSceneRect(0, 0, maxX * zoom, maxY * zoom); +} + +int FrequencyPlotScene::getBandCount() const +{ + return bandCount; +} + +void FrequencyPlotScene::setBandCount(int value) +{ + if(value != bandCount) + { + bandCount = value; + update(); + FrequencyPlotView* view = qobject_cast(views()[0]); + view->updateHRuler(); + } +} + +const vector& FrequencyPlotScene::getBands() +{ + return getBands(bandCount); +} + +const std::vector&FrequencyPlotScene::getBands(int count) +{ + switch(count) + { + case 15: + return bands15; + case 31: + return bands31; + default: + return bandsVar; + } +} diff --git a/Editor/widgets/FrequencyPlotScene.h b/Editor/widgets/FrequencyPlotScene.h new file mode 100644 index 0000000..18c705a --- /dev/null +++ b/Editor/widgets/FrequencyPlotScene.h @@ -0,0 +1,59 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include +#include + +#include "FrequencyPlotItem.h" + +class FrequencyPlotScene : public QGraphicsScene +{ + Q_OBJECT +public: + explicit FrequencyPlotScene(QObject *parent = 0); + + void addItem(FrequencyPlotItem *item); + + double xToHz(double x); + double hzToX(double hz); + double yToDb(double y); + double dbToY(double db); + + double getZoom() const; + void setZoom(double value); + + int getBandCount() const; + virtual void setBandCount(int value); + + const std::vector& getBands(); + const std::vector& getBands(int count); + +protected: + static std::vector bands15; + static std::vector bands31; + static std::vector bandsVar; + +private: + void updateSceneRect(); + + double zoom = 1.0; + int bandCount = -1; +}; diff --git a/Editor/widgets/FrequencyPlotVRuler.cpp b/Editor/widgets/FrequencyPlotVRuler.cpp new file mode 100644 index 0000000..1510b3e --- /dev/null +++ b/Editor/widgets/FrequencyPlotVRuler.cpp @@ -0,0 +1,60 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "FrequencyPlotView.h" +#include "FrequencyPlotScene.h" +#include "FrequencyPlotVRuler.h" + +FrequencyPlotVRuler::FrequencyPlotVRuler(QWidget *parent) : + QWidget(parent) +{ +} + +void FrequencyPlotVRuler::paintEvent(QPaintEvent*) +{ + QPainter painter(this); + FrequencyPlotView* view = qobject_cast(parentWidget()); + FrequencyPlotScene* scene = view->scene(); + + QPointF topLeft = view->mapToScene(0, 0); + QPointF bottomRight = view->mapToScene(view->viewport()->width(), view->viewport()->height()); + double dbHeight = abs(scene->yToDb(topLeft.y()) - scene->yToDb(bottomRight.y())); + double dbStep = dbHeight / 4; + + double dbBase = pow(10, floor(log10(dbStep))); + if(dbStep >= 5 * dbBase) + dbStep = 5 * dbBase; + else if(dbStep >= 2 * dbBase) + dbStep = 2 * dbBase; + else + dbStep = dbBase; + + double fromDb = floor(scene->yToDb(topLeft.y()) / dbStep) * dbStep; + double toDb = ceil(scene->yToDb(bottomRight.y()) / dbStep) * dbStep; + + painter.setPen(QColor(50, 50, 50)); + for(double db = toDb; db <= fromDb; db += dbStep) + { + if(abs(db) < 1e-6) + db = 0; + double y = scene->dbToY(db); + if(y != -1) + painter.drawText(0, y - topLeft.y() - 1, width(), 0, Qt::TextDontClip | Qt::AlignCenter, QString("%0").arg(db)); + } +} diff --git a/Editor/widgets/FrequencyPlotVRuler.h b/Editor/widgets/FrequencyPlotVRuler.h new file mode 100644 index 0000000..46cb136 --- /dev/null +++ b/Editor/widgets/FrequencyPlotVRuler.h @@ -0,0 +1,32 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include + +class FrequencyPlotVRuler : public QWidget +{ + Q_OBJECT +public: + explicit FrequencyPlotVRuler(QWidget *parent = 0); + +protected: + void paintEvent(QPaintEvent*) override; +}; diff --git a/Editor/widgets/FrequencyPlotView.cpp b/Editor/widgets/FrequencyPlotView.cpp new file mode 100644 index 0000000..682f9d1 --- /dev/null +++ b/Editor/widgets/FrequencyPlotView.cpp @@ -0,0 +1,191 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include +#include + +#include "FrequencyPlotItem.h" +#include "FrequencyPlotView.h" + +using namespace std; + +FrequencyPlotView::FrequencyPlotView(QWidget *parent) : + QGraphicsView(parent) +{ + setViewportMargins(20, 0, 0, 20); + hRuler = new FrequencyPlotHRuler(this); + vRuler = new FrequencyPlotVRuler(this); +} + +FrequencyPlotScene* FrequencyPlotView::scene() const +{ + return qobject_cast(QGraphicsView::scene()); +} + +void FrequencyPlotView::setScene(FrequencyPlotScene* scene) +{ + QGraphicsView::setScene(scene); +} + +void FrequencyPlotView::drawBackground(QPainter* painter, const QRectF& drawRect) +{ + painter->setRenderHint(QPainter::Antialiasing, false); + + QRectF rect = drawRect; + if(!sceneRect().contains(rect)) + { + rect = rect.intersected(sceneRect()); + painter->setClipRect(rect); + } + + FrequencyPlotScene* s = scene(); + QPointF topLeft = mapToScene(0, 0); + QPointF bottomRight = mapToScene(viewport()->width(), viewport()->height()); + double dbHeight = abs(s->yToDb(topLeft.y()) - s->yToDb(bottomRight.y())); + double dbStep = dbHeight / 4; + + double dbBase = pow(10, floor(log10(dbStep))); + if(dbStep >= 5 * dbBase) + dbStep = 5 * dbBase; + else if(dbStep >= 2 * dbBase) + dbStep = 2 * dbBase; + else + dbStep = dbBase; + + double fromDb = floor(s->yToDb(rect.top() + rect.height()) / dbStep) * dbStep; + double toDb = ceil(s->yToDb(rect.top()) / dbStep) * dbStep; + + painter->setPen(QColor(200, 200, 200)); + for(double db = fromDb; db <= toDb; db += dbStep) + { + double y = floor(s->dbToY(db)) + 0.5; + if(y != -1) + painter->drawLine(topLeft.x(), y, bottomRight.x(), y); + } + + double fromHz = s->xToHz(rect.left()); + double toHz = s->xToHz(rect.left() + rect.width()); + + const vector& bands = s->getBands(); + if(bands.empty()) + { + double hzBase = pow(10, floor(log10(fromHz))); + fromHz = floor(fromHz / hzBase) * hzBase; + fromHz += hzBase; + if(round(fromHz / hzBase) >= 10) + hzBase *= 10; + for(double hz = fromHz; hz <= toHz;) + { + double x = floor(s->hzToX(hz)) + 0.5; + if(x >= 0) + painter->drawLine(x, topLeft.y(), x, bottomRight.y()); + + hz += hzBase; + if(round(hz / hzBase) >= 10) + hzBase *= 10; + } + } + else + { + vector::const_iterator it = lower_bound(bands.cbegin(), bands.cend(), fromHz); + for(; it != bands.cend() && *it < toHz; it++) + { + double hz = *it; + double x = floor(s->hzToX(hz)) + 0.5; + if(x >= 0) + painter->drawLine(x, topLeft.y(), x, bottomRight.y()); + } + } +} + +void FrequencyPlotView::updateHRuler() +{ + hRuler->update(); +} + +void FrequencyPlotView::wheelEvent(QWheelEvent* event) +{ + FrequencyPlotScene* s = scene(); + QPointF scenePos = mapToScene(event->pos()); + double hz = s->xToHz(scenePos.x()); + double db = s->yToDb(scenePos.y()); + + qreal zoomFactor = pow(1.001, event->angleDelta().y()); + s->setZoom(max(0.5, min(30, s->getZoom() * zoomFactor))); + + double x = s->hzToX(hz); + if(x != -1) + horizontalScrollBar()->setValue(horizontalScrollBar()->value() + round(x - scenePos.x())); + double y = s->dbToY(db); + if(y != -1) + verticalScrollBar()->setValue(verticalScrollBar()->value() + round(y - scenePos.y())); + + resetCachedContent(); + viewport()->update(); + hRuler->update(); + vRuler->update(); +} + +void FrequencyPlotView::scrollContentsBy(int dx, int dy) +{ + QGraphicsView::scrollContentsBy(dx, dy); + + if(dx != 0) + hRuler->update(); + if(dy != 0) + vRuler->update(); +} + +void FrequencyPlotView::resizeEvent(QResizeEvent* event) +{ + QGraphicsView::resizeEvent(event); + + const QRect rect = viewport()->geometry(); + hRuler->setGeometry(rect.x() - 20, rect.y() + rect.height(), rect.width() + 20, 20); + vRuler->setGeometry(rect.x() - 20, rect.y(), 20, rect.height() + 20); +} + +void FrequencyPlotView::mousePressEvent(QMouseEvent* event) +{ + QGraphicsView::mousePressEvent(event); + + lastMousePos = event->pos(); +} + +void FrequencyPlotView::mouseMoveEvent(QMouseEvent* event) +{ + if(event->buttons() & Qt::RightButton) + { + horizontalScrollBar()->setValue(horizontalScrollBar()->value() - (event->x() - lastMousePos.x())); + verticalScrollBar()->setValue(verticalScrollBar()->value() - (event->y() - lastMousePos.y())); + } + else + { + QGraphicsView::mouseMoveEvent(event); + } + lastMousePos = event->pos(); +} + +void FrequencyPlotView::showEvent(QShowEvent* event) +{ + QGraphicsView::showEvent(event); + + horizontalScrollBar()->setValue(scene()->hzToX(20)); +} diff --git a/Editor/widgets/FrequencyPlotView.h b/Editor/widgets/FrequencyPlotView.h new file mode 100644 index 0000000..c80f972 --- /dev/null +++ b/Editor/widgets/FrequencyPlotView.h @@ -0,0 +1,53 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include + +#include "FrequencyPlotScene.h" +#include "FrequencyPlotHRuler.h" +#include "FrequencyPlotVRuler.h" + +class FrequencyPlotView : public QGraphicsView +{ + Q_OBJECT +public: + explicit FrequencyPlotView(QWidget *parent = 0); + + FrequencyPlotScene* scene() const; + void setScene(FrequencyPlotScene* scene); + + void drawBackground(QPainter* painter, const QRectF& drawRect) override; + + void updateHRuler(); + +protected: + void wheelEvent(QWheelEvent* event) override; + void scrollContentsBy(int dx, int dy) override; + void resizeEvent(QResizeEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + void showEvent(QShowEvent* event) override; + +private: + FrequencyPlotHRuler* hRuler; + FrequencyPlotVRuler* vRuler; + QPoint lastMousePos; +}; diff --git a/Editor/widgets/ResizingLineEdit.cpp b/Editor/widgets/ResizingLineEdit.cpp new file mode 100644 index 0000000..bb5d576 --- /dev/null +++ b/Editor/widgets/ResizingLineEdit.cpp @@ -0,0 +1,54 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "ResizingLineEdit.h" + +ResizingLineEdit::ResizingLineEdit(QWidget *parent) : + QLineEdit(parent) +{ + connect(this, SIGNAL(textChanged(QString)), this, SLOT(readjustSize())); + + readjustSize(); +} + +ResizingLineEdit::ResizingLineEdit(const QString& text, QWidget* parent) : + QLineEdit(text, parent) +{ + connect(this, SIGNAL(textChanged(QString)), this, SLOT(readjustSize())); + + readjustSize(); +} + +QSize ResizingLineEdit::sizeHint() const +{ + QSize originalSize = QLineEdit::sizeHint(); + + QFontMetrics metrics = fontMetrics(); + QSize contentSize(metrics.width(text()), metrics.height()); + QStyleOptionFrameV2 opt; + initStyleOption(&opt); + + QSize size = style()->sizeFromContents(QStyle::CT_LineEdit, &opt, contentSize, this); + return QSize(size.width(), originalSize.height()); +} + +void ResizingLineEdit::readjustSize() +{ + setFixedWidth(sizeHint().width()); +} diff --git a/Editor/widgets/ResizingLineEdit.h b/Editor/widgets/ResizingLineEdit.h new file mode 100644 index 0000000..717f543 --- /dev/null +++ b/Editor/widgets/ResizingLineEdit.h @@ -0,0 +1,35 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include + +class ResizingLineEdit : public QLineEdit +{ + Q_OBJECT +public: + explicit ResizingLineEdit(QWidget *parent = 0); + explicit ResizingLineEdit(const QString& text, QWidget* parent=0); + + QSize sizeHint() const override; + +private slots: + void readjustSize(); +}; diff --git a/EqualizerAPO.cpp b/EqualizerAPO.cpp index ef35926..05bfd7d 100644 --- a/EqualizerAPO.cpp +++ b/EqualizerAPO.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include #include #define INITGUID diff --git a/EqualizerAPO.h b/EqualizerAPO.h index d17fe0d..073a524 100644 --- a/EqualizerAPO.h +++ b/EqualizerAPO.h @@ -31,11 +31,6 @@ class INonDelegatingUnknown virtual ULONG __stdcall NonDelegatingRelease() = 0; }; -// {EACD2258-FCAC-4FF4-B36D-419E924A6D79} -const GUID EQUALIZERAPO_PRE_MIX_GUID = { 0xeacd2258, 0xfcac, 0x4ff4, { 0xb3, 0x6d, 0x41, 0x9e, 0x92, 0x4a, 0x6d, 0x79 } }; -// {EC1CC9CE-FAED-4822-828A-82A81A6F018F} -const GUID EQUALIZERAPO_POST_MIX_GUID = { 0xec1cc9ce, 0xfaed, 0x4822, { 0x82, 0x8a, 0x82, 0xa8, 0x1a, 0x6f, 0x01, 0x8f } }; - class EqualizerAPO : public CBaseAudioProcessingObject, public IAudioSystemEffects, public INonDelegatingUnknown { diff --git a/EqualizerAPO.sln b/EqualizerAPO.sln index 40bd2fa..e0a577b 100644 --- a/EqualizerAPO.sln +++ b/EqualizerAPO.sln @@ -1,6 +1,8 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.40629.0 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EqualizerAPO", "EqualizerAPO.vcxproj", "{5AF4EE2B-CC80-4AE5-A786-86FACC0CC649}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Configurator", "Configurator\Configurator.vcxproj", "{E18E1CA7-3D6C-4CD5-9CE9-E72565A206E5}" diff --git a/EqualizerAPO.vcxproj b/EqualizerAPO.vcxproj index 0c21d12..1f80157 100644 --- a/EqualizerAPO.vcxproj +++ b/EqualizerAPO.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -22,6 +22,7 @@ {5AF4EE2B-CC80-4AE5-A786-86FACC0CC649} Win32Proj EqualizerAPO + 10.0.10240.0 @@ -29,24 +30,28 @@ true Unicode false + v120 DynamicLibrary true Unicode false + v120 DynamicLibrary false true Unicode + v120 DynamicLibrary false true Unicode + v120 @@ -66,27 +71,27 @@ true - $(SolutionDir);$(IncludePath);$(DDK_PATH)\inc\api - $(LibraryPath);$(DDK_PATH)\lib\win7\i386 + $(SolutionDir);$(IncludePath);$(DDK_PATH)\inc\api;C:\Program Files\libsndfile\include;C:\Program Files (x86)\fftw3 + C:\Program Files (x86)\libsndfile\lib;C:\Program Files (x86)\fftw3;$(LibraryPath);$(DDK_PATH)\lib\win7\i386 true - $(SolutionDir);$(IncludePath);$(DDK_PATH)\inc\api - $(LibraryPath);$(DDK_PATH)\lib\win7\amd64 + $(SolutionDir);$(IncludePath);$(DDK_PATH)\inc\api;C:\Program Files\libsndfile\include;C:\Program Files\fftw3 + C:\Program Files\libsndfile\lib;C:\Program Files\fftw3;$(LibraryPath) false - $(SolutionDir);$(IncludePath);$(DDK_PATH)\inc\api - $(LibraryPath);$(DDK_PATH)\lib\win7\i386 + $(SolutionDir);$(IncludePath);$(DDK_PATH)\inc\api;C:\Program Files\libsndfile\include;C:\Program Files (x86)\fftw3 + C:\Program Files (x86)\libsndfile\lib;C:\Program Files (x86)\fftw3;$(LibraryPath);$(DDK_PATH)\lib\win7\i386 false - $(SolutionDir);$(IncludePath);$(DDK_PATH)\inc\api - $(LibraryPath);$(DDK_PATH)\lib\win7\amd64 + $(SolutionDir);$(IncludePath);$(DDK_PATH)\inc\api;C:\Program Files\libsndfile\include;C:\Program Files\fftw3 + C:\Program Files\libsndfile\lib;C:\Program Files\fftw3;$(LibraryPath) - NotUsing + Use Level3 Disabled WIN32;USE_WINDDK;_DEBUG;_WINDOWS;_USRDLL;EQUALIZERAPO_EXPORTS;%(PreprocessorDefinitions) @@ -94,7 +99,7 @@ Windows true - muparserxd.lib;audioeng.lib;version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;%(AdditionalDependencies) + muparserxd.lib;audioeng.lib;version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) EqualizerAPO.def @@ -103,7 +108,7 @@ - NotUsing + Use Level3 Disabled WIN32;USE_WINDDK;_DEBUG;_WINDOWS;_USRDLL;EQUALIZERAPO_EXPORTS;%(PreprocessorDefinitions) @@ -111,7 +116,7 @@ Windows true - muparserxd.lib;audioeng.lib;version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;%(AdditionalDependencies) + muparserxd.lib;audioeng.lib;version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) EqualizerAPO.def @@ -121,7 +126,7 @@ Level3 - NotUsing + Use MaxSpeed true true @@ -133,14 +138,14 @@ true true true - muparserx.lib;audioeng.lib;version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;%(AdditionalDependencies) + muparserx.lib;audioeng.lib;version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) EqualizerAPO.def Level3 - NotUsing + Use MaxSpeed true true @@ -152,7 +157,7 @@ true true true - muparserx.lib;audioeng.lib;version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;%(AdditionalDependencies) + muparserx.lib;audioeng.lib;version.lib;wmcodecdspuuid.lib;AudioBaseProcessingObject.lib;audiomediatypecrt.lib;Shlwapi.lib;libsndfile-1.lib;libfftw3f-3.lib;%(AdditionalDependencies) EqualizerAPO.def @@ -167,12 +172,16 @@ + + + + @@ -180,6 +189,8 @@ + + @@ -187,10 +198,12 @@ + + @@ -205,12 +218,16 @@ + + + + @@ -218,14 +235,23 @@ + + + + + Create + Create + Create + Create + diff --git a/EqualizerAPO.vcxproj.filters b/EqualizerAPO.vcxproj.filters index d515cb5..22e58b7 100644 --- a/EqualizerAPO.vcxproj.filters +++ b/EqualizerAPO.vcxproj.filters @@ -83,6 +83,28 @@ parser + + helpers + + + filters + + + filters + + + helpers + + + libHybridConv-0.1.1 + + + filters + + + filters + + @@ -172,6 +194,28 @@ parser + + helpers + + + filters + + + filters + + + helpers + + + libHybridConv-0.1.1 + + + filters + + + filters + + @@ -190,5 +234,8 @@ {df98049b-1ada-41ad-a5ab-0a175600a767} + + {f4e10c18-382a-4262-9d1b-308f65c34af3} + \ No newline at end of file diff --git a/FilterConfiguration.cpp b/FilterConfiguration.cpp index fcc9f18..7e5df39 100644 --- a/FilterConfiguration.cpp +++ b/FilterConfiguration.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include #include "FilterEngine.h" diff --git a/FilterEngine.cpp b/FilterEngine.cpp index 1451298..4110b97 100644 --- a/FilterEngine.cpp +++ b/FilterEngine.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #define _USE_MATH_DEFINES #include #include @@ -37,6 +38,7 @@ #include "helpers/StringHelper.h" #include "helpers/LogHelper.h" #include "helpers/MemoryHelper.h" +#include "helpers/ChannelHelper.h" #include "FilterEngine.h" #include "filters/ExpressionFilterFactory.h" #include "filters/DeviceFilterFactory.h" @@ -49,6 +51,8 @@ #include "filters/DelayFilterFactory.h" #include "filters/CopyFilterFactory.h" #include "filters/IncludeFilterFactory.h" +#include "filters/ConvolutionFilterFactory.h" +#include "filters/GraphicEQFilterFactory.h" using namespace std; using namespace mup; @@ -81,19 +85,8 @@ FilterEngine::FilterEngine() factories.push_back(new PreampFilterFactory()); factories.push_back(new DelayFilterFactory()); factories.push_back(new CopyFilterFactory()); - - channelNameToPosMap[L"L"] = SPEAKER_FRONT_LEFT; - channelNameToPosMap[L"R"] = SPEAKER_FRONT_RIGHT; - channelNameToPosMap[L"C"] = SPEAKER_FRONT_CENTER; - channelNameToPosMap[L"SUB"] = SPEAKER_LOW_FREQUENCY; - channelNameToPosMap[L"RL"] = SPEAKER_BACK_LEFT; - channelNameToPosMap[L"RR"] = SPEAKER_BACK_RIGHT; - channelNameToPosMap[L"RC"] = SPEAKER_BACK_CENTER; - channelNameToPosMap[L"SL"] = SPEAKER_SIDE_LEFT; - channelNameToPosMap[L"SR"] = SPEAKER_SIDE_RIGHT; - - for(hash_map::iterator it=channelNameToPosMap.begin(); it!=channelNameToPosMap.end(); it++) - channelPosToNameMap[it->second] = it->first; + factories.push_back(new ConvolutionFilterFactory()); + factories.push_back(new GraphicEQFilterFactory()); } FilterEngine::~FilterEngine() @@ -155,45 +148,12 @@ void FilterEngine::initialize(float sampleRate, unsigned inputChannelCount, unsi deviceChannelCount = outputChannelCount; if(channelMask == 0) - { - switch(deviceChannelCount) - { - case 1: - channelMask = KSAUDIO_SPEAKER_MONO; - break; - case 2: - channelMask = KSAUDIO_SPEAKER_STEREO; - break; - case 4: - channelMask = KSAUDIO_SPEAKER_QUAD; - break; - case 6: - channelMask = KSAUDIO_SPEAKER_5POINT1_SURROUND; - break; - case 8: - channelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND; - break; - } - } + channelMask = ChannelHelper::getDefaultChannelMask(deviceChannelCount); + this->channelMask = channelMask; - wstringstream channelStream; - unsigned c=0; - for(int i=0; i<31; i++) - { - int channelPos = 1< 0) - channelStream << L" "; - if(channelPosToNameMap.find(channelPos) != channelPosToNameMap.end()) - channelStream << channelPosToNameMap[channelPos]; - else - channelStream << c; - } - } - TraceF(L"%d channels for this device: %s", deviceChannelCount, channelStream.str().c_str()); + vector channelNames = ChannelHelper::getChannelNames(deviceChannelCount, channelMask); + TraceF(L"%d channels for this device: %s", deviceChannelCount, StringHelper::join(channelNames, L" ").c_str()); try { @@ -250,24 +210,7 @@ void FilterEngine::loadConfig() previousConfig = NULL; } - allChannelNames.clear(); - unsigned c=1; - for(int i=0; i<31; i++) - { - int channelPos = 1< #include -#include -#include +#include #define WIN32_LEAN_AND_MEAN #include #include @@ -104,9 +103,7 @@ private: PrecisionTimer timer; void* threadHandle; void* shutdownEvent; - stdext::hash_set watchRegistryKeys; + std::unordered_set watchRegistryKeys; bool lastInputWasSilent; - stdext::hash_map channelNameToPosMap; - stdext::hash_map channelPosToNameMap; }; #pragma AVRT_VTABLES_END diff --git a/IFilter.cpp b/IFilter.cpp index fb551e6..674045e 100644 --- a/IFilter.cpp +++ b/IFilter.cpp @@ -17,49 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include - -#include "helpers/LogHelper.h" +#include "stdafx.h" #include "IFilter.h" using namespace std; - -int IFilter::getChannelIndex(std::wstring word, const std::vector& channelNames, bool allowNew) -{ - int channelIndex = -1; - - if(iswdigit(word[0])) - { - channelIndex = wcstol(word.c_str(), NULL, 10) - 1; - - if(channelIndex < 0 || channelIndex >= (int)channelNames.size()) - { - LogF(L"Channel number %s out of range (1 - %d)", word.c_str(), channelNames.size()); - channelIndex = -1; - } - } - else - { - vector::const_iterator pos = find(channelNames.begin(), channelNames.end(), word); - - if(pos == channelNames.end()) - { - //Special handling to accept "wrong", but unambiguous positions - if(word == L"SL") - pos = find(channelNames.begin(), channelNames.end(), L"RL"); - else if(word == L"SR") - pos = find(channelNames.begin(), channelNames.end(), L"RR"); - else if(word == L"RL") - pos = find(channelNames.begin(), channelNames.end(), L"SL"); - else if(word == L"RR") - pos = find(channelNames.begin(), channelNames.end(), L"SR"); - } - - if(pos != channelNames.end()) - channelIndex = (int)(pos - channelNames.begin()); - else if(!allowNew) - LogF(L"Invalid channel position %s", word.c_str()); - } - - return channelIndex; -} diff --git a/IFilter.h b/IFilter.h index fc65ca8..41e8aa5 100644 --- a/IFilter.h +++ b/IFilter.h @@ -41,6 +41,5 @@ public: virtual void process(float** output, float** input, unsigned frameCount) = 0; protected: - int getChannelIndex(std::wstring word, const std::vector& channelNames, bool allowNew = false); }; #pragma AVRT_VTABLES_END diff --git a/Setup/Setup.nsi b/Setup/Setup.nsi index f723bbc..ba8b917 100644 --- a/Setup/Setup.nsi +++ b/Setup/Setup.nsi @@ -1,6 +1,9 @@ !include "MUI2.nsh" !include "NSISpcre.nsh" +;Use more efficient compression +SetCompressor /SOLID lzma + !insertmacro REReplace !searchparse /file ..\version.h `#define MAJOR ` MAJOR @@ -106,16 +109,35 @@ Section "Install" SecInstall ;Rename before delete as these files may be in use !insertmacro RenameAndDelete "$INSTDIR\EqualizerAPO.dll" + !insertmacro RenameAndDelete "$INSTDIR\libsndfile-1.dll" + !insertmacro RenameAndDelete "$INSTDIR\libfftw3f-3.dll" !insertmacro RenameAndDelete "$INSTDIR\msvcp100.dll" !insertmacro RenameAndDelete "$INSTDIR\msvcr100.dll" + !insertmacro RenameAndDelete "$INSTDIR\msvcp120.dll" + !insertmacro RenameAndDelete "$INSTDIR\msvcr120.dll" File "${BINPATH}\EqualizerAPO.dll" File "${BINPATH}\Configurator.exe" File "${BINPATH}\Benchmark.exe" + File "${BINPATH_EDITOR}\Editor.exe" + File "${LIBPATH}\libsndfile-1.dll" - File "${LIBPATH}\msvcp100.dll" - File "${LIBPATH}\msvcr100.dll" + File "${LIBPATH}\libfftw3f-3.dll" + File "${LIBPATH}\msvcp120.dll" + File "${LIBPATH}\msvcr120.dll" + File "${LIBPATH}\Qt5Core.dll" + File "${LIBPATH}\Qt5Gui.dll" + File "${LIBPATH}\Qt5Widgets.dll" + + CreateDirectory "$INSTDIR\qt" + CreateDirectory "$INSTDIR\qt\platforms" + CreateDirectory "$INSTDIR\qt\imageformats" + + File /oname=qt\platforms\qwindows.dll "${LIBPATH}\qt\platforms\qwindows.dll" + File /oname=qt\imageformats\qgif.dll "${LIBPATH}\qt\imageformats\qgif.dll" + File /oname=qt\imageformats\qico.dll "${LIBPATH}\qt\imageformats\qico.dll" + File /oname=qt\imageformats\qjpeg.dll "${LIBPATH}\qt\imageformats\qjpeg.dll" File "Configuration tutorial (online).url" File "Configuration reference (online).url" @@ -143,6 +165,7 @@ Section "Install" SecInstall !insertmacro MUI_STARTMENU_WRITE_BEGIN Application ;Create shortcuts CreateDirectory "$SMPROGRAMS\$StartMenuFolder" + CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Configuration editor.lnk" "$INSTDIR\Editor.exe" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Configuration tutorial (online).lnk" "$INSTDIR\Configuration tutorial (online).url" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Configuration reference (online).lnk" "$INSTDIR\Configuration reference (online).url" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Configurator.lnk" "$INSTDIR\Configurator.exe" @@ -191,9 +214,17 @@ Section "-un.Uninstall" Delete "$INSTDIR\Configuration reference (online).url" Delete "$INSTDIR\Configuration tutorial (online).url" - Delete /REBOOTOK "$INSTDIR\msvcr100.dll" - Delete /REBOOTOK "$INSTDIR\msvcp100.dll" - Delete "$INSTDIR\libsndfile-1.dll" + RMDir /r "$INSTDIR\qt" + + Delete "$INSTDIR\Qt5Widgets.dll" + Delete "$INSTDIR\Qt5Gui.dll" + Delete "$INSTDIR\Qt5Core.dll" + Delete /REBOOTOK "$INSTDIR\msvcr120.dll" + Delete /REBOOTOK "$INSTDIR\msvcp120.dll" + Delete /REBOOTOK "$INSTDIR\libfftw3f-3.dll" + Delete /REBOOTOK "$INSTDIR\libsndfile-1.dll" + + Delete "$INSTDIR\Editor.exe" Delete "$INSTDIR\Benchmark.exe" Delete "$INSTDIR\Configurator.exe" diff --git a/Setup/Setup32.nsi b/Setup/Setup32.nsi index bc5fa01..d24ba41 100644 --- a/Setup/Setup32.nsi +++ b/Setup/Setup32.nsi @@ -1,5 +1,6 @@ !define BINPATH "..\Release" +!define BINPATH_EDITOR "..\build-Editor-Desktop_Qt_5_5_MSVC2013_32bit-Release\release" !define LIBPATH "lib32" !include "Setup.nsi" diff --git a/Setup/Setup64.nsi b/Setup/Setup64.nsi index 2a94fc2..e04b9e1 100644 --- a/Setup/Setup64.nsi +++ b/Setup/Setup64.nsi @@ -1,5 +1,6 @@ !define BINPATH "..\x64\Release" +!define BINPATH_EDITOR "..\build-Editor-Desktop_Qt_5_5_MSVC2013_64bit-Release\release" !define LIBPATH "lib64" !include "Setup.nsi" diff --git a/Setup/config/config.txt b/Setup/config/config.txt index 771eb23..f7b980b 100644 --- a/Setup/config/config.txt +++ b/Setup/config/config.txt @@ -1,2 +1,3 @@ Preamp: -6 dB Include: example.txt +GraphicEQ: 25 0; 40 0; 63 0; 100 0; 160 0; 250 0; 400 0; 630 0; 1000 0; 1600 0; 2500 0; 4000 0; 6300 0; 10000 0; 16000 0 \ No newline at end of file diff --git a/Setup/config/iir_lowpass.txt b/Setup/config/iir_lowpass.txt index f76a164..34055db 100644 --- a/Setup/config/iir_lowpass.txt +++ b/Setup/config/iir_lowpass.txt @@ -15,4 +15,4 @@ Do: `b0 = 1 + alpha` Do: `b1 = -2 * cs` Do: `b2 = 1 - alpha` -Filter: ON IIR Order 2 Coefficients `a0` `a1` `a2` `b0` `b1` `b2` +Filter: ON IIR Order 2 Coefficients `a0` `a1` `a2` `b0` `b1` `b2` \ No newline at end of file diff --git a/Setup/config/multichannel.txt b/Setup/config/multichannel.txt index 5114384..fbe8520 100644 --- a/Setup/config/multichannel.txt +++ b/Setup/config/multichannel.txt @@ -9,4 +9,4 @@ Include: demo.txt Channel: 2 C #Filters for second(right) and center channel -Include: example.txt +Include: example.txt \ No newline at end of file diff --git a/Setup/config/selective_delay.txt b/Setup/config/selective_delay.txt index cf01740..db826c8 100644 --- a/Setup/config/selective_delay.txt +++ b/Setup/config/selective_delay.txt @@ -8,4 +8,4 @@ Filter: ON LP Fc 2000 Hz # Apply delay Delay: 500 ms # Add the delayed signal back to the original channels -Copy: L=0.5*L+0.5*L2 R=0.5*R+0.5*R2 +Copy: L=0.5*L+0.5*L2 R=0.5*R+0.5*R2 \ No newline at end of file diff --git a/Setup/lib32/Qt5Core.dll b/Setup/lib32/Qt5Core.dll new file mode 100644 index 0000000..4dac0cb Binary files /dev/null and b/Setup/lib32/Qt5Core.dll differ diff --git a/Setup/lib32/Qt5Gui.dll b/Setup/lib32/Qt5Gui.dll new file mode 100644 index 0000000..562d3e8 Binary files /dev/null and b/Setup/lib32/Qt5Gui.dll differ diff --git a/Setup/lib32/Qt5Widgets.dll b/Setup/lib32/Qt5Widgets.dll new file mode 100644 index 0000000..e7e1c10 Binary files /dev/null and b/Setup/lib32/Qt5Widgets.dll differ diff --git a/Setup/lib32/libfftw3f-3.dll b/Setup/lib32/libfftw3f-3.dll new file mode 100644 index 0000000..e40d992 Binary files /dev/null and b/Setup/lib32/libfftw3f-3.dll differ diff --git a/Setup/lib32/msvcp100.dll b/Setup/lib32/msvcp100.dll deleted file mode 100644 index e9eae44..0000000 Binary files a/Setup/lib32/msvcp100.dll and /dev/null differ diff --git a/Setup/lib32/msvcp120.dll b/Setup/lib32/msvcp120.dll new file mode 100644 index 0000000..a237d2d Binary files /dev/null and b/Setup/lib32/msvcp120.dll differ diff --git a/Setup/lib32/msvcr100.dll b/Setup/lib32/msvcr100.dll deleted file mode 100644 index fd91c89..0000000 Binary files a/Setup/lib32/msvcr100.dll and /dev/null differ diff --git a/Setup/lib32/msvcr120.dll b/Setup/lib32/msvcr120.dll new file mode 100644 index 0000000..8c36149 Binary files /dev/null and b/Setup/lib32/msvcr120.dll differ diff --git a/Setup/lib32/qt/imageformats/qgif.dll b/Setup/lib32/qt/imageformats/qgif.dll new file mode 100644 index 0000000..e63e7ef Binary files /dev/null and b/Setup/lib32/qt/imageformats/qgif.dll differ diff --git a/Setup/lib32/qt/imageformats/qico.dll b/Setup/lib32/qt/imageformats/qico.dll new file mode 100644 index 0000000..15c4f36 Binary files /dev/null and b/Setup/lib32/qt/imageformats/qico.dll differ diff --git a/Setup/lib32/qt/imageformats/qjpeg.dll b/Setup/lib32/qt/imageformats/qjpeg.dll new file mode 100644 index 0000000..b5f27b3 Binary files /dev/null and b/Setup/lib32/qt/imageformats/qjpeg.dll differ diff --git a/Setup/lib32/qt/platforms/qwindows.dll b/Setup/lib32/qt/platforms/qwindows.dll new file mode 100644 index 0000000..2287e7b Binary files /dev/null and b/Setup/lib32/qt/platforms/qwindows.dll differ diff --git a/Setup/lib64/Qt5Core.dll b/Setup/lib64/Qt5Core.dll new file mode 100644 index 0000000..57e743d Binary files /dev/null and b/Setup/lib64/Qt5Core.dll differ diff --git a/Setup/lib64/Qt5Gui.dll b/Setup/lib64/Qt5Gui.dll new file mode 100644 index 0000000..6e58aba Binary files /dev/null and b/Setup/lib64/Qt5Gui.dll differ diff --git a/Setup/lib64/Qt5Widgets.dll b/Setup/lib64/Qt5Widgets.dll new file mode 100644 index 0000000..a8464f9 Binary files /dev/null and b/Setup/lib64/Qt5Widgets.dll differ diff --git a/Setup/lib64/libfftw3f-3.dll b/Setup/lib64/libfftw3f-3.dll new file mode 100644 index 0000000..3244b4f Binary files /dev/null and b/Setup/lib64/libfftw3f-3.dll differ diff --git a/Setup/lib64/msvcp100.dll b/Setup/lib64/msvcp100.dll deleted file mode 100644 index 68fa0d3..0000000 Binary files a/Setup/lib64/msvcp100.dll and /dev/null differ diff --git a/Setup/lib64/msvcp120.dll b/Setup/lib64/msvcp120.dll new file mode 100644 index 0000000..4ea1efa Binary files /dev/null and b/Setup/lib64/msvcp120.dll differ diff --git a/Setup/lib64/msvcr100.dll b/Setup/lib64/msvcr100.dll deleted file mode 100644 index 0318fb0..0000000 Binary files a/Setup/lib64/msvcr100.dll and /dev/null differ diff --git a/Setup/lib64/msvcr120.dll b/Setup/lib64/msvcr120.dll new file mode 100644 index 0000000..d711c92 Binary files /dev/null and b/Setup/lib64/msvcr120.dll differ diff --git a/Setup/lib64/qt/imageformats/qgif.dll b/Setup/lib64/qt/imageformats/qgif.dll new file mode 100644 index 0000000..eed6421 Binary files /dev/null and b/Setup/lib64/qt/imageformats/qgif.dll differ diff --git a/Setup/lib64/qt/imageformats/qico.dll b/Setup/lib64/qt/imageformats/qico.dll new file mode 100644 index 0000000..337e5fc Binary files /dev/null and b/Setup/lib64/qt/imageformats/qico.dll differ diff --git a/Setup/lib64/qt/imageformats/qjpeg.dll b/Setup/lib64/qt/imageformats/qjpeg.dll new file mode 100644 index 0000000..ac8526f Binary files /dev/null and b/Setup/lib64/qt/imageformats/qjpeg.dll differ diff --git a/Setup/lib64/qt/platforms/qwindows.dll b/Setup/lib64/qt/platforms/qwindows.dll new file mode 100644 index 0000000..0ec1b43 Binary files /dev/null and b/Setup/lib64/qt/platforms/qwindows.dll differ diff --git a/Wiki/Configuration reference.txt b/Wiki/Configuration reference.txt index 87b8554..7455941 100644 --- a/Wiki/Configuration reference.txt +++ b/Wiki/Configuration reference.txt @@ -147,6 +147,36 @@ Replaces the audio on the target channel by the sum of the given source channels # (useful for measuring the subwoofer response in REW) Copy: SUB=L L=0.0 R=0.0 C=0.0 RL=0.0 RR=0.0 +
+## GraphicEQ (since version 1.0) +**Syntax:** +GraphicEQ: <Frequency> <Gain (dB)>; <Frequency> <Gain (dB)>; ... + +**Description:** +Adds a graphic equalizer with the specified number of bands and corresponding gain values. The gain values are interpolated linearly in the logarithmic frequency spectrum (so that the lines appear linear in a logarithmic view) between the specified bands. Outside of the specified bands, the frequency response is flat. + +**Example:** + + :::perl + # A 15-band graphic equalizer with ISO bands + GraphicEQ: 25 6; 40 4.5; 63 3; 100 1.5; 160 0; 250 0; 400 0; 630 0; 1000 0; 1600 0; 2500 0; 4000 0; 6300 1.5; 10000 3; 16000 3 + # A custom graphic equalizer + GraphicEQ: 20.00 0.00; 25.00 -1.75; 30.00 -3.20; 35.00 -4.15; 40.00 -4.90; 45.00 -5.55; 50.00 -6.10; 60.00 -6.90; 70.00 -7.40; 80.00 -7.80; 90.00 -8.10; 100.00 -8.30 + +
+## Convolution (since version 1.0) +**Syntax:** +Convolution: <File name> + +**Description:** +Adds a convolver that processes the signal using the impulse response contained in the specified file. The file must be in one of the formats supported by [libsndfile](http://www.mega-nerd.com/libsndfile/#Features) (e.g. wav, flac or ogg). If the file contains multiple channels, the channels are assigned to the selected channels in round-robin order (e.g. a stereo file is assigned to 4 channels as L->1, R->2, L->3, R->4). The sample rate of the file must match the sample rate of the device, otherwise the convolver can not be created. Latency and CPU usage depends on the length and the phase behaviour of the impulse response (linear-phase will have a latency of half the file length while minimum-phase has a lower, but inconsistent latency). The specified file name is relative to the current configuration file's path. While impulse response files can be opened from any directory with sufficient access rights, if the files reside in Equalizer APO's config path or a subdirectory, the configuration will be reloaded automatically if the files are changed so that the change is applied immediately. + +**Example:** + + :::perl + # Convolve with a recorded impulse response for a reverberation effect + Convolution: church.wav +
# Control commands These command do not directly affect the audio but control which commands are executed or how they affect the audio. diff --git a/build.bat b/build.bat index b4a6276..fc438e7 100644 --- a/build.bat +++ b/build.bat @@ -1,9 +1,27 @@ -call "%VS100COMNTOOLS%\vsvars32.bat" +call "%VS120COMNTOOLS%\vsvars32.bat" msbuild EqualizerAPO.sln /p:Configuration=release /p:platform=win32 /t:rebuild if %ERRORLEVEL% GEQ 1 goto done msbuild EqualizerAPO.sln /p:Configuration=release /p:platform=x64 /t:rebuild if %ERRORLEVEL% GEQ 1 goto done +if not exist build-Editor-Desktop_Qt_5_5_MSVC2013_32bit-Release mkdir build-Editor-Desktop_Qt_5_5_MSVC2013_32bit-Release +cd build-Editor-Desktop_Qt_5_5_MSVC2013_32bit-Release +call "%VCINSTALLDIR%vcvarsall.bat" x86 +"C:\Qt\5.5\msvc2013\bin\qmake.exe" ..\Editor\Editor.pro -r -spec win32-msvc2013 +"C:\Qt\Tools\QtCreator\bin\jom.exe" clean +"C:\Qt\Tools\QtCreator\bin\jom.exe" +if %ERRORLEVEL% GEQ 1 goto done +cd.. + +if not exist build-Editor-Desktop_Qt_5_5_MSVC2013_64bit-Release mkdir build-Editor-Desktop_Qt_5_5_MSVC2013_64bit-Release +cd build-Editor-Desktop_Qt_5_5_MSVC2013_64bit-Release +call "%VCINSTALLDIR%vcvarsall.bat" amd64 +"C:\Qt\5.5\msvc2013_64\bin\qmake.exe" ..\Editor\Editor.pro -r -spec win32-msvc2013 +"C:\Qt\Tools\QtCreator\bin\jom.exe" clean +"C:\Qt\Tools\QtCreator\bin\jom.exe" +if %ERRORLEVEL% GEQ 1 goto done +cd.. + if defined ProgramFiles(x86) ( set nsis="%ProgramFiles(x86)%\NSIS\makensis.exe" ) else ( diff --git a/filters/BiQuad.cpp b/filters/BiQuad.cpp index 370388d..1172ae7 100644 --- a/filters/BiQuad.cpp +++ b/filters/BiQuad.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include "BiQuad.h" using namespace std; diff --git a/filters/BiQuadFilter.cpp b/filters/BiQuadFilter.cpp index b2ff76c..a72e7a2 100644 --- a/filters/BiQuadFilter.cpp +++ b/filters/BiQuadFilter.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include "helpers/MemoryHelper.h" #include "BiQuadFilter.h" diff --git a/filters/BiQuadFilter.h b/filters/BiQuadFilter.h index 1035cd3..29b324e 100644 --- a/filters/BiQuadFilter.h +++ b/filters/BiQuadFilter.h @@ -28,9 +28,9 @@ class BiQuadFilter : public IFilter public: BiQuadFilter(BiQuad::Type type, double dbGain, double freq, double bandwidthOrQOrS, bool isBandwidth, bool isCornerFreq); virtual ~BiQuadFilter(); - virtual bool getInPlace() {return true;} - virtual std::vector initialize(float sampleRate, unsigned maxFrameCount, std::vector channelNames); - virtual void process(float** output, float** input, unsigned frameCount); + bool getInPlace() override {return true;} + std::vector initialize(float sampleRate, unsigned maxFrameCount, std::vector channelNames) override; + void process(float** output, float** input, unsigned frameCount) override; BiQuad::Type getType() const; double getDbGain() const; diff --git a/filters/BiQuadFilterFactory.cpp b/filters/BiQuadFilterFactory.cpp index 0e7079f..70c348b 100644 --- a/filters/BiQuadFilterFactory.cpp +++ b/filters/BiQuadFilterFactory.cpp @@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #define _USE_MATH_DEFINES #include #include diff --git a/filters/BiQuadFilterFactory.h b/filters/BiQuadFilterFactory.h index 538f73e..9015a91 100644 --- a/filters/BiQuadFilterFactory.h +++ b/filters/BiQuadFilterFactory.h @@ -20,7 +20,7 @@ #pragma once #include -#include +#include #include "IFilterFactory.h" #include "IFilter.h" @@ -30,11 +30,11 @@ class BiQuadFilterFactory : public IFilterFactory { public: BiQuadFilterFactory(); - virtual std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters); + std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters) override; private: double getFreq(const std::wstring& freqString); - stdext::hash_map filterNameToTypeMap; - stdext::hash_map filterTypeToDescriptionMap; + std::unordered_map filterNameToTypeMap; + std::unordered_map filterTypeToDescriptionMap; }; diff --git a/filters/ChannelFilter.cpp b/filters/ChannelFilter.cpp index ec24bdc..cf71477 100644 --- a/filters/ChannelFilter.cpp +++ b/filters/ChannelFilter.cpp @@ -17,11 +17,13 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include #include #include "helpers/MemoryHelper.h" #include "helpers/LogHelper.h" +#include "helpers/ChannelHelper.h" #include "ChannelFilter.h" using namespace std; @@ -48,7 +50,7 @@ vector ChannelFilter::initialize(float sampleRate, unsigned maxFrameCou if(currentWord == L"ALL") selectedChannels = vector(channelCount, true); else - channelNr = getChannelIndex(currentWord, channelNames); + channelNr = ChannelHelper::getChannelIndex(currentWord, channelNames); if(channelNr != -1 && channelNr < (int)channelCount) { diff --git a/filters/ChannelFilter.h b/filters/ChannelFilter.h index 8abe6e5..2693ef4 100644 --- a/filters/ChannelFilter.h +++ b/filters/ChannelFilter.h @@ -30,10 +30,10 @@ class ChannelFilter : public IFilter public: ChannelFilter(const std::vector& words); virtual ~ChannelFilter(); - virtual bool getAllChannels() {return true;} - virtual bool getSelectChannels() {return true;} - virtual std::vector initialize(float sampleRate, unsigned maxFrameCount, std::vector channelNames); - virtual void process(float** output, float** input, unsigned frameCount); + bool getAllChannels() override {return true;} + bool getSelectChannels() override {return true;} + std::vector initialize(float sampleRate, unsigned maxFrameCount, std::vector channelNames) override; + void process(float** output, float** input, unsigned frameCount) override; private: std::vector words; diff --git a/filters/ChannelFilterFactory.cpp b/filters/ChannelFilterFactory.cpp index 5683fc8..81238f1 100644 --- a/filters/ChannelFilterFactory.cpp +++ b/filters/ChannelFilterFactory.cpp @@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include "helpers/MemoryHelper.h" #include "helpers/StringHelper.h" #include "helpers/LogHelper.h" diff --git a/filters/ChannelFilterFactory.h b/filters/ChannelFilterFactory.h index af5d14a..d1ea348 100644 --- a/filters/ChannelFilterFactory.h +++ b/filters/ChannelFilterFactory.h @@ -27,5 +27,5 @@ class ChannelFilterFactory : public IFilterFactory { public: - virtual std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters); + std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters) override; }; diff --git a/filters/ConvolutionFilter.cpp b/filters/ConvolutionFilter.cpp new file mode 100644 index 0000000..d75c8e0 --- /dev/null +++ b/filters/ConvolutionFilter.cpp @@ -0,0 +1,136 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "stdafx.h" +#include +#define WIN32_LEAN_AND_MEAN +#include +#define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1 +#include +#include + +#include "helpers/LogHelper.h" +#include "helpers/MemoryHelper.h" +#include "ConvolutionFilter.h" + +using namespace std; + +ConvolutionFilter::ConvolutionFilter(wstring filename) +{ + this->filename = filename; + filters = NULL; +} + +ConvolutionFilter::~ConvolutionFilter() +{ + cleanup(); +} + +vector ConvolutionFilter::initialize(float sampleRate, unsigned maxFrameCount, vector channelNames) +{ + cleanup(); + + channelCount = (unsigned)channelNames.size(); + + SF_INFO info; + + SNDFILE* inFile = sf_wchar_open(filename.c_str(), SFM_READ, &info); + if(inFile == NULL) + { + LogF(L"Error while reading impulse response file: %S", sf_strerror(inFile)); + } + else if(abs(sampleRate - info.samplerate) > 1.0f) + { + LogF(L"Impulse response sample rate (%d Hz) does not match device sample rate (%f Hz)", info.samplerate, sampleRate); + } + else + { + TraceF(L"Convolving using impulse response file %s", filename.c_str()); + unsigned fileChannelCount = info.channels; + unsigned frameCount = (unsigned)info.frames; + + float* interleavedBuf = new float[frameCount * fileChannelCount]; + + sf_count_t numRead = 0; + while(numRead < frameCount) + numRead += sf_readf_float(inFile, interleavedBuf + numRead * fileChannelCount, frameCount - numRead); + + sf_close(inFile); + inFile = NULL; + + float** bufs = new float*[fileChannelCount]; + for(unsigned i=0; i initialize(float sampleRate, unsigned maxFrameCount, std::vector channelNames) override; + void process(float** output, float** input, unsigned frameCount) override; + +private: + void cleanup(); + + std::wstring filename; + HConvSingle* filters; + unsigned channelCount; +}; +#pragma AVRT_VTABLES_END diff --git a/filters/ConvolutionFilterFactory.cpp b/filters/ConvolutionFilterFactory.cpp new file mode 100644 index 0000000..5616cce --- /dev/null +++ b/filters/ConvolutionFilterFactory.cpp @@ -0,0 +1,64 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "stdafx.h" +#include + +#include "helpers/MemoryHelper.h" +#include "helpers/StringHelper.h" +#include "helpers/LogHelper.h" +#include "ConvolutionFilter.h" +#include "ConvolutionFilterFactory.h" + +using namespace std; + +vector ConvolutionFilterFactory::createFilter(const wstring& configPath, wstring& command, wstring& parameters) +{ + ConvolutionFilter* filter = NULL; + + if(command == L"Convolution") + { + wstring value = parameters; + while(value.length() > 0 && iswspace(value[0])) + value = value.substr(1); + + wstring absolutePath; + if(PathIsRelativeW(value.c_str())) + { + wchar_t filePath[MAX_PATH]; + configPath._Copy_s(filePath, sizeof(filePath)/sizeof(wchar_t), MAX_PATH); + if(configPath.size() < MAX_PATH) + filePath[configPath.size()] = L'\0'; + else + filePath[MAX_PATH - 1] = L'\0'; + PathRemoveFileSpecW(filePath); + PathAppendW(filePath, value.c_str()); + absolutePath = filePath; + } + else + absolutePath = value; + + void* mem = MemoryHelper::alloc(sizeof(ConvolutionFilter)); + filter = new(mem) ConvolutionFilter(absolutePath); + } + + if(filter == NULL) + return vector(0); + return vector(1, filter); +} diff --git a/filters/ConvolutionFilterFactory.h b/filters/ConvolutionFilterFactory.h new file mode 100644 index 0000000..727821b --- /dev/null +++ b/filters/ConvolutionFilterFactory.h @@ -0,0 +1,31 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include + +#include "IFilterFactory.h" +#include "IFilter.h" + +class ConvolutionFilterFactory : public IFilterFactory +{ +public: + std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters) override; +}; diff --git a/filters/CopyFilter.cpp b/filters/CopyFilter.cpp index f2c5b2a..fb702b3 100644 --- a/filters/CopyFilter.cpp +++ b/filters/CopyFilter.cpp @@ -17,11 +17,13 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include #include #include "helpers/LogHelper.h" #include "helpers/MemoryHelper.h" +#include "helpers/ChannelHelper.h" #include "CopyFilter.h" using namespace std; @@ -54,7 +56,7 @@ vector CopyFilter::initialize(float sampleRate, unsigned maxFrameCount, Assignment& a = assignments[i]; wstring channelName = a.targetChannel; - int channelIndex = getChannelIndex(a.targetChannel, channelNames, true); + int channelIndex = ChannelHelper::getChannelIndex(a.targetChannel, channelNames, true); if(channelIndex != -1) channelName = channelNames[channelIndex]; vector::const_iterator it = find(outChannelNames.begin(), outChannelNames.end(), channelName); @@ -69,9 +71,9 @@ vector CopyFilter::initialize(float sampleRate, unsigned maxFrameCount, { InternalAssignment::InternalSummand& is = ia.sourceSum[j]; Assignment::Summand& s = a.sourceSum[j]; - + if(s.channel != L"") - is.channel = getChannelIndex(s.channel, channelNames); + is.channel = ChannelHelper::getChannelIndex(s.channel, channelNames); else is.channel = -1; @@ -165,3 +167,8 @@ void CopyFilter::cleanup() internalAssignments = NULL; } } + +std::vector CopyFilter::getAssignments() const +{ + return assignments; +} diff --git a/filters/CopyFilter.h b/filters/CopyFilter.h index 57c0f53..fd5251d 100644 --- a/filters/CopyFilter.h +++ b/filters/CopyFilter.h @@ -44,10 +44,12 @@ class CopyFilter : public IFilter public: CopyFilter(const std::vector& assignments); virtual ~CopyFilter(); - virtual bool getAllChannels() {return true;} - virtual bool getInPlace() {return false;} - virtual std::vector initialize(float sampleRate, unsigned maxFrameCount, std::vector channelNames); - virtual void process(float** output, float** input, unsigned frameCount); + bool getAllChannels() override {return true;} + bool getInPlace() override {return false;} + std::vector initialize(float sampleRate, unsigned maxFrameCount, std::vector channelNames) override; + void process(float** output, float** input, unsigned frameCount) override; + + std::vector getAssignments() const; private: void cleanup(); diff --git a/filters/CopyFilterFactory.cpp b/filters/CopyFilterFactory.cpp index 5c874d8..717bb06 100644 --- a/filters/CopyFilterFactory.cpp +++ b/filters/CopyFilterFactory.cpp @@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include "helpers/MemoryHelper.h" #include "helpers/StringHelper.h" #include "helpers/LogHelper.h" diff --git a/filters/CopyFilterFactory.h b/filters/CopyFilterFactory.h index 33e7cb0..3ca1a5e 100644 --- a/filters/CopyFilterFactory.h +++ b/filters/CopyFilterFactory.h @@ -27,5 +27,5 @@ class CopyFilterFactory : public IFilterFactory { public: - virtual std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters); + std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters) override; }; diff --git a/filters/DelayFilter.cpp b/filters/DelayFilter.cpp index 55540a4..b10249e 100644 --- a/filters/DelayFilter.cpp +++ b/filters/DelayFilter.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include #include "helpers/MemoryHelper.h" @@ -109,4 +110,14 @@ void DelayFilter::cleanup() MemoryHelper::free(buffers); buffers = NULL; } -} \ No newline at end of file +} + +bool DelayFilter::getIsMs() const +{ + return isMs; +} + +double DelayFilter::getDelay() const +{ + return delay; +} diff --git a/filters/DelayFilter.h b/filters/DelayFilter.h index 56f4925..b45b046 100644 --- a/filters/DelayFilter.h +++ b/filters/DelayFilter.h @@ -27,9 +27,12 @@ class DelayFilter : public IFilter public: DelayFilter(double delay, bool isMs); virtual ~DelayFilter(); - virtual bool getInPlace() {return false;} - virtual std::vector initialize(float sampleRate, unsigned maxFrameCount, std::vector channelNames); - virtual void process(float** output, float** input, unsigned frameCount); + bool getInPlace() override {return false;} + std::vector initialize(float sampleRate, unsigned maxFrameCount, std::vector channelNames) override; + void process(float** output, float** input, unsigned frameCount) override; + + double getDelay() const; + bool getIsMs() const; private: void cleanup(); diff --git a/filters/DelayFilterFactory.cpp b/filters/DelayFilterFactory.cpp index 153e27b..0de86af 100644 --- a/filters/DelayFilterFactory.cpp +++ b/filters/DelayFilterFactory.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include #include "helpers/MemoryHelper.h" @@ -36,12 +37,12 @@ vector DelayFilterFactory::createFilter(const wstring& configPath, wst //Conversion to period as decimal mark, if needed wstring value = StringHelper::replaceCharacters(parameters, L",", L"."); - double delay = 0; + double delay = -1; wstring unit; wstringstream stream(value); stream >> delay >> unit; - if(delay > 0) + if(delay >= 0) { if(StringHelper::toLowerCase(unit) == L"ms") { @@ -61,4 +62,4 @@ vector DelayFilterFactory::createFilter(const wstring& configPath, wst if(filter == NULL) return vector(0); return vector(1, filter); -} \ No newline at end of file +} diff --git a/filters/DelayFilterFactory.h b/filters/DelayFilterFactory.h index 304f1ea..1444ea3 100644 --- a/filters/DelayFilterFactory.h +++ b/filters/DelayFilterFactory.h @@ -27,5 +27,5 @@ class DelayFilterFactory : public IFilterFactory { public: - virtual std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters); + std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters) override; }; diff --git a/filters/DeviceFilterFactory.cpp b/filters/DeviceFilterFactory.cpp index e58bb0d..e0f3174 100644 --- a/filters/DeviceFilterFactory.cpp +++ b/filters/DeviceFilterFactory.cpp @@ -17,13 +17,17 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include "helpers/LogHelper.h" #include "helpers/StringHelper.h" +#ifndef NO_FILTERENGINE #include "FilterEngine.h" +#endif #include "DeviceFilterFactory.h" using namespace std; +#ifndef NO_FILTERENGINE void DeviceFilterFactory::initialize(FilterEngine* engine) { deviceString = engine->getDeviceName() + L" " + engine->getConnectionName() + L" " + engine->getDeviceGuid(); @@ -33,6 +37,7 @@ void DeviceFilterFactory::initialize(FilterEngine* engine) parser->DefineConst(L"connectionName", engine->getConnectionName()); parser->DefineConst(L"deviceGuid", engine->getDeviceGuid()); } +#endif vector DeviceFilterFactory::startOfConfiguration() { @@ -45,58 +50,9 @@ vector DeviceFilterFactory::createFilter(const wstring& configPath, ws { if(command == L"Device") { - wstring value = StringHelper::trim(parameters) + L";"; + bool matches = matchDevice(deviceString, parameters); - vector> fullList; - vector currentList; - wstring currentWord; - - for(unsigned i=0; i 0) - { - currentList.push_back(currentWord); - currentWord.clear(); - } - if(c == L';' && currentList.size() > 0) - { - fullList.push_back(currentList); - currentList.clear(); - } - } - else - { - currentWord += c; - } - } - - bool matches = false; - - for(unsigned i=0; i DeviceFilterFactory::endOfFile(const std::wstring& configP return vector(); } + +bool DeviceFilterFactory::matchDevice(const std::wstring& deviceString, const std::wstring& pattern) +{ + wstring value = StringHelper::trim(pattern) + L";"; + + vector> fullList; + vector currentList; + wstring currentWord; + + for(unsigned i=0; i 0) + { + currentList.push_back(currentWord); + currentWord.clear(); + } + if(c == L';' && currentList.size() > 0) + { + fullList.push_back(currentList); + currentList.clear(); + } + } + else + { + currentWord += c; + } + } + + bool matches = false; + + for(unsigned i=0; i startOfConfiguration(); - virtual std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters); - virtual std::vector endOfFile(const std::wstring& configPath); +#ifndef NO_FILTERENGINE + void initialize(FilterEngine* engine) override; +#endif + std::vector startOfConfiguration() override; + std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters) override; + std::vector endOfFile(const std::wstring& configPath) override; + + static bool matchDevice(const std::wstring& deviceString, const std::wstring& pattern); private: bool deviceMatches; diff --git a/filters/ExpressionFilterFactory.cpp b/filters/ExpressionFilterFactory.cpp index 0dfdf50..20bdf1b 100644 --- a/filters/ExpressionFilterFactory.cpp +++ b/filters/ExpressionFilterFactory.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include #include "helpers/LogHelper.h" diff --git a/filters/ExpressionFilterFactory.h b/filters/ExpressionFilterFactory.h index 9f75a5b..db76fae 100644 --- a/filters/ExpressionFilterFactory.h +++ b/filters/ExpressionFilterFactory.h @@ -28,8 +28,8 @@ class ExpressionFilterFactory : public IFilterFactory { public: - virtual void initialize(FilterEngine* engine); - virtual std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters); + void initialize(FilterEngine* engine) override; + std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters) override; private: mup::ParserX* parser; diff --git a/filters/GraphicEQFilter.cpp b/filters/GraphicEQFilter.cpp new file mode 100644 index 0000000..7e90845 --- /dev/null +++ b/filters/GraphicEQFilter.cpp @@ -0,0 +1,186 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "stdafx.h" +#define _USE_MATH_DEFINES +#include +#define WIN32_LEAN_AND_MEAN +#include +#define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1 +#include + +#include "helpers/LogHelper.h" +#include "helpers/MemoryHelper.h" +#include "GraphicEQFilter.h" + +using namespace std; + +GraphicEQFilter::GraphicEQFilter(const std::vector& nodes, unsigned filterLength) + :nodes(nodes), filterLength(filterLength) +{ + filters = NULL; +} + +GraphicEQFilter::~GraphicEQFilter() +{ + cleanup(); +} + +vector GraphicEQFilter::initialize(float sampleRate, unsigned maxFrameCount, vector channelNames) +{ + cleanup(); + + channelCount = (unsigned)channelNames.size(); + + fftwf_complex* timeData = fftwf_alloc_complex(filterLength * 2); + fftwf_complex* freqData = fftwf_alloc_complex(filterLength * 2); + fftwf_plan planForward = fftwf_plan_dft_1d(filterLength * 2, timeData, freqData, FFTW_FORWARD, FFTW_ESTIMATE); + fftwf_plan planReverse = fftwf_plan_dft_1d(filterLength * 2, freqData, timeData, FFTW_BACKWARD, FFTW_ESTIMATE); + + GainIterator gainIterator(nodes); + for(unsigned i=0; i& GraphicEQFilter::getNodes() +{ + return nodes; +} + +void GraphicEQFilter::cleanup() +{ + if(filters != NULL) + { + for(unsigned i=0; i + +#include "IFilter.h" +#include "libHybridConv-0.1.1/libHybridConv_eapo.h" +#include "helpers/GainIterator.h" + +#pragma AVRT_VTABLES_BEGIN +class GraphicEQFilter : public IFilter +{ +public: + GraphicEQFilter(const std::vector& nodes, unsigned filterLength); + virtual ~GraphicEQFilter(); + bool getInPlace() override {return true;} + std::vector initialize(float sampleRate, unsigned maxFrameCount, std::vector channelNames) override; + void process(float** output, float** input, unsigned frameCount) override; + + const std::vector& getNodes(); + +private: + void cleanup(); + void mps(fftwf_complex* timeData, fftwf_complex* freqData, fftwf_plan planForward, fftwf_plan planReverse); + + std::vector nodes; + unsigned filterLength; + HConvSingle* filters; + unsigned channelCount; +}; +#pragma AVRT_VTABLES_END diff --git a/filters/GraphicEQFilterFactory.cpp b/filters/GraphicEQFilterFactory.cpp new file mode 100644 index 0000000..724a5f6 --- /dev/null +++ b/filters/GraphicEQFilterFactory.cpp @@ -0,0 +1,70 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "stdafx.h" +#include + +#include "helpers/MemoryHelper.h" +#include "helpers/StringHelper.h" +#include "helpers/LogHelper.h" +#include "GraphicEQFilter.h" +#include "GraphicEQFilterFactory.h" + +using namespace std; + +static wregex regexNumber(L"[-+0-9.eE]+"); + +vector GraphicEQFilterFactory::createFilter(const wstring& configPath, wstring& command, wstring& parameters) +{ + GraphicEQFilter* filter = NULL; + + if(command == L"GraphicEQ") + { + wstring value = parameters; + if(value.find(L'.') == wstring::npos) + value = StringHelper::replaceCharacters(value, L",", L"."); + + wsregex_iterator begin(value.begin(), value.end(), regexNumber); + wsregex_iterator end; + + vector nodes; + for(wsregex_iterator it = begin; it != end; it++) + { + wsmatch freqMatch = *it++; + if(it != end) + { + wsmatch gainMatch = *it; + double freq = wcstod(freqMatch.str(0).c_str(), NULL); + double gain = wcstod(gainMatch.str(0).c_str(), NULL); + FilterNode node(freq, gain); + nodes.push_back(node); + } + } + sort(nodes.begin(), nodes.end()); + + TraceF(L"Graphic equalizer with %d nodes", nodes.size()); + + void* mem = MemoryHelper::alloc(sizeof(GraphicEQFilter)); + filter = new(mem) GraphicEQFilter(nodes, 16384); + } + + if(filter == NULL) + return vector(0); + return vector(1, filter); +} diff --git a/filters/GraphicEQFilterFactory.h b/filters/GraphicEQFilterFactory.h new file mode 100644 index 0000000..f771d6d --- /dev/null +++ b/filters/GraphicEQFilterFactory.h @@ -0,0 +1,31 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include + +#include "IFilterFactory.h" +#include "IFilter.h" + +class GraphicEQFilterFactory : public IFilterFactory +{ +public: + std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters) override; +}; diff --git a/filters/IIRFilter.cpp b/filters/IIRFilter.cpp index 3a2f9fb..d35a6eb 100644 --- a/filters/IIRFilter.cpp +++ b/filters/IIRFilter.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include "helpers/MemoryHelper.h" #include "IIRFilter.h" diff --git a/filters/IIRFilter.h b/filters/IIRFilter.h index 6c3cfe3..79689a3 100644 --- a/filters/IIRFilter.h +++ b/filters/IIRFilter.h @@ -27,9 +27,9 @@ class IIRFilter : public IFilter public: IIRFilter(const std::vector& coefficients); virtual ~IIRFilter(); - virtual bool getInPlace() {return true;} - virtual std::vector initialize(float sampleRate, unsigned maxFrameCount, std::vector channelNames); - virtual void process(float** output, float** input, unsigned frameCount); + bool getInPlace() override {return true;} + std::vector initialize(float sampleRate, unsigned maxFrameCount, std::vector channelNames) override; + void process(float** output, float** input, unsigned frameCount) override; private: unsigned order; diff --git a/filters/IIRFilterFactory.cpp b/filters/IIRFilterFactory.cpp index 96aa82a..6dc9ddf 100644 --- a/filters/IIRFilterFactory.cpp +++ b/filters/IIRFilterFactory.cpp @@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #define _USE_MATH_DEFINES #include #include diff --git a/filters/IIRFilterFactory.h b/filters/IIRFilterFactory.h index 8cf8cbc..e59302c 100644 --- a/filters/IIRFilterFactory.h +++ b/filters/IIRFilterFactory.h @@ -20,7 +20,6 @@ #pragma once #include -#include #include "IFilterFactory.h" #include "IFilter.h" @@ -29,5 +28,5 @@ class IIRFilterFactory : public IFilterFactory { public: IIRFilterFactory(); - virtual std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters); + std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters) override; }; diff --git a/filters/IfFilterFactory.cpp b/filters/IfFilterFactory.cpp index f167456..96eb189 100644 --- a/filters/IfFilterFactory.cpp +++ b/filters/IfFilterFactory.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include "helpers/LogHelper.h" #include "helpers/StringHelper.h" #include "parser/RegexFunctions.h" diff --git a/filters/IfFilterFactory.h b/filters/IfFilterFactory.h index f37deeb..59bf353 100644 --- a/filters/IfFilterFactory.h +++ b/filters/IfFilterFactory.h @@ -28,11 +28,11 @@ class IfFilterFactory : public IFilterFactory { public: - virtual void initialize(FilterEngine* engine); - virtual std::vector startOfConfiguration(); - virtual std::vector startOfFile(const std::wstring& configPath); - virtual std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters); - virtual std::vector endOfFile(const std::wstring& configPath); + void initialize(FilterEngine* engine) override; + std::vector startOfConfiguration() override; + std::vector startOfFile(const std::wstring& configPath) override; + std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters) override; + std::vector endOfFile(const std::wstring& configPath) override; private: mup::ParserX* parser; diff --git a/filters/IncludeFilterFactory.cpp b/filters/IncludeFilterFactory.cpp index d6855b1..6295125 100644 --- a/filters/IncludeFilterFactory.cpp +++ b/filters/IncludeFilterFactory.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include #include "helpers/LogHelper.h" diff --git a/filters/IncludeFilterFactory.h b/filters/IncludeFilterFactory.h index 5e81378..f773cd8 100644 --- a/filters/IncludeFilterFactory.h +++ b/filters/IncludeFilterFactory.h @@ -27,8 +27,8 @@ class IncludeFilterFactory : public IFilterFactory { public: - virtual void initialize(FilterEngine* engine); - virtual std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters); + void initialize(FilterEngine* engine) override; + std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters) override; private: FilterEngine* engine; diff --git a/filters/PreampFilter.cpp b/filters/PreampFilter.cpp index f88e5bd..34e8499 100644 --- a/filters/PreampFilter.cpp +++ b/filters/PreampFilter.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #define _USE_MATH_DEFINES #include diff --git a/filters/PreampFilter.h b/filters/PreampFilter.h index 033b634..24d65e8 100644 --- a/filters/PreampFilter.h +++ b/filters/PreampFilter.h @@ -26,8 +26,10 @@ class PreampFilter : public IFilter { public: PreampFilter(double dbGain); - virtual std::vector initialize(float sampleRate, unsigned maxFrameCount, std::vector channelNames); - virtual void process(float** output, float** input, unsigned frameCount); + std::vector initialize(float sampleRate, unsigned maxFrameCount, std::vector channelNames) override; + void process(float** output, float** input, unsigned frameCount) override; + + double getDbGain() const {return dbGain;} private: float gain; diff --git a/filters/PreampFilterFactory.cpp b/filters/PreampFilterFactory.cpp index b6f5a97..8ed0e00 100644 --- a/filters/PreampFilterFactory.cpp +++ b/filters/PreampFilterFactory.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include "helpers/MemoryHelper.h" #include "helpers/StringHelper.h" #include "helpers/LogHelper.h" @@ -48,4 +49,4 @@ vector PreampFilterFactory::createFilter(const wstring& configPath, ws if(filter == NULL) return vector(0); return vector(1, filter); -} \ No newline at end of file +} diff --git a/filters/PreampFilterFactory.h b/filters/PreampFilterFactory.h index 8022b32..7d86331 100644 --- a/filters/PreampFilterFactory.h +++ b/filters/PreampFilterFactory.h @@ -27,5 +27,5 @@ class PreampFilterFactory : public IFilterFactory { public: - virtual std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters); + std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters) override; }; diff --git a/filters/StageFilterFactory.cpp b/filters/StageFilterFactory.cpp index f0b60be..2a35e7d 100644 --- a/filters/StageFilterFactory.cpp +++ b/filters/StageFilterFactory.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include "helpers/LogHelper.h" #include "helpers/StringHelper.h" #include "FilterEngine.h" diff --git a/filters/StageFilterFactory.h b/filters/StageFilterFactory.h index 099e911..3bfa632 100644 --- a/filters/StageFilterFactory.h +++ b/filters/StageFilterFactory.h @@ -27,11 +27,11 @@ class StageFilterFactory : public IFilterFactory { public: - virtual void initialize(FilterEngine* engine); - virtual std::vector startOfConfiguration(); - virtual std::vector startOfFile(const std::wstring& configPath); - virtual std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters); - virtual std::vector endOfFile(const std::wstring& configPath); + void initialize(FilterEngine* engine) override; + std::vector startOfConfiguration() override; + std::vector startOfFile(const std::wstring& configPath) override; + std::vector createFilter(const std::wstring& configPath, std::wstring& command, std::wstring& parameters) override; + std::vector endOfFile(const std::wstring& configPath) override; private: bool preMix; diff --git a/helpers/ChannelHelper.cpp b/helpers/ChannelHelper.cpp new file mode 100644 index 0000000..a1ef4dc --- /dev/null +++ b/helpers/ChannelHelper.cpp @@ -0,0 +1,143 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "stdafx.h" +#include +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +#include "LogHelper.h" +#include "ChannelHelper.h" + +using namespace std; + +unordered_map ChannelHelper::channelNameToPosMap; +unordered_map ChannelHelper::channelPosToNameMap; +// must come last, so that the static members are already initialized +ChannelHelper ChannelHelper::instance; + +ChannelHelper::ChannelHelper() +{ + channelNameToPosMap[L"L"] = SPEAKER_FRONT_LEFT; + channelNameToPosMap[L"R"] = SPEAKER_FRONT_RIGHT; + channelNameToPosMap[L"C"] = SPEAKER_FRONT_CENTER; + channelNameToPosMap[L"SUB"] = SPEAKER_LOW_FREQUENCY; + channelNameToPosMap[L"RL"] = SPEAKER_BACK_LEFT; + channelNameToPosMap[L"RR"] = SPEAKER_BACK_RIGHT; + channelNameToPosMap[L"RC"] = SPEAKER_BACK_CENTER; + channelNameToPosMap[L"SL"] = SPEAKER_SIDE_LEFT; + channelNameToPosMap[L"SR"] = SPEAKER_SIDE_RIGHT; + + for(unordered_map::iterator it=channelNameToPosMap.begin(); it!=channelNameToPosMap.end(); it++) + channelPosToNameMap[it->second] = it->first; +} + +int ChannelHelper::getDefaultChannelMask(int channelCount) +{ + int channelMask; + + switch(channelCount) + { + case 1: + channelMask = KSAUDIO_SPEAKER_MONO; + break; + case 2: + channelMask = KSAUDIO_SPEAKER_STEREO; + break; + case 4: + channelMask = KSAUDIO_SPEAKER_QUAD; + break; + case 6: + channelMask = KSAUDIO_SPEAKER_5POINT1_SURROUND; + break; + case 8: + channelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND; + break; + default: + channelMask = 0; + } + + return channelMask; +} + +vector ChannelHelper::getChannelNames(int channelCount, int channelMask) +{ + vector channelNames; + int c=1; + for(int i=0; i<31; i++) + { + int channelPos = 1<& channelNames, bool allowNew) +{ + int channelIndex = -1; + + if(iswdigit(word[0])) + { + channelIndex = wcstol(word.c_str(), NULL, 10) - 1; + + if(channelIndex < 0 || channelIndex >= (int)channelNames.size()) + { + LogFStatic(L"Channel number %s out of range (1 - %d)", word.c_str(), channelNames.size()); + channelIndex = -1; + } + } + else + { + vector::const_iterator pos = find(channelNames.begin(), channelNames.end(), word); + + if(pos == channelNames.end()) + { + //Special handling to accept "wrong", but unambiguous positions + if(word == L"SL") + pos = find(channelNames.begin(), channelNames.end(), L"RL"); + else if(word == L"SR") + pos = find(channelNames.begin(), channelNames.end(), L"RR"); + else if(word == L"RL") + pos = find(channelNames.begin(), channelNames.end(), L"SL"); + else if(word == L"RR") + pos = find(channelNames.begin(), channelNames.end(), L"SR"); + } + + if(pos != channelNames.end()) + channelIndex = (int)(pos - channelNames.begin()); + else if(!allowNew) + LogFStatic(L"Invalid channel position %s", word.c_str()); + } + + return channelIndex; +} diff --git a/helpers/ChannelHelper.h b/helpers/ChannelHelper.h new file mode 100644 index 0000000..4307122 --- /dev/null +++ b/helpers/ChannelHelper.h @@ -0,0 +1,39 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include +#include +#include + +class ChannelHelper +{ +public: + static int getDefaultChannelMask(int channelCount); + static std::vector getChannelNames(int channelCount, int channelMask); + static int getChannelIndex(std::wstring word, const std::vector& channelNames, bool allowNew = false); + +private: + ChannelHelper(); + static ChannelHelper instance; + + static std::unordered_map channelNameToPosMap; + static std::unordered_map channelPosToNameMap; +}; diff --git a/helpers/GainIterator.cpp b/helpers/GainIterator.cpp new file mode 100644 index 0000000..dc9b1b3 --- /dev/null +++ b/helpers/GainIterator.cpp @@ -0,0 +1,92 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "stdafx.h" +#include + +#include "GainIterator.h" + +using namespace std; + +GainIterator::GainIterator(const vector& nodes) +{ + this->nodes = nodes; + nodeLeft = NULL; + nodeRight = NULL; +} + +double GainIterator::gainAt(double freq) +{ + if(nodeLeft == NULL && nodeRight == NULL || nodeLeft != NULL && freq < nodeLeft->freq) + { + FilterNode findNode(freq, 0); + vector::iterator it = lower_bound(nodes.begin(), nodes.end(), findNode); + if(it != nodes.begin()) + nodeLeft = &*(it - 1); + if(it != nodes.end()) + nodeRight = &*it; + + if(nodeLeft != NULL && nodeRight != NULL) + { + logLeft = log(nodeLeft->freq); + logRightMinusLeft = log(nodeRight->freq) - logLeft; + } + } + else if(nodeRight != NULL && freq > nodeRight->freq) + { + vector::iterator it = nodes.begin() + (nodeRight - &*nodes.begin()); + while(it != nodes.end() && freq > it->freq) + { + it++; + } + if(it == nodes.end()) + nodeRight = NULL; + else + nodeRight = &*it; + + if(it != nodes.begin()) + nodeLeft = &*(it - 1); + + if(nodeLeft != NULL && nodeRight != NULL) + { + logLeft = log(nodeLeft->freq); + logRightMinusLeft = log(nodeRight->freq) - logLeft; + } + } + + double dbGain; + if(nodeLeft == NULL) + { + if(nodeRight == NULL) + dbGain = 0.0; + else + dbGain = nodeRight->dbGain; + } + else if(nodeRight == NULL) + { + dbGain = nodeLeft->dbGain; + } + else + { + double t = (log(freq) - logLeft) / logRightMinusLeft; + dbGain = nodeLeft->dbGain + t * (nodeRight->dbGain - nodeLeft->dbGain); + } + + return dbGain; +} diff --git a/helpers/GainIterator.h b/helpers/GainIterator.h new file mode 100644 index 0000000..7afa98e --- /dev/null +++ b/helpers/GainIterator.h @@ -0,0 +1,53 @@ +/* + This file is part of EqualizerAPO, a system-wide equalizer. + Copyright (C) 2015 Jonas Thedering + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include + +struct FilterNode +{ + double freq; + double dbGain; + + FilterNode(double freq, double dbGain) + { + this->freq = freq; + this->dbGain = dbGain; + } + + bool operator < (FilterNode other) + { + return freq < other.freq; + } +}; + +class GainIterator +{ +public: + GainIterator(const std::vector& nodes); + double gainAt(double freq); + +private: + std::vector nodes; + FilterNode* nodeLeft; + FilterNode* nodeRight; + double logLeft; + double logRightMinusLeft; +}; diff --git a/helpers/LogHelper.cpp b/helpers/LogHelper.cpp index 3615e3a..c2324ea 100644 --- a/helpers/LogHelper.cpp +++ b/helpers/LogHelper.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include #define WIN32_LEAN_AND_MEAN #include @@ -87,7 +88,7 @@ void LogHelper::log(const char* file, int line, const void* caller, bool trace, GetLocalTime(&___st); DWORD threadId = GetCurrentThreadId(); fwprintf(fp, L"%04d-%02d-%02d %02d:%02d:%02d.%03d %d %08X (%S:%d): ", - ___st.wYear, ___st.wMonth, ___st.wDay, ___st.wHour, ___st.wMinute, ___st.wSecond, ___st.wMilliseconds, threadId, (DWORD)caller, file, line); + ___st.wYear, ___st.wMonth, ___st.wDay, ___st.wHour, ___st.wMinute, ___st.wSecond, ___st.wMilliseconds, threadId, (DWORD)(unsigned long long)caller, file, line); } if(trace) diff --git a/helpers/MemoryHelper.cpp b/helpers/MemoryHelper.cpp index 5a4e0e9..f4c0b2b 100644 --- a/helpers/MemoryHelper.cpp +++ b/helpers/MemoryHelper.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #define WIN32_LEAN_AND_MEAN #include #ifdef _DEBUG diff --git a/helpers/RegistryHelper.cpp b/helpers/RegistryHelper.cpp index b3c52b4..0d66e50 100644 --- a/helpers/RegistryHelper.cpp +++ b/helpers/RegistryHelper.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include #include #include @@ -110,6 +111,39 @@ unsigned long RegistryHelper::readDWORDValue(wstring key, wstring valuename) return result; } +vector RegistryHelper::readBinaryValue(wstring key, wstring valuename) +{ + HKEY keyHandle = openKey(key, KEY_QUERY_VALUE | KEY_WOW64_64KEY); + + LSTATUS status; + DWORD type; + DWORD bufSize; + status = RegQueryValueExW(keyHandle, valuename.c_str(), NULL, &type, NULL, &bufSize); + if(status != ERROR_SUCCESS) + { + RegCloseKey(keyHandle); + throw RegistryException(L"Error while reading registry value " + key + L"\\" + valuename + L": " + StringHelper::getSystemErrorString(status)); + } + + if(type != REG_BINARY) + { + RegCloseKey(keyHandle); + throw RegistryException(L"Registry value " + key + L"\\" + valuename + L" has wrong type"); + } + + vector result(bufSize, 0); + status = RegQueryValueExW(keyHandle, valuename.c_str(), NULL, NULL, result.data(), &bufSize); + + RegCloseKey(keyHandle); + + if(status != ERROR_SUCCESS) + { + throw RegistryException(L"Error while reading registry value " + key + L"\\" + valuename + L": " + StringHelper::getSystemErrorString(status)); + } + + return result; +} + void RegistryHelper::writeValue(wstring key, wstring valuename, wstring value) { HKEY keyHandle = openKey(key, KEY_SET_VALUE | KEY_WOW64_64KEY); @@ -122,6 +156,18 @@ void RegistryHelper::writeValue(wstring key, wstring valuename, wstring value) throw RegistryException(L"Error while writing to registry value " + key + L"\\" + valuename + L": " + StringHelper::getSystemErrorString(status)); } +void RegistryHelper::writeDWORDValue(wstring key, wstring valuename, unsigned long value) +{ + HKEY keyHandle = openKey(key, KEY_SET_VALUE | KEY_WOW64_64KEY); + + LSTATUS status = RegSetValueExW(keyHandle, valuename.c_str(), 0, REG_DWORD, (const BYTE*)&value, sizeof(unsigned long)); + + RegCloseKey(keyHandle); + + if (status != ERROR_SUCCESS) + throw RegistryException(L"Error while writing to registry value " + key + L"\\" + valuename + L": " + StringHelper::getSystemErrorString(status)); +} + void RegistryHelper::writeMultiValue(wstring key, wstring valuename, wstring value) { HKEY keyHandle = openKey(key, KEY_SET_VALUE | KEY_WOW64_64KEY); diff --git a/helpers/RegistryHelper.h b/helpers/RegistryHelper.h index 4cf4d79..d8b1ff8 100644 --- a/helpers/RegistryHelper.h +++ b/helpers/RegistryHelper.h @@ -25,6 +25,11 @@ #define WIN32_LEAN_AND_MEAN #include +// {EACD2258-FCAC-4FF4-B36D-419E924A6D79} +const GUID EQUALIZERAPO_PRE_MIX_GUID = { 0xeacd2258, 0xfcac, 0x4ff4, { 0xb3, 0x6d, 0x41, 0x9e, 0x92, 0x4a, 0x6d, 0x79 } }; +// {EC1CC9CE-FAED-4822-828A-82A81A6F018F} +const GUID EQUALIZERAPO_POST_MIX_GUID = { 0xec1cc9ce, 0xfaed, 0x4822, { 0x82, 0x8a, 0x82, 0xa8, 0x1a, 0x6f, 0x01, 0x8f } }; + #define APP_REGPATH L"HKEY_LOCAL_MACHINE\\SOFTWARE\\EqualizerAPO" class RegistryHelper @@ -32,7 +37,9 @@ class RegistryHelper public: static std::wstring readValue(std::wstring key, std::wstring valuename); static unsigned long readDWORDValue(std::wstring key, std::wstring valuename); + static std::vector readBinaryValue(std::wstring key, std::wstring valuename); static void writeValue(std::wstring key, std::wstring valuename, std::wstring value); + static void writeDWORDValue(std::wstring key, std::wstring valuename, unsigned long value); static void writeMultiValue(std::wstring key, std::wstring valuename, std::wstring value); static void deleteValue(std::wstring key, std::wstring valuename); static void createKey(std::wstring key); diff --git a/helpers/StringHelper.cpp b/helpers/StringHelper.cpp index da3188a..f444d03 100644 --- a/helpers/StringHelper.cpp +++ b/helpers/StringHelper.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include #include #define WIN32_LEAN_AND_MEAN @@ -130,6 +131,24 @@ vector StringHelper::split(const wstring& s, wchar_t splitChar, bool sk return result; } +wstring StringHelper::join(const vector& strings, const wstring& separator) +{ + wstringstream stream; + + bool first = true; + + for(vector::const_iterator it = strings.cbegin(); it != strings.cend(); it++) + { + if(first) + first = false; + else + stream << separator; + stream << *it; + } + + return stream.str(); +} + wstring StringHelper::getSystemErrorString(long status) { wchar_t* buf; @@ -143,4 +162,4 @@ wstring StringHelper::getSystemErrorString(long status) } else return L""; -} \ No newline at end of file +} diff --git a/helpers/StringHelper.h b/helpers/StringHelper.h index f3fb5e1..60d9aab 100644 --- a/helpers/StringHelper.h +++ b/helpers/StringHelper.h @@ -33,5 +33,6 @@ public: static std::wstring toUpperCase(const std::wstring& s); static std::wstring trim(const std::wstring& s); static std::vector split(const std::wstring& s, wchar_t splitChar, bool skipEmpty = true); + static std::wstring join(const std::vector& strings, const std::wstring& separator); static std::wstring getSystemErrorString(long status); }; diff --git a/libHybridConv-0.1.1/Makefile b/libHybridConv-0.1.1/Makefile new file mode 100644 index 0000000..887d1c9 --- /dev/null +++ b/libHybridConv-0.1.1/Makefile @@ -0,0 +1,27 @@ +CC=gcc + +INCDIR= +LIBDIR= + +#CFLAGS=-g -O0 -msse -msse2 -fomit-frame-pointer -funroll-loops -ffast-math -Wall -Wextra +CFLAGS=-O2 -msse -msse2 -fomit-frame-pointer -funroll-loops -ffast-math -Wall -Wextra +LFLAGS=-lm -lfftw3f + + +OBJ=libHybridConv.o hcTest.o + + +all : hcTest + +hcTest : $(OBJ) + $(CC) -o hcTest $(OBJ) $(LIBDIR) $(LFLAGS) + +%.o:%.c + $(CC) $(CFLAGS) -c $< $(INCDIR) + +install: + echo Nothing to be done + +clean: + rm $(OBJ) hcTest + diff --git a/libHybridConv-0.1.1/hcTest.c b/libHybridConv-0.1.1/hcTest.c new file mode 100644 index 0000000..4c07c8a --- /dev/null +++ b/libHybridConv-0.1.1/hcTest.c @@ -0,0 +1,138 @@ +/*************************************************************************** + * Copyright (C) 2009 by Christian Borss * + * christian.borss@rub.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + + +#include "libHybridConv.h" +#include + + +int main(void) +{ + int s, m, l; + int sflen, mflen, lflen; + int num; + double tau_1, tau_16; + double cpu_load; + double c0[9]; + double c1[9]; + double tau_s, tau_m, tau_l; + int num_s, num_m, num_l; + int hlen = 96000; + int f_s = 48000; + + // performance measurement with uniform segmentation + for (s = 0; s < 9; s++) + { + sflen = 64 << s; + num = 1; + printf("%5d / %2d ", sflen, num); + tau_1 = getProcTime(sflen, num, 2.0); + num = 16; + printf("%5d / %2d ", sflen, num); + tau_16 = getProcTime(sflen, num, 2.0); + c1[s] = (tau_16 - tau_1) / 15.0; + c0[s] = tau_1 - c1[s]; + printf("\n"); + } + printf("\n"); + + // performance prediction with 3 segment lengths + for (s = 0; s < 5; s++) + { + for (m = 1; m < 5; m++) + { + for (l = 1; l+m+s < 9; l++) + { + sflen = 64 << s; + mflen = sflen << m; + lflen = mflen << l; + + num_s = mflen / sflen; + num_m = 2 * lflen / mflen; + num_l = ceil((hlen - num_s * sflen - num_m * mflen) / (double)lflen); + tau_s = c0[s] + c1[s] * num_s; + tau_m = c0[s+m] + c1[s+m] * num_m; + tau_l = c0[s+m+l] + c1[s+m+l] * num_l; + + cpu_load = 100.0 * (tau_s * lflen / sflen + tau_m * lflen / mflen + tau_l) * f_s / (double)lflen; + + printf("%4d / %4d / %4d ", sflen, mflen, lflen); + printf("Predicted CPU load: %5.2f %%\n", cpu_load); + } + } + printf("\n"); + } + printf("\n"); + + // performance prediction with 2 segment lengths + for (m = 0; m < 8; m++) + { + for (l = 1; l+m < 9; l++) + { + mflen = 64 << m; + lflen = mflen << l; + + num_m = 2 * lflen / mflen; + num_l = ceil((hlen - num_m * mflen) / (double)lflen); + tau_m = c0[m] + c1[m] * num_m; + tau_l = c0[m+l] + c1[m+l] * num_l; + + cpu_load = 100.0 * (tau_m * lflen / mflen + tau_l) * f_s / (double)lflen; + + printf(" / %4d / %4d ", mflen, lflen); + printf("Predicted CPU load: %5.2f %%\n", cpu_load); + } + printf("\n"); + } + printf("\n"); + + // performance prediction with 1 segment lengths + for (l = 0; l < 9; l++) + { + lflen = 64 << l; + + num_l = ceil(hlen / (double)lflen); + tau_l = c0[l] + c1[l] * num_l; + + cpu_load = 100.0 * tau_l * f_s / (double)lflen; + + printf(" / / %4d ", lflen); + printf("Predicted CPU load: %5.2f %%\n", cpu_load); + } + printf("\n"); + + for (s = 0; s < 5; s++) + { + for (m = 1; m < 5; m++) + { + for (l = 1; l+m+s < 9; l++) + { + sflen = 64 << s; + mflen = sflen << m; + lflen = mflen << l; + printf("%4d / %4d / %4d ", sflen, mflen, lflen); + hcBenchmarkTripple(sflen, mflen, lflen); + } + } + printf("\n"); + } + + return 0; +} diff --git a/libHybridConv-0.1.1/libHybridConv.c b/libHybridConv-0.1.1/libHybridConv.c new file mode 100644 index 0000000..e3de596 --- /dev/null +++ b/libHybridConv-0.1.1/libHybridConv.c @@ -0,0 +1,829 @@ +/*************************************************************************** + * Copyright (C) 2009 by Christian Borss * + * christian.borss@rub.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + + +#include +#include +#include +#include +#ifdef WIN32 +#include +#else +#include +#endif +#include +#include +#include "libHybridConv.h" + + +double hcTime(void) +{ +#ifdef WIN32 + DWORD t; + + t = GetTickCount(); + return (double)t * 0.001; +#else + struct timeval tv; + + gettimeofday(&tv, NULL); + return tv.tv_sec + tv.tv_usec * 0.000001; +#endif +} + +//////////////////////////////////////////////////////////////// + +double getProcTime(int flen, int num, double dur) +{ + HConvSingle filter; + float *x; + float *h; + float *y; + int xlen, hlen, ylen; + int size, n; + int pos; + double t_start, t_diff; + double counter = 0.0; + double proc_time; + double lin, mul; + + xlen = 2048*2048; + size = sizeof(float) * xlen; + x = (float *)fftwf_malloc(size); + lin = pow(10.0, -100.0 / 20.0); // 0.00001 = -100dB + mul = pow(lin, 1.0 / (double)xlen); + x[0] = 1.0; + for (n = 1; n < xlen; n++) + x[n] = -mul * x[n - 1]; + + hlen = flen * num; + size = sizeof(float) * hlen; + h = (float *)fftwf_malloc(size); + lin = pow(10.0, -60.0 / 20.0); // 0.001 = -60dB + mul = pow(lin, 1.0 / (double)hlen); + h[0] = 1.0; + for (n = 1; n < hlen; n++) + h[n] = mul * h[n - 1]; + + ylen = flen; + size = sizeof(float) * ylen; + y = (float *)fftwf_malloc(size); + + hcInitSingle(&filter, h, hlen, flen, 1); + + t_diff = 0.0; + t_start = hcTime(); + pos = 0; + while (t_diff < dur) + { + hcPutSingle(&filter, &x[pos]); + hcProcessSingle(&filter); + hcGetSingle(&filter, y); + pos += flen; + if (pos >= xlen) + pos = 0; + counter += 1.0; + t_diff = hcTime() - t_start; + } + proc_time = t_diff / counter; + printf("Processing time: %7.3f us\n", 1000000.0 * proc_time); + + hcCloseSingle(&filter); + fftwf_free(x); + fftwf_free(h); + fftwf_free(y); + + return proc_time; +} + + + +void hcPutSingle(HConvSingle *filter, float *x) +{ + int j, flen, size; + + flen = filter->framelength; + size = sizeof(float) * flen; + memcpy(filter->dft_time, x, size); + memset(&(filter->dft_time[flen]), 0, size); + fftwf_execute(filter->fft); + for (j = 0; j < flen + 1; j++) + { + filter->in_freq_real[j] = filter->dft_freq[j][0]; + filter->in_freq_imag[j] = filter->dft_freq[j][1]; + } +} + + +void hcProcessSingle(HConvSingle *filter) +{ +#if 0 + int s, n, start, stop, flen; + float *x_real; + float *x_imag; + float *h_real; + float *h_imag; + float *y_real; + float *y_imag; + + flen = filter->framelength; + x_real = filter->in_freq_real; + x_imag = filter->in_freq_imag; + start = filter->steptask[filter->step]; + stop = filter->steptask[filter->step + 1]; + for (s = start; s < stop; s++) + { + n = (s + filter->mixpos) % filter->num_mixbuf; + y_real = filter->mixbuf_freq_real[n]; + y_imag = filter->mixbuf_freq_imag[n]; + h_real = filter->filterbuf_freq_real[s]; + h_imag = filter->filterbuf_freq_imag[s]; + for (n = 0; n < flen + 1; n++) + { + y_real[n] += x_real[n] * h_real[n] - + x_imag[n] * h_imag[n]; + y_imag[n] += x_real[n] * h_imag[n] + + x_imag[n] * h_real[n]; + } + } + filter->step = (filter->step + 1) % filter->maxstep; +#endif + + int s, n, start, stop, flen, flen4; + __m128 *x4_real; + __m128 *x4_imag; + __m128 *h4_real; + __m128 *h4_imag; + __m128 *y4_real; + __m128 *y4_imag; + float *x_real; + float *x_imag; + float *h_real; + float *h_imag; + float *y_real; + float *y_imag; + + flen = filter->framelength; + x_real = filter->in_freq_real; + x_imag = filter->in_freq_imag; + x4_real = (__m128*)x_real; + x4_imag = (__m128*)x_imag; + start = filter->steptask[filter->step]; + stop = filter->steptask[filter->step + 1]; + for (s = start; s < stop; s++) + { + n = (s + filter->mixpos) % filter->num_mixbuf; + y_real = filter->mixbuf_freq_real[n]; + y_imag = filter->mixbuf_freq_imag[n]; + y4_real = (__m128*)y_real; + y4_imag = (__m128*)y_imag; + h_real = filter->filterbuf_freq_real[s]; + h_imag = filter->filterbuf_freq_imag[s]; + h4_real = (__m128*)h_real; + h4_imag = (__m128*)h_imag; + flen4 = flen / 4; + for (n = 0; n < flen4; n++) + { +#ifdef WIN32 + __m128 a = _mm_mul_ps(x4_real[n], h4_real[n]); + __m128 b = _mm_mul_ps(x4_imag[n], h4_imag[n]); + __m128 c = _mm_sub_ps(a, b); + y4_real[n] = _mm_add_ps(y4_real[n], c); + a = _mm_mul_ps(x4_real[n], h4_imag[n]); + b = _mm_mul_ps(x4_imag[n], h4_real[n]); + c = _mm_add_ps(a, b); + y4_imag[n] = _mm_add_ps(y4_imag[n], c); +#else + y4_real[n] += x4_real[n] * h4_real[n] - + x4_imag[n] * h4_imag[n]; + y4_imag[n] += x4_real[n] * h4_imag[n] + + x4_imag[n] * h4_real[n]; +#endif + } + y_real[flen] += x_real[flen] * h_real[flen] - + x_imag[flen] * h_imag[flen]; + y_imag[flen] += x_real[flen] * h_imag[flen] + + x_imag[flen] * h_real[flen]; + } + filter->step = (filter->step + 1) % filter->maxstep; +} + + +void hcGetSingle(HConvSingle *filter, float *y) +{ + int flen, mpos; + float *out; + float *hist; + int size, n, j; + + flen = filter->framelength; + mpos = filter->mixpos; + out = filter->dft_time; + hist = filter->history_time; + for (j = 0; j < flen + 1; j++) + { + filter->dft_freq[j][0] = filter->mixbuf_freq_real[mpos][j]; + filter->dft_freq[j][1] = filter->mixbuf_freq_imag[mpos][j]; + filter->mixbuf_freq_real[mpos][j] = 0.0; + filter->mixbuf_freq_imag[mpos][j] = 0.0; + } + fftwf_execute(filter->ifft); + for (n = 0; n < flen; n++) + { + y[n] = out[n] + hist[n]; + } + size = sizeof(float) * flen; + memcpy(hist, &(out[flen]), size); + filter->mixpos = (filter->mixpos + 1) % filter->num_mixbuf; +} + + +void hcGetAddSingle(HConvSingle *filter, float *y) +{ + int flen, mpos; + float *out; + float *hist; + int size, n, j; + + flen = filter->framelength; + mpos = filter->mixpos; + out = filter->dft_time; + hist = filter->history_time; + for (j = 0; j < flen + 1; j++) + { + filter->dft_freq[j][0] = filter->mixbuf_freq_real[mpos][j]; + filter->dft_freq[j][1] = filter->mixbuf_freq_imag[mpos][j]; + filter->mixbuf_freq_real[mpos][j] = 0.0; + filter->mixbuf_freq_imag[mpos][j] = 0.0; + } + fftwf_execute(filter->ifft); + for (n = 0; n < flen; n++) + { + y[n] += out[n] + hist[n]; + } + size = sizeof(float) * flen; + memcpy(hist, &(out[flen]), size); + filter->mixpos = (filter->mixpos + 1) % filter->num_mixbuf; +} + + +void hcInitSingle(HConvSingle *filter, float *h, int hlen, int flen, int steps) +{ + int i, j, size, num, pos; + float gain; + + // processing step counter + filter->step = 0; + + // number of processing steps per audio frame + filter->maxstep = steps; + + // current frame index + filter->mixpos = 0; + + // number of samples per audio frame + filter->framelength = flen; + + // DFT buffer (time domain) + size = sizeof(float) * 2 * flen; + filter->dft_time = (float *)fftwf_malloc(size); + + // DFT buffer (frequency domain) + size = sizeof(fftwf_complex) * (flen + 1); + filter->dft_freq = (fftwf_complex*)fftwf_malloc(size); + + // input buffer (frequency domain) + size = sizeof(float) * (flen + 1); + filter->in_freq_real = (float*)fftwf_malloc(size); + filter->in_freq_imag = (float*)fftwf_malloc(size); + + // number of filter segments + filter->num_filterbuf = (hlen + flen - 1) / flen; + + // processing tasks per step + size = sizeof(int) * (steps + 1); + filter->steptask = (int *)malloc(size); + num = filter->num_filterbuf / steps; + for (i = 0; i <= steps; i++) + filter->steptask[i] = i * num; + if (filter->steptask[1] == 0) + pos = 1; + else + pos = 2; + num = filter->num_filterbuf % steps; + for (j = pos; j < pos + num; j++) + { + for (i = j; i <= steps; i++) + filter->steptask[i]++; + } + + // filter segments (frequency domain) + size = sizeof(float*) * filter->num_filterbuf; + filter->filterbuf_freq_real = (float**)fftwf_malloc(size); + filter->filterbuf_freq_imag = (float**)fftwf_malloc(size); + for (i = 0; i < filter->num_filterbuf; i++) + { + size = sizeof(float) * (flen + 1); + filter->filterbuf_freq_real[i] = (float*)fftwf_malloc(size); + filter->filterbuf_freq_imag[i] = (float*)fftwf_malloc(size); + } + + // number of mixing segments + filter->num_mixbuf = filter->num_filterbuf + 1; + + // mixing segments (frequency domain) + size = sizeof(float*) * filter->num_mixbuf; + filter->mixbuf_freq_real = (float**)fftwf_malloc(size); + filter->mixbuf_freq_imag = (float**)fftwf_malloc(size); + for (i = 0; i < filter->num_mixbuf; i++) + { + size = sizeof(float) * (flen + 1); + filter->mixbuf_freq_real[i] = (float*)fftwf_malloc(size); + filter->mixbuf_freq_imag[i] = (float*)fftwf_malloc(size); + memset(filter->mixbuf_freq_real[i], 0, size); + memset(filter->mixbuf_freq_imag[i], 0, size); + } + + // history buffer (time domain) + size = sizeof(float) * flen; + filter->history_time = (float *)fftwf_malloc(size); + memset(filter->history_time, 0, size); + + // FFT transformation plan + filter->fft = fftwf_plan_dft_r2c_1d(2 * flen, filter->dft_time, filter->dft_freq, FFTW_ESTIMATE|FFTW_PRESERVE_INPUT); + + // IFFT transformation plan + filter->ifft = fftwf_plan_dft_c2r_1d(2 * flen, filter->dft_freq, filter->dft_time, FFTW_ESTIMATE|FFTW_PRESERVE_INPUT); + + // generate filter segments + gain = 0.5f / flen; + size = sizeof(float) * 2 * flen; + memset(filter->dft_time, 0, size); + for (i = 0; i < filter->num_filterbuf - 1; i++) + { + for (j = 0; j < flen; j++) + filter->dft_time[j] = gain * h[i * flen + j]; + fftwf_execute(filter->fft); + for (j = 0; j < flen + 1; j++) + { + filter->filterbuf_freq_real[i][j] = filter->dft_freq[j][0]; + filter->filterbuf_freq_imag[i][j] = filter->dft_freq[j][1]; + } + } + for (j = 0; j < hlen - i * flen; j++) + filter->dft_time[j] = gain * h[i * flen + j]; + size = sizeof(float) * ((i + 1) * flen - hlen); + memset(&(filter->dft_time[hlen - i * flen]), 0, size); + fftwf_execute(filter->fft); + for (j = 0; j < flen + 1; j++) + { + filter->filterbuf_freq_real[i][j] = filter->dft_freq[j][0]; + filter->filterbuf_freq_imag[i][j] = filter->dft_freq[j][1]; + } +} + + +void hcCloseSingle(HConvSingle *filter) +{ + int i; + + fftwf_destroy_plan(filter->ifft); + fftwf_destroy_plan(filter->fft); + fftwf_free(filter->history_time); + for (i = 0; i < filter->num_mixbuf; i++) + { + fftwf_free(filter->mixbuf_freq_real[i]); + fftwf_free(filter->mixbuf_freq_imag[i]); + } + fftwf_free(filter->mixbuf_freq_real); + fftwf_free(filter->mixbuf_freq_imag); + for (i = 0; i < filter->num_filterbuf; i++) + { + fftwf_free(filter->filterbuf_freq_real[i]); + fftwf_free(filter->filterbuf_freq_imag[i]); + } + fftwf_free(filter->filterbuf_freq_real); + fftwf_free(filter->filterbuf_freq_imag); + fftwf_free(filter->in_freq_real); + fftwf_free(filter->in_freq_imag); + fftwf_free(filter->dft_freq); + fftwf_free(filter->dft_time); + free(filter->steptask); + memset(filter, 0, sizeof(HConvSingle)); +} + + +//////////////////////////////////////////////////////////////// + + +void hcBenchmarkDual(int sflen, int lflen) +{ + HConvDual filter; + float *x; + float *h; + float *y; + int xlen, hlen, ylen; + int size, n; + int pos; + double t_start, t_diff; + double counter = 0.0; + double signal_time; + double cpu_load; + double lin, mul; + +// xlen = sflen; + xlen = 2048*2048; + size = sizeof(float) * xlen; + x = (float *)fftwf_malloc(size); + lin = pow(10.0, -100.0 / 20.0); // 0.00001 = -100dB + mul = pow(lin, 1.0 / (double)xlen); + x[0] = 1.0; + for (n = 1; n < xlen; n++) + x[n] = -mul * x[n - 1]; + + hlen = 96000; + size = sizeof(float) * hlen; + h = (float *)fftwf_malloc(size); + lin = pow(10.0, -60.0 / 20.0); // 0.001 = -60dB + mul = pow(lin, 1.0 / (double)hlen); + h[0] = 1.0; + for (n = 1; n < hlen; n++) + h[n] = mul * h[n - 1]; + + ylen = sflen; + size = sizeof(float) * ylen; + y = (float *)fftwf_malloc(size); + + hcInitDual(&filter, h, hlen, sflen, lflen); + + t_diff = 0.0; + t_start = hcTime(); + pos = 0; + while (t_diff < 10.0) + { + hcProcessDual(&filter, &(x[pos]), y); + pos += sflen; + if (pos >= xlen) + pos = 0; + counter += 1.0; + t_diff = hcTime() - t_start; + } + signal_time = counter * sflen / 48000.0; + cpu_load = 100.0 * t_diff / signal_time; + printf("Estimated CPU load: %5.2f %%\n", cpu_load); + + hcCloseDual(&filter); + fftwf_free(x); + fftwf_free(h); + fftwf_free(y); +} + + +void hcProcessDual(HConvDual *filter, float *in, float *out) +{ + int lpos, size, i; + + // convolution with short segments + hcPutSingle(filter->f_short, in); + hcProcessSingle(filter->f_short); + hcGetSingle(filter->f_short, out); + + // add contribution from last long frame + lpos = filter->step * filter->flen_short; + for (i = 0; i < filter->flen_short; i++) + out[i] += filter->out_long[lpos + i]; + + // convolution with long segments + if (filter->step == 0) + hcPutSingle(filter->f_long, filter->in_long); + hcProcessSingle(filter->f_long); + if (filter->step == filter->maxstep - 1) + hcGetSingle(filter->f_long, filter->out_long); + + // add current frame to long input buffer + lpos = filter->step * filter->flen_short; + size = sizeof(float) * filter->flen_short; + memcpy(&(filter->in_long[lpos]), in, size); + + // increase step counter + filter->step = (filter->step + 1) % filter->maxstep; +} + + +void hcProcessAddDual(HConvDual *filter, float *in, float *out) +{ + int lpos, size, i; + + // convolution with short segments + hcPutSingle(filter->f_short, in); + hcProcessSingle(filter->f_short); + hcGetAddSingle(filter->f_short, out); + + // add contribution from last long frame + lpos = filter->step * filter->flen_short; + for (i = 0; i < filter->flen_short; i++) + out[i] += filter->out_long[lpos + i]; + + // convolution with long segments + if (filter->step == 0) + hcPutSingle(filter->f_long, filter->in_long); + hcProcessSingle(filter->f_long); + if (filter->step == filter->maxstep - 1) + hcGetSingle(filter->f_long, filter->out_long); + + // add current frame to long input buffer + lpos = filter->step * filter->flen_short; + size = sizeof(float) * filter->flen_short; + memcpy(&(filter->in_long[lpos]), in, size); + + // increase step counter + filter->step = (filter->step + 1) % filter->maxstep; +} + + +void hcInitDual(HConvDual *filter, float *h, int hlen, int sflen, int lflen) +{ + int size; + float *h2 = NULL; + int h2len; + + // sanity check: minimum impulse response length + h2len = 2 * lflen + 1; + if (hlen < h2len) + { + size = sizeof(float) * h2len; + h2 = (float*)fftwf_malloc(size); + memset(h2, 0, size); + size = sizeof(float) * hlen; + memcpy(h2, h, size); + h = h2; + hlen = h2len; + } + + // processing step counter + filter->step = 0; + + // number of processing steps per long audio frame + filter->maxstep = lflen / sflen; + + // number of samples per long audio frame + filter->flen_long = lflen; + + // number of samples per short audio frame + filter->flen_short = sflen; + + // input buffer (long frame) + size = sizeof(float) * lflen; + filter->in_long = (float *)fftwf_malloc(size); + memset(filter->in_long, 0, size); + + // output buffer (long frame) + size = sizeof(float) * lflen; + filter->out_long = (float *)fftwf_malloc(size); + memset(filter->out_long, 0, size); + + // convolution filter (short segments) + size = sizeof(HConvSingle); + filter->f_short = (HConvSingle *)malloc(size); + hcInitSingle(filter->f_short, h, 2 * lflen, sflen, 1); + + // convolution filter (long segments) + size = sizeof(HConvSingle); + filter->f_long = (HConvSingle *)malloc(size); + hcInitSingle(filter->f_long, &(h[2 * lflen]), hlen - 2 * lflen, lflen, lflen / sflen); + + if (h2 != NULL) + { + fftwf_free(h2); + } +} + + +void hcCloseDual(HConvDual *filter) +{ + hcCloseSingle(filter->f_short); + free(filter->f_short); + hcCloseSingle(filter->f_long); + free(filter->f_long); + fftwf_free(filter->out_long); + fftwf_free(filter->in_long); + memset(filter, 0, sizeof(HConvDual)); +} + + +//////////////////////////////////////////////////////////////// + + +void hcBenchmarkTripple(int sflen, int mflen, int lflen) +{ + HConvTripple filter; + float *x; + float *h; + float *y; + int xlen, hlen, ylen; + int size, n; + int pos; + double t_start, t_diff; + double counter = 0.0; + double signal_time; + double cpu_load; + double lin, mul; + +// xlen = sflen; + xlen = 2048*2048; + size = sizeof(float) * xlen; + x = (float *)fftwf_malloc(size); + lin = pow(10.0, -100.0 / 20.0); // 0.00001 = -100dB + mul = pow(lin, 1.0 / (double)xlen); + x[0] = 1.0; + for (n = 1; n < xlen; n++) + x[n] = -mul * x[n - 1]; + + hlen = 96000; + size = sizeof(float) * hlen; + h = (float *)fftwf_malloc(size); + lin = pow(10.0, -60.0 / 20.0); // 0.001 = -60dB + mul = pow(lin, 1.0 / (double)hlen); + h[0] = 1.0; + for (n = 1; n < hlen; n++) + h[n] = mul * h[n - 1]; + + ylen = sflen; + size = sizeof(float) * ylen; + y = (float *)fftwf_malloc(size); + + hcInitTripple(&filter, h, hlen, sflen, mflen, lflen); + + t_diff = 0.0; + t_start = hcTime(); + size = mflen / sflen; + pos = 0; + while (t_diff < 10.0) + { + for (n = 0; n < size; n++) + { +// hcProcessTripple(&filter, x, y); + hcProcessTripple(&filter, &(x[pos]), y); + pos += sflen; + if (pos >= xlen) + pos = 0; + } + counter += 1.0; + t_diff = hcTime() - t_start; + } + signal_time = counter * (double)mflen / 48000.0; + cpu_load = 100.0 * t_diff / signal_time; + printf("Estimated CPU load: %5.2f %%\n", cpu_load); + + hcCloseTripple(&filter); + fftwf_free(x); + fftwf_free(h); + fftwf_free(y); +} + + +void hcProcessTripple(HConvTripple *filter, float *in, float *out) +{ + int lpos, size, i; + + // convolution with short segments + hcPutSingle(filter->f_short, in); + hcProcessSingle(filter->f_short); + hcGetSingle(filter->f_short, out); + + // add contribution from last medium frame + lpos = filter->step * filter->flen_short; + for (i = 0; i < filter->flen_short; i++) + out[i] += filter->out_medium[lpos + i]; + + // add current frame to medium input buffer + lpos = filter->step * filter->flen_short; + size = sizeof(float) * filter->flen_short; + memcpy(&(filter->in_medium[lpos]), in, size); + + // convolution with medium segments + if (filter->step == filter->maxstep - 1) + hcProcessDual(filter->f_medium, + filter->in_medium, + filter->out_medium); + + // increase step counter + filter->step = (filter->step + 1) % filter->maxstep; +} + + +void hcProcessAddTripple(HConvTripple *filter, float *in, float *out) +{ + int lpos, size, i; + + // convolution with short segments + hcPutSingle(filter->f_short, in); + hcProcessSingle(filter->f_short); + hcGetAddSingle(filter->f_short, out); + + // add contribution from last medium frame + lpos = filter->step * filter->flen_short; + for (i = 0; i < filter->flen_short; i++) + out[i] += filter->out_medium[lpos + i]; + + // add current frame to medium input buffer + lpos = filter->step * filter->flen_short; + size = sizeof(float) * filter->flen_short; + memcpy(&(filter->in_medium[lpos]), in, size); + + // convolution with medium segments + if (filter->step == filter->maxstep - 1) + hcProcessDual(filter->f_medium, + filter->in_medium, + filter->out_medium); + + // increase step counter + filter->step = (filter->step + 1) % filter->maxstep; +} + + +void hcInitTripple(HConvTripple *filter, float *h, int hlen, int sflen, int mflen, int lflen) +{ + int size; + float *h2 = NULL; + int h2len; + + // sanity check: minimum impulse response length + h2len = mflen + 2 * lflen + 1; + if (hlen < h2len) + { + size = sizeof(float) * h2len; + h2 = (float*)fftwf_malloc(size); + memset(h2, 0, size); + size = sizeof(float) * hlen; + memcpy(h2, h, size); + h = h2; + hlen = h2len; + } + + // processing step counter + filter->step = 0; + + // number of processing steps per medium audio frame + filter->maxstep = mflen / sflen; + + // number of samples per medium audio frame + filter->flen_medium = mflen; + + // number of samples per short audio frame + filter->flen_short = sflen; + + // input buffer (medium frame) + size = sizeof(float) * mflen; + filter->in_medium = (float *)fftwf_malloc(size); + memset(filter->in_medium, 0, size); + + // output buffer (medium frame) + size = sizeof(float) * mflen; + filter->out_medium = (float *)fftwf_malloc(size); + memset(filter->out_medium, 0, size); + + // convolution filter (short segments) + size = sizeof(HConvSingle); + filter->f_short = (HConvSingle *)malloc(size); + hcInitSingle(filter->f_short, h, mflen, sflen, 1); + + // convolution filter (medium segments) + size = sizeof(HConvDual); + filter->f_medium = (HConvDual *)malloc(size); + hcInitDual(filter->f_medium, &(h[mflen]), hlen - mflen, mflen, lflen); + + if (h2 != NULL) + { + fftwf_free(h2); + } +} + + +void hcCloseTripple(HConvTripple *filter) +{ + hcCloseSingle(filter->f_short); + free(filter->f_short); + hcCloseDual(filter->f_medium); + free(filter->f_medium); + fftwf_free(filter->out_medium); + fftwf_free(filter->in_medium); + memset(filter, 0, sizeof(HConvTripple)); +} diff --git a/libHybridConv-0.1.1/libHybridConv.h b/libHybridConv-0.1.1/libHybridConv.h new file mode 100644 index 0000000..a39246b --- /dev/null +++ b/libHybridConv-0.1.1/libHybridConv.h @@ -0,0 +1,102 @@ +/*************************************************************************** + * Copyright (C) 2009 by Christian Borss * + * christian.borss@rub.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + + +#ifndef __LIBHYBRIDCONV_H__ +#define __LIBHYBRIDCONV_H__ + + +#include + + +typedef struct str_HConvSingle +{ + int step; // processing step counter + int maxstep; // number of processing steps per audio frame + int mixpos; // current frame index + int framelength; // number of samples per audio frame + int *steptask; // processing tasks per step + float *dft_time; // DFT buffer (time domain) + fftwf_complex *dft_freq; // DFT buffer (frequency domain) + float *in_freq_real; // input buffer (frequency domain) + float *in_freq_imag; // input buffer (frequency domain) + int num_filterbuf; // number of filter segments + float **filterbuf_freq_real; // filter segments (frequency domain) + float **filterbuf_freq_imag; // filter segments (frequency domain) + int num_mixbuf; // number of mixing segments + float **mixbuf_freq_real; // mixing segments (frequency domain) + float **mixbuf_freq_imag; // mixing segments (frequency domain) + float *history_time; // history buffer (time domain) + fftwf_plan fft; // FFT transformation plan + fftwf_plan ifft; // IFFT transformation plan +} HConvSingle; + + +typedef struct str_HConvDual +{ + int step; // processing step counter + int maxstep; // number of processing steps per long audio frame + int flen_long; // number of samples per long audio frame + int flen_short; // number of samples per short audio frame + float *in_long; // input buffer (long frame) + float *out_long; // output buffer (long frame) + HConvSingle *f_long; // convolution filter (long segments) + HConvSingle *f_short; // convolution filter (short segments) +} HConvDual; + + +typedef struct str_HConvTripple +{ + int step; // processing step counter + int maxstep; // number of processing steps per long audio frame + int flen_medium; // number of samples per long audio frame + int flen_short; // number of samples per short audio frame + float *in_medium; // input buffer (long frame) + float *out_medium; // output buffer (long frame) + HConvDual *f_medium; // convolution filter (long segments) + HConvSingle *f_short; // convolution filter (short segments) +} HConvTripple; + + +/* single filter functions */ +double getProcTime(int flen, int num, double dur); +void hcPutSingle(HConvSingle *filter, float *x); +void hcProcessSingle(HConvSingle *filter); +void hcGetSingle(HConvSingle *filter, float *y); +void hcGetAddSingle(HConvSingle *filter, float *y); +void hcInitSingle(HConvSingle *filter, float *h, int hlen, int flen, int steps); +void hcCloseSingle(HConvSingle *filter); + +/* dual filter functions */ +void hcBenchmarkDual(int sflen, int lflen); +void hcProcessDual(HConvDual *filter, float *in, float *out); +void hcProcessAddDual(HConvDual *filter, float *in, float *out); +void hcInitDual(HConvDual *filter, float *h, int hlen, int sflen, int lflen); +void hcCloseDual(HConvDual *filter); + +/* tripple filter functions */ +void hcBenchmarkTripple(int sflen, int mflen, int lflen); +void hcProcessTripple(HConvTripple *filter, float *in, float *out); +void hcProcessAddTripple(HConvTripple *filter, float *in, float *out); +void hcInitTripple(HConvTripple *filter, float *h, int hlen, int sflen, int mflen, int lflen); +void hcCloseTripple(HConvTripple *filter); + + +#endif // __LIBHYBRIDCONV_H__ diff --git a/libHybridConv-0.1.1/libHybridConv_eapo.cpp b/libHybridConv-0.1.1/libHybridConv_eapo.cpp new file mode 100644 index 0000000..fda7320 --- /dev/null +++ b/libHybridConv-0.1.1/libHybridConv_eapo.cpp @@ -0,0 +1,830 @@ +/*************************************************************************** + * Copyright (C) 2009 by Christian Borss * + * christian.borss@rub.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as * + * published by the Free Software Foundation; either version 2 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +// Adapted version for Equalizer APO. For original version see libHybridConv.c + +#include "stdafx.h" +#include +#include +#include +#include +#ifdef WIN32 +#include +#else +#include +#endif +#include +#include +#include "libHybridConv.h" + + +double hcTime(void) +{ +#ifdef WIN32 + DWORD t; + + t = GetTickCount(); + return (double)t * 0.001; +#else + struct timeval tv; + + gettimeofday(&tv, NULL); + return tv.tv_sec + tv.tv_usec * 0.000001; +#endif +} + +//////////////////////////////////////////////////////////////// + +double getProcTime(int flen, int num, double dur) +{ + HConvSingle filter; + float *x; + float *h; + float *y; + int xlen, hlen, ylen; + int size, n; + int pos; + double t_start, t_diff; + double counter = 0.0; + double proc_time; + double lin, mul; + + xlen = 2048*2048; + size = sizeof(float) * xlen; + x = (float *)fftwf_malloc(size); + lin = pow(10.0, -100.0 / 20.0); // 0.00001 = -100dB + mul = pow(lin, 1.0 / (double)xlen); + x[0] = 1.0; + for (n = 1; n < xlen; n++) + x[n] = float(-mul * x[n - 1]); + + hlen = flen * num; + size = sizeof(float) * hlen; + h = (float *)fftwf_malloc(size); + lin = pow(10.0, -60.0 / 20.0); // 0.001 = -60dB + mul = pow(lin, 1.0 / (double)hlen); + h[0] = 1.0; + for (n = 1; n < hlen; n++) + h[n] = float(mul * h[n - 1]); + + ylen = flen; + size = sizeof(float) * ylen; + y = (float *)fftwf_malloc(size); + + hcInitSingle(&filter, h, hlen, flen, 1); + + t_diff = 0.0; + t_start = hcTime(); + pos = 0; + while (t_diff < dur) + { + hcPutSingle(&filter, &x[pos]); + hcProcessSingle(&filter); + hcGetSingle(&filter, y); + pos += flen; + if (pos >= xlen) + pos = 0; + counter += 1.0; + t_diff = hcTime() - t_start; + } + proc_time = t_diff / counter; + printf("Processing time: %7.3f us\n", 1000000.0 * proc_time); + + hcCloseSingle(&filter); + fftwf_free(x); + fftwf_free(h); + fftwf_free(y); + + return proc_time; +} + + + +void hcPutSingle(HConvSingle *filter, float *x) +{ + int j, flen, size; + + flen = filter->framelength; + size = sizeof(float) * flen; + memcpy(filter->dft_time, x, size); + memset(&(filter->dft_time[flen]), 0, size); + fftwf_execute(filter->fft); + for (j = 0; j < flen + 1; j++) + { + filter->in_freq_real[j] = filter->dft_freq[j][0]; + filter->in_freq_imag[j] = filter->dft_freq[j][1]; + } +} + + +void hcProcessSingle(HConvSingle *filter) +{ +#if 0 + int s, n, start, stop, flen; + float *x_real; + float *x_imag; + float *h_real; + float *h_imag; + float *y_real; + float *y_imag; + + flen = filter->framelength; + x_real = filter->in_freq_real; + x_imag = filter->in_freq_imag; + start = filter->steptask[filter->step]; + stop = filter->steptask[filter->step + 1]; + for (s = start; s < stop; s++) + { + n = (s + filter->mixpos) % filter->num_mixbuf; + y_real = filter->mixbuf_freq_real[n]; + y_imag = filter->mixbuf_freq_imag[n]; + h_real = filter->filterbuf_freq_real[s]; + h_imag = filter->filterbuf_freq_imag[s]; + for (n = 0; n < flen + 1; n++) + { + y_real[n] += x_real[n] * h_real[n] - + x_imag[n] * h_imag[n]; + y_imag[n] += x_real[n] * h_imag[n] + + x_imag[n] * h_real[n]; + } + } + filter->step = (filter->step + 1) % filter->maxstep; +#endif + + int s, n, start, stop, flen, flen4; + __m128 *x4_real; + __m128 *x4_imag; + __m128 *h4_real; + __m128 *h4_imag; + __m128 *y4_real; + __m128 *y4_imag; + float *x_real; + float *x_imag; + float *h_real; + float *h_imag; + float *y_real; + float *y_imag; + + flen = filter->framelength; + x_real = filter->in_freq_real; + x_imag = filter->in_freq_imag; + x4_real = (__m128*)x_real; + x4_imag = (__m128*)x_imag; + start = filter->steptask[filter->step]; + stop = filter->steptask[filter->step + 1]; + for (s = start; s < stop; s++) + { + n = (s + filter->mixpos) % filter->num_mixbuf; + y_real = filter->mixbuf_freq_real[n]; + y_imag = filter->mixbuf_freq_imag[n]; + y4_real = (__m128*)y_real; + y4_imag = (__m128*)y_imag; + h_real = filter->filterbuf_freq_real[s]; + h_imag = filter->filterbuf_freq_imag[s]; + h4_real = (__m128*)h_real; + h4_imag = (__m128*)h_imag; + flen4 = flen / 4; + for (n = 0; n < flen4; n++) + { +#ifdef WIN32 + __m128 a = _mm_mul_ps(x4_real[n], h4_real[n]); + __m128 b = _mm_mul_ps(x4_imag[n], h4_imag[n]); + __m128 c = _mm_sub_ps(a, b); + y4_real[n] = _mm_add_ps(y4_real[n], c); + a = _mm_mul_ps(x4_real[n], h4_imag[n]); + b = _mm_mul_ps(x4_imag[n], h4_real[n]); + c = _mm_add_ps(a, b); + y4_imag[n] = _mm_add_ps(y4_imag[n], c); +#else + y4_real[n] += x4_real[n] * h4_real[n] - + x4_imag[n] * h4_imag[n]; + y4_imag[n] += x4_real[n] * h4_imag[n] + + x4_imag[n] * h4_real[n]; +#endif + } + y_real[flen] += x_real[flen] * h_real[flen] - + x_imag[flen] * h_imag[flen]; + y_imag[flen] += x_real[flen] * h_imag[flen] + + x_imag[flen] * h_real[flen]; + } + filter->step = (filter->step + 1) % filter->maxstep; +} + + +void hcGetSingle(HConvSingle *filter, float *y) +{ + int flen, mpos; + float *out; + float *hist; + int size, n, j; + + flen = filter->framelength; + mpos = filter->mixpos; + out = filter->dft_time; + hist = filter->history_time; + for (j = 0; j < flen + 1; j++) + { + filter->dft_freq[j][0] = filter->mixbuf_freq_real[mpos][j]; + filter->dft_freq[j][1] = filter->mixbuf_freq_imag[mpos][j]; + filter->mixbuf_freq_real[mpos][j] = 0.0; + filter->mixbuf_freq_imag[mpos][j] = 0.0; + } + fftwf_execute(filter->ifft); + for (n = 0; n < flen; n++) + { + y[n] = out[n] + hist[n]; + } + size = sizeof(float) * flen; + memcpy(hist, &(out[flen]), size); + filter->mixpos = (filter->mixpos + 1) % filter->num_mixbuf; +} + + +void hcGetAddSingle(HConvSingle *filter, float *y) +{ + int flen, mpos; + float *out; + float *hist; + int size, n, j; + + flen = filter->framelength; + mpos = filter->mixpos; + out = filter->dft_time; + hist = filter->history_time; + for (j = 0; j < flen + 1; j++) + { + filter->dft_freq[j][0] = filter->mixbuf_freq_real[mpos][j]; + filter->dft_freq[j][1] = filter->mixbuf_freq_imag[mpos][j]; + filter->mixbuf_freq_real[mpos][j] = 0.0; + filter->mixbuf_freq_imag[mpos][j] = 0.0; + } + fftwf_execute(filter->ifft); + for (n = 0; n < flen; n++) + { + y[n] += out[n] + hist[n]; + } + size = sizeof(float) * flen; + memcpy(hist, &(out[flen]), size); + filter->mixpos = (filter->mixpos + 1) % filter->num_mixbuf; +} + + +void hcInitSingle(HConvSingle *filter, float *h, int hlen, int flen, int steps) +{ + int i, j, size, num, pos; + float gain; + + // processing step counter + filter->step = 0; + + // number of processing steps per audio frame + filter->maxstep = steps; + + // current frame index + filter->mixpos = 0; + + // number of samples per audio frame + filter->framelength = flen; + + // DFT buffer (time domain) + size = sizeof(float) * 2 * flen; + filter->dft_time = (float *)fftwf_malloc(size); + + // DFT buffer (frequency domain) + size = sizeof(fftwf_complex) * (flen + 1); + filter->dft_freq = (fftwf_complex*)fftwf_malloc(size); + + // input buffer (frequency domain) + size = sizeof(float) * (flen + 1); + filter->in_freq_real = (float*)fftwf_malloc(size); + filter->in_freq_imag = (float*)fftwf_malloc(size); + + // number of filter segments + filter->num_filterbuf = (hlen + flen - 1) / flen; + + // processing tasks per step + size = sizeof(int) * (steps + 1); + filter->steptask = (int *)malloc(size); + num = filter->num_filterbuf / steps; + for (i = 0; i <= steps; i++) + filter->steptask[i] = i * num; + if (filter->steptask[1] == 0) + pos = 1; + else + pos = 2; + num = filter->num_filterbuf % steps; + for (j = pos; j < pos + num; j++) + { + for (i = j; i <= steps; i++) + filter->steptask[i]++; + } + + // filter segments (frequency domain) + size = sizeof(float*) * filter->num_filterbuf; + filter->filterbuf_freq_real = (float**)fftwf_malloc(size); + filter->filterbuf_freq_imag = (float**)fftwf_malloc(size); + for (i = 0; i < filter->num_filterbuf; i++) + { + size = sizeof(float) * (flen + 1); + filter->filterbuf_freq_real[i] = (float*)fftwf_malloc(size); + filter->filterbuf_freq_imag[i] = (float*)fftwf_malloc(size); + } + + // number of mixing segments + filter->num_mixbuf = filter->num_filterbuf + 1; + + // mixing segments (frequency domain) + size = sizeof(float*) * filter->num_mixbuf; + filter->mixbuf_freq_real = (float**)fftwf_malloc(size); + filter->mixbuf_freq_imag = (float**)fftwf_malloc(size); + for (i = 0; i < filter->num_mixbuf; i++) + { + size = sizeof(float) * (flen + 1); + filter->mixbuf_freq_real[i] = (float*)fftwf_malloc(size); + filter->mixbuf_freq_imag[i] = (float*)fftwf_malloc(size); + memset(filter->mixbuf_freq_real[i], 0, size); + memset(filter->mixbuf_freq_imag[i], 0, size); + } + + // history buffer (time domain) + size = sizeof(float) * flen; + filter->history_time = (float *)fftwf_malloc(size); + memset(filter->history_time, 0, size); + + // FFT transformation plan + filter->fft = fftwf_plan_dft_r2c_1d(2 * flen, filter->dft_time, filter->dft_freq, FFTW_ESTIMATE|FFTW_PRESERVE_INPUT); + + // IFFT transformation plan + filter->ifft = fftwf_plan_dft_c2r_1d(2 * flen, filter->dft_freq, filter->dft_time, FFTW_ESTIMATE|FFTW_PRESERVE_INPUT); + + // generate filter segments + gain = 0.5f / flen; + size = sizeof(float) * 2 * flen; + memset(filter->dft_time, 0, size); + for (i = 0; i < filter->num_filterbuf - 1; i++) + { + for (j = 0; j < flen; j++) + filter->dft_time[j] = gain * h[i * flen + j]; + fftwf_execute(filter->fft); + for (j = 0; j < flen + 1; j++) + { + filter->filterbuf_freq_real[i][j] = filter->dft_freq[j][0]; + filter->filterbuf_freq_imag[i][j] = filter->dft_freq[j][1]; + } + } + for (j = 0; j < hlen - i * flen; j++) + filter->dft_time[j] = gain * h[i * flen + j]; + size = sizeof(float) * ((i + 1) * flen - hlen); + memset(&(filter->dft_time[hlen - i * flen]), 0, size); + fftwf_execute(filter->fft); + for (j = 0; j < flen + 1; j++) + { + filter->filterbuf_freq_real[i][j] = filter->dft_freq[j][0]; + filter->filterbuf_freq_imag[i][j] = filter->dft_freq[j][1]; + } +} + + +void hcCloseSingle(HConvSingle *filter) +{ + int i; + + fftwf_destroy_plan(filter->ifft); + fftwf_destroy_plan(filter->fft); + fftwf_free(filter->history_time); + for (i = 0; i < filter->num_mixbuf; i++) + { + fftwf_free(filter->mixbuf_freq_real[i]); + fftwf_free(filter->mixbuf_freq_imag[i]); + } + fftwf_free(filter->mixbuf_freq_real); + fftwf_free(filter->mixbuf_freq_imag); + for (i = 0; i < filter->num_filterbuf; i++) + { + fftwf_free(filter->filterbuf_freq_real[i]); + fftwf_free(filter->filterbuf_freq_imag[i]); + } + fftwf_free(filter->filterbuf_freq_real); + fftwf_free(filter->filterbuf_freq_imag); + fftwf_free(filter->in_freq_real); + fftwf_free(filter->in_freq_imag); + fftwf_free(filter->dft_freq); + fftwf_free(filter->dft_time); + free(filter->steptask); + memset(filter, 0, sizeof(HConvSingle)); +} + + +//////////////////////////////////////////////////////////////// + + +void hcBenchmarkDual(int sflen, int lflen) +{ + HConvDual filter; + float *x; + float *h; + float *y; + int xlen, hlen, ylen; + int size, n; + int pos; + double t_start, t_diff; + double counter = 0.0; + double signal_time; + double cpu_load; + double lin, mul; + +// xlen = sflen; + xlen = 2048*2048; + size = sizeof(float) * xlen; + x = (float *)fftwf_malloc(size); + lin = pow(10.0, -100.0 / 20.0); // 0.00001 = -100dB + mul = pow(lin, 1.0 / (double)xlen); + x[0] = 1.0; + for (n = 1; n < xlen; n++) + x[n] = float(-mul * x[n - 1]); + + hlen = 96000; + size = sizeof(float) * hlen; + h = (float *)fftwf_malloc(size); + lin = pow(10.0, -60.0 / 20.0); // 0.001 = -60dB + mul = pow(lin, 1.0 / (double)hlen); + h[0] = 1.0; + for (n = 1; n < hlen; n++) + h[n] = float(mul * h[n - 1]); + + ylen = sflen; + size = sizeof(float) * ylen; + y = (float *)fftwf_malloc(size); + + hcInitDual(&filter, h, hlen, sflen, lflen); + + t_diff = 0.0; + t_start = hcTime(); + pos = 0; + while (t_diff < 10.0) + { + hcProcessDual(&filter, &(x[pos]), y); + pos += sflen; + if (pos >= xlen) + pos = 0; + counter += 1.0; + t_diff = hcTime() - t_start; + } + signal_time = counter * sflen / 48000.0; + cpu_load = 100.0 * t_diff / signal_time; + printf("Estimated CPU load: %5.2f %%\n", cpu_load); + + hcCloseDual(&filter); + fftwf_free(x); + fftwf_free(h); + fftwf_free(y); +} + + +void hcProcessDual(HConvDual *filter, float *in, float *out) +{ + int lpos, size, i; + + // convolution with short segments + hcPutSingle(filter->f_short, in); + hcProcessSingle(filter->f_short); + hcGetSingle(filter->f_short, out); + + // add contribution from last long frame + lpos = filter->step * filter->flen_short; + for (i = 0; i < filter->flen_short; i++) + out[i] += filter->out_long[lpos + i]; + + // convolution with long segments + if (filter->step == 0) + hcPutSingle(filter->f_long, filter->in_long); + hcProcessSingle(filter->f_long); + if (filter->step == filter->maxstep - 1) + hcGetSingle(filter->f_long, filter->out_long); + + // add current frame to long input buffer + lpos = filter->step * filter->flen_short; + size = sizeof(float) * filter->flen_short; + memcpy(&(filter->in_long[lpos]), in, size); + + // increase step counter + filter->step = (filter->step + 1) % filter->maxstep; +} + + +void hcProcessAddDual(HConvDual *filter, float *in, float *out) +{ + int lpos, size, i; + + // convolution with short segments + hcPutSingle(filter->f_short, in); + hcProcessSingle(filter->f_short); + hcGetAddSingle(filter->f_short, out); + + // add contribution from last long frame + lpos = filter->step * filter->flen_short; + for (i = 0; i < filter->flen_short; i++) + out[i] += filter->out_long[lpos + i]; + + // convolution with long segments + if (filter->step == 0) + hcPutSingle(filter->f_long, filter->in_long); + hcProcessSingle(filter->f_long); + if (filter->step == filter->maxstep - 1) + hcGetSingle(filter->f_long, filter->out_long); + + // add current frame to long input buffer + lpos = filter->step * filter->flen_short; + size = sizeof(float) * filter->flen_short; + memcpy(&(filter->in_long[lpos]), in, size); + + // increase step counter + filter->step = (filter->step + 1) % filter->maxstep; +} + + +void hcInitDual(HConvDual *filter, float *h, int hlen, int sflen, int lflen) +{ + int size; + float *h2 = NULL; + int h2len; + + // sanity check: minimum impulse response length + h2len = 2 * lflen + 1; + if (hlen < h2len) + { + size = sizeof(float) * h2len; + h2 = (float*)fftwf_malloc(size); + memset(h2, 0, size); + size = sizeof(float) * hlen; + memcpy(h2, h, size); + h = h2; + hlen = h2len; + } + + // processing step counter + filter->step = 0; + + // number of processing steps per long audio frame + filter->maxstep = lflen / sflen; + + // number of samples per long audio frame + filter->flen_long = lflen; + + // number of samples per short audio frame + filter->flen_short = sflen; + + // input buffer (long frame) + size = sizeof(float) * lflen; + filter->in_long = (float *)fftwf_malloc(size); + memset(filter->in_long, 0, size); + + // output buffer (long frame) + size = sizeof(float) * lflen; + filter->out_long = (float *)fftwf_malloc(size); + memset(filter->out_long, 0, size); + + // convolution filter (short segments) + size = sizeof(HConvSingle); + filter->f_short = (HConvSingle *)malloc(size); + hcInitSingle(filter->f_short, h, 2 * lflen, sflen, 1); + + // convolution filter (long segments) + size = sizeof(HConvSingle); + filter->f_long = (HConvSingle *)malloc(size); + hcInitSingle(filter->f_long, &(h[2 * lflen]), hlen - 2 * lflen, lflen, lflen / sflen); + + if (h2 != NULL) + { + fftwf_free(h2); + } +} + + +void hcCloseDual(HConvDual *filter) +{ + hcCloseSingle(filter->f_short); + free(filter->f_short); + hcCloseSingle(filter->f_long); + free(filter->f_long); + fftwf_free(filter->out_long); + fftwf_free(filter->in_long); + memset(filter, 0, sizeof(HConvDual)); +} + + +//////////////////////////////////////////////////////////////// + + +void hcBenchmarkTripple(int sflen, int mflen, int lflen) +{ + HConvTripple filter; + float *x; + float *h; + float *y; + int xlen, hlen, ylen; + int size, n; + int pos; + double t_start, t_diff; + double counter = 0.0; + double signal_time; + double cpu_load; + double lin, mul; + +// xlen = sflen; + xlen = 2048*2048; + size = sizeof(float) * xlen; + x = (float *)fftwf_malloc(size); + lin = pow(10.0, -100.0 / 20.0); // 0.00001 = -100dB + mul = pow(lin, 1.0 / (double)xlen); + x[0] = 1.0; + for (n = 1; n < xlen; n++) + x[n] = float(-mul * x[n - 1]); + + hlen = 96000; + size = sizeof(float) * hlen; + h = (float *)fftwf_malloc(size); + lin = pow(10.0, -60.0 / 20.0); // 0.001 = -60dB + mul = pow(lin, 1.0 / (double)hlen); + h[0] = 1.0; + for (n = 1; n < hlen; n++) + h[n] = float(mul * h[n - 1]); + + ylen = sflen; + size = sizeof(float) * ylen; + y = (float *)fftwf_malloc(size); + + hcInitTripple(&filter, h, hlen, sflen, mflen, lflen); + + t_diff = 0.0; + t_start = hcTime(); + size = mflen / sflen; + pos = 0; + while (t_diff < 10.0) + { + for (n = 0; n < size; n++) + { +// hcProcessTripple(&filter, x, y); + hcProcessTripple(&filter, &(x[pos]), y); + pos += sflen; + if (pos >= xlen) + pos = 0; + } + counter += 1.0; + t_diff = hcTime() - t_start; + } + signal_time = counter * (double)mflen / 48000.0; + cpu_load = 100.0 * t_diff / signal_time; + printf("Estimated CPU load: %5.2f %%\n", cpu_load); + + hcCloseTripple(&filter); + fftwf_free(x); + fftwf_free(h); + fftwf_free(y); +} + + +void hcProcessTripple(HConvTripple *filter, float *in, float *out) +{ + int lpos, size, i; + + // convolution with short segments + hcPutSingle(filter->f_short, in); + hcProcessSingle(filter->f_short); + hcGetSingle(filter->f_short, out); + + // add contribution from last medium frame + lpos = filter->step * filter->flen_short; + for (i = 0; i < filter->flen_short; i++) + out[i] += filter->out_medium[lpos + i]; + + // add current frame to medium input buffer + lpos = filter->step * filter->flen_short; + size = sizeof(float) * filter->flen_short; + memcpy(&(filter->in_medium[lpos]), in, size); + + // convolution with medium segments + if (filter->step == filter->maxstep - 1) + hcProcessDual(filter->f_medium, + filter->in_medium, + filter->out_medium); + + // increase step counter + filter->step = (filter->step + 1) % filter->maxstep; +} + + +void hcProcessAddTripple(HConvTripple *filter, float *in, float *out) +{ + int lpos, size, i; + + // convolution with short segments + hcPutSingle(filter->f_short, in); + hcProcessSingle(filter->f_short); + hcGetAddSingle(filter->f_short, out); + + // add contribution from last medium frame + lpos = filter->step * filter->flen_short; + for (i = 0; i < filter->flen_short; i++) + out[i] += filter->out_medium[lpos + i]; + + // add current frame to medium input buffer + lpos = filter->step * filter->flen_short; + size = sizeof(float) * filter->flen_short; + memcpy(&(filter->in_medium[lpos]), in, size); + + // convolution with medium segments + if (filter->step == filter->maxstep - 1) + hcProcessDual(filter->f_medium, + filter->in_medium, + filter->out_medium); + + // increase step counter + filter->step = (filter->step + 1) % filter->maxstep; +} + + +void hcInitTripple(HConvTripple *filter, float *h, int hlen, int sflen, int mflen, int lflen) +{ + int size; + float *h2 = NULL; + int h2len; + + // sanity check: minimum impulse response length + h2len = mflen + 2 * lflen + 1; + if (hlen < h2len) + { + size = sizeof(float) * h2len; + h2 = (float*)fftwf_malloc(size); + memset(h2, 0, size); + size = sizeof(float) * hlen; + memcpy(h2, h, size); + h = h2; + hlen = h2len; + } + + // processing step counter + filter->step = 0; + + // number of processing steps per medium audio frame + filter->maxstep = mflen / sflen; + + // number of samples per medium audio frame + filter->flen_medium = mflen; + + // number of samples per short audio frame + filter->flen_short = sflen; + + // input buffer (medium frame) + size = sizeof(float) * mflen; + filter->in_medium = (float *)fftwf_malloc(size); + memset(filter->in_medium, 0, size); + + // output buffer (medium frame) + size = sizeof(float) * mflen; + filter->out_medium = (float *)fftwf_malloc(size); + memset(filter->out_medium, 0, size); + + // convolution filter (short segments) + size = sizeof(HConvSingle); + filter->f_short = (HConvSingle *)malloc(size); + hcInitSingle(filter->f_short, h, mflen, sflen, 1); + + // convolution filter (medium segments) + size = sizeof(HConvDual); + filter->f_medium = (HConvDual *)malloc(size); + hcInitDual(filter->f_medium, &(h[mflen]), hlen - mflen, mflen, lflen); + + if (h2 != NULL) + { + fftwf_free(h2); + } +} + + +void hcCloseTripple(HConvTripple *filter) +{ + hcCloseSingle(filter->f_short); + free(filter->f_short); + hcCloseDual(filter->f_medium); + free(filter->f_medium); + fftwf_free(filter->out_medium); + fftwf_free(filter->in_medium); + memset(filter, 0, sizeof(HConvTripple)); +} diff --git a/libHybridConv-0.1.1/libHybridConv_eapo.h b/libHybridConv-0.1.1/libHybridConv_eapo.h new file mode 100644 index 0000000..e54fbf9 --- /dev/null +++ b/libHybridConv-0.1.1/libHybridConv_eapo.h @@ -0,0 +1,102 @@ +/*************************************************************************** + * Copyright (C) 2009 by Christian Borss * + * christian.borss@rub.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +// Adapted version for Equalizer APO. For original version see libHybridConv.h + +#ifndef __LIBHYBRIDCONV_H__ +#define __LIBHYBRIDCONV_H__ + + +#include + + +typedef struct str_HConvSingle +{ + int step; // processing step counter + int maxstep; // number of processing steps per audio frame + int mixpos; // current frame index + int framelength; // number of samples per audio frame + int *steptask; // processing tasks per step + float *dft_time; // DFT buffer (time domain) + fftwf_complex *dft_freq; // DFT buffer (frequency domain) + float *in_freq_real; // input buffer (frequency domain) + float *in_freq_imag; // input buffer (frequency domain) + int num_filterbuf; // number of filter segments + float **filterbuf_freq_real; // filter segments (frequency domain) + float **filterbuf_freq_imag; // filter segments (frequency domain) + int num_mixbuf; // number of mixing segments + float **mixbuf_freq_real; // mixing segments (frequency domain) + float **mixbuf_freq_imag; // mixing segments (frequency domain) + float *history_time; // history buffer (time domain) + fftwf_plan fft; // FFT transformation plan + fftwf_plan ifft; // IFFT transformation plan +} HConvSingle; + + +typedef struct str_HConvDual +{ + int step; // processing step counter + int maxstep; // number of processing steps per long audio frame + int flen_long; // number of samples per long audio frame + int flen_short; // number of samples per short audio frame + float *in_long; // input buffer (long frame) + float *out_long; // output buffer (long frame) + HConvSingle *f_long; // convolution filter (long segments) + HConvSingle *f_short; // convolution filter (short segments) +} HConvDual; + + +typedef struct str_HConvTripple +{ + int step; // processing step counter + int maxstep; // number of processing steps per long audio frame + int flen_medium; // number of samples per long audio frame + int flen_short; // number of samples per short audio frame + float *in_medium; // input buffer (long frame) + float *out_medium; // output buffer (long frame) + HConvDual *f_medium; // convolution filter (long segments) + HConvSingle *f_short; // convolution filter (short segments) +} HConvTripple; + + +/* single filter functions */ +double getProcTime(int flen, int num, double dur); +void hcPutSingle(HConvSingle *filter, float *x); +void hcProcessSingle(HConvSingle *filter); +void hcGetSingle(HConvSingle *filter, float *y); +void hcGetAddSingle(HConvSingle *filter, float *y); +void hcInitSingle(HConvSingle *filter, float *h, int hlen, int flen, int steps); +void hcCloseSingle(HConvSingle *filter); + +/* dual filter functions */ +void hcBenchmarkDual(int sflen, int lflen); +void hcProcessDual(HConvDual *filter, float *in, float *out); +void hcProcessAddDual(HConvDual *filter, float *in, float *out); +void hcInitDual(HConvDual *filter, float *h, int hlen, int sflen, int lflen); +void hcCloseDual(HConvDual *filter); + +/* tripple filter functions */ +void hcBenchmarkTripple(int sflen, int mflen, int lflen); +void hcProcessTripple(HConvTripple *filter, float *in, float *out); +void hcProcessAddTripple(HConvTripple *filter, float *in, float *out); +void hcInitTripple(HConvTripple *filter, float *h, int hlen, int sflen, int mflen, int lflen); +void hcCloseTripple(HConvTripple *filter); + + +#endif // __LIBHYBRIDCONV_H__ diff --git a/parser/RegexFunctions.cpp b/parser/RegexFunctions.cpp index 41ab865..c105b27 100644 --- a/parser/RegexFunctions.cpp +++ b/parser/RegexFunctions.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include #include "RegexFunctions.h" diff --git a/parser/RegexFunctions.h b/parser/RegexFunctions.h index 11bf792..001a0ba 100644 --- a/parser/RegexFunctions.h +++ b/parser/RegexFunctions.h @@ -25,16 +25,16 @@ class RegexSearchFunction:public mup::ICallback { public: RegexSearchFunction(); - virtual void Eval(mup::ptr_val_type& ret, const mup::ptr_val_type *arg, int argc); + void Eval(mup::ptr_val_type& ret, const mup::ptr_val_type *arg, int argc) override; const mup::char_type* GetDesc() const; - virtual mup::IToken* Clone() const; + mup::IToken* Clone() const override; }; class RegexReplaceFunction:public mup::ICallback { public: RegexReplaceFunction(); - virtual void Eval(mup::ptr_val_type& ret, const mup::ptr_val_type *arg, int argc); + void Eval(mup::ptr_val_type& ret, const mup::ptr_val_type *arg, int argc) override; const mup::char_type* GetDesc() const; - virtual mup::IToken* Clone() const; + mup::IToken* Clone() const override; }; diff --git a/parser/RegistryFunctions.cpp b/parser/RegistryFunctions.cpp index 210f40a..54c9904 100644 --- a/parser/RegistryFunctions.cpp +++ b/parser/RegistryFunctions.cpp @@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include "RegistryFunctions.h" #include "../helpers/RegistryHelper.h" #include "../FilterEngine.h" diff --git a/parser/RegistryFunctions.h b/parser/RegistryFunctions.h index dad73af..c84a5fd 100644 --- a/parser/RegistryFunctions.h +++ b/parser/RegistryFunctions.h @@ -27,9 +27,9 @@ class ReadRegStringFunction:public mup::ICallback { public: ReadRegStringFunction(FilterEngine* engine); - virtual void Eval(mup::ptr_val_type& ret, const mup::ptr_val_type *arg, int argc); + void Eval(mup::ptr_val_type& ret, const mup::ptr_val_type *arg, int argc) override; const mup::char_type* GetDesc() const; - virtual mup::IToken* Clone() const; + mup::IToken* Clone() const override; private: FilterEngine* engine; @@ -39,9 +39,9 @@ class ReadRegDWORDFunction:public mup::ICallback { public: ReadRegDWORDFunction(FilterEngine* engine); - virtual void Eval(mup::ptr_val_type& ret, const mup::ptr_val_type *arg, int argc); + void Eval(mup::ptr_val_type& ret, const mup::ptr_val_type *arg, int argc) override; const mup::char_type* GetDesc() const; - virtual mup::IToken* Clone() const; + mup::IToken* Clone() const override; private: FilterEngine* engine; diff --git a/parser/StringOperators.cpp b/parser/StringOperators.cpp index 471249b..3b0ad8e 100644 --- a/parser/StringOperators.cpp +++ b/parser/StringOperators.cpp @@ -17,6 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "stdafx.h" #include "StringOperators.h" using namespace std; diff --git a/parser/StringOperators.h b/parser/StringOperators.h index 2c27015..b3d938c 100644 --- a/parser/StringOperators.h +++ b/parser/StringOperators.h @@ -25,7 +25,7 @@ class AddOperator:public mup::OprtAdd { public: - virtual void Eval(mup::ptr_val_type& ret, const mup::ptr_val_type *arg, int argc); + void Eval(mup::ptr_val_type& ret, const mup::ptr_val_type *arg, int argc) override; const mup::char_type* GetDesc() const; - virtual mup::IToken* Clone() const; + mup::IToken* Clone() const override; }; diff --git a/stdafx.cpp b/stdafx.cpp new file mode 100644 index 0000000..1577c4e --- /dev/null +++ b/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" \ No newline at end of file diff --git a/stdafx.h b/stdafx.h new file mode 100644 index 0000000..bfb0a94 --- /dev/null +++ b/stdafx.h @@ -0,0 +1,19 @@ +#define _USE_MATH_DEFINES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include diff --git a/version.h b/version.h index 5c3fd05..198fa0e 100644 --- a/version.h +++ b/version.h @@ -1,3 +1,3 @@ -#define MAJOR 0 -#define MINOR 9 -#define REVISION 2 +#define MAJOR 1 +#define MINOR 0 +#define REVISION 0