Ryle Radio 1.0.0
An open-source "radio" system for Unity, allowing tracks, tuning, broadcasters, and more!
Loading...
Searching...
No Matches
ProceduralRadioTrack.cs
1using NaughtyAttributes;
2using UnityEngine;
3
4namespace RyleRadio.Tracks
5{
6
7 /// <summary>
8 /// A RadioTrack that plays procedurally generated audio, such as noice, silence, and waveforms.
9 /// </summary>
10 [System.Serializable]
12 {
13 /// <summary>
14 /// The name of this class in the editor- required by RadioTrack
15 /// </summary>
16 public const string DISPLAY_NAME = "Procedural";
17
18 /// <summary>
19 /// The eventType of procedural audio this track is generating.
20 /// </summary>
21 public enum ProceduralType // you can add custom ones of these if you like, but they're not as malleable as tracks themselves and you'll have to adjust the code
22 {
23 WhiteNoise, ///< White noise: random samples between 0 and 1
24 PinkNoise, ///< Special eventType of noise defined by Paul Kellet's refined method (pk3): sounds "fuller" than white noise
25 BrownNoise, ///< Special eventType of noise using a value (\ref brownWalkPower): sounds softer and deeper
26 SineWave, ///< A waveform: shaped as a sine wave at a given frequency
27 Silence, ///< Silence: samples at 0
28 }
29
30 private const float NOISE_MULTIPLIER = .2f; ///< A base multiplier for noise- because the samples can go all the way up to 1, noise tends to be a lot louder than other tracks, e.g: AudioClips in ClipRadioTrack
31 private const float PINK_MULTIPLIER = .5f; ///< Pink noise is even louder than the other noise types, so we curb it a little
32 private const float BASE_SAMPLE_RATE = 44100; ///< The default sample rate for the procedural tracks, can adjust this if required
33
34 /// <summary>
35 /// The selected eventType of noise for this track
36 /// </summary>
38
39 /// <summary>
40 /// If this track is inside of a StationRadioTrack, then it should only play for a certain duration- this is that duration
41 /// </summary>
42 [AllowNesting, ShowIf("IsInStation")]
43 public float duration = 0;
44
45 /// <summary>
46 /// The frequency/pitch of the waveform
47 /// </summary>
48 [AllowNesting, ShowIf("proceduralType", ProceduralType.SineWave), Range(1, 2000)]
49 public float waveFrequency = 100;
50
51 /// <summary>
52 /// The value used to define the sound of brown noise.<br><br>
53 /// Brown noise works by adding the generated sample to all previous generated samples. This float is what these generated samples are multiplied by when stored.
54 /// This means that the higher the walk power, the larger the difference that each sample makes on average, and the closer it sounds to white noise.
55 /// </summary>
56 [AllowNesting, ShowIf("proceduralType", ProceduralType.BrownNoise), Range(0, 1)]
57 public float brownWalkPower = 0.5f;
58
59#if !SKIP_IN_DOXYGEN
60 // we can't use UnityEngine.Random during audio updates as it runs on a different thread, so we need to use System.Random instead
61 private System.Random random;
62#endif
63
64 /// <summary>
65 /// The progress of the waveform used when generating it
66 /// </summary>
67 private float phase = 0;
68
69#if !SKIP_IN_DOXYGEN
70 // values used for the sample generation of pink noise- it needs to use running values and stores them here
71 private float p0 = 0, p1 = 0, p2 = 0, p3 = 0, p4 = 0, p5 = 0, p6 = 0;
72#endif
73
74 /// <summary>
75 /// The generated brown noise from the previous sample
76 /// </summary>
77 private float lastBrown = 0;
78
79 /// <summary>
80 /// Whether this is in a station or not. Required by IStationTrack
81 /// </summary>
82 public bool IsInStation { get; set; }
83
84
85 /// <summary>
86 /// Initializes this track
87 /// </summary>
88 public override void Init()
89 {
90 random = new System.Random();
91 phase = 0;
92
93 // if this track has a duration, set the sample count to that duration, otherwise set it to ''''infinite'''' (as big a number as possible)
94 SampleCount = (duration > 0) ? (int)(duration * BASE_SAMPLE_RATE) : int.MaxValue;
96 }
97
98 /// <summary>
99 /// Get the next sample of the selected procedural audio eventType
100 /// </summary>
101 /// <param name="_sampleIndex">The index of the sample- useless for noise, useful for waveforms</param>
102 /// <returns>The sample of generated audio</returns>
103 public override float GetSample(int _sampleIndex)
104 {
105 // all the noise algorithms use a white noise float
106 float white = 0;
107
108 switch (proceduralType)
109 {
110 // generate a random value between -1 and 1
111 case ProceduralType.WhiteNoise:
112 white = ((float)random.NextDouble() * 2) - 1;
113 return white * NOISE_MULTIPLIER;
114
115 // generate a random value with a pink noise filter applied
116 case ProceduralType.PinkNoise:
117 white = ((float)random.NextDouble() * 2) - 1;
118
119 // generated using paul kellet's refined method (pk3)
120 // https://www.firstpr.com.au/dsp/pink-noise/#Filtering:~:text=(This%20is%20pke,p2%20%2B%20white%20*%200.1848%3B
121 p0 = 0.99886f * p0 + white * 0.0555179f;
122 p1 = 0.99332f * p1 + white * 0.0750759f;
123 p2 = 0.96900f * p2 + white * 0.1538520f;
124 p3 = 0.86650f * p3 + white * 0.3104856f;
125 p4 = 0.55000f * p4 + white * 0.5329522f;
126 p5 = -0.7616f * p5 - white * 0.0168980f;
127 float pink = p0 + p1 + p2 + p3 + p4 + p5 + p6 + white * 0.5362f;
128 p6 = white * 0.115926f;
129
130 // pink is louder than normal noise so we use a secondary multiplier on it
131 return pink * NOISE_MULTIPLIER * PINK_MULTIPLIER;
132
133 // generate a random value, using previous values to soften the noise
134 // theory explained by gemini, adjusted from https://forum.juce.com/t/creating-colored-noise/30012/4
135 case ProceduralType.BrownNoise:
136 white = ((float)random.NextDouble() * 2) - 1;
137
138 lastBrown += white * brownWalkPower;
139 lastBrown = Mathf.Clamp(lastBrown, -1, 1) * 0.998f; // use a small <1 constant to ensure the noise doesn't get constantly louder
140
142
143 // use a sine wave to create a basic single tone
144 // from https://discussions.unity.com/t/generating-a-simple-sinewave/665023/16
145 case ProceduralType.SineWave:
146 float lastPhase = phase + (2 * Mathf.PI * (waveFrequency / SampleRate));
147
148 phase = lastPhase;
149 if (phase > 2 * Mathf.PI) // ensure that phase doesn't get exponentially larger
150 phase -= 2 * Mathf.PI;
151
152 return Mathf.Sin(lastPhase) * NOISE_MULTIPLIER;
153
154 // just silence in case you need it
155 case ProceduralType.Silence:
156 return 0;
157
158 default:
159 Debug.LogError("Attempting to get a sample from a procedural RadioTrack with an invalid ProceduralType- this should not be possible.");
160 return 0;
161 }
162 }
163
164
165 }
166
167}
A RadioTrack that plays procedurally generated audio, such as noice, silence, and waveforms.
float brownWalkPower
The value used to define the sound of brown noise. Brown noise works by adding the generated sample...
override float GetSample(int _sampleIndex)
Get the next sample of the selected procedural audio eventType.
float duration
If this track is inside of a StationRadioTrack, then it should only play for a certain duration- this...
float phase
The progress of the waveform used when generating it.
const float NOISE_MULTIPLIER
A base multiplier for noise- because the samples can go all the way up to 1, noise tends to be a lot ...
override void Init()
Initializes this track.
const float BASE_SAMPLE_RATE
The default sample rate for the procedural tracks, can adjust this if required.
ProceduralType proceduralType
The selected eventType of noise for this track.
float lastBrown
The generated brown noise from the previous sample.
ProceduralType
The eventType of procedural audio this track is generating.
@ PinkNoise
Special eventType of noise defined by Paul Kellet's refined method (pk3): sounds "fuller" than white ...
@ BrownNoise
Special eventType of noise using a value (brownWalkPower): sounds softer and deeper.
@ SineWave
A waveform: shaped as a sine wave at a given frequency.
@ WhiteNoise
White noise: random samples between 0 and 1.
float waveFrequency
The frequency/pitch of the waveform.
bool IsInStation
Whether this is in a station or not. Required by IStationTrack.
const float PINK_MULTIPLIER
Pink noise is even louder than the other noise types, so we curb it a little.
const string DISPLAY_NAME
The name of this class in the editor- required by RadioTrack.
A track to play as part of a radio. These are the fundamental objects that define the content of the ...
Definition RadioTrack.cs:20
float SampleRate
The sample rate of this track.
Definition RadioTrack.cs:27
virtual int SampleCount
The number of samples in this track.
Definition RadioTrack.cs:33
A RadioTrack that can be played as part of a station.
Tracks to be used on a radio- includes base classes.
Definition RadioUtils.cs:20