diff --git a/nodes/flex/feature_extractors_midi.py b/nodes/flex/feature_extractors_midi.py index b065939..6fd40ac 100644 --- a/nodes/flex/feature_extractors_midi.py +++ b/nodes/flex/feature_extractors_midi.py @@ -9,13 +9,19 @@ from .feature_extractors import FeatureExtractorBase class MIDILoadAndExtract(FeatureExtractorBase): + @classmethod + def feature_type(cls) -> type[MIDIFeature]: + return MIDIFeature + + + @classmethod def INPUT_TYPES(cls): return { "required": { + **super().INPUT_TYPES()["required"], "midi_file": (folder_paths.get_filename_list("midi_files"),), "track_selection": (["all"],), - "attribute": (MIDIFeature.get_attribute_names(), {"default": "Note On/Off"}), "frame_rate": ("FLOAT", {"default": 30, "min": 0.1, "max": 120, "step": 0.1}), "video_frames": ("IMAGE",), "chord_only": ("BOOLEAN", {"default": False}), @@ -27,7 +33,7 @@ def INPUT_TYPES(cls): FUNCTION = "process_midi" CATEGORY = "RyanOnTheInside/Audio" - def process_midi(self, midi_file, track_selection, notes, attribute, frame_rate, video_frames, chord_only=False): + def process_midi(self, midi_file, track_selection, notes, extraction_method, frame_rate, video_frames, chord_only=False): try: midi_path = folder_paths.get_full_path("midi_files", midi_file) if not midi_path or not os.path.exists(midi_path): @@ -39,7 +45,7 @@ def process_midi(self, midi_file, track_selection, notes, attribute, frame_rate, feature_pipe = FeaturePipe(frame_rate, video_frames) # Convert friendly attribute name to internal attribute name - internal_attribute = MIDIFeature.get_attribute_value(attribute) + internal_attribute = MIDIFeature.get_attribute_value(extraction_method) feature = MIDIFeature(f"midi_{internal_attribute}", midi_data, internal_attribute, feature_pipe.frame_rate, feature_pipe.frame_count, @@ -79,9 +85,14 @@ def analyze_midi(cls, midi_path): } @classmethod - def VALIDATE_INPUTS(cls, midi_file, track_selection, notes, attribute, frame_rate, video_frames): - if not folder_paths.exists_and_is_file(midi_file): - return "MIDI file not found: {}".format(midi_file) + def VALIDATE_INPUTS(cls, midi_file, track_selection, notes, extraction_method, frame_rate, video_frames): + midi_path = folder_paths.get_full_path("midi_files", midi_file) + if not midi_path or not os.path.isfile(midi_path): + return f"MIDI file not found: {midi_file}" + + # Check if the file has a .mid or .midi extension + if not midi_file.lower().endswith(('.mid', '.midi')): + return f"Invalid file type. Expected .mid or .midi file, got: {midi_file}" if notes != "all": try: diff --git a/nodes/flex/midi_feature.py b/nodes/flex/midi_feature.py index a808089..0081a1e 100644 --- a/nodes/flex/midi_feature.py +++ b/nodes/flex/midi_feature.py @@ -21,7 +21,13 @@ class MIDIFeature(BaseFeature): "Sustain (CC64)": "cc64" } + @classmethod + def get_extraction_methods(cls): + """Return a list of parameter names that can be modulated.""" + return list(cls.ATTRIBUTE_MAP.keys()) + def __init__(self, name, midi_data, attribute, frame_rate, frame_count, notes=None, chord_only=False): + super().__init__(name, "midi", frame_rate, frame_count) self.midi_data = midi_data self.attribute = attribute self.notes = set(notes) if notes is not None else set() @@ -33,7 +39,7 @@ def __init__(self, name, midi_data, attribute, frame_rate, frame_count, notes=No self.pitchbend_data = [] self.aftertouch_data = [] self.poly_pressure_data = [] - super().__init__(name, "midi", frame_rate, frame_count) + def extract(self): try: diff --git a/pyproject.toml b/pyproject.toml index 8645992..b534677 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui_ryanonyheinside" description = "Custom nodes introducing particle simulations, optical flow, audio manipulation & reactivity, and temporal masks" -version = "1.8.3" +version = "1.8.5" license = {file = "LICENSE"} dependencies = ["opencv-python==4.10.0","scipy","torchaudio", "pillow","librosa","pymunk==6.8.1","matplotlib","openunmix","mido"] diff --git a/readme.md b/readme.md index 84d5afb..22c397d 100644 --- a/readme.md +++ b/readme.md @@ -33,6 +33,7 @@ *Examples showcasing various effects using particle emitters, vortices, and other node features* ##### 🆕 Recent Updates +-9/27/24 - MIDI feature bug fix -9/25/24 - **FlexImageContrast**: Adds contrast and brightness to images, with an option to preserve luminosity. -9/15/24 - **alot** Depth From Shape, Audio Pitch Feature, Pitch Range filters, new MIDI keybord for Pitch Range specification, Image from audio, mask from audio, Improved depth chamber, wave propagation, emanating rings, and a lot more - 9/8/24 - **Area Feature**: Adds area as a driving reactivity feature!