1 /*
2 * FxMixer.cpp - effect mixer for LMMS
3 *
4 * Copyright (c) 2008-2011 Tobias Doerffel <tobydox/at/users.sourceforge.net>
5 *
6 * This file is part of LMMS - https://lmms.io
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this program (see COPYING); if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301 USA.
22 *
23 */
24
25 #include <QDomElement>
26
27 #include "BufferManager.h"
28 #include "FxMixer.h"
29 #include "Mixer.h"
30 #include "MixerWorkerThread.h"
31 #include "MixHelpers.h"
32 #include "Song.h"
33
34 #include "InstrumentTrack.h"
35 #include "BBTrackContainer.h"
36
FxRoute(FxChannel * from,FxChannel * to,float amount)37 FxRoute::FxRoute( FxChannel * from, FxChannel * to, float amount ) :
38 m_from( from ),
39 m_to( to ),
40 m_amount( amount, 0, 1, 0.001, NULL,
41 tr( "Amount to send from channel %1 to channel %2" ).arg( m_from->m_channelIndex ).arg( m_to->m_channelIndex ) )
42 {
43 //qDebug( "created: %d to %d", m_from->m_channelIndex, m_to->m_channelIndex );
44 // create send amount model
45 }
46
47
~FxRoute()48 FxRoute::~FxRoute()
49 {
50 }
51
52
updateName()53 void FxRoute::updateName()
54 {
55 m_amount.setDisplayName(
56 tr( "Amount to send from channel %1 to channel %2" ).arg( m_from->m_channelIndex ).arg( m_to->m_channelIndex ) );
57 }
58
59
FxChannel(int idx,Model * _parent)60 FxChannel::FxChannel( int idx, Model * _parent ) :
61 m_fxChain( NULL ),
62 m_hasInput( false ),
63 m_stillRunning( false ),
64 m_peakLeft( 0.0f ),
65 m_peakRight( 0.0f ),
66 m_buffer( new sampleFrame[Engine::mixer()->framesPerPeriod()] ),
67 m_muteModel( false, _parent ),
68 m_soloModel( false, _parent ),
69 m_volumeModel( 1.0, 0.0, 2.0, 0.001, _parent ),
70 m_name(),
71 m_lock(),
72 m_channelIndex( idx ),
73 m_queued( false ),
74 m_dependenciesMet( 0 )
75 {
76 BufferManager::clear( m_buffer, Engine::mixer()->framesPerPeriod() );
77 }
78
79
80
81
~FxChannel()82 FxChannel::~FxChannel()
83 {
84 delete[] m_buffer;
85 }
86
87
processed()88 inline void FxChannel::processed()
89 {
90 for( const FxRoute * receiverRoute : m_sends )
91 {
92 if( receiverRoute->receiver()->m_muted == false )
93 {
94 receiverRoute->receiver()->incrementDeps();
95 }
96 }
97 }
98
incrementDeps()99 void FxChannel::incrementDeps()
100 {
101 int i = m_dependenciesMet.fetchAndAddOrdered( 1 ) + 1;
102 if( i >= m_receives.size() && ! m_queued )
103 {
104 m_queued = true;
105 MixerWorkerThread::addJob( this );
106 }
107 }
108
unmuteForSolo()109 void FxChannel::unmuteForSolo()
110 {
111 //TODO: Recursively activate every channel, this channel sends to
112 m_muteModel.setValue(false);
113 }
114
115
116
doProcessing()117 void FxChannel::doProcessing()
118 {
119 const fpp_t fpp = Engine::mixer()->framesPerPeriod();
120
121 if( m_muted == false )
122 {
123 for( FxRoute * senderRoute : m_receives )
124 {
125 FxChannel * sender = senderRoute->sender();
126 FloatModel * sendModel = senderRoute->amount();
127 if( ! sendModel ) qFatal( "Error: no send model found from %d to %d", senderRoute->senderIndex(), m_channelIndex );
128
129 if( sender->m_hasInput || sender->m_stillRunning )
130 {
131 // figure out if we're getting sample-exact input
132 ValueBuffer * sendBuf = sendModel->valueBuffer();
133 ValueBuffer * volBuf = sender->m_volumeModel.valueBuffer();
134
135 // mix it's output with this one's output
136 sampleFrame * ch_buf = sender->m_buffer;
137
138 // use sample-exact mixing if sample-exact values are available
139 if( ! volBuf && ! sendBuf ) // neither volume nor send has sample-exact data...
140 {
141 const float v = sender->m_volumeModel.value() * sendModel->value();
142 MixHelpers::addSanitizedMultiplied( m_buffer, ch_buf, v, fpp );
143 }
144 else if( volBuf && sendBuf ) // both volume and send have sample-exact data
145 {
146 MixHelpers::addSanitizedMultipliedByBuffers( m_buffer, ch_buf, volBuf, sendBuf, fpp );
147 }
148 else if( volBuf ) // volume has sample-exact data but send does not
149 {
150 const float v = sendModel->value();
151 MixHelpers::addSanitizedMultipliedByBuffer( m_buffer, ch_buf, v, volBuf, fpp );
152 }
153 else // vice versa
154 {
155 const float v = sender->m_volumeModel.value();
156 MixHelpers::addSanitizedMultipliedByBuffer( m_buffer, ch_buf, v, sendBuf, fpp );
157 }
158 m_hasInput = true;
159 }
160 }
161
162
163 const float v = m_volumeModel.value();
164
165 if( m_hasInput )
166 {
167 // only start fxchain when we have input...
168 m_fxChain.startRunning();
169 }
170
171 m_stillRunning = m_fxChain.processAudioBuffer( m_buffer, fpp, m_hasInput );
172
173 float peakLeft = 0.;
174 float peakRight = 0.;
175 Engine::mixer()->getPeakValues( m_buffer, fpp, peakLeft, peakRight );
176 m_peakLeft = qMax( m_peakLeft, peakLeft * v );
177 m_peakRight = qMax( m_peakRight, peakRight * v );
178 }
179 else
180 {
181 m_peakLeft = m_peakRight = 0.0f;
182 }
183
184 // increment dependency counter of all receivers
185 processed();
186 }
187
188
189
FxMixer()190 FxMixer::FxMixer() :
191 Model( NULL ),
192 JournallingObject(),
193 m_fxChannels()
194 {
195 // create master channel
196 createChannel();
197 m_lastSoloed = -1;
198 }
199
200
201
~FxMixer()202 FxMixer::~FxMixer()
203 {
204 while( ! m_fxRoutes.isEmpty() )
205 {
206 deleteChannelSend( m_fxRoutes.first() );
207 }
208 while( m_fxChannels.size() )
209 {
210 FxChannel * f = m_fxChannels[m_fxChannels.size() - 1];
211 m_fxChannels.pop_back();
212 delete f;
213 }
214 }
215
216
217
createChannel()218 int FxMixer::createChannel()
219 {
220 const int index = m_fxChannels.size();
221 // create new channel
222 m_fxChannels.push_back( new FxChannel( index, this ) );
223
224 // reset channel state
225 clearChannel( index );
226
227 return index;
228 }
229
activateSolo()230 void FxMixer::activateSolo()
231 {
232 for (int i = 1; i < m_fxChannels.size(); ++i)
233 {
234 m_fxChannels[i]->m_muteBeforeSolo = m_fxChannels[i]->m_muteModel.value();
235 m_fxChannels[i]->m_muteModel.setValue( true );
236 }
237 }
238
deactivateSolo()239 void FxMixer::deactivateSolo()
240 {
241 for (int i = 1; i < m_fxChannels.size(); ++i)
242 {
243 m_fxChannels[i]->m_muteModel.setValue( m_fxChannels[i]->m_muteBeforeSolo );
244 }
245 }
246
toggledSolo()247 void FxMixer::toggledSolo()
248 {
249 int soloedChan = -1;
250 bool resetSolo = m_lastSoloed != -1;
251 //untoggle if lastsoloed is entered
252 if (resetSolo)
253 {
254 m_fxChannels[m_lastSoloed]->m_soloModel.setValue( false );
255 }
256 //determine the soloed channel
257 for (int i = 0; i < m_fxChannels.size(); ++i)
258 {
259 if (m_fxChannels[i]->m_soloModel.value() == true)
260 soloedChan = i;
261 }
262 // if no channel is soloed, unmute everything, else mute everything
263 if (soloedChan != -1)
264 {
265 if (resetSolo)
266 {
267 deactivateSolo();
268 activateSolo();
269 } else {
270 activateSolo();
271 }
272 // unmute the soloed chan and every channel it sends to
273 m_fxChannels[soloedChan]->unmuteForSolo();
274 } else {
275 deactivateSolo();
276 }
277 m_lastSoloed = soloedChan;
278 }
279
280
281
deleteChannel(int index)282 void FxMixer::deleteChannel( int index )
283 {
284 // channel deletion is performed between mixer rounds
285 Engine::mixer()->requestChangeInModel();
286
287 // go through every instrument and adjust for the channel index change
288 TrackContainer::TrackList tracks;
289 tracks += Engine::getSong()->tracks();
290 tracks += Engine::getBBTrackContainer()->tracks();
291
292 for( Track* t : tracks )
293 {
294 if( t->type() == Track::InstrumentTrack )
295 {
296 InstrumentTrack* inst = dynamic_cast<InstrumentTrack *>( t );
297 int val = inst->effectChannelModel()->value(0);
298 if( val == index )
299 {
300 // we are deleting this track's fx send
301 // send to master
302 inst->effectChannelModel()->setValue(0);
303 }
304 else if( val > index )
305 {
306 // subtract 1 to make up for the missing channel
307 inst->effectChannelModel()->setValue(val-1);
308 }
309 }
310 }
311
312 FxChannel * ch = m_fxChannels[index];
313
314 // delete all of this channel's sends and receives
315 while( ! ch->m_sends.isEmpty() )
316 {
317 deleteChannelSend( ch->m_sends.first() );
318 }
319 while( ! ch->m_receives.isEmpty() )
320 {
321 deleteChannelSend( ch->m_receives.first() );
322 }
323
324 // actually delete the channel
325 m_fxChannels.remove(index);
326 delete ch;
327
328 for( int i = index; i < m_fxChannels.size(); ++i )
329 {
330 validateChannelName( i, i + 1 );
331
332 // set correct channel index
333 m_fxChannels[i]->m_channelIndex = i;
334
335 // now check all routes and update names of the send models
336 for( FxRoute * r : m_fxChannels[i]->m_sends )
337 {
338 r->updateName();
339 }
340 for( FxRoute * r : m_fxChannels[i]->m_receives )
341 {
342 r->updateName();
343 }
344 }
345
346 Engine::mixer()->doneChangeInModel();
347 }
348
349
350
moveChannelLeft(int index)351 void FxMixer::moveChannelLeft( int index )
352 {
353 // can't move master or first channel
354 if( index <= 1 || index >= m_fxChannels.size() )
355 {
356 return;
357 }
358 // channels to swap
359 int a = index - 1, b = index;
360
361 // go through every instrument and adjust for the channel index change
362 QVector<Track *> songTrackList = Engine::getSong()->tracks();
363 QVector<Track *> bbTrackList = Engine::getBBTrackContainer()->tracks();
364
365 QVector<Track *> trackLists[] = {songTrackList, bbTrackList};
366 for(int tl=0; tl<2; ++tl)
367 {
368 QVector<Track *> trackList = trackLists[tl];
369 for(int i=0; i<trackList.size(); ++i)
370 {
371 if( trackList[i]->type() == Track::InstrumentTrack )
372 {
373 InstrumentTrack * inst = (InstrumentTrack *) trackList[i];
374 int val = inst->effectChannelModel()->value(0);
375 if( val == a )
376 {
377 inst->effectChannelModel()->setValue(b);
378 }
379 else if( val == b )
380 {
381 inst->effectChannelModel()->setValue(a);
382 }
383 }
384 }
385 }
386
387 // Swap positions in array
388 qSwap(m_fxChannels[index], m_fxChannels[index - 1]);
389
390 // Update m_channelIndex of both channels
391 m_fxChannels[index]->m_channelIndex = index;
392 m_fxChannels[index - 1]->m_channelIndex = index -1;
393 }
394
395
396
moveChannelRight(int index)397 void FxMixer::moveChannelRight( int index )
398 {
399 moveChannelLeft( index + 1 );
400 }
401
402
403
createChannelSend(fx_ch_t fromChannel,fx_ch_t toChannel,float amount)404 FxRoute * FxMixer::createChannelSend( fx_ch_t fromChannel, fx_ch_t toChannel,
405 float amount )
406 {
407 // qDebug( "requested: %d to %d", fromChannel, toChannel );
408 // find the existing connection
409 FxChannel * from = m_fxChannels[fromChannel];
410 FxChannel * to = m_fxChannels[toChannel];
411
412 for( int i=0; i<from->m_sends.size(); ++i )
413 {
414 if( from->m_sends[i]->receiver() == to )
415 {
416 // simply adjust the amount
417 from->m_sends[i]->amount()->setValue( amount );
418 return from->m_sends[i];
419 }
420 }
421
422 // connection does not exist. create a new one
423 return createRoute( from, to, amount );
424 }
425
426
createRoute(FxChannel * from,FxChannel * to,float amount)427 FxRoute * FxMixer::createRoute( FxChannel * from, FxChannel * to, float amount )
428 {
429 if( from == to )
430 {
431 return NULL;
432 }
433 Engine::mixer()->requestChangeInModel();
434 FxRoute * route = new FxRoute( from, to, amount );
435
436 // add us to from's sends
437 from->m_sends.append( route );
438
439 // add us to to's receives
440 to->m_receives.append( route );
441
442 // add us to fxmixer's list
443 Engine::fxMixer()->m_fxRoutes.append( route );
444 Engine::mixer()->doneChangeInModel();
445
446 return route;
447 }
448
449
450 // delete the connection made by createChannelSend
deleteChannelSend(fx_ch_t fromChannel,fx_ch_t toChannel)451 void FxMixer::deleteChannelSend( fx_ch_t fromChannel, fx_ch_t toChannel )
452 {
453 // delete the send
454 FxChannel * from = m_fxChannels[fromChannel];
455 FxChannel * to = m_fxChannels[toChannel];
456
457 // find and delete the send entry
458 for( int i = 0; i < from->m_sends.size(); ++i )
459 {
460 if( from->m_sends[i]->receiver() == to )
461 {
462 deleteChannelSend( from->m_sends[i] );
463 break;
464 }
465 }
466 }
467
468
deleteChannelSend(FxRoute * route)469 void FxMixer::deleteChannelSend( FxRoute * route )
470 {
471 Engine::mixer()->requestChangeInModel();
472 // remove us from from's sends
473 route->sender()->m_sends.remove( route->sender()->m_sends.indexOf( route ) );
474 // remove us from to's receives
475 route->receiver()->m_receives.remove( route->receiver()->m_receives.indexOf( route ) );
476 // remove us from fxmixer's list
477 Engine::fxMixer()->m_fxRoutes.remove( Engine::fxMixer()->m_fxRoutes.indexOf( route ) );
478 delete route;
479 Engine::mixer()->doneChangeInModel();
480 }
481
482
isInfiniteLoop(fx_ch_t sendFrom,fx_ch_t sendTo)483 bool FxMixer::isInfiniteLoop( fx_ch_t sendFrom, fx_ch_t sendTo )
484 {
485 if( sendFrom == sendTo ) return true;
486 FxChannel * from = m_fxChannels[sendFrom];
487 FxChannel * to = m_fxChannels[sendTo];
488 bool b = checkInfiniteLoop( from, to );
489 return b;
490 }
491
492
checkInfiniteLoop(FxChannel * from,FxChannel * to)493 bool FxMixer::checkInfiniteLoop( FxChannel * from, FxChannel * to )
494 {
495 // can't send master to anything
496 if( from == m_fxChannels[0] )
497 {
498 return true;
499 }
500
501 // can't send channel to itself
502 if( from == to )
503 {
504 return true;
505 }
506
507 // follow sendTo's outputs recursively looking for something that sends
508 // to sendFrom
509 for( int i=0; i < to->m_sends.size(); ++i )
510 {
511 if( checkInfiniteLoop( from, to->m_sends[i]->receiver() ) )
512 {
513 return true;
514 }
515 }
516
517 return false;
518 }
519
520
521 // how much does fromChannel send its output to the input of toChannel?
channelSendModel(fx_ch_t fromChannel,fx_ch_t toChannel)522 FloatModel * FxMixer::channelSendModel( fx_ch_t fromChannel, fx_ch_t toChannel )
523 {
524 if( fromChannel == toChannel )
525 {
526 return NULL;
527 }
528 const FxChannel * from = m_fxChannels[fromChannel];
529 const FxChannel * to = m_fxChannels[toChannel];
530
531 for( FxRoute * route : from->m_sends )
532 {
533 if( route->receiver() == to )
534 {
535 return route->amount();
536 }
537 }
538
539 return NULL;
540 }
541
542
543
mixToChannel(const sampleFrame * _buf,fx_ch_t _ch)544 void FxMixer::mixToChannel( const sampleFrame * _buf, fx_ch_t _ch )
545 {
546 if( m_fxChannels[_ch]->m_muteModel.value() == false )
547 {
548 m_fxChannels[_ch]->m_lock.lock();
549 MixHelpers::add( m_fxChannels[_ch]->m_buffer, _buf, Engine::mixer()->framesPerPeriod() );
550 m_fxChannels[_ch]->m_hasInput = true;
551 m_fxChannels[_ch]->m_lock.unlock();
552 }
553 }
554
555
556
557
prepareMasterMix()558 void FxMixer::prepareMasterMix()
559 {
560 BufferManager::clear( m_fxChannels[0]->m_buffer,
561 Engine::mixer()->framesPerPeriod() );
562 }
563
564
565
masterMix(sampleFrame * _buf)566 void FxMixer::masterMix( sampleFrame * _buf )
567 {
568 const int fpp = Engine::mixer()->framesPerPeriod();
569
570 // add the channels that have no dependencies (no incoming senders, ie.
571 // no receives) to the jobqueue. The channels that have receives get
572 // added when their senders get processed, which is detected by
573 // dependency counting.
574 // also instantly add all muted channels as they don't need to care
575 // about their senders, and can just increment the deps of their
576 // recipients right away.
577 MixerWorkerThread::resetJobQueue( MixerWorkerThread::JobQueue::Dynamic );
578 for( FxChannel * ch : m_fxChannels )
579 {
580 ch->m_muted = ch->m_muteModel.value();
581 if( ch->m_muted ) // instantly "process" muted channels
582 {
583 ch->processed();
584 ch->done();
585 }
586 else if( ch->m_receives.size() == 0 )
587 {
588 ch->m_queued = true;
589 MixerWorkerThread::addJob( ch );
590 }
591 }
592 while( m_fxChannels[0]->state() != ThreadableJob::Done )
593 {
594 bool found = false;
595 for( FxChannel * ch : m_fxChannels )
596 {
597 int s = ch->state();
598 if( s == ThreadableJob::Queued
599 || s == ThreadableJob::InProgress )
600 {
601 found = true;
602 break;
603 }
604 }
605 if( !found )
606 {
607 break;
608 }
609 MixerWorkerThread::startAndWaitForJobs();
610 }
611
612 // handle sample-exact data in master volume fader
613 ValueBuffer * volBuf = m_fxChannels[0]->m_volumeModel.valueBuffer();
614
615 if( volBuf )
616 {
617 for( int f = 0; f < fpp; f++ )
618 {
619 m_fxChannels[0]->m_buffer[f][0] *= volBuf->values()[f];
620 m_fxChannels[0]->m_buffer[f][1] *= volBuf->values()[f];
621 }
622 }
623
624 const float v = volBuf
625 ? 1.0f
626 : m_fxChannels[0]->m_volumeModel.value();
627 MixHelpers::addSanitizedMultiplied( _buf, m_fxChannels[0]->m_buffer, v, fpp );
628
629 // clear all channel buffers and
630 // reset channel process state
631 for( int i = 0; i < numChannels(); ++i)
632 {
633 BufferManager::clear( m_fxChannels[i]->m_buffer,
634 Engine::mixer()->framesPerPeriod() );
635 m_fxChannels[i]->reset();
636 m_fxChannels[i]->m_queued = false;
637 // also reset hasInput
638 m_fxChannels[i]->m_hasInput = false;
639 m_fxChannels[i]->m_dependenciesMet = 0;
640 }
641 }
642
643
644
645
clear()646 void FxMixer::clear()
647 {
648 while( m_fxChannels.size() > 1 )
649 {
650 deleteChannel(1);
651 }
652
653 clearChannel(0);
654 }
655
656
657
clearChannel(fx_ch_t index)658 void FxMixer::clearChannel(fx_ch_t index)
659 {
660 FxChannel * ch = m_fxChannels[index];
661 ch->m_fxChain.clear();
662 ch->m_volumeModel.setValue( 1.0f );
663 ch->m_muteModel.setValue( false );
664 ch->m_soloModel.setValue( false );
665 ch->m_name = ( index == 0 ) ? tr( "Master" ) : tr( "FX %1" ).arg( index );
666 ch->m_volumeModel.setDisplayName( ch->m_name + ">" + tr( "Volume" ) );
667 ch->m_muteModel.setDisplayName( ch->m_name + ">" + tr( "Mute" ) );
668 ch->m_soloModel.setDisplayName( ch->m_name + ">" + tr( "Solo" ) );
669
670 // send only to master
671 if( index > 0)
672 {
673 // delete existing sends
674 while( ! ch->m_sends.isEmpty() )
675 {
676 deleteChannelSend( ch->m_sends.first() );
677 }
678
679 // add send to master
680 createChannelSend( index, 0 );
681 }
682
683 // delete receives
684 while( ! ch->m_receives.isEmpty() )
685 {
686 deleteChannelSend( ch->m_receives.first() );
687 }
688 }
689
saveSettings(QDomDocument & _doc,QDomElement & _this)690 void FxMixer::saveSettings( QDomDocument & _doc, QDomElement & _this )
691 {
692 // save channels
693 for( int i = 0; i < m_fxChannels.size(); ++i )
694 {
695 FxChannel * ch = m_fxChannels[i];
696
697 QDomElement fxch = _doc.createElement( QString( "fxchannel" ) );
698 _this.appendChild( fxch );
699
700 ch->m_fxChain.saveState( _doc, fxch );
701 ch->m_volumeModel.saveSettings( _doc, fxch, "volume" );
702 ch->m_muteModel.saveSettings( _doc, fxch, "muted" );
703 ch->m_soloModel.saveSettings( _doc, fxch, "soloed" );
704 fxch.setAttribute( "num", i );
705 fxch.setAttribute( "name", ch->m_name );
706
707 // add the channel sends
708 for( int si = 0; si < ch->m_sends.size(); ++si )
709 {
710 QDomElement sendsDom = _doc.createElement( QString( "send" ) );
711 fxch.appendChild( sendsDom );
712
713 sendsDom.setAttribute( "channel", ch->m_sends[si]->receiverIndex() );
714 ch->m_sends[si]->amount()->saveSettings( _doc, sendsDom, "amount" );
715 }
716 }
717 }
718
719 // make sure we have at least num channels
allocateChannelsTo(int num)720 void FxMixer::allocateChannelsTo(int num)
721 {
722 while( num > m_fxChannels.size() - 1 )
723 {
724 createChannel();
725
726 // delete the default send to master
727 deleteChannelSend( m_fxChannels.size()-1, 0 );
728 }
729 }
730
731
loadSettings(const QDomElement & _this)732 void FxMixer::loadSettings( const QDomElement & _this )
733 {
734 clear();
735 QDomNode node = _this.firstChild();
736
737 while( ! node.isNull() )
738 {
739 QDomElement fxch = node.toElement();
740
741 // index of the channel we are about to load
742 int num = fxch.attribute( "num" ).toInt();
743
744 // allocate enough channels
745 allocateChannelsTo( num );
746
747 m_fxChannels[num]->m_volumeModel.loadSettings( fxch, "volume" );
748 m_fxChannels[num]->m_muteModel.loadSettings( fxch, "muted" );
749 m_fxChannels[num]->m_soloModel.loadSettings( fxch, "soloed" );
750 m_fxChannels[num]->m_name = fxch.attribute( "name" );
751
752 m_fxChannels[num]->m_fxChain.restoreState( fxch.firstChildElement(
753 m_fxChannels[num]->m_fxChain.nodeName() ) );
754
755 // mixer sends
756 QDomNodeList chData = fxch.childNodes();
757 for( unsigned int i=0; i<chData.length(); ++i )
758 {
759 QDomElement chDataItem = chData.at(i).toElement();
760 if( chDataItem.nodeName() == QString( "send" ) )
761 {
762 int sendTo = chDataItem.attribute( "channel" ).toInt();
763 allocateChannelsTo( sendTo ) ;
764 FxRoute * fxr = createChannelSend( num, sendTo, 1.0f );
765 if( fxr ) fxr->amount()->loadSettings( chDataItem, "amount" );
766 }
767 }
768
769
770
771 node = node.nextSibling();
772 }
773
774 emit dataChanged();
775 }
776
777
validateChannelName(int index,int oldIndex)778 void FxMixer::validateChannelName( int index, int oldIndex )
779 {
780 if( m_fxChannels[index]->m_name == tr( "FX %1" ).arg( oldIndex ) )
781 {
782 m_fxChannels[index]->m_name = tr( "FX %1" ).arg( index );
783 }
784 }
785
786