Skip to content

Commit db6c098

Browse files
committed
Refactor
1 parent d3272e9 commit db6c098

28 files changed

+538
-766
lines changed

audiokit/__init__.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ def setup_logging(level=None, log_file=None):
5757
class AudioKit:
5858
"""Main AudioKit class providing access to all audio processing features."""
5959

60+
disable_progress = False # Add this class variable
61+
6062
def __init__(self, log_level=logging.INFO, log_file=None):
6163
"""Initialize AudioKit with default configuration."""
6264
self._setup_logging(level=log_level, log_file=log_file)
@@ -124,6 +126,9 @@ def analyze_audio(self, audio_path: str) -> Dict[str, Any]:
124126

125127
return results
126128

129+
except ValidationError:
130+
# Re-raise ValidationError directly
131+
raise
127132
except Exception as e:
128133
logger.opt(exception=True).error("Audio analysis failed")
129134
raise AudioKitError("Audio analysis failed") from e
@@ -135,6 +140,7 @@ def _initialize_services(self):
135140
api_key=os.getenv("SOUNDCHARTS_API_KEY")
136141
)
137142

143+
@logger.catch(reraise=True)
138144
def process_audio(
139145
self,
140146
audio_path: str,
@@ -157,20 +163,29 @@ def process_audio(
157163
dict: Paths to processed audio files
158164
"""
159165
try:
166+
logger.info("Starting audio processing for: %s", audio_path)
160167
results = {}
161168
path = self._validate_audio_file(audio_path)
169+
logger.debug("Validated audio file: %s", path)
162170

163171
if extract_vocals:
172+
logger.info("Extracting vocals")
164173
results["vocals"] = self.processor.extract_vocals(audio_path)
174+
logger.debug("Vocals extracted to: %s", results["vocals"])
165175

166176
if separate_stems:
177+
logger.info("Separating stems")
167178
results["stems"] = self.processor.separate_stems(audio_path, output_dir)
179+
logger.debug("Stems separated to: %s", results["stems"])
168180

169181
if reduce_noise:
182+
logger.info("Reducing noise")
170183
results["cleaned"] = self.processor.reduce_noise(audio_path)
184+
logger.debug("Noise reduced, output: %s", results["cleaned"])
171185

172186
# Index processing results
173187
if results:
188+
logger.info("Indexing processing results")
174189
audio_index.index_data(
175190
str(path),
176191
{
@@ -184,9 +199,10 @@ def process_audio(
184199
"processing"
185200
)
186201

202+
logger.info("Audio processing completed successfully")
187203
return results
188204
except Exception as e:
189-
logger.exception("Audio processing failed")
205+
logger.exception("Audio processing failed: %s", str(e))
190206
raise AudioKitError("Audio processing failed") from e
191207

192208
def generate_content(

audiokit/ai/analysis.py

+60
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
import torchaudio
1010
import torchaudio.transforms as transforms
1111
from typing import Dict, Any
12+
from pathlib import Path
1213

1314
from ..core.logging import get_logger
15+
from ..core.exceptions import ValidationError, AudioKitError
1416

1517
logger = get_logger(__name__)
1618

@@ -21,6 +23,64 @@ def __init__(self):
2123
self.sample_rate = 16000
2224
logger.debug("Initialized AudioAnalyzer with sample_rate={}", self.sample_rate)
2325

26+
@logger.catch(reraise=True)
27+
def analyze_audio(self, audio_path: str) -> Dict[str, Any]:
28+
"""
29+
Perform comprehensive audio analysis.
30+
31+
Args:
32+
audio_path: Path to audio file
33+
34+
Returns:
35+
dict: Analysis results including bpm, key, genre, and instruments
36+
37+
Raises:
38+
AudioKitError: If analysis fails
39+
ValidationError: If input is invalid
40+
"""
41+
try:
42+
logger.info("Starting audio analysis for: {}", audio_path)
43+
44+
# Validate input file
45+
path = Path(audio_path)
46+
logger.debug("Validating audio file: {}", path)
47+
if not path.exists():
48+
logger.error("Audio file not found: {}", path)
49+
raise ValidationError(f"Audio file not found: {audio_path}")
50+
51+
# Check file size
52+
file_size = path.stat().st_size
53+
logger.debug("Audio file size: {} bytes", file_size)
54+
if file_size == 0:
55+
logger.error("Empty audio file: {}", path)
56+
raise ValidationError("Audio file is empty")
57+
58+
# Perform analysis
59+
logger.info("Performing BPM/key detection")
60+
bpm_key = self.detect_bpm_key(audio_path)
61+
logger.debug("BPM/key results: {}", bpm_key)
62+
63+
logger.info("Performing genre classification")
64+
genre = self.classify_genre(audio_path)
65+
logger.debug("Genre results: {}", genre)
66+
67+
logger.info("Identifying instruments")
68+
instruments = self.identify_instruments(audio_path)
69+
logger.debug("Instrument results: {}", instruments)
70+
71+
results = {
72+
"bpm_key": bpm_key,
73+
"genre": genre,
74+
"instruments": instruments
75+
}
76+
77+
logger.success("Audio analysis completed successfully")
78+
return results
79+
80+
except Exception as e:
81+
logger.opt(exception=True).error("Audio analysis failed")
82+
raise AudioKitError("Audio analysis failed") from e
83+
2484
@logger.catch(reraise=True)
2585
def detect_bpm_key(self, audio_path: str) -> Dict[str, Any]:
2686
"""

audiokit/ai/processing.py

+17-11
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
class AudioProcessor:
1818
"""Handles all audio processing functionality."""
1919

20+
def __init__(self):
21+
logger.debug("Initialized AudioProcessor")
22+
2023
@logger.catch(reraise=True)
2124
def separate_stems(
2225
self,
@@ -33,10 +36,10 @@ def separate_stems(
3336
Returns:
3437
dict: Paths to separated stem files
3538
"""
36-
logger.info("Separating stems for: {}", audio_path)
39+
logger.info("Separating stems for: %s", audio_path)
3740
with logger.contextualize(operation="stem_separation"):
3841
output_path = Path(output_dir)
39-
logger.debug("Creating output directory: {}", output_path)
42+
logger.debug("Creating output directory: %s", output_path)
4043
output_path.mkdir(parents=True, exist_ok=True)
4144

4245
# Placeholder implementation
@@ -47,7 +50,8 @@ def separate_stems(
4750
"other": str(output_path / "other.wav")
4851
}
4952

50-
logger.success("Stem separation complete: {}", stems)
53+
logger.debug("Stems separated: %s", stems)
54+
logger.success("Stem separation complete: %s", stems)
5155
return stems
5256

5357
@logger.catch(reraise=True)
@@ -66,18 +70,19 @@ def extract_vocals(
6670
Returns:
6771
str: Path to extracted vocals file
6872
"""
69-
logger.info("Extracting vocals from: {}", audio_path)
73+
logger.info("Extracting vocals from: %s", audio_path)
7074
with logger.contextualize(operation="vocal_extraction"):
7175
if output_path is None:
7276
output_path = "vocals.wav"
73-
logger.debug("Using default output path: {}", output_path)
77+
logger.debug("Using default output path: %s", output_path)
7478

7579
waveform, sample_rate = torchaudio.load(audio_path)
76-
logger.debug("Loaded audio with sample rate: {}", sample_rate)
80+
logger.debug("Loaded audio with sample rate: %s", sample_rate)
7781

7882
# Placeholder implementation - just save the original
7983
torchaudio.save(output_path, waveform, sample_rate)
80-
logger.success("Vocal extraction complete: {}", output_path)
84+
logger.debug("Vocals extracted to: %s", output_path)
85+
logger.success("Vocal extraction complete: %s", output_path)
8186
return output_path
8287

8388
@logger.catch(reraise=True)
@@ -96,16 +101,17 @@ def reduce_noise(
96101
Returns:
97102
str: Path to cleaned audio file
98103
"""
99-
logger.info("Reducing noise in: {}", audio_path)
104+
logger.info("Reducing noise for: %s", audio_path)
100105
with logger.contextualize(operation="noise_reduction"):
101106
if output_path is None:
102107
output_path = "cleaned.wav"
103-
logger.debug("Using default output path: {}", output_path)
108+
logger.debug("Using default output path: %s", output_path)
104109

105110
waveform, sample_rate = torchaudio.load(audio_path)
106-
logger.debug("Loaded audio with sample rate: {}", sample_rate)
111+
logger.debug("Loaded audio with sample rate: %s", sample_rate)
107112

108113
# Placeholder implementation - just save the original
109114
torchaudio.save(output_path, waveform, sample_rate)
110-
logger.success("Noise reduction complete: {}", output_path)
115+
logger.debug("Noise reduced, output: %s", output_path)
116+
logger.success("Noise reduction complete: %s", output_path)
111117
return output_path

audiokit/cli.py

+42-19
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
from . import ak
2222
from .core.logging import get_logger, setup_logging
23+
from .core.config import config
2324

2425
# Initialize logger
2526
logger = get_logger(__name__)
@@ -62,8 +63,14 @@ def analyze(
6263
)
6364
):
6465
"""Analyze an audio file and display comprehensive results."""
65-
with console.status("[bold green]Analyzing audio...") as status:
66-
try:
66+
from audiokit import ak # Import here to access disable_progress
67+
68+
# Temporarily disable progress if needed
69+
original_progress_setting = ak.disable_progress
70+
ak.disable_progress = True
71+
72+
try:
73+
with console.status("[bold green]Analyzing audio...") as status:
6774
logger.info("Starting analysis of: {}", audio_path)
6875
results = ak.analyze_audio(str(audio_path))
6976

@@ -72,26 +79,40 @@ def analyze(
7279
console.print_json(data=results)
7380
return
7481

75-
# Create rich table output
76-
logger.debug("Creating results table")
77-
table = Table(title=f"Analysis Results: {audio_path.name}")
78-
table.add_column("Feature", style="cyan")
79-
table.add_column("Value", style="green")
82+
# Format the output using Rich
83+
console.print(f"\n[bold green]Analysis Results: {audio_path.name}[/bold green]")
8084

81-
for category, details in results.items():
82-
if isinstance(details, dict):
83-
for key, value in details.items():
84-
table.add_row(f"{category.upper()} - {key}", str(value))
85-
else:
86-
table.add_row(category.upper(), str(details))
85+
# Create table for BPM/Key
86+
bpm_key_table = Table(title="BPM & Key", show_header=True, header_style="bold magenta")
87+
bpm_key_table.add_column("Feature", style="dim")
88+
bpm_key_table.add_column("Value")
89+
bpm_key_table.add_row("BPM", str(results["bpm_key"]["bpm"]))
90+
bpm_key_table.add_row("Key", results["bpm_key"]["key"])
91+
92+
# Create table for Genre/Mood
93+
genre_table = Table(title="Genre & Mood", show_header=True, header_style="bold blue")
94+
genre_table.add_column("Feature", style="dim")
95+
genre_table.add_column("Value")
96+
genre_table.add_row("Genre", results["genre"]["genre"])
97+
genre_table.add_row("Mood", results["genre"]["mood"])
98+
99+
# Create table for Instruments
100+
instruments_table = Table(title="Instruments", show_header=True, header_style="bold yellow")
101+
instruments_table.add_column("Instrument", style="dim")
102+
instruments_table.add_column("Confidence")
103+
for instrument, confidence in results["instruments"].items():
104+
instruments_table.add_row(instrument, f"{confidence:.2f}")
105+
106+
# Print all tables
107+
console.print(bpm_key_table)
108+
console.print(genre_table)
109+
console.print(instruments_table)
87110

88-
console.print(table)
89111
logger.success("Analysis complete")
90112

91-
except Exception as e:
92-
logger.exception("Analysis failed")
93-
console.print(f"[red]Error analyzing audio:[/red] {str(e)}")
94-
raise typer.Exit(1)
113+
finally:
114+
# Restore original progress setting
115+
ak.disable_progress = original_progress_setting
95116

96117
@app.command()
97118
@logger.catch(reraise=True)
@@ -338,7 +359,9 @@ def similar(
338359
@app.callback()
339360
def main():
340361
"""AudioKit CLI - AI-powered audio processing toolkit."""
341-
setup_cli()
362+
# Setup logging with default configuration
363+
setup_logging()
364+
logger.info("AudioKit CLI initialized")
342365

343366
if __name__ == "__main__":
344367
app()

audiokit/core/config.py

+13-11
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,9 @@
88
import os
99
from pathlib import Path
1010
from typing import Optional, Dict, Any
11-
from dotenv import load_dotenv
12-
13-
# Load environment variables from .env file
14-
load_dotenv()
1511

1612
# Import logger after other imports to avoid circular dependency
17-
from .logging import get_logger
18-
logger = get_logger(__name__)
13+
# from .logging import get_logger
1914

2015
class Config:
2116
"""Global configuration settings."""
@@ -34,24 +29,31 @@ def __init__(self):
3429

3530
# Pinecone configuration
3631
self.pinecone_api_key = self.get("PINECONE_API_KEY")
37-
logger.debug(f"Pinecone API key: {'*' * 8}{self.pinecone_api_key[-4:]}" if self.pinecone_api_key else "No API key found")
32+
# logger.debug(f"Pinecone API key: {'*' * 8}{self.pinecone_api_key[-4:]}" if self.pinecone_api_key else "No API key found")
3833
self.pinecone_index_name = self.get("PINECONE_INDEX_NAME")
3934

4035
# OpenRouter configuration
41-
self.openrouter_api_key = os.getenv("OPENROUTER_API_KEY")
36+
self.openrouter_api_key = self.get("OPENROUTER_API_KEY")
4237
if not self.openrouter_api_key:
4338
raise ValueError("OpenRouter API key is required. Set OPENROUTER_API_KEY in .env")
4439

4540
# Create necessary directories
4641
self._setup_directories()
47-
logger.debug("Configuration initialized")
42+
# logger.debug("Configuration initialized")
43+
44+
# Initialize configuration values
45+
self._config = {}
46+
47+
# Import and setup logging after initialization
48+
from .logging import setup_logging
49+
setup_logging()
4850

4951
def _setup_directories(self):
5052
"""Create necessary directories if they don't exist."""
5153
for dir_path in [self.models_dir, self.output_dir, self.temp_dir]:
5254
path = Path(dir_path)
5355
if not path.exists():
54-
logger.debug("Creating directory: {}", path)
56+
# logger.debug("Creating directory: {}", path)
5557
path.mkdir(parents=True, exist_ok=True)
5658

5759
def get_model_path(self, model_name: str) -> Path:
@@ -87,5 +89,5 @@ def get_bool(key: str, default: bool = False) -> bool:
8789
value = os.getenv(key, str(default)).lower()
8890
return value in ('true', '1', 't', 'y', 'yes')
8991

90-
# Global configuration instance
92+
# Create global configuration instance
9193
config = Config()

0 commit comments

Comments
 (0)