diff --git a/api/api/tasks.py b/api/api/tasks.py
index 716bbec..0c5cc54 100644
--- a/api/api/tasks.py
+++ b/api/api/tasks.py
@@ -1,5 +1,7 @@
import os
import shutil
+import json
+import numpy as np
from pathlib import Path
from celery import shared_task
from freqsplit.input.file_reader import read_audio
@@ -10,6 +12,7 @@ from freqsplit.preprocessing.resample import resample
from freqsplit.postprocessing.audio_writer import export_audio
from freqsplit.separation.demucs_wrapper import separate_audio_with_demucs
from freqsplit.refinement.deepfilternet_wrapper import noisereduce
+from freqsplit.spectrogram.generator import generate_spectrogram
@shared_task
@@ -24,8 +27,14 @@ def save_and_classify(file_path, file_content):
# Classify the audio
audio_class = classify_audio(waveform, sr)
-
- return audio_class, org_sr
+
+ # Generate spectrogram
+ spec_db, plot_data = generate_spectrogram(file_path)
+ # Convert numpy array to JSON-safe list
+ spec_db = np.nan_to_num(spec_db, nan=-80.0, posinf=-80.0, neginf=-80.0)
+ spec_data_json = json.dumps(spec_db.tolist())
+
+ return audio_class, org_sr, spec_data_json, plot_data['sr']
@shared_task
def normalize_audio_task(file_path):
@@ -122,6 +131,20 @@ def noisereduce_task(file_path):
return False
@shared_task
+def generate_spectrogram_task(file_path):
+ """Celery task to generate spectrogram"""
+ try:
+ file_path = Path(file_path)
+
+ # Generate spectrogram
+ spec_db, plot_data = generate_spectrogram(file_path)
+ spec_db = np.nan_to_num(spec_db, nan=-80.0, posinf=-80.0, neginf=-80.0)
+ spec_data_json = json.dumps(spec_db.tolist())
+
+ return True, spec_data_json, plot_data['sr']
+ except Exception as e:
+ return False
+@shared_task
def cleanup_task(file_path):
"""Celery task to cleanup files"""
file_path = Path(file_path)
diff --git a/api/api/views.py b/api/api/views.py
index 4f3cc4f..6c0cf6f 100644
--- a/api/api/views.py
+++ b/api/api/views.py
@@ -12,6 +12,7 @@ from .tasks import trim_audio_task
from .tasks import resample_audio_task
from .tasks import music_separation_task
from .tasks import noisereduce_task
+from .tasks import generate_spectrogram_task
from .tasks import cleanup_task
from freqsplit.input.format_checker import is_supported_format
@@ -47,17 +48,21 @@ def upload_audio(request):
# Save the uploaded file
task = save_and_classify.apply(args=(file_path, audio_file.read()))
- if task.successful():
- audio_class = task.result[0]
+ if task.ready() and task.successful():
+ result = task.result
return Response(
{
"Status": "File uploaded successfully",
"file_uuid": file_uuid,
- "audio_class": audio_class,
- "sr": task.result[1]
- },
+ "audio_class": result[0],
+ "sr": result[1],
+ "spectrogram": result[2],
+ "spec_sr": result[3]
+ },
status=status.HTTP_201_CREATED,
- )
+ )
+ else:
+ return Response({"error": "Processing failed"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
# Endpoint to normalize audio
@api_view(['POST'])
@@ -143,6 +148,35 @@ def noisereduce(request):
else:
return Response({"error": "Failed to remove noise from audio"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
+# Endpoint to generate spectrograms
+@api_view(['POST'])
+def generate_spectrogram(request):
+ """Handle generation of spectrogram"""
+ file_uuid = request.data.get("file_uuid")
+ file_name = request.data.get("file_name")
+ file_path = os.path.join(UPLOAD_DIR, file_uuid, file_name)
+
+ # Check if file exists
+ if os.path.exists(file_path):
+ # Call Celery task synchronously
+ task = generate_spectrogram_task.apply(args=(file_path,))
+
+ if task.ready() and task.successful():
+ result = task.result
+ if result[0]:
+ return Response(
+ {
+ "Status": "Spectrogram generated successfully",
+ "spectrogram": result[1],
+ "spec_sr": result[2]
+ },
+ status=status.HTTP_200_OK
+ )
+ else:
+ return Response({"error": "Failed to generate spectrogram"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
+ else:
+ return Response({"error": "File Not Found"}, status=status.HTTP_404_NOT_FOUND)
+
# Endpoint to download audio file or zipped directory
@api_view(['GET'])
def download_audio(request):
diff --git a/api/backend/urls.py b/api/backend/urls.py
index 727daa7..a675ee7 100644
--- a/api/backend/urls.py
+++ b/api/backend/urls.py
@@ -23,6 +23,7 @@ from api.views import resample_audio
from api.views import separate_music
from api.views import noisereduce
from api.views import download_audio
+from api.views import generate_spectrogram
from api.views import cleanup
from api.views import cleanup_zip
@@ -35,6 +36,7 @@ urlpatterns = [
path('api/separate', separate_music, name="separate_music"),
path('api/noisereduce', noisereduce, name="noisreduce"),
path('api/download', download_audio, name="download_audio"),
+ path('api/spectrogram', generate_spectrogram, name="generate_spectrogram"),
path('api/cleanup', cleanup, name="cleanup"),
path('api/cleanup_zip', cleanup_zip, name="cleanup_zip")
]
diff --git a/client/package.json b/client/package.json
index c521b62..2e8c182 100644
--- a/client/package.json
+++ b/client/package.json
@@ -18,6 +18,7 @@
"axios": "^1.8.3",
"jszip": "^3.10.1",
"react": "^18.2.0",
+ "react-audio-spectrogram-player": "^2.0.1",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.1"
},
diff --git a/client/src/Pages/PreviewPage.tsx b/client/src/Pages/PreviewPage.tsx
index b8d976b..250158d 100644
--- a/client/src/Pages/PreviewPage.tsx
+++ b/client/src/Pages/PreviewPage.tsx
@@ -11,6 +11,8 @@ import {
import { VolumeUp as VolumeUpIcon, ErrorOutline as ErrorIcon } from '@mui/icons-material';
import StepperComponent from '../components/StepperComponent';
import { useMediaContext } from '../contexts/MediaContext';
+// @ts-ignore
+import SpectrogramPlayer from "react-audio-spectrogram-player"
function PreviewPage() {
const navigate = useNavigate();
@@ -83,12 +85,15 @@ function PreviewPage() {
Audio Classification: {audioClass || "No data received"}
diff --git a/client/src/Pages/ProcessingPage.tsx b/client/src/Pages/ProcessingPage.tsx index df2e7c8..8a44a90 100644 --- a/client/src/Pages/ProcessingPage.tsx +++ b/client/src/Pages/ProcessingPage.tsx @@ -8,7 +8,7 @@ import JSZip from "jszip"; function ProcessingPage() { const navigate = useNavigate(); - const { mediaFile, response, setExtractedFiles, setDownloadedFileURL } = useMediaContext(); + const { mediaFile, response, setExtractedFiles, setDownloadedFileURL, setDownloadedFileSpectrogram } = useMediaContext(); const [progress, setProgress] = useState(0); const [statusText, setStatusText] = useState("Analyzing media..."); @@ -73,7 +73,36 @@ function ProcessingPage() { const fileURL = URL.createObjectURL(blob); setDownloadedFileURL( fileURL ); - setProgress(100); + setProgress(90); + + // Get spectrogram + setProgress(95); + setStatusText("Calculating Spectrogram"); + + const formData = new FormData(); + formData.append("file_uuid", response.file_uuid); + if (mediaFile?.name) { + formData.append("file_name", mediaFile?.name); + } + + const startTime = Date.now(); + const resp = await axios.post<{ + Status: String; + spectrogram: string; + spec_sr: number; + }>("/api/spectrogram", formData, { + headers: { + "Content-Type": "multipart/form-data", + } + }) + + if (resp.status === 200 && resp.data) { + const elapsedTime = Date.now() - startTime; + if(elapsedTime < 5000) await delay(5000 - elapsedTime); + setDownloadedFileSpectrogram({spectrogram: resp.data.spectrogram, spec_sr: resp.data.spec_sr}) + setProgress(100); + } + } else { console.log("Failed to download the file"); @@ -94,9 +123,32 @@ function ProcessingPage() { if (!fileData.dir) { const fileBlob = await fileData.async("blob"); const fileURL = URL.createObjectURL(fileBlob); - fileURLs.push({ name: filename, url: fileURL }); + + // Get spectrograms + setProgress(95); + setStatusText("Calculating Spectrograms"); + + const formData = new FormData(); + formData.append("file_uuid", response.file_uuid); + formData.append("file_name", filename); + + const res = await axios.post<{ + Status: string; + spectrogram: string; + spec_sr: number; + }>("/api/spectrogram", formData, { + headers: { + "Content-Type": "multipart/form-data", + }, + }) + + if (res.status === 200 && res.data){ + + } + fileURLs.push({ name: filename, url: fileURL, spectrogram: res.data.spectrogram, spec_sr: res.data.spec_sr }); } } + console.log(fileURLs) setExtractedFiles(fileURLs); setProgress(100); }; diff --git a/client/src/Pages/ResultsPage.tsx b/client/src/Pages/ResultsPage.tsx index 36988ba..2735a50 100644 --- a/client/src/Pages/ResultsPage.tsx +++ b/client/src/Pages/ResultsPage.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef } from 'react'; +import { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; //import axios from 'axios'; import { @@ -18,14 +18,14 @@ import { } from '@mui/icons-material'; import StepperComponent from '../components/StepperComponent'; import { useMediaContext } from '../contexts/MediaContext'; +// @ts-ignore +import SpectrogramPlayer from "react-audio-spectrogram-player" function ResultsPage() { const navigate = useNavigate(); - const { mediaFile, response, extractedFiles, downloadedFileURL } = useMediaContext(); + const { mediaFile, response, extractedFiles, downloadedFileURL, downloadedFileSpectrogram } = useMediaContext(); console.log("Extracted files are", extractedFiles); // const [isPlaying, setIsPlaying] = useState(false); - const audioRefs = [useRef(null), useRef(null), useRef(null),useRef(null)]; - const mediaFileRef = useRef(null); const audioClass = response.audio_class const isVideo = mediaFile?.type.includes('video'); @@ -97,15 +97,18 @@ function ResultsPage() {