Files
-
blinds / hardware_design / pcb / blinds_v60.brd
-
blinds / hardware_design / pcb / blinds_v60.sch
-
braids / hardware_design / pcb / braids_v50.brd
-
braids / hardware_design / pcb / braids_v50.sch
-
branches / hardware_design / pcb / branches_v40.brd
-
branches / hardware_design / pcb / branches_v40.sch
-
clouds / hardware_design / pcb / clouds_v30.brd
-
clouds / hardware_design / pcb / clouds_v30.sch
-
ears / hardware_design / panel / ears_panel_v30.brd
-
ears / hardware_design / panel / ears_panel_v30.sch
-
ears / hardware_design / pcb / ears_v40.brd
-
ears / hardware_design / pcb / ears_v40.sch
-
edges / hardware_design / pcb / edges_expander_v01.brd
-
edges / hardware_design / pcb / edges_expander_v01.sch
-
edges / hardware_design / pcb / edges_v20.brd
-
edges / hardware_design / pcb / edges_v20.sch
-
elements / hardware_design / pcb / elements_v02.brd
-
elements / hardware_design / pcb / elements_v02.sch
-
frames / hardware_design / pcb / frames_v03.brd
-
frames / hardware_design / pcb / frames_v03.sch
-
grids / hardware_design / pcb / grids_v02.brd
-
grids / hardware_design / pcb / grids_v02.sch
-
kinks / hardware_design / pcb / kinks_v41.brd
-
kinks / hardware_design / pcb / kinks_v41.sch
-
links / hardware_design / pcb / links_v40.brd
-
links / hardware_design / pcb / links_v40.sch
-
marbles / hardware_design / pcb / marbles_v70.brd
-
marbles / hardware_design / pcb / marbles_v70.sch
-
peaks / hardware_design / pcb / peaks_v30.brd
-
peaks / hardware_design / pcb / peaks_v30.sch
-
plaits / hardware_design / pcb / plaits_v50.brd
-
plaits / hardware_design / pcb / plaits_v50.sch
-
rings / hardware_design / pcb / rings_v30.brd
-
rings / hardware_design / pcb / rings_v30.sch
-
ripples / hardware_design / pcb / ripples_v40.brd
-
ripples / hardware_design / pcb / ripples_v40.sch
-
shades / hardware_design / pcb / shades_v30.brd
-
shades / hardware_design / pcb / shades_v30.sch
-
shelves / hardware_design / pcb / shelves_expander_v10.brd
-
shelves / hardware_design / pcb / shelves_expander_v10.sch
-
shelves / hardware_design / pcb / shelves_v05.brd
-
shelves / hardware_design / pcb / shelves_v05.sch
-
stages / hardware_design / pcb / stages_v70.brd
-
stages / hardware_design / pcb / stages_v70.sch
-
streams / hardware_design / pcb / streams_v02_bargraph.brd
-
streams / hardware_design / pcb / streams_v02_bargraph.sch
-
streams / hardware_design / pcb / streams_v05.brd
-
streams / hardware_design / pcb / streams_v05.sch
-
tides / hardware_design / pcb / tides_v40.brd
-
tides / hardware_design / pcb / tides_v40.sch
-
veils / hardware_design / pcb / veils_v40.brd
-
veils / hardware_design / pcb / veils_v40.sch
-
volts / hardware_design / pcb / volts_v01.brd
-
volts / hardware_design / pcb / volts_v01.sch
-
warps / hardware_design / pcb / warps_v30.brd
-
warps / hardware_design / pcb / warps_v30.sch
-
yarns / hardware_design / pcb / yarns_v03.brd
-
yarns / hardware_design / pcb / yarns_v03.sch
Last update 6 years 1 month
by
Olivier Gillet
ominous_voice.cc// Copyright 2014 Olivier Gillet. // // Author: Olivier Gillet (ol.gillet@gmail.com) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // // See http://creativecommons.org/licenses/MIT/ for more information. // // ----------------------------------------------------------------------------- // // "Ominous" is a dark 2x2-op FM synth (based on Braids' FM and FBFM modes). #include "elements/dsp/ominous_voice.h" #include <algorithm> #include <cstdio> namespace elements { using namespace std; using namespace stmlib; // scipy.signal.remez(101, [0, 0.3 / 8, 0.495 / 8, 0.5], [1, 0]); const float kDownsamplingFilter[] = { -0.001859272945f, 0.001184937535f, 0.001212413444f, 0.001369688661f, 0.001555406705f, 0.001685761819f, 0.001692922383f, 0.001526182555f, 0.001157229282f, 0.000582212588f, -0.000172916131f, -0.001054896973f, -0.001981709311f, -0.002852566234f, -0.003555372122f, -0.003977161477f, -0.004020648315f, -0.003616652394f, -0.002735861951f, -0.001399720214f, 0.000314317050f, 0.002272758644f, 0.004295073578f, 0.006166173855f, 0.007654982354f, 0.008534833653f, 0.008614353501f, 0.007761247708f, 0.005919024408f, 0.003133736224f, -0.000445934810f, -0.004563290490f, -0.008867277113f, -0.012932060625f, -0.016287075604f, -0.018456326393f, -0.018994127671f, -0.017542469537f, -0.013838153737f, -0.007789574712f, 0.000541523640f, 0.010920252821f, 0.022936167260f, 0.036030557729f, 0.049530599001f, 0.062696232995f, 0.074769247024f, 0.085030908865f, 0.092854033134f, 0.097753623479f, 0.099422113893f, 0.097753623479f, 0.092854033134f, 0.085030908865f, 0.074769247024f, 0.062696232995f, 0.049530599001f, 0.036030557729f, 0.022936167260f, 0.010920252821f, 0.000541523640f, -0.007789574712f, -0.013838153737f, -0.017542469537f, -0.018994127671f, -0.018456326393f, -0.016287075604f, -0.012932060625f, -0.008867277113f, -0.004563290490f, -0.000445934810f, 0.003133736224f, 0.005919024408f, 0.007761247708f, 0.008614353501f, 0.008534833653f, 0.007654982354f, 0.006166173855f, 0.004295073578f, 0.002272758644f, 0.000314317050f, -0.001399720214f, -0.002735861951f, -0.003616652394f, -0.004020648315f, -0.003977161477f, -0.003555372122f, -0.002852566234f, -0.001981709311f, -0.001054896973f, -0.000172916131f, 0.000582212588f, 0.001157229282f, 0.001526182555f, 0.001692922383f, 0.001685761819f, 0.001555406705f, 0.001369688661f, 0.001212413444f, 0.001184937535f, -0.001859272945f, }; void Spatializer::Init(float fixed_position) { angle_ = 0.0f; fixed_position_ = fixed_position; left_ = 0.0f; right_ = 0.0f; distance_ = 0.0f; behind_filter_.Init(); behind_filter_.set_f_q<FREQUENCY_EXACT>(0.05f, 1.0f); } void Spatializer::Process( float* source, float* center, float* sides, size_t size) { behind_filter_.Process<FILTER_MODE_LOW_PASS>(source, behind_, size, 1); float angle = angle_; float x = distance_ * stmlib::InterpolateWrap( lut_sine, angle, 4096.0f); float y = distance_ * stmlib::InterpolateWrap( lut_sine, angle + 0.25f, 4096.0f); float backfront = (1.0f + y) * 0.5f * distance_; x += fixed_position_ * (1.0f - distance_); float target_left = stmlib::InterpolateWrap( lut_sine, (1.0f + x) * 0.125f, 4096.0f); float target_right = stmlib::InterpolateWrap( lut_sine, (3.0f + x) * 0.125f, 4096.0f); // Prevent zipper noise during rendering. float step = 1.0f / static_cast<float>(size); float left_increment = (target_left - left_) * step; float right_increment = (target_right - right_) * step; for (size_t i = 0; i < size; ++i) { left_ += left_increment; right_ += right_increment; float y = source[i] + backfront * (behind_[i] - source[i]); float l = left_ * y; float r = right_ * y; center[i] += (l + r) * 0.5f; sides[i] += (l - r) * 0.5f / 0.7f; } } void FmOscillator::Process( float frequency, float ratio, float feedback_amount, float target_fm_amount, const float* external_fm, float* destination, size_t size) { ratio = Interpolate(lut_fm_frequency_quantizer, ratio, 128.0f); uint32_t inc_carrier = midi_to_increment(frequency); uint32_t inc_mod = midi_to_increment(frequency + ratio); uint32_t phase_carrier = phase_carrier_; uint32_t phase_mod = phase_mod_; // Linear interpolation on FM amount parameter. float step = 1.0f / static_cast<float>(size); float fm_amount = fm_amount_; float fm_amount_increment = (target_fm_amount - fm_amount) * step; float previous_sample = previous_sample_; // To prevent aliasing, reduce FM amount when frequency or feedback are // too high. float brightness = frequency + ratio * 0.75f - 60.0f + \ feedback_amount * 24.0f; float amount_attenuation = brightness <= 0.0f ? 1.0f : 1.0f - brightness * brightness * 0.0015f; if (amount_attenuation < 0.0f) { amount_attenuation = 0.0f; } for (size_t i = 0; i < size; ++i) { fm_amount += fm_amount_increment; phase_carrier += inc_carrier; phase_mod += inc_mod; float mod = SineFm(phase_mod, feedback_amount * previous_sample); destination[i] = previous_sample = SineFm( phase_carrier, amount_attenuation * (mod * fm_amount + external_fm[i])); } phase_carrier_ = phase_carrier; phase_mod_ = phase_mod; fm_amount_ = fm_amount; previous_sample_ = previous_sample; } void OminousVoice::Init() { envelope_.Init(); envelope_.set_adsr(0.5f, 0.5f, 0.5f, 0.5f); previous_gate_ = false; level_state_ = 0.0f; for (size_t i = 0; i < kNumOscillators; ++i) { external_fm_state_[i] = 0.0f; oscillator_[i].Init(); // Downsampling is done mostly by the FIR, but since the stopband // attenuation peaks at -48dB, we can get a few extra dB of attenution with // the IIR for the highest frequencies. fir_downsampler_[i].Init(kDownsamplingFilter); iir_downsampler_[i].Init(); iir_downsampler_[i].set_f_q<FREQUENCY_EXACT>( 1.0f / kOversamplingUp * 0.8f, 0.5f); osc_level_[i] = 0.0f; filter_[i].Init(); spatializer_[i].Init(i == 0 ? - 0.7f : 0.7f); } } void OminousVoice::ConfigureEnvelope(const Patch& patch) { if (patch.exciter_envelope_shape < 0.4f) { float a = 0.0f; float dr = (patch.exciter_envelope_shape * 0.625f + 0.2f) * 1.8f; envelope_.set_adsr(a, dr, 0.0f, dr); } else if (patch.exciter_envelope_shape < 0.6f) { float s = (patch.exciter_envelope_shape - 0.4f) * 5.0f; envelope_.set_adsr(0.0f, 0.80f, s, 0.80f); } else { float a = 0.0f; float dr = ((1.0f - patch.exciter_envelope_shape) * 0.75f + 0.15f) * 1.8f; envelope_.set_adsr(a, dr, 1.0f, dr); } } void OminousVoice::Process( const Patch& patch, float frequency, float strength, const bool gate_in, const float* blow_in, const float* strike_in, float* raw, float* center, float* sides, size_t size) { uint8_t flags = GetGateFlags(gate_in); // Compute the envelope. ConfigureEnvelope(patch); float level = envelope_.Process(flags); level += strength >= 0.5f ? 2.0f * strength - 1.0f : 0.0f; float level_increment = (level - level_state_) / size; damping_ += 0.1f * (patch.resonator_damping - damping_); float filter_env_amount = damping_ <= 0.9f ? 1.1f * damping_ : 0.99f; float vca_env_amount = 1.0f + \ damping_ * damping_ * damping_ * damping_ * 0.5f; // Comfigure the filter. float cutoff_midi = 12.0f; cutoff_midi += patch.resonator_brightness * 140.0f; cutoff_midi += filter_env_amount * level * 120.0f; cutoff_midi += 0.5f * (frequency - 64.0f); float cutoff = midi_to_frequency(cutoff_midi); float q_bump = patch.resonator_geometry - 0.6f; float q = 1.72f - q_bump * q_bump * 2.0f; float cutoff_2 = cutoff * (1.0f + patch.resonator_modulation_offset); filter_[0].set_f_q<FREQUENCY_FAST>(cutoff, q); filter_[1].set_f_q<FREQUENCY_FAST>(cutoff_2, q * 1.25f); // Process each oscillator. fill(¢er[0], ¢er[size], 0.0f); fill(&sides[0], &sides[size], 0.0f); fill(&raw[0], &raw[size], 0.0f); const float rotation_speed[2] = { 1.0f, 1.123456f }; feedback_ += 0.01f * (patch.exciter_bow_timbre - feedback_); frequency += kOversamplingDownMidi; for (size_t i = 0; i < 2; ++i) { Upsample<kOversamplingUp>( &external_fm_state_[i], i == 0 ? blow_in : strike_in, external_fm_oversampled_, size); float detune, ratio, amount, level; if (i == 0) { detune = 0.0f; ratio = patch.exciter_blow_meta; amount = patch.exciter_blow_timbre; level = patch.exciter_blow_level; } else { detune = Interpolate( lut_detune_quantizer, patch.exciter_bow_level, 64.0f); ratio = patch.exciter_strike_meta; amount = patch.exciter_strike_timbre; level = patch.exciter_strike_level; } oscillator_[i].Process( frequency + detune, ratio, feedback_ * (0.25f + 0.15f * patch.exciter_signature), (2.0f - patch.exciter_signature * feedback_) * amount, external_fm_oversampled_, osc_oversampled_, size * kOversamplingUp); iir_downsampler_[i].Process<FILTER_MODE_LOW_PASS>( osc_oversampled_, osc_oversampled_, size * kOversamplingUp, 1); fir_downsampler_[i].Process(osc_oversampled_, osc_, size * kOversamplingUp); // Copy to raw buffer. float level_state = osc_level_[i]; for (size_t j = 0; j < size; ++j) { level_state += 0.01f * (level - level_state); osc_[j] *= level_state; raw[j] += osc_[j]; } osc_level_[i] = level_state; // Apply filter. filter_[i].ProcessMultimode(osc_, osc_, size, patch.resonator_geometry); // Apply VCA. float l = level_state_; for (size_t j = 0; j < size; ++j) { float gain = l * vca_env_amount; if (gain >= 1.0f) gain = 1.0f; osc_[j] *= gain; l += level_increment; } // Spatialize. float f = patch.resonator_position * patch.resonator_position * 0.001f; float distance = patch.resonator_position; spatializer_[i].Rotate(f * rotation_speed[i]); spatializer_[i].set_distance(distance * (2.0f - distance)); spatializer_[i].Process(osc_, center, sides, size); } level_state_ = level; } } // namespace elements