![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/.cursor-server/data/User/History/-7d274db2/ |
# BPM and Key Search Implementation
This document explains the BPM (Beats Per Minute) and musical key detection and search functionality.
## Overview
The system now supports:
- **BPM Detection**: Automatic detection of beats per minute from audio files
- **Key Detection**: Automatic detection of musical key (e.g., "C major", "A minor")
- **Camelot Wheel**: DJ-friendly Camelot wheel notation (e.g., "8B", "1A")
- **Global Search**: Search tracks by BPM range and/or musical key
## Database Schema
New columns added to `music_tracks` table:
- `bpm` (INT) - Beats per minute (40-300 range)
- `musical_key` (VARCHAR(20)) - Musical key (e.g., "C major", "A minor")
- `camelot_key` (VARCHAR(5)) - Camelot wheel notation (e.g., "8B", "1A")
All columns are indexed for fast searching.
## Setup Instructions
### Step 1: Run Database Migration
Visit: `https://soundstudiopro.com/migrations/add_bpm_key_columns.php`
This will:
- Add `bpm`, `musical_key`, and `camelot_key` columns
- Create indexes for fast searching
- Create composite index for BPM+key searches
### Step 2: Extract Existing Data
Visit: `https://soundstudiopro.com/utils/extract_bpm_key_from_metadata.php`
This will:
- Extract BPM and key from existing metadata JSON
- Populate the new dedicated columns
- Process all completed tracks
**Note**: To force re-extraction (overwrite existing values), add `?force=1` to the URL.
### Step 3: Verify
Check how many tracks have BPM/key data:
```sql
SELECT
COUNT(*) as total,
COUNT(bpm) as with_bpm,
COUNT(musical_key) as with_key,
COUNT(CASE WHEN bpm IS NOT NULL AND musical_key IS NOT NULL THEN 1 END) as complete
FROM music_tracks
WHERE status = 'complete';
```
## How It Works
### Client-Side Analysis (Existing Tracks)
When a user views a track page (`track.php`), the JavaScript audio analyzer:
1. Loads the audio file
2. Analyzes it using Web Audio API
3. Detects BPM and musical key
4. Sends results to `/api/save_audio_analysis.php`
5. API saves to both metadata JSON and dedicated columns
### New Track Creation
When new tracks are created:
- BPM/key may be provided in the callback metadata
- If not, client-side analysis runs on first view
- Data is stored in both metadata JSON and dedicated columns
## Search API Usage
### Basic Text Search (unchanged)
```
GET /api_global_search.php?q=house&limit=20
```
### Search by BPM Range
```
GET /api_global_search.php?min_bpm=120&max_bpm=140&limit=20
```
### Search by Musical Key
```
GET /api_global_search.php?key=C major&limit=20
```
### Search by Camelot Key (DJ-friendly)
```
GET /api_global_search.php?camelot=8B&limit=20
```
### Combined Search
```
GET /api_global_search.php?q=techno&min_bpm=130&max_bpm=150&key=A minor&limit=20
```
### Search by BPM/Key Only (no text query)
```
GET /api_global_search.php?min_bpm=128&max_bpm=132&limit=50
```
## API Parameters
| Parameter | Type | Description | Example |
|-----------|------|-------------|---------|
| `q` | string | Text search query (optional if filters provided) | `"techno"` |
| `min_bpm` | integer | Minimum BPM (40-300) | `120` |
| `max_bpm` | integer | Maximum BPM (40-300) | `140` |
| `key` | string | Musical key (case-insensitive) | `"C major"` or `"a minor"` |
| `camelot` | string | Camelot wheel notation | `"8B"` or `"1A"` |
| `limit` | integer | Max results (default: 10, max: 20) | `20` |
## Response Format
The API returns tracks with BPM and key data included:
```json
{
"tracks": [
{
"id": 123,
"title": "Techno Track",
"bpm": 128,
"musical_key": "A minor",
"camelot_key": "8A",
"artist_name": "DJ Name",
...
}
],
...
}
```
## Camelot Wheel Reference
The Camelot wheel is used by DJs for harmonic mixing. Compatible keys are adjacent:
- **Same number, different letter** (e.g., 8A ↔ 8B) - Perfect match
- **Adjacent numbers, same letter** (e.g., 8A ↔ 7A or 9A) - Good match
- **Adjacent numbers, different letter** (e.g., 8A ↔ 7B or 9B) - Acceptable
Example: A track in 8B (C major) mixes well with:
- 8A (A minor) - Same number
- 7B (F major) or 9B (G major) - Adjacent numbers, same letter
- 7A (D minor) or 9A (E minor) - Adjacent numbers, different letter
## Frontend Integration
To add BPM/Key filters to your search UI:
```javascript
// Example search with BPM filter
async function searchTracks(query, minBPM, maxBPM, key) {
const params = new URLSearchParams();
if (query) params.append('q', query);
if (minBPM) params.append('min_bpm', minBPM);
if (maxBPM) params.append('max_bpm', maxBPM);
if (key) params.append('key', key);
params.append('limit', 20);
const response = await fetch(`/api_global_search.php?${params}`);
return await response.json();
}
```
## Maintenance
### Re-analyzing Tracks
If you need to re-analyze tracks (e.g., after improving the analyzer):
1. Visit `/utils/extract_bpm_key_from_metadata.php?force=1`
2. Or manually trigger client-side analysis by visiting track pages
### Batch Processing New Tracks
For server-side batch processing (if needed), you can:
1. Create a cron job that processes tracks without BPM/key
2. Use the existing client-side analyzer via headless browser
3. Or integrate a server-side audio analysis library
## Performance Notes
- Indexes on `bpm`, `musical_key`, and `camelot_key` ensure fast searches
- Composite index on `(bpm, musical_key)` optimizes combined searches
- JSON metadata is still stored for backward compatibility
- Dedicated columns avoid JSON parsing on every search query
## Troubleshooting
### No BPM/Key Data
1. Check if migration ran: `SHOW COLUMNS FROM music_tracks LIKE 'bpm'`
2. Run extraction script: `/utils/extract_bpm_key_from_metadata.php`
3. Check metadata has BPM/key: `SELECT metadata FROM music_tracks WHERE id = ?`
### Search Not Working
1. Verify columns exist and have indexes
2. Check API logs for SQL errors
3. Test with simple query: `/api_global_search.php?min_bpm=120&max_bpm=140`
### Inaccurate BPM/Key
- Client-side analysis may vary slightly
- Higher confidence scores indicate more reliable results
- Consider server-side analysis for production-critical accuracy