|
|
import streamlit as st |
|
|
import requests |
|
|
import os |
|
|
import torchaudio |
|
|
from pydub import AudioSegment |
|
|
from audiocraft.models import MusicGen |
|
|
from tempfile import NamedTemporaryFile |
|
|
|
|
|
|
|
|
RESEMBLE_API_KEY = os.getenv("RESEMBLE_API_KEY", "your_resemble_api_key") |
|
|
PROJECT_UUID = "96e7c9b4-voice" |
|
|
|
|
|
st.set_page_config(page_title="Suno-Like Music App", layout="centered") |
|
|
st.title("Suno-Like AI Music Generator") |
|
|
|
|
|
|
|
|
st.subheader("Step 1: Enter Lyrics") |
|
|
lyrics = st.text_area("Write your lyrics here:", "We go hard, never stop, always chasing dreams") |
|
|
|
|
|
|
|
|
st.subheader("Step 2: Describe Your Beat") |
|
|
prompt = st.text_input("Beat style (e.g. dark trap, chill pop):", "dark emotional trap beat with heavy bass") |
|
|
|
|
|
|
|
|
music_path = None |
|
|
if st.button("Generate Music"): |
|
|
with st.spinner("Generating instrumental..."): |
|
|
model = MusicGen.get_pretrained("facebook/musicgen-small") |
|
|
model.set_generation_params(duration=10) |
|
|
output = model.generate([prompt]) |
|
|
music_path = "output_music.wav" |
|
|
torchaudio.save(music_path, output[0].cpu(), 32000) |
|
|
st.audio(music_path, format="audio/wav") |
|
|
st.success("Instrumental ready!") |
|
|
|
|
|
|
|
|
def generate_voice_with_resemble(lyrics, project_uuid): |
|
|
url = f"https://app.resemble.ai/api/v2/projects/{project_uuid}/clips" |
|
|
headers = { |
|
|
"Authorization": f"Token {RESEMBLE_API_KEY}", |
|
|
"Content-Type": "application/json" |
|
|
} |
|
|
|
|
|
payload = { |
|
|
"title": "AI Generated Clip", |
|
|
"body": lyrics, |
|
|
"is_active": True |
|
|
} |
|
|
|
|
|
response = requests.post(url, headers=headers, json=payload) |
|
|
|
|
|
if response.status_code != 200: |
|
|
st.error(f"Resemble API Error: {response.status_code}") |
|
|
st.text(response.text) |
|
|
return None |
|
|
|
|
|
res_data = response.json() |
|
|
clip_url = res_data.get("item", {}).get("audio_src") |
|
|
return clip_url |
|
|
|
|
|
voice_path = None |
|
|
if st.button("Generate Voice with Resemble"): |
|
|
if not lyrics: |
|
|
st.warning("Please enter lyrics first.") |
|
|
else: |
|
|
with st.spinner("Generating voice..."): |
|
|
audio_url = generate_voice_with_resemble(lyrics, PROJECT_UUID) |
|
|
if audio_url: |
|
|
response = requests.get(audio_url) |
|
|
if response.status_code == 200: |
|
|
with NamedTemporaryFile(delete=False, suffix=".mp3") as f: |
|
|
f.write(response.content) |
|
|
voice_path = f.name |
|
|
st.audio(voice_path, format="audio/mp3") |
|
|
st.success("Voice generation complete!") |
|
|
else: |
|
|
st.error("Failed to download audio from Resemble.") |
|
|
else: |
|
|
st.error("Voice generation failed.") |
|
|
|
|
|
|
|
|
if st.button("Combine Voice and Beat"): |
|
|
if not music_path or not voice_path: |
|
|
st.warning("Generate both voice and music before combining.") |
|
|
else: |
|
|
try: |
|
|
beat = AudioSegment.from_wav(music_path) |
|
|
voice = AudioSegment.from_mp3(voice_path) |
|
|
|
|
|
voice = voice - 2 |
|
|
beat = beat - 4 |
|
|
|
|
|
combined = beat.overlay(voice) |
|
|
combined_path = "combined_output.mp3" |
|
|
combined.export(combined_path, format="mp3") |
|
|
|
|
|
st.audio(combined_path, format="audio/mp3") |
|
|
st.success("Final output ready!") |
|
|
except Exception as e: |
|
|
st.error(f"Error while c |
|
|
ombining: {e}") |