@@ -823,114 +823,9 @@ function applyMasteringSettings(s) {
if ( sig === _lastMasteringSig ) return ;
_lastMasteringSig = sig ;
const now = audioCtx . currentTime ;
/ / C l a m p e v e r y p a r a m e t e r s o a s t a l e / i n c o m p l e t e s e t t i n g s o b j e c t c a n n e v e r p u s h
/ / N a N o r a n e x t r e m e v a l u e i n t o t h e b i q u a d f i l t e r s — t h a t p u t s t h e m a s t e r
/ / c h a i n i n t o a b a d s t a t e a n d s i l e n c e s A L L a u d i o ( t h e " m ấ t s o u n d f o n t " s y m p t o m ) .
const clamp = ( v , lo , hi ) => {
const n = Number ( v ) ;
if ( ! isFinite ( n ) ) return 0 ; / / m i s s i n g / N a N → n e u t r a l , n e v e r a f i l t e r - b r e a k i n g v a l u e
return Math . min ( hi , Math . max ( lo , n ) ) ;
} ;
/ / 1 . E Q S e t t i n g s
/ / c a n c e l S c h e d u l e d V a l u e s + a s l o w e r t i m e c o n s t a n t k e e p s r a p i d s l i d e r d r a g s
/ / f r o m p i l i n g u p a u t o m a t i o n e v e n t s o n t h e b i q u a d f i l t e r s ( t h e t r i g g e r f o r
/ / C h r o m i u m ' s " B i q u a d F i l t e r N o d e : s t a t e i s b a d " ) .
/ / ⚠ ️ G u a r d N a N : s e t V a l u e A t T i m e ( N a N ) t r ê n b i q u a d → C h r o m i u m " B i q u a d F i l t e r N o d e :
/ / s t a t e i s b a d " + c â m . F i e l d s e t t i n g s u n d e f i n e d / N a N → m ặ c đ ị n h 0 .
const _g = ( v , lo , hi ) => ( typeof v === 'number' && isFinite ( v ) ) ? clamp ( v , lo , hi ) : 0 ;
masterBus . eqLowFilter . gain . cancelScheduledValues ( now ) ;
masterBus . eqLowFilter . gain . setValueAtTime ( s . eqActive ? _g ( s . eqLowGain , - 24 , 24 ) : 0 , now ) ;
masterBus . eqMid1Filter . gain . cancelScheduledValues ( now ) ;
masterBus . eqMid1Filter . gain . setValueAtTime ( s . eqActive ? _g ( s . eqMid1Gain , - 24 , 24 ) : 0 , now ) ;
masterBus . eqMid2Filter . gain . cancelScheduledValues ( now ) ;
masterBus . eqMid2Filter . gain . setValueAtTime ( s . eqActive ? _g ( s . eqMid2Gain , - 24 , 24 ) : 0 , now ) ;
masterBus . eqHighFilter . gain . cancelScheduledValues ( now ) ;
masterBus . eqHighFilter . gain . setValueAtTime ( s . eqActive ? _g ( s . eqHighGain , - 24 , 24 ) : 0 , now ) ;
/ / 2 . I m a g e r S e t t i n g s ( M i d / S i d e w i d t h p e r b a n d — i m a g e r _ s p e c . m d )
/ / W i d t h % s e m a n t i c s p e r t h e g u i d e : 0 % = M O N O ( S × 0 ) , 1 0 0 % = o r i g i n a l
/ / ( S × 1 ) , 2 0 0 % = d o u b l e w i d t h ( S × 2 ) . T h e L / R c r o s s f e e d g a i n s b e l o w a r e
/ / e x a c t l y e q u i v a l e n t t o M / S s c a l i n g : L ' = g 1 · L + g 2 · R , R ' = g 1 · R + g 2 · L w i t h
/ / g 1 = ( w + 1 0 0 ) / 2 0 0 , g 2 = ( 1 0 0 − w ) / 2 0 0 → M i d ( L + R ) / 2 u n t o u c h e d , S i d e ( L − R ) / 2
/ / s c a l e d b y w / 1 0 0 . B a n d s : 1 = 2 0 - 1 0 0 H z , 2 = 1 0 0 H z - 1 k H z , 3 = 1 k - 6 k H z , 4 = 6 k - 2 0 k H z .
const updateImagerBand = ( w , active , gainLL , gainRL , gainLR , gainRR ) => {
const widthVal = ( s . imagerActive && active ) ? clamp ( w , 0 , 200 ) : 100 ;
const g1 = ( widthVal + 100 ) / 200 ;
const g2 = ( 100 - widthVal ) / 200 ;
gainLL . gain . setTargetAtTime ( g1 , now , 0.01 ) ;
gainRR . gain . setTargetAtTime ( g1 , now , 0.01 ) ;
gainRL . gain . setTargetAtTime ( g2 , now , 0.01 ) ;
gainLR . gain . setTargetAtTime ( g2 , now , 0.01 ) ;
} ;
updateImagerBand ( s . w1 , true , masterBus . gainLL1 , masterBus . gainRL1 , masterBus . gainLR1 , masterBus . gainRR1 ) ;
updateImagerBand ( s . w2 , true , masterBus . gainLL2 , masterBus . gainRL2 , masterBus . gainLR2 , masterBus . gainRR2 ) ;
updateImagerBand ( s . w3 , true , masterBus . gainLL3 , masterBus . gainRL3 , masterBus . gainLR3 , masterBus . gainRR3 ) ;
updateImagerBand ( s . w4 , true , masterBus . gainLL4 , masterBus . gainRL4 , masterBus . gainLR4 , masterBus . gainRR4 ) ;
/ / 3 . M a x i m i z e r S e t t i n g s
const boostLinear = ( s . maximizerActive ) ? Math . pow ( 10 , clamp ( s . maxGain , - 60 , 30 ) / 20 ) : 1.0 ;
masterBus . maximizerBoostGain . gain . setTargetAtTime ( boostLinear , now , 0.01 ) ;
/ / S o f t C l i p p e r ( i d e n t i t y p a s s t h r o u g h w h e n o f f — n e v e r n u l l c u r v e ) .
/ / U n i t y - g a i n c u r v e : b o g a i n a n c u a a t a n ( s l o p e ~ 2 . 1 x c u c o n g + 6 d B l e n
/ / m a x G a i n - > t i n h i e u > ~ - 1 2 d B F S b i h a r d - c l i p m e o " b e " ) .
if ( s . maximizerActive && s . maxSoftClip > 0 ) {
masterBus . maximizerSoftClipper . curve = makeSoftClipCurve ( s . maxSoftClip ) ;
} else {
masterBus . maximizerSoftClipper . curve = new Float32Array ( [ - 1 , 1 ] ) ;
}
/ / U p w a r d C o m p r e s s o r
const upwardGainLinear = ( s . maximizerActive && s . maxUpward > 0 ) ? ( Math . pow ( 10 , clamp ( s . maxUpward , 0 , 30 ) / 20 ) - 1.0 ) : 0.0 ;
masterBus . upwardGain . gain . setTargetAtTime ( upwardGainLinear , now , 0.01 ) ;
/ / L i m i t e r T h r e s h o l d ( W a v e S h a p e r c e i l i n g — _ s e t C e i l i n g r e b u i l d c u r v e )
const ceilingVal = s . maximizerActive ? clamp ( s . ceiling , - 60 , 0 ) : - 0.1 ;
if ( masterBus . maximizerCompressor . _setCeiling ) masterBus . maximizerCompressor . _setCeiling ( ceilingVal ) ;
/ / 4 . B u s C o m p r e s s o r m o d u l e ( m a s t e r i n g _ e x p a n d . m d § I I . 2 )
if ( masterBus . compNode ) {
const compOn = ! ! s . compActive ;
masterBus . compNode . threshold . setTargetAtTime ( compOn ? clamp ( s . compThreshold , - 60 , 0 ) : 0 , now , 0.02 ) ;
masterBus . compNode . ratio . setTargetAtTime ( compOn ? clamp ( s . compRatio , 1 , 20 ) : 1 , now , 0.02 ) ;
masterBus . compMakeup . gain . setTargetAtTime ( compOn ? Math . pow ( 10 , clamp ( s . compMakeup , 0 , 12 ) / 20 ) : 1.0 , now , 0.02 ) ;
}
/ / 5 . B r i c k w a l l L i m i t e r m o d u l e ( W a v e S h a p e r t a n h — t h r e s h o l d = m ứ c c l i p ; O F F = i d e n t i t y )
if ( masterBus . limNode ) {
const limOn = ! ! s . limActive ;
if ( limOn ) {
if ( masterBus . limNode . _setThreshold ) masterBus . limNode . _setThreshold ( clamp ( s . limThreshold , - 24 , 0 ) ) ;
} else {
try { masterBus . limNode . curve = new Float32Array ( [ - 1 , 1 ] ) ; } catch ( e ) { }
}
}
/ / 6 . H a r m o n i c E x c i t e r m o d u l e ( d r y / w e t m i x ; d r y s t a y s 1 . 0 f o r s u b t l e w a r m t h )
if ( masterBus . excWet ) {
const excOn = ! ! s . excActive ;
const wetAmt = excOn ? clamp ( s . excDrive , 0 , 100 ) / 100 : 0 ;
masterBus . excWet . gain . setTargetAtTime ( wetAmt * 0.6 , now , 0.02 ) ;
masterBus . excDry . gain . setTargetAtTime ( 1.0 , now , 0.02 ) ;
}
/ / 7 . M a s t e r R e b a l a n c e m o d u l e ( M / S g a i n s v i a L / R c r o s s f e e d )
/ / L ' = a · L + b · R , R ' = b · L + a · R w i t h a = ( m i d + s i d e ) / 2 , b = ( m i d − s i d e ) / 2
if ( masterBus . gLLr ) {
const rebOn = ! ! s . rebalActive ;
const midLin = rebOn ? Math . pow ( 10 , clamp ( s . rebalMid , - 24 , 24 ) / 20 ) : 1.0 ;
const sideLin = rebOn ? Math . pow ( 10 , clamp ( s . rebalSide , - 24 , 24 ) / 20 ) : 1.0 ;
const a = ( midLin + sideLin ) / 2 ;
const b = ( midLin - sideLin ) / 2 ;
masterBus . gLLr . gain . setTargetAtTime ( a , now , 0.01 ) ;
masterBus . gRRr . gain . setTargetAtTime ( a , now , 0.01 ) ;
masterBus . gRLr . gain . setTargetAtTime ( b , now , 0.01 ) ;
masterBus . gLRr . gain . setTargetAtTime ( b , now , 0.01 ) ;
}
/ / P h a s e 4 . 5 : m a s t e r c h a i n D S P c h ạ y t r o n g f x _ v s t _ b r i d g e — W e b A u d i o c h ỉ
/ / I / O . P a r a m đ ổ i → đ ẩ y T O À N c h a i n v ề e n g i n e q u a S E T _ P A R A M .
if ( fxRt && fxRt . ws && fxRt . ws . readyState === 1 ) { try { fxRtPushAllParams ( ) ; } catch ( e ) { } }
}
function initMasterBus ( ctx ) {
@@ -1255,19 +1150,6 @@ let _lastMasteringActive = null;
let _lastMasteringSig = null ;
let _lastChainSig = null ;
/ / M o d u l e i n p u t / o u t p u t b o u n d a r y n o d e s f o r d y n a m i c c h a i n r e - r o u t i n g
/ / ( m a s t e r i n g _ e x p a n d . m d § I I . 3 — r e b u i l d A u d i o G r a p h ) . E a c h m o d u l e ' s I N T E R N A L
/ / w i r i n g i s f i x e d ; o n l y t h e b o u n d a r i e s g e t r e - c o n n e c t e d p e r c h a i n o r d e r .
const MASTER _MODULE _IO = {
eq : { input : 'eqLowFilter' , output : 'eqHighFilter' } ,
imager : { input : 'imagerInput' , output : 'imagerOutput' } ,
maximizer : { input : 'maximizerBoostGain' , output : 'maximizerCompressor' } ,
compressor : { input : 'compInput' , output : 'compOutput' } ,
limiter : { input : 'limInput' , output : 'limOutput' } ,
exciter : { input : 'excInput' , output : 'excOutput' } ,
rebalance : { input : 'rebalInput' , output : 'rebalOutput' }
} ;
function chainSignature ( chainArray ) {
return ( chainArray || [ ] ) . map ( m => ( m . type || '' ) + ( m . active ? '1' : '0' ) ) . join ( ',' ) ;
}
@@ -1671,13 +1553,52 @@ const fxRt = { active: false, sessionId: null, ws: null, node: null, inQueue: []
/ / r e o r d e r V S T ) → r e s t a r t s e s s i o n v ớ i c h a i n m ớ i ( f x R t S t a r t e a r l y - r e t u r n k h i
/ / a c t i v e , n ế u k h ô n g s o c h a i n K e y t h ì V S T m ớ i i n s e r t k h ô n g b a o g i ờ đ ư ợ c á p ) .
function fxRtChainKey ( chain ) {
return ( chain || [ ] ) . map ( s => ( s . path || '' ) + '#' + ( ( s . preset _b64 || '' ) . slice ( 0 , 16 ) ) ) . join ( '|' ) ;
return ( chain || [ ] ) . map ( s => ( s . type || '' ) + ':' + ( s . type === 'vst3' ? ( s . path || '' ) + '#' + ( ( s . preset _b64 || '' ) . slice ( 0 , 16 ) ) : ( s . type || '' ) ) ) . join ( '|' ) ;
}
function fxRtChain ( ) {
const ms = window . currentMasteringSettings ;
if ( ! ms || ! ms . masterConnected || ms . isBypassed ) return [ ] ;
const chain = activeVstChain ( ( ms && ms . vstFxChain ) || [ ] ) ;
return ( chain . length > 0 && ! ( ms && ms . vstBypass ) ) ? chain : [ ] ;
/ / C h a i n h ợ p n h ấ t ( b u i l t i n + v s t 3 t h e o U I o r d e r ) — m ọ i s l o t a c t i v e g ử i b r i d g e .
return ( ms . chain || [ ] ) . filter ( s => s && s . active !== false && ( s . type !== 'vst3' || ( ! ms . vstBypass && s . path && ! s . bypass ) ) ) ;
}
/ / P a r a m m a s t e r c h a i n e n t r y → C + + B u i l t i n F x k e y ( m i r r o r P y t h o n _ B U I L T I N _ P A R A M _ M A P ) .
const FX _RT _PARAM _MAP = {
eq : [ [ 'g1' , 'eqLowGain' ] , [ 'g2' , 'eqMid1Gain' ] , [ 'g3' , 'eqMid2Gain' ] , [ 'g4' , 'eqHighGain' ] ] ,
compressor : [ [ 'threshold' , 'compThreshold' ] , [ 'ratio' , 'compRatio' ] , [ 'makeup' , 'compMakeup' ] ] ,
limiter : [ [ 'ceiling' , 'limThreshold' ] ] ,
exciter : [ [ 'drive' , 'excDrive' ] ] ,
rebalance : [ [ 'mid' , 'rebalMid' ] , [ 'side' , 'rebalSide' ] ] ,
imager : [ [ 'w1' , 'w1' ] , [ 'w2' , 'w2' ] , [ 'w3' , 'w3' ] , [ 'w4' , 'w4' ] ] ,
maximizer : [ [ 'boost_db' , 'maxGain' ] , [ 'soft_clip' , 'maxSoftClip' ] , [ 'upward' , 'maxUpward' ] , [ 'ceiling_db' , 'ceiling' ] ]
} ;
function fxRtEntryParams ( entry , ms ) {
const out = [ ] ;
if ( ! entry || entry . type === 'vst3' ) return out ;
const src = ms || ( window . currentMasteringSettings || { } ) ;
const push = ( k , v ) => { const n = Number ( v ) ; if ( isFinite ( n ) ) out . push ( { key : k , value : n } ) ; } ;
const map = FX _RT _PARAM _MAP [ entry . type ] || [ ] ;
map . forEach ( ( [ cppKey , jsKey ] ) => {
if ( entry . params && entry . params [ cppKey ] !== undefined ) push ( cppKey , entry . params [ cppKey ] ) ;
else if ( entry . params && entry . params [ jsKey ] !== undefined ) push ( jsKey , entry . params [ jsKey ] ) ;
else if ( src [ jsKey ] !== undefined ) push ( cppKey , src [ jsKey ] ) ;
} ) ;
if ( entry . type === 'eqpro' ) {
if ( entry . params && entry . params . amount !== undefined ) push ( 'amount' , entry . params . amount ) ;
const bands = ( entry . params && entry . params . bands ) || [ ] ;
bands . forEach ( ( b , i ) => {
[ 'freq' , 'gain' , 'q' ] . forEach ( k => { if ( b && b [ k ] !== undefined ) push ( 'band' + i + '_' + k , b [ k ] ) ; } ) ;
} ) ;
}
return out ;
}
function fxRtPushAllParams ( ) {
if ( ! fxRt || ! fxRt . ws || fxRt . ws . readyState !== 1 ) return ;
const chain = fxRtChain ( ) ;
chain . forEach ( ( s , i ) => {
fxRtEntryParams ( s , null ) . forEach ( p => {
try { fxRt . ws . send ( JSON . stringify ( { cmd : 'set_param' , slot : i , key : p . key , value : p . value } ) ) ; } catch ( e ) { }
} ) ;
} ) ;
}
async function fxRtStart ( ) {
const chain = fxRtChain ( ) ;
@@ -1693,7 +1614,9 @@ async function fxRtStart() {
if ( ! chain . length ) return ;
const ctx = getAudioContext ( ) ;
if ( ! ctx || ! ctx . audioWorklet || ! masterBus || ! masterBus . output || ! masterBus . analyser ) return ;
const chainPayload = chain . map ( s => ( { type : 'vst3' , path : s . path , name : s . name || s . path , preset _b64 : s . preset _b64 || '' , bypass : false } ) ) ;
const chainPayload = chain . map ( s => s . type === 'vst3'
? { type : 'vst3' , path : s . path , name : s . name || s . path , preset _b64 : s . preset _b64 || '' , bypass : false }
: { type : 'builtin' , id : s . type , params : ( s . params && Object . keys ( s . params ) . length ? s . params : { } ) , bypass : false } ) ;
let st ;
try {
st = await window . SonicAPI . fxRealtimeStart ( { fx _chain : chainPayload , sample _rate : ctx . sampleRate || 44100 } ) ;
@@ -1714,7 +1637,7 @@ async function fxRtStart() {
const ws = new WebSocket ( wsUrl ) ;
fxRt . ws = ws ;
ws . binaryType = 'arraybuffer' ;
ws . onopen = ( ) => { fxRt . active = true ; } ;
ws . onopen = ( ) => { fxRt . active = true ; try { fxRtPushAllParams ( ) ; } catch ( e ) { } } ;
ws . onmessage = ( ev ) => {
if ( ! ( ev . data instanceof ArrayBuffer ) || ! fxRt . node ) return ;
const f = new Float32Array ( ev . data ) ;
@@ -1728,7 +1651,12 @@ async function fxRtStart() {
ws . onerror = ( ) => { try { ws . close ( ) ; } catch ( e ) { } } ;
fxRt . node . port . onmessage = ( ev ) => {
const d = ev . data ;
if ( ! d || d . type !== 'in' || ! fxRt . ws ) return ;
if ( ! d || ! fxRt . ws ) return ;
if ( d . type === 'set_param' ) {
try { fxRt . ws . send ( JSON . stringify ( { cmd : 'set_param' , slot : d . slot , key : d . key , value : d . value } ) ) ; } catch ( e ) { }
return ;
}
if ( d . type !== 'in' ) return ;
if ( fxRt . inQueue . length > 64 ) fxRt . inQueue . shift ( ) ;
fxRt . inQueue . push ( d . data ) ;
} ;
@@ -1743,16 +1671,12 @@ async function fxRtStart() {
const d = fxRt . inQueue . shift ( ) ;
try { ws . send ( d ) ; } catch ( e ) { fxRt . inQueue . unshift ( d ) ; }
} , blockMs ) ;
try { masterBus . output . disconnect ( masterBus . analyser ) ; } catch ( e ) { }
/ / V U m e t e r + p e a k g r a p h ( l e f t A n a l y s e r / r i g h t A n a l y s e r q u a s p l i t t e r ) đ o â m
/ / S A U V S T r e a l t i m e : n g ắ t s p l i t t e r k h ỏ i o u t p u t , n ố i v à o f x R t . n o d e đ ể m e t e r
/ / h i ể n t h ị a u d i o d a t a đ ã q u a c h u ỗ i V S T ( u s e r r e q u i r e m e n t — p e a k / V U r e a l t i m e ) .
if ( masterBus . splitter ) {
try { masterBus . output . disconnect ( masterBus . splitter ) ; } catch ( e ) { }
}
masterBus . output . connect ( fxRt . node ) ;
fxRt . node . connect ( masterBus . analyser ) ;
if ( masterBus . splitter ) fxRt . node . connect ( masterBus . splitter ) ;
/ / P h a s e 4 . 2 : f a d e r S A U w o r k l e t — w o r k l e t c h è n g i ữ a o u t p u t A n a l y s e r ( c h a i n
/ / o u t p u t ) v à o u t p u t ( f a d e r / d e s t i n a t i o n ) . o u t p u t → a n a l y s e r / s p l i t t e r g i ữ
/ / n g u y ê n ( m e t e r đ o â m S A U f a d e r ) .
try { masterBus . outputAnalyser . disconnect ( masterBus . output ) ; } catch ( e ) { }
masterBus . outputAnalyser . connect ( fxRt . node ) ;
fxRt . node . connect ( masterBus . output ) ;
fxRt . chainKey = ck ;
} catch ( e ) {
console . warn ( '[FX RT] setup fail:' , e ) ;
@@ -1766,12 +1690,9 @@ function fxRtStop(silent) {
try {
if ( fxRt . node ) {
fxRt . node . disconnect ( ) ;
if ( masterBus && masterBus . output && masterBus . analyser ) {
try { masterBus . output . connect ( masterBus . analyser ) ; } catch ( e ) { }
/ / K h ô i p h ụ c V U m e t e r s p l i t t e r v ề o u t p u t ( p r e - V S T ) k h i t ắ t r e a l t i m e .
if ( masterBus . splitter ) {
try { masterBus . output . connect ( masterBus . splitter ) ; } catch ( e ) { }
}
if ( masterBus && masterBus . outputAnalyser && masterBus . output ) {
try { masterBus . outputAnalyser . disconnect ( masterBus . output ) ; } catch ( e ) { }
try { masterBus . outputAnalyser . connect ( masterBus . output ) ; } catch ( e ) { }
}
fxRt . node . port . onmessage = null ;
fxRt . node = null ;
@@ -1814,8 +1735,15 @@ function fxRtGuiPushShm() {
}
function fxRtTrackChain ( track ) {
if ( ! track ) return [ ] ;
const chain = activeVstChain ( track . vstFxChain || [ ] ) ;
return ( chain . length > 0 && ! track . vstFxBypass ) ? chain : [ ] ;
let chain = ( track . fxChain || [ ] ) . filter ( s => s && s . active !== false && ( s . type !== 'vst3' || ( ! track . vstFxBypass && s . path && ! s . bypass ) ) ) ;
/ / L e g a c y : p r o j e c t c ũ c h ỉ c ó v s t F x C h a i n r i ê n g — a p p e n d s a u b u i l t i n .
if ( ! chain . some ( s => s && s . type === 'vst3' ) && ! track . vstFxBypass ) {
chain = chain . concat ( activeVstChain ( track . vstFxChain || [ ] ) ) ;
}
return chain ;
}
function fxRtTrackChainKey ( chain ) {
return ( chain || [ ] ) . map ( s => ( s . type || '' ) + ':' + ( s . type === 'vst3' ? ( s . path || '' ) + '#' + ( ( s . preset _b64 || '' ) . slice ( 0 , 16 ) ) : ( s . type || '' ) ) ) . join ( '|' ) ;
}
function fxRtTrackPaths ( track ) {
const paths = [ ] ;
@@ -1838,65 +1766,16 @@ const TRACK_EQ_PRESETS = {
/ / b e t w e e n i n p u t A n a l y s e r ( c h a i n i n p u t ) a n d o u t p u t A n a l y s e r ( c h a i n o u t p u t ) .
function rebuildMasteringGraph ( activate , chainArray ) {
if ( ! masterBus ) return ;
const active = ! ! ( activate ) ;
try {
/ / 1 . D i s c o n n e c t a l l m o d u l e b o u n d a r y o u t p u t s ( b r e a k s s t a t i c + p r e v i o u s d y n a m i c l i n k s )
/ / P h a s e 4 . 2 : m a s t e r c h a i n x ử l ý T O À N b ộ t r o n g f x _ v s t _ b r i d g e — W e b A u d i o
/ / m a s t e r l à p a s s - t h r o u g h ( i n p u t A n a l y s e r → o u t p u t A n a l y s e r ) .
masterBus . inputAnalyser . disconnect ( ) ;
masterBus . eqHighFilter . disconnect ( ) ;
masterBus . imagerOutput . disconnect ( ) ;
masterBus . maximizerCompressor . disconnect ( ) ;
masterBus . compOutput . disconnect ( ) ;
masterBus . limOutput . disconnect ( ) ;
masterBus . excOutput . disconnect ( ) ;
masterBus . rebalOutput . disconnect ( ) ;
if ( ! active ) {
/ / 2 a . N o m a s t e r i n g → s t r a i g h t c h a i n : i n p u t A n a l y s e r → o u t p u t A n a l y s e r
masterBus . inputAnalyser . connect ( masterBus . outputAnalyser ) ;
masterBus . masteringActive = false ;
return ;
}
/ / 2 b . F i l t e r a c t i v e m o d u l e s
const activeMods = ( chainArray || [ ] ) . filter ( m => m && m . active ) ;
if ( activeMods . length === 0 ) {
masterBus . inputAnalyser . connect ( masterBus . outputAnalyser ) ;
masterBus . masteringActive = true ;
return ;
}
/ / 3 . W i r e i n s e r i e s : I n p u t → m o d [ 0 ] . i n p u t → m o d [ 0 ] . o u t p u t → m o d [ 1 ] . i n p u t → … → O u t p u t
let prev = masterBus . inputAnalyser ;
/ / D y n a m i c m o d u l e s ( e q p r o ) : r e b u i l t o n e v e r y g r a p h r e b u i l d ; i n s t a n c e s k e p t
/ / i n m a s t e r B u s . e q P r o I n s t a n c e s s o t h e M a s t e r i n g p a n e l c a n p a t c h t h e m l i v e .
const eqProStore = masterBus . eqProInstances = masterBus . eqProInstances || { } ;
Object . keys ( eqProStore ) . forEach ( k => { try { eqProStore [ k ] . destroy && eqProStore [ k ] . destroy ( ) ; } catch ( e ) { } } ) ;
Object . keys ( eqProStore ) . forEach ( k => delete eqProStore [ k ] ) ;
activeMods . forEach ( mod => {
if ( mod . type === 'vst3' ) {
/ / V S T F X c h ạ y N G O À I ( f x _ v s t _ b r i d g e ) — k h ô n g c ó n o d e W e b A u d i o t r o n g
/ / m a s t e r c h a i n : p a s s - t h r o u g h , p r e v g i ữ n g u y ê n .
return ;
}
if ( mod . type === 'eqpro' ) {
const m = createEqProModule ( getAudioContext ( ) , mod . params || { } ) ;
eqProStore [ mod . id ] = m ;
prev . connect ( m . input ) ;
prev = m . output ;
} else {
const io = MASTER _MODULE _IO [ mod . type ] || MASTER _MODULE _IO . eq ;
const inNode = masterBus [ io . input ] ;
if ( inNode ) prev . connect ( inNode ) ;
prev = masterBus [ io . output ] || prev ;
}
} ) ;
prev . connect ( masterBus . outputAnalyser ) ;
masterBus . masteringActive = true ;
masterBus . inputAnalyser . connect ( masterBus . outputAnalyser ) ;
masterBus . masteringActive = ! ! ( activate ) ;
} catch ( e ) {
console . warn ( 'rebuildMasteringGraph error:' , e ) ;
}
}
function toggleMasteringOnMaster ( activate , isBypassed ) {
if ( ! masterBus ) return ;
@@ -11184,8 +11063,19 @@ const serializeTracksList = (tracksList, secondsPerBar) => {
soundfont _program : t . soundfont _program !== undefined ? t . soundfont _program : ( t . synth _engine ? t . synth _engine . soundfont _program : null ) ,
synth _engine : t . synth _engine || undefined ,
midi _channel : t . midiChannel !== undefined ? t . midiChannel : null ,
fx _chain : ( t . fxChain || [ ] ) . map ( m => typeof m === 'string' ? { type : m , active : true } : { type : m . type , active : m . active !== false } ) ,
vst _fx _chain : ( t . vstFxChain || [ ] ) . map ( s => ( { type : s . type || 'v st3' , path : s . path || ' ', name : s . name || '' , preset _b64 : _capturedPresetByPath [ s . path ] || s . preset _b64 || '' , bypass : ! ! s . bypass } ) ) ,
fx _chain : ( t . fxChain || [ ] ) . map ( m => {
if ( typeof m === 'string ' ) return { type : m , active : true } ;
const o = { type : m . type , active : m . active !== false } ;
if ( m . type === 'vst3' ) {
o . path = m . path || '' ; o . name = m . name || '' ; o . preset _b64 = _capturedPresetByPath [ m . path ] || m . preset _b64 || '' ; o . bypass = ! ! m . bypass ;
} else if ( m . params && Object . keys ( m . params ) . length ) o . params = m . params ;
return o ;
} ) ,
vst _fx _chain : ( ( ) => {
const chainVst = ( t . fxChain || [ ] ) . filter ( m => m && m . type === 'vst3' ) ;
const vstSrc = chainVst . length ? chainVst : ( t . vstFxChain || [ ] ) ;
return vstSrc . map ( s => ( { type : 'vst3' , path : s . path || '' , name : s . name || '' , preset _b64 : _capturedPresetByPath [ s . path ] || s . preset _b64 || '' , bypass : ! ! s . bypass } ) ) ;
} ) ( ) ,
vst _fx _bypass : ! ! t . vstFxBypass ,
vst _gui _open _path : ( t . id === _persistFxGuiTrackId && _persistFxGuiOpen && _persistFxGuiOpen . path ) ? _persistFxGuiOpen . path : ( t . vstGuiOpenPath || null ) ,
vst _gui _open _name : ( t . id === _persistFxGuiTrackId && _persistFxGuiOpen && _persistFxGuiOpen . name ) ? _persistFxGuiOpen . name : ( t . vstGuiOpenName || null ) ,
@@ -11288,8 +11178,17 @@ const deserializeTracksList = (schemaTracks, secondsPerBar, sectionStore, visite
soundfont _bank : t . soundfont _bank !== null ? t . soundfont _bank : undefined ,
soundfont _program : t . soundfont _program !== null ? t . soundfont _program : undefined ,
synth _engine : t . synth _engine || undefined ,
fxChain : ( t . fx _chain || [ ] ) . map ( m => typeof m === 'string' ? { type : m , active : true } : { type : m . type || m , active : m . active !== false } ) ,
vstFxChain : ( t . vst _fx _chain || [ ] ) . map ( s => ( { type : s . type || 'vst3' , path : s . path || '' , name : s . name || '' , preset _b64 : s . preset _b64 || '' , bypass : ! ! s . bypass } ) ) ,
fxChain : ( ( ) => {
const parsed = ( t . fx _chain || [ ] ) . map ( m => typeof m === 'string' ? { type : m , active : true } : ( { type : m . type || m , active : m . active !== false , ... ( m . params ? { params : m . params } : { } ) , ... ( m . type === 'vst3' ? { path : m . path || '' , name : m . name || '' , preset _b64 : m . preset _b64 || '' , bypass : ! ! m . bypass } : { } ) } ) ) ;
const seen = new Set ( parsed . filter ( m => m . type === 'vst3' ) . map ( m => m . path ) ) ;
( t . vst _fx _chain || [ ] ) . forEach ( s => { if ( s && s . path && ! seen . has ( s . path ) ) { parsed . push ( { type : 'vst3' , path : s . path || '' , name : s . name || '' , preset _b64 : s . preset _b64 || '' , bypass : ! ! s . bypass , active : true } ) ; seen . add ( s . path ) ; } } ) ;
return parsed ;
} ) ( ) ,
vstFxChain : ( ( ) => {
const fromChain = ( t . fx _chain || [ ] ) . filter ( m => m && m . type === 'vst3' ) ;
const src = fromChain . length ? fromChain : ( t . vst _fx _chain || [ ] ) ;
return src . map ( s => ( { type : s . type || 'vst3' , path : s . path || '' , name : s . name || '' , preset _b64 : s . preset _b64 || '' , bypass : ! ! s . bypass } ) ) ;
} ) ( ) ,
vstFxBypass : t . vst _fx _bypass !== undefined ? ! ! t . vst _fx _bypass : false ,
vstGuiOpenPath : t . vst _gui _open _path || null ,
vstGuiOpenName : t . vst _gui _open _name || null ,
@@ -11509,13 +11408,26 @@ const deserializeProjectFromSchema = (schemaObj) => {
tracks : restoredTracks ,
sessionTabs : restoredSessionTabs ,
subTabs : restoredSubTabs ,
masteringSettings : {
... _migratedMastering ,
vstFxChain : _masterFx . map ( s => ( { type : s . type || 'vst3' , path : s . path || '' , name : s . name || '' , preset _b64 : s . preset _b64 || '' , bypass : ! ! s . bypass } ) ) ,
vstBypass : ! ! _masterBus . bypass ,
gui _open _path : ( schemaObj . mastering _settings && schemaObj . mastering _settings . gui _open _path ) ? schemaObj . mastering _settings . gui _open _path : null ,
gui _open _name : ( schemaObj . mastering _settings && schemaObj . mastering _settings . gui _open _name ) ? schemaObj . mastering _settings . gui _open _name : null
} ,
masteringSettings : ( ( ) => {
const migrated = { ... _migratedMastering } ;
const _masterVstSources = [ ] ;
( migrated . vstFxChain || [ ] ) . forEach ( s => { if ( s && s . path && ! _masterVstSources . some ( x => x . path === s . path ) ) _masterVstSources . push ( s ) ; } ) ;
_masterFx . forEach ( s => { if ( s && s . path && ! _masterVstSources . some ( x => x . path === s . path ) ) _masterVstSources . push ( s ) ; } ) ;
const _finalChain = [ ... ( migrated . chain || [ ] ) ] ;
_masterVstSources . forEach ( s => {
if ( ! _finalChain . some ( m => m . type === 'vst3' && m . path === s . path ) ) {
_finalChain . push ( { id : 'mod_vst_' + Date . now ( ) + '_' + Math . random ( ) . toString ( 36 ) . slice ( 2 , 8 ) , type : 'vst3' , path : s . path , name : s . name || '' , preset _b64 : s . preset _b64 || '' , bypass : ! ! s . bypass , active : true } ) ;
}
} ) ;
return {
... migrated ,
chain : _finalChain ,
vstFxChain : _finalChain . filter ( m => m . type === 'vst3' ) . map ( s => ( { type : 'vst3' , path : s . path || '' , name : s . name || '' , preset _b64 : s . preset _b64 || '' , bypass : ! ! s . bypass } ) ) ,
vstBypass : ! ! _masterBus . bypass ,
gui _open _path : ( schemaObj . mastering _settings && schemaObj . mastering _settings . gui _open _path ) ? schemaObj . mastering _settings . gui _open _path : null ,
gui _open _name : ( schemaObj . mastering _settings && schemaObj . mastering _settings . gui _open _name ) ? schemaObj . mastering _settings . gui _open _name : null
} ;
} ) ( ) ,
masterVolume : ( typeof _masterBus . volume _db === 'number' && isFinite ( _masterBus . volume _db ) ) ? _masterBus . volume _db : 0 ,
zoom : schemaObj . metadata && schemaObj . metadata . zoom ? parseFloat ( schemaObj . metadata . zoom ) : 1.0
} ;
@@ -12300,14 +12212,16 @@ const FXRackModal = ({ track, onUpdateTrack, onClose }) => {
const chain = track . fxChain || [ ] ;
/ / ─ ─ V S T F X c h a i n ( n a t i v e _ b r i d g e - - r e n d e r - f x , á p d ụ n g k h i E x p o r t / O f f l i n e r e n d e r ) ─ ─
const vstChain = ( track . vstFxChain || [ ] ) ;
/ / ─ ─ V S T F X c h a i n ( n a t i v e _ b r i d g e - - r e n d e r - f x ) — t h a m c h i ế u t ừ u n i f i e d c h a i n ─ ─
const vstChain = chain . filter ( m => m && m . type === 'vst3' ) ;
/ / U n i f i e d c h a i n s t r i p : b u i l t i n D S P + V S T F X c h u n g 1 c h a i n ( k é o đ ổ i t h ứ t ự g i ữ a c ả 2 l o ạ i ) .
const unifiedChain = [
... chain . map ( ( m , i ) => ( { kind : 'builtin' , m , i } ) ) ,
... vstChain . map ( ( s , i ) => ( { kind : 'vst' , s , i } ) ) ,
] ;
const setVstChain = ( next ) => { if ( onUpdateTrack ) onUpdateTrack ( track . id , { vstFxChain : next , vstFxBypass : vstBypass } ) ; if ( window . _ _rebuildTrackFxGraph ) window . _ _rebuildTrackFxGraph ( track . id , { vstFxChain : next , vstFxBypass : vstBypass } ) ; } ;
const unifiedChain = chain . map ( ( m , i ) => ( { kind : m && m . type === 'vst3' ? 'vst' : 'builtin' , m , i } ) ) ;
const setVstChain = ( next ) => {
const builtin = chain . filter ( m => ! m || m . type !== 'vst3 ' ) ;
const merged = [ ... builtin , ... next . map ( s => ( { type : 'vst3' , path : s . path || '' , name : s . name || '' , preset _b64 : s . preset _b64 || '' , bypass : ! ! s . bypass } ) ) ] ;
if ( onUpdateTrack ) onUpdateTrack ( track . id , { fxChain : merged , vstFxChain : next , vstFxBypass : vstBypass } ) ;
if ( window . _ _rebuildTrackFxGraph ) window . _ _rebuildTrackFxGraph ( track . id , { fxChain : merged , vstFxChain : next , vstFxBypass : vstBypass } ) ;
} ;
/ / E m b e d d e d V S T G U I s t a t e ( P L A N _ M A S T E R B U S _ V S T _ G U I _ E M B E D . m d P h a s e 3 )
const [ fxGuiOpen , setFxGuiOpenState ] = React . useState ( _persistFxGuiOpen ) ; / / { p a t h , u r l } | n u l l
const setFxGuiOpen = ( v ) => { if ( _persistFxGuiOpen && _persistFxGuiOpen . url ) _fxGuiUnregisterSource ( _persistFxGuiOpen . url ) ; _persistFxGuiOpen = v ; _persistFxGuiTrackId = v ? ( track && track . id ) : null ; if ( v && v . url ) _fxGuiRegisterSource ( v . url , track && track . id ) ; setFxGuiOpenState ( v ) ; } ;
@@ -12332,7 +12246,7 @@ const FXRackModal = ({ track, onUpdateTrack, onClose }) => {
/ / R e o p e n t h e s a v e d G U I w h e n t h e r a c k i s m o u n t e d a f t e r a p r o j e c t r e s t o r e .
React . useEffect ( ( ) => {
if ( ! track || ! track . vstGuiOpenPath || fxGuiOpen ) return ;
const s = ( track . vstFxChain || [ ] ) . find ( x => x . path === track . vstGuiOpenPath ) ;
const s = ( track . fxChain || [ ] ) . find ( x => x . path === track . vstGuiOpenPath ) ;
if ( ! s || ! window . SonicAPI || ! window . SonicAPI . openFxGui ) return ;
window . SonicAPI . openFxGui ( { path : s . path , name : s . name || s . path , embed : true , shm : fxRtTrackShmName ( track && track . id ) } ) . then ( r => {
if ( r && r . embed _url ) setFxGuiOpen ( { path : s . path , url : r . embed _url } ) ;
@@ -12627,7 +12541,7 @@ const addVst = (path) => { const fx = (fxList || []).find(f => f.path === path);
draggable
onDragStart = { e => { dragChainIndexRef . current = ui ; e . dataTransfer . effectAllowed = 'move' ; } }
onDragOver = { e => { e . preventDefault ( ) ; e . dataTransfer . dropEffect = 'move' ; } }
onDrop = { e => { e . preventDefault ( ) ; const from = dragChainIndexRef . current ; if ( from !== null && from !== ui ) { const next = [ ... unifiedChain ] ; const mv = next . splice ( from , 1 ) [ 0 ] ; next . splice ( ui , 0 , mv ) ; const nb = next . filter ( x => x . kind === 'builtin' ) . map ( x => x . m ) ; const nv = next . filter ( x => x . kind === 'vst' ) . map ( x => x . s ) ; setChain ( nb ) ; setVstChain ( nv ) ; if ( mv . kind === 'vst' ) setActiveVstIdx ( nv . indexOf ( mv . s ) ) ; } dragChainIndexRef . current = null ; } }
onDrop = { e => { e . preventDefault ( ) ; const from = dragChainIndexRef . current ; if ( from !== null && from !== ui ) { const next = [ ... unifiedChain ] ; const mv = next . splice ( from , 1 ) [ 0 ] ; next . splice ( ui , 0 , mv ) ; const nb = next . filter ( x => x . kind === 'builtin' ) . map ( x => x . m ) ; const nv = next . filter ( x => x . kind === 'vst' ) . map ( x => x . m ) ; setChain ( nb ) ; setVstChain ( nv ) ; if ( mv . kind === 'vst' ) setActiveVstIdx ( nv . indexOf ( mv . m ) ) ; } dragChainIndexRef . current = null ; } }
onClick = { ( ) => { closeFxGui ( ) ; setActiveType ( m . type ) ; } }
className = { ` w-36 h-12 rounded-lg p-2 flex items-center justify-between cursor-pointer transition-all shrink-0 ${ activeType === m . type ? 'border-2 border-cyan-400 bg-slate-800' : 'bg-slate-900 border border-slate-800 hover:border-slate-600' } ` } >
< div className = "flex items-center gap-2 min-w-0" >
@@ -12645,17 +12559,17 @@ const addVst = (path) => { const fx = (fxList || []).find(f => f.path === path);
< / div >
) ;
}
const s = u . s ;
const s = u . m ;
return (
< div key = { ` v ${ u . i } ` }
draggable
onDragStart = { e => { dragChainIndexRef . current = ui ; e . dataTransfer . effectAllowed = 'move' ; } }
onDragOver = { e => { e . preventDefault ( ) ; e . dataTransfer . dropEffect = 'move' ; } }
onDrop = { e => { e . preventDefault ( ) ; const from = dragChainIndexRef . current ; if ( from !== null && from !== ui ) { const next = [ ... unifiedChain ] ; const mv = next . splice ( from , 1 ) [ 0 ] ; next . splice ( ui , 0 , mv ) ; const nb = next . filter ( x => x . kind === 'builtin' ) . map ( x => x . m ) ; const nv = next . filter ( x => x . kind === 'vst' ) . map ( x => x . s ) ; setChain ( nb ) ; setVstChain ( nv ) ; if ( mv . kind === 'vst' ) setActiveVstIdx ( nv . indexOf ( mv . s ) ) ; } dragChainIndexRef . current = null ; } }
onClick = { ( ) => { closeFxGui ( ) ; setActiveVstIdx ( u . i ) ; } }
className = { ` w-36 h-12 rounded-lg p-2 flex items-center justify-between cursor-pointer transition-all shrink-0 ${ activeVstIdx === u . i ? 'border-2 border-teal-400 bg-slate-800' : 'bg-slate-900 border border-teal-900/70 hover:border-teal-600' } ` } >
onDrop = { e => { e . preventDefault ( ) ; const from = dragChainIndexRef . current ; if ( from !== null && from !== ui ) { const next = [ ... unifiedChain ] ; const mv = next . splice ( from , 1 ) [ 0 ] ; next . splice ( ui , 0 , mv ) ; const nb = next . filter ( x => x . kind === 'builtin' ) . map ( x => x . m ) ; const nv = next . filter ( x => x . kind === 'vst' ) . map ( x => x . m ) ; setChain ( nb ) ; setVstChain ( nv ) ; if ( mv . kind === 'vst' ) setActiveVstIdx ( nv . indexOf ( mv . m ) ) ; } dragChainIndexRef . current = null ; } }
onClick = { ( ) => { closeFxGui ( ) ; setActiveVstIdx ( vstChain . indexOf ( s ) ) ; } }
className = { ` w-36 h-12 rounded-lg p-2 flex items-center justify-between cursor-pointer transition-all shrink-0 ${ activeVstIdx === vstChain . indexOf ( s ) ? 'border-2 border-teal-400 bg-slate-800' : 'bg-slate-900 border border-teal-900/70 hover:border-teal-600' } ` } >
< div className = "flex items-center gap-2 min-w-0" >
< button onClick = { e => { e . stopPropagation ( ) ; toggleVst ( u . i ) ; } } className = "w-4 h-4 rounded-full flex items-center justify-center text-[9px] font-bold shrink-0" style = { { backgroundColor : ! s . bypass ? '#2dd4bf' : '#334155' , color : ! s . bypass ? '#0f172a' : '#94a3b8' } } >
< button onClick = { e => { e . stopPropagation ( ) ; toggleVst ( vstChain . indexOf ( s ) ) ; } } className = "w-4 h-4 rounded-full flex items-center justify-center text-[9px] font-bold shrink-0" style = { { backgroundColor : ! s . bypass ? '#2dd4bf' : '#334155' , color : ! s . bypass ? '#0f172a' : '#94a3b8' } } >
< i data - lucide = "power" className = "w-2.5 h-2.5" > < / i >
< / button >
< div className = "min-w-0" >
@@ -12663,7 +12577,7 @@ const addVst = (path) => { const fx = (fxList || []).find(f => f.path === path);
< div className = "text-[9px] font-mono truncate text-teal-500/80" > { u . i + 1 } . { s . preset _b64 ? '✓ preset' : 'no preset' } < / div >
< / div >
< / div >
< button onClick = { e => { e . stopPropagation ( ) ; removeVst ( u . i ) ; } } className = "text-slate-600 hover:text-red-400 text-xs px-0.5 shrink-0" title = "Xóa slot" >
< button onClick = { e => { e . stopPropagation ( ) ; removeVst ( vstChain . indexOf ( s ) ) ; } } className = "text-slate-600 hover:text-red-400 text-xs px-0.5 shrink-0" title = "Xóa slot" >
< i data - lucide = "x" className = "w-3 h-3" > < / i >
< / button >
< / div >
@@ -12923,7 +12837,20 @@ const MasteringModal = ({ isOpen, onClose, masteringSettings, setMasteringSettin
} ) . catch ( ( ) => { } ) ;
/ / e s l i n t - d i s a b l e - n e x t - l i n e r e a c t - h o o k s / e x h a u s t i v e - d e p s
} , [ isOpen , ozState && ozState . gui _open _path ] ) ;
const setMasterVstChain = ( next ) => setOzState ( prev => ( { ... prev , vstFxChain : next } ) ) ;
const setMasterVstChain = ( next ) => setOzState ( prev => {
const chain = prev . chain || [ ] ;
const out = [ ] ;
let vi = 0 ;
const mk = ( s , i ) => ( { id : 'mod_vst_' + Date . now ( ) + '_' + i + '_' + Math . random ( ) . toString ( 36 ) . slice ( 2 , 8 ) , type : 'vst3' , path : s . path , name : s . name || '' , preset _b64 : s . preset _b64 || '' , bypass : ! ! s . bypass , active : true } ) ;
chain . forEach ( m => {
if ( m . type === 'vst3' ) {
const s = next [ vi ] ;
if ( s ) { out . push ( mk ( s , vi ) ) ; vi ++ ; }
} else out . push ( m ) ;
} ) ;
for ( ; vi < next . length ; vi ++ ) out . push ( mk ( next [ vi ] , vi ) ) ;
return { ... prev , vstFxChain : next , chain : out } ;
} ) ;
/ / L I V E m a s t e r F X x ử l ý b ở i f x R t S t a r t ( ) ( S H M l o o p q u a f x _ v s t _ b r i d g e
/ / - - r e a l t i m e - f x ) — K H Ô N G p u s h c h a i n x u ố n g n a t i v e b r i d g e ( s e t F x C h a i n ) :
/ / n ế u p u s h , M I D I / V S T i i n s t r u m e n t b ị x ử l ý 2 l ầ n ( n a t i v e b r i d g e + S H M l o o p )
@@ -13507,7 +13434,8 @@ const addMasterVst = (path) => { const fx = (masterFxList || []).find(f => f.pat
const chain = [ ... prev . chain ] ;
const moved = chain . splice ( fromIdx , 1 ) [ 0 ] ;
chain . splice ( toIdx , 0 , moved ) ;
return { ... prev , chain } ;
const vstFxChain = chain . filter ( m => m && m . type === 'vst3' ) . map ( s => ( { type : 'vst3' , path : s . path || '' , name : s . name || '' , preset _b64 : s . preset _b64 || '' , bypass : ! ! s . bypass } ) ) ;
return { ... prev , chain , vstFxChain } ;
} ) ;
} ;
const addModuleToChain = ( type ) => {
@@ -13568,21 +13496,8 @@ const addMasterVst = (path) => { const fx = (masterFxList || []).find(f => f.pat
const applyEQPreset = ( presetKey ) => {
const preset = EQ _PRESET _LIBRARY [ presetKey ] ;
if ( ! preset ) return ;
const bus = masterBus ;
if ( bus && bus . eqLowFilter ) {
const now = audioCtx ? audioCtx . currentTime : 0 ;
const filters = [ bus . eqLowFilter , bus . eqMid1Filter , bus . eqMid2Filter , bus . eqHighFilter ] ;
preset . bands . forEach ( ( bd , i ) => {
const f = filters [ i ] ;
if ( ! f ) return ;
try {
/ / s e t V a l u e A t T i m e ( k h ô n g a u t o m a t i o n ) — t r á n h " B i q u a d F i l t e r N o d e : s t a t e i s b a d "
f . frequency . setValueAtTime ( bd . freq , now ) ;
f . gain . setValueAtTime ( bd . gain , now ) ;
f . Q . setValueAtTime ( bd . q , now ) ;
} catch ( e ) { }
} ) ;
}
/ / P h a s e 4 . 5 : E Q D S P c h ạ y t r o n g b r i d g e — p r e s e t c h ỉ s y n c s t a t e ; e n g i n e
/ / n h ậ n q u a f x R t P u s h A l l P a r a m s ( ) ở a p p l y M a s t e r i n g S e t t i n g s .
/ / S y n c U I k n o b s + c a n v a s : [ e q L o w G a i n , e q M i d 1 G a i n , e q M i d 2 G a i n , e q H i g h G a i n ]
setOzState ( prev => ( {
... prev ,
@@ -13771,9 +13686,24 @@ const addMasterVst = (path) => { const fx = (masterFxList || []).find(f => f.pat
track = { null }
params = { cm . params || { } }
onChange = { ( next ) => updateChainEntryParams ( cm . id , next ) }
getModule = { ( ) => masterBus && masterBus . eqProInstances ? masterBus . eqProInstances [ cm . id ] : null }
spectrumModules = { ( ) => { const m = masterBus && masterBus . eqProInstances ? masterBus . eqProInstances [ cm . id ] : null ; return m ? [ m ] : [ ] ; } }
applyTo = { ( fn ) => { const m = masterBus && masterBus . eqProInstances ? masterBus . eqProInstances [ cm . id ] : null ; if ( m ) fn ( m ) ; } }
getModule = { ( ) => null }
spectrumModules = { ( ) => [ ] }
applyTo = { ( fn ) => {
const chain = fxRtChain ( ) ;
const slot = chain . findIndex ( m => m && m . type === 'eqpro' ) ;
if ( slot < 0 ) return ;
fn ( {
setBand : ( idx , patch ) => {
Object . keys ( patch || { } ) . forEach ( k => { if ( fxRt && fxRt . ws && fxRt . ws . readyState === 1 ) fxRt . ws . send ( JSON . stringify ( { cmd : 'set_param' , slot , key : 'band' + idx + '_' + k , value : patch [ k ] } ) ) ; } ) ;
} ,
setAmount : ( v ) => { if ( fxRt && fxRt . ws && fxRt . ws . readyState === 1 ) fxRt . ws . send ( JSON . stringify ( { cmd : 'set_param' , slot , key : 'amount' , value : v } ) ) ; } ,
syncBands : ( bands ) => {
( bands || [ ] ) . forEach ( ( b , i ) => {
[ 'freq' , 'gain' , 'q' ] . forEach ( k => { if ( fxRt && fxRt . ws && fxRt . ws . readyState === 1 ) fxRt . ws . send ( JSON . stringify ( { cmd : 'set_param' , slot , key : 'band' + i + '_' + k , value : b [ k ] } ) ) ; } ) ;
} ) ;
}
} ) ;
} }
/ > ;
} ) ( ) }
< / div >
@@ -23028,15 +22958,8 @@ const App = () => {
/ / F X P o w e r ( n ú t P W R ) : O N = m ọ i i t e m s c o n ( m i d i / s e c t i o n / a u d i o c l i p ) q u a F X
/ / R a c k P a n e l c h a i n ; O F F = b ỏ t o à n b ộ t r a c k F X ( f x C h a i n + c h o r u s / r e v e r b ) .
const fxEnabled = track . fxActive !== false ;
const chainMods = fxEnabled ? fxChain . filter ( m => m && m . active !== false ) . map ( m => {
try { return createTrackFxModule ( m . type || m , context , m . params ) ; } catch ( e ) { return null ; }
} ) . filter ( Boolean ) : [ ] ;
let fxChainTail = fxEntry ;
chainMods . forEach ( mod => {
fxChainTail . connect ( mod . input ) ;
fxChainTail = mod . output ;
} ) ;
fxChainTail . connect ( fxLegacyIn ) ;
/ / P h a s e 4 . 3 : t r a c k b u i l t i n D S P c h ạ y t r o n g b r i d g e — W e b A u d i o p a s s - t h r o u g h .
fxEntry . connect ( fxLegacyIn ) ;
if ( fxEnabled && track . fxType === 'chorus' ) {
const fxInput = context . createGain ( ) ;
fxLegacyIn . connect ( fxInput ) ;
@@ -23090,18 +23013,10 @@ const App = () => {
sfRouteGain . connect ( masterBus . input ) ;
sfDryGain . connect ( masterBus . dryInput ) ;
sfOut . connect ( sfAnalyser ) ;
const sfModsBuilt = fxEnabled ? fxChain . filter ( m => m && m . active !== false ) . map ( m => {
try { return createTrackFxModule ( m . type || m , context , m . params ) ; } catch ( e ) { return null ; }
} ) . filter ( Boolean ) : [ ] ;
sfMods = sfModsBuilt ;
let sfTail = sfEntry ;
sfMods . forEach ( mod => {
sfTail . connect ( mod . input ) ;
sfTail = mod . output ;
} ) ;
sfTail . connect ( sfOut ) ;
sfMods = [ ] ;
sfEntry . connect ( sfOut ) ;
}
node = { gainNode , pannerNode , fxStopFn , analyserNode , route , fxEntry , fxLegacyIn , scopeAnalyserL , scopeAnalyserR , sfEntry , sfOut , sfPan , sfAnalyser , sfRouteGain , sfDryGain , fxMods : chainMods , sfMods } ;
node = { gainNode , pannerNode , fxStopFn , analyserNode , route , fxEntry , fxLegacyIn , scopeAnalyserL , scopeAnalyserR , sfEntry , sfOut , sfPan , sfAnalyser , sfRouteGain , sfDryGain , fxMods : [ ] , sfMods : [ ] } ;
/ / R e a l t i m e m u t e / s o l o : a p p l y t h e t r a c k ' s c u r r e n t m u t e / s o l o / v o l u m e s t a t e t o
/ / t h e f r e s h n o d e s o i t e m s o f m u t e d / s o l o e d t r a c k s s t a r t c o r r e c t l y .
const trackList = ( activeTracksRef . current && activeTracksRef . current . length ) ? activeTracksRef . current : [ track ] ;
@@ -23132,30 +23047,13 @@ const App = () => {
if ( ! track ) return ;
if ( patch ) track = { ... track , ... patch } ;
node . fxEntry . disconnect ( ) ;
const fxEnabled = track . fxActive !== false ;
const chainMods = fxEnabled ? ( track . fxChain || [ ] ) . filter ( m => m && m . active !== false ) . map ( m => {
try { return createTrackFxModule ( m . type || m , getAudioContext ( ) , m . params ) ; } catch ( e ) { return null ; }
} ) . filter ( Boolean ) : [ ] ;
let tail = node . fxEntry ;
chainMods . forEach ( mod => {
tail . connect ( mod . input ) ;
tail = mod . output ;
} ) ;
tail . connect ( node . fxLegacyIn ) ;
node . fxMods = chainMods ;
node . fxEntry . connect ( node . fxLegacyIn ) ;
node . fxMods = [ ] ;
/ / R e b u i l d t h e i n d e p e n d e n t S F c h a i n t o o ( s a m e m o d u l e s , s e p a r a t e i n s t a n c e s )
if ( node . sfEntry && node . sfOut ) {
node . sfEntry . disconnect ( ) ;
const sfMods = fxEnabled ? ( track . fxChain || [ ] ) . filter ( m => m && m . active !== false ) . map ( m => {
try { return createTrackFxModule ( m . type || m , getAudioContext ( ) , m . params ) ; } catch ( e ) { return null ; }
} ) . filter ( Boolean ) : [ ] ;
node . sfMods = sfMods ;
let sfTail = node . sfEntry ;
sfMods . forEach ( mod => {
sfTail . connect ( mod . input ) ;
sfTail = mod . output ;
} ) ;
sfTail . connect ( node . sfOut ) ;
node . sfEntry . connect ( node . sfOut ) ;
node . sfMods = [ ] ;
}
if ( node . sfPan && getAudioContext ( ) ) {
node . sfPan . pan . setTargetAtTime ( ( track . pan ? ? 0 ) / 100 , getAudioContext ( ) . currentTime , 0.02 ) ;
@@ -23186,7 +23084,9 @@ const App = () => {
}
const ctx = getAudioContext ( ) ;
if ( ! ctx || ! ctx . audioWorklet ) return ;
const chainPayload = chain . map ( s => ( { type : 'vst3' , path : s . path , name : s . name || s . path , preset _b64 : s . preset _b64 || '' , bypass : false } ) ) ;
const chainPayload = chain . map ( s => s . type === 'vst3'
? { type : 'vst3' , path : s . path , name : s . name || s . path , preset _b64 : s . preset _b64 || '' , bypass : false }
: { type : 'builtin' , id : s . type , params : ( s . params && Object . keys ( s . params ) . length ? s . params : { } ) , bypass : false } ) ;
let st ;
try {
st = await window . SonicAPI . fxRealtimeStart ( { fx _chain : chainPayload , sample _rate : ctx . sampleRate || 44100 } ) ;
@@ -23194,7 +23094,7 @@ const App = () => {
console . warn ( '[FX RT track] start fail' , trackId , path , e ) ;
return ;
}
const entry = { sessionId : st . session _id , ws : null , node : null , ready : false , path , spliced : null , chainKey : JSON . stringify ( chainPayload ) , inQueue : [ ] , pumpTimer : null } ;
const entry = { sessionId : st . session _id , ws : null , node : null , ready : false , path , spliced : null , chainKey : fxRtTrackChainKey ( chain ) , paramsBySlot : chain . map ( s => fxRtEntryParams ( s , null ) ) , inQueue : [ ] , pumpTimer : null } ;
fxRtTracks [ key ] = entry ;
fxRtGuiPushShm ( ) ;
try {
@@ -23207,7 +23107,13 @@ const App = () => {
const ws = new WebSocket ( ` ${ wsProto } ${ u . host } ${ st . ws _url } ${ tok ? '?token=' + encodeURIComponent ( tok ) : '' } ` ) ;
entry . ws = ws ;
ws . binaryType = 'arraybuffer' ;
ws . onopen = ( ) => { entry . ready = true ; fxRtTrackSplice ( trackId , path ) ; } ;
ws . onopen = ( ) => {
entry . ready = true ;
fxRtTrackSplice ( trackId , path ) ;
try {
( entry . paramsBySlot || [ ] ) . forEach ( ( pl , slot ) => pl . forEach ( p => ws . send ( JSON . stringify ( { cmd : 'set_param' , slot , key : p . key , value : p . value } ) ) ) ) ;
} catch ( e ) { }
} ;
ws . onmessage = ( ev ) => {
if ( ! ( ev . data instanceof ArrayBuffer ) || ! entry . node ) return ;
const f = new Float32Array ( ev . data ) ;
@@ -23221,7 +23127,12 @@ const App = () => {
ws . onerror = ( ) => { try { ws . close ( ) ; } catch ( e ) { } } ;
entry . node . port . onmessage = ( ev ) => {
const d = ev . data ;
if ( ! d || d . type !== 'in' || ! entry . ws ) return ;
if ( ! d || ! entry . ws ) return ;
if ( d . type === 'set_param' ) {
try { entry . ws . send ( JSON . stringify ( { cmd : 'set_param' , slot : d . slot , key : d . key , value : d . value } ) ) ; } catch ( e ) { }
return ;
}
if ( d . type !== 'in' ) return ;
if ( entry . inQueue . length > 64 ) entry . inQueue . shift ( ) ;
entry . inQueue . push ( d . data ) ;
} ;
@@ -23251,9 +23162,7 @@ const App = () => {
const entry = fxRtTracks [ key ] ;
const node = activeTrackNodesRef . current [ trackId ] ;
if ( ! entry || ! entry . ready || ! entry . node || ! node || entry . spliced ) return ;
const tail = path === 'audio'
? ( node . fxMods && node . fxMods . length ? node . fxMods [ node . fxMods . length - 1 ] . output : node . fxEntry )
: ( node . sfMods && node . sfMods . length ? node . sfMods [ node . sfMods . length - 1 ] . output : node . sfEntry ) ;
const tail = path === 'audio' ? node . fxEntry : node . sfEntry ;
const dest = path === 'audio' ? node . fxLegacyIn : node . sfOut ;
if ( ! tail || ! dest ) return ;
try {
@@ -23285,6 +23194,7 @@ const App = () => {
const fxRtTrackReconcile = ( trackId , node , track ) => {
try {
const chain = fxRtTrackChain ( track ) ;
const chainKey = fxRtTrackChainKey ( chain ) ;
const wantPaths = chain . length ? fxRtTrackPaths ( track ) : [ ] ;
const prefix = trackId + ':' ;
Object . keys ( fxRtTracks ) . forEach ( k => {
@@ -23297,6 +23207,7 @@ const App = () => {
const key = trackId + ':' + p ;
const entry = fxRtTracks [ key ] ;
if ( ! entry ) fxRtTrackStart ( trackId , track , p ) ;
else if ( entry . chainKey !== chainKey ) { fxRtTrackStop ( trackId , p , true ) ; fxRtTrackStart ( trackId , track , p ) ; }
else if ( entry . ready && node ) {
fxRtTrackUnsplice ( trackId , p ) ; / / c h a i n m ớ i ( s a u r e b u i l d ) — s p l i c e l ạ i đ ú n g m o d s
fxRtTrackSplice ( trackId , p ) ;
@@ -23566,19 +23477,30 @@ const App = () => {
/ / U I h e l p e r : f e t c h a l i v e m o d u l e i n s t a n c e ( e . g . ' e q p r o ' ) f r o m t h e a c t i v e t r a c k
/ / n o d e s o i n t e r a c t i v e p a n e l s c a n d r i v e i t s D S P p a r a m s i n r e a l t i m e .
window . _ _getTrackFxModule = ( trackId , type ) => {
const node = activeTrackNodesRef . current [ trackId ] ;
if ( ! node ) return null ;
const found = ( node . fxMods || [ ] ) . find ( m => m && m . type === type ) ;
return found || null ;
if ( type !== 'eqpro' ) return null ;
const findSlot = ( ) => {
const list = ( activeTracksRef . current && activeTracksRef . current . length ) ? activeTracksRef . current : tracks ;
const track = list . find ( t => t . id === trackId ) ;
return track ? fxRtTrackChain ( track ) . findIndex ( m => m && m . type === 'eqpro' ) : - 1 ;
} ;
const sendAll = ( fn ) => {
const slot = findSlot ( ) ;
if ( slot < 0 ) return ;
Object . keys ( fxRtTracks ) . forEach ( k => {
if ( ! k . startsWith ( trackId + ':' ) ) return ;
const e = fxRtTracks [ k ] ;
if ( e && e . ws && e . ws . readyState === 1 ) fn ( e . ws , slot ) ;
} ) ;
} ;
return {
setBand : ( idx , patch ) => sendAll ( ( ws , slot ) => Object . keys ( patch || { } ) . forEach ( k => { try { ws . send ( JSON . stringify ( { cmd : 'set_param' , slot , key : 'band' + idx + '_' + k , value : patch [ k ] } ) ) ; } catch ( e ) { } } ) ) ,
setAmount : ( v ) => sendAll ( ( ws , slot ) => { try { ws . send ( JSON . stringify ( { cmd : 'set_param' , slot , key : 'amount' , value : v } ) ) ; } catch ( e ) { } } ) ,
syncBands : ( bands ) => sendAll ( ( ws , slot ) => ( bands || [ ] ) . forEach ( ( b , i ) => [ 'freq' , 'gain' , 'q' ] . forEach ( k => { try { ws . send ( JSON . stringify ( { cmd : 'set_param' , slot , key : 'band' + i + '_' + k , value : b [ k ] } ) ) ; } catch ( e ) { } } ) ) )
} ;
} ;
/ / S a m e f o r t h e s o u n d f o n t ( M I D I ) m o d u l e i n s t a n c e s — l e t s t h e E Q P R O p a n e l d r i v e
/ / B O T H t h e a u d i o - c l i p c h a i n a n d t h e S F c h a i n i n r e a l t i m e .
window . _ _getTrackSfFxModule = ( trackId , type ) => {
const node = activeTrackNodesRef . current [ trackId ] ;
if ( ! node ) return null ;
const found = ( node . sfMods || [ ] ) . find ( m => m && m . type === type ) ;
return found || null ;
} ;
window . _ _getTrackSfFxModule = ( trackId , type ) => window . _ _getTrackFxModule ( trackId , type ) ;
const getOrCreateSubTrackNode = ( track , subTrack , context ) => {
if ( ! track || ! subTrack ) return null ;
const subKey = track . id + '_sub_' + subTrack . id ;