From 1470d0d030c27cf589c28e358ea43a921a40940a Mon Sep 17 00:00:00 2001 From: Frank Cwitkowitz Date: Sat, 25 Apr 2026 10:20:11 -0400 Subject: [PATCH 1/9] Added link to JSON component. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b8cd460..93ef735 100644 --- a/README.md +++ b/README.md @@ -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, ... From d8ef2c0ef42bbef6cfa8362dc786e672324e6fb0 Mon Sep 17 00:00:00 2001 From: derekllanes Date: Wed, 29 Apr 2026 02:59:12 -0400 Subject: [PATCH 2/9] Add generic file_track support in pyharp --- pyharp/core.py | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/pyharp/core.py b/pyharp/core.py index 8fed9f9..369ca74 100644 --- a/pyharp/core.py +++ b/pyharp/core.py @@ -32,6 +32,12 @@ class HarpMidiTrack(HarpComponent): required: bool type: str = "midi_track" +# Generic file +@dataclass +class HarpFileTrack(HarpComponent): + required: bool + type: str = "file_track" + @dataclass class HarpSlider(HarpComponent): minimum: float @@ -115,15 +121,24 @@ 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 it specifically asks for MIDI, keep the original behavior + 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: + # For .txt, .csv, or any other file types, use our new general file track + harp_cmp = HarpFileTrack( + label=gr_cmp.label, + info=gr_cmp.info, + required=gr_cmp.is_harp_required + ) elif isinstance(gr_cmp, gr.Slider): harp_cmp = HarpSlider( minimum=gr_cmp.minimum, From 80b265ea1adf3fded4e2f3963a28062215e21e0d Mon Sep 17 00:00:00 2001 From: derekllanes Date: Fri, 15 May 2026 15:22:04 -0500 Subject: [PATCH 3/9] Represent generic file inputs as file picker controls --- examples/ui_tester/app.py | 12 ++++++++++++ pyharp/core.py | 15 +++++++-------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/examples/ui_tester/app.py b/examples/ui_tester/app.py index a36a18d..518a216 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,6 +71,8 @@ def process_fn( checkbox_3, text_control ) -> Tuple[str, str, LabelList]: + + print("\n\nGENERIC FILE PATH:", generic_file_path, "\n\n") # Paths to files to use for output audio_url = f"https://github.com/TEAMuP-dev/HARP/blob/main/test/{dropdown_1}" @@ -208,6 +211,15 @@ 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."), + + gr.Slider( minimum=0, maximum=100, diff --git a/pyharp/core.py b/pyharp/core.py index 369ca74..f543113 100644 --- a/pyharp/core.py +++ b/pyharp/core.py @@ -32,11 +32,11 @@ class HarpMidiTrack(HarpComponent): required: bool type: str = "midi_track" -# Generic file @dataclass -class HarpFileTrack(HarpComponent): +class HarpFilePicker(HarpComponent): required: bool - type: str = "file_track" + file_types: List[str] + type: str = "file_picker" @dataclass class HarpSlider(HarpComponent): @@ -124,8 +124,7 @@ def get_harp_component(gr_cmp: Component) -> HarpComponent: elif isinstance(gr_cmp, gr.File): assert gr_cmp.type == "filepath", \ f"File input must be of type filepath, not {gr_cmp.type}" - - # If it specifically asks for MIDI, keep the original behavior + 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, @@ -133,11 +132,11 @@ def get_harp_component(gr_cmp: Component) -> HarpComponent: required=gr_cmp.is_harp_required ) else: - # For .txt, .csv, or any other file types, use our new general file track - harp_cmp = HarpFileTrack( + harp_cmp = HarpFilePicker( label=gr_cmp.label, info=gr_cmp.info, - required=gr_cmp.is_harp_required + 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( From 219eb2f2730ec4980eac0c14b4cfd382d585d4b1 Mon Sep 17 00:00:00 2001 From: Frank Cwitkowitz Date: Thu, 21 May 2026 12:57:56 -0400 Subject: [PATCH 4/9] Fixed links. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 93ef735..e3d1abe 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) -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: @@ -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. From cac2010b97bf225f99b21719b9b69b5bf0ef5cba Mon Sep 17 00:00:00 2001 From: Frank Cwitkowitz Date: Thu, 28 May 2026 15:55:31 -0400 Subject: [PATCH 5/9] Fixed file component and added generic file output to example. --- examples/ui_tester/app.py | 10 +++++----- pyharp/core.py | 8 +++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/examples/ui_tester/app.py b/examples/ui_tester/app.py index 518a216..0afa34b 100644 --- a/examples/ui_tester/app.py +++ b/examples/ui_tester/app.py @@ -200,7 +200,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 @@ -211,15 +211,12 @@ 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."), - - gr.Slider( minimum=0, maximum=100, @@ -287,6 +284,10 @@ def process_fn( file_types=[".mid", ".midi"]) .set_info("The fixed MIDI file."), gr.JSON(label="Output Labels"), + gr.File( + type="filepath", + label="Output File" + ) ] # Build a HARP-compatible endpoint @@ -297,5 +298,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 f543113..378699e 100644 --- a/pyharp/core.py +++ b/pyharp/core.py @@ -33,10 +33,9 @@ class HarpMidiTrack(HarpComponent): type: str = "midi_track" @dataclass -class HarpFilePicker(HarpComponent): - required: bool +class HarpFileComponent(HarpComponent): file_types: List[str] - type: str = "file_picker" + type: str = "generic_file" @dataclass class HarpSlider(HarpComponent): @@ -132,10 +131,9 @@ def get_harp_component(gr_cmp: Component) -> HarpComponent: required=gr_cmp.is_harp_required ) else: - harp_cmp = HarpFilePicker( + 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): From 1436311c4b8c89914eec429cb7d3412871144d4c Mon Sep 17 00:00:00 2001 From: Frank Cwitkowitz Date: Mon, 1 Jun 2026 11:44:39 -0400 Subject: [PATCH 6/9] Added ability to make generic file inputs optional and fixed resource links. --- examples/ui_tester/app.py | 10 ++++------ pyharp/core.py | 2 ++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/ui_tester/app.py b/examples/ui_tester/app.py index 0afa34b..07ed014 100644 --- a/examples/ui_tester/app.py +++ b/examples/ui_tester/app.py @@ -71,12 +71,9 @@ def process_fn( checkbox_3, text_control ) -> Tuple[str, str, LabelList]: - - print("\n\nGENERIC FILE PATH:", generic_file_path, "\n\n") - # Paths to files to use for output - audio_url = f"https://github.com/TEAMuP-dev/HARP/blob/main/test/{dropdown_1}" - midi_url = "https://github.com/TEAMuP-dev/HARP/blob/main/test/test.mid" + 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 local_audio_path = download_file(audio_url) @@ -216,7 +213,8 @@ def process_fn( 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."), + .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, diff --git a/pyharp/core.py b/pyharp/core.py index 378699e..e691217 100644 --- a/pyharp/core.py +++ b/pyharp/core.py @@ -34,6 +34,7 @@ class HarpMidiTrack(HarpComponent): @dataclass class HarpFileComponent(HarpComponent): + required: bool file_types: List[str] type: str = "generic_file" @@ -134,6 +135,7 @@ def get_harp_component(gr_cmp: Component) -> HarpComponent: 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): From 2142853f5bb15b962cad81d72edd8163c5f39e2f Mon Sep 17 00:00:00 2001 From: Frank Cwitkowitz Date: Mon, 1 Jun 2026 12:34:11 -0400 Subject: [PATCH 7/9] Added protection for empty input file and updated output info in UI example. --- examples/ui_tester/app.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/ui_tester/app.py b/examples/ui_tester/app.py index 07ed014..482c313 100644 --- a/examples/ui_tester/app.py +++ b/examples/ui_tester/app.py @@ -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() @@ -276,16 +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 From 80025dd33ff3c651a7308d7f416b9b581df15a55 Mon Sep 17 00:00:00 2001 From: Frank Cwitkowitz Date: Sat, 11 Jul 2026 10:29:19 -0400 Subject: [PATCH 8/9] Bumped version / copyright, and updated authors in setup.py. --- LICENSE | 2 +- setup.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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/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(), From 20fe620e73fccac1623af1df9dedd5b576f81ae9 Mon Sep 17 00:00:00 2001 From: Frank Cwitkowitz Date: Sun, 12 Jul 2026 19:03:35 -0400 Subject: [PATCH 9/9] Fixed HARP card in README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e3d1abe..29f10cd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![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://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.