1 /***************************************************************************
2  *                                                                         *
3  *   Copyright (C) 2008 - 2020 Christian Schoenebeck                       *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the Free Software           *
17  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston,                 *
18  *   MA  02111-1307  USA                                                   *
19  ***************************************************************************/
20 
21 #include "EffectChain.h"
22 
23 #include "../common/global_private.h"
24 
25 namespace LinuxSampler {
26 
EffectChain(AudioOutputDevice * pDevice,int iEffectChainId)27 EffectChain::EffectChain(AudioOutputDevice* pDevice, int iEffectChainId) {
28     this->pDevice = pDevice;
29     iID = iEffectChainId;
30 }
31 
AppendEffect(Effect * pEffect)32 void EffectChain::AppendEffect(Effect* pEffect) {
33     pEffect->InitEffect(pDevice);
34     _ChainEntry entry = { pEffect, true };
35     vEntries.push_back(entry);
36     pEffect->SetParent(this);
37 }
38 
InsertEffect(Effect * pEffect,int iChainPos)39 void EffectChain::InsertEffect(Effect* pEffect, int iChainPos) throw (Exception) {
40     if (iChainPos == vEntries.size()) {
41         AppendEffect(pEffect);
42         return;
43     }
44     if (iChainPos < 0 || iChainPos >= vEntries.size())
45         throw Exception(
46             "Cannot insert effect at chain position " +
47             ToString(iChainPos) + ", index out of bounds."
48         );
49     pEffect->InitEffect(pDevice); // might throw Exception !
50     std::vector<_ChainEntry>::iterator iter = vEntries.begin();
51     for (int i = 0; i < iChainPos; ++i) ++iter;
52     _ChainEntry entry = { pEffect, true };
53     vEntries.insert(iter, entry);
54     pEffect->SetParent(this);
55 }
56 
RemoveEffect(int iChainPos)57 void EffectChain::RemoveEffect(int iChainPos) throw (Exception) {
58     if (iChainPos < 0 || iChainPos >= vEntries.size())
59         throw Exception(
60             "Cannot remove effect at chain position " +
61             ToString(iChainPos) + ", index out of bounds."
62         );
63     std::vector<_ChainEntry>::iterator iter = vEntries.begin();
64     for (int i = 0; i < iChainPos; ++i) ++iter;
65     Effect* pEffect = (*iter).pEffect;
66     vEntries.erase(iter);
67     pEffect->SetParent(NULL); // mark effect as not in use anymore
68 }
69 
RenderAudio(uint Samples)70 void EffectChain::RenderAudio(uint Samples) {
71     for (int i = 0; i < vEntries.size(); ++i) {
72         Effect* pCurrentEffect = vEntries[i].pEffect;
73         if (i) { // import signal from previous effect
74             Effect* pPrevEffect = vEntries[i - 1].pEffect;
75             for (int iChan = 0; iChan < pPrevEffect->OutputChannelCount() && iChan < pCurrentEffect->InputChannelCount(); ++iChan) {
76                 pPrevEffect->OutputChannel(iChan)->MixTo(
77                     pCurrentEffect->InputChannel(iChan),
78                     Samples
79                 );
80             }
81         }
82         if (IsEffectActive(i)) pCurrentEffect->RenderAudio(Samples);
83         else { //TODO: lazy, suboptimal implementation of inactive, bypassed effects
84             for (int iChan = 0; iChan < pCurrentEffect->OutputChannelCount() && iChan < pCurrentEffect->InputChannelCount(); ++iChan) {
85                 pCurrentEffect->InputChannel(iChan)->MixTo(
86                     pCurrentEffect->OutputChannel(iChan),
87                     Samples
88                 );
89             }
90         }
91     }
92 }
93 
GetEffect(int iChainPos) const94 Effect* EffectChain::GetEffect(int iChainPos) const {
95     if (iChainPos < 0 || iChainPos >= vEntries.size()) return NULL;
96     return vEntries[iChainPos].pEffect;
97 }
98 
EffectCount() const99 int EffectChain::EffectCount() const {
100     return (int) vEntries.size();
101 }
102 
Reconnect(AudioOutputDevice * pDevice)103 void EffectChain::Reconnect(AudioOutputDevice* pDevice) {
104     for (int i = 0; i < vEntries.size(); ++i) {
105         Effect* pEffect = vEntries[i].pEffect;
106         pEffect->InitEffect(pDevice);
107     }
108 }
109 
SetEffectActive(int iChainPos,bool bOn)110 void EffectChain::SetEffectActive(int iChainPos, bool bOn) throw (Exception) {
111     if (iChainPos < 0 || iChainPos >= vEntries.size())
112         throw Exception(
113             "Cannot change active state of effect at chain position " +
114             ToString(iChainPos) + ", index out of bounds."
115         );
116     vEntries[iChainPos].bActive = bOn;
117 }
118 
IsEffectActive(int iChainPos) const119 bool EffectChain::IsEffectActive(int iChainPos) const {
120     if (iChainPos < 0 || iChainPos >= vEntries.size()) return false;
121     return vEntries[iChainPos].bActive;
122 }
123 
ClearAllChannels()124 void EffectChain::ClearAllChannels() {
125     for (int iEffect = 0; iEffect < vEntries.size(); ++iEffect) {
126         Effect* pEffect = vEntries[iEffect].pEffect;
127         for (int i = 0; i < pEffect->InputChannelCount(); ++i)
128             pEffect->InputChannel(i)->Clear(); // zero out buffers
129         for (int i = 0; i < pEffect->OutputChannelCount(); ++i)
130             pEffect->OutputChannel(i)->Clear(); // zero out buffers
131     }
132 }
133 
ID() const134 int EffectChain::ID() const {
135     return iID;
136 }
137 
138 } // namespace LinuxSampler
139