Skip to content

scramblerlab/generative-radio

Repository files navigation

Generative Radio

A fully local, offline AI radio app. Pick a genre, mood, vocal language, and describe what you're doing — the app generates and plays an endless stream of original AI-composed songs with no cloud APIs required.

Available as a web app (React + Vite) and a mobile app (Expo / React Native) for iOS and Android.

Requirements

  • Mac with Apple Silicon (M1/M2/M3/M4/M5)
  • macOS 14+
  • 16 GB+ unified memory (24 GB+ recommended for development, 64 GB for production)
  • 50 GB+ free SSD space

Quick Start

1. One-time setup

./scripts/setup.sh

This installs Homebrew tools, Ollama, the LLM model, clones ACE-Step 1.5, installs all dependencies, and installs cloudflared for remote access.

2. Add performance env vars

echo 'export PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.0' >> ~/.zshrc
echo 'export PYTORCH_ENABLE_MPS_FALLBACK=1' >> ~/.zshrc
source ~/.zshrc

3. Start everything

Development (hot-reload):

./scripts/start.sh

Production (compiled bundle, no reload):

./scripts/start_prod.sh

Open http://localhost:5173 in your browser.

When cloudflared is installed, a public URL is printed in the startup banner — share it to access the app from any device.

How it works

  1. Select a genre (36 options) and optional mood keywords (60 keywords across 4 categories)
  2. Choose a vocal language (11 languages) or instrumental mode
  3. Optionally describe what you're doing now in free text
  4. Optionally tune advanced ACE-Step parameters (time signature, inference steps, model variant, CoT flags)
  5. Click Start Radio
  6. A local LLM (Ollama + Qwen3.5:4b) generates a dimension-based song prompt (style, instruments, mood, vocal style, production)
  7. ACE-Step 1.5 generates a full MP3 with semantic audio codes for melodic structure
  8. The song plays in your browser with a live activity log showing generation progress
  9. The next song is pre-generated while the current one plays — the frontend pre-fetches audio bytes into memory for seamless, zero-latency transitions

Multi-listener mode

Multiple browsers can connect to the same session. The first local-network connection becomes the controller — they pick genres, start/stop the radio, save tracks, and see connected listeners. Everyone else joins as a viewer with a read-only player.

Remote visitors connecting via the Cloudflare tunnel always join as viewers regardless of order.

If the controller disconnects, the next local viewer is automatically promoted.

"Everyone can be a DJ" mode

Viewers can request the DJ slot via the Be the DJ button. When granted:

  • A DJ panel opens where the viewer enters their name and configures genre, mood, and language
  • Their selection becomes the next track's generation parameters
  • A cooldown timer (configurable, default 30 min) prevents rapid DJ switching
  • The active DJ's name is shown in the player ("PRESENTED BY [NAME]")

Mid-session genre changes

The controller can navigate back to the genre selector at any time without stopping the current track. The new settings take effect from the next generated track onward.

Save tracks

The controller can save the currently playing track to disk — both the MP3 and a JSON metadata file (title, genre, BPM, key, seed, lyrics, tags) are written to saved_tracks/. Remote viewers cannot trigger saves.

Reactions

Any connected listener (controller or viewer) can react to the currently playing track with a thumb up or thumb down. Reactions use toggle semantics — pressing the same button again removes the vote; pressing the opposite side switches. Reaction counts are broadcast to all listeners in real time and persisted to disk.

Supported languages

English, Español, Français, Deutsch, Italiano, 中文, Ελληνικά, Suomi, Svenska, 日本語, 한국어, and a No Vocal (instrumental) mode.

Advanced options

The controller can configure ACE-Step parameters before starting:

Option Default Range
Time Signature Auto 2/4, 3/4, 4/4, 6/8
Inference Steps 8 4–100 (more = higher quality, slower)
DiT Model Variant turbo turbo, turbo-shift1, turbo-shift3, turbo-continuous
ACE-Step CoT Flags Thinking ON, CoT Caption/Metas OFF, CoT Language ON per-flag toggles
DJ Cooldown 30 min 1–120 min

See the ACE-Step 1.5 Tutorial for details on what each parameter does.

Remote access

start.sh supports two tunnel modes:

Named tunnel (production): If ~/.cloudflared/config.yml is configured, the app is available at a fixed domain (e.g., https://radio.scrambler-lab.com). See docs/cloudflare-named-tunnel-setup.md for one-time setup.

Quick tunnel (dev fallback): If no named tunnel is configured, a random *.trycloudflare.com URL is generated on each startup.

Both modes proxy all traffic including WebSockets. Viewers joining via the tunnel automatically get the read-only listener experience.

Mobile app

The mobile/ directory contains an Expo / React Native app for iOS and Android. The mobile app is always a viewer — it connects to the same backend WebSocket and plays the radio stream, but cannot control the session (no genre selector, no save track). It is designed for listening on the go while the desktop session drives generation.

Prerequisites

  • Xcode (iOS) or Android Studio (Android)
  • Node.js + npm
  • Expo CLI: npm install -g expo-cli

Build and run

cd mobile
npm install
npx expo prebuild          # generates ios/ and android/ from app.json
npx expo run:ios --device  # or: eas build --platform ios

See docs/ios-simulator-guide.md for iOS setup and docs/android-setup-guide.md for Android setup.

Background audio

The mobile app uses expo-audio with a silence bridge pattern to keep the iOS audio session alive during track transitions (AI generation can take 30–120 s). Background audio requires a production build — it does not work in Expo Go.

After making changes to app.json (plugin config, permissions), run npx expo prebuild --clean before rebuilding.

See docs/ios-background-audio-investigation.md for the full background audio analysis.

Architecture

Service Port Description
Frontend 5173 React + Vite (dev HMR or compiled preview, proxies /api and /ws)
Backend 5555 FastAPI (REST + WebSocket)
ACE-Step API 8001 Music generation (MLX / Apple Silicon)
Ollama 11434 LLM inference
Cloudflare Tunnel Exposes port 5173 publicly (optional)

Mobile connects directly to the backend WebSocket (wss://radio.scrambler-lab.com/ws in production, ws://localhost:5555/ws in dev).

See BUILD_SPEC.md for the full technical specification.

LLM and audio duration

The app always uses qwen3.5:4b (~2.5 GB) for song prompt generation, generating 5 dimension fields (style, instruments, mood, vocal style, production) that are concatenated into a rich ACE-Step caption.

Audio duration is selected automatically at startup based on unified memory:

Memory Duration Rationale
≤ 32 GB 30 s Fast iteration on dev machines
33–47 GB 60 s Safe within MLX VAE Metal buffer limits
≥ 48 GB 60 s → 120 s → 180 s Progressive ramp — first track starts quickly, subsequent tracks get longer

See docs/acestep-memory-vs-duration.md for the full memory vs. duration analysis.

Debugging

All services write logs to /tmp/:

tail -f /tmp/generative-radio-backend.log     # FastAPI backend
tail -f /tmp/generative-radio-acestep.log     # ACE-Step API
tail -f /tmp/generative-radio-frontend.log    # Vite dev server
tail -f /tmp/generative-radio-cloudflared.log # Cloudflare tunnel

Backend log format: HH:MM:SS [LEVEL] module: [component] message

Frontend logs are in the browser DevTools console with [WS], [Radio], [Audio], and [GenreSelector] prefixes.

Manual service control

# Override ACE-Step location
ACESTEP_PATH=/path/to/ACE-Step-1.5 ./scripts/start.sh

# Run backend directly with custom log level
cd backend
uvicorn main:app --port 5555 --log-level debug

# Run frontend (dev, hot-reload)
cd frontend
npm run dev

# Run frontend (production preview of compiled bundle)
cd frontend
npm run build && npm run preview

Stopping

Press Ctrl+C in the terminal running start.sh — the backend, frontend, and Cloudflare tunnel are all shut down cleanly.

ACE-Step is intentionally left running because it takes several minutes to warm up. To stop it manually, use the PID printed in the startup banner:

kill <ACESTEP_PID>

About

100% AI-powered radio station with Qwen3 (prompt generation) and ACE-Step 1.5 (song generation)

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors