diff --git a/LICENSE b/LICENSE index 2ef4cca..47f8531 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2025, TEAMuP-dev +Copyright (c) 2026, TEAMuP-dev Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/README.md b/README.md index b8cd460..29f10cd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -[![HARP Repo](https://github-readme-stats.vercel.app/api/pin/?username=TEAMuP-dev&repo=HARP)](https://github.com/TEAMuP-dev/HARP) +[![HARP](https://gh-card.dev/repos/TEAMuP-dev/HARP.svg)](https://github.com/TEAMuP-dev/HARP) -PyHARP is a **companion package** for [HARP](https://github.com/TEAMuP-dev/HARP), an application which enables the seamless integration of machine learning models into Digital Audio Workstations (DAWs). This repository provides a lightweight wrapper to embed **arbitrary Python code** for audio processing into [Gradio](https://www.gradio.app) endpoints accessible through HARP. In this way, HARP supports offline remote processing with algorithms or models that may be too resource-hungry to run on common hardware. HARP can be run as a standalone or from within DAWs that support external sample editors (_e.g._, [REAPER](https://www.reaper.fm), [Logic Pro X](https://www.apple.com/logic-pro/), or [Ableton Live](https://www.ableton.com/en/live/)). Please see [our website](https://harp-plugin.netlify.app/content/supported_os.html) for more information and instructions on how to install and run HARP with various operating systems and DAWs. +PyHARP is a **companion package** for [HARP](https://github.com/TEAMuP-dev/HARP), an application which enables the seamless integration of machine learning models into Digital Audio Workstations (DAWs). This repository provides a lightweight wrapper to embed **arbitrary Python code** for audio processing into [Gradio](https://www.gradio.app) endpoints accessible through HARP. In this way, HARP supports offline remote processing with algorithms or models that may be too resource-hungry to run on common hardware. HARP can be run as a standalone or from within DAWs that support external sample editors (_e.g._, [REAPER](https://www.reaper.fm), [Logic Pro X](https://www.apple.com/logic-pro/), or [Ableton Live](https://www.ableton.com/en/live/)). Please see [our website](https://harp3.netlify.app/content/supported_os.html) for more information and instructions on how to install and run HARP with various operating systems and DAWs. ## Table of Contents * **[Usage](#usage)** @@ -31,7 +31,7 @@ Note that PyHARP depends on [Gradio](https://www.gradio.app/). We recommend inst # PyHARP Apps ## Examples -We provide several examples of how to create a PyHARP app under the `examples/` directory. You can also find a list of models already deployed as PyHARP apps on [our website](https://harp-plugin.netlify.app/content/usage/models.html). +We provide several examples of how to create a PyHARP app under the `examples/` directory. You can also find a list of models already deployed as PyHARP apps on [our website](https://harp3.netlify.app/content/usage/models.html). In order to run an app, you will need to install its corresponding dependencies, including `gradio` and `pyharp`. For example, to install the dependences for our [pitch shifter](https://github.com/TEAMuP-dev/pyharp/tree/main/examples/pitch_shifter) example: @@ -221,7 +221,7 @@ with gr.Blocks() as demo: Note that by default PyHARP uses the [symusic](https://github.com/Yikai-Liao/symusic) package to load and save MIDI, but any standard method will work. ## Output Labels -In order to display output labels in HARP, you must define an output JSON component and return our custom `LabelList` object in `process_fn`: +In order to display output labels in HARP, you must define an output [JSON](https://www.gradio.app/docs/gradio/json) component and return our custom `LabelList` object in `process_fn`: ```python from pyharp import LabelList, AudioLabel, MidiLabel, OutputLabel, ... @@ -398,4 +398,4 @@ For more information, please refer to the offical document from Hugging Face abo ## Accessing Within HARP PyHARP apps deployed to HuggingFace will begin running at `https://huggingface.co/spaces//`. The shorthand `/` can also be used within HARP to reference the endpoint. The two deployment methods above produce identical UIs and functionality. -PyHARP apps can be accessed from within HARP through the local or forwarded URL corresponding to their active Gradio endpoints ([see above](#examples)), or the URL corresponding to their dedicated hosting service ([see above](#hosting-endpoints-huggingface-spaces))), if applicable. +PyHARP apps can be accessed from within HARP through the local or forwarded URL corresponding to their active Gradio endpoints ([see above](#examples)), or the URL corresponding to their dedicated hosting service ([see above](#hosting-endpoints-huggingface-spaces)), if applicable. diff --git a/examples/ui_tester/app.py b/examples/ui_tester/app.py index 04c1896..482c313 100644 --- a/examples/ui_tester/app.py +++ b/examples/ui_tester/app.py @@ -60,6 +60,7 @@ def download_file(url): # Define the process function def process_fn( input_audio_path, # unused + generic_file_path, slider_1_time_sleep, slider_2, slider_3, @@ -70,9 +71,8 @@ def process_fn( checkbox_3, text_control ) -> Tuple[str, str, LabelList]: - # Paths to files to use for output - audio_url = f"https://github.com/TEAMuP-dev/HARP/blob/main/resources/media/test.wav" + audio_url = "https://github.com/TEAMuP-dev/HARP/blob/main/resources/media/test.wav" midi_url = "https://github.com/TEAMuP-dev/HARP/blob/main/resources/media/test.mid" # Download audio and MIDI file @@ -94,6 +94,12 @@ def process_fn( output_audio_path = save_audio(audio) output_midi_path = save_midi(midi) + if generic_file_path is None: + generic_file_path = get_default_path(".txt") + + with open(generic_file_path, "w") as f: + f.write("generic file output") + # Create an empty label list output_labels = LabelList() @@ -197,7 +203,7 @@ def process_fn( # Delay return for chosen amount of time time.sleep(int(slider_1_time_sleep)) - return output_audio_path, output_midi_path, output_labels + return output_audio_path, output_midi_path, output_labels, generic_file_path # Build Gradio endpoint @@ -208,6 +214,13 @@ def process_fn( label="Optional AudioInp") .harp_required(False) .set_info('This is an optional input track that has no effect on the output.'), + gr.File( + type="filepath", + label="Generic Config File", + file_types=[".txt", ".csv", ".json", ".nam"] + ) + .set_info("Select a generic file input. HARP should show this as a GUI file picker, not an input track.") + .harp_required(False), gr.Slider( minimum=0, maximum=100, @@ -269,12 +282,17 @@ def process_fn( output_components = [ gr.Audio(type="filepath", label="Output Audio") - .set_info("The selected audio file."), + .set_info("Audio file."), gr.File(type="filepath", label="Output Midi", file_types=[".mid", ".midi"]) - .set_info("The fixed MIDI file."), + .set_info("MIDI file."), gr.JSON(label="Output Labels"), + gr.File( + type="filepath", + label="Output File" + ) + .set_info("Generic file."), ] # Build a HARP-compatible endpoint @@ -285,5 +303,4 @@ def process_fn( process_fn=process_fn, ) - demo.queue().launch(share=True, show_error=False, pwa=True) diff --git a/pyharp/core.py b/pyharp/core.py index e5950c9..8a05967 100644 --- a/pyharp/core.py +++ b/pyharp/core.py @@ -32,6 +32,12 @@ class HarpMidiTrack(HarpComponent): required: bool type: str = "midi_track" +@dataclass +class HarpFileComponent(HarpComponent): + required: bool + file_types: List[str] + type: str = "generic_file" + @dataclass class HarpSlider(HarpComponent): minimum: float @@ -115,15 +121,23 @@ def get_harp_component(gr_cmp: Component) -> HarpComponent: info=gr_cmp.info, required=gr_cmp.is_harp_required ) - elif isinstance(gr_cmp, gr.File) \ - and('.mid' in gr_cmp.file_types or '.midi' in gr_cmp.file_types): + elif isinstance(gr_cmp, gr.File): assert gr_cmp.type == "filepath", \ f"File input must be of type filepath, not {gr_cmp.type}" - harp_cmp = HarpMidiTrack( - label=gr_cmp.label, - info=gr_cmp.info, - required=gr_cmp.is_harp_required - ) + + if gr_cmp.file_types is not None and ('.mid' in gr_cmp.file_types or '.midi' in gr_cmp.file_types): + harp_cmp = HarpMidiTrack( + label=gr_cmp.label, + info=gr_cmp.info, + required=gr_cmp.is_harp_required + ) + else: + harp_cmp = HarpFileComponent( + label=gr_cmp.label, + info=gr_cmp.info, + required=gr_cmp.is_harp_required, + file_types=gr_cmp.file_types if gr_cmp.file_types is not None else [] + ) elif isinstance(gr_cmp, gr.Slider): harp_cmp = HarpSlider( minimum=gr_cmp.minimum, diff --git a/setup.py b/setup.py index 0970f92..4a65a95 100644 --- a/setup.py +++ b/setup.py @@ -2,9 +2,9 @@ setup( name='pyharp', - version='0.3.0', + version='0.3.1', url='https://github.com/TEAMuP-dev/pyharp', - author='Frank Cwitkowitz, Christodoulos Benetatos, Hugo Flores GarcĂ­a, Patrick O\'Reilly, Nathan Pruyne, and Aldo Aguilar', + author='TEAMuP', author_email='fcwitkow@ur.rochester.edu', description='', packages=find_packages(),