Scripts de música y radio para FiveM: xSound y más
Reproduce música en el juego como es debido. xSound, apps de radio, boomboxes y los mejores scripts de audio para fiestas, bares y coches.
Agency Scripts
Fundador y desarrollador principal en Agency Scripts
Por qué la radio personalizada es importante en el roleplay
La música y la radio son elementos esenciales de la ambientación que dan vida a un servidor de roleplay de FiveM. Aunque GTA V incluye sus propias emisoras de radio, los sistemas de radio personalizados permiten a los propietarios de servidores crear emisoras temáticas que reflejen la identidad de su comunidad, emitir sesiones de DJ en directo, reproducir listas de reproducción seleccionadas por la comunidad e incluso transmitir boletines de noticias dentro del personaje. Un sistema de música bien diseñado va más allá de la simple reproducción de audio. Ofrece experiencias de escucha sincronizadas para jugadores que están en el mismo vehículo, audio espacial de radiocasetes portátiles y altavoces de clubes, listas de reproducción personales que los jugadores pueden gestionar desde una aplicación del teléfono e integración con cabinas de DJ para el roleplay en discotecas. La base técnica se apoya en las capacidades de audio de NUI, que te permiten aprovechar todo el potencial de las API de audio basadas en navegador dentro del cliente de FiveM.
Arquitectura de audio e integración con NUI
La capa NUI de FiveM se ejecuta en un navegador basado en Chromium integrado en el cliente del juego, lo que significa que tienes acceso a la Web Audio API, a los elementos de audio HTML5 e incluso a la API MediaSource para la transmisión adaptativa. La arquitectura se divide en tres componentes: el script Lua del cliente gestiona las interacciones con el mundo del juego, como entrar en vehículos o acercarse a altavoces; la capa HTML y JavaScript de NUI gestiona la reproducción y visualización del audio; y el servidor coordina los estados de reproducción sincronizados entre varios clientes. Cuando un jugador entra en un vehículo, el script del cliente envía un mensaje NUI para iniciar la reproducción de la emisora actual. La capa NUI crea un elemento de audio, establece la URL de origen e inicia la reproducción. Los controles de volumen se gestionan íntegramente en la capa NUI mediante el GainNode de la Web Audio API para lograr transiciones de volumen suaves:
-- Client-side: Vehicle radio controller
local currentStation = nil
local radioActive = false
CreateThread(function()
while true do
Wait(500)
local ped = PlayerPedId()
if IsPedInAnyVehicle(ped, false) then
local vehicle = GetVehiclePedIsIn(ped, false)
if not radioActive then
radioActive = true
-- Disable default GTA radio
SetVehicleRadioEnabled(vehicle, false)
-- Load saved station preference
local savedStation = LocalPlayer.state.radioStation or 'station_1'
SetStation(savedStation)
end
elseif radioActive then
radioActive = false
SendNUIMessage({action = 'stopRadio'})
currentStation = nil
end
end
end)
function SetStation(stationId)
local station = Config.Stations[stationId]
if not station then return end
currentStation = stationId
SendNUIMessage({
action = 'playStation',
url = station.url,
name = station.name,
volume = LocalPlayer.state.radioVolume or 0.5,
})
end
Configuración del reproductor de audio de NUI
La parte de NUI necesita un reproductor de audio robusto que gestione las conexiones a las transmisiones, el almacenamiento en búfer, la recuperación ante errores y el control de volumen. Usa la Web Audio API en lugar de simples elementos de audio HTML5, porque te ofrece control sobre el enrutamiento del audio, permite crear visualizaciones a partir de datos de frecuencia y admite el procesamiento de audio espacial. Crea un AudioContext con un GainNode para controlar el volumen y un AnalyserNode para obtener los datos de visualización:
// NUI JavaScript: Audio engine
class RadioPlayer {
constructor() {
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
this.gainNode = this.audioContext.createGain();
this.analyser = this.audioContext.createAnalyser();
this.analyser.fftSize = 256;
this.gainNode.connect(this.analyser);
this.analyser.connect(this.audioContext.destination);
this.audio = new Audio();
this.audio.crossOrigin = 'anonymous';
this.source = null;
this.isPlaying = false;
}
play(url, volume) {
this.stop();
this.audio = new Audio(url);
this.audio.crossOrigin = 'anonymous';
this.audio.volume = 1.0;
this.source = this.audioContext.createMediaElementSource(this.audio);
this.source.connect(this.gainNode);
this.gainNode.gain.value = volume;
this.audio.play().then(() => {
this.isPlaying = true;
}).catch(err => {
console.error('Radio playback failed:', err);
setTimeout(() => this.play(url, volume), 3000);
});
}
stop() {
if (this.audio) {
this.audio.pause();
this.audio.src = '';
this.isPlaying = false;
}
}
setVolume(vol) {
this.gainNode.gain.linearRampToValueAtTime(
vol, this.audioContext.currentTime + 0.1
);
}
getFrequencyData() {
const data = new Uint8Array(this.analyser.frequencyBinCount);
this.analyser.getByteFrequencyData(data);
return data;
}
}
const radio = new RadioPlayer();
Configuración de emisoras y transmisión
Define tus emisoras de radio con URL de transmisión, metadatos y categorías de género. La mayoría de los sistemas de radio personalizados para FiveM usan transmisiones de radio por internet en formato MP3 o AAC porque son ampliamente compatibles y fáciles de configurar. Puedes alojar tu propio servidor Icecast o Shoutcast para tener control total, o usar URL públicas de transmisiones de radio. Para cada emisora, guarda la URL de transmisión, un nombre para mostrar, una etiqueta de género y, opcionalmente, una imagen de logotipo para la interfaz. Considera añadir compatibilidad con URL de YouTube y SoundCloud mediante servicios proxy, pero ten en cuenta las condiciones del servicio y las restricciones de derechos de autor. Para la función de DJ en directo, intégrate con servicios que conviertan transmisiones RTMP a HTTP, para que los DJ puedan emitir usando OBS o software similar:
Config.Stations = {
['station_1'] = {
name = 'Agency Radio',
genre = 'Hip Hop',
url = 'https://stream.example.com/hiphop',
logo = 'station_hiphop.webp',
description = 'The freshest beats in Los Santos',
},
['station_2'] = {
name = 'LS Rock Radio',
genre = 'Rock',
url = 'https://stream.example.com/rock',
logo = 'station_rock.webp',
description = 'Classic and modern rock anthems',
},
['station_3'] = {
name = 'Vinewood Lounge',
genre = 'Jazz / Lofi',
url = 'https://stream.example.com/lofi',
logo = 'station_lofi.webp',
description = 'Smooth vibes for the night drive',
},
['station_live'] = {
name = 'Live DJ',
genre = 'Live',
url = 'https://stream.example.com/live',
logo = 'station_live.webp',
description = 'Live DJ sets from the community',
isLive = true,
},
}
Radio sincronizada en vehículos
Cuando varios jugadores viajan en el mismo vehículo, todos deben escuchar la misma emisora de radio. Esto requiere coordinación en el servidor mediante state bags o eventos de red. Cuando el conductor cambia de emisora, el servidor transmite la nueva emisora a todos los ocupantes. Usa el sistema de state bags de FiveM para una sincronización eficiente, ya que gestiona automáticamente la replicación de red sin necesidad de administrar eventos manualmente. Establece la emisora en el state bag de la entidad del vehículo para que cualquier jugador que entre en él reciba automáticamente la emisora actual. Gestiona casos límite, como cuando un pasajero entra en un vehículo que ya tiene una emisora reproduciéndose, cuando el conductor se marcha y los pasajeros se quedan, y cuando los pasajeros cambian a una emisora distinta si permites que puedan controlarla:
-- Server-side: Sync radio state via state bags
RegisterNetEvent('radio:server:setStation', function(vehicleNet, stationId)
local src = source
local vehicle = NetworkGetEntityFromNetworkId(vehicleNet)
if not DoesEntityExist(vehicle) then return end
-- Verify player is the driver
local ped = GetPlayerPed(src)
if GetPedInVehicleSeat(vehicle, -1) ~= ped then return end
-- Set station on vehicle state bag
Entity(vehicle).state:set('radioStation', stationId, true)
end)
-- Client-side: Listen for state bag changes
AddStateBagChangeHandler('radioStation', nil, function(bagName, key, value)
if not value then return end
local entity = GetEntityFromStateBagName(bagName)
if not DoesEntityExist(entity) then return end
local ped = PlayerPedId()
if not IsPedInVehicle(ped, entity, false) then return end
SetStation(value)
end)
Audio espacial y sistemas de radiocasetes portátiles
El audio espacial añade un nivel de inmersión que la reproducción de audio plana no puede igualar. Los radiocasetes portátiles, los altavoces de clubes y los equipos de sonido de los vehículos deben emitir un sonido cuyo volumen disminuya con la distancia y cuya panoramización dependa de la posición del oyente respecto a la fuente. FiveM ofrece funciones de audio nativas como PlaySoundFromCoord para sonidos espaciales sencillos, pero para audio en streaming debes implementar el escalado del volumen según la distancia en la capa NUI. Calcula la distancia entre el jugador y la fuente de audio en cada frame y luego asigna esa distancia a una curva de volumen. Usa una atenuación de inverso del cuadrado para obtener un sonido más realista, con distancias mínimas y máximas de escucha configurables. Para los radiocasetes portátiles que los jugadores pueden colocar en el mundo, crea un objeto desplegable que genere un prop en la ubicación del jugador y lo registre como fuente de audio. Los demás jugadores dentro del alcance deben escuchar la música automáticamente, con el volumen ajustado según la distancia al prop del radiocasete.
Listas de reproducción de los jugadores e integración con el teléfono
Ofrece a los jugadores control personal sobre su experiencia musical mediante un sistema de listas de reproducción integrado con el recurso del teléfono de tu servidor. Los jugadores deben poder crear listas personalizadas añadiendo URL de canciones individuales, reordenar las canciones arrastrándolas y soltándolas, configurar una lista para que se reproduzca aleatoriamente o en bucle y compartir listas con otros jugadores. Guarda las listas en la base de datos, vinculadas al identificador del jugador, para que se conserven entre sesiones. La interfaz de la aplicación del teléfono debe mostrar la canción que se está reproduciendo, con los metadatos del artista y el título, controles de reproducción para reproducir, pausar, saltar y volver a la canción anterior, un control deslizante de volumen y una lista de las listas guardadas. Cuando el jugador esté en un vehículo, el reproductor del teléfono debe dirigir automáticamente el audio al sistema de radio del vehículo para que los pasajeros también puedan escuchar la música. Cuando vaya a pie con auriculares equipados como objeto, la música se reproduce en privado y los demás jugadores ven un pequeño icono de auriculares que indica que el personaje está escuchando música sin que los demás puedan oírla.
Visualizador y diseño de la interfaz
La interfaz del reproductor de música debe sentirse dinámica y responder al audio que se reproduce. Usa el AnalyserNode de la Web Audio API para extraer datos de frecuencia en tiempo real y mostrar un visualizador de audio. Las barras, las formas de onda y los analizadores circulares del espectro funcionan bien, según la estética de tu interfaz. Renderiza el visualizador con un elemento canvas para obtener un buen rendimiento y actualízalo a 30 o 60 frames por segundo, según la complejidad de la visualización. La interfaz de la radio debe ser minimalista y discreta, y mostrarse como un pequeño widget en una esquina de la pantalla con el nombre de la emisora actual, el título de la canción si está disponible en los metadatos de la transmisión y controles básicos de reproducción. Cuando el jugador abra el menú completo de la radio para cambiar de emisora, muestra una cuadrícula o una lista de emisoras disponibles con sus logotipos, géneros y cantidad de oyentes. Incluye una barra de búsqueda para servidores con muchas emisoras y un sistema de favoritos para que los jugadores puedan acceder rápidamente a sus emisoras preferidas sin recorrer toda la lista.
Consideraciones de rendimiento
La transmisión de audio consume ancho de banda y recursos del cliente, así que optimiza cuidadosamente tu implementación. Limita la cantidad de transmisiones de audio simultáneas por cliente para evitar fugas de memoria y picos de uso de CPU. Cuando un jugador no esté en un vehículo y no tenga una boombox cerca, destruye por completo el contexto de audio en lugar de simplemente pausarlo. Para el audio espacial de varias boomboxes, crea transmisiones de audio solo para las fuentes que estén dentro del alcance auditivo y elimina las transmisiones cuando las fuentes salgan de ese alcance. Usa transmisiones de baja tasa de bits para la música ambiental de fondo y reserva las de mayor calidad para la experiencia de escucha principal. En el servidor, evita enviar URL de transmisiones directamente en eventos, ya que pueden interceptarse y usarse de forma indebida. En su lugar, usa un sistema de callbacks en el que el cliente solicite la URL de transmisión para un ID de estación específico y el servidor responda con una URL firmada con límite de tiempo. Supervisa los errores de reproducción reportados por los clientes y vuelve a conectar automáticamente las transmisiones que se interrumpan por problemas de red, con un retraso exponencial entre intentos para evitar saturar un servidor de transmisiones que esté teniendo problemas.