1using NaughtyAttributes;
5using System.Collections;
6using System.Collections.Generic;
9using Random = UnityEngine.Random;
19 [AddComponentMenu(
"Ryle Radio/Radio Output")]
20 [RequireComponent(typeof(AudioSource))]
43 protected List<RadioTrackPlayer>
players =
new();
65 private AudioClip cacheClipFirst;
92 private float[] cacheClipSamples;
98 public List<RadioObserver>
Observers {
get;
private set; } =
new();
103 public Action<float>
OnTune {
get;
set; } =
new(_ => { });
108 public Action
OnInit {
get;
set; } =
new(() => { });
139 get => Mathf.Round(
tune * 10) / 10;
192 source = GetComponent<AudioSource>();
212 cacheClipFirst =
null;
224 List<AudioClip> unloadedClips =
new List<AudioClip>();
229 if (wrapper.TryGetClip(out AudioClip clip))
230 unloadedClips.Add(clip);
233 foreach (AudioClip clip
in unloadedClips)
234 clip.LoadAudioData();
237 while (unloadedClips.Count > 0)
240 for (
int i = 0; i < unloadedClips.Count; i++)
243 if (unloadedClips[i].loadState == AudioDataLoadState.Loaded)
246 unloadedClips.Remove(unloadedClips[i]);
258 Debug.Log(
"loaded!");
291 cacheClipFirst = AudioClip.Create(
292 this.GetInstanceID() +
"_CacheClip2",
299 this.GetInstanceID() +
"_CacheClip",
308 source.clip = cacheClipFirst;
337 int indexInData =
Data.TrackIDs.IndexOf(_player.TrackW.id);
340 int indexForNewPlayer =
players.Count;
343 for (
int i = 0; i <
players.Count; i++)
345 if (
Data.TrackIDs.IndexOf(
players[i].TrackW.id) > indexInData)
347 indexForNewPlayer = i;
356 players.Insert(indexForNewPlayer, _player);
397 player.DoDestroy += player =>
409 Debug.LogWarning($
"Can't find track with id {_id} to play as a one-shot!");
437 Debug.LogWarning($
"Can't find track with id {_id} to play as a loop!");
455 bool _createNew =
false,
460 var found =
players.Where(p => p.TrackW.id == _trackID);
463 if (found.Count() > 1)
466 switch (_multiplePlayerSelector)
470 _player = found.Last();
475 _player = found.First();
480 int index =
Random.Range(0, found.Count());
481 _player = found.ElementAt(index);
490 else if (found.Count() > 0)
493 _player = found.First();
537 protected void GetOutput(ref
float[] _data,
int _channels)
544 int monoSampleCount = _data.Length / _channels;
547 for (
int index = 0; index < monoSampleCount; index++)
551 float combinedVolume = 0;
563 combinedVolume += outVolume;
569 int indexWithChannels = index * _channels;
572 for (
int channel = indexWithChannels; channel < indexWithChannels + _channels; channel++)
573 _data[channel] += sample;
614 int prog =
source.timeSamples;
617 cacheClipFirst.SetData(cacheClipSamples, 0);
621 source.clip = cacheClipFirst;
625 source.timeSamples = prog;
633 cacheClipSamples[sample] = newData[sample];
637 int prog =
source.timeSamples;
640 cacheClipFirst.SetData(cacheClipSamples, 0);
648 source.timeSamples = prog;
A scene component that holds a reference to a RadioData.
RadioData Data
Read-only accessor for data.
void OnDestroy()
Unlink Init() from the radio's init.
RadioData data
The RadioData (aka just radio) that this component is linked to.
A component used to watch for specific happenings on a RadioOutput, e.g: a clip being a certain volum...
string[] AffectedTracks
The tracks selected on this Observer.
void AssignEvents(RadioTrackPlayer _player)
Assigns each event to a RadioTrackPlayer for one of our AffectedTracks. This is called when a new Rad...
The main scene component for a radio that plays it through an AudioSource.
bool isInFirstHalf
Whether or not the cache clip playback is in the second half of the clip.
RadioTrackPlayer PlayLoop(string _id)
Plays a track as a loop. A loop restarts when the track ends, then continues to play.
Action< float > OnTune
Event called whenever Tune is changed.
List< RadioObserver > Observers
Every RadioObserver associated with this output.
float Tune01
Tune scaled to [0 - 1], useful for UI
void LocalInit()
Initializes the RadioData- this needs to be separated from Init() as it would be recursive otherwise.
virtual void OnAudioFilterRead(float[] _data, int _channels)
Assigns a set of samples from the radio to play from the AudioSource- this preserves the settings on ...
AudioSource source
The AudioSource attached to this Output. This is assigned automatically.
IEnumerator AwaitLoadingThenInit()
Waits for all AudioClips used in this radio to be loaded before initializing the radio....
float Tune
The tune clamped to the full range.
void StartPlayers()
Creates every RadioTrackPlayer that this output needs for playback.
void GetOutput(ref float[] _data, int _channels)
Gets a set of samples from the radio, and push it along to the next samples- that is,...
void FixedUpdate()
Updates audio playback in WebGL, does nothing on other platforms.
Vector3 cachedPos
The position of this object as of the last frame update. We can't access transform....
bool Loaded
Public accessor for loaded.
float cacheClipChunkDuration
The size of half the cache clip used for WebGL playback- this is the amount of audio the cache clip s...
float DisplayTune
Tune with limited decimal points- looks better when displayed, more like an actual radio
Action playEvents
Called at the end of every audio cycle so that we don't interrupt threads when manipulating RadioTrac...
override void Init()
Initializes the output itself, and creates all required every required RadioTrackPlayer.
bool loaded
Whether or not the clips in this radio have been loaded.
float tune
The current tune value of this output- akin to the frequency of a real radio. Controls what tracks ca...
List< RadioTrackPlayer > players
The players used by this output.
void FillCacheClip()
Generate and store the next chunk of audio that will be played on WebGL. This is called just before t...
Action OnInit
Event called when this output is finished initializing- mainly useful for WebGL when we use AwaitLoad...
MultiplePlayersSelector
The method by which a RadioTrackPlayer is chosen from this output. Really only matters when you're pl...
@ Oldest
Selects the oldest player.
@ Random
Selects a random player (probably useless but funny to have)
@ Youngest
Selects the youngest player.
void PlayerCreation(RadioTrackPlayer _player)
Sets up and stores a new RadioTrackPlayer and alerts any RadioObserver of its creation.
AudioClip cacheClipSecond
The AudioClip used internally to emulate audio streaming on WebGL- half of this is played at a time,...
float baseSampleRate
The normal sample rate of this output, applied to each RadioTrackPlayer.
void ExecOnTune()
Called when tune is modified in the inspector.
int totalCacheChunkLength
The total number of samples in each half of the cache clip.
bool TryGetPlayer(string _trackID, out RadioTrackPlayer _player, bool _createNew=false, MultiplePlayersSelector _multiplePlayerSelector=MultiplePlayersSelector.Youngest)
Gets an active RadioTrackPlayer from this output.
virtual void Update()
Updates cachedPos.
RadioTrackPlayer PlayOneShot(string _id)
Plays a track as a one-shot. A one-shot destroys itself when its track ends.
The central data object defining the radio. Contains the tracks and information required to play the ...
void ClearCache()
Clears track names and IDs.
const float LOW_TUNE
The lower limit for tune on this radio. This may become non-const at some point.
const float HIGH_TUNE
The upper limit for tune on this radio. This may become non-const at some point.
A class that plays a certain RadioTrack at runtime. It's created newly for each track on each RadioOu...
float GetSample(float _tune, Vector3 _receiverPosition, float _otherVolume, out float _outVolume, bool _applyVolume=true)
Gets the current sample from the track according to playback. I would recommend reading the code comm...
PlayerType
The different types of Player- mainly changes what it does when the end of the track is reached.
void IncrementSample()
Move this player to the next sample.
A wrapper class for RadioTrack so that track types can be switched between in the inspector!...
bool playOnInit
If true, this track plays on RadioData.Init() - usually on game start.
Base interfaces and classes for components, e.g: track accessors, output accessors.
Components to be placed on scene objects, e.g: Outputs, Broadcasters, Observers.
Tracks to be used on a radio- includes base classes.