2
2
==============================================================================
3
3
4
4
AudioEngine.cpp
5
- (Fully Functional Send/Return FX Architecture )
5
+ (Fixed: Implemented robust two-stage preset loading orchestration )
6
6
7
7
==============================================================================
8
8
*/
9
9
10
10
#include " AudioEngine.h"
11
+ #include " ../Data/AppState.h"
11
12
#include " ../GUI/Layout/TrackComponent.h"
12
13
#include " ../GUI/Layout/MasterUtilityComponent.h"
13
14
#include " juce_audio_devices/juce_audio_devices.h"
@@ -20,18 +21,14 @@ AudioEngine::AudioEngine(juce::AudioDeviceManager& manager)
20
21
musicFxChain(Identifiers::MusicFx1State, Identifiers::MusicFx2State, Identifiers::MusicFx3State, Identifiers::MusicFx4State)
21
22
{
22
23
formatManager.registerBasicFormats ();
23
-
24
24
audioRecorder = std::make_unique<AudioRecorder>(formatManager, " " );
25
25
vocalTrackRecorder = std::make_unique<AudioRecorder>(formatManager, " Vocal" );
26
26
musicTrackRecorder = std::make_unique<AudioRecorder>(formatManager, " Music" );
27
27
rawVocalRecorder = std::make_unique<AudioRecorder>(formatManager, " Projects" );
28
28
rawMusicRecorder = std::make_unique<AudioRecorder>(formatManager, " Projects" );
29
-
30
29
soundPlayer = std::make_unique<IdolAZ::SoundPlayer>();
31
-
32
30
soundboardMixer.addInputSource (soundPlayer.get (), false );
33
31
directOutputMixer.addInputSource (&playbackSource, false );
34
-
35
32
}
36
33
37
34
AudioEngine::~AudioEngine ()
@@ -42,42 +39,31 @@ void AudioEngine::prepareAllProcessors(double sampleRate, int samplesPerBlock)
42
39
{
43
40
stableSampleRate = sampleRate;
44
41
stableBlockSize = samplesPerBlock;
45
-
46
42
currentSampleRate = sampleRate;
47
43
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." );
52
45
juce::dsp::ProcessSpec stereoSpec{ sampleRate, static_cast <uint32_t >(samplesPerBlock), 2 };
53
-
54
46
vocalProcessor.prepare (stereoSpec);
55
47
musicProcessor.prepare (stereoSpec);
56
48
masterProcessor.prepare (stereoSpec);
57
-
58
49
for (auto & fxProc : vocalFxChain.processors ) fxProc.prepare (stereoSpec);
59
50
for (auto & fxProc : musicFxChain.processors ) fxProc.prepare (stereoSpec);
60
-
61
51
soundboardMixer.prepareToPlay (samplesPerBlock, sampleRate);
62
52
directOutputMixer.prepareToPlay (samplesPerBlock, sampleRate);
63
53
playbackSource.prepareToPlay (samplesPerBlock, sampleRate);
64
54
vocalTrackSource.prepareToPlay (samplesPerBlock, sampleRate);
65
55
musicTrackSource.prepareToPlay (samplesPerBlock, sampleRate);
66
-
67
56
vocalBuffer.setSize (2 , samplesPerBlock);
68
57
musicStereoBuffer.setSize (2 , samplesPerBlock);
69
58
mixBuffer.setSize (2 , samplesPerBlock);
70
59
soundboardBuffer.setSize (2 , samplesPerBlock);
71
60
directOutputBuffer.setSize (2 , samplesPerBlock);
72
-
73
61
fxSendBuffer.setSize (2 , samplesPerBlock);
74
62
for (auto & buffer : vocalFxReturnBuffers) buffer.setSize (2 , samplesPerBlock);
75
63
for (auto & buffer : musicFxReturnBuffers) buffer.setSize (2 , samplesPerBlock);
76
-
77
64
vocalProcessor.reset ();
78
65
musicProcessor.reset ();
79
66
masterProcessor.reset ();
80
-
81
67
for (auto & fxProc : vocalFxChain.processors ) fxProc.reset ();
82
68
for (auto & fxProc : musicFxChain.processors ) fxProc.reset ();
83
69
}
@@ -86,28 +72,22 @@ void AudioEngine::prepareAllProcessors(double sampleRate, int samplesPerBlock)
86
72
void AudioEngine::audioDeviceAboutToStart (juce::AudioIODevice* device)
87
73
{
88
74
prepareAllProcessors (device->getCurrentSampleRate (), device->getCurrentBufferSizeSamples ());
89
-
90
75
if (onDeviceStarted)
91
- {
92
76
juce::MessageManager::callAsync (onDeviceStarted);
93
- }
94
77
}
95
78
96
79
void AudioEngine::audioDeviceStopped ()
97
80
{
98
81
vocalProcessor.reset ();
99
82
musicProcessor.reset ();
100
83
masterProcessor.reset ();
101
-
102
84
for (auto & fxProc : vocalFxChain.processors ) fxProc.reset ();
103
85
for (auto & fxProc : musicFxChain.processors ) fxProc.reset ();
104
-
105
86
soundboardMixer.releaseResources ();
106
87
directOutputMixer.releaseResources ();
107
88
playbackSource.releaseResources ();
108
89
vocalTrackSource.releaseResources ();
109
90
musicTrackSource.releaseResources ();
110
-
111
91
currentSampleRate = 0.0 ;
112
92
currentBlockSize = 0 ;
113
93
vocalInputChannel.store (-1 );
@@ -117,47 +97,30 @@ void AudioEngine::audioDeviceStopped()
117
97
selectedOutputRightChannel.store (-1 );
118
98
}
119
99
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)
123
101
{
124
102
juce::ignoreUnused (context);
125
-
126
- // <<< FIX: Use deviceManager to get the current device safely >>>
127
103
if (auto * currentDevice = deviceManager.getCurrentAudioDevice ())
128
104
{
129
105
if (numSamples != currentBlockSize || currentDevice->getCurrentSampleRate () != currentSampleRate)
130
- {
131
106
prepareAllProcessors (currentDevice->getCurrentSampleRate (), numSamples);
132
- }
133
107
}
134
-
135
108
juce::ScopedNoDenormals noDenormals;
136
-
137
- // --- 1. Xóa tất cả các buffer làm việc ---
138
109
vocalBuffer.clear ();
139
110
musicStereoBuffer.clear ();
140
111
mixBuffer.clear ();
141
112
soundboardBuffer.clear ();
142
113
directOutputBuffer.clear ();
143
114
for (auto & buffer : vocalFxReturnBuffers) buffer.clear ();
144
115
for (auto & buffer : musicFxReturnBuffers) buffer.clear ();
145
-
146
116
vocalPlayerBuffer.setSize (2 , numSamples);
147
117
musicPlayerBuffer.setSize (2 , numSamples);
148
118
vocalPlayerBuffer.clear ();
149
119
musicPlayerBuffer.clear ();
150
-
151
-
152
- // --- 2. Lấy tín hiệu từ các Player của Track ---
153
120
juce::AudioSourceChannelInfo vocalPlayerInfo (&vocalPlayerBuffer, 0 , numSamples);
154
121
vocalTrackSource.getNextAudioBlock (vocalPlayerInfo);
155
-
156
122
juce::AudioSourceChannelInfo musicPlayerInfo (&musicPlayerBuffer, 0 , numSamples);
157
123
musicTrackSource.getNextAudioBlock (musicPlayerInfo);
158
-
159
-
160
- // --- 3. Xử lý Track Vocal ---
161
124
const int currentVocalIn = vocalInputChannel.load ();
162
125
if (juce::isPositiveAndBelow (currentVocalIn, numInputChannels))
163
126
{
@@ -170,13 +133,9 @@ void AudioEngine::audioDeviceIOCallbackWithContext(const float* const* inputChan
170
133
vocalBuffer.addFrom (1 , 0 , vocalPlayerBuffer, 1 , 0 , numSamples);
171
134
vocalProcessor.process (vocalBuffer);
172
135
vocalTrackRecorder->processBlock (vocalBuffer, currentSampleRate);
173
-
174
-
175
- // --- 4. Xử lý Track Music ---
176
136
const int currentMusicLeftIn = musicInputLeftChannel.load ();
177
137
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))
180
139
{
181
140
juce::AudioBuffer<float > rawInput (const_cast <float **>(inputChannelData) + currentMusicLeftIn, 2 , numSamples);
182
141
musicStereoBuffer.copyFrom (0 , 0 , rawInput, 0 , 0 , numSamples);
@@ -187,14 +146,8 @@ void AudioEngine::audioDeviceIOCallbackWithContext(const float* const* inputChan
187
146
musicStereoBuffer.addFrom (1 , 0 , musicPlayerBuffer, 1 , 0 , numSamples);
188
147
musicProcessor.process (musicStereoBuffer);
189
148
musicTrackRecorder->processBlock (musicStereoBuffer, currentSampleRate);
190
-
191
-
192
- // --- 5. Lấy tín hiệu từ Soundboard ---
193
149
juce::AudioSourceChannelInfo soundboardChannelInfo (&soundboardBuffer, 0 , numSamples);
194
150
soundboardMixer.getNextAudioBlock (soundboardChannelInfo);
195
-
196
-
197
- // --- 6. Xử lý FX Sends/Returns ---
198
151
for (int i = 0 ; i < 4 ; ++i)
199
152
{
200
153
fxSendBuffer.copyFrom (0 , 0 , vocalBuffer, 0 , 0 , numSamples);
@@ -215,20 +168,13 @@ void AudioEngine::audioDeviceIOCallbackWithContext(const float* const* inputChan
215
168
musicFxReturnBuffers[i].copyFrom (1 , 0 , fxSendBuffer, 1 , 0 , numSamples);
216
169
musicFxReturnBuffers[i].applyGain (musicFxChain.processors [i].getReturnLevel ());
217
170
}
218
-
219
-
220
- // --- 7. Tổng hợp tất cả vào Kênh Master ---
221
171
mixBuffer.clear ();
222
-
223
172
mixBuffer.copyFrom (0 , 0 , vocalBuffer, 0 , 0 , numSamples);
224
173
mixBuffer.copyFrom (1 , 0 , vocalBuffer, 1 , 0 , numSamples);
225
-
226
174
mixBuffer.addFrom (0 , 0 , musicStereoBuffer, 0 , 0 , numSamples);
227
175
mixBuffer.addFrom (1 , 0 , musicStereoBuffer, 1 , 0 , numSamples);
228
-
229
176
mixBuffer.addFrom (0 , 0 , soundboardBuffer, 0 , 0 , numSamples);
230
177
mixBuffer.addFrom (1 , 0 , soundboardBuffer, 1 , 0 , numSamples);
231
-
232
178
for (int i = 0 ; i < 4 ; ++i)
233
179
{
234
180
mixBuffer.addFrom (0 , 0 , vocalFxReturnBuffers[i], 0 , 0 , numSamples);
@@ -239,22 +185,14 @@ void AudioEngine::audioDeviceIOCallbackWithContext(const float* const* inputChan
239
185
mixBuffer.addFrom (0 , 0 , musicFxReturnBuffers[i], 0 , 0 , numSamples);
240
186
mixBuffer.addFrom (1 , 0 , musicFxReturnBuffers[i], 1 , 0 , numSamples);
241
187
}
242
-
243
188
masterProcessor.process (mixBuffer);
244
189
audioRecorder->processBlock (mixBuffer, currentSampleRate);
245
-
246
-
247
- // --- 8. Lấy tín hiệu đi thẳng ra Output (Player của REC & PLAY) ---
248
190
juce::AudioSourceChannelInfo directOutputChannelInfo (&directOutputBuffer, 0 , numSamples);
249
191
directOutputMixer.getNextAudioBlock (directOutputChannelInfo);
250
-
251
-
252
- // --- 9. Gửi tín hiệu cuối cùng ra loa ---
253
192
const int currentOutputLeft = selectedOutputLeftChannel.load ();
254
193
const int currentOutputRight = selectedOutputRightChannel.load ();
255
194
for (int i = 0 ; i < numOutputChannels; ++i)
256
195
juce::FloatVectorOperations::clear (outputChannelData[i], numSamples);
257
-
258
196
if (juce::isPositiveAndBelow (currentOutputLeft, numOutputChannels))
259
197
{
260
198
juce::FloatVectorOperations::copy (outputChannelData[currentOutputLeft], mixBuffer.getReadPointer (0 ), numSamples);
@@ -672,4 +610,75 @@ bool AudioEngine::isProjectPlaybackActive() const
672
610
juce::ValueTree& AudioEngine::getProjectState ()
673
611
{
674
612
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 ;
675
684
}
0 commit comments