Skip to content

Commit

Permalink
Black run
Browse files Browse the repository at this point in the history
  • Loading branch information
araray committed Dec 7, 2024
1 parent 1ce6bcc commit a22a511
Show file tree
Hide file tree
Showing 20 changed files with 269 additions and 112 deletions.
1 change: 0 additions & 1 deletion plugins/custom_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,3 @@
def amplify(data, factor=2):
"""Amplifies the signal."""
return data * factor

34 changes: 17 additions & 17 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
from setuptools import find_packages, setup

setup(
name='sygnals',
version='0.1.0',
description='A versatile CLI for signal and audio processing.',
author='Araray Velho',
author_email='[email protected]',
name="sygnals",
version="0.1.0",
description="A versatile CLI for signal and audio processing.",
author="Araray Velho",
author_email="[email protected]",
packages=find_packages(),
install_requires=[
'numpy',
'scipy',
'pandas',
'pandasql',
'librosa',
'soundfile',
'matplotlib',
'click',
'tabulate',
'pywavelets'
"numpy",
"scipy",
"pandas",
"pandasql",
"librosa",
"soundfile",
"matplotlib",
"click",
"tabulate",
"pywavelets",
],
entry_points={
'console_scripts': [
'sygnals=sygnals.cli:cli',
"console_scripts": [
"sygnals=sygnals.cli:cli",
],
},
)
149 changes: 121 additions & 28 deletions sygnals/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,37 @@
from rich.table import Table
from tabulate import tabulate

from sygnals.core import (audio_handler, batch_processor, custom_exec,
data_handler, dsp, filters, plugin_manager, storage,
transforms)
from sygnals.core import (
audio_handler,
batch_processor,
custom_exec,
data_handler,
dsp,
filters,
plugin_manager,
storage,
transforms,
)
from sygnals.utils import visualizations

# Global Console Object
console = Console()


@click.group()
def cli():
"""Sygnals: A versatile CLI for signal and audio processing."""
pass


@cli.command()
@click.argument("file", type=click.Path(exists=True))
@click.option("--output", type=click.Choice(["json", "csv", "tabulated"]), default="json")
@click.option(
"--output", type=click.Choice(["json", "csv", "tabulated"]), default="json"
)
def analyze(file, output):
"""Analyze a data or audio file."""
if file.endswith(('.wav', '.mp3')):
if file.endswith((".wav", ".mp3")):
data, sr = audio_handler.load_audio(file) # Load both data and sample rate
metrics = audio_handler.get_audio_metrics(data, sr)
else:
Expand All @@ -36,44 +48,63 @@ def analyze(file, output):
"rows": len(data),
"mean": data.mean().to_dict(),
"max": data.max().to_dict(),
"min": data.min().to_dict()
"min": data.min().to_dict(),
}

if output == "json":
# Ensure compatibility with JSON serialization
click.echo(json.dumps(metrics, indent=2, default=lambda x: float(x) if isinstance(x, (int, float)) else x))
click.echo(
json.dumps(
metrics,
indent=2,
default=lambda x: float(x) if isinstance(x, (int, float)) else x,
)
)
elif output == "csv":
pd.DataFrame([metrics]).to_csv("analysis.csv", index=False)
click.echo("Analysis saved to analysis.csv")
else:
click.echo(tabulate(metrics.items(), headers=["Metric", "Value"]))


@cli.command()
@click.argument("file", type=click.Path(exists=True))
@click.option("--fft", is_flag=True, help="Apply FFT.")
@click.option("--wavelet", type=str, help="Apply Wavelet Transform (e.g., db4).")
@click.option("--output", type=click.Path(), required=True)
def transform(file, fft, wavelet, output):
"""Apply transforms (FFT, Wavelet) to data or audio."""
data = data_handler.read_data(file) if not file.endswith(('.wav', '.mp3')) else audio_handler.load_audio(file)
data = (
data_handler.read_data(file)
if not file.endswith((".wav", ".mp3"))
else audio_handler.load_audio(file)
)

if fft:
freqs, magnitudes = dsp.compute_fft(data['value'], fs=1) # fs=1 for time-series data
freqs, magnitudes = dsp.compute_fft(
data["value"], fs=1
) # fs=1 for time-series data
result = pd.DataFrame({"Frequency (Hz)": freqs, "Magnitude": magnitudes})
elif wavelet:
coeffs = transforms.wavelet_transform(data['value'], wavelet)
coeffs = transforms.wavelet_transform(data["value"], wavelet)
result = pd.DataFrame({f"Level {i+1}": coeff for i, coeff in enumerate(coeffs)})
else:
raise click.UsageError("Specify a transform (FFT or Wavelet).")

data_handler.save_data(result, output)
click.echo(f"Transform saved to {output}")


@cli.command()
@click.argument("file", type=click.Path(exists=True))
@click.option("--low-pass", type=float, help="Low-pass filter cutoff frequency (Hz).")
@click.option("--high-pass", type=float, help="High-pass filter cutoff frequency (Hz).")
@click.option("--band-pass", nargs=2, type=float, help="Band-pass filter cutoff frequencies (low, high).")
@click.option(
"--band-pass",
nargs=2,
type=float,
help="Band-pass filter cutoff frequencies (low, high).",
)
@click.option("--output", type=click.Path(), required=True)
def filter(file, low_pass, high_pass, band_pass, output):
"""Apply filters to a signal or audio."""
Expand All @@ -87,17 +118,22 @@ def filter(file, low_pass, high_pass, band_pass, output):
elif band_pass:
result = filters.band_pass_filter(data["value"], band_pass[0], band_pass[1], fs)
else:
raise click.UsageError("Specify a filter type (low-pass, high-pass, or band-pass).")
raise click.UsageError(
"Specify a filter type (low-pass, high-pass, or band-pass)."
)

# Save filtered data
filtered_df = pd.DataFrame({"time": data["time"], "value": result})
data_handler.save_data(filtered_df, output)
click.echo(f"Filtered signal saved to {output}")


@cli.command()
@click.argument("file", type=click.Path(exists=True))
@click.option("--query", type=str, help="SQL query to run on the data.")
@click.option("--filter", type=str, help="Pandas filter expression (e.g., 'value > 10').")
@click.option(
"--filter", type=str, help="Pandas filter expression (e.g., 'value > 10')."
)
@click.option("--output", type=click.Path(), required=True)
def manipulate(file, query, filter, output):
"""Manipulate data using SQL or Pandas expressions."""
Expand All @@ -119,11 +155,21 @@ def audio():
"""Audio processing commands."""
pass


@audio.command()
@click.argument("file", type=click.Path(exists=True))
@click.option("--output", type=click.Path(), help="Export audio data to a file (csv, json, wav).")
@click.option("--format", type=click.Choice(["raw", "csv", "json", "tabulate", "wav"]), default="tabulate", help="Format for displaying or exporting audio data.")
@click.option("--full-output", is_flag=True, help="Force full output instead of truncating.")
@click.option(
"--output", type=click.Path(), help="Export audio data to a file (csv, json, wav)."
)
@click.option(
"--format",
type=click.Choice(["raw", "csv", "json", "tabulate", "wav"]),
default="tabulate",
help="Format for displaying or exporting audio data.",
)
@click.option(
"--full-output", is_flag=True, help="Force full output instead of truncating."
)
def show(file, output, format, full_output):
"""Show audio data and optionally export it."""
data, sr = audio_handler.load_audio(file)
Expand All @@ -134,7 +180,9 @@ def show(file, output, format, full_output):

if format == "raw":
# Raw format: space-separated values for piping
raw_output = "\n".join(f"{row.time} {row.amplitude}" for _, row in audio_df.iterrows())
raw_output = "\n".join(
f"{row.time} {row.amplitude}" for _, row in audio_df.iterrows()
)
if output:
with open(output, "w") as f:
f.write(raw_output)
Expand Down Expand Up @@ -166,6 +214,7 @@ def show(file, output, format, full_output):
audio_handler.save_audio(data, sr, output)
click.echo(f"Audio data exported to {output}")


@audio.command()
@click.argument("file", type=click.Path(exists=True))
def info(file):
Expand All @@ -181,9 +230,12 @@ def info(file):
}
click.echo(json.dumps(info, indent=2))


@audio.command()
@click.argument("file", type=click.Path(exists=True))
@click.option("--new-sr", type=int, required=True, help="New sampling rate for the audio file.")
@click.option(
"--new-sr", type=int, required=True, help="New sampling rate for the audio file."
)
@click.option("--output", type=click.Path(), required=True)
def resample(file, new_sr, output):
"""Resample audio to a new sampling rate."""
Expand All @@ -192,9 +244,15 @@ def resample(file, new_sr, output):
audio_handler.save_audio(resampled_data, new_sr, output)
click.echo(f"Resampled audio saved to {output} with sampling rate {new_sr} Hz")


@audio.command()
@click.argument("file", type=click.Path(exists=True))
@click.option("--target-amplitude", type=float, required=True, help="Target peak amplitude for normalization.")
@click.option(
"--target-amplitude",
type=float,
required=True,
help="Target peak amplitude for normalization.",
)
@click.option("--output", type=click.Path(), required=True)
def normalize(file, target_amplitude, output):
"""Normalize audio to a target amplitude."""
Expand All @@ -204,10 +262,19 @@ def normalize(file, target_amplitude, output):
audio_handler.save_audio(normalized_data, sr, output)
click.echo(f"Normalized audio saved to {output}")


@audio.command()
@click.argument("file", type=click.Path(exists=True))
@click.option("--effect", type=click.Choice(["stretch", "pitch-shift", "compression"]), required=True)
@click.option("--factor", type=float, help="Stretch factor or semitone shift (e.g., 1.5 for stretch, 2 for pitch shift).")
@click.option(
"--effect",
type=click.Choice(["stretch", "pitch-shift", "compression"]),
required=True,
)
@click.option(
"--factor",
type=float,
help="Stretch factor or semitone shift (e.g., 1.5 for stretch, 2 for pitch shift).",
)
@click.option("--output", type=click.Path(), required=True)
def effect(file, effect, factor, output):
"""Apply audio effects like time-stretching or pitch-shifting."""
Expand All @@ -229,6 +296,7 @@ def effect(file, effect, factor, output):
audio_handler.save_audio(result, sr, output)
click.echo(f"Effect applied and saved to {output}")


@audio.command()
@click.argument("file", type=click.Path(exists=True))
@click.option("--start", type=float, required=True, help="Start time in seconds.")
Expand All @@ -241,35 +309,51 @@ def slice(file, start, end, output):
audio_handler.save_audio(sliced_data, sr, output)
click.echo(f"Audio sliced and saved to {output}")


# Register the audio group
cli.add_command(audio)


@cli.command()
@click.option("--input-dir", type=click.Path(exists=True), required=True)
@click.option("--transform", type=str, required=True, help="Transform to apply (e.g., fft, wavelet).")
@click.option(
"--transform",
type=str,
required=True,
help="Transform to apply (e.g., fft, wavelet).",
)
@click.option("--output-dir", type=click.Path(), required=True)
def batch(input_dir, transform, output_dir):
"""Process multiple files in a directory."""
batch_processor.process_batch(input_dir, output_dir, transform)
click.echo(f"Batch processing completed. Results saved in {output_dir}")


@cli.command()
@click.argument("file", type=click.Path(exists=True))
@click.option("--type", type=click.Choice(["fft", "spectrogram", "waveform"]), required=True)
@click.option(
"--type", type=click.Choice(["fft", "spectrogram", "waveform"]), required=True
)
@click.option("--output", type=click.Path(), required=True)
@click.option("--min_freq", type=click.Path(), required=False)
@click.option("--max_freq", type=click.Path(), required=False)
@click.option("--extra-params", type=str, help="Additional parameters for visualization libraries in key=value format (comma-separated).")
@click.option(
"--extra-params",
type=str,
help="Additional parameters for visualization libraries in key=value format (comma-separated).",
)
def visualize(file, type, output, min_freq, max_freq, extra_params):
"""Generate visualizations like spectrograms or FFT plots."""
params = {}
if extra_params:
# Parse key=value pairs
for param in extra_params.split(","):
key, value = param.split("=")
params[key.strip()] = eval(value.strip()) # Safely evaluate numeric or tuple values
params[key.strip()] = eval(
value.strip()
) # Safely evaluate numeric or tuple values

if file.endswith(('.wav', '.mp3')):
if file.endswith((".wav", ".mp3")):
data, sr = audio_handler.load_audio(file)
else:
data = data_handler.read_data(file).values.flatten()
Expand All @@ -286,8 +370,11 @@ def visualize(file, type, output, min_freq, max_freq, extra_params):

click.echo(f"{type.capitalize()} visualization saved to {output}")


@cli.command()
@click.option("--list", "list_plugins", is_flag=True, help="List all available plugins.")
@click.option(
"--list", "list_plugins", is_flag=True, help="List all available plugins."
)
@click.argument("plugin", required=False)
@click.argument("file", type=click.Path(exists=True), required=False)
@click.option("--output", type=click.Path(), help="Output file for plugin result.")
Expand All @@ -311,9 +398,15 @@ def plugin(list_plugins, plugin, file, output):
else:
raise click.UsageError("Specify --list to view plugins or a plugin to execute.")


@cli.command()
@click.argument("expression", type=str)
@click.option("--x-range", type=str, required=True, help="Range for x as start,end,step (e.g., 0,1,0.01).")
@click.option(
"--x-range",
type=str,
required=True,
help="Range for x as start,end,step (e.g., 0,1,0.01).",
)
@click.option("--output", type=click.Path(), required=True)
def math(expression, x_range, output):
"""Evaluate a custom mathematical expression."""
Expand Down
2 changes: 1 addition & 1 deletion sygnals/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"plugin_manager",
"batch_processor",
"custom_exec",
"storage"
"storage",
]
Loading

0 comments on commit a22a511

Please sign in to comment.