@@ -17,6 +17,9 @@ public class LevelMeterService: ObservableObject {
1717 /// Published current track name for compatibility with v0.6 UI
1818 @Published public var currentTrack : String = " Master Bus "
1919
20+ /// Dedicated published property for TR1 (kick drum) to ensure VU meter updates
21+ @Published public var tr1Level = AudioLevel ( id: " TR1 " , rmsValue: 0.0 , peakRmsValue: 0.0 , trackName: " Kick " )
22+
2023 /// Compatibility properties for v0.6 UI - mapping between old channel-based and new track-based system
2124 @Published public var leftChannel = AudioLevel ( id: " LEFT " , rmsValue: 0.0 , peakRmsValue: 0.0 , trackName: " Master Bus " )
2225 @Published public var rightChannel = AudioLevel ( id: " RIGHT " , rmsValue: 0.0 , peakRmsValue: 0.0 , trackName: " Master Bus " )
@@ -36,6 +39,7 @@ public class LevelMeterService: ObservableObject {
3639 logger. info ( " LevelMeterService initialized " )
3740 // Set up default master bus audio level
3841 audioLevels [ masterBusUUID] = AudioLevel ( id: masterBusUUID, rmsValue: 0.0 , peakRmsValue: 0.0 , trackName: " Master Bus " )
42+ audioLevels [ " TR1 " ] = tr1Level // Initialize TR1 in the dictionary
3943 startPeakDecayTimer ( )
4044
4145 // Start a timer to update the v0.6 compatibility properties from the master bus data
@@ -49,7 +53,7 @@ public class LevelMeterService: ObservableObject {
4953 /// - rmsValue: The new RMS value (0.0 to 1.0).
5054 /// - peakRmsValueOverride: Optional peak value if provided directly by the source.
5155 public func updateLevel( logicTrackUUID: String , rmsValue: Float , peakRmsValueOverride: Float ? = nil ) {
52- var level = audioLevels [ logicTrackUUID] ?? AudioLevel ( id: logicTrackUUID)
56+ var level = audioLevels [ logicTrackUUID] ?? AudioLevel ( id: logicTrackUUID, rmsValue : 0.0 , peakRmsValue : 0.0 , trackName : logicTrackUUID )
5357
5458 level. rmsValue = max ( 0.0 , min ( 1.0 , rmsValue) ) // Clamp value
5559
@@ -62,6 +66,22 @@ public class LevelMeterService: ObservableObject {
6266
6367 audioLevels [ logicTrackUUID] = level
6468
69+ // CRITICAL FIX: Update the dedicated TR1 property when TR1 data arrives
70+ if logicTrackUUID == " TR1 " {
71+ tr1Level = level
72+ // Commented out to reduce console spam at 24 Hz
73+ // logger.info("Updated TR1 dedicated property: RMS \(level.rmsValue, privacy: .public), Peak \(level.peakRmsValue, privacy: .public)")
74+ }
75+
76+ // Force SwiftUI to detect the change by replacing the entire dictionary
77+ let newDict = audioLevels
78+ audioLevels = newDict
79+
80+ // Force objectWillChange to fire to ensure SwiftUI updates
81+ DispatchQueue . main. async {
82+ self . objectWillChange. send ( )
83+ }
84+
6585 // If this is the master bus, update the current track name for UI compatibility
6686 if logicTrackUUID == masterBusUUID {
6787 if let trackName = level. trackName {
@@ -72,8 +92,9 @@ public class LevelMeterService: ObservableObject {
7292 updateCompatibilityChannels ( from: level)
7393 }
7494
75- // Removed excessive debug logging - this was generating thousands of log entries per second
76- // logger.debug("Updated level for \(logicTrackUUID, privacy: .public): RMS \(level.rmsValue, privacy: .public), Peak \(level.peakRmsValue, privacy: .public)")
95+ // Commented out verbose logging to reduce console spam at 24 Hz
96+ // logger.info("Updated level for \(logicTrackUUID, privacy: .public): RMS \(level.rmsValue, privacy: .public), Peak \(level.peakRmsValue, privacy: .public)")
97+
7798 }
7899
79100 /// Updates the v0.6 compatibility channel properties from the master bus data
@@ -82,9 +103,44 @@ public class LevelMeterService: ObservableObject {
82103 rightChannel = AudioLevel ( id: " RIGHT " , rmsValue: masterLevel. rmsValue, peakRmsValue: masterLevel. peakRmsValue, trackName: masterLevel. trackName)
83104 }
84105
106+ /// Updates the master bus level with the sum of all track levels
107+ private func updateMasterBusLevel( ) {
108+ // Calculate RMS sum of all tracks (excluding master bus itself)
109+ var sumOfSquares : Float = 0.0
110+ var peakValue : Float = 0.0
111+ var trackCount = 0
112+
113+ for (uuid, level) in audioLevels {
114+ if uuid != masterBusUUID {
115+ // RMS values are summed as power (squared values)
116+ sumOfSquares += level. rmsValue * level. rmsValue
117+ peakValue = max ( peakValue, level. peakRmsValue)
118+ trackCount += 1
119+ }
120+ }
121+
122+ // Calculate combined RMS (square root of sum of squares)
123+ let combinedRMS = trackCount > 0 ? sqrt ( sumOfSquares) : 0.0
124+
125+ // Apply 10x amplification and clamp to valid range
126+ let amplifiedRMS = min ( 1.0 , combinedRMS * 10.0 )
127+ let amplifiedPeak = min ( 1.0 , peakValue * 10.0 )
128+
129+ // Update master bus level
130+ var masterLevel = audioLevels [ masterBusUUID] ?? AudioLevel ( id: masterBusUUID, rmsValue: 0.0 , peakRmsValue: 0.0 , trackName: " Master Bus " )
131+ masterLevel. rmsValue = amplifiedRMS
132+ masterLevel. peakRmsValue = max ( masterLevel. peakRmsValue, amplifiedPeak)
133+ masterLevel. lastUpdateTime = Date ( )
134+
135+ audioLevels [ masterBusUUID] = masterLevel
136+
137+ // Update compatibility channels
138+ updateCompatibilityChannels ( from: masterLevel)
139+ }
140+
85141 /// Starts a timer to update the v0.6 compatibility properties from the master bus data
86142 private func startCompatibilityTimer( ) {
87- Timer . scheduledTimer ( withTimeInterval: 1.0 / 30 .0, repeats: true ) { [ weak self] _ in
143+ Timer . scheduledTimer ( withTimeInterval: 1.0 / 24 .0, repeats: true ) { [ weak self] _ in
88144 guard let self = self else { return }
89145 Task { @MainActor in
90146 if let masterLevel = self . audioLevels [ self . masterBusUUID] {
@@ -121,6 +177,11 @@ public class LevelMeterService: ObservableObject {
121177 audioLevels [ logicTrackUUID] ? . peakRmsValue = audioLevels [ logicTrackUUID] ? . rmsValue ?? 0.0
122178 logger. info ( " Peak value reset for track: \( logicTrackUUID, privacy: . public) " )
123179
180+ // If this is TR1, also update the dedicated property
181+ if logicTrackUUID == " TR1 " , let level = audioLevels [ logicTrackUUID] {
182+ tr1Level = level
183+ }
184+
124185 // If this is the master bus, also update the compatibility channels
125186 if logicTrackUUID == masterBusUUID {
126187 leftChannel. peakValue = leftChannel. value
@@ -140,7 +201,7 @@ public class LevelMeterService: ObservableObject {
140201 /// Starts a timer to handle peak value decay.
141202 private func startPeakDecayTimer( ) {
142203 peakDecayTimer? . invalidate ( ) // Invalidate existing timer if any
143- peakDecayTimer = Timer . scheduledTimer ( withTimeInterval: 0.05 , repeats: true ) { [ weak self] _ in
204+ peakDecayTimer = Timer . scheduledTimer ( withTimeInterval: 1.0 / 24.0 , repeats: true ) { [ weak self] _ in
144205 guard let strongSelf = self else { return }
145206
146207 Task { @MainActor in
@@ -155,16 +216,14 @@ public class LevelMeterService: ObservableObject {
155216 }
156217 strongSelf. audioLevels [ uuid] = level
157218
158- // If this is the master bus, also update compatibility channels
159- if uuid == strongSelf. masterBusUUID {
160- strongSelf. leftChannel. peakValue *= strongSelf. peakDecayRate
161- strongSelf. rightChannel. peakValue *= strongSelf. peakDecayRate
162- if strongSelf. leftChannel. peakValue < 0.001 {
163- strongSelf. leftChannel. peakValue = 0.0
164- }
165- if strongSelf. rightChannel. peakValue < 0.001 {
166- strongSelf. rightChannel. peakValue = 0.0
167- }
219+ // Update TR1 dedicated property if needed
220+ if uuid == " TR1 " {
221+ strongSelf. tr1Level = level
222+ }
223+
224+ // Update master bus level after individual track decay
225+ if uuid != strongSelf. masterBusUUID {
226+ strongSelf. updateMasterBusLevel ( )
168227 }
169228 }
170229 }
0 commit comments