@@ -105,16 +105,49 @@ def evaluate_track(track_name, track_path, test_model, mus_db):
105105 track_duration_minutes = get_track_duration (track_path )
106106 logger .info (f"Track duration: { track_duration_minutes :.2f} minutes" )
107107
108+ # Initialize variables to track processing time
109+ processing_time = 0
110+ seconds_per_minute = 0
111+
112+ # Create a basic result structure that will be returned even if evaluation fails
113+ basic_model_results = {"track_name" : track_name , "scores" : {}}
114+
108115 # Check if evaluation results already exist in combined file
109116 museval_results = load_combined_results ()
110117 if test_model in museval_results and track_name in museval_results [test_model ]:
111118 logger .info ("Found existing evaluation results in combined file..." )
112119 track_data = museval_results [test_model ][track_name ]
113120 scores = museval .TrackStore (track_name )
114121 scores .scores = track_data
122+
123+ # Try to extract existing speed metrics if available
124+ try :
125+ if isinstance (track_data , dict ) and "targets" in track_data :
126+ for target in track_data ["targets" ]:
127+ if "metrics" in target and "seconds_per_minute_m3" in target ["metrics" ]:
128+ basic_model_results ["scores" ]["seconds_per_minute_m3" ] = target ["metrics" ]["seconds_per_minute_m3" ]
129+ break
130+ except Exception :
131+ pass # Ignore errors in extracting existing speed metrics
115132 else :
116- # Expanded stem mapping to include "no-stem" outputs
117- stem_mapping = {"Vocals" : "vocals" , "Instrumental" : "instrumental" , "Drums" : "drums" , "Bass" : "bass" , "Other" : "other" , "No Drums" : "nodrums" , "No Bass" : "nobass" , "No Other" : "noother" }
133+ # Expanded stem mapping to include "no-stem" outputs and custom stem formats
134+ stem_mapping = {
135+ # Standard stems
136+ "Vocals" : "vocals" ,
137+ "Instrumental" : "instrumental" ,
138+ "Drums" : "drums" ,
139+ "Bass" : "bass" ,
140+ "Other" : "other" ,
141+ # No-stem variants
142+ "No Drums" : "nodrums" ,
143+ "No Bass" : "nobass" ,
144+ "No Other" : "noother" ,
145+ # Custom stem formats (with hyphens)
146+ "Drum-Bass" : "drumbass" ,
147+ "No Drum-Bass" : "nodrumbass" ,
148+ "Vocals-Other" : "vocalsother" ,
149+ "No Vocals-Other" : "novocalsother" ,
150+ }
118151
119152 # Create a temporary directory for separation files
120153 with tempfile .TemporaryDirectory () as temp_dir :
@@ -135,26 +168,41 @@ def evaluate_track(track_name, track_path, test_model, mus_db):
135168 logger .info (f"Separation completed in { processing_time :.2f} seconds" )
136169 logger .info (f"Processing speed: { seconds_per_minute :.2f} seconds per minute of audio" )
137170
138- # Check which stems were actually created and pair them appropriately
139- available_stems = {}
140- stem_pairs = {"drums" : "nodrums" , "bass" : "nobass" , "other" : "noother" , "vocals" : "instrumental" }
171+ # Always add the speed metric to our basic results
172+ basic_model_results ["scores" ]["seconds_per_minute_m3" ] = round (seconds_per_minute , 1 )
141173
142- for main_stem , no_stem in stem_pairs .items ():
143- # Construct full file paths for both the isolated stem and its complement
144- main_path = os .path .join (temp_dir , f"{ main_stem } .wav" )
145- no_stem_path = os .path .join (temp_dir , f"{ no_stem } .wav" )
174+ # Check which stems were actually created
175+ wav_files = [f for f in os .listdir (temp_dir ) if f .endswith (".wav" )]
176+ logger .info (f"Found WAV files: { wav_files } " )
146177
147- # Only process this pair if both files exist
148- if os .path .exists (main_path ) and os .path .exists (no_stem_path ):
149- # Add the main stem with its path to available_stems
150- available_stems [main_stem ] = main_path # This is already using the correct musdb name
178+ # Determine if this is a standard vocal/instrumental model that can be evaluated with museval
179+ standard_model = False
180+ if len (wav_files ) == 2 :
181+ # Check if one of the files is named vocals.wav or instrumental.wav
182+ if "vocals.wav" in wav_files and "instrumental.wav" in wav_files :
183+ standard_model = True
184+ logger .info ("Detected standard vocals/instrumental model, will run museval evaluation" )
151185
152- # For the complement stem, always use "accompaniment" as that's what museval expects
153- available_stems ["accompaniment" ] = no_stem_path
186+ # If not a standard model, skip museval evaluation and just return speed metrics
187+ if not standard_model :
188+ logger .info (f"Non-standard stem configuration detected for model { test_model } , skipping museval evaluation" )
154189
155- if not available_stems :
156- logger .info (f"No evaluatable stems found for model { test_model } , skipping evaluation" )
157- return None , None
190+ # Store the speed metric in the combined results
191+ if test_model not in museval_results :
192+ museval_results [test_model ] = {}
193+
194+ # Create a minimal structure for the speed metric
195+ minimal_results = {"targets" : [{"name" : "speed_metrics_only" , "metrics" : {"seconds_per_minute_m3" : round (seconds_per_minute , 1 )}}]}
196+
197+ museval_results [test_model ][track_name ] = minimal_results
198+ save_combined_results (museval_results )
199+
200+ return None , basic_model_results
201+
202+ # For standard models, proceed with museval evaluation
203+ available_stems = {}
204+ available_stems ["vocals" ] = os .path .join (temp_dir , "vocals.wav" )
205+ available_stems ["accompaniment" ] = os .path .join (temp_dir , "instrumental.wav" )
158206
159207 # Get track from MUSDB
160208 track = next ((t for t in mus_db if t .name == track_name ), None )
@@ -171,39 +219,64 @@ def evaluate_track(track_name, track_path, test_model, mus_db):
171219
172220 # Evaluate using museval
173221 logger .info (f"Evaluating stems: { list (estimates .keys ())} " )
174- scores = museval .eval_mus_track (track , estimates , output_dir = temp_dir , mode = "v4" )
175-
176- # Update the combined results file with the new evaluation
177- if test_model not in museval_results :
178- museval_results [test_model ] = {}
179- museval_results [test_model ][track_name ] = scores .scores
180- save_combined_results (museval_results )
181-
182- # Calculate aggregate scores for available stems
183- results_store = museval .EvalStore ()
184- results_store .add_track (scores .df )
185- methods = museval .MethodStore ()
186- methods .add_evalstore (results_store , name = test_model )
187- agg_scores = methods .agg_frames_tracks_scores ()
188-
189- # Return the aggregate scores in a structured format with 6 significant figures
190- model_results = {"track_name" : track_name , "scores" : {}}
191-
192- for stem in ["vocals" , "drums" , "bass" , "other" , "accompaniment" ]:
193- try :
194- stem_scores = {metric : float (f"{ agg_scores .loc [(test_model , stem , metric )]:.6g} " ) for metric in ["SDR" , "SIR" , "SAR" , "ISR" ]}
195- # Rename 'accompaniment' to 'instrumental' in the output
196- output_stem = "instrumental" if stem == "accompaniment" else stem
197- model_results ["scores" ][output_stem ] = stem_scores
198- except KeyError :
199- continue
222+ try :
223+ scores = museval .eval_mus_track (track , estimates , output_dir = temp_dir , mode = "v4" )
224+
225+ # Add the speed metric to the scores
226+ if not hasattr (scores , "speed_metric_added" ):
227+ for target in scores .scores ["targets" ]:
228+ if "metrics" not in target :
229+ target ["metrics" ] = {}
230+ target ["metrics" ]["seconds_per_minute_m3" ] = round (seconds_per_minute , 1 )
231+ scores .speed_metric_added = True
232+
233+ # Update the combined results file with the new evaluation
234+ if test_model not in museval_results :
235+ museval_results [test_model ] = {}
236+ museval_results [test_model ][track_name ] = scores .scores
237+ save_combined_results (museval_results )
238+ except Exception as e :
239+ logger .error (f"Error during museval evaluation: { str (e )} " )
240+ logger .exception ("Evaluation exception details:" )
241+ # Return basic results with just the speed metric
242+ return None , basic_model_results
200243
201- # Add the seconds_per_minute_m3 metric if it was calculated
202- if "processing_time" in locals () and track_duration_minutes > 0 :
203- seconds_per_minute = processing_time / track_duration_minutes
204- model_results ["scores" ]["seconds_per_minute_m3" ] = round (seconds_per_minute , 1 )
244+ try :
245+ # Only process museval results if we have them
246+ if "scores" in locals () and scores is not None :
247+ # Calculate aggregate scores for available stems
248+ results_store = museval .EvalStore ()
249+ results_store .add_track (scores .df )
250+ methods = museval .MethodStore ()
251+ methods .add_evalstore (results_store , name = test_model )
252+ agg_scores = methods .agg_frames_tracks_scores ()
253+
254+ # Return the aggregate scores in a structured format with 6 significant figures
255+ model_results = {"track_name" : track_name , "scores" : {}}
256+
257+ for stem in ["vocals" , "drums" , "bass" , "other" , "accompaniment" ]:
258+ try :
259+ stem_scores = {metric : float (f"{ agg_scores .loc [(test_model , stem , metric )]:.6g} " ) for metric in ["SDR" , "SIR" , "SAR" , "ISR" ]}
260+ # Rename 'accompaniment' to 'instrumental' in the output
261+ output_stem = "instrumental" if stem == "accompaniment" else stem
262+ model_results ["scores" ][output_stem ] = stem_scores
263+ except KeyError :
264+ continue
265+
266+ # Add the seconds_per_minute_m3 metric if it was calculated
267+ if processing_time > 0 and track_duration_minutes > 0 :
268+ model_results ["scores" ]["seconds_per_minute_m3" ] = round (seconds_per_minute , 1 )
269+
270+ return scores , model_results if model_results ["scores" ] else basic_model_results
271+ else :
272+ # If we don't have scores, just return the basic results with speed metrics
273+ return None , basic_model_results
205274
206- return scores , model_results if model_results ["scores" ] else None
275+ except Exception as e :
276+ logger .error (f"Error processing evaluation results: { str (e )} " )
277+ logger .exception ("Results processing exception details:" )
278+ # Return basic results with just the speed metric
279+ return None , basic_model_results
207280
208281
209282def convert_decimal_to_float (obj ):
0 commit comments