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