Skip to content

Commit 3a0cf0d

Browse files
committed
Fix Antares plugin startup issue, improve app launch speed, resolve memory leak in beat search, promote hotkeys to global scope, add support for accessibility keys (Ctrl, Shift, Alt, F1...)
1 parent 80f9c3c commit 3a0cf0d

23 files changed

+824
-511
lines changed

Resources/Languages/lang_en.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@
2929
"assignPreset": "Assign Preset to this Slot",
3030
"clearSlot": "Clear this Slot"
3131
},
32+
"globalHotkeys": {
33+
"hookInstallOk": "Global keyboard hook installed successfully.",
34+
"hookInstallFail": "Failed to install global keyboard hook.",
35+
"hookUninstall": "Global keyboard hook uninstalled.",
36+
"managerUpdated": "Global Hotkey Manager updated with {{count}} slots."
37+
},
3238
"tracks": {
3339
"vocal": "VOCAL",
3440
"music": "MUSIC",
@@ -139,12 +145,13 @@
139145
"masterUtility": {
140146
"masterPlugins": "Master Plugins",
141147
"quickKeySettings": "QuickKey Settings",
142-
"recAndPlayTitle": "REC & PLAY",
148+
"recAndPlayTitle": "MASTER RECORD & PLAYER",
143149
"record": "Record",
144150
"play": "Play",
145151
"pause": "Pause",
146152
"stop": "Stop",
147-
"list": "List"
153+
"list": "List",
154+
"noFileLoaded": "No File Loaded"
148155
},
149156
"recordingListWindow": {
150157
"title": "Recorded Tracks",

Resources/Languages/lang_vi.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@
2929
"assignPreset": "Gán Preset vào ô này",
3030
"clearSlot": "Xóa gán ở ô này"
3131
},
32+
"globalHotkeys": {
33+
"hookInstallOk": "Đã cài đặt hook bàn phím toàn cục thành công.",
34+
"hookInstallFail": "Cài đặt hook bàn phím toàn cục thất bại.",
35+
"hookUninstall": "Đã gỡ cài đặt hook bàn phím toàn cục.",
36+
"managerUpdated": "Trình quản lý phím tắt toàn cục đã được cập nhật với {{count}} ô."
37+
},
3238
"tracks": {
3339
"vocal": "GIỌNG HÁT",
3440
"music": "NHẠC NỀN",
@@ -139,12 +145,13 @@
139145
"masterUtility": {
140146
"masterPlugins": "Plugin Kênh Tổng",
141147
"quickKeySettings": "Cài đặt Phím tắt",
142-
"recAndPlayTitle": "GHI ÂM & PHÁT",
148+
"recAndPlayTitle": "GHI ÂM & PHÁT KÊNH TỔNG",
143149
"record": "Ghi Âm",
144150
"play": "Phát",
145151
"pause": "Dừng",
146152
"stop": "Ngừng",
147-
"list": "DS Ghi"
153+
"list": "DS Ghi",
154+
"noFileLoaded": "Chưa có tệp nào"
148155
},
149156
"recordingListWindow": {
150157
"title": "Danh sách Bản ghi",

Source/Application/Application.cpp

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1+
#define NOMINMAX
2+
13
#include "Application.h"
24
#include "../GUI/MainComponent/MainComponent.h"
35
#include "../GUI/Windows/PresetManagerWindow.h"
46
#include "../GUI/Windows/PluginManagerWindow.h"
57
#include "../GUI/Windows/SplashScreenComponent.h"
68
#include "../Data/AppState.h"
79
#include "../Data/LanguageManager/LanguageManager.h"
8-
// Dòng #include "CrashHandler.h" đã được xóa
9-
10+
#include "../Data/SoundboardManager.h"
11+
#include "../AudioEngine/SoundPlayer.h"
12+
#include <algorithm> // This is still required for std::max
1013

14+
//==============================================================================
1115
PluginManager& getSharedPluginManager()
1216
{
1317
auto* app = dynamic_cast<idolLiveAudioApplication*>(juce::JUCEApplication::getInstance());
@@ -26,22 +30,20 @@ PresetManager& getSharedPresetManager()
2630
}
2731

2832
//==============================================================================
29-
3033
idolLiveAudioApplication::idolLiveAudioApplication() {}
3134

3235
idolLiveAudioApplication::MainWindow::MainWindow(juce::String name)
3336
: DocumentWindow(name,
3437
juce::Desktop::getInstance().getDefaultLookAndFeel()
3538
.findColour(juce::ResizableWindow::backgroundColourId),
36-
DocumentWindow::allButtons)
39+
DocumentWindow::minimiseButton | DocumentWindow::closeButton)
3740
{
3841
setUsingNativeTitleBar(true);
3942
setContentOwned(new MainComponent(), true);
4043
#if JUCE_IOS || JUCE_ANDROID
4144
setFullScreen(true);
4245
#else
43-
setResizable(true, true);
44-
setResizeLimits(1640, 1010, 10000, 10000);
46+
setResizable(false, false);
4547
centreWithSize(1640, 1010);
4648
#endif
4749
setVisible(false);
@@ -62,7 +64,7 @@ void idolLiveAudioApplication::MainWindow::closeButtonPressed()
6264
}
6365
}
6466

65-
// ... (Class SplashWindow không thay đổi) ...
67+
6668
class SplashWindow final : public juce::DocumentWindow
6769
{
6870
public:
@@ -76,23 +78,15 @@ class SplashWindow final : public juce::DocumentWindow
7678
const int desiredHeight = static_cast<int>(desiredWidth * aspectRatio);
7779

7880
setContentOwned(new SplashScreenComponent(), true);
79-
8081
setUsingNativeTitleBar(false);
8182
setResizable(false, false);
82-
8383
centreWithSize(static_cast<int>(desiredWidth), desiredHeight);
84-
8584
setVisible(true);
8685
}
8786

88-
SplashScreenComponent* getSplashContent()
89-
{
90-
return dynamic_cast<SplashScreenComponent*>(getContentComponent());
91-
}
92-
9387
void setStatusMessage(const juce::String& msg)
9488
{
95-
if (auto* content = getSplashContent())
89+
if (auto* content = dynamic_cast<SplashScreenComponent*>(getContentComponent()))
9690
content->setStatusMessage(msg);
9791
}
9892

@@ -101,11 +95,8 @@ class SplashWindow final : public juce::DocumentWindow
10195

10296

10397
//==============================================================================
104-
10598
void idolLiveAudioApplication::initialise(const juce::String& commandLine)
10699
{
107-
// Lời gọi CrashHandler::install(); đã được xóa
108-
109100
juce::ignoreUnused(commandLine);
110101

111102
splashWindow = std::make_unique<SplashWindow>();
@@ -115,6 +106,9 @@ void idolLiveAudioApplication::initialise(const juce::String& commandLine)
115106
splashWindow->setStatusMessage(lang.get("splash.initManagers"));
116107
pluginManager = std::make_unique<PluginManager>();
117108
presetManager = std::make_unique<PresetManager>();
109+
#if JUCE_WINDOWS
110+
globalHotkeyManager = std::make_unique<GlobalHotkeyManager>();
111+
#endif
118112

119113
splashWindow->setStatusMessage(lang.get("splash.createWindow"));
120114
mainWindow.reset(new MainWindow(getApplicationName()));
@@ -140,6 +134,25 @@ void idolLiveAudioApplication::initialise(const juce::String& commandLine)
140134
}
141135
}
142136

137+
#if JUCE_WINDOWS
138+
if (globalHotkeyManager != nullptr)
139+
{
140+
globalHotkeyManager->onHotkeyTriggered = [mainComp](int slotIndex)
141+
{
142+
if (mainComp)
143+
{
144+
auto& soundPlayer = mainComp->getAudioEngine().getSoundPlayer();
145+
const auto& slot = getSharedSoundboardProfileManager().getCurrentSlots().getReference(slotIndex);
146+
if (!slot.isEmpty())
147+
{
148+
soundPlayer.play(slot.audioFile, slotIndex);
149+
}
150+
}
151+
};
152+
globalHotkeyManager->start();
153+
}
154+
#endif
155+
143156
auto error = deviceManager.initialise(32, 32, deviceSettingsXml.get(), true);
144157
if (error.isNotEmpty()) { DBG("Audio device initialisation failed: " + error); }
145158
}
@@ -148,6 +161,7 @@ void idolLiveAudioApplication::initialise(const juce::String& commandLine)
148161
const auto elapsedTime = juce::Time::getMillisecondCounter() - startTime;
149162
const int minDisplayTime = 4000;
150163

164+
// Now this line will compile correctly without conflicts.
151165
int timeToWait = std::max(0, minDisplayTime - (int)elapsedTime);
152166

153167
auto* mainWin = mainWindow.get();
@@ -160,11 +174,23 @@ void idolLiveAudioApplication::initialise(const juce::String& commandLine)
160174

161175
void idolLiveAudioApplication::shutdown()
162176
{
177+
#if JUCE_WINDOWS
178+
if (globalHotkeyManager != nullptr)
179+
{
180+
globalHotkeyManager->stop();
181+
globalHotkeyManager.reset();
182+
}
183+
#endif
184+
185+
/*
186+
// <<< VÔ HIỆU HÓA KHỐI LỆNH NÀY >>>
187+
// Ngăn không cho lưu trạng thái của MainComponent khi tắt ứng dụng.
163188
if (mainWindow != nullptr)
164189
{
165190
if (auto* mainComp = dynamic_cast<MainComponent*>(mainWindow->getContentComponent()))
166191
AppState::getInstance().saveState(*mainComp);
167192
}
193+
*/
168194

169195
if (auto* mainComp = dynamic_cast<MainComponent*>(mainWindow->getContentComponent()))
170196
{
@@ -197,5 +223,5 @@ void idolLiveAudioApplication::showPluginManagerWindow()
197223
pluginManagerWindow->toFront(true);
198224
}
199225

200-
226+
//==============================================================================
201227
START_JUCE_APPLICATION(idolLiveAudioApplication)

Source/Application/Application.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include "JuceHeader.h"
44
#include "Data/PluginManager/PluginManager.h"
55
#include "Data/PresetManager.h"
6+
// <<< ADDED: Include the new manager >>>
7+
#include "GlobalHotkeyManager.h"
68

79
// Forward declarations
810
class PresetManagerWindow;
@@ -35,6 +37,10 @@ class idolLiveAudioApplication : public juce::JUCEApplication
3537
void showPresetManagerWindow();
3638
void showPluginManagerWindow();
3739

40+
#if JUCE_WINDOWS
41+
GlobalHotkeyManager* getPublicGlobalHotkeyManager() { return globalHotkeyManager.get(); }
42+
#endif
43+
3844
private:
3945
class MainWindow : public juce::DocumentWindow
4046
{
@@ -49,6 +55,11 @@ class idolLiveAudioApplication : public juce::JUCEApplication
4955
std::unique_ptr<PluginManager> pluginManager;
5056
std::unique_ptr<PresetManager> presetManager;
5157

58+
// <<< ADDED: The manager for global hotkeys >>>
59+
#if JUCE_WINDOWS
60+
std::unique_ptr<GlobalHotkeyManager> globalHotkeyManager;
61+
#endif
62+
5263
std::unique_ptr<SplashWindow> splashWindow;
5364
std::unique_ptr<MainWindow> mainWindow;
5465
std::unique_ptr<PresetManagerWindow> presetManagerWindow;
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#include "GlobalHotkeyManager.h"
2+
#include "../Data/LanguageManager/LanguageManager.h"
3+
4+
#if JUCE_WINDOWS
5+
6+
// Define the static instance pointer
7+
GlobalHotkeyManager* GlobalHotkeyManager::instance = nullptr;
8+
9+
GlobalHotkeyManager::GlobalHotkeyManager()
10+
{
11+
instance = this;
12+
}
13+
14+
GlobalHotkeyManager::~GlobalHotkeyManager()
15+
{
16+
stop();
17+
instance = nullptr;
18+
}
19+
20+
void GlobalHotkeyManager::start()
21+
{
22+
auto& lang = LanguageManager::getInstance();
23+
juce::ignoreUnused(lang);
24+
if (hook == nullptr)
25+
{
26+
hook = SetWindowsHookExW(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(nullptr), 0);
27+
if (hook != nullptr)
28+
{
29+
DBG(lang.get("globalHotkeys.hookInstallOk"));
30+
}
31+
else
32+
{
33+
DBG(lang.get("globalHotkeys.hookInstallFail") + " Error: " + juce::String(GetLastError()));
34+
}
35+
}
36+
}
37+
38+
void GlobalHotkeyManager::stop()
39+
{
40+
if (hook != nullptr)
41+
{
42+
UnhookWindowsHookEx(hook);
43+
hook = nullptr;
44+
DBG(LanguageManager::getInstance().get("globalHotkeys.hookUninstall"));
45+
}
46+
}
47+
48+
void GlobalHotkeyManager::updateHotkeys(const juce::Array<SoundboardSlot>& newSlots)
49+
{
50+
hotkeySlots = newSlots;
51+
DBG(LanguageManager::getInstance().get("globalHotkeys.managerUpdated").replace("{{count}}", juce::String(hotkeySlots.size())));
52+
}
53+
54+
void GlobalHotkeyManager::setCaptureMode(std::function<void(const juce::KeyPress&)> onKeyCaptured)
55+
{
56+
captureCallback = std::move(onKeyCaptured);
57+
}
58+
59+
LRESULT CALLBACK GlobalHotkeyManager::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
60+
{
61+
if (nCode == HC_ACTION && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
62+
{
63+
if (instance != nullptr)
64+
{
65+
KBDLLHOOKSTRUCT* pkhs = (KBDLLHOOKSTRUCT*)lParam;
66+
67+
// BƯỚC 1: Lọc chính xác các phím bổ trợ Ctrl, Alt, Shift
68+
const int relevantModifiersMask = juce::ModifierKeys::ctrlModifier | juce::ModifierKeys::altModifier | juce::ModifierKeys::shiftModifier;
69+
const juce::ModifierKeys currentMods = juce::ModifierKeys::getCurrentModifiersRealtime().getRawFlags() & relevantModifiersMask;
70+
71+
// BƯỚC 2: Tạo KeyPress với virtual key code gốc (đây là thay đổi quan trọng)
72+
// Chúng ta không còn đặt keyCode = 0 cho các phím bổ trợ nữa.
73+
const juce::KeyPress currentPress(pkhs->vkCode, currentMods, 0);
74+
75+
// Ưu tiên chế độ bắt phím
76+
if (instance->captureCallback != nullptr)
77+
{
78+
juce::MessageManager::callAsync([press = currentPress]() {
79+
if (GlobalHotkeyManager::instance != nullptr && GlobalHotkeyManager::instance->captureCallback != nullptr)
80+
{
81+
GlobalHotkeyManager::instance->captureCallback(press);
82+
GlobalHotkeyManager::instance->captureCallback = nullptr;
83+
}
84+
});
85+
return 1; // Chặn không cho các ứng dụng khác nhận phím
86+
}
87+
88+
// Nếu không bắt phím, kiểm tra trigger hotkey
89+
if (instance->onHotkeyTriggered != nullptr)
90+
{
91+
for (int i = 0; i < instance->hotkeySlots.size(); ++i)
92+
{
93+
const auto& slot = instance->hotkeySlots.getReference(i);
94+
if (slot.hotkey == currentPress && !slot.isEmpty())
95+
{
96+
juce::MessageManager::callAsync([i]() {
97+
if (GlobalHotkeyManager::instance != nullptr && GlobalHotkeyManager::instance->onHotkeyTriggered)
98+
GlobalHotkeyManager::instance->onHotkeyTriggered(i);
99+
});
100+
101+
if (slot.consumeKey)
102+
{
103+
return 1; // Nếu người dùng muốn CHIẾM DỤNG, ta dừng ở đây
104+
}
105+
106+
}
107+
}
108+
}
109+
}
110+
}
111+
return CallNextHookEx(instance ? instance->hook : nullptr, nCode, wParam, lParam);
112+
}
113+
114+
115+
#endif // JUCE_WINDOWS

0 commit comments

Comments
 (0)