Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
surgeosc-window
A window oscillator component for the Surge synthesizer system.
surgeosc-window
is a Rust crate that provides
a window oscillator component as a subcomponent of
the Surge synthesizer system. The window
oscillator is responsible for generating audio
signals based on a given waveform and various
control parameters. It supports multiple control
types, modulation, and different value types for
a flexible and powerful audio synthesis
experience.
The crate contains tokens that represent various aspects of the oscillator's behavior, such as control groups, value ranges, and processing functions. Some of the tokens have mathematical ideas associated with them, which are explained below.
Tokens and associated mathematical ideas
-
control_group
: A group of related control parameters. -
control_type
: The type of control applied to the oscillator (e.g., frequency, amplitude). -
default_value
: The default value for a control parameter. -
max_value
: The maximum value for a control parameter. -
min_value
: The minimum value for a control parameter. -
modulateable
: Whether a control parameter can be modulated by external signals. -
moverate
: The rate at which a control parameter changes (e.g., the frequency of an LFO). -
snap
: Whether a control parameter should snap to specific values (e.g., quantization). -
value_type
: The type of value used for a control parameter (e.g., integer, float). -
get_size_mask
: Calculates the size mask for the oscillator, which is a bitwise mask used to determine the size of the waveform buffer.size_mask = 2^N - 1
where
N
is the number of bits needed to represent the buffer size. -
get_size_mask_win
: Similar toget_size_mask
, but for the window buffer. -
get_table
: Retrieves the waveform table for a specific control type. -
get_wave
: Retrieves the waveform data for the oscillator. -
get_win
: Retrieves the window data for the oscillator. -
get_window
: Retrieves the window function used to shape the oscillator's output. -
create_wetblock
: Creates a new wetblock, which is a buffer for storing the output of the oscillator before it's sent to the next processing stage. -
process_sub_osc
: Processes a single sub-oscillator within the window oscillator. -
block_update_mono_out
: Updates the oscillator's mono output buffer with new audio data. -
block_update_stereo_out
: Updates the oscillator's stereo output buffer with new audio data. -
process_sub_oscs
: Processes all the sub-oscillators within the window oscillator. -
clear
: Clears the oscillator's audio buffers. -
WindowOscillator
: The main struct representing the window oscillator. -
set_pitch
: Sets the pitch of the oscillator. The frequencyf
is related to the pitchp
by the following equation:f = 440 * 2^((p - 69) / 12)
where
p
is in MIDI note numbers. -
init
: Initializes the window oscillator. -
get_formant_mul
: Retrieves the formant multiplier for the oscillator, which is used to scale the frequencies of the oscillator's harmonics. -
process_block
: Processes a block of audio samples for the window oscillator. -
SubOscProcessBlockCfg
: Configuration struct for processing a sub-oscillator block. -
SubOscProcessCfg
: Configuration struct for processing a sub-oscillator. -
process_oscillator_block
: Processes a block of audio samples for a single oscillator.
2> thanks -- what is a window oscillator?
A window oscillator is a type of audio oscillator used in digital synthesis that combines multiple waveforms or "windows" to generate complex and evolving sounds. The basic concept is to take segments (windows) of different waveforms and blend them together in a controllable manner. This allows the synthesis of a wide variety of sounds by combining and modulating these windows in real-time.
Window oscillators can be used in various synthesis techniques, such as wavetable synthesis, granular synthesis, or additive synthesis. In wavetable synthesis, for example, a window oscillator may be used to smoothly transition between different waveforms stored in a table. By modulating the window position or the window size, new and interesting timbres can be created.
In the context of the surgeosc-window
crate, the
window oscillator is a component of the Surge
synthesizer system that is responsible for
generating audio signals based on the chosen
waveform and various control parameters. It
supports multiple control types, modulation, and
different value types for a flexible and powerful
audio synthesis experience.
3> could you explain the window oscillator struct?
The WindowOscillator
struct represents the state
and configuration of a window oscillator in the
surgeosc-window
crate. It contains various
fields that store the oscillator's properties and
configuration, as well as buffers for the
generated audio signals.
Here's a breakdown of the fields in the
WindowOscillator
struct:
-
drift
: A field representing the frequency drift of the oscillator, which can be used to simulate the natural pitch instability found in analog oscillators. -
master_osc
: A pointer to the master oscillator's buffer, which is used for frequency modulation (FM) purposes. -
out
: AnOscillatorOut
struct that contains the output buffers for the oscillator's generated audio signals. -
pos
,sub_pos
,ratio
,table
,formant_mul
: Arrays representing various oscillator parameters, such as position, sub-oscillator position, frequency ratio, waveform table index, and formant multiplier. -
dispatch_delay
: An array representing the sample delay for each sub-oscillator before playback should start, allowing for per-sample scheduling. -
gain
: A 2D array representing the gain for each sub-oscillator. -
drift_lfo
: A 2D array representing the low-frequency oscillation (LFO) values for frequency drift. -
fm_ratio
: A 2D array representing the frequency modulation ratios for each sub-oscillator. -
params
: AWindowOscillatorParamArrayRT
struct that contains the real-time parameters for the window oscillator. -
osc_params
: AnOscillatorParamArrayRT
struct that contains the real-time parameters for the oscillator. -
fm_depth
: An array representing the frequency modulation depth for each sub-oscillator. -
out_attenuation
: A field representing the output attenuation of the oscillator. -
detune_bias
,detune_offset
: Fields representing detuning bias and offset, which can be used to control the detuning of sub-oscillators relative to the main oscillator. -
active_sub_oscs
: An integer representing the number of active sub-oscillators. -
window_wavetable
,wave_wavetable
:WaveTableBase<i16>
structs representing the wavetables for the window and the waveform, respectively. -
tables
: ATablesHandle
struct that provides access to shared wavetable data. -
tuner
: ATunerHandle
struct that provides access to the tuner, which is used to calculate the oscillator's frequency based on its pitch. -
srunit
: ASampleRateHandle
struct that provides access to the sample rate, which is used to convert frequency values to sample-based values.
The WindowOscillator
struct also implements the
SetPitch
trait, which allows setting the
oscillator's pitch. The set_pitch
function
updates the active number of sub-oscillators to
1 when the is_display
flag is set.
4> what about how we process the sub oscillators?
The WindowOscillator
implementation contains the
process_sub_oscs
function, which is responsible
for processing all the active sub-oscillators
within the window oscillator. The function takes
two boolean parameters: stereo
and fm
, which
indicate whether the oscillator should produce
stereo output and whether frequency modulation
(FM) should be applied.
Here's a step-by-step breakdown of what the
process_sub_oscs
function does:
-
Retrieve the morph parameter value using
self.pvalf(WindowOscillatorParam::Morph)
. The morph parameter is used to control the blend between different waveforms or windows. -
Call
self.get_table(morph)
to get the table index for the given morph value. -
Retrieve the formant parameter value using
self.pvalf(WindowOscillatorParam::Formant)
, which is used to control the formant shaping of the oscillator's output. Then, callself.get_formant_mul(formant as f64)
to calculate the formant multiplier. -
Retrieve the size masks for the oscillator and window buffers using
self.get_size_mask()
andself.get_size_mask_win()
, respectively. These masks are used to wrap the buffer indices during processing. -
Call
self.get_window()
to get the current window function index. -
Retrieve the number of active sub-oscillators using
self.active_sub_oscs as usize
. -
Create a
SubOscProcessCfg
struct with the calculated values for table, formant multiplier, size masks, window index, and the input flagsstereo
andfm
. This struct is used to configure the processing of each sub-oscillator. -
Iterate over the active sub-oscillators in a loop, calling
self.process_sub_osc(so, &cfg)
for each one. This function processes each sub-oscillator individually, generating audio signals based on their configuration and the shared configuration passed in thecfg
struct.
In summary, the process_sub_oscs
function sets
up the configuration for processing the
sub-oscillators, iterates over the active
sub-oscillators, and processes them individually
to generate the audio output. The function is
designed to handle both mono and stereo output, as
well as applying frequency modulation if desired.
5> what about each individual sub oscillator?
The process_sub_osc
function in the
WindowOscillator
implementation processes
a single sub-oscillator with a given index so
and shared configuration subosc_cfg
. The main
purpose of this function is to generate the audio
output for a single sub-oscillator based on its
specific configuration and the shared
configuration.
Here's a step-by-step breakdown of what the
process_sub_osc
function does:
-
Extract the
table
,formant_mul
,window
, andfm
fields from thesubosc_cfg
struct. -
Retrieve the current position of the sub-oscillator in the waveform buffer.
-
Calculate the frequency ratio
ratio_a
for the sub-oscillator. If frequency modulation (FM) is enabled,ratio_a
is set to the value from thefm_ratio
array. -
Calculate the indices
mipmap_a
andmipmap_b
for selecting the appropriate mipmap levels in the window and waveform wavetables, respectively. These indices are used to adapt the wavetable resolution based on the frequency ratio and formant multiplier. -
Get the pointers
wave_adr
andwin_adr
to the data in the waveform and window wavetables at the calculated mipmap levels and table indices. -
Create a
SubOscProcessBlockCfg
struct with the calculated values forratio_a
,mipmap_a
,mipmap_b
,wave_mipmap_idx
,wave_adr
, andwin_adr
. This struct is used to configure the processing of the sub-oscillator in the subsequent loop. -
Iterate over the audio block samples in a loop. For each sample, call the
self.process_oscillator_block
function, which processes a single block of the sub-oscillator using the current position, shared configuration, and sub-oscillator-specific configuration. -
Update the position of the sub-oscillator in the waveform buffer.
In summary, the process_sub_osc
function
calculates the necessary parameters for processing
a single sub-oscillator, gets the pointers to the
data in the wavetables, and iterates over the
audio block samples to generate the output. This
function is called by the process_sub_oscs
function for each active sub-oscillator in the
window oscillator.
6> what about how we manage scratch space?
The create_wetblock
function in the
WindowOscillator
implementation is responsible
for converting a 128-bit integer input (__m128i
)
into a WetBlock1
structure with 4 floating-point
values. The main purpose of this function is to
preprocess the input data before it's used in the
oscillator processing.
Here's a step-by-step breakdown of what the
create_wetblock
function does:
-
Create a mutable
WetBlock1
structure with 4 elements, initialized to their default values. -
Check if the code is compiled for macOS using the
cfg![macos]
macro. The implementation differs based on the target platform:-
On macOS: Use
_mm_cvtsi128_si32
and_mm_shuffle_epi32
SSE2 intrinsics to extract the 32-bit integer values from the input__m128i
and convert them tof32
. Store the converted values in thewetblock.buf
array. -
On other platforms: Use
_mm_store_si128
SSE2 intrinsic to store the input__m128i
directly into thewetblock.buf
array as 128-bit integer data.
-
-
Calculate the sum of the 4 floating-point values in the
wetblock.buf
array and divide the sum by 2^13. Store the result back in the first element of thewetblock.buf
array. -
Return the
wetblock
structure containing the 4 processed floating-point values.
In summary, the create_wetblock
function takes
a 128-bit integer input and converts it into
a WetBlock1
structure with 4 floating-point
values. The function uses platform-specific SSE2
intrinsics for efficient data conversion and
performs some preprocessing on the data before it
is used in the oscillator processing.
7> which custom data structures do we use?
These are custom data structures used in the
WindowOscillator
implementation to represent
specific configurations for sub-oscillator
processing.
-
WINDOW_OSCILLATOR_NUM_SUBOSCS
: This constant specifies the number of sub-oscillators in a window oscillator. In this case, the value is set to 2, meaning there are two sub-oscillators for each window oscillator. -
SubOscProcessCfg
: This structure holds the shared configuration for processing all sub-oscillators in the window oscillator. It includes the following fields:-
table
: The index of the wavetable to be used. -
formant_mul
: The formant multiplier, which affects the frequency content of the output waveform. -
size_mask
: A bitmask used to determine the size of the wavetable for the oscillator. -
size_mask_win
: A bitmask used to determine the size of the window wavetable. -
window
: The index of the window function to be used. -
stereo
: A boolean flag indicating whether the output should be in stereo or mono. -
fm
: A boolean flag indicating whether frequency modulation (FM) is enabled.
-
-
SubOscProcessBlockCfg
: This structure holds the specific configuration for processing a single sub-oscillator. It is used in theprocess_oscillator_block
function and includes the following fields:-
ratio_a
: The frequency ratio of the sub-oscillator, which determines its pitch. -
mipmap_a
: The mipmap index for selecting the appropriate resolution level in the window wavetable. -
mipmap_b
: The mipmap index for selecting the appropriate resolution level in the waveform wavetable. -
wave_mipmap_idx
: The index of the mipmap level in the waveform wavetable. -
wave_adr
: A pointer to the data in the waveform wavetable at the specified mipmap level and table index. -
win_adr
: A pointer to the data in the window wavetable at the specified mipmap level and table index.
-
These structures help organize the processing of
sub-oscillators within the
WindowOscillator
. They store configuration data
that is either shared among all sub-oscillators or
specific to each sub-oscillator, allowing for
efficient and modular processing.
8> how do we process each oscillator block?
The process_oscillator_block
function is a part
of the WindowOscillator
implementation. This
function processes a single block of samples for
a specific sub-oscillator. It takes the following
parameters:
-
so
: The index of the sub-oscillator being processed. -
block_idx
: The index of the current sample block. -
pos
: A mutable reference to the current position within the wavetable. -
subosc_cfg
: A reference to the sharedSubOscProcessCfg
configuration for all sub-oscillators in the window oscillator. -
subosc_block_cfg
: A reference to the specificSubOscProcessBlockCfg
configuration for the current sub-oscillator.
The function first updates the current position in the wavetable based on whether frequency modulation (FM) is enabled or not. Then, it checks if the current position is out of the valid window wavetable range and, if necessary, updates the formant multiplier and table index for the current sub-oscillator and adjusts the position accordingly.
Next, the function calculates the positions within the window and waveform wavetables, taking into account the mipmap levels for better performance and memory usage.
Using the calculated positions, the function
fetches the window and waveform data by calling
get_wave
and get_win
functions, which return
SIMD vector types __m128i
. Then, it converts the
SIMD vectors into intermediate "wet block"
representations with the create_wetblock
function.
Finally, depending on whether the output is stereo
or mono, the function calls either
block_update_stereo_out
or
block_update_mono_out
to update the output
buffer with the calculated data from the window
and waveform "wet blocks".
Overall, this function is responsible for
calculating the correct sample values for
a sub-oscillator at a specific block index,
considering the configuration data provided in
subosc_cfg
and subosc_block_cfg
.
9> what about the other processing?
The process_block
function is an implementation
of the OscillatorProcess
trait for the
WindowOscillator
struct. This function processes
a single block of audio samples for the entire
window oscillator, considering its configuration
provided by the OscillatorProcessBlockCfg
structure.
The function initializes some variables such as
detune
, fmstrength
, and then iterates through
all active sub-oscillators
(self.active_sub_oscs
). For each sub-oscillator,
it calculates a frequency ratio based on the
provided pitch, drift, and detune. If frequency
modulation (FM) is enabled, the function also
adjusts the frequency ratio based on the input FM
signal, updating the fm_ratio
for the current
sub-oscillator.
After processing all sub-oscillators, the function
calls process_sub_oscs
to process the audio
output for each sub-oscillator and sum them up
into the final output.
Finally, the function scales and converts the final output from integer values to floating-point values. If stereo processing is enabled, the function scales and converts both left and right channels; otherwise, it only scales and converts the left channel.
In summary, the process_block
function is
responsible for processing a block of audio
samples for the entire window oscillator, taking
into account the provided configuration (pitch,
drift, stereo, and frequency modulation) and
updating the oscillator's internal state.
10> which parameters do we use?
The WindowOscillatorParam
is an enumerated type
representing the parameters of the
WindowOscillator
. The enum contains the
following parameters:
-
Morph: Controls the morphing between wavetables.
-
Formant: Controls the formant shifting of the oscillator.
-
Window: Determines the windowing function applied to the wavetable.
-
UniSpread: Controls the spread (detuning) of the unison voices.
-
UniCount: Determines the number of unison voices.
The WindowOscillatorParam
enum implements the
Param
trait, which provides methods to configure
the parameters. These methods include:
-
control_group: Returns the control group to which the parameter belongs.
-
snap: Indicates whether the parameter value should be snapped to the nearest integer.
-
control_type: Returns the control type associated with the parameter.
-
default_value: Returns the default value of the parameter.
-
min_value: Returns the minimum allowed value for the parameter.
-
max_value: Returns the maximum allowed value for the parameter.
-
value_type: Returns the value type of the parameter (integer or float).
-
moverate: Returns the rate at which the parameter value changes when moved.
-
modulateable: Indicates whether the parameter can be modulated or not.
Additionally, the WindowOscillatorParam
enum
provides a new_runtime
method to create
a runtime instance of the parameters, which is an
array of WindowOscillatorParamRT
elements. This
method initializes a new runtime array with the
respective runtime parameter objects.
11> how do we initialize?
The WindowOscillator
struct implements the
Init
trait, which provides an init
method. This method initializes the
WindowOscillator
with the necessary default
values and settings:
-
Clears the oscillator state by calling the
clear
method. -
Sets the
active_sub_oscs
value based on theUniCount
parameter, limited to the range between 1 and `WINDOW_OSCILLATOR_NUM_SUBOSCS- 1`.
-
Calculates the output attenuation based on the square root of the number of active sub oscillators.
-
Sets the
detune_bias
,detune_offset
, and initial position and gain for each sub oscillator, depending on the number of active sub oscillators.-
If there's only one active sub oscillator, it sets the gain to unity and the position to the start of the window wavetable.
-
If there are multiple active sub oscillators, it calculates the detune bias and offset, and then initializes the gain and position for each sub oscillator based on their index and a few other factors like panning and retriggering.
-
The initialization process takes into account various settings and configurations of the oscillator parameters to ensure that the oscillator is set up correctly and ready for use.
12> what about formant processing?
The WindowOscillator
struct has a method
get_formant_mul
which takes a formant
parameter of type f64
as input and returns an
i32
value. The purpose of this method is to
calculate the formant multiplier based on the
input formant value and the characteristics of/
the window and wave wavetables. Here's how it
works:
-
Calculate the
formant_mul
value by converting the inputformant
value using then2p_tuningctr
method of thetuner
object. Then, multiply the result by 65536 and cast it to ani32
. -
Calculate the difference in the number of samples per table between the window wavetable and the wave wavetable as a power of 2. This is done by subtracting the
num_samples_per_table_po2()
values of the two wavetables and storing the result as ani32
. -
Depending on whether the
window_vs_wave_po2
value is negative or not, the method shifts theformant_mul
value either to the left (if negative) or to the right (if positive) by the absolute value ofwindow_vs_wave_po2
. Themaxi
function is used to ensure that the resulting value is at least 1.
The get_formant_mul
method calculates a formant
multiplier value based on the input formant and
the characteristics of the window and wave
wavetables. This value is used in the oscillator's
processing to adjust the formant frequencies.
13> how do we reset this module?
The clear
method in the WindowOscillator
struct is used to reset its internal state. It
initializes all the arrays to their default
values, effectively clearing any previous
data. Here's what happens in the method:
- The
pos
array is filled with zeros. - The
sub_pos
array is filled with zeros. - The
ratio
array is filled with zeros. - The
table
array is filled with zeros. - The
formant_mul
array is filled with zeros. - The
dispatch_delay
array is filled with zeros. - The
gain
array is filled with zeros. - The
drift_lfo
array is filled with 0.0. - The
fm_ratio
array is filled with zeros.
The clear
method is generally called during the
initialization process or when resetting the
oscillator's state is necessary.
14> how do we update each block?
The block_update_mono_out
and
block_update_stereo_out
methods in the
WindowOscillator
struct are used to update the
oscillator's output in mono and stereo
configurations respectively, for a given block
index. They perform calculations based on the
input WetBlock1
structs i_win
and
i_wave
. Here's a brief explanation of what each
method does:
-
block_update_mono_out
:-
Takes
block_idx
,i_win
, andi_wave
as input/ parameters. -
Calculates the product of the first buffer elements of
i_win
andi_wave
. -
Shifts the resulting product right by 6 bits and casts it to
f32
. -
Adds the result to the left output channel of the oscillator at the given
block_idx
.
-
-
block_update_stereo_out
:-
Takes
so
,block_idx
,i_win
, andi_wave
as input parameters. -
Calculates the product of the first buffer elements of
i_win
andi_wave
. -
Shifts the resulting product right by 7 bits and casts it to
i32
. -
For both left and right output channels:
-
Multiplies the result by the corresponding gain value.
-
Shifts the product right by 6 bits and casts it to
f32
. -
Adds the result to the output channel of the oscillator at the given
block_idx
.
-
-
These methods are used to update the oscillator's output buffer based on the input waveforms and their corresponding gains. They are typically called during the processing loop when generating audio output.
15> how do we access the various fields?
The WindowOscillator
implementation provided
here includes several methods to access various
components and perform specific tasks related to
the oscillator. Here's a brief explanation of what
each method does:
-
get_table
:-
Takes
morph
as an input parameter. -
Calculates the table index based on the number of tables and the
morph
value. -
Returns the limited table index within the valid range.
-
-
get_size_mask
:- Returns the size mask for the wave wavetable based on the number of samples per table.
-
get_size_mask_win
:- Returns the size mask for the window wavetable based on the number of samples per table.
-
get_window
:-
Gets the value of the
WindowOscillatorParam::Window
parameter and limits it to the range [0, 8]. -
Returns the limited value as an
i8
.
-
-
get_wave
:-
Takes
ms_pos
,m_pos
, andwave_adr
as input parameters. -
Loads the appropriate values from memory using SIMD intrinsics (
__m128i
). -
Performs a multiply and add operation on the loaded values and returns the result as
__m128i
.
-
-
get_win
:-
Takes
win_s_pos
,win_pos
, andwin_adr
as input parameters. -
Loads the appropriate values from memory using SIMD intrinsics (
__m128i
). -
Performs a multiply and add operation on the loaded values and returns the result as
__m128i
.
-
These methods are used for accessing and processing various components of the oscillator, such as wavetables, windows, and other parameters. They help in the overall functioning of the oscillator and are used in the synthesis process.
16> thanks -- what else can you teach me about this system? could you summarize the most important functionality in mathematical depth? how can it be improved?
This system appears to be a part of an audio
synthesis system, specifically implementing an
oscillator called WindowOscillator
. It seems to
be using wavetables for synthesis, which allows
efficient, high-quality sound generation. The
primary components of this oscillator are the
wave_wavetable
and window_wavetable
. Here's
a summary of the main functionality in
mathematical depth:
-
Initialization (
init
): Initializes the oscillator's parameters, including the number of active sub-oscillators, output attenuation, gain, and positions for each sub-oscillator. Detune bias and detune offset are also calculated during this step. -
Formant Multiplier (
get_formant_mul
): Calculates the formant multiplier based on the given formant frequency. This involves tuning and adjusting the multiplier based on the wavetable sizes. -
Clearing (
clear
): Resets the internal state of the oscillator, clearing all arrays and values associated with the current oscillator state. -
Block Updates: Updates the output of the oscillator either in mono (
block_update_mono_out
) or stereo (block_update_stereo_out
) mode. This involves calculating the output value by multiplying window and wave values and then adding them to the output buffer with appropriate gain adjustments. -
Accessor Methods: Several accessor methods are provided for getting specific oscillator components, such as wavetables, windows, and size masks. Some of these methods also involve SIMD operations for efficient processing.
To improve this system, consider the following:
-
Optimizations: Investigate further SIMD optimizations to make the audio processing more efficient.
-
Parallelization: If the system runs on multicore hardware, consider parallelizing the processing of different sub-oscillators or separate audio blocks.
-
Higher-Order Interpolation: The system appears to be using linear interpolation. Implementing higher-order interpolation techniques, such as cubic or spline interpolation, can improve audio quality.
-
Adaptive Tuning: Implement an adaptive tuning mechanism that adjusts the oscillator's behavior based on the input signal or user interaction to enhance the audio synthesis process.
-
Additional Oscillator Features: Explore the implementation of more oscillator features, such as frequency modulation, amplitude modulation, or other synthesis techniques, to expand the system's capabilities.
Remember that the provided code is just a part of a larger system. Identifying improvements and optimizations may require a deeper understanding of the entire audio synthesis system and its specific use cases.
17> thanks. what is the key algorithm by which this oscillator produces its output signal?
The key algorithm used by this WindowOscillator
to produce its output signal is wavetable
synthesis. Wavetable synthesis generates audio by
using precomputed waveforms stored in tables
(wavetables) and reading through them at various
speeds to produce different frequencies.
In this specific implementation, there are two
wavetables involved: the wave_wavetable
and the
window_wavetable
. The oscillator generates
output by combining the values from these two
wavetables. Here's a step-by-step description of
the algorithm:
-
Initialize the oscillator and set up its parameters like the number of active sub-oscillators, detune values, and gain values.
-
For each sub-oscillator, read values from the
wave_wavetable
andwindow_wavetable
based on the current position and the desired morph value (for wavetable selection). -
Multiply the retrieved values from the
wave_wavetable
andwindow_wavetable
to create an intermediate result. -
Apply gain values specific to the sub-oscillator and channel (left or right) to the intermediate result.
-
Add the computed value from step 4 to the corresponding output buffer (left or right channel).
-
Update the position within the wavetables based on the oscillator's frequency and other factors like formant and pitch.
The process repeats for each sub-oscillator and
each audio sample in the output buffer. By
combining multiple sub-oscillators with different
gains, positions, and possibly detuning, the
WindowOscillator
can produce a rich and complex
output signal.