1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
//! Contains tools for keeping track of the state of individual channels.

use crate::{Command, waveform, errors::*};

/// The time it takes for amplitude and panning changes to occur. This prevents clicks from abrupt changes.
pub const RAMPING_RATE: f32 = 500.0;
/// Used to reduce the wandering of brownian noise. Calculated as `x / (BROWNIAN_LEAK * timestep + 1)^2`
pub const BROWNIAN_LEAK: f32 = 0.03;
/// The maximum amplitude of individual brownian noise samples. Does not represent the maximum power of the noise waveform overall.
pub const BROWNIAN_STEP: f32 = 0.5;

/// All the parameters needed in order to sample from a channel.
pub(crate) struct ChannelState {
	/// The progress along a repeating waveform on a scale of 0..1. Alternatively, the progress towards generating a new noise sample.
	period: f32,
	/// The current waveform type to use.
	waveform: usize,
	/// The current custom waveform data loaded. Requires the waveform field to be 7 to be generated.
	custom_waveform: waveform::CustomWaveform,
	
	/// The current frequency of the waveform in hertz. Affects the rate at which period is increased.
	frequency: f32,
	/// The current amplitude of the waveform on a scale of 0..1
	amplitude: f32,
	/// The current panning of the waveform on a scale of -1..1. Affects the amplitude of both stereo samples independently.
	panning: f32,
	
	/// The amplitude after being dampened by ramping. This is the actual value the sample uses.
	ramped_amplitude: f32,
	/// The panning after being dampened by ramping. This is the actual value the sample uses.
	ramped_panning: f32,
	
	/// The frequency that the channel is attempting to approach.
	frequency_slide_target: f32,
	/// The amplitude that the channel is attempting to approach.
	amplitude_slide_target: f32,
	/// The panning that the channel is attempting to approach.
	panning_slide_target: f32,
	
	/// The rate at which the frequency approaches ```frequency_slide_target``` in hertz/second
	frequency_rate: f32,
	/// The rate at which the amplitude approaches ```amplitude_slide_target``` in units/second.
	amplitude_rate: f32,
	/// The rate at which the panning approaches ```panning_slide_target``` in units/second.
	panning_rate: f32,
	
	/// The last random value that was generated by the channel. This is what will be sampled until the period elapses.
	noise_sample: f32,
}

impl ChannelState {
	/// Creates a new channel.
	pub(crate) fn new() -> ChannelState {
		ChannelState {
			period: 0.0,
			waveform: 0,
			custom_waveform: [0.0; waveform::CUSTOM_WIDTH],
			
			frequency: 440.0,
			amplitude: 0.0,
			panning: 0.0,
			
			ramped_amplitude: 0.0,
			ramped_panning: 0.0,
			
			frequency_slide_target: 440.0,
			amplitude_slide_target: 0.0,
			panning_slide_target: 0.0,
			
			frequency_rate: 0.0,
			amplitude_rate: 0.0,
			panning_rate: 0.0,
			
			noise_sample: 0.0,
		}
	}
	
	/// Samples the channel in its current state.
	#[no_mangle]
	pub fn sample(&self) -> (f32, f32) {
		let sample_output = match self.waveform {
			0 => waveform::sine(self.period),
			1 => waveform::triangle(self.period),
			2 => waveform::rec_sine(self.period),
			3 => waveform::saw(self.period),
			4 => waveform::square(self.period),
			5 => waveform::pulse(self.period),
			6 => self.noise_sample,
			7 => waveform::custom(self.period, &self.custom_waveform),
			_ => 0.0,
		} * self.ramped_amplitude;
		
		let left_sample = sample_output * (-self.ramped_panning + 1.0).min(1.0);
		let right_sample = sample_output * (self.ramped_panning + 1.0).min(1.0);
		(left_sample, right_sample)
	}
	
	/// Updates the state of the channel by the provided timestep in seconds.
	#[no_mangle]
	pub fn advance(&mut self, step: f32) {
		self.period += self.frequency * step;
		
		if self.waveform == 6 {
			while self.period >= 1.0 {
				let decay = self.frequency * step * BROWNIAN_LEAK + 1.0;
				self.noise_sample /= decay * decay;
				self.noise_sample += waveform::noise() * BROWNIAN_STEP;
				self.period -= 1.0
			}
		}
		
		// This is a really nice way of looping ascending values around 0-1.
		self.period -= self.period.floor();
		
		self.ramped_amplitude = approach(self.ramped_amplitude, self.amplitude, RAMPING_RATE * step);
		self.ramped_panning = approach(self.ramped_panning, self.panning, RAMPING_RATE * step);
		self.frequency = approach(self.frequency, self.frequency_slide_target, self.frequency_rate * step);
		self.amplitude = approach(self.amplitude, self.amplitude_slide_target, self.amplitude_rate * step);
		self.panning = approach(self.panning, self.panning_slide_target, self.panning_rate * step);
	}
	
	/// Executes the provided command immediately.
	#[no_mangle]
	pub fn execute_command(&mut self, command: Command) -> core::result::Result<(), LSynthError> {
		match command {
			Command::ForceSetAmplitude(value) => {
				let value = value.clamp(0_f32, 1_f32);
				self.amplitude = value;
				self.ramped_amplitude = value;
				self.amplitude_slide_target = value;
			}
			
			Command::SetAmplitude(value) => {
				let value = value.clamp(0_f32, 1_f32);
				self.amplitude = value;
				self.amplitude_slide_target = value;
			}
			
			Command::AmplitudeSlide(value, rate) => {
				let value = value.clamp(0_f32, 1_f32);
				self.amplitude_slide_target = value;
				self.amplitude_rate = rate;
			}
			
			Command::SetFrequency(value) => {
				let value = value.max(0_f32);
				self.frequency = value;
				self.frequency_slide_target = value;
			}
			
			Command::FrequencySlide(value, rate) => {
				let value = value.max(0_f32);
				self.frequency_slide_target = value;
				self.frequency_rate = rate;
			}
			
			Command::ForceSetPanning(value) => {
				let value = value.clamp(-1_f32, 1_f32);
				self.panning = value;
				self.ramped_panning = value;
				self.panning_slide_target = value;
			}
			
			Command::SetPanning(value) => {
				let value = value.clamp(-1_f32, 1_f32);
				self.panning = value;
				self.panning_slide_target = value;
			}
			
			Command::PanningSlide(value, rate) => {
				let value = value.clamp(-1_f32, 1_f32);
				self.panning_slide_target = value;
				self.panning_rate = rate;
			}
			
			Command::SetWaveform(value) => {
				if value > 7 {
					return Err(LSynthError::InvalidWaveform(InvalidWaveformError {
						attempted_waveform: value,
					}));
				}
				else {
					self.waveform = value;
				}
			}
			
			Command::SetCustomWaveform(mut waveform) => {
				for value in waveform.iter_mut() {
					*value = value.clamp(-1_f32, 1_f32);
				}
				self.custom_waveform = waveform;
			}
			
			Command::SetPhase(period) => {
				self.period = period % 1.0;
			}
			//_ => panic!("Command not implemented"),
		};
		Ok(())
	}
}

/// Advances value towards target with the provided step.
fn approach(value: f32, target: f32, step: f32) -> f32 {
	let abs_rate = step.abs();
	value + (target - value).min(abs_rate).max(-abs_rate)
}