Files

copied
Last update 6 years 4 months by Olivier Gillet
Filesringsdsp
..
fx
dsp.h
fm_voice.cc
fm_voice.h
follower.h
limiter.h
note_filter.h
onset_detector.h
part.cc
part.h
patch.h
performance_state.h
plucker.h
resonator.cc
resonator.h
string.cc
string.h
string_synth_envelope.h
string_synth_oscillator.h
string_synth_part.cc
string_synth_part.h
string_synth_voice.h
strummer.h
string_synth_part.cc
// 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
Report a bug