Skip to content

Commit b089a72

Browse files
committed
Implement hot-swap preset feature; fix related issues with locked state loading
1 parent 3a0cf0d commit b089a72

File tree

12 files changed

+560
-445
lines changed

12 files changed

+560
-445
lines changed

Source/Application/Application.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,6 @@ void idolLiveAudioApplication::initialise(const juce::String& commandLine)
161161
const auto elapsedTime = juce::Time::getMillisecondCounter() - startTime;
162162
const int minDisplayTime = 4000;
163163

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

167166
auto* mainWin = mainWindow.get();
@@ -182,15 +181,13 @@ void idolLiveAudioApplication::shutdown()
182181
}
183182
#endif
184183

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.
184+
// Re-enabled state saving on shutdown. The logic inside saveState now
185+
// controls what is actually saved (excluding ACTIVE_PRESET).
188186
if (mainWindow != nullptr)
189187
{
190188
if (auto* mainComp = dynamic_cast<MainComponent*>(mainWindow->getContentComponent()))
191189
AppState::getInstance().saveState(*mainComp);
192190
}
193-
*/
194191

195192
if (auto* mainComp = dynamic_cast<MainComponent*>(mainWindow->getContentComponent()))
196193
{

Source/AudioEngine/AudioEngine.cpp

Lines changed: 76 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
==============================================================================
33
44
AudioEngine.cpp
5-
(Fully Functional Send/Return FX Architecture)
5+
(Fixed: Implemented robust two-stage preset loading orchestration)
66
77
==============================================================================
88
*/
99

1010
#include "AudioEngine.h"
11+
#include "../Data/AppState.h"
1112
#include "../GUI/Layout/TrackComponent.h"
1213
#include "../GUI/Layout/MasterUtilityComponent.h"
1314
#include "juce_audio_devices/juce_audio_devices.h"
@@ -20,18 +21,14 @@ AudioEngine::AudioEngine(juce::AudioDeviceManager& manager)
2021
musicFxChain(Identifiers::MusicFx1State, Identifiers::MusicFx2State, Identifiers::MusicFx3State, Identifiers::MusicFx4State)
2122
{
2223
formatManager.registerBasicFormats();
23-
2424
audioRecorder = std::make_unique<AudioRecorder>(formatManager, "");
2525
vocalTrackRecorder = std::make_unique<AudioRecorder>(formatManager, "Vocal");
2626
musicTrackRecorder = std::make_unique<AudioRecorder>(formatManager, "Music");
2727
rawVocalRecorder = std::make_unique<AudioRecorder>(formatManager, "Projects");
2828
rawMusicRecorder = std::make_unique<AudioRecorder>(formatManager, "Projects");
29-
3029
soundPlayer = std::make_unique<IdolAZ::SoundPlayer>();
31-
3230
soundboardMixer.addInputSource(soundPlayer.get(), false);
3331
directOutputMixer.addInputSource(&playbackSource, false);
34-
3532
}
3633

3734
AudioEngine::~AudioEngine()
@@ -42,42 +39,31 @@ void AudioEngine::prepareAllProcessors(double sampleRate, int samplesPerBlock)
4239
{
4340
stableSampleRate = sampleRate;
4441
stableBlockSize = samplesPerBlock;
45-
4642
currentSampleRate = sampleRate;
4743
currentBlockSize = samplesPerBlock;
48-
49-
DBG("AudioEngine preparing all processors with settings: "
50-
<< sampleRate << " Hz, " << samplesPerBlock << " samples.");
51-
44+
DBG("AudioEngine preparing all processors with settings: " << sampleRate << " Hz, " << samplesPerBlock << " samples.");
5245
juce::dsp::ProcessSpec stereoSpec{ sampleRate, static_cast<uint32_t>(samplesPerBlock), 2 };
53-
5446
vocalProcessor.prepare(stereoSpec);
5547
musicProcessor.prepare(stereoSpec);
5648
masterProcessor.prepare(stereoSpec);
57-
5849
for (auto& fxProc : vocalFxChain.processors) fxProc.prepare(stereoSpec);
5950
for (auto& fxProc : musicFxChain.processors) fxProc.prepare(stereoSpec);
60-
6151
soundboardMixer.prepareToPlay(samplesPerBlock, sampleRate);
6252
directOutputMixer.prepareToPlay(samplesPerBlock, sampleRate);
6353
playbackSource.prepareToPlay(samplesPerBlock, sampleRate);
6454
vocalTrackSource.prepareToPlay(samplesPerBlock, sampleRate);
6555
musicTrackSource.prepareToPlay(samplesPerBlock, sampleRate);
66-
6756
vocalBuffer.setSize(2, samplesPerBlock);
6857
musicStereoBuffer.setSize(2, samplesPerBlock);
6958
mixBuffer.setSize(2, samplesPerBlock);
7059
soundboardBuffer.setSize(2, samplesPerBlock);
7160
directOutputBuffer.setSize(2, samplesPerBlock);
72-
7361
fxSendBuffer.setSize(2, samplesPerBlock);
7462
for (auto& buffer : vocalFxReturnBuffers) buffer.setSize(2, samplesPerBlock);
7563
for (auto& buffer : musicFxReturnBuffers) buffer.setSize(2, samplesPerBlock);
76-
7764
vocalProcessor.reset();
7865
musicProcessor.reset();
7966
masterProcessor.reset();
80-
8167
for (auto& fxProc : vocalFxChain.processors) fxProc.reset();
8268
for (auto& fxProc : musicFxChain.processors) fxProc.reset();
8369
}
@@ -86,28 +72,22 @@ void AudioEngine::prepareAllProcessors(double sampleRate, int samplesPerBlock)
8672
void AudioEngine::audioDeviceAboutToStart(juce::AudioIODevice* device)
8773
{
8874
prepareAllProcessors(device->getCurrentSampleRate(), device->getCurrentBufferSizeSamples());
89-
9075
if (onDeviceStarted)
91-
{
9276
juce::MessageManager::callAsync(onDeviceStarted);
93-
}
9477
}
9578

9679
void AudioEngine::audioDeviceStopped()
9780
{
9881
vocalProcessor.reset();
9982
musicProcessor.reset();
10083
masterProcessor.reset();
101-
10284
for (auto& fxProc : vocalFxChain.processors) fxProc.reset();
10385
for (auto& fxProc : musicFxChain.processors) fxProc.reset();
104-
10586
soundboardMixer.releaseResources();
10687
directOutputMixer.releaseResources();
10788
playbackSource.releaseResources();
10889
vocalTrackSource.releaseResources();
10990
musicTrackSource.releaseResources();
110-
11191
currentSampleRate = 0.0;
11292
currentBlockSize = 0;
11393
vocalInputChannel.store(-1);
@@ -117,47 +97,30 @@ void AudioEngine::audioDeviceStopped()
11797
selectedOutputRightChannel.store(-1);
11898
}
11999

120-
void AudioEngine::audioDeviceIOCallbackWithContext(const float* const* inputChannelData, int numInputChannels,
121-
float* const* outputChannelData, int numOutputChannels,
122-
int numSamples, const juce::AudioIODeviceCallbackContext& context)
100+
void AudioEngine::audioDeviceIOCallbackWithContext(const float* const* inputChannelData, int numInputChannels, float* const* outputChannelData, int numOutputChannels, int numSamples, const juce::AudioIODeviceCallbackContext& context)
123101
{
124102
juce::ignoreUnused(context);
125-
126-
// <<< FIX: Use deviceManager to get the current device safely >>>
127103
if (auto* currentDevice = deviceManager.getCurrentAudioDevice())
128104
{
129105
if (numSamples != currentBlockSize || currentDevice->getCurrentSampleRate() != currentSampleRate)
130-
{
131106
prepareAllProcessors(currentDevice->getCurrentSampleRate(), numSamples);
132-
}
133107
}
134-
135108
juce::ScopedNoDenormals noDenormals;
136-
137-
// --- 1. Xóa tất cả các buffer làm việc ---
138109
vocalBuffer.clear();
139110
musicStereoBuffer.clear();
140111
mixBuffer.clear();
141112
soundboardBuffer.clear();
142113
directOutputBuffer.clear();
143114
for (auto& buffer : vocalFxReturnBuffers) buffer.clear();
144115
for (auto& buffer : musicFxReturnBuffers) buffer.clear();
145-
146116
vocalPlayerBuffer.setSize(2, numSamples);
147117
musicPlayerBuffer.setSize(2, numSamples);
148118
vocalPlayerBuffer.clear();
149119
musicPlayerBuffer.clear();
150-
151-
152-
// --- 2. Lấy tín hiệu từ các Player của Track ---
153120
juce::AudioSourceChannelInfo vocalPlayerInfo(&vocalPlayerBuffer, 0, numSamples);
154121
vocalTrackSource.getNextAudioBlock(vocalPlayerInfo);
155-
156122
juce::AudioSourceChannelInfo musicPlayerInfo(&musicPlayerBuffer, 0, numSamples);
157123
musicTrackSource.getNextAudioBlock(musicPlayerInfo);
158-
159-
160-
// --- 3. Xử lý Track Vocal ---
161124
const int currentVocalIn = vocalInputChannel.load();
162125
if (juce::isPositiveAndBelow(currentVocalIn, numInputChannels))
163126
{
@@ -170,13 +133,9 @@ void AudioEngine::audioDeviceIOCallbackWithContext(const float* const* inputChan
170133
vocalBuffer.addFrom(1, 0, vocalPlayerBuffer, 1, 0, numSamples);
171134
vocalProcessor.process(vocalBuffer);
172135
vocalTrackRecorder->processBlock(vocalBuffer, currentSampleRate);
173-
174-
175-
// --- 4. Xử lý Track Music ---
176136
const int currentMusicLeftIn = musicInputLeftChannel.load();
177137
const int currentMusicRightIn = musicInputRightChannel.load();
178-
if (juce::isPositiveAndBelow(currentMusicLeftIn, numInputChannels) &&
179-
juce::isPositiveAndBelow(currentMusicRightIn, numInputChannels))
138+
if (juce::isPositiveAndBelow(currentMusicLeftIn, numInputChannels) && juce::isPositiveAndBelow(currentMusicRightIn, numInputChannels))
180139
{
181140
juce::AudioBuffer<float> rawInput(const_cast<float**>(inputChannelData) + currentMusicLeftIn, 2, numSamples);
182141
musicStereoBuffer.copyFrom(0, 0, rawInput, 0, 0, numSamples);
@@ -187,14 +146,8 @@ void AudioEngine::audioDeviceIOCallbackWithContext(const float* const* inputChan
187146
musicStereoBuffer.addFrom(1, 0, musicPlayerBuffer, 1, 0, numSamples);
188147
musicProcessor.process(musicStereoBuffer);
189148
musicTrackRecorder->processBlock(musicStereoBuffer, currentSampleRate);
190-
191-
192-
// --- 5. Lấy tín hiệu từ Soundboard ---
193149
juce::AudioSourceChannelInfo soundboardChannelInfo(&soundboardBuffer, 0, numSamples);
194150
soundboardMixer.getNextAudioBlock(soundboardChannelInfo);
195-
196-
197-
// --- 6. Xử lý FX Sends/Returns ---
198151
for (int i = 0; i < 4; ++i)
199152
{
200153
fxSendBuffer.copyFrom(0, 0, vocalBuffer, 0, 0, numSamples);
@@ -215,20 +168,13 @@ void AudioEngine::audioDeviceIOCallbackWithContext(const float* const* inputChan
215168
musicFxReturnBuffers[i].copyFrom(1, 0, fxSendBuffer, 1, 0, numSamples);
216169
musicFxReturnBuffers[i].applyGain(musicFxChain.processors[i].getReturnLevel());
217170
}
218-
219-
220-
// --- 7. Tổng hợp tất cả vào Kênh Master ---
221171
mixBuffer.clear();
222-
223172
mixBuffer.copyFrom(0, 0, vocalBuffer, 0, 0, numSamples);
224173
mixBuffer.copyFrom(1, 0, vocalBuffer, 1, 0, numSamples);
225-
226174
mixBuffer.addFrom(0, 0, musicStereoBuffer, 0, 0, numSamples);
227175
mixBuffer.addFrom(1, 0, musicStereoBuffer, 1, 0, numSamples);
228-
229176
mixBuffer.addFrom(0, 0, soundboardBuffer, 0, 0, numSamples);
230177
mixBuffer.addFrom(1, 0, soundboardBuffer, 1, 0, numSamples);
231-
232178
for (int i = 0; i < 4; ++i)
233179
{
234180
mixBuffer.addFrom(0, 0, vocalFxReturnBuffers[i], 0, 0, numSamples);
@@ -239,22 +185,14 @@ void AudioEngine::audioDeviceIOCallbackWithContext(const float* const* inputChan
239185
mixBuffer.addFrom(0, 0, musicFxReturnBuffers[i], 0, 0, numSamples);
240186
mixBuffer.addFrom(1, 0, musicFxReturnBuffers[i], 1, 0, numSamples);
241187
}
242-
243188
masterProcessor.process(mixBuffer);
244189
audioRecorder->processBlock(mixBuffer, currentSampleRate);
245-
246-
247-
// --- 8. Lấy tín hiệu đi thẳng ra Output (Player của REC & PLAY) ---
248190
juce::AudioSourceChannelInfo directOutputChannelInfo(&directOutputBuffer, 0, numSamples);
249191
directOutputMixer.getNextAudioBlock(directOutputChannelInfo);
250-
251-
252-
// --- 9. Gửi tín hiệu cuối cùng ra loa ---
253192
const int currentOutputLeft = selectedOutputLeftChannel.load();
254193
const int currentOutputRight = selectedOutputRightChannel.load();
255194
for (int i = 0; i < numOutputChannels; ++i)
256195
juce::FloatVectorOperations::clear(outputChannelData[i], numSamples);
257-
258196
if (juce::isPositiveAndBelow(currentOutputLeft, numOutputChannels))
259197
{
260198
juce::FloatVectorOperations::copy(outputChannelData[currentOutputLeft], mixBuffer.getReadPointer(0), numSamples);
@@ -672,4 +610,75 @@ bool AudioEngine::isProjectPlaybackActive() const
672610
juce::ValueTree& AudioEngine::getProjectState()
673611
{
674612
return projectState;
613+
}
614+
615+
void AudioEngine::setPlaybackGain(float newGain)
616+
{
617+
playbackSource.setGain(newGain);
618+
}
619+
620+
float AudioEngine::getPlaybackGain() const
621+
{
622+
return playbackSource.getGain();
623+
}
624+
625+
juce::ValueTree AudioEngine::getFullState()
626+
{
627+
juce::ValueTree state("Preset");
628+
state.addChild(vocalProcessor.getState(), -1, nullptr);
629+
state.addChild(musicProcessor.getState(), -1, nullptr);
630+
state.addChild(masterProcessor.getState(), -1, nullptr);
631+
for (int i = 0; i < 4; ++i)
632+
{
633+
if (auto* p = getFxProcessorForVocal(i)) state.addChild(p->getState(), -1, nullptr);
634+
if (auto* p = getFxProcessorForMusic(i)) state.addChild(p->getState(), -1, nullptr);
635+
}
636+
637+
auto& appState = AppState::getInstance();
638+
const bool isLocked = appState.isSystemLocked();
639+
state.setProperty(Identifiers::lockState, isLocked, nullptr);
640+
if (isLocked)
641+
{
642+
state.setProperty(Identifiers::lockPasswordHash, appState.getPasswordHash(), nullptr);
643+
}
644+
645+
return state;
646+
}
647+
648+
bool AudioEngine::prepareToLoadState(const juce::ValueTree& newState)
649+
{
650+
if (!vocalProcessor.prepareToLoadState(newState)) return false;
651+
if (!musicProcessor.prepareToLoadState(newState)) return false;
652+
if (!masterProcessor.prepareToLoadState(newState)) return false;
653+
for (int i = 0; i < 4; ++i)
654+
{
655+
if (auto* p = getFxProcessorForVocal(i)) { if (!p->prepareToLoadState(newState)) return false; }
656+
if (auto* p = getFxProcessorForMusic(i)) { if (!p->prepareToLoadState(newState)) return false; }
657+
}
658+
return true;
659+
}
660+
661+
void AudioEngine::commitStateLoad()
662+
{
663+
vocalProcessor.commitStateLoad();
664+
musicProcessor.commitStateLoad();
665+
masterProcessor.commitStateLoad();
666+
for (int i = 0; i < 4; ++i)
667+
{
668+
if (auto* p = getFxProcessorForVocal(i)) p->commitStateLoad();
669+
if (auto* p = getFxProcessorForMusic(i)) p->commitStateLoad();
670+
}
671+
}
672+
673+
bool AudioEngine::tryHotSwapState(const juce::ValueTree& newState)
674+
{
675+
if (!vocalProcessor.tryHotSwapState(newState)) return false;
676+
if (!musicProcessor.tryHotSwapState(newState)) return false;
677+
if (!masterProcessor.tryHotSwapState(newState)) return false;
678+
for (int i = 0; i < 4; ++i)
679+
{
680+
if (auto* p = getFxProcessorForVocal(i)) { if (!p->tryHotSwapState(newState)) return false; }
681+
if (auto* p = getFxProcessorForMusic(i)) { if (!p->tryHotSwapState(newState)) return false; }
682+
}
683+
return true;
675684
}

0 commit comments

Comments
 (0)