1 /*
2 * Song.cpp - root of the model tree
3 *
4 * Copyright (c) 2004-2014 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 "Song.h"
26 #include <QTextStream>
27 #include <QCoreApplication>
28 #include <QDebug>
29 #include <QFile>
30 #include <QFileInfo>
31 #include <QMessageBox>
32
33 #include <functional>
34
35 #include "AutomationTrack.h"
36 #include "AutomationEditor.h"
37 #include "BBEditor.h"
38 #include "BBTrack.h"
39 #include "BBTrackContainer.h"
40 #include "ConfigManager.h"
41 #include "ControllerRackView.h"
42 #include "ControllerConnection.h"
43 #include "embed.h"
44 #include "EnvelopeAndLfoParameters.h"
45 #include "ExportProjectDialog.h"
46 #include "FxMixer.h"
47 #include "FxMixerView.h"
48 #include "GuiApplication.h"
49 #include "ImportFilter.h"
50 #include "ExportFilter.h"
51 #include "MainWindow.h"
52 #include "FileDialog.h"
53 #include "Pattern.h"
54 #include "PianoRoll.h"
55 #include "ProjectJournal.h"
56 #include "ProjectNotes.h"
57 #include "SongEditor.h"
58 #include "TextFloat.h"
59 #include "TimeLineWidget.h"
60 #include "PeakController.h"
61 #include "VersionedSaveDialog.h"
62
63
64 tick_t MidiTime::s_ticksPerTact = DefaultTicksPerTact;
65
66
67
Song()68 Song::Song() :
69 TrackContainer(),
70 m_globalAutomationTrack( dynamic_cast<AutomationTrack *>(
71 Track::create( Track::HiddenAutomationTrack,
72 this ) ) ),
73 m_tempoModel( DefaultTempo, MinTempo, MaxTempo, this, tr( "Tempo" ) ),
74 m_timeSigModel( this ),
75 m_oldTicksPerTact( DefaultTicksPerTact ),
76 m_masterVolumeModel( 100, 0, 200, this, tr( "Master volume" ) ),
77 m_masterPitchModel( 0, -12, 12, this, tr( "Master pitch" ) ),
78 m_nLoadingTrack( 0 ),
79 m_fileName(),
80 m_oldFileName(),
81 m_modified( false ),
82 m_loadOnLaunch( true ),
83 m_recording( false ),
84 m_exporting( false ),
85 m_exportLoop( false ),
86 m_renderBetweenMarkers( false ),
87 m_playing( false ),
88 m_paused( false ),
89 m_loadingProject( false ),
90 m_isCancelled( false ),
91 m_playMode( Mode_None ),
92 m_length( 0 ),
93 m_patternToPlay( NULL ),
94 m_loopPattern( false ),
95 m_elapsedMilliSeconds( 0 ),
96 m_elapsedTicks( 0 ),
97 m_elapsedTacts( 0 )
98 {
99 connect( &m_tempoModel, SIGNAL( dataChanged() ),
100 this, SLOT( setTempo() ), Qt::DirectConnection );
101 connect( &m_tempoModel, SIGNAL( dataUnchanged() ),
102 this, SLOT( setTempo() ), Qt::DirectConnection );
103 connect( &m_timeSigModel, SIGNAL( dataChanged() ),
104 this, SLOT( setTimeSignature() ), Qt::DirectConnection );
105
106
107 connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this,
108 SLOT( updateFramesPerTick() ) );
109
110 connect( &m_masterVolumeModel, SIGNAL( dataChanged() ),
111 this, SLOT( masterVolumeChanged() ), Qt::DirectConnection );
112 /* connect( &m_masterPitchModel, SIGNAL( dataChanged() ),
113 this, SLOT( masterPitchChanged() ) );*/
114
115 qRegisterMetaType<Note>( "Note" );
116 setType( SongContainer );
117 }
118
119
120
121
~Song()122 Song::~Song()
123 {
124 m_playing = false;
125 delete m_globalAutomationTrack;
126 }
127
128
129
130
masterVolumeChanged()131 void Song::masterVolumeChanged()
132 {
133 Engine::mixer()->setMasterGain( m_masterVolumeModel.value() /
134 100.0f );
135 }
136
137
138
139
setTempo()140 void Song::setTempo()
141 {
142 Engine::mixer()->requestChangeInModel();
143 const bpm_t tempo = ( bpm_t ) m_tempoModel.value();
144 PlayHandleList & playHandles = Engine::mixer()->playHandles();
145 for( PlayHandleList::Iterator it = playHandles.begin();
146 it != playHandles.end(); ++it )
147 {
148 NotePlayHandle * nph = dynamic_cast<NotePlayHandle *>( *it );
149 if( nph && !nph->isReleased() )
150 {
151 nph->lock();
152 nph->resize( tempo );
153 nph->unlock();
154 }
155 }
156 Engine::mixer()->doneChangeInModel();
157
158 Engine::updateFramesPerTick();
159
160 m_vstSyncController.setTempo( tempo );
161
162 emit tempoChanged( tempo );
163 }
164
165
166
167
setTimeSignature()168 void Song::setTimeSignature()
169 {
170 MidiTime::setTicksPerTact( ticksPerTact() );
171 emit timeSignatureChanged( m_oldTicksPerTact, ticksPerTact() );
172 emit dataChanged();
173 m_oldTicksPerTact = ticksPerTact();
174
175 m_vstSyncController.setTimeSignature(
176 getTimeSigModel().getNumerator(), getTimeSigModel().getDenominator() );
177 }
178
179
180
181
savePos()182 void Song::savePos()
183 {
184 TimeLineWidget * tl = m_playPos[m_playMode].m_timeLine;
185
186 if( tl != NULL )
187 {
188 tl->savePos( m_playPos[m_playMode] );
189 }
190 }
191
192
193
194
processNextBuffer()195 void Song::processNextBuffer()
196 {
197 m_vstSyncController.setPlaybackJumped( false );
198
199 // if not playing, nothing to do
200 if( m_playing == false )
201 {
202 return;
203 }
204
205 TrackList trackList;
206 int tcoNum = -1; // track content object number
207
208 // determine the list of tracks to play and the track content object
209 // (TCO) number
210 switch( m_playMode )
211 {
212 case Mode_PlaySong:
213 trackList = tracks();
214 // at song-start we have to reset the LFOs
215 if( m_playPos[Mode_PlaySong] == 0 )
216 {
217 EnvelopeAndLfoParameters::instances()->reset();
218 }
219 break;
220
221 case Mode_PlayBB:
222 if( Engine::getBBTrackContainer()->numOfBBs() > 0 )
223 {
224 tcoNum = Engine::getBBTrackContainer()->
225 currentBB();
226 trackList.push_back( BBTrack::findBBTrack(
227 tcoNum ) );
228 }
229 break;
230
231 case Mode_PlayPattern:
232 if( m_patternToPlay != NULL )
233 {
234 tcoNum = m_patternToPlay->getTrack()->
235 getTCONum( m_patternToPlay );
236 trackList.push_back(
237 m_patternToPlay->getTrack() );
238 }
239 break;
240
241 default:
242 return;
243
244 }
245
246 // if we have no tracks to play, nothing to do
247 if( trackList.empty() == true )
248 {
249 return;
250 }
251
252 // check for looping-mode and act if necessary
253 TimeLineWidget * tl = m_playPos[m_playMode].m_timeLine;
254 bool checkLoop =
255 tl != NULL && m_exporting == false && tl->loopPointsEnabled();
256
257 if( checkLoop )
258 {
259 // if looping-mode is enabled and we are outside of the looping
260 // range, go to the beginning of the range
261 if( m_playPos[m_playMode] < tl->loopBegin() ||
262 m_playPos[m_playMode] >= tl->loopEnd() )
263 {
264 m_elapsedMilliSeconds =
265 ( tl->loopBegin().getTicks() * 60 * 1000 / 48 ) / getTempo();
266 m_playPos[m_playMode].setTicks(
267 tl->loopBegin().getTicks() );
268
269 m_vstSyncController.setPlaybackJumped( true );
270
271 emit updateSampleTracks();
272 }
273 }
274
275 if( m_playPos[m_playMode].jumped() )
276 {
277 m_vstSyncController.setPlaybackJumped( true );
278 m_playPos[m_playMode].setJumped( false );
279 }
280
281 f_cnt_t framesPlayed = 0;
282 const float framesPerTick = Engine::framesPerTick();
283
284 while( framesPlayed < Engine::mixer()->framesPerPeriod() )
285 {
286 m_vstSyncController.update();
287
288 float currentFrame = m_playPos[m_playMode].currentFrame();
289 // did we play a tick?
290 if( currentFrame >= framesPerTick )
291 {
292 int ticks = m_playPos[m_playMode].getTicks() +
293 ( int )( currentFrame / framesPerTick );
294
295 // did we play a whole tact?
296 if( ticks >= MidiTime::ticksPerTact() )
297 {
298 // per default we just continue playing even if
299 // there's no more stuff to play
300 // (song-play-mode)
301 int maxTact = m_playPos[m_playMode].getTact()
302 + 2;
303
304 // then decide whether to go over to next tact
305 // or to loop back to first tact
306 if( m_playMode == Mode_PlayBB )
307 {
308 maxTact = Engine::getBBTrackContainer()
309 ->lengthOfCurrentBB();
310 }
311 else if( m_playMode == Mode_PlayPattern &&
312 m_loopPattern == true &&
313 tl != NULL &&
314 tl->loopPointsEnabled() == false )
315 {
316 maxTact = m_patternToPlay->length()
317 .getTact();
318 }
319
320 // end of played object reached?
321 if( m_playPos[m_playMode].getTact() + 1
322 >= maxTact )
323 {
324 // then start from beginning and keep
325 // offset
326 ticks %= ( maxTact * MidiTime::ticksPerTact() );
327
328 // wrap milli second counter
329 m_elapsedMilliSeconds =
330 ( ticks * 60 * 1000 / 48 ) / getTempo();
331
332 m_vstSyncController.setPlaybackJumped( true );
333 }
334 }
335 m_playPos[m_playMode].setTicks( ticks );
336
337 if( checkLoop )
338 {
339 m_vstSyncController.startCycle(
340 tl->loopBegin().getTicks(), tl->loopEnd().getTicks() );
341
342 // if looping-mode is enabled and we have got
343 // past the looping range, return to the
344 // beginning of the range
345 if( m_playPos[m_playMode] >= tl->loopEnd() )
346 {
347 ticks = tl->loopBegin().getTicks();
348 m_playPos[m_playMode].setTicks( ticks );
349
350 m_elapsedMilliSeconds =
351 ( ticks * 60 * 1000 / 48 ) / getTempo();
352
353 m_vstSyncController.setPlaybackJumped( true );
354
355 emit updateSampleTracks();
356 }
357 }
358 else
359 {
360 m_vstSyncController.stopCycle();
361 }
362
363 currentFrame = fmodf( currentFrame, framesPerTick );
364 m_playPos[m_playMode].setCurrentFrame( currentFrame );
365 }
366
367 if( framesPlayed == 0 )
368 {
369 // update VST sync position after we've corrected frame/
370 // tick count but before actually playing any frames
371 m_vstSyncController.setAbsolutePosition(
372 m_playPos[m_playMode].getTicks()
373 + m_playPos[m_playMode].currentFrame()
374 / (double) framesPerTick );
375 }
376
377 f_cnt_t framesToPlay =
378 Engine::mixer()->framesPerPeriod() - framesPlayed;
379
380 f_cnt_t framesLeft = ( f_cnt_t )framesPerTick -
381 ( f_cnt_t )currentFrame;
382 // skip last frame fraction
383 if( framesLeft == 0 )
384 {
385 ++framesPlayed;
386 m_playPos[m_playMode].setCurrentFrame( currentFrame
387 + 1.0f );
388 continue;
389 }
390 // do we have samples left in this tick but these are less
391 // than samples we have to play?
392 if( framesLeft < framesToPlay )
393 {
394 // then set framesToPlay to remaining samples, the
395 // rest will be played in next loop
396 framesToPlay = framesLeft;
397 }
398
399 if( ( f_cnt_t ) currentFrame == 0 )
400 {
401 processAutomations(trackList, m_playPos[m_playMode], framesToPlay);
402
403 // loop through all tracks and play them
404 for( int i = 0; i < trackList.size(); ++i )
405 {
406 trackList[i]->play( m_playPos[m_playMode],
407 framesToPlay,
408 framesPlayed, tcoNum );
409 }
410 }
411
412 // update frame-counters
413 framesPlayed += framesToPlay;
414 m_playPos[m_playMode].setCurrentFrame( framesToPlay +
415 currentFrame );
416 m_elapsedMilliSeconds +=
417 ( ( framesToPlay / framesPerTick ) * 60 * 1000 / 48 )
418 / getTempo();
419 m_elapsedTacts = m_playPos[Mode_PlaySong].getTact();
420 m_elapsedTicks = ( m_playPos[Mode_PlaySong].getTicks() % ticksPerTact() ) / 48;
421 }
422 }
423
424
processAutomations(const TrackList & tracklist,MidiTime timeStart,fpp_t)425 void Song::processAutomations(const TrackList &tracklist, MidiTime timeStart, fpp_t)
426 {
427 AutomatedValueMap values;
428
429 QSet<const AutomatableModel*> recordedModels;
430
431 TrackContainer* container = this;
432 int tcoNum = -1;
433
434 switch (m_playMode)
435 {
436 case Mode_PlaySong:
437 break;
438 case Mode_PlayBB:
439 {
440 Q_ASSERT(tracklist.size() == 1);
441 Q_ASSERT(tracklist.at(0)->type() == Track::BBTrack);
442 auto bbTrack = dynamic_cast<BBTrack*>(tracklist.at(0));
443 auto bbContainer = Engine::getBBTrackContainer();
444 container = bbContainer;
445 tcoNum = bbTrack->index();
446 }
447 break;
448 default:
449 return;
450 }
451
452 values = container->automatedValuesAt(timeStart, tcoNum);
453 TrackList tracks = container->tracks();
454
455 Track::tcoVector tcos;
456 for (Track* track : tracks)
457 {
458 if (track->type() == Track::AutomationTrack) {
459 track->getTCOsInRange(tcos, 0, timeStart);
460 }
461 }
462
463 // Process recording
464 for (TrackContentObject* tco : tcos)
465 {
466 auto p = dynamic_cast<AutomationPattern *>(tco);
467 MidiTime relTime = timeStart - p->startPosition();
468 if (p->isRecording() && relTime >= 0 && relTime < p->length())
469 {
470 const AutomatableModel* recordedModel = p->firstObject();
471 p->recordValue(relTime, recordedModel->value<float>());
472
473 recordedModels << recordedModel;
474 }
475 }
476
477 // Apply values
478 for (auto it = values.begin(); it != values.end(); it++)
479 {
480 if (! recordedModels.contains(it.key()))
481 {
482 it.key()->setAutomatedValue(it.value());
483 }
484 }
485 }
486
getExportEndpoints() const487 std::pair<MidiTime, MidiTime> Song::getExportEndpoints() const
488 {
489 if ( m_renderBetweenMarkers )
490 {
491 return std::pair<MidiTime, MidiTime>(
492 m_playPos[Mode_PlaySong].m_timeLine->loopBegin(),
493 m_playPos[Mode_PlaySong].m_timeLine->loopEnd()
494 );
495 }
496 else if ( m_exportLoop )
497 {
498 return std::pair<MidiTime, MidiTime>( MidiTime(0, 0), MidiTime(m_length, 0) );
499 }
500 else
501 {
502 // if not exporting as a loop, we leave one bar of padding at the end of the song to accomodate reverb, etc.
503 return std::pair<MidiTime, MidiTime>( MidiTime(0, 0), MidiTime(m_length+1, 0) );
504 }
505 }
506
507
508
509
playSong()510 void Song::playSong()
511 {
512 m_recording = false;
513
514 if( isStopped() == false )
515 {
516 stop();
517 }
518
519 m_playMode = Mode_PlaySong;
520 m_playing = true;
521 m_paused = false;
522
523 m_vstSyncController.setPlaybackState( true );
524
525 savePos();
526
527 emit playbackStateChanged();
528 }
529
530
531
532
record()533 void Song::record()
534 {
535 m_recording = true;
536 // TODO: Implement
537 }
538
539
540
541
playAndRecord()542 void Song::playAndRecord()
543 {
544 playSong();
545 m_recording = true;
546 }
547
548
549
550
playBB()551 void Song::playBB()
552 {
553 if( isStopped() == false )
554 {
555 stop();
556 }
557
558 m_playMode = Mode_PlayBB;
559 m_playing = true;
560 m_paused = false;
561
562 m_vstSyncController.setPlaybackState( true );
563
564 savePos();
565
566 emit playbackStateChanged();
567 }
568
569
570
571
playPattern(const Pattern * patternToPlay,bool loop)572 void Song::playPattern( const Pattern* patternToPlay, bool loop )
573 {
574 if( isStopped() == false )
575 {
576 stop();
577 }
578
579 m_patternToPlay = patternToPlay;
580 m_loopPattern = loop;
581
582 if( m_patternToPlay != NULL )
583 {
584 m_playMode = Mode_PlayPattern;
585 m_playing = true;
586 m_paused = false;
587 }
588
589 savePos();
590
591 emit playbackStateChanged();
592 }
593
594
595
596
updateLength()597 void Song::updateLength()
598 {
599 m_length = 0;
600 m_tracksMutex.lockForRead();
601 for( TrackList::const_iterator it = tracks().begin();
602 it != tracks().end(); ++it )
603 {
604 if( Engine::getSong()->isExporting() &&
605 ( *it )->isMuted() )
606 {
607 continue;
608 }
609
610 const tact_t cur = ( *it )->length();
611 if( cur > m_length )
612 {
613 m_length = cur;
614 }
615 }
616 m_tracksMutex.unlock();
617
618 emit lengthChanged( m_length );
619 }
620
621
622
623
setPlayPos(tick_t ticks,PlayModes playMode)624 void Song::setPlayPos( tick_t ticks, PlayModes playMode )
625 {
626 m_elapsedTicks += m_playPos[playMode].getTicks() - ticks;
627 m_elapsedMilliSeconds +=
628 ( ( ( ( ticks - m_playPos[playMode].getTicks() ) ) * 60 * 1000 / 48) /
629 getTempo() );
630 m_playPos[playMode].setTicks( ticks );
631 m_playPos[playMode].setCurrentFrame( 0.0f );
632 m_playPos[playMode].setJumped( true );
633
634 // send a signal if playposition changes during playback
635 if( isPlaying() )
636 {
637 emit playbackPositionChanged();
638 emit updateSampleTracks();
639 }
640 }
641
642
643
644
togglePause()645 void Song::togglePause()
646 {
647 if( m_paused == true )
648 {
649 m_playing = true;
650 m_paused = false;
651 }
652 else
653 {
654 m_playing = false;
655 m_paused = true;
656 }
657
658 m_vstSyncController.setPlaybackState( m_playing );
659
660 emit playbackStateChanged();
661 }
662
663
664
665
stop()666 void Song::stop()
667 {
668 // do not stop/reset things again if we're stopped already
669 if( m_playMode == Mode_None )
670 {
671 return;
672 }
673
674 TimeLineWidget * tl = m_playPos[m_playMode].m_timeLine;
675 m_paused = false;
676 m_recording = true;
677
678 if( tl != NULL )
679 {
680
681 switch( tl->behaviourAtStop() )
682 {
683 case TimeLineWidget::BackToZero:
684 m_playPos[m_playMode].setTicks( 0 );
685 m_elapsedMilliSeconds = 0;
686 if( gui && gui->songEditor() &&
687 ( tl->autoScroll() == TimeLineWidget::AutoScrollEnabled ) )
688 {
689 QMetaObject::invokeMethod(gui->songEditor()->m_editor, "updatePosition", Qt::AutoConnection, Q_ARG(MidiTime, 0));
690 }
691 break;
692
693 case TimeLineWidget::BackToStart:
694 if( tl->savedPos() >= 0 )
695 {
696 m_playPos[m_playMode].setTicks( tl->savedPos().getTicks() );
697 m_elapsedMilliSeconds =
698 ( ( ( tl->savedPos().getTicks() ) * 60 * 1000 / 48 ) /
699 getTempo() );
700 if( gui && gui->songEditor() &&
701 ( tl->autoScroll() == TimeLineWidget::AutoScrollEnabled ) )
702 {
703 QMetaObject::invokeMethod(gui->songEditor()->m_editor, "updatePosition", Qt::AutoConnection, Q_ARG(MidiTime, tl->savedPos().getTicks()));
704 }
705 tl->savePos( -1 );
706 }
707 break;
708
709 case TimeLineWidget::KeepStopPosition:
710 default:
711 break;
712 }
713 }
714 else
715 {
716 m_playPos[m_playMode].setTicks( 0 );
717 m_elapsedMilliSeconds = 0;
718 }
719 m_playing = false;
720
721 m_playPos[m_playMode].setCurrentFrame( 0 );
722
723 m_vstSyncController.setPlaybackState( m_exporting );
724 m_vstSyncController.setAbsolutePosition(
725 m_playPos[m_playMode].getTicks()
726 + m_playPos[m_playMode].currentFrame()
727 / (double) Engine::framesPerTick() );
728
729 // remove all note-play-handles that are active
730 Engine::mixer()->clear();
731
732 m_playMode = Mode_None;
733
734 emit playbackStateChanged();
735 }
736
737
738
739
startExport()740 void Song::startExport()
741 {
742 stop();
743 if(m_renderBetweenMarkers)
744 {
745 m_playPos[Mode_PlaySong].setTicks( m_playPos[Mode_PlaySong].m_timeLine->loopBegin().getTicks() );
746 }
747 else
748 {
749 m_playPos[Mode_PlaySong].setTicks( 0 );
750 }
751
752 playSong();
753
754 m_exporting = true;
755
756 m_vstSyncController.setPlaybackState( true );
757 }
758
759
760
761
stopExport()762 void Song::stopExport()
763 {
764 stop();
765 m_exporting = false;
766 m_exportLoop = false;
767
768 m_vstSyncController.setPlaybackState( m_playing );
769 }
770
771
772
773
insertBar()774 void Song::insertBar()
775 {
776 m_tracksMutex.lockForRead();
777 for( TrackList::const_iterator it = tracks().begin();
778 it != tracks().end(); ++it )
779 {
780 ( *it )->insertTact( m_playPos[Mode_PlaySong] );
781 }
782 m_tracksMutex.unlock();
783 }
784
785
786
787
removeBar()788 void Song::removeBar()
789 {
790 m_tracksMutex.lockForRead();
791 for( TrackList::const_iterator it = tracks().begin();
792 it != tracks().end(); ++it )
793 {
794 ( *it )->removeTact( m_playPos[Mode_PlaySong] );
795 }
796 m_tracksMutex.unlock();
797 }
798
799
800
801
addBBTrack()802 void Song::addBBTrack()
803 {
804 Track * t = Track::create( Track::BBTrack, this );
805 Engine::getBBTrackContainer()->setCurrentBB( dynamic_cast<BBTrack *>( t )->index() );
806 }
807
808
809
810
addSampleTrack()811 void Song::addSampleTrack()
812 {
813 ( void )Track::create( Track::SampleTrack, this );
814 }
815
816
817
818
addAutomationTrack()819 void Song::addAutomationTrack()
820 {
821 ( void )Track::create( Track::AutomationTrack, this );
822 }
823
824
825
826
getTempo()827 bpm_t Song::getTempo()
828 {
829 return ( bpm_t )m_tempoModel.value();
830 }
831
832
833
834
tempoAutomationPattern()835 AutomationPattern * Song::tempoAutomationPattern()
836 {
837 return AutomationPattern::globalAutomationPattern( &m_tempoModel );
838 }
839
840
automatedValuesAt(MidiTime time,int tcoNum) const841 AutomatedValueMap Song::automatedValuesAt(MidiTime time, int tcoNum) const
842 {
843 return TrackContainer::automatedValuesFromTracks(TrackList{m_globalAutomationTrack} << tracks(), time, tcoNum);
844 }
845
846
847
848
clearProject()849 void Song::clearProject()
850 {
851 Engine::projectJournal()->setJournalling( false );
852
853 if( m_playing )
854 {
855 stop();
856 }
857
858 for( int i = 0; i < Mode_Count; i++ )
859 {
860 setPlayPos( 0, ( PlayModes )i );
861 }
862
863
864 Engine::mixer()->requestChangeInModel();
865
866 if( gui && gui->getBBEditor() )
867 {
868 gui->getBBEditor()->trackContainerView()->clearAllTracks();
869 }
870 if( gui && gui->songEditor() )
871 {
872 gui->songEditor()->m_editor->clearAllTracks();
873 }
874 if( gui && gui->fxMixerView() )
875 {
876 gui->fxMixerView()->clear();
877 }
878 QCoreApplication::sendPostedEvents();
879 Engine::getBBTrackContainer()->clearAllTracks();
880 clearAllTracks();
881
882 Engine::fxMixer()->clear();
883
884 if( gui && gui->automationEditor() )
885 {
886 gui->automationEditor()->setCurrentPattern( NULL );
887 }
888
889 if( gui && gui->pianoRoll() )
890 {
891 gui->pianoRoll()->reset();
892 }
893
894 m_tempoModel.reset();
895 m_masterVolumeModel.reset();
896 m_masterPitchModel.reset();
897 m_timeSigModel.reset();
898
899 AutomationPattern::globalAutomationPattern( &m_tempoModel )->clear();
900 AutomationPattern::globalAutomationPattern( &m_masterVolumeModel )->
901 clear();
902 AutomationPattern::globalAutomationPattern( &m_masterPitchModel )->
903 clear();
904
905 Engine::mixer()->doneChangeInModel();
906
907 if( gui && gui->getProjectNotes() )
908 {
909 gui->getProjectNotes()->clear();
910 }
911
912 removeAllControllers();
913
914 emit dataChanged();
915
916 Engine::projectJournal()->clearJournal();
917
918 Engine::projectJournal()->setJournalling( true );
919
920 InstrumentTrackView::cleanupWindowCache();
921 }
922
923
924
925
926 // create new file
createNewProject()927 void Song::createNewProject()
928 {
929
930 QString defaultTemplate = ConfigManager::inst()->userTemplateDir()
931 + "default.mpt";
932
933
934 if( QFile::exists( defaultTemplate ) )
935 {
936 createNewProjectFromTemplate( defaultTemplate );
937 return;
938 }
939
940 defaultTemplate = ConfigManager::inst()->factoryProjectsDir()
941 + "templates/default.mpt";
942 if( QFile::exists( defaultTemplate ) )
943 {
944 createNewProjectFromTemplate( defaultTemplate );
945 return;
946 }
947
948 m_loadingProject = true;
949
950 clearProject();
951
952 Engine::projectJournal()->setJournalling( false );
953
954 m_fileName = m_oldFileName = "";
955
956 Track * t;
957 t = Track::create( Track::InstrumentTrack, this );
958 dynamic_cast<InstrumentTrack * >( t )->loadInstrument(
959 "tripleoscillator" );
960 t = Track::create( Track::InstrumentTrack,
961 Engine::getBBTrackContainer() );
962 dynamic_cast<InstrumentTrack * >( t )->loadInstrument(
963 "kicker" );
964 Track::create( Track::SampleTrack, this );
965 Track::create( Track::BBTrack, this );
966 Track::create( Track::AutomationTrack, this );
967
968 m_tempoModel.setInitValue( DefaultTempo );
969 m_timeSigModel.reset();
970 m_masterVolumeModel.setInitValue( 100 );
971 m_masterPitchModel.setInitValue( 0 );
972
973 QCoreApplication::instance()->processEvents();
974
975 m_loadingProject = false;
976
977 Engine::getBBTrackContainer()->updateAfterTrackAdd();
978
979 Engine::projectJournal()->setJournalling( true );
980
981 QCoreApplication::sendPostedEvents();
982
983 m_modified = false;
984 m_loadOnLaunch = false;
985
986 if( gui->mainWindow() )
987 {
988 gui->mainWindow()->resetWindowTitle();
989 }
990 }
991
992
993
994
createNewProjectFromTemplate(const QString & templ)995 void Song::createNewProjectFromTemplate( const QString & templ )
996 {
997 loadProject( templ );
998 // clear file-name so that user doesn't overwrite template when
999 // saving...
1000 m_fileName = m_oldFileName = "";
1001 // update window title
1002 m_loadOnLaunch = false;
1003 if( gui->mainWindow() )
1004 {
1005 gui->mainWindow()->resetWindowTitle();
1006 }
1007 }
1008
1009
1010
1011
1012 // load given song
loadProject(const QString & fileName)1013 void Song::loadProject( const QString & fileName )
1014 {
1015 QDomNode node;
1016
1017 m_loadingProject = true;
1018
1019 Engine::projectJournal()->setJournalling( false );
1020
1021 m_oldFileName = m_fileName;
1022 m_fileName = fileName;
1023
1024 DataFile dataFile( m_fileName );
1025 // if file could not be opened, head-node is null and we create
1026 // new project
1027 if( dataFile.head().isNull() )
1028 {
1029 if( m_loadOnLaunch )
1030 {
1031 createNewProject();
1032 }
1033 m_fileName = m_oldFileName;
1034 return;
1035 }
1036
1037 m_oldFileName = m_fileName;
1038
1039 clearProject();
1040
1041 clearErrors();
1042
1043 Engine::mixer()->requestChangeInModel();
1044
1045 // get the header information from the DOM
1046 m_tempoModel.loadSettings( dataFile.head(), "bpm" );
1047 m_timeSigModel.loadSettings( dataFile.head(), "timesig" );
1048 m_masterVolumeModel.loadSettings( dataFile.head(), "mastervol" );
1049 m_masterPitchModel.loadSettings( dataFile.head(), "masterpitch" );
1050
1051 if( m_playPos[Mode_PlaySong].m_timeLine )
1052 {
1053 // reset loop-point-state
1054 m_playPos[Mode_PlaySong].m_timeLine->toggleLoopPoints( 0 );
1055 }
1056
1057 if( !dataFile.content().firstChildElement( "track" ).isNull() )
1058 {
1059 m_globalAutomationTrack->restoreState( dataFile.content().
1060 firstChildElement( "track" ) );
1061 }
1062
1063 //Backward compatibility for LMMS <= 0.4.15
1064 PeakController::initGetControllerBySetting();
1065
1066 // Load mixer first to be able to set the correct range for FX channels
1067 node = dataFile.content().firstChildElement( Engine::fxMixer()->nodeName() );
1068 if( !node.isNull() )
1069 {
1070 Engine::fxMixer()->restoreState( node.toElement() );
1071 if( gui )
1072 {
1073 // refresh FxMixerView
1074 gui->fxMixerView()->refreshDisplay();
1075 }
1076 }
1077
1078 node = dataFile.content().firstChild();
1079
1080 QDomNodeList tclist=dataFile.content().elementsByTagName("trackcontainer");
1081 m_nLoadingTrack=0;
1082 for( int i=0,n=tclist.count(); i<n; ++i )
1083 {
1084 QDomNode nd=tclist.at(i).firstChild();
1085 while(!nd.isNull())
1086 {
1087 if( nd.isElement() && nd.nodeName() == "track" )
1088 {
1089 ++m_nLoadingTrack;
1090 if( nd.toElement().attribute("type").toInt() == Track::BBTrack )
1091 {
1092 n += nd.toElement().elementsByTagName("bbtrack").at(0)
1093 .toElement().firstChildElement().childNodes().count();
1094 }
1095 nd=nd.nextSibling();
1096 }
1097 }
1098 }
1099
1100 while( !node.isNull() && !isCancelled() )
1101 {
1102 if( node.isElement() )
1103 {
1104 if( node.nodeName() == "trackcontainer" )
1105 {
1106 ( (JournallingObject *)( this ) )->restoreState( node.toElement() );
1107 }
1108 else if( node.nodeName() == "controllers" )
1109 {
1110 restoreControllerStates( node.toElement() );
1111 }
1112 else if( gui )
1113 {
1114 if( node.nodeName() == gui->getControllerRackView()->nodeName() )
1115 {
1116 gui->getControllerRackView()->restoreState( node.toElement() );
1117 }
1118 else if( node.nodeName() == gui->pianoRoll()->nodeName() )
1119 {
1120 gui->pianoRoll()->restoreState( node.toElement() );
1121 }
1122 else if( node.nodeName() == gui->automationEditor()->m_editor->nodeName() )
1123 {
1124 gui->automationEditor()->m_editor->restoreState( node.toElement() );
1125 }
1126 else if( node.nodeName() == gui->getProjectNotes()->nodeName() )
1127 {
1128 gui->getProjectNotes()->SerializingObject::restoreState( node.toElement() );
1129 }
1130 else if( node.nodeName() == m_playPos[Mode_PlaySong].m_timeLine->nodeName() )
1131 {
1132 m_playPos[Mode_PlaySong].m_timeLine->restoreState( node.toElement() );
1133 }
1134 }
1135 }
1136 node = node.nextSibling();
1137 }
1138
1139 // quirk for fixing projects with broken positions of TCOs inside
1140 // BB-tracks
1141 Engine::getBBTrackContainer()->fixIncorrectPositions();
1142
1143 // Connect controller links to their controllers
1144 // now that everything is loaded
1145 ControllerConnection::finalizeConnections();
1146
1147 // Remove dummy controllers that was added for correct connections
1148 m_controllers.erase(std::remove_if(m_controllers.begin(), m_controllers.end(),
1149 [](Controller* c){return c->type() == Controller::DummyController;}),
1150 m_controllers.end());
1151
1152 // resolve all IDs so that autoModels are automated
1153 AutomationPattern::resolveAllIDs();
1154
1155
1156 Engine::mixer()->doneChangeInModel();
1157
1158 ConfigManager::inst()->addRecentlyOpenedProject( fileName );
1159
1160 Engine::projectJournal()->setJournalling( true );
1161
1162 emit projectLoaded();
1163
1164 if( isCancelled() )
1165 {
1166 m_isCancelled = false;
1167 createNewProject();
1168 return;
1169 }
1170
1171 if ( hasErrors())
1172 {
1173 if ( gui )
1174 {
1175 QMessageBox::warning( NULL, tr("LMMS Error report"), errorSummary(),
1176 QMessageBox::Ok );
1177 }
1178 else
1179 {
1180 QTextStream(stderr) << Engine::getSong()->errorSummary() << endl;
1181 }
1182 }
1183
1184 m_loadingProject = false;
1185 m_modified = false;
1186 m_loadOnLaunch = false;
1187
1188 if( gui && gui->mainWindow() )
1189 {
1190 gui->mainWindow()->resetWindowTitle();
1191 }
1192 }
1193
1194
1195 // only save current song as _filename and do nothing else
saveProjectFile(const QString & filename)1196 bool Song::saveProjectFile( const QString & filename )
1197 {
1198 DataFile dataFile( DataFile::SongProject );
1199
1200 m_tempoModel.saveSettings( dataFile, dataFile.head(), "bpm" );
1201 m_timeSigModel.saveSettings( dataFile, dataFile.head(), "timesig" );
1202 m_masterVolumeModel.saveSettings( dataFile, dataFile.head(), "mastervol" );
1203 m_masterPitchModel.saveSettings( dataFile, dataFile.head(), "masterpitch" );
1204
1205 saveState( dataFile, dataFile.content() );
1206
1207 m_globalAutomationTrack->saveState( dataFile, dataFile.content() );
1208 Engine::fxMixer()->saveState( dataFile, dataFile.content() );
1209 if( gui )
1210 {
1211 gui->getControllerRackView()->saveState( dataFile, dataFile.content() );
1212 gui->pianoRoll()->saveState( dataFile, dataFile.content() );
1213 gui->automationEditor()->m_editor->saveState( dataFile, dataFile.content() );
1214 gui->getProjectNotes()->SerializingObject::saveState( dataFile, dataFile.content() );
1215 m_playPos[Mode_PlaySong].m_timeLine->saveState( dataFile, dataFile.content() );
1216 }
1217
1218 saveControllerStates( dataFile, dataFile.content() );
1219
1220 return dataFile.writeFile( filename );
1221 }
1222
1223
1224
1225 // save current song and update the gui
guiSaveProject()1226 bool Song::guiSaveProject()
1227 {
1228 DataFile dataFile( DataFile::SongProject );
1229 m_fileName = dataFile.nameWithExtension( m_fileName );
1230 if( saveProjectFile( m_fileName ) && gui != nullptr )
1231 {
1232 TextFloat::displayMessage( tr( "Project saved" ),
1233 tr( "The project %1 is now saved."
1234 ).arg( m_fileName ),
1235 embed::getIconPixmap( "project_save", 24, 24 ),
1236 2000 );
1237 ConfigManager::inst()->addRecentlyOpenedProject( m_fileName );
1238 m_modified = false;
1239 gui->mainWindow()->resetWindowTitle();
1240 }
1241 else if( gui != nullptr )
1242 {
1243 TextFloat::displayMessage( tr( "Project NOT saved." ),
1244 tr( "The project %1 was not saved!" ).arg(
1245 m_fileName ),
1246 embed::getIconPixmap( "error" ), 4000 );
1247 return false;
1248 }
1249
1250 return true;
1251 }
1252
1253
1254
1255
1256 // save current song in given filename
guiSaveProjectAs(const QString & _file_name)1257 bool Song::guiSaveProjectAs( const QString & _file_name )
1258 {
1259 QString o = m_oldFileName;
1260 m_oldFileName = m_fileName;
1261 m_fileName = _file_name;
1262 if( guiSaveProject() == false )
1263 {
1264 m_fileName = m_oldFileName;
1265 m_oldFileName = o;
1266 return false;
1267 }
1268 m_oldFileName = m_fileName;
1269 return true;
1270 }
1271
1272
1273
1274
importProject()1275 void Song::importProject()
1276 {
1277 FileDialog ofd( NULL, tr( "Import file" ),
1278 ConfigManager::inst()->userProjectsDir(),
1279 tr("MIDI sequences") +
1280 " (*.mid *.midi *.rmi);;" +
1281 tr("Hydrogen projects") +
1282 " (*.h2song);;" +
1283 tr("All file types") +
1284 " (*.*)");
1285
1286 ofd.setFileMode( FileDialog::ExistingFiles );
1287 if( ofd.exec () == QDialog::Accepted && !ofd.selectedFiles().isEmpty() )
1288 {
1289 ImportFilter::import( ofd.selectedFiles()[0], this );
1290 }
1291 m_loadOnLaunch = false;
1292 }
1293
1294
1295
1296
saveControllerStates(QDomDocument & doc,QDomElement & element)1297 void Song::saveControllerStates( QDomDocument & doc, QDomElement & element )
1298 {
1299 // save settings of controllers
1300 QDomElement controllersNode = doc.createElement( "controllers" );
1301 element.appendChild( controllersNode );
1302 for( int i = 0; i < m_controllers.size(); ++i )
1303 {
1304 m_controllers[i]->saveState( doc, controllersNode );
1305 }
1306 }
1307
1308
1309
1310
restoreControllerStates(const QDomElement & element)1311 void Song::restoreControllerStates( const QDomElement & element )
1312 {
1313 QDomNode node = element.firstChild();
1314 while( !node.isNull() && !isCancelled() )
1315 {
1316 Controller * c = Controller::create( node.toElement(), this );
1317 if (c) {addController(c);}
1318 else
1319 {
1320 // Fix indices to ensure correct connections
1321 m_controllers.append(Controller::create(
1322 Controller::DummyController, this));
1323 }
1324
1325 node = node.nextSibling();
1326 }
1327 }
1328
1329
1330
removeAllControllers()1331 void Song::removeAllControllers()
1332 {
1333 while (m_controllers.size() != 0)
1334 {
1335 removeController(m_controllers.at(0));
1336 }
1337
1338 m_controllers.clear();
1339 }
1340
1341
1342
exportProjectTracks()1343 void Song::exportProjectTracks()
1344 {
1345 exportProject( true );
1346 }
1347
exportProject(bool multiExport)1348 void Song::exportProject( bool multiExport )
1349 {
1350 if( isEmpty() )
1351 {
1352 QMessageBox::information( gui->mainWindow(),
1353 tr( "Empty project" ),
1354 tr( "This project is empty so exporting makes "
1355 "no sense. Please put some items into "
1356 "Song Editor first!" ) );
1357 return;
1358 }
1359
1360 FileDialog efd( gui->mainWindow() );
1361
1362 if ( multiExport )
1363 {
1364 efd.setFileMode( FileDialog::Directory);
1365 efd.setWindowTitle( tr( "Select directory for writing exported tracks..." ) );
1366 if( !m_fileName.isEmpty() )
1367 {
1368 efd.setDirectory( QFileInfo( m_fileName ).absolutePath() );
1369 }
1370 }
1371 else
1372 {
1373 efd.setFileMode( FileDialog::AnyFile );
1374 int idx = 0;
1375 QStringList types;
1376 while( ProjectRenderer::fileEncodeDevices[idx].m_fileFormat != ProjectRenderer::NumFileFormats)
1377 {
1378 if(ProjectRenderer::fileEncodeDevices[idx].isAvailable()) {
1379 types << tr(ProjectRenderer::fileEncodeDevices[idx].m_description);
1380 }
1381 ++idx;
1382 }
1383 efd.setNameFilters( types );
1384 QString baseFilename;
1385 if( !m_fileName.isEmpty() )
1386 {
1387 efd.setDirectory( QFileInfo( m_fileName ).absolutePath() );
1388 baseFilename = QFileInfo( m_fileName ).completeBaseName();
1389 }
1390 else
1391 {
1392 efd.setDirectory( ConfigManager::inst()->userProjectsDir() );
1393 baseFilename = tr( "untitled" );
1394 }
1395 efd.selectFile( baseFilename + ProjectRenderer::fileEncodeDevices[0].m_extension );
1396 efd.setWindowTitle( tr( "Select file for project-export..." ) );
1397 }
1398
1399 QString suffix = "wav";
1400 efd.setDefaultSuffix( suffix );
1401 efd.setAcceptMode( FileDialog::AcceptSave );
1402
1403 if( efd.exec() == QDialog::Accepted && !efd.selectedFiles().isEmpty() &&
1404 !efd.selectedFiles()[0].isEmpty() )
1405 {
1406
1407 QString exportFileName = efd.selectedFiles()[0];
1408 if ( !multiExport )
1409 {
1410 int stx = efd.selectedNameFilter().indexOf( "(*." );
1411 int etx = efd.selectedNameFilter().indexOf( ")" );
1412
1413 if ( stx > 0 && etx > stx )
1414 {
1415 // Get first extension from selected dropdown.
1416 // i.e. ".wav" from "WAV-File (*.wav), Dummy-File (*.dum)"
1417 suffix = efd.selectedNameFilter().mid( stx + 2, etx - stx - 2 ).split( " " )[0].trimmed();
1418
1419 Qt::CaseSensitivity cs = Qt::CaseSensitive;
1420 #if defined(LMMS_BUILD_APPLE) || defined(LMMS_BUILD_WIN32)
1421 cs = Qt::CaseInsensitive;
1422 #endif
1423 exportFileName.remove( "." + suffix, cs );
1424 if ( efd.selectedFiles()[0].endsWith( suffix ) )
1425 {
1426 if( VersionedSaveDialog::fileExistsQuery( exportFileName + suffix,
1427 tr( "Save project" ) ) )
1428 {
1429 exportFileName += suffix;
1430 }
1431 }
1432 }
1433 }
1434
1435 ExportProjectDialog epd( exportFileName, gui->mainWindow(), multiExport );
1436 epd.exec();
1437 }
1438 }
1439
1440
exportProjectMidi()1441 void Song::exportProjectMidi()
1442 {
1443 if( isEmpty() )
1444 {
1445 QMessageBox::information( gui->mainWindow(),
1446 tr( "Empty project" ),
1447 tr( "This project is empty so exporting makes "
1448 "no sense. Please put some items into "
1449 "Song Editor first!" ) );
1450 return;
1451 }
1452
1453 FileDialog efd( gui->mainWindow() );
1454
1455 efd.setFileMode( FileDialog::AnyFile );
1456
1457 QStringList types;
1458 types << tr("MIDI File (*.mid)");
1459 efd.setNameFilters( types );
1460 QString base_filename;
1461 if( !m_fileName.isEmpty() )
1462 {
1463 efd.setDirectory( QFileInfo( m_fileName ).absolutePath() );
1464 base_filename = QFileInfo( m_fileName ).completeBaseName();
1465 }
1466 else
1467 {
1468 efd.setDirectory( ConfigManager::inst()->userProjectsDir() );
1469 base_filename = tr( "untitled" );
1470 }
1471 efd.selectFile( base_filename + ".mid" );
1472 efd.setDefaultSuffix( "mid");
1473 efd.setWindowTitle( tr( "Select file for project-export..." ) );
1474
1475 efd.setAcceptMode( FileDialog::AcceptSave );
1476
1477
1478 if( efd.exec() == QDialog::Accepted && !efd.selectedFiles().isEmpty() && !efd.selectedFiles()[0].isEmpty() )
1479 {
1480 const QString suffix = ".mid";
1481
1482 QString export_filename = efd.selectedFiles()[0];
1483 if (!export_filename.endsWith(suffix)) export_filename += suffix;
1484
1485 // NOTE start midi export
1486
1487 // instantiate midi export plugin
1488 TrackContainer::TrackList tracks;
1489 TrackContainer::TrackList tracks_BB;
1490 tracks = Engine::getSong()->tracks();
1491 tracks_BB = Engine::getBBTrackContainer()->tracks();
1492 ExportFilter *exf = dynamic_cast<ExportFilter *> (Plugin::instantiate("midiexport", NULL, NULL));
1493 if (exf==NULL) {
1494 qDebug() << "failed to load midi export filter!";
1495 return;
1496 }
1497 exf->tryExport(tracks, tracks_BB, getTempo(), m_masterPitchModel.value(), export_filename);
1498 }
1499 }
1500
1501
1502
updateFramesPerTick()1503 void Song::updateFramesPerTick()
1504 {
1505 Engine::updateFramesPerTick();
1506 }
1507
1508
1509
1510
setModified()1511 void Song::setModified()
1512 {
1513 if( !m_loadingProject )
1514 {
1515 m_modified = true;
1516 if( gui != nullptr && gui->mainWindow() &&
1517 QThread::currentThread() == gui->mainWindow()->thread() )
1518 {
1519 gui->mainWindow()->resetWindowTitle();
1520 }
1521 }
1522 }
1523
1524
1525
1526
addController(Controller * controller)1527 void Song::addController( Controller * controller )
1528 {
1529 if( controller && !m_controllers.contains( controller ) )
1530 {
1531 m_controllers.append( controller );
1532 emit controllerAdded( controller );
1533
1534 this->setModified();
1535 }
1536 }
1537
1538
1539
1540
removeController(Controller * controller)1541 void Song::removeController( Controller * controller )
1542 {
1543 int index = m_controllers.indexOf( controller );
1544 if( index != -1 )
1545 {
1546 m_controllers.remove( index );
1547
1548 emit controllerRemoved( controller );
1549 delete controller;
1550
1551 this->setModified();
1552 }
1553 }
1554
1555
1556
1557
clearErrors()1558 void Song::clearErrors()
1559 {
1560 m_errors.clear();
1561 }
1562
1563
1564
collectError(const QString error)1565 void Song::collectError( const QString error )
1566 {
1567 m_errors.append( error );
1568 }
1569
1570
1571
hasErrors()1572 bool Song::hasErrors()
1573 {
1574 return ( m_errors.length() > 0 );
1575 }
1576
1577
1578
errorSummary()1579 QString Song::errorSummary()
1580 {
1581 QString errors = m_errors.join("\n") + '\n';
1582
1583 errors.prepend( "\n\n" );
1584 errors.prepend( tr( "The following errors occured while loading: " ) );
1585
1586 return errors;
1587 }
1588
1589
1590
1591
1592