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 };