|
- // Copyright 2015 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.
- //
- // -----------------------------------------------------------------------------
- //
- // String synth part.
-
- #include "rings/dsp/string_synth_part.h"
-
- #include "rings/dsp/dsp.h"
-
- namespace rings {
-
- using namespace std;
- using namespace stmlib;
-
- void StringSynthPart::Init(uint16_t* reverb_buffer) {
- active_group_ = 0;
- acquisition_delay_ = 0;
-
- polyphony_ = 1;
- fx_type_ = FX_ENSEMBLE;
-
- for (int32_t i = 0; i < kStringSynthVoices; ++i) {
- voice_[i].Init();
- }
-
- for (int32_t i = 0; i < kMaxStringSynthPolyphony; ++i) {
- group_[i].tonic = 0.0f;
- group_[i].envelope.Init();
- }
-
- for (int32_t i = 0; i < kNumFormants; ++i) {
- formant_filter_[i].Init();
- }
-
- limiter_.Init();
-
- reverb_.Init(reverb_buffer);
- chorus_.Init(reverb_buffer);
- ensemble_.Init(reverb_buffer);
-
- note_filter_.Init(
- kSampleRate / kMaxBlockSize,
- 0.001f, // Lag time with a sharp edge on the V/Oct input or trigger.
- 0.005f, // Lag time after the trigger has been received.
- 0.050f, // Time to transition from reactive to filtered.
- 0.004f); // Prevent a sharp edge to partly leak on the previous voice.
- }
-
- const int32_t kRegistrationTableSize = 11;
- const float registrations[kRegistrationTableSize][kNumHarmonics * 2] = {
- { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
- { 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f },
- { 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f },
- { 1.0f, 0.1f, 0.0f, 0.0f, 1.0f, 0.0f },
- { 1.0f, 0.5f, 1.0f, 0.0f, 1.0f, 0.0f },
- { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f },
- { 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f },
- { 0.0f, 0.5f, 1.0f, 0.0f, 1.0f, 0.0f },
- { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f },
- { 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f },
- { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f },
- };
-
- void StringSynthPart::ComputeRegistration(
- float gain,
- float registration,
- float* amplitudes) {
- registration *= (kRegistrationTableSize - 1.001f);
- MAKE_INTEGRAL_FRACTIONAL(registration);
- float total = 0.0f;
- for (int32_t i = 0; i < kNumHarmonics * 2; ++i) {
- float a = registrations[registration_integral][i];
- float b = registrations[registration_integral + 1][i];
- amplitudes[i] = a + (b - a) * registration_fractional;
- total += amplitudes[i];
- }
- for (int32_t i = 0; i < kNumHarmonics * 2; ++i) {
- amplitudes[i] = gain * amplitudes[i] / total;
- }
- }
-
- #ifdef BRYAN_CHORDS
-
- // Chord table by Bryan Noll:
- // - more compact, leaving room for a bass
- // - more frequent note changes between adjacent chords.
- // - dropped fifth.
- const float chords[kMaxStringSynthPolyphony][kNumChords][kMaxChordSize] = {
- {
- { -12.0f, -0.01f, 0.0f, 0.01f, 0.02f, 11.99f, 12.0f, 24.0f }, // OCT
- { -12.0f, -5.01f, -5.0f, 0.0f, 7.0f, 12.0f, 19.0f, 24.0f }, // 5
- { -12.0f, -5.0f, 0.0f, 5.0f, 7.0f, 12.0f, 17.0f, 24.0f }, // sus4
- { -12.0f, -5.0f, 0.0f, 0.01f, 3.0f, 12.0f, 19.0f, 24.0f }, // m
- { -12.0f, -5.01f, -5.0f, 0.0f, 3.0f, 10.0f, 19.0f, 24.0f }, // m7
- { -12.0f, -5.0f, 0.0f, 3.0f, 10.0f, 14.0f, 19.0f, 24.0f }, // m9
- { -12.0f, -5.01f, -5.0f, 0.0f, 3.0f, 10.0f, 17.0f, 24.0f }, // m11
- { -12.0f, -5.0f, 0.0f, 2.0f, 9.0f, 16.0f, 19.0f, 24.0f }, // 69
- { -12.0f, -5.0f, 0.0f, 4.0f, 11.0f, 14.0f, 19.0f, 24.0f }, // M9
- { -12.0f, -5.0f, 0.0f, 4.0f, 7.0f, 11.0f, 19.0f, 24.0f }, // M7
- { -12.0f, -5.0f, 0.0f, 4.0f, 7.0f, 12.0f, 19.0f, 24.0f }, // M
- },
- {
- { -12.0f, -0.01f, 0.0f, 0.01f, 12.0f, 12.01f }, // OCT
- { -12.0f, -5.01f, -5.0f, 0.0f, 7.0f, 12.0f }, // 5
- { -12.0f, -5.0f, 0.0f, 5.0f, 7.0f, 12.0f }, // sus4
- { -12.0f, -5.0f, 0.0f, 0.01f, 3.0f, 12.0f }, // m
- { -12.0f, -5.01f, -5.0f, 0.0f, 3.0f, 10.0f }, // m7
- { -12.0f, -5.0f, 0.0f, 3.0f, 10.0f, 14.0f }, // m9
- { -12.0f, -5.0f, 0.0f, 3.0f, 10.0f, 17.0f }, // m11
- { -12.0f, -5.0f, 0.0f, 2.0f, 9.0f, 16.0f }, // 69
- { -12.0f, -5.0f, 0.0f, 4.0f, 11.0f, 14.0f }, // M9
- { -12.0f, -5.0f, 0.0f, 4.0f, 7.0f, 11.0f }, // M7
- { -12.0f, -5.0f, 0.0f, 4.0f, 7.0f, 12.0f }, // M
- },
- {
- { -12.0f, 0.0f, 0.01f, 12.0f }, // OCT
- { -12.0f, 6.99f, 7.0f, 12.0f }, // 5
- { -12.0f, 5.0f, 7.0f, 12.0f }, // sus4
- { -12.0f, 3.0f, 11.99f, 12.0f }, // m
- { -12.0f, 3.0f, 9.99f, 10.0f }, // m7
- { -12.0f, 3.0f, 10.0f, 14.0f }, // m9
- { -12.0f, 3.0f, 10.0f, 17.0f }, // m11
- { -12.0f, 2.0f, 9.0f, 16.0f }, // 69
- { -12.0f, 4.0f, 11.0f, 14.0f }, // M9
- { -12.0f, 4.0f, 7.0f, 11.0f }, // M7
- { -12.0f, 4.0f, 7.0f, 12.0f }, // M
- },
- {
- { 0.0f, 0.01f, 12.0f }, // OCT
- { 0.0f, 7.0f, 12.0f }, // 5
- { 5.0f, 7.0f, 12.0f }, // sus4
- { 0.0f, 3.0f, 12.0f }, // m
- { 0.0f, 3.0f, 10.0f }, // m7
- { 3.0f, 10.0f, 14.0f }, // m9
- { 3.0f, 10.0f, 17.0f }, // m11
- { 2.0f, 9.0f, 16.0f }, // 69
- { 4.0f, 11.0f, 14.0f }, // M9
- { 4.0f, 7.0f, 11.0f }, // M7
- { 4.0f, 7.0f, 12.0f }, // M
- }
- };
-
- #else
-
- // Original chord table:
- // - wider, occupies more room in the spectrum.
- // - minimum number of note changes between adjacent chords.
- // - consistant with the chord table used for the sympathetic strings model.
- const float chords[kMaxStringSynthPolyphony][kNumChords][kMaxChordSize] = {
- {
- { -24.0f, -12.0f, 0.0f, 0.01f, 0.02f, 11.99f, 12.0f, 24.0f },
- { -24.0f, -12.0f, 0.0f, 3.0f, 7.0f, 10.0f, 19.0f, 24.0f },
- { -24.0f, -12.0f, 0.0f, 3.0f, 7.0f, 12.0f, 19.0f, 24.0f },
- { -24.0f, -12.0f, 0.0f, 3.0f, 7.0f, 14.0f, 19.0f, 24.0f },
- { -24.0f, -12.0f, 0.0f, 3.0f, 7.0f, 17.0f, 19.0f, 24.0f },
- { -24.0f, -12.0f, 0.0f, 6.99f, 7.0f, 18.99f, 19.0f, 24.0f },
- { -24.0f, -12.0f, 0.0f, 4.0f, 7.0f, 17.0f, 19.0f, 24.0f },
- { -24.0f, -12.0f, 0.0f, 4.0f, 7.0f, 14.0f, 19.0f, 24.0f },
- { -24.0f, -12.0f, 0.0f, 4.0f, 7.0f, 12.0f, 19.0f, 24.0f },
- { -24.0f, -12.0f, 0.0f, 4.0f, 7.0f, 11.0f, 19.0f, 24.0f },
- { -24.0f, -12.0f, 0.0f, 5.0f, 7.0f, 12.0f, 17.0f, 24.0f },
- },
- {
- { -24.0f, -12.0f, 0.0f, 0.01f, 12.0f, 12.01f },
- { -24.0f, -12.0f, 0.0f, 3.00f, 7.0f, 10.0f },
- { -24.0f, -12.0f, 0.0f, 3.00f, 7.0f, 12.0f },
- { -24.0f, -12.0f, 0.0f, 3.00f, 7.0f, 14.0f },
- { -24.0f, -12.0f, 0.0f, 3.00f, 7.0f, 17.0f },
- { -24.0f, -12.0f, 0.0f, 6.99f, 12.0f, 19.0f },
- { -24.0f, -12.0f, 0.0f, 4.00f, 7.0f, 17.0f },
- { -24.0f, -12.0f, 0.0f, 4.00f, 7.0f, 14.0f },
- { -24.0f, -12.0f, 0.0f, 4.00f, 7.0f, 12.0f },
- { -24.0f, -12.0f, 0.0f, 4.00f, 7.0f, 11.0f },
- { -24.0f, -12.0f, 0.0f, 5.00f, 7.0f, 12.0f },
- },
- {
- { -12.0f, 0.0f, 0.01f, 12.0f },
- { -12.0f, 3.0f, 7.0f, 10.0f },
- { -12.0f, 3.0f, 7.0f, 12.0f },
- { -12.0f, 3.0f, 7.0f, 14.0f },
- { -12.0f, 3.0f, 7.0f, 17.0f },
- { -12.0f, 7.0f, 12.0f, 19.0f },
- { -12.0f, 4.0f, 7.0f, 17.0f },
- { -12.0f, 4.0f, 7.0f, 14.0f },
- { -12.0f, 4.0f, 7.0f, 12.0f },
- { -12.0f, 4.0f, 7.0f, 11.0f },
- { -12.0f, 5.0f, 7.0f, 12.0f },
- },
- {
- { 0.0f, 0.01f, 12.0f },
- { 0.0f, 3.0f, 10.0f },
- { 0.0f, 3.0f, 7.0f },
- { 0.0f, 3.0f, 14.0f },
- { 0.0f, 3.0f, 17.0f },
- { 0.0f, 7.0f, 19.0f },
- { 0.0f, 4.0f, 17.0f },
- { 0.0f, 4.0f, 14.0f },
- { 0.0f, 4.0f, 7.0f },
- { 0.0f, 4.0f, 11.0f },
- { 0.0f, 5.0f, 7.0f },
- }
- };
-
- #endif // BRYAN_CHORDS
-
- void StringSynthPart::ProcessEnvelopes(
- float shape,
- uint8_t* flags,
- float* values) {
- float decay = shape;
- float attack = 0.0f;
- if (shape < 0.5f) {
- attack = 0.0f;
- } else {
- attack = (shape - 0.5f) * 2.0f;
- }
-
- // Convert the arbitrary values to actual units.
- float period = kSampleRate / kMaxBlockSize;
- float attack_time = SemitonesToRatio(attack * 96.0f) * 0.005f * period;
- // float decay_time = SemitonesToRatio(decay * 96.0f) * 0.125f * period;
- float decay_time = SemitonesToRatio(decay * 84.0f) * 0.180f * period;
- float attack_rate = 1.0f / attack_time;
- float decay_rate = 1.0f / decay_time;
-
- for (int32_t i = 0; i < polyphony_; ++i) {
- float drone = shape < 0.98f ? 0.0f : (shape - 0.98f) * 55.0f;
- if (drone >= 1.0f) drone = 1.0f;
-
- group_[i].envelope.set_ad(attack_rate, decay_rate);
- float value = group_[i].envelope.Process(flags[i]);
- values[i] = value + (1.0f - value) * drone;
- }
- }
-
- const int32_t kFormantTableSize = 5;
- const float formants[kFormantTableSize][kNumFormants] = {
- { 700, 1100, 2400 },
- { 500, 1300, 1700 },
- { 400, 2000, 2500 },
- { 600, 800, 2400 },
- { 300, 900, 2200 },
- };
-
- void StringSynthPart::ProcessFormantFilter(
- float vowel,
- float shift,
- float resonance,
- float* out,
- float* aux,
- size_t size) {
- for (size_t i = 0; i < size; ++i) {
- filter_in_buffer_[i] = out[i] + aux[i];
- }
- fill(&out[0], &out[size], 0.0f);
- fill(&aux[0], &aux[size], 0.0f);
-
- vowel *= (kFormantTableSize - 1.001f);
- MAKE_INTEGRAL_FRACTIONAL(vowel);
-
- for (int32_t i = 0; i < kNumFormants; ++i) {
- float a = formants[vowel_integral][i];
- float b = formants[vowel_integral + 1][i];
- float f = a + (b - a) * vowel_fractional;
- f *= shift;
- formant_filter_[i].set_f_q<FREQUENCY_DIRTY>(f / kSampleRate, resonance);
- formant_filter_[i].Process<FILTER_MODE_BAND_PASS>(
- filter_in_buffer_,
- filter_out_buffer_,
- size);
- const float pan = i * 0.3f + 0.2f;
- for (size_t j = 0; j < size; ++j) {
- out[j] += filter_out_buffer_[j] * pan * 0.5f;
- aux[j] += filter_out_buffer_[j] * (1.0f - pan) * 0.5f;
- }
- }
- }
-
- struct ChordNote {
- float note;
- float amplitude;
- };
-
- void StringSynthPart::Process(
- const PerformanceState& performance_state,
- const Patch& patch,
- const float* in,
- float* out,
- float* aux,
- size_t size) {
- // Assign note to a voice.
- uint8_t envelope_flags[kMaxStringSynthPolyphony];
-
- fill(&envelope_flags[0], &envelope_flags[polyphony_], 0);
- note_filter_.Process(performance_state.note, performance_state.strum);
- if (performance_state.strum) {
- group_[active_group_].tonic = note_filter_.stable_note();
- envelope_flags[active_group_] = ENVELOPE_FLAG_FALLING_EDGE;
- active_group_ = (active_group_ + 1) % polyphony_;
- envelope_flags[active_group_] = ENVELOPE_FLAG_RISING_EDGE;
- acquisition_delay_ = 3;
- }
- if (acquisition_delay_) {
- --acquisition_delay_;
- } else {
- group_[active_group_].tonic = note_filter_.note();
- group_[active_group_].chord = performance_state.chord;
- group_[active_group_].structure = patch.structure;
- envelope_flags[active_group_] |= ENVELOPE_FLAG_GATE;
- }
-
- // Process envelopes.
- float envelope_values[kMaxStringSynthPolyphony];
- ProcessEnvelopes(patch.damping, envelope_flags, envelope_values);
-
- copy(&in[0], &in[size], &aux[0]);
- copy(&in[0], &in[size], &out[0]);
- int32_t chord_size = min(kStringSynthVoices / polyphony_, kMaxChordSize);
- for (int32_t group = 0; group < polyphony_; ++group) {
- ChordNote notes[kMaxChordSize];
- float harmonics[kNumHarmonics * 2];
-
- ComputeRegistration(
- envelope_values[group] * 0.25f,
- patch.brightness,
- harmonics);
-
- // Note enough polyphony for smooth transition between chords.
- for (int32_t i = 0; i < chord_size; ++i) {
- float n = chords[polyphony_ - 1][group_[group].chord][i];
- notes[i].note = n;
- notes[i].amplitude = n >= 0.0f && n <= 17.0f ? 1.0f : 0.7f;
- }
-
- for (int32_t chord_note = 0; chord_note < chord_size; ++chord_note) {
- float note = 0.0f;
- note += group_[group].tonic;
- note += performance_state.tonic;
- note += performance_state.fm;
- note += notes[chord_note].note;
-
- float amplitudes[kNumHarmonics * 2];
- for (int32_t i = 0; i < kNumHarmonics * 2; ++i) {
- amplitudes[i] = notes[chord_note].amplitude * harmonics[i];
- }
-
- // Fold truncated harmonics.
- size_t num_harmonics = polyphony_ >= 2 && chord_note < 2
- ? kNumHarmonics - 1
- : kNumHarmonics;
- for (int32_t i = num_harmonics; i < kNumHarmonics; ++i) {
- amplitudes[2 * (num_harmonics - 1)] += amplitudes[2 * i];
- amplitudes[2 * (num_harmonics - 1) + 1] += amplitudes[2 * i + 1];
- }
-
- float frequency = SemitonesToRatio(note - 69.0f) * a3;
- voice_[group * chord_size + chord_note].Render(
- frequency,
- amplitudes,
- num_harmonics,
- (group + chord_note) & 1 ? out : aux,
- size);
- }
- }
-
- if (clear_fx_) {
- reverb_.Clear();
- clear_fx_ = false;
- }
-
- switch (fx_type_) {
- case FX_FORMANT:
- case FX_FORMANT_2:
- ProcessFormantFilter(
- patch.position,
- fx_type_ == FX_FORMANT ? 1.0f : 1.1f,
- fx_type_ == FX_FORMANT ? 25.0f : 10.0f,
- out,
- aux,
- size);
- break;
-
- case FX_CHORUS:
- chorus_.set_amount(patch.position);
- chorus_.set_depth(0.15f + 0.5f * patch.position);
- chorus_.Process(out, aux, size);
- break;
-
- case FX_ENSEMBLE:
- ensemble_.set_amount(patch.position * (2.0f - patch.position));
- ensemble_.set_depth(0.2f + 0.8f * patch.position * patch.position);
- ensemble_.Process(out, aux, size);
- break;
-
- case FX_REVERB:
- case FX_REVERB_2:
- reverb_.set_amount(patch.position * 0.5f);
- reverb_.set_diffusion(0.625f);
- reverb_.set_time(fx_type_ == FX_REVERB
- ? (0.5f + 0.49f * patch.position)
- : (0.3f + 0.6f * patch.position));
- reverb_.set_input_gain(0.2f);
- reverb_.set_lp(fx_type_ == FX_REVERB ? 0.3f : 0.6f);
- reverb_.Process(out, aux, size);
- break;
-
- default:
- break;
- }
-
- // Prevent main signal cancellation when EVEN gets summed with ODD through
- // normalization.
- for (size_t i = 0; i < size; ++i) {
- aux[i] = -aux[i];
- }
- limiter_.Process(out, aux, size, 1.0f);
- }
-
- } // namespace rings
|