yoanbernabeu/music-gpt-bundle

Symfony bundle for integrating Music GPT API into your applications

Installs: 2

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

Type:symfony-bundle

pkg:composer/yoanbernabeu/music-gpt-bundle

V0.1.0 2025-11-29 17:06 UTC

This package is auto-updated.

Last update: 2025-11-29 17:11:49 UTC


README

A Symfony bundle for integrating Music GPT API into your applications.

🎵 Powered by MusicGPT.com - Generate AI music and covers
🔌 API Access: musicgpt.com/api

PHP Version Symfony License QA

Features

  • 🎵 Music AI - Generate custom music from text prompts
  • 🎤 Cover Songs - Transform audio with AI voice models
  • 🗣️ Text To Speech - Convert text to realistic speech with voice models
  • 🎼 Audio Extraction - Extract stems (vocals, drums, bass, etc.) from audio
  • 🎙️ Voice Management - Search and list AI voices
  • 🔍 Conversion Tracking - Check status and retrieve results
  • 🚀 Type-Safe - Full PHP 8.4 type safety with DTOs and Enums
  • 📦 Easy to Use - Simple, intuitive API

Installation

composer require yoanbernabeu/music-gpt-bundle

If you're using Symfony Flex, the bundle will be automatically registered. Otherwise, add it to your config/bundles.php:

return [
    // ...
    YoanBernabeu\MusicGptBundle\MusicGptBundle::class => ['all' => true],
];

Configuration

Create config/packages/music_gpt.yaml:

music_gpt:
    api:
        api_key: '%env(MUSIC_GPT_API_KEY)%'

Add your API key to .env:

MUSIC_GPT_API_KEY=your_api_key_here

Get your API key at musicgpt.com/api.

Usage

Generate Music with AI

use YoanBernabeu\MusicGptBundle\Contract\MusicAIServiceInterface;
use YoanBernabeu\MusicGptBundle\DTO\MusicAI\MusicAIRequest;

class MusicController
{
    public function __construct(
        private readonly MusicAIServiceInterface $musicAI
    ) {}

    public function generate(): void
    {
        // Simple prompt
        $request = new MusicAIRequest(
            prompt: 'A cheerful song about coding in PHP'
        );

        $response = $this->musicAI->generate($request);

        echo "Task ID: {$response->getTaskId()}\n";
        echo "ETA: {$response->getEta()} seconds\n";
    }

    public function generateAdvanced(): void
    {
        // With custom style and vocals
        $request = new MusicAIRequest(
            musicStyle: 'Lo-fi Hip Hop',
            lyrics: 'Coding all night long...',
            voiceId: 'Drake',
            makeInstrumental: false
        );

        $response = $this->musicAI->generate($request);
        
        // The API generates 2 versions
        [$version1, $version2] = $response->getConversionIds();
    }
}

Available Parameters:

  • prompt - Natural language description
  • musicStyle - Genre (Rock, Pop, Jazz, Lo-fi, etc.)
  • lyrics - Custom lyrics
  • voiceId - Voice model for vocals
  • makeInstrumental - Generate without vocals (default: false)
  • vocalOnly - Generate vocals only (default: false)
  • webhookUrl - Callback URL for async notifications

Create AI Voice Covers

use YoanBernabeu\MusicGptBundle\Contract\CoverServiceInterface;
use YoanBernabeu\MusicGptBundle\DTO\Cover\CoverRequest;

class CoverController
{
    public function __construct(
        private readonly CoverServiceInterface $cover
    ) {}

    public function create(): void
    {
        // From URL
        $request = new CoverRequest(
            audioUrl: 'https://example.com/song.mp3',
            voiceId: 'Drake'
        );

        $response = $this->cover->createCover($request);

        echo "Task ID: {$response->getTaskId()}\n";
        echo "Conversion ID: {$response->getConversionId()}\n";
    }

    public function createWithPitch(): void
    {
        // From local file with pitch adjustment
        $request = new CoverRequest(
            audioFile: '/path/to/audio.wav',
            voiceId: 'Taylor Swift',
            pitch: -2  // -12 to +12 semitones
        );

        $response = $this->cover->createCover($request);
    }
}

Note: Provide either audioUrl OR audioFile, not both.

Generate Text To Speech

use YoanBernabeu\MusicGptBundle\Contract\TextToSpeechServiceInterface;
use YoanBernabeu\MusicGptBundle\DTO\TextToSpeech\TextToSpeechRequest;

class TextToSpeechController
{
    public function __construct(
        private readonly TextToSpeechServiceInterface $textToSpeech
    ) {}

    public function create(): void
    {
        // Simple text to speech with voice ID
        $request = new TextToSpeechRequest(
            text: 'Hello world, this is a test of the text to speech feature.',
            gender: 'male',
            voiceId: 'Drake'
        );

        $response = $this->textToSpeech->createTextToSpeech($request);

        echo "Task ID: {$response->getTaskId()}\n";
        echo "ETA: {$response->getEta()} seconds\n";
    }

    public function createWithSampleAudio(): void
    {
        // Using a sample audio URL for voice cloning
        $request = new TextToSpeechRequest(
            text: 'The character Sherlock Holmes first appeared in print in 1887.',
            gender: 'female',
            sampleAudioUrl: 'https://example.com/voice-sample.mp3'
        );

        $response = $this->textToSpeech->createTextToSpeech($request);
    }

    public function createWithWebhook(): void
    {
        // With webhook for async notification
        $request = new TextToSpeechRequest(
            text: 'When I think of superheroes I think of super humans.',
            gender: 'male',
            voiceId: 'Drake',
            webhookUrl: 'https://example.com/webhook'
        );

        $response = $this->textToSpeech->createTextToSpeech($request);
    }
}

Available Parameters:

  • text (required) - Text content to convert to speech
  • gender (required) - Voice gender: "male" or "female"
  • voiceId (optional) - Voice model ID (e.g., "Drake", "Adele")
  • sampleAudioUrl (optional) - URL of voice sample for cloning
  • webhookUrl (optional) - Callback URL for async notifications

Note: Priority is given to sampleAudioUrl, then voiceId, then gender.

Extract Audio Stems

use YoanBernabeu\MusicGptBundle\Contract\ExtractionServiceInterface;
use YoanBernabeu\MusicGptBundle\DTO\Extraction\ExtractionRequest;

class ExtractionController
{
    public function __construct(
        private readonly ExtractionServiceInterface $extraction
    ) {}

    public function extractVocals(): void
    {
        // Extract vocals from a song
        $request = new ExtractionRequest(
            audioUrl: 'https://example.com/song.mp3',
            stems: ['vocals', 'instrumental']
        );

        $response = $this->extraction->extractStems($request);

        echo "Task ID: {$response->getTaskId()}\n";
        echo "ETA: {$response->getEta()} seconds\n";
    }

    public function extractMultipleStems(): void
    {
        // Extract multiple stems from a song
        $request = new ExtractionRequest(
            audioUrl: 'https://example.com/song.mp3',
            stems: ['vocals', 'drums', 'bass', 'guitar', 'piano']
        );

        $response = $this->extraction->extractStems($request);
    }

    public function extractWithPreprocessing(): void
    {
        // Extract with audio cleanup
        $request = new ExtractionRequest(
            audioFile: '/path/to/audio.wav',
            stems: ['vocals'],
            preprocessingOptions: ['Denoise', 'Dereverb']
        );

        $response = $this->extraction->extractStems($request);
    }

    public function cleanupAudio(): void
    {
        // Only apply preprocessing without extraction
        $request = new ExtractionRequest(
            audioUrl: 'https://example.com/noisy-audio.mp3',
            preprocessingOptions: ['Denoise', 'Deecho', 'Dereverb']
        );

        $response = $this->extraction->extractStems($request);
    }
}

Available Stems:

  • Basic: vocals, instrumental
  • Vocal types: male_vocal, female_vocal, lead_vocal, back_vocal
  • Instruments: bass, drums, guitar, piano, keys, strings, winds
  • Guitar types: rhythm_guitar, solo_guitar, acoustic_guitar, electric_guitar
  • Drum components: kick_drum, snare_drum, toms, hi_hat, ride, crash

Preprocessing Options:

  • Denoise - Remove background noise
  • Deecho - Remove echo effects
  • Dereverb - Remove reverb effects

Note: Provide either audioUrl OR audioFile, not both.

Search and List Voices

use YoanBernabeu\MusicGptBundle\Contract\VoiceServiceInterface;

class VoiceController
{
    public function __construct(
        private readonly VoiceServiceInterface $voice
    ) {}

    public function searchVoices(): void
    {
        // Search for voices by name
        $response = $this->voice->searchVoices('Drake');

        echo "Found {$response->getTotal()} voices\n";
        
        foreach ($response->getVoices() as $voice) {
            echo "ID: {$voice->getVoiceId()} - Name: {$voice->getVoiceName()}\n";
        }
    }

    public function searchWithPagination(): void
    {
        // Search with pagination
        $response = $this->voice->searchVoices(
            query: 'Taylor',
            limit: 50,
            page: 0
        );

        echo "Page {$response->getPage()} of " . 
             ceil($response->getTotal() / $response->getLimit()) . "\n";
    }

    public function listAllVoices(): void
    {
        // Get all available voices
        $response = $this->voice->getAllVoices(limit: 100, page: 0);

        echo "Total voices available: {$response->getTotal()}\n";
        echo "Showing: " . count($response->getVoices()) . " voices\n";
    }

    public function browseVoices(): void
    {
        // Browse through all voices with pagination
        $page = 0;
        $limit = 20;

        do {
            $response = $this->voice->getAllVoices($limit, $page);
            
            foreach ($response->getVoices() as $voice) {
                echo "{$voice->getVoiceName()}\n";
            }
            
            $page++;
        } while (($page * $limit) < $response->getTotal());
    }
}

Available Methods:

  • searchVoices(string $query, int $limit = 20, int $page = 0) - Search voices by name
  • getAllVoices(int $limit = 20, int $page = 0) - Get all available voices

Response Data:

  • getVoices() - Array of VoiceInfo objects
  • getTotal() - Total number of voices available
  • getLimit() - Results per page
  • getPage() - Current page number

Track Conversion Status

use YoanBernabeu\MusicGptBundle\Contract\ConversionServiceInterface;
use YoanBernabeu\MusicGptBundle\Enum\ConversionType;

class ConversionController
{
    public function __construct(
        private readonly ConversionServiceInterface $conversion
    ) {}

    public function checkStatus(): void
    {
        // Get details by Task ID
        $details = $this->conversion->getByTaskId(
            taskId: 'task_123456',
            conversionType: ConversionType::MUSIC_AI
        );

        if ($details->isCompleted()) {
            echo "✅ Completed!\n";
            echo "Audio 1: {$details->getAudioUrl1()}\n";
            echo "Audio 2: {$details->getAudioUrl2()}\n";
        } elseif ($details->isProcessing()) {
            echo "⏳ Processing: {$details->getStatus()}\n";
        } elseif ($details->isFailed()) {
            echo "❌ Failed: {$details->getStatusMessage()}\n";
        }
    }

    public function getByConversionId(): void
    {
        // Get details by Conversion ID
        $details = $this->conversion->getByConversionId(
            conversionId: 'conv_789012',
            conversionType: ConversionType::COVER
        );

        if ($details->isCompleted()) {
            echo "Audio: {$details->getAudioUrl()}\n";
            echo "Video: {$details->getVideoUrl()}\n";
            echo "Cover: {$details->getImageUrl()}\n";
        }
    }

    public function checkTextToSpeech(): void
    {
        // Check Text To Speech conversion
        $details = $this->conversion->getByTaskId(
            taskId: 'task_tts_123',
            conversionType: ConversionType::TEXT_TO_SPEECH
        );

        if ($details->isCompleted()) {
            echo "✅ Speech generated!\n";
            echo "Audio (MP3): {$details->getAudioUrl()}\n";
            echo "Audio (WAV): {$details->getAudioUrlWav()}\n";
        }
    }

    public function checkExtraction(): void
    {
        // Check Extraction conversion
        $details = $this->conversion->getByTaskId(
            taskId: 'task_extract_456',
            conversionType: ConversionType::EXTRACTION
        );

        if ($details->isCompleted()) {
            echo "✅ Stems extracted!\n";
            echo "Vocals: {$details->getVocalsUrl()}\n";
            echo "Instrumental: {$details->getInstrumentalUrl()}\n";
        }
    }
}

Complete Workflow Example

use YoanBernabeu\MusicGptBundle\Contract\MusicAIServiceInterface;
use YoanBernabeu\MusicGptBundle\Contract\ConversionServiceInterface;
use YoanBernabeu\MusicGptBundle\DTO\MusicAI\MusicAIRequest;
use YoanBernabeu\MusicGptBundle\Enum\ConversionType;

class WorkflowController
{
    public function __construct(
        private readonly MusicAIServiceInterface $musicAI,
        private readonly ConversionServiceInterface $conversion
    ) {}

    public function generateAndWait(): void
    {
        // 1. Generate music
        $request = new MusicAIRequest(prompt: 'A relaxing piano melody');
        $response = $this->musicAI->generate($request);
        
        $taskId = $response->getTaskId();
        
        // 2. Poll for completion
        $maxAttempts = 30;
        for ($i = 0; $i < $maxAttempts; $i++) {
            sleep(10);
            
            $details = $this->conversion->getByTaskId(
                $taskId,
                ConversionType::MUSIC_AI
            );
            
            if ($details->isCompleted()) {
                echo "✅ Done!\n";
                echo "Download: {$details->getAudioUrl1()}\n";
                break;
            }
            
            if ($details->isFailed()) {
                echo "❌ Failed: {$details->getStatusMessage()}\n";
                break;
            }
            
            echo "⏳ Processing... (attempt {$i}/{$maxAttempts})\n";
        }
    }
}

Error Handling

use YoanBernabeu\MusicGptBundle\Exception\AuthenticationException;
use YoanBernabeu\MusicGptBundle\Exception\PaymentRequiredException;
use YoanBernabeu\MusicGptBundle\Exception\RateLimitException;
use YoanBernabeu\MusicGptBundle\Exception\ValidationException;

try {
    $response = $this->musicAI->generate($request);
} catch (AuthenticationException $e) {
    // Invalid API key (401/403)
    echo "Authentication error: {$e->getMessage()}";
} catch (PaymentRequiredException $e) {
    // Insufficient credits (402)
    echo "Payment required: {$e->getMessage()}";
} catch (RateLimitException $e) {
    // Too many requests (429)
    echo "Rate limited. Retry after: {$e->getRetryAfter()} seconds";
} catch (ValidationException $e) {
    // Invalid parameters (400/422)
    echo "Validation errors: " . print_r($e->getErrors(), true);
}

Rate Limits

Different plans have different limits. When rate limited (429), use $exception->getRetryAfter() to know when to retry.

Plan Audio Generations Get by ID
Free 1 parallel 20/min
PLUS 5 parallel 200/min
PRO 10 parallel 500/min

See official rate limits documentation.

API Documentation

Main Documentation

Conversion Endpoints

Helper Endpoints

Development

Run tests:

composer test

Check code style:

composer cs-check
composer cs-fix

Static analysis:

composer phpstan

Run all checks:

composer cs-check && composer phpstan && composer test

License

This bundle is open-sourced software licensed under the MIT license.

Author

Yoan Bernabeu

Support

For questions or issues, please open an issue on GitHub.