The Evolution of Project Eagle's Radio System
The Evolution of Project Eagle's Radio System
This document covers the technical progression of our region-specific radio implementation, from the initial problem to the final production system.
Phase 1: The Problem – SA Radio Everywhere
When building the expanded map connecting Liberty City, Vice City, and other regions, we faced a fundamental issue: San Andreas radio stations were hardcoded to play everywhere. Hearing Playback FM in Liberty City broke immersion entirely.
The core problem is that GTA San Andreas' audio engine (AERadioTrackManager) is tightly coupled to exactly 13 radio stations. The system is built around complex track sequencing where each station has pre-arranged sequences of DJ banter segments, commercial breaks, individual music tracks, and special mission-triggered commentary. The engine maintains listening time statistics per station, prevents recent track repeats through history tracking, and manages bass enhancement settings individually for each station.
Simply adding new stations wasn't feasible. The architecture doesn't allow for dynamic station registration or extension—it's a fixed 13-station system compiled into the game's core audio manager. Modifying it would require decompiling thousands of lines of audio code, understanding Rockstar's internal state machines for track sequencing, and rewriting the entire manager to be dynamic. This approach would be fragile, difficult to maintain, and risk breaking existing SA radio functionality.
The solution had to be different: build a parallel radio system that operates independently in non-SA regions while leaving the vanilla system untouched.
Phase 2: The CLEO Script Approach
Our first solution leveraged CLEO 5. We created a CLEO script (LCRAD.cs) that implements a complete independent radio system specifically for custom regions.
The script handled the core requirements:
- Loaded 16 audio streams for Liberty City stations, each as a looping file accessed through BASS opcodes
- Detected player location using coordinate-based region checking
- Monitored D-Pad input for station switching
- Managed volume synchronization by reading the game's master volume setting from memory
- Loaded and displayed radio station sprites from texture dictionaries
- Maintained per-vehicle radio memory so each car remembered which station it was tuned to
- Produced static noise effects during station transitions
- Toggled between the custom radio and SA radio based on region
The CLEO implementation was functional and proved the core concept worked. However, it exposed several limitations inherent to the scripting approach.
Code organization was problematic. Adding a new station required changes in multiple places: allocating a new variable, loading the audio stream, initializing its volume, setting it to looped, starting playback, and most painfully, adding it to a chain of conditional statements that checked each station index to set its volume. The volume-setting logic alone required 16 separate if-else checks, each duplicating the same pattern.
Most critically, the system couldn't implement background playback. Switching from one station to another caused the previous station to stop and reset. If you switched back, it started from the beginning. This fundamentally broke the immersion of driving between regions—you lose your place in the music every time you change the station, which is the opposite of how real radio works.
The CLEO script was a proof-of-concept that validated the approach, but it became clear we needed something faster, more maintainable, and capable of sophisticated features like position tracking across all stations simultaneously.
Phase 3: The C++ Implementation – RadioEx
Based on lessons from the CLEO prototype, we built RadioEx as a native ASI plugin written in modern C++. Rather than a general-purpose solution, it's a specialized system designed specifically for regional radio with tight integration into the project's architecture.
Data-Driven Architecture
Instead of hardcoding stations, RadioEx uses a configuration file approach. Station data is defined in a structured format that specifies which region owns which stations, their audio files, sprite names, and metadata. Adding a new region requires only adding a new configuration section—no recompilation, no code changes. This separation of data from logic makes the system truly extensible.
Background Playback Through Virtual Position Tracking
The key technical innovation addresses the main limitation of the CLEO version: all stations play simultaneously in the background, even when you're not listening to them. This is achieved through a position tracking system that maintains a virtual playback position for each station.
When a station is actively playing, its position is read directly from the BASS audio library, giving the exact real-time playback position. For stations that aren't selected, the system simulates forward progress by accumulating delta time and wrapping around when the duration is reached. The effect is seamless: if you switch from Head Radio to another station and come back ten minutes later, Head Radio is exactly ten minutes further into the track. This matches the behavior of real radio stations and maintains immersion during region transitions.
SA Radio Integration
RadioEx doesn't attempt to replace the vanilla SA radio system. Instead, it coexists with it through region-aware switching. When the player enters a Liberty City region from Los Santos, RadioEx silences the game's native radio manager and takes control. The previous SA radio station is saved. When the player returns to San Andreas, RadioEx stops and restores the saved SA station, with the game's internal manager resuming where it left off.
This cooperative design means the complex DJ banter, ad sequencing, and track history logic that the game's AERadioTrackManager maintains remains untouched in SA regions. We only intercept control during transitions and when in non-SA territories. The player experiences seamless audio transitions: static noise as they leave the LC broadcast range, then SA radio fades back in at exactly the station they were listening to before.
Per-Vehicle State Persistence
Each vehicle in the game is mapped to a station index. When a player enters a car, the system checks if that specific vehicle has a remembered station and restores it. If not, a random station is selected for first-time vehicles. This creates the authentic experience where stealing a car tunes to its owner's preferred station, and your personal vehicle always remembers what you were listening to.
Input Handling and HUD Response
The original CLEO script had noticeable latency between pressing the D-Pad and the HUD updating. RadioEx addresses this through predictive state tracking. The moment a button is pressed, the system immediately calculates what the next station should be and displays it in the HUD. The actual audio stream updates happen in the next frame or two when BASS catches up, but the player sees instant feedback on their button presses. This eliminates the confusion of "did I press it twice?" and makes the radio feel responsive.
Flexible Display Options
RadioEx supports both sprite-based and text-based HUD rendering, configurable through the INI system. This provides flexibility for different visual preferences. The HUD respects fade timing constants (fade-in time, display duration, fade-out time) to create smooth transitions that don't distract from gameplay. For SA radio in SA regions, the system displays sprites from the vanilla radio texture dictionary, maintaining visual consistency with the original game.
Memory Efficiency
The system only allocates resources when needed. Audio streams are loaded when entering a region and unloaded when exiting. Sprite textures are loaded on-demand. This lazy-loading approach prevents memory bloat when multiple regions might be defined but not all are visited frequently.
Configuration and Extensibility
The entire radio system is configured through data files, not hardcoded logic. Defining a new region involves specifying its boundaries (via the MapRegion enum), providing station metadata, and including audio files. The C++ code doesn't change. This design allows the system to scale to many regions without code duplication or increasing complexity.
Data-Driven Architecture and Future Extensibility
The shift from CLEO scripting to C++ RadioEx brings a fundamental change in how the system scales: configuration is now completely separated from code logic through the radio.dat file.
With CLEO, the system was rigidly hardcoded to 16 stations in a single region. Adding a new region meant writing a completely new script from scratch or attempting to modify the existing one with its goto-heavy control flow. Each station and region was baked into conditional logic scattered throughout the file. The architecture didn't allow for growth; it was a point solution for Liberty City radio.
RadioEx changes this entirely. A region is defined as a data structure with a set of stations, each station is a few lines in a configuration file with its audio path and sprite name, and the C++ code never touches this information. To add Vice City radio, you don't modify the code—you add a [ViceCity] section to radio.dat with 15 new station entries. To add a remote wilderness radio for some outback region, same process. The system is infinitely extensible without touching a single line of C++.
This data-driven approach makes RadioEx fundamentally different from the CLEO prototype. The code is written once to handle any region with any number of stations. Configuration files describe what regions exist and what stations they contain. This is the architecture that allows massive projects like connecting multiple cities—the code doesn't care how many regions are defined or how many stations each has. It reads radio.dat, loads the configuration, and works identically for region 1 or region 100.
The implications for Project Eagle are significant. Early on, we had to hack together a CLEO script to get Liberty City radio working. Now, adding entire new regions is purely a data problem: provide audio files, define the configuration, and the system handles it. Want Alderney radio? Add it to radio.dat. Want a pirate radio station on the water? Add it. The infrastructure is completely agnostic to what you configure.
Complete Radio Coverage Map
The comprehensive radio map of Project Eagle's entire USA network, displaying all major regions and their respective radio stations. Each numbered region (1-8+) represents a distinct city or area with its own curated radio lineup, featuring a mix of classic GTA stations and new original stations. The color-coded map shows how RadioEx seamlessly manages radio playback across the entire interconnected world, with each region maintaining its own station identity while allowing players to experience continuity as they travel from city to city.
The User Experience
These architectural improvements translate into tangible differences when playing, but more importantly, they set the stage for how we build content going forward.
When driving from San Fierro toward Liberty City, the player experiences a smooth transition. As they approach the region boundary, their current station fades as the broadcast signal weakens. A help message appears: "SA Radio out of range." Static noise plays, providing audio feedback for the transition. The game silences the native SA radio manager.
The player enters Liberty City. More static. Then a second message: "Liberty City Radio in range." The static fades and Head Radio fades in. Crucially, the station continues from where it would be if it had been playing the entire time. If Head Radio was at the 3:47 mark when the player left, and 15 minutes of gameplay have passed in San Andreas, the station resumes at approximately the 18:47 mark—because it has been "playing in the background" during all that time, advancing through its continuous audio stream.
If the player switches between stations while in Liberty City, pressing D-Pad up instantly shows the next station in the HUD. No perceptible delay. The station change accompanied by static and the sprite changing in real-time.
When the player returns to Los Santos, the sequence reverses. Static plays again, and Playback FM (or whichever station was playing before) resumes. Not from the beginning, and not from where the player left it—from where it would naturally be if it had continued playing throughout the player's time in Liberty City. The SA radio has been advancing in the background just like the custom regional stations.
This level of fidelity requires sophisticated state management, position tracking across multiple audio streams, careful timing of transitions, and tight integration with both the custom radio system and the vanilla game audio manager. It feels complete because the system was designed as a real infrastructure, not as a quick fix. The same infrastructure that handles today's three regions can handle ten more regions without any code modification—just configuration changes.
Future Possibilities: Building Toward Full Radio Systems
The current RadioEx implementation handles continuous audio playback and region-aware station switching, but the architecture opens the door for significantly more complex radio systems that rival SA's own implementation.
The most obvious next step is adding DJ banter. SA's radio system is built around track sequencing where each station has scheduled segments: DJ introductions, station identification, commercial breaks, and multiple music tracks. RadioEx currently treats stations as monolithic audio files that loop continuously, but this could be extended to support sequenced playlists. A station could be defined not as a single file but as a sequence of segments: "play DJ intro, then tracks 1-5, then a commercial, then the outro, then loop." The same position tracking system we built would apply—the sequencer advances through segments, and background stations keep their place in the sequence.
Building DJ personality and dynamic commentary is feasible within the current architecture. Since configuration is data-driven, stations could reference personality profiles with different voice characteristics and station identities. DJ banter could reference in-game events: traffic conditions, weather, mission progress, or time of day. The infrastructure for querying game state is already there; extending it to influence what the radio plays is straightforward.
The architecture that currently plays Liberty City radio identically is the same architecture that could simultaneously manage Liberty City, Vice City, San Andreas, and custom regions with completely different station personalities and content strategies.
What makes this feasible is the separation of data from code. None of these features require core changes to RadioEx. They're all variations on what the system already does: load configuration, track positions, manage playback, and render HUD. The framework is in place for radio to become as sophisticated and nuanced as any feature in the game.
- Project Eagle Team