zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

Reverter.cpp (9744B)


      1 /*
      2   ZynAddSubFX - a software synthesizer
      3 
      4   Reverter.cpp - Reverse Delay
      5   Copyright (C) 2023-2024 Michael Kirchner
      6   Author: Michael Kirchner
      7 
      8   This program is free software; you can redistribute it and/or
      9   modify it under the teabs_value of the GNU General Public License
     10   as published by the Free Software Foundation; either version 2
     11   of the License, or (at your option) any later version.
     12 */
     13 
     14 #include <cassert>
     15 #include <cstdio>
     16 #include <cmath>
     17 #include <cstring>
     18 
     19 #include "../Misc/Allocator.h"
     20 #include "../Misc/Time.h"
     21 #include "Reverter.h"
     22 
     23 namespace zyn {
     24 
     25 Reverter::Reverter(Allocator *alloc, float delay_, unsigned int srate, int bufsize, float tRef_, const AbsTime *time_)
     26     : syncMode(NOTEON), input(nullptr), gain(1.0f), delay(0.0f), phase(0.0f), crossfade(0.16f),
     27       tRef(tRef_), reverse_index(0.0f), phase_offset_old(0.0f),
     28       phase_offset_fade(0.0f), fade_counter(0), mean_abs_value(999.9f), time(time_), memory(*alloc),
     29       samplerate(srate), buffersize(bufsize), max_delay_samples(srate * MAX_REV_DELAY_SECONDS)
     30 {
     31     setdelay(delay_);
     32     pos_writer = 0;
     33     pos_reader = 0;
     34     pos_start = 0;
     35     reverse_index = 0;
     36     state = PLAYING;
     37 }
     38 
     39 Reverter::~Reverter() {
     40     memory.dealloc(input);
     41 }
     42 
     43 inline float Reverter::sampleLerp(const float *smp,const float pos) {
     44     int poshi = static_cast<int>(pos);
     45     float poslo = pos - static_cast<float>(poshi);
     46     return smp[poshi] + poslo * (smp[poshi + 1] - smp[poshi]);
     47 }
     48 
     49 inline void Reverter::switchBuffers() {
     50     reverse_index = 0;
     51     phase_offset = phase * delay;
     52     // Reset the reverse index to start fresh for the new reverse playback segment.
     53     pos_start = pos_writer;
     54 
     55     float pos_next = fmodf(float(pos_start + mem_size) - (reverse_index + phase_offset), mem_size);
     56     // Determine the position of the next sample (`pos_next`) after switching buffers:
     57     // - Accounts for the current `reverse_index` (reset to 0 above) and phase offset.
     58     // - Uses modulo (`fmodf`) to ensure the position wraps correctly within the circular buffer.
     59     // The formula is the same as for pos_reader in updateReaderPosition()
     60 
     61     delta_crossfade = pos_reader - 1.0f - pos_next;
     62     // Calculate the sample distance between the current read position (`pos_reader`)
     63     // and the newly computed `pos_next`:
     64     // - Subtracting `1.0f` compensates for the fact that `pos_reader` corresponds to a read
     65     //   position from the previous tick.
     66 
     67     fade_counter = 0;
     68     // Reset the fade counter to begin a new crossfade for the next segment.
     69 }
     70 
     71 void Reverter::filterout(float *smp) {
     72     writeToRingBuffer(smp);
     73     processBuffer(smp);
     74 }
     75 
     76 void Reverter::writeToRingBuffer(const float *smp) {
     77     int space_to_end = mem_size - pos_writer;
     78     if (buffersize <= space_to_end) {
     79         // No wrap around, copy in one go
     80         memcpy(&input[pos_writer], smp, buffersize * sizeof(float));
     81     } else {
     82         // Wrap around, split into two copies
     83         memcpy(&input[pos_writer], smp, space_to_end * sizeof(float));
     84         memcpy(&input[0], smp + space_to_end, (buffersize - space_to_end) * sizeof(float));
     85     }
     86     // Update pos_writer after copying new buffer
     87     pos_writer = (pos_writer + buffersize) % mem_size;
     88     // calculate mean abs value of new buffer (for silence detection)
     89     float abs_value = 0.0f;
     90     for (int i = 0; i < buffersize; i++) {
     91         abs_value += fabsf(smp[i]);
     92     }
     93     mean_abs_value = abs_value / static_cast<float>(buffersize);
     94 }
     95 
     96 void Reverter::processBuffer(float *smp) {
     97     for (int i = 0; i < buffersize; i++) {
     98         reverse_index++;
     99         checkSync();
    100         updateReaderPosition();
    101         crossfadeSamples(smp, i);
    102         applyGain(smp[i]);
    103     }
    104 
    105     phase_offset_old = phase_offset;
    106     phase_offset_fade = 0.0f;
    107 }
    108 
    109 void Reverter::checkSync() {
    110     float delay_samples = delay * static_cast<float>(samplerate);
    111     switch (syncMode) {
    112         case AUTO:
    113             if (reverse_index >= delay_samples) {
    114                 switchBuffers();
    115             }
    116             break;
    117         case HOST:
    118             if ( (doSync && (float)reverse_index >= syncPos) || // external sync time OR
    119                 reverse_index >= max_delay_samples ) {   // note is too long buffer ends here
    120                 switchBuffers();
    121                 doSync = false;
    122             }
    123             // silence the effect if host transport is stopped
    124             if(!time->playing)
    125                 state = IDLE;
    126             else
    127                 if (state != PLAYING)
    128                 {
    129                     state = PLAYING;
    130                     reset();
    131                 }
    132 
    133             break;
    134         case NOTEON:
    135             if (reverse_index >= delay_samples && state != IDLE)
    136                 handleStateChange();
    137             break;
    138         case NOTEONOFF:
    139             if ( (reverse_index >= recorded_samples && state == PLAYING) || // finished playing OR
    140                  (reverse_index >= max_delay_samples && state == PLAYING) ||// note is too long buffer ends here OR
    141                  (mean_abs_value < 0.001f && state == RECORDING) )          // note has decayed
    142                 handleStateChange();
    143             break;
    144         default:
    145             {}
    146             break;
    147     }
    148 }
    149 
    150 void Reverter::handleStateChange() {
    151     if (state == RECORDING) {
    152         recorded_samples = reverse_index;
    153         state = PLAYING;
    154     } else if (state == PLAYING) {
    155         state = IDLE;
    156     }
    157     switchBuffers();
    158 }
    159 
    160 void Reverter::sync(float pos) {
    161     if (state == IDLE) {
    162         reset();
    163         state = RECORDING;
    164         reverse_index = 0;
    165     } else {
    166         syncPos = pos + reverse_index;
    167         doSync = true;
    168     }
    169 }
    170 
    171 void Reverter::updateReaderPosition() {
    172     //                 buffersize
    173     //                  |<---->|
    174     // ---+------+------+------+------+------+---
    175     //    |      |      |      |      |      |
    176     // ...|  b4  |  b5  |  b6  |  b7  |  b8  |...
    177     // ---+------+------+------+------+------+---
    178     //             ^           ^             ^
    179     //             |           L pos_start   L pos_writer
    180     //             L pos_reader
    181     //             <--><------->
    182     //              |       L reverse_index
    183     //              L phase_offset
    184     // //////////////////////////////////////////////////
    185 
    186     // update reading position
    187     pos_reader = fmodf(float(pos_start + mem_size) - (reverse_index + phase_offset), mem_size);
    188 }
    189 
    190 void Reverter::crossfadeSamples(float *smp, int i) {
    191     if (fade_counter < fading_samples) {
    192         float fadeinFactor = static_cast<float>(fade_counter) / static_cast<float>(fading_samples);
    193         float fadeoutFactor = 1.0f - fadeinFactor;
    194         fade_counter++;
    195 
    196         if (state == IDLE) {
    197             // in IDLE only the fade out part is needed
    198             smp[i] = fadeoutFactor * sampleLerp(input, fmodf(pos_reader + mem_size + delta_crossfade, mem_size));
    199         } else {
    200             smp[i] = applyFade(fadeinFactor, fadeoutFactor);
    201         }
    202     } else {
    203         smp[i] = (state == PLAYING) ? sampleLerp(input, pos_reader) : 0.0f;
    204     }
    205 }
    206 
    207 float Reverter::applyFade(float fadein, float fadeout) {
    208     if (syncMode == NOTEON || syncMode == NOTEONOFF) {
    209         return fadein * sampleLerp(input, pos_reader);
    210     } else {
    211         return fadein * sampleLerp(input, pos_reader) + fadeout * sampleLerp(input, fmodf(pos_reader + mem_size + delta_crossfade, mem_size));
    212     }
    213 }
    214 
    215 void Reverter::setdelay(float value) {
    216 
    217     if (value == delay)
    218         return;
    219     else
    220         delay = value;
    221 
    222     // update crossfade since it depends on delay
    223     setcrossfade(crossfade);
    224     // recalculate global offset using new delay
    225     global_offset = fmodf(tRef, delay);
    226     // update mem_size since it depends on delay and crossfade
    227     update_memsize();
    228 }
    229 
    230 void Reverter::update_memsize() {
    231     // Calculate mem_size for reverse delay effect:
    232     // - 1 times delay for recording
    233     // - 1 times delay for reverse playing
    234     // - 1 times delay for phase changing while playing
    235     // - Add crossfade duration
    236     // - Add 2 extra samples as a safety margin for interpolation and circular buffer wrapping.
    237     // - Add buffersize to prevent a buffer write into reading area
    238     unsigned int mem_size_new;
    239     if (syncMode == NOTEON || syncMode == AUTO)
    240         mem_size_new = static_cast<int>(ceilf(3.0f * delay * static_cast<float>(samplerate))) + fading_samples + 2 + buffersize;
    241     else
    242         mem_size_new = static_cast<int>(ceilf(3.0f * max_delay_samples)) + fading_samples + 2 + buffersize;
    243 
    244     if (mem_size_new == mem_size)
    245         return;
    246     else
    247         mem_size = mem_size_new;
    248 
    249     if (input != nullptr) {
    250         memory.devalloc(input);
    251     }
    252 
    253     input = memory.valloc<float>(mem_size);
    254     reset();
    255 
    256 }
    257 
    258 void Reverter::setcrossfade(float value) {
    259     crossfade = value;
    260     float delay_samples = delay * static_cast<float>(samplerate);
    261     fading_samples = static_cast<int>(crossfade * static_cast<float>(samplerate));
    262     if (delay_samples < 1.25f * static_cast<float>(fading_samples))
    263         fading_samples = static_cast<int>(delay_samples * 0.8f);
    264 }
    265 
    266 void Reverter::setsyncMode(SyncMode value) {
    267     if (value != syncMode) {
    268         syncMode = value;
    269         // initialize state machine appropriate to the new mode
    270         state = (syncMode == NOTEON || syncMode == NOTEONOFF) ? IDLE : PLAYING;
    271         // update mem_size since it depends on syncMode
    272         update_memsize();
    273     }
    274 }
    275 
    276 void Reverter::reset() {
    277     if (input != nullptr)
    278         memset(input, 0, mem_size * sizeof(float));
    279     pos_writer = 0;
    280     reverse_index = 0;
    281     syncPos = mem_size;
    282 }
    283 
    284 void Reverter::setphase(float value) {
    285     phase = value;
    286 }
    287 
    288 void Reverter::setgain(float value) {
    289     gain = dB2rap(value);
    290 }
    291 
    292 void Reverter::applyGain(float &sample) {
    293     sample *= gain;
    294 }
    295 
    296 };