juzer09 commited on
Commit
f280f9f
Β·
verified Β·
1 Parent(s): 75ea931

Upload 19 files

Browse files
Files changed (19) hide show
  1. .dockerignore +16 -0
  2. .gitattributes +35 -35
  3. .gitignore +169 -0
  4. Dockerfile +33 -21
  5. README.md +105 -20
  6. USAGE_GUIDE.md +152 -0
  7. api.py +385 -0
  8. app.py +451 -0
  9. app_hf.py +272 -0
  10. classify_audio.py +122 -0
  11. config.json +100 -0
  12. deploy.ps1 +68 -0
  13. deploy.sh +51 -0
  14. docker-compose.yml +34 -0
  15. env.example +19 -0
  16. pytorch_model.bin +3 -0
  17. requirements.txt +8 -3
  18. requirements_hf.txt +9 -0
  19. test_api.py +183 -0
.dockerignore ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .git
2
+ .gitignore
3
+ README.md
4
+ USAGE_GUIDE.md
5
+ test_api.py
6
+ *.md
7
+ .DS_Store
8
+ __pycache__
9
+ *.pyc
10
+ *.pyo
11
+ *.pyd
12
+ .env
13
+ .venv
14
+ venv/
15
+ .pytest_cache
16
+ .coverage
.gitattributes CHANGED
@@ -1,35 +1,35 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ share/python-wheels/
20
+ *.egg-info/
21
+ .installed.cfg
22
+ *.egg
23
+ MANIFEST
24
+
25
+ # PyInstaller
26
+ *.manifest
27
+ *.spec
28
+
29
+ # Installer logs
30
+ pip-log.txt
31
+ pip-delete-this-directory.txt
32
+
33
+ # Unit test / coverage reports
34
+ htmlcov/
35
+ .tox/
36
+ .nox/
37
+ .coverage
38
+ .coverage.*
39
+ .cache
40
+ nosetests.xml
41
+ coverage.xml
42
+ *.cover
43
+ *.py,cover
44
+ .hypothesis/
45
+ .pytest_cache/
46
+ cover/
47
+
48
+ # Translations
49
+ *.mo
50
+ *.pot
51
+
52
+ # Django stuff:
53
+ *.log
54
+ local_settings.py
55
+ db.sqlite3
56
+ db.sqlite3-journal
57
+
58
+ # Flask stuff:
59
+ instance/
60
+ .webassets-cache
61
+
62
+ # Scrapy stuff:
63
+ .scrapy
64
+
65
+ # Sphinx documentation
66
+ docs/_build/
67
+
68
+ # PyBuilder
69
+ .pybuilder/
70
+ target/
71
+
72
+ # Jupyter Notebook
73
+ .ipynb_checkpoints
74
+
75
+ # IPython
76
+ profile_default/
77
+ ipython_config.py
78
+
79
+ # pyenv
80
+ .python-version
81
+
82
+ # pipenv
83
+ Pipfile.lock
84
+
85
+ # poetry
86
+ poetry.lock
87
+
88
+ # pdm
89
+ .pdm.toml
90
+
91
+ # PEP 582
92
+ __pypackages__/
93
+
94
+ # Celery stuff
95
+ celerybeat-schedule
96
+ celerybeat.pid
97
+
98
+ # SageMath parsed files
99
+ *.sage.py
100
+
101
+ # Environments
102
+ .env
103
+ .venv
104
+ env/
105
+ venv/
106
+ ENV/
107
+ env.bak/
108
+ venv.bak/
109
+
110
+ # Spyder project settings
111
+ .spyderproject
112
+ .spyproject
113
+
114
+ # Rope project settings
115
+ .ropeproject
116
+
117
+ # mkdocs documentation
118
+ /site
119
+
120
+ # mypy
121
+ .mypy_cache/
122
+ .dmypy.json
123
+ dmypy.json
124
+
125
+ # Pyre type checker
126
+ .pyre/
127
+
128
+ # pytype static type analyzer
129
+ .pytype/
130
+
131
+ # Cython debug symbols
132
+ cython_debug/
133
+
134
+ # PyCharm
135
+ .idea/
136
+
137
+ # VS Code
138
+ .vscode/
139
+
140
+ # OS generated files
141
+ .DS_Store
142
+ .DS_Store?
143
+ ._*
144
+ .Spotlight-V100
145
+ .Trashes
146
+ ehthumbs.db
147
+ Thumbs.db
148
+
149
+ # Audio files (test files)
150
+ *.wav
151
+ *.mp3
152
+ *.flac
153
+ *.m4a
154
+ *.ogg
155
+
156
+ # Temporary files
157
+ *.tmp
158
+ *.temp
159
+ temp/
160
+ tmp/
161
+
162
+ # Logs
163
+ *.log
164
+ logs/
165
+
166
+ # API keys and secrets
167
+ .env
168
+ secrets.json
169
+ config.local.json
Dockerfile CHANGED
@@ -1,21 +1,33 @@
1
- FROM python:3.9-slim
2
-
3
- WORKDIR /app
4
-
5
- RUN apt-get update && apt-get install -y \
6
- build-essential \
7
- curl \
8
- software-properties-common \
9
- git \
10
- && rm -rf /var/lib/apt/lists/*
11
-
12
- COPY requirements.txt ./
13
- COPY src/ ./src/
14
-
15
- RUN pip3 install -r requirements.txt
16
-
17
- EXPOSE 8501
18
-
19
- HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health
20
-
21
- ENTRYPOINT ["streamlit", "run", "src/streamlit_app.py", "--server.port=8501", "--server.address=0.0.0.0"]
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ # Set working directory
4
+ WORKDIR /app
5
+
6
+ # Install system dependencies
7
+ RUN apt-get update && apt-get install -y \
8
+ ffmpeg \
9
+ libsndfile1 \
10
+ && rm -rf /var/lib/apt/lists/*
11
+
12
+ # Copy requirements first for better caching
13
+ COPY requirements.txt .
14
+
15
+ # Install Python dependencies
16
+ RUN pip install --no-cache-dir -r requirements.txt
17
+
18
+ # Copy application files
19
+ COPY . .
20
+
21
+ # Create non-root user for security
22
+ RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
23
+ USER appuser
24
+
25
+ # Expose port
26
+ EXPOSE 8000
27
+
28
+ # Health check
29
+ HEALTHCHECK --interval=30s --timeout=30s --start-period=60s --retries=3 \
30
+ CMD curl -f http://localhost:8000/health || exit 1
31
+
32
+ # Start the application
33
+ CMD ["python", "api.py"]
README.md CHANGED
@@ -1,20 +1,105 @@
1
- ---
2
- title: Ai Music Detection
3
- emoji: πŸš€
4
- colorFrom: red
5
- colorTo: red
6
- sdk: docker
7
- app_port: 8501
8
- tags:
9
- - streamlit
10
- pinned: false
11
- short_description: Streamlit template space
12
- license: mit
13
- ---
14
-
15
- # Welcome to Streamlit!
16
-
17
- Edit `/src/streamlit_app.py` to customize this app to your heart's desire. :heart:
18
-
19
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
20
- forums](https://discuss.streamlit.io).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Madverse Music: AI Music Detection
2
+
3
+ Detect AI-generated music vs human-created music using advanced AI technology.
4
+
5
+ ## What is Madverse Music?
6
+
7
+ Madverse Music is an AI-powered tool that can detect whether music is created by AI or human artists. With the growth of AI music generation platforms like Suno and Udio, it's important to distinguish between human creativity and artificial intelligence.
8
+
9
+ ## Key Features
10
+
11
+ - Upload and analyze audio files instantly
12
+ - Web interface for easy testing
13
+ - REST API for integration
14
+ - High accuracy: 97% F1 score with 96% sensitivity and 99% specificity
15
+ - Supports WAV, MP3, FLAC, M4A, and OGG files
16
+ - Uses SpecTTTra transformer technology
17
+ - Trained on 97,000+ songs
18
+
19
+ ## Quick Start
20
+
21
+ ### Hugging Face Space (Recommended)
22
+ 1. Go to our Hugging Face Space
23
+ 2. Upload your audio file
24
+ 3. Get instant results
25
+
26
+ ### Local Setup
27
+ ```bash
28
+ # Install dependencies
29
+ pip install -r requirements.txt
30
+
31
+ # Start web interface
32
+ streamlit run app.py
33
+
34
+ # Or start API server
35
+ python api.py
36
+ ```
37
+
38
+ ## Web Interface
39
+
40
+ Use the web interface for easy testing:
41
+
42
+ ```bash
43
+ streamlit run app.py
44
+ ```
45
+
46
+ Then open your browser to `http://localhost:8501` and:
47
+ 1. Upload your audio file
48
+ 2. Click "Analyze Audio"
49
+ 3. Get instant results
50
+
51
+ The web interface provides:
52
+ - Audio player to preview your file
53
+ - Detailed analysis with confidence scores
54
+ - Clean visualization of results
55
+
56
+ ## Model Performance
57
+
58
+ Our model (SpecTTTra-Ξ± 120s) achieves:
59
+ - F1 Score: 0.97
60
+ - Sensitivity: 0.96
61
+ - Specificity: 0.99
62
+
63
+ ## Technical Details
64
+
65
+ - Model: SpecTTTra (Spectro-Temporal Tokens Transformer)
66
+ - Sample Rate: 16kHz
67
+ - Max Duration: 120 seconds
68
+ - Trained on 97,000+ songs
69
+
70
+ ## API Usage
71
+
72
+ ### Hugging Face Space
73
+ ```bash
74
+ # Health check
75
+ curl https://your-space-name.hf.space/health
76
+
77
+ # Analyze audio file
78
+ curl -X POST "https://your-space-name.hf.space/analyze" \
79
80
+ ```
81
+
82
+ ### Local Setup
83
+ ```bash
84
+ # Install dependencies
85
+ pip install -r requirements.txt
86
+
87
+ # Start the API server
88
+ python api.py
89
+ ```
90
+
91
+ ### Command Line Usage
92
+
93
+ ```bash
94
+ # Analyze a single file
95
+ python classify_audio.py "your_song.mp3"
96
+
97
+ # Analyze all files in directory
98
+ python classify_audio.py
99
+ ```
100
+
101
+ ## Acknowledgments
102
+
103
+ This tool is designed for research, education, and transparency in AI music detection. Results may vary depending on audio quality and content type.
104
+
105
+ Visit [madverse.co](https://madverse.co) for more information.
USAGE_GUIDE.md ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Madverse Music: AI Audio Classifier - Usage Guide
2
+
3
+ ## Quick Start
4
+
5
+ ### Option 1: Hugging Face Space (Recommended)
6
+ Use our deployed model on Hugging Face Spaces:
7
+
8
+ **Web Interface:**
9
+ 1. Go to the Hugging Face Space URL
10
+ 2. Upload your audio file
11
+ 3. Click "Analyze Audio"
12
+ 4. Get instant results
13
+
14
+ **API Access:**
15
+ ```bash
16
+ # Health check
17
+ curl https://your-space-name.hf.space/health
18
+
19
+ # Analyze audio file
20
+ curl -X POST "https://your-space-name.hf.space/analyze" \
21
22
+ ```
23
+
24
+ ### Option 2: Local Setup
25
+ ```bash
26
+ # Install dependencies
27
+ pip install -r requirements.txt
28
+
29
+ # Start the API server
30
+ python api.py
31
+
32
+ # Or start web interface
33
+ streamlit run app.py
34
+ ```
35
+
36
+ ## Supported Audio Formats
37
+ - WAV (.wav)
38
+ - MP3 (.mp3)
39
+ - FLAC (.flac)
40
+ - M4A (.m4a)
41
+ - OGG (.ogg)
42
+
43
+ ## API Usage
44
+
45
+ ### Hugging Face Space API
46
+
47
+ #### Health Check
48
+ ```bash
49
+ GET /health
50
+ ```
51
+
52
+ #### Analyze Audio
53
+ ```bash
54
+ POST /analyze
55
+ ```
56
+ Upload audio file using multipart/form-data
57
+
58
+ **Request:**
59
+ Upload file using form data with field name "file"
60
+
61
+ **Response Format:**
62
+ ```json
63
+ {
64
+ "classification": "Real",
65
+ "confidence": 0.85,
66
+ "probability": 0.15,
67
+ "raw_score": -1.73,
68
+ "duration": 30.5,
69
+ "message": "Detected as real music"
70
+ }
71
+ ```
72
+
73
+
74
+
75
+ ### Usage Examples
76
+
77
+ #### Python
78
+ ```python
79
+ import requests
80
+
81
+ # Upload file to HF Space
82
+ with open('your_song.mp3', 'rb') as f:
83
+ response = requests.post('https://your-space-name.hf.space/analyze',
84
+ files={'file': f})
85
+ result = response.json()
86
+ print(result)
87
+ ```
88
+
89
+ #### JavaScript
90
+ ```javascript
91
+ const formData = new FormData();
92
+ formData.append('file', fileInput.files[0]);
93
+
94
+ const response = await fetch('https://your-space-name.hf.space/analyze', {
95
+ method: 'POST',
96
+ body: formData
97
+ });
98
+ const result = await response.json();
99
+ ```
100
+
101
+ ## Understanding Results
102
+
103
+ The classifier will output:
104
+ - **"Real"** = Human-created music
105
+ - **"Fake"** = AI-generated music (from Suno, Udio, etc.)
106
+
107
+ ### API Response Format:
108
+ ```json
109
+ {
110
+ "classification": "Real",
111
+ "confidence": 0.85,
112
+ "probability": 0.15,
113
+ "raw_score": -1.73,
114
+ "duration": 30.5,
115
+ "message": "Detected as real music"
116
+ }
117
+ ```
118
+
119
+ ### Command Line Output:
120
+ ```
121
+ Analyzing: my_song.wav
122
+ Result: Fake (AI-generated music)
123
+ Confidence: 0.96 | Raw output: 3.786
124
+ ```
125
+
126
+ ## Model Specifications
127
+ - Model: SpecTTTra-Ξ± (120 seconds)
128
+ - Sample Rate: 16kHz
129
+ - Performance: 97% F1 score, 96% sensitivity, 99% specificity
130
+ - Max Duration: 120 seconds (2 minutes)
131
+
132
+ ## Technical Details
133
+
134
+ ### How It Works:
135
+ 1. Audio is loaded and resampled to 16kHz
136
+ 2. Converted to mel-spectrograms
137
+ 3. Processed by the SpecTTTra transformer model
138
+ 4. Output logit is converted to probability using sigmoid
139
+ 5. Classification: `prob < 0.5` = Real, `prob β‰₯ 0.5` = Fake
140
+
141
+ ### Testing Your Music
142
+
143
+ 1. Get AI-generated samples: Download from Suno, Udio, or other AI music platforms
144
+ 2. Get real music samples: Use traditional human-created songs
145
+ 3. Run the classifier: Compare results to see how well it detects AI vs human music
146
+
147
+ ## Expected Performance
148
+ - High accuracy on detecting modern AI-generated music
149
+ - Works best with full songs (up to 120 seconds)
150
+ - Optimized for music from platforms like Suno and Udio
151
+
152
+ Note: This model was trained specifically for detecting AI-generated songs, not just AI vocals over real instrumentals. It analyzes the entire musical composition.
api.py ADDED
@@ -0,0 +1,385 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Madverse Music API
4
+ AI Music Detection Service
5
+ """
6
+
7
+ from fastapi import FastAPI, HTTPException, BackgroundTasks, Header, Depends
8
+ from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
9
+ from pydantic import BaseModel, HttpUrl
10
+ import torch
11
+ import librosa
12
+ import tempfile
13
+ import os
14
+ import requests
15
+ from pathlib import Path
16
+ import time
17
+ from typing import Optional, Annotated, List
18
+ import uvicorn
19
+ import asyncio
20
+
21
+ # Initialize FastAPI app
22
+ app = FastAPI(
23
+ title="Madverse Music API",
24
+ description="AI-powered music detection API to identify AI-generated vs human-created music",
25
+ version="1.0.0",
26
+ docs_url="/",
27
+ redoc_url="/docs"
28
+ )
29
+
30
+ # API Key Configuration
31
+ API_KEY = os.getenv("MADVERSE_API_KEY", "madverse-music-api-key-2024") # Default key for demo
32
+
33
+ # Global model variable
34
+ model = None
35
+
36
+ async def verify_api_key(x_api_key: Annotated[str | None, Header()] = None):
37
+ """Verify API key from header"""
38
+ if x_api_key is None:
39
+ raise HTTPException(
40
+ status_code=401,
41
+ detail="Missing API key. Please provide a valid X-API-Key header."
42
+ )
43
+ if x_api_key != API_KEY:
44
+ raise HTTPException(
45
+ status_code=401,
46
+ detail="Invalid API key. Please provide a valid X-API-Key header."
47
+ )
48
+ return x_api_key
49
+
50
+ class MusicAnalysisRequest(BaseModel):
51
+ urls: List[HttpUrl]
52
+
53
+ def check_api_key_first(request: MusicAnalysisRequest, x_api_key: Annotated[str | None, Header()] = None):
54
+ """Check API key before processing request"""
55
+ if x_api_key is None:
56
+ raise HTTPException(
57
+ status_code=401,
58
+ detail="Missing API key. Please provide a valid X-API-Key header."
59
+ )
60
+ if x_api_key != API_KEY:
61
+ raise HTTPException(
62
+ status_code=401,
63
+ detail="Invalid API key. Please provide a valid X-API-Key header."
64
+ )
65
+ return request
66
+
67
+ class FileAnalysisResult(BaseModel):
68
+ url: str
69
+ success: bool
70
+ classification: Optional[str] = None # "Real" or "Fake"
71
+ confidence: Optional[float] = None # 0.0 to 1.0
72
+ probability: Optional[float] = None # Raw sigmoid probability
73
+ raw_score: Optional[float] = None # Raw model output
74
+ duration: Optional[float] = None # Audio duration in seconds
75
+ message: str
76
+ processing_time: Optional[float] = None
77
+ error: Optional[str] = None
78
+
79
+ class MusicAnalysisResponse(BaseModel):
80
+ success: bool
81
+ total_files: int
82
+ successful_analyses: int
83
+ failed_analyses: int
84
+ results: List[FileAnalysisResult]
85
+ total_processing_time: float
86
+ message: str
87
+
88
+ class ErrorResponse(BaseModel):
89
+ success: bool
90
+ error: str
91
+ message: str
92
+
93
+ @app.on_event("startup")
94
+ async def load_model():
95
+ """Load the AI model on startup"""
96
+ global model
97
+ try:
98
+ from sonics import HFAudioClassifier
99
+ print("πŸ”„ Loading Madverse Music AI model...")
100
+ model = HFAudioClassifier.from_pretrained("awsaf49/sonics-spectttra-alpha-120s")
101
+ model.eval()
102
+ print("βœ… Model loaded successfully!")
103
+ except Exception as e:
104
+ print(f"❌ Failed to load model: {e}")
105
+ raise
106
+
107
+ def cleanup_file(file_path: str):
108
+ """Background task to cleanup temporary files"""
109
+ try:
110
+ if os.path.exists(file_path):
111
+ os.unlink(file_path)
112
+ except:
113
+ pass
114
+
115
+ def download_audio(url: str, max_size_mb: int = 100) -> str:
116
+ """Download audio file from URL with size validation"""
117
+ try:
118
+ # Check if URL is accessible
119
+ response = requests.head(str(url), timeout=10)
120
+
121
+ # Check content size
122
+ content_length = response.headers.get('Content-Length')
123
+ if content_length and int(content_length) > max_size_mb * 1024 * 1024:
124
+ raise HTTPException(
125
+ status_code=413,
126
+ detail=f"File too large. Maximum size: {max_size_mb}MB"
127
+ )
128
+
129
+ # Download file
130
+ response = requests.get(str(url), timeout=30, stream=True)
131
+ response.raise_for_status()
132
+
133
+ # Create temporary file
134
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.tmp') as tmp_file:
135
+ downloaded_size = 0
136
+ for chunk in response.iter_content(chunk_size=8192):
137
+ downloaded_size += len(chunk)
138
+ if downloaded_size > max_size_mb * 1024 * 1024:
139
+ os.unlink(tmp_file.name)
140
+ raise HTTPException(
141
+ status_code=413,
142
+ detail=f"File too large. Maximum size: {max_size_mb}MB"
143
+ )
144
+ tmp_file.write(chunk)
145
+
146
+ return tmp_file.name
147
+
148
+ except requests.exceptions.RequestException as e:
149
+ raise HTTPException(
150
+ status_code=400,
151
+ detail=f"Failed to download audio: {str(e)}"
152
+ )
153
+ except Exception as e:
154
+ raise HTTPException(
155
+ status_code=500,
156
+ detail=f"Error downloading file: {str(e)}"
157
+ )
158
+
159
+ def classify_audio(file_path: str) -> dict:
160
+ """Classify audio file using the AI model"""
161
+ try:
162
+ # Load audio (model uses 16kHz sample rate)
163
+ audio, sr = librosa.load(file_path, sr=16000)
164
+
165
+ # Convert to tensor and add batch dimension
166
+ audio_tensor = torch.FloatTensor(audio).unsqueeze(0)
167
+
168
+ # Get prediction
169
+ with torch.no_grad():
170
+ output = model(audio_tensor)
171
+
172
+ # Convert logit to probability using sigmoid
173
+ prob = torch.sigmoid(output).item()
174
+
175
+ # Classify: prob < 0.5 = Real, prob >= 0.5 = Fake
176
+ if prob < 0.5:
177
+ classification = "Real"
178
+ confidence = (1 - prob) * 2 # Convert to 0-1 scale
179
+ else:
180
+ classification = "Fake"
181
+ confidence = (prob - 0.5) * 2 # Convert to 0-1 scale
182
+
183
+ return {
184
+ "classification": classification,
185
+ "confidence": min(confidence, 1.0), # Cap at 1.0
186
+ "probability": prob,
187
+ "raw_score": output.item(),
188
+ "duration": len(audio) / sr
189
+ }
190
+
191
+ except Exception as e:
192
+ raise HTTPException(
193
+ status_code=500,
194
+ detail=f"Error analyzing audio: {str(e)}"
195
+ )
196
+
197
+ async def process_single_url(url: str) -> FileAnalysisResult:
198
+ """Process a single URL and return result"""
199
+ start_time = time.time()
200
+
201
+ try:
202
+ # Download audio file
203
+ temp_file = download_audio(url)
204
+
205
+ # Classify audio
206
+ result = classify_audio(temp_file)
207
+
208
+ # Calculate processing time
209
+ processing_time = time.time() - start_time
210
+
211
+ # Cleanup file in background
212
+ try:
213
+ os.unlink(temp_file)
214
+ except:
215
+ pass
216
+
217
+ # Prepare response
218
+ emoji = "🎀" if result["classification"] == "Real" else "πŸ€–"
219
+ message = f'{emoji} Detected as {result["classification"].lower()} music'
220
+
221
+ return FileAnalysisResult(
222
+ url=str(url),
223
+ success=True,
224
+ classification=result["classification"],
225
+ confidence=result["confidence"],
226
+ probability=result["probability"],
227
+ raw_score=result["raw_score"],
228
+ duration=result["duration"],
229
+ message=message,
230
+ processing_time=processing_time
231
+ )
232
+
233
+ except Exception as e:
234
+ processing_time = time.time() - start_time
235
+ error_msg = str(e)
236
+
237
+ return FileAnalysisResult(
238
+ url=str(url),
239
+ success=False,
240
+ message=f"❌ Failed to process: {error_msg}",
241
+ processing_time=processing_time,
242
+ error=error_msg
243
+ )
244
+
245
+ @app.post("/analyze", response_model=MusicAnalysisResponse)
246
+ async def analyze_music(
247
+ request: MusicAnalysisRequest = Depends(check_api_key_first)
248
+ ):
249
+ """
250
+ Analyze music from URL(s) to detect if it's AI-generated or human-created
251
+
252
+ - **urls**: Array of direct URLs to audio files (MP3, WAV, FLAC, M4A, OGG)
253
+ - Returns classification results for each file
254
+ - Processes files concurrently for better performance when multiple URLs provided
255
+ """
256
+ start_time = time.time()
257
+
258
+ if not model:
259
+ raise HTTPException(
260
+ status_code=503,
261
+ detail="Model not loaded. Please try again later."
262
+ )
263
+
264
+ if len(request.urls) > 50: # Limit processing
265
+ raise HTTPException(
266
+ status_code=400,
267
+ detail="Too many URLs. Maximum 50 files per request."
268
+ )
269
+
270
+ if len(request.urls) == 0:
271
+ raise HTTPException(
272
+ status_code=400,
273
+ detail="At least one URL is required."
274
+ )
275
+
276
+ try:
277
+ # Process all URLs concurrently with limited concurrency
278
+ semaphore = asyncio.Semaphore(5) # Limit to 5 concurrent downloads
279
+
280
+ async def process_with_semaphore(url):
281
+ async with semaphore:
282
+ return await process_single_url(str(url))
283
+
284
+ # Create tasks for all URLs
285
+ tasks = [process_with_semaphore(url) for url in request.urls]
286
+
287
+ # Wait for all tasks to complete
288
+ results = await asyncio.gather(*tasks, return_exceptions=True)
289
+
290
+ # Process results and handle any exceptions
291
+ processed_results = []
292
+ successful_count = 0
293
+ failed_count = 0
294
+
295
+ for i, result in enumerate(results):
296
+ if isinstance(result, Exception):
297
+ # Handle exception case
298
+ processed_results.append(FileAnalysisResult(
299
+ url=str(request.urls[i]),
300
+ success=False,
301
+ message=f"❌ Processing failed: {str(result)}",
302
+ error=str(result)
303
+ ))
304
+ failed_count += 1
305
+ else:
306
+ processed_results.append(result)
307
+ if result.success:
308
+ successful_count += 1
309
+ else:
310
+ failed_count += 1
311
+
312
+ # Calculate total processing time
313
+ total_processing_time = time.time() - start_time
314
+
315
+ # Prepare summary message
316
+ total_files = len(request.urls)
317
+ if total_files == 1:
318
+ # Single file message
319
+ if successful_count == 1:
320
+ message = processed_results[0].message
321
+ else:
322
+ message = processed_results[0].message
323
+ else:
324
+ # Multiple files message
325
+ if successful_count == total_files:
326
+ message = f"βœ… Successfully analyzed all {total_files} files"
327
+ elif successful_count > 0:
328
+ message = f"⚠️ Analyzed {successful_count}/{total_files} files successfully"
329
+ else:
330
+ message = f"❌ Failed to analyze any files"
331
+
332
+ return MusicAnalysisResponse(
333
+ success=successful_count > 0,
334
+ total_files=total_files,
335
+ successful_analyses=successful_count,
336
+ failed_analyses=failed_count,
337
+ results=processed_results,
338
+ total_processing_time=total_processing_time,
339
+ message=message
340
+ )
341
+
342
+ except Exception as e:
343
+ raise HTTPException(
344
+ status_code=500,
345
+ detail=f"Internal server error during processing: {str(e)}"
346
+ )
347
+
348
+ @app.get("/health")
349
+ async def health_check():
350
+ """Health check endpoint"""
351
+ return {
352
+ "status": "healthy",
353
+ "model_loaded": model is not None,
354
+ "service": "Madverse Music API"
355
+ }
356
+
357
+ @app.get("/info")
358
+ async def get_info():
359
+ """Get API information"""
360
+ return {
361
+ "name": "Madverse Music API",
362
+ "version": "1.0.0",
363
+ "description": "AI-powered music detection to identify AI-generated vs human-created music",
364
+ "model": "SpecTTTra-Ξ± (120s)",
365
+ "accuracy": {
366
+ "f1_score": 0.97,
367
+ "sensitivity": 0.96,
368
+ "specificity": 0.99
369
+ },
370
+ "supported_formats": ["MP3", "WAV", "FLAC", "M4A", "OGG"],
371
+ "max_file_size": "100MB",
372
+ "max_duration": "120 seconds",
373
+ "authentication": {
374
+ "required": True,
375
+ "type": "API Key",
376
+ "header": "X-API-Key",
377
+ "example": "X-API-Key: your-api-key-here"
378
+ },
379
+ "usage": {
380
+ "curl_example": "curl -X POST 'http://localhost:8000/analyze' -H 'X-API-Key: your-api-key' -H 'Content-Type: application/json' -d '{\"url\":\"https://example.com/song.mp3\"}'"
381
+ }
382
+ }
383
+
384
+ if __name__ == "__main__":
385
+ uvicorn.run(app, host="0.0.0.0", port=8000)
app.py ADDED
@@ -0,0 +1,451 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Madverse Music: AI Music Detection Web App
4
+ A Streamlit interface for testing AI-generated music detection
5
+ """
6
+
7
+ import streamlit as st
8
+ import torch
9
+ import librosa
10
+ import numpy as np
11
+ import tempfile
12
+ import os
13
+ from pathlib import Path
14
+ import time
15
+
16
+ # Configure the page
17
+ st.set_page_config(
18
+ page_title="Madverse Music: AI Music Detector",
19
+ page_icon="🎡",
20
+ layout="wide",
21
+ initial_sidebar_state="expanded"
22
+ )
23
+
24
+ # Custom CSS for styling
25
+ st.markdown("""
26
+ <style>
27
+ .main-header {
28
+ font-size: 3rem;
29
+ color: #1f77b4;
30
+ text-align: center;
31
+ margin-bottom: 2rem;
32
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
33
+ -webkit-background-clip: text;
34
+ -webkit-text-fill-color: transparent;
35
+ font-weight: bold;
36
+ }
37
+
38
+ .sub-header {
39
+ font-size: 1.2rem;
40
+ color: #666;
41
+ text-align: center;
42
+ margin-bottom: 3rem;
43
+ }
44
+
45
+ .result-box {
46
+ padding: 1.5rem;
47
+ border-radius: 10px;
48
+ margin: 1rem 0;
49
+ border-left: 5px solid;
50
+ }
51
+
52
+ .real-music {
53
+ background-color: #d4edda;
54
+ border-left-color: #28a745;
55
+ color: #155724;
56
+ }
57
+
58
+ .fake-music {
59
+ background-color: #f8d7da;
60
+ border-left-color: #dc3545;
61
+ color: #721c24;
62
+ }
63
+
64
+ .info-box {
65
+ background-color: #e3f2fd;
66
+ padding: 1rem;
67
+ border-radius: 8px;
68
+ border-left: 4px solid #2196f3;
69
+ margin: 1rem 0;
70
+ }
71
+
72
+ .metric-card {
73
+ background: white;
74
+ padding: 1rem;
75
+ border-radius: 8px;
76
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
77
+ text-align: center;
78
+ margin: 0.5rem;
79
+ }
80
+ </style>
81
+ """, unsafe_allow_html=True)
82
+
83
+ # Initialize session state
84
+ if 'model' not in st.session_state:
85
+ st.session_state.model = None
86
+ if 'model_loaded' not in st.session_state:
87
+ st.session_state.model_loaded = False
88
+
89
+ @st.cache_resource
90
+ def load_model():
91
+ """Load the AI model (cached for performance)"""
92
+ try:
93
+ from sonics import HFAudioClassifier
94
+ model = HFAudioClassifier.from_pretrained("awsaf49/sonics-spectttra-alpha-120s")
95
+ model.eval()
96
+ return model
97
+ except Exception as e:
98
+ st.error(f"Failed to load model: {e}")
99
+ return None
100
+
101
+ def classify_audio_file(audio_file, model):
102
+ """Classify uploaded audio file"""
103
+ try:
104
+ # Create temporary file
105
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.wav') as tmp_file:
106
+ tmp_file.write(audio_file.getvalue())
107
+ tmp_file_path = tmp_file.name
108
+
109
+ # Load audio (model uses 16kHz sample rate)
110
+ audio, sr = librosa.load(tmp_file_path, sr=16000)
111
+
112
+ # Convert to tensor and add batch dimension
113
+ audio_tensor = torch.FloatTensor(audio).unsqueeze(0)
114
+
115
+ # Get prediction
116
+ with torch.no_grad():
117
+ output = model(audio_tensor)
118
+
119
+ # Convert logit to probability using sigmoid
120
+ prob = torch.sigmoid(output).item()
121
+
122
+ # Classify: prob < 0.5 = Real, prob >= 0.5 = Fake
123
+ if prob < 0.5:
124
+ classification = "Real"
125
+ confidence = (1 - prob) * 2 # Convert to 0-1 scale
126
+ else:
127
+ classification = "Fake"
128
+ confidence = (prob - 0.5) * 2 # Convert to 0-1 scale
129
+
130
+ # Cleanup
131
+ os.unlink(tmp_file_path)
132
+
133
+ return {
134
+ "classification": classification,
135
+ "confidence": min(confidence, 1.0), # Cap at 1.0
136
+ "probability": prob,
137
+ "raw_score": output.item(),
138
+ "duration": len(audio) / sr
139
+ }
140
+
141
+ except Exception as e:
142
+ # Cleanup on error
143
+ if 'tmp_file_path' in locals() and os.path.exists(tmp_file_path):
144
+ os.unlink(tmp_file_path)
145
+ raise e
146
+
147
+ # Main UI
148
+ def main():
149
+ # Header
150
+ st.markdown('<h1 class="main-header">🎡 Madverse Music AI Detector</h1>', unsafe_allow_html=True)
151
+ st.markdown('<p class="sub-header">Upload audio files to detect if they\'re AI-generated or human-created</p>', unsafe_allow_html=True)
152
+
153
+ # Sidebar
154
+ with st.sidebar:
155
+ st.header("πŸ”¬ Model Information")
156
+
157
+ # Model loading status
158
+ if not st.session_state.model_loaded:
159
+ if st.button("πŸš€ Load AI Model", type="primary"):
160
+ with st.spinner("Loading Madverse Music AI model..."):
161
+ st.session_state.model = load_model()
162
+ if st.session_state.model:
163
+ st.session_state.model_loaded = True
164
+ st.success("βœ… Model loaded successfully!")
165
+ st.rerun()
166
+ else:
167
+ st.error("❌ Failed to load model")
168
+ else:
169
+ st.success("βœ… Model Ready")
170
+
171
+ # Model specs
172
+ st.markdown("""
173
+ **Model Details:**
174
+ - **Architecture**: SpecTTTra-Ξ±
175
+ - **Duration**: 120 seconds
176
+ - **Sample Rate**: 16kHz
177
+ - **F1 Score**: 97%
178
+ - **Sensitivity**: 96%
179
+ - **Specificity**: 99%
180
+ """)
181
+
182
+ if st.button("πŸ”„ Reload Model"):
183
+ st.session_state.model_loaded = False
184
+ st.session_state.model = None
185
+ st.cache_resource.clear()
186
+ st.rerun()
187
+
188
+ # Main content
189
+ if st.session_state.model_loaded:
190
+ st.markdown("### πŸ“ Upload Audio File")
191
+
192
+ # File uploader with bulk support
193
+ uploaded_files = st.file_uploader(
194
+ "Choose audio file(s)",
195
+ type=['wav', 'mp3', 'flac', 'm4a', 'ogg'],
196
+ accept_multiple_files=True,
197
+ help="Supported formats: WAV, MP3, FLAC, M4A, OGG (Max 200MB each). You can upload multiple files for bulk analysis."
198
+ )
199
+
200
+ if uploaded_files:
201
+ # Display summary info for multiple files
202
+ if len(uploaded_files) == 1:
203
+ # Single file - show detailed info
204
+ uploaded_file = uploaded_files[0]
205
+ col1, col2, col3 = st.columns(3)
206
+ with col1:
207
+ st.metric("πŸ“ Filename", uploaded_file.name)
208
+ with col2:
209
+ file_size_mb = uploaded_file.size / (1024 * 1024)
210
+ st.metric("πŸ“Š File Size", f"{file_size_mb:.2f} MB")
211
+ with col3:
212
+ st.metric("🎡 Format", uploaded_file.type)
213
+
214
+ # Audio player
215
+ st.audio(uploaded_file, format='audio/wav')
216
+ else:
217
+ # Multiple files - show summary
218
+ total_size = sum(f.size for f in uploaded_files) / (1024 * 1024)
219
+ col1, col2, col3 = st.columns(3)
220
+ with col1:
221
+ st.metric("πŸ“ Files Selected", len(uploaded_files))
222
+ with col2:
223
+ st.metric("πŸ“Š Total Size", f"{total_size:.2f} MB")
224
+ with col3:
225
+ formats = list(set(f.type.split('/')[-1].upper() for f in uploaded_files))
226
+ st.metric("🎡 Formats", ", ".join(formats))
227
+
228
+ # Show file list
229
+ with st.expander(f"πŸ“‹ File List ({len(uploaded_files)} files)"):
230
+ for i, file in enumerate(uploaded_files, 1):
231
+ size_mb = file.size / (1024 * 1024)
232
+ st.write(f"{i}. **{file.name}** ({size_mb:.2f} MB)")
233
+
234
+ # Analyze button
235
+ if len(uploaded_files) == 1:
236
+ button_text = "πŸ” Analyze Audio"
237
+ else:
238
+ button_text = f"πŸ” Analyze {len(uploaded_files)} Files"
239
+
240
+ if st.button(button_text, type="primary", use_container_width=True):
241
+ try:
242
+ if len(uploaded_files) == 1:
243
+ # Single file analysis
244
+ with st.spinner("🧠 Analyzing audio with AI..."):
245
+ start_time = time.time()
246
+ result = classify_audio_file(uploaded_files[0], st.session_state.model)
247
+ processing_time = time.time() - start_time
248
+
249
+ # Display results for single file
250
+ st.markdown("### 🎯 Analysis Results")
251
+
252
+ # Main result
253
+ if result["classification"] == "Real":
254
+ st.markdown(f"""
255
+ <div class="result-box real-music">
256
+ <h3>🎀 Human-Created Music</h3>
257
+ <p>This audio appears to be created by human artists.</p>
258
+ </div>
259
+ """, unsafe_allow_html=True)
260
+ else:
261
+ st.markdown(f"""
262
+ <div class="result-box fake-music">
263
+ <h3>πŸ€– AI-Generated Music</h3>
264
+ <p>This audio appears to be generated by AI (like Suno, Udio, etc.)</p>
265
+ </div>
266
+ """, unsafe_allow_html=True)
267
+
268
+ # Detailed metrics for single file
269
+ col1, col2, col3, col4 = st.columns(4)
270
+
271
+ with col1:
272
+ st.metric(
273
+ "🎯 Classification",
274
+ result["classification"],
275
+ help="AI model's prediction"
276
+ )
277
+
278
+ with col2:
279
+ confidence_pct = result["confidence"] * 100
280
+ st.metric(
281
+ "πŸ“Š Confidence",
282
+ f"{confidence_pct:.1f}%",
283
+ help="How confident the model is in its prediction"
284
+ )
285
+
286
+ with col3:
287
+ st.metric(
288
+ "⏱️ Duration",
289
+ f"{result['duration']:.1f}s",
290
+ help="Audio file duration"
291
+ )
292
+
293
+ with col4:
294
+ st.metric(
295
+ "⚑ Processing Time",
296
+ f"{processing_time:.2f}s",
297
+ help="Time taken to analyze"
298
+ )
299
+
300
+ # Technical details (expandable)
301
+ with st.expander("πŸ”¬ Technical Details"):
302
+ col1, col2 = st.columns(2)
303
+ with col1:
304
+ st.metric("Raw Sigmoid Probability", f"{result['probability']:.4f}")
305
+ st.metric("Raw Model Output", f"{result['raw_score']:.4f}")
306
+ with col2:
307
+ st.info("""
308
+ **How it works:**
309
+ - Audio is resampled to 16kHz
310
+ - Processed by SpecTTTra transformer
311
+ - Output < 0.5 = Real, β‰₯ 0.5 = Fake
312
+ """)
313
+
314
+ # Progress bars for visualization
315
+ st.markdown("### πŸ“ˆ Confidence Visualization")
316
+ if result["classification"] == "Real":
317
+ st.progress(result["confidence"], text=f"Human Confidence: {result['confidence']:.1%}")
318
+ else:
319
+ st.progress(result["confidence"], text=f"AI Confidence: {result['confidence']:.1%}")
320
+
321
+ else:
322
+ # Multiple files analysis
323
+ progress_bar = st.progress(0, text="🧠 Analyzing files...")
324
+ results = []
325
+ start_time = time.time()
326
+
327
+ for i, file in enumerate(uploaded_files):
328
+ progress = (i + 1) / len(uploaded_files)
329
+ progress_bar.progress(progress, text=f"🧠 Analyzing file {i+1}/{len(uploaded_files)}: {file.name}")
330
+
331
+ try:
332
+ file_result = classify_audio_file(file, st.session_state.model)
333
+ file_result["filename"] = file.name
334
+ file_result["success"] = True
335
+ results.append(file_result)
336
+ except Exception as e:
337
+ results.append({
338
+ "filename": file.name,
339
+ "success": False,
340
+ "error": str(e)
341
+ })
342
+
343
+ total_time = time.time() - start_time
344
+ progress_bar.progress(1.0, text="βœ… Analysis complete!")
345
+
346
+ # Display bulk results
347
+ st.markdown("### 🎯 Bulk Analysis Results")
348
+
349
+ # Summary metrics
350
+ successful = sum(1 for r in results if r["success"])
351
+ failed = len(results) - successful
352
+
353
+ col1, col2, col3, col4 = st.columns(4)
354
+ with col1:
355
+ st.metric("πŸ“ Total Files", len(results))
356
+ with col2:
357
+ st.metric("βœ… Successful", successful)
358
+ with col3:
359
+ st.metric("❌ Failed", failed)
360
+ with col4:
361
+ st.metric("⏱️ Total Time", f"{total_time:.1f}s")
362
+
363
+ # Results breakdown
364
+ if successful > 0:
365
+ real_count = sum(1 for r in results if r.get("success") and r.get("classification") == "Real")
366
+ fake_count = sum(1 for r in results if r.get("success") and r.get("classification") == "Fake")
367
+
368
+ col1, col2 = st.columns(2)
369
+ with col1:
370
+ st.markdown(f"""
371
+ <div class="result-box real-music">
372
+ <h4>🎀 Human Music: {real_count} files</h4>
373
+ </div>
374
+ """, unsafe_allow_html=True)
375
+ with col2:
376
+ st.markdown(f"""
377
+ <div class="result-box fake-music">
378
+ <h4>πŸ€– AI Music: {fake_count} files</h4>
379
+ </div>
380
+ """, unsafe_allow_html=True)
381
+
382
+ # Detailed results table
383
+ st.markdown("### πŸ“‹ Detailed Results")
384
+
385
+ for i, result in enumerate(results, 1):
386
+ with st.expander(f"πŸ“„ {i}. {result['filename']}" +
387
+ (" βœ…" if result["success"] else " ❌")):
388
+ if result["success"]:
389
+ col1, col2, col3 = st.columns(3)
390
+ with col1:
391
+ st.metric("Classification", result["classification"])
392
+ with col2:
393
+ st.metric("Confidence", f"{result['confidence']:.1%}")
394
+ with col3:
395
+ st.metric("Duration", f"{result['duration']:.1f}s")
396
+
397
+ # Confidence bar
398
+ if result["classification"] == "Real":
399
+ st.progress(result["confidence"],
400
+ text=f"Human Confidence: {result['confidence']:.1%}")
401
+ else:
402
+ st.progress(result["confidence"],
403
+ text=f"AI Confidence: {result['confidence']:.1%}")
404
+ else:
405
+ st.error(f"❌ Analysis failed: {result['error']}")
406
+
407
+ except Exception as e:
408
+ st.error(f"❌ Error analyzing audio: {str(e)}")
409
+
410
+ else:
411
+ # Model not loaded
412
+ st.markdown("""
413
+ <div class="info-box">
414
+ <h3>πŸš€ Getting Started</h3>
415
+ <p>Click "Load AI Model" in the sidebar to begin analyzing audio files.</p>
416
+ <p><strong>Note:</strong> The first load may take a moment as the model downloads.</p>
417
+ </div>
418
+ """, unsafe_allow_html=True)
419
+
420
+ # Show supported formats
421
+ st.markdown("### πŸ“ Supported Audio Formats")
422
+ col1, col2, col3, col4, col5 = st.columns(5)
423
+ formats = [
424
+ ("🎡 WAV", ".wav"),
425
+ ("🎧 MP3", ".mp3"),
426
+ ("πŸ’Ώ FLAC", ".flac"),
427
+ ("πŸ“± M4A", ".m4a"),
428
+ ("🎼 OGG", ".ogg")
429
+ ]
430
+
431
+ for i, (icon_name, ext) in enumerate(formats):
432
+ with [col1, col2, col3, col4, col5][i]:
433
+ st.markdown(f"""
434
+ <div class="metric-card">
435
+ <h4>{icon_name}</h4>
436
+ <p>{ext}</p>
437
+ </div>
438
+ """, unsafe_allow_html=True)
439
+
440
+ # Footer
441
+ st.markdown("---")
442
+ st.markdown("""
443
+ <div style="text-align: center; color: #666; padding: 1rem;">
444
+ <p>🎡 <strong>Madverse Music</strong> - AI Music Detection Technology</p>
445
+ <p>Visit <a href="https://madverse.co" target="_blank">madverse.co</a> for more AI music tools</p>
446
+ <p><em>This tool is designed for research and testing purposes.</em></p>
447
+ </div>
448
+ """, unsafe_allow_html=True)
449
+
450
+ if __name__ == "__main__":
451
+ main()
app_hf.py ADDED
@@ -0,0 +1,272 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Madverse Music - Hugging Face Spaces Version
4
+ Streamlit app for HF Spaces deployment
5
+ """
6
+
7
+ import streamlit as st
8
+ import torch
9
+ import librosa
10
+ import tempfile
11
+ import os
12
+ import time
13
+ import numpy as np
14
+
15
+ # Import the sonics library for model loading
16
+ try:
17
+ from sonics import HFAudioClassifier
18
+ except ImportError:
19
+ st.error("Sonics library not found. Please install it first.")
20
+ st.stop()
21
+
22
+ # Global model variable
23
+ model = None
24
+
25
+ # Page configuration
26
+ st.set_page_config(
27
+ page_title="Madverse Music: AI Music Detector",
28
+ page_icon="🎡",
29
+ layout="wide",
30
+ initial_sidebar_state="expanded"
31
+ )
32
+
33
+ # Custom CSS
34
+ st.markdown("""
35
+ <style>
36
+ .main-header {
37
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
38
+ padding: 1rem;
39
+ border-radius: 10px;
40
+ color: white;
41
+ text-align: center;
42
+ margin-bottom: 2rem;
43
+ }
44
+ .result-box {
45
+ padding: 1rem;
46
+ border-radius: 10px;
47
+ margin: 1rem 0;
48
+ border-left: 5px solid;
49
+ }
50
+ .real-music {
51
+ background-color: #d4edda;
52
+ border-left-color: #28a745;
53
+ }
54
+ .fake-music {
55
+ background-color: #f8d7da;
56
+ border-left-color: #dc3545;
57
+ }
58
+ </style>
59
+ """, unsafe_allow_html=True)
60
+
61
+ @st.cache_resource
62
+ def load_model():
63
+ """Load the model with caching for HF Spaces"""
64
+ try:
65
+ with st.spinner("Loading AI model... This may take a moment..."):
66
+ # Use the same loading method as the working API
67
+ model = HFAudioClassifier.from_pretrained("awsaf49/sonics-spectttra-alpha-120s")
68
+ model.eval()
69
+ return model
70
+ except Exception as e:
71
+ st.error(f"Failed to load model: {str(e)}")
72
+ return None
73
+
74
+ def process_audio(audio_file, model):
75
+ """Process audio file and return classification"""
76
+ try:
77
+ # Save uploaded file temporarily
78
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.wav') as tmp_file:
79
+ tmp_file.write(audio_file.read())
80
+ tmp_path = tmp_file.name
81
+
82
+ # Load audio (model uses 16kHz sample rate)
83
+ audio, sr = librosa.load(tmp_path, sr=16000)
84
+
85
+ # Convert to tensor and add batch dimension
86
+ audio_tensor = torch.FloatTensor(audio).unsqueeze(0)
87
+
88
+ # Get prediction using the same pattern as working API
89
+ with torch.no_grad():
90
+ output = model(audio_tensor)
91
+
92
+ # Convert logit to probability using sigmoid
93
+ probability = torch.sigmoid(output).item()
94
+
95
+ # Classify: prob < 0.5 = Real, prob >= 0.5 = Fake
96
+ if probability < 0.5:
97
+ classification = "Real"
98
+ confidence = (1 - probability) * 2 # Convert to 0-1 scale
99
+ else:
100
+ classification = "Fake"
101
+ confidence = (probability - 0.5) * 2 # Convert to 0-1 scale
102
+
103
+ # Calculate duration
104
+ duration = len(audio) / sr
105
+
106
+ # Clean up
107
+ os.unlink(tmp_path)
108
+
109
+ return {
110
+ 'classification': classification,
111
+ 'confidence': min(confidence, 1.0), # Cap at 1.0
112
+ 'probability': probability,
113
+ 'raw_score': output.item(),
114
+ 'duration': duration,
115
+ 'success': True
116
+ }
117
+
118
+ except Exception as e:
119
+ # Clean up on error
120
+ if 'tmp_path' in locals():
121
+ try:
122
+ os.unlink(tmp_path)
123
+ except:
124
+ pass
125
+ return {
126
+ 'success': False,
127
+ 'error': str(e)
128
+ }
129
+
130
+ def main():
131
+ # Header
132
+ st.markdown("""
133
+ <div class="main-header">
134
+ <h1>Madverse Music: AI Music Detector</h1>
135
+ <p>Detect AI-generated music vs human-created music using advanced AI technology</p>
136
+ </div>
137
+ """, unsafe_allow_html=True)
138
+
139
+ # Sidebar
140
+ with st.sidebar:
141
+ st.markdown("### About")
142
+ st.markdown("""
143
+ This AI model can detect whether music is:
144
+ - **Real**: Human-created music
145
+ - **Fake**: AI-generated music (Suno, Udio, etc.)
146
+
147
+ **Model**: SpecTTTra-Ξ± (120s)
148
+ **Accuracy**: 97% F1 score
149
+ **Max Duration**: 120 seconds
150
+ """)
151
+
152
+ st.markdown("### Supported Formats")
153
+ st.markdown("- WAV (.wav)")
154
+ st.markdown("- MP3 (.mp3)")
155
+ st.markdown("- FLAC (.flac)")
156
+ st.markdown("- M4A (.m4a)")
157
+ st.markdown("- OGG (.ogg)")
158
+
159
+ st.markdown("### Links")
160
+ st.markdown("- [Madverse Website](https://madverse.co)")
161
+ st.markdown("- [GitHub Repository](#)")
162
+
163
+ # Load model
164
+ model = load_model()
165
+
166
+ if model is None:
167
+ st.error("Model failed to load. Please refresh the page.")
168
+ return
169
+
170
+ st.success("AI model loaded successfully!")
171
+
172
+ # File upload
173
+ st.markdown("### Upload Audio File")
174
+ uploaded_file = st.file_uploader(
175
+ "Choose an audio file",
176
+ type=['wav', 'mp3', 'flac', 'm4a', 'ogg'],
177
+ help="Upload an audio file to analyze (max 120 seconds)"
178
+ )
179
+
180
+ if uploaded_file is not None:
181
+ # Display file info
182
+ st.markdown("### File Information")
183
+ col1, col2, col3 = st.columns(3)
184
+
185
+ with col1:
186
+ st.metric("Filename", uploaded_file.name)
187
+ with col2:
188
+ st.metric("File Size", f"{uploaded_file.size / 1024:.1f} KB")
189
+ with col3:
190
+ st.metric("Format", uploaded_file.type)
191
+
192
+ # Audio player
193
+ st.markdown("### Preview")
194
+ st.audio(uploaded_file)
195
+
196
+ # Analysis button
197
+ if st.button("Analyze Audio", type="primary", use_container_width=True):
198
+ try:
199
+ with st.spinner("Analyzing audio... This may take a few seconds..."):
200
+ # Reset file pointer
201
+ uploaded_file.seek(0)
202
+
203
+ # Process audio
204
+ start_time = time.time()
205
+ result = process_audio(uploaded_file, model)
206
+ processing_time = time.time() - start_time
207
+
208
+ if not result['success']:
209
+ st.error(f"Error processing audio: {result['error']}")
210
+ return
211
+
212
+ # Display results
213
+ st.markdown("### Analysis Results")
214
+
215
+ classification = result['classification']
216
+ confidence = result['confidence']
217
+
218
+ # Result box
219
+ if classification == "Real":
220
+ st.markdown(f"""
221
+ <div class="result-box real-music">
222
+ <h3>Result: Human-Created Music</h3>
223
+ <p><strong>Classification:</strong> {classification}</p>
224
+ <p><strong>Confidence:</strong> {confidence:.1%}</p>
225
+ <p><strong>Message:</strong> This appears to be human-created music!</p>
226
+ </div>
227
+ """, unsafe_allow_html=True)
228
+ else:
229
+ st.markdown(f"""
230
+ <div class="result-box fake-music">
231
+ <h3>Result: AI-Generated Music</h3>
232
+ <p><strong>Classification:</strong> {classification}</p>
233
+ <p><strong>Confidence:</strong> {confidence:.1%}</p>
234
+ <p><strong>Message:</strong> This appears to be AI-generated music!</p>
235
+ </div>
236
+ """, unsafe_allow_html=True)
237
+
238
+ # Detailed metrics
239
+ with st.expander("Detailed Metrics"):
240
+ col1, col2, col3 = st.columns(3)
241
+
242
+ with col1:
243
+ st.metric("Confidence", f"{confidence:.1%}")
244
+ with col2:
245
+ st.metric("Probability", f"{result['probability']:.3f}")
246
+ with col3:
247
+ st.metric("Processing Time", f"{processing_time:.2f}s")
248
+
249
+ if result['duration'] > 0:
250
+ st.metric("Duration", f"{result['duration']:.1f}s")
251
+
252
+ st.markdown("**Interpretation:**")
253
+ st.markdown("""
254
+ - **Probability < 0.5**: Classified as Real (human-created)
255
+ - **Probability β‰₯ 0.5**: Classified as Fake (AI-generated)
256
+ - **Confidence**: How certain the model is about its prediction
257
+ """)
258
+
259
+ except Exception as e:
260
+ st.error(f"Error processing audio: {str(e)}")
261
+
262
+ # Footer
263
+ st.markdown("---")
264
+ st.markdown("""
265
+ <div style="text-align: center; color: #666;">
266
+ <p>Powered by <strong>Madverse Music</strong> | Built with Streamlit & PyTorch</p>
267
+ <p>This tool is for research and educational purposes. Results may vary depending on audio quality.</p>
268
+ </div>
269
+ """, unsafe_allow_html=True)
270
+
271
+ if __name__ == "__main__":
272
+ main()
classify_audio.py ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Simple Audio Classifier using Madverse Music AI Model
4
+ Usage: python classify_audio.py <audio_file_path>
5
+ """
6
+
7
+ import sys
8
+ import os
9
+ from pathlib import Path
10
+
11
+ def load_model():
12
+ """Load the Madverse Music AI model"""
13
+ try:
14
+ from sonics import HFAudioClassifier
15
+ print("πŸ”„ Loading Madverse Music AI model...")
16
+ model = HFAudioClassifier.from_pretrained("awsaf49/sonics-spectttra-alpha-120s")
17
+ print("βœ… Model loaded successfully!")
18
+ return model
19
+ except Exception as e:
20
+ print(f"❌ Error loading model: {e}")
21
+ return None
22
+
23
+ def classify_audio(model, audio_path):
24
+ """Classify a single audio file"""
25
+ try:
26
+ import librosa
27
+ import torch
28
+
29
+ print(f"🎡 Analyzing: {audio_path}")
30
+
31
+ # Load audio file (model uses 16kHz sample rate)
32
+ audio, sr = librosa.load(audio_path, sr=16000)
33
+
34
+ # Convert to tensor and add batch dimension
35
+ audio_tensor = torch.FloatTensor(audio).unsqueeze(0)
36
+
37
+ # Set model to evaluation mode
38
+ model.eval()
39
+
40
+ # Get prediction
41
+ with torch.no_grad():
42
+ output = model(audio_tensor)
43
+
44
+ # Convert logit to probability using sigmoid
45
+ prob = torch.sigmoid(output).item()
46
+
47
+ # Classify: prob < 0.5 = Real, prob >= 0.5 = Fake
48
+ if prob < 0.5:
49
+ result = "Real"
50
+ emoji = "🎀"
51
+ description = "Human-created music"
52
+ confidence = (1 - prob) * 2 # Convert to 0-1 scale
53
+ else:
54
+ result = "Fake"
55
+ emoji = "πŸ€–"
56
+ description = "AI-generated music"
57
+ confidence = (prob - 0.5) * 2 # Convert to 0-1 scale
58
+
59
+ print(f"{emoji} Result: {result} ({description})")
60
+ print(f" Confidence: {confidence:.2f} | Raw output: {output.item():.3f}")
61
+ return result
62
+
63
+ except Exception as e:
64
+ print(f"❌ Error classifying {audio_path}: {e}")
65
+ return None
66
+
67
+ def classify_multiple_files(model, directory_path="."):
68
+ """Classify all audio files in a directory"""
69
+ audio_extensions = ['.wav', '.mp3', '.flac', '.m4a', '.ogg']
70
+ directory = Path(directory_path)
71
+
72
+ audio_files = []
73
+ for ext in audio_extensions:
74
+ audio_files.extend(list(directory.glob(f'*{ext}')))
75
+
76
+ if not audio_files:
77
+ print(f"No audio files found in {directory}")
78
+ return
79
+
80
+ print(f"Found {len(audio_files)} audio file(s) to classify:")
81
+ print("=" * 50)
82
+
83
+ results = {}
84
+ for audio_file in audio_files:
85
+ result = classify_audio(model, str(audio_file))
86
+ if result:
87
+ results[str(audio_file)] = result
88
+ print()
89
+
90
+ # Summary
91
+ if results:
92
+ print("πŸ“Š Summary:")
93
+ print("=" * 30)
94
+ real_count = sum(1 for r in results.values() if r.lower() == 'real')
95
+ fake_count = len(results) - real_count
96
+ print(f"🎀 Real music: {real_count}")
97
+ print(f"πŸ€– AI-generated: {fake_count}")
98
+
99
+ def main():
100
+ """Main function"""
101
+ print("🎡 Madverse Music: AI Audio Classifier")
102
+ print("=" * 40)
103
+
104
+ # Load model
105
+ model = load_model()
106
+ if not model:
107
+ return
108
+
109
+ if len(sys.argv) > 1:
110
+ # Classify specific file
111
+ audio_path = sys.argv[1]
112
+ if os.path.exists(audio_path):
113
+ classify_audio(model, audio_path)
114
+ else:
115
+ print(f"❌ File not found: {audio_path}")
116
+ else:
117
+ # Classify all files in current directory
118
+ print("\nNo specific file provided. Scanning current directory...")
119
+ classify_multiple_files(model)
120
+
121
+ if __name__ == "__main__":
122
+ main()
config.json ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "audio": {
3
+ "max_len": 1920000,
4
+ "max_time": 120,
5
+ "normalize": true,
6
+ "random_sampling": true,
7
+ "sample_rate": 16000,
8
+ "skip_time": false
9
+ },
10
+ "augment": {
11
+ "freq_mask_param": 8,
12
+ "mixup_alpha": 2.5,
13
+ "mixup_p": 0.5,
14
+ "n_freq_masks": 1,
15
+ "n_time_masks": 2,
16
+ "time_freq_mask_p": 0.5,
17
+ "time_mask_param": 8
18
+ },
19
+ "dataset": {
20
+ "test_dataframe": "test.csv",
21
+ "train_dataframe": "train.csv",
22
+ "valid_dataframe": "valid.csv"
23
+ },
24
+ "environment": {
25
+ "mixed_precision": true,
26
+ "num_workers": 2,
27
+ "seed": 42
28
+ },
29
+ "experiment_name": "spectttra_alpha-t=120",
30
+ "logger": {
31
+ "primary_metric": "f1",
32
+ "project": "sonics"
33
+ },
34
+ "loss": {
35
+ "label_smoothing": 0.02,
36
+ "name": "BCEWithLogitsLoss"
37
+ },
38
+ "melspec": {
39
+ "f_max": 8000,
40
+ "f_min": 20,
41
+ "hop_length": 512,
42
+ "n_fft": 2048,
43
+ "n_mels": 128,
44
+ "norm": "mean_std",
45
+ "power": 2,
46
+ "top_db": 80,
47
+ "win_length": 2048
48
+ },
49
+ "model": {
50
+ "attn_drop_rate": 0.1,
51
+ "embed_dim": 384,
52
+ "f_clip": 1,
53
+ "input_shape": [
54
+ 128,
55
+ 3744
56
+ ],
57
+ "mlp_ratio": 2.67,
58
+ "name": "SpecTTTra",
59
+ "num_heads": 6,
60
+ "num_layers": 12,
61
+ "pe_learnable": true,
62
+ "pos_drop_rate": 0.1,
63
+ "pre_norm": true,
64
+ "proj_drop_rate": 0.0,
65
+ "resume": null,
66
+ "t_clip": 3,
67
+ "use_init_weights": false
68
+ },
69
+ "num_classes": 1,
70
+ "optimizer": {
71
+ "clip_grad_norm": 5.0,
72
+ "grad_accum_steps": 1,
73
+ "momentum": 0.9,
74
+ "opt": "adamw",
75
+ "opt_betas": [
76
+ 0.9,
77
+ 0.999
78
+ ],
79
+ "opt_eps": 1e-08,
80
+ "weight_decay": 0.05
81
+ },
82
+ "scheduler": {
83
+ "decay_rate": 0.1,
84
+ "lr": 0.0005,
85
+ "lr_base": 0.001,
86
+ "lr_base_scale": "linear",
87
+ "lr_base_size": 256,
88
+ "min_lr": 0.0,
89
+ "sched": "cosine",
90
+ "warmup_epochs": 5,
91
+ "warmup_lr": 1e-06
92
+ },
93
+ "training": {
94
+ "batch_size": 96,
95
+ "epochs": 50
96
+ },
97
+ "validation": {
98
+ "batch_size": 96
99
+ }
100
+ }
deploy.ps1 ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Madverse Music Deployment Script for Windows
2
+ # Run with: .\deploy.ps1
3
+
4
+ Write-Host "🎡 Deploying Madverse Music API..." -ForegroundColor Cyan
5
+
6
+ # Check if Docker is installed
7
+ try {
8
+ docker --version | Out-Null
9
+ Write-Host "βœ… Docker found" -ForegroundColor Green
10
+ } catch {
11
+ Write-Host "❌ Docker is not installed. Please install Docker Desktop first." -ForegroundColor Red
12
+ exit 1
13
+ }
14
+
15
+ # Check if docker-compose is installed
16
+ try {
17
+ docker-compose --version | Out-Null
18
+ Write-Host "βœ… Docker Compose found" -ForegroundColor Green
19
+ } catch {
20
+ Write-Host "❌ Docker Compose is not installed. Please install Docker Desktop with Compose." -ForegroundColor Red
21
+ exit 1
22
+ }
23
+
24
+ # Set environment variables
25
+ if (-not $env:MADVERSE_API_KEY) {
26
+ $env:MADVERSE_API_KEY = "madverse-music-api-key-2024"
27
+ }
28
+
29
+ Write-Host "πŸ”§ Building Docker image..." -ForegroundColor Yellow
30
+ docker-compose build
31
+
32
+ if ($LASTEXITCODE -ne 0) {
33
+ Write-Host "❌ Failed to build Docker image" -ForegroundColor Red
34
+ exit 1
35
+ }
36
+
37
+ Write-Host "πŸš€ Starting services..." -ForegroundColor Yellow
38
+ docker-compose up -d
39
+
40
+ if ($LASTEXITCODE -ne 0) {
41
+ Write-Host "❌ Failed to start services" -ForegroundColor Red
42
+ exit 1
43
+ }
44
+
45
+ Write-Host "⏳ Waiting for services to be healthy..." -ForegroundColor Yellow
46
+ Start-Sleep -Seconds 30
47
+
48
+ # Test the API
49
+ Write-Host "πŸ§ͺ Testing API health..." -ForegroundColor Yellow
50
+ try {
51
+ $response = Invoke-WebRequest -Uri "http://localhost:8000/health" -Method GET -TimeoutSec 10
52
+ if ($response.StatusCode -eq 200) {
53
+ Write-Host "βœ… API is healthy and running!" -ForegroundColor Green
54
+ Write-Host "πŸ“ API URL: http://localhost:8000" -ForegroundColor Cyan
55
+ Write-Host "πŸ“– API Docs: http://localhost:8000/" -ForegroundColor Cyan
56
+ Write-Host "πŸ”‘ API Key: $env:MADVERSE_API_KEY" -ForegroundColor Cyan
57
+ } else {
58
+ throw "Health check failed"
59
+ }
60
+ } catch {
61
+ Write-Host "❌ API health check failed. Check logs:" -ForegroundColor Red
62
+ docker-compose logs madverse-api
63
+ exit 1
64
+ }
65
+
66
+ Write-Host ""
67
+ Write-Host "🎯 Quick Test Command:" -ForegroundColor Yellow
68
+ Write-Host "Invoke-RestMethod -Uri 'http://localhost:8000/analyze' -Method POST -Headers @{'X-API-Key'='$env:MADVERSE_API_KEY'; 'Content-Type'='application/json'} -Body '{`"urls`": [`"https://example.com/song.mp3`"]}'" -ForegroundColor Gray
deploy.sh ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # Madverse Music Deployment Script
4
+
5
+ set -e
6
+
7
+ echo "🎡 Deploying Madverse Music API..."
8
+
9
+ # Check if Docker is installed
10
+ if ! command -v docker &> /dev/null; then
11
+ echo "❌ Docker is not installed. Please install Docker first."
12
+ exit 1
13
+ fi
14
+
15
+ # Check if docker-compose is installed
16
+ if ! command -v docker-compose &> /dev/null; then
17
+ echo "❌ Docker Compose is not installed. Please install Docker Compose first."
18
+ exit 1
19
+ fi
20
+
21
+ # Set environment variables
22
+ export MADVERSE_API_KEY=${MADVERSE_API_KEY:-madverse-music-api-key-2024}
23
+
24
+ echo "πŸ”§ Building Docker image..."
25
+ docker-compose build
26
+
27
+ echo "πŸš€ Starting services..."
28
+ docker-compose up -d
29
+
30
+ echo "⏳ Waiting for services to be healthy..."
31
+ sleep 30
32
+
33
+ # Test the API
34
+ echo "πŸ§ͺ Testing API health..."
35
+ if curl -f http://localhost:8000/health > /dev/null 2>&1; then
36
+ echo "βœ… API is healthy and running!"
37
+ echo "πŸ“ API URL: http://localhost:8000"
38
+ echo "πŸ“– API Docs: http://localhost:8000/"
39
+ echo "πŸ”‘ API Key: $MADVERSE_API_KEY"
40
+ else
41
+ echo "❌ API health check failed. Check logs:"
42
+ docker-compose logs madverse-api
43
+ exit 1
44
+ fi
45
+
46
+ echo ""
47
+ echo "🎯 Quick Test:"
48
+ echo "curl -X POST 'http://localhost:8000/analyze' \\"
49
+ echo " -H 'X-API-Key: $MADVERSE_API_KEY' \\"
50
+ echo " -H 'Content-Type: application/json' \\"
51
+ echo " -d '{\"urls\": [\"https://example.com/song.mp3\"]}'"
docker-compose.yml ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ madverse-api:
5
+ build: .
6
+ ports:
7
+ - "8000:8000"
8
+ environment:
9
+ - MADVERSE_API_KEY=${MADVERSE_API_KEY:-madverse-music-api-key-2024}
10
+ volumes:
11
+ # Optional: Mount model files if you want to update them without rebuilding
12
+ - ./pytorch_model.bin:/app/pytorch_model.bin:ro
13
+ - ./config.json:/app/config.json:ro
14
+ restart: unless-stopped
15
+ healthcheck:
16
+ test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
17
+ interval: 30s
18
+ timeout: 10s
19
+ retries: 3
20
+ start_period: 60s
21
+
22
+ # Optional: Add nginx proxy for production
23
+ nginx:
24
+ image: nginx:alpine
25
+ ports:
26
+ - "80:80"
27
+ - "443:443"
28
+ volumes:
29
+ - ./nginx.conf:/etc/nginx/nginx.conf:ro
30
+ depends_on:
31
+ - madverse-api
32
+ restart: unless-stopped
33
+ profiles:
34
+ - production
env.example ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Madverse Music API Configuration
2
+
3
+ # API Authentication
4
+ MADVERSE_API_KEY=your-secure-api-key-here
5
+
6
+ # Optional: Model Configuration (if using custom model paths)
7
+ MODEL_PATH=./pytorch_model.bin
8
+ CONFIG_PATH=./config.json
9
+
10
+ # Optional: Server Configuration
11
+ HOST=0.0.0.0
12
+ PORT=8000
13
+
14
+ # Optional: Logging
15
+ LOG_LEVEL=INFO
16
+
17
+ # Optional: Performance Tuning
18
+ MAX_WORKERS=4
19
+ TIMEOUT_SECONDS=300
pytorch_model.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:082df7e387041b654315656efb891c07f831dcb01f9c047b74d33b2777a8373b
3
+ size 75315274
requirements.txt CHANGED
@@ -1,3 +1,8 @@
1
- altair
2
- pandas
3
- streamlit
 
 
 
 
 
 
1
+ fastapi==0.104.1
2
+ uvicorn==0.24.0
3
+ streamlit>=1.28.0
4
+ torch>=2.0.0
5
+ librosa>=0.9.0
6
+ requests>=2.25.0
7
+ pydantic>=2.0.0
8
+ git+https://github.com/awsaf49/sonics.git
requirements_hf.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ streamlit>=1.28.0
2
+ torch>=2.0.0
3
+ librosa>=0.9.0
4
+ numpy>=1.24.0
5
+ soundfile>=0.12.0
6
+ fastapi>=0.100.0
7
+ uvicorn>=0.20.0
8
+ python-multipart>=0.0.6
9
+ git+https://github.com/awsaf49/sonics.git
test_api.py ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Test script for Madverse Music API
4
+ """
5
+
6
+ import requests
7
+ import json
8
+ import time
9
+ import os
10
+
11
+ # API endpoint
12
+ API_URL = "http://localhost:8000"
13
+
14
+ # API Key (same as in api.py)
15
+ API_KEY = os.getenv("MADVERSE_API_KEY", "madverse-music-api-key-2024")
16
+
17
+ # Headers for authenticated requests
18
+ HEADERS = {
19
+ "X-API-Key": API_KEY,
20
+ "Content-Type": "application/json"
21
+ }
22
+
23
+ def test_health():
24
+ """Test health check endpoint"""
25
+ print("πŸ” Testing health check...")
26
+ response = requests.get(f"{API_URL}/health")
27
+ print(f"Status: {response.status_code}")
28
+ print(f"Response: {response.json()}")
29
+ print()
30
+
31
+ def test_info():
32
+ """Test info endpoint"""
33
+ print("πŸ“Š Testing info endpoint...")
34
+ response = requests.get(f"{API_URL}/info")
35
+ print(f"Status: {response.status_code}")
36
+ print(f"Response: {json.dumps(response.json(), indent=2)}")
37
+ print()
38
+
39
+ def test_analyze_single(audio_url):
40
+ """Test music analysis endpoint with single URL"""
41
+ print(f"🎡 Testing single file analysis with URL: {audio_url}")
42
+
43
+ payload = {
44
+ "urls": [audio_url]
45
+ }
46
+
47
+ start_time = time.time()
48
+ response = requests.post(f"{API_URL}/analyze", json=payload, headers=HEADERS)
49
+ end_time = time.time()
50
+
51
+ print(f"Status: {response.status_code}")
52
+ print(f"Request time: {end_time - start_time:.2f} seconds")
53
+
54
+ if response.status_code == 200:
55
+ result = response.json()
56
+ print("βœ… Analysis successful!")
57
+ print(f"Total files: {result['total_files']}")
58
+ print(f"Successful: {result['successful_analyses']}")
59
+ print(f"Message: {result['message']}")
60
+
61
+ if result['results']:
62
+ file_result = result['results'][0]
63
+ if file_result['success']:
64
+ print(f"Classification: {file_result['classification']}")
65
+ print(f"Confidence: {file_result['confidence']:.2%}")
66
+ print(f"Duration: {file_result['duration']:.1f} seconds")
67
+ print(f"Processing time: {file_result['processing_time']:.2f} seconds")
68
+ else:
69
+ print("❌ Analysis failed!")
70
+ print(f"Error: {response.json()}")
71
+ print()
72
+
73
+ def test_analyze_multiple(audio_urls):
74
+ """Test music analysis endpoint with multiple URLs"""
75
+ print(f"🎡 Testing multiple files analysis with {len(audio_urls)} URLs...")
76
+
77
+ payload = {
78
+ "urls": audio_urls
79
+ }
80
+
81
+ start_time = time.time()
82
+ response = requests.post(f"{API_URL}/analyze", json=payload, headers=HEADERS)
83
+ end_time = time.time()
84
+
85
+ print(f"Status: {response.status_code}")
86
+ print(f"Request time: {end_time - start_time:.2f} seconds")
87
+
88
+ if response.status_code == 200:
89
+ result = response.json()
90
+ print("βœ… Multiple files analysis successful!")
91
+ print(f"Total files: {result['total_files']}")
92
+ print(f"Successful: {result['successful_analyses']}")
93
+ print(f"Failed: {result['failed_analyses']}")
94
+ print(f"Message: {result['message']}")
95
+ print(f"Total processing time: {result['total_processing_time']:.2f} seconds")
96
+
97
+ print("\nπŸ“‹ Individual Results:")
98
+ for i, file_result in enumerate(result['results'][:3]): # Show first 3 results
99
+ status = "βœ…" if file_result['success'] else "❌"
100
+ print(f" {i+1}. {status} {file_result['url'][:50]}...")
101
+ if file_result['success']:
102
+ print(f" Classification: {file_result['classification']}")
103
+ print(f" Confidence: {file_result['confidence']:.2%}")
104
+ else:
105
+ print(f" Error: {file_result['message']}")
106
+
107
+ if len(result['results']) > 3:
108
+ print(f" ... and {len(result['results']) - 3} more results")
109
+
110
+ else:
111
+ print("❌ Multiple files analysis failed!")
112
+ print(f"Error: {response.json()}")
113
+ print()
114
+
115
+ def test_unauthorized_access():
116
+ """Test unauthorized access (without API key)"""
117
+ print("πŸ”’ Testing unauthorized access...")
118
+
119
+ payload = {
120
+ "urls": ["https://example.com/test.mp3"]
121
+ }
122
+
123
+ # Make request without API key
124
+ response = requests.post(f"{API_URL}/analyze", json=payload)
125
+
126
+ print(f"Status: {response.status_code}")
127
+ if response.status_code == 401:
128
+ print("βœ… Unauthorized access properly blocked!")
129
+ print(f"Error: {response.json()}")
130
+ else:
131
+ print("❌ Unauthorized access not blocked!")
132
+ print(f"Response: {response.json()}")
133
+ print()
134
+
135
+ def main():
136
+ """Main test function"""
137
+ print("πŸš€ Madverse Music API Test")
138
+ print("=" * 40)
139
+
140
+ # Test basic endpoints
141
+ test_health()
142
+ test_info()
143
+
144
+ # Test authentication
145
+ test_unauthorized_access()
146
+
147
+ # Test with a sample audio URL (you can replace with your own)
148
+ # This is just an example - replace with actual audio URLs
149
+ sample_urls = [
150
+ "https://example.com/sample.mp3", # Replace with real URLs
151
+ "https://example.com/sample2.wav"
152
+ ]
153
+
154
+ print("πŸ“ To test with real audio:")
155
+ print("1. Replace sample URLs in this script with real audio URLs")
156
+ print("2. Or use curl for single file:")
157
+ print(f' curl -X POST "{API_URL}/analyze" \\')
158
+ print(f' -H "X-API-Key: {API_KEY}" \\')
159
+ print(f' -H "Content-Type: application/json" \\')
160
+ print(f' -d \'{{"urls": ["YOUR_AUDIO_URL"]}}\'')
161
+ print()
162
+ print("3. Or use curl for multiple files:")
163
+ print(f' curl -X POST "{API_URL}/analyze" \\')
164
+ print(f' -H "X-API-Key: {API_KEY}" \\')
165
+ print(f' -H "Content-Type: application/json" \\')
166
+ print(f' -d \'{{"urls": ["URL1", "URL2", "URL3"]}}\'')
167
+ print()
168
+
169
+ # Uncomment below to test with real URLs
170
+ # for url in sample_urls:
171
+ # try:
172
+ # test_analyze_single(url)
173
+ # except Exception as e:
174
+ # print(f"Error testing {url}: {e}")
175
+
176
+ # Uncomment below to test multiple files analysis
177
+ # try:
178
+ # test_analyze_multiple(sample_urls)
179
+ # except Exception as e:
180
+ # print(f"Error testing multiple files: {e}")
181
+
182
+ if __name__ == "__main__":
183
+ main()