1 /*
2 * EffectChain.cpp - class for processing and effects chain
3 *
4 * Copyright (c) 2006-2008 Danny McRae <khjklujn/at/users.sourceforge.net>
5 * Copyright (c) 2008-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
6 *
7 * This file is part of LMMS - https://lmms.io
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public
20 * License along with this program (see COPYING); if not, write to the
21 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301 USA.
23 *
24 */
25
26
27 #include <QDomElement>
28
29 #include "EffectChain.h"
30 #include "Effect.h"
31 #include "DummyEffect.h"
32 #include "MixHelpers.h"
33 #include "Song.h"
34
35
EffectChain(Model * _parent)36 EffectChain::EffectChain( Model * _parent ) :
37 Model( _parent ),
38 SerializingObject(),
39 m_enabledModel( false, NULL, tr( "Effects enabled" ) )
40 {
41 }
42
43
44
45
~EffectChain()46 EffectChain::~EffectChain()
47 {
48 clear();
49 }
50
51
52
53
saveSettings(QDomDocument & _doc,QDomElement & _this)54 void EffectChain::saveSettings( QDomDocument & _doc, QDomElement & _this )
55 {
56 m_enabledModel.saveSettings( _doc, _this, "enabled" );
57 _this.setAttribute( "numofeffects", m_effects.count() );
58
59 for( Effect* effect : m_effects)
60 {
61 if( DummyEffect* dummy = dynamic_cast<DummyEffect*>(effect) )
62 {
63 _this.appendChild( dummy->originalPluginData() );
64 }
65 else
66 {
67 QDomElement ef = effect->saveState( _doc, _this );
68 ef.setAttribute( "name", QString::fromUtf8( effect->descriptor()->name ) );
69 ef.appendChild( effect->key().saveXML( _doc ) );
70 }
71 }
72 }
73
74
75
76
loadSettings(const QDomElement & _this)77 void EffectChain::loadSettings( const QDomElement & _this )
78 {
79 clear();
80
81 // TODO This method should probably also lock the mixer
82
83 m_enabledModel.loadSettings( _this, "enabled" );
84
85 const int plugin_cnt = _this.attribute( "numofeffects" ).toInt();
86
87 QDomNode node = _this.firstChild();
88 int fx_loaded = 0;
89 while( !node.isNull() && fx_loaded < plugin_cnt )
90 {
91 if( node.isElement() && node.nodeName() == "effect" )
92 {
93 QDomElement effectData = node.toElement();
94
95 const QString name = effectData.attribute( "name" );
96 EffectKey key( effectData.elementsByTagName( "key" ).item( 0 ).toElement() );
97
98 Effect* e = Effect::instantiate( name.toUtf8(), this, &key );
99
100 if( e != NULL && e->isOkay() && e->nodeName() == node.nodeName() )
101 {
102 e->restoreState( effectData );
103 }
104 else
105 {
106 delete e;
107 e = new DummyEffect( parentModel(), effectData );
108 }
109
110 m_effects.push_back( e );
111 ++fx_loaded;
112 }
113 node = node.nextSibling();
114 }
115
116 emit dataChanged();
117 }
118
119
120
121
appendEffect(Effect * _effect)122 void EffectChain::appendEffect( Effect * _effect )
123 {
124 Engine::mixer()->requestChangeInModel();
125 m_effects.append( _effect );
126 Engine::mixer()->doneChangeInModel();
127
128 emit dataChanged();
129 }
130
131
132
133
removeEffect(Effect * _effect)134 void EffectChain::removeEffect( Effect * _effect )
135 {
136 Engine::mixer()->requestChangeInModel();
137
138 Effect ** found = qFind( m_effects.begin(), m_effects.end(), _effect );
139 if( found == m_effects.end() )
140 {
141 Engine::mixer()->doneChangeInModel();
142 return;
143 }
144 m_effects.erase( found );
145
146 Engine::mixer()->doneChangeInModel();
147 emit dataChanged();
148 }
149
150
151
152
moveDown(Effect * _effect)153 void EffectChain::moveDown( Effect * _effect )
154 {
155 if( _effect != m_effects.last() )
156 {
157 int i = 0;
158 for( EffectList::Iterator it = m_effects.begin();
159 it != m_effects.end(); it++, i++ )
160 {
161 if( *it == _effect )
162 {
163 break;
164 }
165 }
166
167 Effect * temp = m_effects[i + 1];
168 m_effects[i + 1] = _effect;
169 m_effects[i] = temp;
170 }
171 }
172
173
174
175
moveUp(Effect * _effect)176 void EffectChain::moveUp( Effect * _effect )
177 {
178 if( _effect != m_effects.first() )
179 {
180 int i = 0;
181 for( EffectList::Iterator it = m_effects.begin();
182 it != m_effects.end(); it++, i++ )
183 {
184 if( *it == _effect )
185 {
186 break;
187 }
188 }
189
190 Effect * temp = m_effects[i - 1];
191 m_effects[i - 1] = _effect;
192 m_effects[i] = temp;
193 }
194 }
195
196
197
198
processAudioBuffer(sampleFrame * _buf,const fpp_t _frames,bool hasInputNoise)199 bool EffectChain::processAudioBuffer( sampleFrame * _buf, const fpp_t _frames, bool hasInputNoise )
200 {
201 if( m_enabledModel.value() == false )
202 {
203 return false;
204 }
205
206 MixHelpers::sanitize( _buf, _frames );
207
208 bool moreEffects = false;
209 for( EffectList::Iterator it = m_effects.begin(); it != m_effects.end(); ++it )
210 {
211 if( hasInputNoise || ( *it )->isRunning() )
212 {
213 moreEffects |= ( *it )->processAudioBuffer( _buf, _frames );
214 MixHelpers::sanitize( _buf, _frames );
215 }
216 }
217
218 return moreEffects;
219 }
220
221
222
223
startRunning()224 void EffectChain::startRunning()
225 {
226 if( m_enabledModel.value() == false )
227 {
228 return;
229 }
230
231 for( EffectList::Iterator it = m_effects.begin();
232 it != m_effects.end(); it++ )
233 {
234 ( *it )->startRunning();
235 }
236 }
237
238
239
240
clear()241 void EffectChain::clear()
242 {
243 emit aboutToClear();
244
245 Engine::mixer()->requestChangeInModel();
246
247 m_enabledModel.setValue( false );
248 while( m_effects.count() )
249 {
250 Effect * e = m_effects[m_effects.count() - 1];
251 m_effects.pop_back();
252 delete e;
253 }
254
255 Engine::mixer()->doneChangeInModel();
256 }
257