1 /*************************************************************************** 2 * * 3 * LinuxSampler - modular, streaming capable sampler * 4 * * 5 * Copyright (C) 2011 - 2012 Grigor Iliev * 6 * * 7 * This program is free software; you can redistribute it and/or modify * 8 * it under the terms of the GNU General Public License as published by * 9 * the Free Software Foundation; either version 2 of the License, or * 10 * (at your option) any later version. * 11 * * 12 * This program is distributed in the hope that it will be useful, * 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 15 * GNU General Public License for more details. * 16 * * 17 * You should have received a copy of the GNU General Public License * 18 * along with this program; if not, write to the Free Software * 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * 20 * MA 02111-1307 USA * 21 ***************************************************************************/ 22 23 #ifndef __LS_SIGNALUNIT_H__ 24 #define __LS_SIGNALUNIT_H__ 25 26 #include "../../common/ArrayList.h" 27 #include "../../common/Pool.h" 28 29 30 namespace LinuxSampler { 31 32 template<typename T> 33 class FixedArray { 34 public: FixedArray(int capacity)35 FixedArray(int capacity) { 36 iSize = 0; 37 iCapacity = capacity; 38 pData = new T[iCapacity]; 39 } 40 ~FixedArray()41 ~FixedArray() { 42 delete[] pData; 43 pData = NULL; 44 } 45 size()46 inline int size() const { return iSize; } capacity()47 inline int capacity() { return iCapacity; } 48 add(T element)49 void add(T element) { 50 if (iSize >= iCapacity) throw Exception("Array out of bounds"); 51 pData[iSize++] = element; 52 } 53 54 increment()55 T& increment() { 56 if (iSize >= iCapacity) throw Exception("Array out of bounds"); 57 return pData[iSize++]; 58 } 59 clear()60 void clear() { iSize = 0; } 61 copy(const FixedArray<T> & array)62 void copy(const FixedArray<T>& array) { 63 if(array.size() >= capacity()) throw Exception("Not enough space to copy array"); 64 for (int i = 0; i < array.size(); i++) pData[i] = array[i]; 65 iSize = array.size(); 66 } 67 68 inline T& operator[](int idx) const { 69 return pData[idx]; 70 } 71 72 private: 73 T* pData; 74 int iSize; 75 int iCapacity; 76 }; 77 78 class SignalUnitRack; 79 80 /** 81 * A signal unit consist of internal signal generator (like envelope generator, 82 * low frequency oscillator, etc) with a number of generator parameters which 83 * influence/modulate/dynamically change the generator's signal in some manner. 84 * Each generator parameter (also called signal unit parameter) can receive 85 * signal from another signal unit and use this signal to dynamically change the 86 * behavior of the signal generator. In turn, the signal of this unit can be fed 87 * to another unit(s) and influence its parameters. 88 */ 89 class SignalUnit { 90 public: 91 92 /** 93 * This class represents a parameter which will influence the signal 94 * unit to which it belongs in certain way. 95 * For example, let's say the signal unit is a low frequency oscillator 96 * with frequency 1Hz. If we want to modulate the LFO to start with 1Hz 97 * and increment its frequency to 5Hz in 1 second, we can add 98 * a parameter which signal source is an envelope 99 * generator with attack time of 1 second and coefficient 4. Thus, the 100 * normalized level of the EG will move from 0 to 1 in one second. 101 * On every time step (sample point) the normalized level 102 * will be multiplied by 4 (the parameter coefficient) and added to the 103 * LFO's frequency. 104 * So, after 1 second, the LFO frequency will be 1x4 + 1 = 5Hz. 105 * We can also add another parameter for modulating the LFO's pitch depth 106 * and so on. 107 */ 108 class Parameter { 109 public: 110 SignalUnit* pUnit; /* The source unit whose output signal 111 * will modulate the parameter. 112 */ 113 114 float Coeff; // The modulation coefficient 115 116 Parameter()117 Parameter() : pUnit(NULL), Coeff(1) { } 118 119 /** 120 * @param unit The source unit used to influence this parameter. 121 * @param coeff The coefficient by which the normalized signal 122 * received from the source unit should be multiplied when a 123 * default transformation is done. 124 */ 125 Parameter(SignalUnit* unit, float coeff = 1) { 126 pUnit = unit; 127 Coeff = coeff; 128 } 129 Parameter(const Parameter & Prm)130 Parameter(const Parameter& Prm) { Copy(Prm); } 131 void operator=(const Parameter& Prm) { Copy(Prm); } 132 Copy(const Parameter & Prm)133 void Copy(const Parameter& Prm) { 134 if (this == &Prm) return; 135 136 pUnit = Prm.pUnit; 137 Coeff = Prm.Coeff; 138 } 139 140 141 /** 142 * Calculates the transformation for this parameter 143 * which should be applied to the parameter's value 144 * and multiplies by Coeff. 145 * This implementation of the method just multiplies by Coeff. 146 */ Transform(float val)147 virtual float Transform(float val) { 148 return val * Coeff; 149 } 150 151 /** 152 * Gets the current value of the parameter. 153 * This implementation returns the current signal level of the 154 * source unit with applied transformation if the source unit is 155 * active, otherwise returns 1. 156 * Note that this method assume that pUnit is not NULL. 157 */ GetValue()158 virtual float GetValue() { 159 return pUnit->Active() ? Transform(pUnit->GetLevel()) : 1.0f; 160 } 161 }; 162 163 164 public: 165 ArrayList<SignalUnit::Parameter> Params; // The list of parameters which are modulating the signal unit 166 SignalUnit(SignalUnitRack * rack)167 SignalUnit(SignalUnitRack* rack): pRack(rack), bActive(false), Level(0.0f), bRecalculate(true), bCalculating(false), uiDelayTrigger(0) { } SignalUnit(const SignalUnit & Unit)168 SignalUnit(const SignalUnit& Unit): pRack(Unit.pRack) { Copy(Unit); } 169 void operator=(const SignalUnit& Unit) { Copy(Unit); } ~SignalUnit()170 virtual ~SignalUnit() { } 171 Copy(const SignalUnit & Unit)172 void Copy(const SignalUnit& Unit) { 173 if (this == &Unit) return; 174 175 bActive = Unit.bActive; 176 Level = Unit.Level; 177 Params = Unit.Params; 178 uiDelayTrigger = Unit.uiDelayTrigger; 179 bCalculating = false; 180 } 181 182 /* 183 * Determines whether the unit is active. 184 * If the unit is not active, its level should be ignored. 185 * For endpoint unit this method determines whether 186 * the rendering should be stopped. 187 */ Active()188 virtual bool Active() { return bActive; } 189 190 /** 191 * Override this method to process the current control change events. 192 * @param itEvent - iterator pointing to the event to be processed. 193 */ ProcessCCEvent(uint8_t Controller,uint8_t Value)194 virtual void ProcessCCEvent(uint8_t Controller, uint8_t Value) { } 195 EnterReleaseStage()196 virtual void EnterReleaseStage() { } 197 CancelRelease()198 virtual void CancelRelease() { } 199 200 /** 201 * Gets the normalized level of the unit for the current 202 * time step (sample point). The level is calculated if it's not 203 * calculated for the current step yet. Because the level depends on 204 * the parameters, their levels are calculated too. 205 */ GetLevel()206 virtual float GetLevel() { 207 if (Params.empty() || !bRecalculate) return Level; 208 209 if (bCalculating) { 210 std::cerr << "SignalUnit: Loop detected. Aborted!"; 211 return Level; 212 } 213 214 bCalculating = true; 215 216 for(int i = 0; i < Params.size(); i++) { 217 Params[i].GetValue(); 218 } 219 220 bRecalculate = bCalculating = false; 221 return Level; 222 } 223 224 /** 225 * Will be called to increment the time with one sample point. 226 * The unit should recalculate or prepare for recalculation 227 * its current level on every call of this function. 228 * Note that it is not known whether all source signal unit's levels 229 * are recalculated before the call of this method. So, the calculations 230 * that depends on the unit's parameters should be postponed to 231 * the call of GetLevel(). 232 */ Increment()233 virtual void Increment() { bRecalculate = true; } 234 235 /** 236 * Initializes and triggers the unit. 237 * Note that when a voice is the owner of a unit rack, all settings 238 * should be reset when this method is called, because the sampler 239 * is reusing the voice objects. 240 */ 241 virtual void Trigger() = 0; 242 243 /** 244 * When the signal unit rack is triggered, it triggers all signal 245 * units it holds. If for some reason the triggering of a unit 246 * should be delayed, this method can be set to return non-zero value 247 * specifying the delay in time steps. 248 * Note that this is only a helper method and the implementation 249 * should be done manually. 250 */ DelayTrigger()251 virtual uint DelayTrigger() { return uiDelayTrigger; } 252 253 /** 254 * A helper method which checks whether the delay 255 * stage is finished. 256 */ 257 bool DelayStage(); 258 259 protected: 260 SignalUnitRack* const pRack; 261 262 bool bActive; /* Don't use it to check the active state of the unit!!! 263 * Use Active() instead! */ 264 float Level; 265 bool bRecalculate; /* Determines whether the unit's level should be recalculated. */ 266 bool bCalculating; /* Determines whether the unit is in process of calculating 267 * its level. Used for preventing infinite loops. 268 */ 269 270 uint uiDelayTrigger; /* in time steps */ 271 272 }; 273 274 class EndpointSignalUnit: public SignalUnit { 275 public: EndpointSignalUnit(SignalUnitRack * rack)276 EndpointSignalUnit(SignalUnitRack* rack): SignalUnit(rack) { } 277 278 /** 279 * Gets the volume modulation value 280 * for the current time step (sample point). 281 */ 282 virtual float GetVolume() = 0; 283 284 /** 285 * Gets the filter cutoff frequency modulation value 286 * for the current time step (sample point). 287 */ 288 virtual float GetFilterCutoff() = 0; 289 290 /** 291 * Gets the pitch modulation value 292 * for the current time step (sample point). 293 */ 294 virtual float GetPitch() = 0; 295 296 /** 297 * Gets the resonance modulation value 298 * for the current time step (sample point). 299 */ 300 virtual float GetResonance() = 0; 301 302 /** Should return value in the range [-100, 100] (L <-> R) */ 303 virtual float GetPan() = 0; 304 CalculateFilterCutoff(float cutoff)305 virtual float CalculateFilterCutoff(float cutoff) { 306 cutoff *= GetFilterCutoff(); 307 return cutoff > 13500 ? 13500 : cutoff; 308 } 309 CalculatePitch(float pitch)310 virtual float CalculatePitch(float pitch) { 311 return GetPitch() * pitch; 312 } 313 CalculateResonance(float res)314 virtual float CalculateResonance(float res) { 315 return GetResonance() * res; 316 } 317 318 /** Should return value in the range [0, 127] (L <-> R) */ CalculatePan(int pan)319 virtual uint8_t CalculatePan(int pan) { 320 int p = pan + GetPan() * 0.63; 321 if (p < 0) return 0; 322 if (p > 127) return 127; 323 return p; 324 } 325 326 /** 327 * Decreases the delay by Sample time steps. 328 * This method is used to delay the sample playback. 329 * While the endpoint unit is in delay stage the rack is not incremented. 330 */ DecreaseDelay(uint Samples)331 void DecreaseDelay(uint Samples) { 332 uiDelayTrigger -= Samples; 333 } 334 }; 335 336 /** 337 * Used to smooth out the parameter changes. 338 */ 339 class Smoother { 340 protected: 341 uint timeSteps; // The number of time steps to reach the goal 342 uint currentTimeStep; 343 float goal; 344 float prev; 345 346 public: 347 /** 348 * 349 * @param time The time (in seconds) to reach the goal 350 * @param sampleRate 351 * @param val The initial value 352 */ 353 void trigger(float time, float sampleRate, float val = 0) { 354 currentTimeStep = timeSteps = time * sampleRate; 355 prev = goal = val; 356 } 357 358 /** 359 * Set the current value, which the smoother will not smooth out. 360 * If you want the value to be smoothen out, use update() instead. 361 */ setValue(float val)362 void setValue( float val) { 363 currentTimeStep = timeSteps; 364 prev = goal = val; 365 } 366 367 /** 368 * Sets a new value. The render function will return 369 * values gradually approaching this value. 370 */ update(float val)371 void update(float val) { 372 if (val == goal) return; 373 374 prev = prev + (goal - prev) * (currentTimeStep / (float)timeSteps); 375 goal = val; 376 currentTimeStep = 0; 377 } 378 render()379 float render() { 380 if (currentTimeStep >= timeSteps) return goal; 381 return prev + (goal - prev) * (currentTimeStep++ / (float)timeSteps); 382 } 383 isSmoothingOut()384 bool isSmoothingOut() { return currentTimeStep < timeSteps; } 385 getGoal()386 float getGoal() { return goal; } 387 }; 388 389 /** 390 * Continuous controller signal unit. 391 * The level of this unit corresponds to the controllers changes 392 * and their influences. 393 */ 394 class CCSignalUnit: public SignalUnit { 395 public: 396 /** Listener which will be notified when the level of the unit is changed. */ 397 class Listener { 398 public: 399 virtual void ValueChanged(CCSignalUnit* pUnit) = 0; 400 }; 401 402 class CC { 403 public: 404 uint8_t Controller; ///< MIDI controller number. 405 uint8_t Value; ///< Controller Value. 406 short int Curve; ///< specifies the curve type 407 float Influence; 408 float Step; 409 410 Smoother* pSmoother; 411 412 CC ( 413 uint8_t Controller = 0, 414 float Influence = 0.0f, 415 short int Curve = -1, 416 Smoother* pSmoother = NULL, 417 float Step = 0 418 ) { 419 this->Controller = Controller; 420 this->Value = 0; 421 this->Curve = Curve; 422 this->Influence = Influence; 423 this->pSmoother = pSmoother; 424 this->Step = Step; 425 } 426 CC(const CC & cc)427 CC(const CC& cc) { Copy(cc); } 428 void operator=(const CC& cc) { Copy(cc); } 429 Copy(const CC & cc)430 void Copy(const CC& cc) { 431 Controller = cc.Controller; 432 Value = cc.Value; 433 Influence = cc.Influence; 434 Curve = cc.Curve; 435 pSmoother = cc.pSmoother; 436 Step = cc.Step; 437 } 438 }; 439 440 protected: 441 RTList<CC>* pCtrls; // The MIDI controllers which modulates this signal unit. 442 Listener* pListener; 443 bool hasSmoothCtrls; // determines whether there are smooth controllers (used for optimization) 444 bool isSmoothingOut; // determines whether there is a CC which is in process of smoothing out (used for optimization) 445 446 public: 447 SignalUnit(rack)448 CCSignalUnit(SignalUnitRack* rack, Listener* l = NULL): SignalUnit(rack), pCtrls(NULL) { 449 pListener = l; 450 hasSmoothCtrls = isSmoothingOut = false; 451 } 452 CCSignalUnit(const CCSignalUnit & Unit)453 CCSignalUnit(const CCSignalUnit& Unit): SignalUnit(Unit.pRack), pCtrls(NULL) { Copy(Unit); } 454 void operator=(const CCSignalUnit& Unit) { Copy(Unit); } 455 ~CCSignalUnit()456 virtual ~CCSignalUnit() { 457 if (pCtrls != NULL) delete pCtrls; 458 } 459 Copy(const CCSignalUnit & Unit)460 void Copy(const CCSignalUnit& Unit) { 461 if (pCtrls != NULL) delete pCtrls; 462 pCtrls = new RTList<CC>(*(Unit.pCtrls)); 463 if (pCtrls->poolIsEmpty() && pCtrls->count() < Unit.pCtrls->count()) { 464 std::cerr << "Maximum number of CC reached!" << std::endl; 465 } 466 467 pListener = Unit.pListener; 468 hasSmoothCtrls = Unit.hasSmoothCtrls; 469 isSmoothingOut = Unit.isSmoothingOut; 470 SignalUnit::Copy(Unit); 471 } 472 InitCCList(Pool<CC> * pCCPool,Pool<Smoother> * pSmootherPool)473 virtual void InitCCList(Pool<CC>* pCCPool, Pool<Smoother>* pSmootherPool) { 474 if (pCtrls != NULL) delete pCtrls; 475 pCtrls = new RTList<CC>(pCCPool); 476 } 477 478 void AddCC(uint8_t Controller, float Influence, short int Curve = -1, Smoother* pSmoother = NULL, float Step = 0) { 479 if(pCtrls->poolIsEmpty()) { 480 std::cerr << "Maximum number of CC reached!" << std::endl; 481 return; 482 } 483 *(pCtrls->allocAppend()) = CC(Controller, Influence, Curve, pSmoother, Step); 484 if (pSmoother != NULL) hasSmoothCtrls = true; 485 } 486 RemoveAllCCs()487 virtual void RemoveAllCCs() { pCtrls->clear(); } 488 GetCCCount()489 int GetCCCount() { return pCtrls->count(); } 490 HasCCs()491 bool HasCCs() { return GetCCCount() > 0; } 492 Increment()493 virtual void Increment() { 494 if (hasSmoothCtrls && isSmoothingOut) Calculate(); 495 } 496 Trigger()497 virtual void Trigger() { 498 Calculate(); 499 bActive = Level != 0; 500 } 501 ProcessCCEvent(uint8_t Controller,uint8_t Value)502 virtual void ProcessCCEvent(uint8_t Controller, uint8_t Value) { 503 bool recalculate = false; 504 505 RTList<CC>::Iterator ctrl = pCtrls->first(); 506 RTList<CC>::Iterator end = pCtrls->end(); 507 for(; ctrl != end; ++ctrl) { 508 if (Controller != (*ctrl).Controller) continue; 509 if ((*ctrl).Value == Value) continue; 510 511 (*ctrl).Value = Value; 512 513 if ((*ctrl).Step > 0 && (*ctrl).pSmoother != NULL) { 514 float oldGoal = (*ctrl).pSmoother->getGoal(); 515 float newGoal = Normalize(Value, (*ctrl).Curve) * (*ctrl).Influence; 516 newGoal = ((int) (newGoal / (*ctrl).Step)) * (*ctrl).Step; 517 if (oldGoal != newGoal) (*ctrl).pSmoother->update(newGoal); 518 } 519 520 if ((*ctrl).pSmoother != NULL && (*ctrl).Step <= 0) (*ctrl).pSmoother->update(Value); 521 if (!bActive) bActive = true; 522 recalculate = true; 523 } 524 525 if (!(hasSmoothCtrls && isSmoothingOut) && recalculate) Calculate(); 526 } 527 Calculate()528 virtual void Calculate() { 529 float l = 0; 530 isSmoothingOut = false; 531 RTList<CC>::Iterator ctrl = pCtrls->first(); 532 RTList<CC>::Iterator end = pCtrls->end(); 533 for(; ctrl != end; ++ctrl) { 534 if ((*ctrl).pSmoother == NULL) { 535 float val = Normalize((*ctrl).Value, (*ctrl).Curve) * (*ctrl).Influence; 536 if ((*ctrl).Step > 0) val = ( (int)(val / (*ctrl).Step) ) * (*ctrl).Step; 537 l += val; 538 } else { 539 if ((*ctrl).pSmoother->isSmoothingOut()) isSmoothingOut = true; 540 541 if ((*ctrl).Step > 0) { 542 l += (*ctrl).pSmoother->render(); 543 } else { 544 l += Normalize((*ctrl).pSmoother->render(), (*ctrl).Curve) * (*ctrl).Influence; 545 } 546 } 547 } 548 if (Level != l) { 549 Level = l; 550 if (pListener != NULL) pListener->ValueChanged(this); 551 } 552 } 553 554 virtual float Normalize(uint8_t val, short int curve = -1) { 555 return val / 127.0f; 556 } 557 }; 558 559 } // namespace LinuxSampler 560 561 #endif /* __LS_SIGNALUNIT_H__ */ 562