Reverse.cpp (10550B)
1 /* 2 ZynAddSubFX - a software synthesizer 3 4 Reverse.cpp - Reverse Delay Effect 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 terms 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 <cmath> 15 #include <rtosc/ports.h> 16 #include <rtosc/port-sugar.h> 17 #include "../DSP/Reverter.h" 18 #include "../Misc/Allocator.h" 19 #include "../Misc/Util.h" 20 #include "../Misc/Time.h" 21 #include "Reverse.h" 22 23 namespace zyn { 24 #define HZ2BPM 60.0f // frequency (Hz) * HZ2BPM = frequency (BPM) 25 26 #define rObject Reverse 27 #define rBegin [](const char *msg, rtosc::RtData &d) { 28 #define rEnd } 29 30 rtosc::Ports Reverse::ports = { 31 {"preset::i", rProp(parameter) 32 rOptions(noteon, noteonoff, auto) 33 rProp(alias) 34 rShort("preset") 35 rDefault(0) 36 rDoc("Instrument Presets"), 0, 37 rBegin; 38 rObject *o = (rObject*)d.obj; 39 if(rtosc_narguments(msg)) 40 o->setpreset(rtosc_argument(msg, 0).i); 41 else 42 d.reply(d.loc, "i", o->Ppreset); 43 rEnd}, 44 rPresetForVolume, 45 rEffParVol(rDefaultDepends(presetOfVolume), 46 rPresets(32, 32, 32), 47 rPresetsAt(16, 64, 64, 64)), 48 rEffParPan(), 49 rEffPar(Pdelay, 2, rShort("delay"), rDefault(25), 50 "Length of Reversed Segment"), 51 rEffParTF(Pstereo,3, rShort("stereo"), rDefault(false), 52 "Enable Stereo Processing"), 53 rEffPar(Pphase, 4, rShort("phase"), rDefault(64), 54 "Phase offset for Reversed Segment"), 55 rEffPar(Pcrossfade, 5, rShort("fade"), rPresets(32, 16, 50), rUnit(1/100 s), 56 "Cross Fade Time between Reversed Segments"), 57 rEffParOpt(Psyncmode, 6, rShort("mode"), rPresets(NOTEON, NOTEONOFF, AUTO), 58 rOptions(SYNCMODES), 59 "Sync Mode"), 60 }; 61 #undef rBegin 62 #undef rEnd 63 #undef rObject 64 65 Reverse::Reverse(EffectParams pars, const AbsTime *time_) 66 :Effect(pars),Pvolume(insertion?64:32),Pdelay(25),Pphase(64), Pcrossfade(32), 67 PsyncMode(Reverter::SyncMode::NOTEON), Pstereo(0),time(time_), tick_hist(0) 68 { 69 float tRef = float(time->time()); 70 reverterL = memory.alloc<Reverter>(&memory, float(Pdelay+1)/128.0f*MAX_REV_DELAY_SECONDS, samplerate, buffersize, tRef, time); 71 reverterR = memory.alloc<Reverter>(&memory, float(Pdelay+1)/128.0f*MAX_REV_DELAY_SECONDS, samplerate, buffersize, tRef, time); 72 setpanning(64); 73 setvolume(Pvolume); 74 tick_at_host_buffer_start = (time->beat - 1) * time->ppq + time->tick; 75 playing_hist = false; 76 } 77 78 Reverse::~Reverse() 79 { 80 memory.dealloc(reverterL); 81 memory.dealloc(reverterR); 82 } 83 84 //Cleanup the effect 85 void Reverse::cleanup(void) 86 { 87 reverterR->reset(); 88 reverterL->reset(); 89 } 90 91 //Effect output 92 void Reverse::out(const Stereo<float *> &input) 93 { 94 // prepare the processing buffers 95 if(Pstereo) //Stereo 96 for(int i = 0; i < buffersize; ++i) { 97 efxoutl[i] = input.l[i] * pangainL; 98 efxoutr[i] = input.r[i] * pangainR; 99 } 100 else //Mono 101 for(int i = 0; i < buffersize; ++i) 102 efxoutl[i] = (input.l[i] * pangainL + input.r[i] * pangainR); 103 104 // process external timecode for syncing to host beat 105 // but only if we have timing info, speedfactor is set and we are in host mode. 106 if (time->tempo && speedfactor && (PsyncMode == Reverter::SyncMode::HOST)) { 107 // in host mode we want to find out if (condition) and when (position) a beat happens inside the buffer 108 // and call sync at that position 109 // condition: at the end of the buffer: ticks % ticks_per_beat < ticks_per buffer 110 // position: ticks % ticks_per_beat 111 // 112 // to complicate things: 113 // when running as plugin, the host may have a larger buffer than buffersize. 114 // in this case the processing function is called multiple times for one host buffer 115 // and we have to interpolate the ticks for each internal buffer 116 // 117 // 1:01:00 (start of "song") 1:02:00 internal buffer end 118 // | | 119 // |-------------------------------|-------------------------------| 120 // 1 beat 121 // 122 // 123 // |-----------------------------------------------------------| 124 // "tick" 125 // |--"ticks_since_last_beat"--| 126 // 127 // |---"internal_buffer_ticks"------------------| if beat change inside internal buffer 128 // |-"internal_buffer_ticks"-| if beat change not inside internal buffer 129 130 131 // number of ticks during the length of one internal buffer 132 const float internal_buffer_ticks = ((float)buffersize / (float)samplerate) * // seconds per host buffer * 133 ((float)time->tempo / HZ2BPM) * // beats per second * 134 (float)time->ppq; // ticks per beat = ticks per buffer 135 136 // check if there is new timing information 137 // that indicates a new host buffer 138 // therefore we reset the current subbuffer index. 139 // and calculate the new tick at time of host buffer start 140 if(time->tick != tick_hist) { 141 currentSubbufferIndex = 0; 142 tick_hist = time->tick; 143 tick_at_host_buffer_start = (((time->bar - 1) * time->beatsPerBar) + (time->beat - 1)) * time->ppq + time->tick; 144 if(time->playing != playing_hist) { 145 reverterL->sync(0); 146 if (Pstereo) reverterR->sync(0); 147 } 148 playing_hist = time->playing; 149 150 } 151 else 152 currentSubbufferIndex++; 153 154 { 155 // tick offset from the host buffer to the current internal buffer 156 const float tick_offset = internal_buffer_ticks * (float)currentSubbufferIndex; 157 // tick at time of internal buffer start 158 const float tick_at_buffer_start = tick_at_host_buffer_start + tick_offset; 159 // now calculate the tick at time of internal buffer end 160 // this is needed to determine if a beat change will happen during this internal buffer 161 162 tick = tick_at_buffer_start + internal_buffer_ticks; 163 } 164 165 166 const float ticks_per_beat = (float)time->ppq / speedfactor; 167 const float ticks_since_last_beat = fmodf(tick, ticks_per_beat); 168 if(ticks_since_last_beat < internal_buffer_ticks) { // Ensure beat is inside the buffer 169 const float syncPos = ( (internal_buffer_ticks - ticks_since_last_beat) / (float)internal_buffer_ticks) * (float)buffersize; 170 reverterL->sync(syncPos); 171 if (Pstereo) reverterR->sync(syncPos); 172 } 173 } 174 175 // do the actual processing 176 reverterL->filterout(efxoutl); 177 if(Pstereo) reverterR->filterout(efxoutr); 178 else memcpy(efxoutr, efxoutl, bufferbytes); 179 } 180 181 void Reverse::update() 182 { 183 using SyncMode = Reverter::SyncMode; 184 // process noteon trigger 185 if( (PsyncMode == SyncMode::NOTEON || PsyncMode == SyncMode::NOTEONOFF) ) { 186 reverterL->sync(0.0f); 187 if(Pstereo) reverterR->sync(0.0f); 188 } 189 } 190 //Parameter control 191 void Reverse::setvolume(unsigned char _Pvolume) 192 { 193 Pvolume = _Pvolume; 194 195 if(insertion == 0) { 196 if (Pvolume == 0) { 197 outvolume = 0.0f; 198 } else { 199 outvolume = powf(0.01f, (1.0f - Pvolume / 127.0f)) * 4.0f; 200 } 201 volume = 1.0f; 202 } 203 else 204 volume = outvolume = Pvolume / 127.0f; 205 if(Pvolume == 0) 206 cleanup(); 207 } 208 209 void Reverse::setdelay(unsigned char value) 210 { 211 Pdelay = limit(value,static_cast<unsigned char>(0),static_cast<unsigned char>(127)); 212 reverterL->setdelay(float(Pdelay+1)/128.0f*MAX_REV_DELAY_SECONDS); 213 reverterR->setdelay(float(Pdelay+1)/128.0f*MAX_REV_DELAY_SECONDS); 214 } 215 216 void Reverse::setphase(unsigned char value) 217 { 218 Pphase = value; 219 reverterL->setphase(float(Pphase)/127.0f); 220 reverterR->setphase(float(Pphase)/127.0f); 221 } 222 223 void Reverse::setcrossfade(unsigned char value) 224 { 225 Pcrossfade = value; 226 reverterL->setcrossfade(float(value+1)/100.0f); 227 reverterR->setcrossfade(float(value+1)/100.0f); 228 } 229 230 void Reverse::setsyncMode(unsigned char value) 231 { 232 PsyncMode = value; 233 using SyncMode = Reverter::SyncMode; 234 reverterL->setsyncMode((SyncMode)value); 235 reverterR->setsyncMode((SyncMode)value); 236 } 237 238 unsigned char Reverse::getpresetpar(unsigned char npreset, unsigned int npar) 239 { 240 #define PRESET_SIZE 7 241 #define NUM_PRESETS 3 242 static const unsigned char presets[NUM_PRESETS][PRESET_SIZE] = { 243 //NOTEON 244 {64, 64, 25, 0, 64, 32, Reverter::NOTEON}, 245 //NOTEONOFF 246 {64, 64, 25, 0, 64, 16, Reverter::NOTEONOFF}, 247 //AUTO 248 {64, 64, 25, 0, 64, 50, Reverter::AUTO} 249 }; 250 if(npreset < NUM_PRESETS && npar < PRESET_SIZE) { 251 if (npar == 0 && insertion == 0) { 252 /* lower the volume if this is system effect */ 253 return presets[npreset][npar] / 2; 254 } 255 return presets[npreset][npar]; 256 } 257 return 0; 258 } 259 260 void Reverse::setpreset(unsigned char npreset) 261 { 262 if(npreset >= NUM_PRESETS) 263 npreset = NUM_PRESETS - 1; 264 for(int n = 0; n != 128; n++) 265 changepar(n, getpresetpar(npreset, n)); 266 Ppreset = npreset; 267 } 268 269 void Reverse::changepar(int npar, unsigned char value) 270 { 271 switch(npar) { 272 case 0: 273 setvolume(value); 274 break; 275 case 1: 276 setpanning(value); 277 break; 278 case 2: 279 setdelay(value); 280 break; 281 case 3: 282 Pstereo = (value > 1) ? 1 : value; 283 break; 284 case 4: 285 setphase(value); 286 break; 287 case 5: 288 setcrossfade(value); 289 break; 290 case 6: 291 setsyncMode(value); 292 break; 293 } 294 } 295 296 unsigned char Reverse::getpar(int npar) const 297 { 298 switch(npar) { 299 case 0: return Pvolume; 300 case 1: return Ppanning; 301 case 2: return Pdelay; 302 case 3: return Pstereo; 303 case 4: return Pphase; 304 case 5: return Pcrossfade; 305 case 6: return PsyncMode; 306 default: return 0; // in case of bogus parameter number 307 } 308 } 309 310 }